Skip to content

Commit 22b699a

Browse files
author
Ellen Arteca
committed
PR edits (as requested by @RalfJung), and update with upstream master
1 parent fe5d1a9 commit 22b699a

File tree

7 files changed

+64
-54
lines changed

7 files changed

+64
-54
lines changed

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -285,14 +285,6 @@ environment variable. We first document the most relevant and most commonly used
285285
`TERM` environment variable is excluded by default to [speed up the test
286286
harness](https://github.com/rust-lang/miri/issues/1702). This has no effect unless
287287
`-Zmiri-disable-isolation` is also set.
288-
* `-Zmiri-extern-so-file=<path to a shared object file>` is an experimental flag for providing support
289-
for FFI calls.
290-
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause undefined behaviour in Miri itself!
291-
And of course, Miri cannot do any checks on the actions taken by the external code.
292-
This is **work in progress**; currently, only integer arguments and return values are
293-
supported (and no, pointer/integer casts to work around this limitation will not work;
294-
they will fail horribly).
295-
Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365).
296288
* `-Zmiri-env-forward=<var>` forwards the `var` environment variable to the interpreted program. Can
297289
be used multiple times to forward several variables. This takes precedence over
298290
`-Zmiri-env-exclude`: if a variable is both forwarded and exluced, it *will* get forwarded. This
@@ -355,6 +347,17 @@ to Miri failing to detect cases of undefined behavior in a program.
355347
this flag is **unsound**.
356348
* `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
357349
memory effects.
350+
* `-Zmiri-extern-so-file=<path to a shared object file>` is an experimental flag for providing support
351+
for FFI calls. Functions not provided by that file are still executed via the usual Miri shims.
352+
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause undefined behaviour in Miri itself!
353+
And of course, Miri cannot do any checks on the actions taken by the external code.
354+
Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions
355+
working on file descriptors, you will have to replace *all* of them, or the two kinds of
356+
file descriptors will be mixed up.
357+
This is **work in progress**; currently, only integer arguments and return values are
358+
supported (and no, pointer/integer casts to work around this limitation will not work;
359+
they will fail horribly).
360+
Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365).
358361
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
359362
This can be used to find which parts of your program are executing slowly under Miri.
360363
The profile is written out to a file with the prefix `<name>`, and can be processed

src/machine.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ pub struct Evaluator<'mir, 'tcx> {
357357
/// The number of blocks that passed since the last progress report.
358358
pub(crate) since_progress_report: u32,
359359

360-
/// Handle of the optional C shared object file
360+
/// Handle of the optional shared object file for external functions.
361361
pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>,
362362
}
363363

src/shims/ffi_support.rs

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use libffi::{high::call::*, low::CodePtr};
1+
use libffi::{high::call as ffi, low::CodePtr};
22
use std::ops::Deref;
33

44
use rustc_middle::ty::{IntTy, Ty, TyKind, UintTy};
@@ -34,6 +34,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
3434
return Ok(CArg::Int64(k.to_i64()?));
3535
}
3636
TyKind::Int(IntTy::Isize) => {
37+
// This will fail if host != target, but then the entire FFI thing probably won't work well
38+
// in that situation.
3739
return Ok(CArg::ISize(k.to_machine_isize(cx)?.try_into().unwrap()));
3840
}
3941
// the uints
@@ -50,6 +52,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
5052
return Ok(CArg::UInt64(k.to_u64()?));
5153
}
5254
TyKind::Uint(UintTy::Usize) => {
55+
// This will fail if host != target, but then the entire FFI thing probably won't work well
56+
// in that situation.
5357
return Ok(CArg::USize(k.to_machine_usize(cx)?.try_into().unwrap()));
5458
}
5559
_ => {}
@@ -82,55 +86,55 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8286
match dest.layout.ty.kind() {
8387
// ints
8488
TyKind::Int(IntTy::I8) => {
85-
let x = call::<i8>(ptr, libffi_args.as_slice());
89+
let x = ffi::call::<i8>(ptr, libffi_args.as_slice());
8690
this.write_int(x, dest)?;
8791
return Ok(());
8892
}
8993
TyKind::Int(IntTy::I16) => {
90-
let x = call::<i16>(ptr, libffi_args.as_slice());
94+
let x = ffi::call::<i16>(ptr, libffi_args.as_slice());
9195
this.write_int(x, dest)?;
9296
return Ok(());
9397
}
9498
TyKind::Int(IntTy::I32) => {
95-
let x = call::<i32>(ptr, libffi_args.as_slice());
99+
let x = ffi::call::<i32>(ptr, libffi_args.as_slice());
96100
this.write_int(x, dest)?;
97101
return Ok(());
98102
}
99103
TyKind::Int(IntTy::I64) => {
100-
let x = call::<i64>(ptr, libffi_args.as_slice());
104+
let x = ffi::call::<i64>(ptr, libffi_args.as_slice());
101105
this.write_int(x, dest)?;
102106
return Ok(());
103107
}
104108
TyKind::Int(IntTy::Isize) => {
105-
let x = call::<isize>(ptr, libffi_args.as_slice());
109+
let x = ffi::call::<isize>(ptr, libffi_args.as_slice());
106110
// `isize` doesn't `impl Into<i128>`, so convert manually.
107111
// Convert to `i64` since this covers both 32- and 64-bit machines.
108112
this.write_int(i64::try_from(x).unwrap(), dest)?;
109113
return Ok(());
110114
}
111115
// uints
112116
TyKind::Uint(UintTy::U8) => {
113-
let x = call::<u8>(ptr, libffi_args.as_slice());
117+
let x = ffi::call::<u8>(ptr, libffi_args.as_slice());
114118
this.write_int(x, dest)?;
115119
return Ok(());
116120
}
117121
TyKind::Uint(UintTy::U16) => {
118-
let x = call::<u16>(ptr, libffi_args.as_slice());
122+
let x = ffi::call::<u16>(ptr, libffi_args.as_slice());
119123
this.write_int(x, dest)?;
120124
return Ok(());
121125
}
122126
TyKind::Uint(UintTy::U32) => {
123-
let x = call::<u32>(ptr, libffi_args.as_slice());
127+
let x = ffi::call::<u32>(ptr, libffi_args.as_slice());
124128
this.write_int(x, dest)?;
125129
return Ok(());
126130
}
127131
TyKind::Uint(UintTy::U64) => {
128-
let x = call::<u64>(ptr, libffi_args.as_slice());
132+
let x = ffi::call::<u64>(ptr, libffi_args.as_slice());
129133
this.write_int(x, dest)?;
130134
return Ok(());
131135
}
132136
TyKind::Uint(UintTy::Usize) => {
133-
let x = call::<usize>(ptr, libffi_args.as_slice());
137+
let x = ffi::call::<usize>(ptr, libffi_args.as_slice());
134138
// `usize` doesn't `impl Into<i128>`, so convert manually.
135139
// Convert to `u64` since this covers both 32- and 64-bit machines.
136140
this.write_int(u64::try_from(x).unwrap(), dest)?;
@@ -140,7 +144,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
140144
// have the output_type `Tuple([])`.
141145
TyKind::Tuple(t_list) =>
142146
if t_list.len() == 0 {
143-
call::<()>(ptr, libffi_args.as_slice());
147+
ffi::call::<()>(ptr, libffi_args.as_slice());
144148
return Ok(());
145149
},
146150
_ => {}
@@ -200,7 +204,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
200204
/// a form compatible with C (through `libffi` call).
201205
/// Then, convert return from the C call into a corresponding form that
202206
/// can be stored in Miri internal memory.
203-
fn call_and_add_external_c_fct_to_context(
207+
fn call_external_c_fct(
204208
&mut self,
205209
link_name: Symbol,
206210
dest: &PlaceTy<'tcx, Provenance>,
@@ -270,16 +274,16 @@ impl<'a> CArg {
270274
/// Convert a `CArg` to a `libffi` argument type.
271275
pub fn arg_downcast(&'a self) -> libffi::high::Arg<'a> {
272276
match self {
273-
CArg::Int8(i) => arg(i),
274-
CArg::Int16(i) => arg(i),
275-
CArg::Int32(i) => arg(i),
276-
CArg::Int64(i) => arg(i),
277-
CArg::ISize(i) => arg(i),
278-
CArg::UInt8(i) => arg(i),
279-
CArg::UInt16(i) => arg(i),
280-
CArg::UInt32(i) => arg(i),
281-
CArg::UInt64(i) => arg(i),
282-
CArg::USize(i) => arg(i),
277+
CArg::Int8(i) => ffi::arg(i),
278+
CArg::Int16(i) => ffi::arg(i),
279+
CArg::Int32(i) => ffi::arg(i),
280+
CArg::Int64(i) => ffi::arg(i),
281+
CArg::ISize(i) => ffi::arg(i),
282+
CArg::UInt8(i) => ffi::arg(i),
283+
CArg::UInt16(i) => ffi::arg(i),
284+
CArg::UInt32(i) => ffi::arg(i),
285+
CArg::UInt64(i) => ffi::arg(i),
286+
CArg::USize(i) => ffi::arg(i),
283287
}
284288
}
285289
}

src/shims/foreign_items.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
373373
// An Ok(false) here means that the function being called was not exported
374374
// by the specified SO file; we should continue and check if it corresponds to
375375
// a provided shim.
376-
if this.call_and_add_external_c_fct_to_context(link_name, dest, args)? {
376+
if this.call_external_c_fct(link_name, dest, args)? {
377377
return Ok(EmulateByNameResult::NeedsJumping);
378378
}
379379
}

tests/compiletest.rs

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,28 @@ fn miri_path() -> PathBuf {
88
PathBuf::from(option_env!("MIRI").unwrap_or(env!("CARGO_BIN_EXE_miri")))
99
}
1010

11+
// Build the shared object file for testing external C function calls.
12+
fn build_so_for_c_ffi_tests() {
13+
let cc = option_env!("CC").unwrap_or("cc");
14+
let cc_output = Command::new(cc)
15+
.args([
16+
"-shared",
17+
"-o",
18+
"tests/extern-so/libtestlib.so",
19+
"tests/extern-so/test.c",
20+
// Only add the functions specified in libcode.version to the shared object file.
21+
// This is to avoid automatically adding `malloc`, etc.
22+
// Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/
23+
"-fPIC",
24+
"-Wl,--version-script=tests/extern-so/libcode.version",
25+
])
26+
.output()
27+
.expect("failed to generate shared object file for testing external C function calls");
28+
if !cc_output.status.success() {
29+
panic!("error in generating shared object file for testing external C function calls");
30+
}
31+
}
32+
1133
fn run_tests(mode: Mode, path: &str, target: Option<String>) -> Result<()> {
1234
let in_rustc_test_suite = option_env!("RUSTC_STAGE").is_some();
1335

@@ -37,24 +59,7 @@ fn run_tests(mode: Mode, path: &str, target: Option<String>) -> Result<()> {
3759

3860
// If we're on linux, then build the shared object file for testing external C function calls.
3961
if cfg!(target_os = "linux") {
40-
let cc = option_env!("CC").unwrap_or("cc");
41-
let cc_output = Command::new(cc)
42-
.args([
43-
"-shared",
44-
"-o",
45-
"tests/extern-so/libtestlib.so",
46-
"tests/extern-so/test.c",
47-
// Only add the functions specified in libcode.version to the shared object file.
48-
// This is to avoid automatically adding `malloc`, etc.
49-
// Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/
50-
"-fPIC",
51-
"-Wl,--version-script=tests/extern-so/libcode.version",
52-
])
53-
.output()
54-
.expect("failed to generate shared object file for testing external C function calls");
55-
if !cc_output.status.success() {
56-
panic!("error in generating shared object file for testing external C function calls");
57-
}
62+
build_so_for_c_ffi_tests();
5863
}
5964

6065
let skip_ui_checks = env::var_os("MIRI_SKIP_UI_CHECKS").is_some();

tests/pass/extern-so/call_extern_c_fcts.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ fn main() {
4141
// test function that returns -10 as an unsigned int
4242
assert_eq!(get_unsigned_int(), (-10i32) as u32);
4343

44-
// test void function that prints from C -- call it twice
45-
printer();
44+
// test void function that prints from C
4645
printer();
4746
}
4847
}
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
printing from C
2-
printing from C

0 commit comments

Comments
 (0)