//2019.5.2
PC平台流行的可执行文件存储格式:
Windows:PE
Linux:ELF
可执行文件(Windows的.exe和Linux的ELF可执行文件)、动态链接库(Windows的.dll和Linux的.so)、静态链接库(Windows的.lib和Linux的.a)都是按照可执行文件格式存储
在Windows平台下,按照PE-COFF格式存储
在Linux平台下,按照ELF格式存储
程序源代码被编译后主要分为两种段:程序指令和程序数据
真正了不起的程序员对自己的程序每一个字节都了如指掌。
EFL结构:
- 代码段
- 数据段和只读数据段
.data:保存已经初始化了的全局静态变量和局部静态变量
.rodata:保存只读数据(比如const变量)和字符串变量
有时候编译器会把字符串变量放到.data段而不会单独放到.rodata - BSS段
.bss:存放未初始化的全局变量和局部静态变量 - 其他段
objdump查看object内部的结构
运行obidump -h XXX.o可以查看各个段的信息,比如:
图中:
0:代码段
1:数据段
2:BSS段
3:只读数据段
4:注释信息段
5:堆栈提示段
用size可以查看elf文件的代码段、数据段和BSS段的长度(dec:三个段长度的十进制,hex长度和的十六进制)
查看具体内容:
-s:将所有段的内容以十六进制的方式打印出来
-d:将所有包含指令的段反汇编
elf文件头
elf的文件头定义了elf魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、elf重定位类型、硬件平台、硬件平台版本、入口地址、程序头入口和长度、段表的位置和长度及段的数量等。
这些字段的相关常量都定义在elf.h里面。
elf魔数
Magic的16个字节对应的是e_ident成员。
这16个字节被elf标准规定用来标识elf文件的平台属性。
最开始的4个字节是所有elf文件都必须相同的标识码,分别为:0x7f、0x45、0x4c、0x46,第一个字节对应ASCII字符里的DEL控制符,后面3个字节是ELF这3个字母的ASCII码。这4个字节又被称为elf文件的魔数,几乎所有的可执行文件格式的最开始的几个字节都是魔数。
魔数通常用来确认文件的类型,操作系统在夹杂可执行文件的时候会确认魔数是否正确,如果不正确会拒绝加载。
PE/COFF文件最开始的两个字节为0x4d、0x5a,即ASCII码字符MZ
//2019.5.4
段表:
重定位表
链接器在处理目标文件的时候,需要对目标文件中的某些部位进行重定位,即代码段和数据段中那些对绝对地址的引用的位置。
这些重定位的信息被记录在elf表中,对于每个需要重定位的代码段或数据段,都会有一个相应地重定位表。
符号表
readelf -s XXX.o
elf文件中符号表往往是文件中的一个段,段名:.symtab
符号所在表:如果符号定义在本目标文件中,那么这个成员标识符号所在的段在段表的下表;如果符号不是定义在本目标文件中,或者对于某些特殊符号。sh_shndx的值如下:
符号值:每个符号都有一个对应的值,如果这个符号是一个函数或变量的定义,那么符号的值就是这个函数或变量的地址。
特殊符号:
__executable_start:程序的起始地址(不是入口地址)
__etext、_exext、etext:代码段结束地址
_edata、edata:数据段的结束地址
_end、end:程序的结束地址
函数签名 :包含了一个函数的信息,包括函数名、参数类型、所在的类和名称空间以及其他信息。用于标识不同的函数。
extern “C”:把内部代码当做C语言代码处理
- 弱符号和强符号
- 弱引用和强引用
调试信息
在gcc编译时加上-g参数,编译器就会在产生的目标文件里加上调试信息。
ELF文件采用一个叫DWARF(Debug With Arbitrary Record Format)的标准调试信息格式
在Linux下,去掉调试信息:
$strip foo