Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement calls to exported symbols (#1170) #1776

Merged
merged 12 commits into from Jun 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
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, _| {
This conversation was marked as resolved.
Show resolved Hide resolved
// `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]
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
/* 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 @@ -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)
}
};

Expand All @@ -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(),
Expand Down Expand Up @@ -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);
Expand All @@ -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
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