我一直不清楚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如果

你的程序没有函数,而且—— 你的程序是一个单独运行的可执行文件(即一个嵌入式系统),它实际上是唯一在运行的东西,它不需要与其他任何东西对话。

过度简化的总结:

API:“这里是你可以调用的所有函数。” ABI:“这是调用函数的方法。”

ABI是编译器和链接器遵守的一组规则,以便编译您的程序,使其正常工作。ABIs涵盖多个主题:

Arguably the biggest and most important part of an ABI is the procedure call standard sometimes known as the "calling convention". Calling conventions standardize how "functions" are translated to assembly code. ABIs also dictate the how the names of exposed functions in libraries should be represented so that other code can call those libraries and know what arguments should be passed. This is called "name mangling". ABIs also dictate what type of data types can be used, how they must be aligned, and other low-level details.

更深入地了解调用约定,我认为它是ABI的核心:

机器本身没有“功能”的概念。当你用高级语言(如c)编写函数时,编译器会生成一行汇编代码,如_MyFunction1:。这是一个标签,它最终将被汇编程序解析为一个地址。这个标签标记了程序集代码中“函数”的“开始”。在高级代码中,当你“调用”这个函数时,你真正做的是导致CPU跳转到那个标签的地址并继续在那里执行。

在为跳转做准备时,编译器必须做一些重要的事情。调用约定就像一个清单,编译器遵循它来完成所有这些事情:

First, the compiler inserts a little bit of assembly code to save the current address, so that when your "function" is done, the CPU can jump back to the right place and continue executing. Next, the compiler generates assembly code to pass the arguments. Some calling conventions dictate that arguments should be put on the stack (in a particular order of course). Other conventions dictate that the arguments should be put in particular registers (depending on their data types of course). Still other conventions dictate that a specific combination of stack and registers should be used. Of course, if there was anything important in those registers before, those values are now overwritten and lost forever, so some calling conventions may dictate that the compiler should save some of those registers prior to putting the arguments in them. Now the compiler inserts a jump instruction telling the CPU to go to that label it made previously (_MyFunction1:). At this point, you can consider the CPU to be "in" your "function". At the end of the function, the compiler puts some assembly code that will make the CPU write the return value in the correct place. The calling convention will dictate whether the return value should be put into a particular register (depending on its type), or on the stack. Now it's time for clean-up. The calling convention will dictate where the compiler places the cleanup assembly code. Some conventions say that the caller must clean up the stack. This means that after the "function" is done and the CPU jumps back to where it was before, the very next code to be executed should be some very specific cleanup code. Other conventions say that the some particular parts of the cleanup code should be at the end of the "function" before the jump back.

有许多不同的abi /调用约定。主要有:

x86或x86-64 CPU(32位环境): CDECL STDCALL FASTCALL VECTORCALL THISCALL x86-64(64位环境): SYSTEMV MSNATIVE VECTORCALL ARM CPU(32位) AAPCS ARM处理器(64位) AAPCS64

这里有一个很棒的页面,它实际显示了为不同的abi编译时生成的程序集的差异。

另一件需要提及的事情是,ABI不仅仅与程序的可执行模块内部相关。链接器还使用它来确保程序正确调用库函数。您的计算机上运行着多个共享库,只要编译器知道它们各自使用的ABI,它就可以正确地从它们调用函数,而不会破坏堆栈。

编译器理解如何调用库函数是非常重要的。在一个托管平台上(也就是说,一个OS加载程序的平台),如果不调用内核,您的程序甚至不能闪烁。

其他回答

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)

ABI -应用二进制接口是关于运行时两个二进制部分之间的机器码通信,如应用程序,库,操作系统…ABI描述了如何将对象保存在内存中,如何调用函数(调用约定),如何修改…

API和ABI的一个很好的例子是iOS生态系统从v5开始使用Swift语言。

Application layer - When you create an application using different languages. For example you can create application using Swift and Objective-C[Mixing Swift and Objective-C] Application - OS layer - runtime - Swift Standard Library and Swift Run Time Library[About] are parts of OS and they should not be included into each bundle(e.g. app, framework). It is the same as like Objective-C uses. Available from iOS v12.2 Library layer - Module Stability case - compile time - you will be able to import a framework which was built with another version of Swift's compiler. It means that it is safety to create a closed-source(pre-build) binary which will be consumed by a different version of compiler( .swiftinterface is used with .swiftmodule[About]) and you will not get Module compiled with _ cannot be imported by the _ compiler //or Compiled module was created by a newer version of the compiler Library layer - Library Evolution case Compile time - if a dependency was changed, a client has not to be recompiled. Runtime - a system library or a dynamic framework can be hot-swapped by a new one.

[API vs ABI] [Swift模块稳定性和库稳定性]

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

实际上你根本不需要ABI如果

你的程序没有函数,而且—— 你的程序是一个单独运行的可执行文件(即一个嵌入式系统),它实际上是唯一在运行的东西,它不需要与其他任何东西对话。

过度简化的总结:

API:“这里是你可以调用的所有函数。” ABI:“这是调用函数的方法。”

ABI是编译器和链接器遵守的一组规则,以便编译您的程序,使其正常工作。ABIs涵盖多个主题:

