1 //! An allocator definition for this embedding.
2 //!
3 //! The Rust standard library and Wasmtime require a memory allocator to be
4 //! configured. For custom embeddings of Wasmtime this might likely already be
5 //! defined elsewhere in the system in which case that should be used. This file
6 //! contains an example implementation using the Rust `dlmalloc` crate using
7 //! memory created by `wasmtime_*` platform symbols. This provides a file that
8 //! manages memory without any extra runtime dependencies, but this is just an
9 //! example.
10 //!
11 //! Allocators in Rust are configured with the `#[global_allocator]` attribute
12 //! and the `GlobalAlloc for T` trait impl. This should be used when hooking
13 //! up to an allocator elsewhere in the system.
14 
15 use dlmalloc::Dlmalloc;
16 use std::alloc::{GlobalAlloc, Layout};
17 use std::ptr;
18 use std::sync::Mutex;
19 
20 #[global_allocator]
21 static MALLOC: MyGlobalDmalloc = MyGlobalDmalloc {
22     dlmalloc: Mutex::new(Dlmalloc::new_with_allocator(MyAllocator)),
23 };
24 
25 struct MyGlobalDmalloc {
26     dlmalloc: Mutex<Dlmalloc<MyAllocator>>,
27 }
28 
29 unsafe impl Send for MyGlobalDmalloc {}
30 unsafe impl Sync for MyGlobalDmalloc {}
31 
32 struct MyAllocator;
33 
34 unsafe impl GlobalAlloc for MyGlobalDmalloc {
35     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
36         self.dlmalloc
37             .lock()
38             .unwrap()
39             .malloc(layout.size(), layout.align())
40     }
41 
42     unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
43         self.dlmalloc
44             .lock()
45             .unwrap()
46             .calloc(layout.size(), layout.align())
47     }
48 
49     unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
50         self.dlmalloc
51             .lock()
52             .unwrap()
53             .realloc(ptr, layout.size(), layout.align(), new_size)
54     }
55 
56     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
57         self.dlmalloc
58             .lock()
59             .unwrap()
60             .free(ptr, layout.size(), layout.align())
61     }
62 }
63 
64 // Hand-copied from `crates/runtime/src/sys/custom/capi.rs`.
65 const PROT_READ: u32 = 1 << 0;
66 const PROT_WRITE: u32 = 1 << 1;
67 extern "C" {
68     fn wasmtime_mmap_new(size: usize, prot_flags: u32, ret: &mut *mut u8) -> i32;
69     fn wasmtime_page_size() -> usize;
70     fn wasmtime_munmap(ptr: *mut u8, size: usize) -> i32;
71 }
72 
73 unsafe impl dlmalloc::Allocator for MyAllocator {
74     fn alloc(&self, size: usize) -> (*mut u8, usize, u32) {
75         unsafe {
76             let mut ptr = ptr::null_mut();
77             let rc = wasmtime_mmap_new(size, PROT_READ | PROT_WRITE, &mut ptr);
78             if rc != 0 {
79                 (ptr::null_mut(), 0, 0)
80             } else {
81                 (ptr, size, 0)
82             }
83         }
84     }
85 
86     fn remap(&self, _ptr: *mut u8, _old: usize, _new: usize, _can_move: bool) -> *mut u8 {
87         std::ptr::null_mut()
88     }
89 
90     fn free_part(&self, _ptr: *mut u8, _old: usize, _new: usize) -> bool {
91         false
92     }
93 
94     fn free(&self, ptr: *mut u8, size: usize) -> bool {
95         unsafe {
96             wasmtime_munmap(ptr, size);
97             true
98         }
99     }
100 
101     fn can_release_part(&self, _flags: u32) -> bool {
102         false
103     }
104 
105     fn allocates_zeros(&self) -> bool {
106         true
107     }
108 
109     fn page_size(&self) -> usize {
110         unsafe { wasmtime_page_size() }
111     }
112 }
113