该文档描述了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 |
byte和rune类型分别是uint8和int32的别名,因此它们具有与这些类型相同的大小和对齐方式。
map、chan和func类型的布局等效于*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中定义的常量。