1
Fork 0

Auto merge of #74681 - RalfJung:miri-extern-fn, r=oli-obk

Miri: use extern fn to expose interpreter operations to program; fix leak checker on Windows

This PR realizes an idea that @oli-obk has been suggesting for a while: to use Miri-specific `extern` functions to provide some extra capabilities to the program. Initially, we have two of these methods, which libstd itself needs:
* `miri_start_panic`, which replaces the intrinsic of the same name (mostly for consistency, to avoid having multiple mechanisms for Miri-specific functionality).
* `miri_static_root`, which adds an allocation to a list of static "roots" that Miri considers as not having leaked (including all memory reachable through them). This is needed for https://github.com/rust-lang/miri/issues/1302.

We use `extern` functions instead of intrinsics for this so that user code can more easily call these Miri hoolks -- e.g. `miri_static_root` should be useful for https://github.com/rust-lang/miri/issues/1318.

The Miri side of this is at https://github.com/rust-lang/miri/pull/1485.

r? @oli-obk
This commit is contained in:
bors 2020-07-24 22:00:23 +00:00
commit 5ef299eb98
7 changed files with 27 additions and 23 deletions

View file

@ -1947,14 +1947,6 @@ extern "rust-intrinsic" {
#[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")]
pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize; pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
/// Internal hook used by Miri to implement unwinding.
/// ICEs when encountered during non-Miri codegen.
///
/// The `payload` ptr here will be exactly the one `do_catch` gets passed by `try`.
///
/// Perma-unstable: do not use.
pub fn miri_start_panic(payload: *mut u8) -> !;
/// Internal placeholder for injecting code coverage counters when the "instrument-coverage" /// Internal placeholder for injecting code coverage counters when the "instrument-coverage"
/// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code /// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code
/// generation. /// generation.

View file

@ -6,11 +6,16 @@ use core::any::Any;
// Must be pointer-sized. // Must be pointer-sized.
type Payload = Box<Box<dyn Any + Send>>; type Payload = Box<Box<dyn Any + Send>>;
extern "Rust" {
/// Miri-provided extern function to begin unwinding.
fn miri_start_panic(payload: *mut u8) -> !;
}
pub unsafe fn panic(payload: Box<dyn Any + Send>) -> u32 { pub unsafe fn panic(payload: Box<dyn Any + Send>) -> u32 {
// The payload we pass to `miri_start_panic` will be exactly the argument we get // The payload we pass to `miri_start_panic` will be exactly the argument we get
// in `cleanup` below. So we just box it up once, to get something pointer-sized. // in `cleanup` below. So we just box it up once, to get something pointer-sized.
let payload_box: Payload = Box::new(payload); let payload_box: Payload = Box::new(payload);
core::intrinsics::miri_start_panic(Box::into_raw(payload_box) as *mut u8) miri_start_panic(Box::into_raw(payload_box) as *mut u8)
} }
pub unsafe fn cleanup(payload_box: *mut u8) -> Box<dyn Any + Send> { pub unsafe fn cleanup(payload_box: *mut u8) -> Box<dyn Any + Send> {

View file

@ -606,11 +606,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return; return;
} }
// For normal codegen, this Miri-specific intrinsic should never occur.
if intrinsic == Some(sym::miri_start_panic) {
bug!("`miri_start_panic` should never end up in compiled code");
}
if self.codegen_panic_intrinsic( if self.codegen_panic_intrinsic(
&helper, &helper,
&mut bx, &mut bx,

View file

@ -716,7 +716,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
} }
} }
pub fn leak_report(&self) -> usize { /// Print leaked memory. Allocations reachable from `static_roots` or a `Global` allocation
/// are not considered leaked. Leaks whose kind `may_leak()` returns true are not reported.
pub fn leak_report(&self, static_roots: &[AllocId]) -> usize {
// Collect the set of allocations that are *reachable* from `Global` allocations. // Collect the set of allocations that are *reachable* from `Global` allocations.
let reachable = { let reachable = {
let mut reachable = FxHashSet::default(); let mut reachable = FxHashSet::default();
@ -724,6 +726,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
let mut todo: Vec<_> = self.alloc_map.filter_map_collect(move |&id, &(kind, _)| { let mut todo: Vec<_> = self.alloc_map.filter_map_collect(move |&id, &(kind, _)| {
if Some(kind) == global_kind { Some(id) } else { None } if Some(kind) == global_kind { Some(id) } else { None }
}); });
todo.extend(static_roots);
while let Some(id) = todo.pop() { while let Some(id) = todo.pop() {
if reachable.insert(id) { if reachable.insert(id) {
// This is a new allocation, add its relocations to `todo`. // This is a new allocation, add its relocations to `todo`.

View file

@ -677,7 +677,6 @@ symbols! {
minnumf32, minnumf32,
minnumf64, minnumf64,
mips_target_feature, mips_target_feature,
miri_start_panic,
mmx_target_feature, mmx_target_feature,
module, module,
module_path, module_path,

View file

@ -379,12 +379,6 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
sym::miri_start_panic => {
// FIXME - the relevant types aren't lang items,
// so it's not trivial to check this
return;
}
sym::count_code_region => { sym::count_code_region => {
(0, vec![tcx.types.u64, tcx.types.u32, tcx.types.u32, tcx.types.u32], tcx.mk_unit()) (0, vec![tcx.types.u64, tcx.types.u32, tcx.types.u32, tcx.types.u32], tcx.mk_unit())
} }

View file

@ -110,6 +110,16 @@ struct Node {
next: *mut Node, next: *mut Node,
} }
#[cfg(miri)]
extern "Rust" {
/// Miri-provided extern function to mark the block `ptr` points to as a "root"
/// for some static memory. This memory and everything reachable by it is not
/// considered leaking even if it still exists when the program terminates.
///
/// `ptr` has to point to the beginning of an allocated block.
fn miri_static_root(ptr: *const u8);
}
unsafe fn register_dtor(key: Key, dtor: Dtor) { unsafe fn register_dtor(key: Key, dtor: Dtor) {
let mut node = Box::new(Node { key, dtor, next: ptr::null_mut() }); let mut node = Box::new(Node { key, dtor, next: ptr::null_mut() });
@ -117,7 +127,13 @@ unsafe fn register_dtor(key: Key, dtor: Dtor) {
loop { loop {
node.next = head; node.next = head;
match DTORS.compare_exchange(head, &mut *node, SeqCst, SeqCst) { match DTORS.compare_exchange(head, &mut *node, SeqCst, SeqCst) {
Ok(_) => return mem::forget(node), Ok(_) => {
#[cfg(miri)]
miri_static_root(&*node as *const _ as *const u8);
mem::forget(node);
return;
}
Err(cur) => head = cur, Err(cur) => head = cur,
} }
} }