Files
gasm-sdk/docs/GOOBJ.md
2026-09-26 11:08:43 +02:00

31 KiB
Raw Permalink Blame History

The GOOBJ object file format

This document is a complete specification of GOOBJ, the object file format that the Go toolchain's assembler, compiler and linker exchange, written for implementers of independent producers and consumers. It documents the format as shipped by Go 1.27.1, identified by the magic string "\x00go120ld".

No comparable document exists upstream. The format is defined only by the source of the cmd/internal/goobj package inside the toolchain tree, it is an internal interface with no stability promise, and it can change in any release. This specification was therefore produced by reverse engineering that source and by parsing real objects produced by go tool asm and go tool compile, byte for byte, against the layout described here. Within gasm-sdk it is kept honest by the differential tests in asm/goobj_test.go and asm/link_test.go, which compare gasm asm --format goobj output against the toolchain's own products and feed gasm objects to go build.

Every numeric value in this document, every block index, structure size, flag bit, type code and relocation number, was read from the Go 1.27.1 source at /usr/local/go/src/cmd/internal/goobj, cmd/internal/obj and cmd/internal/objabi, and exercised against assembled objects.

Containers

The unit this document specifies is the object: one package's worth of symbols, relocations and data. An object is never consumed naked. Two wrappers exist in practice, and the linker dispatches on the first bytes of the file.

The bare object, written by go tool asm:

"go object linux amd64 go1.27.1 GOAMD64=v1 X:regabiwrappers,...\n"
"!\n"
<GOOBJ blob>

The first line is the toolchain configuration string, produced by objabi.HeaderString: go object, the GOOS, the GOARCH, the toolchain version, an optional architecture qualifier such as GOAMD64=v1, and X: followed by the enabled experiments, comma separated. The linker requires this line to match its own configuration exactly and rejects the file otherwise; the -f linker flag waives the check. Header lines may be followed by export data delimited by $$ markers; the header region always ends at the first line consisting of exactly !, and the GOOBJ blob starts immediately after that line.

The package archive, written by the compiler output pipeline and consumed by go build: the classic ar format, magic !<arch>\n, with the export data in a __.PKGDEF member and one or more objects as further members, each carrying the bare-object structure above. go tool pack creates and inspects such archives.

Consumer Role
cmd/asm writes objects from .s files
cmd/compile writes objects from Go source
cmd/link reads objects and archives, produces executables
cmd/nm, cmd/objdump read objects through cmd/internal/objfile

Conventions

  • All integers are little endian.
  • There is no alignment or padding anywhere in the file; structures follow one another byte by byte.
  • Every offset stored in the file is relative to the first byte of the GOOBJ blob, not to the start of the container.
  • The blob opens with a 96 byte header that carries the byte offset of every block. A block's length is the difference between its own offset and the next block's, so the offset array is the only index the format needs.

Layout overview

flowchart TB
    A[Container header line and ! terminator] --> B[File header, 96 bytes]
    B --> C[String table, implicit region]
    C --> D[Autolib]
    D --> E[PkgIndex]
    E --> F[Files]
    F --> G[Symbol definition arrays: Symdef, Hashed64def, Hasheddef, Nonpkgdef, Nonpkgref]
    G --> H[RefFlags]
    H --> I[Hash64 and Hash]
    I --> J[RelocIndex, AuxIndex, DataIndex]
    J --> K[Relocs]
    K --> L[Aux]
    L --> M[Data]
    M --> N[RefNames]
    N --> O[BlkEnd marks the end of the blob]

The file header

Exactly 96 bytes: 8 magic, 8 fingerprint, 4 flags, and 19 four byte block offsets.

Offset Size Field Meaning
0 8 Magic "\x00go120ld". A reader rejects anything else. The digits are the format version and have moved before; a new toolchain release may move them again.
8 8 Fingerprint Identifies the package build. The compiler writes a hash of the export data; the assembler leaves all zero. The linker compares this against the fingerprint recorded by importers.
16 4 Flags Bit field, see below.
20 76 Offsets 19 uint32 entries, one per block index 0 to 18.

