程序减肥,strip,eu-strip 及其符号表

我们要给我们生成的可执行文件和DSO瘦身,因为这样可以节省更多的磁盘空间,所以我们移除了debug信息,移除了符号表信息,同时我们还希望万一出事了,比如coredump了,我们能获取更多的信息,这时候我们又希望有符号表。
    我们等不能做到呢。Linux下是怎么解决这个矛盾的呢?先看第一个问题,程序减肥。
     1 程序减肥
    我写了个简单的代码,main调用了foo,foo调用了bar,其中bar故意访问了非法地址,为了引起core dump。

  1. root@manu:~/code/c/self/debug_symbol# cat test.c
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. int bar()
  5. {
  6. char *p = NULL;
  7. fprintf(stderr,"I am bar,I will core dump\n");
  8. fprintf(stderr,"%s",p);
  9. return 0;
  10. }
  11. int foo()
  12. {
  13. int i ;
  14. fprintf(stderr, "I am foo,I will call bar\n");
  15. bar();
  16. return 0;
  17. }
  18. int main()
  19. {
  20. fprintf(stderr,"I am main, I wll can foo\n");
  21. foo();
  22. return 0;
  23. }
  24. root@manu:~/code/c/self/debug_symbol#

先编译出一个debug版本来,然后我们看下可执行程序的大小

  1. root@manu:~/code/c/self/debug_symbol# ll
  2. 总用量 24
  3. drwxr-xr-x  2 root root 4096 3月 16 15:56 ./
  4. drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
  5. -rwxr-xr-x  1 root root 9703 3月 16 15:56 test*
  6. -rw-r--r--  1 root root  361 3月 16 15:53 test.c
  7. root@manu:~/code/c/self/debug_symbol# readelf -S test

然后我们看下section信息:

  1. root@manu:~/code/c/self/debug_symbol# readelf -S test

然后,我们用strip命令将debug info 去除,指令如下,

  1. root@manu:~/code/c/self/debug_symbol# strip --strip-debug test
  2. root@manu:~/code/c/self/debug_symbol# ll
  3. 总用量 20
  4. drwxr-xr-x  2 root root 4096 3月 16 16:10 ./
  5. drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
  6. -rwxr-xr-x  1 root root 7205 3月 16 16:10 test*
  7. -rw-r--r--  1 root root  361 3月 16 15:53 test.c
  8. 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。但是我们注意到白色两行,是符号表信息和字符串信息,这两个还在。

  1. root@manu:~/code/c/self/debug_symbol# nm test
  2. 08049f28 d _DYNAMIC
  3. 08049ff4 d _GLOBAL_OFFSET_TABLE_
  4. 080485cc R _IO_stdin_used
  5. w _Jv_RegisterClasses
  6. 08049f18 d __CTOR_END__
  7. 。。。。。。。

此时如果执行这个test可执行程序,会产生coredump文件,我们用gdb调试coredump文件的时候,我们可以打印出堆栈信息,因为符号表还在。

  1. root@manu:~/code/c/self/debug_symbol# ulimit -c unlimited
  2. root@manu:~/code/c/self/debug_symbol# ./test
  3. I am main, I wll can foo
  4. I am foo,I will call bar
  5. I am bar,I will core dump
  6. 段错误 (核心已转储)
  7. root@manu:~/code/c/self/debug_symbol# ll
  8. 总用量 224
  9. drwxr-xr-x  2 root root   4096 3月 16 16:23 ./
  10. drwxr-xr-x 31 manu root   4096 3月 16 14:07 ../
  11. -rw-r-----  1 root root 200704 3月 16 16:23 core
  12. -rwxr-xr-x  1 root root   7205 3月 16 16:10 test*
  13. -rw-r--r--  1 root root    361 3月 16 15:53 test.c

