作者原文地址:http://blog.chinaunix.net/uid-24774106-id-3526766.html
我们公司产品里面的可执行程序和动态共享库(DSO)里面的符号表都被移除了,所以每次遇到core dump的时候,都需要将符号表导入到/usr/lib/debug目录下。一直没弄明白为啥是这个目录,能不能是其他目录,今天没啥事儿,研究了下这个主题。
我们要给我们生成的可执行文件和DSO瘦身,因为这样可以节省更多的磁盘空间,所以我们移除了debug信息,移除了符号表信息,同时我们还希望万一出事了,比如coredump了,我们能获取更多的信息,这时候我们又希望有符号表。
我们等不能做到呢。Linux下是怎么解决这个矛盾的呢?先看第一个问题,程序减肥。
1、程序减肥
我写了个简单的代码,main调用了foo,foo调用了bar,其中bar故意访问了非法地址,为了引起core dump。
root@manu:~/code/c/self/debug_symbol# cat test.c
#include
#include
int bar()
{
char *p = NULL;
fprintf(stderr,"I am bar,I will core dump\n");
fprintf(stderr,"%s",p);
return 0;
}
int foo()
{
int i ;
fprintf(stderr, "I am foo,I will call bar\n");
bar();
return 0;
}
int main()
{
fprintf(stderr,"I am main, I wll can foo\n");
foo();
return 0;
}
root@manu:~/code/c/self/debug_symbol#
先编译出一个debug版本来,然后我们看下可执行程序的大小
root@manu:~/code/c/self/debug_symbol# ll
总用量 24
drwxr-xr-x 2 root root 4096 3月 16 15:56 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rwxr-xr-x 1 root root 9703 3月 16 15:56 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
root@manu:~/code/c/self/debug_symbol# readelf -S test
然后我们看下section信息:
root@manu:~/code/c/self/debug_symbol# readelf -S test
root@manu:~/code/c/self/debug_symbol# strip --strip-debug test
root@manu:~/code/c/self/debug_symbol# ll
总用量 20
drwxr-xr-x 2 root root 4096 3月 16 16:10 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rwxr-xr-x 1 root root 7205 3月 16 16:10 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
root@manu:~/code/c/self/debug_symbol#
可执行文件的大小从9703减小到了7205.当然了,我们也可以直接用gcc生成一个release版本的test。
去除掉debug info的test 和之前的test有什么区别呢,我们看下section信息:
我们可以看到.debug_info/.debug_line/.debug_str等六个debug相关的section都已经不在了,原来的35个section减少到了29个section。但是我们注意到白色两行,是符号表信息和字符串信息,这两个还在。
root@manu:~/code/c/self/debug_symbol# nm test
08049f28 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
080485cc R _IO_stdin_used
w _Jv_RegisterClasses
08049f18 d __CTOR_END__
。。。。。。。
root@manu:~/code/c/self/debug_symbol# ulimit -c unlimited
root@manu:~/code/c/self/debug_symbol# ./test
I am main, I wll can foo
I am foo,I will call bar
I am bar,I will core dump
段错误 (核心已转储)
root@manu:~/code/c/self/debug_symbol# ll
总用量 224
drwxr-xr-x 2 root root 4096 3月 16 16:23 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rw-r----- 1 root root 200704 3月 16 16:23 core
-rwxr-xr-x 1 root root 7205 3月 16 16:10 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
root@manu:~/code/c/self/debug_symbol# gdb -c core test
root@manu:~/code/c/self/debug_symbol# strip --strip-all test
root@manu:~/code/c/self/debug_symbol# ll
总用量 216
drwxr-xr-x 2 root root 4096 3月 16 16:33 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rw-r----- 1 root root 200704 3月 16 16:23 core
-rwxr-xr-x 1 root root 5520 3月 16 16:33 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
root@manu:~/code/c/self/debug_symbol#
此时的可执行程序test已经从7205减小到了5520,文件进一步瘦了身,此时符号表已经不在了,请看下图:
root@manu:~/code/c/self/debug_symbol# objcopy -R .comment -R .note.ABI-tag -R .gnu.version test
root@manu:~/code/c/self/debug_symbol# ll
总用量 216
drwxr-xr-x 2 root root 4096 3月 16 16:48 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rw-r----- 1 root root 200704 3月 16 16:23 core
-rwxr-xr-x 1 root root 5320 3月 16 16:48 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
root@manu:~/code/c/self/debug_symbol# ./test
I am main, I wll can foo
I am foo,I will call bar
I am bar,I will core dump
段错误 (核心已转储)
root@manu:~/code/c/self/debug_symbol#
2、符号表与可执行程序(及DSO)分离
到目前为止,我们玩的很happy,文件越来越小,节省了大量的空间。可惜给自己挖了个坑。常在河边走,哪能不湿鞋,玩C的人,哪能不处理几个core dump。现在我们把符号表移除了,发生了coredump我们就傻眼了。请看:
root@manu:~/code/c/self/debug_symbol# rm core
root@manu:~/code/c/self/debug_symbol# ulimit -c unlimited
root@manu:~/code/c/self/debug_symbol# ./test
I am main, I wll can foo
I am foo,I will call bar
I am bar,I will core dump
段错误 (核心已转储)
root@manu:~/code/c/self/debug_symbol# ll
总用量 216
drwxr-xr-x 2 root root 4096 3月 16 17:03 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rw-r----- 1 root root 200704 3月 16 17:03 core
-rwxr-xr-x 1 root root 5320 3月 16 16:48 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
root@manu:~/code/c/self/debug_symbol#
看到堆栈信息里面的这些??,有没有在一种叫天天不应,叫地地不灵的感觉?strip文件的符号表的时候有多爽,现在就有多痛苦。
有没有一种办法,把符号表信息保留,需要用符号表的时候在将符号表的信息导入?答案是肯定的。
方法1 使用eu-strip
eu-strip可以把文件的符号表保存起来,需要用的时候,导入需要的符号表就能调试coredump文件了。
这次我直接生成了release版本的test了,然后用eu-strip将
root@manu:~/code/c/self/debug_symbol# gcc -o test test.c
root@manu:~/code/c/self/debug_symbol# ll
总用量 20
drwxr-xr-x 2 root root 4096 3月 16 17:12 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rwxr-xr-x 1 root root 7271 3月 16 17:12 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
root@manu:~/code/c/self/debug_symbol# eu-strip test -f test.sym
root@manu:~/code/c/self/debug_symbol# ll
总用量 24
drwxr-xr-x 2 root root 4096 3月 16 17:13 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rwxr-xr-x 1 root root 5592 3月 16 17:13 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
-rwxr-xr-x 1 root root 3524 3月 16 17:13 test.sym*
root@manu:~/code/c/self/debug_symbol#
(gdb) bt
#0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
#1 0xb761d12e in __GI__IO_fputs (str=0x0, fp=0xb775d980) at iofputs.c:37
#2 0x0804847d in bar ()
#3 0x080484b7 in foo ()
#4 0x080484f4 in main ()
(gdb)
我们进一步思考下,是不是因为符号表文件test.sym在当前目录下所以可以找,我们将符号表换个位置,放入/root下面,看下能否调试:
root@manu:~/code/c/self/debug_symbol# mv test.sym /root/
root@manu:~/code/c/self/debug_symbol# ll
总用量 216
drwxr-xr-x 2 root root 4096 3月 16 17:39 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rw-r----- 1 root root 200704 3月 16 17:34 core
-rwxr-xr-x 1 root root 5592 3月 16 17:13 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
(gdb) bt
#0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
#1 0xb761d12e in __GI__IO_fputs (str=0x0, fp=0xb775d980) at iofputs.c:37
#2 0x0804847d in ?? ()
#3 0x080484b7 in ?? ()
#4 0x080484f4 in ?? ()
#5 0xb75d04d3 in __libc_start_main (main=0x80484be, argc=1, ubp_av=0xbfb851c4, init=0x8048500, fini=0x8048570, rtld_fini=0xb778d270 <_dl_fini>,
stack_end=0xbfb851bc) at libc-start.c:226
#6 0x080483a1 in ?? ()
(gdb)
root@manu:~/code/c/self/debug_symbol# strace gdb -c core test >>strace_search_symbol.log 2>&1
在strace_search_symbol.log中我们发现了下面内容:
access("/usr/lib/debug/.build-id/0d/5ded87764286512bfa6f6a2c4f9993c0669021.debug", F_OK) = -1 ENOENT (No such file or directory)
open("/home/manu/code/c/self/debug_symbol/test.sym", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/home/manu/code/c/self/debug_symbol/.debug/test.sym", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/debug//home/manu/code/c/self/debug_symbol/test.sym", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/usr/lib/debug/home/manu/code/c/self/debug_symbol/test.sym", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
第一行我们暂且不管,我们发现了,gdb尝试在以下路径中寻找符号表:
/home/manu/code/c/self/debug_symbol/test.sym
/home/manu/code/c/self/debug_symbol/.debug/test.sym
/usr/lib/debug//home/manu/code/c/self/debug_symbol/test.sym
/usr/lib/debug/home/manu/code/c/self/debug_symbol/test.sym
(gdb) show debug-file-directory
The directory where separate debug symbols are searched for is "/usr/lib/debug".
(gdb)
root@manu:~/code/c/self/debug_symbol# mkdir -p /usr/lib/debug/home/manu/code/c/self/debug_symbol/
root@manu:~/code/c/self/debug_symbol# mv /root/test.sym /usr/lib/debug/home/manu/code/c/self/debug_symbol/
root@manu:~/code/c/self/debug_symbol# ll /usr/lib/debug/home/manu/code/c/self/debug_symbol/
总用量 12
drwxr-xr-x 2 root root 4096 3月 16 18:27 ./
drwxr-xr-x 3 root root 4096 3月 16 15:23 ../
-rwxr-xr-x 1 root root 3524 3月 16 17:13 test.sym*
看下gdb打印堆栈信息的时候可以找到符号表:
(gdb) bt
#0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
#1 0xb761d12e in __GI__IO_fputs (str=0x0, fp=0xb775d980) at iofputs.c:37
#2 0x0804847d in bar ()
#3 0x080484b7 in foo ()
#4 0x080484f4 in main ()
当然了,如果我们的符号表既不在当前路径下,又不在/usr/lib/debug/+执行路径下,比如我们刚才放到了/root路径下,我们还可以用命令行 symbol-file告诉gdb符号表的位置。
(gdb) bt
#0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
#1 0xb767612e in __GI__IO_fputs (str=0x0, fp=0xb77b6980) at iofputs.c:37
#2 0x0804847d in ?? ()
#3 0x080484b7 in ?? ()
#4 0x080484f4 in ?? ()
#5 0xb76294d3 in __libc_start_main (main=0x80484be, argc=1, ubp_av=0xbf83bfb4, init=0x8048500, fini=0x8048570, rtld_fini=0xb77e6270 <_dl_fini>,
stack_end=0xbf83bfac) at libc-start.c:226
#6 0x080483a1 in ?? ()
(gdb) symbol-file /root/test.sym
Load new symbol table from "/root/test.sym"? (y or n) y
Reading symbols from /root/test.sym...(no debugging symbols found)...done.
(gdb) bt
#0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
#1 0xb767612e in __GI__IO_fputs (str=0x0, fp=0xb77b6980) at iofputs.c:37
#2 0x0804847d in bar ()
#3 0x080484b7 in foo ()
#4 0x080484f4 in main ()
方法二 objcopy
root@manu:~/code/c/self/debug_symbol# rm core test
root@manu:~/code/c/self/debug_symbol# ll
总用量 256
drwxr-xr-x 2 root root 4096 3月 16 19:14 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rw-r--r-- 1 root root 248743 3月 16 18:06 strace_search_symbol.log
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
root@manu:~/code/c/self/debug_symbol# gcc -o test test.c
root@manu:~/code/c/self/debug_symbol# objcopy --only-keep-debug test test.debug
root@manu:~/code/c/self/debug_symbol# strip test
root@manu:~/code/c/self/debug_symbol# ll
总用量 268
drwxr-xr-x 2 root root 4096 3月 16 19:15 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rw-r--r-- 1 root root 248743 3月 16 18:06 strace_search_symbol.log
-rwxr-xr-x 1 root root 5520 3月 16 19:15 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
-rwxr-xr-x 1 root root 3579 3月 16 19:14 test.debug*
root@manu:~/code/c/self/debug_symbol# objcopy --add-
--add-gnu-debuglink --add-section
root@manu:~/code/c/self/debug_symbol# objcopy --add-gnu-debuglink=test.debug test
root@manu:~/code/c/self/debug_symbol# ll
总用量 268
drwxr-xr-x 2 root root 4096 3月 16 19:15 ./
drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
-rw-r--r-- 1 root root 248743 3月 16 18:06 strace_search_symbol.log
-rwxr-xr-x 1 root root 5592 3月 16 19:15 test*
-rw-r--r-- 1 root root 361 3月 16 15:53 test.c
-rwxr-xr-x 1 root root 3579 3月 16 19:14 test.debug*
另外说一句,这两种方法,我发现都只能将符号表信息放到 当前路径和默认路径/usr/lib/debug下对应的路径,放到其他路径下会找不到。eu-strip -f选项和objcopy --only-keep-debug制定其他路径都没有用。感兴趣的筒子可以自己验证下。
我制作了pdf格式的文档,需要的可以自行下载:
strip,eu-strip及符号表.pdf
参考文献:
1 Separate debug info
2 Split debugging info -- symbols