Implement calls to exported symbols
This commit is contained in:
parent
71f4140550
commit
41f33a64f8
@ -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 affects 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.
|
||||
|
@ -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![],
|
||||
};
|
||||
@ -90,26 +108,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)
|
||||
}
|
||||
};
|
||||
|
||||
@ -118,7 +136,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(),
|
||||
@ -157,7 +175,7 @@ fn report_msg<'tcx>(
|
||||
error: bool,
|
||||
title: &str,
|
||||
span_msg: String,
|
||||
mut helps: Vec<String>,
|
||||
mut helps: Vec<(Option<SpanData>, String)>,
|
||||
stacktrace: &[FrameInfo<'tcx>],
|
||||
) {
|
||||
let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
|
||||
@ -177,9 +195,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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -17,7 +17,7 @@ use rustc_middle::{
|
||||
ty::{
|
||||
self,
|
||||
layout::{LayoutCx, LayoutError, TyAndLayout},
|
||||
TyCtxt,
|
||||
Instance, TyCtxt,
|
||||
},
|
||||
};
|
||||
use rustc_span::def_id::DefId;
|
||||
@ -294,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<String, measureme::StringId>,
|
||||
|
||||
/// Cache of `Instance` exported under the given `Symbol` name.
|
||||
pub(crate) exported_symbols_cache: FxHashMap<Symbol, Instance<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
|
||||
@ -322,6 +325,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
|
||||
static_roots: Vec::new(),
|
||||
profiler,
|
||||
string_cache: Default::default(),
|
||||
exported_symbols_cache: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,19 +6,37 @@ 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};
|
||||
|
||||
/// 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> {
|
||||
/// Returns the minimum alignment for the target architecture for allocations of the given size.
|
||||
@ -108,6 +126,76 @@ 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)),
|
||||
))
|
||||
{
|
||||
// FIXME: Do we need to check `SymbolExportLevel` (the `_` below)?
|
||||
for &(symbol, _) 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 +212,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 = match this.tcx.sess.first_attr_value_str_by_name(&attrs, sym::link_name)
|
||||
{
|
||||
Some(name) => name,
|
||||
None => 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;
|
||||
@ -164,48 +254,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
"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(this, 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 +290,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
|
||||
@ -549,7 +626,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:
|
||||
|
@ -6,6 +6,7 @@ use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::*;
|
||||
use helpers::{check_abi, 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,7 +20,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();
|
||||
|
||||
match link_name {
|
||||
@ -524,6 +525,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
};
|
||||
|
||||
Ok(true)
|
||||
Ok(EmulateByNameResult::NeedsJumping)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::helpers::{check_abi, 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,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();
|
||||
|
||||
match link_name {
|
||||
@ -214,10 +215,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
||||
_ => throw_unsup_format!("can't call foreign function: {}", link_name),
|
||||
_ => return Ok(EmulateByNameResult::NotSupported),
|
||||
};
|
||||
|
||||
Ok(true)
|
||||
Ok(EmulateByNameResult::NeedsJumping)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::*;
|
||||
use helpers::{check_abi, check_arg_count};
|
||||
use shims::foreign_items::EmulateByNameResult;
|
||||
use shims::posix::fs::EvalContextExt as _;
|
||||
use shims::posix::thread::EvalContextExt as _;
|
||||
|
||||
@ -15,7 +16,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();
|
||||
|
||||
match link_name {
|
||||
@ -156,9 +157,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
this.write_scalar(addr, dest)?;
|
||||
}
|
||||
|
||||
_ => throw_unsup_format!("can't call foreign function: {}", link_name),
|
||||
_ => return Ok(EmulateByNameResult::NotSupported),
|
||||
};
|
||||
|
||||
Ok(true)
|
||||
Ok(EmulateByNameResult::NeedsJumping)
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::*;
|
||||
use helpers::{check_abi, 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.
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
12
test-cargo-miri/Cargo.lock
generated
12
test-cargo-miri/Cargo.lock
generated
@ -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"
|
||||
|
@ -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" }
|
||||
|
5
test-cargo-miri/exported-symbol-dep/Cargo.toml
Normal file
5
test-cargo-miri/exported-symbol-dep/Cargo.toml
Normal file
@ -0,0 +1,5 @@
|
||||
[package]
|
||||
name = "exported_symbol_dep"
|
||||
version = "0.1.0"
|
||||
authors = ["Miri Team"]
|
||||
edition = "2018"
|
4
test-cargo-miri/exported-symbol-dep/src/lib.rs
Normal file
4
test-cargo-miri/exported-symbol-dep/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
||||
#[no_mangle]
|
||||
fn exported_symbol() -> i32 {
|
||||
123456
|
||||
}
|
11
test-cargo-miri/exported-symbol/Cargo.toml
Normal file
11
test-cargo-miri/exported-symbol/Cargo.toml
Normal file
@ -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" }
|
1
test-cargo-miri/exported-symbol/src/lib.rs
Normal file
1
test-cargo-miri/exported-symbol/src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
extern crate exported_symbol_dep;
|
@ -1,6 +1,21 @@
|
||||
extern crate exported_symbol;
|
||||
|
||||
/// Doc-test test
|
||||
/// ```rust
|
||||
/// assert!(cargo_miri_test::make_true());
|
||||
/// // 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() });
|
||||
/// }
|
||||
/// ```
|
||||
/// ```compile_fail
|
||||
/// // Make sure `exported_symbol_dep` is not a direct dependency for doctests.
|
||||
/// use exported_symbol_dep;
|
||||
/// ```
|
||||
/// ```rust,no_run
|
||||
/// assert!(!cargo_miri_test::make_true());
|
||||
@ -8,6 +23,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();
|
||||
|
@ -10,10 +10,11 @@ running 7 tests
|
||||
test result: ok. 6 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
|
||||
|
||||
|
||||
running 3 tests
|
||||
test src/lib.rs - make_true (line 2) ... ok
|
||||
test src/lib.rs - make_true (line 5) ... ok
|
||||
test src/lib.rs - make_true (line 8) ... ok
|
||||
running 4 tests
|
||||
test src/lib.rs - make_true (line 16) ... ok
|
||||
test src/lib.rs - make_true (line 20) ... ok
|
||||
test src/lib.rs - make_true (line 23) ... ok
|
||||
test src/lib.rs - make_true (line 4) ... ok
|
||||
|
||||
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
||||
|
@ -13,5 +13,5 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 6 filtered out
|
||||
|
||||
running 0 tests
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 3 filtered out; finished in $TIME
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in $TIME
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
// revisions: no_cache cache
|
||||
|
||||
#[no_mangle]
|
||||
fn foo() {}
|
||||
|
||||
fn main() {
|
||||
#[cfg(cache)]
|
||||
{
|
||||
// `Instance` caching should not suppress ABI check.
|
||||
extern "Rust" {
|
||||
fn foo();
|
||||
}
|
||||
unsafe { foo() }
|
||||
}
|
||||
#[cfg_attr(cache, allow(clashing_extern_declarations))]
|
||||
extern "C" {
|
||||
fn foo();
|
||||
}
|
||||
unsafe { foo() }
|
||||
//[no_cache]~^ ERROR Undefined Behavior: calling a function with ABI Rust using caller ABI C
|
||||
//[cache]~^^ ERROR Undefined Behavior: calling a function with ABI Rust using caller ABI C
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(c_unwind)] // make sure it doesn't insert abort-on-unwind for the `#[unwind(allowed)]` function
|
||||
|
||||
#[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
|
||||
}
|
@ -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`
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
10
tests/compile-fail/panic/bad_miri_start_panic.rs
Normal file
10
tests/compile-fail/panic/bad_miri_start_panic.rs
Normal file
@ -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
|
||||
}
|
10
tests/compile-fail/panic/rustc_allocator_nounwind.rs
Normal file
10
tests/compile-fail/panic/rustc_allocator_nounwind.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#![feature(rustc_attrs, c_unwind)]
|
||||
|
||||
#[rustc_allocator_nounwind]
|
||||
extern "C-unwind" fn nounwind() {
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
nounwind(); //~ ERROR unwinding past a stack frame that does not allow unwinding
|
||||
}
|
44
tests/run-pass/function_calls/exported_symbol.rs
Normal file
44
tests/run-pass/function_calls/exported_symbol.rs
Normal file
@ -0,0 +1,44 @@
|
||||
#![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);
|
||||
assert_eq!(unsafe { foo() }, -1);
|
||||
extern "Rust" {
|
||||
fn bar() -> i32;
|
||||
fn baz() -> i32;
|
||||
}
|
||||
assert_eq!(unsafe { bar() }, -2);
|
||||
assert_eq!(unsafe { baz() }, -3);
|
||||
}
|
||||
}
|
49
tests/run-pass/function_calls/exported_symbol_good_unwind.rs
Normal file
49
tests/run-pass/function_calls/exported_symbol_good_unwind.rs
Normal file
@ -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
|
||||
// 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(()) }
|
||||
}
|
@ -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
|
@ -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() }
|
||||
}
|
@ -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
|
27
tests/run-pass/panic/good_unwind.rs
Normal file
27
tests/run-pass/panic/good_unwind.rs
Normal file
@ -0,0 +1,27 @@
|
||||
#![feature(c_unwind, unboxed_closures, unwind_attributes)]
|
||||
|
||||
use std::panic;
|
||||
|
||||
extern "C-unwind" fn good_unwind_c() {
|
||||
panic!();
|
||||
}
|
||||
|
||||
#[unwind(allowed)]
|
||||
extern "C" fn good_unwind_allowed() {
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn good_unwind_rust() {
|
||||
panic!();
|
||||
}
|
||||
|
||||
extern "rust-call" fn good_unwind_rust_call(_: ()) {
|
||||
panic!();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
panic::catch_unwind(|| good_unwind_c()).unwrap_err();
|
||||
panic::catch_unwind(|| good_unwind_allowed()).unwrap_err();
|
||||
panic::catch_unwind(|| good_unwind_rust()).unwrap_err();
|
||||
good_unwind_rust_call(());
|
||||
}
|
5
tests/run-pass/panic/good_unwind.stderr
Normal file
5
tests/run-pass/panic/good_unwind.stderr
Normal file
@ -0,0 +1,5 @@
|
||||
thread 'main' panicked at 'explicit panic', $DIR/good_unwind.rs:6:5
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
thread 'main' panicked at 'explicit panic', $DIR/good_unwind.rs:11:5
|
||||
thread 'main' panicked at 'explicit panic', $DIR/good_unwind.rs:15:5
|
||||
thread 'main' panicked at 'explicit panic', $DIR/good_unwind.rs:19:5
|
Loading…
x
Reference in New Issue
Block a user