Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 25a3b66

Browse files
committedMay 10, 2024
rename 'extern-so' to 'native-lib'
1 parent d3f4d06 commit 25a3b66

File tree

16 files changed

+283
-333
lines changed

16 files changed

+283
-333
lines changed
 

‎src/tools/miri/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ tex/*/out
99
perf.data
1010
perf.data.old
1111
flamegraph.svg
12-
tests/extern-so/libtestlib.so
12+
tests/native-lib/libtestlib.so
1313
.auto-*

‎src/tools/miri/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -374,17 +374,17 @@ to Miri failing to detect cases of undefined behavior in a program.
374374
this flag is **unsound**.
375375
* `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak
376376
memory effects.
377-
* `-Zmiri-extern-so-file=<path to a shared object file>` is an experimental flag for providing support
378-
for FFI calls. Functions not provided by that file are still executed via the usual Miri shims.
379-
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause undefined behaviour in Miri itself!
380-
And of course, Miri cannot do any checks on the actions taken by the external code.
377+
* `-Zmiri-native-lib=<path to a shared object file>` is an experimental flag for providing support
378+
for calling native functions from inside the interpreter via FFI. Functions not provided by that
379+
file are still executed via the usual Miri shims.
380+
**WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in Miri itself!
381+
And of course, Miri cannot do any checks on the actions taken by the native code.
381382
Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions
382383
working on file descriptors, you will have to replace *all* of them, or the two kinds of
383384
file descriptors will be mixed up.
384385
This is **work in progress**; currently, only integer arguments and return values are
385386
supported (and no, pointer/integer casts to work around this limitation will not work;
386-
they will fail horribly). It also only works on unix hosts for now.
387-
Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365).
387+
they will fail horribly). It also only works on Linux hosts for now.
388388
* `-Zmiri-measureme=<name>` enables `measureme` profiling for the interpreted program.
389389
This can be used to find which parts of your program are executing slowly under Miri.
390390
The profile is written out to a file inside a directory called `<name>`, and can be processed

‎src/tools/miri/src/bin/miri.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -575,18 +575,15 @@ fn main() {
575575
"full" => BacktraceStyle::Full,
576576
_ => show_error!("-Zmiri-backtrace may only be 0, 1, or full"),
577577
};
578-
} else if let Some(param) = arg.strip_prefix("-Zmiri-extern-so-file=") {
578+
} else if let Some(param) = arg.strip_prefix("-Zmiri-native-lib=") {
579579
let filename = param.to_string();
580580
if std::path::Path::new(&filename).exists() {
581-
if let Some(other_filename) = miri_config.external_so_file {
582-
show_error!(
583-
"-Zmiri-extern-so-file is already set to {}",
584-
other_filename.display()
585-
);
581+
if let Some(other_filename) = miri_config.native_lib {
582+
show_error!("-Zmiri-native-lib is already set to {}", other_filename.display());
586583
}
587-
miri_config.external_so_file = Some(filename.into());
584+
miri_config.native_lib = Some(filename.into());
588585
} else {
589-
show_error!("-Zmiri-extern-so-file `{}` does not exist", filename);
586+
show_error!("-Zmiri-native-lib `{}` does not exist", filename);
590587
}
591588
} else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") {
592589
let num_cpus = param

‎src/tools/miri/src/eval.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ pub struct MiriConfig {
142142
/// Whether Stacked Borrows and Tree Borrows retagging should recurse into fields of datatypes.
143143
pub retag_fields: RetagFields,
144144
/// The location of a shared object file to load when calling external functions
145-
/// FIXME! consider allowing users to specify paths to multiple SO files, or to a directory
146-
pub external_so_file: Option<PathBuf>,
145+
/// FIXME! consider allowing users to specify paths to multiple files, or to a directory
146+
pub native_lib: Option<PathBuf>,
147147
/// Run a garbage collector for BorTags every N basic blocks.
148148
pub gc_interval: u32,
149149
/// The number of CPUs to be reported by miri.
@@ -188,7 +188,7 @@ impl Default for MiriConfig {
188188
preemption_rate: 0.01, // 1%
189189
report_progress: None,
190190
retag_fields: RetagFields::Yes,
191-
external_so_file: None,
191+
native_lib: None,
192192
gc_interval: 10_000,
193193
num_cpus: 1,
194194
page_size: None,

‎src/tools/miri/src/machine.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -535,11 +535,11 @@ pub struct MiriMachine<'mir, 'tcx> {
535535
// The total number of blocks that have been executed.
536536
pub(crate) basic_block_count: u64,
537537

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

544544
/// Run a garbage collector for BorTags every N basic blocks.
545545
pub(crate) gc_interval: u32,
@@ -665,7 +665,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
665665
basic_block_count: 0,
666666
clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
667667
#[cfg(target_os = "linux")]
668-
external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| {
668+
native_lib: config.native_lib.as_ref().map(|lib_file_path| {
669669
let target_triple = layout_cx.tcx.sess.opts.target_triple.triple();
670670
// Check if host target == the session target.
671671
if env!("TARGET") != target_triple {
@@ -687,7 +687,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
687687
)
688688
}),
689689
#[cfg(not(target_os = "linux"))]
690-
external_so_lib: config.external_so_file.as_ref().map(|_| {
690+
native_lib: config.native_lib.as_ref().map(|_| {
691691
panic!("loading external .so files is only supported on Linux")
692692
}),
693693
gc_interval: config.gc_interval,
@@ -802,7 +802,7 @@ impl VisitProvenance for MiriMachine<'_, '_> {
802802
preemption_rate: _,
803803
report_progress: _,
804804
basic_block_count: _,
805-
external_so_lib: _,
805+
native_lib: _,
806806
gc_interval: _,
807807
since_gc: _,
808808
num_cpus: _,

‎src/tools/miri/src/shims/ffi_support.rs

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

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,12 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
211211

212212
// First deal with any external C functions in linked .so file.
213213
#[cfg(target_os = "linux")]
214-
if this.machine.external_so_lib.as_ref().is_some() {
215-
use crate::shims::ffi_support::EvalContextExt as _;
214+
if this.machine.native_lib.as_ref().is_some() {
215+
use crate::shims::native_lib::EvalContextExt as _;
216216
// An Ok(false) here means that the function being called was not exported
217217
// by the specified `.so` file; we should continue and check if it corresponds to
218218
// a provided shim.
219-
if this.call_external_c_fct(link_name, dest, args)? {
219+
if this.call_native_fn(link_name, dest, args)? {
220220
return Ok(EmulateItemResult::NeedsJumping);
221221
}
222222
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
mod alloc;
44
mod backtrace;
5-
#[cfg(target_os = "linux")]
6-
pub mod ffi_support;
75
pub mod foreign_items;
6+
#[cfg(target_os = "linux")]
7+
pub mod native_lib;
88
pub mod unix;
99
pub mod windows;
1010
mod x86;
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
//! Implements calling functions from a native library.
2+
use libffi::{high::call as ffi, low::CodePtr};
3+
use std::ops::Deref;
4+
5+
use rustc_middle::ty::{self as ty, IntTy, UintTy};
6+
use rustc_span::Symbol;
7+
use rustc_target::abi::{Abi, HasDataLayout};
8+
9+
use crate::*;
10+
11+
impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
12+
trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
13+
/// Call native host function and return the output as an immediate.
14+
fn call_native_with_args<'a>(
15+
&mut self,
16+
link_name: Symbol,
17+
dest: &MPlaceTy<'tcx, Provenance>,
18+
ptr: CodePtr,
19+
libffi_args: Vec<libffi::high::Arg<'a>>,
20+
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
21+
let this = self.eval_context_mut();
22+
23+
// Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
24+
// as the specified primitive integer type
25+
let scalar = match dest.layout.ty.kind() {
26+
// ints
27+
ty::Int(IntTy::I8) => {
28+
// Unsafe because of the call to native code.
29+
// Because this is calling a C function it is not necessarily sound,
30+
// but there is no way around this and we've checked as much as we can.
31+
let x = unsafe { ffi::call::<i8>(ptr, libffi_args.as_slice()) };
32+
Scalar::from_i8(x)
33+
}
34+
ty::Int(IntTy::I16) => {
35+
let x = unsafe { ffi::call::<i16>(ptr, libffi_args.as_slice()) };
36+
Scalar::from_i16(x)
37+
}
38+
ty::Int(IntTy::I32) => {
39+
let x = unsafe { ffi::call::<i32>(ptr, libffi_args.as_slice()) };
40+
Scalar::from_i32(x)
41+
}
42+
ty::Int(IntTy::I64) => {
43+
let x = unsafe { ffi::call::<i64>(ptr, libffi_args.as_slice()) };
44+
Scalar::from_i64(x)
45+
}
46+
ty::Int(IntTy::Isize) => {
47+
let x = unsafe { ffi::call::<isize>(ptr, libffi_args.as_slice()) };
48+
Scalar::from_target_isize(x.try_into().unwrap(), this)
49+
}
50+
// uints
51+
ty::Uint(UintTy::U8) => {
52+
let x = unsafe { ffi::call::<u8>(ptr, libffi_args.as_slice()) };
53+
Scalar::from_u8(x)
54+
}
55+
ty::Uint(UintTy::U16) => {
56+
let x = unsafe { ffi::call::<u16>(ptr, libffi_args.as_slice()) };
57+
Scalar::from_u16(x)
58+
}
59+
ty::Uint(UintTy::U32) => {
60+
let x = unsafe { ffi::call::<u32>(ptr, libffi_args.as_slice()) };
61+
Scalar::from_u32(x)
62+
}
63+
ty::Uint(UintTy::U64) => {
64+
let x = unsafe { ffi::call::<u64>(ptr, libffi_args.as_slice()) };
65+
Scalar::from_u64(x)
66+
}
67+
ty::Uint(UintTy::Usize) => {
68+
let x = unsafe { ffi::call::<usize>(ptr, libffi_args.as_slice()) };
69+
Scalar::from_target_usize(x.try_into().unwrap(), this)
70+
}
71+
// Functions with no declared return type (i.e., the default return)
72+
// have the output_type `Tuple([])`.
73+
ty::Tuple(t_list) if t_list.len() == 0 => {
74+
unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) };
75+
return Ok(ImmTy::uninit(dest.layout));
76+
}
77+
_ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name),
78+
};
79+
Ok(ImmTy::from_scalar(scalar, dest.layout))
80+
}
81+
82+
/// Get the pointer to the function of the specified name in the shared object file,
83+
/// if it exists. The function must be in the shared object file specified: we do *not*
84+
/// return pointers to functions in dependencies of the library.
85+
fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option<CodePtr> {
86+
let this = self.eval_context_mut();
87+
// Try getting the function from the shared library.
88+
// On windows `_lib_path` will be unused, hence the name starting with `_`.
89+
let (lib, _lib_path) = this.machine.native_lib.as_ref().unwrap();
90+
let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe {
91+
match lib.get(link_name.as_str().as_bytes()) {
92+
Ok(x) => x,
93+
Err(_) => {
94+
return None;
95+
}
96+
}
97+
};
98+
99+
// FIXME: this is a hack!
100+
// The `libloading` crate will automatically load system libraries like `libc`.
101+
// On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202
102+
// and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the
103+
// library if it can't find the symbol in the library itself.
104+
// So, in order to check if the function was actually found in the specified
105+
// `machine.external_so_lib` we need to check its `dli_fname` and compare it to
106+
// the specified SO file path.
107+
// This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`,
108+
// from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411
109+
// using the `libc` crate where this interface is public.
110+
let mut info = std::mem::MaybeUninit::<libc::Dl_info>::uninit();
111+
unsafe {
112+
if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 {
113+
if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap()
114+
!= _lib_path.to_str().unwrap()
115+
{
116+
return None;
117+
}
118+
}
119+
}
120+
// Return a pointer to the function.
121+
Some(CodePtr(*func.deref() as *mut _))
122+
}
123+
}
124+
125+
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
126+
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
127+
/// Call the native host function, with supplied arguments.
128+
/// Needs to convert all the arguments from their Miri representations to
129+
/// a native form (through `libffi` call).
130+
/// Then, convert the return value from the native form into something that
131+
/// can be stored in Miri's internal memory.
132+
fn call_native_fn(
133+
&mut self,
134+
link_name: Symbol,
135+
dest: &MPlaceTy<'tcx, Provenance>,
136+
args: &[OpTy<'tcx, Provenance>],
137+
) -> InterpResult<'tcx, bool> {
138+
let this = self.eval_context_mut();
139+
// Get the pointer to the function in the shared object file if it exists.
140+
let code_ptr = match this.get_func_ptr_explicitly_from_lib(link_name) {
141+
Some(ptr) => ptr,
142+
None => {
143+
// Shared object file does not export this function -- try the shims next.
144+
return Ok(false);
145+
}
146+
};
147+
148+
// Get the function arguments, and convert them to `libffi`-compatible form.
149+
let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
150+
for arg in args.iter() {
151+
if !matches!(arg.layout.abi, Abi::Scalar(_)) {
152+
throw_unsup_format!("only scalar argument types are support for native calls")
153+
}
154+
libffi_args.push(imm_to_carg(this.read_immediate(arg)?, this)?);
155+
}
156+
157+
// Convert them to `libffi::high::Arg` type.
158+
let libffi_args = libffi_args
159+
.iter()
160+
.map(|arg| arg.arg_downcast())
161+
.collect::<Vec<libffi::high::Arg<'_>>>();
162+
163+
// Call the function and store output, depending on return type in the function signature.
164+
let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
165+
this.write_immediate(*ret, dest)?;
166+
Ok(true)
167+
}
168+
}
169+
170+
#[derive(Debug, Clone)]
171+
/// Enum of supported arguments to external C functions.
172+
// We introduce this enum instead of just calling `ffi::arg` and storing a list
173+
// of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference
174+
// to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html
175+
// and we need to store a copy of the value, and pass a reference to this copy to C instead.
176+
enum CArg {
177+
/// 8-bit signed integer.
178+
Int8(i8),
179+
/// 16-bit signed integer.
180+
Int16(i16),
181+
/// 32-bit signed integer.
182+
Int32(i32),
183+
/// 64-bit signed integer.
184+
Int64(i64),
185+
/// isize.
186+
ISize(isize),
187+
/// 8-bit unsigned integer.
188+
UInt8(u8),
189+
/// 16-bit unsigned integer.
190+
UInt16(u16),
191+
/// 32-bit unsigned integer.
192+
UInt32(u32),
193+
/// 64-bit unsigned integer.
194+
UInt64(u64),
195+
/// usize.
196+
USize(usize),
197+
}
198+
199+
impl<'a> CArg {
200+
/// Convert a `CArg` to a `libffi` argument type.
201+
fn arg_downcast(&'a self) -> libffi::high::Arg<'a> {
202+
match self {
203+
CArg::Int8(i) => ffi::arg(i),
204+
CArg::Int16(i) => ffi::arg(i),
205+
CArg::Int32(i) => ffi::arg(i),
206+
CArg::Int64(i) => ffi::arg(i),
207+
CArg::ISize(i) => ffi::arg(i),
208+
CArg::UInt8(i) => ffi::arg(i),
209+
CArg::UInt16(i) => ffi::arg(i),
210+
CArg::UInt32(i) => ffi::arg(i),
211+
CArg::UInt64(i) => ffi::arg(i),
212+
CArg::USize(i) => ffi::arg(i),
213+
}
214+
}
215+
}
216+
217+
/// Extract the scalar value from the result of reading a scalar from the machine,
218+
/// and convert it to a `CArg`.
219+
fn imm_to_carg<'tcx>(
220+
v: ImmTy<'tcx, Provenance>,
221+
cx: &impl HasDataLayout,
222+
) -> InterpResult<'tcx, CArg> {
223+
Ok(match v.layout.ty.kind() {
224+
// If the primitive provided can be converted to a type matching the type pattern
225+
// then create a `CArg` of this primitive value with the corresponding `CArg` constructor.
226+
// the ints
227+
ty::Int(IntTy::I8) => CArg::Int8(v.to_scalar().to_i8()?),
228+
ty::Int(IntTy::I16) => CArg::Int16(v.to_scalar().to_i16()?),
229+
ty::Int(IntTy::I32) => CArg::Int32(v.to_scalar().to_i32()?),
230+
ty::Int(IntTy::I64) => CArg::Int64(v.to_scalar().to_i64()?),
231+
ty::Int(IntTy::Isize) =>
232+
CArg::ISize(v.to_scalar().to_target_isize(cx)?.try_into().unwrap()),
233+
// the uints
234+
ty::Uint(UintTy::U8) => CArg::UInt8(v.to_scalar().to_u8()?),
235+
ty::Uint(UintTy::U16) => CArg::UInt16(v.to_scalar().to_u16()?),
236+
ty::Uint(UintTy::U32) => CArg::UInt32(v.to_scalar().to_u32()?),
237+
ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?),
238+
ty::Uint(UintTy::Usize) =>
239+
CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()),
240+
_ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
241+
})
242+
}

‎src/tools/miri/tests/ui.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ pub fn flagsplit(flags: &str) -> Vec<String> {
2727
flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
2828
}
2929

30-
// Build the shared object file for testing external C function calls.
31-
fn build_so_for_c_ffi_tests() -> PathBuf {
30+
// Build the shared object file for testing native function calls.
31+
fn build_native_lib() -> PathBuf {
3232
let cc = option_env!("CC").unwrap_or("cc");
3333
// Target directory that we can write to.
34-
let so_target_dir = Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-extern-so");
34+
let so_target_dir =
35+
Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-native-lib");
3536
// Create the directory if it does not already exist.
3637
std::fs::create_dir_all(&so_target_dir)
3738
.expect("Failed to create directory for shared object file");
@@ -41,18 +42,18 @@ fn build_so_for_c_ffi_tests() -> PathBuf {
4142
"-shared",
4243
"-o",
4344
so_file_path.to_str().unwrap(),
44-
"tests/extern-so/test.c",
45+
"tests/native-lib/test.c",
4546
// Only add the functions specified in libcode.version to the shared object file.
4647
// This is to avoid automatically adding `malloc`, etc.
4748
// Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/
4849
"-fPIC",
49-
"-Wl,--version-script=tests/extern-so/libtest.map",
50+
"-Wl,--version-script=tests/native-lib/libtest.map",
5051
])
5152
.output()
52-
.expect("failed to generate shared object file for testing external C function calls");
53+
.expect("failed to generate shared object file for testing native function calls");
5354
if !cc_output.status.success() {
5455
panic!(
55-
"error in generating shared object file for testing external C function calls:\n{}",
56+
"error generating shared object file for testing native function calls:\n{}",
5657
String::from_utf8_lossy(&cc_output.stderr),
5758
);
5859
}
@@ -132,13 +133,12 @@ fn run_tests(
132133
config.program.args.push("--target".into());
133134
config.program.args.push(target.into());
134135

135-
// If we're testing the extern-so functionality, then build the shared object file for testing
136+
// If we're testing the native-lib functionality, then build the shared object file for testing
136137
// external C function calls and push the relevant compiler flag.
137-
if path.starts_with("tests/extern-so/") {
138-
assert!(cfg!(target_os = "linux"));
139-
let so_file_path = build_so_for_c_ffi_tests();
140-
let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file=");
141-
flag.push(so_file_path.into_os_string());
138+
if path.starts_with("tests/native-lib/") {
139+
let native_lib = build_native_lib();
140+
let mut flag = std::ffi::OsString::from("-Zmiri-native-lib=");
141+
flag.push(native_lib.into_os_string());
142142
config.program.args.push(flag);
143143
}
144144

@@ -292,10 +292,10 @@ fn main() -> Result<()> {
292292
tmpdir.path(),
293293
)?;
294294
if cfg!(target_os = "linux") {
295-
ui(Mode::Pass, "tests/extern-so/pass", &target, WithoutDependencies, tmpdir.path())?;
295+
ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?;
296296
ui(
297297
Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled },
298-
"tests/extern-so/fail",
298+
"tests/native-lib/fail",
299299
&target,
300300
WithoutDependencies,
301301
tmpdir.path(),

0 commit comments

Comments
 (0)
Please sign in to comment.