此时我们用gdb 调试下:

  1. root@manu:~/code/c/self/debug_symbol# gdb -c core test


    这似乎是个比较合理的点,程序已经瘦了身,没有什么debug信息,一旦出了core dump,还有符号表信息。程序员喜欢。可惜大部分的发行版的程序都会将符号表信息删除。OK,我们进一步减肥。

这一步就会要删掉符号表了,可以直接用:strip命令,或者strip --strip-all命令。

  1. root@manu:~/code/c/self/debug_symbol# strip --strip-all test
  2. root@manu:~/code/c/self/debug_symbol# ll
  3. 总用量 216
  4. drwxr-xr-x  2 root root   4096 3月 16 16:33 ./
  5. drwxr-xr-x 31 manu root   4096 3月 16 14:07 ../
  6. -rw-r-----  1 root root 200704 3月 16 16:23 core
  7. -rwxr-xr-x  1 root root   5520 3月 16 16:33 test*
  8. -rw-r--r--  1 root root    361 3月 16 15:53 test.c
  9. root@manu:~/code/c/self/debug_symbol#

此时的可执行程序test已经从7205减小到了5520,文件进一步瘦了身,此时符号表已经不在了,请看下图: 

    symtab和strtab两个section不见了,section从29个减少到了27个。nm 执行也看不到符号表。
    能不能进一步的减肥呢? 答案是肯定的。上面提到的这些section中 .note.ABI-tag      .gnu.version   .comment  本质上都可以移除:

  1. root@manu:~/code/c/self/debug_symbol# objcopy -R .comment -R .note.ABI-tag -R .gnu.version test
  2. root@manu:~/code/c/self/debug_symbol# ll
  3. 总用量 216
  4. drwxr-xr-x  2 root root   4096 3月 16 16:48 ./
  5. drwxr-xr-x 31 manu root   4096 3月 16 14:07 ../
  6. -rw-r-----  1 root root 200704 3月 16 16:23 core
  7. -rwxr-xr-x  1 root root   5320 3月 16 16:48 test*
  8. -rw-r--r--  1 root root    361 3月 16 15:53 test.c

可以看到test可执行程序再次减小了,从5520减小到了5320.到目前位置,程序依然是可执行的:

  1. root@manu:~/code/c/self/debug_symbol# ./test
  2. I am main, I wll can foo
  3. I am foo,I will call bar
  4. I am bar,I will core dump
  5. 段错误 (核心已转储)
  6. root@manu:~/code/c/self/debug_symbol#

当然了,这种操作其实没必要,对于大型程序而言,用strip移除符号表,文件会变小很多,但是用objcopy移除上面三个section,节省不了多少空间。

    2 符号表与 可执行程序(及DSO)分离
    到目前为止,我们玩的很happy,文件越来越小,节省了大量的空间。可惜给自己挖了个坑。常在河边走,哪能不湿鞋,玩C的人,哪能不处理几个core dump。现在我们把符号表移除了,发生了coredump我们就傻眼了。请看:

  1. root@manu:~/code/c/self/debug_symbol# rm core
  2. root@manu:~/code/c/self/debug_symbol# ulimit -c unlimited
  3. root@manu:~/code/c/self/debug_symbol# ./test
  4. I am main, I wll can foo
  5. I am foo,I will call bar
  6. I am bar,I will core dump
  7. 段错误 (核心已转储)
  8. root@manu:~/code/c/self/debug_symbol# ll
  9. 总用量 216
  10. drwxr-xr-x  2 root root   4096 3月 16 17:03 ./
  11. drwxr-xr-x 31 manu root   4096 3月 16 14:07 ../
  12. -rw-r-----  1 root root 200704 3月 16 17:03 core
  13. -rwxr-xr-x  1 root root   5320 3月 16 16:48 test*
  14. -rw-r--r--  1 root root    361 3月 16 15:53 test.c
  15. root@manu:~/code/c/self/debug_symbol#


    看到堆栈信息里面的这些??,有没有在一种叫天天不应,叫地地不灵的感觉?strip文件的符号表的时候有多爽,现在就有多痛苦。
    有没有一种办法,把符号表信息保留,需要用符号表的时候在将符号表的信息导入?答案是肯定的。

   方法1 使用eu-strip
    eu-strip可以把文件的符号表保存起来,需要用的时候,导入需要的符号表就能调试coredump文件了。
    这次我直接生成了release版本的test了,然后用eu-strip将

  1. root@manu:~/code/c/self/debug_symbol# gcc -o test test.c
  2. root@manu:~/code/c/self/debug_symbol# ll
  3. 总用量 20
  4. drwxr-xr-x  2 root root 4096 3月 16 17:12 ./
  5. drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
  6. -rwxr-xr-x  1 root root 7271 3月 16 17:12 test*
  7. -rw-r--r--  1 root root  361 3月 16 15:53 test.c
  8. root@manu:~/code/c/self/debug_symbol# eu-strip test -f test.sym
  9. root@manu:~/code/c/self/debug_symbol# ll
  10. 总用量 24
  11. drwxr-xr-x  2 root root 4096 3月 16 17:13 ./
  12. drwxr-xr-x 31 manu root 4096 3月 16 14:07 ../
  13. -rwxr-xr-x  1 root root 5592 3月 16 17:13 test*
  14. -rw-r--r--  1 root root  361 3月 16 15:53 test.c
  15. -rwxr-xr-x  1 root root 3524 3月 16 17:13 test.sym*
  16. root@manu:~/code/c/self/debug_symbol#

