6.0 KiB
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
// func Add(a, b int64) int64
TEXT ·Add(SB), NOSPLIT, $0-24
...instructions...
RET
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 errorillegal or missing addressing mode for symbol NOSPLIT: include the header first. $framesize-argsizeis 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 ofArgsSizeUnknownfromfuncdata.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
RETdeliberately. - 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:
NOSPLITremoves 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
ABIInternalmust carry NOSPLIT: the assembler rejects it otherwise, because it cannot generate the split path for a register-ABI function. RODATAimplies NOPTR for the garbage collector.
DATA
DATA ·table+0(SB)/8, $0x0102030405060708
DATA ·msg+0(SB)/14, $"hello, world\n"
GLOBL ·msg(SB), RODATA, $14
DATA symbol+offset(SB)/width, value
widthis exactly 1, 2, 4 or 8: the initialiser is written into the data image atsymbol+offsetin 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
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
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
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.