10.程序的链接
可执行文件生成过程
程序的功能是在屏幕上输出 hello world。
文本编辑器输入程序,hello.c ,用 ASCII 表示的文本文件
预处理,(cpp) hello.c 处理为 hello.i 用 ASCII 表示的文本文件
编译,(cc1) hello.i 处理为 hello.s 汇编语言程序文件,这还是用 ascii 表示的文本
汇编,(as) hello.c 处理为 hello.o 可重定位目标程序,二进制文件,一条一条机器指令,计算机直接是被执行
源程序调用了 printf ,需要用 ld 链接,把不同模块拼接起来
Linux 下使用的工具
预处理过程,处理预编译指令,
gcc -E hello.c -o hello.i
cpp hello.c > hello.i
删除 #define 并展开对应的宏
处理条件预编译指令
插入头文件到 #include ,递归方式处理
删除注释
添加行号和文件摸标识,供调试用
保留编译指令,#pragma 后面编译器用
编译过程,词法分析,语法分析,语义分析,生成汇编代码文件。此程序称为编译器,compiler
gcc -S hello.i -o hello.s
gcc -S hello.c -o hello.s
两步合一步/usr/lib/gcc/***/cc1 hello.c
编译后,得到汇编代码文件(汇编语言源程序),人可读的 ascii 文件,CPU 不可读
汇编过程,汇编器把汇编语言源程序转换为机器指令序列(机器语言程序)。汇编代码源程序和机器语言程序都是机器级代码,一一对应,
gcc -c hello.s -o hello.o
gcc -c hello.c -o hello.o
三步合一as hello.s -o hello.o
得到的为可重定位目标文件,
链接,前面步骤针对的是一个模块(一个 .c 文件),得到一个目标文件,链接将多个 .o 合并,生成可执行文件
gcc -static -o myproc main.o test.o
ld -static -o myproc main.o test.o
不指定输出文件名,默认为 a.out
gcc 是上面的 cpp cc1 as 工具的集合。
链接器
最早的时候,穿孔纸袋编程。存在跳转地址,但是只有修改了就比较麻烦。后来出现了汇编,用符号表示跳转位置,而非绝对地址,即汇编程序中做的事,程序确定后,手工汇编。链接时,先确定标号的地址,然后把地址填入最终的二进制文件中。
高级语言出现后,多人开发,子程序的地址,实际上函数名就是符号。函数名、变量名就是符号的定义引用。本质上和汇编中的符号没区别。最终编译,在链接之前,调用时跳转的地址是无法确定的。链接时,才能确定最终的地址
链接操作的步骤
符号解析:确定符号的引用,互相之间的调用关系
合并模块,把代码和数据都合到一起,放到同一个地址空间,虚拟地址空间,
确定符号地址,合并不同模块到一个地址空间以后,代码放一起,数据放一起,相对位置救恩确定了
指令中填入新地址。
链接的好处:
模块化,
一个程序可以分模块
公共库,如数学、C标准库
效率高
时间上,可以分开编译,只编译修改过的模块
空间上,源文件无需包含共享库的源码,
链接的本质
例程
每个模块有自己的代码数据。初始化的全局变量,未初始化的全局变量,静态变量,局部变量。
局部变量分配在栈中,不能在过程(函数)外被引用,因此不是符号定义。
gcc -O2 -g -o p main.c swap.c
两个源码分别转换生成 main.o 和 swap.o ,生成可重定位的二进制文件,此时这里面的符号还没有值,地址没有值。
链接过程的本质 : 合并相同的节
上面代码里,每个模块有的节 .text .data .bss
可执行文件存储
可执行文件里有一段,程序头表,程序头表描述了如何映射,即链接时,程序不知道是去哪里执行的,代码段会装到内存里去执行,具体装载的位置还不知道。链接的时候把可执行文件合并到了虚拟地址空间里,
在程序运行时的空间,
目标文件
目标文件指包含目标代码(机器语言代码)的文件。早期非标准,几种标准的格式
DOS 里 .com ,
linux 里 elf 格式
三类目标文件
可重定位目标文件。代码和数据可以和其他可重定位文件合并为可执行文件
每个 .o 由对应的 .c 生成
每个 .o 文件代码和数据地址从 0 开始
可执行目标文件
linux 默认为 a.out
windows 为 .exe
包含的代码和数据可以直接被复制到内存执行
代码和数据地址为虚拟地址空间中的地址
共享的目标文件 (linux中的
*.so
)特殊的可重定位目标文件,在运行时装入内存并自动链接,共享库文件
windows 中为
*.dll
可重定位文件和可执行文件都是 ELF 格式的文件,可以使用 file
指令查看。
ELF 文件的两种视图
链接视图(被链接):可重定位目标文件
执行视图(被执行):可执行目标文件
从链接视图来看,ELF 文件由节构成,节是 ELF 中由相同特征的最小处理单位
.text
.data
.rodata
.bss
从执行视图来看,ELF 文件由段组成,程序头表反映了不同节和段的关系。节到段的映射。比如 .data 节和 .bss 节映射到一个可读可写段中。
可重定位文件
.c 经过编译汇编生成 .o ,这个文件里有不同的节
ELF 头,
包含16byte标识信息,
文件l类型 (.o ,so)
机器类型 (IA-32)
节头表的偏移、节头表表项大小和个数
.text 节
编译之后的代码部分
.rodata 节
只读数据,如printf的格式串,switch case 的跳转表
.data 节
已初始化的全部变量
.bss 节
未初始化的全局变量
.bss 节仅是占位符,文件中不占用磁盘空间,仅在节头表里说有这么个东西。节头表指出长度。区分初始化和未初始化是为了空间效率。
最后更新于