go/c/c++常用功能对应的汇编指令

github原文, 欢迎star 😜

变量声明

     5: func main() {
=>   6:     var a int
     7:     a = 2

通过dlvdisass查看对应的汇编指令:

        var.go:6    0x10cbacf   488d05aaae0000          lea rax, ptr [rip+0xaeaa]         #lea 取地址,[rip+0xaeaa]对应的地址的值赋给rax([rip+0xaeaa]代表地址)
    var.go:6    0x10cbad6   48890424            mov qword ptr [rsp], rax          #把rax存储的值赋给[rsp]指向地址的值 *rsp = rax
    var.go:6    0x10cbada   e88122f4ff          call $runtime.newobject           #调用runtime.newobject函数,call <label>
    var.go:6    0x10cbadf   488b442408          mov rax, qword ptr [rsp+0x8]      # 调用newobject函数后的返回值存储在[rsp+0x8],rax接收返回值(地址)
    var.go:6    0x10cbae4   4889442478          mov qword ptr [rsp+0x78], rax     # 使用局部变量[rsp+0x78]来接收newobject的返回值
    var.go:6    0x10cbae9   48c70000000000          mov qword ptr [rax], 0x0          # a = 0 ,[rax]代表的不是地址,而是rax指向地址的值
    var.go:7    0x10cbaf0   488b442478          mov rax, qword ptr [rsp+0x78]     # 把[rsp+0x78]存储的内容加载到rax
    var.go:7    0x10cbaf5   48c70002000000          mov qword ptr [rax], 0x2          # 把rax地址对应的值赋于2

通过dlvsi进行汇编级调试,首先使用regs查看寄存器的状态

var.go:6    0x10cbacf   488d05aaae0000      lea rax, ptr [rip+0xaeaa]
var.go:6    0x10cbad6   48890424        mov qword ptr [rsp], rax 

代码执行后的变化

   Rip = 0x00000000010cbacf  =>  Rip = 0x00000000010cbad6  #代码地址移到下一行 0x10cbad6    
   Rsp = 0x000000c00009de88  =>  Rsp = 0x000000c00009de88  # 
   Rax = 0x000000c00009df08  =>  Rax = 0x00000000010d6980  #保存代码地址0x10d6980
   Rbp = 0x000000c00009df78  =>  Rbp = 0x000000c00009df78

// rip = 0x00000000010cbacf  [rip+0xaeaa]对应的地址为`0x10d6979`
(dlv) x -fmt hex -count 24 -size 1 0x10d6979

// lea rax, ptr [rip+0xaeaa] 
(dlv) x -fmt hex -count 24 -size 1 0x000000c00009de88
0xc000065e88:   0x00   0x00   0x00   0x00   0x00   0x00   0x00   0x00

//mov qword ptr [rsp], rax 
(dlv) x -fmt hex -count 24 -size 1 0x000000c00009de88
0xc000065e88:   0x80   0x69   0x0d   0x01   0x00   0x00   0x00   0x00   //0x10d6980

(dlv) disass -a 0x10d6980 0x10d6988
    .:0 0x10d6980   0800    or byte ptr [rax], al
    .:0 0x10d6982   0000    add byte ptr [rax], al
    .:0 0x10d6984   0000    add byte ptr [rax], al
    .:0 0x10d6986   0000    add byte ptr [rax], al

执行语句:

    var.go:6    0x10cbad6   48890424                mov qword ptr [rsp], rax

代码执行后的变化

Rip = 0x00000000010cbad6  =>   Rip = 0x00000000010cbada
Rsp = 0x000000c000065e88  =>   Rsp = 0x000000c000065e88
Rax = 0x00000000010d6980  =>   Rax = 0x00000000010d6980
Rbp = 0x000000c000065f78  =>   Rbp = 0x000000c000065f78

可以看到声明变量的时候,编译器调用了runtime.newobject,函数返回为内存地址。使用dlv查看函数跳转的地址

(dlv) print &runtime.newobject
(*)(0x100dd60)

(dlv) disass -a 0x100dd60 0x100dd66
TEXT runtime.newobject(SB) /usr/local/go/src/runtime/malloc.go
    malloc.go:1176  0x100dd60   65  gs
    malloc.go:1176  0x100dd61   48  rex.w
    malloc.go:1176  0x100dd62   8b  prefix(0x8b)
    malloc.go:1176  0x100dd63   0c25    or al, 0x25
    malloc.go:1176  0x100dd65   30  prefix(0x30)

# /usr/local/go/src/runtime/malloc.go:1176
        // implementation of new builtin
        // compiler (both frontend and SSA backend) knows the signature
        // of this function
1176:   func newobject(typ *_type) unsafe.Pointer {
1177:       return mallocgc(typ.size, typ, true)
1178:   }

执行语句:

    var.go:6    0x10cbadf   488b442408              mov rax, qword ptr [rsp+0x8]

执行之后的值

Rip = 0x00000000010cbadf  =>      Rip = 0x00000000010cbae4    
Rsp = 0x000000c000065e88  =>      Rsp = 0x000000c000065e88   
Rax = 0x000000c00009e000  =>      Rax = 0x000000c00009e000   
Rbp = 0x000000c000065f78  =>      Rbp = 0x000000c000065f78

这种可以看出rsp的地址为0xc000065e88[rsp+0x8]的值为0xc000065e90,查看[rsp+0x8]的内容是0xc00009e000

(dlv) x -fmt hex -count 24 -size 1 0x000000c000065e90
0xc000065e90:   0x00   0xe0   0x09   0x00   0xc0   0x00   0x00   0x00   
0xc000065e98:   0x28   0x27   0x03   0x00   0xc0   0x00   0x00   0x00   
0xc000065ea0:   0xf5   0xd7   0x00   0x01   0x00   0x00   0x00   0x00

mov rax, qword ptr [rsp+0x8]的含义就是把[rsp+0x8]地址存储的内容赋给rax

以下执行的代码解释

    var.go:7    0x10cbaf0   488b442478          mov rax, qword ptr [rsp+0x78]
    var.go:7    0x10cbaf5   48c70002000000          mov qword ptr [rax], 0x2

[图片上传失败...(image-3cf9f5-1643256677698)]

=>   9:     b := new(int)
    10:     b = &a
    11:     *b = 5

汇编指令:

=>  var.go:9    0x10cbafc   488d057dae0000      lea rax, ptr [rip+0xae7d]    
    var.go:9    0x10cbb03   48890424        mov qword ptr [rsp], rax
    var.go:9    0x10cbb07   e85422f4ff      call $runtime.newobject        #创建对象
    var.go:9    0x10cbb0c   488b442408      mov rax, qword ptr [rsp+0x8]   #接收返回值[rsp+0x8],放在rax中
    var.go:9    0x10cbb11   4889442448      mov qword ptr [rsp+0x48], rax  #再把返回值放在本地变量[rsp+0x48]中
    var.go:10   0x10cbb16   488b442478      mov rax, qword ptr [rsp+0x78]  #首先是取[rsp+0x78]地址的内容
    var.go:10   0x10cbb1b   4889442448      mov qword ptr [rsp+0x48], rax  # b = &a
    var.go:11   0x10cbb20   8400            test byte ptr [rax], al        # 
    var.go:11   0x10cbb22   48c70005000000      mov qword ptr [rax], 0x5       # *b = 5

执行语句:

=>  13:     s := make([]int, 6)
    14:     s[0] = 2

创建数组调用func makeslice(et *_type, len, cap int) unsafe.Pointer,需要传入参数lencap,返回一个地址指针int

=>  var.go:13   0x10cbb29   488d0550ae0000              lea rax, ptr [rip+0xae50]
    var.go:13   0x10cbb30   48890424            mov qword ptr [rsp], rax
    var.go:13   0x10cbb34   48c744240806000000          mov qword ptr [rsp+0x8], 0x6    # len长度参数
    var.go:13   0x10cbb3d   48c744241006000000          mov qword ptr [rsp+0x10], 0x6   # cap容量参数
    var.go:13   0x10cbb46   e85550f8ff          call $runtime.makeslice
    var.go:13   0x10cbb4b   488b442418          mov rax, qword ptr [rsp+0x18]   # 返回值unsafe.Pointer
    var.go:13   0x10cbb50   4889842480000000            mov qword ptr [rsp+0x80], rax   #使用[rsp+0x80]接收返回值
    var.go:13   0x10cbb58   48c784248800000006000000    mov qword ptr [rsp+0x88], 0x6   #使用[rsp+0x88]存储len
    var.go:13   0x10cbb64   48c784249000000006000000    mov qword ptr [rsp+0x90], 0x6   #使用[rsp+0x90]存储cap
    var.go:14   0x10cbb70   eb00                jmp 0x10cbb72
    var.go:14   0x10cbb72   48c70002000000              mov qword ptr [rax], 0x2        # s[0] = 2

使用dlv查看数组的值

(dlv) print &s
(*[]int)(0xc000111f08)

(dlv) x -fmt hex -count 24 -size 1 0xc000111f08
0xc000111f08:   0x30   0x60   0x12   0x00   0xc0   0x00   0x00   0x00   # 数组的地址arr = 0xc000126030
0xc000111f10:   0x06   0x00   0x00   0x00   0x00   0x00   0x00   0x00   # len = 0x06
0xc000111f18:   0x06   0x00   0x00   0x00   0x00   0x00   0x00   0x00   # cap = 0x06

(dlv) x -fmt hex -count 24 -size 1 0xc000126030
0xc000126030:   0x02   0x00   0x00   0x00   0x00   0x00   0x00   0x00   # 数组的首个元素 0x02
0xc000126038:   0x00   0x00   0x00   0x00   0x00   0x00   0x00   0x00   
0xc000126040:   0x00   0x00   0x00   0x00   0x00   0x00   0x00   0x00

执行语句:

=>  16:     c := make(chan int, 5)
    17:     c <- 3

调用的函数func makechan(t *chantype, size int) *hchan

=>  var.go:16   0x10cbb79   488d0500a80000      lea rax, ptr [rip+0xa800]
    var.go:16   0x10cbb80   48890424        mov qword ptr [rsp], rax
    var.go:16   0x10cbb84   48c744240805000000  mov qword ptr [rsp+0x8], 0x5    # size 5
    var.go:16   0x10cbb8d   e80e90f3ff      call $runtime.makechan          
    var.go:16   0x10cbb92   488b442410      mov rax, qword ptr [rsp+0x10]   # 返回值*hchan
    var.go:16   0x10cbb97   4889442440      mov qword ptr [rsp+0x40], rax   # 使用[rsp+0x40]保存makechan函数返回值
    var.go:17   0x10cbb9c   48890424        mov qword ptr [rsp], rax        # func chansend1(c *hchan, elem unsafe.Pointer) 第一个参数*hchan
    var.go:17   0x10cbba0   488d05011b0300      lea rax, ptr [rip+0x31b01]      # 0x00000000010fd6a8 代表的是3
    var.go:17   0x10cbba7   4889442408      mov qword ptr [rsp+0x8], rax 
    var.go:17   0x10cbbac   e82f93f3ff      call $runtime.chansend1         # 开始发送数据

[rip+0x31b01]代表常量3

(dlv) x -fmt hex -count 24 -size 1 0x00000000010fd6a8
0x10fd6a8:   0x03   0x00   0x00   0x00   0x00   0x00   0x00   0x00   
0x10fd6b0:   0x16   0x00   0x00   0x00   0x00   0x00   0x00   0x00   
0x10fd6b8:   0x15   0x00   0x00   0x00   0x00   0x00   0x00   0x00

执行语句:

=>  31:     man := Man{}
    32:     var manImpl IMan
    33:     manImpl = &man

代码对应的汇编语句:

=>  var.go:31   0x10cbc45*  488d05f4160100      ea rax, ptr [rip+0x116f4]                   #指向*_type参数
    var.go:31   0x10cbc4c   48890424        mov qword ptr [rsp], rax                    #
    var.go:31   0x10cbc50   e80b21f4ff      call $runtime.newobject
    var.go:31   0x10cbc55   488b7c2408      mov rdi, qword ptr [rsp+0x8]                #newobject返回值
    var.go:31   0x10cbc5a   4889bc2488000000    mov qword ptr [rsp+0x88], rdi               #使用[rsp+0x88]接收返回值
    var.go:31   0x10cbc62   48c7470800000000    mov qword ptr [rdi+0x8], 0x0                #置零
    var.go:31   0x10cbc6a   833d7f670d0000      cmp dword ptr [runtime.writeBarrier], 0x0   
    var.go:31   0x10cbc71   7405            jz 0x10cbc78
    var.go:31   0x10cbc73   e9bc020000      jmp 0x10cbf34
    var.go:31   0x10cbc78   48c70700000000      mov qword ptr [rdi], 0x0
    var.go:31   0x10cbc7f   90          nop
    var.go:31   0x10cbc80   eb00            jmp 0x10cbc82
    var.go:32   0x10cbc82   0f57c0          xorps xmm0, xmm0
    var.go:32   0x10cbc85   0f11842498000000    movups xmmword ptr [rsp+0x98], xmm0         #本地变量[rsp+0x98]
    var.go:33   0x10cbc8d   488b842488000000    mov rax, qword ptr [rsp+0x88]               #man的地址赋给rax
    var.go:33   0x10cbc95   4889442458      mov qword ptr [rsp+0x58], rax               #
    var.go:33   0x10cbc9a   488d0d17370300      lea rcx, ptr [rip+0x33717]
    var.go:33   0x10cbca1   48898c2498000000    mov qword ptr [rsp+0x98], rcx
    var.go:33   0x10cbca9   48898424a0000000    mov qword ptr [rsp+0xa0], rax               #接口指针.data = &man

函数调用

     5: func main() {
=>   6:     b := 8
     7:     c := callMe(b)
     8:     fmt.Println(c)
     9: }
    10: 
    11: func callMe(a int) int {
    12:     return a + 5
    13:}
// 打印函数地址
(dlv) print &main.callMe
(*)(0x10cbba0)

// 调用函数对应的汇编指令:
=>  func.go:6   0x10cbac1   48c744243808000000  mov qword ptr [rsp+0x38], 0x8
    func.go:7   0x10cbaca   48c7042408000000    mov qword ptr [rsp], 0x8       #rsp作为第一个参数8
    func.go:7   0x10cbad2   e8c9000000      call $main.callMe              
    func.go:7   0x10cbad7   488b442408      mov rax, qword ptr [rsp+0x8]   #rax接收返回值
    func.go:7   0x10cbadc   4889442430      mov qword ptr [rsp+0x30], rax  #本地变量c就是[rsp+0x30]接收返回值  

// 查看main.callMe的函数对应的汇编指令:  
(dlv) disass -a 0x10cbba0 0x10cbbc0
TEXT main.callMe(SB) /Users/ymm/work/mygithub/go-build/code/go/assembly/func/func.go
    func.go:11  0x10cbba0   48c744241000000000  mov qword ptr [rsp+0x10], 0x0   # 返回值
    func.go:12  0x10cbba9   488b442408      mov rax, qword ptr [rsp+0x8]    # 第一个参数
    func.go:12  0x10cbbae   4883c005        add rax, 0x5
    .:0     0x10cbbb2   4889442410      mov qword ptr [rsp+0x10], rax   # 把结果放到返回值中
    .:0     0x10cbbb7   c3          ret                             # 返回
    .:0     0x10cbbb8   0000            add byte ptr [rax], al
    .:0     0x10cbbba   0000            add byte ptr [rax], al
    .:0     0x10cbbbc   0000            add byte ptr [rax], al
    .:0     0x10cbbbe   0000            add byte ptr [rax], al

👉 c/c++常用语句对应的汇编指令

对象调用方法的实现

go对象调用方法实现

package main

import "fmt"

type Man struct {
    Name string
    Age  int
}

func (man *Man) walk() string {
    return man.Name
}

func main() {
    man := Man{Name: "xiaoming", Age: 18}
    man.walk()
}

go语言中string的数据结构为StringHeader,包含数据部分和len两部分。

type StringHeader struct {
    Data uintptr
    Len  int
}

使用dlv调试ls查看代码

    15: func main() {
    16:     man := Man{Name: "xiaoming", Age: 18}
=>  17:     man.walk()
    18: }

对应的汇编代码

(dlv) disass
TEXT main.main(SB) /Users/ymm/work/mygithub/go-build/code/go/assembly/object/object.go
    object.go:15    0x10cbc80   65488b0c2530000000  mov rcx, qword ptr gs:[0x30]
    object.go:15    0x10cbc89   483b6110        cmp rsp, qword ptr [rcx+0x10]
    object.go:15    0x10cbc8d   7655            jbe 0x10cbce4
    object.go:15    0x10cbc8f   4883ec38        sub rsp, 0x38
    object.go:15    0x10cbc93   48896c2430      mov qword ptr [rsp+0x30], rbp
    object.go:15    0x10cbc98   488d6c2430      lea rbp, ptr [rsp+0x30]
    object.go:16    0x10cbc9d   48c744241800000000  mov qword ptr [rsp+0x18], 0x0     #把[rsp+0x18]内容置零
    object.go:16    0x10cbca6   0f57c0          xorps xmm0, xmm0                      
    object.go:16    0x10cbca9   0f11442420      movups xmmword ptr [rsp+0x20], xmm0    
    object.go:16    0x10cbcae   488d0517560200      lea rax, ptr [rip+0x25617]        #加载字符串的地址
    object.go:16    0x10cbcb5   4889442418      mov qword ptr [rsp+0x18], rax     #把字符串的地址赋给Name string部分
    object.go:16    0x10cbcba   48c744242008000000  mov qword ptr [rsp+0x20], 0x8     #把字符串的长度赋给string len部分
    object.go:16    0x10cbcc3   48c744242812000000  mov qword ptr [rsp+0x28], 0x12    #把age赋给结构体man.age福分
=>  object.go:17    0x10cbccc*  488d442418      lea rax, ptr [rsp+0x18]           #把man变量地址加载到rax
    object.go:17    0x10cbcd1   48890424        mov qword ptr [rsp], rax          #把man变量地址加载到rsp,作为第一个参数
    object.go:17    0x10cbcd5   e8c6fdffff      call $main.(*Man).walk            #调用方法walk
    object.go:18    0x10cbcda   488b6c2430      mov rbp, qword ptr [rsp+0x30]
    object.go:18    0x10cbcdf   4883c438        add rsp, 0x38
    object.go:18    0x10cbce3   c3          ret
    object.go:15    0x10cbce4   e83712faff      call $runtime.morestack_noctxt
    .:0     0x10cbce9   eb95            jmp $main.main

查看字符串的内存

// xiaoming 78 69 61 6f 6d 69 6e 67
(dlv) x -fmt hex -count 32 -size 1 0x10f12cc
0x10f12cc:   0x78   0x69   0x61   0x6f   0x6d   0x69   0x6e   0x67   
0x10f12d4:   0x20   0x28   0x66   0x6f   0x72   0x63   0x65   0x64   
0x10f12dc:   0x29   0x20   0x2d   0x3e   0x20   0x6e   0x6f   0x64   
0x10f12e4:   0x65   0x3d   0x20   0x62   0x6c   0x6f   0x63   0x6b

查看walk方法

=>   8: func (man *Man) walk() string {
     9:     return man.Name
    10: }

对应的汇编指令

(dlv) disass
TEXT main.(*Man).walk(SB) /Users/ymm/work/mygithub/go-build/code/go/assembly/object/object.go
=>  object.go:8 0x1067bc0   0f57c0      xorps xmm0, xmm0
    object.go:8 0x1067bc3   0f11442410  movups xmmword ptr [rsp+0x10], xmm0  # 初始化返回值
    object.go:9 0x1067bc8   488b442408  mov rax, qword ptr [rsp+0x8]         #[rsp+0x8]就是传进来的man变量地址
    object.go:9 0x1067bcd   8400        test byte ptr [rax], al              #
    .:0     0x1067bcf   488b08      mov rcx, qword ptr [rax]             #获取man结构体中的string数据
    .:0     0x1067bd2   488b4008    mov rax, qword ptr [rax+0x8]         #获取man结构体中的string长度
    .:0     0x1067bd6   48894c2410  mov qword ptr [rsp+0x10], rcx        #返回值string的数据部分
    .:0     0x1067bdb   4889442418  mov qword ptr [rsp+0x18], rax        #返回值string的len部分
    .:0     0x1067be0   c3      ret

从汇编语言可以看出man.walk()是把man变量作为方法walk参数使用,这样就可以使用结构体man的成员变量。相当于c++中的this

c++对象调用方法实现

#include <iostream>

class Man
{
public:
    char *name;
    int age;

public:
    char *walk();
};

char *Man::walk()
{
    return this->name;
}

int main()
{
    Man man;
    man.name = "xiaoming";
    man.age = 19;
    char *name = man.walk();

    std::cout << "Hello World " << name << std::endl;
}

编译参数为g++ -g -o test test.cpp,使用gdb对test文件进行调试

-exec disass /m
Dump of assembler code for function main():
19  {
   0x00000000004007ef <+0>: push   rbp
   0x00000000004007f0 <+1>: mov    rbp,rsp
   0x00000000004007f3 <+4>: sub    rsp,0x20

20      Man man;
21      man.name = "xiaoming";
   0x00000000004007f7 <+8>: mov    QWORD PTR [rbp-0x20],0x400930 #man变量地址为[rbp-0x20], 也是name的地址 8个字节

22      man.age = 19;
   0x00000000004007ff <+16>:    mov    DWORD PTR [rbp-0x18],0x13     #[rbp-0x18]为man.age成员变量的地址

23      char *name = man.walk(); 
=> 0x0000000000400806 <+23>:    lea    rax,[rbp-0x20]                #[rbp-0x20]为变量man的地址,加载到rax寄存器
   0x000000000040080a <+27>:    mov    rdi,rax                       #把man对象的地址加载到rdi
   0x000000000040080d <+30>:    call   0x4007de <Man::walk()>        #调用方法0x4007de,也就是 <Man::walk()>
   0x0000000000400812 <+35>:    mov    QWORD PTR [rbp-0x8],rax       #walk函数的返回值放到rax中,存储到本地变量[rbp-0x8]中

24  
25      std::cout << "Hello World " << name << std::endl;
   0x0000000000400816 <+39>:    mov    esi,0x400939
   0x000000000040081b <+44>:    mov    edi,0x601060
   0x0000000000400820 <+49>:    call   0x4006c0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x0000000000400825 <+54>:    mov    rdx,QWORD PTR [rbp-0x8]
   0x0000000000400829 <+58>:    mov    rsi,rdx
   0x000000000040082c <+61>:    mov    rdi,rax
   0x000000000040082f <+64>:    call   0x4006c0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
   0x0000000000400834 <+69>:    mov    esi,0x4006e0
   0x0000000000400839 <+74>:    mov    rdi,rax
   0x000000000040083c <+77>:    call   0x4006d0 <_ZNSolsEPFRSoS_E@plt>

26  }   0x0000000000400841 <+82>:   mov    eax,0x0
   0x0000000000400846 <+87>:    leave  
   0x0000000000400847 <+88>:    ret    

End of assembler dump.

man.walk()方法的汇编实现

Dump of assembler code for function Man::walk():
14  {
   0x00000000004007de <+0>: push   rbp
   0x00000000004007df <+1>: mov    rbp,rsp
   0x00000000004007e2 <+4>: mov    QWORD PTR [rbp-0x8],rdi   #跳转到walk方法是,rdi为对象man的内存地址

15      return this->name;
=> 0x00000000004007e6 <+8>: mov    rax,QWORD PTR [rbp-0x8]   #[rbp-0x8]的地址页尾man.name的地址 
   0x00000000004007ea <+12>:    mov    rax,QWORD PTR [rax]       #rax为返回值

16  }
   0x00000000004007ed <+15>:    pop    rbp
   0x00000000004007ee <+16>:    ret 

可以看出golangc++调用方法的汇编实现是一致的,首先把对象作为参数传入方法,然后再去使用对象。

goroutine的汇编实现

package main

import "time"

func main() {
    a := 5
    go func() {
        a = 6
    }()
    
    time.Sleep(time.Second)
}

查看汇编实现

     5: func main() {
     6:     a := 5
     7:     go func() {
     8:         a = 6
=>   9:     }()
    10: 
    11:     time.Sleep(time.Second)
    12: }
(dlv) disass -a 0x106ea00 0x106ea8c
TEXT main.main(SB) /Users/ymm/work/mygithub/go-build/code/go/assembly/goroutine/goroutine.go
    goroutine.go:6  0x106ea00   5c          pop rsp                       
    goroutine.go:6  0x106ea01   7600            jbe 0x106ea03
    goroutine.go:6  0x106ea03   004889          add byte ptr [rax-0x77], cl
    goroutine.go:6  0x106ea06   0424            add al, 0x24
    goroutine.go:6  0x106ea08   e893e0f9ff      call $runtime.newobject
    goroutine.go:6  0x106ea0d   488b442408      mov rax, qword ptr [rsp+0x8]
    goroutine.go:6  0x106ea12   4889442420      mov qword ptr [rsp+0x20], rax
    goroutine.go:6  0x106ea17   48c70005000000      mov qword ptr [rax], 0x5         # a := 5
    goroutine.go:7  0x106ea1e   488b442420      mov rax, qword ptr [rsp+0x20]    # 把a变量地址加载到rax
    goroutine.go:9  0x106ea23   4889442418      mov qword ptr [rsp+0x18], rax    # 把a变量地址加载到[rsp+0x18]
    goroutine.go:7  0x106ea28   c7042408000000      mov dword ptr [rsp], 0x8         # 把8加载到rsp,作为第一个参数
    goroutine.go:7  0x106ea2f   488d0ddac80100      lea rcx, ptr [rip+0x1c8da]       # [rip+0x1c8da]对应匿名函数地址
    goroutine.go:7  0x106ea36   48894c2408      mov qword ptr [rsp+0x8], rcx     # 第二个参数为匿名函数地址 [rsp+0x8] = [rip+0x1c8da]
    goroutine.go:7  0x106ea3b   4889442410      mov qword ptr [rsp+0x10], rax    # 第三个值为a变量的地址 
    goroutine.go:7  0x106ea40   e85b01fdff      call $runtime.newproc            # 调用newproc函数,参数为[rsp]和[rsp+0x8]
    goroutine.go:11 0x106ea45   48c7042400ca9a3b    mov qword ptr [rsp], 0x3b9aca00  
    goroutine.go:11 0x106ea4d   e8ce49ffff      call $time.Sleep
    goroutine.go:12 0x106ea52   488b6c2428      mov rbp, qword ptr [rsp+0x28]
    goroutine.go:12 0x106ea57   4883c430        add rsp, 0x30
    goroutine.go:12 0x106ea5b   c3          ret
    goroutine.go:5  0x106ea5c   0f1f4000        nop dword ptr [rax], eax
    goroutine.go:5  0x106ea60   e8bb5cffff      call $runtime.morestack_noctxt
    .:0     0x106ea65   e976ffffff      jmp $main.main
    .:0     0x106ea6a   cc          int3

go func调用的是匿名函数

// lea rcx, ptr [rip+0x1c8da] 执行后的结果 
Rcx = 0x000000000108b310

// 查看rcx对应的内存值
(dlv) x -fmt hex -count 32 -size 1 0x000000000108b310
0x108b310:   0x80   0xea   0x06   0x01   0x00   0x00   0x00   0x00   
0x108b318:   0xe0   0xf7   0x05   0x01   0x00   0x00   0x00   0x00   
0x108b320:   0x00   0x0c   0x06   0x01   0x00   0x00   0x00   0x00   
0x108b328:   0x20   0xfa   0x03   0x01   0x00   0x00   0x00   0x00 

// 匿名函数地址为`0x106ea80`,查看函数实现, 对应goroutine.go:8也就是 a = 6 
(dlv) disass -a 0x106ea80 0x106ea88
TEXT main.main.func1(SB) /Users/ymm/work/mygithub/go-build/code/go/assembly/goroutine/goroutine.go
    goroutine.go:8  0x106ea80   488b442408  mov rax, qword ptr [rsp+0x8]
    goroutine.go:8  0x106ea85   48      rex.w
    goroutine.go:8  0x106ea86   c7      prefix(0xc7)
    goroutine.go:8  0x106ea87   00      <no instruction>

call $runtime.newproc函数实现如下,最终还是由协程调用匿名函数fn *funcval

// siz 参数个数, fn是协程调用的函数  
func newproc(siz int32, fn *funcval) {
    argp := add(unsafe.Pointer(&fn), sys.PtrSize)
    gp := getg()
    pc := getcallerpc()
    systemstack(func() {
        newg := newproc1(fn, argp, siz, gp, pc)

        _p_ := getg().m.p.ptr()
        runqput(_p_, newg, true)

        if mainStarted {
            wakep()
        }
    })
}

//对应的函数
type funcval struct {
    fn uintptr
    // variable-size, fn-specific data here
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容