使用strip, eu-strip, objcopy等剝離與導回符號表及調試信息

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 <option[s]> <in-file[s]>

常用選項如下:

-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 命令移除和添加符號表及調試信息

  1. 刪除指定的section
    objcopy -R .comment -R .note.ABI-tag
  1. 移除和添加符號表及調試信息
    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去除調試符號信息。

-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

參考文獻

  • https://www.technovelty.org/code/split-debugging-info-symbols.html
  • https://www.technovelty.org/code/separate-debug-info.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章