0%

还是看实际例子更直接,对于这样一份 C 代码:

1
2
3
4
5
6
7
8
9
10
int add (int a, int b) {
return a + b;
}

int main (void) {
int a = 10;
int b = 20;
int c = add(a, b);
return c;
}

先使用 gcc 编译

1
[dou@localhost ~ 0 ]$ gcc -g -O0 hello.c -o hello

然后使用 objdump 生成 Intel 风格的汇编代码
只摘取其中结果中最重要的汇编代码,# 之后的内容为手动加的注释

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
[dou@localhost ~ 0 ]$ objdump -M intel -j .text -d hello

00000000004004ed <add>:
4004ed: 55 push rbp # 将 rbp 寄存器的值压入栈
4004ee: 48 89 e5 mov rbp,rsp # 将 rsp 寄存器的值 移动到 rbp 寄存器,栈底(rbp)移动到原来的栈顶的位置(rsp)
4004f1: 89 7d fc mov DWORD PTR [rbp-0x4],edi # 将 edi 寄存器的值(参数 a),移动到 -0x4(相对于 rbp 的
地址)
4004f4: 89 75 f8 mov DWORD PTR [rbp-0x8],esi # 将 esi 寄存器的值(参数 b),移动到 -0x8(相对于 rbp 的
地址)
4004f7: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8] # 将 -0x8 的值移动到 eax
4004fa: 8b 55 fc mov edx,DWORD PTR [rbp-0x4] # 将 -0x4 的值移动到 edx
4004fd: 01 d0 add eax,edx # eax += edx // a + b;
4004ff: 5d pop rbp # 从栈顶弹出一个值,放到 rbp 里
400500: c3 ret # 从栈顶弹出一个值,放到 rip 里,也就是相当于 pop rip

0000000000400501 <main>:
400501: 55 push rbp # 将 rbp 压入栈
400502: 48 89 e5 mov rbp,rsp # 将 rsp 寄存器的值 移动到 rbp 寄存器,栈底(rbp)移动到原来的栈顶的位置(rsp)
400505: 48 83 ec 10 sub rsp,0x10 # rsp -= 0x10,栈顶向下生长高度 0x10
400509: c7 45 fc 0a 00 00 00 mov DWORD PTR [rbp-0x4],0xa # 将整数 0xa 移动到 -0x4(相对于 rbp) // a = 10
400510: c7 45 f8 14 00 00 00 mov DWORD PTR [rbp-0x8],0x14 # 将整数 0x14 移动到 -0x8(相对于 rbp) // b = 20
400517: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8] # 将 -0x8 移动到 edx
40051a: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] # 将 -0x4 移动到 eax
40051d: 89 d6 mov esi,edx # esi = edx; 为第一个参数寄存器赋值
40051f: 89 c7 mov edi,eax # edi = eax; 为第二个参数寄存器赋值
400521: e8 c7 ff ff ff call 4004ed <add> # 调用函数 add // add(a, b)
400526: 89 45 f4 mov DWORD PTR [rbp-0xc],eax # 将 eax 移动到 -0xc; 返回值入栈
400529: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc] # 将 -0xc 移动到 eax; 准备 main 函数返回值
40052c: c9 leave # 相当于mov rsp, rbp; + pop rbp; 将 rbp 和 rsp 回退到
上一帧
40052d: c3 ret # 从栈顶弹出一个值,放到 rip 里,也就是相当于 pop rip
40052e: 66 90 xchg ax,ax # nop

这里有几个知识点

0> 函数返回值寄存器

函数调用的返回值,会放入 rax 寄存器。

1> 函数参数寄存器

当函数参数少于 6 个的时候,参数从左到右依次放入:rdi, rsi, rdx, rcx, r8, r9
当大于 6 个参数时,剩余的参数从右边往左一次压入栈(取参数的时候,依次弹出,就是自然从左到右的顺序)

2> 函数调用的栈操作

call 相当于 push rip; + jump [address];
ret 相当于 pop rip;

3> callee-saved vs caller-saved

r12, r13, r14, r15, rbx, rbp 是 callee-saved 寄存器。
r10, r11,函数参数、返回值寄存器,都是 caller-saved 寄存器。

rsp 寄存器有一点点特殊,但是严格意义上也属于 callee-saved 寄存器。

豆浆大叔

OpenResty 老司机,MOSN 核心成员

名字来源

“豆浆” 这个外号最早是高中语文送给我的。

当年上高中的时候,语文老师发音又不太标准。在一次课上点名的时候,叫了好几次 “豆浆”,都没人答应,后来喊出全名,才反应过来是我。于是,“豆浆” 这个外号就这么叫开了,我觉得也挺好的,一直沿用至今。

后来上大学的时候,那会流行用微博,“豆浆” 这么大众的名字,肯定是早就被抢注了。

然后那会因为不修边幅,长得又比较成熟,被人嫌弃是大叔,我倒是欣然接受 “大叔” 这个角色,于是就以 “豆浆大叔” 为基础,再加了其他修饰词作为微博名。

而今,已然是货真价实的大叔一枚,”豆浆大叔“ 就显得非常合适了,哈哈。

终于又开始维护博客了。

以前

多年以前维护过一个博客,当时用 wordpress 搭的。
当时也写过几篇水文,只是后面懒得维护了,域名和服务器都没有续费了,汗 …

消失之后,记得有个哥们还想看下上面的一个文章,只是当时也没有备份,也看不了了 …

现在

这次打算长期维护这个博客,域名直接买了十年 …

也换了现在比较流行的 hexo + markdown 来构建静态网站,markdown 就直接在 github 上来保存了。

内容计划

个人空间而已,主要是自己的技术研究心得,或者一些人生感悟。

希望可以保持一个月两篇的节奏。