1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
// Copyright 2017 the authors. See the 'Copyright and license' section of the
// README.md file at the top-level directory of this repository.
//
// Licensed under the Apache License, Version 2.0 (the LICENSE file). This file
// may not be copied, modified, or distributed except according to those terms.

//! Bindings for the C `malloc` API to Rust allocators.
//!
//! This crate provides a mechanism to construct a C allocator - an implementation of `malloc`,
//! `free`, and related functions - that is backed by a Rust allocator (an implementation of the
//! `Alloc` trait).
//!
//! In order to create bindings, two things must be provided: an implementation of the `Alloc`
//! trait, and an implementation of the `LayoutFinder` trait (defined in this crate). Since the C
//! API does not provide size or alignment on `free`, but the Rust `Alloc` API requires both size
//! and alignment on `dealloc`, a mapping must be maintained between allocated objects and those
//! objects' size and alignment. The `LayoutFinder` provides this functionality.

#![no_std]
#![feature(allocator_api)]
#![feature(alloc)]
#![feature(core_intrinsics)]
#![feature(const_fn)]

extern crate alloc;
extern crate libc;
extern crate errno;
extern crate sysconf;
// lazy_static's macros are only used in the macros we define, so if no macros are called (which is
// the case when compiling this crate on its own), then lazy_static's macros (and thus the
// #[macro_use] attribute) will appear unused.  Due to an issue with clippy
// (https://rust-lang-nursery.github.io/rust-clippy/master/index.html#useless_attribute), this
// allow(unused_imports) directive will be seen as useless, so we suppress the useless_attribute
// warning as well.
#[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
#[allow(unused_imports)]
#[macro_use]
extern crate lazy_static;
use alloc::allocator::{Alloc, AllocErr, Layout};

use libc::{c_void, size_t};
use core::{mem, ptr};
use core::cmp::max;

const WORD_SIZE: usize = mem::size_of::<*mut c_void>();

/// A mechanism for mapping allocated objects to their `Layout`s.
///
/// A `LayoutFinder` is an object that can store and look up the `Layout` associated with an
/// allocated object. In the functions generated by this crate, newly-allocated objects will be
/// inserted into a global `LayoutFinder` object, and this `LayoutFinder` will be used to look up
/// the `Layout`s associated with objects passed to `free` and other functions.
pub trait LayoutFinder {
    /// Get the `Layout` associated with an allocated object.
    ///
    /// `get_layout` is passed a pointer to an allocated object, and it returns a `Layout`
    /// describing that object. `ptr` is guaranteed to be an object previously allocated using one
    /// of the various C allocation functions.
    fn get_layout(&self, ptr: *mut u8) -> Layout;

    /// Insert a new object to `Layout` mapping.
    ///
    /// `insert_layout` is passed a pointer to a newly-allocated object and a `Layout` describing
    /// that object, and it stores this mapping. `insert_layout` is called immediately after
    /// allocation in all of the C allocation functions.
    ///
    /// The default implementation of `insert_layout` is a no-op, as some allocators may already
    /// keep track of the information necessary to implement `get_layout` internally.
    fn insert_layout(&self, _ptr: *mut u8, _layout: Layout) {}

    /// Delete an existing object to `Layout` mapping.
    ///
    /// `delete_layout` is passed a pointer to an object whose mapping has previously been
    /// inserted, and it deletes this mapping. `delete_layout` is called immediately after
    /// deallocation in all of the C deallocation functions.
    ///
    /// The default implementation of `delete_layout` is a no-op, as some allocators may already
    /// keep track of the information necessary to implement `get_layout` internally.
    fn delete_layout(&self, _ptr: *mut u8) {}
}

/// A wrapper for a Rust allocator providing C bindings.
///
/// `Malloc` wraps existing `Alloc` and `LayoutFinder` instances and provides methods for each of
/// the various C allocation functions. Most users should simply call the `define_malloc` or
/// `define_malloc_lazy_static` macros, which take care of constructing a `Malloc` instance and
/// defining the various `extern "C"` functions of the C allocation API. Users who wish to expose
/// only a subset of this API will need to instantiate a `Malloc` and define the `extern "C"`
/// functions manually.
pub struct Malloc<A, L: LayoutFinder>
    where for<'a> &'a A: Alloc
{
    alloc: A,
    layout_finder: L,
}

