摘自自博客网址 http://www.cnblogs.com/bangerlee/archive/2012/05/22/2508772.html
- ax(accumulator): 可用于存放函数返回值
- bp(base pointer): 用于存放执行中的函数对应的栈帧的栈底地址
- sp(stack poinger): 用于存放执行中的函数对应的栈帧的栈顶地址
- ip(instruction pointer): 指向当前执行指令的下一条指令
example code:
//func_call.c
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, 5);
return 0;
}
main函数汇编代码:
(gdb) disassemble /rm
Dump of assembler code for function main:
{
0x0000000000400521 : 55 push %rbp
0x0000000000400522 : 48 89 e5 mov %rsp,%rbp
foo(2, 5);
0x0000000000400525 : be 05 00 00 00 mov $0x5,%esi
0x000000000040052a : bf 02 00 00 00 mov $0x2,�i
0x000000000040052f : e8 d2 ff ff ff callq 0x400506
return 0;
0x0000000000400534 : b8 00 00 00 00 mov $0x0,eax
}
0x0000000000400539 : c9 leaveq
0x000000000040053a : c3 retq
End of assembler dump.
函数调用关系
main->foo->bar
# gcc -g func_call.c -o func_call
反汇编分析
(gdb) start
start命令用于拉起被调试程序,并执行至main函数的开始位置
现进程跑在main函数中,我们disassemble命令显示当前函数的汇编信息:
(gdb) disassemble /rm
/m指示显示汇编指令的同时,显示相应的程序源码;/r指示显示十六进制的计算机指令(raw instruction)
一个函数被调用,首先默认要完成以下动作:
将调用函数的栈帧栈底地址入栈,即将bp寄存器的值压入调用栈中
建立新的栈帧,将被调函数的栈帧栈底地址放入bp寄存器中
以下两条指令即完成上面动作:
push %rbp
mov %rsp, %rbp
main并不是程序拉起后第一个被执行的函数,它被_start函数调用
传递参数
mov $0x5, %esi
mov $0x2, �i
或者:\x86 is diffrent from x86_64
sub $0x8, %esp
mov $0x5, -0x4(%ebp) //栈底对应低地址
mov $0x2, -0x8(�p)
call指令完成控制全转移
0x000000000040052f 14>: e8 d2 ff ff ff callq 0x400506
执行完start命令后,现在程序停在0x400522的位置,下面我们通过gdb的si指令,让程序执行完call指令
(gdb) si 3
foo (a=0, b=4195328) at func_call.c:8
8 {
(gdb)
查看rsp、rbp寄存器的值,它们保存了程序实际用到的物理内存地址
(gdb) info registers rbp rsp
rbp 0x7fffffffe8e0 0x7fffffffe8e0
rsp 0x7fffffffe8d8 0x7fffffffe8d8
(gdb)
将main函数栈帧的栈底地址入栈,建立foo函数的栈帧:
0x0000000000400506 0>: 55 push %rbp
0x0000000000400507 1>: 48 89 e5 mov %rsp,%rbp //rsp的值从哪里得到的?
将参数传入esi、edi寄存器,然后执行call指令
0x000000000040051a 20>: e8 cd ff ff ff callq 0x4004ec
10 }
0x000000000040051f 25>: c9 leaveq
0x0000000000400520 26>: c3 retq
最后一个被调用的函数结果保存在eax寄存器中,以作为结果返回
函数调用过程对应着调用栈的建立,而函数返回则是进行调用栈的销毁,
0x0000000000400504 24>: c9 leaveq//相当于mov %rbp, %rsp/pop %rbp
0x0000000000400505 25>: c3 retq
修改sp、bp寄存器记录栈帧的高、低地址,以此完成函数调转;
push/mov操作保存caller变量、指令信息,保证callee返回之后caller继续正常执行;
下一个函数的bp由sp传入,从kernel传入的?
调用栈变化过程:
call foo之前 ox400534是call后main的下一条指令地址
call bar后,汇编bar前,0x40051f是foo执行bar后的下一条指令
汇编bar后
执行完bar后