From 4814f9d6e33d0693f493a7d83299e4eeb925a846 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 2 Dec 2019 21:15:58 -0500 Subject: [PATCH 01/11] Add API for capturing backtrace This PR adds two new Miri-defined extern functions: `miri_get_backtrace` and `miri_resolve_frame`, which are documented in the README. Together, they allow obtaining a backtrace for the currently executing program. I've added a test showing how these APIs are used. I've also prepared a companion PR `backtrace-rs`, which will allow `backtrace::Backtrace::new()` to work automatically under Miri. Once these two PRs are merged, we will be able to print backtraces from the normal Rust panic hook (since libstd is now using backtrace-rs). A few notes: * Resolving the backtrace frames is *very* slow - you can actually see each line being printed out one at a time. Some local testing showed that this is not (primrary) caused by resolving a `Span` - it seems to be just Miri being slow. * For the first time, we now interact directly with a user-defined struct (instead of just executing the user-provided MIR that manipulates the struct). To allow for future changes, I've added a 'version' parameter (currently required to be 0). This should allow us to change the `MiriFrame` struct should the need ever arise. * I used the approach suggested by @oli-obk - a returned backtrace pointer consists of a base function allocation, with the 'offset' used to encode the `Span.lo`. This allows losslessly reconstructing the location information in `miri_resolve_frame`. * There are a few quirks on the `backtrace-rs` side: * `backtrace-rs` calls `getcwd()` by default to try to simplify the filename. This results in an isolation error by default, which could be annoying when printing a backtrace from libstd. * `backtrace-rs` tries to remove 'internal' frames (everything between the call to `Backtrace::new()` and the internal API call made by backtrace-rs) by comparing the returned frame pointer value to a Rust function pointer. This doesn't work due to the way we construct the frame pointers passed to the caller. We could attempt to support this kind of comparison, or just add a `#[cfg(miri)]` and ignore the frames ourselves. --- README.md | 26 ++++++ src/shims/foreign_items.rs | 88 ++++++++++++++++++- .../backtrace/bad-backtrace-decl.rs | 13 +++ .../backtrace/bad-backtrace-ptr.rs | 9 ++ .../backtrace/bad-backtrace-version.rs | 9 ++ tests/run-pass/backtrace-api.rs | 24 +++++ tests/run-pass/backtrace-api.stderr | 10 +++ 7 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/backtrace/bad-backtrace-decl.rs create mode 100644 tests/compile-fail/backtrace/bad-backtrace-ptr.rs create mode 100644 tests/compile-fail/backtrace/bad-backtrace-version.rs create mode 100644 tests/run-pass/backtrace-api.rs create mode 100644 tests/run-pass/backtrace-api.stderr diff --git a/README.md b/README.md index 657ea096e6..524c8e10cf 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,32 @@ extern "Rust" { /// `ptr` has to point to the beginning of an allocated block. fn miri_static_root(ptr: *const u8); + /// Miri-provided extern function to obtain a backtrace of the current call stack. + /// This returns a boxed slice of pointers - each pointer is an opaque value + /// that is only useful when passed to `miri_resolve_frame` + fn miri_get_backtrace() -> Box<[*mut ()]>; + + /// Miri-provided extern function to resolve a frame pointer obtained + /// from `miri_get_backtrace`. The `version` argument must be `0`, + /// and `MiriFrame` should be declared as follows: + /// + /// ```rust + /// struct MiriFrame { + /// // The name of the function being executed, encoded in UTF-8 + /// name: Box<[u8]>, + /// // The filename of the function being executed, encoded in UTF-8 + /// filename: Box<[u8]>, + /// // The line number currently being executed in `filename`, starting from '1'. + /// lineno: u32, + /// // The column number currently being executed in `filename`, starting from '1'. + /// colno: u32, + /// } + /// ``` + /// + /// The fields must be declared in exactly the same order as they appear in `MiriFrame` above. + /// This function can be called on any thread (not just the one which obtained `frame`) + fn miri_resolve_frame(version: u8, frame: *mut ()) -> MiriFrame; + /// Miri-provided extern function to begin unwinding with the given payload. /// /// This is internal and unstable and should not be used; we give it here diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 5bcbd797ca..4d1ead8f0f 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -3,10 +3,13 @@ use std::{convert::{TryInto, TryFrom}, iter}; use log::trace; use rustc_hir::def_id::DefId; -use rustc_middle::{mir, ty}; +use rustc_middle::mir; use rustc_target::{abi::{Align, Size}, spec::PanicStrategy}; +use rustc_middle::ty::{self, ParamEnv, TypeAndMut}; +use rustc_ast::ast::Mutability; use rustc_apfloat::Float; use rustc_span::symbol::sym; +use rustc_span::BytePos; use crate::*; use helpers::check_arg_count; @@ -211,6 +214,89 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.machine.static_roots.push(ptr.alloc_id); } + // Obtains a Miri backtrace. See the README for details. + "miri_get_backtrace" => { + let tcx = this.tcx; + let mut data = Vec::new(); + for frame in this.active_thread_stack().iter().rev() { + data.push((frame.instance, frame.current_span().lo())); + } + + let ptrs: Vec<_> = data.into_iter().map(|(instance, pos)| { + let mut fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance)); + fn_ptr.offset = Size::from_bytes(pos.0); + Scalar::Ptr(fn_ptr) + }).collect(); + + let len = ptrs.len(); + + let ptr_ty = tcx.mk_ptr(TypeAndMut { + ty: tcx.types.unit, + mutbl: Mutability::Mut + }); + + let array_ty = tcx.mk_array(ptr_ty, ptrs.len().try_into().unwrap()); + let array_ty_and_env = ParamEnv::empty().and(array_ty); + + // Write pointers into array + let alloc = this.allocate(tcx.layout_of(array_ty_and_env).unwrap(), MiriMemoryKind::Rust.into()); + for (i, ptr) in ptrs.into_iter().enumerate() { + let place = this.mplace_index(alloc, i as u64)?; + this.write_immediate_to_mplace(ptr.into(), place)?; + } + + this.write_immediate(Immediate::new_slice(alloc.ptr.into(), len.try_into().unwrap(), this), dest)?; + } + + // Resolves a Miri backtrace frame. See the README for details. + "miri_resolve_frame" => { + let tcx = this.tcx; + let &[version, ptr] = check_arg_count(args)?; + + let version = this.read_scalar(version)?.to_u8()?; + if version != 0 { + throw_ub_format!("Unknown `miri_resolve_frame` version {}", version); + } + + let ptr = match this.read_scalar(ptr)?.check_init()? { + Scalar::Ptr(ptr) => ptr, + Scalar::Raw { .. } => throw_ub_format!("Expected a pointer in `rust_miri_resolve_frame`, found {:?}", ptr) + }; + + let fn_instance = if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(ptr.alloc_id) { + instance + } else { + throw_ub_format!("Expect function pointer, found {:?}", ptr); + }; + + if dest.layout.layout.fields.count() != 4 { + throw_ub_format!("Bad declaration of miri_resolve_frame - should return a struct with 4 fields"); + } + + let pos = BytePos(ptr.offset.bytes().try_into().unwrap()); + let name = fn_instance.to_string(); + + let lo = tcx.sess.source_map().lookup_char_pos(pos); + + let filename = lo.file.name.to_string(); + let lineno: u32 = lo.line as u32; + // `lo.col` is 0-based - add 1 to make it 1-based for the caller. + let colno: u32 = lo.col.0 as u32 + 1; + + let name_alloc = this.allocate_str(&name, MiriMemoryKind::Rust.into()); + let filename_alloc = this.allocate_str(&filename, MiriMemoryKind::Rust.into()); + let lineno_alloc = Scalar::from_u32(lineno); + let colno_alloc = Scalar::from_u32(colno); + + let dest = this.force_allocation_maybe_sized(dest, MemPlaceMeta::None)?.0; + + this.write_immediate(name_alloc.to_ref(), this.mplace_field(dest, 0)?.into())?; + this.write_immediate(filename_alloc.to_ref(), this.mplace_field(dest, 1)?.into())?; + this.write_scalar(lineno_alloc, this.mplace_field(dest, 2)?.into())?; + this.write_scalar(colno_alloc, this.mplace_field(dest, 3)?.into())?; + } + + // Standard C allocation "malloc" => { let &[size] = check_arg_count(args)?; diff --git a/tests/compile-fail/backtrace/bad-backtrace-decl.rs b/tests/compile-fail/backtrace/bad-backtrace-decl.rs new file mode 100644 index 0000000000..7c250fbbe3 --- /dev/null +++ b/tests/compile-fail/backtrace/bad-backtrace-decl.rs @@ -0,0 +1,13 @@ +extern "Rust" { + fn miri_get_backtrace() -> Box<[*mut ()]>; + fn miri_resolve_frame(version: u8, ptr: *mut ()); +} + +fn main() { + let frames = unsafe { miri_get_backtrace() }; + for frame in frames.into_iter() { + unsafe { + miri_resolve_frame(0, *frame); //~ ERROR Undefined Behavior: Bad declaration of miri_resolve_frame - should return a struct with 4 fields + } + } +} diff --git a/tests/compile-fail/backtrace/bad-backtrace-ptr.rs b/tests/compile-fail/backtrace/bad-backtrace-ptr.rs new file mode 100644 index 0000000000..49b8ac8849 --- /dev/null +++ b/tests/compile-fail/backtrace/bad-backtrace-ptr.rs @@ -0,0 +1,9 @@ +extern "Rust" { + fn miri_resolve_frame(version: u8, ptr: *mut ()); +} + +fn main() { + unsafe { + miri_resolve_frame(0, 0 as *mut _); //~ ERROR Undefined Behavior: Expected a pointer + } +} diff --git a/tests/compile-fail/backtrace/bad-backtrace-version.rs b/tests/compile-fail/backtrace/bad-backtrace-version.rs new file mode 100644 index 0000000000..b0183ca99e --- /dev/null +++ b/tests/compile-fail/backtrace/bad-backtrace-version.rs @@ -0,0 +1,9 @@ +extern "Rust" { + fn miri_resolve_frame(version: u8, ptr: *mut ()); +} + +fn main() { + unsafe { + miri_resolve_frame(1, 0 as *mut _); //~ ERROR Undefined Behavior: Unknown `miri_resolve_frame` version 1 + } +} diff --git a/tests/run-pass/backtrace-api.rs b/tests/run-pass/backtrace-api.rs new file mode 100644 index 0000000000..fa86debf17 --- /dev/null +++ b/tests/run-pass/backtrace-api.rs @@ -0,0 +1,24 @@ +// normalize-stderr-test ".*rustlib" -> "RUSTLIB" + +extern "Rust" { + fn miri_get_backtrace() -> Box<[*mut ()]>; + fn miri_resolve_frame(version: u8, ptr: *mut ()) -> MiriFrame; +} + +#[derive(Debug)] +struct MiriFrame { + name: Box<[u8]>, + filename: Box<[u8]>, + lineno: u32, + colno: u32 +} + +fn main() { + let frames = unsafe { miri_get_backtrace() }; + for frame in frames.into_iter() { + let miri_frame = unsafe { miri_resolve_frame(0, *frame) }; + let name = String::from_utf8(miri_frame.name.into()).unwrap(); + let filename = String::from_utf8(miri_frame.filename.into()).unwrap(); + eprintln!("{}:{}:{} ({})", filename, miri_frame.lineno, miri_frame.colno, name); + } +} diff --git a/tests/run-pass/backtrace-api.stderr b/tests/run-pass/backtrace-api.stderr new file mode 100644 index 0000000000..91a99070eb --- /dev/null +++ b/tests/run-pass/backtrace-api.stderr @@ -0,0 +1,10 @@ +$DIR/backtrace-api.rs:17:27 (main) +RUSTLIB/src/rust/library/core/src/ops/function.rs:227:5 (>::call_once - shim(fn())) +RUSTLIB/src/rust/library/std/src/sys_common/backtrace.rs:137:18 (std::sys_common::backtrace::__rust_begin_short_backtrace::) +RUSTLIB/src/rust/library/std/src/rt.rs:66:18 (std::rt::lang_start::<()>::{{closure}}#0) +RUSTLIB/src/rust/library/core/src/ops/function.rs:259:13 (std::ops::function::impls:: for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once) +RUSTLIB/src/rust/library/std/src/panicking.rs:381:40 (std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>) +RUSTLIB/src/rust/library/std/src/panicking.rs:345:19 (std::panicking::r#try:: i32 + std::marker::Sync + std::panic::RefUnwindSafe>) +RUSTLIB/src/rust/library/std/src/panic.rs:382:14 (std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>) +RUSTLIB/src/rust/library/std/src/rt.rs:51:25 (std::rt::lang_start_internal) +RUSTLIB/src/rust/library/std/src/rt.rs:65:5 (std::rt::lang_start::<()>) From f89e2d21ae5640f58b7968263add5776a7618727 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 22 Sep 2020 10:58:18 -0400 Subject: [PATCH 02/11] Normalize line and column numbers from the sysroot --- tests/run-pass/backtrace-api.rs | 1 + tests/run-pass/backtrace-api.stderr | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/run-pass/backtrace-api.rs b/tests/run-pass/backtrace-api.rs index fa86debf17..51da6cf9c4 100644 --- a/tests/run-pass/backtrace-api.rs +++ b/tests/run-pass/backtrace-api.rs @@ -1,4 +1,5 @@ // normalize-stderr-test ".*rustlib" -> "RUSTLIB" +// normalize-stderr-test "RUSTLIB/(.*):\d+:\d+ "-> "RUSTLIB/$1:LL:COL " extern "Rust" { fn miri_get_backtrace() -> Box<[*mut ()]>; diff --git a/tests/run-pass/backtrace-api.stderr b/tests/run-pass/backtrace-api.stderr index 91a99070eb..042ca843d0 100644 --- a/tests/run-pass/backtrace-api.stderr +++ b/tests/run-pass/backtrace-api.stderr @@ -1,10 +1,10 @@ -$DIR/backtrace-api.rs:17:27 (main) -RUSTLIB/src/rust/library/core/src/ops/function.rs:227:5 (>::call_once - shim(fn())) -RUSTLIB/src/rust/library/std/src/sys_common/backtrace.rs:137:18 (std::sys_common::backtrace::__rust_begin_short_backtrace::) -RUSTLIB/src/rust/library/std/src/rt.rs:66:18 (std::rt::lang_start::<()>::{{closure}}#0) -RUSTLIB/src/rust/library/core/src/ops/function.rs:259:13 (std::ops::function::impls:: for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once) -RUSTLIB/src/rust/library/std/src/panicking.rs:381:40 (std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>) -RUSTLIB/src/rust/library/std/src/panicking.rs:345:19 (std::panicking::r#try:: i32 + std::marker::Sync + std::panic::RefUnwindSafe>) -RUSTLIB/src/rust/library/std/src/panic.rs:382:14 (std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>) -RUSTLIB/src/rust/library/std/src/rt.rs:51:25 (std::rt::lang_start_internal) -RUSTLIB/src/rust/library/std/src/rt.rs:65:5 (std::rt::lang_start::<()>) +$DIR/backtrace-api.rs:18:27 (main) +RUSTLIB/src/rust/library/core/src/ops/function.rs:LL:COL (>::call_once - shim(fn())) +RUSTLIB/src/rust/library/std/src/sys_common/backtrace.rs:LL:COL (std::sys_common::backtrace::__rust_begin_short_backtrace::) +RUSTLIB/src/rust/library/std/src/rt.rs:LL:COL (std::rt::lang_start::<()>::{{closure}}#0) +RUSTLIB/src/rust/library/core/src/ops/function.rs:LL:COL (std::ops::function::impls:: for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once) +RUSTLIB/src/rust/library/std/src/panicking.rs:LL:COL (std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>) +RUSTLIB/src/rust/library/std/src/panicking.rs:LL:COL (std::panicking::r#try:: i32 + std::marker::Sync + std::panic::RefUnwindSafe>) +RUSTLIB/src/rust/library/std/src/panic.rs:LL:COL (std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>) +RUSTLIB/src/rust/library/std/src/rt.rs:LL:COL (std::rt::lang_start_internal) +RUSTLIB/src/rust/library/std/src/rt.rs:LL:COL (std::rt::lang_start::<()>) From 362e34a0d2d0a31e46dddeee0dceaaca2f7ac8c2 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 24 Sep 2020 14:18:47 -0400 Subject: [PATCH 03/11] Use a 'flags' parameter instead of 'version' --- README.md | 2 +- src/shims/foreign_items.rs | 8 ++++---- tests/compile-fail/backtrace/bad-backtrace-decl.rs | 4 ++-- tests/compile-fail/backtrace/bad-backtrace-ptr.rs | 4 ++-- tests/compile-fail/backtrace/bad-backtrace-version.rs | 4 ++-- tests/run-pass/backtrace-api.rs | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 524c8e10cf..55d3bef727 100644 --- a/README.md +++ b/README.md @@ -290,7 +290,7 @@ extern "Rust" { /// /// The fields must be declared in exactly the same order as they appear in `MiriFrame` above. /// This function can be called on any thread (not just the one which obtained `frame`) - fn miri_resolve_frame(version: u8, frame: *mut ()) -> MiriFrame; + fn miri_resolve_frame(frame: *mut (), flags: u64) -> MiriFrame; /// Miri-provided extern function to begin unwinding with the given payload. /// diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 4d1ead8f0f..b1a02a8eb6 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -251,11 +251,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Resolves a Miri backtrace frame. See the README for details. "miri_resolve_frame" => { let tcx = this.tcx; - let &[version, ptr] = check_arg_count(args)?; + let &[ptr, flags] = check_arg_count(args)?; - let version = this.read_scalar(version)?.to_u8()?; - if version != 0 { - throw_ub_format!("Unknown `miri_resolve_frame` version {}", version); + let flags = this.read_scalar(flags)?.to_u64()?; + if flags != 0 { + throw_ub_format!("Unknown `miri_resolve_frame` flags {}", flags); } let ptr = match this.read_scalar(ptr)?.check_init()? { diff --git a/tests/compile-fail/backtrace/bad-backtrace-decl.rs b/tests/compile-fail/backtrace/bad-backtrace-decl.rs index 7c250fbbe3..c55a1c6d38 100644 --- a/tests/compile-fail/backtrace/bad-backtrace-decl.rs +++ b/tests/compile-fail/backtrace/bad-backtrace-decl.rs @@ -1,13 +1,13 @@ extern "Rust" { fn miri_get_backtrace() -> Box<[*mut ()]>; - fn miri_resolve_frame(version: u8, ptr: *mut ()); + fn miri_resolve_frame(ptr: *mut (), flags: u64); } fn main() { let frames = unsafe { miri_get_backtrace() }; for frame in frames.into_iter() { unsafe { - miri_resolve_frame(0, *frame); //~ ERROR Undefined Behavior: Bad declaration of miri_resolve_frame - should return a struct with 4 fields + miri_resolve_frame(*frame, 0); //~ ERROR Undefined Behavior: Bad declaration of miri_resolve_frame - should return a struct with 4 fields } } } diff --git a/tests/compile-fail/backtrace/bad-backtrace-ptr.rs b/tests/compile-fail/backtrace/bad-backtrace-ptr.rs index 49b8ac8849..3f672eb2dc 100644 --- a/tests/compile-fail/backtrace/bad-backtrace-ptr.rs +++ b/tests/compile-fail/backtrace/bad-backtrace-ptr.rs @@ -1,9 +1,9 @@ extern "Rust" { - fn miri_resolve_frame(version: u8, ptr: *mut ()); + fn miri_resolve_frame(ptr: *mut (), flags: u64); } fn main() { unsafe { - miri_resolve_frame(0, 0 as *mut _); //~ ERROR Undefined Behavior: Expected a pointer + miri_resolve_frame(0 as *mut _, 0); //~ ERROR Undefined Behavior: Expected a pointer } } diff --git a/tests/compile-fail/backtrace/bad-backtrace-version.rs b/tests/compile-fail/backtrace/bad-backtrace-version.rs index b0183ca99e..d6743ae8ff 100644 --- a/tests/compile-fail/backtrace/bad-backtrace-version.rs +++ b/tests/compile-fail/backtrace/bad-backtrace-version.rs @@ -1,9 +1,9 @@ extern "Rust" { - fn miri_resolve_frame(version: u8, ptr: *mut ()); + fn miri_resolve_frame(ptr: *mut (), flags: u64); } fn main() { unsafe { - miri_resolve_frame(1, 0 as *mut _); //~ ERROR Undefined Behavior: Unknown `miri_resolve_frame` version 1 + miri_resolve_frame(0 as *mut _, 1); //~ ERROR Undefined Behavior: Unknown `miri_resolve_frame` flags 1 } } diff --git a/tests/run-pass/backtrace-api.rs b/tests/run-pass/backtrace-api.rs index 51da6cf9c4..231b718cd3 100644 --- a/tests/run-pass/backtrace-api.rs +++ b/tests/run-pass/backtrace-api.rs @@ -3,7 +3,7 @@ extern "Rust" { fn miri_get_backtrace() -> Box<[*mut ()]>; - fn miri_resolve_frame(version: u8, ptr: *mut ()) -> MiriFrame; + fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame; } #[derive(Debug)] @@ -17,7 +17,7 @@ struct MiriFrame { fn main() { let frames = unsafe { miri_get_backtrace() }; for frame in frames.into_iter() { - let miri_frame = unsafe { miri_resolve_frame(0, *frame) }; + let miri_frame = unsafe { miri_resolve_frame(*frame, 0) }; let name = String::from_utf8(miri_frame.name.into()).unwrap(); let filename = String::from_utf8(miri_frame.filename.into()).unwrap(); eprintln!("{}:{}:{} ({})", filename, miri_frame.lineno, miri_frame.colno, name); From 5080ac4cce7a1dd54c643aeeda108c0766de9771 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 24 Sep 2020 15:05:50 -0400 Subject: [PATCH 04/11] Print non-std frames to stdout in `backtrace-api` test --- tests/run-pass/backtrace-api.rs | 18 ++++++++++++++++-- tests/run-pass/backtrace-api.stderr | 5 ++++- tests/run-pass/backtrace-api.stdout | 4 ++++ 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 tests/run-pass/backtrace-api.stdout diff --git a/tests/run-pass/backtrace-api.rs b/tests/run-pass/backtrace-api.rs index 231b718cd3..73a3f6242d 100644 --- a/tests/run-pass/backtrace-api.rs +++ b/tests/run-pass/backtrace-api.rs @@ -14,12 +14,26 @@ struct MiriFrame { colno: u32 } +fn func_a() -> Box<[*mut ()]> { func_b::() } +fn func_b() -> Box<[*mut ()]> { func_c() } +fn func_c() -> Box<[*mut ()]> { unsafe { miri_get_backtrace() } } + fn main() { - let frames = unsafe { miri_get_backtrace() }; + let mut seen_main = false; + let frames = func_a(); for frame in frames.into_iter() { let miri_frame = unsafe { miri_resolve_frame(*frame, 0) }; let name = String::from_utf8(miri_frame.name.into()).unwrap(); let filename = String::from_utf8(miri_frame.filename.into()).unwrap(); - eprintln!("{}:{}:{} ({})", filename, miri_frame.lineno, miri_frame.colno, name); + + // Print every frame to stderr. + let out = format!("{}:{}:{} ({})", filename, miri_frame.lineno, miri_frame.colno, name); + eprintln!("{}", out); + // Print the 'main' frame (and everything before it) to stdout, skipping + // the printing of internal (and possibly fragile) libstd frames. + if !seen_main { + println!("{}", out); + seen_main = name == "main"; + } } } diff --git a/tests/run-pass/backtrace-api.stderr b/tests/run-pass/backtrace-api.stderr index 042ca843d0..b96def8093 100644 --- a/tests/run-pass/backtrace-api.stderr +++ b/tests/run-pass/backtrace-api.stderr @@ -1,4 +1,7 @@ -$DIR/backtrace-api.rs:18:27 (main) +$DIR/backtrace-api.rs:19:42 (func_c) +$DIR/backtrace-api.rs:18:36 (func_b::) +$DIR/backtrace-api.rs:17:33 (func_a) +$DIR/backtrace-api.rs:23:18 (main) RUSTLIB/src/rust/library/core/src/ops/function.rs:LL:COL (>::call_once - shim(fn())) RUSTLIB/src/rust/library/std/src/sys_common/backtrace.rs:LL:COL (std::sys_common::backtrace::__rust_begin_short_backtrace::) RUSTLIB/src/rust/library/std/src/rt.rs:LL:COL (std::rt::lang_start::<()>::{{closure}}#0) diff --git a/tests/run-pass/backtrace-api.stdout b/tests/run-pass/backtrace-api.stdout new file mode 100644 index 0000000000..0d8aaa7a32 --- /dev/null +++ b/tests/run-pass/backtrace-api.stdout @@ -0,0 +1,4 @@ +$DIR/backtrace-api.rs:19:42 (func_c) +$DIR/backtrace-api.rs:18:36 (func_b::) +$DIR/backtrace-api.rs:17:33 (func_a) +$DIR/backtrace-api.rs:23:18 (main) From d9c27e74295fc87409b89648f5688078a8fb46db Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 24 Sep 2020 18:52:17 -0400 Subject: [PATCH 05/11] Move things around --- src/shims/backtrace.rs | 110 ++++++++++++++++++ src/shims/foreign_items.rs | 81 +------------ src/shims/mod.rs | 2 +- .../backtrace/bad-backtrace-decl.rs | 4 +- .../backtrace/bad-backtrace-version.rs | 2 +- tests/run-pass/backtrace-api.rs | 4 +- 6 files changed, 120 insertions(+), 83 deletions(-) create mode 100644 src/shims/backtrace.rs diff --git a/src/shims/backtrace.rs b/src/shims/backtrace.rs new file mode 100644 index 0000000000..2b360cb85a --- /dev/null +++ b/src/shims/backtrace.rs @@ -0,0 +1,110 @@ +use crate::*; +use helpers::check_arg_count; +use rustc_middle::ty::TypeAndMut; +use rustc_ast::ast::Mutability; +use rustc_span::BytePos; +use rustc_target::abi::Size; +use std::convert::TryInto as _; +use crate::rustc_target::abi::LayoutOf as _; + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + + fn handle_miri_get_backtrace( + &mut self, + args: &[OpTy<'tcx, Tag>], + dest: PlaceTy<'tcx, Tag> + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let tcx = this.tcx; + let &[flags] = check_arg_count(args)?; + + let flags = this.read_scalar(flags)?.to_u64()?; + if flags != 0 { + throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags); + } + + let mut data = Vec::new(); + for frame in this.active_thread_stack().iter().rev() { + data.push((frame.instance, frame.current_span().lo())); + } + + let ptrs: Vec<_> = data.into_iter().map(|(instance, pos)| { + let mut fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance)); + fn_ptr.offset = Size::from_bytes(pos.0); + Scalar::Ptr(fn_ptr) + }).collect(); + + let len = ptrs.len(); + + let ptr_ty = tcx.mk_ptr(TypeAndMut { + ty: tcx.types.unit, + mutbl: Mutability::Mut + }); + + let array_ty = tcx.mk_array(ptr_ty, ptrs.len().try_into().unwrap()); + + // Write pointers into array + let alloc = this.allocate(this.layout_of(array_ty).unwrap(), MiriMemoryKind::Rust.into()); + for (i, ptr) in ptrs.into_iter().enumerate() { + let place = this.mplace_index(alloc, i as u64)?; + this.write_immediate_to_mplace(ptr.into(), place)?; + } + + this.write_immediate(Immediate::new_slice(alloc.ptr.into(), len.try_into().unwrap(), this), dest)?; + Ok(()) + } + + fn handle_miri_resolve_frame( + &mut self, + args: &[OpTy<'tcx, Tag>], + dest: PlaceTy<'tcx, Tag> + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let tcx = this.tcx; + let &[ptr, flags] = check_arg_count(args)?; + + let flags = this.read_scalar(flags)?.to_u64()?; + if flags != 0 { + throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags); + } + + let ptr = match this.read_scalar(ptr)?.check_init()? { + Scalar::Ptr(ptr) => ptr, + Scalar::Raw { .. } => throw_ub_format!("Expected a pointer in `rust_miri_resolve_frame`, found {:?}", ptr) + }; + + let fn_instance = if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(ptr.alloc_id) { + instance + } else { + throw_ub_format!("expected function pointer, found {:?}", ptr); + }; + + if dest.layout.layout.fields.count() != 4 { + throw_ub_format!("Bad declaration of miri_resolve_frame - should return a struct with 4 fields"); + } + + let pos = BytePos(ptr.offset.bytes().try_into().unwrap()); + let name = fn_instance.to_string(); + + let lo = tcx.sess.source_map().lookup_char_pos(pos); + + let filename = lo.file.name.to_string(); + let lineno: u32 = lo.line as u32; + // `lo.col` is 0-based - add 1 to make it 1-based for the caller. + let colno: u32 = lo.col.0 as u32 + 1; + + let name_alloc = this.allocate_str(&name, MiriMemoryKind::Rust.into()); + let filename_alloc = this.allocate_str(&filename, MiriMemoryKind::Rust.into()); + let lineno_alloc = Scalar::from_u32(lineno); + let colno_alloc = Scalar::from_u32(colno); + + let dest = this.force_allocation_maybe_sized(dest, MemPlaceMeta::None)?.0; + + this.write_immediate(name_alloc.to_ref(), this.mplace_field(dest, 0)?.into())?; + this.write_immediate(filename_alloc.to_ref(), this.mplace_field(dest, 1)?.into())?; + this.write_scalar(lineno_alloc, this.mplace_field(dest, 2)?.into())?; + this.write_scalar(colno_alloc, this.mplace_field(dest, 3)?.into())?; + Ok(()) + } +} diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index b1a02a8eb6..7118fbda24 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -5,13 +5,12 @@ use log::trace; use rustc_hir::def_id::DefId; use rustc_middle::mir; use rustc_target::{abi::{Align, Size}, spec::PanicStrategy}; -use rustc_middle::ty::{self, ParamEnv, TypeAndMut}; -use rustc_ast::ast::Mutability; +use rustc_middle::ty; use rustc_apfloat::Float; use rustc_span::symbol::sym; -use rustc_span::BytePos; use crate::*; +use super::backtrace::EvalContextExt as _; use helpers::check_arg_count; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} @@ -216,84 +215,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Obtains a Miri backtrace. See the README for details. "miri_get_backtrace" => { - let tcx = this.tcx; - let mut data = Vec::new(); - for frame in this.active_thread_stack().iter().rev() { - data.push((frame.instance, frame.current_span().lo())); - } - - let ptrs: Vec<_> = data.into_iter().map(|(instance, pos)| { - let mut fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance)); - fn_ptr.offset = Size::from_bytes(pos.0); - Scalar::Ptr(fn_ptr) - }).collect(); - - let len = ptrs.len(); - - let ptr_ty = tcx.mk_ptr(TypeAndMut { - ty: tcx.types.unit, - mutbl: Mutability::Mut - }); - - let array_ty = tcx.mk_array(ptr_ty, ptrs.len().try_into().unwrap()); - let array_ty_and_env = ParamEnv::empty().and(array_ty); - - // Write pointers into array - let alloc = this.allocate(tcx.layout_of(array_ty_and_env).unwrap(), MiriMemoryKind::Rust.into()); - for (i, ptr) in ptrs.into_iter().enumerate() { - let place = this.mplace_index(alloc, i as u64)?; - this.write_immediate_to_mplace(ptr.into(), place)?; - } - - this.write_immediate(Immediate::new_slice(alloc.ptr.into(), len.try_into().unwrap(), this), dest)?; + this.handle_miri_get_backtrace(args, dest)?; } // Resolves a Miri backtrace frame. See the README for details. "miri_resolve_frame" => { - let tcx = this.tcx; - let &[ptr, flags] = check_arg_count(args)?; - - let flags = this.read_scalar(flags)?.to_u64()?; - if flags != 0 { - throw_ub_format!("Unknown `miri_resolve_frame` flags {}", flags); - } - - let ptr = match this.read_scalar(ptr)?.check_init()? { - Scalar::Ptr(ptr) => ptr, - Scalar::Raw { .. } => throw_ub_format!("Expected a pointer in `rust_miri_resolve_frame`, found {:?}", ptr) - }; - - let fn_instance = if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(ptr.alloc_id) { - instance - } else { - throw_ub_format!("Expect function pointer, found {:?}", ptr); - }; - - if dest.layout.layout.fields.count() != 4 { - throw_ub_format!("Bad declaration of miri_resolve_frame - should return a struct with 4 fields"); - } - - let pos = BytePos(ptr.offset.bytes().try_into().unwrap()); - let name = fn_instance.to_string(); - - let lo = tcx.sess.source_map().lookup_char_pos(pos); - - let filename = lo.file.name.to_string(); - let lineno: u32 = lo.line as u32; - // `lo.col` is 0-based - add 1 to make it 1-based for the caller. - let colno: u32 = lo.col.0 as u32 + 1; - - let name_alloc = this.allocate_str(&name, MiriMemoryKind::Rust.into()); - let filename_alloc = this.allocate_str(&filename, MiriMemoryKind::Rust.into()); - let lineno_alloc = Scalar::from_u32(lineno); - let colno_alloc = Scalar::from_u32(colno); - - let dest = this.force_allocation_maybe_sized(dest, MemPlaceMeta::None)?.0; - - this.write_immediate(name_alloc.to_ref(), this.mplace_field(dest, 0)?.into())?; - this.write_immediate(filename_alloc.to_ref(), this.mplace_field(dest, 1)?.into())?; - this.write_scalar(lineno_alloc, this.mplace_field(dest, 2)?.into())?; - this.write_scalar(colno_alloc, this.mplace_field(dest, 3)?.into())?; + this.handle_miri_resolve_frame(args, dest)?; } diff --git a/src/shims/mod.rs b/src/shims/mod.rs index eab27496cb..90dcc4d8ff 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -1,4 +1,4 @@ - +mod backtrace; pub mod foreign_items; pub mod intrinsics; pub mod posix; diff --git a/tests/compile-fail/backtrace/bad-backtrace-decl.rs b/tests/compile-fail/backtrace/bad-backtrace-decl.rs index c55a1c6d38..bccc7063af 100644 --- a/tests/compile-fail/backtrace/bad-backtrace-decl.rs +++ b/tests/compile-fail/backtrace/bad-backtrace-decl.rs @@ -1,10 +1,10 @@ extern "Rust" { - fn miri_get_backtrace() -> Box<[*mut ()]>; + fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>; fn miri_resolve_frame(ptr: *mut (), flags: u64); } fn main() { - let frames = unsafe { miri_get_backtrace() }; + let frames = unsafe { miri_get_backtrace(0) }; for frame in frames.into_iter() { unsafe { miri_resolve_frame(*frame, 0); //~ ERROR Undefined Behavior: Bad declaration of miri_resolve_frame - should return a struct with 4 fields diff --git a/tests/compile-fail/backtrace/bad-backtrace-version.rs b/tests/compile-fail/backtrace/bad-backtrace-version.rs index d6743ae8ff..4579b5d0ad 100644 --- a/tests/compile-fail/backtrace/bad-backtrace-version.rs +++ b/tests/compile-fail/backtrace/bad-backtrace-version.rs @@ -4,6 +4,6 @@ extern "Rust" { fn main() { unsafe { - miri_resolve_frame(0 as *mut _, 1); //~ ERROR Undefined Behavior: Unknown `miri_resolve_frame` flags 1 + miri_resolve_frame(0 as *mut _, 1); //~ ERROR unsupported operation: unknown `miri_resolve_frame` flags 1 } } diff --git a/tests/run-pass/backtrace-api.rs b/tests/run-pass/backtrace-api.rs index 73a3f6242d..655a52c7fc 100644 --- a/tests/run-pass/backtrace-api.rs +++ b/tests/run-pass/backtrace-api.rs @@ -2,7 +2,7 @@ // normalize-stderr-test "RUSTLIB/(.*):\d+:\d+ "-> "RUSTLIB/$1:LL:COL " extern "Rust" { - fn miri_get_backtrace() -> Box<[*mut ()]>; + fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>; fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame; } @@ -16,7 +16,7 @@ struct MiriFrame { fn func_a() -> Box<[*mut ()]> { func_b::() } fn func_b() -> Box<[*mut ()]> { func_c() } -fn func_c() -> Box<[*mut ()]> { unsafe { miri_get_backtrace() } } +fn func_c() -> Box<[*mut ()]> { unsafe { miri_get_backtrace(0) } } fn main() { let mut seen_main = false; From 8de92c3531e8dfb6752381753857e3c9de3493f7 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 24 Sep 2020 19:06:39 -0400 Subject: [PATCH 06/11] Explain encoding scheme --- src/shims/backtrace.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/shims/backtrace.rs b/src/shims/backtrace.rs index 2b360cb85a..2c031f179d 100644 --- a/src/shims/backtrace.rs +++ b/src/shims/backtrace.rs @@ -30,6 +30,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } let ptrs: Vec<_> = data.into_iter().map(|(instance, pos)| { + // We represent a frame pointer by using the `span.lo` value + // as an offset into the function's allocation. This gives us an + // opaque pointer that we can return to user code, and allows us + // to reconstruct the needed frame information in `handle_miri_resolve_frame`. + // Note that we never actually read or write anything from/to this pointer - + // all of the data is represented by the pointer value itself. let mut fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance)); fn_ptr.offset = Size::from_bytes(pos.0); Scalar::Ptr(fn_ptr) From e5546b65ce32eddadf18d98b3cfde2de8f42f10e Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 24 Sep 2020 19:10:02 -0400 Subject: [PATCH 07/11] Make some error messages lowercase --- src/shims/backtrace.rs | 4 ++-- tests/compile-fail/backtrace/bad-backtrace-decl.rs | 2 +- tests/compile-fail/backtrace/bad-backtrace-ptr.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/shims/backtrace.rs b/src/shims/backtrace.rs index 2c031f179d..57d59dd4c0 100644 --- a/src/shims/backtrace.rs +++ b/src/shims/backtrace.rs @@ -77,7 +77,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let ptr = match this.read_scalar(ptr)?.check_init()? { Scalar::Ptr(ptr) => ptr, - Scalar::Raw { .. } => throw_ub_format!("Expected a pointer in `rust_miri_resolve_frame`, found {:?}", ptr) + Scalar::Raw { .. } => throw_ub_format!("expected a pointer in `rust_miri_resolve_frame`, found {:?}", ptr) }; let fn_instance = if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(ptr.alloc_id) { @@ -87,7 +87,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; if dest.layout.layout.fields.count() != 4 { - throw_ub_format!("Bad declaration of miri_resolve_frame - should return a struct with 4 fields"); + throw_ub_format!("bad declaration of miri_resolve_frame - should return a struct with 4 fields"); } let pos = BytePos(ptr.offset.bytes().try_into().unwrap()); diff --git a/tests/compile-fail/backtrace/bad-backtrace-decl.rs b/tests/compile-fail/backtrace/bad-backtrace-decl.rs index bccc7063af..b9f1c779ae 100644 --- a/tests/compile-fail/backtrace/bad-backtrace-decl.rs +++ b/tests/compile-fail/backtrace/bad-backtrace-decl.rs @@ -7,7 +7,7 @@ fn main() { let frames = unsafe { miri_get_backtrace(0) }; for frame in frames.into_iter() { unsafe { - miri_resolve_frame(*frame, 0); //~ ERROR Undefined Behavior: Bad declaration of miri_resolve_frame - should return a struct with 4 fields + miri_resolve_frame(*frame, 0); //~ ERROR Undefined Behavior: bad declaration of miri_resolve_frame - should return a struct with 4 fields } } } diff --git a/tests/compile-fail/backtrace/bad-backtrace-ptr.rs b/tests/compile-fail/backtrace/bad-backtrace-ptr.rs index 3f672eb2dc..5e245952e9 100644 --- a/tests/compile-fail/backtrace/bad-backtrace-ptr.rs +++ b/tests/compile-fail/backtrace/bad-backtrace-ptr.rs @@ -4,6 +4,6 @@ extern "Rust" { fn main() { unsafe { - miri_resolve_frame(0 as *mut _, 0); //~ ERROR Undefined Behavior: Expected a pointer + miri_resolve_frame(0 as *mut _, 0); //~ ERROR Undefined Behavior: expected a pointer } } From 43d1eac1e27e12681d3853ba9735f86f96b3af92 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 24 Sep 2020 19:10:09 -0400 Subject: [PATCH 08/11] Update README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 55d3bef727..1ebacfc26b 100644 --- a/README.md +++ b/README.md @@ -269,10 +269,11 @@ extern "Rust" { /// Miri-provided extern function to obtain a backtrace of the current call stack. /// This returns a boxed slice of pointers - each pointer is an opaque value /// that is only useful when passed to `miri_resolve_frame` - fn miri_get_backtrace() -> Box<[*mut ()]>; + /// The `flags` argument must be `0`. + fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>; /// Miri-provided extern function to resolve a frame pointer obtained - /// from `miri_get_backtrace`. The `version` argument must be `0`, + /// from `miri_get_backtrace`. The `flags` argument must be `0`, /// and `MiriFrame` should be declared as follows: /// /// ```rust From 58573655ae469df896b2fae4ee2946ec10854446 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 24 Sep 2020 23:00:32 -0400 Subject: [PATCH 09/11] Apply #[inline(never)] to functions that we want in the backtrace --- tests/run-pass/backtrace-api.rs | 6 +++--- tests/run-pass/backtrace-api.stderr | 6 +++--- tests/run-pass/backtrace-api.stdout | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/run-pass/backtrace-api.rs b/tests/run-pass/backtrace-api.rs index 655a52c7fc..1322e15fc9 100644 --- a/tests/run-pass/backtrace-api.rs +++ b/tests/run-pass/backtrace-api.rs @@ -14,9 +14,9 @@ struct MiriFrame { colno: u32 } -fn func_a() -> Box<[*mut ()]> { func_b::() } -fn func_b() -> Box<[*mut ()]> { func_c() } -fn func_c() -> Box<[*mut ()]> { unsafe { miri_get_backtrace(0) } } +#[inline(never)] fn func_a() -> Box<[*mut ()]> { func_b::() } +#[inline(never)] fn func_b() -> Box<[*mut ()]> { func_c() } +#[inline(never)] fn func_c() -> Box<[*mut ()]> { unsafe { miri_get_backtrace(0) } } fn main() { let mut seen_main = false; diff --git a/tests/run-pass/backtrace-api.stderr b/tests/run-pass/backtrace-api.stderr index b96def8093..d94a7ce4ae 100644 --- a/tests/run-pass/backtrace-api.stderr +++ b/tests/run-pass/backtrace-api.stderr @@ -1,6 +1,6 @@ -$DIR/backtrace-api.rs:19:42 (func_c) -$DIR/backtrace-api.rs:18:36 (func_b::) -$DIR/backtrace-api.rs:17:33 (func_a) +$DIR/backtrace-api.rs:19:59 (func_c) +$DIR/backtrace-api.rs:18:53 (func_b::) +$DIR/backtrace-api.rs:17:50 (func_a) $DIR/backtrace-api.rs:23:18 (main) RUSTLIB/src/rust/library/core/src/ops/function.rs:LL:COL (>::call_once - shim(fn())) RUSTLIB/src/rust/library/std/src/sys_common/backtrace.rs:LL:COL (std::sys_common::backtrace::__rust_begin_short_backtrace::) diff --git a/tests/run-pass/backtrace-api.stdout b/tests/run-pass/backtrace-api.stdout index 0d8aaa7a32..0353f98ed7 100644 --- a/tests/run-pass/backtrace-api.stdout +++ b/tests/run-pass/backtrace-api.stdout @@ -1,4 +1,4 @@ -$DIR/backtrace-api.rs:19:42 (func_c) -$DIR/backtrace-api.rs:18:36 (func_b::) -$DIR/backtrace-api.rs:17:33 (func_a) +$DIR/backtrace-api.rs:19:59 (func_c) +$DIR/backtrace-api.rs:18:53 (func_b::) +$DIR/backtrace-api.rs:17:50 (func_a) $DIR/backtrace-api.rs:23:18 (main) From 2a2a93da22ad8b92d1415649e4d6b32f3d29ff02 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 26 Sep 2020 14:40:41 -0400 Subject: [PATCH 10/11] Require #[repr(C)] on MiriFrame --- README.md | 1 + src/shims/backtrace.rs | 9 +++++++-- tests/run-pass/backtrace-api.rs | 1 + tests/run-pass/backtrace-api.stderr | 8 ++++---- tests/run-pass/backtrace-api.stdout | 8 ++++---- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1ebacfc26b..b84102598c 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,7 @@ extern "Rust" { /// and `MiriFrame` should be declared as follows: /// /// ```rust + /// #[repr(C)] /// struct MiriFrame { /// // The name of the function being executed, encoded in UTF-8 /// name: Box<[u8]>, diff --git a/src/shims/backtrace.rs b/src/shims/backtrace.rs index 57d59dd4c0..75cd61b0f5 100644 --- a/src/shims/backtrace.rs +++ b/src/shims/backtrace.rs @@ -1,6 +1,6 @@ use crate::*; use helpers::check_arg_count; -use rustc_middle::ty::TypeAndMut; +use rustc_middle::ty::{self, TypeAndMut}; use rustc_ast::ast::Mutability; use rustc_span::BytePos; use rustc_target::abi::Size; @@ -105,7 +105,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx let lineno_alloc = Scalar::from_u32(lineno); let colno_alloc = Scalar::from_u32(colno); - let dest = this.force_allocation_maybe_sized(dest, MemPlaceMeta::None)?.0; + let dest = this.force_allocation(dest)?; + if let ty::Adt(adt, _) = dest.layout.ty.kind() { + if !adt.repr.c() { + throw_ub_format!("miri_resolve_frame must be declared with a `#[repr(C)]` return type"); + } + } this.write_immediate(name_alloc.to_ref(), this.mplace_field(dest, 0)?.into())?; this.write_immediate(filename_alloc.to_ref(), this.mplace_field(dest, 1)?.into())?; diff --git a/tests/run-pass/backtrace-api.rs b/tests/run-pass/backtrace-api.rs index 1322e15fc9..a58fb83d92 100644 --- a/tests/run-pass/backtrace-api.rs +++ b/tests/run-pass/backtrace-api.rs @@ -7,6 +7,7 @@ extern "Rust" { } #[derive(Debug)] +#[repr(C)] struct MiriFrame { name: Box<[u8]>, filename: Box<[u8]>, diff --git a/tests/run-pass/backtrace-api.stderr b/tests/run-pass/backtrace-api.stderr index d94a7ce4ae..92c5331d61 100644 --- a/tests/run-pass/backtrace-api.stderr +++ b/tests/run-pass/backtrace-api.stderr @@ -1,7 +1,7 @@ -$DIR/backtrace-api.rs:19:59 (func_c) -$DIR/backtrace-api.rs:18:53 (func_b::) -$DIR/backtrace-api.rs:17:50 (func_a) -$DIR/backtrace-api.rs:23:18 (main) +$DIR/backtrace-api.rs:20:59 (func_c) +$DIR/backtrace-api.rs:19:53 (func_b::) +$DIR/backtrace-api.rs:18:50 (func_a) +$DIR/backtrace-api.rs:24:18 (main) RUSTLIB/src/rust/library/core/src/ops/function.rs:LL:COL (>::call_once - shim(fn())) RUSTLIB/src/rust/library/std/src/sys_common/backtrace.rs:LL:COL (std::sys_common::backtrace::__rust_begin_short_backtrace::) RUSTLIB/src/rust/library/std/src/rt.rs:LL:COL (std::rt::lang_start::<()>::{{closure}}#0) diff --git a/tests/run-pass/backtrace-api.stdout b/tests/run-pass/backtrace-api.stdout index 0353f98ed7..e4130ade62 100644 --- a/tests/run-pass/backtrace-api.stdout +++ b/tests/run-pass/backtrace-api.stdout @@ -1,4 +1,4 @@ -$DIR/backtrace-api.rs:19:59 (func_c) -$DIR/backtrace-api.rs:18:53 (func_b::) -$DIR/backtrace-api.rs:17:50 (func_a) -$DIR/backtrace-api.rs:23:18 (main) +$DIR/backtrace-api.rs:20:59 (func_c) +$DIR/backtrace-api.rs:19:53 (func_b::) +$DIR/backtrace-api.rs:18:50 (func_a) +$DIR/backtrace-api.rs:24:18 (main) From 1670f8c0d7b6fee2015692348fc2b6fc8be8963b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 28 Sep 2020 19:34:18 +0200 Subject: [PATCH 11/11] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b84102598c..2b01510482 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ extern "Rust" { /// ``` /// /// The fields must be declared in exactly the same order as they appear in `MiriFrame` above. - /// This function can be called on any thread (not just the one which obtained `frame`) + /// This function can be called on any thread (not just the one which obtained `frame`). fn miri_resolve_frame(frame: *mut (), flags: u64) -> MiriFrame; /// Miri-provided extern function to begin unwinding with the given payload.