Featured image of post Learning Go's Plan 9 Assembly

Learning Go's Plan 9 Assembly

First Experience with Assembly

Common Identifiers (See Registers and Operation Instructions tables at the end for more)

  • $ prefix followed by numbers represents a constant value
  • FUNCDATA and PCDATA instructions contain garbage collector information generated by the compiler
  • PCDATA syntax: PCDATA tableid, tableoffset

PCDATA has two parameters: table type and table offset. Current implementations include PCDATA_StackMapIndex and PCDATA_InlTreeIndex table types. Both contain code location info (file path, line numbers, function info), with PCDATA_InlTreeIndex used for inlined functions.

  • FUNCDATA tableid, tableoffset

    Similar to PCDATA: first parameter is table type, second is table offset. Current implementations define three FUNC table types: FUNCDATA_ArgsPointerMaps (function argument pointers), FUNCDATA_LocalsPointerMaps (local pointers), and FUNCDATA_InlTree (inlined function pointers). These tables help Go’s garbage collector track pointer lifecycles.

Operations Below

  • C:\Users\seth-shi> cat main.go
package main

func Add(x, y int) int {
    return x + y
}
  • C:\Users\seth-shi> go build -gcflags -S main.go
  • Comments starting with ## are my understanding/research results. [] indicates areas needing further study
# command-line-arguments
## Defines process "".Add with args=0x0 locals=0x18 (hex 24) [Need to study size/args]
"".Add STEXT nosplit size=19 args=0x18 locals=0x0 funcid=0x0
        ## TEXT pseudo-operation defines process at "".Add(SB) address
        ## $0-24 indicates stack size (3*8 bytes for ints?) [Needs verification]
        ## ABIInternal = Application Binary Interface Internal
        0x0000 00000 (C:\Users\seth-shi\main.go:3)      TEXT    "".Add(SB), NOSPLIT|ABIInternal, $0-24
        ## GC info (ignore)
        0x0000 00000 (C:\Users\seth-shi\main.go:3)      FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x0000 00000 (C:\Users\seth-shi\main.go:3)      FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        ## Move stack+16 to AX [Need to verify stack direction]
        0x0000 00000 (C:\Users\seth-shi\main.go:4)      MOVQ    "".y+16(SP), AX
        ## Move stack+8 to CX
        0x0005 00005 (C:\Users\seth-shi\main.go:4)      MOVQ    "".x+8(SP), CX
        ## Add CX to AX
        0x000a 00010 (C:\Users\seth-shi\main.go:4)      ADDQ    CX, AX
        ## Store result at stack+24
        0x000d 00013 (C:\Users\seth-shi\main.go:4)      MOVQ    AX, "".~r2+24(SP)
        ## Return
        0x0012 00018 (C:\Users\seth-shi\main.go:4)      RET
        0x0000 48 8b 44 24 10 48 8b 4c 24 08 48 01 c8 48 89 44  H.D$.H.L$.H..H.D
        0x0010 24 18 c3                                         $..
go.cuinfo.packagename.main SDWARFCUINFO dupok size=0
        0x0000 6d 61 69 6e                                      main
go.string."0w\xaf\f\x92t\b\x02A\xe1\xc1\a\xe6\xd6\x18\xe6path\tcommand-line-arguments\nmod\tseth\t(devel)\t\n\xf92C1\x86\x18 r\x00\x82B\x10A\x16\xd8\xf2" SRODATA dupok size=78
        0x0000 30 77 af 0c 92 74 08 02 41 e1 c1 07 e6 d6 18 e6  0w...t..A.......
        0x0010 70 61 74 68 09 63 6f 6d 6d 61 6e 64 2d 6c 69 6e  path.command-lin
        0x0020 65 2d 61 72 67 75 6d 65 6e 74 73 0a 6d 6f 64 09  e-arguments.mod.
        0x0030 73 65 74 68 09 28 64 65 76 65 6c 29 09 0a f9 32  seth.(devel)...2
        0x0040 43 31 86 18 20 72 00 82 42 10 41 16 d8 f2        C1.. r..B.A...
""..inittask SNOPTRDATA size=24
        0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
        0x0010 00 00 00 00 00 00 00 00                          ........
runtime.modinfo SDATA size=16
        0x0000 00 00 00 00 00 00 00 00 4e 00 00 00 00 00 00 00  ........N.......
        rel 0+8 t=1 go.string."0w\xaf\f\x92t\b\x02A\xe1\xc1\a\xe6\xd6\x18\xe6path\tcommand-line-arguments\nmod\tseth\t(devel)\t\n\xf92C1\x86\x18 r\x00\x82B\x10A\x16\xd8\xf2"+0
type..importpath.unsafe. SRODATA dupok size=9
        0x0000 00 00 06 75 6e 73 61 66 65                       ...unsafe
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
        0x0000 01 00 00 00 00 00 00 00                          ........
# command-line-arguments
runtime.main_main·f: function main is undeclared in the main package

Registers

Mnemonic Name Purpose
AX Accumulator Register Data storage for arithmetic, operands, results, and temporary addresses
BX Base Register Memory access address storage
CX Count Register Counter values
DX Data Register Data transfer and I/O port addresses
SP Stack Pointer symbol+offset(SP) = Go pseudo-register; offset(SP) = hardware register
BP Base Pointer Stack base address before function entry
SB Static Base Pointer Go pseudo-register. foo(SB) = memory address; foo+4(SB) = 4-byte offset
FP Frame Pointer Go pseudo-register. References function parameters via symbol+offset(FP)
SI Source Index Source operand offset
DI Destination Index Destination operand offset

Operation Instructions

Mnemonic Type Purpose Example
MOVQ Data Transfer Move data MOVQ 48, AX moves 48 to AX
LEAQ Address Transfer Load effective address LEAQ AX, BX loads AX address into BX
ADDQ Arithmetic Add and assign ADDQ BX, AX stores BX+AX in AX
SUBQ Arithmetic Subtract and assign (Similar to ADDQ)
IMULQ Arithmetic Unsigned multiply (Similar to ADDQ)
IDIVQ Arithmetic Unsigned divide IDIVQ CX divides AX by CX, result in AX
CMPQ Comparison Compare values CMPQ SI, CX compares registers
CALL Control Flow Function call CALL runtime.printnl(SB) calls println
JMP Control Flow Unconditional jump JMP 389 jumps to 0x0185
JLS Control Flow Conditional jump (less than) JLS 389 jumps if SI < CX

References

A Morning Spent on Golang’s Plan9 Assembly
Go Compiler Tools: Plan9 Assembly
Go Plan9 Assembly Primer - Bridging Application and Low-Level [Go Night Reading]