为了便于开发,现在经常把可执行文件里的debug信息取出来单独作为一个文件,这样,可以使得可执行文件体积变小很多。比如,rpm包普遍采用这样的方法。
这种情况下,如果要对产生的 coredump进行调试,需要做一些特殊的设置。
下面以一个简单的例子来说明这种方法。
1. 先写几个测试文件:
$ cat lib1.c
#include <stdio.h>
void fun1(void)
{
printf("This is func1\n");
int a = 1234;
*(int*)a = 100;
}
$ cat lib2.c
#include <stdio.h>
void fun2(void)
{
printf("This is lib2\n");
}
$ cat test-build-id.c
#include <stdio.h>
extern void fun1(void);
extern void fun2(void);
int main(void)
{
fun1();
fun2();
return 0;
}
2.编译,取出每个文件的debug信息。
arm-linux-gnueabi-gcc -g -fpic -shared -Wl,--build-id lib2.c -o libtest2.so
arm-linux-gnueabi-gcc -g -fpic -shared -Wl,--build-id lib1.c -o libtest1.so
arm-linux-gnueabi-gcc -g -Wl,--build-id test-build-id.c -o test-build-id -ltest1 -ltest2 -L.
arm-linux-gnueabi-objcopy --only-keep-debug libtest1.so libtest1.so.debug
arm-linux-gnueabi-objcopy --only-keep-debug libtest2.so libtest2.so.debug
arm-linux-gnueabi-objcopy --only-keep-debug test-build-id test-build-id.debug
arm-linux-gnueabi-strip -g libtest1.so
arm-linux-gnueabi-strip -g libtest2.so
arm-linux-gnueabi-strip -g test-build-id
3. 到 ARM系统上运行编出的程序,会产生一个 coredump文件。
下面对coredump文件进行调试
首先要把动态库放到合适的位置,以便gdb能找到。
我把两个动态库放到了
/home/charles/code/build-id/mnt/code/build-id
下面, /mnt/code/build-id 对应着在 ARM目标系统上动态库的位置。
然后,设置 sysroot 为 /home/charles/code/build-id
(gdb) set sysroot /home/charles/code/build-id
Reading symbols from /home/charles/code/build-id/mnt/code/build-id/libtest1.so...(no debugging symbols found)...done.
Reading symbols from /home/charles/code/build-id/mnt/code/build-id/libtest2.so...(no debugging symbols found)...done.
Reading symbols from /home/charles/code/build-id/lib/libc.so.6...done.
Reading symbols from /home/charles/code/build-id/lib/ld-linux.so.3...done.
(gdb) info locals
No symbol table info available.
(gdb) bt full
#0 0xb6ee65e8 in fun1 () from /home/charles/code/build-id/mnt/code/build-id/libtest1.so
No symbol table info available.
#1 0x00010640 in main ()
No symbol table info available.
可以看到,因为动态库没有包含 debug信息,gdb有些信息是查不到。
4. 配置 debug文件。
先查看 libtest1.so.debug的 build-id:
file libtest1.so.debug
libtest1.so.debug: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=c383d3b680a398ba033e9d3793f1ed70f8c4790b, not stripped
先创建目录:
/home/charles/code/debug_files/.build-id
在这个目录下创建目录 c3.
c3为 libtest1.so.debug的 build id的前两个字符。
然后把文件libtest1.so.debug 名字改为 其 build-d的其余的字符:
mv libtest1.so.debug 83d3b680a398ba033e9d3793f1ed70f8c4790b.debug
把这个文件放到 c3目录下:
mv 83d3b680a398ba033e9d3793f1ed70f8c4790b.debug c3/
同理,对另外一个动态库的debug文件做类似的操作。
5. 重新进入到 gdb里面,重新设置debug信息:
(gdb) set debug-file-directory /home/charles/code/debug_files
(gdb) set sysroot /home/charles/code/build-id
Reading symbols from /home/charles/code/build-id/mnt/code/build-id/libtest1.so...Reading symbols from /home/charles/code/debug_files/.build-id/c3/83d3b680a398ba033e9d3793f1ed70f8c4790b.debug...done.
done.
Reading symbols from /home/charles/code/build-id/mnt/code/build-id/libtest2.so...Reading symbols from /home/charles/code/debug_files/.build-id/d1/39eaafb3d70487679e222d5a2e548543ab4dae.debug...done.
done.
Reading symbols from /home/charles/code/build-id/lib/libc.so.6...done.
Reading symbols from /home/charles/code/build-id/lib/ld-linux.so.3...done.
可以看到,现在 gdb找到了动态库的debug文件,并且load进来了。
(gdb) info locals
a = 1234
(gdb) bt
#0 0xb6ee65e8 in fun1 () at lib1.c:7
#1 0x00010640 in main ()
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
0xb6ee6438 0xb6ee65f8 Yes /home/charles/code/build-id/mnt/code/build-id/libtest1.so
0xb6ed5438 0xb6ed55dc Yes /home/charles/code/build-id/mnt/code/build-id/libtest2.so
0xb6dadc40 0xb6e9f9f8 Yes /home/charles/code/build-id/lib/libc.so.6
0xb6ef77c0 0xb6f124b8 Yes /home/charles/code/build-id/lib/ld-linux.so.3
(gdb)
现在能看到具体的信息了。
在把debug信息单独存放的时候,有两种方法:一种是 debug link的方法,另外一种就是 build-id,即是本文提到的方法。
在使用 build-id的时候,gdb 会到 debug-file-directories 设置的目录下检查有没有 .build-id目录,如果有,就到这个目录下查找
nn/nnnnnnnn.debug这样的文件, 其中 nn为文件的 build-id的前两个字符, nnnnn为剩余的字符。
P.S.
debug-file-directories 也可以设置多个不同的目录,之间用 冒号(:)隔开就行。
References:
1. https://sourceware.org/gdb/current/onlinedocs/gdb/Separate-Debug-Files.html