我一直不清楚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的一个共同之处是它是一个接口。可重用程序公开了一个稳定的接口(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).

其他回答

总结

对于定义ABI(应用程序二进制接口)的确切层有各种各样的解释和强烈的意见。

在我看来,ABI是对特定API的给定/平台的主观约定。ABI是对于特定API“不会改变”的约定的“剩余”部分,或者由运行时环境解决:执行器、工具、链接器、编译器、jvm和OS。

定义接口:ABI, API

如果你想使用像joda-time这样的库,你必须声明一个依赖joda-time-<major>.<minor>.<patch>.jar。标准库遵循最佳实践并使用语义版本控制。这在三个层次上定义了API的兼容性:

补丁——你根本不需要修改你的代码。这个库只是修复了一些错误。 次要-你不需要改变你的代码,因为添加的东西(开闭原则是尊重的) 重要—接口(API)已更改,您可能需要更改代码。

为了让你使用同一个库的一个新的主要版本,还有很多其他的约定需要遵守:

库使用的二进制语言(在Java情况下是定义Java字节码的JVM目标版本) 调用约定 JVM规范 链接约定 运行时约定 所有这些都是由我们使用的工具定义和管理的。

例子

Java案例研究

例如,Java标准化了所有这些约定,不是在一个工具中,而是在一个正式的JVM规范中。该规范允许其他供应商提供一组不同的工具来输出兼容的库。

Java为ABI提供了另外两个有趣的案例研究:Scala版本和Dalvik虚拟机。

Dalvik虚拟机破坏了ABI

The Dalvik VM needs a different type of bytecode than the Java bytecode. The Dalvik libraries are obtained by converting the Java bytecode (with same API) for Dalvik. In this way you can get two versions of the same API: defined by the original joda-time-1.7.2.jar. We could call it joda-time-1.7.2.jar and joda-time-1.7.2-dalvik.jar. They use a different ABI one is for the stack-oriented standard Java vms: Oracle's one, IBM's one, open Java or any other; and the second ABI is the one around Dalvik.

Scala后续版本不兼容

Scala在次要的Scala版本之间不具有二进制兼容性:2。X。由于这个原因,相同的API“io。reactivex" %% "rxscala" % "0.26.5"有三个版本(将来会有更多):针对Scala 2.10、2.11和2.12。改变了什么?我现在不知道,但是二进制文件是不兼容的。可能最新的版本增加了一些东西,使得库在旧的虚拟机上无法使用,可能是与链接/命名/参数约定有关的东西。

Java连续版本是不兼容的

Java在JVM的主要版本上也有问题:4,5,6,7,8,9。它们只提供向后兼容性。Jvm9知道如何运行针对所有其他版本的编译/目标代码(javac的-target选项),而JVM 4不知道如何运行针对JVM 5的代码。而你只有一个joda-library。由于有不同的解决方案,这种不兼容性变得显而易见:

语义版本控制:当库的目标是更高的JVM时,它们通常会改变主版本。 使用JVM 4作为ABI,您就安全了。 Java 9增加了一个关于如何在同一个库中包含特定目标JVM的字节码的规范。

为什么我要从API定义开始呢?

API and ABI are just conventions on how you define compatibility. The lower layers are generic in respect of a plethora of high level semantics. That's why it's easy to make some conventions. The first kind of conventions are about memory alignment, byte encoding, calling conventions, big and little endian encodings, etc. On top of them you get the executable conventions like others described, linking conventions, intermediate byte code like the one used by Java or LLVM IR used by GCC. Third you get conventions on how to find libraries, how to load them (see Java classloaders). As you go higher and higher in concepts you have new conventions that you consider as a given. That's why they didn't made it to the semantic versioning. They are implicit or collapsed in the major version. We could amend semantic versioning with <major>-<minor>-<patch>-<platform/ABI>. This is what is actually happening already: platform is already a rpm, dll, jar (JVM bytecode), war(jvm+web server), apk, 2.11 (specific Scala version) and so on. When you say APK you already talk about a specific ABI part of your API.

API可以移植到不同的ABI

