链接概述和目标文件格式
#include <stdio.h>
int main(){
printf("hello world");
}
步骤:
1. 源文件以ascll文本形式存在
2. 预处理:hello.c->hello.i(预处理文本)
3. 编译:hello.i->hello.s(汇编程序文本)
4. 汇编:helo.s->hello.o(二进制可重定位目标代码)
5. 链接:多个.o->可执行文件(二进制)
预处理:
gcc -E hello.c o hello.i
cpp hello.c > hello.i
1. 处理define/include/等宏
2. 删除注释
3. 标记行号和文件标识符
4. 保留#pragma编译指令
结果:一个不带宏的源程序文本
编译:由编译程序(编译器)执行
gcc -S hello.i -o hello.s
gcc -S hello.c -o hello.s
1. 词法 分析
2. 语法分析
3. 语义分析
结果:由汇编指令(mov代码)构成的文本,汇编源代码
汇编:汇编器将汇编源代码转成01指令(机器码)
gcc -c hello.s -o hello.o
gcc -c hello.c -o hello.o
注意:汇编源代码和机器指令是一一对应的,所以都认为是机器级代码
结果:可重定位的目标文件。虽说01指令机器可懂,但是内存地址都还没有确定,所以不可直接执行
链接:多个.o合并成可执行文件
gcc -static -o hello hello1.o hello2.o//-static :静态链接
- 符号解析:确定符号引用关系
- 合并多个.o
- 确定每个符号地址(虚内存中)
- 指令中填入新的地址(更新指令中具体使用的地址)
链接概述:
好处:
1. 模块化:多个源文件,重用性公共库
2. 效率高:时间效率:并行开发,空间:内存只需装载所需要的函数
过程:
1. 对两个.o 的对应的节合并
2. 更新地址
目标文件概述:包含目标代码的文件
格式:
1. dos:COM格式
2. 早期unix:COFF
3. windows:PE
4. Linux等:ELF
ELF:
三类目标文件:
1. 可重定位的:没经过链接,地址从0开始,按节分,链接视图
2. 可执行:经过链接,代码和数据地址是虚内存中的地址,按段分,执行视图
3. 共享的:动态链接到某个文件执行
链接视图:
1. ELF头:
1. 32位:魔数+说明信息(文件格式,机器格式,系统类型,等。。)共52B
2. 64位:
2. 节头表:描述每个节的信息(名字,起始,偏移,大小等)
执行视图:
不同之处:
1. ELF头中给出第一条指令的地址,链接视图是0
2. 多了一个程序头表(段头表),描述段中的节如何载入虚内存
3. 多.init节,初始化工作
4. 不需要.rel重定位节:已经定位好了
存储器(虚内存)映像:
1. ELF头+段头表+.init节+.text节+.rodata节---->只读代码段(0x08048000,4KB对齐)
2. .data+.bss---->读写数据段(4KB对齐)
注意.bss在文件(磁盘)不占空间,但载入虚内存是要分配空间(从节头表获取)