# 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.