Skip to content

Commit 88a7882

Browse files
author
Ellen Arteca
committed
C FFI support for functions with int args and returns
1 parent d5853bc commit 88a7882

19 files changed

+565
-4
lines changed

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@ target
22
/doc
33
tex/*/out
44
*.dot
5+
*.out
56
*.rs.bk
67
.vscode
78
*.mm_profdata
89
perf.data
910
perf.data.old
1011
flamegraph.svg
12+
<<<<<<< HEAD
13+
=======
14+
<<<<<<< HEAD
15+
tests/extern-so/libtestlib.so
16+
=======
17+
>>>>>>> master
18+
>>>>>>> 58ba05a0 (C FFI support for functions with int args and returns)
1119
.auto-*

Cargo.lock

+38
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ doctest = false # and no doc tests
2020
[dependencies]
2121
getrandom = { version = "0.2", features = ["std"] }
2222
env_logger = "0.9"
23+
libffi = "3.0.0"
24+
libloading = "0.7"
2325
log = "0.4"
2426
shell-escape = "0.1.4"
2527
rand = "0.8"

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,17 @@ to Miri failing to detect cases of undefined behavior in a program.
346346
this flag is **unsound**.
347347
* `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
348348
memory effects.
349+
* `-Zmiri-extern-so-file=<path to a shared object file>` is an experimental flag for providing support
350+
for FFI calls. Functions not provided by that file are still executed via the usual Miri shims.
351+
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause undefined behaviour in Miri itself!
352+
And of course, Miri cannot do any checks on the actions taken by the external code.
353+
Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions
354+
working on file descriptors, you will have to replace *all* of them, or the two kinds of
355+
file descriptors will be mixed up.
356+
This is **work in progress**; currently, only integer arguments and return values are
357+
supported (and no, pointer/integer casts to work around this limitation will not work;
358+
they will fail horribly).
359+
Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365).
349360
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
350361
This can be used to find which parts of your program are executing slowly under Miri.
351362
The profile is written out to a file with the prefix `<name>`, and can be processed

build.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
// Re-export the TARGET environment variable so it can
3+
// be accessed by miri.
4+
let target = std::env::var("TARGET").unwrap();
5+
println!("cargo:rustc-env=TARGET={:?}", target);
6+
}

miri

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ esac
108108

109109
## Prepare the environment
110110
# Determine some toolchain properties
111+
# export the target so its available in miri
111112
TARGET=$(rustc +$TOOLCHAIN --version --verbose | grep "^host:" | cut -d ' ' -f 2)
112113
SYSROOT=$(rustc +$TOOLCHAIN --print sysroot)
113114
LIBDIR=$SYSROOT/lib/rustlib/$TARGET/lib

src/bin/miri.rs

+13
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,19 @@ fn main() {
530530
"full" => BacktraceStyle::Full,
531531
_ => show_error!("-Zmiri-backtrace may only be 0, 1, or full"),
532532
};
533+
} else if let Some(param) = arg.strip_prefix("-Zmiri-extern-so-file=") {
534+
let filename = param.to_string();
535+
if std::path::Path::new(&filename).exists() {
536+
if let Some(other_filename) = miri_config.external_so_file {
537+
panic!(
538+
"-Zmiri-extern-so-file external SO file is already set to {}",
539+
other_filename.display()
540+
);
541+
}
542+
miri_config.external_so_file = Some(filename.into());
543+
} else {
544+
panic!("-Zmiri-extern-so-file path {} does not exist", filename);
545+
}
533546
} else {
534547
// Forward to rustc.
535548
rustc_args.push(arg);

src/eval.rs

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::ffi::{OsStr, OsString};
44
use std::iter;
55
use std::panic::{self, AssertUnwindSafe};
6+
use std::path::PathBuf;
67
use std::thread;
78

89
use log::info;
@@ -128,6 +129,9 @@ pub struct MiriConfig {
128129
pub report_progress: Option<u32>,
129130
/// Whether Stacked Borrows retagging should recurse into fields of datatypes.
130131
pub retag_fields: bool,
132+
/// The location of a shared object file to load when calling external functions
133+
/// FIXME! consider allowing users to specify paths to multiple SO files, or to a directory
134+
pub external_so_file: Option<PathBuf>,
131135
}
132136

133137
impl Default for MiriConfig {
@@ -159,6 +163,7 @@ impl Default for MiriConfig {
159163
preemption_rate: 0.01, // 1%
160164
report_progress: None,
161165
retag_fields: false,
166+
external_so_file: None,
162167
}
163168
}
164169
}

src/machine.rs

+22
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,14 @@ pub struct Evaluator<'mir, 'tcx> {
358358
pub(crate) report_progress: Option<u32>,
359359
/// The number of blocks that passed since the last progress report.
360360
pub(crate) since_progress_report: u32,
361+
362+
/// Handle of the optional shared object file for external functions.
363+
pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>,
361364
}
362365

363366
impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
364367
pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self {
368+
let target_triple = &layout_cx.tcx.sess.opts.target_triple.to_string();
365369
let local_crates = helpers::get_local_crates(layout_cx.tcx);
366370
let layouts =
367371
PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
@@ -412,6 +416,24 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
412416
preemption_rate: config.preemption_rate,
413417
report_progress: config.report_progress,
414418
since_progress_report: 0,
419+
external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| {
420+
// Check if host target == the session target.
421+
if option_env!("TARGET") == Some(target_triple) {
422+
panic!(
423+
"calling external C functions in linked .so file requires target and host to be the same"
424+
);
425+
}
426+
// Note: it is the user's responsibility to provide a correct SO file.
427+
// WATCH OUT: If an invalid/incorrect SO file is specified, this can cause
428+
// undefined behaviour in Miri itself!
429+
(
430+
unsafe {
431+
libloading::Library::new(lib_file_path)
432+
.expect("Failed to read specified shared object file")
433+
},
434+
lib_file_path.clone(),
435+
)
436+
}),
415437
}
416438
}
417439

0 commit comments

Comments
 (0)