> 文章列表 > 从编译器角度理解C++编译和连接原理

从编译器角度理解C++编译和连接原理

从编译器角度理解C++编译和连接原理

C++编译链接整体介绍

在这里插入图片描述

链接主要工作

  1. 1 所有.o文件段的合并符号表合并后,进行符号解析

    链接时就是在符号表中找对应的符号是否只出现于.text.data段一次,若一次都无,则符号未定义;若出现多次,符号重定义

  2. 符号重定位(重定向),给所有符号分配虚拟地址,前面都是全0

根据如下两段代码分析

其中注释表示符号和存放在进程虚拟地址空间的位置

//main.cpp
// 引用sum.cpp里定义得全局变量及函数
extern int gdata;  // gdata *UND*
int sum(int, int); // _Z3sumii *UND*int data = 20; // data .dataint 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.cppjyhlinux@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 -gjyhlinux@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虚拟地址,做一个地址映射

页面异常=》执行地址映射页面异常处理程序,分配物理内存

杂项

进阶阅读

  1. 《CSAPP》第七章
  2. 《程序员的自我修养》第2、3、4、6章