impl<A, L: LayoutFinder> Malloc<A, L>
    where for<'a> &'a A: Alloc
{
    /// Construct a new `Malloc`.
    ///
    /// `new` constructs a new `Malloc` using the provided allocator and `LayoutFinder`. Since C
    /// allocation functions can be called from many threads simultaneously, the allocator must be
    /// thread-safe. Thus, `A` (the type of the `alloc` parameter) isn't required to implement
    /// `Alloc`. Instead, `&A` must implement `Alloc` so that `Alloc`'s methods can be called
    /// concurrently.
    pub const fn new(alloc: A, layout_finder: L) -> Malloc<A, L> {
        Malloc {
            alloc,
            layout_finder,
        }
    }

    /// The C `malloc` function.
    pub unsafe fn malloc(&self, size: size_t) -> *mut c_void {
        if size == 0 {
            return ptr::null_mut();
        }

        // According to the posix_memalign manpage, "The glibc malloc(3) always returns 8-byte
        // aligned memory addresses..." Thus, we round up the size of allocations to 8 bytes in
        // order guarantee that 8 is a valid alignment (since Layout requires that the size is a
        // multiple of the alignment).
        let size = max(size, 8);

        let layout = Layout::from_size_align(size as usize, 8).unwrap();
        match (&self.alloc).alloc(layout.clone()) {
            Ok(ptr) => {
                self.layout_finder.insert_layout(ptr, layout);
                ptr as *mut c_void
            }
            Err(AllocErr::Exhausted { .. }) => ptr::null_mut(),
            Err(AllocErr::Unsupported { .. }) => core::intrinsics::abort(),
        }
    }

    /// The C `free` function.
    pub unsafe fn free(&self, ptr: *mut c_void) {
        if ptr.is_null() {
            return;
        }

        let layout = self.layout_finder.get_layout(ptr as *mut u8);
        self.layout_finder.delete_layout(ptr as *mut u8);
        (&self.alloc).dealloc(ptr as *mut u8, layout);
    }

    /// The obsolete C `cfree` function.
    pub unsafe fn cfree(&self, ptr: *mut c_void) {
        // See https://linux.die.net/man/3/cfree
        self.free(ptr)
    }

    /// The C `calloc` function.
    pub unsafe fn calloc(&self, nmemb: size_t, size: size_t) -> *mut c_void {
        if nmemb == 0 || size == 0 {
            return ptr::null_mut();
        }

        // According to the posix_memalign manpage, "The glibc malloc(3) always returns 8-byte
        // aligned memory addresses..." Thus, we round up the size of allocations to 8 bytes in
        // order guarantee that 8 is a valid alignment (since Layout requires that the size is a
        // multiple of the alignment).
        let size = max(size, 8);

        let layout = Layout::from_size_align(nmemb * size as usize, 8).unwrap();
        match (&self.alloc).alloc_zeroed(layout.clone()) {
            Ok(ptr) => {
                self.layout_finder.insert_layout(ptr, layout);
                ptr as *mut c_void
            }
            Err(AllocErr::Exhausted { .. }) => ptr::null_mut(),
            Err(AllocErr::Unsupported { .. }) => core::intrinsics::abort(),
        }
    }

    /// The obsolete C `valloc` function.
    pub unsafe fn valloc(&self, size: size_t) -> *mut c_void {
        if size == 0 {
            return ptr::null_mut();
        }

        let layout = Layout::from_size_align(size as usize, sysconf::page::pagesize()).unwrap();
        match (&self.alloc).alloc_zeroed(layout.clone()) {
            Ok(ptr) => {
                self.layout_finder.insert_layout(ptr, layout);
                ptr as *mut c_void
            }
            Err(AllocErr::Exhausted { .. }) => ptr::null_mut(),
            Err(AllocErr::Unsupported { .. }) => core::intrinsics::abort(),
        }
    }

    /// The obsolete C `pvalloc` function (only implemented on Linux).
    #[cfg(target_os = "linux")]
    pub unsafe fn pvalloc(&self, size: size_t) -> *mut c_void {
        // See http://man7.org/linux/man-pages/man3/posix_memalign.3.html

        if size == 0 {
            return ptr::null_mut();
        }

        // TODO: round size up to the next multiple of the page size.

        let layout = Layout::from_size_align(size as usize, sysconf::page::pagesize()).unwrap();
        match (&self.alloc).alloc_zeroed(layout.clone()) {
            Ok(ptr) => {
                self.layout_finder.insert_layout(ptr, layout);
                ptr as *mut c_void
            }
            Err(AllocErr::Exhausted { .. }) => ptr::null_mut(),
            Err(AllocErr::Unsupported { .. }) => core::intrinsics::abort(),
        }
    }

    /// The C `realloc` function.
    pub unsafe fn realloc(&self, ptr: *mut c_void, size: size_t) -> *mut c_void {
        // See http://man7.org/linux/man-pages/man3/malloc.3.html,
        // http://www.manpagez.com/man/3/malloc/osx-10.6.php

        if ptr.is_null() {
            return self.malloc(size);
        }

        if size == 0 {
            // According to the Linux manpage: "if size is equal to zero, and ptr is not NULL, then
            // the call is equivalent to free(ptr)." However, according to Darwin: "If size is zero
            // and ptr is not NULL, a new, minimum sized object is allocated and the original
            // object is freed." Since it is valid for malloc(0) to simply return NULL, we opt to
            // implement the Linux behavior in both cases. The only way for this to cause problems
            // is for Darwin programs to rely on the fact that the returned pointer represents the
            // "minimum sized object" instead of only assuming that, since the size passed was 0,
            // the object has 0 size. Since "minimum sized object" does not seem to be a
            // well-defined term, reliance on such behavior is erroneous.

            // TODO: What should we return?
            self.free(ptr);
            return ptr::null_mut();
        }

        // TODO: Round size up to 8 and use 8-byte alignment like in malloc/calloc?

        let layout = self.layout_finder.get_layout(ptr as *mut u8);
        // TODO: What's the right choice of alignment here?
        let new_layout = Layout::from_size_align(size as usize, 1).unwrap();
        match (&self.alloc).realloc(ptr as *mut u8, layout, new_layout.clone()) {
            Ok(ptr) => {
                self.layout_finder.delete_layout(ptr);
                self.layout_finder.insert_layout(ptr, new_layout);
                ptr as *mut c_void
            }
            Err(AllocErr::Exhausted { .. }) => ptr::null_mut(),
            Err(AllocErr::Unsupported { .. }) => core::intrinsics::abort(),
        }
    }

    /// The C `reallocf` function (only implemented on Mac).
    #[cfg(target_os = "macos")]
    pub unsafe fn reallocf(&self, ptr: *mut c_void, size: size_t) -> *mut c_void {
        // See http://www.manpagez.com/man/3/malloc/osx-10.6.php

        if ptr.is_null() {
            return self.malloc(size);
        }

        if size == 0 {
            // According to the malloc manpage: "If size is zero and ptr is not NULL, a new,
            // minimum sized object is allocated and the original object is freed." See the
            // equivalent comment in realloc for why we do this.

            // TODO: What should we return?
            self.free(ptr);
            return ptr::null_mut();
        }

        // TODO: Round size up to 8 and use 8-byte alignment like in malloc/calloc?

        let layout = self.layout_finder.get_layout(ptr as *mut u8);
        // TODO: What's the right choice of alignment here?
        let new_layout = Layout::from_size_align(size as usize, 1).unwrap();
        match (&self.alloc).realloc(ptr as *mut u8, layout, new_layout.clone()) {
            Ok(ptr) => {
                self.layout_finder.delete_layout(ptr);
                self.layout_finder.insert_layout(ptr, new_layout);
                ptr as *mut c_void
            }
            Err(AllocErr::Exhausted { .. }) => {
                self.free(ptr);
                ptr::null_mut()
            }
            Err(AllocErr::Unsupported { .. }) => core::intrinsics::abort(),
        }
    }

    /// The C `reallocarray` function (only implemented on Linux).
    #[cfg(target_os = "linux")]
    pub unsafe fn reallocarray(&self,
                               ptr: *mut c_void,
                               nmemb: size_t,
                               size: size_t)
                               -> *mut c_void {
        // See http://man7.org/linux/man-pages/man3/malloc.3.html

        // According to the malloc manpage, "unlike that realloc() call, reallocarray() fails
        // safely in the case where the multiplication would overflow. If such an overflow occurs,
        // reallocarray() returns NULL, sets errno to ENOMEM, and leaves the original block of
        // memory unchanged."
        match nmemb.checked_mul(size) {
            Some(product) => self.realloc(ptr, product),
            None => {
                errno::set_errno(errno::Errno(libc::ENOMEM));
                ptr::null_mut()
            }
        }
    }


    /// The C `posix_memalign` function.
    pub unsafe fn posix_memalign(&self,
                                 memptr: *mut *mut c_void,
                                 alignment: size_t,
                                 size: size_t)
                                 -> i32 {
        // See http://man7.org/linux/man-pages/man3/posix_memalign.3.html

        // The manpage also specifies that the alignment must be a multiple of the word size, but
        // all powers of two greater than or equal to the word size are multiples of the word size,
        // so we omit that check.
        if alignment <= WORD_SIZE || !alignment.is_power_of_two() {
            return libc::EINVAL;
        }

        if size == 0 {
            *memptr = ptr::null_mut();
            return 0;
        }

        // TODO: posix_memalign does not require that size is a multiple of alignment. Thus, we
        // need to manually round up since valid Layouts must have that property. This is safe
        // because this API never takes the memory region size on deallocation, so it's fine that
        // the caller might think they have a smaller memory region than they actually do.

        let layout = Layout::from_size_align(size as usize, alignment).unwrap();
        match (&self.alloc).alloc(layout.clone()) {
            Ok(ptr) => {
                self.layout_finder.insert_layout(ptr, layout);
                *memptr = ptr as *mut c_void;
                0
            }
            Err(AllocErr::Exhausted { .. }) => libc::ENOMEM,
            Err(AllocErr::Unsupported { .. }) => core::intrinsics::abort(),
        }
    }

    /// The obsolete C `memalign` function (only implemented on Linux).
    #[cfg(target_os = "linux")]
    pub unsafe fn memalign(&self, alignment: size_t, size: size_t) -> *mut c_void {
        // See http://man7.org/linux/man-pages/man3/posix_memalign.3.html

        if !alignment.is_power_of_two() {
            return ptr::null_mut();
        }

        if size == 0 {
            return ptr::null_mut();
        }

        // TODO: memalign does not require that size is a multiple of alignment. Thus, we need to
        // manually round up since valid Layouts must have that property. This is safe because this
        // API never takes the memory region size on deallocation, so it's fine that the caller
        // might think they have a smaller memory region than they actually do.

        let layout = Layout::from_size_align(size as usize, alignment).unwrap();
        match (&self.alloc).alloc(layout.clone()) {
            Ok(ptr) => {
                self.layout_finder.insert_layout(ptr, layout);
                ptr as *mut c_void
            }
            Err(AllocErr::Exhausted { .. }) => ptr::null_mut(),
            Err(AllocErr::Unsupported { .. }) => core::intrinsics::abort(),
        }
    }

    /// The C `aligned_alloc` function (only implemented on Linux).
    #[cfg(target_os = "linux")]
    pub unsafe fn aligned_alloc(&self, alignment: size_t, size: size_t) -> *mut c_void {
        // See http://man7.org/linux/man-pages/man3/posix_memalign.3.html

        // From the aligned_alloc manpage: "The function aligned_alloc() is the same as memalign(),
        // except for the added restriction that size should be a multiple of alignment."
        if size % alignment != 0 {
            return ptr::null_mut();
        }
        self.memalign(alignment, size)
    }
}