Arguably the biggest and most important part of an ABI is the procedure call standard sometimes known as the "calling convention". Calling conventions standardize how "functions" are translated to assembly code. ABIs also dictate the how the names of exposed functions in libraries should be represented so that other code can call those libraries and know what arguments should be passed. This is called "name mangling". ABIs also dictate what type of data types can be used, how they must be aligned, and other low-level details.

更深入地了解调用约定,我认为它是ABI的核心:

机器本身没有“功能”的概念。当你用高级语言(如c)编写函数时,编译器会生成一行汇编代码,如_MyFunction1:。这是一个标签,它最终将被汇编程序解析为一个地址。这个标签标记了程序集代码中“函数”的“开始”。在高级代码中,当你“调用”这个函数时,你真正做的是导致CPU跳转到那个标签的地址并继续在那里执行。

在为跳转做准备时,编译器必须做一些重要的事情。调用约定就像一个清单,编译器遵循它来完成所有这些事情:

First, the compiler inserts a little bit of assembly code to save the current address, so that when your "function" is done, the CPU can jump back to the right place and continue executing. Next, the compiler generates assembly code to pass the arguments. Some calling conventions dictate that arguments should be put on the stack (in a particular order of course). Other conventions dictate that the arguments should be put in particular registers (depending on their data types of course). Still other conventions dictate that a specific combination of stack and registers should be used. Of course, if there was anything important in those registers before, those values are now overwritten and lost forever, so some calling conventions may dictate that the compiler should save some of those registers prior to putting the arguments in them. Now the compiler inserts a jump instruction telling the CPU to go to that label it made previously (_MyFunction1:). At this point, you can consider the CPU to be "in" your "function". At the end of the function, the compiler puts some assembly code that will make the CPU write the return value in the correct place. The calling convention will dictate whether the return value should be put into a particular register (depending on its type), or on the stack. Now it's time for clean-up. The calling convention will dictate where the compiler places the cleanup assembly code. Some conventions say that the caller must clean up the stack. This means that after the "function" is done and the CPU jumps back to where it was before, the very next code to be executed should be some very specific cleanup code. Other conventions say that the some particular parts of the cleanup code should be at the end of the "function" before the jump back.

有许多不同的abi /调用约定。主要有:

x86或x86-64 CPU(32位环境): CDECL STDCALL FASTCALL VECTORCALL THISCALL x86-64(64位环境): SYSTEMV MSNATIVE VECTORCALL ARM CPU(32位) AAPCS ARM处理器(64位) AAPCS64

这里有一个很棒的页面,它实际显示了为不同的abi编译时生成的程序集的差异。

另一件需要提及的事情是,ABI不仅仅与程序的可执行模块内部相关。链接器还使用它来确保程序正确调用库函数。您的计算机上运行着多个共享库,只要编译器知道它们各自使用的ABI,它就可以正确地从它们调用函数,而不会破坏堆栈。

编译器理解如何调用库函数是非常重要的。在一个托管平台上(也就是说,一个OS加载程序的平台),如果不调用内核,您的程序甚至不能闪烁。

答:简单地说,ABI与API的一个共同之处是它是一个接口。可重用程序公开了一个稳定的接口(API),可用于在另一个程序中重用该程序。

B. However, an ABI is an interface issued for some specific processor-platform for some specific language. All compiler-vendors desiring to target that platform for that same language will have to ensure that not only compiled code in form of relocatable object codes comply with the interface to be able to link and cross-link with each other but also executables comply with it to be able to run on the platform at all. So, ABI is much broader set of specifications/standard than a typical function API. It may include some API objects to be enforced upon the language-users by the compiler. The compiler-vendor will have to include support for the same in their distributions. Needless to say, the platform vendor is the rightful authority to issue ABIs for its platform. Both compiler vendors and ABIs need to comply with the corresponding language-standard (e.g. ISO standard for C++).

C.平台供应商对ABI的定义是:

“1。可执行文件为了在特定的执行环境中执行而必须遵守的规范。例如,Arm架构的Linux ABI。

独立生成的可重定位文件必须遵守的规范的一个特定方面,以便静态可链接和可执行。例如,Arm架构的c++ ABI, Arm架构的运行时ABI, Arm架构的C库ABI。”

D.举例;基于Itanium架构的c++通用ABI也由一个联盟发布。平台供应商自己的c++的abi在多大程度上符合它完全取决于平台供应商。

E.作为另一个例子。Arm架构的c++ ABI在这里。

F.前面已经说过,处理器体系结构的ABI将确保一个可重用程序和另一个重用它的程序之间的API适用于该处理器体系结构。

G. That brings us to service-oriented components (e.g. SOAP-based web services). They too require an API to exist between a SOAP-based web service and client program (could be an app, front-end or another web service) for the client program to reuse the web service.The API is described in terms of standardized protocols like WSDL (interface description) and SOAP(message format) and is language-neutral and platform-neutral. It is not targeted to any specific processor-platform and thus it is not "binary" like ABI. A client-program on any one platform type and written in any language can remotely reuse a web service written in any other language and hosted on an entirely different processor-platform. This is made possible by the fact that both WSDL and SOAP are text-based (XML) protocols. In case of RESTful web services, the transport protocol http--also a text-based protocol-- itself acts as the API (CRUD methods).