深入理解链接和加载过程(一)

最近学习链接器的链接和装载过程。

首先说说一个程序从源代码到可执行文件的流程(以linux平台上c程序为例):

深入理解链接和加载过程(一)_第1张图片

 

第一步预编译过程的命令如下:

gcc -E test.c -o test.i 或 cpp test.c > test.i

由.c文件生成.i预处理文件

第二步:

gcc -S test.i -o test.s

由.i生成.s汇编文件

第三步:

as test.s -o test.o 或

gcc -c test.s -o test.o

生成目标文件

第四步:

ld -static /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i686-linux-gnu/4.6.3/crtbeginT.o -L/usr/lib/gcc/i686-linux-gnu/4.6.3 -L/usr/lib -L/usr/lib -L/lib test.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/i686-linux-gnu/4.6.3/crtend.o /usr/lib/crtn.o

现在就是来看看这一坨乱七八糟的东西是啥

这里先从最基本的看起吧。

首先看看目标文件的格式,在linux上中间文件的格式和可执行文件的格式很像,差别无非是中间文件中一些引用的函数和变量的地址是相对地址,经过链接后在可执行文件中变成了绝对地址

linux上可执行文件的格式是ELF(Executable Linkable Format)

以下是一个实例小程序test.c 这个东西应该是非常具备典型行的。

1 #include<stdio.h> 

2

3 int a = 1

4 double b; 

5 static int e; 

6 

7 static int c = 2

8 static double d; 9

10 void func(int x, double y){

11 printf("%d, %f\n", x, y); 

12 }

13 int main(){ 

14 

15 static int c_m = 4;

16 static double d_m; 

17 

18 int a_m = 3;

19 double b_m; 

20 func(a_m, d_m);

21 return 0;

22 }

以下是test.c代码与生成的test.o文件一些最主要段的对应关系:

深入理解链接和加载过程(一)_第2张图片

这个是通过objdump -h test.o命令得来的:

深入理解链接和加载过程(一)_第3张图片

size表示段的大小,VMA表示段的虚拟内存地址,LMA表示段的加载地址,在链接之前都是0,file off表示段在文件中的偏移量

.text是文件代码段,存放可执行代码,局部变量的声明和定义也是在其中。

.data是数据段,存放已初始化的全局变量,全局静态变量,和局部静态变量。

.bss存放的是未初始化的全局静态变量,局部静态变量,可能还有未初始化的全局变量(这个取决于不同的编译器,我的gcc编译器是不将未初始化的全局变量放在.bss中的,只是声明了一个符号,等到最终链接成可执行文件的时候再在.bss段分配空间)

从上图也可以看到,.bss段的CONTENTS属性不存在,即表明这个段实际上是不存在的,只是标示了符号而已。

还有一点,变量的存放是要字节对齐的,故一个int和两个double实际上占了24个字节。

.rodata是只读数据段,在这里存放的是printf("%d, %f\n", x, y);中的d和f

.comment段.note.GNU-stack和.eh_frame暂时不管了

可以使用objdump -s -d test.o察看各个段中的内容:

深入理解链接和加载过程(一)_第4张图片

先看section .text中的内容:

对照程序的反汇编结果

深入理解链接和加载过程(一)_第5张图片

 可以发现.text中存放的正是func和main函数的指令0000-002e存放func的指令。002f-005c存放的是main的指令。

在看section .data:

发现从低地址至高地址12个字节依次存放的是a, c, c_m值

看section .rodata:

从后面的ASII码就知道他存放的确实是%d,%f两个只读的数据

现在已经基本弄清楚了ELF文件的一些布局和结果,之后再继续研究研究。

你可能感兴趣的:(深入理解链接和加载过程(一))