/// Define `extern "C"` functions for the C allocation API.
///
/// `define_malloc` is a convenience macro that constructs a global instance of `Malloc` and
/// defines each of the functions of the C allocation API by calling methods on that instance. One
/// function is defined for each of the methods on `Malloc`. Users who only want to define a subset
/// of the C allocation API should instead define these functions manually.
///
/// `define_malloc` takes an allocator type, an expression to construct a new instance of that
/// type, a `LayoutFinder` type, and an expression to construct a new instance of that type. Both
/// expressions must be constant expressions, as they will be used in the initialization of a
/// static variable.
#[macro_export]
macro_rules! define_malloc {
    ($alloc_ty:ty, $alloc_new:expr, $layout_finder_ty:ty, $layout_finder_new:expr) => (
        static HEAP: $crate::Malloc<$alloc_ty, $layout_finder_ty> = $crate::Malloc::new($alloc_new, $layout_finder_new);

        #[no_mangle]
        pub extern "C" fn malloc(size: size_t) -> *mut c_void {
            unsafe { HEAP.malloc(size) }
        }

        #[no_mangle]
        pub extern "C" fn free(ptr: *mut c_void) {
            unsafe { HEAP.free(ptr) }
        }

        #[no_mangle]
        pub extern "C" fn cfree(ptr: *mut c_void) {
            unsafe { HEAP.cfree(ptr) }
        }

        #[no_mangle]
        pub extern "C" fn calloc(nmemb: size_t, size: size_t) -> *mut c_void {
            unsafe { HEAP.calloc(nmemb, size) }
        }

        #[no_mangle]
        pub extern "C" fn valloc(size: size_t) -> *mut c_void {
            unsafe { HEAP.valloc(size) }
        }

        #[cfg(target_os = "linux")]
        #[no_mangle]
        pub extern "C" fn pvalloc(size: size_t) -> *mut c_void {
            unsafe { HEAP.pvalloc(size) }
        }

        #[no_mangle]
        pub extern "C" fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void {
            unsafe { HEAP.realloc(ptr, size) }
        }

        #[cfg(target_os = "macos")]
        #[no_mangle]
        pub extern "C" fn reallocf(ptr: *mut c_void, size: size_t) -> *mut c_void {
            unsafe { HEAP.reallocf(ptr, size) }
        }

        #[cfg(target_os = "linux")]
        pub extern "C" fn reallocarray(ptr: *mut c_void, nmemb: size_t, size: size_t) -> *mut c_void {
            unsafe { HEAP.reallocarray(ptr, nmemb, size) }
        }

        #[no_mangle]
        pub extern "C" fn posix_memalign(memptr: *mut *mut c_void, alignment: size_t, size: size_t) -> i32 {
            unsafe { HEAP.posix_memalign(memptr, alignment, size) }
        }

        #[cfg(target_os = "linux")]
        #[no_mangle]
        pub extern "C" fn memalign(alignment: size_t, size: size_t) -> *mut c_void {
            unsafe { HEAP.memalign(alignment, size) }
        }

        #[cfg(target_os = "linux")]
        #[no_mangle]
        pub extern "C" fn aligned_alloc(alignment: size_t, size: size_t) -> *mut c_void {
            unsafe { HEAP.aligned_alloc(alignment, size) }
        }
    )
}

