什么是应用程序二进制接口(ABI)?

时间:2010-01-31 09:30:24

标签: api compiler-construction binary operating-system abi

我从未明白ABI是什么。请不要指向维基百科的文章。如果我能理解,我就不会在这里张贴这么长的帖子。

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

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

  

界面:它是一个“现有实体”层   该功能的functionalityconsumer。一个接口本身   什么都不做它只是调用后面的功能。

     

现在取决于用户是谁,有不同类型的接口。

     

命令行界面(CLI)命令是现有实体,   消费者是用户和功能所在。

     

functionality:我的软件功能解决了一些问题   我们描述这个界面的目的。

     

existing entities:命令

     

consumer:用户

     

图形用户界面(GUI)窗口,按钮等是现有的   实体,消费者又是用户和功能所在。

     

functionality:我的软件功能解决了我们描述这个界面的一些问题。

     

existing entities:窗口,按钮等..

     

consumer:用户

     

应用程序编程接口(API)功能(或者是   更正确的)接口(基于接口的编程)是   现有实体,消费者在这里是另一个程序而不是用户,并且再次   功能就在这一层背后。

     

functionality:我的软件功能解决了一些问题   我们正在描述这个界面的问题。

     

existing entities:个函数,接口(函数数组)。

     

consumer:另一个程序/应用程序。

     

应用程序二进制接口(ABI)以下是我的问题所在。

     

functionality: ???

     

existing entities: ???

     

consumer: ???

  • 我用不同语言编写软件并提供不同类型的界面(CLI,GUI和API),但我不确定我是否提供过任何ABI。

Wikipedia says:

  

ABI涵盖了

等详细信息      
      
  • 数据类型,大小和对齐方式;
  •   
  • 调用约定,它控制函数的参数   传递并返回检索的值;
  •   
  • 系统调用号以及应用程序应如何进行系统调用   到操作系统;
  •   
     

其他ABI标准化细节,例如

     
      
  • C ++名称修改,
  •   
  • 异常传播,
  •   
  • 在同一平台上的编译器之间调用约定,但是   不需要跨平台兼容性。
  •   
  • 谁需要这些细节?请不要说操作系统。我知道汇编编程。我知道如何链接&装载工程。我确切地知道里面发生了什么。

  • 为什么C ++名称输入?我以为我们正在谈二进制。语言为什么会进来?

无论如何,我已经下载了[PDF] System V Application Binary Interface Edition 4.1 (1997-03-18)以查看其中包含的内容。好吧,大部分都没有任何意义。

  • 为什么它包含两章(第4和第5章)来描述ELF文件格式?实际上,这些是该规范中仅有的两个重要章节。其余章节是“处理器特定的”。无论如何,我认为这是一个完全不同的话题。请不要说ELF文件格式规范 ABI。根据定义,它不符合接口的条件。

  • 我知道,因为我们谈论的水平很低,所以必须非常具体。但我不确定它是如何“指令集架构(ISA)”具体的?

  • 我在哪里可以找到Microsoft Windows'ABI?

所以,这些是困扰我的主要疑问。

16 个答案:

答案 0 :(得分:422)

理解“ABI”的一种简单方法是将其与“API”进行比较。

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

ABI非常相似。可以将其视为API的编译版本(或作为机器语言级别的API)。编写源代码时,可以通过API访问库。编译代码后,您的应用程序将通过ABI访问库中的二进制数据。 ABI定义了编译的应用程序用于访问外部库的结构和方法(就像API一样),只在较低的层次上。

ABI对于使用外部库的应用程序非常重要。如果构建程序以使用特定库并且稍后更新该库,则您不希望必须重新编译该应用程序(并且从最终用户的角度来看,您可能没有源代码)。如果更新的库使用相同的ABI,那么您的程序将不需要更改。即使内部工作可能已经改变,库的接口(这是您的所有程序真正关心的)也是相同的。具有相同ABI的库的两个版本有时被称为“二进制兼容”,因为它们具有相同的低级接口(您应该能够用新版本替换旧版本并且没有任何重大问题)。 / p>

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