我们看到生成了test.sym文件,而原始的test文件也变小了,移除了符号表信息。请看下图:
    记性好的同学还记得,用strip之后,是27个section(不算NULL),但是我们用了eu-strip居然多了一个section,定睛一看,原来多一个.gnu_deubg_link.这是啥东东 :

    原来记录的是符号表的位置。OK ,现在我们试一试,调试coredump的时候,打印堆栈信息的时候,有没有符号表。

  1. (gdb) bt
  2. #0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
  3. #1 0xb761d12e in __GI__IO_fputs (str=0x0, fp=0xb775d980) at iofputs.c:37
  4. #2 0x0804847d in bar ()
  5. #3 0x080484b7 in foo ()
  6. #4 0x080484f4 in main ()
  7. (gdb)

Bingo,我们堆栈信息里面有符号表的信息。
   我们进一步思考下,是不是因为符号表文件test.sym在当前目录下所以可以找,我们将符号表换个位置,放入/root下面,看下能否调试:

  1. root@manu:~/code/c/self/debug_symbol# mv test.sym /root/
  2. root@manu:~/code/c/self/debug_symbol# ll
  3. 总用量 216
  4. drwxr-xr-x  2 root root   4096 3月 16 17:39 ./
  5. drwxr-xr-x 31 manu root   4096 3月 16 14:07 ../
  6. -rw-r-----  1 root root 200704 3月 16 17:34 core
  7. -rwxr-xr-x  1 root root   5592 3月 16 17:13 test*
  8. -rw-r--r--  1 root root    361 3月 16 15:53 test.c
  1. (gdb) bt
  2. #0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
  3. #1 0xb761d12e in __GI__IO_fputs (str=0x0, fp=0xb775d980) at iofputs.c:37
  4. #2 0x0804847d in ?? ()
  5. #3 0x080484b7 in ?? ()
  6. #4 0x080484f4 in ?? ()
  7. #5 0xb75d04d3 in __libc_start_main (main=0x80484be, argc=1, ubp_av=0xbfb851c4, init=0x8048500, fini=0x8048570, rtld_fini=0xb778d270 <_dl_fini>,
  8. stack_end=0xbfb851bc) at libc-start.c:226
  9. #6 0x080483a1 in ?? ()
  10. (gdb)

我们发现,gdb找不到符号表。尽管我们有符号表在/root目录下。WHY?我们可以用strace 跟踪下gdb 调试core文件的过程,看下gdb是怎么寻找符号表的。

  1. root@manu:~/code/c/self/debug_symbol# strace gdb -c core test >>strace_search_symbol.log 2>&1

