Contents
  1. 1. 常用的三个和栈有关的寄存器:
  2. 2. 栈和寄存器的关系:
  3. 3. 原函数

常用的三个和栈有关的寄存器:

1.EIP

2.ESP

3.EBP

当调用fun函数开始时,三者的作用。

1.EIP寄存器里存储的是CPU下次要执行的指令的地址。

也就是调用完fun函数后,让CPU知道应该执行main函数中的printf(”函数调用结束”)语句了。

2.EBP寄存器里存储的是是栈的栈底指针,通常叫栈基址,这个是一开始进行fun()函数调用之前,由ESP传递给EBP的。(在函数调用前你可以这么理解:ESP存储的是栈顶地址,也是栈底地址。)

3.ESP寄存器里存储的是在调用函数fun()之后,栈的栈顶。并且始终指向栈顶。

栈和寄存器的关系:

栈用来临时存放变量的值或地址,只要涉及到CPU的运算,都要有寄存器(至少有一个)才能做

eax,ebx,ecx,edx就是这类常见的寄存器

压栈一般是先压参数,这个是调用函数做

然后才设置被调用函数的栈底地址和栈顶地址,这个是被调用函数做

原函数

int bar(int c, int d)

{

int e = c + d;

return e;

}

int foo(int a, int b)

{

return bar(a, b);

}

int main(void)

{

foo(2, 3);

return 0;

}

在栈中的位置图,和一般的栈表示不一样,位置反了过来

函数调用过程中的压栈退栈_01

相应的汇编代码

$ gcc main.c -g

$ objdump -dS a.out

08048394 :

int bar(int c, int d)

{

8048394: 55 push %ebp//保存调用函数的栈底地址

8048395: 89 e5 mov %esp,%ebp//调用函数的栈顶就是被调用函数的栈底,更改ebp的值

8048397: 83 ec 10 sub $0x10,%esp//为本函数的局部变量分配空间,更改esp的值

int e = c + d;

804839a: 8b 55 0c mov 0xc(%ebp),%edx //把栈中地址是以栈底为基准,偏移量+12的变量存入寄存器

804839d: 8b 45 08 mov 0x8(%ebp),%eax//同上

80483a0: 01 d0 add %edx,%eax//相加操作

80483a2: 89 45 fc mov %eax,-0x4(%ebp)//把结果返回给本函数中的变量e

return e;

​ //函数有一个int型的返回值,这个返回值是通过eax寄存器传递的

80483a5: 8b 45 fc mov -0x4(%ebp),%eax

}

80483a8: c9 leave //函数开头的push %ebp和mov %esp,%ebp的逆操作

80483a9: c3 ret //call指令的逆操作:

1.现在esp所指向的栈顶保存着返回地址,把这个值恢复给eip,同时esp增加4,esp的值变成0xbff1c40c。

2.修改了程序计数器eip,因此跳转到返回地址0x80483c2继续执行。

080483aa :

int foo(int a, int b)

{

80483aa: 55 push %ebp//同bar

80483ab: 89 e5 mov %esp,%ebp//同bar

80483ad: 83 ec 08 sub $0x8,%esp//同bar

return bar(a, b);

​ //压入参数c

80483b0: 8b 45 0c mov 0xc(%ebp),%eax

80483b3: 89 44 24 04 mov %eax,0x4(%esp)

​ //压入参数d

80483b7: 8b 45 08 mov 0x8(%ebp),%eax

80483ba: 89 04 24 mov %eax,(%esp)

80483bd: e8 d2 ff ff ff call 8048394 //修改eip为8048394调用bar

}

80483c2: c9 leave //同bar

80483c3: c3 ret //同bar

080483c4

:

int main(void)

{

80483c4: 8d 4c 24 04 lea 0x4(%esp),%ecx

80483c8: 83 e4 f0 and $0xfffffff0,%esp

80483cb: ff 71 fc pushl -0x4(%ecx)

80483ce: 55 push %ebp

80483cf: 89 e5 mov %esp,%ebp

80483d1: 51 push %ecx

80483d2: 83 ec 08 sub $0x8,%esp

foo(2, 3);

​ //压缩参数a,对栈中地址是以栈底为基准,偏移量+4的地址进行赋值

80483d5: c7 44 24 04 03 00 00 movl $0x3,0x4(%esp)

80483dc: 00

80483dd: c7 04 24 02 00 00 00 movl $0x2,(%esp)//

80483e4: e8 c1 ff ff ff call 80483aa //修改eip为80483aa,调用foo

return 0;

80483e9: b8 00 00 00 00 mov $0x0,%eax

}

80483ee: 83 c4 08 add $0x8,%esp//释放栈空间

80483f1: 59 pop %ecx

80483f2: 5d pop %ebp//栈底地址出栈

80483f3: 8d 61 fc lea -0x4(%ecx),%esp

80483f6: c3 ret

注:

lea命令 和mov 命令操作方向跟编译器有关,有时候会是这样:

mov %esp,%ebp//%ebp = %esp

lea 0x4(%esp),%ecx//0x4(%esp) = 0x4(%esp)

Contents
  1. 1. 常用的三个和栈有关的寄存器:
  2. 2. 栈和寄存器的关系:
  3. 3. 原函数