出于这个原因,图书馆编写者倾向于保持他们的ABI稳定(以尽量减少中断)。保持ABI稳定意味着不改变函数接口(返回类型和数量,类型和参数的顺序),数据类型或数据结构的定义,定义的常量等。可以添加新函数和数据类型,但现有函数和数据类型必须保持不变相同。如果将16位数据结构字段扩展为32位字段,则使用该数据结构的已编译代码将无法正确访问该字段(或任何后续字段)。访问数据结构成员在编译期间转换为内存地址和偏移量,如果数据结构发生变化,那么这些偏移量将不会指向代码期望它们指向的内容,结果最多也是不可预测的。

ABI不一定是您明确提供的内容,除非您希望人们使用程序集与您的代码进行交互。它也不是特定于语言的,因为(例如)C应用程序和Pascal应用程序在编译后将使用相同的ABI。

编辑:关于有关SysV ABI文档中有关ELF文件格式的章节的问题:包含此信息的原因是因为ELF格式定义了操作系统和应用程序之间的接口。当您告诉操作系统运行程序时,它希望程序以某种方式格式化(例如)期望二进制文件的第一部分是包含特定内存偏移量的某些信息的ELF头。这是应用程序将有关其自身的重要信息传达给操作系统的方式。如果以非ELF二进制格式(例如a.out或PE)构建程序,那么期望ELF格式的应用程序的操作系统将无法解释二进制文件或运行应用程序。这是Windows应用无法直接在Linux机器上运行(反之亦然)的一个重要原因,无需重新编译或在某种类型的仿真层中运行,仿真层可以从一种二进制格式转换为另一种二进制格式。

IIRC,Windows目前使用Portable Executable(或PE)格式。该维基百科页面的“外部链接”部分中有链接,其中包含有关PE格式的更多信息。

另外,关于C ++名称修改的注释:ABI可以为C ++编译器定义一种“标准化”方式,以便为兼容性进行名称修改。也就是说,如果我创建一个库并且您开发了一个使用该库的程序,那么您应该能够使用与我不同的编译器,而不必担心由于不同的名称修改方案而导致的二进制文件不兼容。如果您要定义新的二进制文件格式或编写编译器或链接器,那么这只是有用的。

答案 1 :(得分:123)

如果您了解程序集以及操作系统级别的工作方式,那么您就符合某个ABI。 ABI管理诸如如何传递参数,放置返回值的位置。对于许多平台,只有一个ABI可供选择,在这些情况下,ABI只是“如何工作”。

然而,ABI也管理如何在C ++中布置类/对象。如果您希望能够跨模块边界传递对象引用,或者想要混合使用不同编译器编译的代码,那么这是必要的。

此外,如果您有一个可以执行32位二进制文​​件的64位操作系统,那么对于32位和64位代码,您将拥有不同的ABI。

通常,链接到同一可执行文件的任何代码都必须符合相同的ABI。如果要使用不同的ABI在代码之间进行通信,则必须使用某种形式的RPC或序列化协议。

我认为你很难将不同类型的接口压缩成一组固定的特征。例如,界面不一定必须拆分为消费者和生产者。接口只是两个实体交互的惯例。

ABI可以(部分)与ISA无关。某些方面(例如调用约定)依赖于ISA,而其他方面(例如C ++类布局)则不依赖。

明确定义的ABI对于编写编译器的人来说非常重要。如果没有明确定义的ABI,就不可能生成可互操作的代码。

编辑:澄清的一些注释:

    ABI中的
  • “Binary”不排除使用字符串或文本。如果要链接导出C ++类的DLL,则必须对其中的方法和类型签名进行编码。这就是C ++名称变换的用武之地。
  • 你从未提供过ABI的原因是绝大多数程序员都不会这样做。 ABI由设计平台的同一人(即操作系统)提供,很少有程序员有权设计广泛使用的ABI。

答案 2 :(得分:27)

如果 -

