我对各类 abi 的理解
从最简单的开始,api 决定了我有哪些函数可以调,abi 决定了调用函数的时候二进制的规范是怎么样的,传参是怎么传的,最简单的举例就是 abi 规定用 x1 寄存器传参,这样调用者会将参数放入 x1 中,被调用者将参数从 x1 中取出,正确的实现整个函数的调用,这是对 abi 最简单的理解。
abi 分类
私以为 abi 可以进行如下的分类:
- 操作系统 abi:操作系统已经是运行于最底层的软件了,操作系统的 abi 规定了函数的调用规范(寄存器传参的语义)、ELF文件规范(怎么样的文件才能被操作系统加载)、内存布局(一个数据类型占用多少的内存)等等。对于 linux 而言,好像操作系统的 abi 延续的是 system v 的 abi 规范,称为通用的 system v abi。
- 体系结构 abi:操作系统运行于不同的体系结构之上,不同的体系结构有不同的寄存器,也有很多自己不同的特性,因此不同的体系结构会对 system v abi 进行一些扩展,形成自己体系结构的 abi,在 abi 之中增加一些自己的特性。简单的理解, system v 是一个基类,不同的体系结构形成不同体系结构的 abi 为子类,等于拓展了 system v abi,形成了自己的一些特性。以 risc-v 举例,risc-v psabi 中规定了寄存器传参的规范,在 elf 文件方面沿用了 system v 通用 abi,并增加了一些自己的拓展。常说的 c 语言中一个 long 编译出来的长度在不同的环境下可能不同,这就是因为不同的环境下体系结构 abi 是不同的,不同的体系结构 abi 规定的数据类型长度决定了编译器编译出来的长度是多少。
- 编程语言 abi:编程语言 abi,比如说 c++ 的 abi,更多的是保证库之间相互的兼容性。比如要调用一个库 A,库 A 必须和当前编译的程序使用兼容的一个 abi 才能进行调用。变成语言的 abi 中,比如 c++,比较著名的是安腾 abi 的标准,这些 abi 也会对 elf 中包含了一些信息做出规定。考虑 abi 不兼容的情况,一个库的 elf 中包含了某个信息,但是由于 abi 的不兼容,另一个程序找不到,导致了库兼容性之间的问题。