I would like to strip an ELF binary compiled with debug information and keep only the minimal required information to provide symbols / source code location output in a GDB backtrace.
It would be binary post-processing to “downgrade” from -g
to -g1
.
Consider this example C++ source file hello.cpp:
#include <iostream>
#include <cstring>
static void *invalid_mem = (void *)1;
static void crash() {
memset((char *)invalid_mem, 1, 100);
}
int main() {
std::cout << "Hello World!" << std::endl;
crash();
return 0;
}
I compile it with debug information, run the produced hello
binary to get a core file and print the backtrace with GDB:
$ g++ -g hello.cpp -o hello
$ ./hello
Hello World!
Segmentation fault (core dumped)
$ gdb --quiet -ex bt -ex quit hello core
Reading symbols from hello...
[New LWP 479]
Core was generated by `./hello'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007f16330982e0 in __memset_avx2_unaligned_erms () from /lib64/libc.so.6
#0 0x00007f16330982e0 in __memset_avx2_unaligned_erms () from /lib64/libc.so.6
#1 0x00000000004011a3 in crash () at hello.cpp:7
#2 0x00000000004011cb in main () at hello.cpp:12
If I strip the debug information from the hello
binary, I lose the source file location output in GDB but not the symbols since the .symtab
section was not removed:
$ cp hello hello.full
$ objcopy --strip-debug hello.full hello
$ gdb --quiet -ex bt -ex quit hello core
Reading symbols from hello...
(No debugging symbols found in hello)
[New LWP 512]
Core was generated by `./hello'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007fc20289f2e0 in __memset_avx2_unaligned_erms () from /lib64/libc.so.6
#0 0x00007fc20289f2e0 in __memset_avx2_unaligned_erms () from /lib64/libc.so.6
#1 0x00000000004011a3 in crash() ()
#2 0x00000000004011cb in main ()
According to the DWARF 4 standard specification (DWARF4 (pdf), the .debug_line
section seems to be the one I need to keep but it does not seem to be enough:
$ objcopy --strip-debug --keep-section .debug_line hello.full hello
$ diff --color -Naur <(readelf -S hello.full | grep -oP '.K.[^ ]+') <(readelf -S hello | grep -oP '.K.[^ ]+')
--- /dev/fd/63 2024-04-29 12:23:49.023699848 +0200
+++ /dev/fd/62 2024-04-29 12:23:49.027699860 +0200
@@ -24,11 +24,7 @@
bss
comment
gnu.build.a[...]
-debug_aranges
-debug_info
-debug_abbrev
debug_line
-debug_str
symtab
strtab
shstrtab
$ gdb --quiet -ex bt -ex quit hello core
Reading symbols from hello...
(No debugging symbols found in hello)
[New LWP 572]
Core was generated by `./hello'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007f182a0462e0 in __memset_avx2_unaligned_erms () from /lib64/libc.so.6
#0 0x00007f182a0462e0 in __memset_avx2_unaligned_erms () from /lib64/libc.so.6
#1 0x00000000004011a3 in crash() ()
#2 0x00000000004011cb in main ()
I have tried adding/removing individual .debug_*
sections but it seems they are tied to .debug_info
and nothing will work if this section is removed.
It would be even better if this could work with GDB’s MiniDebugInfo to be able to fully strip the ELF binary and still get the symbols and source file location output in the GDB backtrace:
# Adding .gnu_debugdata section in binary for minidebuginfo (see link)
$ gdb --quiet -ex bt -ex quit hello core
Reading symbols from hello...
Reading symbols from .gnu_debugdata for hello...
(No debugging symbols found in .gnu_debugdata for hello)
[New LWP 599]
Core was generated by `./hello'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007f1de1f2d2e0 in __memset_avx2_unaligned_erms () from /lib64/libc.so.6
#0 0x00007f1de1f2d2e0 in __memset_avx2_unaligned_erms () from /lib64/libc.so.6
#1 0x00000000004011a3 in crash() ()
#2 0x00000000004011cb in main ()
I know that I could use -g1
instead of -g
but I need the full debug information (-g
) that would be stripped and stored remotely while retaining minimal local debug information (-g1
) in the binary.
Does anyone know if this is achievable?