// This line re-exports the macros from lazy_static so that they'll be available to the code
// calling define_malloc_lazy_static. This allows define_malloc_lazy_static to be used without the
// caller needing to know about lazy_static and import its macros themselves.
//
// Credit to https://users.rust-lang.org/t/how-to-use-macro-inside-another-macro/12061/2
pub use lazy_static::*;

/// Define `extern "C"` functions for the C allocation API with non-constant initializers.
///
/// `define_malloc_lazy_static` is like `define_malloc`, except there is no requirement that the
/// initialization expressions must be constant. Instead, `lazy_static` is used to construct the
/// global `Malloc` instance.
#[macro_export]
macro_rules! define_malloc_lazy_static {
    ($alloc_ty:ty, $alloc_new:expr, $layout_finder_ty:ty, $layout_finder_new:expr) => (
        lazy_static!{
            static ref HEAP: $crate::Malloc<$alloc_ty, $layout_finder_ty> = $crate::Malloc::new($alloc_new, $layout_finder_new);
        }

        #[no_mangle]
        pub extern "C" fn malloc(size: size_t) -> *mut c_void {
            unsafe { HEAP.malloc(size) }
        }

        #[no_mangle]
        pub extern "C" fn free(ptr: *mut c_void) {
            unsafe { HEAP.free(ptr) }
        }

        #[no_mangle]
        pub extern "C" fn cfree(ptr: *mut c_void) {
            unsafe { HEAP.cfree(ptr) }
        }

        #[no_mangle]
        pub extern "C" fn calloc(nmemb: size_t, size: size_t) -> *mut c_void {
            unsafe { HEAP.calloc(nmemb, size) }
        }

        #[no_mangle]
        pub extern "C" fn valloc(size: size_t) -> *mut c_void {
            unsafe { HEAP.valloc(size) }
        }

        #[cfg(target_os = "linux")]
        #[no_mangle]
        pub extern "C" fn pvalloc(size: size_t) -> *mut c_void {
            unsafe { HEAP.pvalloc(size) }
        }

        #[no_mangle]
        pub extern "C" fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void {
            unsafe { HEAP.realloc(ptr, size) }
        }

        #[cfg(target_os = "macos")]
        #[no_mangle]
        pub extern "C" fn reallocf(ptr: *mut c_void, size: size_t) -> *mut c_void {
            unsafe { HEAP.reallocf(ptr, size) }
        }

        #[cfg(target_os = "linux")]
        pub extern "C" fn reallocarray(ptr: *mut c_void, nmemb: size_t, size: size_t) -> *mut c_void {
            unsafe { HEAP.reallocarray(ptr, nmemb, size) }
        }

        #[no_mangle]
        pub extern "C" fn posix_memalign(memptr: *mut *mut c_void, alignment: size_t, size: size_t) -> i32 {
            unsafe { HEAP.posix_memalign(memptr, alignment, size) }
        }

        #[cfg(target_os = "linux")]
        #[no_mangle]
        pub extern "C" fn memalign(alignment: size_t, size: size_t) -> *mut c_void {
            unsafe { HEAP.memalign(alignment, size) }
        }

        #[cfg(target_os = "linux")]
        #[no_mangle]
        pub extern "C" fn aligned_alloc(alignment: size_t, size: size_t) -> *mut c_void {
            unsafe { HEAP.aligned_alloc(alignment, size) }
        }
    )
}