Header flags:

Bit Value Name Meaning
0 1 ObjFlagShared built with -shared
1 2 reserved was ObjFlagNeedNameExpansion, now unused
2 4 ObjFlagFromAssembly produced from assembly source; go tool asm and gasm set this
3 8 ObjFlagUnlinkable package path is invalid, the linker refuses to link
4 16 ObjFlagStd standard library package

Block indices

The offset array is indexed by these constants, in file order:

Index Constant Contents
0 BlkAutolib imported packages
1 BlkPkgIndex referenced packages, indexed
2 BlkFile source file names
3 BlkSymdef symbol definitions, package scope
4 BlkHashed64def short hashed definitions
5 BlkHasheddef hashed definitions
6 BlkNonpkgdef non-package definitions
7 BlkNonpkgref non-package references
8 BlkRefFlags flags of referenced symbols
9 BlkHash64 8 byte hashes for short hashed definitions
10 BlkHash 16 byte hashes for hashed definitions
11 BlkRelocIndex per symbol relocation start index
12 BlkAuxIndex per symbol aux start index
13 BlkDataIndex per symbol data offset
14 BlkReloc relocations
15 BlkAux aux symbol entries
16 BlkData symbol payloads
17 BlkRefName names of referenced symbols, for tools
18 BlkEnd no contents; its offset is the end of the blob

The string table

There is no block index for strings. The table occupies the implicit region between the end of the header (offset 96) and Offsets[BlkAutolib], and every string offset in the file points into that region. The writer de-duplicates: each distinct string is stored once, in first-use order, and the empty string is always the first entry, so its reference is length 0 and offset 96.

A string reference is 8 bytes: uint32 length, then uint32 absolute offset of the bytes. The bytes are stored raw, with no terminator.

Symbol references and the package index

A symbol reference (SymRef) is 8 bytes: two uint32, PkgIdx and SymIdx. The pair {0, 0} means nil. PkgIdx says which array the symbol lives in:

Value Constant SymIdx indexes
0 PkgIdxInvalid never valid in a written file
1 and up, ascending (imported packages) the SymbolDefs array of the package named at PkgIndex entry PkgIdx
0x7ffffffb PkgIdxSelf this object's Symdef array
0x7ffffffc PkgIdxBuiltin the compiler's builtin table, see Builtins
0x7ffffffd PkgIdxHashed this object's Hasheddef array
0x7ffffffe PkgIdxHashed64 this object's Hashed64def array
0x7fffffff PkgIdxNone NonPkgDefs, overflowing into NonPkgRefs

Assignment rules, as the toolchain performs them:

  • Every definition a package exports to the linker by index lands in Symdefs with PkgIdxSelf. The compiler puts its functions and data here; the assembler puts only its file-local static symbols here, everything else by name, see below.
  • External package references take indices 1, 2, 3, in order of first reference during assembly; the package names go into PkgIndex at those indices, entry 0 is the empty package and is never referenced.
  • References to the compiler's builtin functions become PkgIdxBuiltin with SymIdx set to the builtin's index.
  • A symbol referenced by name rather than by index becomes PkgIdxNone and its index counts through NonPkgDefs first, then continues into NonPkgRefs. A producer must emit the definitions it made in NonPkgDefs and the pure references in NonPkgRefs.
  • The assembler's rule, from cmd/internal/obj/sym.go: every assembly symbol is referenced by name, PkgIdxNone, except file-local static symbols, whose names carry <> and which are referenced by index. The compiler also forces references by name for symbols marked //go:linkname and for any symbol with the DUPOK attribute, which the linker de-duplicates by name.

Symbol definition entries

The five definition and reference arrays (block indices 3 to 7) share one element layout, 21 bytes:

