连接器和加载器::第0章 写在前面
原著:John R. Levine
原文:收藏
翻译:lover_P
[内容]
我的妻子Tonia和我的女儿Sarah。
连接器(linker)和加载器(loader)几乎在计算机刚一出现就成为了软件工具包的一部分,因为若允许以多个模块的形式建立程序而不是将其作为一个整体,连接器和加载器就是不可或缺的工具。
早在1947年,程序员们就开始使用原始的加载器来获取存贮在不同的磁带上的程序例程并将它们组合、重定位(relocating)到一个程序中。二十世纪60年代早期,这些加载器进化为羽翼丰满的连接编辑器。由于程序存储器仍然昂贵和有限、计算机(以现在的标准来看)仍然很慢,这些连接器往往具有复杂的特性,用来建立复杂的存储复用(relay)结构以将大程序塞到小存储器中,以及用来重新编译已经连接的程序以节约从头开始建立一个程序所需的时间。
二十世纪70年代到80年代之间,连接技术没有什么进展。连接器愈加简单,虚拟存储(virtual memory)将存储管理的工作从应用程序和占位程序中转移到操作系统中
。同时,计算机更快了,磁盘也更大了,通过替换一些模块来重新建立一个已连接的程序也比仅重连接改变的部分要容易得多了。到了90年代,连接器又复杂起来,添加了对现代特性包括动态连接的共享库和C++中不同寻常的需求的支持。具有宽指令字和编译器调度存储访问的新型处理器架构,如IA64,还为连接器提出了新的需求
,以确保已连接的程序中代码的复杂要求得以实现。
本书适合很多读者:
学生:编译器构造和操作系统课程通常缺乏对连接和加载的重视,这通常是由于连接过程看起来是微不足道的或显而易见的。尽管当使用的语言是Fortran、Pascal或C,并且操作系统没有使用存储映射
(memory mapping)和共享库(shared library)时确实是这样,但如今看来却不再是了。C++、Java和其他面向对象语言需要一个更为复杂的连接环境。存储映射的可执行程序、共享库和动态连接
(dynamic linking)影响着一个操作系统的方方面面,而操作系统的设计者却不愿意承担连接问题的风险。
从业程序员也应该知道连接器都做什么,尤其对于现代语言。C++对连接器有着独特的需求,连接期间出现的非预期情况通常导致大型C++程序产生难于诊断的BUG。(最著名的就是静态构造器,它以程序员所不期望的顺序执行。)连接特性如共享库和动态连接能够提供巨大的灵活性和效率,当然是在适当使用的条件下。
语言设计者和开发者在建立语言和编译器时要了解连接器做什么以及能够做什么。已掌握了30余年的编程任务在C++中却是自动的,这依赖于连接器处理的细节。(考虑要
想在C中获得C++模板的等价物,一个程序员都需要做什么;或者如何在一个有上百个C源文件的程序开始运行之前确保调用了每一个初始化历程。)将来的语言将通过更为强大的连接器来自动完成更多的程序管理任务。连接器还将参与到全局程序优化中,因为连接器是编译过程中唯一能够处理整个程序的代码并将它们作为一个单元进行转换的阶段。
(当然,编写连接器的人也应该需要这本书。但这个世界上所有的连接器作者或许都聚集在一间屋子中并且它们中的一半都有着这样一份副本,因为他们掌握着原稿。)
第1章 连接和加载,对连接器的历史作了一个简短的概述,并讨论了连接过程中的各个阶段。这一章以一个简短但完整的例子——从输入的目标文件到一个可执行的“Hello、world”程序的过程中连接器的运行过程——作为结束。
第2章 架构问题,从连接器设计的角度回顾计算机架构。这一章研究了SPARC——一个典型的精简指令集架构、IBM360/370——一个古老但仍然有用的寄存器-存储器架构。以及自成一家的Intel
x86。重要的架构方面包括存储架构、程序定址架构和单个指令的地址域布局。
第3章 目标文件,研究了目标文件(或称“对象文件,object file)和可执行文件的内部结构。这一章从非常简单的文件——MS-DOS
.COM文件——开始,进一步研究了更复杂的文件,包括DOS EXE、Windows COFF和PE(EXE和DLL)、Unix a.out和ELF以及Intel/Microsoft
OMF。
第4章 存储分配,涵盖了连接的第一步——为连接后的程序的段分配存储空间,并辅以真实编译器例子。
第5章 符号管理,涵盖了符号的绑定和解析,这个过程将一个文件中对另一个文件中的名字进行的符号引用解析为一个机器地址。
第6章 库,涵盖了目标代码库的建立和使用,以及库的结构和性能问题。
第7章 重定位,
涵盖了地址重定位、调整程序中的目标代码以在运行时反映实际地址的过程。这一章还涵盖了地址无关的代码(PIC,Position Independent
Code),以这种方式建立的代码可以避免重定位;以及这样做所需要的开销和可以获得的利益。
第8章 加载和复用,涵盖了加载的过程,从文件获取一个程序并装入到计算机内存中进行运行。这一章还涵盖了树状的复用结构——一个古老但仍然有效的节约地址空间的技术。
第9章 共享库,
研究在多个程序之间共享一个库代码的一份单独的副本都需要什么。这一章集中讨论了静态连接共享库。
第10章 动态连接和加载,继续第9章,对动态连接的共享库进行讨论。这一章详细地介绍了两个例子,Windows32动态连接库(DLL,Dynamic
Link Libraries)和Unix/Linux ELF共享库。
第11章 高级技术,研究精密的现代连接器所做的很多事情。这一章涵盖了C++所要求的新特性,包括“名字管理”、全局构造器和析构器、模板展开以及重复代码的清除;其他技术,包括增量连接
(incremental linking)、连接时垃圾收集、连接时代码生成和优化、加载时代码生成,以及剖面测试和仪器工具。这一章以对一种比其他连接器予以更复杂的连接模型——Java连接模型的概述告终。
第12章 参考,一个带有注解的参考条目。
从第3章到第11章有一个持续的项目——用Perl开发一个很小但功能俱全的连接器。尽管Perl并不是一个成品连接器的实现语言,但它对于团队项目来说却是一个卓越的选择。Perl可以
避免低级语言如C或C++中一些小毛病所导致的程序终止,这可以使学生们集中精力于项目所需的算法和数据结构。Perl可以免费地用在很多现代计算机上,包括Windows
95/98和NT、Unix和Linux,并且还有很多优秀的书籍可以教会新手使用Perl。(参考第12章可以获得一些建议。)
第3章中最初的项目是建立一个连接器的骨架,可以按照一个简单但完整的目标格式读取和写入文件,接下来的各章中依次向这个编译器中添加功能,直到最后产生一个羽翼丰满的连接器,能够支持共享库并产生动态可连接的对象。
Perl能够处理任意的二进制文件和数据结构,因此项目所建立的连接器将可以适用于处理本地目标格式。
很多很多的人都慷慨地将他们的时间投入到对本书原稿的阅读和评论中,这些人既有出版社的评论员,也有comp.compilers
usenet新闻组中阅读并评论过原稿的在线版本的读者。他们包括(按字母顺序):Mike Albaugh、Rod Bates、Gunnar
Blomberg、Robert Bowdidge、Keith Breinholt、Brad Brisco、Andreas Buschmann、David S.
Cargo、John Carr、David Chase、Ben Combee、Ralph Corderoy、Paul Curtis、Lars
Duening、Phil Edwards、Oisin Feeley、Mary Fernandez、Michael Lee Finney、Peter H.
Froehlich、Robert Goldberg、James Grosbach、Rohit Grover、Quinn Tyler Jackson、Colin
Jensen、Glenn Kasten、Louis Krupp、Terry Lambert、Doug Landauer、Jim Larus、Len
Lattanzi、Greg Lindahl、Peter Ludemann、Steven D. Majewski、John McEnerney、Larry
Meadows、Jason Merrill、Carl Montgomery、Cyril Muerillon、Sameer Nanajkar、Jacob
Navia、Simon Peyton-Jones、Allan Porterfield、Charles Randall、Thomas David
Rivers、Ken Rose、Alex Rosenberg、Raymond Roth、Timur Safin、Kenneth G Salter、Donn
Seeley、Aaron F. Stanton、Harlan Stenn、Mark Stone、Robert Strandh、Bjorn De
Sutter、Ian Taylor、Michael Trofimov、Hans Walheim、and Roger Wong。
这些人多这本书中很多的陈述负有责任。其他没有提到的仍然是作者的责任。(如果你发现了一些,请按照后面的地址和我们联系,他们可以将其添加到后续版本中。)
我要特别感谢我的编辑Morgan-Kaufmann Tim Cox和Sarah Luger,他们容忍了我在写作过程中过多的耽搁,并把这本书的片断集中到一起。
这本书有一个支持站点http://linker.iecc.com。这里包含本书的示例章节、Perl代码示例和项目的目标文件,以及更新和勘误表。
你可以为作者发送电子邮件,地址为linker@iecc.com。作者会阅读所有的邮件,但由于数量庞大,可能不会迅速地回答所有的问题。
[回顶端]