ABI 与 API 的区别

1.ABI简介

C++可执行文件是由编译器将源代码编译成目标文件后链接生成的,那么如果目标文件由不同的编译器编译生成,能否相互链接呢?例如,能够将MSVC编译出来的目标文件和GCC编译出来的目标文件链接到一起,生成一个可执行文件吗?

对于上面这个问题,如果链接器可以将MSVC编译出来的目标文件和GCC编译出来的目标文件链接到一起,那么链接器首先需要支持MSVC编译生成的目标文件的格式PE/COFF和GCC的ELF格式。此外,不同格式的目标文件需要拥有相同的符号修饰标准、变量内存分布方式、函数调用方式等等。其中目标文件格式、符号修饰标准、变量内存分布方式、函数调用方式等这些跟二进制可执行代码兼容性相关的内容称为ABI(Application Binary Interface)。

ABI(Application Binary Interface)为应用程序二进制接口,描述了应用程序和操作系统之间,一个应用和它的库之间,或者应用的组成部分之间的二进制层面的底层接口。影响ABI的因素非常多,硬件、编程语言、编译器、链接器、操作系统等都会影响ABI。我们以C语言为例,来看编程语言是如何影响ABI的。对于C语言的目标代码来说,以下几个方面会决定目标文件之间是否二进制兼容:
(1)内置数据类型(如char、int、float等)的大小和在存储器中的存放方式(大端、小端、对齐方式等);
(2)组合数据类型(struct、union等)的存储方式和内存分布;
(3)外部符号和用户定义的符号之间的命名方式和解析方式。比如函数名func在C语言目标文件中是否会被解析成外部符号_func;
(4)函数调用方式,比如参数入栈顺序、返回值如何保存等;
(5)函数栈的分布方式,比如参数和局部变量在堆栈里的位置,参数传递方法等;
(6)寄存器使用约定,函数调用时哪些寄存器可以修改,哪些需要保存等。

以上只是部分因素会影响到ABI,其它的不再一一列举。到了C++,语言层面对ABI的影响又增加了许多额外的内容,可见,这些内容使得C++做到二进制兼容比C更加困难。增加的内容有:
(1)继承体系下的对象内存布局,比如基类、虚基类在继承类中的位置;
(2)指向类成员函数的指针(Pointer to Member)的内存分,如何通过指向成员函数的指针调用成员函数,如何传递this指针;
(3)如何调用虚函数,虚函数表的内容和分布形式,虚函数表指针在对象中的位置等;
(4)template如何实例化;
(5)外部符号的修饰;
(6)全局对象的构造和析构;
(7)异常的产生和捕获机制;
(8)标准库的细节问题、RTTI如何实现等;
(9)内联函数访问细节。

C++新增的内容包括但限于以上内容,对ABI的兼容带了极大的挑战,C++二进制兼容性不够友好也一直为人诟病。不仅不同编译器编译的二进制代码之间无法相互兼容,有时候连同一个编译器的不同版本之间的兼容性也不好。比如开发团队A开发了一个使用CompilerA版本的编译器编译的库libA.a,开发团队B开发了一个使用CompilerB版本的编译器编译的库libB.a,开发团队C想将库 libA.a 和库 libB.a 链接到程序ProgramC中,可能会出现不兼容的情况。

事实上C++ ABI尚未统一,目前的兼容情况仍不乐观,主要有两套体系,一是微软的Visual C++使用的标准,另一个是GNU的GCC所使用的标准,二者互不兼容。

2.API简介

API(Application Programming Interface)是应用程序编程接口,是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需了解源码和内部实现细节。比如,POSIX 标准,C99 标准等都是对API的规定。其中,POSIX 标准是不同操作系统间的API标准,POSIX 标准定义了类 Unix 操作系统应该为应用程序提供的接口。C99 标准则规定了C语言所需提供的库函数。

API是一种源代码级别的接口,同一个API,在不同平台(不同操作系统或硬件平台)的具体实现方式可能不同,但是接口的形式和功能必须一致,这样就能够保证使用统一的接口实现跨平台开发。如果API不兼容,那么开发出来的程序换一个环境将无法运行,即出现不兼容的情况。比如 POSIX 规定 printf() 原型必须统一,不同平台间的功能也需要是相同的,即向标准输出格式化输出用户指定的内容,这样就能保证应用程序在使用 printf() 后,在不同平台可以正确运行。

3.ABI 与 API 的区别

通过上面的介绍,可以知道 ABI 和 API 是两种完全不同的东西。二者的区别可总结概括为如下几点:
(1)描述的内容不同。ABI规定了二进制文件的格式、内容、装载/卸载程序的要求、函数调用时的参数传递规则、寄存器、堆栈的使用;API规定操作系统、硬件平台、服务组件、语言函数库等需要提供的功能函数接口;
(2)作用的层面不同。ABI描述二进制层面的接口,API描述代码级层面的接口;
(3)兼容的难度不同。ABI的兼容程度比API更为严格,即ABI实现兼容更加困难。


参考文献

[1]余甲子,石凡,潘爱民.程序员的自我修养——链接、装载与库[M].北京:机械工业出版社,2009.C4.4.3C++与ABI.P115-P117

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页