我一直不清楚ABI是什么。别给我指维基百科上的文章。如果我能理解,我就不会在这里发这么长的帖子了。

这是我对不同界面的看法:

电视遥控器是用户和电视之间的接口。它是一个现有的实体,但本身无用(不提供任何功能)。遥控器上每个按钮的所有功能都在电视机中实现。

Interface: It is an "existing entity" layer between the functionality and consumer of that functionality. An interface by itself doesn't do anything. It just invokes the functionality lying behind. Now depending on who the user is there are different type of interfaces. Command Line Interface (CLI) commands are the existing entities, the consumer is the user and functionality lies behind. functionality: my software functionality which solves some purpose to which we are describing this interface. existing entities: commands consumer: user Graphical User Interface(GUI) window, buttons, etc. are the existing entities, and again the consumer is the user and functionality lies behind. functionality: my software functionality which solves some problem to which we are describing this interface. existing entities: window, buttons etc.. consumer: user Application Programming Interface(API) functions (or to be more correct) interfaces (in interfaced based programming) are the existing entities, consumer here is another program not a user, and again functionality lies behind this layer. functionality: my software functionality which solves some problem to which we are describing this interface. existing entities: functions, Interfaces (array of functions). consumer: another program/application. Application Binary Interface (ABI) Here is where my problem starts. functionality: ??? existing entities: ??? consumer: ???

我用不同的语言编写过软件,并提供过不同类型的接口(CLI、GUI和API),但我不确定是否曾经提供过ABI。

维基百科说:

abi涵盖了诸如 数据类型、大小和对齐方式; 调用约定,它控制函数的实参 传递和返回检索到的值; 系统调用编号以及应用程序应该如何进行系统调用 到操作系统; 其他abi标准化细节,如 c++名字mangling, 异常传播,以及 调用约定的编译器之间在同一平台,但做 不需要跨平台兼容性。

谁需要这些细节?请不要说操作系统。我懂汇编编程。我知道如何链接和加载工作。我知道里面发生了什么。 为什么c++会出现名字混淆?我以为我们是在谈论二元的层面。为什么会出现语言?

无论如何,我已经下载了[PDF] System V应用程序二进制接口版4.1(1997-03-18)来看看它到底包含了什么。大部分都说不通啊。

Why does it contain two chapters (4th & 5th) to describe the ELF file format? In fact, these are the only two significant chapters of that specification. The rest of the chapters are "processor specific". Anyway, I though that it is a completely different topic. Please don't say that ELF file format specifications are the ABI. It doesn't qualify to be an interface according to the definition. I know, since we are talking at such a low level it must be very specific. But I'm not sure how is it "instruction set architecture (ISA)" specific? Where can I find Microsoft Windows' ABI?

这些是困扰我的主要问题。


当前回答

简而言之,从哲学上讲,只有某种类型的东西才能相处得很好,而ABI可以被看作是一种软件东西一起工作的东西。

其他回答

为了调用共享库中的代码,或者在编译单元之间调用代码,object文件需要包含调用的标签。c++修改了方法标签的名称,以加强数据隐藏并允许重载方法。这就是为什么您不能混合来自不同c++编译器的文件,除非它们显式地支持相同的ABI。

区分ABI和API的最好方法是了解它的用途和原因:

对于x86-64,通常有一个ABI(对于x86 32位,有另一组ABI):

http://www.x86-64.org/documentation/abi.pdf

https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/140-x86-64_Function_Calling_Conventions/x86_64.html

http://people.freebsd.org/~obrien/amd64-elf-abi.pdf

Linux + FreeBSD + MacOSX紧随其后,略有变化。Windows x64有自己的ABI:

http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/

Knowing the ABI and assuming other compiler follows it as well, then the binaries theoretically know how to call each other (libraries API in particular) and pass parameters over the stack or by registers etc. Or what registers will be changed upon calling the functions etc. Essentially these knowledge will help software to integrate with one another. Knowing the order of the registers / stack layout I can easily piece together different software written in assemblies together without much problem.

但是API是不同的:

它是一个定义了参数的高级函数名,这样如果不同的软件使用这些API构建,就可以相互调用。但是必须遵守SAME ABI的附加要求。

例如,Windows曾经是POSIX API兼容的:

https://en.wikipedia.org/wiki/Windows_Services_for_UNIX

https://en.wikipedia.org/wiki/POSIX

Linux也是POSIX兼容的。但是二进制文件不能被移动并立即运行。但是因为它们在POSIX兼容的API中使用了相同的NAMES,所以您可以在C语言中使用相同的软件,在不同的操作系统中重新编译它,并立即让它运行起来。

API是为了简化软件集成-预编译阶段。所以在编译之后,如果ABI不同的话,软件看起来会完全不同。

ABI的目的是在二进制/汇编级别定义软件的精确集成。

Linux共享库最小可运行ABI示例

在共享库的上下文中,“拥有稳定的ABI”最重要的含义是,在库更改后不需要重新编译程序。

例如:

