diff --git a/README.md b/README.md index b214ab6d09..f0e14a9125 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,8 @@ environment variable: `compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail). You can change it to any value between `0.0` and `1.0`, where `1.0` means it will always fail and `0.0` means it will never fail. +* `-Zmiri-disable-abi-check` disables checking [function ABI]. Using this flag + is **unsound**. * `-Zmiri-disable-alignment-check` disables checking pointer alignment, so you can focus on other failures, but it means Miri can miss bugs in your program. Using this flag is **unsound**. @@ -263,6 +265,8 @@ environment variable: with `-Zmiri-track-raw-pointers` also works without `-Zmiri-track-raw-pointers`, but for the vast majority of code, this will be the case. +[function ABI]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier + Some native rustc `-Z` flags are also very relevant for Miri: * `-Zmir-opt-level` controls how many MIR optimizations are performed. Miri diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 47cde5c353..514e5b0aeb 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -1,14 +1,17 @@ -#![feature(rustc_private)] +#![feature(rustc_private, bool_to_option, stmt_expr_attributes)] extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_interface; +extern crate rustc_metadata; extern crate rustc_middle; extern crate rustc_session; use std::convert::TryFrom; use std::env; +use std::path::PathBuf; +use std::rc::Rc; use std::str::FromStr; use hex::FromHexError; @@ -16,14 +19,34 @@ use log::debug; use rustc_driver::Compilation; use rustc_errors::emitter::{ColorConfig, HumanReadableErrorType}; -use rustc_middle::ty::TyCtxt; -use rustc_session::{config::ErrorOutputType, CtfeBacktrace}; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_interface::interface::Config; +use rustc_middle::{ + middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}, + ty::{query::Providers, TyCtxt}, +}; +use rustc_session::{config::ErrorOutputType, search_paths::PathKind, CtfeBacktrace}; struct MiriCompilerCalls { miri_config: miri::MiriConfig, } impl rustc_driver::Callbacks for MiriCompilerCalls { + fn config(&mut self, config: &mut Config) { + config.override_queries = Some(|_, _, external_providers| { + external_providers.used_crate_source = |tcx, cnum| { + let mut providers = Providers::default(); + rustc_metadata::provide_extern(&mut providers); + let mut crate_source = (providers.used_crate_source)(tcx, cnum); + // HACK: rustc will emit "crate ... required to be available in rlib format, but + // was not found in this form" errors once we use `tcx.dependency_formats()` if + // there's no rlib provided, so setting a dummy path here to workaround those errors. + Rc::make_mut(&mut crate_source).rlib = Some((PathBuf::new(), PathKind::All)); + crate_source + }; + }); + } + fn after_analysis<'tcx>( &mut self, compiler: &rustc_interface::interface::Compiler, @@ -67,6 +90,39 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { } } +struct MiriBeRustCompilerCalls { + target_crate: bool, +} + +impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { + fn config(&mut self, config: &mut Config) { + if config.opts.prints.is_empty() && self.target_crate { + // Queries overriden here affect the data stored in `rmeta` files of dependencies, + // which will be used later in non-`MIRI_BE_RUSTC` mode. + config.override_queries = Some(|_, local_providers, _| { + // `exported_symbols()` provided by rustc always returns empty result if + // `tcx.sess.opts.output_types.should_codegen()` is false. + local_providers.exported_symbols = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + tcx.arena.alloc_from_iter( + // This is based on: + // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L62-L63 + // https://github.com/rust-lang/rust/blob/2962e7c0089d5c136f4e9600b7abccfbbde4973d/compiler/rustc_codegen_ssa/src/back/symbol_export.rs#L174 + tcx.reachable_set(()).iter().filter_map(|&local_def_id| { + tcx.codegen_fn_attrs(local_def_id) + .contains_extern_indicator() + .then_some(( + ExportedSymbol::NonGeneric(local_def_id.to_def_id()), + SymbolExportLevel::C, + )) + }), + ) + } + }); + } + } +} + fn init_early_loggers() { // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to // initialize them both, and we always initialize `miri`'s first. @@ -179,11 +235,7 @@ fn main() { if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") { rustc_driver::init_rustc_env_logger(); - // Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building a - // "host" crate. That may cause procedural macros (and probably build scripts) to depend - // on Miri-only symbols, such as `miri_resolve_frame`: - // https://github.com/rust-lang/miri/issues/1760 - let insert_default_args = if crate_kind == "target" { + let target_crate = if crate_kind == "target" { true } else if crate_kind == "host" { false @@ -192,8 +244,16 @@ fn main() { }; // We cannot use `rustc_driver::main` as we need to adjust the CLI arguments. - let mut callbacks = rustc_driver::TimePassesCallbacks::default(); - run_compiler(env::args().collect(), &mut callbacks, insert_default_args) + run_compiler( + env::args().collect(), + &mut MiriBeRustCompilerCalls { target_crate }, + // Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building + // a "host" crate. That may cause procedural macros (and probably build scripts) to + // depend on Miri-only symbols, such as `miri_resolve_frame`: + // https://github.com/rust-lang/miri/issues/1760 + #[rustfmt::skip] + /* insert_default_args: */ target_crate, + ) } // Init loggers the Miri way. @@ -227,6 +287,9 @@ fn main() { "-Zmiri-symbolic-alignment-check" => { miri_config.check_alignment = miri::AlignmentCheck::Symbolic; } + "-Zmiri-disable-abi-check" => { + miri_config.check_abi = false; + } "-Zmiri-disable-isolation" => { miri_config.communicate = true; } diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 456b6fb4d4..f273cf0420 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -5,7 +5,7 @@ use std::num::NonZeroU64; use log::trace; use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::{source_map::DUMMY_SP, Span}; +use rustc_span::{source_map::DUMMY_SP, Span, SpanData, Symbol}; use crate::*; @@ -14,8 +14,18 @@ pub enum TerminationInfo { Exit(i64), Abort(String), UnsupportedInIsolation(String), - ExperimentalUb { msg: String, url: String }, + ExperimentalUb { + msg: String, + url: String, + }, Deadlock, + MultipleSymbolDefinitions { + link_name: Symbol, + first: SpanData, + first_crate: Symbol, + second: SpanData, + second_crate: Symbol, + }, } impl fmt::Display for TerminationInfo { @@ -27,6 +37,8 @@ impl fmt::Display for TerminationInfo { UnsupportedInIsolation(msg) => write!(f, "{}", msg), ExperimentalUb { msg, .. } => write!(f, "{}", msg), Deadlock => write!(f, "the evaluated program deadlocked"), + MultipleSymbolDefinitions { link_name, .. } => + write!(f, "multiple definitions of symbol `{}`", link_name), } } } @@ -55,19 +67,25 @@ pub fn report_error<'tcx, 'mir>( use TerminationInfo::*; let title = match info { Exit(code) => return Some(*code), - Abort(_) => "abnormal termination", - UnsupportedInIsolation(_) => "unsupported operation", - ExperimentalUb { .. } => "Undefined Behavior", - Deadlock => "deadlock", + Abort(_) => Some("abnormal termination"), + UnsupportedInIsolation(_) => Some("unsupported operation"), + ExperimentalUb { .. } => Some("Undefined Behavior"), + Deadlock => Some("deadlock"), + MultipleSymbolDefinitions { .. } => None, }; #[rustfmt::skip] let helps = match info { UnsupportedInIsolation(_) => - vec![format!("pass the flag `-Zmiri-disable-isolation` to disable isolation")], + vec![(None, format!("pass the flag `-Zmiri-disable-isolation` to disable isolation"))], ExperimentalUb { url, .. } => vec![ - format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental"), - format!("see {} for further information", url), + (None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental")), + (None, format!("see {} for further information", url)), + ], + MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } => + vec![ + (Some(*first), format!("it's first defined here, in crate `{}`", first_crate)), + (Some(*second), format!("then it's defined here again, in crate `{}`", second_crate)), ], _ => vec![], }; @@ -92,26 +110,26 @@ pub fn report_error<'tcx, 'mir>( #[rustfmt::skip] let helps = match e.kind() { Unsupported(UnsupportedOpInfo::NoMirFor(..)) => - vec![format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`")], + vec![(None, format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`"))], Unsupported(UnsupportedOpInfo::ReadBytesAsPointer | UnsupportedOpInfo::ThreadLocalStatic(_) | UnsupportedOpInfo::ReadExternStatic(_)) => panic!("Error should never be raised by Miri: {:?}", e.kind()), Unsupported(_) => - vec![format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support")], + vec![(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"))], UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. }) if ecx.memory.extra.check_alignment == AlignmentCheck::Symbolic => vec![ - format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior"), - format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives"), + (None, format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior")), + (None, format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives")), ], UndefinedBehavior(_) => vec![ - format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"), - format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"), + (None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")), + (None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")), ], _ => vec![], }; - (title, helps) + (Some(title), helps) } }; @@ -120,7 +138,7 @@ pub fn report_error<'tcx, 'mir>( report_msg( *ecx.tcx, /*error*/ true, - &format!("{}: {}", title, msg), + &if let Some(title) = title { format!("{}: {}", title, msg) } else { msg.clone() }, msg, helps, &ecx.generate_stacktrace(), @@ -159,7 +177,7 @@ fn report_msg<'tcx>( error: bool, title: &str, span_msg: String, - mut helps: Vec, + mut helps: Vec<(Option, String)>, stacktrace: &[FrameInfo<'tcx>], ) { let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span); @@ -179,9 +197,13 @@ fn report_msg<'tcx>( // Show help messages. if !helps.is_empty() { // Add visual separator before backtrace. - helps.last_mut().unwrap().push_str("\n"); - for help in helps { - err.help(&help); + helps.last_mut().unwrap().1.push_str("\n"); + for (span_data, help) in helps { + if let Some(span_data) = span_data { + err.span_help(span_data.span(), &help); + } else { + err.help(&help); + } } } // Add backtrace diff --git a/src/eval.rs b/src/eval.rs index 52e554f57d..6646783d34 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -31,6 +31,8 @@ pub struct MiriConfig { pub stacked_borrows: bool, /// Controls alignment checking. pub check_alignment: AlignmentCheck, + /// Controls function [ABI](Abi) checking. + pub check_abi: bool, /// Determines if communication with the host environment is enabled. pub communicate: bool, /// Determines if memory leaks should be ignored. @@ -65,6 +67,7 @@ impl Default for MiriConfig { validate: true, stacked_borrows: true, check_alignment: AlignmentCheck::Int, + check_abi: true, communicate: false, ignore_leaks: false, excluded_env_vars: vec![], diff --git a/src/helpers.rs b/src/helpers.rs index 29fde55d86..b71f830b2b 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -165,7 +165,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let this = self.eval_context_mut(); let param_env = ty::ParamEnv::reveal_all(); // in Miri this is always the param_env we use... and this.param_env is private. let callee_abi = f.ty(*this.tcx, param_env).fn_sig(*this.tcx).abi(); - if callee_abi != caller_abi { + if this.machine.enforce_abi && callee_abi != caller_abi { throw_ub_format!( "calling a function with ABI {} using caller ABI {}", callee_abi.name(), @@ -616,6 +616,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(wchars) } + + /// Check that the ABI is what we expect. + fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> { + if self.eval_context_ref().machine.enforce_abi && abi != exp_abi { + throw_ub_format!( + "calling a function with ABI {} using caller ABI {}", + exp_abi.name(), + abi.name() + ) + } + Ok(()) + } } /// Check that the number of args is what we expect. @@ -631,19 +643,6 @@ where throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N) } -/// Check that the ABI is what we expect. -pub fn check_abi<'a>(abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> { - if abi == exp_abi { - Ok(()) - } else { - throw_ub_format!( - "calling a function with ABI {} using caller ABI {}", - exp_abi.name(), - abi.name() - ) - } -} - pub fn isolation_error(name: &str) -> InterpResult<'static> { throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( "{} not available when isolation is enabled", diff --git a/src/lib.rs b/src/lib.rs index c9645d12fa..25806b472b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ #![feature(map_try_insert)] #![feature(never_type)] #![feature(try_blocks)] +#![feature(bool_to_option)] #![warn(rust_2018_idioms)] #![allow(clippy::cast_lossless)] @@ -14,6 +15,7 @@ extern crate rustc_data_structures; extern crate rustc_hir; extern crate rustc_index; extern crate rustc_mir; +extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; diff --git a/src/machine.rs b/src/machine.rs index 4f643e7f50..9c49575ded 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -17,7 +17,7 @@ use rustc_middle::{ ty::{ self, layout::{LayoutCx, LayoutError, TyAndLayout}, - TyCtxt, + Instance, TyCtxt, }, }; use rustc_span::def_id::DefId; @@ -270,6 +270,9 @@ pub struct Evaluator<'mir, 'tcx> { /// Whether to enforce the validity invariant. pub(crate) validate: bool, + /// Whether to enforce [ABI](Abi) of function calls. + pub(crate) enforce_abi: bool, + pub(crate) file_handler: shims::posix::FileHandler, pub(crate) dir_handler: shims::posix::DirHandler, @@ -291,6 +294,9 @@ pub struct Evaluator<'mir, 'tcx> { /// Used with `profiler` to cache the `StringId`s for event names /// uesd with `measureme`. string_cache: FxHashMap, + + /// Cache of `Instance` exported under the given `Symbol` name. + pub(crate) exported_symbols_cache: FxHashMap>, } impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { @@ -310,6 +316,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { tls: TlsData::default(), communicate: config.communicate, validate: config.validate, + enforce_abi: config.check_abi, file_handler: Default::default(), dir_handler: Default::default(), time_anchor: Instant::now(), @@ -318,6 +325,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { static_roots: Vec::new(), profiler, string_cache: Default::default(), + exported_symbols_cache: FxHashMap::default(), } } } @@ -371,6 +379,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { ecx.machine.validate } + #[inline(always)] + fn enforce_abi(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + ecx.machine.enforce_abi + } + #[inline(always)] fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 4c96c99eee..c838c136c3 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -6,18 +6,36 @@ use std::{ use log::trace; use rustc_apfloat::Float; -use rustc_hir::def_id::DefId; +use rustc_hir::{ + def::DefKind, + def_id::{CrateNum, DefId, LOCAL_CRATE}, +}; +use rustc_middle::middle::{ + codegen_fn_attrs::CodegenFnAttrFlags, dependency_format::Linkage, + exported_symbols::ExportedSymbol, +}; use rustc_middle::mir; use rustc_middle::ty; -use rustc_span::symbol::sym; +use rustc_session::config::CrateType; +use rustc_span::{symbol::sym, Symbol}; use rustc_target::{ abi::{Align, Size}, - spec::{abi::Abi, PanicStrategy}, + spec::abi::Abi, }; use super::backtrace::EvalContextExt as _; use crate::*; -use helpers::{check_abi, check_arg_count}; +use helpers::check_arg_count; + +/// Returned by `emulate_foreign_item_by_name`. +pub enum EmulateByNameResult { + /// The caller is expected to jump to the return block. + NeedsJumping, + /// Jumping has already been taken care of. + AlreadyJumped, + /// The item is not supported. + NotSupported, +} impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { @@ -108,6 +126,77 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } + /// Lookup the body of a function that has `link_name` as the symbol name. + fn lookup_exported_symbol( + &mut self, + link_name: Symbol, + ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> { + let this = self.eval_context_mut(); + let tcx = this.tcx.tcx; + + // If the result was cached, just return it. + if let Some(instance) = this.machine.exported_symbols_cache.get(&link_name) { + return Ok(Some(this.load_mir(instance.def, None)?)); + } + + // Find it if it was not cached. + let mut instance_and_crate: Option<(ty::Instance<'_>, CrateNum)> = None; + // `dependency_formats` includes all the transitive informations needed to link a crate, + // which is what we need here since we need to dig out `exported_symbols` from all transitive + // dependencies. + let dependency_formats = tcx.dependency_formats(()); + let dependency_format = dependency_formats + .iter() + .find(|(crate_type, _)| *crate_type == CrateType::Executable) + .expect("interpreting a non-executable crate"); + for cnum in + iter::once(LOCAL_CRATE).chain(dependency_format.1.iter().enumerate().filter_map( + |(num, &linkage)| (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1)), + )) + { + // We can ignore `_export_level` here: we are a Rust crate, and everything is exported + // from a Rust crate. + for &(symbol, _export_level) in tcx.exported_symbols(cnum) { + if let ExportedSymbol::NonGeneric(def_id) = symbol { + let attrs = tcx.codegen_fn_attrs(def_id); + let symbol_name = if let Some(export_name) = attrs.export_name { + export_name + } else if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { + tcx.item_name(def_id) + } else { + // Skip over items without an explicitly defined symbol name. + continue; + }; + if symbol_name == link_name { + if let Some((instance, original_cnum)) = instance_and_crate { + throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions { + link_name, + first: tcx.def_span(instance.def_id()).data(), + first_crate: tcx.crate_name(original_cnum), + second: tcx.def_span(def_id).data(), + second_crate: tcx.crate_name(cnum), + }); + } + if tcx.def_kind(def_id) != DefKind::Fn { + throw_ub_format!( + "attempt to call an exported symbol that is not defined as a function" + ); + } + instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum)); + } + } + } + } + + // Cache it and load its MIR, if found. + instance_and_crate + .map(|(instance, _)| { + this.machine.exported_symbols_cache.insert(link_name, instance); + this.load_mir(instance.def, None) + }) + .transpose() + } + /// Emulates calling a foreign item, failing if the item is not supported. /// This function will handle `goto_block` if needed. /// Returns Ok(None) if the foreign item was completely handled @@ -124,10 +213,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> { let this = self.eval_context_mut(); let attrs = this.tcx.get_attrs(def_id); - let link_name = match this.tcx.sess.first_attr_value_str_by_name(&attrs, sym::link_name) { - Some(name) => name.as_str(), - None => this.tcx.item_name(def_id).as_str(), - }; + let link_name_sym = this + .tcx + .sess + .first_attr_value_str_by_name(&attrs, sym::link_name) + .unwrap_or_else(|| this.tcx.item_name(def_id)); + let link_name = link_name_sym.as_str(); // Strip linker suffixes (seen on 32-bit macOS). let link_name = link_name.trim_end_matches("$UNIX2003"); let tcx = this.tcx.tcx; @@ -136,14 +227,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let (dest, ret) = match ret { None => match link_name { "miri_start_panic" => { - check_abi(abi, Abi::Rust)?; + this.check_abi(abi, Abi::Rust)?; this.handle_miri_start_panic(args, unwind)?; return Ok(None); } // This matches calls to the foreign item `panic_impl`. // The implementation is provided by the function with the `#[panic_handler]` attribute. "panic_impl" => { - check_abi(abi, Abi::Rust)?; + this.check_abi(abi, Abi::Rust)?; let panic_impl_id = tcx.lang_items().panic_impl().unwrap(); let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id); return Ok(Some(&*this.load_mir(panic_impl_instance.def, None)?)); @@ -152,60 +243,47 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "exit" | "ExitProcess" => { - check_abi(abi, if link_name == "exit" { Abi::C { unwind: false } } else { Abi::System { unwind: false } })?; + this.check_abi(abi, if link_name == "exit" { Abi::C { unwind: false } } else { Abi::System { unwind: false } })?; let &[ref code] = check_arg_count(args)?; // it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway let code = this.read_scalar(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit(code.into())); } "abort" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; throw_machine_stop!(TerminationInfo::Abort( "the program aborted execution".to_owned() )) } - _ => throw_unsup_format!("can't call (diverging) foreign function: {}", link_name), + _ => { + if let Some(body) = this.lookup_exported_symbol(link_name_sym)? { + return Ok(Some(body)); + } + throw_unsup_format!("can't call (diverging) foreign function: {}", link_name); + } }, Some(p) => p, }; - // Second: some functions that we forward to MIR implementations. - match link_name { - // This matches calls to the foreign item `__rust_start_panic`, that is, - // calls to `extern "Rust" { fn __rust_start_panic(...) }` - // (and `__rust_panic_cleanup`, respectively). - // We forward this to the underlying *implementation* in the panic runtime crate. - // Normally, this will be either `libpanic_unwind` or `libpanic_abort`, but it could - // also be a custom user-provided implementation via `#![feature(panic_runtime)]` - #[rustfmt::skip] - "__rust_start_panic" | - "__rust_panic_cleanup" => { - check_abi(abi, Abi::C { unwind: false })?; - // This replicates some of the logic in `inject_panic_runtime`. - // FIXME: is there a way to reuse that logic? - let panic_runtime = match this.tcx.sess.panic_strategy() { - PanicStrategy::Unwind => sym::panic_unwind, - PanicStrategy::Abort => sym::panic_abort, - }; - let start_panic_instance = - this.resolve_path(&[&*panic_runtime.as_str(), link_name]); - return Ok(Some(&*this.load_mir(start_panic_instance.def, None)?)); + // Second: functions that return. + match this.emulate_foreign_item_by_name(link_name, abi, args, dest, ret)? { + EmulateByNameResult::NeedsJumping => { + trace!("{:?}", this.dump_place(**dest)); + this.go_to_block(ret); + } + EmulateByNameResult::AlreadyJumped => (), + EmulateByNameResult::NotSupported => { + if let Some(body) = this.lookup_exported_symbol(link_name_sym)? { + return Ok(Some(body)); + } + throw_unsup_format!("can't call foreign function: {}", link_name); } - _ => {} - } - - // Third: functions that return. - if this.emulate_foreign_item_by_name(link_name, abi, args, dest, ret)? { - trace!("{:?}", this.dump_place(**dest)); - this.go_to_block(ret); } Ok(None) } - /// Emulates calling a foreign item using its name, failing if the item is not supported. - /// Returns `true` if the caller is expected to jump to the return block, and `false` if - /// jumping has already been taken care of. + /// Emulates calling a foreign item using its name. fn emulate_foreign_item_by_name( &mut self, link_name: &str, @@ -213,7 +291,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, ret: mir::BasicBlock, - ) -> InterpResult<'tcx, bool> { + ) -> InterpResult<'tcx, EmulateByNameResult> { let this = self.eval_context_mut(); // Here we dispatch all the shims for foreign functions. If you have a platform specific @@ -221,7 +299,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match link_name { // Miri-specific extern functions "miri_static_root" => { - check_abi(abi, Abi::Rust)?; + this.check_abi(abi, Abi::Rust)?; let &[ref ptr] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let ptr = this.force_ptr(ptr)?; @@ -233,27 +311,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Obtains a Miri backtrace. See the README for details. "miri_get_backtrace" => { - check_abi(abi, Abi::Rust)?; + this.check_abi(abi, Abi::Rust)?; this.handle_miri_get_backtrace(args, dest)?; } // Resolves a Miri backtrace frame. See the README for details. "miri_resolve_frame" => { - check_abi(abi, Abi::Rust)?; + this.check_abi(abi, Abi::Rust)?; this.handle_miri_resolve_frame(args, dest)?; } // Standard C allocation "malloc" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref size] = check_arg_count(args)?; let size = this.read_scalar(size)?.to_machine_usize(this)?; let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C); this.write_scalar(res, dest)?; } "calloc" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref items, ref len] = check_arg_count(args)?; let items = this.read_scalar(items)?.to_machine_usize(this)?; let len = this.read_scalar(len)?.to_machine_usize(this)?; @@ -263,13 +341,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(res, dest)?; } "free" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref ptr] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; this.free(ptr, MiriMemoryKind::C)?; } "realloc" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref old_ptr, ref new_size] = check_arg_count(args)?; let old_ptr = this.read_scalar(old_ptr)?.check_init()?; let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?; @@ -281,7 +359,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // (Usually these would be forwarded to to `#[global_allocator]`; we instead implement a generic // allocation that also checks that all conditions are met, such as not permitting zero-sized allocations.) "__rust_alloc" => { - check_abi(abi, Abi::Rust)?; + this.check_abi(abi, Abi::Rust)?; let &[ref size, ref align] = check_arg_count(args)?; let size = this.read_scalar(size)?.to_machine_usize(this)?; let align = this.read_scalar(align)?.to_machine_usize(this)?; @@ -294,7 +372,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(ptr, dest)?; } "__rust_alloc_zeroed" => { - check_abi(abi, Abi::Rust)?; + this.check_abi(abi, Abi::Rust)?; let &[ref size, ref align] = check_arg_count(args)?; let size = this.read_scalar(size)?.to_machine_usize(this)?; let align = this.read_scalar(align)?.to_machine_usize(this)?; @@ -309,7 +387,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(ptr, dest)?; } "__rust_dealloc" => { - check_abi(abi, Abi::Rust)?; + this.check_abi(abi, Abi::Rust)?; let &[ref ptr, ref old_size, ref align] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; @@ -323,7 +401,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx )?; } "__rust_realloc" => { - check_abi(abi, Abi::Rust)?; + this.check_abi(abi, Abi::Rust)?; let &[ref ptr, ref old_size, ref align, ref new_size] = check_arg_count(args)?; let ptr = this.force_ptr(this.read_scalar(ptr)?.check_init()?)?; let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; @@ -344,7 +422,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // C memory handling functions "memcmp" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref left, ref right, ref n] = check_arg_count(args)?; let left = this.read_scalar(left)?.check_init()?; let right = this.read_scalar(right)?.check_init()?; @@ -365,7 +443,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_i32(result), dest)?; } "memrchr" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref ptr, ref val, ref num] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let val = this.read_scalar(val)?.to_i32()? as u8; @@ -384,7 +462,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } "memchr" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref ptr, ref val, ref num] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let val = this.read_scalar(val)?.to_i32()? as u8; @@ -402,7 +480,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } "strlen" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref ptr] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let n = this.read_c_str(ptr)?.len(); @@ -419,7 +497,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "asinf" | "atanf" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref f] = check_arg_count(args)?; // FIXME: Using host floats. let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); @@ -440,7 +518,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "hypotf" | "atan2f" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref f1, ref f2] = check_arg_count(args)?; // underscore case for windows, here and below // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) @@ -463,7 +541,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "asin" | "atan" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref f] = check_arg_count(args)?; // FIXME: Using host floats. let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); @@ -484,7 +562,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "hypot" | "atan2" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref f1, ref f2] = check_arg_count(args)?; // FIXME: Using host floats. let f1 = f64::from_bits(this.read_scalar(f1)?.to_u64()?); @@ -501,7 +579,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "ldexp" | "scalbn" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref x, ref exp] = check_arg_count(args)?; // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. let x = this.read_scalar(x)?.to_f64()?; @@ -523,12 +601,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Architecture-specific shims "llvm.x86.sse2.pause" if this.tcx.sess.target.arch == "x86" || this.tcx.sess.target.arch == "x86_64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[] = check_arg_count(args)?; this.yield_active_thread(); } "llvm.aarch64.isb" if this.tcx.sess.target.arch == "aarch64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref arg] = check_arg_count(args)?; let arg = this.read_scalar(arg)?.to_i32()?; match arg { @@ -549,7 +627,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } }; - Ok(true) + // We only fall through to here if we did *not* hit the `_` arm above, + // i.e., if we actually emulated the function. + Ok(EmulateByNameResult::NeedsJumping) } /// Check some basic requirements for this allocation request: diff --git a/src/shims/posix/dlsym.rs b/src/shims/posix/dlsym.rs index df9e945f29..b07bf91a69 100644 --- a/src/shims/posix/dlsym.rs +++ b/src/shims/posix/dlsym.rs @@ -2,7 +2,6 @@ use rustc_middle::mir; use rustc_target::spec::abi::Abi; use crate::*; -use helpers::check_abi; use shims::posix::linux::dlsym as linux; use shims::posix::macos::dlsym as macos; @@ -35,7 +34,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; match dlsym { Dlsym::Linux(dlsym) => linux::EvalContextExt::call_dlsym(this, dlsym, args, ret), diff --git a/src/shims/posix/foreign_items.rs b/src/shims/posix/foreign_items.rs index 52b41b49bd..a756256aab 100644 --- a/src/shims/posix/foreign_items.rs +++ b/src/shims/posix/foreign_items.rs @@ -5,7 +5,8 @@ use rustc_target::abi::{Align, LayoutOf, Size}; use rustc_target::spec::abi::Abi; use crate::*; -use helpers::{check_abi, check_arg_count}; +use helpers::check_arg_count; +use shims::foreign_items::EmulateByNameResult; use shims::posix::fs::EvalContextExt as _; use shims::posix::sync::EvalContextExt as _; use shims::posix::thread::EvalContextExt as _; @@ -19,38 +20,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, ret: mir::BasicBlock, - ) -> InterpResult<'tcx, bool> { + ) -> InterpResult<'tcx, EmulateByNameResult> { let this = self.eval_context_mut(); match link_name { // Environment related shims "getenv" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref name] = check_arg_count(args)?; let result = this.getenv(name)?; this.write_scalar(result, dest)?; } "unsetenv" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref name] = check_arg_count(args)?; let result = this.unsetenv(name)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "setenv" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref name, ref value, ref overwrite] = check_arg_count(args)?; this.read_scalar(overwrite)?.to_i32()?; let result = this.setenv(name, value)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "getcwd" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref buf, ref size] = check_arg_count(args)?; let result = this.getcwd(buf, size)?; this.write_scalar(result, dest)?; } "chdir" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref path] = check_arg_count(args)?; let result = this.chdir(path)?; this.write_scalar(Scalar::from_i32(result), dest)?; @@ -58,18 +59,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // File related shims "open" | "open64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref path, ref flag, ref mode] = check_arg_count(args)?; let result = this.open(path, flag, mode)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "fcntl" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let result = this.fcntl(args)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "read" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd, ref buf, ref count] = check_arg_count(args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_scalar(buf)?.check_init()?; @@ -78,7 +79,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; } "write" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd, ref buf, ref n] = check_arg_count(args)?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_scalar(buf)?.check_init()?; @@ -89,62 +90,62 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; } "unlink" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref path] = check_arg_count(args)?; let result = this.unlink(path)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "symlink" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref target, ref linkpath] = check_arg_count(args)?; let result = this.symlink(target, linkpath)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "rename" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref oldpath, ref newpath] = check_arg_count(args)?; let result = this.rename(oldpath, newpath)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "mkdir" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref path, ref mode] = check_arg_count(args)?; let result = this.mkdir(path, mode)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "rmdir" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref path] = check_arg_count(args)?; let result = this.rmdir(path)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "closedir" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref dirp] = check_arg_count(args)?; let result = this.closedir(dirp)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "lseek" | "lseek64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd, ref offset, ref whence] = check_arg_count(args)?; let result = this.lseek64(fd, offset, whence)?; // "lseek" is only used on macOS which is 64bit-only, so `i64` always works. this.write_scalar(Scalar::from_i64(result), dest)?; } "fsync" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd] = check_arg_count(args)?; let result = this.fsync(fd)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "fdatasync" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd] = check_arg_count(args)?; let result = this.fdatasync(fd)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "readlink" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref pathname, ref buf, ref bufsize] = check_arg_count(args)?; let result = this.readlink(pathname, buf, bufsize)?; this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; @@ -152,7 +153,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Allocation "posix_memalign" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref ret, ref align, ref size] = check_arg_count(args)?; let ret = this.deref_operand(ret)?; let align = this.read_scalar(align)?.to_machine_usize(this)?; @@ -183,7 +184,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Dynamic symbol loading "dlsym" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref handle, ref symbol] = check_arg_count(args)?; this.read_scalar(handle)?.to_machine_usize(this)?; let symbol = this.read_scalar(symbol)?.check_init()?; @@ -198,7 +199,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Querying system information "sysconf" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref name] = check_arg_count(args)?; let name = this.read_scalar(name)?.to_i32()?; @@ -224,7 +225,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Thread-local storage "pthread_key_create" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref key, ref dtor] = check_arg_count(args)?; let key_place = this.deref_operand(key)?; let dtor = this.read_scalar(dtor)?.check_init()?; @@ -253,7 +254,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } "pthread_key_delete" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref key] = check_arg_count(args)?; let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?; this.machine.tls.delete_tls_key(key)?; @@ -261,7 +262,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } "pthread_getspecific" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref key] = check_arg_count(args)?; let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?; let active_thread = this.get_active_thread(); @@ -269,7 +270,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(ptr, dest)?; } "pthread_setspecific" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref key, ref new_ptr] = check_arg_count(args)?; let key = this.force_bits(this.read_scalar(key)?.check_init()?, key.layout.size)?; let active_thread = this.get_active_thread(); @@ -282,132 +283,132 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Synchronization primitives "pthread_mutexattr_init" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref attr] = check_arg_count(args)?; let result = this.pthread_mutexattr_init(attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutexattr_settype" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref attr, ref kind] = check_arg_count(args)?; let result = this.pthread_mutexattr_settype(attr, kind)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutexattr_destroy" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref attr] = check_arg_count(args)?; let result = this.pthread_mutexattr_destroy(attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_init" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref mutex, ref attr] = check_arg_count(args)?; let result = this.pthread_mutex_init(mutex, attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_lock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref mutex] = check_arg_count(args)?; let result = this.pthread_mutex_lock(mutex)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_trylock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref mutex] = check_arg_count(args)?; let result = this.pthread_mutex_trylock(mutex)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_unlock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref mutex] = check_arg_count(args)?; let result = this.pthread_mutex_unlock(mutex)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_destroy" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref mutex] = check_arg_count(args)?; let result = this.pthread_mutex_destroy(mutex)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_rdlock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref rwlock] = check_arg_count(args)?; let result = this.pthread_rwlock_rdlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_tryrdlock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref rwlock] = check_arg_count(args)?; let result = this.pthread_rwlock_tryrdlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_wrlock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref rwlock] = check_arg_count(args)?; let result = this.pthread_rwlock_wrlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_trywrlock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref rwlock] = check_arg_count(args)?; let result = this.pthread_rwlock_trywrlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_unlock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref rwlock] = check_arg_count(args)?; let result = this.pthread_rwlock_unlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_destroy" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref rwlock] = check_arg_count(args)?; let result = this.pthread_rwlock_destroy(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_condattr_init" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref attr] = check_arg_count(args)?; let result = this.pthread_condattr_init(attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_condattr_destroy" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref attr] = check_arg_count(args)?; let result = this.pthread_condattr_destroy(attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_cond_init" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref cond, ref attr] = check_arg_count(args)?; let result = this.pthread_cond_init(cond, attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_cond_signal" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref cond] = check_arg_count(args)?; let result = this.pthread_cond_signal(cond)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_cond_broadcast" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref cond] = check_arg_count(args)?; let result = this.pthread_cond_broadcast(cond)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_cond_wait" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref cond, ref mutex] = check_arg_count(args)?; let result = this.pthread_cond_wait(cond, mutex)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_cond_timedwait" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref cond, ref mutex, ref abstime] = check_arg_count(args)?; this.pthread_cond_timedwait(cond, mutex, abstime, dest)?; } "pthread_cond_destroy" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref cond] = check_arg_count(args)?; let result = this.pthread_cond_destroy(cond)?; this.write_scalar(Scalar::from_i32(result), dest)?; @@ -415,36 +416,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Threading "pthread_create" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref thread, ref attr, ref start, ref arg] = check_arg_count(args)?; let result = this.pthread_create(thread, attr, start, arg)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_join" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref thread, ref retval] = check_arg_count(args)?; let result = this.pthread_join(thread, retval)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_detach" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref thread] = check_arg_count(args)?; let result = this.pthread_detach(thread)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_self" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[] = check_arg_count(args)?; this.pthread_self(dest)?; } "sched_yield" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[] = check_arg_count(args)?; let result = this.sched_yield()?; this.write_scalar(Scalar::from_i32(result), dest)?; } "nanosleep" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref req, ref rem] = check_arg_count(args)?; let result = this.nanosleep(req, rem)?; this.write_scalar(Scalar::from_i32(result), dest)?; @@ -452,7 +453,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Miscellaneous "isatty" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd] = check_arg_count(args)?; this.read_scalar(fd)?.to_i32()?; // "returns 1 if fd is an open file descriptor referring to a terminal; otherwise 0 is returned, and errno is set to indicate the error" @@ -462,7 +463,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } "pthread_atfork" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref prepare, ref parent, ref child] = check_arg_count(args)?; this.force_bits(this.read_scalar(prepare)?.check_init()?, this.memory.pointer_size())?; this.force_bits(this.read_scalar(parent)?.check_init()?, this.memory.pointer_size())?; @@ -475,7 +476,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // These shims are enabled only when the caller is in the standard library. "pthread_attr_getguardsize" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref _attr, ref guard_size] = check_arg_count(args)?; let guard_size = this.deref_operand(guard_size)?; let guard_size_layout = this.libc_ty_layout("size_t")?; @@ -488,13 +489,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "pthread_attr_init" | "pthread_attr_destroy" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[_] = check_arg_count(args)?; this.write_null(dest)?; } | "pthread_attr_setstacksize" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[_, _] = check_arg_count(args)?; this.write_null(dest)?; } @@ -502,14 +503,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "signal" | "sigaltstack" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[_, _] = check_arg_count(args)?; this.write_null(dest)?; } | "sigaction" | "mprotect" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[_, _, _] = check_arg_count(args)?; this.write_null(dest)?; } @@ -524,6 +525,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } }; - Ok(true) + Ok(EmulateByNameResult::NeedsJumping) } } diff --git a/src/shims/posix/linux/foreign_items.rs b/src/shims/posix/linux/foreign_items.rs index f7d7706e3f..598d89fe1f 100644 --- a/src/shims/posix/linux/foreign_items.rs +++ b/src/shims/posix/linux/foreign_items.rs @@ -1,8 +1,9 @@ use rustc_middle::mir; use rustc_target::spec::abi::Abi; -use crate::helpers::{check_abi, check_arg_count}; +use crate::helpers::check_arg_count; use crate::*; +use shims::foreign_items::EmulateByNameResult; use shims::posix::fs::EvalContextExt as _; use shims::posix::linux::sync::futex; use shims::posix::sync::EvalContextExt as _; @@ -17,13 +18,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, _ret: mir::BasicBlock, - ) -> InterpResult<'tcx, bool> { + ) -> InterpResult<'tcx, EmulateByNameResult> { let this = self.eval_context_mut(); match link_name { // errno "__errno_location" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[] = check_arg_count(args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?; @@ -33,32 +34,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // These symbols have different names on Linux and macOS, which is the only reason they are not // in the `posix` module. "close" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd] = check_arg_count(args)?; let result = this.close(fd)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "opendir" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref name] = check_arg_count(args)?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "readdir64_r" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref dirp, ref entry, ref result] = check_arg_count(args)?; let result = this.linux_readdir64_r(dirp, entry, result)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "ftruncate64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd, ref length] = check_arg_count(args)?; let result = this.ftruncate64(fd, length)?; this.write_scalar(Scalar::from_i32(result), dest)?; } // Linux-only "posix_fadvise" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd, ref offset, ref len, ref advice] = check_arg_count(args)?; this.read_scalar(fd)?.to_i32()?; this.read_scalar(offset)?.to_machine_isize(this)?; @@ -68,7 +69,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } "sync_file_range" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd, ref offset, ref nbytes, ref flags] = check_arg_count(args)?; let result = this.sync_file_range(fd, offset, nbytes, flags)?; this.write_scalar(Scalar::from_i32(result), dest)?; @@ -76,7 +77,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Time related shims "clock_gettime" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; // This is a POSIX function but it has only been tested on linux. let &[ref clk_id, ref tp] = check_arg_count(args)?; let result = this.clock_gettime(clk_id, tp)?; @@ -85,7 +86,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Querying system information "pthread_attr_getstack" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here. let &[ref attr_place, ref addr_place, ref size_place] = check_arg_count(args)?; this.deref_operand(attr_place)?; @@ -107,19 +108,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Threading "prctl" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref option, ref arg2, ref arg3, ref arg4, ref arg5] = check_arg_count(args)?; let result = this.prctl(option, arg2, arg3, arg4, arg5)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_condattr_setclock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref attr, ref clock_id] = check_arg_count(args)?; let result = this.pthread_condattr_setclock(attr, clock_id)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_condattr_getclock" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref attr, ref clock_id] = check_arg_count(args)?; let result = this.pthread_condattr_getclock(attr, clock_id)?; this.write_scalar(Scalar::from_i32(result), dest)?; @@ -127,7 +128,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Dynamically invoked syscalls "syscall" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; // The syscall variadic function is legal to call with more arguments than needed, // extra arguments are simply ignored. However, all arguments need to be scalars; // other types might be treated differently by the calling convention. @@ -188,12 +189,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Miscelanneous "getrandom" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref ptr, ref len, ref flags] = check_arg_count(args)?; getrandom(this, ptr, len, flags, dest)?; } "sched_getaffinity" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref pid, ref cpusetsize, ref mask] = check_arg_count(args)?; this.read_scalar(pid)?.to_i32()?; this.read_scalar(cpusetsize)?.to_machine_usize(this)?; @@ -209,15 +210,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref _thread, ref _attr] = check_arg_count(args)?; this.write_null(dest)?; } - _ => throw_unsup_format!("can't call foreign function: {}", link_name), + _ => return Ok(EmulateByNameResult::NotSupported), }; - Ok(true) + Ok(EmulateByNameResult::NeedsJumping) } } diff --git a/src/shims/posix/macos/foreign_items.rs b/src/shims/posix/macos/foreign_items.rs index 9a7d3be1eb..d54e1bfbe8 100644 --- a/src/shims/posix/macos/foreign_items.rs +++ b/src/shims/posix/macos/foreign_items.rs @@ -2,7 +2,8 @@ use rustc_middle::mir; use rustc_target::spec::abi::Abi; use crate::*; -use helpers::{check_abi, check_arg_count}; +use helpers::check_arg_count; +use shims::foreign_items::EmulateByNameResult; use shims::posix::fs::EvalContextExt as _; use shims::posix::thread::EvalContextExt as _; @@ -15,13 +16,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, _ret: mir::BasicBlock, - ) -> InterpResult<'tcx, bool> { + ) -> InterpResult<'tcx, EmulateByNameResult> { let this = self.eval_context_mut(); match link_name { // errno "__error" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[] = check_arg_count(args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?; @@ -29,43 +30,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // File related shims "close" | "close$NOCANCEL" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref result] = check_arg_count(args)?; let result = this.close(result)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "stat" | "stat$INODE64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref path, ref buf] = check_arg_count(args)?; let result = this.macos_stat(path, buf)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "lstat" | "lstat$INODE64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref path, ref buf] = check_arg_count(args)?; let result = this.macos_lstat(path, buf)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "fstat" | "fstat$INODE64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd, ref buf] = check_arg_count(args)?; let result = this.macos_fstat(fd, buf)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "opendir" | "opendir$INODE64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref name] = check_arg_count(args)?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "readdir_r" | "readdir_r$INODE64" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref dirp, ref entry, ref result] = check_arg_count(args)?; let result = this.macos_readdir_r(dirp, entry, result)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "ftruncate" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref fd, ref length] = check_arg_count(args)?; let result = this.ftruncate64(fd, length)?; this.write_scalar(Scalar::from_i32(result), dest)?; @@ -73,27 +74,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Environment related shims "_NSGetEnviron" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[] = check_arg_count(args)?; this.write_scalar(this.machine.env_vars.environ.unwrap().ptr, dest)?; } // Time related shims "gettimeofday" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref tv, ref tz] = check_arg_count(args)?; let result = this.gettimeofday(tv, tz)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "mach_absolute_time" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[] = check_arg_count(args)?; let result = this.mach_absolute_time()?; this.write_scalar(Scalar::from_u64(result), dest)?; } "mach_timebase_info" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref info] = check_arg_count(args)?; let result = this.mach_timebase_info(info)?; this.write_scalar(Scalar::from_i32(result), dest)?; @@ -101,19 +102,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Access to command-line arguments "_NSGetArgc" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[] = check_arg_count(args)?; this.write_scalar(this.machine.argc.expect("machine must be initialized"), dest)?; } "_NSGetArgv" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[] = check_arg_count(args)?; this.write_scalar(this.machine.argv.expect("machine must be initialized"), dest)?; } // Thread-local storage "_tlv_atexit" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref dtor, ref data] = check_arg_count(args)?; let dtor = this.read_scalar(dtor)?.check_init()?; let dtor = this.memory.get_fn(dtor)?.as_instance()?; @@ -124,14 +125,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Querying system information "pthread_get_stackaddr_np" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref thread] = check_arg_count(args)?; this.read_scalar(thread)?.to_machine_usize(this)?; let stack_addr = Scalar::from_uint(STACK_ADDR, this.pointer_size()); this.write_scalar(stack_addr, dest)?; } "pthread_get_stacksize_np" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref thread] = check_arg_count(args)?; this.read_scalar(thread)?.to_machine_usize(this)?; let stack_size = Scalar::from_uint(STACK_SIZE, this.pointer_size()); @@ -140,7 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Threading "pthread_setname_np" => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; let &[ref name] = check_arg_count(args)?; let name = this.read_scalar(name)?.check_init()?; this.pthread_setname_np(name)?; @@ -149,16 +150,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "mmap" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - check_abi(abi, Abi::C { unwind: false })?; + this.check_abi(abi, Abi::C { unwind: false })?; // This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value. let &[ref addr, _, _, _, _, _] = check_arg_count(args)?; let addr = this.read_scalar(addr)?.check_init()?; this.write_scalar(addr, dest)?; } - _ => throw_unsup_format!("can't call foreign function: {}", link_name), + _ => return Ok(EmulateByNameResult::NotSupported), }; - Ok(true) + Ok(EmulateByNameResult::NeedsJumping) } } diff --git a/src/shims/windows/dlsym.rs b/src/shims/windows/dlsym.rs index ace6c4674a..325100bdb3 100644 --- a/src/shims/windows/dlsym.rs +++ b/src/shims/windows/dlsym.rs @@ -2,7 +2,6 @@ use rustc_middle::mir; use rustc_target::spec::abi::Abi; use crate::*; -use helpers::check_abi; #[derive(Debug, Copy, Clone)] pub enum Dlsym {} @@ -31,7 +30,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let (_dest, _ret) = ret.expect("we don't support any diverging dlsym"); assert!(this.tcx.sess.target.os == "windows"); - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; match dlsym {} } diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index b246ccc33c..3c7451352d 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -5,7 +5,8 @@ use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use crate::*; -use helpers::{check_abi, check_arg_count}; +use helpers::check_arg_count; +use shims::foreign_items::EmulateByNameResult; use shims::windows::sync::EvalContextExt as _; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} @@ -17,7 +18,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, _ret: mir::BasicBlock, - ) -> InterpResult<'tcx, bool> { + ) -> InterpResult<'tcx, EmulateByNameResult> { let this = self.eval_context_mut(); // Windows API stubs. @@ -28,37 +29,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match link_name { // Environment related shims "GetEnvironmentVariableW" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref name, ref buf, ref size] = check_arg_count(args)?; let result = this.GetEnvironmentVariableW(name, buf, size)?; this.write_scalar(Scalar::from_u32(result), dest)?; } "SetEnvironmentVariableW" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref name, ref value] = check_arg_count(args)?; let result = this.SetEnvironmentVariableW(name, value)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "GetEnvironmentStringsW" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[] = check_arg_count(args)?; let result = this.GetEnvironmentStringsW()?; this.write_scalar(result, dest)?; } "FreeEnvironmentStringsW" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref env_block] = check_arg_count(args)?; let result = this.FreeEnvironmentStringsW(env_block)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "GetCurrentDirectoryW" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref size, ref buf] = check_arg_count(args)?; let result = this.GetCurrentDirectoryW(size, buf)?; this.write_scalar(Scalar::from_u32(result), dest)?; } "SetCurrentDirectoryW" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref path] = check_arg_count(args)?; let result = this.SetCurrentDirectoryW(path)?; this.write_scalar(Scalar::from_i32(result), dest)?; @@ -66,7 +67,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // File related shims "GetStdHandle" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref which] = check_arg_count(args)?; let which = this.read_scalar(which)?.to_i32()?; // We just make this the identity function, so we know later in `WriteFile` @@ -74,7 +75,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?; } "WriteFile" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref handle, ref buf, ref n, ref written_ptr, ref overlapped] = check_arg_count(args)?; this.read_scalar(overlapped)?.to_machine_usize(this)?; // this is a poiner, that we ignore @@ -110,7 +111,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Allocation "HeapAlloc" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref handle, ref flags, ref size] = check_arg_count(args)?; this.read_scalar(handle)?.to_machine_isize(this)?; let flags = this.read_scalar(flags)?.to_u32()?; @@ -120,7 +121,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(res, dest)?; } "HeapFree" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref handle, ref flags, ref ptr] = check_arg_count(args)?; this.read_scalar(handle)?.to_machine_isize(this)?; this.read_scalar(flags)?.to_u32()?; @@ -129,7 +130,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_i32(1), dest)?; } "HeapReAlloc" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref handle, ref flags, ref ptr, ref size] = check_arg_count(args)?; this.read_scalar(handle)?.to_machine_isize(this)?; this.read_scalar(flags)?.to_u32()?; @@ -141,13 +142,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // errno "SetLastError" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref error] = check_arg_count(args)?; let error = this.read_scalar(error)?.check_init()?; this.set_last_error(error)?; } "GetLastError" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[] = check_arg_count(args)?; let last_error = this.get_last_error()?; this.write_scalar(last_error, dest)?; @@ -155,7 +156,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Querying system information "GetSystemInfo" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref system_info] = check_arg_count(args)?; let system_info = this.deref_operand(system_info)?; // Initialize with `0`. @@ -171,7 +172,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Thread-local storage "TlsAlloc" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; // This just creates a key; Windows does not natively support TLS destructors. // Create key and return it. @@ -180,7 +181,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; } "TlsGetValue" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref key] = check_arg_count(args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.get_active_thread(); @@ -188,7 +189,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(ptr, dest)?; } "TlsSetValue" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref key, ref new_ptr] = check_arg_count(args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.get_active_thread(); @@ -201,7 +202,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Access to command-line arguments "GetCommandLineW" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[] = check_arg_count(args)?; this.write_scalar( this.machine.cmd_line.expect("machine must be initialized"), @@ -211,20 +212,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Time related shims "GetSystemTimeAsFileTime" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; #[allow(non_snake_case)] let &[ref LPFILETIME] = check_arg_count(args)?; this.GetSystemTimeAsFileTime(LPFILETIME)?; } "QueryPerformanceCounter" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; #[allow(non_snake_case)] let &[ref lpPerformanceCount] = check_arg_count(args)?; let result = this.QueryPerformanceCounter(lpPerformanceCount)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "QueryPerformanceFrequency" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; #[allow(non_snake_case)] let &[ref lpFrequency] = check_arg_count(args)?; let result = this.QueryPerformanceFrequency(lpFrequency)?; @@ -233,33 +234,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Synchronization primitives "AcquireSRWLockExclusive" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref ptr] = check_arg_count(args)?; this.AcquireSRWLockExclusive(ptr)?; } "ReleaseSRWLockExclusive" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref ptr] = check_arg_count(args)?; this.ReleaseSRWLockExclusive(ptr)?; } "TryAcquireSRWLockExclusive" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref ptr] = check_arg_count(args)?; let ret = this.TryAcquireSRWLockExclusive(ptr)?; this.write_scalar(Scalar::from_u8(ret), dest)?; } "AcquireSRWLockShared" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref ptr] = check_arg_count(args)?; this.AcquireSRWLockShared(ptr)?; } "ReleaseSRWLockShared" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref ptr] = check_arg_count(args)?; this.ReleaseSRWLockShared(ptr)?; } "TryAcquireSRWLockShared" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref ptr] = check_arg_count(args)?; let ret = this.TryAcquireSRWLockShared(ptr)?; this.write_scalar(Scalar::from_u8(ret), dest)?; @@ -267,7 +268,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Dynamic symbol loading "GetProcAddress" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; #[allow(non_snake_case)] let &[ref hModule, ref lpProcName] = check_arg_count(args)?; this.read_scalar(hModule)?.to_machine_isize(this)?; @@ -283,7 +284,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Miscellaneous "SystemFunction036" => { // This is really 'RtlGenRandom'. - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref ptr, ref len] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let len = this.read_scalar(len)?.to_u32()?; @@ -291,7 +292,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_bool(true), dest)?; } "BCryptGenRandom" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[ref algorithm, ref ptr, ref len, ref flags] = check_arg_count(args)?; let algorithm = this.read_scalar(algorithm)?; let ptr = this.read_scalar(ptr)?.check_init()?; @@ -312,7 +313,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; // STATUS_SUCCESS } "GetConsoleScreenBufferInfo" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; // `term` needs this, so we fake it. let &[ref console, ref buffer_info] = check_arg_count(args)?; this.read_scalar(console)?.to_machine_isize(this)?; @@ -322,7 +323,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } "GetConsoleMode" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; // Windows "isatty" (in libtest) needs this, so we fake it. let &[ref console, ref mode] = check_arg_count(args)?; this.read_scalar(console)?.to_machine_isize(this)?; @@ -332,7 +333,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } "SwitchToThread" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[] = check_arg_count(args)?; // Note that once Miri supports concurrency, this will need to return a nonzero // value if this call does result in switching to another thread. @@ -341,7 +342,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Better error for attempts to create a thread "CreateThread" => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; throw_unsup_format!("Miri does not support concurrency on Windows"); } @@ -350,7 +351,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "GetProcessHeap" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; let &[] = check_arg_count(args)?; // Just fake a HANDLE this.write_scalar(Scalar::from_machine_isize(1, this), dest)?; @@ -358,7 +359,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; #[allow(non_snake_case)] let &[ref _hConsoleOutput, ref _wAttribute] = check_arg_count(args)?; // Pretend these does not exist / nothing happened, by returning zero. @@ -367,7 +368,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "AddVectoredExceptionHandler" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; #[allow(non_snake_case)] let &[ref _First, ref _Handler] = check_arg_count(args)?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. @@ -376,7 +377,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "SetThreadStackGuarantee" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; #[allow(non_snake_case)] let &[_StackSizeInBytes] = check_arg_count(args)?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. @@ -388,7 +389,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "DeleteCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; #[allow(non_snake_case)] let &[ref _lpCriticalSection] = check_arg_count(args)?; assert_eq!( @@ -403,7 +404,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "TryEnterCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { - check_abi(abi, Abi::System { unwind: false })?; + this.check_abi(abi, Abi::System { unwind: false })?; #[allow(non_snake_case)] let &[ref _lpCriticalSection] = check_arg_count(args)?; assert_eq!( @@ -415,9 +416,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_i32(1), dest)?; } - _ => throw_unsup_format!("can't call foreign function: {}", link_name), + _ => return Ok(EmulateByNameResult::NotSupported), } - Ok(true) + Ok(EmulateByNameResult::NeedsJumping) } } diff --git a/test-cargo-miri/Cargo.lock b/test-cargo-miri/Cargo.lock index 403b932793..9a9fa4797b 100644 --- a/test-cargo-miri/Cargo.lock +++ b/test-cargo-miri/Cargo.lock @@ -14,6 +14,7 @@ version = "0.1.0" dependencies = [ "byteorder", "cdylib", + "exported_symbol", "getrandom 0.1.16", "getrandom 0.2.2", "issue_1567", @@ -37,6 +38,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "exported_symbol" +version = "0.1.0" +dependencies = [ + "exported_symbol_dep", +] + +[[package]] +name = "exported_symbol_dep" +version = "0.1.0" + [[package]] name = "getrandom" version = "0.1.16" diff --git a/test-cargo-miri/Cargo.toml b/test-cargo-miri/Cargo.toml index 4dbe1aeff2..cf557bd60e 100644 --- a/test-cargo-miri/Cargo.toml +++ b/test-cargo-miri/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["subcrate", "issue-1567"] +members = ["subcrate", "issue-1567", "exported-symbol-dep"] [package] name = "cargo-miri-test" @@ -10,6 +10,7 @@ edition = "2018" [dependencies] byteorder = "1.0" cdylib = { path = "cdylib" } +exported_symbol = { path = "exported-symbol" } issue_1567 = { path = "issue-1567" } issue_1691 = { path = "issue-1691" } issue_1705 = { path = "issue-1705" } diff --git a/test-cargo-miri/exported-symbol-dep/Cargo.toml b/test-cargo-miri/exported-symbol-dep/Cargo.toml new file mode 100644 index 0000000000..00c41172c3 --- /dev/null +++ b/test-cargo-miri/exported-symbol-dep/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "exported_symbol_dep" +version = "0.1.0" +authors = ["Miri Team"] +edition = "2018" diff --git a/test-cargo-miri/exported-symbol-dep/src/lib.rs b/test-cargo-miri/exported-symbol-dep/src/lib.rs new file mode 100644 index 0000000000..4cc18fb9b2 --- /dev/null +++ b/test-cargo-miri/exported-symbol-dep/src/lib.rs @@ -0,0 +1,4 @@ +#[no_mangle] +fn exported_symbol() -> i32 { + 123456 +} diff --git a/test-cargo-miri/exported-symbol/Cargo.toml b/test-cargo-miri/exported-symbol/Cargo.toml new file mode 100644 index 0000000000..7c01be1a85 --- /dev/null +++ b/test-cargo-miri/exported-symbol/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "exported_symbol" +version = "0.1.0" +authors = ["Miri Team"] +edition = "2018" + +[dependencies] +# This will become a transitive dependency of doctests in `test-cargo-miri/src/lib.rs`, +# and the purpose of the test is to make sure Miri can find a `#[no_mangle]` function in a +# transitive dependency like `exported_symbol_dep`. +exported_symbol_dep = { path = "../exported-symbol-dep" } diff --git a/test-cargo-miri/exported-symbol/src/lib.rs b/test-cargo-miri/exported-symbol/src/lib.rs new file mode 100644 index 0000000000..de55eb2a1a --- /dev/null +++ b/test-cargo-miri/exported-symbol/src/lib.rs @@ -0,0 +1 @@ +extern crate exported_symbol_dep; diff --git a/test-cargo-miri/src/lib.rs b/test-cargo-miri/src/lib.rs index 9d8eb067d6..6d9158c54e 100644 --- a/test-cargo-miri/src/lib.rs +++ b/test-cargo-miri/src/lib.rs @@ -8,6 +8,7 @@ /// ```rust,compile_fail /// assert!(cargo_miri_test::make_true() == 5); /// ``` +#[no_mangle] pub fn make_true() -> bool { issue_1567::use_the_dependency(); issue_1705::use_the_dependency(); diff --git a/test-cargo-miri/src/main.rs b/test-cargo-miri/src/main.rs index 16b173b287..a5669ef308 100644 --- a/test-cargo-miri/src/main.rs +++ b/test-cargo-miri/src/main.rs @@ -57,4 +57,20 @@ mod test { assert_ne!(x as usize, y); assert_ne!(y as u128, z); } + + #[test] + fn exported_symbol() { + extern crate cargo_miri_test; + extern crate exported_symbol; + // Test calling exported symbols in (transitive) dependencies. + // Repeat calls to make sure the `Instance` cache is not broken. + for _ in 0..3 { + extern "Rust" { + fn exported_symbol() -> i32; + fn make_true() -> bool; + } + assert_eq!(unsafe { exported_symbol() }, 123456); + assert!(unsafe { make_true() }); + } + } } diff --git a/test-cargo-miri/test.bin-target.stdout.ref b/test-cargo-miri/test.bin-target.stdout.ref index 4caa30a7f0..62cfd1d37a 100644 --- a/test-cargo-miri/test.bin-target.stdout.ref +++ b/test-cargo-miri/test.bin-target.stdout.ref @@ -1,6 +1,7 @@ -running 1 test +running 2 tests +test test::exported_symbol ... ok test test::rng ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out diff --git a/test-cargo-miri/test.cross-target.stdout.ref b/test-cargo-miri/test.cross-target.stdout.ref index e36abd8706..aa4a5839b1 100644 --- a/test-cargo-miri/test.cross-target.stdout.ref +++ b/test-cargo-miri/test.cross-target.stdout.ref @@ -1,7 +1,7 @@ -running 1 test -. -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +running 2 tests +.. +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out imported main diff --git a/test-cargo-miri/test.default.stdout.ref b/test-cargo-miri/test.default.stdout.ref index 470e5b36fc..6c7d284a84 100644 --- a/test-cargo-miri/test.default.stdout.ref +++ b/test-cargo-miri/test.default.stdout.ref @@ -1,7 +1,7 @@ -running 1 test -. -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +running 2 tests +.. +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out imported main diff --git a/test-cargo-miri/test.filter.cross-target.stdout.ref b/test-cargo-miri/test.filter.cross-target.stdout.ref index fb722c5e71..b38aac9aa8 100644 --- a/test-cargo-miri/test.filter.cross-target.stdout.ref +++ b/test-cargo-miri/test.filter.cross-target.stdout.ref @@ -1,7 +1,7 @@ running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out imported main diff --git a/test-cargo-miri/test.filter.stdout.ref b/test-cargo-miri/test.filter.stdout.ref index cf1b309a12..6b26e17ff8 100644 --- a/test-cargo-miri/test.filter.stdout.ref +++ b/test-cargo-miri/test.filter.stdout.ref @@ -1,7 +1,7 @@ running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out imported main diff --git a/tests/compile-fail/concurrency/unwind_top_of_stack.rs b/tests/compile-fail/concurrency/unwind_top_of_stack.rs index 8f3bb17470..138a43d9d7 100644 --- a/tests/compile-fail/concurrency/unwind_top_of_stack.rs +++ b/tests/compile-fail/concurrency/unwind_top_of_stack.rs @@ -1,10 +1,8 @@ // ignore-windows: Concurrency on Windows is not supported yet. -// error-pattern: calling a function with ABI C-unwind using caller ABI C +// compile-flags: -Zmiri-disable-abi-check +// error-pattern: unwinding past the topmost frame of the stack //! Unwinding past the top frame of a stack is Undefined Behavior. -//! However, it is impossible to do that in pure Rust since one cannot write an unwinding -//! function with `C` ABI... so let's instead test that we are indeed correctly checking -//! the callee ABI in `pthread_create`. #![feature(rustc_private, c_unwind)] diff --git a/tests/compile-fail/check_arg_abi.rs b/tests/compile-fail/function_calls/check_arg_abi.rs similarity index 100% rename from tests/compile-fail/check_arg_abi.rs rename to tests/compile-fail/function_calls/check_arg_abi.rs diff --git a/tests/compile-fail/check_arg_count_too_few_args.rs b/tests/compile-fail/function_calls/check_arg_count_too_few_args.rs similarity index 100% rename from tests/compile-fail/check_arg_count_too_few_args.rs rename to tests/compile-fail/function_calls/check_arg_count_too_few_args.rs diff --git a/tests/compile-fail/check_arg_count_too_many_args.rs b/tests/compile-fail/function_calls/check_arg_count_too_many_args.rs similarity index 100% rename from tests/compile-fail/check_arg_count_too_many_args.rs rename to tests/compile-fail/function_calls/check_arg_count_too_many_args.rs diff --git a/tests/compile-fail/function_calls/check_callback_abi.rs b/tests/compile-fail/function_calls/check_callback_abi.rs new file mode 100644 index 0000000000..e36bfe1c57 --- /dev/null +++ b/tests/compile-fail/function_calls/check_callback_abi.rs @@ -0,0 +1,17 @@ +#![feature(core_intrinsics)] + +extern "C" fn try_fn(_: *mut u8) { + unreachable!(); +} + +fn main() { + unsafe { + // Make sure we check the ABI when Miri itself invokes a function + // as part of a shim implementation. + std::intrinsics::r#try( //~ ERROR calling a function with ABI C using caller ABI Rust + std::mem::transmute::(try_fn), + std::ptr::null_mut(), + |_, _| unreachable!(), + ); + } +} diff --git a/tests/compile-fail/function_calls/exported_symbol_abi_mismatch.rs b/tests/compile-fail/function_calls/exported_symbol_abi_mismatch.rs new file mode 100644 index 0000000000..f92fae5d29 --- /dev/null +++ b/tests/compile-fail/function_calls/exported_symbol_abi_mismatch.rs @@ -0,0 +1,29 @@ +// revisions: no_cache cache fn_ptr + +#[no_mangle] +fn foo() {} + +fn main() { + #[cfg(any(cache, fn_ptr))] + extern "Rust" { + fn foo(); + } + + #[cfg(fn_ptr)] + unsafe { std::mem::transmute::(foo)() } + //[fn_ptr]~^ ERROR calling a function with ABI Rust using caller ABI C + + // `Instance` caching should not suppress ABI check. + #[cfg(cache)] + unsafe { foo() } + + { + #[cfg_attr(any(cache, fn_ptr), allow(clashing_extern_declarations))] + extern "C" { + fn foo(); + } + unsafe { foo() } + //[no_cache]~^ ERROR calling a function with ABI Rust using caller ABI C + //[cache]~^^ ERROR calling a function with ABI Rust using caller ABI C + } +} diff --git a/tests/compile-fail/function_calls/exported_symbol_bad_unwind1.rs b/tests/compile-fail/function_calls/exported_symbol_bad_unwind1.rs new file mode 100644 index 0000000000..91b0e8fc03 --- /dev/null +++ b/tests/compile-fail/function_calls/exported_symbol_bad_unwind1.rs @@ -0,0 +1,15 @@ +// compile-flags: -Zmiri-disable-abi-check +#![feature(c_unwind)] + +#[no_mangle] +extern "C-unwind" fn unwind() { + panic!(); +} + +fn main() { + extern "C" { + fn unwind(); + } + unsafe { unwind() } + //~^ ERROR unwinding past a stack frame that does not allow unwinding +} diff --git a/tests/compile-fail/function_calls/exported_symbol_bad_unwind2.rs b/tests/compile-fail/function_calls/exported_symbol_bad_unwind2.rs new file mode 100644 index 0000000000..85cca8f1a6 --- /dev/null +++ b/tests/compile-fail/function_calls/exported_symbol_bad_unwind2.rs @@ -0,0 +1,19 @@ +// revisions: extern_block definition both +#![feature(rustc_attrs, c_unwind)] + +#[cfg_attr(any(definition, both), rustc_allocator_nounwind)] +#[no_mangle] +extern "C-unwind" fn nounwind() { + panic!(); +} + +fn main() { + extern "C-unwind" { + #[cfg_attr(any(extern_block, both), rustc_allocator_nounwind)] + fn nounwind(); + } + unsafe { nounwind() } + //[extern_block]~^ ERROR unwinding past a stack frame that does not allow unwinding + //[definition]~^^ ERROR unwinding past a stack frame that does not allow unwinding + //[both]~^^^ ERROR unwinding past a stack frame that does not allow unwinding +} diff --git a/tests/compile-fail/function_calls/exported_symbol_bad_unwind3.rs b/tests/compile-fail/function_calls/exported_symbol_bad_unwind3.rs new file mode 100644 index 0000000000..bbbe677d36 --- /dev/null +++ b/tests/compile-fail/function_calls/exported_symbol_bad_unwind3.rs @@ -0,0 +1,15 @@ +#![feature(unwind_attributes)] + +#[unwind(allowed)] +#[no_mangle] +extern "C" fn unwind() { + panic!(); +} + +fn main() { + extern "C" { + fn unwind(); + } + unsafe { unwind() } + //~^ ERROR unwinding past a stack frame that does not allow unwinding +} diff --git a/tests/compile-fail/function_calls/exported_symbol_clashing.rs b/tests/compile-fail/function_calls/exported_symbol_clashing.rs new file mode 100644 index 0000000000..105d98fc10 --- /dev/null +++ b/tests/compile-fail/function_calls/exported_symbol_clashing.rs @@ -0,0 +1,15 @@ +#[no_mangle] +fn foo() {} +//~^ HELP then it's defined here again, in crate `exported_symbol_clashing` + +#[export_name = "foo"] +fn bar() {} +//~^ HELP it's first defined here, in crate `exported_symbol_clashing` + +fn main() { + extern "Rust" { + fn foo(); + } + unsafe { foo() } + //~^ ERROR multiple definitions of symbol `foo` +} diff --git a/tests/compile-fail/function_calls/exported_symbol_wrong_arguments.rs b/tests/compile-fail/function_calls/exported_symbol_wrong_arguments.rs new file mode 100644 index 0000000000..8fb364bb9b --- /dev/null +++ b/tests/compile-fail/function_calls/exported_symbol_wrong_arguments.rs @@ -0,0 +1,9 @@ +#[no_mangle] +fn foo() {} + +fn main() { + extern "Rust" { + fn foo(_: i32); + } + unsafe { foo(1) } //~ ERROR calling a function with more arguments than it expected +} diff --git a/tests/compile-fail/function_calls/exported_symbol_wrong_type.rs b/tests/compile-fail/function_calls/exported_symbol_wrong_type.rs new file mode 100644 index 0000000000..3ffd506c94 --- /dev/null +++ b/tests/compile-fail/function_calls/exported_symbol_wrong_type.rs @@ -0,0 +1,9 @@ +#[no_mangle] +static FOO: () = (); + +fn main() { + extern "C" { + fn FOO(); + } + unsafe { FOO() } //~ ERROR attempt to call an exported symbol that is not defined as a function +} diff --git a/tests/compile-fail/panic/bad_miri_start_panic.rs b/tests/compile-fail/panic/bad_miri_start_panic.rs new file mode 100644 index 0000000000..f77f892abc --- /dev/null +++ b/tests/compile-fail/panic/bad_miri_start_panic.rs @@ -0,0 +1,10 @@ +// compile-flags: -Zmiri-disable-abi-check + +extern "C" { + fn miri_start_panic(payload: *mut u8) -> !; +} + +fn main() { + unsafe { miri_start_panic(&mut 0) } + //~^ ERROR unwinding past a stack frame that does not allow unwinding +} diff --git a/tests/run-pass/function_calls/disable_abi_check.rs b/tests/run-pass/function_calls/disable_abi_check.rs new file mode 100644 index 0000000000..1f85547413 --- /dev/null +++ b/tests/run-pass/function_calls/disable_abi_check.rs @@ -0,0 +1,24 @@ +// compile-flags: -Zmiri-disable-abi-check +#![feature(core_intrinsics)] + +fn main() { + fn foo() {} + + extern "C" fn try_fn(ptr: *mut u8) { + assert!(ptr.is_null()); + } + + extern "Rust" { + fn malloc(size: usize) -> *mut std::ffi::c_void; + } + + unsafe { + let _ = malloc(0); + std::mem::transmute::(foo)(); + std::intrinsics::r#try( + std::mem::transmute::(try_fn), + std::ptr::null_mut(), + |_, _| unreachable!(), + ); + } +} diff --git a/tests/run-pass/function_calls/exported_symbol.rs b/tests/run-pass/function_calls/exported_symbol.rs new file mode 100644 index 0000000000..96bf8170c6 --- /dev/null +++ b/tests/run-pass/function_calls/exported_symbol.rs @@ -0,0 +1,73 @@ +#![feature(rustc_attrs)] + +#[no_mangle] +extern "C" fn foo() -> i32 { + -1 +} + +#[export_name = "bar"] +fn bar() -> i32 { + -2 +} + +#[rustc_std_internal_symbol] +fn baz() -> i32 { + -3 +} + +// Make sure shims take precedence. +#[no_mangle] +extern "C" fn exit(_: i32) -> ! { + unreachable!() +} + +#[no_mangle] +extern "C" fn ExitProcess(_: u32) -> ! { + unreachable!() +} + +fn main() { + // Repeat calls to make sure the `Instance` cache is not broken. + for _ in 0..3 { + extern "C" { + fn foo() -> i32; + } + + assert_eq!(unsafe { foo() }, -1); + + extern "Rust" { + fn bar() -> i32; + fn baz() -> i32; + } + + assert_eq!(unsafe { bar() }, -2); + assert_eq!(unsafe { baz() }, -3); + + #[allow(clashing_extern_declarations)] + { + extern "Rust" { + fn foo() -> i32; + } + + assert_eq!( + unsafe { + std::mem::transmute:: i32, unsafe extern "C" fn() -> i32>(foo)() + }, + -1 + ); + + extern "C" { + fn bar() -> i32; + fn baz() -> i32; + } + + unsafe { + let transmute = |f| { + std::mem::transmute:: i32, unsafe fn() -> i32>(f) + }; + assert_eq!(transmute(bar)(), -2); + assert_eq!(transmute(baz)(), -3); + } + } + } +} diff --git a/tests/run-pass/function_calls/exported_symbol_good_unwind.rs b/tests/run-pass/function_calls/exported_symbol_good_unwind.rs new file mode 100644 index 0000000000..3dd3b8f22d --- /dev/null +++ b/tests/run-pass/function_calls/exported_symbol_good_unwind.rs @@ -0,0 +1,49 @@ +// Make sure the workaround for "crate ... required to be available in rlib format, but was not +// found in this form" errors works without `-C prefer-dynamic` (`panic!` calls foreign function +// `__rust_start_panic`). +// no-prefer-dynamic +#![feature(c_unwind, unboxed_closures, unwind_attributes)] + +use std::panic; + +#[no_mangle] +#[unwind(allowed)] +extern "C" fn good_unwind_allowed() { + panic!(); +} + +#[no_mangle] +extern "C-unwind" fn good_unwind_c() { + panic!(); +} + +#[no_mangle] +fn good_unwind_rust() { + panic!(); +} + +// Diverging function calls are on a different code path. +#[no_mangle] +extern "rust-call" fn good_unwind_rust_call(_: ()) -> ! { + panic!(); +} + +fn main() -> ! { + extern "C" { + #[unwind(allowed)] + fn good_unwind_allowed(); + } + panic::catch_unwind(|| unsafe { good_unwind_allowed() }).unwrap_err(); + extern "C-unwind" { + fn good_unwind_c(); + } + panic::catch_unwind(|| unsafe { good_unwind_c() }).unwrap_err(); + extern "Rust" { + fn good_unwind_rust(); + } + panic::catch_unwind(|| unsafe { good_unwind_rust() }).unwrap_err(); + extern "rust-call" { + fn good_unwind_rust_call(_: ()) -> !; + } + unsafe { good_unwind_rust_call(()) } +} diff --git a/tests/run-pass/function_calls/exported_symbol_good_unwind.stderr b/tests/run-pass/function_calls/exported_symbol_good_unwind.stderr new file mode 100644 index 0000000000..3347f00b65 --- /dev/null +++ b/tests/run-pass/function_calls/exported_symbol_good_unwind.stderr @@ -0,0 +1,5 @@ +thread 'main' panicked at 'explicit panic', $DIR/exported_symbol_good_unwind.rs:12:5 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +thread 'main' panicked at 'explicit panic', $DIR/exported_symbol_good_unwind.rs:17:5 +thread 'main' panicked at 'explicit panic', $DIR/exported_symbol_good_unwind.rs:22:5 +thread 'main' panicked at 'explicit panic', $DIR/exported_symbol_good_unwind.rs:28:5 diff --git a/tests/run-pass/function_calls/exported_symbol_unwind_allowed.rs b/tests/run-pass/function_calls/exported_symbol_unwind_allowed.rs new file mode 100644 index 0000000000..0e4ec5739a --- /dev/null +++ b/tests/run-pass/function_calls/exported_symbol_unwind_allowed.rs @@ -0,0 +1,15 @@ +// compile-flags: -Zmiri-disable-abi-check +#![feature(unwind_attributes, c_unwind)] + +#[no_mangle] +extern "C-unwind" fn unwind() { + panic!(); +} + +fn main() { + extern "C" { + #[unwind(allowed)] + fn unwind(); + } + unsafe { unwind() } +} diff --git a/tests/run-pass/function_calls/exported_symbol_unwind_allowed.stderr b/tests/run-pass/function_calls/exported_symbol_unwind_allowed.stderr new file mode 100644 index 0000000000..14ee43950c --- /dev/null +++ b/tests/run-pass/function_calls/exported_symbol_unwind_allowed.stderr @@ -0,0 +1,2 @@ +thread 'main' panicked at 'explicit panic', $DIR/exported_symbol_unwind_allowed.rs:6:5 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace