天天看点

从编译器角度来理解C++代码的编译和链接原理

我们可以在linux系统下对下列代码是如何进行编译进行一个理解

main.cpp:

//引用sum.cpp文件里面定义的全局变量以及函数
extern int gdata;
int sum (int, int) ;
int data = 20;
int main ()
{
	int a = gdata;
	int b = data;
	int ret =sum (a, b);
	return o;
}

           

sum.cpp:

int gdata = 10;
int sum(int a, int b)
{
	return a+b;
}
           
从编译器角度来理解C++代码的编译和链接原理

接下来我们将从以下几个问题展开:

1.*.o文件的格式组成是什么样的?

2.可执行文件的组成格式是什么样子?

3.上图中步骤一和步骤二是做的什么事情?

4.符号表的输出里面的符号怎么理解?

5.符号什么时候分配虚拟地址?

预编译(.c -> .i): 1.#开头的命令的预处理(除了#pragma lib和#pragma link)2.将所有的“#define”删除,并且展开所有的宏定义。3.注释的删除和替换4.头文件的引入。4.保留所有的#pragma编译器指令,因为编译器须要使用它们。

编译(.i -> .s):词法分析;语法分析;语义分析;以及优化后生产相应的汇编代码文件。

汇编(.s -> .o):汇编分为两种x86和AT&T;将汇编代码转变成机器可以执行的指令

,生成二进制可重定位的目标文件。

链接(.o -> a.out):编译完成的所有.o文件+静态库文件进行链接,第一个步骤就是将所有.o文件段的合并,符号表合并后进行符号解析;第二个步骤就是符号的重定位(链接的核心)。

.o文件的格式组成:

elf文件头

.text

.data

.bss

.symbal

.section table

下面我们详解一下.o文件段的合并:

就是当我们main.o sum.o合并的时候各个段就要进行合并了.text <=> .text .bss <=> .bss .data<=> .data等等之间进行合并

符号解析:所有对符号的引用,都要找到该符号定义的地方。

符号解析成功以后!!!!就要给所有的符号分配虚拟地址。(即符号的重定向)

符号什么时候分配虚拟地址:

链接的第一步符号解析完成后进行分配虚拟地址的。(编译过程符号是不分配虚拟地址的)

我们二进制可重定位目标文件和可执行文件最大的区别是什么呢?

可执行文件有program headers段。由两个load告诉系统运行这个程序的时候把哪些内容加载到内存中,加载的是代码段和数据段,由下图可以看出:

从编译器角度来理解C++代码的编译和链接原理

可执行文件加载的大概过程如下图:

从编译器角度来理解C++代码的编译和链接原理