gcc的编译过程

 

GCC的编译链接过程包括:

1. C预处理器(cpp),处理所有的宏定义,生成文本文件.i

2. C编译器(cc1),生成汇编语言文件.s

3. 汇编器(as)生成可重定位的目标文件.o

4. 链接器(ld)把所有的.o和库组合成一个可执行的目标文件

a) 符号解析:将每个符号的引用和一个符号定义联系起来

b) 重定位:编译(cc1和as)生成的代码(.text)和数据(.data)节的起始地址是0,需要把每个符号的定义和一个存储器位置联系起来,然后修改所有对这些符号的引用。

典型的ELF包含以下几个节:

1. .text:指令

2. .rodata:只读数据,例如printf的格式串

3. .data:已初始化的全局变量

4. .bss:未初始化的全局变量

5. .symtab:符号表,每个可重定位目标文件都有,不包含局部变量(和-g编译选项区别)

6. .rel.text:.text节的重定位信息,所有引用外部函数或全局变量的指令都需要修改

7. .rel.data:.data节的重定位信息,例如已初始化全局变量的值是全局变量或外部定义函数的地址

8. .strtab:以null结尾的字符串列表,包含.symtab和.debug等节的字符串信息(符号名字)

每个可重定位目标模块m都有一个符号表,包含:

1. 由m定义并能被其他模块引用的全局符号,非静态的函数和非静态的全局变量

2. 由其他模块定义并被模块m引用的全局符号,也就是外部符号

3. 只被模块m定义和引用的本地符号,静态的函数和静态的全局变量

符号表条目定义:

typedef struct {

int name;

int value;

int size;

char type:4,

binding:4;

char reserved;

char section;

} Elf_Symbol;

1. name:是.strtab表中的偏移量,指向符号的名字

2. value:对于可重定位模块,value是距离定义该符号的节的起始位置的偏移;对于可执行文件,该值是一个绝对运行时地址

3. size:目标的大小

4. type:函数或者数据

5. binding:全局或者本地

6. section:定义该符号的节的索引(节头表)。有3个伪节:

a) ABS:不能被重定位的符号

b) UNDEF:未定义的符号

c) COMMON:未被分配位置的未初始化的数据目标,value给出对齐的要求。最终链接时将作为一个.bss目标分配

静态库(.a)

1. 所有的标准C函数放在一个单独的可重定位目标模块中(libc.o),缺点是:每个可执行程序都包含libc.o的完整拷贝;对标准函数的任何改变都需要重新编译整个源文件。

2. 为每个标准函数创建分离的可重定位文件(printf.o strcpy.o…),缺点是:要求程序员显式的指定所用到的所有目标文件。

把各个独立的目标模块封装成一个单独的静态库文件,在链接时,链接器只拷贝被引用的目标模块。

Linux的静态库是由ar工具创建的一组可重定位目标文件的集合。

ar rcs out.a in1.o in2.o

假设链接器定义了3个集合:

1. 可重定位目标文件的集合E

2. 未解析符号的集合U

3. 前面输入文件中已定义符号的集合D

初始的E、U和D都是空。

链接器的规则:

1. 命令行输入的文件f是目标文件,把f添加到E,修改U和D来反映f中的符号定义和引用

2. 命令行输入的文件f是静态库

a) 如果其中的模块m,定义了一个符号解析U中的一个引用,把m加入E,修改U和D来反映m中的符号定义和引用

b) 静态库中的每个模块都重复动作a,直到U和D不再变化为止。

c) 没被包含进E的所有静态库的模块被丢弃

3. 最终,假如U非空,链接器报错。否则合并和重定位E中所有文件,生成可执行文件。

可推导出2个结论:

1. 构建静态库时的目标文件顺序不重要

2. 链接时的库和目标文件之间的顺序很重要,假如定义一个符号的库出现在引用这个符号的目标文件之前,那么引用就不能被解析,链接失败。

链接器完成符号解析后进行重定位过程:

1. 重定位节和符号定义:链接器将所有同类型的节合并,把运行时地址赋给新的聚合节,同时修改符号表,把输入模块定义的每个节和每个符号的地址都被更新。

2. 重定位节中的符号引用:根据重定位表目修改代码节和数据节中对每个符号的引用,指向正确的运行时地址。

汇编器遇到对最终位置未知的目标引用,就生成一个重定位表目,告诉链接器在合并生成可执行文件时如何修改这个引用。

重定位表目定义:

typedef {

int offset;

int symbol:24,

type:8;

}Elf32_Rel;

1. offset:需要被修改的引用的节偏移

2. symbol:被修改引用所指向的符号

3. type:如何修改

a) R_386_PC32:使用32位PC相关的地址引用

b) R_386_32:使用32位绝对地址的引用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值