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 命令移除和添加符號表及調試信息
- 刪除指定的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去除調試符號信息。
-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