Skip to main content

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}