符号表是啥?strip去符号去了啥?

一直对符号和符号表不是很了解,这次彻底搞懂这玩意,在《程序员的自我修养》3.4和7.5章节介绍了关于符号表的相关知识。符号,这俩中文字放一起,有以下几种意思:

  1. 数学中的加减乘除,我们叫符号
  2. 数学中的xyz,有时我们也叫符号,因为不是具体的数字
  3. 代码中的函数名,变量名,段名,标号等,也叫符号

符号表的中的符号的意义就是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等。

参考: