docs(asm): open the assembly language reference
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
# Directives: TEXT, DATA, GLOBL and the annotations
|
||||
|
||||
Layer 1, the common language, with the flag vocabulary both layers share.
|
||||
Verified against `go tool asm` of Go 1.27.1, against the shipped headers
|
||||
`textflag.h` and `funcdata.h` in `$GOROOT/pkg/include`, and against gasm's
|
||||
parser. Where gasm extends a directive, the extension says so and is marked.
|
||||
|
||||
Six directives exist. Three define things: TEXT, DATA, GLOBL. Three
|
||||
annotate: FUNCDATA, PCDATA, PCALIGN.
|
||||
|
||||
## TEXT
|
||||
|
||||
```text
|
||||
// func Add(a, b int64) int64
|
||||
TEXT ·Add(SB), NOSPLIT, $0-24
|
||||
...instructions...
|
||||
RET
|
||||
```
|
||||
|
||||
```text
|
||||
TEXT symbol(SB), [flags,] $framesize[-argsize]
|
||||
```
|
||||
|
||||
- The symbol is an `·Name(SB)` reference into the current package, or a
|
||||
fully qualified name.
|
||||
- The optional flag argument is a constant expression, normally an OR of the
|
||||
names from `textflag.h`, the table below. Without `#include "textflag.h"`
|
||||
the names are not macros and the assembler reports the misleading error
|
||||
`illegal or missing addressing mode for symbol NOSPLIT`: include the
|
||||
header first.
|
||||
- `$framesize-argsize` is two constants, not a subtraction: the local frame
|
||||
size in bytes, and the caller's argument area in bytes. The argument size
|
||||
may be omitted entirely, `$16`, which marks the argument size unknown
|
||||
(0x80000000 in the object, the value of `ArgsSizeUnknown` from
|
||||
`funcdata.h`); a frame size may be negative only in the generated ABI
|
||||
wrappers.
|
||||
- A function whose last instruction is not a branch cannot fall through into
|
||||
the next TEXT: the toolchain appends a jump to itself, so end functions
|
||||
with `RET` deliberately.
|
||||
- One TEXT per symbol; redeclaring is an error. The TEXT line also fixes the
|
||||
function's source line for traceback: it is the line number that pcln
|
||||
reports for the function's start.
|
||||
|
||||
The framesize and argsize fields do real work: the framesize drives the
|
||||
stack-split preamble (RUNTIME.md carries the contract), and both travel into
|
||||
the FuncInfo record of the object (GOOBJ.md carries its layout).
|
||||
|
||||
### The flag table
|
||||
|
||||
Values from `textflag.h`, in agreement with `cmd/internal/obj/textflag.go`:
|
||||
|
||||
| Name | Value | Applies to | Meaning |
|
||||
|---|---|---|---|
|
||||
| NOPROF | 1 | both | do not profile; deprecated |
|
||||
| DUPOK | 2 | both | the linker may keep one of several duplicates |
|
||||
| NOSPLIT | 4 | TEXT | no stack-split preamble |
|
||||
| RODATA | 8 | data | put the data in a read-only section |
|
||||
| NOPTR | 16 | data | the data contains no pointers |
|
||||
| WRAPPER | 32 | TEXT | a wrapper; must not disable `recover` |
|
||||
| NEEDCTXT | 64 | TEXT | a closure consuming the context register |
|
||||
| TLSBSS | 256 | data | a thread local word in BSS |
|
||||
| NOFRAME | 512 | TEXT | no frame setup; only valid with a frame size of 0 |
|
||||
| REFLECTMETHOD | 1024 | TEXT | the function calls `reflect.Type.Method` or `MethodByName` |
|
||||
| TOPFRAME | 2048 | TEXT | the outermost frame; unwinders stop here |
|
||||
| ABIWRAPPER | 4096 | TEXT | an ABI transition wrapper |
|
||||
|
||||
Rules with teeth:
|
||||
|
||||
- `NOSPLIT` removes the split check, so the frame plus everything the
|
||||
function calls must fit in the stack segment that remains. It exists to
|
||||
protect the splitting code itself; reaching for it to save two instructions
|
||||
is how stack overflows corrupt memory. On amd64 the assembler additionally
|
||||
marks small leaf functions NoSplit itself and omits the check, so the
|
||||
absence of the preamble is not proof the flag was written.
|
||||
- A TEXT whose symbol is declared `ABIInternal` must carry NOSPLIT: the
|
||||
assembler rejects it otherwise, because it cannot generate
|
||||
the split path for a register-ABI function.
|
||||
- `RODATA` implies NOPTR for the garbage collector.
|
||||
|
||||
## DATA
|
||||
|
||||
```text
|
||||
DATA ·table+0(SB)/8, $0x0102030405060708
|
||||
DATA ·msg+0(SB)/14, $"hello, world\n"
|
||||
GLOBL ·msg(SB), RODATA, $14
|
||||
```
|
||||
|
||||
```text
|
||||
DATA symbol+offset(SB)/width, value
|
||||
```
|
||||
|
||||
- `width` is exactly 1, 2, 4 or 8: the initialiser is written into the data
|
||||
image at `symbol+offset` in that many bytes.
|
||||
- The value is an integer or character constant of the width, or a string
|
||||
literal whose byte length equals the width exactly; escapes count. Long
|
||||
data is written as successive DATA lines at increasing offsets; bytes the
|
||||
directives never name are zero.
|
||||
- Every symbol initialised with DATA ends with a GLOBL line declaring its
|
||||
total size, after all of its DATA lines.
|
||||
|
||||
A symbol containing pointers cannot be defined in assembly, because the
|
||||
collector cannot see into it: define it in Go and refer to it by name. As a
|
||||
rule, data that is not read-only belongs in Go.
|
||||
|
||||
Extension, gasm only: a DATA initialiser may name a symbol,
|
||||
`DATA ·fn+0(SB)/8, $·handler(SB)`, which gasm lays down as an absolute
|
||||
relocation on that field. The toolchain offers no ground truth for this
|
||||
form; gasm's behaviour is verified by linking and execution.
|
||||
|
||||
## GLOBL
|
||||
|
||||
```text
|
||||
GLOBL symbol(SB), [flags,] $size
|
||||
```
|
||||
|
||||
Declares the symbol global with its total size in bytes. The useful flags
|
||||
are RODATA, NOPTR, DUPOK and TLSBSS from the table above. Uninitialised
|
||||
bytes are zero, which makes GLOBL with no DATA the language's BSS.
|
||||
|
||||
## FUNCDATA and PCDATA
|
||||
|
||||
```text
|
||||
FUNCDATA $functypeid, symbol(SB)
|
||||
PCDATA $pctypeid, $value
|
||||
```
|
||||
|
||||
The compiler's annotations for the garbage collector and traceback, named by
|
||||
the ids in `funcdata.h`: FUNCDATA 0 to 7 (args pointer maps, locals pointer
|
||||
maps, stack objects, inline tree, open-coded defer info, argument info,
|
||||
argument liveness, wrap info), PCDATA 0 to 4 (unsafe point, stack map index,
|
||||
inline tree index, argument liveness index, panic bounds). Assembly code
|
||||
normally reaches them only through the macro forms in `funcdata.h`, which
|
||||
RUNTIME.md explains. Outside the macros, hand-written PCDATA is meaningless:
|
||||
the values are pc-value tables the compiler builds from its own view of the
|
||||
program.
|
||||
|
||||
## PCALIGN
|
||||
|
||||
```text
|
||||
PCALIGN $32
|
||||
```
|
||||
|
||||
Pads the code so that the next instruction lands on the given boundary,
|
||||
which must be a power of two and at least the target's instruction
|
||||
alignment. Supported on amd64, arm64, ppc64, loong64 and riscv64. The
|
||||
padding instructions are the target's NOP encoding, so the bytes between
|
||||
functions differ from what the instruction stream alone would produce, which
|
||||
matters to anyone comparing encodings byte for byte.
|
||||
Reference in New Issue
Block a user