Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:

- name: Test libmimalloc-sys crate bindings (no secure)
run: cargo run -p libmimalloc-sys-test

- name: Build (extended)
run: cargo build --features extended

Expand Down Expand Up @@ -87,6 +87,10 @@ jobs:
- name: Test libmimalloc-sys crate bindings (v3, extended)
run: cargo run --features libmimalloc-sys-test/v3,libmimalloc-sys-test/extended -p libmimalloc-sys-test

- name: Test override dylib
if: ${{ !contains(matrix.os, 'windows') }}
run: cargo run -ptest-override-with-dylib --features override

lint:
name: Rustfmt / Clippy
runs-on: ubuntu-latest
Expand Down
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ license = "MIT"
readme = "README.md"

[workspace]
members = ["libmimalloc-sys", "libmimalloc-sys/sys-test"]
members = [
"libmimalloc-sys",
"libmimalloc-sys/sys-test",
"test-override-with-dylib",
]

[badges]
travis-ci = { repository = "purpleprotocol/mimalloc_rust" }
Expand Down
5 changes: 5 additions & 0 deletions libmimalloc-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn main() {

let target_os = env::var("CARGO_CFG_TARGET_OS").expect("target_os not defined!");
let target_family = env::var("CARGO_CFG_TARGET_FAMILY").expect("target_family not defined!");
let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").expect("target_vendor not defined!");
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("target_arch not defined!");

if target_family != "windows" {
Expand All @@ -27,6 +28,10 @@ fn main() {
if target_family != "windows" {
build.define("MI_MALLOC_OVERRIDE", None);
}
if target_vendor == "apple" {
build.define("MI_OSX_ZONE", Some("1"));
build.define("MI_OSX_INTERPOSE", Some("1"));
}
}

if env::var_os("CARGO_FEATURE_SECURE").is_some() {
Expand Down
7 changes: 7 additions & 0 deletions libmimalloc-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,11 @@ mod tests {
let ptr = unsafe { mi_realloc_aligned(ptr as *mut c_void, 8, 8) } as *mut u8;
unsafe { mi_free(ptr as *mut c_void) };
}

#[cfg(all(feature = "override", target_vendor = "apple"))]
#[test]
fn mimalloc_and_libc_are_interoperable_when_overridden() {
let ptr = unsafe { mi_malloc(42) };
unsafe { libc::free(ptr) };
}
}
17 changes: 17 additions & 0 deletions test-override-with-dylib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "test-override-with-dylib"
version = "0.0.0"
license = "MIT OR Apache-2.0"
description = "A test helper for mimalloc"
edition = "2018"
publish = false

[dependencies]
libc = { version = "^0.2.8", default-features = false }
libmimalloc-sys = { path = "../libmimalloc-sys" }

[build-dependencies]
cc = "^1.0.13"

[features]
override = ["libmimalloc-sys/override"]
29 changes: 29 additions & 0 deletions test-override-with-dylib/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! Build shared library `dep.c`.
use std::{env, path::PathBuf};

fn main() {
println!("cargo:rerun-if-changed=src/dep.c");

let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());

// NOTE: Only for testing, extension is wrong when cross-compiling.
let dylib = out_dir.join(format!(
"{}dep{}",
env::consts::DLL_PREFIX,
env::consts::DLL_SUFFIX
));

let status = cc::Build::new()
.get_compiler()
.to_command()
.arg("src/dep.c")
.arg("-shared")
.arg("-o")
.arg(&dylib)
.status()
.unwrap();
assert!(status.success());

println!("cargo:rustc-link-lib=dylib=dep");
println!("cargo:rustc-link-search=native={}", out_dir.display());
}
21 changes: 21 additions & 0 deletions test-override-with-dylib/src/dep.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>

const char* dep_lookup_malloc_address(void) {
Dl_info info;
if (!dladdr((void *)malloc, &info)) {
printf("failed finding `malloc`\n");
abort();
}
return info.dli_fname;
}

void* dep_malloc(size_t size) {
return malloc(size);
}

void dep_free(void* ptr) {
free(ptr);
}
63 changes: 63 additions & 0 deletions test-override-with-dylib/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! Test that when overriding, that the `malloc` and `free` symbols are
//! interoperable, even across a dylib boundary.
use core::ffi::{c_char, c_void, CStr};

// Make sure that `rustc` links this.
use libmimalloc_sys as _;

extern "C-unwind" {
fn dep_lookup_malloc_address() -> *const c_char;
fn dep_malloc(size: libc::size_t) -> *mut c_void;
fn dep_free(ptr: *mut c_void);
}

fn lookup_malloc_address() -> *const c_char {
unsafe {
let mut info: libc::Dl_info = core::mem::zeroed();
let fnptr: unsafe extern "C" fn(libc::size_t) -> *mut c_void = libc::malloc;
let fnptr = fnptr as *const c_void;
if libc::dladdr(fnptr, &mut info) == 0 {
libc::printf(b"failed finding `malloc`\n\0".as_ptr().cast());
libc::abort();
}
info.dli_fname
}
}

fn main() {
// Check that pointers created with `malloc` in a dylib dependency can be
// free'd with `free` here.
let ptr = unsafe { libc::malloc(10) };
unsafe { dep_free(ptr) };
let ptr = unsafe { dep_malloc(10) };
unsafe { libc::free(ptr) };

// If overidden, test that the same is true for `mi_malloc` being
// interoperable with `free`.
if cfg!(feature = "override") {
let ptr = unsafe { libmimalloc_sys::mi_malloc(10) };
unsafe { dep_free(ptr) };
let ptr = unsafe { libmimalloc_sys::mi_malloc(10) };
unsafe { libc::free(ptr) };

let ptr = unsafe { libc::malloc(10) };
unsafe { libmimalloc_sys::mi_free(ptr) };
let ptr = unsafe { dep_malloc(10) };
unsafe { libmimalloc_sys::mi_free(ptr) };
}

// Extra check that the symbol was actually from the same place.
let dep = unsafe { CStr::from_ptr(dep_lookup_malloc_address()) };
let here = unsafe { CStr::from_ptr(lookup_malloc_address()) };

if cfg!(target_vendor = "apple") {
// macOS / Mach-O symbols are not overriden in dependencies, they are
// hooked into with `zone_register`.
assert_eq!(
dep.to_str().unwrap(),
"/usr/lib/system/libsystem_malloc.dylib"
);
} else {
assert_eq!(dep, here);
}
}
Loading