.... to be continued

Golang中很多代码实现,例如strings.Index(),调度器以及初始化等等都是用汇编实现的,因此需要对汇编有基本的了解。同时本文只专注于 AMD64 Linux 平台下 AT&T 格式的汇编指令。另外本文主要是作为一个学习笔记和总结,很多地方会引用参考文献中的内容。

基础知识

程序的存储空间布局

Programsource fileobject fileexecutable moduleprogram image

进程(Process)是一个正在执行的程序实例(instance)。每个实例有自己的地址空间和执行状态。当操作系统向内核数据结构中添加了适当的信息,并为运行程序代码分配必要的资源之后,程序就变成了进程。

a thread of execution within a process
BSS 段data段text段
BSS段(未初始化数据区)bss segmentBlock Started by Symbol数据段data segment代码段text segment
sizesection sizetotal size-ASystem VBerkeley
(ENV) [root@ceph-2 ~]# size bazil
   text    data     bss     dec     hex filename
8373080  315568  144968 8833616  86ca50 bazil
栈区堆区
  • 栈区:由编译器自动释放,存放函数的参数值、局部变量等。每当一个函数被调用时,该函数的返回类型和一些调用的信息被存放到栈中。然后这个被调用的函数再为他的自动变量和临时变量在栈上分配空间。每调用一个函数一个新的栈就会被使用。栈区是从高地址位向低地址位增长的,是一块连续的内存区域,最大容量是由系统预先定义好的,申请的栈空间超过这个界限时会提示溢出,用户能从栈中获取的空间较小。
  • 堆区:用于动态分配内存,位于BSS和栈中间的地址区域。由程序员申请分配和释放。堆是从低地址位向高地址位增长,采用链式存储结构。频繁的 malloc/free造成内存空间的不连续,产生碎片。当申请堆空间时库函数是按照一定的算法搜索可用的足够大的空间。因此堆的效率比栈要低的多。

指令集

可以通过如下的命令查询CPU支持的指令集。

(ENV) [root@ceph-2 ~]# cat /proc/cpuinfo | grep flags
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss ht syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts nopl xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq vmx ssse3 cx16 pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx hypervisor lahf_lm tpr_shadow vnmi ept vpid tsc_adjust dtherm arat pln pts
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss ht syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts nopl xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq vmx ssse3 cx16 pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx hypervisor lahf_lm tpr_shadow vnmi ept vpid tsc_adjust dtherm arat pln pts
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss ht syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts nopl xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq vmx ssse3 cx16 pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx hypervisor lahf_lm tpr_shadow vnmi ept vpid tsc_adjust dtherm arat pln pts
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss ht syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts nopl xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq vmx ssse3 cx16 pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx hypervisor lahf_lm tpr_shadow vnmi ept vpid tsc_adjust dtherm arat pln pts
汇编知识介绍

AT&T 汇编指令的基本格式为:

操作码 [操作数]

如果操作数有两个,则第一个为源操作数,第二个为目的操作数,目的操作数表示这条指令执行完后结果应该保存的地方。
对于 AT&T 格式的汇编指令,一些说明如下:

%$offset(%register)(%register)bwlq1248

寄存器

应用层代码一般会用到三类 19 个寄存器:

通用寄存器rbprsp程序计数寄存器rip段寄存器fsgs线程本地存储(TLS)
伪寄存器
FP(Frame pointer)PC(Program counter)SB(Static base pointer)SP(Stack pointer)

Plan 9汇编

LEAMOV
LEAQ 8(SP), SI // argv 把 8(SP)地址放入 SI 寄存器中
MOVQ 0(SP), DI // argc 把0(SP)内容放入 DI 寄存器中

Go Assembler

