make native-lib support compile-time-optional, and centralize cfg usage

This commit is contained in:
Ralf Jung 2025-07-18 09:40:58 +02:00
parent 83395909d0
commit 24ea3e22d3
9 changed files with 52 additions and 37 deletions

View file

@ -38,14 +38,14 @@ features = ['unprefixed_malloc_on_supported_platforms']
[target.'cfg(unix)'.dependencies]
libc = "0.2"
libffi = "4.0.0"
libloading = "0.8"
libffi = { version = "4.0.0", optional = true }
libloading = { version = "0.8", optional = true }
nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"], optional = true }
[target.'cfg(target_os = "linux")'.dependencies]
nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"] }
ipc-channel = "0.19.0"
serde = { version = "1.0.219", features = ["derive"] }
capstone = "0.13"
ipc-channel = { version = "0.19.0", optional = true }
serde = { version = "1.0.219", features = ["derive"], optional = true }
capstone = { version = "0.13", optional = true }
[dev-dependencies]
ui_test = "0.29.1"
@ -64,11 +64,12 @@ name = "ui"
harness = false
[features]
default = ["stack-cache"]
default = ["stack-cache", "native-lib"]
genmc = []
stack-cache = []
stack-cache-consistency-check = ["stack-cache"]
tracing = ["serde_json"]
native-lib = ["dep:libffi", "dep:libloading", "dep:capstone", "dep:ipc-channel", "dep:nix", "dep:serde"]
[lints.rust.unexpected_cfgs]
level = "warn"

View file

