该文档描述了Go的内部应用程序二进制接口(ABI),称为ABIInternal。 Go的ABI定义了内存中数据的布局和调用Go函数之间的约定。该ABI不稳定,并且在Go版本之间会发生变化。如果您正在编写汇编代码,请参考Go的汇编文档,该文档描述了Go的稳定ABI,称为ABI0。

在Go源代码中定义的所有函数都遵循ABIInternal。但是,ABIInternal和ABI0函数可以通过透明的ABI包装器相互调用,这在内部调用约定提案中有描述。

Go在所有架构上使用相同的ABI设计。我们首先描述通用ABI,然后涵盖每种架构的特定内容。

内存布局

Go的内置类型具有以下大小和对齐方式。尽管不是所有的类型大小都被语言规范保证,但许多类型大小是有保证的。那些不被保证的类型大小可能会在将来的Go版本中发生改变(例如,我们曾考虑在32位上改变int64的对齐方式)。

Type 64-bit 32-bit
Size Align Size Align
bool, uint8, int8 1 1 1 1
uint16, int16 2 2 2 2
uint32, int32 4 4 4 4
uint64, int64 8 8 8 4
int, uint 8 8 4 4
float32 4 4 4 4
float64 8 8 8 4
complex64 8 4 8 4
complex128 16 8 16 4
uintptr, *T, unsafe.Pointer 8 8 4 4

byterune类型分别是uint8和int32的别名,因此它们具有与这些类型相同的大小和对齐方式。

mapchanfunc类型的布局等效于*T。

为了描述其余复合类型的布局,我们首先定义一个包含N个类型为t1、t2、…、tN的字段的序列S的布局。我们按照以下方式定义每个字段相对于基地址0开始的字节偏移量,以及序列的大小和对齐方式:

offset(S, i) = 0  if i = 1
             = align(offset(S, i-1) + sizeof(t_(i-1)), alignof(t_i))
alignof(S)   = 1  if N = 0
             = max(alignof(t_i) | 1 <= i <= N)
sizeof(S)    = 0  if N = 0
             = align(offset(S, N) + sizeof(t_N), alignof(S))

其中,sizeof(T)和alignof(T)分别是类型T的大小和对齐方式,align(x, y)将x向上舍入为y的倍数。

接口类型 interface{}

  • 接口的动态类型的运行时类型描述符指针
  • 一个unsafe.Pointer数据字段的序列

任何其他的接口类型(除了空接口)

  • 指向“itab”的运行时指针,该指针提供方法指针和数据字段类型
  • 一个unsafe.Pointer数据字段的序列

接口可以是“直接的”或“间接的”,具体取决于动态类型:直接接口直接将值存储在数据字段中,而间接接口将值的指针存储在数据字段中。如果值只包含单个指针字,接口才能是直接的。

一个数组类型[N]T是一个类型为T的N个字段的序列。

切片类型[]T是一个序列,包含一个指向*[cap]T的指针,一个int表示切片的长度,以及一个int表示切片的容量。

字符串类型是一个序列,包含一个指向*[len]byte的指针,以及一个int表示字符串的长度。

结构体类型struct { f1 t1; …; fM tM }的布局为序列t1,…,tM,tP,其中tP为:

  • byte类型, 如果sizeof( tM ) = 0且sizeof( ti ) ≠ 0:
  • 否则为空类型(大小为0,对齐方式为1)。

填充字节防止通过获取最后一个空的fN字段的地址来创建越界指针。请注意,用户编写的汇编代码通常不应依赖于Go类型布局,而应使用go_asm.h中定义的常量。