(ENV) 🍺 /Users/xsky/go/src/github.com/microyahoo/go-exercises ☞ git:(master) ✗ go tool compile -S x.go
os.(*File).close STEXT dupok nosplit size=26 args=0x18 locals=0x0
    0x0000 00000 (<autogenerated>:1)    TEXT    os.(*File).close(SB), DUPOK|NOSPLIT|ABIInternal, $0-24
    0x0000 00000 (<autogenerated>:1)    FUNCDATA    $0, gclocals·e6397a44f8e1b6e77d0f200b4fba5269(SB)
    0x0000 00000 (<autogenerated>:1)    FUNCDATA    $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
    0x0000 00000 (<autogenerated>:1)    FUNCDATA    $2, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
    0x0000 00000 (<autogenerated>:1)    PCDATA  $0, $1
    0x0000 00000 (<autogenerated>:1)    PCDATA  $1, $1
    0x0000 00000 (<autogenerated>:1)    MOVQ    ""..this+8(SP), AX
    0x0005 00005 (<autogenerated>:1)    MOVQ    (AX), AX
    0x0008 00008 (<autogenerated>:1)    PCDATA  $0, $0
    0x0008 00008 (<autogenerated>:1)    PCDATA  $1, $0
    0x0008 00008 (<autogenerated>:1)    MOVQ    AX, ""..this+8(SP)
    0x000d 00013 (<autogenerated>:1)    XORPS   X0, X0
    0x0010 00016 (<autogenerated>:1)    MOVUPS  X0, "".~r0+16(SP)
    0x0015 00021 (<autogenerated>:1)    JMP os.(*file).close(SB)
(ENV) 🍺 /Users/xsky/go/src/github.com/microyahoo/go-exercises ☞ git:(master) ✗ go build -o test_map2 test_map2.go
(ENV) 🍺 /Users/xsky/go/src/github.com/microyahoo/go-exercises ☞ git:(master) ✗ go tool objdump -s main.main test_map2
TEXT main.main(SB) /Users/xsky/go/src/github.com/microyahoo/go-exercises/test_map2.go
  test_map2.go:7    0x1099850       65488b0c2530000000  MOVQ GS:0x30, CX
  test_map2.go:7    0x1099859       488d442490      LEAQ -0x70(SP), AX
  test_map2.go:7    0x109985e       483b4110        CMPQ 0x10(CX), AX
  test_map2.go:7    0x1099862       0f8645030000        JBE 0x1099bad
  test_map2.go:7    0x1099868       4881ecf0000000      SUBQ $0xf0, SP
  test_map2.go:7    0x109986f       4889ac24e8000000    MOVQ BP, 0xe8(SP)
  test_map2.go:7    0x1099877       488dac24e8000000    LEAQ 0xe8(SP), BP
  test_map2.go:8    0x109987f       e88c28f7ff      CALL runtime.makemap_small(SB)
  test_map2.go:8    0x1099884       488b0424        MOVQ 0(SP), AX
  test_map2.go:8    0x1099888       4889442440      MOVQ AX, 0x40(SP)
  test_map2.go:9    0x109988d       488d0dcc890100      LEAQ runtime.types+100000(SB), CX
  test_map2.go:9    0x1099894       48890c24        MOVQ CX, 0(SP)
  test_map2.go:9    0x1099898       4889442408      MOVQ AX, 0x8(SP)
  test_map2.go:9    0x109989d       48c744241001000000  MOVQ $0x1, 0x10(SP)
  test_map2.go:9    0x10998a6       e8255af7ff      CALL runtime.mapassign_fast64(SB)
  test_map2.go:9    0x10998ab       488b7c2418      MOVQ 0x18(SP), DI
  test_map2.go:9    0x10998b0       48c7470801000000    MOVQ $0x1, 0x8(DI)
  test_map2.go:9    0x10998b8       833d21b20f0000      CMPL $0x0, runtime.writeBarrier(SB)
  test_map2.go:9    0x10998bf       0f85d7020000        JNE 0x1099b9c
  test_map2.go:9    0x10998c5       488d050f700300      LEAQ go.string.*+27(SB), AX
  test_map2.go:9    0x10998cc       488907          MOVQ AX, 0(DI)
  test_map2.go:10   0x10998cf       488d058a890100      LEAQ runtime.types+100000(SB), AX
  test_map2.go:10   0x10998d6       48890424        MOVQ AX, 0(SP)
  test_map2.go:10   0x10998da       488b4c2440      MOVQ 0x40(SP), CX
  test_map2.go:10   0x10998df       48894c2408      MOVQ CX, 0x8(SP)
  test_map2.go:10   0x10998e4       48c744241002000000  MOVQ $0x2, 0x10(SP)
  test_map2.go:10   0x10998ed       e8de59f7ff      CALL runtime.mapassign_fast64(SB)
