Skip to content

Commit

Permalink
Auto merge of #1776 - hyd-dev:1170, r=RalfJung
Browse files Browse the repository at this point in the history
Implement calls to exported symbols (#1170)

Closes #1170.
  • Loading branch information
bors committed Jun 3, 2021
2 parents 44122f9 + 57e4f1d commit 28717a0
Show file tree
Hide file tree
Showing 46 changed files with 828 additions and 284 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**.
Expand Down Expand Up @@ -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
Expand Down
83 changes: 73 additions & 10 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,52 @@
#![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;
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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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;
}
Expand Down
64 changes: 43 additions & 21 deletions src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand All @@ -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 {
Expand All @@ -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),
}
}
}
Expand Down Expand Up @@ -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![],
};
Expand All @@ -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)
}
};

Expand All @@ -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(),
Expand Down Expand Up @@ -159,7 +177,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);
Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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![],
Expand Down
27 changes: 13 additions & 14 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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.
Expand All @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]

Expand All @@ -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;

Expand Down
Loading

0 comments on commit 28717a0

Please sign in to comment.