Featured image of post 学习 Go 的 Plan 9 汇编

学习 Go 的 Plan 9 汇编

汇编的初体验

常见的标识符 (更多参考最后的寄存器操作指令表格)

  • $开头后面跟数字代表是一个常量数字
  • FUNCDATAPCDATA指令包含了由垃圾回收器使用的信息,他们由编译器引入。
  • PCDATA的指令用法为:PCDATA tableid, tableoffset

PCDATA有个两个参数,第一个参数为表格的类型,第二个是表格的地址。在目前的实现中,有PCDATA_StackMapIndexPCDATA_InlTreeIndex两种表格类型。两种表格的数据是类似的,应该包含了代码所在的文件路径、行号和函数的信息,只不过PCDATA_InlTreeIndex用于内联函数的表格。

  • FUNCDATA tableid, tableoffset

    FUNCDATA指令和PCDATA的格式类似:第一个参数为表格的类型,第二个是表格的地址。目前的实现中定义了三种FUNC表格类型:FUNCDATA_ArgsPointerMaps表示函数参数的指针信息表,FUNCDATA_LocalsPointerMaps表示局部指针信息表,FUNCDATA_InlTree表示被内联展开的指针信息表。通过FUNC表格,Go语言的垃圾回收器可以跟踪全部指针的生命周期,同时根据指针指向的地址是否在被移动的栈范围来确定是否要进行指针移动。

操作如下

  • 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
  • 两个#开头的是我的理解和上网查的结果, []中是还需要我去了解的
# command-line-arguments
## 指定过程名字为"".main,args=0x0 locals=0x18 对应下一行的 $24 的十六进制值 [size, args 还需要了解]
"".Add STEXT nosplit size=19 args=0x18 locals=0x0 funcid=0x0
        ## TEXT 是一个伪操作符, 以过程名的内存地址("".Add(SB))为定义过程的参数
        ## $0-24 代表需要的栈内存,总共按个整型变量所以是3*8[使用 main 函数时, 发现对不上,还需要研究]
        ## ABIInternal 是应用程序二进制接口内部(Application Binary Interface Internal) 的意思
        0x0000 00000 (C:\Users\seth-shi\main.go:3)      TEXT    "".Add(SB), NOSPLIT|ABIInternal, $0-24
        ## 垃圾回收信息, 忽略
        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)
        ## 这里可以查看阮老师的栈的使用, 栈顶偏移 16 个字节数据写入 AX 寄存器 [阮老师的是栈在内存块最上面, 堆在下面, 理论上应该是减去地址,而不是+地址,这里需要研究]
        0x0000 00000 (C:\Users\seth-shi\main.go:4)      MOVQ    "".y+16(SP), AX
        ## 栈顶偏移 8 个字节数据写入 BX 寄存器
        0x0005 00005 (C:\Users\seth-shi\main.go:4)      MOVQ    "".x+8(SP), CX
        ## 相加操作 AX += CX 
        0x000a 00010 (C:\Users\seth-shi\main.go:4)      ADDQ    CX, AX
        ## 然后把 AX 寄存器里的数据传输到 栈顶初始字节[这里需要研究, 地址的寻址位置方式]
        0x000d 00013 (C:\Users\seth-shi\main.go:4)      MOVQ    AX, "".~r2+24(SP)
        ## 返回数据
        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

寄存器

助记符 名字 用途
AX 累加寄存器(AccumulatorRegister) 用于存放数据,包括算术、操作数、结果和临时存放地址
BX 基址寄存器(BaseRegister) 用于存放访问存储器时的地址
CX 计数寄存器(CountRegister) 用于保存计算值,用作计数器
DX 数据寄存器(DataRegister) 用于数据传递,在寄存器间接寻址中的I/O指令中存放I/O端口的地址
SP 堆栈顶指针(StackPointer) 如果是symbol+offset(SP)的形式表示go汇编的伪寄存器;如果是offset(SP)的形式表示硬件寄存器
BP 堆栈基指针(BasePointer) 保存在进入函数前的栈顶基址
SB 静态基指针(StaticBasePointer) go汇编的伪寄存器。foo(SB)用于表示变量在内存中的地址,foo+4(SB)表示foo起始地址往后偏移四字节。一般用来声明函数或全局变量
FP 栈帧指针(FramePointer) go汇编的伪寄存器。引用函数的输入参数,形式是symbol+offset(FP),例如arg0+0(FP)
SI 源变址寄存器(SourceIndex) 用于存放源操作数的偏移地址
DI 目的寄存器(DestinationIndex) 用于存放目的操作数的偏移地址

操作指令

助记符 指令种类 用途 示例
MOVQ 传送 数据传送 MOVQ 48, AX表示把48传送AX中
LEAQ 传送 地址传送 LEAQ AX, BX表示把AX有效地址传送到BX中
PUSHQ 传送 栈压入 PUSHQ AX表示先修改栈顶指针,将AX内容送入新的栈顶位置在go汇编中使用SUBQ代替
POPQ 传送 栈弹出 POPQ AX表示先弹出栈顶的数据,然后修改栈顶指针在go汇编中使用ADDQ代替
ADDQ 运算 相加并赋值 ADDQ BX, AX表示BX和AX的值相加并赋值给AX
SUBQ 运算 相减并赋值 略,同上
IMULQ 运算 无符号乘法 略,同上
IDIVQ 运算 无符号除法 IDIVQ CX除数是CX,被除数是AX,结果存储到AX中
CMPQ 运算 对两数相减,比较大小 CMPQ SI CX表示比较SI和CX的大小。与SUBQ类似,只是不返回相减的结果
CALL 转移 调用函数 CALL runtime.printnl(SB)表示通过println函数的内存地址发起调用
JMP 转移 无条件转移指令 JMP 389无条件转至0x0185地址处(十进制389转换成十六进制0x0185)
JLS 转移 条件转移指令 JLS 389上一行的比较结果,左边小于右边则执行跳到0x0185地址处(十进制389转换成十六进制0x0185)

引用

肝了一上午golang之plan9入门

go编译工具的使用之plan9汇编

go plan9 汇编入门,带你打通应用和底层 【 Go 夜读 】