Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for external C functions that have integer (or empty) args and/or returns #2363

Merged
merged 1 commit into from
Aug 26, 2022
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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@ target
/doc
tex/*/out
*.dot
*.out
*.rs.bk
.vscode
*.mm_profdata
perf.data
perf.data.old
flamegraph.svg
<<<<<<< HEAD
=======
<<<<<<< HEAD
tests/extern-so/libtestlib.so
=======
>>>>>>> master
>>>>>>> 58ba05a0 (C FFI support for functions with int args and returns)
Comment on lines +12 to +18
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a merge conflict issue?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, I created a fix PR for this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh no -- good catch and thanks for fixing it!

.auto-*
38 changes: 38 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ doctest = false # and no doc tests
[dependencies]
getrandom = { version = "0.2", features = ["std"] }
env_logger = "0.9"
libffi = "3.0.0"
libloading = "0.7"
log = "0.4"
shell-escape = "0.1.4"
rand = "0.8"
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,17 @@ to Miri failing to detect cases of undefined behavior in a program.
this flag is **unsound**.
* `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
memory effects.
* `-Zmiri-extern-so-file=<path to a shared object file>` is an experimental flag for providing support
for FFI calls. Functions not provided by that file are still executed via the usual Miri shims.
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause undefined behaviour in Miri itself!
And of course, Miri cannot do any checks on the actions taken by the external code.
Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions
working on file descriptors, you will have to replace *all* of them, or the two kinds of
file descriptors will be mixed up.
This is **work in progress**; currently, only integer arguments and return values are
supported (and no, pointer/integer casts to work around this limitation will not work;
they will fail horribly).
Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365).
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
This can be used to find which parts of your program are executing slowly under Miri.
The profile is written out to a file with the prefix `<name>`, and can be processed
Expand Down
6 changes: 6 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn main() {
// Re-export the TARGET environment variable so it can
// be accessed by miri.
let target = std::env::var("TARGET").unwrap();
println!("cargo:rustc-env=TARGET={:?}", target);
}
1 change: 1 addition & 0 deletions miri
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ esac

## Prepare the environment
# Determine some toolchain properties
# export the target so its available in miri
TARGET=$(rustc +$TOOLCHAIN --version --verbose | grep "^host:" | cut -d ' ' -f 2)
SYSROOT=$(rustc +$TOOLCHAIN --print sysroot)
LIBDIR=$SYSROOT/lib/rustlib/$TARGET/lib
Expand Down
13 changes: 13 additions & 0 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,19 @@ fn main() {
"full" => BacktraceStyle::Full,
_ => show_error!("-Zmiri-backtrace may only be 0, 1, or full"),
};
} else if let Some(param) = arg.strip_prefix("-Zmiri-extern-so-file=") {
let filename = param.to_string();
if std::path::Path::new(&filename).exists() {
if let Some(other_filename) = miri_config.external_so_file {
panic!(
"-Zmiri-extern-so-file external SO file is already set to {}",
other_filename.display()
);
}
miri_config.external_so_file = Some(filename.into());
} else {
panic!("-Zmiri-extern-so-file path {} does not exist", filename);
}
} else {
// Forward to rustc.
rustc_args.push(arg);
Expand Down
5 changes: 5 additions & 0 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::ffi::{OsStr, OsString};
use std::iter;
use std::panic::{self, AssertUnwindSafe};
use std::path::PathBuf;
use std::thread;

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

impl Default for MiriConfig {
Expand Down Expand Up @@ -159,6 +163,7 @@ impl Default for MiriConfig {
preemption_rate: 0.01, // 1%
report_progress: None,
retag_fields: false,
external_so_file: None,
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,14 @@ pub struct Evaluator<'mir, 'tcx> {
pub(crate) report_progress: Option<u32>,
/// The number of blocks that passed since the last progress report.
pub(crate) since_progress_report: u32,

/// Handle of the optional shared object file for external functions.
pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>,
}

impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self {
let target_triple = &layout_cx.tcx.sess.opts.target_triple.to_string();
let local_crates = helpers::get_local_crates(layout_cx.tcx);
let layouts =
PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
Expand Down Expand Up @@ -412,6 +416,24 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
preemption_rate: config.preemption_rate,
report_progress: config.report_progress,
since_progress_report: 0,
external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the right spot to error if host != target.

// Check if host target == the session target.
if option_env!("TARGET") == Some(target_triple) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we panic if they are equal? How does that make sense?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hiding a bug, the comparison doesn't actually work right now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by #2511

panic!(
"calling external C functions in linked .so file requires target and host to be the same"
);
}
// Note: it is the user's responsibility to provide a correct SO file.
// WATCH OUT: If an invalid/incorrect SO file is specified, this can cause
// undefined behaviour in Miri itself!
(
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
unsafe {
libloading::Library::new(lib_file_path)
.expect("Failed to read specified shared object file")
},
lib_file_path.clone(),
)
}),
}
}

Expand Down
Loading