Linux C函数调用栈帧结构

2024-03-31 21:08

本文主要是介绍Linux C函数调用栈帧结构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux C程序的反汇编,每个函数第一个指令都是push %rbp,即当caller者调callee时,指令callq <fun addr> callee返回后的下一个指令地址压入栈帧,然后push %rbp 保存 rbp寄存器,紧接着mov %rsp,%rbp 更新rbp 寄存器这样层层调用构成函数调用栈,每个栈帧开始就是存的上一个栈帧的rbp, 结尾就是调用callee后的下一个执行令地址。函数调用详细介绍可以看这篇文章从汇编看Linux C函数的调用约定和参数传递的细节。

通过前面分析,可以得出,rbp寄存器存放的是当前栈帧的基址,当前栈帧的基址即开始地址则存放的是上一个栈帧的基址,即*rbp就是上一个栈帧的基址。对于x64,每个栈帧开头的八个字节存放的就是上个栈帧的基址。调用栈的结构图大致如下:

call stack

下面用一个例子来说明栈帧结构的细节。

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:5
5       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                          // rbp0x0040051f  0x00000000  // return addr
0x7fff1c4d0cf0: 0xa68271a8  0x00000007  0xa6dfe4c0  0x00007fe4
0x7fff1c4d0d00: 0x00000002  0x00007fe40x1c4d0d30  0x00007fff  // rbp
0x7fff1c4d0d10: 0x00400543  0x00000000                          // return addr0x004005bd  0x00000007
0x7fff1c4d0d20: 0x1c4d0d50  0x00007fff  0x00000001  0x00000000
0x7fff1c4d0d30: 0x1c4d0d50  0x00007fff                          // rbp0x00400564  0x00000000  // return addr

还可以用pmap命令看布局:

root@ubuntu:~# pmap -p 23620
23620:   ./a.out
0000000000400000      4K r-x-- /root/a.out
0000000000600000      4K r---- /root/a.out
0000000000601000      4K rw--- /root/a.out
00007fe4a6817000   1776K r-x-- /lib/x86_64-linux-gnu/libc-2.19.so
00007fe4a69d3000   2044K ----- /lib/x86_64-linux-gnu/libc-2.19.so
00007fe4a6bd2000     16K r---- /lib/x86_64-linux-gnu/libc-2.19.so
00007fe4a6bd6000      8K rw--- /lib/x86_64-linux-gnu/libc-2.19.so
00007fe4a6bd8000     20K rw---   [ anon ]
00007fe4a6bdd000    140K r-x-- /lib/x86_64-linux-gnu/ld-2.19.so
00007fe4a6de6000     12K rw---   [ anon ]
00007fe4a6dfd000      8K rw---   [ anon ]
00007fe4a6dff000      4K r---- /lib/x86_64-linux-gnu/ld-2.19.so
00007fe4a6e00000      4K rw--- /lib/x86_64-linux-gnu/ld-2.19.so
00007fe4a6e01000      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)
函数调用栈的获取原理分析

这篇关于Linux C函数调用栈帧结构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/865351

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Java集合中的链表与结构详解

《Java集合中的链表与结构详解》链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序的通过链表中的引用链接次序实现,文章对比ArrayList与LinkedList的结构差异,详细讲解了链表... 目录一、链表概念与结构二、当向单链表的实现2.1 准备工作2.2 初始化链表2.3 打印数据、链表长

Linux查询服务器 IP 地址的命令详解

《Linux查询服务器IP地址的命令详解》在服务器管理和网络运维中,快速准确地获取服务器的IP地址是一项基本但至关重要的技能,下面我们来看看Linux中查询服务器IP的相关命令使用吧... 目录一、hostname 命令:简单高效的 IP 查询工具命令详解实际应用技巧注意事项二、ip 命令:新一代网络配置全