Offset Size Field Meaning
0 8 Name string reference
8 2 ABI see table below
10 1 Type symbol kind, see the kind table
11 1 Flag bit field, see below
12 1 Flag2 second bit field, see below
13 4 Siz payload size in bytes, uint32
17 4 Align alignment the linker must honour, uint32

The Name is a real string reference for hand-written symbols. The auxiliary symbols the toolchain generates per function, the FuncInfo payload, the DWARF entries, have empty names: length 0, and their identity is only via the Aux entries that point at them by index.

The ABI field

Value Meaning
0 ABI0, the stack based ABI, the ABI of every hand-written assembly function
1 ABIInternal, the register ABI of compiler-generated functions
0xffff static, a file-local symbol (name<>(SB)), SymABIstatic

The Flag byte

Bit Value Name Meaning
0 1 SymFlagDupok duplicates allowed, the linker merges them
1 2 SymFlagLocal file-local
2 4 SymFlagTypelink belongs in the typelink table
3 8 SymFlagLeaf leaf function
4 16 SymFlagNoSplit no stack-split preamble
5 32 SymFlagReflectMethod //go:reflectmethod reachability
6 64 SymFlagGoType a Go type descriptor, type: name and SRODATA

Note that NoSplit is not reserved for explicit NOSPLIT declarations. On amd64 the assembler itself marks any function whose frame is below abi.StackSmall and whose body calls nothing that needs stack as NoSplit and omits the split check, so a TEXT without NOSPLIT can still carry the bit.

The Flag2 byte

Bit Value Name Meaning
0 1 SymFlagUsedInIface type or itab reachable through an interface
1 2 SymFlagItab an itab, go:itab. name and SRODATA
2 4 SymFlagDict a generic dictionary symbol
3 8 SymFlagPkgInit package initialisation function
4 16 SymFlagLinkname reachable through //go:linkname; the assembler also sets it on main.main
5 32 SymFlagLinknameStd linkname into the standard library
6 64 SymFlagABIWrapper ABI transition wrapper
7 128 SymFlagWasmExport //go:wasmexport target

The Type byte: symbol kinds

Values of objabi.SymKind, in numeric order:

Value Name Meaning
0 Sxxx invalid zero value
1 STEXT executable code
2 STEXTFIPS executable code, FIPS section
3 SRODATA read only data
4 SRODATAFIPS read only data, FIPS section
5 SNOPTRDATA data without pointers
6 SNOPTRDATAFIPS data without pointers, FIPS section
7 SDATA data, may contain pointers
8 SDATAFIPS data, FIPS section
9 SBSS zero initialised data
10 SNOPTRBSS zero initialised data without pointers
11 STLSBSS thread local zero initialised data
12 SDWARFCUINFO DWARF compile unit information
13 SDWARFCONST DWARF constants
14 SDWARFFCN DWARF function entry
15 SDWARFABSFCN DWARF absolute function entry
16 SDWARFTYPE DWARF type information
17 SDWARFVAR DWARF variable information
18 SDWARFRANGE DWARF range lists
19 SDWARFLOC DWARF location lists
20 SDWARFLINES DWARF line programs
21 SDWARFADDR DWARF address table
22 SLIBFUZZER_8BIT_COUNTER libFuzzer coverage counter
23 SCOVERAGE_COUNTER coverage counter
24 SCOVERAGE_AUXVAR coverage auxiliary variable
25 SSEHUNWINDINFO Windows SEH unwind information

Referenced symbol flags (RefFlags)

Element size 10 bytes, one per referenced external indexed symbol that carries a non-zero Flag2:

Offset Size Field
0 8 Sym, a SymRef into another package
8 1 Flag, always 0 in current writers
9 1 Flag2, only SymFlagUsedInIface is ever written

The linker uses these to preserve reachability of interface conversions across package boundaries. Entries with no flags are omitted entirely.

Hashes

