658 lines
31 KiB
Markdown
658 lines
31 KiB
Markdown
# 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-devkit 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`:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
"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
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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-devkit 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.
|