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"):
|
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) |
|
} else { |
(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 (28ec93b) 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 #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.
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"):backtrace-rs/src/symbolize/gimli/libs_dl_iterate_phdr.rs
Lines 75 to 80 in 28ec93b
(which is with
(deleted)suffix, and in any case, the path no longer exists).If we just use
/proc/self/exedirectly - things work. This example demonstrates that:When run against master (28ec93b) I get:
I apply this patch:
and get:
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 #488 fixed (when
/proc/self/exeis the interpreter we should still use/proc/pid/mapsand not/proc/self/exe) but other than that I believe this fix is okay.