1. 符号表信息和调试信息

符号表信息(symbols)和调试信息(debug info)是由不同段区分的。

使用 readelf -S binfile 可以查看ELF文件的所有段。

调试信息相关的段:

# readelf -S a.out | grep debug
  [27] .debug_aranges    PROGBITS         0000000000000000  000016d0
  [28] .debug_info       PROGBITS         0000000000000000  00001700
  [29] .debug_abbrev     PROGBITS         0000000000000000  00001a0f
  [30] .debug_line       PROGBITS         0000000000000000  00001adb
  [31] .debug_str        PROGBITS         0000000000000000  00001bd2

符号表相关的段:

# readelf -S a.out | grep tab
  [32] .symtab           SYMTAB           0000000000000000  00001e18
  [33] .strtab           STRTAB           0000000000000000  00002670
  [34] .shstrtab         STRTAB           0000000000000000  00002a8f

注: 下文中提及的符号表相关段将不包括 .shstrtab 段,因其不会被strip或eu-strip移除。

RedHat的system libraries仍保留symbols;这使得它的库文件稍大,但调试方便;

Debian的system libraries不保留symbols,而是将symbols和调试信息都保存在.debug文件中; 这样, 系统库更小,但调试时需要拥有这些.debug文件。

2. strip命令

strip

常用选项如下:

-s --strip-all
    Remove all symbol and relocation information
    注: 删除其他符号表段和调试信息段,但不删除 .shstrtab 段
    
-g -S -d --strip-debug
    Remove all debugging symbols & sections
    这几个选项的功能是一样,即移除上述5个".debug_"开头的调试信息段,仍会保留符号表
    
--only-keep-debug
    Strip everything but the debug information
    注:段的总数量没有减少,但文件大小减少了;对比了"readelf -S"输出中的"offset"段,发现其中前面若干段的offset都没有变化,即size为0了。
    
-R --remove-section=<name>
    Also remove section <name> from the output
    移除指定段,比如 
    strip --remove-section=.symtab a.out
    strip --remove-section=.strtab a.out

不输入任何选项的默认行为是”-s”,即”–strip-all”.

3. eu-strip 命令

功能: 可将符号表和调试信息都导入指定文件中,以减小原二进制文件的大小。

至于如何将导出的文件告知gdb,请参见下面的第5节”objcopy命令”

使用举例:

eu-strip a.out -f a.debug

以上命令将a.out中的符号表段和调试信息段都移出到 a.debug 文件中。这样,a.out的size会减小很多。

而此时,a.out 中会多一个 .gnu_debuglink 段,它是用来保存符号表位置的。

之后,再用gdb去打开并运行 a.out 时,gdb还可以找到 a.debug 这样的符号表及调试信息文件。

另注: CentOS安装eu-strip

yum install elfutils

4. gdb 寻找符号表和调试信息文件

用 gdb 查看 coredump 的时候,或者用 gdb 去运行上述被剥离了符号表和调试信息的二进制文件时,gdb会去自动搜索符号表。

gdb 会去查找当前目录、gdb默认的搜索路径 /usr/lib/debug 、 以及 /usr/lib/debug 下的子路径。具体顺序和具体子路径,请参阅参考文档。

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

如果符号表文件既不在当前目录,也不在 /usr/lib/debug, 那么可以使用 命令告诉gdb去哪里找到符号表,如下:

(gdb) symbol-file /root/test.sym
(gdb) bt 

5. objcopy 命令移除和添加符号表及调试信息

删除指定的section

objcopy -R .comment -R .note.ABI-tag

移除和添加符号表及调试信息

    gcc -g -o test test.c
    
    # test.debug 将包含调试信息和符号表; 而test将只包含调试信息
    objcopy --only-keep-debug test test.debug
    
    # 从test文件里剥离debug段
    objcopy --strip-debug test
    
    # 更彻底地,上面这句可以换成下面这句以移除所有的debug信息和符号表
    strip -s test 
    
    # 在二进制文件 test 中添加 .gnu_debuglink 段以指向符号表和调试信息文件
    objcopy --add-gnu-debuglink=test.debug test
    
    # objdump 命令可以查看指定的section
    objdump -s -j .gnu_debuglink test 

6. 使用链接器ld去除符号表

动态链接库是ELF(Executable and Linkable Format)文件的一种,其中包含了2个符号表:

  • symtab 包含大量的信息(包括全局符号global symbols)
  • dynsym 只保留.symtab中的全局符号
  • dynsym 是 .symtab 的子集;strip命令会去掉ELF文件中.symtab,但不会去掉.dynsym

使用ld 的 -s 和 -S 选项可以在链接的时候去除符号表。-s去除所有符号表信息;-S去除调试符号信息。

--strip-all
    Omit all symbol information from the output file.

-S
--strip-debug
    Omit debugger symbol information (but not all symbols) from the output file.

7. gcc 静态编译

# 让可执行文件没有.dynsym动态链接表;在支持动态链接的系统上,阻止连接共享库。该选项在其它系统上无效。
gcc -static

# 让可执行文件没有.dynstr动态链接字符表;不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。
gcc -nostdlib

参考文献