Skip to content

Commit f857f07

Browse files
committed
Fix short backtraces from stripped executables
Locate the beginning and ending frames for short backtraces by address in addition to symbol name, so that they work even when symbols have been stripped from the executable. We need to retain matching by symbol name for the time being, because rustc (and some associated ui tests) rely upon it to have ICE backtraces abbreviated. They can be updated once this commit lands in beta/stage0.
1 parent d222ddc commit f857f07

File tree

4 files changed

+87
-22
lines changed

4 files changed

+87
-22
lines changed

library/std/src/backtrace.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ use crate::panic::UnwindSafe;
9494
use crate::sync::LazyLock;
9595
use crate::sync::atomic::Ordering::Relaxed;
9696
use crate::sync::atomic::{Atomic, AtomicU8};
97+
#[unstable(
98+
feature = "short_backtrace_termini",
99+
reason = "for rustc to have ICE backtraces abbreviated",
100+
issue = "none"
101+
)]
102+
#[doc(hidden)]
103+
pub use crate::sys::backtrace::{__rust_begin_short_backtrace, __rust_end_short_backtrace};
97104
use crate::sys::backtrace::{lock, output_filename, set_image_base};
98105
use crate::{env, fmt};
99106

library/std/src/sys/backtrace.rs

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt};
55
use crate::borrow::Cow;
66
use crate::io::prelude::*;
7+
use crate::mem::{ManuallyDrop, MaybeUninit};
78
use crate::path::{self, Path, PathBuf};
89
use crate::sync::{Mutex, MutexGuard, PoisonError};
910
use crate::{env, fmt, io};
@@ -68,6 +69,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
6869
let mut first_omit = true;
6970
// If we're using a short backtrace, ignore all frames until we're told to start printing.
7071
let mut print = print_fmt != PrintFmt::Short;
72+
let mut skip = false;
7173
set_image_base();
7274
// SAFETY: we roll our own locking in this town
7375
unsafe {
@@ -81,12 +83,30 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
8183
let frame_ip = frame.ip();
8284
res = writeln!(bt_fmt.formatter(), "{idx:4}: {frame_ip:HEX_WIDTH$?}");
8385
} else {
86+
// `call_with_short_backtrace_marker::<End>` means we are done hiding symbols
87+
// for now. Print until we see `call_with_short_backtrace_marker::<Begin>`.
88+
if print_fmt == PrintFmt::Short {
89+
let sym = frame.symbol_address();
90+
if sym == call_with_short_backtrace_marker::<End> as _ {
91+
skip = true;
92+
print = true;
93+
} else if print && sym == call_with_short_backtrace_marker::<Begin> as _ {
94+
skip = true;
95+
print = false;
96+
}
97+
}
98+
8499
let mut hit = false;
85100
backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| {
86101
hit = true;
102+
if skip {
103+
skip = false;
104+
return;
105+
}
87106

88-
// `__rust_end_short_backtrace` means we are done hiding symbols
89-
// for now. Print until we see `__rust_begin_short_backtrace`.
107+
// Hide `__rust_[begin|end]_short_backtrace` frames from short backtraces.
108+
// Unfortunately these generic functions have to be matched by name, as we do
109+
// not know their generic parameters.
90110
if print_fmt == PrintFmt::Short {
91111
if let Some(sym) = symbol.name().and_then(|s| s.as_str()) {
92112
if sym.contains("__rust_end_short_backtrace") {
@@ -159,32 +179,43 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
159179
/// this is only inline(never) when backtraces in std are enabled, otherwise
160180
/// it's fine to optimize away.
161181
#[cfg_attr(feature = "backtrace", inline(never))]
162-
pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
163-
where
164-
F: FnOnce() -> T,
165-
{
166-
let result = f();
182+
fn call_with_short_backtrace_marker<T: Terminus>(f: &mut dyn FnMut()) {
183+
f();
167184

168-
// prevent this frame from being tail-call optimised away
169-
crate::hint::black_box(());
185+
// (Try to) prevent both ICF and TCO
186+
crate::hint::black_box(T::ID);
187+
}
170188

171-
result
189+
trait Terminus {
190+
const ID: u32;
172191
}
173192

174-
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
175-
/// this is only inline(never) when backtraces in std are enabled, otherwise
176-
/// it's fine to optimize away.
177-
#[cfg_attr(feature = "backtrace", inline(never))]
178-
pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
179-
where
180-
F: FnOnce() -> T,
181-
{
182-
let result = f();
193+
macro_rules! short_backtrace_termini {
194+
($($f:ident => $t:ident($id:literal)),* $(,)?) => {$(
195+
struct $t;
196+
impl Terminus for $t {
197+
const ID: u32 = $id;
198+
}
183199

184-
// prevent this frame from being tail-call optimised away
185-
crate::hint::black_box(());
200+
#[doc(hidden)]
201+
#[unstable(feature = "short_backtrace_termini", reason = "for rustc to have ICE backtraces abbreviated", issue = "none")]
202+
#[inline(always)]
203+
pub fn $f<F, T>(f: F) -> T
204+
where
205+
F: FnOnce() -> T,
206+
{
207+
let mut result = MaybeUninit::<T>::uninit();
208+
let mut f = ManuallyDrop::new(f);
209+
let mut f = || { result.write(unsafe { ManuallyDrop::take(&mut f) }()); };
210+
call_with_short_backtrace_marker::<$t>(&mut f);
211+
unsafe { result.assume_init() }
212+
}
213+
)*};
214+
}
186215

187-
result
216+
short_backtrace_termini! {
217+
__rust_begin_short_backtrace => Begin(0),
218+
__rust_end_short_backtrace => End(1),
188219
}
189220

190221
/// Prints the filename of the backtrace frame.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//! Short backtraces should still be emitted from stripped binaries.
2+
//! Regression test for https://github.com/rust-lang/rust/issues/147846
3+
//
4+
//@ compile-flags: -Cstrip=symbols
5+
//@ exec-env: RUST_BACKTRACE=1
6+
//@ run-fail
7+
//@ check-run-results
8+
//
9+
// Name mangling scheme differences
10+
//@ normalize-stderr: "begin_panic::<&str>" -> "begin_panic"
11+
//
12+
// macOS with `rust.debuginfo-level = "line-tables-only"` (#133997)
13+
//@ normalize-stderr: " begin_panic<&str>" -> " std::panicking::begin_panic"
14+
//
15+
// debuginfo
16+
//@ normalize-stderr: "\n +at [^\n]+" -> ""
17+
18+
fn main() { panic!(); }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
thread 'main' ($TID) panicked at $DIR/short-backtrace-in-stripped.rs:18:13:
3+
explicit panic
4+
stack backtrace:
5+
0: std::panicking::begin_panic
6+
1: <unknown>
7+
2: <unknown>
8+
3: <unknown>
9+
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

0 commit comments

Comments
 (0)