先看一下从源代码到目标文件的过程
那么目标文件具体是什么格式,里面包含什么样的内容是我们这一节需要弄清楚的问题,不过首先需要知道,目标文件从结构上讲,其实和可执行文件是相同的,只是还没经过链接的过程,其中某些符号或地址没有进行调整而已。
目标文件的格式
按照平台区分主要有两种格式
- window下:PE(Portable Executable)格式,形式为 .obj 文件
- Linux下:ELF(Executable Linkable Format)格式,形式为 .o 文件
目标文件形式
- ELF 是按照分段的形式来存储数据的
- 下面描述的是 ELF 的文件格式,实际上包含很多段,这里只是列举了一些常用的段
为什么要将代码段和数据段分开存储?
- 代码段是只读,数据段是可读性,分开存储可以防止程序被改写
- 现代CPU有强大的缓存,指令和数据分开存储可以提高程序的局部性,有利于提高缓存命中率
- 当系统运行多个程序副本时,指令是相同的,所以内存中只需要保存一份程序的指令部分即可,可以节省很多内存
以下面的源代码为例查看各个段内容
// SimpleSection.c
#include <stdio.h>
int global_init_var = 84;
int global_unitit_var;
void func1(int i)
{
printf("%d\n", i);
}
int main(void)
{
static int static_var = 85;
static int static_var2;
int a = 1;
int b;
func1(static_var + static_var2 + a + b);
return 0;
}
编译成目标文件后,使用 objdump 的 -h 参数查看段信息objhump -h SimpleSection.o
使用 size 命令查看各个段的长度size SimpleSection.o
上面相当于总览,下面使用 objhump 命令查看每个段具体的内容
- -s:将所有段内容按照16进制打出来
- -d:将所有包含指令的段反汇编
objdump -s -d SimpleSection.o
文件头
可以通过下面命令来查看 ELF 的文件头
readelf -h SimpleSection.o
段表
通过下面命令可以查看段表
readelf -S SimpleSection.o
链接的接口-符号
- 所谓链接出不同目标文件对函数和变量地址的引用
- 每个函数或变量需要有自己独特的名称,才可以避免链接过程中不同变量和函数之间的混淆
- 这些函数和变量我们统称为符号,也就是变量名或函数名就是符号
- 每一个目标文件都会有一个符号表,记录了目标文件中所有使用到的符号,每个定义的符号只有一个对应的指,叫做符号值;
- 对于变量和函数来说,符号值就是它们的地址
我们可以使用很多的命令来查看 ELF 文件的符号表,比如 readelf、objdump、nm 等,下面使用 nm 命令来查看符号表
nm SimpleSection.o
使用 readelf 命令查看符号表
readelf -s SimpleSection.o