首先,我们来看一个经典例子hello.c的可执行文件的生成过程。
hello.c是通过一系列处理过程才得到最终可被机器执行的程序文件,其中的链接过程就是这次学习的重点。
链接
链接过程将多个可重定位目标文件合并以生成可执行目标文件。在Linux中使用ELF格式描述目标文件。
在这里我想通过一个例子来帮助理解链接过程的目标文件ELF格式:
在此先附上可重定位目标文件和可执行目标文件的ELF格式图(左为可重定位目标文件,右为可执行目标文件)
- 下面给出一个C程序:
/* main.c */
/* $begin main */
int sum(int *a, int n);
int array[2] = {1, 2};
int main()
{
int val = sum(array, 2);
return val;
}
/* $end main */
/* sum.c */
/* $begin sum */
int sum(int *a, int n)
{
int i, s = 0;
for (i = 0; i < n; i++) {
s += a[i];
}
return s;
}
/* $end sum */
-
使用gcc编译命令生成可重定位目标文件并查看main.o的ELF头(以下均只给出main.o的相关信息):
可以看出ELF头包括16字节标识信息、文件类型(REL)、机器类型(X86-64)、节头表的偏移(11)、节头表的表项大小(64字节)以及表项个数(12)。 -
查看符号表:
其中(bind为GLOBAL)全局符号main是偏移量为0(value=0)的33字节(size=33)的位于.text节(Ndx=3)的函数(type为FUNC)。 -
查看节头表:
节头表描述了目标文件的节。
一个典型的ELF可重定位目标文件包含下面几个节:
.text:已编辑程序的机器代码。
.rodata:只读数据。
.data:已初始化的全局变量和静态C变量。
.bss: 未初始化的全局变量和静态C变量。
.symtab: 一个符号表。
.rel.text: 指令的重定位信息。
.rel.data:数据的重定位信息。
.debug:一个调试符号表。
.line:原始C源程序中的行号和.text节中机器指令之间的映射。
.strtab:一个包括.symtab和.debug节中的符号表以及节头表中的节名字。 -
链接生成可执行目标文件p并查看ELF头:
其中可执行文件中包括和可重定位目标文件不一样的程序头表,init节,少了两个.rel节,下面查看程序头表
程序头表描述可执行文件中的节与虚拟空间中的存储段之间的映射关系。
ps:本文部分图片来源mooc计算机系统基础(一)南京大学袁春风老师的课件ppt。