1. 什么是链接
链接是将代码和数据片段整合成一个可以被加载(复制)到内存中执行的文件。现代操作系统中,链接是由链接器自动执行的。链接最大的作用是分离编译,在编写大型应用程序时,不需要再编译出一个巨大的源文件,可以把代码编译成比较小的,单独的模块。文件修改时只需要重新编译单独的模块就可以了,不需要重新编译整个文件。
2. 静态链接流程
下图为通过静态链接将两个C源文件编译成可执行目标文件的过程。
静态链接:将可重定位目标文件组合成可执行目标文件。
静态链接的两个主要工作为符号解析和重定位。
3. 目标文件
在介绍符号解析和重定位之前,先介绍一下目标文件。现代linux和unix系统使用可执行可链接格式(ELF)。目标文件有三种形式:可重定位目标文件,可执行目标文件,共享目标文件(特殊类型的可重定位目标文件)。
3.1 可重定位目标文件
下图展示了可重定位目标文件的格式
ELF头以一个16字节的描述了生成该文件的系统的字的大小和字节顺序的序列开始,除此之外,ELF头中还包含帮助链接器语法分析和解释目标文件的信息。其中包括ELF头的大小,目标文件的类型,机器类型(x86-64),节头部表的文件偏移,节头部表中条目的大小和数量。
在ELF头和节头部表之间的都是节。一个典型的ELF可重定位目标文件包含下面几个节。
-
.text:已编译程序的机器代码。
-
.rodata:只读数据。
-
.data:以初始化的全局和静态C变量。
-
.bss:为初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量。不占据实际的空间,仅仅用做占位。运行时,在内存中分配这些变量。
-
.symtab:符号表,存放在程序中定义和引用的函数和全局变量的信息。
-
.rel.text:一个.text节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。
-
.strtab:一个字符串表,内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。
linux下的readelf可以方便的阅读elf文件。mac系统下使用brew update && brew install binutils
,然后用greadelf和gobjdump
。
下面是使用greadelf查看的so文件:
PS:这不是一个可重定位目标文件,作者暂时没有linux系统,找了一个共享目标文件。总体的结构是差不多的。
greadelf -a libapp.so
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: ARM
Version: 0x1
Entry point address: 0x0
Start of program headers: 52 (bytes into file)
Start of section headers: 5668992 (bytes into file)
Flags: 0x5000200, Version5 EABI, soft-float ABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 10
Size of section headers: 40 (bytes)
Number of section headers: 12
Section header string table index: 11
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .note.gnu.bu[...] NOTE 00001000 001000 000020 00 WA 0