rust-lang / rust-lang/backtrace-rs
Self backtrace of deleted binaries
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 628
- Forks
- 291
- PR merge metrics
- No merged PRs in 30d
Description
In case the main binary is deleted, getting a backtrace from it fails, because the path we try to work against is the result of readlink("/proc/self/exe"):
https://github.com/rust-lang/backtrace-rs/blob/28ec93b503bf0410745bc3d571bf3dc1caac3019/src/symbolize/gimli/libs_dl_iterate_phdr.rs#L75-L80
(which is with (deleted) suffix, and in any case, the path no longer exists).
If we just use /proc/self/exe directly - things work. This example demonstrates that:
$ cat src/main.rs
fn inner() {
// Delete our own binary from disk.
let exe = std::env::current_exe().unwrap();
std::fs::remove_file(&exe).unwrap();
// Capture and print the backtrace.
let bt = backtrace::Backtrace::new();
eprintln!("\n{bt:?}");
}
fn main() {
inner();
}
$ cat Cargo.toml
[package]
name = "bt-deleted-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
backtrace = { path = "/home/jong/src/rust/backtrace-rs" }
[profile.release]
debug = true # keep debug info (DWARF)
strip = "debuginfo" # then strip DWARF, keeping .symtab (like `strip --strip-debug`)
When run against master (28ec93b503bf0410745bc3d571bf3dc1caac3019) I get:
0: <unknown>
1: <unknown>
2: <unknown>
3: <unknown>
4: <unknown>
5: __libc_start_call_main
at ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
6: __libc_start_main_impl
at ./csu/../csu/libc-start.c:360:3
7: <unknown>
I apply this patch:
diff --git i/src/symbolize/gimli/libs_dl_iterate_phdr.rs w/src/symbolize/gimli/libs_dl_iterate_phdr.rs
index 2d1da7c..e5c21e2 100644
--- i/src/symbolize/gimli/libs_dl_iterate_phdr.rs
+++ w/src/symbolize/gimli/libs_dl_iterate_phdr.rs
@@ -72,11 +72,8 @@ unsafe extern "C" fn callback(
let no_given_name = dlpi_name.is_null()
// SAFETY: we just checked for null
|| unsafe { *dlpi_name == 0 };
- let name = if is_static {
- // don't try to look up our name from /proc/self/maps, it'll get silly
- env::current_exe().unwrap_or_default().into_os_string()
- } else if is_main && no_given_name {
- infer_current_exe(&maps, dlpi_addr as usize)
+ let name = if is_static || (is_main && no_given_name) {
+ OsString::from("/proc/self/exe")
} else {
// this fallback works even if we are main, because some platforms give the name anyways
if dlpi_name.is_null() {
and get:
0: bt_deleted_demo::main
1: std::sys::backtrace::__rust_begin_short_backtrace
2: std::rt::lang_start::{{closure}}
3: std::rt::lang_start_internal
4: main
5: __libc_start_call_main
at ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
6: __libc_start_main_impl
at ./csu/../csu/libc-start.c:360:3
7: _start
This is relevant in one of our apps, where the binary might be deleted in some cases but we want the self-backtrace to keep working as expected.
Do you think it's a valid change? I'm happy to submit it. We need some handling around what https://github.com/rust-lang/backtrace-rs/pull/488 fixed (when /proc/self/exe is the interpreter we should still use /proc/pid/maps and not /proc/self/exe) but other than that I believe this fix is okay.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/symbolize/gimli/libs_dl_iterate_phdr.rs, especially the callback's executable-name selection, and compare it with the behavior addressed by pull request 488. Reproduce the deleted-binary example from the issue, then verify that self-backtraces resolve deleted binaries while interpreter cases still use /proc/pid/maps when required.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- operating-systems
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100