函数调用之详细分析
来源:互联网 发布:java中log 编辑:程序博客网 时间:2024/06/04 17:46
印象中,函数调用是编译器负责的事情,之前学过汇编了解到,在函数调用时先要保存调用者函数的相关寄存器入栈,调用完再进行出栈等操作。今天偶然看到一篇文章详细分析了函数调用的过程,再次也对概念进行一下梳理。
在window下,栈的生长方向是由高地址向低地址。在嵌入式中,栈的空间申请是放在*.s启动文件中的,我们可以对其进行修改。栈的默认空间相比于堆而言比较小,是一段连续的空间,经常会发生栈溢出问题。
先来看看在栈中函数调用过程中其数据的存储方式,在window下通过一个小程序,查看其反汇编语言来说明。在此之前,先需要简单了解一下汇编机制下一些寄存器及指令。
1.32位通用数据寄存器 eax/ebx/ecx/edx,低16位寄存器(AX/BX/CX/DX),8位寄存器AH
-AL、BH-BL、CH-CL、DH-DL
寄存器AX和AL通常称为累加器(Accumulator),用累加器进行的操作可能需要更少时间。累加器可用于乘、除、输入/输出等操作,它们的使用频率很高;
寄存器BX称为基地址寄存器(Base Register)。它可作为存储器指针来使用;
寄存器CX称为计数寄存器(Count Register)。在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL来指明移位的位数;
寄存器DX称为数据寄存器(Data Register)。在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。
2.变址和指针寄存器 ESI 和EDI
(1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
(2)EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。
这是在codeblock下运行的一个C++程序
在主函数中:
0x0040134D lea 0x4(%esp),%ecx0x00401351 and $0xfffffff0,%esp0x00401354 pushl -0x4(%ecx)0x00401357 push %ebp0x00401358 mov %esp,%ebp0x0040135A push %ecx0x0040135B sub $0x24,%esp0x0040135E call 0x417cd0 <__main>0x00401363 movl $0x1,-0xc(%ebp)0x0040136A movl $0x2,-0x10(%ebp)0x00401371 movl $0x2,0x4(%esp)0x00401379 movl $0x1,(%esp)0x00401380 call 0x401340 <sum(int, int)>0x00401385 mov %eax,-0x14(%ebp)0x00401388 movl $0x474024,0x4(%esp)0x00401390 movl $0x47e860,(%esp)0x00401397 call 0x46f360 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>0x0040139C mov -0x14(%ebp),%edx0x0040139F mov %edx,(%esp)0x004013A2 mov %eax,%ecx0x004013A4 call 0x44fca0 <std::ostream::operator<<(int)>0x004013A9 sub $0x4,%esp0x004013AC movl $0x46dce0,(%esp)0x004013B3 mov %eax,%ecx0x004013B5 call 0x44f9e0 <std::ostream::operator<<(std::ostream& (*)(std::ostream&))>0x004013BA sub $0x4,%esp0x004013BD mov $0x0,%eax0x004013C2 mov -0x4(%ebp),%ecx0x004013C5 leave0x004013C6 lea -0x4(%ecx),%esp0x004013C9 ret
在函数内部插入一个断点,可以看到下面程序:
0x00401340 push %ebp0x00401341 mov %esp,%ebp0x00401343 mov 0xc(%ebp),%eax0x00401346 mov 0x8(%ebp),%edx0x00401349 add %edx,%eax0x0040134B pop %ebp0x0040134C ret
看以看到当函数被调用时,首先会把调用函数的栈底压栈到自己函数的栈中(push %ebp),然后将原来函数栈顶rsp作为当前函数的栈底(mov %esp, %ebp)。函数运行完成时,会将压入栈中的rbp重新出栈到rbp中(popq %rbp)。
我们知道,main主函数也是通过启动文件对其进行调用,是供程序员开发程序的主要入口。
在linux下系统调用堆栈的情形还不一样,有机会再进行linux的汇编分析。
参考文献:
1. http://blog.jobbole.com/99301/
- 函数调用之详细分析
- LINUX之 Copy_from_user函数详细分析
- lua源码分析之函数调用
- Copy_from_user函数详细分析
- ioctl函数详细分析
- Copy_from_user函数详细分析
- Copy_from_user函数详细分析
- Copy_from_user函数详细分析
- Copy_from_user函数详细分析
- Copy_from_user函数详细分析
- Copy_from_user函数详细分析
- device_create 函数详细分析
- device_create 函数详细分析
- do_mpage_readpage函数详细分析
- copy_from_user函数详细分析
- VC++ 之获取主机IP gethostbyname()函数 详细实例分析
- MeasureSpec之详细分析
- 汇编调用c函数时 堆栈的变化的详细分析
- JavaScript事件委托的技术原理
- IP地址类
- JAVA对象JSON数据互相转换的四种常见情况
- GDataXMLNode:xml解析库
- Android4.4 Systemui状态栏之信号逻辑流程分析
- 函数调用之详细分析
- 解决在低版本的xcode上使用高版本iOS系统手机进行真机测试
- Android app设置全屏模式
- solaris下清除log文件命令
- idea maven jetty 配置
- AndroidStudioSDK下tools下各个文件(夹)的用处
- 笔记,TCP协议socket网络编程
- 尝试用顺序表实现排序算法(持续更新..)
- 关于alter table enable row movement的应用