你实际上根本不需要ABI

  • 您的程序没有功能,而且 -
  • 您的程序是一个单独运行的可执行程序(即嵌入式系统),它实际上是唯一运行的程序,它不需要与任何其他程序进行通信。

过于简化的摘要:

  

API: "以下是您可以调用的所有功能。"

     

ABI: "这是如何来调用某个功能。"

ABI是编译器和链接器遵守的一套规则,用于编译程序以便正常工作。 ABI涉及多个主题:

  • 可以说,ABI中最重要和最重要的部分是procedure call standard,有时被称为"调用约定"。呼叫约定标准化"功能"被翻译成汇编代码。
  • ABI还规定了如何表示库中公开函数的名称,以便其他代码可以调用这些库并知道应该传递哪些参数。这被称为"名称错误"。
  • ABI还规定了可以使用的数据类型,必须如何对齐以及其他低级别细节。

深入研究调用约定,我认为它是ABI的核心:

机器本身没有"功能"的概念。当您使用高级语言(如c)编写函数时,编译器会生成一行汇编代码,如_MyFunction1:。这是标签,最终将由汇编程序解析为一个地址。这个标签标志着"开始"您的"功能"在汇编代码中。在高级代码中,当您" call"那个功能,你真正做的是让CPU 到那个标签的地址并继续在那里执行。

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

  • 首先,编译器插入一些汇编代码来保存当前地址,这样当你的"函数"完成后,CPU可以跳回正确的位置并继续执行。
  • 接下来,编译器生成汇编代码以传递参数。
    • 一些调用约定规定应该将参数放在堆栈上(当然,按特定顺序)。
    • 其他约定规定参数应放在特定的寄存器中(取决于它们的数据类型当然)。
    • 还有其他约定规定应该使用堆栈和寄存器的特定组合。
  • 当然,如果之前这些寄存器中有任何重要内容,那么这些值现在会被覆盖并永远丢失,因此一些调用约定可能要求编译器在将参数放入其中之前保存其中的一些寄存器。
  • 现在,编译器插入一条跳转指令,告诉CPU转到之前创建的标签(_MyFunction1:)。此时,您可以将CPU视为" in"你的"功能"。
  • 在函数结束时,编译器会放置一些汇编代码,使CPU在正确的位置写入返回值。调用约定将指示是否应将返回值放入特定寄存器(取决于其类型)或堆栈上。
  • 现在是清理的时候了。调用约定将指示编译器放置清理汇编代码的位置。
    • 一些约定说调用者必须清理堆栈。这意味着在"功能"之后完成并且CPU跳回到之前的位置,下一个要执行的代码应该是一些非常具体的清理代码。
    • 其他惯例表示清理代码的某些特定部分应该位于"函数的末尾。 之前跳回来。

有许多不同的ABI /调用约定。一些主要的是:

  • 对于x86或x86-64 CPU(32位环境):
    • CDECL
    • STDCALL
    • FASTCALL
    • VECTORCALL
    • THISCALL
  • 对于x86-64 CPU(64位环境):
    • SYSTEMV
    • MSNATIVE
    • VECTORCALL
  • 对于ARM CPU(32位)
    • AAPCS
  • 对于ARM CPU(64位)
    • AAPCS64

Here是一个很棒的页面,实际上显示了编译不同ABI时生成的程序集的差异。

另外需要提及的是,ABI不仅仅与您程序的可执行模块中的相关 。链接器使用来确保程序正确调用库函数。您的计算机上运行了多个共享库,只要您的编译器知道它们各自使用的ABI,它就可以正确调用它们中的函数而不会炸毁堆栈。

您的编译器了解如何调用库函数非常重要。在托管平台(即操作系统加载程序的平台)上,您的程序甚至无法在不进行内核调用的情况下闪烁。

答案 3 :(得分:17)

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

ABI可以在处理器架构级别或OS级别定义。 ABI是编译器的代码生成器阶段遵循的标准。该标准由OS或处理器修复。

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

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

