Skip to content

Commit 989f63b

Browse files
committed
Auto merge of #3856 - jder:mac-native-libs, r=RalfJung
Enable native libraries on macOS Fixes #3595 by using `-fvisibility=hidden` and the visibility attribute supported by both gcc and clang rather than the previous gcc-only mechanism for symbol hiding. Also brings over cfg changes from #3594 which enable native-lib functionality on all unixes. Thanks for taking a look, feedback very welcome! cc `@RalfJung`
2 parents d0f23b4 + de96082 commit 989f63b

14 files changed

+78
-52
lines changed

src/tools/miri/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ features = ['unprefixed_malloc_on_supported_platforms']
3737

3838
[target.'cfg(unix)'.dependencies]
3939
libc = "0.2"
40-
41-
[target.'cfg(target_os = "linux")'.dependencies]
4240
libffi = "3.2.0"
4341
libloading = "0.8"
4442

src/tools/miri/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ to Miri failing to detect cases of undefined behavior in a program.
383383
file descriptors will be mixed up.
384384
This is **work in progress**; currently, only integer arguments and return values are
385385
supported (and no, pointer/integer casts to work around this limitation will not work;
386-
they will fail horribly). It also only works on Linux hosts for now.
386+
they will fail horribly). It also only works on Unix hosts for now.
387387
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
388388
This can be used to find which parts of your program are executing slowly under Miri.
389389
The profile is written out to a file inside a directory called `<name>`, and can be processed

src/tools/miri/src/machine.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -535,9 +535,9 @@ pub struct MiriMachine<'tcx> {
535535
pub(crate) basic_block_count: u64,
536536

537537
/// Handle of the optional shared object file for native functions.
538-
#[cfg(target_os = "linux")]
538+
#[cfg(unix)]
539539
pub native_lib: Option<(libloading::Library, std::path::PathBuf)>,
540-
#[cfg(not(target_os = "linux"))]
540+
#[cfg(not(unix))]
541541
pub native_lib: Option<!>,
542542