Hash64, block 9: one uint64 per Hashed64def entry, in array order. Not a hash at all: the writer copies the first 8 bytes of the symbol's payload. Only symbols whose content-hash section byte is 0 may use the short form.

Hash, block 10: 16 bytes per Hasheddef entry: the first 16 bytes of a SHA-256 computation over a seed byte 0x01 followed by the hash input. The input, from cmd/internal/obj/objfile.go:

  1. the payload size, little endian uint64;
  2. the section byte, one of t for STEXT, f for STEXTFIPS, P for pcdata, F for the go:func.* and go:funcrel.* families, T for type: symbols, otherwise 0;
  3. for text symbols, the symbol name, which keeps distinct functions from merging;
  4. the payload with trailing zero bytes trimmed;
  5. for each relocation: a 14 byte record, offset uint32, size uint8, low type byte uint8, addend int64, followed by an encoding of the target: tag byte 0 then the target's short hash, tag 1 then its full hash, tag 2 then its expanded name, tag 3 then its builtin index, or, for PkgIdxSelf and imported packages, no tag, then the package path and the symbol index.

Two symbols with equal hashes are interchangeable at link time, which is what makes content addressing work. A producer that computes these hashes wrongly produces objects that link but de-duplicate wrongly; gasm verifies them by byte comparison against go tool asm.

The index arrays

Three arrays of uint32, one element per defined symbol plus one final element, in the order Symdefs, Hashed64defs, Hasheddefs, NonPkgDefs. With N defined symbols, each array holds N + 1 entries, and the entry at N is the total.

  • RelocIndex: entry i is where symbol i's relocations start in BlkReloc; entry i + 1 minus entry i is its count.
  • AuxIndex: the same construction over BlkAux.
  • DataIndex: entry i is the byte offset of symbol i's payload within BlkData; the count is the difference of neighbours.

The toolchain writes relocations grouped per symbol in definition order, and sorts each symbol's relocations by their Off field first. A producer that skips the sort produces objects the linker still accepts, but that no longer compare byte-for-byte with the toolchain's output.

Relocations

Element size 23 bytes:

Offset Size Field Meaning
0 4 Off patch position, bytes from the start of the symbol's payload, int32
4 1 Siz patch width in bytes
5 2 Type relocation type, uint16, see the table
7 8 Add addend, int64
15 8 Sym target SymRef

The computed value payload[Off:Off+Siz] += address(Sym) + Add in the flavour the type prescribes is the linker's job; the object only records the request. A size 0 relocation patches nothing and exists purely as a marker for the linker's reachability analysis.

Relocation types

Values of objabi.RelocType. The assembler and compiler emit the generic ones plus their own architecture's family; the rest exist for other ports and for the linker itself.

