Author: XianJim lee
It is hard to debug in embedded system, especially the bug can't be reproduced in simulator. Don't be surprise, if it takes several days to find out why the system failed--just because there is no suitable debugger!
You are lucky, if you develop programs in Linux embedded environment. GDB server is powerful tool for you, actually, it helps me solved many difficult problems. It is a pity that it doesn't support shared library, you can't insert break points in the shared library code.
There is a command named add-shared-symbol-files, but it doesn't work. The mechanism of inserting break points is very simple: generally, the debugger insert a piece of special instruction at the address, when the CPU execute that instruction, an exception will be throw, then the debugger take over the control of execution.
Why doesn't it work? The most possible answer is that the symbol does not match the according address. After reading the help information of the command add-symbol-file, I knew that I should specify an address for it. But what address should I specify? We should know where the code of the shared library locates in the memory. You will say, that is simple, we can consult the /proc/$PID/maps. Yes, you are right, but not enough. The following example shows a complete demonstration of debugging shared library.
1. Let's create a shared library.
int foo(int a, int b)
int s = a + b;
2. Then create an executable file that calls the shared library.
extern int foo(int a, int b);
int main(int argc, char* argv)
int s = foo(10, 20);
3. Of course we need a Makefile.
all: so main
gcc -g foo.c -shared -o libfoo.so
gcc -g main.c -L./ -lfoo -o test
rm -f test *.so
4. Make and prepare for running.
# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
5. Run the gdbserver
# gdbserver localhost:2000 ./test
6. Connect to gdbserver and run to function main.
(gdb) symbol-file test
(gdb) target remote localhost:2000
(gdb) b main
7. Now, It is time to check where library is loaded.
# ps -efgrep ./test
(You can get the PID from the output, here is 7186)
# cat /proc/ 7186/maps
It will output something like:
007b1000-007cc000 r-xp 00000000 08:02 2737838 /lib/ld-2.6.so
007cc000-007cd000 r--p 0001a000 08:02 2737838 /lib/ld-2.6.so
007cd000-007ce000 rw-p 0001b000 08:02 2737838 /lib/ld-2.6.so
08048000-08049000 r-xp 00000000 08:02 1759415 /root/writting/gdbserver/test
08049000-0804a000 rw-p 00000000 08:02 1759415 /root/writting/gdbserver/test
4d940000-4da8e000 r-xp 00000000 08:02 2738392 /lib/libc-2.6.so
4da8e000-4da90000 r--p 0014e000 08:02 2738392 /lib/libc-2.6.so
4da90000-4da91000 rw-p 00150000 08:02 2738392 /lib/libc-2.6.so
4da91000-4da94000 rw-p 4da91000 00:00 0
b7efc000-b7efd000 rw-p b7efc000 00:00 0
b7f11000-b7f12000 r-xp 00000000 08:02 1759414 /root/writting/gdbserver/libfoo.so
b7f12000-b7f13000 rw-p 00000000 08:02 1759414 /root/writting/gdbserver/libfoo.so
b7f13000-b7f14000 rw-p b7f13000 00:00 0
bff04000-bff19000 rw-p bffeb000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
This means the code segment of libfoo.so is loaded at 0xb7f11000.
8. With the help of objdump, we can get the offset.
# objdump -h libfoo.so grep text
It will output something like:
.text 00000154 000002f0 000002f0 000002f0 2**4
So, the offset is 0x000002f0
9. Add the loaded address and offset, we can get the real address.
10. Now, we can load the symbol file into gdb.
(gdb) add-symbol-file libfoo.so 0xb7f112f0
add symbol table from file "libfoo.so" at
.text_addr = 0xb7f112f0
(y or n) y
Reading symbols from /root/writting/gdbserver/libfoo.so...done.
11. Done, debug it as normal case.
Well, it works, but it is still complex. If you get better solution, let me know please, thank you in advance.