目录
  • go简单代码反汇编
  • go语言调用约定分析
    • 1.C/C++调用约定类别
    • 2.go语言调用约定
  • go语言如何实现多返回值的
    • 总结

      go简单代码反汇编

      用简单的代码用以分析go的调用约定及多返回值的返回方式。

      package main
      func vals(c, d int) (a int, b int) {
      	e := 1
      	f := 2
      	a = c + d + e + f
      	b = d * 2
      	return
      }
      func testMutil() {
      	i, j := vals(1, 2)
      	i = i + 1
      	j = j + 1
      }
      func main() {
      	testMutil()
      }
      

      编译go build -gcflags "-N -l" test.go并通过反编译软件获得部分汇编:

      main_vals:
          sub     rsp, 18h
          mov     [rsp+18h+var_8], rbp
          lea     rbp, [rsp+18h+var_8]
          mov     [rsp+18h+arg_10], 0
          mov     [rsp+18h+arg_18], 0
          mov     [rsp+18h+var_10], 1
          mov     [rsp+18h+var_18], 2
          mov     rax, [rsp+18h+arg_0]
          add     rax, [rsp+18h+arg_8]
          add     rax, [rsp+18h+var_10]
          add     rax, 2
          mov     [rsp+18h+arg_10], rax
          mov     rax, [rsp+18h+arg_8]
          shl     rax, 1
          mov     [rsp+18h+arg_18], rax
          mov     rbp, [rsp+18h+var_8]
          add     rsp, 18h
          retn
      main_vals endp
      main_testMutil:
          mov     rcx, gs:28h
          mov     rcx, [rcx+0]
          cmp     rsp, [rcx+10h]
          jbe     short morestack_noctxt
          sub     rsp, 48h
          mov     [rsp+48h+var_8], rbp
          lea     rbp, [rsp+48h+var_8]
          mov     [rsp+48h+var_48], 1
          mov     [rsp+48h+var_40], 2
          call    main_vals
          mov     rax, [rsp+48h+var_38]
          mov     [rsp+48h+var_10], rax
          mov     rax, [rsp+48h+var_30]
          mov     [rsp+48h+var_18], rax
          mov     rax, [rsp+48h+var_10]
          mov     [rsp+48h+var_20], rax
          mov     rax, [rsp+48h+var_18]
          mov     [rsp+48h+var_28], rax
          mov     rax, [rsp+48h+var_20]
          inc     rax
          mov     [rsp+48h+var_20], rax
          mov     rax, [rsp+48h+var_28]
          inc     rax
          mov     [rsp+48h+var_28], rax
          mov     rbp, [rsp+48h+var_8]
          add     rsp, 48h
          retn
      morestack_noctxt:
          call    runtime_morestack_noctxt
      main_testMutil endp
      

      go语言调用约定分析

      1.C/C++调用约定类别

      __stdcall调用约定:函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈。

      _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。注意:对于可变参数的成员函数,始终使用__cdecl的转换方式。

      __fastcall调用约定:它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。

      thiscall仅仅应用于”C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

      naked call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。

      2.go语言调用约定

      sub     rsp, 18h
      mov     [rsp+18h+var_8], rbp
      ...
      mov     rbp, [rsp+18h+var_8]
      add     rsp, 18h
      

      这段代码分别对应栈帧的构造与销毁。
      根据反汇编并且调试,可以发现go语言参数是自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈。所以GO语言符合__stdcall调用约定。

      go语言如何实现多返回值的

      go语言可以返回多个返回值, 但同为编译型语言的C、C++却不支持。

      1.C/C++返回值返回方式。

      C/C++是通过eax/rax(32/64bit)寄存器返回的返回值。

      2.go语言多返回值返回方式

      可以看到vals函数的汇编,通过调试,可知arg_10与arg_18就是返回值a和b, arg_0与arg_8分别是参数c和d。其中

      mov     [rsp+18h+arg_10], rax
      ...
      mov     [rsp+18h+arg_18], rax
      

      分别将参数值返回到参数上。之后在main_testMutil中将参数返回值拷贝到对应局部变量中

      mov     rax, [rsp+48h+var_38]
      mov     [rsp+48h+var_10], rax
      mov     rax, [rsp+48h+var_30]
      mov     [rsp+48h+var_18], rax
      

      这就是go语言多返回值的实现方法了。

      总结

      go语言采用的是__stdcall调用约定。go多返回值是通过栈传递的。将多个返回值先传回参数上,函数栈帧销毁后并不会销毁参数部分(这里用作返回值),再将参数部分进行拷贝然后再参与运算。

      以上就是解析go语言调用约定多返回值实现原理的详细内容,更多关于go调用约定多返回值的资料请关注其它相关文章!

      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。