Value Name Meaning
1 R_ADDR absolute address
2 R_ADDRPOWER ppc64: high adjusted plus low 16 bits across two D-form instructions
3 R_ADDRARM64 arm64: adrp plus add pair
4 R_ADDRMIPS mips: low 16 bits of an external address
5 R_ADDROFF 32-bit offset from the section start to the symbol
6 R_SIZE size of the referenced symbol
7 R_CALL direct call, PC relative
8 R_CALLARM arm: call with a shifted 24-bit field
9 R_CALLARM64 arm64: BL
10 R_CALLIND indirect call marker
11 R_CALLPOWER ppc64: call
12 R_CALLMIPS mips: non-PC-relative call target
13 R_CONST constant value of the symbol
14 R_PCREL PC relative displacement
15 R_TLS_LE thread local, local exec offset
16 R_TLS_IE thread local, initial exec GOT offset
17 R_GOTOFF offset from the GOT base
18 R_PLT0 PLT sequence, first instruction
19 R_PLT1 PLT sequence, second instruction
20 R_PLT2 PLT sequence, third instruction
21 R_USEFIELD field reachability marker
22 R_USETYPE type reachability marker, no bytes patched
23 R_USEIFACE interface conversion marker, size 0
24 R_USEIFACEMETHOD interface method marker, size 0, addend is the method offset
25 R_USENAMEDMETHOD keeps named methods alive
26 R_METHODOFF like R_ADDROFF, the linker may zero it when the method is dead
27 R_KEEP keeps the target alive if the source survives
28 R_POWER_TOC ppc64: TOC relative
29 R_GOTPCREL 32-bit PC relative GOT slot
30 R_JMPMIPS mips: non-PC-relative jump target
31 R_DWARFSECREF offset of the symbol from its section, DWARF use
32 R_ARM64_TLS_LE arm64: MOV[NZ] immediate, TLS local exec
33 R_ARM64_TLS_IE arm64: adrp plus ldr, TLS initial exec
34 R_ARM64_GOTPCREL arm64: adrp plus ldr GOT slot
35 R_ARM64_GOT arm64: GOT relative sequence
36 R_ARM64_PCREL arm64: adrp plus add PC relative
37 R_ARM64_PCREL_LDST8 arm64: adrp plus 8-bit load or store
38 R_ARM64_PCREL_LDST16 arm64: adrp plus 16-bit load or store
39 R_ARM64_PCREL_LDST32 arm64: adrp plus 32-bit load or store
40 R_ARM64_PCREL_LDST64 arm64: adrp plus 64-bit load or store
41 R_ARM64_LDST8 arm64: 12-bit load or store immediate, byte
42 R_ARM64_LDST16 arm64: bits 11 to 1 of the address
43 R_ARM64_LDST32 arm64: bits 11 to 2
44 R_ARM64_LDST64 arm64: bits 11 to 3
45 R_ARM64_LDST128 arm64: bits 11 to 4
46 R_POWER_TLS_LE ppc64: TLS local exec across two instructions
47 R_POWER_TLS_IE ppc64: TLS initial exec via GOT
48 R_POWER_TLS ppc64: marks the X-form instruction completing a TLS sequence
49 R_POWER_TLS_IE_PCREL34 ppc64: prefixed TLS initial exec load
50 R_POWER_TLS_LE_TPREL34 ppc64: prefixed TLS local exec
51 R_ADDRPOWER_DS ppc64: DS-form second instruction, bits 15 to 2
52 R_ADDRPOWER_GOT ppc64: GOT entry relative to TOC
53 R_ADDRPOWER_GOT_PCREL34 ppc64: PC relative GOT, prefixed
54 R_ADDRPOWER_PCREL ppc64: PC relative across two D-form instructions
55 R_ADDRPOWER_TOCREL ppc64: TOC relative across two D-form instructions
56 R_ADDRPOWER_TOCREL_DS ppc64: TOC relative, DS form
57 R_ADDRPOWER_D34 ppc64: prefixed absolute, 34 bits
58 R_ADDRPOWER_PCREL34 ppc64: prefixed PC relative, 34 bits
59 R_RISCV_JAL riscv64: 20-bit J-type offset
60 R_RISCV_JAL_TRAMP riscv64: as R_RISCV_JAL, linker-generated trampolines only
61 R_RISCV_CALL riscv64: AUIPC plus JALR pair
62 R_RISCV_PCREL_ITYPE riscv64: AUIPC plus I-type pair
63 R_RISCV_PCREL_STYPE riscv64: AUIPC plus S-type pair
64 R_RISCV_TLS_IE riscv64: TLS initial exec, AUIPC plus I-type
65 R_RISCV_TLS_LE riscv64: TLS local exec, LUI plus I-type
66 R_RISCV_GOT_HI20 riscv64: high 20 bits of a GOT address
67 R_RISCV_GOT_PCREL_ITYPE riscv64: GOT entry, AUIPC plus I-type
68 R_RISCV_PCREL_HI20 riscv64: high 20 bits of a PC relative address
69 R_RISCV_PCREL_LO12_I riscv64: low 12 bits, I-type
70 R_RISCV_PCREL_LO12_S riscv64: low 12 bits, S-type
71 R_RISCV_BRANCH riscv64: 12-bit branch offset
72 R_RISCV_ADD32 riscv64: in-place addition, V + S + A
73 R_RISCV_SUB32 riscv64: in-place subtraction, V - S - A
74 R_RISCV_RVC_BRANCH riscv64: 8-bit compressed branch offset
75 R_RISCV_RVC_JUMP riscv64: 11-bit compressed jump offset
76 R_PCRELDBL s390x: PC relative, 2-byte aligned
77 R_LOONG64_ADDR_HI loong64: bits 31 to 12 of an address
78 R_LOONG64_ADDR_LO loong64: low 12 bits
79 R_LOONG64_ADDR64_HI loong64: bits 63 to 52
80 R_LOONG64_ADDR64_LO loong64: bits 51 to 32
81 R_LOONG64_ADDR_PCREL20_S2 loong64: 22-bit aligned PC relative, PCADDI
82 R_LOONG64_TLS_LE_HI loong64: TLS local exec, high bits
83 R_LOONG64_TLS_LE_LO loong64: TLS local exec, low bits
84 R_CALLLOONG64 loong64: 28-bit aligned BL
85 R_LOONG64_CALL36 loong64: 38-bit aligned PCADDU18I plus JIRL
86 R_LOONG64_TLS_IE_HI loong64: TLS initial exec via GOT, high
87 R_LOONG64_TLS_IE_LO loong64: TLS initial exec via GOT, low
88 R_LOONG64_GOT_HI loong64: GOT entry, high bits
89 R_LOONG64_GOT_LO loong64: GOT entry, low bits
90 R_LOONG64_GOT64_HI loong64: 64-bit GOT entry, high
91 R_LOONG64_GOT64_LO loong64: 64-bit GOT entry, low
92 R_LOONG64_ADD64 loong64: 64-bit in-place addition
93 R_LOONG64_SUB64 loong64: 64-bit in-place subtraction
94 R_JMP16LOONG64 loong64: 18-bit aligned conditional jump
95 R_JMP21LOONG64 loong64: 23-bit aligned BEQZ or BNEZ
96 R_ADDRMIPSU mips: sign-adjusted upper 16 bits
97 R_ADDRMIPSTLS mips: TLS low 16 bits
98 R_ADDRCUOFF pointer-sized offset from the DWARF compile unit start
99 R_WASMIMPORT wasm: import module and name indices
100 R_XCOFFREF aix: keeps the target alive, patches nothing
101 R_PEIMAGEOFF windows: offset from the image base
102 R_INITORDER orders inittask records, patches nothing
103 R_DWTXTADDR_U1 writes a 1-byte ULEB .debug_addr index for the target function
104 R_DWTXTADDR_U2 as above, 2 bytes
105 R_DWTXTADDR_U3 as above, 3 bytes
106 R_DWTXTADDR_U4 as above, 4 bytes; the assembler always picks this one
-32768 R_WEAK mask: the target need not be reachable, see below
-32767 R_WEAKADDR R_WEAK or R_ADDR
-32763 R_WEAKADDROFF R_WEAK or R_ADDROFF