543543
/// Run a garbage collector for BorTags every N basic blocks.
@@ -678,7 +678,7 @@ impl<'tcx> MiriMachine<'tcx> {
678678
report_progress: config.report_progress,
679679
basic_block_count: 0,
680680
clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
681-
#[cfg(target_os = "linux")]
681+
#[cfg(unix)]
682682
native_lib: config.native_lib.as_ref().map(|lib_file_path| {
683683
let target_triple = layout_cx.tcx.sess.opts.target_triple.triple();
684684
// Check if host target == the session target.
@@ -700,9 +700,9 @@ impl<'tcx> MiriMachine<'tcx> {
700700
lib_file_path.clone(),
701701
)
702702
}),
703-
#[cfg(not(target_os = "linux"))]
703+
#[cfg(not(unix))]
704704
native_lib: config.native_lib.as_ref().map(|_| {
705-
panic!("loading external .so files is only supported on Linux")
705+
panic!("calling functions from native libraries via FFI is only supported on Unix")
706706
}),
707707
gc_interval: config.gc_interval,
708708
since_gc: 0,

src/tools/miri/src/shims/foreign_items.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
226226
let this = self.eval_context_mut();
227227

228228
// First deal with any external C functions in linked .so file.
229-
#[cfg(target_os = "linux")]
229+
#[cfg(unix)]
230230
if this.machine.native_lib.as_ref().is_some() {
231231
use crate::shims::native_lib::EvalContextExt as _;
232232
// An Ok(false) here means that the function being called was not exported

src/tools/miri/src/shims/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
mod alloc;
44
mod backtrace;
5-
#[cfg(target_os = "linux")]
5+
#[cfg(unix)]
66
mod native_lib;
77
mod unix;
88
mod wasi;

src/tools/miri/tests/native-lib/fail/function_not_in_so.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//@only-target-linux
1+
// Only works on Unix targets
2+
//@ignore-target-windows
3+
//@ignore-target-wasm
24
//@only-on-host
35
//@normalize-stderr-test: "OS `.*`" -> "$$OS"
46

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Only works on Unix targets
2+
//@ignore-target-windows
3+
//@ignore-target-wasm
4+
//@only-on-host
5+
//@normalize-stderr-test: "OS `.*`" -> "$$OS"
6+
7+
extern "C" {
8+
fn not_exported();
9+
}
10+
11+
fn main() {
12+
unsafe {
13+
not_exported(); //~ ERROR: unsupported operation: can't call foreign function `not_exported`
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: unsupported operation: can't call foreign function `not_exported` on $OS
2+
--> $DIR/private_function.rs:LL:CC
3+
|
4+
LL | not_exported();
5+
| ^^^^^^^^^^^^^^ can't call foreign function `not_exported` on $OS
6+
|
7+
= help: if this is a basic API commonly used on this target, please report an issue with Miri
8+
= help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases
9+
= note: BACKTRACE:
10+
= note: inside `main` at $DIR/private_function.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to 1 previous error
15+

src/tools/miri/tests/native-lib/native-lib.map

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/tools/miri/tests/native-lib/pass/ptr_read_access.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//@only-target-linux
1+
// Only works on Unix targets
2+
//@ignore-target-windows
3+
//@ignore-target-wasm
24
//@only-on-host
35

46
fn main() {

src/tools/miri/tests/native-lib/pass/scalar_arguments.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//@only-target-linux
1+
// Only works on Unix targets
2+
//@ignore-target-windows
3+
//@ignore-target-wasm
24
//@only-on-host
35

46
extern "C" {

src/tools/miri/tests/native-lib/ptr_read_access.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
#include <stdio.h>
22

3+
// See comments in build_native_lib()
4+
#define EXPORT __attribute__((visibility("default")))
5+
36
/* Test: test_pointer */
47

5-
void print_pointer(const int *ptr) {
8+
EXPORT void print_pointer(const int *ptr) {
69
printf("printing pointer dereference from C: %d\n", *ptr);
710
}
811

@@ -12,7 +15,7 @@ typedef struct Simple {
1215
int field;
1316
} Simple;
1417

15-
int access_simple(const Simple *s_ptr) {
18+
EXPORT int access_simple(const Simple *s_ptr) {
1619
return s_ptr->field;
1720
}
1821

@@ -24,7 +27,7 @@ typedef struct Nested {
2427
} Nested;
2528

2629
// Returns the innermost/last value of a Nested pointer chain.
27-
int access_nested(const Nested *n_ptr) {
30+
EXPORT int access_nested(const Nested *n_ptr) {
2831
// Edge case: `n_ptr == NULL` (i.e. first Nested is None).
2932
if (!n_ptr) { return 0; }
3033

@@ -42,6 +45,6 @@ typedef struct Static {
4245
struct Static *recurse;
4346
} Static;
4447

45-
int access_static(const Static *s_ptr) {
48+
EXPORT int access_static(const Static *s_ptr) {
4649
return s_ptr->recurse->recurse->value;
4750
}
Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
11
#include <stdio.h>
22

3-
int add_one_int(int x) {
3+
// See comments in build_native_lib()
4+
#define EXPORT __attribute__((visibility("default")))
5+
6+
EXPORT int add_one_int(int x) {
47
return 2 + x;
58
}
69

7-
void printer() {
10+
EXPORT void printer(void) {
811
printf("printing from C\n");
912
}
1013

1114
// function with many arguments, to test functionality when some args are stored
1215
// on the stack
13-
int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
16+
EXPORT int test_stack_spill(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l) {
1417
return a+b+c+d+e+f+g+h+i+j+k+l;
1518
}
1619

17-
unsigned int get_unsigned_int() {
20+
EXPORT unsigned int get_unsigned_int(void) {
1821
return -10;
1922
}
2023

21-
short add_int16(short x) {
24+
EXPORT short add_int16(short x) {
2225
return x + 3;
2326
}
2427

25-
long add_short_to_long(short x, long y) {
28+
EXPORT long add_short_to_long(short x, long y) {
2629
return x + y;
2730
}
31+
32+
// To test that functions not marked with EXPORT cannot be called by Miri.
33+
int not_exported(void) {
34+
return 0;
35+
}

src/tools/miri/tests/ui.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,21 @@ fn build_native_lib() -> PathBuf {
3636
// Create the directory if it does not already exist.
3737
std::fs::create_dir_all(&so_target_dir)
3838
.expect("Failed to create directory for shared object file");
39-
let so_file_path = so_target_dir.join("native-lib.so");
39+
// We use a platform-neutral file extension to avoid having to hard-code alternatives.
40+
let native_lib_path = so_target_dir.join("native-lib.module");
4041
let cc_output = Command::new(cc)
4142
.args([
4243
"-shared",
44+
"-fPIC",
45+
// We hide all symbols by default and export just the ones we need
46+
// This is to future-proof against a more complex shared object which eg defines its own malloc
47+
// (but we wouldn't want miri to call that, as it would if it was exported).
48+
"-fvisibility=hidden",
4349
"-o",
44-
so_file_path.to_str().unwrap(),
50+
native_lib_path.to_str().unwrap(),
4551
// FIXME: Automate gathering of all relevant C source files in the directory.
4652
"tests/native-lib/scalar_arguments.c",
4753
"tests/native-lib/ptr_read_access.c",
48-
// Only add the functions specified in libcode.version to the shared object file.
49-
// This is to avoid automatically adding `malloc`, etc.
50-
// Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/
51-
"-fPIC",
52-
"-Wl,--version-script=tests/native-lib/native-lib.map",
5354
// Ensure we notice serious problems in the C code.
5455
"-Wall",
5556
"-Wextra",
@@ -64,7 +65,7 @@ fn build_native_lib() -> PathBuf {
6465
String::from_utf8_lossy(&cc_output.stderr),
6566
);
6667
}
67-
so_file_path
68+
native_lib_path
6869
}
6970

7071
/// Does *not* set any args or env vars, since it is shared between the test runner and
@@ -300,7 +301,7 @@ fn main() -> Result<()> {
300301
WithDependencies,
301302
tmpdir.path(),
302303
)?;
303-
if cfg!(target_os = "linux") {
304+
if cfg!(unix) {
304305
ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?;
305306
ui(
306307
Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },

0 commit comments

Comments
 (0)