-
Notifications
You must be signed in to change notification settings - Fork 2
/
asm.html
203 lines (198 loc) · 12.9 KB
/
asm.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<!-- 2021-01-23 Sat 18:03 -->
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>golang汇编基础知识</title>
<meta name="generator" content="Org mode" />
<link rel="stylesheet" type="text/css" href="./css/org-css.css"/>
<link rel="stylesheet" type="text/css" href="./css/gitalk.css"/> <script src="./js/gitalk.min.js"></script>
</head>
<body>
<div id="org-div-home-and-up">
<a accesskey="h" href="index.html"> UP </a>
|
<a accesskey="H" href="index.html"> HOME </a>
</div><div id="content">
<h1 class="title">golang汇编基础知识</h1>
<div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#org45e8e1f">1. golang汇编基础知识</a>
<ul>
<li><a href="#org1264c7d">1.1. 前言</a></li>
<li><a href="#org0c10809">1.2. 寄存器</a></li>
<li><a href="#org790433a">1.3. 跟例子学汇编</a></li>
<li><a href="#org75d01ff">1.4. 第一个例子</a></li>
<li><a href="#org7ff5117">1.5. 第二个例子</a></li>
<li><a href="#org0e0adfb">1.6. 参考资料</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="outline-container-org45e8e1f" class="outline-2">
<h2 id="org45e8e1f"><span class="section-number-2">1</span> golang汇编基础知识</h2>
<div class="outline-text-2" id="text-1">
</div>
<div id="outline-container-org1264c7d" class="outline-3">
<h3 id="org1264c7d"><span class="section-number-3">1.1</span> 前言</h3>
<div class="outline-text-3" id="text-1-1">
<p>
golang的汇编基于plan9汇编,是一个中间汇编方式。这样可以忽略底层不同架构之间的一些差别。汇编主要了解各种寄存器的使用跟寻址方式。根据汇编我们能够一探golang的底层实现。比如内存如何分配,栈如何扩张。接口如何转变。
</p>
</div>
</div>
<div id="outline-container-org0c10809" class="outline-3">
<h3 id="org0c10809"><span class="section-number-3">1.2</span> 寄存器</h3>
<div class="outline-text-3" id="text-1-2">
<p>
各种伪计数器:
</p>
<ul class="org-ul">
<li>FP: Frame pointer: arguments and locals.(指向当前栈帧)</li>
<li>PC: Program counter: jumps and branches.(指向指令地址)</li>
<li>SB: Static base pointer: global symbols.(指向全局符号表)</li>
<li>SP: Stack pointer: top of stack.(指向当前栈顶部)</li>
</ul>
<p>
注意: 栈是向下整长
golang的汇编是调用者维护参数返回值跟返回地址。所以FP的值小于参数跟返回值。
</p>
</div>
</div>
<div id="outline-container-org790433a" class="outline-3">
<h3 id="org790433a"><span class="section-number-3">1.3</span> 跟例子学汇编</h3>
<div class="outline-text-3" id="text-1-3">
</div>
<div id="outline-container-org75d01ff" class="outline-3">
<h3 id="org75d01ff"><span class="section-number-3">1.4</span> 第一个例子</h3>
<div class="outline-text-3" id="text-1-4">
<div class="org-src-container">
<pre class="src src-go"><span style="color: #89DDFF;">package</span> main
<span style="color: #676E95;">//</span><span style="color: #676E95;">go:noinline</span>
<span style="color: #89DDFF;">func</span> <span style="color: #82aaff;">add</span>(<span style="color: #ffcb6b;">a</span>, <span style="color: #ffcb6b;">b</span> <span style="color: #c792ea;">int32</span>) (<span style="color: #c792ea;">int32</span>, <span style="color: #c792ea;">bool</span>) {
<span style="color: #89DDFF;">return</span> a + b, <span style="color: #f78c6c;">true</span>
}
<span style="color: #89DDFF;">func</span> <span style="color: #82aaff;">main</span>() {
<span style="color: #82aaff;">add</span>(10, 32)
}
</pre>
</div>
<p>
如上的这段代码会被编译为以下汇编:
</p>
<div class="org-src-container">
<pre class="src src-asm"><span style="color: #c3e88d;">""</span>.add STEXT nosplit size=20 args=0x10 locals=0x0
<span style="color: #89DDFF;">0x0000</span> 00000 (call.go:4) TEXT <span style="color: #c3e88d;">""</span>.add(SB), NOSPLIT, $0-16
<span style="color: #89DDFF;">0x0000</span> 00000 (call.go:4) FUNCDATA $0, gclocals·f207267fbf96a0178e8758c6e3e0ce28(SB)
<span style="color: #89DDFF;">0x0000</span> 00000 (call.go:4) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
<span style="color: #89DDFF;">0x0000</span> 00000 (call.go:4) MOVL <span style="color: #c3e88d;">""</span>.b+12(SP), AX
<span style="color: #89DDFF;">0x0004</span> 00004 (call.go:4) MOVL <span style="color: #c3e88d;">""</span>.a+8(SP), CX
<span style="color: #89DDFF;">0x0008</span> 00008 (call.go:5) ADDL CX, AX
<span style="color: #89DDFF;">0x000a</span> 00010 (call.go:5) MOVL AX, <span style="color: #c3e88d;">""</span>.~r2+16(SP)
<span style="color: #89DDFF;">0x000e</span> 00014 (call.go:5) MOVB $1, <span style="color: #c3e88d;">""</span>.~r3+20(SP)
<span style="color: #89DDFF;">0x0013</span> 00019 (call.go:5) RET
<span style="color: #89DDFF;">0x0000</span> 8b 44 24 0c 8b 4c 24 08 01 c8 89 44 24 10 c6 44 .D$..L$....D$..D
<span style="color: #89DDFF;">0x0010</span> 24 14 01 c3 $...
</pre>
</div>
<p>
<code>TEXT "".add(SB), NOSPLIT, $0-16</code> 这行代码声明了一个add函数,NOSPLIT告诉编译器不要插入分裂栈检查点。这里可以不用检查栈的大小是因为这里add函数的栈大小为0. $0 表示栈大小为0, $16 表示参数大小为16个字节。
<code>FUNCDATA</code> 跟垃圾回收有关,暂时不用理会
SP 指向当前栈顶部 .a+8(SP) 之前的.a表示助记符,没有特别含义。8(SP)表示地址SP+8 这里是第一参数a, 因为当前栈为0且SP+0存储了函数返回地址。同理12(SP) 表示第二个参数b。16(SP)表示第一个返回值,20(SP)表示第二个返回值。
</p>
</div>
</div>
<div id="outline-container-org7ff5117" class="outline-3">
<h3 id="org7ff5117"><span class="section-number-3">1.5</span> 第二个例子</h3>
<div class="outline-text-3" id="text-1-5">
<div class="org-src-container">
<pre class="src src-go"><span style="color: #89DDFF;">package</span> main
<span style="color: #676E95;">//</span><span style="color: #676E95;">go:noinline</span>
<span style="color: #89DDFF;">func</span> <span style="color: #82aaff;">add</span>(<span style="color: #ffcb6b;">a</span>, <span style="color: #ffcb6b;">b</span> <span style="color: #c792ea;">int32</span>) (<span style="color: #c792ea;">int32</span>, <span style="color: #c792ea;">bool</span>) {
<span style="color: #89DDFF;">return</span> a + b, <span style="color: #f78c6c;">true</span>
}
<span style="color: #676E95;">//</span><span style="color: #676E95;">go:noinline</span>
<span style="color: #89DDFF;">func</span> <span style="color: #82aaff;">callAdd</span>() <span style="color: #c792ea;">int32</span> {
<span style="color: #ffcb6b;">a</span>, <span style="color: #ffcb6b;">_</span> := <span style="color: #82aaff;">add</span>(10, 20)
<span style="color: #89DDFF;">return</span> a
}
<span style="color: #89DDFF;">func</span> <span style="color: #82aaff;">main</span>() {
<span style="color: #82aaff;">callAdd</span>()
}
</pre>
</div>
<div class="org-src-container">
<pre class="src src-asm"><span style="color: #c3e88d;">""</span>.callAdd STEXT size=73 args=0x8 locals=0x18
<span style="color: #82aaff;">0x0000</span> <span style="color: #89DDFF;">00000</span> (call.go:9) TEXT <span style="color: #c3e88d;">""</span>.callAdd(SB), $24-8
<span style="color: #676E95;">; </span><span style="color: #676E95;">这三行代码检查是否需要扩展栈, 需要的话,跳到0x0042</span>
<span style="color: #82aaff;">0x0000</span> <span style="color: #89DDFF;">00000</span> (call.go:9) MOVQ (TLS), CX
<span style="color: #82aaff;">0x0009</span> <span style="color: #89DDFF;">00009</span> (call.go:9) CMPQ SP, 16(CX)
<span style="color: #82aaff;">0x000d</span> <span style="color: #89DDFF;">00013</span> (call.go:9) JLS 66
<span style="color: #82aaff;">0x000f</span> <span style="color: #89DDFF;">00015</span> (call.go:9) SUBQ $24, SP
<span style="color: #82aaff;">0x0013</span> <span style="color: #89DDFF;">00019</span> (call.go:9) MOVQ BP, 16(SP)
<span style="color: #82aaff;">0x0018</span> <span style="color: #89DDFF;">00024</span> (call.go:9) LEAQ 16(SP), BP
<span style="color: #82aaff;">0x001d</span> <span style="color: #89DDFF;">00029</span> (call.go:9) FUNCDATA $0, gclocals·2a5305abe05176240e61b8620e19a815(SB)
<span style="color: #82aaff;">0x001d</span> <span style="color: #89DDFF;">00029</span> (call.go:9) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
<span style="color: #82aaff;">0x001d</span> <span style="color: #89DDFF;">00029</span> (call.go:10) MOVQ $85899345930, AX
<span style="color: #82aaff;">0x0027</span> <span style="color: #89DDFF;">00039</span> (call.go:10) MOVQ AX, (SP)
<span style="color: #82aaff;">0x002b</span> <span style="color: #89DDFF;">00043</span> (call.go:10) PCDATA $0, $0
<span style="color: #82aaff;">0x002b</span> <span style="color: #89DDFF;">00043</span> (call.go:10) CALL <span style="color: #c3e88d;">""</span>.add(SB)
<span style="color: #82aaff;">0x0030</span> <span style="color: #89DDFF;">00048</span> (call.go:10) MOVL 8(SP), AX
<span style="color: #82aaff;">0x0034</span> <span style="color: #89DDFF;">00052</span> (call.go:11) MOVL AX, <span style="color: #c3e88d;">""</span>.~r0+32(SP)
<span style="color: #82aaff;">0x0038</span> <span style="color: #89DDFF;">00056</span> (call.go:11) MOVQ 16(SP), BP
<span style="color: #82aaff;">0x003d</span> <span style="color: #89DDFF;">00061</span> (call.go:11) ADDQ $24, SP
<span style="color: #82aaff;">0x0041</span> <span style="color: #89DDFF;">00065</span> (call.go:11) RET
<span style="color: #676E95;">; </span><span style="color: #676E95;">扩展栈, 扩展完了后跳到最开始</span>
<span style="color: #82aaff;">0x0042</span> <span style="color: #89DDFF;">00066</span> (call.go:11) NOP
<span style="color: #82aaff;">0x0042</span> <span style="color: #89DDFF;">00066</span> (call.go:9) PCDATA $0, $-1
<span style="color: #82aaff;">0x0042</span> <span style="color: #89DDFF;">00066</span> (call.go:9) CALL runtime.morestack_noctxt(SB)
<span style="color: #82aaff;">0x0047</span> <span style="color: #89DDFF;">00071</span> (call.go:9) JMP 0
</pre>
</div>
<p>
现在我们添加一层函数调用,看看生成后的样子, 主要关注callAdd函数。在这个例子中我们看到这里的栈大小是24byte(2个参数2个返回值(bool 对齐到4字节, 4 * 4 = 16 byte),再加上一个8byte的bp),参数是8byte(对齐到8byte)。
我们从0x000f说起,前面那段代码主要用于分裂栈检查。
0x000f 把栈减了24个字节。增大了栈空间。(向下增长)
0x0013-0x0018保存老的bp设置新的bp。这里的bp是真实的寄存器,这几行代码属于惯用法。在汇编中可以用bp来引用参数跟本地变量。
0x001d 是10跟20合起来的(八字节)二进制表示。本来需要两次move,现在只需要一次move,也算个小优化了吧。
接下来就是调用add方法,移动返回值。虽然我们没有使用add的第二个返回值,但是我们也要为他分配内存。
0x0038恢复BP寄存器,0x003d缩减栈空间.
RET指令加载返回地址,跳到返回地址。
</p>
</div>
</div>
</div>
<div id="outline-container-org0e0adfb" class="outline-3">
<h3 id="org0e0adfb"><span class="section-number-3">1.6</span> 参考资料</h3>
<div class="outline-text-3" id="text-1-6">
<ul class="org-ul">
<li><a href="https://lrita.github.io/2017/12/12/golang-asm/">https://lrita.github.io/2017/12/12/golang-asm/</a></li>
<li><a href="https://lrita.github.io/images/posts/go/GoFunctionsInAssembly.pdf">https://lrita.github.io/images/posts/go/GoFunctionsInAssembly.pdf</a></li>
<li><a href="https://github.com/go-internals-cn/go-internals">https://github.com/go-internals-cn/go-internals</a></li>
<li><a href="https://golang.org/doc/asm">https://golang.org/doc/asm</a></li>
<li><a href="https://9p.io/sys/doc/asm.html">https://9p.io/sys/doc/asm.html</a></li>
</ul>
</div>
</div>
</div>
</div>
<div id="postamble" class="status">
<div id="gitalk" /> <script> var gitalk = new Gitalk({
clientID: 'f30e66bb5ab9089aa742',
clientSecret: '5d256d445447bd4db16540c2aab0e0884218ed12',
repo: 'guidao.github.io',
owner: 'guidao',
admin: ['guidao'],
id: location.pathname, // Ensure uniqueness and length less than 50
distractionFreeMode: false // Facebook-like distraction free mode
})
gitalk.render('gitalk') </script>
</div>
</body>
</html>