better_mimalloc_sys/lib.rs
1#![no_std]
2// Copyright 2019 Octavian Oncescu
3
4use core::ffi::c_void;
5
6extern crate libc;
7
8#[cfg(feature = "extended")]
9mod extended;
10#[cfg(feature = "extended")]
11pub use extended::*;
12
13extern "C" {
14 /// Allocate zero-initialized `size` bytes.
15 ///
16 /// Returns a pointer to newly allocated zero-initialized memory, or null if
17 /// out of memory.
18 pub fn mi_zalloc(size: usize) -> *mut c_void;
19
20 /// Allocate `size` bytes.
21 ///
22 /// Returns pointer to the allocated memory or null if out of memory.
23 /// Returns a unique pointer if called with `size` 0.
24 pub fn mi_malloc(size: usize) -> *mut c_void;
25
26 /// Re-allocate memory to `newsize` bytes.
27 ///
28 /// Return pointer to the allocated memory or null if out of memory. If null
29 /// is returned, the pointer `p` is not freed. Otherwise the original
30 /// pointer is either freed or returned as the reallocated result (in case
31 /// it fits in-place with the new size).
32 ///
33 /// If `p` is null, it behaves as [`mi_malloc`]. If `newsize` is larger than
34 /// the original `size` allocated for `p`, the bytes after `size` are
35 /// uninitialized.
36 pub fn mi_realloc(p: *mut c_void, newsize: usize) -> *mut c_void;
37
38 /// Allocate `size` bytes aligned by `alignment`, initialized to zero.
39 ///
40 /// Return pointer to the allocated memory or null if out of memory.
41 ///
42 /// Returns a unique pointer if called with `size` 0.
43 pub fn mi_zalloc_aligned(size: usize, alignment: usize) -> *mut c_void;
44
45 /// Allocate `size` bytes aligned by `alignment`.
46 ///
47 /// Return pointer to the allocated memory or null if out of memory.
48 ///
49 /// Returns a unique pointer if called with `size` 0.
50 pub fn mi_malloc_aligned(size: usize, alignment: usize) -> *mut c_void;
51
52 /// Re-allocate memory to `newsize` bytes, aligned by `alignment`.
53 ///
54 /// Return pointer to the allocated memory or null if out of memory. If null
55 /// is returned, the pointer `p` is not freed. Otherwise the original
56 /// pointer is either freed or returned as the reallocated result (in case
57 /// it fits in-place with the new size).
58 ///
59 /// If `p` is null, it behaves as [`mi_malloc_aligned`]. If `newsize` is
60 /// larger than the original `size` allocated for `p`, the bytes after
61 /// `size` are uninitialized.
62 pub fn mi_realloc_aligned(p: *mut c_void, newsize: usize, alignment: usize) -> *mut c_void;
63
64 /// Free previously allocated memory.
65 ///
66 /// The pointer `p` must have been allocated before (or be null).
67 pub fn mi_free(p: *mut c_void);
68}
69
70/// When using the `"override"` feature flag, the user wants us to globally
71/// override the system allocator.
72///
73/// However, since we build and link `mimalloc` as a static library/archive,
74/// the linker may decide to not care about our overrides if it can't directly
75/// see references to the symbols, see the following link for details:
76/// <https://maskray.me/blog/2021-06-20-symbol-processing#archive-processing>
77///
78/// This is problematic if `mimalloc` is used from a library that by itself
79/// doesn't allocate, yet invokes other shared libraries that do, since then
80/// the linker wouldn't see any references to `malloc`/`free`, and the symbols
81/// would not be overridden.
82///
83/// To avoid this problem, we make sure that the allocator functions are
84/// visible to the linker.
85///
86/// To avoid this problem, we reference `mi_malloc` in a `#[used]` static.
87/// This makes it known to `rustc`, which will create a reference to it in a
88/// `symbols.o` stub file that is later passed directly to the linker (instead
89/// of being in an archive). See the following link for details on how this
90/// works: <https://github.com/rust-lang/rust/pull/95604>
91///
92/// NOTE: This works because `mimalloc` is compiled into a single object file
93/// in `static.c`. If it was split across multiple files, we'd need to
94/// reference each symbol. See also the comment at the top of `static.c`.
95///
96/// NOTE: On macOS, mimalloc doesn't just override malloc/free, but also
97/// registers itself with the allocator's zone APIs in a ctor
98/// (`_mi_macos_override_malloc`, marked with `__attribute__((constructor))`).
99/// Similarly to above, for the Mach-O linker to actually consider ctors as
100/// "used" when defined in an archive member in a static library, so we need
101/// to explicitly reference something in the object file. The constructor
102/// symbol itself is static, so we can't get a reference to that, so instead
103/// we reference `mi_malloc` here too).
104#[cfg(feature = "override")]
105mod set_up_statics {
106 use super::*;
107 #[used] // Could be `#[used(linker)]` once stable
108 static USED: unsafe extern "C" fn(usize) -> *mut c_void = mi_malloc;
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn it_frees_memory_malloc() {
117 let ptr = unsafe { mi_malloc_aligned(8, 8) } as *mut u8;
118 unsafe { mi_free(ptr as *mut c_void) };
119 }
120
121 #[test]
122 fn it_frees_memory_zalloc() {
123 let ptr = unsafe { mi_zalloc_aligned(8, 8) } as *mut u8;
124 unsafe { mi_free(ptr as *mut c_void) };
125 }
126
127 #[test]
128 fn it_frees_memory_realloc() {
129 let ptr = unsafe { mi_malloc_aligned(8, 8) } as *mut u8;
130 let ptr = unsafe { mi_realloc_aligned(ptr as *mut c_void, 8, 8) } as *mut u8;
131 unsafe { mi_free(ptr as *mut c_void) };
132 }
133
134 #[cfg(all(feature = "override", target_vendor = "apple"))]
135 #[test]
136 fn mimalloc_and_libc_are_interoperable_when_overridden() {
137 let ptr = unsafe { mi_malloc(42) };
138 unsafe { libc::free(ptr) };
139 }
140}