一、开发流程
1. 编译可执行文件1 #include <stdio.h> 2 #include <unistd.h> 3 4 void test() 5 { 6 char * s = "hello world\n"; 7 while(1){ 8 //int v = 0/0; 9 printf("%s\n", s); 10 sleep(1); 11 } 12 } 13 14 int main() 15 { 16 test(); 17 }View Code 2. 生成独立的调试符号文件
gcc -ggdb3 test.c -o test3. 删除可执行文件内的调试符号
objcopy --only-keep-debug test test.debug strip --strip-debug --strip-unneeded test4. 一般情况 在 ELF 文件内埋桩指定符号文件,gdb 就可以在调试的时候自动加载调试符号了:
objcopy --add-gnu-debuglink=test.debug test在 ELF文件内也可以看到 .gnu_debuglink 段内保存的调试符号文件。 但是,当我们换了调试环境,或者符号文件路径和 ELF 文件内的路径不匹配的时候,就会报符号找不到的错,这时候仍然需要手动处理符号文件。
二、开始gdb调试
未通过 .gnu_debuglink 指定符号文件时,通过 bt 查看进程的调用栈,我们会看到缺失的符号无法解析。gdb 执行 bt 指令时, 会拿地址信息在已知的符号表内搜索最接近的地址。显示问号,就是说 gdb 完全找不到接近的符号,这时候,我们通过 info symbol [address] 指令也是得不到地址对应的符号信息的。 尝试手动加载调试符号文件 gdb 需要知道 [symbol] -> [address] 的映射信息,而 gdb 正好提供了 symbol-file 和 add-symbol-file 指令来手动添加这个信息。因此,我们要做的就是把 .debug 文件内的符号信息告诉 gdb 。先看下 .debug 文件内保存的符号信息: 其中 _start 符号是个特殊的符号,一般是 ELF 文件的入口。通过 readelf -h 命令,我们可以看到 ELF 文件的入口地址与 _start 符号地址是相同的。 该地址也是 readelf -S 输出的 .text 段的地址很明显,这里的地址和 bt trace 里的地址相差甚远,由于操作系统的保护机制(ASLR) ,ELF 文件并不会直接加载到这个地址,而是会加载到一个随机地址上。因此,我们需要找到这个地址,并把符号表内所有符号的地址都加上这个偏移量(gdb 应该也是可以自动完成这个操作的?)
找到 .text 段加载的位置:
通过 cat /proc/[pid]/maps 我们可以获得进程内存镜像内所有段信息,其中以 ELF 文件名命名并且带有可执行权限的段,就是该 ELF 文件 .text 段加载的地址,这也是我们希望 gdb: info address _start 指令得到的地址。gdb 加载调试符号:
由于我们的目标是让 gdb 解析 .text 段地址为 addr(.text) , 也就是这里的 0x56266ea06000, 而当前符号文件内的 .text 段地址是 addr(elf.entry_point),也就是 0x1060,所以我们偏移量的计算方法是: addr(.text) - addr(elf.entry_point) 添加符号文件指令: symbol-file xxx.debug -o offset 给个调试符号文件,给这个调试文件一个偏移地址,gdb加载符号文件的时候,会自动把符号对应的地址都加上这个偏移量。 ok, 符号解析出来了,查看对应内存上的数据和 ELF文件内的数据是对的上的。 看下 trace,诡异,仍然不对...detach & attach 后一切正常了,似乎是 gdb 的一个bug,先attach后加载符号文件会有问题。
如果需要解析 ELF 的其它 section 同理 ... 似乎写个脚本自动处理更好~三、动态加载的SO添加符号文件:
代码:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include "test.h" 4 5 static void test() 6 { 7 char * s = "hello so\n"; 8 while(1){ 9 printf("%s\n", s); 10 sleep(1); 11 } 12 } 13 14 void test_so() 15 { 16 test(); 17 }View Code
1 #include "test.h" 2 3 int main() 4 { 5 test_so(); 6 }View Code
编译 & 链接 & strip:
gcc test.c -ggdb3 -shared -fPIC -o libtest.so gcc test.c -ggdb3 -shared -o libtest.so objcopy --only-keep-debug libtest.so libtest.so.debug strip --strip-debug --strip-unneeded libtest.so