R_WEAK is bit 15 set on a negative int16: a weak relocation is the base type's value with bit 15 set. The linker strips the bit before dispatch.

Aux symbol entries

Element size 9 bytes: a uint8 type then a SymRef. Aux entries attach auxiliary symbols to a definition; the arrays run per symbol in the order given by AuxIndex.

Value Name Attaches
0 AuxGotype the Go type of a data symbol
1 AuxFuncInfo the FuncInfo payload of a text symbol
2 AuxFuncdata one funcdata symbol; one entry per slot, nil slots carry the {0,0} reference
3 AuxDwarfInfo DWARF debug info for the function
4 AuxDwarfLoc DWARF location lists
5 AuxDwarfRanges DWARF range lists
6 AuxDwarfLines DWARF line program
7 AuxPcsp pc-value table: SP adjustments
8 AuxPcfile pc-value table: source file indices
9 AuxPcline pc-value table: line numbers
10 AuxPcinline pc-value table: inlining tree positions
11 AuxPcdata one pc-value table per live variable slot
12 AuxWasmImport wasm import description
13 AuxWasmType wasm export type description
14 AuxSehUnwindInfo Windows SEH unwind info

The writer emits them in the order Gotype, FuncInfo, Funcdata entries, DwarfInfo, DwarfLoc, DwarfRanges, DwarfLines, Pcsp, Pcfile, Pcline, Pcinline, SehUnwindInfo, Pcdata entries, WasmImport, WasmType, and skips any whose payload would be empty. A function assembled from .s source by Go 1.27.1 carries exactly: FuncInfo, the Funcdata slots including nils, DwarfInfo, DwarfLines, Pcsp, Pcfile, Pcline and Pcinline; gasm's writer produces the same set.

