go version: 1.16
汇编指令
AMD64 rax rbx rcx rdx rdi rsi rbp rsp r8 r9 r10 r11 r12 r13 r14 rip
Plan9 AX BX CX DX DI SI BP SP R8 R9 R10 R11 R12 R13 R14 PC
rip寄存器:存放的是CPU即将执行的下一条指令在内存中的地址。这个rip是CPU自动控制的,不用我们修改。rsp栈顶寄存器和rbp栈基寄存器:rsp存放栈帧顶部地址,rbp存放栈帧起始地址。- 其他的寄存器,没有做特殊规定,我们可以拿来自己用 Go程序预定义了4个伪寄存器
FP: Frame pointer: arguments and locals.PC: Program counter: jumps and branches.SB: Static base pointer: global symbols.SP: Stack pointer: the highest address within the local stack frame.
x86汇编
方法:将代码编译成可执行程序,在通过gdb反汇编成平台相关的汇编代码
package main
func sum(a, b int) int {
c := a + b
return c
}
func main() {
sum(1, 2)
}
编译
go build -gcflags "-N -l" main.go
使用gdb反汇编
gdb main
~/workspace/study/$ gdb main ✔ 1h 21m 15s 22:16:31
GNU gdb (GDB) 11.1
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin20.4.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...
(No debugging symbols found in main)
Loading Go Runtime support.
(gdb) disas 'main.main' //反汇编main函数
Dump of assembler code for function main.main:
0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
(gdb) b *0x000000000105e2a0 //在main函数汇编代码第一行下断点
Breakpoint 1 at 0x105e2a0
(gdb) r //运行,停在刚打的断点位置
Starting program: /Users/zixuan.xu/workspace/study/learn-go-plan9/goroutine/main
[New Thread 0x2b03 of process 27105]
[New Thread 0x5403 of process 27105]
warning: unhandled dyld version (17)
[New Thread 0x2c07 of process 27105]
[New Thread 0x2e03 of process 27105]
[New Thread 0x3003 of process 27105]
[New Thread 0x5303 of process 27105]
Thread 2 hit Breakpoint 1, 0x000000000105e2a0 in main.main ()
(gdb) disas // 列出汇编代码
Dump of assembler code for function main.main:
=> 0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx // => 代表当前block的位置
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
(gdb) i r rbp rsp rip // 查看这几个寄存器的值
rbp 0xc0000507d0 0xc0000507d0
rsp 0xc000050780 0xc000050780
rip 0x105e2a0 0x105e2a0 <main.main>
(gdb)
此时main函数的栈帧如下:
此时只是停在main汇编代码中第一行,还并未执行这行,所以rip存的就是这行代码的地址,CPU下一条指令就会执行。
继续向下走,直接下断点到第7行
(gdb) b *0x000000000105e2bd
Breakpoint 2 at 0x105e2bd
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, 0x000000000105e2bd in main.main ()
(gdb) disas
Dump of assembler code for function main.main:
0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
=> 0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
(gdb) i r rbp rsp rip
rbp 0xc000050778 0xc000050778
rsp 0xc000050760 0xc000050760
rip 0x105e2bd 0x105e2bd <main.main+29>
(gdb)
由于main函数需要给sum提供参数和返回值,所以main函数要预留32字节的空间(sizeof a + b + return value = 32)
所以第四行指令,sub将rsp下移32字节大小,第五行指令将rbp的内容放到rsp向上偏移24字节的位置上(对应plan9就是24(SP)),第6行将·24(rsp)的地址存入rbp中,此时rbp指向24(rsp)
此时main函数的栈帧如下:
下断点到call指向位置
(gdb) b *0x000000000105e2ce
Breakpoint 3 at 0x105e2ce
(gdb) c
Continuing.
Thread 2 hit Breakpoint 3, 0x000000000105e2ce in main.main ()
(gdb) disas
Dump of assembler code for function main.main:
0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
=> 0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
(gdb) i r rbp rsp rip
rbp 0xc000050778 0xc000050778
rsp 0xc000050760 0xc000050760
rip 0x105e2ce 0x105e2ce <main.main+46>
(gdb)
movq $0x1,(%rsp)和movq $0x2,0x8(%rsp)分别把1和2放入rsp和8(rsp)位置上,作为sum的参数.
此时main函数的栈帧如下:
接下来该运行call指令了: 先扫一眼
sum函数
(gdb) disas 'main.sum'
Dump of assembler code for function main.sum:
0x000000000105e260 <+0>: sub $0x10,%rsp
0x000000000105e264 <+4>: mov %rbp,0x8(%rsp)
0x000000000105e269 <+9>: lea 0x8(%rsp),%rbp
0x000000000105e26e <+14>: movq $0x0,0x28(%rsp)
0x000000000105e277 <+23>: mov 0x18(%rsp),%rax
0x000000000105e27c <+28>: add 0x20(%rsp),%rax
0x000000000105e281 <+33>: mov %rax,(%rsp)
0x000000000105e285 <+37>: mov %rax,0x28(%rsp)
0x000000000105e28a <+42>: mov 0x8(%rsp),%rbp
0x000000000105e28f <+47>: add $0x10,%rsp
0x000000000105e293 <+51>: ret
0x000000000105e294 <+52>: int3
0x000000000105e295 <+53>: int3
0x000000000105e296 <+54>: int3
0x000000000105e297 <+55>: int3
0x000000000105e298 <+56>: int3
0x000000000105e299 <+57>: int3
0x000000000105e29a <+58>: int3
0x000000000105e29b <+59>: int3
0x000000000105e29c <+60>: int3
0x000000000105e29d <+61>: int3
0x000000000105e29e <+62>: int3
0x000000000105e29f <+63>: int3
End of assembler dump.
(gdb) b *0x000000000105e260
Breakpoint 4 at 0x105e260
(gdb) disas
Dump of assembler code for function main.main:
0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
=> 0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
(gdb) i r rbp rsp rip
rbp 0xc000050778 0xc000050778
rsp 0xc000050760 0xc000050760
rip 0x105e2ce 0x105e2ce <main.main+46>
(gdb) c
Continuing.
Thread 2 hit Breakpoint 4, 0x000000000105e260 in main.sum ()
(gdb) disas
Dump of assembler code for function main.sum:
=> 0x000000000105e260 <+0>: sub $0x10,%rsp
0x000000000105e264 <+4>: mov %rbp,0x8(%rsp)
0x000000000105e269 <+9>: lea 0x8(%rsp),%rbp
0x000000000105e26e <+14>: movq $0x0,0x28(%rsp)
0x000000000105e277 <+23>: mov 0x18(%rsp),%rax
0x000000000105e27c <+28>: add 0x20(%rsp),%rax
0x000000000105e281 <+33>: mov %rax,(%rsp)
0x000000000105e285 <+37>: mov %rax,0x28(%rsp)
0x000000000105e28a <+42>: mov 0x8(%rsp),%rbp
0x000000000105e28f <+47>: add $0x10,%rsp
0x000000000105e293 <+51>: ret
0x000000000105e294 <+52>: int3
0x000000000105e295 <+53>: int3
0x000000000105e296 <+54>: int3
0x000000000105e297 <+55>: int3
0x000000000105e298 <+56>: int3
0x000000000105e299 <+57>: int3
0x000000000105e29a <+58>: int3
0x000000000105e29b <+59>: int3
0x000000000105e29c <+60>: int3
0x000000000105e29d <+61>: int3
0x000000000105e29e <+62>: int3
0x000000000105e29f <+63>: int3
End of assembler dump.
(gdb) i r rbp rsp rip
rbp 0xc000050778 0xc000050778
rsp 0xc000050758 0xc000050758
rip 0x105e260 0x105e260 <main.sum>
(gdb)
执行call的时候,rip的值会编程下一条指令的地0x000000000105e2d3,并且call会把当前rip的值入栈(这里代表函数栈的return addr位置),同时栈顶的rsp也会下移到return addr的位置,同时又将rip的值设置为call执行调用的那个函数的第一条指令的位置0x000000000105e260
此时main函数的栈帧如下:
继续执行
sum,
(gdb) b *0x000000000105e26e
Breakpoint 5 at 0x105e26e
(gdb) c
Continuing.
Thread 2 hit Breakpoint 5, 0x000000000105e26e in main.sum ()
(gdb) disas
Dump of assembler code for function main.sum:
0x000000000105e260 <+0>: sub $0x10,%rsp
0x000000000105e264 <+4>: mov %rbp,0x8(%rsp)
0x000000000105e269 <+9>: lea 0x8(%rsp),%rbp
=> 0x000000000105e26e <+14>: movq $0x0,0x28(%rsp)
0x000000000105e277 <+23>: mov 0x18(%rsp),%rax
0x000000000105e27c <+28>: add 0x20(%rsp),%rax
0x000000000105e281 <+33>: mov %rax,(%rsp)
0x000000000105e285 <+37>: mov %rax,0x28(%rsp)
0x000000000105e28a <+42>: mov 0x8(%rsp),%rbp
0x000000000105e28f <+47>: add $0x10,%rsp
0x000000000105e293 <+51>: ret
0x000000000105e294 <+52>: int3
0x000000000105e295 <+53>: int3
0x000000000105e296 <+54>: int3
0x000000000105e297 <+55>: int3
0x000000000105e298 <+56>: int3
0x000000000105e299 <+57>: int3
0x000000000105e29a <+58>: int3
0x000000000105e29b <+59>: int3
0x000000000105e29c <+60>: int3
0x000000000105e29d <+61>: int3
0x000000000105e29e <+62>: int3
0x000000000105e29f <+63>: int3
End of assembler dump.
(gdb) i r rbp rsp rip
rbp 0xc000050750 0xc000050750
rsp 0xc000050748 0xc000050748
rip 0x105e26e 0x105e26e <main.sum+14>
(gdb)
因为sum栈不为空,并且只有一个本地变量,所以需要下移16字节(8字节给局部变量,8字节给caller's BP),并将rbp的值存到8(rsp)中,将8(rsp)的地址存到rbp中
此时main/sum函数的栈帧如下:
接下来五行汇编代码
=> 0x000000000105e26e <+14>: movq $0x0,0x28(%rsp)
0x000000000105e277 <+23>: mov 0x18(%rsp),%rax
0x000000000105e27c <+28>: add 0x20(%rsp),%rax
0x000000000105e281 <+33>: mov %rax,(%rsp)
0x000000000105e285 <+37>: mov %rax,0x28(%rsp)
- 将
40(rsp)的位置赋值为0 - 将
24(rsp)的值,也就是1存到rax里 - 将
32(rsp)的值,也就是2,与rax相加,并将结果存到rax里,此时rax的值为3 - 将
rax的值赋值给rsp(就是本地变量c) - 将
rax的值赋值给40(rsp),这个位置代表sum函数的返回值
此时main/sum函数的栈帧如下:
继续执行,下断点到
sum的ret处
(gdb) b *0x000000000105e293
Breakpoint 6 at 0x105e293
(gdb) c
Continuing.
Thread 2 hit Breakpoint 6, 0x000000000105e293 in main.sum ()
(gdb) disas
Dump of assembler code for function main.sum:
0x000000000105e260 <+0>: sub $0x10,%rsp
0x000000000105e264 <+4>: mov %rbp,0x8(%rsp)
0x000000000105e269 <+9>: lea 0x8(%rsp),%rbp
0x000000000105e26e <+14>: movq $0x0,0x28(%rsp)
0x000000000105e277 <+23>: mov 0x18(%rsp),%rax
0x000000000105e27c <+28>: add 0x20(%rsp),%rax
0x000000000105e281 <+33>: mov %rax,(%rsp)
0x000000000105e285 <+37>: mov %rax,0x28(%rsp)
0x000000000105e28a <+42>: mov 0x8(%rsp),%rbp
0x000000000105e28f <+47>: add $0x10,%rsp
=> 0x000000000105e293 <+51>: ret
0x000000000105e294 <+52>: int3
0x000000000105e295 <+53>: int3
0x000000000105e296 <+54>: int3
0x000000000105e297 <+55>: int3
0x000000000105e298 <+56>: int3
0x000000000105e299 <+57>: int3
0x000000000105e29a <+58>: int3
0x000000000105e29b <+59>: int3
0x000000000105e29c <+60>: int3
0x000000000105e29d <+61>: int3
0x000000000105e29e <+62>: int3
0x000000000105e29f <+63>: int3
End of assembler dump.
(gdb) i r rbp rsp rip
rbp 0xc000050778 0xc000050778
rsp 0xc000050758 0xc000050758
rip 0x105e293 0x105e293 <main.sum+51>
(gdb)
解读下这两行
0x000000000105e28a <+42>: mov 0x8(%rsp),%rbp
0x000000000105e28f <+47>: add $0x10,%rsp
- 将
8(rsp)的值赋值给rbp rsp上移16字节 此时main函数的栈帧如下:在执行
ret时候,ret把rsp的内容取出来放到rip寄存器中,并且return addr出栈,rsp上移8字节,这样rip就指向了调用sum之前call指令的下一个地址,从而返回到main函数中继续执行 此时main函数的栈帧如下:
下断点到call指令的下一行
(gdb) b *0x000000000105e2d3
Breakpoint 7 at 0x105e2d3
(gdb) c
Continuing.
Thread 2 hit Breakpoint 7, 0x000000000105e2d3 in main.main ()
(gdb) disas
Dump of assembler code for function main.main:
0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
=> 0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
(gdb) i r rbp rsp rip
rbp 0xc000050778 0xc000050778
rsp 0xc000050760 0xc000050760
rip 0x105e2d3 0x105e2d3 <main.main+51>
(gdb)
可以看到这几个寄存器的值都和图片上一样 接下来三行代码
=> 0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
- 将24(rsp)的值放入rbp
- 将rsp上移32字节
- ret返回main函数的调用函数 此时函数的栈帧如下: