汇编相关知识

1. 概述

本文总结了一些汇编相关的基础知识。

2. 详述

2.1. 基础知识

  • 帧指针(FP,frame pointer)寄存器,其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。
  • 栈指针(SP,stack pointer)寄存器,其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
  • 基指针(BP,base pointer)寄存器,其内存中存放着一个指针,该指针指向当前栈帧的底部。
  • 函数栈帧:SP和BP之间的内存空间为当前栈帧,BP标识了当前栈帧的底部,SP标识了当前栈帧的顶部。
  • 指令指针(IP,instruction pointer)寄存器, 其内存放着一个指针,该指针永远指向下一条待执行的指令地址。

2.2. 函数调用的步骤

  • 参数入栈:将参数从右向左依次压入系统栈中
  • 返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行
  • 代码区跳转:处理器从当前代码区跳转到被调用函数的入口处
  • 栈帧调整:具体包括:(1)保存当前栈帧状态值,以备后面恢复本栈帧时使用(EBP入栈)。(2)将当前栈帧切换到新栈帧(将ESP值装入EBP,更新栈帧底部)。(3)给新栈帧分配空间(把ESP减去所需空间的大小,抬高栈顶)。

以如下的C语言程序为例,看一下汇编语言中的过程。C语言代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

int func(int a, int b, int c)
{
a = 100;
b = 200;
c = 300;
printf("%d\n", a);

return a;
}

int main(int argc, char *argv[])
{
func(1, 2, 3);
return 0;
}

汇编代码如下:

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
0000000000000000 <func>:
0: 55 push %rbp ;将BP压栈。
1: 48 89 e5 mov %rsp,%rbp ;用SP的值覆盖BP。
4: 48 83 ec 10 sub $0x10,%rsp ;SP向低地址偏移,开栈,这里开栈16B。
8: 89 7d fc mov %edi,-0x4(%rbp) ;根据BP向负方向偏移,获取三个参数存储到寄存器。
b: 89 75 f8 mov %esi,-0x8(%rbp)
e: 89 55 f4 mov %edx,-0xc(%rbp)
11: c7 45 fc 64 00 00 00 movl $0x64,-0x4(%rbp)
18: c7 45 f8 c8 00 00 00 movl $0xc8,-0x8(%rbp)
1f: c7 45 f4 2c 01 00 00 movl $0x12c,-0xc(%rbp)
26: 8b 45 fc mov -0x4(%rbp),%eax
29: 89 c6 mov %eax,%esi ;根据程序,将第一个参数移到返回值寄存器。
2b: bf 00 00 00 00 mov $0x0,%edi
30: b8 00 00 00 00 mov $0x0,%eax
35: e8 00 00 00 00 callq 3a <func+0x3a>
3a: 8b 45 fc mov -0x4(%rbp),%eax
3d: c9 leaveq
3e: c3 retq

000000000000003f <main>:
3f: 55 push %rbp ;将BP压栈
40: 48 89 e5 mov %rsp,%rbp ;用SP的值覆盖BP
43: 48 83 ec 10 sub $0x10,%rsp ;SP向低地址偏移,开栈。这里开栈16B。
47: 89 7d fc mov %edi,-0x4(%rbp) ;
4a: 48 89 75 f0 mov %rsi,-0x10(%rbp)
4e: ba 03 00 00 00 mov $0x3,%edx ;通过寄存器传参,存储三个参数到edx、esi、edi三个寄存器中。
53: be 02 00 00 00 mov $0x2,%esi
58: bf 01 00 00 00 mov $0x1,%edi
5d: e8 00 00 00 00 callq 62 <main+0x23> ;调用func函数。
62: b8 00 00 00 00 mov $0x0,%eax ;将0存储到eax寄存器中。
67: c9 leaveq
68: c3 retq