首页 文章

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

提问于
浏览
348

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

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

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

界面:它是该功能的功能和消费者之间的“现有实体”层 . 接口本身不起作用 . 它只是调用后面的功能 . 现在取决于用户是谁,有不同类型的接口 . 命令行界面(CLI)命令是现有实体,消费者是用户和功能所在 . 功能:我的软件功能解决了我们描述此接口的一些目的 . 现有实体:命令消费者:用户图形用户界面(GUI)窗口,按钮等是现有实体,而消费者又是用户和功能所在 . 功能:我的软件功能解决了我们描述此接口的一些目的 . 现有实体:窗口,按钮等 . 消费者:用户应用程序编程接口(API)功能还是要更加正确,接口(在基于接口的编程中)是现有实体,这里的消费者是另一个不是用户的程序,而且功能在于在这一层后面 . 功能:我的软件功能解决了我们描述此接口的一些目的 . 现有实体:函数,接口(函数数组) . 消费者:另一个程序/应用程序应用程序二进制接口(ABI)这是我的问题开始的地方 . 功能:???现有实体:???消费者:???

  • 我've written software in different languages and provided different kind of interfaces (CLI, GUI, and API), but I'我不确定,如果有的话,提供任何ABI .

Wikipedia says:

ABI涵盖数据类型,大小和对齐等细节;调用约定,它控制函数参数的传递方式并返回检索的值;系统调用号码以及应用程序应如何向操作系统发出系统调用;其他ABI标准化了同一平台上的编译器之间的C名称修改,异常传播和调用约定等细节,但不需要跨平台兼容性 .

  • 谁需要这些细节?请不要说操作系统 . 我知道汇编编程 . 我知道链接和加载是如何工作的 . 我知道里面究竟发生了什么 .

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

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

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

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

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

所以,这些是困扰我的主要问题 .

