《静态链接》

如何链接两个目标文件成为一个可执行文件?


编译与链接

一句简单的gcc hello.c可分解为4个步骤,预处理、编译、汇编、链接

预处理

1
gcc -E hello.c -o hello.i

此过程只处理以#开始的预编译命令

  • 展开所有宏定义,并删除#define
  • 处理所有条件预编译指令,如#if #ifdef #ifndef
  • 处理所有#include,把包含的文件插入该指令的位置
  • 删除所有注释
  • 添加行号和文件名,为编译器调试和产生错误提供信息
  • 保留所有#pragma

下面是一个预处理之后的文件的一部分

如果无法判断宏定义是否正确或头文件是否正确,可以通过查看预编译后的文件来确定问题

编译

这个过程是这个过程的核心部分,包括词法分析、语法分析、语义分析及优化,具体参见

1
gcc -S hello.c -o hello.s

参数-S输出编译后的汇编代码文件

GCC这个命令只是后台程序的包装,根据不同的参数去调用预编译编译程序cc1、汇编器as、链接器ld

汇编

汇编器将汇编代码转换成机器可以执行的指令,只要根据汇编指令和机器指令的对照表一一翻译就可以

1
2
3
4
5
as hello.s -o hello.o

gcc -c hello.s -o hello.o

gcc -c hello.c -o hello.o

上面三条指令都能直接输出目标文件(Object File),可以用objdump查看目标文件内容

链接

需要一堆文件链接起来才能得到可执行文件,这涉及到编译、链接和库,甚至时操作系统的底层内容

通常上面的一系列编译,终于产生了目标代码,但是目标代码中的变量可能定义在其他地方,这时候就该链接器出场!链接器的年龄比编译器还长!

最开始记录程序用的是纸带,穿孔表示0.没穿孔表示1,例如跳转指令就是0001 XXXX,后四位就是跳转地址,而对于这个地址是需要人工去计算的,而且只要程序修改,就要重新计算,这一过程就叫做重定位

为了消除这个繁琐的过程,人们发明了汇编语言,比如用jmp表示0001 XXXX跳转指令。还可以使用符号来标记位置,如.LEF0,比记录当前指令的开始的第几条指令更容易。之后在修改程序之后,汇编器就会重新计算.LEF0的位置,并把所有引用该符号的指令都修正一遍

有了汇编语言以后,代码规模就快速膨胀。软件组织出现了模块化,模块化的拼1接也就是链接。链接过程主要包括地址和空间分配、符号决议、重定位,本质上就是上面的“原始人”所做的工作

空间与地址分配

符号解析和重定位

静态库链接

链接过程控制