AMD64架构下的 SystemV ABI (1)

1 简介

AMD64 架构是 x86 架构的一种扩展。任何实现 AMD64 架构规范的处理器都会为早期的 Intel 8086 架构(包括 Intel 386,Intel Pentium 和 AMD K6-2 这类 32 位处理器)提供兼容模式。满足 AMD64 ABI 的操作系统可以为这些兼容模式下运行的程序提供支持。AMD64 ABI 并不适用于这类程序。本文档仅适用于在 AMD64 架构提供的 “长”模式下运行的程序。

使用 AMD64 指令集的二进制可以被编写为 32 位模型的程序,在这种模型下,其 C 语言数据类型 int、long 和所有的指针类型是 32 位对象(ILP32);也可以被编写为 64 位模型的程序,其 C 语言数据类型 int 是 32 位对象,但是 long 和 所有的指针类型是 64 位对象(LP64)。该规范涵盖了 LP64 和 ILP32 编程模型。

除非另有说明,AMD64 架构的 ABI 遵循 Intel386 ABI 中描述的约定。AMD64 ABI 仅指示对 Intel386 ABI 进行改进的地方,而不是复制全部的 Intel386 ABI。

鉴于没有尝试为 C 语言之外的语言特殊指定 ABI,这里假设很多编程语言可以直接链接用 C 语言编写的代码,本文档的 ABI 规范也遵守这个假设。

2 软件安装

该文档没有特殊指定软件在 AMD64 架构下的安装方式。

3 低级别系统信息

3.1 机器接口

3.1.1 处理器架构

任何应用程序都可以期望 AMD64 处理器实现了下面微架构级别表中提到的基线特性。大多数特性名称都对应于处理器手册中提到的 CPUID 位。但 OSFXSRSCE 是例外,他们由 %cr4 寄存器和 IA32_EFER MSR(译注:Model Specific Registers) 中的位控制。

表中除基线架构外,还定义了多个由后来 CPU 模块实现的微架构级别,这些级别从 x86-64-v2 开始,旨在支持在与其兼容的系统上的加载优化实现。这个级别是累加的,前面级别的特性隐式被后面的级别所包含。

级别 x86-64-v3x86-64-v4 仅在相应功能已完全启用时可用。 这意味着系统必须通过处理器手册中针对这些功能的完整检查,包括对使用 xgetbv 获得的 XCR0 功能标志的验证。

微架构级别的建议使用

下表中的微架构级别名称可以用于目录名称(可以被动态链接器根据当前 CPU 的支持级别进行搜索),也可以被编译器用于选择 CPU 功能组。发行版还可以指定它需要某个级别的 CPU 支持。

级别CPU 特性示例指令
(baseline)CMOVcmov
CX8cmpxchg8b
FPUfld
FXSRfxsave
MMXemms
OSFXSRfxsave
SCEsyscall
SSEcvtss2si
SSE2cvtpi2pd
x86-64-v2CMPXCHG16Bcmpxchg16b
LAHF-SAHFlahf
POPCNTpopcnt
SSE3addsubpd
SSE4_1blendpd
SSE4_2pcmpestri
SSSE3phaddd
x86-64-v3AVXvzeroall
AVX2vpermd
BMI1andn
BMI2bzhi
F16Cvcvtph2ps
FMAvfmadd132pd
LZCNTlzcnt
MOVBEmovbe
OSXSAVExgetbv
x86-64-v4AVX512Fkmovw
AVX512BWvdbpsadbw
AVX512CDvplzcntd
AVX512DQvpmullq
AVX512VLN/A

