编译和链接

参考《程序员的自我修养》

1.源代码为什么需要编译链接

编译类的语言(c 或者c++)写出的源代码机器是看不懂的,机器看懂的是可执行的代码。而源代码转化成可执行代码需要经过编译和链接。

2.虚拟地址空间布局

生成完一个可执行文件a.out之后,我们需要把指令和数据从磁盘加载到虚拟地址空间中去。我们先看一下可执行文件都加载到虚拟地址空间中哪些地方。

#include <stdio.h>

int gdata1 = 10;
int gdtata2 = 0;
int gdtata3;

static int gdata4 = 11;
static int gdata5 = 0;
static int gdata6;

int main()
{
	int a = 12;
	int b = 0;
	int c;

	static int d = 13;
	static int e = 0;
	static int f;

	return 0;
}


3.编译链接步骤

看完虚拟内存布局之后,我们看一下编译链接步骤

1)预编译  2)编译 3)汇编 4)链接


该图来源《程序员的自我修养》

我们看一下这个图来讲一下每个步骤都做了哪些事情

预编译

test.cpp文件和相关头文件预编译成.i文件

1)将所有#define删除 ,并展开宏定义

2)处理所有的预编译指令  比如#if

3)处理#include 预编译指令,将包含的头文件插入到预编译指令的位置

4)删除注释/* //

5)添加行号和文件标识

6)保留#pragma编译器指令

简单来说就是展开需要展开的东西,删除一些不要的东西。不检查代码

编译

1)词法分析

2)语法分析

3)语义分析

4)代码优化

5)汇总当前文件的所有符号


汇编

1).s文件中有很多汇编指令,根据特定平台(windows linux)把它们转化成特定的机器码。

2)构建.obj格式

链接

1)合并所有obj文件的段,并调整段偏移和段长度, 合并符号表

2)符号解析完成后,分配到虚拟地址

3)链接核心   符号的重定位

看完概述之后,我们具体看一下这几个问题

产生了obj组成格式是什么

链接都做了什么,可执行文件为什么能运行,从哪里运行

4.obj文件格式


这个是.obj文件格式

ELF Header 这个里面保存了整个文件的基本属性,版本,程序入口地址等,文件头大小,这样也能往下偏移多少

主要存储了一个关键段表。段表保存了段名,长度,文件的偏移。这里就包括了.bss段。要知道ELF文件中没有单独保存.bss段

信息是存储在段表中的。.bss少了个数据,其实是gdata3,它是一个弱符号,不知道会不会被其他目标文件强符号代替。所以它是放在.comment段里的。.symtab是符号表,里面存放了符号。符号也是链接不可缺少的东西。

5.链接

符号:

我们将函数统称为符号,符号表就是把所有符号进行分类,函数名和变量名就是符号名。符号表记录了目标文件所用到的符号,每个符号都有一个对应的符号值。对于变量和函数来说,就是它们的地址。

我们先看一段代码

//main.c
#include <stdio.h>
extern int gdata10;
extern int sum(int,int);

int gdata1 = 10;
int gdata2 = 0;
int gdata3;

static gdata4 = 11;
static gdata5 = 0;
static gdata6;

int main()
{
 	int a = 12;
	int b = 0;
	int c = gdata10;

	static int d = 13;
	static int e = 0;
	static int f;
	sum(a,b);
	return 0;
}

//sum.c
int gdata10 = 14;

int sum(int a,int b)
{
	return a+b;
} 

产生的main.o文件中的,两个外部引用的符号。*UND* gdata 10  和 * UND*sum 因为是分离编译,当前文件找不到。

函数和数据在编译阶段都是不分配地址的。数据地址是0地址,函数地址是-4地址


链接步骤

相似段合并

这个就是把所有目标文件相同属性(可读,可写,可读可写之类的)的段合并在一起,因为obj按4个字节对齐,可执行文件是按页面4kb对齐。

在段表里重新调整下,每个段的起始地址和段的偏移量。合并符号表。

符号解析

链接器在扫描完所有的输入目标文件后,所有这些未定义的符号都应该能够在全局符号表中找到,否则链接器报符号未定义错误。

给符号分配地址


这里符号解析完成之后,链接器给所有分配新的地址。但是指令段里面地址还是不对的。于是要把指令段的地址改正确。这就是重定位。

重定位



这个就是重定位,把指令段的符号地址进行改动。数据符号地址是绝对地址,而函数符号涉及到了指令跳转所以存的是偏移量。这样的话就算完成编译链接。

程序运行

./a.out

1)创建虚拟地址空间到物理内存的映射创建页目录和页表

2)加载代码段和数据段

3)把可执行文件的入口地址写到CPU的pc寄存器里面














已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 鲸 设计师:meimeiellie 返回首页