14 回答

  • 16

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

  • 2

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

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

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

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

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

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

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

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

    IIRC,Windows目前使用Portable Executable(或PE)格式 . "external links"中有链接该维基百科页面的一部分,包含有关PE格式的更多信息 .

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

  • 103

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

    • 您的程序没有功能,并且 -

    • 您的程序是一个单独运行的可执行程序(即嵌入式系统),它需要与其他任何程序进行通信 .

    An oversimplified summary:

    API:“以下是您可以调用的所有功能 . ” ABI:“这是如何调用一个函数 . ”

    ABI是编译器和链接器遵守的一组规则,用于编译程序以便正常工作 . ABI涵盖多个主题:

    • 可以说ABI中最大和最重要的部分是procedure call standard,有时也被称为"calling convention" . 调用约定标准化"functions"如何转换为汇编代码 .

    • ABI还规定了如何表示库中公开函数的名称,以便其他代码可以调用这些库并知道应该传递哪些参数 . 这叫做"name mangling" .

    • ABI还规定了可以使用哪种类型的数据类型,它们必须如何对齐以及其他低级细节 .

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

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

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

    • 首先,编译器插入一些汇编代码来保存当前地址,这样当"function"完成后,CPU可以跳回到正确的位置并继续执行 .

    • 接下来,编译器生成汇编代码以传递参数 .

    • 一些调用约定规定应该将参数放在堆栈上(当然按特定顺序) .

    • 其他约定规定参数应放在特定的寄存器中(当然,取决于它们的数据类型) .

    • 还有其他约定规定应该使用堆栈和寄存器的特定组合 .

    • 当然,如果之前这些寄存器中有任何重要内容,那么这些值现在会被覆盖并永远丢失,因此一些调用约定可能要求编译器在将参数放入其中之前保存其中一些寄存器 .

    • 现在,编译器插入一条跳转指令,告诉CPU转到之前创建的标签( _MyFunction1: ) . 此时,您可以将CPU视为"in"您的"function" .

    • 在函数结束时,编译器会放置一些汇编代码,使CPU在正确的位置写入返回值 . 调用约定将指示是否应将返回值放入特定寄存器(取决于其类型)或堆栈上 .

    • 现在是清理的时候了 . 调用约定将指示编译器放置清理汇编代码的位置 .

    • 一些约定说调用者必须清理堆栈 . 这意味着在"function"完成并且CPU跳回到之前的位置之后,下一个要执行的代码应该是一些非常具体的清理代码 .

    • 其他约定说清理代码的某些特定部分应该在跳回之前的"function"的末尾 .

    有许多不同的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,它就可以正确调用它们中的函数而不会炸毁堆栈 .

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

  • 1

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

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

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

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

    消费者:另一种功能(包括另一种语言,由另一个编译器编译,或由另一个链接器链接) .

  • 1

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

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

    消费者:编译器,汇编编写器,操作系统,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再次不同 .

  • 3

    区分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不同:

    It is a high level functions names, with argument defined, such that if different software pieces build using these API, MAY be able to call into one another. But an additional requirement of SAME ABI must be adhered to.

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

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

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

    And Linux is POSIX compliant as well. But the binaries cannot be just moved over and run immediately. But because they used the same NAMES in the POSIX compliant API, you can take the same software in C, recompile it in the different OS, and immediately get it running.

    API are meant to ease integration of software - pre-compilation stage. So after compilation the software can look totally different - if the ABI are different.

    ABI are meant to define exact integration of software at the binary / assembly level.

  • 6

    术语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结构的任何信息,它们都可以隐藏在库中 . 您为此付出的代价是涉及堆操作 .

  • 1

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

  • 4

    简而言之,在哲学中,只有某种东西可以相处得很好,而ABI可以被看作是哪种软件可以协同工作 .

  • 374

    摘要

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

    在我看来,ABI是被认为是特定API的给定/平台的 subjective convention . ABI是"rest"的约定"will not change"用于特定API或将由运行时环境解决:执行程序,工具,链接器,编译器,jvm和OS .

    定义接口:ABI,API

    如果你想使用像joda-time这样的库,你必须声明对 joda-time-<major>.<minor>.<patch>.jar 的依赖 . 该库遵循最佳实践并使用Semantic Versioning . 这定义了三个级别的API兼容性:

    • 补丁 - 您无需更改所有代码 . 该库只修复了一些错误 .

    • 次要 - 自添加以来,您无需更改代码

    • Major - 接口(API)已更改,您可能需要更改代码 .

    为了让您使用同一个库的新主要版本,仍然需要遵守许多其他约定:

    • 用于库的二进制语言(在Java情况下是定义Java的JVM目标版本)字节码)

    • 调用约定

    • JVM约定

    • 链接约定

    • 运行时约定所有这些都是由我们使用的工具定义和管理的 .

    例子

    Java案例研究

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

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

    Dalvik虚拟机打破了ABI

    Dalvik VM需要与Java字节码不同类型的字节码 . Dalvik库是通过为Dalvik转换Java字节码(使用相同的API)获得的 . 通过这种方式,您可以获得相同API的两个版本:由原始 joda-time-1.7.2.jar 定义 . 我们可以叫我 joda-time-1.7.2.jarjoda-time-1.7.2-dalvik.jar . 他们使用不同的ABI,用于面向堆栈的标准Java vms:Oracle 's one, IBM' s,打开Java或任何其他;第二个ABI是Dalvik周围的ABI .

    Scala连续版本不兼容

    Scala在次要Scala版本之间没有二进制兼容性:2.X . 出于这个原因,相同的API "io.reactivex" %% "rxscala"%"0.26.5"有三个版本(将来会更多):对于Scala 2.10,2.11和2.12 . 改变了什么? I don't know for now,但二进制文件不兼容 . 可能最新版本增加了使旧库中的库无法使用的东西,可能与链接/命名/参数约定有关 .

    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和ABI只是关于如何定义兼容性的约定 . 对于过多的高级语义,较低层是通用的 . 那个's why it'很容易做出一些约定 . 第一种约定是关于内存对齐,字节编码,调用约定,大端和小端编码等等 . 在它们之上,您可以像其他描述的那样获得可执行约定,链接约定,如Java或LLVM IR使用的约定GCC使用 . 第三,您将获得有关如何查找库以及如何加载库的约定(请参阅Java类加载器) . 随着概念越来越高,您有了新的约定,您认为这些约定是给定的 . 那's why they didn' t进入semantic versioning . 它们在 major 版本中是隐式或折叠的 . 我们可以使用 <major>-<minor>-<patch>-<platform/ABI> 修改语义版本控制 . 这实际上已经发生了:平台已经是 rpmdlljar (JVM字节码), war (jvm web服务器), apk2.11 (特定的Scala版本)等等 . 当你说APK时,你已经谈到了API的特定ABI部分 .

    API可以移植到不同的ABI

    抽象的顶层(针对最高API编写的源可以重新编译/移植到任何其他较低级别的抽象 .

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

    API跨语言移植

    有些API以多种语言移植,如reactive streams . 通常,它们定义到特定语言/平台的映射 . 我认为API是在人类语言甚至特定编程语言中正式定义的主规范 . 所有其他"mappings"在某种意义上都是ABI,否则比通常的ABI更多的API . REST接口也是如此 .

  • 1

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

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

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

  • 1

    我也试图了解ABI和JesperE的答案非常有帮助 .

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

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

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

    • 相同/向后兼容的指令集架构(处理器指令,寄存器文件结构,堆栈组织,存储器访问类型,以及处理器可以直接访问的基本数据类型的大小,布局和对齐方式)

    • 相同的调用约定

    • 相同的名称修改约定(如果说Fortran程序需要调用某些C库函数,则可能需要这样做) .

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

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

    ABI功能:二进制兼容性现有实体:现有程序/库/ OS使用者:库,OS

    希望这可以帮助!

  • 20

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

    Functionality:

    • 来自程序员的翻译's model to the underlying system'的域数据类型,大小,对齐,调用约定,它控制函数' arguments are passed and return values retrieved; the system call numbers and how an application should make system calls to the operating system; the high-level language compilers'在同一平台上的编译器之间如何命名修改方案,异常传播和调用约定,但不需要跨平台兼容性 . ..

    Existing entities:

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

    consumer:

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

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

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

    虽然JVM可能有其他一些想法,但ELF是对目标文件中链接器的一种可能的期望,用于解释 .

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

  • 9

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

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

    此外,如果您有一个可以执行32位二进制文件的64位操作系统,则32位和64位代码将具有不同的ABI .

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

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

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

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

    编辑:一些注释要澄清:

    ABI中的

    • "Binary"不排除使用字符串或文本 . 如果要链接导出C类的DLL,则必须在其中的某个位置对方法和类型签名进行编码 . 这就是C名称变形的地方 .

    • 你之所以这样绝不提供ABI是绝大多数程序员永远不会这样做 . ABI由设计平台的同一人(即操作系统)提供,并且很少有程序员将有权设计广泛使用的ABI .

相关问题