从汇编简要分析c语言函数调用栈
2011-09-13 20:19
330 查看
先写个简单的小demo, 分析一下c的函数调用过程。
下面是haha()的汇编代码:
Dump of assembler code for function haha:
0x080483a1 <+0>: push %ebp
0x080483a2 <+1>: mov %esp,%ebp
0x080483a4 <+3>: sub $0x8,%esp
=> 0x080483a7 <+6>: movl $0x5,0x4(%esp)
0x080483af <+14>: movl $0x3,(%esp)
0x080483b6 <+21>: call 0x8048394 <test>
0x080483bb <+26>: leave
0x080483bc <+27>: ret
End of assembler dump.
此时程序暂停在test(3, 5) 这一行, 调用test时, 首先会把函数参数压栈。
0x080483a4 <+3>: sub $0x8,%esp 表示栈顶加8个字节, 可以保留两个int型参数。
0x080483a7 <+6>: movl $0x5,0x4(%esp) 第二个参数先入栈
0x080483af <+14>: movl $0x3,(%esp) 第一个参数后如栈
0x080483b6 <+21>: call 0x8048394 <test> call会调用test函数, 这里隐含的操作是eip寄存器内容入栈,用来保存函数调用结束后应该执行的命令地址。
下面是test的汇编代码:
Dump of assembler code for function test:
0x08048394 <+0>: push %ebp
0x08048395 <+1>: mov %esp,%ebp
=> 0x08048397 <+3>: addl $0x1,0x8(%ebp)
0x0804839b <+7>: addl $0x2,0xc(%ebp)
0x0804839f <+11>: pop %ebp
0x080483a0 <+12>: ret
End of assembler dump.
0x08048394 <+0>: push %ebp 首先保存调用函数(haha)的栈底地址
0x08048395 <+1>: mov %esp,%ebp 然后把esp 内容赋给ebp寄存器, ebp保存新栈帧 (test)的栈底地址。 此时esp ebp指向栈的同一个位置。
此时ebp的内容保存着调用函数的栈底地址, 以此为基准, %ebp + 4 地址保存返回地址, %ebp + 8 保存 第一个参数, &ebp + 12 保存第二个参数, 而比%ebp - x (x 为正数) 保存test函数的局部变量(这个例子没有用到局部变量)。
0x08048397 <+3>: addl $0x1,0x8(%ebp) 第一个参数+1
0x0804839b <+7>: addl $0x2,0xc(%ebp) 第二个参数+2
0x080483a0 <+12>: ret 返回。
ebp 具有十分的意义, 它总是保存上层调用时的ebp值, 而且在没层调用中都可一通过ebp (向栈底方向)找到返回地址, 参数, (向栈顶方向) 找到局部变量。 这是一个递归的过程。
如上所示, 当程序执行到 0x08048397 <+3>: addl $0x1,0x8(%ebp) 的时候, 打印ebp 内容:
(gdb) p $ebp
$1 = (void *) 0xbffff310
表示test的栈底地址为0xbffff310
0xbffff310 + 8 为第一参数3
(gdb) x /1uw 0xbffff318
0xbffff318: 3
0xbffff310 + 12 为第一参数5
(gdb) x /1uw 0xbffff31c
0xbffff31c: 5
0xbffff310 + 4 为 返回地址
(gdb) x /1aw 0xbffff314
0xbffff314: 0x80483bb <haha+26>
而 0x80483bb <haha+26> 对应函数haha中的 0x080483bb <+26>: leave
void test(int a, int b) { a++; b+=2; } void haha() { test(3, 5); } int main() { haha(); return 0; }
下面是haha()的汇编代码:
Dump of assembler code for function haha:
0x080483a1 <+0>: push %ebp
0x080483a2 <+1>: mov %esp,%ebp
0x080483a4 <+3>: sub $0x8,%esp
=> 0x080483a7 <+6>: movl $0x5,0x4(%esp)
0x080483af <+14>: movl $0x3,(%esp)
0x080483b6 <+21>: call 0x8048394 <test>
0x080483bb <+26>: leave
0x080483bc <+27>: ret
End of assembler dump.
此时程序暂停在test(3, 5) 这一行, 调用test时, 首先会把函数参数压栈。
0x080483a4 <+3>: sub $0x8,%esp 表示栈顶加8个字节, 可以保留两个int型参数。
0x080483a7 <+6>: movl $0x5,0x4(%esp) 第二个参数先入栈
0x080483af <+14>: movl $0x3,(%esp) 第一个参数后如栈
0x080483b6 <+21>: call 0x8048394 <test> call会调用test函数, 这里隐含的操作是eip寄存器内容入栈,用来保存函数调用结束后应该执行的命令地址。
下面是test的汇编代码:
Dump of assembler code for function test:
0x08048394 <+0>: push %ebp
0x08048395 <+1>: mov %esp,%ebp
=> 0x08048397 <+3>: addl $0x1,0x8(%ebp)
0x0804839b <+7>: addl $0x2,0xc(%ebp)
0x0804839f <+11>: pop %ebp
0x080483a0 <+12>: ret
End of assembler dump.
0x08048394 <+0>: push %ebp 首先保存调用函数(haha)的栈底地址
0x08048395 <+1>: mov %esp,%ebp 然后把esp 内容赋给ebp寄存器, ebp保存新栈帧 (test)的栈底地址。 此时esp ebp指向栈的同一个位置。
此时ebp的内容保存着调用函数的栈底地址, 以此为基准, %ebp + 4 地址保存返回地址, %ebp + 8 保存 第一个参数, &ebp + 12 保存第二个参数, 而比%ebp - x (x 为正数) 保存test函数的局部变量(这个例子没有用到局部变量)。
0x08048397 <+3>: addl $0x1,0x8(%ebp) 第一个参数+1
0x0804839b <+7>: addl $0x2,0xc(%ebp) 第二个参数+2
0x080483a0 <+12>: ret 返回。
ebp 具有十分的意义, 它总是保存上层调用时的ebp值, 而且在没层调用中都可一通过ebp (向栈底方向)找到返回地址, 参数, (向栈顶方向) 找到局部变量。 这是一个递归的过程。
如上所示, 当程序执行到 0x08048397 <+3>: addl $0x1,0x8(%ebp) 的时候, 打印ebp 内容:
(gdb) p $ebp
$1 = (void *) 0xbffff310
表示test的栈底地址为0xbffff310
0xbffff310 + 8 为第一参数3
(gdb) x /1uw 0xbffff318
0xbffff318: 3
0xbffff310 + 12 为第一参数5
(gdb) x /1uw 0xbffff31c
0xbffff31c: 5
0xbffff310 + 4 为 返回地址
(gdb) x /1aw 0xbffff314
0xbffff314: 0x80483bb <haha+26>
而 0x80483bb <haha+26> 对应函数haha中的 0x080483bb <+26>: leave
相关文章推荐
- 【转】PowerPC VxWorks BSP分析(2)--PowerPC汇编
- 汇编语言(王爽)》(第九章、实验8分析一个奇怪的程序)学习笔记
- RxJava && Agera 从源码简要分析基本调用流程(1)
- C 语言函数返回结构体汇编分析
- 简要分析Java的Hibernate框架中的自定义类型
- 【王爽-汇编语言】第八章检测题分析
- 【王爽-汇编语言】第九章检测题分析
- java集合简要分析
- boost::thread简要分析(3):线程局部存储及其它
- Arm汇编学习笔记(二)——编写编译并执行依赖外部模块的汇编代码以及PIC代码分析
- 卷二 Dalvik与Android源码分析 第五章 Interpreter与JIT 5.3-C解释器 5.4--汇编解释器 图书版试读--请勿转发
- StarUML2 建模工具全平台破解及license验证简要分析
- uboot代码简要分析
- python对json的相关操作以及json模块的简要分析
- Java7中的ForkJoin并发框架初探(中)——JDK中实现简要分析
- 菜鸟总结so分析,arm 汇编,IDA静态分析
- U-boot源码简要分析(二)
- MongoDB查询语句简要分析
- 简要分析Ogre渲染队列的实现原理(转)
- 51单片机ucos ii任务切换汇编代码分析(2)