举个例子,如果要选择第二级 x86-64-v3,程序员必须使用 -march=x86-64-v3 的 GCC 参数来构建共享对象(shared object)。生成的共享对象需要安装到目录 /usr/lib64/glibc-hwcaps/x86-64-v3/usr/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v3(防止一个发行版存在多架构文件系统布局)。为了支持仅实现 K8 基线的系统,一个后备(fallback)实现必须被安装到默认位置:/usr/lib64/usr/lib/x86_64-linux/gnu。 它必须使用 -march=x86-64(默认值) 构建。如果不遵循此准则,在系统不支持这些被优化过的共享对象对应的级别的情况下,加载这些共享库将会失败。

安装在对应的 glibc-hwcaps 子目录下的共享对象可以使用该级别和更早级别的 CPU 功能,而无需进一步的检测。 对本节中未列出或仅在以后级别列出的其他 CPU 功能的运行时检测仍然是必需的(即使所有当前的 CPU 都一起实现了那些 CPU 功能)。

如果发行版需要某个级别的支持,它们会使用适当的 -march= 选项构建所有内容,并将构建的二进制文件安装在默认位置。当针对此类发行版时,程序员可以使用相同的 -march= 选项构建他们的二进制文件并将它们安装到默认位置。为更高级别优化的共享对象仍然可以安装到具有适当名称的子目录中。

3.1.2 数据表示

在本规范中,术语 byte 指代一个 8 位对象,术语 twobyte 指代一个 16 位对象,术语 fourbyte 指代一个 32 为对象,术语 eightbyte 指代一个 64 位对象,术语 sixteenbyte 指代一个 128 位对象。

基本类型

下图展示了 ISO C 和处理器标量类型的对应关系。其中:__int128, _Float16, __float80, __float128, __m64, __m128, __m256__m512 是可选的。

__float128 类型使用 15 位指数、113 位尾数(浮点最高有效位是隐式的,被指数 e 隐含,详情参见 IEEE754)和指数偏差16383(译注:在浮点数中,指数存储的值通过指数偏差从实际值进行偏移,进行偏置是因为指数必须是有符号值才能表示微小值和巨大值,但是通常用补码表示有符号值,这会增加比较的难度,为了解决这个问题,指数被存储为适合比较的无符号值,并且在解释时通过减去偏差将其转换为有符号范围内的指数)。

long double 类型使用 15 位指数,64 位尾数,显式最高有效位(详情参见 IEEE754)和指数偏差 16383。虽然 long double 需要 16 字节的存储空间,但只有前 10 字节是有效的。 剩下的六个字节是尾部填充,这些字节的内容是未定义的。

类型C 语言sizeof对齐AMD64 架构
整形_Bool11boolean
char11signed byte
signed char11signed byte
unsigned char11unsigned byte
signed short22signed twobyte
unsigned short22unsigned twobyte
signed int44signed fourbyte
enum44signed fourbyte
unsigned int44unsigned fourbyte
signed long (LP64)88signed eightbyte
unsigned long (LP64)88unsigned eightbyte
signed long (ILP32)44signed fourbyte
unsigned long (ILP32)44unsigned fourbyte
signed long long88signed eightbyte
unsigned long long88unsigned eightbyte
__int1281616signed sixteenbyte
signed __int1281616signed sixteenbyte
unsigned __int1281616unsigned sixteenbyte
指针任意类型 * (LP64)88unsigned eightbyte
任意类型 (*)() (LP64)88unsigned eightbyte
任意类型 * (ILP32)44unsigned fourbyte
任意类型 (*)() (ILP32)44unsigned fourbyte
浮点_Float162216-bit (IEEE-754)
float44single (IEEE-754)
double88double (IEEE-754)
__float80161680-bit extended (IEEE-754)
long double161680-bit extended (IEEE-754)
__float1281616128-bit extended (IEEE-754)
long double1616128-bit extended (IEEE-754)
十进制浮点_Decimal324432bit BID (IEEE-754R)
_Decimal648864bit BID (IEEE-754R)
_Decimal1281616128bit BID (IEEE-754R)
数据包(Packed)__m6488MMX and 3DNow!
__m1281616SSE and SSE-2
__m2563232AVX
__m5126464AVX-512