先看一下从源代码到目标文件的过程
image.png
那么目标文件具体是什么格式,里面包含什么样的内容是我们这一节需要弄清楚的问题,不过首先需要知道,目标文件从结构上讲,其实和可执行文件是相同的,只是还没经过链接的过程,其中某些符号或地址没有进行调整而已。

目标文件的格式

按照平台区分主要有两种格式

  • window下:PE(Portable Executable)格式,形式为 .obj 文件
  • Linux下:ELF(Executable Linkable Format)格式,形式为 .o 文件

目标文件形式

  • ELF 是按照分段的形式来存储数据的
  • 下面描述的是 ELF 的文件格式,实际上包含很多段,这里只是列举了一些常用的段

image.png
为什么要将代码段和数据段分开存储?

  • 代码段是只读,数据段是可读性,分开存储可以防止程序被改写
  • 现代CPU有强大的缓存,指令和数据分开存储可以提高程序的局部性,有利于提高缓存命中率
  • 当系统运行多个程序副本时,指令是相同的,所以内存中只需要保存一份程序的指令部分即可,可以节省很多内存

以下面的源代码为例查看各个段内容

  1. // SimpleSection.c
  2. #include <stdio.h>
  3. int global_init_var = 84;
  4. int global_unitit_var;
  5. void func1(int i)
  6. {
  7. printf("%d\n", i);
  8. }
  9. int main(void)
  10. {
  11. static int static_var = 85;
  12. static int static_var2;
  13. int a = 1;
  14. int b;
  15. func1(static_var + static_var2 + a + b);
  16. return 0;
  17. }

编译成目标文件后,使用 objdump 的 -h 参数查看段信息
objhump -h SimpleSection.o
image.png
使用 size 命令查看各个段的长度
size SimpleSection.o
image.png
上面相当于总览,下面使用 objhump 命令查看每个段具体的内容

  • -s:将所有段内容按照16进制打出来
  • -d:将所有包含指令的段反汇编

objdump -s -d SimpleSection.o
image.png

文件头

可以通过下面命令来查看 ELF 的文件头

  1. readelf -h SimpleSection.o

image.png

段表

通过下面命令可以查看段表

  1. readelf -S SimpleSection.o

image.png

链接的接口-符号

  • 所谓链接出不同目标文件对函数和变量地址的引用
  • 每个函数或变量需要有自己独特的名称,才可以避免链接过程中不同变量和函数之间的混淆
  • 这些函数和变量我们统称为符号,也就是变量名或函数名就是符号
  • 每一个目标文件都会有一个符号表,记录了目标文件中所有使用到的符号,每个定义的符号只有一个对应的指,叫做符号值;
  • 对于变量和函数来说,符号值就是它们的地址

我们可以使用很多的命令来查看 ELF 文件的符号表,比如 readelf、objdump、nm 等,下面使用 nm 命令来查看符号表

  1. nm SimpleSection.o

image.png
使用 readelf 命令查看符号表

  1. readelf -s SimpleSection.o

image.png