36

其他地方的讨论:

C++ 没有标准的 ABI(应用程序二进制接口)

但C也不行,对吧?

在任何给定的平台上,它几乎都可以。如果缺少一种语言,它就无法作为跨语言交流的通用语言。

你对此有何看法?

4

9 回答 9

42

C defines no ABI. In fact, it bends over backwards to avoid defining an ABI. Those people, who like me, who have spent most of their programming lives programming in C on 16/32/64 bit architectures with 8 bit bytes, 2's complement arithmetic and flat address spaces, will usually be quite surprised on reading the convoluted language of the current C standard.

例如,阅读有关指针的内容。该标准并没有说“指针是地址”这样简单的东西,因为那将是对 ABI 的假设。特别是,它允许指针位于不同的地址空间并具有不同的宽度。

ABI 是从语言的执行模型到特定机器/操作系统/编译器组合的映射。在语言规范中定义一个是没有意义的,因为这会冒着在某些架构上排除 C 实现的风险。

于 2010-12-20T14:21:28.663 回答
18

C 原则上没有标准的 ABI,但在实践中,这并不重要:你做你的操作系统供应商所做的事情。

以 x86 Windows 上的调用约定为例: Windows API 使用所谓的“标准”调用约定 (stdcall)。因此,任何想要与操作系统交互的编译器都需要实现它。然而,stdcall 并不支持所有的 C90 语言特性(例如调用没有原型的函数、可变参数函数)。由于 Microsoft 提供了 C 编译器,因此需要第二个调用约定,称为“C”调用约定 (cdecl)。Windows 上的大多数 C 编译器都使用它作为默认调用约定,因此是可互操作的。

原则上,C++ 也可能发生同样的情况,但由于 C++ ABI(包括调用约定)必然要复杂得多,编译器供应商并未就单一 ABI 达成一致,但仍可以通过回退到extern "C".

于 2010-12-20T13:37:31.717 回答
10

C 的 ABI 是特定于平台的——它涵盖了寄存器分配和调用约定等问题,这些问题显然是特定于特定处理器的。这里有些例子:

x86 有很多调用约定,Windows 下的哪些扩展来声明使用哪一个。嵌入式 Linux 的平台 ABI 也随着时间发生变化,导致用户空间不兼容。在此处查看 ARM Linux 移植的一些历史,其中显示了在过渡到更新的 ABI 过程中的问题。

于 2010-12-20T11:15:27.583 回答
5

即使对于 C,ABI 也具有完全独立于平台的部分,依赖于处理器的部分(应该保存哪些寄存器,用于传递参数,...)和依赖于操作系统的部分(或多或少与处理器相同的因素,因为某些选择不是由体系结构强加的,而是权衡的结果,加上一些操作系统具有与语言无关的异常概念,因此任何语言的编译器都必须生成正确的东西来处理那些,线程的处理也可能对 ABI 强加一些东西——如果一个寄存器指向 TLS,你就不能用它来做你想要的)。

理论上,每个编译器都可能有自己的 ABI。但通常,对于一对处理器/操作系统,ABI 由操作系统供应商修复,通常还提供 C 编译器和使用该 ABI 的通用库,并且竞争对手更愿意兼容。(如果 C 不是主要编程语言的某些操作系统存在例外情况,我不会感到惊讶)。

但是操作系统供应商可能出于某种原因切换 ABI(新版本的处理器可能具有您想在 ABI 中使用的功能之一 - 例如,有些人要求 x86_64 的 32 位 ABI 允许使用所有寄存器) . 在迁移阶段(可能会持续很长时间),您可能需要处理两个 ABI。

于 2010-12-20T14:24:31.000 回答
5

尽管在跨多个操作系统(尤其是 Unix 系统上的 i386)为给定架构定义单个 ABI 方面已经进行了多次尝试,但这些努力并未取得如此成功。相反,操作系统倾向于定义自己的 ABI ...

引用 ... Linux 系统编程第 4 页。

于 2010-12-20T11:14:45.867 回答
4

C也不行,对吧?
正确的

在任何给定的平台上,它几乎都可以。如果缺少一种语言,它就无法作为跨语言交流的通用语言。
几乎可以指的是由 C 编译器供应商选择的特定于体系结构的默认值,这些默认值在其他语言中进行了调整。因此,如果 Keil 的 ARM C 编译器将使用从左到右的小端参数排序和堆栈来传递参数和一些预定的返回值寄存器,那么来自其他编译器的 extern "C" 将假定与这种方案兼容。

虽然这种协议可能被认为是 ABI 的一部分,但与托管执行上下文(如 JVM 浏览器沙箱)不同,这远非完整的标准 ABI。

于 2013-04-10T08:30:35.453 回答
2

C 没有标准的 ABI。这很容易通过所有使用的调用约定(cdecl、fastcall 和 stdcall)来说明。每个都是不同的 ABI。

于 2010-12-20T11:11:03.857 回答
1

在 C89 标准之前,许多平台的 C 编译器使用基本相同的 ABI,除了数据大小的变化。对于堆栈向下增长的机器,调用函数的代码将按从右到左的顺序将参数压入堆栈,然后调用该函数(在进程中压入返回地址)。被调用的函数会将其参数留在堆栈上,调用者会在闲暇时调整堆栈指针以删除它们[或者,在某些架构上,可能会调整堆栈值]。尽管<stdarg.h>使大多数程序没有必要依赖该约定,它仍然使用了很多年,因为它简单且运行良好。虽然没有“官方”文档将其确定为跨平台“标准”,但大多数针对具有向下增长堆栈的机器的编译器都以这种方式工作,从而导致比现在更高水平的一致性。

于 2017-01-09T23:35:55.997 回答
1

没有标准的 ABI,因为 C 一直是关于最大运行时性能的,而具有最高性能的 ABI 取决于底层硬件。因此,ABI 可能只使用堆栈或首选寄存器来传递函数调用参数并根据任何给定硬件的需要返回值。

例如,甚至 amd64(又名 x86-64)也有两个调用约定:Microsoft x64 和 System V AMD64 ABI。前者将 4 个第一个参数放入寄存器,其余的放入堆栈。后者将 6 个第一个参数放入寄存器,其余的放入堆栈。我不知道为什么微软为 amd64 硬件创建了不兼容的调用约定。据我所知,Microsoft 变体的性能稍差一些,是后来创建的。

有关更多信息,请参阅https://en.wikipedia.org/wiki/X86_calling_conventions

于 2021-07-21T17:18:36.143 回答