码迷,mamicode.com
首页 > 其他好文 > 详细

函数调用

时间:2016-01-23 21:25:33      阅读:179      评论:0      收藏:0      [点我收藏+]

标签:

在汇编语言中需要调用函数时要call这个函数名,函数的执行过程如下:

准备执行

  在主程序中每次调用函数时,先依次把各参数以相反的顺序入栈;
  然后call func_name, 这里call要做两件事: 一是把函数的返回地址入栈,二是让指令执行指针%eip指向函数开始处。


开始执行

   现在函数要开始执行了,但它执行函数代码前还要做一点小事,首先把原来的基地址寄存器%ebp值入栈(基址寻址?看汇编),因为在程序执行中%ebp要另作它用, 接着堆栈指针%esp的值复制给%ebp, 此后在函数执行中%ebp一直保持不变,可以由此寻址获得函数参数。

 pushl %ebp
 movl %esp, %ebp

    下面开始执行函数代码了。函数先要把它的局部变量保存在栈中,这很简单。比如要保存一个long型数据,只要把%esp指针向下移动4个字节(因为栈增长方向是由高地址到低地址),再根据%esp把该数据移入. 下面是保存两个局部变量long后的堆栈内容:
Parameter #N <--- N*4+4(%ebp)
...
Parameter 2 <--- 12(%ebp)
Parameter 1 <--- 8(%ebp)
Return Address <--- 4(%ebp)
Old %ebp <--- (%ebp)
Local Variable 1 <--- -4(%ebp)
Local Variable 2 <--- -8(%ebp) and (%esp)

从上可以看出通过%ebp基地址寻址(基址寻址?看汇编)可以访问所有的函数参数和局部变量. 当然也可以不用
%ebp而用其它的寄存器进行同样的基地址寻址。但对于x86结构使用%ebp寄存器可能会更
快一点。

执行结束:

现在函数执行要结束了,在它返回之前,还要做下面几件事:
1. 把函数的返回值存放在通用寄存器%eax中,供外部使用
2. 把%esp指向函数开始执行的位置, 即movl %ebp,%esp
3. 在函数返回ret之前,要还原ebx, 即popl %ebp

movl %ebp, %esp
popl %ebp
ret

技术分享

调用函数的过程,再看一下函数是如何退出的。观察mainf不难发现,退出函数使用的是如下指令

leave
ret

leave指令相当于如下指令:

movl	%ebp, %esp
popl	%ebp
     
  • 第一条语句是将esp重置到ebp,可以理解为清空当前函数所使用的栈
  •  
  • 第二条语句是将栈顶值赋值给ebp,并弹出,栈顶值是什么呢?通过上面的分析不难发现,此时的栈顶值实际上是前一个函数的栈基地址,所以第二条语句的意思就是把ebp恢复到前一个函数的栈基地址

接着ret就是相当于,恢复指令指向:

popl %eip

 

总结

最后,通过这个例子,总结一下函数调用的过程:

由main函数进入f函数:

  1. 当前栈基地址压栈(当前栈基地址实际上是前一个函数(main)的栈基地址)  pushl %ebp  ;  movl %esp, %ebp ;

调用f函数:

  1. 参数从右到左进栈(实参)
  2. 下一条指令地址进栈

退出函数:

  1. 栈顶esp归位,回到本函数的ebp     movl %ebp, %esp;
  2. 基地址回退到上一个函数的基地址     popl %ebp
  3. eip退回到上一个函数即将要执行的那条语句的地址上   ret

函数调用

标签:

原文地址:http://www.cnblogs.com/wgang171412/p/5153883.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!