Files
2026-09-21 19:49:04 +02:00

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

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
  • 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

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.