The aux targets are either PkgIdxSelf definitions, PkgIdxHashed pcdata symbols, or, for the funcdata of assembly functions, PkgIdxNone references carrying names such as pkg.Fn.args_stackmap and pkg.Fn.arginfo0, which resolve to definitions in the package's compiled Go code when there is any.

Symbol payloads (BlkData)

The payloads of all defined symbols, in definition order, concatenated with no padding; DataIndex gives each symbol's slice. A text symbol's payload is its machine code, with the stack-split preamble and any morestack block already included. A data symbol's payload is the bytes laid down by its DATA directives, zero filled to its declared size. If a symbol was created from an embedded file, the file's bytes follow the payload and count towards its DataIndex extent; assembly producers never write this extension.

The FuncInfo payload

An SDATA symbol with no name, referenced by AuxFuncInfo. 28 bytes minimum, little endian:

Offset Size Field Meaning
0 4 Args argument area in bytes; 0x80000000 when the producer declared none
4 4 Locals frame size in bytes
8 1 FuncID runtime function classification, 0 means normal
9 1 FuncFlag TopFrame = 1, SPWrite = 2, Asm = 4
10 2 padding zero, reserved to a 4 byte boundary
12 4 StartLine source line of the TEXT declaration
16 4 NumFile count of file indices that follow
20 4 × NumFile Files indices into the Files block, ascending
then 4 NumInlTree count of inlining tree nodes that follow
then 24 × NumInlTree InlTree nodes, see below

One InlTree node, 24 bytes: int32 parent index, uint32 file index, int32 line, uint32 PkgIdx and uint32 SymIdx of the inlined function, and int32 parent PC.

The assembler derives FuncID from the symbol name through objabi.GetFuncID, so a runtime function with a name the runtime treats specially gets that classification even when defined in assembly; an ordinary name yields 0. FuncFlag carries the Asm bit, 4, for every assembly function.

The pc-value tables

The AuxPcsp, AuxPcfile, AuxPcline, AuxPcinline and AuxPcdata payloads are pc-value tables, each a sequence of value deltas and PC deltas:

  • a signed value delta, zig-zag encoded, binary.PutVarint form;
  • an unsigned PC delta in ULEB128 form, counted in instruction units, the raw delta divided by the architecture's minimum instruction length;
  • the table ends with a final PC delta to the end of the function followed by a zero byte.

The first value applies from function entry. The encoding is the one cmd/internal/obj/pcln.go calls funcpctab, and it is the same encoding the final runtime pclntable carries.

The DWARF payloads

AuxDwarfInfo, AuxDwarfLoc, AuxDwarfRanges and AuxDwarfLines reference SDWARF symbols whose payloads are DWARF byte streams. The object format treats them as opaque: the linker concatenates them into the final .debug_* sections and resolves the relocations recorded inside them. The compiler produces DWARF content per its own generation; gasm produces DWARF5 streams in asm/goobj_dwarf.go.

