Skip to content

Commit 2586fdc

Browse files
authored
Merge pull request #145 from madsmtm/override
2 parents 55bcdbc + af89d04 commit 2586fdc

File tree

8 files changed

+152
-2
lines changed

8 files changed

+152
-2
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050

5151
- name: Test libmimalloc-sys crate bindings (no secure)
5252
run: cargo run -p libmimalloc-sys-test
53-
53+
5454
- name: Build (extended)
5555
run: cargo build --features extended
5656

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

90+
- name: Test override dylib
91+
if: ${{ !contains(matrix.os, 'windows') }}
92+
run: cargo run -ptest-override-with-dylib --features override
93+
9094
lint:
9195
name: Rustfmt / Clippy
9296
runs-on: ubuntu-latest

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ license = "MIT"
1515
readme = "README.md"
1616

1717
[workspace]
18-
members = ["libmimalloc-sys", "libmimalloc-sys/sys-test"]
18+
members = [
19+
"libmimalloc-sys",
20+
"libmimalloc-sys/sys-test",
21+
"test-override-with-dylib",
22+
]
1923

2024
[badges]
2125
travis-ci = { repository = "purpleprotocol/mimalloc_rust" }

libmimalloc-sys/build.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ fn main() {
1515

1616
let target_os = env::var("CARGO_CFG_TARGET_OS").expect("target_os not defined!");
1717
let target_family = env::var("CARGO_CFG_TARGET_FAMILY").expect("target_family not defined!");
18+
let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").expect("target_vendor not defined!");
1819
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("target_arch not defined!");
1920

2021
if target_family != "windows" {
@@ -27,6 +28,10 @@ fn main() {
2728
if target_family != "windows" {
2829
build.define("MI_MALLOC_OVERRIDE", None);
2930
}
31+
if target_vendor == "apple" {
32+
build.define("MI_OSX_ZONE", Some("1"));
33+
build.define("MI_OSX_INTERPOSE", Some("1"));
34+
}
3035
}
3136

3237
if env::var_os("CARGO_FEATURE_SECURE").is_some() {

libmimalloc-sys/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,11 @@ mod tests {
8989
let ptr = unsafe { mi_realloc_aligned(ptr as *mut c_void, 8, 8) } as *mut u8;
9090
unsafe { mi_free(ptr as *mut c_void) };
9191
}
92+
93+
#[cfg(all(feature = "override", target_vendor = "apple"))]
94+
#[test]
95+
fn mimalloc_and_libc_are_interoperable_when_overridden() {
96+
let ptr = unsafe { mi_malloc(42) };
97+
unsafe { libc::free(ptr) };
98+
}
9299
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "test-override-with-dylib"
3+
version = "0.0.0"
4+
license = "MIT OR Apache-2.0"
5+
description = "A test helper for mimalloc"
6+
edition = "2018"
7+
publish = false
8+
9+
[dependencies]
10+
libc = { version = "^0.2.8", default-features = false }
11+
libmimalloc-sys = { path = "../libmimalloc-sys" }
12+
13+
[build-dependencies]
14+
cc = "^1.0.13"
15+
16+
[features]
17+
override = ["libmimalloc-sys/override"]

test-override-with-dylib/build.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//! Build shared library `dep.c`.
2+
use std::{env, path::PathBuf};
3+
4+
fn main() {
5+
println!("cargo:rerun-if-changed=src/dep.c");
6+
7+
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
8+
9+
// NOTE: Only for testing, extension is wrong when cross-compiling.
10+
let dylib = out_dir.join(format!(
11+
"{}dep{}",
12+
env::consts::DLL_PREFIX,
13+
env::consts::DLL_SUFFIX
14+
));
15+
16+
let status = cc::Build::new()
17+
.get_compiler()
18+
.to_command()
19+
.arg("src/dep.c")
20+
.arg("-shared")
21+
.arg("-o")
22+
.arg(&dylib)
23+
.status()
24+
.unwrap();
25+
assert!(status.success());
26+
27+
println!("cargo:rustc-link-lib=dylib=dep");
28+
println!("cargo:rustc-link-search=native={}", out_dir.display());
29+
}

test-override-with-dylib/src/dep.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#define _GNU_SOURCE
2+
#include <stdlib.h>
3+
#include <stdio.h>
4+
#include <dlfcn.h>
5+
6+
const char* dep_lookup_malloc_address(void) {
7+
Dl_info info;
8+
if (!dladdr((void *)malloc, &info)) {
9+
printf("failed finding `malloc`\n");
10+
abort();
11+
}
12+
return info.dli_fname;
13+
}
14+
15+
void* dep_malloc(size_t size) {
16+
return malloc(size);
17+
}
18+
19+
void dep_free(void* ptr) {
20+
free(ptr);
21+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//! Test that when overriding, that the `malloc` and `free` symbols are
2+
//! interoperable, even across a dylib boundary.
3+
use core::ffi::{c_char, c_void, CStr};
4+
5+
// Make sure that `rustc` links this.
6+
use libmimalloc_sys as _;
7+
8+
extern "C-unwind" {
9+
fn dep_lookup_malloc_address() -> *const c_char;
10+
fn dep_malloc(size: libc::size_t) -> *mut c_void;
11+
fn dep_free(ptr: *mut c_void);
12+
}
13+
14+
fn lookup_malloc_address() -> *const c_char {
15+
unsafe {
16+
let mut info: libc::Dl_info = core::mem::zeroed();
17+
let fnptr: unsafe extern "C" fn(libc::size_t) -> *mut c_void = libc::malloc;
18+
let fnptr = fnptr as *const c_void;
19+
if libc::dladdr(fnptr, &mut info) == 0 {
20+
libc::printf(b"failed finding `malloc`\n\0".as_ptr().cast());
21+
libc::abort();
22+
}
23+
info.dli_fname
24+
}
25+
}
26+
27+
fn main() {
28+
// Check that pointers created with `malloc` in a dylib dependency can be
29+
// free'd with `free` here.
30+
let ptr = unsafe { libc::malloc(10) };
31+
unsafe { dep_free(ptr) };
32+
let ptr = unsafe { dep_malloc(10) };
33+
unsafe { libc::free(ptr) };
34+
35+
// If overidden, test that the same is true for `mi_malloc` being
36+
// interoperable with `free`.
37+
if cfg!(feature = "override") {
38+
let ptr = unsafe { libmimalloc_sys::mi_malloc(10) };
39+
unsafe { dep_free(ptr) };
40+
let ptr = unsafe { libmimalloc_sys::mi_malloc(10) };
41+
unsafe { libc::free(ptr) };
42+
43+
let ptr = unsafe { libc::malloc(10) };
44+
unsafe { libmimalloc_sys::mi_free(ptr) };
45+
let ptr = unsafe { dep_malloc(10) };
46+
unsafe { libmimalloc_sys::mi_free(ptr) };
47+
}
48+
49+
// Extra check that the symbol was actually from the same place.
50+
let dep = unsafe { CStr::from_ptr(dep_lookup_malloc_address()) };
51+
let here = unsafe { CStr::from_ptr(lookup_malloc_address()) };
52+
53+
if cfg!(target_vendor = "apple") {
54+
// macOS / Mach-O symbols are not overriden in dependencies, they are
55+
// hooked into with `zone_register`.
56+
assert_eq!(
57+
dep.to_str().unwrap(),
58+
"/usr/lib/system/libsystem_malloc.dylib"
59+
);
60+
} else {
61+
assert_eq!(dep, here);
62+
}
63+
}

0 commit comments

Comments
 (0)