参考:《程序员的自我修养》第3章
真正了不起的程序员对自己的程序的每一个字节都了如指掌。
学习笔记,我就是个搬运工。
ELF:Executable Linkable Format。
PE-COFF:Portable Executable Common File Format。
查看目标文件内部结构的工具:
objdump -h file.o // 将elf文件各个段的基本信息打印出来
objdump -x file.o // 将elf文件各个段的基本信息打印出来,更详细
objdump -s file.o // 将段的内容以16进制打印出来
objdump -d file.o // 反汇编
readelf -h file.o // 查看elf文件头信息
readelf -S file.o // 查看elf文件段表结构
nm file.o // 查看elf文件的符号表
查看elf文件代码段、数据段、BSS长度:
size file.o
1. 什么是目标文件
源代码编译后但未进行链接的中间文件,Linux下的.o,Windows下的.obj。
目标文件与可执行文件采用相同的文件格式,主流是Linux为ELF,Windows为PE-COFF。
2. 可执行文件格式
3. ELF文件结构
ELF文件结构大致可分为ELF文件头、段表、字符串表、符号表等。
-
ELF头
ELF魔数: ELF头中的Magic,是ELF魔数,16个字节,对应class到ABI version的内容,用于标识ELF文件的平台属性,如ELF字长,字节序,ELF版本等。
0x7f 45 4c 46:对应的ASCII码为 DEL控制符,E,L,F。
文件类型:系统通过文件类型常量来判断ELF的真正文件类型,而非后缀名。REL(1)—可重定位文件,EXEC(2)—可执行文件,DYN(3)—共享目标文件。
机器类型:表示该ELF文件的平台属性,即可以在什么平台下运行。 -
段表:描述了ELF文件各个段的信息,如段名、段的长度、在文件中的偏移、读写权限等。主要确定段属性的是段的类型和段的标志位。
段类型
段标志位 :表示该段在进程虚拟地址空间中的属性,可读可写可执行,分配空间等。 -
重定位表:链接器在处理目标文件时,需对目标文件中的某些部位进行重定位,即代码段和数据段那些绝对地址的引用的位置。重定位表里记录着这些重定位信息。
-
字符串表:由于字符串长度不固定,所以用固定结构来表示比较困难,所以把字符串集中起来存放到一个表,用字符串在表中的偏移来引用字符串。
.strtab :字符串表,保存普通的字符串。
.shstrtab :段表字符串表,保存段表中用到的字符串,如段名。
4. 链接
目标文件相互拼合实质是目标文件之间对地址的引用,即对函数和变量地址的引用。
- 符号:函数和变量。
- 符号名:函数名和变量名。
- 符号表:每一个目标文件都有一个对应的符号表,记录了目标文件中用到的所有符号。
- 符号值:对于函数和变量而言,就是它们的地址。
ELF符号表结构
- Num:表示符号表数组的下标。
- Value:符号值。
- Size:符号大小。
- Type:符号类型。NOTYPE,未知类型;OBJECT,数据对象;FUNC,函数或其它可执行代码;SECTION,段;FILE,文件。
- Bind:符号绑定信息。LOCAL,局部符号,对目标文件外的符号不可见;GLOBAL:全局符号,外部可见;WEAK:弱引用。
- Ndx:表示该符号所属的段。
- Name:符号名称。
函数签名:用于识别不同的函数,包含了函数名、参数类型、所在类、名称空间及其它。
弱符号和强符号:弱符号和强符号是针对定义来说,而非引用。
- 编译器默认函数和已初始化的全局变量为强符号,未初始化的全局变量为弱符号。
- 可用__attributte__((weak))来定义任何一个强符号为弱符号。
- 链接器处理多次定义的全局符号:
❤❤ 不允许强符号被多次定义,即不同的目标文件不能有相同的强符号;如果有,就报错。
❤❤ 如果一个符号在某个目标文件中是强符号,其它是弱符号,那么选择强符号。
❤❤ 如果一个符号在所有目标文件中都是弱符号,选占用空间最大的那个。
弱引用和强引用:强引用,链接器发现某个符号未定义就会报未定义的错误。弱引用,链接器发现某个符号未定义不报错,默认其为0或者一个特定的值。
- 可用__attributte__((weakref))来申明对一个外部函数的引用为弱引用。