Builtins

Frequently referenced runtime functions are referenced by index rather than by name: PkgIdxBuiltin with SymIdx set to the position in the generated table cmd/internal/goobj/builtinlist.go, 299 entries in Go 1.27.1, names such as runtime.newobject at index 0; 232 entries carry ABI 1 and the remaining 67 ABI 0. Builtin names never enter the string table. The mapping only applies while the object is not linked against shared libraries, and a linkname'd symbol never counts as a builtin even when its name matches.

Fingerprints

The 8 byte fingerprint identifies one build of a package. The compiler fills it with a hash of the package's export data; the assembler leaves it zero. The linker checks a package's fingerprint against the fingerprints its importers recorded in their Autolib entries and rejects a mismatched build, which is how stale objects are caught.

What a producer must do

The checklist a third-party writer must satisfy for go build to accept its objects, in one place:

  1. Write the container exactly: the go object line matching the target toolchain's configuration string, the !\n terminator, then the blob.
  2. Emit the 19 block offsets, in order, and make BlkEnd the blob length.
  3. Deduplicate the string table, keep the empty string at offset 96, and reference it everywhere a name appears.
  4. Index relocations, aux entries and data per symbol with the N + 1 arrays, definitions ordered Symdefs, Hashed64defs, Hasheddefs, NonPkgDefs.
  5. Sort relocations by offset within each symbol.
  6. Fill Siz with the true payload length, set Align for every content-addressable symbol, and keep symbols under 2 GB.
  7. Reference symbols by the package-index rules. An assembly producer references everything outside the object by name, PkgIdxNone, except its own file-local statics and the builtins; PkgIdxSelf is reserved for definitions in this object. Assembly TEXT symbols carry ABI 0.
  8. Compute the content hashes exactly as the toolchain does, or emit no hashed definitions at all.

How gasm-sdk implements and verifies it

The writer lives in asm/goobj.go, which carries the shared container and the amd64 relocation emission, with per-architecture relocation emitters in asm/goobjarm64.go, asm/goobjriscv.go and asm/goobjloong64.go, symbol resolution in asm/goobj_resolve.go and DWARF generation in asm/goobj_dwarf.go. gasm asm --format goobj -p pkg/path writes objects that go build consumes in place of the toolchain's own.

Verification is differential and continuous:

  • asm/goobj_test.go compares gasm's GOOBJ output against go tool asm output for the same source, byte for byte;
  • asm/link_test.go builds real Go programs whose assembly comes from gasm objects and runs them;
  • gasm verify keeps the machine code itself identical to the toolchain's, which is the precondition for the object comparison to be meaningful.

Versioning and drift

The magic string carries the format generation, go120ld in Go 1.27.1. When a toolchain release changes the format, it changes that string first, and the linker refuses blobs whose magic it does not know. The watch points for a new release are, in order: the magic, the block index list, the Aux type list, the tail of the relocation table, the FuncInfo layout, and the builtin table count. gasm's tests fail against any of these changes, which is the mechanism that keeps this document and the writer current.

The authoritative sources, for the release this document covers:

  • cmd/internal/goobj/objfile.go: the format, every structure in this document;
  • cmd/internal/goobj/funcinfo.go: FuncInfo and the inlining tree;
  • cmd/internal/goobj/builtinlist.go: the builtin table;
  • cmd/internal/obj/objfile.go: the writer, hash inputs and aux order;
  • cmd/internal/obj/sym.go: package index assignment and the by-name rule;
  • cmd/internal/obj/pcln.go: the pc-value encoding;
  • cmd/internal/objabi/reloctype.go: relocation types;
  • cmd/internal/objabi/symkind.go: symbol kinds;
  • cmd/link/internal/ld/lib.go: container parsing and fingerprint checks.