在strace_search_symbol.log中我们发现了下面内容:

  1. access("/usr/lib/debug/.build-id/0d/5ded87764286512bfa6f6a2c4f9993c0669021.debug", F_OK) = -1 ENOENT (No such file or directory)
  2. open("/home/manu/code/c/self/debug_symbol/test.sym", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
  3. open("/home/manu/code/c/self/debug_symbol/.debug/test.sym", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
  4. open("/usr/lib/debug//home/manu/code/c/self/debug_symbol/test.sym", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
  5. 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尝试在以下路径中寻找符号表:

  1. /home/manu/code/c/self/debug_symbol/test.sym
  2. /home/manu/code/c/self/debug_symbol/.debug/test.sym
  3. /usr/lib/debug//home/manu/code/c/self/debug_symbol/test.sym
  4. /usr/lib/debug/home/manu/code/c/self/debug_symbol/test.sym

第一条路径和第二条路径 是因为test的.gnu_debuglink里面记录了符号表为test.sym,所以他去找了当前路径下有无test.sym,我们发现,当前路径下的.debug路径也是gdb搜素的对象。这个结论,可以自行验证。

至于第三四条,则是gdb的默认搜索路径。在公司里面调试的时候,需要将符号表信息放在/usr/lib/debug目录下,我百思不得其解,直到今日方才明白,只是gdb的默认搜索路径,

  1. (gdb) show debug-file-directory
  2. The directory where separate debug symbols are searched for is "/usr/lib/debug".
  3. (gdb)

以我的这个为例,如果我的可执行程序在/home/manu/code/c/self/debug_sysbol/目录下,gdb会尝试在/usr/lib/debug/home/manu/code/c/self/debug_symbol/下寻找符号表。我们验证之:

  1. root@manu:~/code/c/self/debug_symbol# mkdir -p /usr/lib/debug/home/manu/code/c/self/debug_symbol/
  2. root@manu:~/code/c/self/debug_symbol# mv /root/test.sym /usr/lib/debug/home/manu/code/c/self/debug_symbol/
  3. root@manu:~/code/c/self/debug_symbol# ll /usr/lib/debug/home/manu/code/c/self/debug_symbol/
  4. 总用量 12
  5. drwxr-xr-x 2 root root 4096 3月 16 18:27 ./
  6. drwxr-xr-x 3 root root 4096 3月 16 15:23 ../
  7. -rwxr-xr-x 1 root root 3524 3月 16 17:13 test.sym*

看下gdb打印堆栈信息的时候可以找到符号表:

  1. (gdb) bt
  2. #0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
  3. #1 0xb761d12e in __GI__IO_fputs (str=0x0, fp=0xb775d980) at iofputs.c:37
  4. #2 0x0804847d in bar ()
  5. #3 0x080484b7 in foo ()
  6. #4 0x080484f4 in main ()

当然了,如果我们的符号表既不在当前路径下,又不在/usr/lib/debug/+执行路径下,比如我们刚才放到了/root路径下,我们还可以用命令行 symbol-file告诉gdb符号表的位置。

  1. (gdb) bt
  2. #0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
  3. #1 0xb767612e in __GI__IO_fputs (str=0x0, fp=0xb77b6980) at iofputs.c:37
  4. #2 0x0804847d in ?? ()
  5. #3 0x080484b7 in ?? ()
  6. #4 0x080484f4 in ?? ()
  7. #5 0xb76294d3 in __libc_start_main (main=0x80484be, argc=1, ubp_av=0xbf83bfb4, init=0x8048500, fini=0x8048570, rtld_fini=0xb77e6270 <_dl_fini>,
  8. stack_end=0xbf83bfac) at libc-start.c:226
  9. #6 0x080483a1 in ?? ()
  10. (gdb) symbol-file /root/test.sym
  11. Load new symbol table from "/root/test.sym"? (y or n) y
  12. Reading symbols from /root/test.sym...(no debugging symbols found)...done.
  13. (gdb) bt
  14. #0 __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
  15. #1 0xb767612e in __GI__IO_fputs (str=0x0, fp=0xb77b6980) at iofputs.c:37
  16. #2 0x0804847d in bar ()
  17. #3 0x080484b7 in foo ()
  18. #4 0x080484f4 in main ()

    第二种方法: objcopy

  1. root@manu:~/code/c/self/debug_symbol# rm core test
  2. root@manu:~/code/c/self/debug_symbol# ll
  3. 总用量 256
  4. drwxr-xr-x  2 root root   4096 3月 16 19:14 ./
  5. drwxr-xr-x 31 manu root   4096 3月 16 14:07 ../
  6. -rw-r--r--  1 root root 248743 3月 16 18:06 strace_search_symbol.log
  7. -rw-r--r--  1 root root    361 3月 16 15:53 test.c
  8. root@manu:~/code/c/self/debug_symbol# gcc -o test test.c
  9. root@manu:~/code/c/self/debug_symbol# objcopy --only-keep-debug test test.debug
  10. root@manu:~/code/c/self/debug_symbol# strip test
  11. root@manu:~/code/c/self/debug_symbol# ll
  12. 总用量 268
  13. drwxr-xr-x  2 root root   4096 3月 16 19:15 ./
  14. drwxr-xr-x 31 manu root   4096 3月 16 14:07 ../
  15. -rw-r--r--  1 root root 248743 3月 16 18:06 strace_search_symbol.log
  16. -rwxr-xr-x  1 root root   5520 3月 16 19:15 test*
  17. -rw-r--r--  1 root root    361 3月 16 15:53 test.c
  18. -rwxr-xr-x 1 root root 3579 3月 16 19:14 test.debug*
  19. root@manu:~/code/c/self/debug_symbol# objcopy --add-
  20. --add-gnu-debuglink --add-section
  21. root@manu:~/code/c/self/debug_symbol# objcopy --add-gnu-debuglink=test.debug test
  22. root@manu:~/code/c/self/debug_symbol# ll
  23. 总用量 268
  24. drwxr-xr-x  2 root root   4096 3月 16 19:15 ./
  25. drwxr-xr-x 31 manu root   4096 3月 16 14:07 ../
  26. -rw-r--r--  1 root root 248743 3月 16 18:06 strace_search_symbol.log
  27. -rwxr-xr-x  1 root root   5592 3月 16 19:15 test*
  28. -rw-r--r--  1 root root    361 3月 16 15:53 test.c
  29. -rwxr-xr-x  1 root root   3579 3月 16 19:14 test.debug*

我们生成了test.debug和用eu-strip生成的test.sym一样,放在当前目录下,或者放在/usr/lib/debug/home/manu/code/c/self/debug_symbol,都可以使用test.debug获取到符号表。

另外说一句,这两种方法,我发现都只能将符号表信息放到
当前路径和默认路径/usr/lib/debug下对应的路径,放到其他路径下会找不到。eu-strip -f选项和objcopy
--only-keep-debug制定其他路径都没有用。感兴趣的筒子可以自己验证下。
       我制作了pdf格式的文档,需要的可以自行下载:
strip,eu-strip及符号表.pdf

参考文献:
1Separate debug info
2Split debugging info -- symbols

程序减肥,strip,eu-strip 及其符号表的更多相关文章

  1. C++编译器符号表有哪些内容?

    http://blog.csdn.net/wangbingcsu/article/details/48340479 C++编译器符号表有哪些内容? 很早就想写一篇关于符号表的学习小结,可是迟迟不能下笔 ...

  2. Lex与Yacc学习(三)之符号表

    符号表 列举单词表的方式虽然简单但是不全面,如果在词法分析程序运行时可以构建一个单词表,那么就可以在添加新的单词时不用修改词法分析程序. 下面示例便利用符号表实现,即在词法分析程序运行时从输入文件中读 ...

  3. 程序运行之ELF 符号表

    当一个工程中有多个文件的时候,链接的本质就是要把多个不同的目标文件相互粘到一起.就想玩具积木一样整合成一个整体.为了使不同的目标文件之间能够相互粘合,这些目标文件之间必须要有固定的规则才行.比如目标文 ...

  4. GDB如何调试没有符号表(未加-g选项的编译)的程序

    /********************************************************************* * Author  : Samson * Date    ...

  5. 符号表 symbol table 符号 地址 互推

    https://zh.wikipedia.org/wiki/符号表 https://en.wikipedia.org/wiki/Symbol_table 在计算机科学中,符号表是一种用于语言翻译器(例 ...

  6. GCC制作动态库导出符号表【转】

    转自:https://blog.csdn.net/whb_fei/article/details/76974543 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.cs ...

  7. GCC 符号表小结【转】

    转自:https://blog.csdn.net/swedenfeng/article/details/53417085 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog ...

  8. C/C++编译和链接过程详解 (重定向表,导出符号表,未解决符号表)

    详解link  有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错 ...

  9. ELF Format 笔记(七)—— 符号表

    最是那一低头的温柔,像一朵水莲花不胜凉风的娇羞,道一声珍重,道一声珍重,那一声珍重里有蜜甜的忧愁 —— 徐志摩 ilocker:关注 Android 安全(新手) QQ: 2597294287 符号表 ...

随机推荐

  1. docker_openwrt

    http://wiki.openwrt.org/doc/howto/docker_openwrt_image https://www.baidu.com/s?wd=lxc%20docker&r ...

  2. HDU3564 --- Another LIS (线段树维护最值问题)

    Another LIS Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  3. Activity小结

    Log日志类的五种级别 1.由高到低分别是:v.i.d.w.e 2.生命周期有七种状态: onCreate:创建 onStart:启动 onResume:显示(可以与用户交互) onPause:暂停 ...

  4. ubuntu14.04 cocos2d-x-3.6 glfw编译出错解决方案

    lib/libcocos2d.a(CCGLViewImpl-desktop.cpp.o): In function `cocos2d::GLViewImpl::GLViewImpl()': /home ...

  5. JAVA模拟表单提交

    这是我网上搜的,自己使用也蛮方便,所以上传供大家分享. package wzh.Http;   import java.io.BufferedReader; import java.io.IOExce ...

  6. wcf简单的创建和运用

    创建一个控制台应用程序,命名为wcftest,并在同一解决方案中添加一个wcf服务应用程序 在wcf项目中会自动生成Service1.svc服务程序文件和IService1.cs契约接口 IServi ...

  7. 自写图片遮罩层放大功能jquery插件源代码,photobox.js 1.0版,不兼容IE6

    阿嚏~~~ 话说本屌丝没啥开发插件的经验,可是天公不作美,公司须要让我自己开发个图片放大的插件 但公司老大的话,宛如吾皇之圣旨,微臣必当肝脑涂地,莫敢不从啊~~~ 于是乎,作为一个超级小白,本人仅仅能 ...

  8. angularjs基本执行流程

    近期温习了下angularjs执行流程,备记下.以便查看. 主要的执行流程例如以下: 1.用户请求应用起始页. 2.用户的浏览器向server发起一次HTTP连接,然后载入index.html页面,这 ...

  9. AAM(Active Appearance Model)算法介绍

    前面介绍ASM算法(http://blog.csdn.net/carson2005/article/details/8194317)的时候,笔者提到,ASM是基于统计形状模型的基础上进行的,而AAM则 ...

  10. Oracle—RMAN备份(一)

    一.RMAN备份相关概念 1.RMAN备份中表空间不需要处于backup模式下,它备份数据文件,归档日志文件,控制文件,spfile和备份集片,但不备份联机重做日志文件,临时文件和口令文件. 2.备份 ...