网站首页 > 博客文章 正文
在项目的开发过程中,为了方便调试,我们一般要在项目的编译时通过添加-g/-ggdb等选项来添加调试信息,从而方便开发过程中的调试。而在程序发布的时候,我们需要删除其中的符号信息。
我们在文章<<[Linux C/C++]如何正确分离可执行程序及其调试符号?>>已经介绍了为什么要删除可执行程序中符号信息,这里不再介绍。
那么,我们应该如何删除ELF目标文件中的符号信息呢?
下面我们以一个简单的例子来说明。
如下的工程中有4个源文件组成,其中main.c为main函数所在为源文件。
[root:~/work/v1/gdb/no-symbols/test1]# tree
.
├── main.c
├── mod1.c
├── mod2.c
├── mod3.c
0 directories, 4 files
[root:~/work/v1/gdb/no-symbols/test1]#
1) 源码
//file name: main.c
#include <stdio.h>
int mod1_func();
int main()
{
printf("in %s...\n", __func__);
mod1_func();
return 0;
}
//file name: mod1.c
#include <stdio.h>
int mod2_func();
int mod1_func()
{
printf("enter %s...\n", __func__);
mod2_func();
return 0;
}
//file name: mod2.c
#include <stdio.h>
int mod3_func();
int mod2_func()
{
printf("enter %s...\n", __func__);
mod3_func();
return 0;
}
//file name: mod3.c
#include <unistd.h>
#include <stdio.h>
int mod3_func()
{
unsigned counter = 0;
printf("enter %s...\n", __func__);
while(1) {
printf("counter:%d\n", counter++);
sleep(5);
}
return 0;
}
2) 编译
[root:~/work/v1/gdb/no-symbols/test1]# gcc -g -o prog main.c mod1.c mod2.c mod3.c
[root:~/work/v1/gdb/no-symbols/test1]#
3) 检查生成的可执行文件 - prog
[root:~/work/v1/gdb/no-symbols/test1]# ls
main.c mod1.c mod2.c mod3.c prog
[root:~/work/v1/gdb/no-symbols/test1]#
[root:~/work/v1/gdb/no-symbols/test1]# file prog
prog: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0c686fa8f234e7fe6e7d8323dce7a067fd8187fe, for GNU/Linux 3.2.0, with debug_info, not stripped
[root:~/work/v1/gdb/no-symbols/test1]#
由于我们在编译的时候使用了-g选项,所以通过file命令查看prog时,可以看到其中的” with debug_info”。
删除prog中的符号信息
一般来说,我们都是通过strip或objcopy命令来删除可执行程序中的符号信息的。
对于strip命令来说,我们可以通过man strip来查看strip命令的帮助信息,其中常用的删除符号信息的命令选项包括:
? strip -s 或 strip --strip-all
不带任何选项的strip相当于strip -s,即strip --strip-all:
man strip
……
-s
--strip-all
Remove all symbols.
该选项用于删除二进制目标文件中的符号表(.symtab)和调试节信息。--strip-all选项可以极大地减小文件大小,但也会使目标文件丧失调试的能力。
对于可重定位目标文件或共享目标文件(静态链接库和动态链接库),最好不要使用strip --strip-all进行瘦身,而应该考虑使用--strip-debug或--strip-unneeded 2个strip选项进行瘦身。这是因为在可重定位目标文件或共享目标文件可能会参与链接过程,需要使用符号表(.symtab)中的信息来解决符号引用的问题,如果删除了它们的符号表,就会造成链接失败。而对于可执行文件,符号表被strip后并不会影响可执行文件的执行,因为对于静态链接的符号,可执行文件是完全链接的,这些符号已经完成了重定位。
- strip -g或strip --strip-debug
-g
-S
-d
--strip-debug
Remove debugging symbols only.
该选项用于删除二进制目标文件中的调试信息。在编译程序时,编译器通常会将一些调试信息嵌入到可执行文件中,以便在调试时使用。这些调试信息包括源代码行号、函数名、变量名等。--strip-debug选项可以移除这些调试信息,从而减小文件大小。需要注意的是,移除调试信息会导致文件变得难以调试,因此只有在不需要调试时才建议使用--strip-debug选项。
- strip --strip--unneeded
--strip-unneeded
Remove all symbols that are not needed for relocation processing.
该选项用于删除二进制目标文件中未使用的符号,而保留其他可能被使用的符号。当编译程序时,编译器会生成一些不被实际使用的符号。--strip-unneeded选项可以移除这些未被使用的符号,从而减小文件大小。
我们可以通过strip --strip-all命令来删除prog文件中符号表和调试信息。在strip prog之前我们先备份prog为prog.debug。
[root:~/work/v1/gdb/no-symbols/test1]# cp prog prog.debug
[root:~/work/v1/gdb/no-symbols/test1]#
删除prog中的符号表及所有和调试相关的节:
[root:~/work/v1/gdb/no-symbols/test1]# strip --strip-all prog
[root:~/work/v1/gdb/no-symbols/test1]# file prog
prog: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0c686fa8f234e7fe6e7d8323dce7a067fd8187fe, for GNU/Linux 3.2.0, stripped
[root:~/work/v1/gdb/no-symbols/test1]#
1) 检查strip后的prog文件
[root:~/work/v1/gdb/no-symbols/test1]# strip --strip-all prog
[root:~/work/v1/gdb/no-symbols/test1]# readelf -WS prog
There are 29 section headers, starting at offset 0x3148:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000000318 000318 00001c 00 A 0 0 1
[ 2] .note.gnu.property NOTE 0000000000000338 000338 000020 00 A 0 0 8
[ 3] .note.gnu.build-id NOTE 0000000000000358 000358 000024 00 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000037c 00037c 000020 00 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003a0 0003a0 000024 00 A 6 0 8
[ 6] .dynsym DYNSYM 00000000000003c8 0003c8 0000c0 18 A 7 1 8
[ 7] .dynstr STRTAB 0000000000000488 000488 00008a 00 A 0 0 1
[ 8] .gnu.version VERSYM 0000000000000512 000512 000010 02 A 6 0 2
[ 9] .gnu.version_r VERNEED 0000000000000528 000528 000020 00 A 7 1 8
[10] .rela.dyn RELA 0000000000000548 000548 0000c0 18 A 6 0 8
[11] .rela.plt RELA 0000000000000608 000608 000030 18 AI 6 24 8
[12] .init PROGBITS 0000000000001000 001000 00001b 00 AX 0 0 4
[13] .plt PROGBITS 0000000000001020 001020 000030 10 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001050 001050 000010 10 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000001060 001060 000020 10 AX 0 0 16
[16] .text PROGBITS 0000000000001080 001080 000245 00 AX 0 0 16
[17] .fini PROGBITS 00000000000012c8 0012c8 00000d 00 AX 0 0 4
[18] .rodata PROGBITS 0000000000002000 002000 000082 00 A 0 0 8
[19] .eh_frame_hdr PROGBITS 0000000000002084 002084 00005c 00 A 0 0 4
[20] .eh_frame PROGBITS 00000000000020e0 0020e0 000168 00 A 0 0 8
[21] .init_array INIT_ARRAY 0000000000003db0 002db0 000008 08 WA 0 0 8
[22] .fini_array FINI_ARRAY 0000000000003db8 002db8 000008 08 WA 0 0 8
[23] .dynamic DYNAMIC 0000000000003dc0 002dc0 0001f0 10 WA 7 0 8
[24] .got PROGBITS 0000000000003fb0 002fb0 000050 08 WA 0 0 8
[25] .data PROGBITS 0000000000004000 003000 000010 00 WA 0 0 8
[26] .bss NOBITS 0000000000004010 003010 000008 00 WA 0 0 1
[27] .comment PROGBITS 0000000000000000 003010 00002b 01 MS 0 0 1
[28] .shstrtab STRTAB 0000000000000000 00303b 00010a 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
[root:~/work/v1/gdb/no-symbols/test1]#
可以看到被strip后的prog文件中的符号表(.symtab)节及所有的调试节已经被删除掉了。
prog.debug是带有调试信息的可执行文件,而prog是移除调试信息的可执行文件。这2个文件都能正常运行,但prog比prog.debug小了很多,而且prog中完全没有了符号信息。
2) 运行prog和prog.debug
[root:~/work/v1/gdb/no-symbols/test1]# ./prog
in main...
enter mod1_func...
enter mod2_func...
enter mod3_func...
counter:0
^C
[root:~/work/v1/gdb/no-symbols/test1]# ./prog.debug
in main...
enter mod1_func...
enter mod2_func...
enter mod3_func...
counter:0
^C
[root:~/work/v1/gdb/no-symbols/test1]#
可以发现prog和prog.debug都运行正常。
3) 对比prog和prog.debug的反汇编
由于反汇编的输出较多,这里我们只对比prog和prog.debug的.text节中的部分反汇编进行示意。
? 查看prog的反汇编:
[root:~/work/v1/gdb/no-symbols/test1]# objdump -d -j .text prog
prog: file format elf64-x86-64
Disassembly of section .text:
0000000000001080 <.text>:
1080: f3 0f 1e fa endbr64
1084: 31 ed xor %ebp,%ebp
1086: 49 89 d1 mov %rdx,%r9
1089: 5e pop %rsi
108a: 48 89 e2 mov %rsp,%rdx
108d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1091: 50 push %rax
1092: 54 push %rsp
1093: 4c 8d 05 26 02 00 00 lea 0x226(%rip),%r8 # 12c0 <sleep@plt+0x250>
109a: 48 8d 0d af 01 00 00 lea 0x1af(%rip),%rcx # 1250 <sleep@plt+0x1e0>
10a1: 48 8d 3d 45 01 00 00 lea 0x145(%rip),%rdi # 11ed <sleep@plt+0x17d>
10a8: ff 15 32 2f 00 00 callq *0x2f32(%rip) # 3fe0 <sleep@plt+0x2f70>
10ae: f4 hlt
10af: 90 nop
10b0: 48 8d 3d 59 2f 00 00 lea 0x2f59(%rip),%rdi # 4010 <sleep@plt+0x2fa0>
10b7: 48 8d 05 52 2f 00 00 lea 0x2f52(%rip),%rax # 4010 <sleep@plt+0x2fa0>
10be: 48 39 f8 cmp %rdi,%rax
10c1: 74 15 je 10d8 <sleep@plt+0x68>
10c3: 48 8b 05 0e 2f 00 00 mov 0x2f0e(%rip),%rax # 3fd8 <sleep@plt+0x2f68>
10ca: 48 85 c0 test %rax,%rax
10cd: 74 09 je 10d8 <sleep@plt+0x68>
10cf: ff e0 jmpq *%rax
10d1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
10d8: c3 retq
10d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
10e0: 48 8d 3d 29 2f 00 00 lea 0x2f29(%rip),%rdi # 4010 <sleep@plt+0x2fa0>
10e7: 48 8d 35 22 2f 00 00 lea 0x2f22(%rip),%rsi # 4010 <sleep@plt+0x2fa0>
10ee: 48 29 fe sub %rdi,%rsi
......
12b0: 41 5e pop %r14
12b2: 41 5f pop %r15
12b4: c3 retq
12b5: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1)
12bc: 00 00 00 00
12c0: f3 0f 1e fa endbr64
12c4: c3 retq
[root:~/work/v1/gdb/no-symbols/test1]#
? 查看prog.debug的反汇编:
[root:~/work/v1/gdb/no-symbols/test1]# objdump -d -j .text prog.debug
prog.debug: file format elf64-x86-64
Disassembly of section .text:
0000000000001080 <_start>:
1080: f3 0f 1e fa endbr64
1084: 31 ed xor %ebp,%ebp
1086: 49 89 d1 mov %rdx,%r9
1089: 5e pop %rsi
108a: 48 89 e2 mov %rsp,%rdx
108d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1091: 50 push %rax
1092: 54 push %rsp
1093: 4c 8d 05 26 02 00 00 lea 0x226(%rip),%r8 # 12c0 <__libc_csu_fini>
109a: 48 8d 0d af 01 00 00 lea 0x1af(%rip),%rcx # 1250 <__libc_csu_init>
10a1: 48 8d 3d 45 01 00 00 lea 0x145(%rip),%rdi # 11ed <main>
10a8: ff 15 32 2f 00 00 callq *0x2f32(%rip) # 3fe0 <__libc_start_main@GLIBC_2.2.5>
10ae: f4 hlt
10af: 90 nop
......
0000000000001169 <mod3_func>:
1169: f3 0f 1e fa endbr64
116d: 55 push %rbp
116e: 48 89 e5 mov %rsp,%rbp
1171: 48 83 ec 10 sub $0x10,%rsp
1175: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
117c: 48 8d 35 a5 0e 00 00 lea 0xea5(%rip),%rsi # 2028 <__func__.2925>
1183: 48 8d 3d 7e 0e 00 00 lea 0xe7e(%rip),%rdi # 2008 <_IO_stdin_used+0x8>
118a: b8 00 00 00 00 mov $0x0,%eax
118f: e8 cc fe ff ff callq 1060 <printf@plt>
1194: 8b 45 fc mov -0x4(%rbp),%eax
1197: 8d 50 01 lea 0x1(%rax),%edx
119a: 89 55 fc mov %edx,-0x4(%rbp)
119d: 89 c6 mov %eax,%esi
119f: 48 8d 3d 6f 0e 00 00 lea 0xe6f(%rip),%rdi # 2015 <_IO_stdin_used+0x15>
11a6: b8 00 00 00 00 mov $0x0,%eax
11ab: e8 b0 fe ff ff callq 1060 <printf@plt>
11b0: bf 05 00 00 00 mov $0x5,%edi
11b5: e8 b6 fe ff ff callq 1070 <sleep@plt>
11ba: eb d8 jmp 1194 <mod3_func+0x2b>
00000000000011bc <mod2_func>:
11bc: f3 0f 1e fa endbr64
11c0: 55 push %rbp
11c1: 48 89 e5 mov %rsp,%rbp
11c4: 48 8d 35 7d 0e 00 00 lea 0xe7d(%rip),%rsi # 2048 <__func__.2316>
11cb: 48 8d 3d 66 0e 00 00 lea 0xe66(%rip),%rdi # 2038 <__func__.2925+0x10>
11d2: b8 00 00 00 00 mov $0x0,%eax
11d7: e8 84 fe ff ff callq 1060 <printf@plt>
11dc: b8 00 00 00 00 mov $0x0,%eax
11e1: e8 83 ff ff ff callq 1169 <mod3_func>
11e6: b8 00 00 00 00 mov $0x0,%eax
11eb: 5d pop %rbp
11ec: c3 retq
00000000000011ed <main>:
11ed: f3 0f 1e fa endbr64
11f1: 55 push %rbp
11f2: 48 89 e5 mov %rsp,%rbp
11f5: 48 8d 35 60 0e 00 00 lea 0xe60(%rip),%rsi # 205c <__func__.2316>
11fc: 48 8d 3d 4f 0e 00 00 lea 0xe4f(%rip),%rdi # 2052 <__func__.2316+0xa>
1203: b8 00 00 00 00 mov $0x0,%eax
1208: e8 53 fe ff ff callq 1060 <printf@plt>
120d: b8 00 00 00 00 mov $0x0,%eax
1212: e8 07 00 00 00 callq 121e <mod1_func>
1217: b8 00 00 00 00 mov $0x0,%eax
121c: 5d pop %rbp
121d: c3 retq
000000000000121e <mod1_func>:
121e: f3 0f 1e fa endbr64
1222: 55 push %rbp
1223: 48 89 e5 mov %rsp,%rbp
1226: 48 8d 35 4b 0e 00 00 lea 0xe4b(%rip),%rsi # 2078 <__func__.2316>
122d: 48 8d 3d 34 0e 00 00 lea 0xe34(%rip),%rdi # 2068 <__func__.2316+0xc>
1234: b8 00 00 00 00 mov $0x0,%eax
1239: e8 22 fe ff ff callq 1060 <printf@plt>
123e: b8 00 00 00 00 mov $0x0,%eax
1243: e8 74 ff ff ff callq 11bc <mod2_func>
1248: b8 00 00 00 00 mov $0x0,%eax
124d: 5d pop %rbp
124e: c3 retq
124f: 90 nop
......
00000000000012c0 <__libc_csu_fini>:
12c0: f3 0f 1e fa endbr64
12c4: c3 retq
[root:~/work/v1/gdb/no-symbols/test1]#
我们从prog.debug的反汇编中可以看到很多函数的名字,如main、mod1_func、mod2_func和mod等_func,但在prog的反汇编中是完全没有的,这是因为我们把prog文件中的符号表(.symtab)删除了,所以无法看到诸如函数名字的信息了
可想而知,由于没有了象main、mod1_func、mod2_func等这些符号信息,调试的时候会非常困难。
【注意】
? 程序中的很多符号信息主要是给程序员看的,计算机看到的主要是字节和地址,未必需要这些符号。
总结:
- 对于可执行目标文件,可以使用strip --strip-all来进行瘦身,这样能极大的减小可执行文件的大小,但由于删除了符号表和调试相关的信息,这会使调试困难。
- 对于可重定位目标文件和共享目标文件(静态链接库和动态链接库),尽量不要使用strip --strip-all进行瘦身,这是因为可重定位目标文件或共享目标文件可能会参与链接过程,需要使用符号表中的信息来解决符号引用的问题,如果删除了它们的符号表,就会造成链接失败。可以考虑使用strip --strip-unneeded对可重定位目标文件和共享目标文件瘦身。
- 对于可重定位目标文件,其实它们一般不参与发布或调试过程,所以它们一般不需要保留,也就谈不上还需要瘦身了。
猜你喜欢
- 2024-10-20 JSA宏教程WPS表格常用内置对象——单元格区域(Range)对象(一)
- 2024-10-20 CAD快捷键命令大全汇总,建议收藏保存
- 2024-10-20 是时候拯救我的 HTML 技术了(是时候拯救我的+html+技术了什么)
- 2024-10-20 Linux内核:Crash学习ARM64虚拟地址空间布局
- 2024-10-20 雾非花:清清爽爽的夏季女士宽松短袖(有编织图解)
- 2024-10-20 几毛钱一片的谷维素,可以用来治哪些病?这4大功效认可度很高
- 2024-10-20 Java 8 之后,还有哪些进化的功能?
- 2024-10-20 扒一扒西欧喷气式战斗机家族谱(3)
- 2024-10-20 「小狮子诊所」内存时序到底有什么作用?
- 2024-10-20 与你相见恨晚的100个CAD快捷键+20个CAD制图技巧,轻松玩转CAD
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)