C++编译链接整体介绍
链接主要工作
-
1 所有.o文件段的合并,符号表合并后,进行符号解析
链接时就是在符号表中找对应的符号是否只出现于
.text
或.data
段一次,若一次都无,则符号未定义;若出现多次,符号重定义 -
符号重定位(重定向),给所有符号分配虚拟地址,前面都是全0
根据如下两段代码分析
其中注释表示符号和存放在进程虚拟地址空间的位置
//main.cpp
// 引用sum.cpp里定义得全局变量及函数
extern int gdata; // gdata *UND*
int sum(int, int); // _Z3sumii *UND*
int data = 20; // data .data
int main() // main .text
{
int a = gdata;
int b = data;
int ret = sum(a, b);
return 0;
}
// sum.cpp
int gdata = 20; // gdata .data
int sum(int a, int b) // _Z3sumii .text
{
return a + b;
}
编译生成目标文件:
jyhlinux@ubuntu:~/share/test$ g++ -c main.cpp
jyhlinux@ubuntu:~/share/test$ g++ -c sum.cpp
查看main.o符号表:
objdump -t main.o
查看sum.o符号表
*UND* -undefine,表示引用该符号
分析main.o文件头
打印目标文件各个段:readelf -S main.o
(seg)
分析main.o的.text段
jyhlinux@ubuntu:~/share/test$ g++ -c main.cpp -g
jyhlinux@ubuntu:~/share/test$ objdump -S main.o
由上图可知编译过程中,符号是不分配虚拟地址的里面看汇编代码发现变量的值都为全0,说明符号值还没加载进来,这也是.o无法执行的原因之一
符号什么时候分配虚拟地址:链接过程,第一步符号解析完成后
手动链接:ld -e main *.o
查看可执行文件a.out
的符号表
可以看出每个符号都有其对应的虚拟地址以及所在的区域,运行时就可以将其加载到指定段中(如.data .text)
分析可执行文件的elf头
再看一下a.out
的汇编代码
可以发现main
函数内第一条指令地址就是前面的入口地址(entry
)
a.out
比 *.o 多了program headers
段,有两个load,告知系统,运行时把哪些内容加载到内存中(.text, .data)
运行程序时的工作大概如下图,数据加载到指定段,将虚拟地址映射到物理地址空间:
cpu虚拟地址,做一个地址映射
页面异常=》执行地址映射页面异常处理程序,分配物理内存
杂项
进阶阅读
- 《CSAPP》第七章
- 《程序员的自我修养》第2、3、4、6章