程序员的自我修养 8.8分
读书笔记 静态链接
knightley
既然每个编译器都能将源代码编译成目标文件,那么有没有不同编译器编译出来的目标文件是不能够相互链接的呢?有没有可能将MSCV编译出来的目标文件和GCC编译出来的目标文件连接到一起,形成一个可执行文件呢?

提出问题——一个好问题。提出问题至少说明是在思考。

对于上面这些问题,首先我们想到的是,如果要将两个不同编译器的编译结果链接到一起,那么,首先连接器必须支持这两个编译器产生的目标文件的格式。比如MSVC编译器的目标文件是COFF/PE格式的,而GCC编译的结果是ELF格式的,那么连接器必须同时认识这两种格式才行,否则肯定没戏。那是不是连接器只要同时认识目标文件的格式就可以了呢?
事实并不是我们想象的这么简单,如果要将两个编译器编译出来的目标文件能够相互链接,那么这两个目标文件必须满足下面这些条件:采用同样的目标文件格式、拥有同样的符号修饰标准、变量的内存分布方式相同、函数的调用方式相同,等等。其中,我们把符号修饰标准,变量内存布局、函数调用方式等这些跟可执行代码二进制兼容相关的内容称为ABI(Application Binary Interface)。

解答上面提出来的问题。查资料,做实验来解决问题。

ABI&API
很多时候我们会碰到API(Application Programming Interface)这个概念,它与ABI只有一字之差,而且非常类似,很多人经常将它们的概念混淆。那么它们之间有什么区别呢?实际上它们都是所谓的应用程序接口,只是它们所描述的接口所在层面不一样。API往往指源代码级别的接口,比如我们可以说POSIX是一个API标准、Windows所规定的的应用程序接口是一个API;而ABI是二进制层面的接口,ABI的兼容程度要比API的更为严格,比如我们可以说C++的对象内存分布(Object Memory Layout)是C++ ABI的一部分。API更关注源代码层面的,比如POSIX规定printf()这个函数的原型,它能保证这个函数定义在所有遵循POSIX标准的系统之间都是一样的,但是它不保证printf在实际的每个系统中执行时,是否按照从右到左将参数压入堆栈,参数在堆栈中如何分布等这些实际运行时的二进制级别的问题。比如有两台机器,一台是Intel X86,另一台是MIPS的,他们都安装了Linux系统,由于Linux支持POSIX标准,所以他们的C运行库都应该有printf函数。但实际上printf在被调用过程中,这些关于参数和堆栈分布的细节在不同机器上肯定是不一样的,甚至调用printf的指令也是不一样的(x86是call指令,MIPS是jal指令),这就是说,API相同并不表示ABI相同。
ABI的概念其实从开始至今一直存在,因为人们总是希望程序能够在不经任何修改的情况下得到重用,最好的情况是二进制的指令和数据能够不加修改地得到重用。人们始终在朝这个方向努力,但是由于现实的因素,二进制级别的重用还是很难实现。最大的问题之一就是各种硬件平台、编程语言、编译器、链接器和操作系统之间的ABI互相不兼容,由于ABI的不兼容,各个目标文件之间无法相互链接,二进制兼容性更加无从谈起。
影响ABI的因素非常多,硬件、编程语言、编译器、链接器、操作系统等都会影响ABI。我们可以从C语言的角度来看一个编程语言是如何影响ABI的。对于C语言的目标代码来说,以下几个方面会决定目标文件之间是否二进制兼容:
1.内置类型(如int、float、char等)的大小和在存储器中的放置方式(大端、小端、对齐方式等)。
2.组合类型(如struct、union、数组等)的存储方式和内存分布。
3.外部符号(external-linkage)与用户定义的符号之间的命名方式和解析方式,如函数名func在C语言的目标文件中是否被解析成外部符号_func。
4.函数调用方式,比如参数入栈顺序、返回值如何保持等。
5.堆栈的分布方式,比如参数和局部变量在堆栈里的位置,参数传递方法等。
6.寄存器使用约定,函数调用时哪些寄存器可以修改,哪些需要保存,等等。
当然这只是一部分因素,还有其他因素我们在此不一一列举了。到了C++的时代,。。。。。。

上面只是通过编程语言这一个角度来说明问题的,实际问题确实太多了。

0
《程序员的自我修养》的全部笔记 294篇
豆瓣
免费下载 iOS / Android 版客户端