avatar

Catalog
C++:从源代码到可执行文件

从源代码到可执行文件大概可分为四步:

  • 预处理gcc -E),产物为 .i 文件(翻译单元)

    #include 替换,#define 宏展开,#ifdef 条件编译等等

  • 编译gcc -S),产物为 .s 汇编文件

  • 汇编gcc -c),产物为 .o 目标文件(可重定位目标文件)

  • 链接gcc),产物为可执行文件(可执行目标文件)

    函数库一般分为两种:

    • 静态(.a):运行时不需要链接操作,节省时间(目标文件打包)
    • 动态(.so):运行时链接,可执行文件本身不包含库文件的代码,节省空间(共享目标文件)

汇编

汇编器的任务是将汇编文件翻译成目标文件,而不仅仅是将汇编代码翻译成机器码(事实上翻译而成的机器码大多只在 .text 节中,只占了目标文件很少的一部分)

Linux 中目标文件采用 ELF 格式。该格式的目标文件可分为四个部分:

  • ELF 头(ELF Header,readelf -h):其中包含段头部表和节头部表的偏移、大小、条目数等等。
  • 段头部表(Program Header Table,readelf -l):表中每个条目对应一个段,包含段的偏移、地址、大小等等。
  • (Sections,readelf -x <name>):可以理解成目标文件的 payload,每个节都是一串字节序列。
  • 节头部表(Section Header Table,readelf -S):表中每个条目对应一个节,包含节的地址、偏移、大小等等。

段是节的集合,其意义在于将连续的节映射到连续的内存段,以便于加载。例如只读内存段(代码段)包括 ELF Header,Program Header Table,.text 节,.rodata 节等等,而读写内存段(数据段)包括 .data 节,.bss 节。

段的设计是为了加载方便,所以常常出现在可执行目标文件中,在可重定位目标文件中可以没有 Program Header Table。

图片来自维基百科

链接

静态链接主要分为两步:

  • 符号解析(symbol resolution):将对每个符号的引用和其定义关联起来。

    符号表(readelf -s)在目标文件中有对应的节,其中保存了该目标模块对符号引用和定义的信息。

  • 重定位(relocation):首先重定位输入目标文件的节,使每个符号定义有确定的运行时地址;然后重定位符号引用,使得它们指向正确的符号定义的位置。

    重定位条目(readelf -r)在目标文件中也有对应的节,每个条目即对应一处本目标模块对一个外部符号的引用,其中保存了重定位时需要用到的信息。

Reference

Author: Gusabary
Link: http://gusabary.cn/2020/05/17/C++-from-source-code-to-executable/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

Comment