.... 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