149 lines
6.0 KiB
Markdown
149 lines
6.0 KiB
Markdown
# 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.
|