I would like to debug with gdb an app on an embedded system. This app requires a newer glibc, threading and works correctly by invoking the Linux dynamic loader ld-linux-x86-64.so.2
. What I would like is to attach gdb and see the symbols and stack trace, but the loader "interferes" with gdb.
Here's a sample test.c
file:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
void *subThread(void *arg) {
printf("Thread started after '%d' chars.\n", (long *)arg);
sleep(2);
printf("Thread '%d' ended.\n", (long *)arg);
pthread_exit(NULL);
}
int main() {
pid_t pid = getpid();
if (pid > 0) {
printf("The process id is %d.\n", pid);
printf("Type 'q' to exit, 't' to spawn a thread.\n");
char readChar;
long count = 0;
do {
count++;
readChar = getchar();
if (readChar == 't') {
// Spawn a thread
pthread_t thread_id;
pthread_create(&thread_id, NULL, subThread, (void *) count);
pthread_join(thread_id, NULL);
}
} while(readChar != 'q');
printf("Main ended.\n");
return(0);
} else {
return(1);
}
}
I compile this source file in a system with a specific glibc version like so:
othersystem:~# gcc -O2 -g -lpthread -o test test.c
Then I move the test
binary file into my embedded system, that has an older glibc version and that cannot run test
directly, so I'm copying the loader and the required libraries too. See also my gdb settings.
[~/test] # ls
ld-linux-x86-64.so.2* libc.so.6* libpthread.so.0* libthread_db.so.1 test*
[~/test] # cat ~/.gdbinit
set auto-load safe-path /
set pagination off
set libthread-db-search-path /root/test
Now I run the code and press CTRLZ to pause and I want to attach gdb using the process id. The problem is that gdb sees the loader instead of the application and doesn't load the debug symbols.
[~/test] # /root/test/ld-linux-x86-64.so.2 --library-path /root/test /root/test/test
The process id is 30622.
Type 'q' to exit, 't' to spawn a thread.
^Z
[1]+ Stopped(SIGTSTP) /root/test/ld-linux-x86-64.so.2 --library-path /root/test /root/test/test
[~/test] # gdb /root/test/test 30622
GNU gdb (GDB) 10.1
Reading symbols from /root/test/test...
Attaching to program: /root/test/test, process 30622
warning: Build ID mismatch between current exec-file /root/test/test
and automatically determined exec-file /root/test/ld-linux-x86-64.so.2
exec-file-mismatch handling is currently "ask"
Load new symbol table from "/root/test/ld-linux-x86-64.so.2"? (y or n) y
Reading symbols from /root/test/ld-linux-x86-64.so.2...
(No debugging symbols found in /root/test/ld-linux-x86-64.so.2)
Reading symbols from /root/test/libpthread.so.0...
(No debugging symbols found in /root/test/libpthread.so.0)
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/root/test/libthread_db.so.1".
Reading symbols from /root/test/libc.so.6...
(No debugging symbols found in /root/test/libc.so.6)
Reading symbols from /root/test/ld-linux-x86-64.so.2...
(No debugging symbols found in /root/test/ld-linux-x86-64.so.2)
Reading symbols from /lib/libgcc_s.so.1...
0x00007fa1ef590494 in read () from /root/test/libc.so.6
(gdb) bt
#0 0x00007fa1ef590494 in read () from /root/test/libc.so.6
#1 0x00007fa1ef522670 in _IO_file_underflow () from /root/test/libc.so.6
#2 0x00007fa1ef5237b2 in _IO_default_uflow () from /root/test/libc.so.6
#3 0x00007fa1ef51de68 in getc () from /root/test/libc.so.6
#4 0x00007fa1ef68b110 in ?? ()
#5 0x00007fa1ef68b290 in ?? ()
#6 0x00007fa1ef4a2700 in ?? ()
#7 0x00007ffcdae50bb8 in ?? ()
#8 0x0000000000000000 in ?? ()
I do not know how to tell gdb to load symbols from /root/test/test
; there are interesting articles here and here and also an answer on StackOveflow, but none worked... I'm not able to get the correct address to use the add-symbol-file
directive.
I've tried like so:
[~/test] # cat /proc/30622/maps | grep "r-xp" | grep "/root/test/test" | awk -F'-' '{printf $1}'
7fa1ef68b000
[~/test] # objdump -s --section=".text" /root/test/test | grep Contents -A 1 | tail -n 1 | awk -F' ' '{printf $1}'
10c0
So the address should be 0x7fa1ef68b000 + 0x10c0 = 0x7fa1ef68c0c0, right? But when I type this into gdb, you can see that it doesn't pick up the debug symbols:
(gdb) add-symbol-file /root/test/test 0x7fa1ef68c0c0
add symbol table from file "/root/test/test" at
.text_addr = 0x7fa1ef68c0c0
(y or n) y
Reading symbols from /root/test/test...
(gdb) bt
#0 0x00007fa1ef590494 in read () from /root/test/libc.so.6
#1 0x00007fa1ef522670 in _IO_file_underflow () from /root/test/libc.so.6
#2 0x00007fa1ef5237b2 in _IO_default_uflow () from /root/test/libc.so.6
#3 0x00007fa1ef51de68 in getc () from /root/test/libc.so.6
#4 0x00007fa1ef68b110 in ?? ()
#5 0x00007fa1ef68b290 in ?? ()
#6 0x00007fa1ef4a2700 in ?? ()
#7 0x00007ffcdae50bb8 in ?? ()
#8 0x0000000000000000 in ?? ()
(gdb)
Is there a way to load the debug symbols without manually specifying the address? If I compile the binary to have the interpreter path hardcoded, like so:
othersystem:~# gcc -O2 -g -lpthread -Wl,-rpath /root/test -Wl,--dynamic-linker=/root/test/ld-linux-x86-64.so.2 -o test2 test.c
Then all is working right:
[~/test] # ldd /root/test/test2
linux-vdso.so.1 (0x00007ffcaa59c000)
libpthread.so.0 => /root/test/libpthread.so.0 (0x00007f7265e1d000)
libc.so.6 => /root/test/libc.so.6 (0x00007f7265c5c000)
/root/test/ld-linux-x86-64.so.2 (0x00007f7265e40000)
[~/test] # /root/test/test2
The process id is 23264.
Type 'q' to exit, 't' to spawn a thread.
^Z
[1]+ Stopped(SIGTSTP) ./test2
[~/test] # gdb /root/test/test2 23264
GNU gdb (GDB) 10.1
Reading symbols from /root/test/test2...
Attaching to program: /root/test/test2, process 23264
Reading symbols from /root/test/libpthread.so.0...
(No debugging symbols found in /root/test/libpthread.so.0)
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/root/test/libthread_db.so.1".
Reading symbols from /root/test/libc.so.6...
(No debugging symbols found in /root/test/libc.so.6)
Reading symbols from /root/test/ld-linux-x86-64.so.2...
(No debugging symbols found in /root/test/ld-linux-x86-64.so.2)
Program received signal SIGTSTP, Stopped (user).
0x00007ffba4e7d461 in read () from /root/test/libc.so.6
(gdb) bt
#0 0x00007ffba4e7d461 in read () from /root/test/libc.so.6
#1 0x00007ffba4e0f670 in _IO_file_underflow () from /root/test/libc.so.6
#2 0x00007ffba4e107b2 in _IO_default_uflow () from /root/test/libc.so.6
#3 0x00005613ef88a110 in getchar () at /usr/include/x86_64-linux-gnu/bits/stdio.h:49
#4 main () at test.c:24
EDIT
As asked by Employed Russian, I'm adding the complete output of the maps and readelf (of course I had to use a new pid):
[~/test] # /root/test/ld-linux-x86-64.so.2 --library-path /root/test /root/test/test
The process id is 16873.
Type 'q' to exit, 't' to spawn a thread.
^Z
[1]+ Stopped(SIGTSTP) /root/test/ld-linux-x86-64.so.2 --library-path /root/test /root/test/test
[~/test] # cat /proc/16873/maps
7f5e3767b000-7f5e3767e000 rw-p 00000000 00:00 0
7f5e3767e000-7f5e376a0000 r--p 00000000 00:10 64323010 /root/test/libc.so.6
7f5e376a0000-7f5e377e8000 r-xp 00022000 00:10 64323010 /root/test/libc.so.6
7f5e377e8000-7f5e37834000 r--p 0016a000 00:10 64323010 /root/test/libc.so.6
7f5e37834000-7f5e37835000 ---p 001b6000 00:10 64323010 /root/test/libc.so.6
7f5e37835000-7f5e37839000 r--p 001b6000 00:10 64323010 /root/test/libc.so.6
7f5e37839000-7f5e3783b000 rw-p 001ba000 00:10 64323010 /root/test/libc.so.6
7f5e3783b000-7f5e3783f000 rw-p 00000000 00:00 0
7f5e3783f000-7f5e37845000 r--p 00000000 00:10 64323011 /root/test/libpthread.so.0
7f5e37845000-7f5e37854000 r-xp 00006000 00:10 64323011 /root/test/libpthread.so.0
7f5e37854000-7f5e3785a000 r--p 00015000 00:10 64323011 /root/test/libpthread.so.0
7f5e3785a000-7f5e3785b000 r--p 0001a000 00:10 64323011 /root/test/libpthread.so.0
7f5e3785b000-7f5e3785c000 rw-p 0001b000 00:10 64323011 /root/test/libpthread.so.0
7f5e3785c000-7f5e37862000 rw-p 00000000 00:00 0
7f5e37862000-7f5e37863000 r--p 00000000 00:10 64323013 /root/test/test
7f5e37863000-7f5e37864000 r-xp 00001000 00:10 64323013 /root/test/test
7f5e37864000-7f5e37865000 r--p 00002000 00:10 64323013 /root/test/test
7f5e37865000-7f5e37866000 r--p 00002000 00:10 64323013 /root/test/test
7f5e37866000-7f5e37867000 rw-p 00003000 00:10 64323013 /root/test/test
7f5e37867000-7f5e37868000 r--p 00000000 00:10 64323009 /root/test/ld-linux-x86-64.so.2
7f5e37868000-7f5e37886000 r-xp 00001000 00:10 64323009 /root/test/ld-linux-x86-64.so.2
7f5e37886000-7f5e3788e000 r--p 0001f000 00:10 64323009 /root/test/ld-linux-x86-64.so.2
7f5e3788e000-7f5e3788f000 r--p 00026000 00:10 64323009 /root/test/ld-linux-x86-64.so.2
7f5e3788f000-7f5e37890000 rw-p 00027000 00:10 64323009 /root/test/ld-linux-x86-64.so.2
7f5e37890000-7f5e37891000 rw-p 00000000 00:00 0
7f5e3905f000-7f5e39080000 rw-p 00000000 00:00 0 [heap]
7fff1fcd3000-7fff1fcf4000 rw-p 00000000 00:00 0 [stack]
7fff1fd82000-7fff1fd85000 r--p 00000000 00:00 0 [vvar]
7fff1fd85000-7fff1fd87000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
[~/test] # readelf -Wl /root/test/test
Elf file type is DYN (Shared object file)
Entry point 0x1160
There are 11 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x000268 0x000268 R 0x8
INTERP 0x0002a8 0x00000000000002a8 0x00000000000002a8 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000768 0x000768 R 0x1000
LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x0002fd 0x0002fd R E 0x1000
LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000210 0x000210 R 0x1000
LOAD 0x002dd8 0x0000000000003dd8 0x0000000000003dd8 0x000290 0x0002a8 RW 0x1000
DYNAMIC 0x002de8 0x0000000000003de8 0x0000000000003de8 0x0001f0 0x0001f0 RW 0x8
NOTE 0x0002c4 0x00000000000002c4 0x00000000000002c4 0x000044 0x000044 R 0x4
GNU_EH_FRAME 0x002098 0x0000000000002098 0x0000000000002098 0x000044 0x000044 R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x002dd8 0x0000000000003dd8 0x0000000000003dd8 0x000228 0x000228 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .dynamic .got .got.plt .data .bss
06 .dynamic
07 .note.ABI-tag .note.gnu.build-id
08 .eh_frame_hdr
09
10 .init_array .fini_array .dynamic .got
[~/test] # readelf -WS /root/test/test
There are 37 section headers, starting at offset 0x4b18:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 00000000000002a8 0002a8 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 00000000000002c4 0002c4 000020 00 A 0 0 4
[ 3] .note.gnu.build-id NOTE 00000000000002e4 0002e4 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000308 000308 000028 00 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000000330 000330 000168 18 A 6 1 8
[ 6] .dynstr STRTAB 0000000000000498 000498 0000da 00 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000000572 000572 00001e 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000000590 000590 000040 00 A 6 2 8
[ 9] .rela.dyn RELA 00000000000005d0 0005d0 0000d8 18 A 5 0 8
[10] .rela.plt RELA 00000000000006a8 0006a8 0000c0 18 AI 5 23 8
[11] .init PROGBITS 0000000000001000 001000 000017 00 AX 0 0 4
[12] .plt PROGBITS 0000000000001020 001020 000090 10 AX 0 0 16
[13] .plt.got PROGBITS 00000000000010b0 0010b0 000008 08 AX 0 0 8
[14] .text PROGBITS 00000000000010c0 0010c0 000231 00 AX 0 0 16
[15] .fini PROGBITS 00000000000012f4 0012f4 000009 00 AX 0 0 4
[16] .rodata PROGBITS 0000000000002000 002000 000097 00 A 0 0 8
[17] .eh_frame_hdr PROGBITS 0000000000002098 002098 000044 00 A 0 0 4
[18] .eh_frame PROGBITS 00000000000020e0 0020e0 000130 00 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000003dd8 002dd8 000008 08 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000003de0 002de0 000008 08 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000003de8 002de8 0001f0 10 WA 6 0 8
[22] .got PROGBITS 0000000000003fd8 002fd8 000028 08 WA 0 0 8
[23] .got.plt PROGBITS 0000000000004000 003000 000058 08 WA 0 0 8
[24] .data PROGBITS 0000000000004058 003058 000010 00 WA 0 0 8
[25] .bss NOBITS 0000000000004070 003068 000010 00 WA 0 0 16
[26] .comment PROGBITS 0000000000000000 003068 00001c 01 MS 0 0 1
[27] .debug_aranges PROGBITS 0000000000000000 003084 000040 00 0 0 1
[28] .debug_info PROGBITS 0000000000000000 0030c4 000626 00 0 0 1
[29] .debug_abbrev PROGBITS 0000000000000000 0036ea 000200 00 0 0 1
[30] .debug_line PROGBITS 0000000000000000 0038ea 00020c 00 0 0 1
[31] .debug_str PROGBITS 0000000000000000 003af6 000308 01 MS 0 0 1
[32] .debug_loc PROGBITS 0000000000000000 003dfe 0000e8 00 0 0 1
[33] .debug_ranges PROGBITS 0000000000000000 003ee6 000090 00 0 0 1
[34] .symtab SYMTAB 0000000000000000 003f78 000780 18 35 52 8
[35] .strtab STRTAB 0000000000000000 0046f8 0002bc 00 0 0 1
[36] .shstrtab STRTAB 0000000000000000 0049b4 000160 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
FINAL SOLUTION
For an easier (future) reading, I'm reporting here the output after Employed Russian's solution.
[~/test] # /root/test/ld-linux-x86-64.so.2 --library-path /root/test /root/test/test
The process id is 16873.
Type 'q' to exit, 't' to spawn a thread.
^Z
[1]+ Stopped(SIGTSTP) /root/test/ld-linux-x86-64.so.2 --library-path /root/test /root/test/test
[~/test] # cat /proc/16873/maps | grep "/root/test/test" | awk -F'-' '{print "0x" $1; exit; }'
0x7f5e37862000
[~/test] # readelf -WS /root/test/test | grep '\.text ' | awk '{print "0x" $5}'
0x0010c0
[~/test] # gdb /root/test/test 16873
GNU gdb (GDB) 10.1
Reading symbols from /root/test/test...
Attaching to program: /root/test/test, process 16873
warning: Build ID mismatch between current exec-file /root/test/test
and automatically determined exec-file /root/test/ld-linux-x86-64.so.2
exec-file-mismatch handling is currently "ask"
Load new symbol table from "/root/test/ld-linux-x86-64.so.2"? (y or n) y
Reading symbols from /root/test/ld-linux-x86-64.so.2...
(No debugging symbols found in /root/test/ld-linux-x86-64.so.2)
Reading symbols from /root/test/libpthread.so.0...
(No debugging symbols found in /root/test/libpthread.so.0)
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/root/test/libthread_db.so.1".
Reading symbols from /root/test/libc.so.6...
(No debugging symbols found in /root/test/libc.so.6)
Reading symbols from /root/test/ld-linux-x86-64.so.2...
(No debugging symbols found in /root/test/ld-linux-x86-64.so.2)
Program received signal SIGTTIN, Stopped (tty input).
0x00007f5e37768461 in read () from /root/test/libc.so.6
(gdb) bt
#0 0x00007f5e37768461 in read () from /root/test/libc.so.6
#1 0x00007f5e376fa670 in _IO_file_underflow () from /root/test/libc.so.6
#2 0x00007f5e376fb7b2 in _IO_default_uflow () from /root/test/libc.so.6
#3 0x00007f5e37863110 in ?? ()
#4 0x00007f5e37863290 in ?? ()
#5 0x00007f5e37863160 in ?? ()
#6 0x00007fff1fcf2578 in ?? ()
#7 0x0000000000000000 in ?? ()
(gdb) add-symbol-file /root/test/test 0x7f5e37862000+0x0010c0
add symbol table from file "/root/test/test" at
.text_addr = 0x7f5e378630c0
(y or n) y
Reading symbols from /root/test/test...
(gdb) bt
#0 0x00007f5e37768461 in read () from /root/test/libc.so.6
#1 0x00007f5e376fa670 in _IO_file_underflow () from /root/test/libc.so.6
#2 0x00007f5e376fb7b2 in _IO_default_uflow () from /root/test/libc.so.6
#3 0x00007f5e37863110 in getchar () at /usr/include/x86_64-linux-gnu/bits/stdio.h:49
#4 main () at test.c:24
#5 0x00007f5e376a209b in __libc_start_main () from /root/test/libc.so.6
#6 0x00007f5e3786318a in _start ()
(gdb) q
A debugging session is active.
Inferior 1 [process 16873] will be detached.
Quit anyway? (y or n) y
Detaching from program: /root/test/ld-linux-x86-64.so.2, process 16873
[Inferior 1 (process 16873) detached]
[1]+ Stopped(SIGTTIN) /root/test/ld-linux-x86-64.so.2 --library-path /root/test /root/test/test
So I have to reply Y to the request of the file mismatch and then add-symbol-file
with the path of the real executable followed by the address (base + offset) retrieved using /proc/pid/maps
(first result) and readelf
.