Consumer:另一个函数(包括另一个语言,由另一个编译器编译,或由另一个链接器链接)。

答案 4 :(得分:10)

功能:一组影响编译器,汇编编写器,链接器和操作系统的契约。合同规定了如何布置函数,传递参数的位置,参数的传递方式,函数返回的工作方式。这些通常特定于(处理器体系结构,操作系统)元组。

现有实体:参数布局,函数语义,寄存器分配。例如,ARM体系结构有许多ABI(APCS,EABI,GNU-EABI,更不用说一堆历史案例) - 使用混合ABI会导致代码在跨越边界调用时根本不起作用。

Consumer:编译器,汇编编写器,操作系统,CPU特定架构。

谁需要这些细节?编译器,汇编编写器,链接器,它们执行代码生成(或对齐要求),操作系统(中断处理,系统调用接口)。如果你进行了汇编编程,那么你就符合ABI!

C ++名称修改是一种特殊情况 - 它是一个链接器和动态链接器居中的问题 - 如果名称修改没有标准化,那么动态链接将不起作用。从此以后,C ++ ABI被称为C ++ ABI。它不是链接器级别问题,而是代码生成问题。一旦有了C ++二进制文件,就无法在不重新编译源代码的情况下使其与另一个C ++ ABI(名称修改,异常处理)兼容。

ELF是一种使用加载器和动态链接器的文件格式。 ELF是二进制代码和数据的容器格式,因此指定了一段代码的ABI。我不认为ELF在严格意义上是ABI,因为PE可执行文件不是ABI。

所有ABI都是特定于指令集的。 ARM ABI在MSP430或x86_64处理器上没有意义。

Windows有几个ABI - 例如,fastcall和stdcall是两种常用的ABI。系统调用ABI再次不同。

答案 5 :(得分:7)

让我至少回答你问题的一部分。以Linux ABI如何影响系统调用为例,以及为什么它有用。

systemcall是一种用户空间程序向内核空间询问内容的方法。它的工作原理是将调用的数字代码和参数放在某个寄存器中并触发中断。比内核空间发生切换,内核查找数字代码和参数,处理请求,将结果放回寄存器并触发切换回用户空间。例如,当应用程序想要分配内存或打开文件(syscalls“brk”和“open”)时,就需要这样做。

现在,系统调用具有短名称“brk”等,以及相应的操作码,这些操作码在系统特定的头文件中定义。只要这些操作码保持不变,您就可以运行具有不同更新内核的相同编译用户程序,而无需重新编译。因此,您有预编译二进制文件使用的接口,因此ABI。

答案 6 :(得分:4)

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

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

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/

知道ABI并假设其他编译器也遵循它,那么二进制文件理论上知道如何相互调用(特别是库API)并通过堆栈或寄存器等传递参数。或者在调用时将更改哪些寄存器功能等。基本上这些知识将有助于软件相互集成。知道了寄存器/堆栈布局的顺序,我可以很容易地将不同的软件拼凑在一起,没有太多问题。

但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旨在定义二进制/汇编级软件的精确集成。

答案 7 :(得分:3)

为了在共享库中调用代码或在编译单元之间调用代码,目标文件需要包含调用的标签。 C ++破坏了方法标签的名称,以便强制执行数据隐藏并允许重载方法。这就是为什么你不能混合来自不同C ++编译器的文件,除非它们明确支持相同的ABI。

答案 8 :(得分:1)

ABI需要在呼叫者和被呼叫者之间保持一致,以确保呼叫成功。堆栈使用,注册使用,例程堆栈弹出。所有这些都是ABI最重要的部分。

答案 9 :(得分:1)

简而言之,在哲学中,只有的东西才能相处得很好,而ABI可以被视为,其中哪些软件可以协同工作。< / p>

答案 10 :(得分:1)

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

<强>功能

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

现有实体:

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

<强>消费者:

  • 语言处理器链接器,汇编程序......

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

