我爱编程

2018-08-08

2018-08-08  本文已影响0人  myonlyzzy

go 汇编

编译一个最简单的go执行程序

package main

import  "fmt"

func main(){
        fmt.Println("helloworld")
}

go build -gcflags "-N -l" test.go

使用go tool objdump 反汇编

go tool objdump test >test.asm

gdb test

goasm1.png

我们看到汇编的入口地址位于0x452100,打开test.asm

TEXT _rt0_amd64_linux(SB) /usr/local/go/src/runtime/rt0_linux_amd64.s
  rt0_linux_amd64.s:8   0x452100        488d742408      LEAQ 0x8(SP), SI    
  rt0_linux_amd64.s:9   0x452105        488b3c24        MOVQ 0(SP), DI      
  rt0_linux_amd64.s:10  0x452109        488d0510000000      LEAQ 0x10(IP), AX   
  rt0_linux_amd64.s:11  0x452110        ffe0            JMP AX          

可以看到入口为rto_linux_amd64.s (当然了不同平台的入口文件肯定不一样),

rto_linux_amd64.s

#include "textflag.h"

TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
   LEAQ   8(SP), SI // argv
   MOVQ   0(SP), DI // argc
   MOVQ   $main(SB), AX
   JMP    AX

// When building with -buildmode=c-shared, this symbol is called when the shared
// library is loaded.
// Note: This function calls external C code, which might required 16-byte stack
// alignment after cmd/internal/obj applies its transformations.
TEXT _rt0_amd64_linux_lib(SB),NOSPLIT,$0x50
   MOVQ   SP, AX
   ANDQ   $-16, SP
   MOVQ   BX, 0x10(SP)
   MOVQ   BP, 0x18(SP)
   MOVQ   R12, 0x20(SP)
   MOVQ   R13, 0x28(SP)
   MOVQ   R14, 0x30(SP)
   MOVQ   R15, 0x38(SP)
   MOVQ   AX, 0x40(SP)

   MOVQ   DI, _rt0_amd64_linux_lib_argc<>(SB)
   MOVQ   SI, _rt0_amd64_linux_lib_argv<>(SB)

   // Synchronous initialization.
   MOVQ   $runtime·libpreinit(SB), AX
   CALL   AX

   // Create a new thread to do the runtime initialization and return.
   MOVQ   _cgo_sys_thread_create(SB), AX
   TESTQ  AX, AX
   JZ nocgo
   MOVQ   $_rt0_amd64_linux_lib_go(SB), DI
   MOVQ   $0, SI
   CALL   AX
   JMP    restore

nocgo:
   MOVQ   $8388608, 0(SP)                    // stacksize
   MOVQ   $_rt0_amd64_linux_lib_go(SB), AX
   MOVQ   AX, 8(SP)                          // fn
   MOVQ   $runtime·newosproc0(SB), AX
   CALL   AX

restore:
   MOVQ   0x10(SP), BX
   MOVQ   0x18(SP), BP
   MOVQ   0x20(SP), R12
   MOVQ   0x28(SP), R13
   MOVQ   0x30(SP), R14
   MOVQ   0x38(SP), R15
   MOVQ   0x40(SP), SP
   RET

TEXT _rt0_amd64_linux_lib_go(SB),NOSPLIT,$0
   MOVQ   _rt0_amd64_linux_lib_argc<>(SB), DI
   MOVQ   _rt0_amd64_linux_lib_argv<>(SB), SI
   MOVQ   $runtime·rt0_go(SB), AX
   JMP    AX

DATA _rt0_amd64_linux_lib_argc<>(SB)/8, $0
GLOBL _rt0_amd64_linux_lib_argc<>(SB),NOPTR, $8
DATA _rt0_amd64_linux_lib_argv<>(SB)/8, $0
GLOBL _rt0_amd64_linux_lib_argv<>(SB),NOPTR, $8

TEXT main(SB),NOSPLIT,$-8
   MOVQ   $runtime·rt0_go(SB), AX
   JMP    AX

上面的是go1.9.2 的rto_linux_amd64.s 文件内容.

但是我对汇编完全是半懂,只能半看半猜了,更何况这种go的汇编指令有些以前都没有看到过.好在有伟大的google,另外gdb里面的汇编单步调试提供了很多方便。

TEXT rt0amd64_linux(SB),NOSPLIT,$-8

   LEAQ   8(SP), SI // argv

   MOVQ   0(SP), DI // argc

   MOVQ   $main(SB), AX

   JMP    AX
   
TEXT main(SB),NOSPLIT,$-8

   MOVQ   $runtime·rt0_go(SB), AX

   JMP    AX

这是rto_linux_asm64.s 的前几行,第4行跳到了main(SB)这里. main(SB)有跳到了runtime·rt0_go(SB) 但是runtime·rt0_go(SB)在那里呢。这里有2种方式可以找到,一种我们可以搜索runtime 目录下的所有汇编文件。另外一种方式是使用gdb的汇编单步调试看它跳到那里

这是使用gdb单步调试的内容,关于gdb汇编单步调试就2指令ni si.

Breakpoint 1, _rt0_amd64_linux () at /usr/local/go/src/runtime/rt0_linux_amd64.s:8
8               LEAQ    8(SP), SI // argv
(gdb) ni
9               MOVQ    0(SP), DI // argc
(gdb) ni

Breakpoint 2, _rt0_amd64_linux () at /usr/local/go/src/runtime/rt0_linux_amd64.s:10
10              MOVQ    $main(SB), AX
(gdb) ni
11              JMP     AX
(gdb) ni
Stopped due to shared library event
(gdb) si
74              JMP     AX
(gdb) si
runtime.rt0_go () at /usr/local/go/src/runtime/asm_amd64.s:12
12              MOVQ    DI, AX          // argc
(gdb) ni
13              MOVQ    SI, BX          // argv
(gdb) 

我们看到是跳到了asm_amd64.s:12的12行执行,这里的代码我们可以通过旁边的注释大概了解做了什么事情

    // create istack out of the given (operating system) stack.
    // _cgo_init may update stackguard.
    MOVQ    $runtime·g0(SB), DI
    LEAQ    (-64*1024+104)(SP), BX
    MOVQ    BX, g_stackguard0(DI)
    MOVQ    BX, g_stackguard1(DI)
    MOVQ    BX, (g_stack+stack_lo)(DI)
    MOVQ    SP, (g_stack+stack_hi)(DI)

    // find out information about the processor we're on
    MOVL    $0, AX
    CPUID
    MOVL    AX, SI
    CMPL    AX, $0
    JE  nocpuinfo
    
    
    MOVL    16(SP), AX      // copy argc
    MOVL    AX, 0(SP)
    MOVQ    24(SP), AX      // copy argv
    MOVQ    AX, 8(SP)
    CALL    runtime·args(SB)
    CALL    runtime·osinit(SB)
    CALL    runtime·schedinit(SB)

主要就是获取初始化的一些事情,包括runtime.osinit runtime.schedinit 等.

    // create a new goroutine to start program
    MOVQ    $runtime·mainPC(SB), AX     // entry
    PUSHQ   AX
    PUSHQ   $0          // arg size
    CALL    runtime·newproc(SB)
    POPQ    AX
    POPQ    AX

这里把main函数入栈,然后调用newproc创建协程.

小结

这篇主要介绍了使用go tool he gdb简单的分析golang程序的启动引导过程,一些细节例如 newproc和mstart 在后面的文章中介绍.

上一篇下一篇

猜你喜欢

热点阅读