通过汇编来了解Go的内部执行方式,比各种形象的比较更具说服力,因此学习如何反编译,了解go汇编代码有何不同很有必要。
1. 示例代码
package main
func main() {
println(Add(1, 2))
}
//go:noinline
func Add(a, b int) int {
return a + b
}
简单打印两个数的相加。
2. 编译
[goer@dev 1-asm]$ ls
go.mod main.go
[goer@dev 1-asm]$ go build -gcflags='-l' -o main main.go
[goer@dev 1-asm]$ ls
go.mod main main.go
编译时加上防止内联参数,同时给定可执行文件名。
3. nm使用
Nm lists the symbols defined or used by an object file, archive, or executable.
nm命令列出可执行文件中定义的符号。
查看如何使用
[goer@dev 1-asm]$ go tool nm -h
usage: go tool nm [options] file...
-n
an alias for -sort address (numeric),
for compatibility with other nm commands
-size
print symbol size in decimal between address and type
-sort {address,name,none,size}
sort output in the given order (default name)
size orders from largest to smallest
-type
print symbol type after name
[goer@dev 1-asm]$ go tool nm main | grep ' T main\.'
45ece0 T main.Add
45ec60 T main.main
可以看到,nm命令列出了在main.go文件中定义的两个函数,从左到右依次为地址、类型、符号名。
其中类型指符号的类型,具体如下表:
T text (code) segment symbol
t static text segment symbol
R read-only data segment symbol
r static read-only data segment symbol
D data segment symbol
d static data segment symbol
B bss segment symbol
b static bss segment symbol
C constant address
U referenced but undefined symbol
4. objdump使用
查看使用
[goer@dev 1-asm]$ go tool objdump
usage: go tool objdump [-S] [-gnu] [-s symregexp] binary [start end]
-S print Go code alongside assembly
-gnu
print GNU assembly next to Go assembly (where supported)
-s string
only dump symbols matching this regexp
类似gnu的objdump,记得加上-S参数,可读性更高。
[goer@dev 1-asm]$ go tool objdump -S -s '^main.Add$' main
TEXT main.Add(SB) /home/goer/workspace/go-codes/1-asm/main.go
return a + b
0x45ece0 488b442410 MOVQ 0x10(SP), AX
0x45ece5 488b4c2408 MOVQ 0x8(SP), CX
0x45ecea 4801c8 ADDQ CX, AX
0x45eced 4889442418 MOVQ AX, 0x18(SP)
0x45ecf2 c3 RET
5. go汇编风格
与王爽那本《汇编语言》差别比较大,官方说法是基于Plan9的汇编风格做了调整。
5.1 操作符的宽度
通过指令的后缀来判断操作符的宽度,后缀W代表16位,后缀L代表32位,后缀Q代表64位。
# Intel
INC EAX
INC RCX
# Go
INCL AX
INCQ CX
5.2 操作符顺序
与Intel汇编相反
# Intel,目的操作数第一位
MOV EAX, ECX
# Go,目的操作数最后一位
MOVL CX, AX
5.3 地址的表示
注意基址寄存器与索引寄存器位置
# Intel
[ESP + EBX * 2 + 16]
# Go
16(SP)(BX*2)