深入理解计算机系统在线看,《深入理解计算机系统》--链接

本文详细介绍了链接器的工作原理,包括静态链接、动态链接和运行时链接。静态链接器将多个目标文件合并成一个可执行文件,而动态链接则允许在程序加载或运行时完成链接。此外,还探讨了目标文件的类型,如可重定位、可执行和共享目标文件,并讲解了加载可执行文件到内存的过程。动态链接库在运行时加载,提高了资源利用率。
摘要由CSDN通过智能技术生成

链接可以执行与编译时,也就是在源代码被翻译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到存储器并执行时;甚至可以执行于运行时,由应用程序来执行。

从传统静态链接到加载时的共享库的动态链接,以及到运行时的共享库的动态链接。

一、编译器驱动程序

/* $begin main */

/* main.c */

void swap();

int buf[2] = {1, 2};

int main()

{

swap();

return 0;

}

/* $end main */

/* $begin swap */

/* swap.c */

extern int buf[];

int *bufp0 = &buf[0];

int *bufp1;

void swap()

{

int temp;

bufp1 = &buf[1];

temp = *bufp0;

*bufp0 = *bufp1;

*bufp1 = temp;

}

/* $end swap */

函数main()调用swap交换外部全局数据buf中的两个元素。这个例子贯穿全文,分析链接是如何工作的。但是上述的交换方式非常奇怪!!!大多数编译系统提供编译驱动程序,它代表用户在需要时调用语言预处理器、编译器、汇编器和链接器。

b91e158617b0bf5f9009635aee78c7fc.png

二、静态链接

静态链接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出。输入的可重定位目标文件由各种不同的代码和数据节组成。指令在一个节中,初始化的全局变量在另一个节中,而未初始化的变量又在另外一个节中。

为构造可执行文件,链接器必须完成两个主要任务:

符号解析 目标文件定义和引用符号。符号解析的目的是将每个符号引用刚好和一个符号定义联系起来。

重定位  编译器额汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置,从而重定位这些节。

目标文件纯粹是字节快的集合。这些块中,有些包含程序代码,有些则包含程序数据,而其他的则包括指导链接器和加载器的数据结构。链接器将这些块链接起来,确定被连接块的运行时位置,并且修改代码和数据块中的各种位置。链接器对目标机器了解甚少。产生目标文件的编译器和汇编器已经完成了大部分工作。

三、目标文件

目标文件有三种形式:

可重定位目标文件 包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。在上图可以发现(我觉得就是单个CPP编译出来的.o文件)

可执行目标文件 包含二进制代码和数据,其形式可以被直接拷贝到存储器并执行(最终被编译好的可执行的二进制文件)

共享目标文件  一种特殊类型的可重定位目标文件,可以在加载或者运行时被动态地加载到存储器并链接。(共享库之类的)

编译器和汇编器生成可重定义目标文件(包括共享目标文件)。链接器生成可执行目标文件。

各个系统之间,目标文件格式都不相同。

四、可重定位目标文件

223670b0ca4f3ecfef27feeed906321c.png

一个典型的ELF可重定位目标文件包含下面几个节:

.text:已编译程序的机器代码

.rodata:只读数据,比如printf语句中的格式串和开关语句的跳转表

.data已初始化的全局C变量。局部C变量在运行时保存在栈中,既不出现在.data节中,也不会出现在.bss中

.bss 未初始化的全局C变量。

八、可执行目标文件

331424502d040752a2434ec7ff0c4859.png

九、加载可执行目标文件

在32位linux系统中,代码段总是从地址0x08048000处开始。数据段是在接下来的一个4KB对齐的地址处。运行时堆在读/写段之后接下来的第一个4KB对齐的地质处,并通过调用malloc库往上增长。用户栈总是从最大的合法用户地址开始,向下增长的。

b46f5b4fcbaf6e7dae4de691bf09576c.png

加载的工作流程:

UNIX系统中的每个程序都运行在一个进程上下文中,有自己的虚拟地址空间。当外壳运行一个程序时,父外壳进程生成一个子进程,它是父进程的一个复制品。子进程通过execve系统调用启动加载器。加载器删除子进程现有的虚拟存储器段,并创建一组新的代码、数据、堆和栈段、新的栈和堆段被初始化为零。通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件的内容。最后,加载器跳转到_start地址,它最终会调用应用程序的main函数。除了一些头部信息,在加载过程中没有任何从磁盘到存储器的数据拷贝。直到CPU应用一个被映射的虚拟页才会进行拷贝,此时,操作系统利用它的页面调度机制自动将页面从磁盘传送到存储器。

十、动态链接共享库

9a8a66a1216bec03b1c4ffff65d68088.png

十一、从应用程序中加载和链接共享库

在程序中加载和链接动态库是通过函数加载的!

ps:链接可以在编译时由静态编译器来完成,也可以在加载时和运行时由动态链接器来完成。链接器处理称为目标文件的二进制文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值