一直对符号和符号表不是很了解,这次彻底搞懂这玩意,在《程序员的自我修养》3.4和7.5章节介绍了关于符号表的相关知识。符号,这俩中文字放一起,有以下几种意思:
- 数学中的加减乘除,我们叫符号
- 数学中的xyz,有时我们也叫符号,因为不是具体的数字
- 代码中的函数名,变量名,段名,标号等,也叫符号
符号表的中的符号的意义就是3中的意义,而符号执行中的符号的是2中的意义,符号执行——从入门到上高速
在程序编译成可执行文件后,这个文件中会有一个表专门来保存函数名,变量名,段名和代码或者数据的对应关系,这个表就是符号表。符号表在链接时起着按符号寻址的作用,但在运行的时候就没有什么作用了,因此这个表即使去掉之后,也并不会影响程序的运行。但是如果是动态链接的函数,比如用到了libc的printf函数,那么这个printf符号如果去掉了,在运行的时候就没法找到这函数了,所以这个符号就不能在去符号表的时候被去掉。
所以ELF文件里有两张符号表,一张叫符号表(.symtab),另一张叫动态符号表(.dynsym)。当去符号的时候,只用去掉符号表,而保留动态符号表,这两张符号表可以用objdump这个工具来查看:
➜ objdump --help | grep "symbol table"
-t, --syms Display the contents of the symbol table(s)
-T, --dynamic-syms Display the contents of the dynamic symbol table
可以利用t和T这两个参数分别查看符号表和动态符号,以及用strip工具对elf文件进行去符号:
➜ objdump -t hello
➜ objdump -T hello
➜ strip hello
当然,也可以尝试用010editor的ELF模板来解析
动态链接
我先随便编译个helloworld的动态链接版,然后看符号表:
➜ cat main.c
# include <stdio.h>
int main(){
printf("hello");
return 0;
}
➜ gcc main.c -o hello
➜ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=059bb04e2a8e116ff33ade2ba00c8b5e5797b261, not stripped
➜ objdump -t hello
hello: file format elf64-x86-64
SYMBOL TABLE:
(省略一堆)
0000000000400238 l d .interp 0000000000000000 .interp
00000000004002b8 l d .dynsym 0000000000000000 .dynsym
0000000000400318 l d .dynstr 0000000000000000 .dynstr
0000000000000000 w *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000601028 w .data 0000000000000000 data_start
0000000000601038 g .data 0000000000000000 _edata
00000000004005b4 g F .fini 0000000000000000 _fini
0000000000000000 F *UND* 0000000000000000 printf@@GLIBC_2.2.5
0000000000000000 F *UND* 0000000000000000 __libc_start_main@@GLIBC_2.2.5
0000000000601028 g .data 0000000000000000 __data_start
0000000000000000 w *UND* 0000000000000000 __gmon_start__
00000000004005c0 g O .rodata 0000000000000004 _IO_stdin_used
0000000000400540 g F .text 0000000000000065 __libc_csu_init
0000000000601040 g .bss 0000000000000000 _end
0000000000400430 g F .text 000000000000002a _start
0000000000601038 g .bss 0000000000000000 __bss_start
0000000000400526 g F .text 000000000000001a main
➜ objdump -T hello
hello: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 printf
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
然后我们对这个文件去符号后,在查看符号表:
➜ strip hello
➜ objdump -t hello
hello: file format elf64-x86-64
SYMBOL TABLE:
no symbols
➜ objdump -T hello
hello: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 printf
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
发现符号表没了,而动态链接的符号表还在,
静态链接
而当我们静态编译一个文件时:
➜ gcc --static main.c -o hello
➜ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=1c740054afa2af329e1ac61d249eea917e20fa80, not stripped
➜ objdump -t hello | head
hello: file format elf64-x86-64
SYMBOL TABLE:
0000000000400190 l d .note.ABI-tag 0000000000000000 .note.ABI-tag
00000000004001b0 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
00000000004001d8 l d .rela.plt 0000000000000000 .rela.plt
00000000004002c8 l d .init 0000000000000000 .init
00000000004002f0 l d .plt 0000000000000000 .plt
0000000000400390 l d .text 0000000000000000 .text
➜ objdump -T hello
hello: file format elf64-x86-64
objdump: hello: not a dynamic object
DYNAMIC SYMBOL TABLE:
no symbols
发现本来就是没有动态符号表,然后我们再去符号:
➜ strip hello
➜ objdump -T hello
hello: file format elf64-x86-64
objdump: hello: not a dynamic object
DYNAMIC SYMBOL TABLE:
no symbols
➜ objdump -t hello
hello: file format elf64-x86-64
SYMBOL TABLE:
no symbols
发现啥符号都没有了
总结
类型 | 符号表 | 动态符号表 |
---|---|---|
动态不去符号 | 有 | 有 |
静态不去符号 | 有 | 无 |
动态去符号 | 无 | 有 |
静态去符号 | 无 | 无 |
所以如果是静态链接的,并且去了符号,是啥符号都没有的,比如入口的_start符号,都没有了,但是如果用IDA分析,则会自动标记一些符号给我们,比如ELF的入口地址会被标记为start等。
参考: