大多数CPU实现函数掉用是通过栈实现的,现在通过一个例子看下函数调用:
fun_call.c:
- int fun2(int c)
- {
- int e = c;
- return e;
- }
- int fun1(int a, int b)
- {
- return fun2(a+b);
- }
- int main(void)
- {
- fun1(1,0);
- return 0;
- }
通过汇编
main()
- main:
- pushl %ebp # 保存调用函数ebp 说明main()函数也是被调函数
- movl %esp, %ebp # 设置ebp =esp
- subl $8, %esp # 栈空间
- movl $0, 4(%esp) # 实参0保存在栈区
- movl $1, (%esp) # 实参1保存在栈区
- call fun1 # 函数调用 fun1
- movl $0, %eax # 函数返回值
- leave
- ret
fun1()
- fun1:
- pushl %ebp # 保存调用函数main()的ebp
- movl %esp, %ebp # 设置新的ebp = esp
- subl $4, %esp # 栈空间
- movl 12(%ebp), %eax # 取形参
- movl 8(%ebp), %edx # 取形参
- leal (%edx,%eax), %eax #
- movl %eax, (%esp) # 返回值
- call fun2 # 调用fun2
- leave
- ret
fun2()
- fun2:
- pushl %ebp #保存调用函数 fun1()的ebp
- movl %esp, %ebp #设置 fun2() 函数的ebp
- subl $16, %esp #设置esp
- movl 8(%ebp), %eax #取形参
- movl %eax, -4(%ebp) #局部变量e=c
- movl -4(%ebp), %eax #返回值
- leave
- ret
call 和 jmp
call :是子程序调用指令,,和 ret 配合使用
jmp:无条件转移指令,用作强行转换
jmp 相对 call 就简单多了,call 在跳转之前要做一些保护,最简单的就是 cs eip 和 esp 寄存器(函数调用 esp 由调用函数自动计算,可以不保存),以便程序可以通过ret 返回,jmp 则不管那么多。
leave 是恢复 ebp 和 esp 的值 即相当与 movl %ebp %esp; popl %ebp;
阅读(2121) | 评论(6) | 转发(0) |