(一)go编译工具使用以及go汇编风格

126 阅读2分钟

通过汇编来了解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)