如果您正在销售一个共享库,您可以为用户省去为每个新版本重新编译依赖于您的库的所有内容的麻烦 如果您正在销售依赖于用户发行版中的共享库的闭源程序,如果您确定ABI在目标操作系统的某些版本上是稳定的,那么您可以发布和测试更少的预构建。 这在C标准库的情况下尤其重要,您的系统中有许多程序都链接到C标准库。

现在我想提供一个最小的具体可运行的示例。

c

#include <assert.h>
#include <stdlib.h>

#include "mylib.h"

int main(void) {
    mylib_mystruct *myobject = mylib_init(1);
    assert(myobject->old_field == 1);
    free(myobject);
    return EXIT_SUCCESS;
}

mylib.c

#include <stdlib.h>

#include "mylib.h"

mylib_mystruct* mylib_init(int old_field) {
    mylib_mystruct *myobject;
    myobject = malloc(sizeof(mylib_mystruct));
    myobject->old_field = old_field;
    return myobject;
}

mylib.h

#ifndef MYLIB_H
#define MYLIB_H

typedef struct {
    int old_field;
} mylib_mystruct;

mylib_mystruct* mylib_init(int old_field);

#endif

编译和运行良好:

cc='gcc -pedantic-errors -std=c89 -Wall -Wextra'
$cc -fPIC -c -o mylib.o mylib.c
$cc -L . -shared -o libmylib.so mylib.o
$cc -L . -o main.out main.c -lmylib
LD_LIBRARY_PATH=. ./main.out

现在,假设对于标准库的v2,我们希望向mylib_mystruct添加一个名为new_field的新字段。

如果我们在old_field之前添加字段,如下所示:

typedef struct {
    int new_field;
    int old_field;
} mylib_mystruct;

重建了图书馆,但不是主要的。Out,则断言失败!

这是因为这一行:

myobject->old_field == 1

已生成程序集,该程序集试图访问结构体的第一个int,该结构体现在是new_field,而不是预期的old_field。

因此,这个更改破坏了ABI。

但是,如果我们在old_field之后添加new_field:

typedef struct {
    int old_field;
    int new_field;
} mylib_mystruct;

那么旧生成的程序集仍然访问结构的第一个int,程序仍然可以工作,因为我们保持了ABI的稳定。

下面是这个例子在GitHub上的一个全自动版本。

保持此ABI稳定的另一种方法是将mylib_mystruct视为不透明结构,仅通过方法帮助程序访问其字段。这样可以更容易地保持ABI的稳定,但是由于我们要进行更多的函数调用,因此会产生性能开销。

API 与 ABI

在前面的例子中,有趣的是,在old_field之前添加new_field只破坏了ABI,而没有破坏API。

这意味着,如果我们根据标准库重新编译main.c程序,无论如何它都会工作。

然而,如果我们改变了例如函数签名,我们也会破坏API:

mylib_mystruct* mylib_init(int old_field, int new_field);

因为在这种情况下,main.c将完全停止编译。

语义API vs编程API

我们还可以将API更改分为第三种类型:语义更改。

语义API通常是API应该做什么的自然语言描述,通常包含在API文档中。

因此,可以在不破坏程序构建本身的情况下破坏语义API。

例如,如果我们修改了

myobject->old_field = old_field;

to:

myobject->old_field = old_field + 1;

那么这既不会破坏编程API,也不会破坏ABI,但是main.c语义API会破坏。

有两种方法可以通过编程方式检查合约API:

测试一些极端情况。这很简单,但你可能总是错过一个。 正式的验证。更难做到,但产生了正确性的数学证明,本质上是将文档和测试统一为“人”/机器可验证的方式!当然,前提是你的正式描述中没有bug;-) 这个概念与数学本身的形式化密切相关:https://math.stackexchange.com/questions/53969/what-does-formal-mean/3297537#3297537

打破C / c++共享库abi的所有东西的列表

待办事项:查找/创建最终列表:

https://github.com/lvc/abi-compliance-checker自动化工具进行检查 https://community.kde.org/Policies/Binary_Compatibility_Issues_With_C%2B%2B KDE c++ ABI指南 https://plan99.net/~mike/writing-shared-libraries.html

Java最小可运行示例

Java中的二进制兼容性是什么?

在Ubuntu 18.10, GCC 8.2.0中测试。

应用程序二进制接口(ABI)类似于API,但是调用者不能在源代码级别上访问该函数。只有二进制表示是可访问的/可用的。

abi可以在处理器体系结构级别或操作系统级别定义。 abi是编译器的代码生成器阶段要遵循的标准。该标准由操作系统或处理器决定。

功能:定义机制/标准,使函数调用独立于实现语言或特定的编译器/链接器/工具链。提供允许JNI或Python-C接口等的机制。

现有实体:机器代码形式的函数。

消费者:另一个函数(包括用另一种语言编写的、由另一种编译器编译或由另一种链接器链接的函数)。

调用方和被调用方之间的ABI需要一致,以确保调用成功。堆栈使用,寄存器使用,程序结束堆栈弹出。所有这些都是ABI中最重要的部分。