dlv
(ENV) 🍺 /Users/xsky/go/src/github.com/microyahoo/go-exercises ☞ git:(master) ✗ dlv exec ./test_map2
Type 'help' for list of commands.
(dlv) l
Stopped at: 0xeb8a19c
=>no source available
(dlv) regs
       rax = 0x0000000000000000
       rbx = 0x0000000000000000
       rcx = 0x0000000000000000
       rdx = 0x0000000000000000
       rdi = 0x0000000000000000
       rsi = 0x0000000000000000
       rbp = 0x0000000000000000
       rsp = 0x00007ffeefbff7e0
        r8 = 0x0000000000000000
        r9 = 0x0000000000000000
       r10 = 0x0000000000000000
       r11 = 0x0000000000000000
       r12 = 0x0000000000000000
       r13 = 0x0000000000000000
       r14 = 0x0000000000000000
       r15 = 0x0000000000000000
       rip = 0x000000000eb8a19c
    rflags = 0x0000000000000200
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000
     fctrl = 0x037f
     fstat = 0x0000
      ftag = 0x0000
       fop = 0x0000
     fioff = 0x00000000
     fiseg = 0x0000
     fooff = 0x00000000
     foseg = 0x0000
     mxcsr = 0x00001f80 [RZ/RN=0 PM UM OM ZM DM IM]
 mxcsrmask = 0x0000ffff
    trapno = 0x00000000
       err = 0x00000000
faultvaddr = 0x0000000000000000
(dlv) b main.main
Breakpoint 1 set at 0x1099868 for main.main() ./test_map2.go:7
(dlv) c
> main.main() ./test_map2.go:7 (hits goroutine(1):1 total:1) (PC: 0x1099868)
Warning: debugging optimized function
     2:
     3: import (
     4:     "fmt"
     5: )
     6:
=>   7: func main() {
     8:     a := make(map[int]string)
     9:     a[1] = "a"
    10:     a[2] = "b"
    11:     b := get(a)
    12:     fmt.Println(b)
(dlv) regs
       rax = 0x000000c00006cee8
       rbx = 0x0000000000000000
       rcx = 0x000000c000000180
       rdx = 0x00000000010d7f10
       rdi = 0x000000c0000101f0
       rsi = 0x0000000000000001
       rbp = 0x000000c00006cfd0
       rsp = 0x000000c00006cf58
        r8 = 0x0000000000000011
        r9 = 0x0000000000000011
       r10 = 0x00000000010e88b0
       r11 = 0x0000000000000001
       r12 = 0xffffffffffffffff
       r13 = 0x0000000000000020
       r14 = 0x000000000000001f
       r15 = 0x0000000000000200
       rip = 0x0000000001099868
    rflags = 0x0000000000000206
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000
     fctrl = 0x037f
     fstat = 0x0000
      ftag = 0x0000
       fop = 0x0000
     fioff = 0x00000000
     fiseg = 0x0000
     fooff = 0x00000000
     foseg = 0x0000
     mxcsr = 0x00001fa0 [RZ/RN=0 PM UM OM ZM DM IM PE]
 mxcsrmask = 0x0000ffff
    trapno = 0x00000001
       err = 0x00000000
faultvaddr = 0x00000000011b5000
References