@ -1,20 +1,18 @@
use std::alloc::Layout;
use std::borrow::Cow;
use std::cell::RefCell;
use std::rc::Rc;
use std::{alloc, slice};
#[cfg(target_os = "linux")]
use std::{cell::RefCell, rc::Rc};
use rustc_abi::{Align, Size};
use rustc_middle::mir::interpret::AllocBytes;
#[cfg(target_os = "linux")]
use crate::alloc::isolated_alloc::IsolatedAlloc;
use crate::helpers::ToU64 as _;
#[derive(Clone, Debug)]
pub enum MiriAllocParams {
Global,
#[cfg(target_os = "linux")]
Isolated(Rc<RefCell<IsolatedAlloc>>),
}
@ -56,7 +54,6 @@ impl Drop for MiriAllocBytes {
unsafe {
match self.params.clone() {
MiriAllocParams::Global => alloc::dealloc(self.ptr, alloc_layout),
#[cfg(target_os = "linux")]
MiriAllocParams::Isolated(alloc) =>
alloc.borrow_mut().dealloc(self.ptr, alloc_layout),
}
@ -123,7 +120,6 @@ impl AllocBytes for MiriAllocBytes {
let alloc_fn = |layout, params: &MiriAllocParams| unsafe {
match params {
MiriAllocParams::Global => alloc::alloc(layout),
#[cfg(target_os = "linux")]
MiriAllocParams::Isolated(alloc) => alloc.borrow_mut().alloc(layout),
}
};
@ -144,7 +140,6 @@ impl AllocBytes for MiriAllocBytes {
let alloc_fn = |layout, params: &MiriAllocParams| unsafe {
match params {
MiriAllocParams::Global => alloc::alloc_zeroed(layout),
#[cfg(target_os = "linux")]
MiriAllocParams::Isolated(alloc) => alloc.borrow_mut().alloc_zeroed(layout),
}
};

View file

@ -1,5 +1,31 @@
mod alloc_bytes;
#[cfg(target_os = "linux")]
#[cfg(all(unix, feature = "native-lib"))]
pub mod isolated_alloc;
#[cfg(not(all(unix, feature = "native-lib")))]
pub mod isolated_alloc {
use std::alloc::Layout;
/// Stub allocator to avoid `cfg`s in the rest of Miri.
#[derive(Debug)]
pub struct IsolatedAlloc(!);
impl IsolatedAlloc {
pub fn new() -> Self {
unreachable!()
}
pub unsafe fn alloc(&mut self, _layout: Layout) -> *mut u8 {
match self.0 {}
}
pub unsafe fn alloc_zeroed(&mut self, _layout: Layout) -> *mut u8 {
match self.0 {}
}
pub unsafe fn dealloc(&mut self, _ptr: *mut u8, _layout: Layout) {
match self.0 {}
}
}
}
pub use self::alloc_bytes::{MiriAllocBytes, MiriAllocParams};

View file

@ -335,9 +335,10 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
fn exit(exit_code: i32) -> ! {
// Drop the tracing guard before exiting, so tracing calls are flushed correctly.
deinit_loggers();
// Make sure the supervisor knows about the code code.
#[cfg(target_os = "linux")]
// Make sure the supervisor knows about the exit code.
#[cfg(all(unix, feature = "native-lib"))]
miri::native_lib::register_retcode_sv(exit_code);
// Actually exit.
std::process::exit(exit_code);
}
@ -754,7 +755,7 @@ fn main() {
debug!("crate arguments: {:?}", miri_config.args);
if !miri_config.native_lib.is_empty() && miri_config.native_lib_enable_tracing {
// SAFETY: No other threads are running
#[cfg(target_os = "linux")]
#[cfg(all(unix, feature = "native-lib"))]
if unsafe { miri::native_lib::init_sv() }.is_err() {
eprintln!(
"warning: The native-lib tracer could not be started. Is this an x86 Linux system, and does Miri have permissions to ptrace?\n\

View file

@ -97,7 +97,7 @@ pub use rustc_const_eval::interpret::{self, AllocMap, Provenance as _};
use rustc_middle::{bug, span_bug};
use tracing::{info, trace};
#[cfg(target_os = "linux")]
#[cfg(all(unix, feature = "native-lib"))]
pub mod native_lib {
pub use crate::shims::{init_sv, register_retcode_sv};
}

View file

@ -530,7 +530,6 @@ pub struct MiriMachine<'tcx> {
pub(crate) rng: RefCell<StdRng>,
/// The allocator used for the machine's `AllocBytes` in native-libs mode.
#[cfg(target_os = "linux")]
pub(crate) allocator: Option<Rc<RefCell<crate::alloc::isolated_alloc::IsolatedAlloc>>>,
/// The allocation IDs to report when they are being allocated
@ -554,9 +553,9 @@ pub struct MiriMachine<'tcx> {
pub(crate) basic_block_count: u64,
/// Handle of the optional shared object file for native functions.
#[cfg(unix)]
#[cfg(all(unix, feature = "native-lib"))]
pub native_lib: Vec<(libloading::Library, std::path::PathBuf)>,
#[cfg(not(unix))]
#[cfg(not(all(unix, feature = "native-lib")))]
pub native_lib: Vec<!>,
/// Run a garbage collector for BorTags every N basic blocks.
@ -603,7 +602,7 @@ pub struct MiriMachine<'tcx> {
/// Remembers whether we already warned about an extern type with Stacked Borrows.
pub(crate) sb_extern_type_warned: Cell<bool>,
/// Remember whether we already warned about sharing memory with a native call.
#[cfg(unix)]
#[allow(unused)]
pub(crate) native_call_mem_warned: Cell<bool>,
/// Remembers which shims have already shown the warning about erroring in isolation.
pub(crate) reject_in_isolation_warned: RefCell<FxHashSet<String>>,
@ -718,7 +717,6 @@ impl<'tcx> MiriMachine<'tcx> {
local_crates,
extern_statics: FxHashMap::default(),
rng: RefCell::new(rng),
#[cfg(target_os = "linux")]
allocator: if !config.native_lib.is_empty() {
Some(Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new())))
} else { None },
@ -730,7 +728,7 @@ impl<'tcx> MiriMachine<'tcx> {
report_progress: config.report_progress,
basic_block_count: 0,
monotonic_clock: MonotonicClock::new(config.isolated_op == IsolatedOp::Allow),
#[cfg(unix)]
#[cfg(all(unix, feature = "native-lib"))]
native_lib: config.native_lib.iter().map(|lib_file_path| {
let host_triple = rustc_session::config::host_tuple();
let target_triple = tcx.sess.opts.target_triple.tuple();
@ -752,9 +750,9 @@ impl<'tcx> MiriMachine<'tcx> {
lib_file_path.clone(),
)
}).collect(),
#[cfg(not(unix))]
#[cfg(not(all(unix, feature = "native-lib")))]
native_lib: config.native_lib.iter().map(|_| {
panic!("calling functions from native libraries via FFI is only supported on Unix")
panic!("calling functions from native libraries via FFI is not supported in this build of Miri")
}).collect(),
gc_interval: config.gc_interval,
since_gc: 0,
@ -771,7 +769,6 @@ impl<'tcx> MiriMachine<'tcx> {
pthread_rwlock_sanity: Cell::new(false),
pthread_condvar_sanity: Cell::new(false),
sb_extern_type_warned: Cell::new(false),
#[cfg(unix)]
native_call_mem_warned: Cell::new(false),
reject_in_isolation_warned: Default::default(),
int2ptr_warned: Default::default(),
@ -924,7 +921,6 @@ impl VisitProvenance for MiriMachine<'_> {
backtrace_style: _,
local_crates: _,
rng: _,
#[cfg(target_os = "linux")]
allocator: _,
tracked_alloc_ids: _,
track_alloc_accesses: _,
@ -949,7 +945,6 @@ impl VisitProvenance for MiriMachine<'_> {
pthread_rwlock_sanity: _,
pthread_condvar_sanity: _,
sb_extern_type_warned: _,
#[cfg(unix)]
native_call_mem_warned: _,
reject_in_isolation_warned: _,
int2ptr_warned: _,
@ -1817,13 +1812,10 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams {
use crate::alloc::MiriAllocParams;
#[cfg(target_os = "linux")]
match &self.allocator {
Some(alloc) => MiriAllocParams::Isolated(alloc.clone()),
None => MiriAllocParams::Global,
}
#[cfg(not(target_os = "linux"))]
MiriAllocParams::Global
}
fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan {

View file

@ -237,7 +237,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
// First deal with any external C functions in linked .so file.
#[cfg(unix)]
#[cfg(all(unix, feature = "native-lib"))]
if !this.machine.native_lib.is_empty() {
use crate::shims::native_lib::EvalContextExt as _;
// An Ok(false) here means that the function being called was not exported

View file

@ -4,7 +4,7 @@ mod aarch64;
mod alloc;
mod backtrace;
mod files;
#[cfg(unix)]
#[cfg(all(unix, feature = "native-lib"))]
mod native_lib;
mod unix;
mod wasi;
@ -23,7 +23,7 @@ pub mod tls;
pub mod unwind;
pub use self::files::FdTable;
#[cfg(target_os = "linux")]
#[cfg(all(unix, feature = "native-lib"))]
pub use self::native_lib::trace::{init_sv, register_retcode_sv};
pub use self::unix::{DirTable, EpollInterestTable};

View file

@ -335,7 +335,7 @@ fn main() -> Result<()> {
ui(Mode::Panic, "tests/panic", &target, WithDependencies, tmpdir.path())?;
ui(Mode::Fail, "tests/fail", &target, WithoutDependencies, tmpdir.path())?;
ui(Mode::Fail, "tests/fail-dep", &target, WithDependencies, tmpdir.path())?;
if cfg!(unix) && target == host {
if cfg!(all(unix, feature = "native-lib")) && target == host {
ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?;
ui(Mode::Fail, "tests/native-lib/fail", &target, WithoutDependencies, tmpdir.path())?;
}