C ++名称错误,因为可能需要在您的应用程序中链接来自不同高级语言的对象文件。考虑使用GCC标准库对使用Visual C ++构建的Windows进行系统调用。

ELF是一个可能期望链接器来自目标文件的解释,尽管JVM可能有其他想法。

对于Windows RT Store应用,如果您真的希望将一些构建工具链协同工作,请尝试搜索ARM ABI。

答案 11 :(得分:1)

我也试图了解ABI,JesperE的答案非常有用。

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

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

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

  1. 相同/向后兼容的指令集架构(处理器指令,寄存器文件结构,堆栈组织,内存访问类型,以及处理器可以直接访问的基本数据类型的大小,布局和对齐方式)
  2. 相同的通话约定
  3. 同名修改约定(如果Fortran程序需要调用某些C ++库函数,则可能需要这样做。)
  4. 当然,还有很多其他细节,但这主要是ABI所涵盖的内容。

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

      

    ABI功能:二进制兼容性

         

    现有实体:现有程序/库/ OS

         

    消费者:库,操作系统

    希望这有帮助!

答案 12 :(得分:1)

术语ABI用于指代两个截然不同但相关的概念。

在谈论编译器时,它指的是用于从源级构造转换为二进制构造的规则。数据类型有多大?堆栈如何工作?如何将参数传递给函数?调用者和被调用者应该保存哪些寄存器?

在谈论库时,它指的是由编译库提供的二进制接口。此接口是许多因素的结果,包括库的源代码,编译器使用的规则以及在某些情况下从其他库中获取的定义。

对库的更改可能会破坏ABI而不会破坏API。例如,考虑一个具有类似。

的接口的库
void initfoo(FOO * foo)
int usefoo(FOO * foo, int bar)
void cleanupfoo(FOO * foo)

并且应用程序员编写类似

的代码
int dostuffwithfoo(int bar) {
  FOO foo;
  initfoo(&foo);
  int result = usefoo(&foo,bar)
  cleanupfoo(&foo);
  return result;
}

应用程序员并不关心FOO的大小或布局,但应用程序二进制文件最终会以硬编码的foo大小结束。如果库程序员向foo添加了一个额外的字段,并且有人将旧的库二进制文件与旧的应用程序二进制文件一起使用,那么该库可能会超出内存访问范围。

OTOH如果图书馆作者设计了他们的API,那就是。

FOO * newfoo(void)
int usefoo(FOO * foo, int bar)
void deletefoo((FOO * foo, int bar))

并且应用程序员编写类似

的代码
int dostuffwithfoo(int bar) {
  FOO * foo;
  foo = newfoo();
  int result = usefoo(&foo,bar)
  deletefoo(&foo);
  return result;
}

然后应用程序二进制文件不需要了解FOO的结构,它们都可以隐藏在库中。您为此付出的代价是涉及堆操作。

答案 13 :(得分:1)

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

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

例如:

  • 如果您要出售共享库,则可以避免用户为每个新版本重新编译依赖于库的所有内容的烦恼

  • 如果您出售的封闭源程序依赖于用户发行版中存在的共享库,则可以确信如果ABI在目标操作系统的某些版本中是稳定的,则可以释放和测试较少的预构建版本。

  • >

    对于C标准库,这尤其重要,系统中许多程序都链接到C标准库。

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

main.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;

并重建了库,但没有重建main.out,则断言失败!

这是因为该行:

myobject->old_field == 1

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

因此,此更改破坏了ABI。

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

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

然后,旧生成的程序集仍然会访问该结构的第一个int,并且该程序仍然可以运行,因为我们保持了ABI的稳定。

这里是fully automated version of this example on GitHub

保持此ABI稳定的另一种方法是将mylib_mystruct视为opaque struct,并且只能通过方法助手来访问其字段。这样可以更轻松地保持ABI的稳定,但是会增加性能开销,因为我们需要执行更多的函数调用。

API与ABI

在前面的示例中,有趣的是注意到在new_field之前添加old_field只会破坏ABI,而不会破坏API。

