C语言程序函数调用栈帧结构
来源:互联网 发布:国际局势知乎 编辑:程序博客网 时间:2024/06/05 21:14
Linux C程序的反汇编,每个函数第一个指令都是push %rbp
,即当caller者调callee时,指令callq <fun addr>
callee返回后的下一个指令地址压入栈帧,然后push %rbp
保存 rbp
寄存器,紧接着mov %rsp,%rbp
更新rbp
寄存器这样层层调用构成函数调用栈,每个栈帧开始就是存的上一个栈帧的rbp
, 结尾就是调用callee后的下一个执行令地址。函数调用详细介绍可以看这篇文章从汇编看Linux C函数的调用约定和参数传递的细节。
通过前面分析,可以得出,rbp寄存器存放的是当前栈帧的基址,当前栈帧的基址即开始地址则存放的是上一个栈帧的基址,即*rbp就是上一个栈帧的基址。对于x64,每个栈帧开头的八个字节存放的就是上个栈帧的基址。调用栈的结构图大致如下:
下面用一个例子来说明栈帧结构的细节。
int fun3(int a3){ int a = 3; int re = a3; while(1); return re;}int fun2(int a2){ int a = 2; int re = fun3(a2); return re;}int fun1(int a1){ int a = 1; int re = fun2(a1); return re;}int main(){ int a = 7; int b = fun1(a); return 0;}
利用gdb对上面里进行详细分析,编译gcc -g main.c
, 执行./a.out
, 然后新开一个shell窗口ps afxu | grep a.out
查找进程PID,gdb - <PID>
进行调试。可以很容易分析出函数的栈帧结构组织关系,详细如下:
fun3 (a3=7) at main.c:55 while(1);(gdb) bt#0 fun3 (a3=7) at main.c:5#1 0x000000000040051f in fun2 (a2=7) at main.c:12#2 0x0000000000400543 in fun1 (a1=7) at main.c:19#3 0x0000000000400564 in main () at main.c:26(gdb) p $rbp // 当前栈帧0 rbp$1 = (void *) 0x7fff1c4d0ce0(gdb) x/i *(long*)($1 + 8) // 栈帧1 返回地址 0x40051f <fun2+28>: mov %eax,-0x4(%rbp)(gdb) p/x *(unsigned long*)$rbp // 栈帧1 rbp地址$2 = 0x7fff1c4d0d08(gdb) x/i *(long*)($2 + 8) // 栈帧2 返回地址 0x400543 <fun1+28>: mov %eax,-0x4(%rbp)(gdb) p/x *(unsigned long*)$2 // 栈帧3 rbp$3 = 0x7fff1c4d0d30(gdb) x/i *(long*)($3 + 8) // 栈帧3 返回地址 0x400564 <main+25>: mov %eax,-0x4(%rbp)(gdb) x/24x $rsp // 从rsp开始24 DWORD的栈内容0x7fff1c4d0ce0: 0x1c4d0d08 0x00007fff // rbp 0x0040051f 0x00000000 // return addr0x7fff1c4d0cf0: 0xa68271a8 0x00000007 0xa6dfe4c0 0x00007fe40x7fff1c4d0d00: 0x00000002 0x00007fe4 0x1c4d0d30 0x00007fff // rbp0x7fff1c4d0d10: 0x00400543 0x00000000 // return addr 0x004005bd 0x000000070x7fff1c4d0d20: 0x1c4d0d50 0x00007fff 0x00000001 0x000000000x7fff1c4d0d30: 0x1c4d0d50 0x00007fff // rbp 0x00400564 0x00000000 // return addr
还可以用pmap
命令看布局:
root@ubuntu:~# pmap -p 2362023620: ./a.out0000000000400000 4K r-x-- /root/a.out0000000000600000 4K r---- /root/a.out0000000000601000 4K rw--- /root/a.out00007fe4a6817000 1776K r-x-- /lib/x86_64-linux-gnu/libc-2.19.so00007fe4a69d3000 2044K ----- /lib/x86_64-linux-gnu/libc-2.19.so00007fe4a6bd2000 16K r---- /lib/x86_64-linux-gnu/libc-2.19.so00007fe4a6bd6000 8K rw--- /lib/x86_64-linux-gnu/libc-2.19.so00007fe4a6bd8000 20K rw--- [ anon ]00007fe4a6bdd000 140K r-x-- /lib/x86_64-linux-gnu/ld-2.19.so00007fe4a6de6000 12K rw--- [ anon ]00007fe4a6dfd000 8K rw--- [ anon ]00007fe4a6dff000 4K r---- /lib/x86_64-linux-gnu/ld-2.19.so00007fe4a6e00000 4K rw--- /lib/x86_64-linux-gnu/ld-2.19.so00007fe4a6e01000 4K rw--- [ anon ]00007fff1c4b2000 132K rw--- [ stack ]00007fff1c5dd000 8K r-x-- [ anon ]ffffffffff600000 4K r-x-- [ anon ]
参考
Computer Systems: A Programmer’s Perspective, 3/E (CS:APP3e)
函数调用栈的获取原理分析
1 0
- C语言程序函数调用栈帧结构
- 浅谈C语言中的函数调用方式-----栈帧结构
- C语言函数调用时的栈帧结构变化
- 函数调用和返回过程栈帧结构变化图(C语言举例)
- C语言函数调用栈
- 7.C语言调用帧结构
- C语言结构体指针在函数调用中的使用
- python 调用C程序的结构体和函数
- python 调用C程序的结构体和函数
- C语言函数调用
- 【C语言】调用函数
- C语言函数调用
- 函数调用栈帧结构
- C语言函数调用及栈帧分析
- c语言中函数的调用的栈帧
- C语言中函数的调用与栈帧
- 【C语言】函数运行过程-----栈帧调用
- C语言函数调用及栈帧分析
- Bmob易出现的各种错误总结
- Java注解的作用,使用,自定义
- leetcode week9
- LintCode 43 最大子数组 III
- HDU
- C语言程序函数调用栈帧结构
- 4.25
- 山科交流赛A,J(思维)(2017)
- getDeclaredConstructor() 与 getConstructor()方法
- android 插件加载机制之二
- 【前端-笔记】JavaScript入门篇
- 大学生活随笔
- eclipse保存之前的工作空间的设置和重新设置eclipse的工作空间的路径
- 每日一题 No.29 学习SQL语言