Files

149 lines
6.0 KiB
Markdown
Raw Permalink Normal View History

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