这意味着,如果我们针对库重新编译main.c程序,则无论如何它都可以工作。

我们也会破坏API,但是如果我们更改了例如函数签名:

mylib_mystruct* mylib_init(int old_field, int new_field);

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

语义API与编程API

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

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

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

例如,如果我们已经修改

myobject->old_field = old_field;

收件人:

myobject->old_field = old_field + 1;

这将不会破坏编程API或ABI,但是main.c语义API会破坏。

有两种方法可以以编程方式检查合同API:

  • 测试一些极端情况。容易做到,但您可能总是会错过一个。
  • formal verification。难度较大,但会产生正确性的数学证明,从本质上将文档和测试统一为“人工” /机器可验证的方式!只要您对课程的形式描述中没有错误;-)

破坏C / C ++共享库ABI的所有内容的列表

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

Java最小可运行示例

What is binary compatibility in Java?

在Ubuntu 18.10,GCC 8.2.0中进行了测试。

答案 14 :(得分:0)

ABI-Application Binary Interface与运行时的机器代码通信有关。通信可以在不同级别的系统上进行:

A good example of API and ABI is iOS ecosystem with Swift language

  • 应用程序-使用其他语言创建应用程序

  • Application - OS-Swift运行时和标准库将包含在每个捆绑软件(例如应用,框架)中,因为它将包含在OS内核中(例如Objective- C),并且编译器知道此标准。

  • Application - Library-Module Stability的情况-您将能够导入使用其他版本的Swift编译器构建的框架。这意味着创建封闭源(预构建)二进制文件是安全的,该二进制文件将由不同版本的编译器使用。

  • Application - OS-Library Evolution的情况-系统库或动态框架可以由新的库更新,而无需重新编译使用者

API vs ABI

答案 15 :(得分:0)

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

B.但是,ABI 是为某些特定语言的某些特定处理器平台发布的接口。所有希望将同一语言的平台作为目标的编译器供应商必须确保不仅可重定位目标代码形式的编译代码符合接口以便能够相互链接和交叉链接,而且可执行文件也符合它完全能够在平台上运行。因此,ABI 是比典型的函数 API 更广泛的规范/标准集。它可能包括一些由编译器对语言用户强制执行的 API 对象。编译器供应商必须在其发行版中包含对相同内容的支持。毋庸置疑,平台供应商是为其平台发布 ABI 的合法权威。编译器供应商和 ABI 都需要遵守相应的语言标准(例如 C++ 的 ISO 标准)。

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

"1. 可执行文件必须符合的规范才能在特定的执行环境中执行。例如,Arm 架构的 Linux ABI。

  1. 独立生成的可重定位文件必须符合规范的特定方面,才能静态链接和执行。例如,Arm 架构的 C++ ABI、Arm 架构的运行时 ABI、Arm 架构的 C 库 ABI。”

D.例如。一个基于 Itanium 体系结构的 C++ 通用 ABI 也已被一个联盟issued。平台供应商自己的 C++ ABI 在多大程度上符合它完全取决于平台供应商。

E。再举一个例子。 Arm 架构的 C++ ABI 是 here

F。话虽如此,在底层,处理器架构的 ABI 将确保一个可重用程序和另一个重用它的程序之间的 API 适用于该处理器架构。

G。这将我们带到面向服务的组件(例如基于 SOAP 的 Web 服务)。它们也需要在基于 SOAP 的 Web 服务和客户端程序(可以是应用程序、前端或其他 Web 服务)之间存在 API,以便客户端程序重用 Web 服务。 API 是根据标准化协议描述的像 WSDL(接口描述)和 SOAP(消息格式)并且是平台中立的。它不针对任何特定的处理器平台,因此它不像 ABI 那样是“二进制的”。任何一种平台类型上的客户端程序都可以远程重用完全不同的处理器平台上的 Web 服务。这是因为 WSDL 和 SOAP 都是基于文本 (XML) 的协议。对于 RESTful Web 服务,传输协议 http(也是一种基于文本的协议)本身充当 API(CRUD 方法)。