常见的标识符 (更多参考最后的寄存器和操作指令表格)
$
开头后面跟数字代表是一个常量数字
FUNCDATA
和PCDATA
指令包含了由垃圾回收器使用的信息,他们由编译器引入。
PCDATA
的指令用法为:PCDATA tableid, tableoffset
。
PCDATA
有个两个参数,第一个参数为表格的类型,第二个是表格的地址。在目前的实现中,有PCDATA_StackMapIndex
和PCDATA_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 夜读 】