抽象的顶层(针对最高API编写的源代码可以被重新编译/移植到任何其他较低层次的抽象。

假设我有一些rxscala的源代码。如果Scala工具改变了,我可以重新编译它们。如果JVM发生了变化,我就可以从旧机器自动转换到新机器,而不需要考虑高级概念。虽然移植可能很困难,但对任何其他客户端都有帮助。如果使用完全不同的汇编代码创建一个新的操作系统,则可以创建一个转换器。

跨语言移植的api

有些api可以移植到多种语言中,比如反应流。一般来说,它们定义到特定语言/平台的映射。我认为API是用人类语言甚至是特定的编程语言正式定义的主规范。在某种意义上,所有其他的“映射”都是ABI,比通常的ABI更多的API。REST接口也是如此。

应用程序二进制接口(ABI)

功能:

从程序员的模型到底层系统的域数据的转换 类型,大小,对齐,调用约定,它控制如何 函数的参数被传递并返回检索到的值;的 系统调用编号以及应用程序应该如何进行系统调用 到操作系统;高级语言编译器的名称 破坏方案、异常传播和调用约定 在同一平台上的编译器之间,但不需要 跨平台兼容性……

现有的实体:

直接参与程序执行的逻辑块:ALU, 通用寄存器,用于内存/ I/O映射的寄存器,等等…

消费者:

语言处理器,链接器,汇编器…

任何必须确保构建工具链作为一个整体工作的人都需要这些。如果你用汇编语言写一个模块,用Python写另一个模块,而不是你自己的引导加载程序想要使用操作系统,那么你的“应用程序”模块是跨“二进制”边界工作的,需要这种“接口”的协议。

c++命名混乱,因为应用程序中可能需要链接来自不同高级语言的目标文件。考虑使用GCC标准库对Visual c++构建的Windows进行系统调用。

ELF是用于解释的来自对象文件的链接器的一种可能期望,尽管JVM可能有其他想法。

对于一个Windows RT商店应用程序,如果你真的想让一些构建工具链一起工作,尝试搜索ARM 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”进行比较。

您已经熟悉了API的概念。如果你想使用某些库或操作系统的特性,你将根据API进行编程。API由数据类型/结构、常量、函数等组成,您可以在代码中使用它们来访问外部组件的功能。

An ABI is very similar. Think of it as the compiled version of an API (or as an API on the machine-language level). When you write source code, you access the library through an API. Once the code is compiled, your application accesses the binary data in the library through the ABI. The ABI defines the structures and methods that your compiled application will use to access the external library (just like the API did), only on a lower level. Your API defines the order in which you pass arguments to a function. Your ABI defines the mechanics of how these arguments are passed (registers, stack, etc.). Your API defines which functions are part of your library. Your ABI defines how your code is stored inside the library file, so that any program using your library can locate the desired function and execute it.

ABIs are important when it comes to applications that use external libraries. Libraries are full of code and other resources, but your program has to know how to locate what it needs inside the library file. Your ABI defines how the contents of a library are stored inside the file, and your program uses the ABI to search through the file and find what it needs. If everything in your system conforms to the same ABI, then any program is able to work with any library file, no matter who created them. Linux and Windows use different ABIs, so a Windows program won't know how to access a library compiled for Linux.

有时,ABI更改是不可避免的。当这种情况发生时,任何使用该库的程序都将无法工作,除非它们被重新编译以使用新版本的库。如果ABI改变了,但API没有改变,那么新旧库版本有时被称为“源代码兼容”。这意味着,虽然为一个库版本编译的程序不能与另一个库版本一起工作,但为一个库版本编写的源代码如果重新编译,则可以与另一个库版本一起工作。

For this reason, developers tend to try to keep their ABI stable (to minimize disruption). Keeping an ABI stable means not changing function interfaces (return type and number, types, and order of arguments), definitions of data types or data structures, defined constants, etc. New functions and data types can be added, but existing ones must stay the same. If, for instance, your library uses 32-bit integers to indicate the offset of a function and you switch to 64-bit integers, then already-compiled code that uses that library will not be accessing that field (or any following it) correctly. Accessing data structure members gets converted into memory addresses and offsets during compilation and if the data structure changes, then these offsets will not point to what the code is expecting them to point to and the results are unpredictable at best.

除非您正在进行非常低级的系统设计工作,否则您不必显式地提供ABI。它也不是特定于语言的,因为(例如)C应用程序和Pascal应用程序在编译后可以使用相同的ABI。

Edit: Regarding your question about the chapters regarding the ELF file format in the SysV ABI docs: The reason this information is included is because the ELF format defines the interface between operating system and application. When you tell the OS to run a program, it expects the program to be formatted in a certain way and (for example) expects the first section of the binary to be an ELF header containing certain information at specific memory offsets. This is how the application communicates important information about itself to the operating system. If you build a program in a non-ELF binary format (such as a.out or PE), then an OS that expects ELF-formatted applications will not be able to interpret the binary file or run the application. This is one big reason why Windows apps cannot be run directly on a Linux machine (or vice versa) without being either re-compiled or run inside some type of emulation layer that can translate from one binary format to another.

IIRC, Windows目前使用可移植可执行文件(PE)格式。在维基百科页面的“外部链接”部分有关于PE格式的更多信息的链接。

Also, regarding your note about C++ name mangling: When locating a function in a library file, the function is typically looked up by name. C++ allows you to overload function names, so name alone is not sufficient to identify a function. C++ compilers have their own ways of dealing with this internally, called name mangling. An ABI can define a standard way of encoding the name of a function so that programs built with a different language or compiler can locate what they need. When you use extern "c" in a C++ program, you're instructing the compiler to use a standardized way of recording names that's understandable by other software.

我也试图理解ABI, JesperE的回答很有帮助。

从一个非常简单的角度来看,我们可以尝试通过考虑二进制兼容性来理解ABI。

KDE wiki将库定义为二进制兼容的,“如果动态链接到库的前版本的程序继续与库的新版本一起运行,而不需要重新编译”。有关动态链接的更多信息,请参阅静态链接与动态链接

现在,让我们试着看看一个库需要二进制兼容性的最基本方面(假设库没有源代码更改):

相同/向后兼容的指令集架构(处理器指令、寄存器文件结构、堆栈组织、内存访问类型,以及处理器可以直接访问的基本数据类型的大小、布局和对齐) 相同调用约定 同名混淆约定(如果Fortran程序需要调用一些c++库函数,这可能是需要的)。

当然,还有许多其他细节,但这主要是ABI所涵盖的内容。

更具体地回答你的问题,由以上,我们可以推断:

ABI功能:二进制兼容性 现有实体:现有程序/库/操作系统 消费者:库,操作系统

希望这能有所帮助!