ARM64架构栈帧回溯
文章目录
- 前言
- 一、栈帧简介
- 二、demo演示
前言
请参考:ARM64架构栈帧以及帧指针FP
一、栈帧简介
假设下列函数调用:
funb()
{func()
}funa()
{funb()
}main()
{funa()
}
main函数,funa函数,funb函数都不是叶子函数,其栈布局如下所示:

LR 和FP寄存器保存在每个函数栈帧的栈顶:
FP = SP + 0
LR = SP + 8
根据这两个寄存器就可以反推出所有函数的调用栈。
FP栈帧指针(X29)指向保存在栈上的上一个栈帧的帧指针。在它之后存储了保存的LR(X30)。链中的最后一个帧指针应设置为0。
知道FP寄存器就能得到每个函数的栈帧基地址。而知道每个函数的栈帧基地址的条件下,可通过当前函数栈帧保存的LR获得当前函数的Entry地址和函数名。
通过FP还可以知道上一级的FP(栈帧基地址)。
在ARM64体系结构中,函数调用栈以单链表形式组织,其中每个栈帧都包含两个地址,用于构建这个链表。这种链表通常被称为调用链或链式栈。
在链式栈中,每个栈帧都有两个64位宽的地址:
(1)低地址(栈顶)存放了指向上一个栈帧的基地址,通常使用FP(Frame Pointer)寄存器来保存。类似于链表中的prev指针,它指向上一个栈帧的基地址,以便在函数返回时回到调用者的上下文。
(2)高地址存放了LR(Link Register)寄存器的值,它保存了当前函数的返回地址。LR寄存器中的值指向了调用当前函数的下一条指令的地址。当函数执行完毕时,该地址将被用于恢复程序控制流,并返回到调用者的位置。
通过这种方式,每个栈帧都可以通过链表中的prev指针链接在一起,形成一个完整的函数调用链。当函数返回时,可以使用prev指针获取上一个栈帧的基地址,并利用LR寄存器中的返回地址将控制流传递给调用者。
ARM64栈回溯:
在AAPC64中,栈指针(SP)指向当前栈帧的顶部,其中包含了上一级函数的LR和FP寄存器现场。通过查看SP所指向的地址,可以找到保存的上一级函数的LR和FP寄存器值。
对于LR寄存器,根据(LR-4)可以找到上一级函数所在的地址,减去4是因为ARM64指令集中的跳转指令(例如BL)会将要跳转到的地址加上4。因此,在栈上保存的LR值实际上是要跳转到的下一条指令的地址,而不是当前指令的地址。所以,为了找到上一级函数所在的地址,需要减去4。
上一级函数的FP寄存器实际上等于上一级函数使用的栈顶地址。通过保存上一级FP寄存器现场的位置,可以在栈上找到上一级函数的栈帧。同样,该栈帧中也会保存更上一级函数的LR和FP寄存器现场,以此类推,形成函数调用链。
通过链式保存的方式,可以回溯整个函数的调用流程,从当前函数一直追溯到最外层的调用者。这种方式使得在函数返回时可以按照相反的顺序恢复各个函数的现场,并正确返回到调用者的位置。
二、demo演示
C语言示例:
int fund(int g, int h)
{return g + h;
}int func(int e, int f)
{int ret = e + f;ret = fund(e, ret);return ret;
}int funb(int c, int d)
{int ret = c + d;ret = func(c, ret);return ret;}int funa(int a, int b)
{int ret = a + b ;ret = funb(a, ret);return ret;
}int main(void)
{int i = 1, j = 2;int ret = funa(i,j);return 0;
}
(gdb) b main
(gdb) b funa
(gdb) b funb
(gdb) b func
(gdb) r
(1)
main:
(gdb) disassemble
Dump of assembler code for function main:......0x0000005555555810 <+32>: bl 0x55555557b4 <funa>0x0000005555555814 <+36>: str w0, [sp, #28]......
x29 0x7ffffff400
x30 0x7ff7e5c110
(2)
funa:
(gdb) c
(gdb) disassemble
Dump of assembler code for function funa:......0x00000055555557dc <+40>: bl 0x5555555778 <funb>0x00000055555557e0 <+44>: str w0, [sp, #44]......
(gdb) info registers
x29 0x7ffffff3d0
x30 0x5555555814
可以看到x30寄存器的值就是main函数 bl funa 下一条指令的地址。
根据x29寄存器得到funa栈帧基地址:
0x7ffffff3d0
读取该地址的值(x29寄存器FP存放了指向上一个栈帧的基地址):
(gdb) x/1xg 0x7ffffff3d0
0x7ffffff3d0: 0x0000007ffffff400
那么可以得到main函数的栈帧基地址:0x0000007ffffff400
这个值就等于执行main函数时,x29寄存器的值。
将main函数的栈帧基地址+8然后读取获取main的返回地址:
这里 + 8 的原因:LR = FP + 8
0x0000007ffffff400 + 8 = 0x0000007ffffff408
(gdb) x/1xg 0x0000007ffffff408
0x7ffffff408: 0x0000007ff7e5c110
main的返回地址:0x0000007ff7e5c110
将main的返回地址 - 4 就可以获取 BL main这条函数跳转指令的地址:
这里 - 4 的原因:执行BL指令时,将下一条指令的地址(即返回地址)写入X30寄存器中,这里我们已经获取到了返回地址,那么 -4 就获取到了 BL 指令的地址。
0x0000007ff7e5c110 - 4 = 0x0000007ff7e5c10c
那么其上一条调用main的指令地址就是0x0000007ff7e5c10c:
(gdb) x/i 0x0000007ff7e5c10c0x7ff7e5c10c <__libc_start_main+228>: blr x3
(gdb) x/2i 0x0000007ff7e5c10c0x7ff7e5c10c <__libc_start_main+228>: blr x30x7ff7e5c110 <__libc_start_main+232>: bl 0x7ff7e71a40 <exit>
可以看到是__libc_start_main函数调用 main 函数。
(3)
funb:
(gdb) disassemble
Dump of assembler code for function funb:......0x00000055555557a0 <+40>: bl 0x555555573c <func>0x00000055555557a4 <+44>: str w0, [sp, #44]......
(gdb) info registers
x29 0x7ffffff3a0
x30 0x55555557e0
可以看到x30寄存器的值就是 funa bl funb下一条指令的地址。
根据x29寄存器得到funb栈帧基地址:
0x7ffffff3a0
读取该地址的值(x29寄存器FP存放了指向上一个栈帧funa的基地址):
(gdb) x/1xg 0x7ffffff3a0
0x7ffffff3a0: 0x0000007ffffff3d0
这个值就等于执行funa函数时,x29寄存器的值。
将funa函数的栈帧基地址+8然后读取获取funa的返回地址:
0x0000007ffffff3d0 + 8 = 0x0000007ffffff3d8
(gdb) x/1xg 0x0000007ffffff3d8
0x7ffffff3d8: 0x0000005555555814
funa的返回地址:0x0000005555555814
将funa的返回地址 - 4 就可以获取 main BL funa这条函数跳转指令的地址:
0x0000005555555814- 4 = 0x0000005555555810
那么其上一条调用funa的指令地址就是0x0000005555555810:
(gdb) x/i 0x00000055555558100x5555555810 <main+32>: bl 0x55555557b4 <funa>
(gdb) x/2i 0x00000055555558100x5555555810 <main+32>: bl 0x55555557b4 <funa>0x5555555814 <main+36>: str w0, [sp, #28]
可以看到是main函数调用 funa 函数。
(4)
func:
(gdb) disassemble
Dump of assembler code for function func:......0x0000005555555764 <+40>: bl 0x555555571c <fund>0x0000005555555768 <+44>: str w0, [sp, #44]......
(gdb) info registers
x29 0x7ffffff370
x30 0x55555557a4
可以看到x30寄存器的值就是 funb bl func下一条指令的地址。
根据x29寄存器得到func栈帧基地址:
0x7ffffff370
读取该地址的值(x29寄存器FP存放了指向上一个栈帧funb的基地址):
(gdb) x/1xg 0x7ffffff370
0x7ffffff370: 0x0000007ffffff3a0
这个值就等于执行funb函数时,x29寄存器的值。
将funb函数的栈帧基地址+8然后读取获取funb的返回地址:
0x0000007ffffff3a0 + 8 = 0x0000007ffffff3a8
(gdb) x/1xg 0x0000007ffffff3a8
0x7ffffff3a8: 0x00000055555557e0
funb的返回地址:0x00000055555557e0
将funb的返回地址 - 4 就可以获取 funaBL funb这条函数跳转指令的地址:
0x00000055555557e0 - 4 = 0x00000055555557dc
那么其上一条调用funb的指令地址就是0x00000055555557dc:
(gdb) x/i 0x00000055555557dc0x55555557dc <funa+40>: bl 0x5555555778 <funb>
(gdb) x/2i 0x00000055555557dc0x55555557dc <funa+40>: bl 0x5555555778 <funb>0x55555557e0 <funa+44>: str w0, [sp, #44]
可以看到是funa函数调用 funb 函数。
相关文章:
ARM64架构栈帧回溯
文章目录 前言一、栈帧简介二、demo演示 前言 请参考:ARM64架构栈帧以及帧指针FP 一、栈帧简介 假设下列函数调用: funb() {func() }funa() {funb() }main() {funa() }main函数,funa函数,funb函数都不是叶子函数,其…...
LangChain:大型语言模型(LLMs)-- 基础知识
1、LangChain的调用大型语言模型模块的介绍 LangChain是一个强大的框架,旨在通过调用大型语言模型(LLM)来开发各种语言驱动的应用程序。在LangChain中,LLM不仅仅是一个简单的模型调用,而是一个复杂链条中的关键部分。…...
总分410+专业130+国防科技大学831信号与系统考研经验国防科大电子信息与通信工程,真题,大纲,参考书。
好几个学弟催着,总结一下我自己的复习经历,希望大家复习少走弯路,投入的复习正比换回分数。我专业课831信号与系统130(感觉比估分要低,后面找Jenny老师讨论了自己拿不准的地方也没有错误,心里最近也这经常回…...
chatgpt Team 4.0共享合租账号的新方式
为了更好地满足工作需求,我订阅了GPT PLUS会员,但我发现,4.0每三小时问答40次经常吃灰,而且每月近200元的费用让我感到有点肉痛。 于是,我开始寻找有没有什么替代品。在逛某论坛的时候,发现了一个共享Team…...
类和对象二
一、运算符重载 为了使自定义类型可以使用加减等运算符,CPP提供了一个功能叫运算符重载。 关键字:operator操作符 运算符重载最好定义在类对象里,这也可以避免访问不到私有成员的问题。 代码演示: 在类里定义之后,…...
GD32 HID键盘矩阵键盘发送数据时,一直发送数据问题处理
这个问题找了两三天,开始并不认为是示例程序的问题,只是感觉是自己代码问题。 这个解决流程大概是: 先调好矩阵键盘=> 调用发送函数。 就是因为调用时,一直发送数据,我也在按键抬起做了操作,始终不行。 最后,发现时示例代码中有个 空闲中断 引起的。 udev->reg…...
小程序地理位置权限申请+uniapp调用uni.getLocation
文章目录 一、小程序地理位置权限申请二、uniapp调用uni.getLocation 一、小程序地理位置权限申请 需要确保小程序类目已经填写 点击左侧导航栏找到最后的“设置”——“基本设置”——“前往填写” 在开发管理——接口设置——地理位置中可以看到: 即可点击想要申…...
后台权限控制及动态路由
需求 后台系统需要能实现不同的用户权限可以看到不同的功能。 用户只能使用他的权限所允许使用的功能。 功能设计 之前在我的SpringSecurity的课程中就介绍过RBAC权限模型。没有学习过的可以去看下 RBAC权限模型 。这里我们就是在RBAC权限模型的基础上去实现这个功能。 表分…...
云计算:Linux 部署 OVS 集群(控制端)实现OpenFlow
目录 一、实验 1.环境 2.Linux 部署 OVS 集群(控制端) 3.控制端对接服务端OVS网元 4.服务端OVS添加流表 5.服务端删除OVS 二、问题 1. ODL如何查找已安装插件 2.查看流表显示不全 3.如何删除OVS流表 一、实验 1.环境 (1) 主机 表1 宿主机 主…...
使用/api/put保存数据到OpenTSDB,报204错误
错误信息 HttpResponseProxy{HTTP/1.1 204 No Content [Content-Type: application/json; charsetUTF-8, Content-Length: 0]} 错误原因 在OpenTSDB中,使用/api/put保执行写入操作,得到204响应,表示已经成功写入数据库。...
Open3D kmeans聚类(马氏距离,Python版本)
文章目录 一、简介二、算法步骤三、代码实现四、实现效果参考资料一、简介 在诸多的聚类方法中,K-Means聚类方法是属于“基于原型的聚类”(也称为原型聚类)的方法,此类方法均是假设聚类结构能通过一组原型刻画,在现实聚类中极为常用。通常情况下,该类算法会先对原型进行初始…...
python抠图程序
import cv2 import numpy as np def color_threshold(image, lower, upper): hsv_image cv2.cvtColor(image, cv2.COLOR_BGR2HSV) mask cv2.inRange(hsv_image, lower, upper) result cv2.bitwise_and(image, image, maskmask) return result # 读取图片…...
Android13 CameraServer启动流程
代码入口 frameworks/av/camera/cameraserver 里面包含了四个文件 我们先来看看Android.bp的内容 package {// See: http://go/android-license-faq// A large-scale-change added default_applicable_licenses to import// all of the license_kinds from "frameworks_a…...
如何升级node.js版本
升级Node.js可以通过多种方式来完成,以下是四种常见的方法: 方法一:使用Node.js官方安装程序 访问Node.js的官方网站,下载对应你操作系统的最新版本安装程序。通常,你可以 https://nodejs.org/en/download 找到你需…...
Excel---一个工作簿中的多个sheet合并成一个PDF
0 Preface/Foreword 1 操作方法 1.1 方法一 文件》 导出 》创建PDF/XPS 》 选项 》发布内容 》“整个工作簿” 1.2 方法二 文件》 打印》 打印机选项中,选择一种PDF阅读器 》设置选项中,选择打印整个工作簿。...
结合文本的目标检测:Open-GroundingDino训练自己的数据集
1、简单介绍 Open-GroundingDino是GroundingDino的第三方实现训练流程的代码,因为官方GroundingDino没有提供训练代码,只提供了demo推理代码。 关于GroundingDino的介绍可以看论文:https://arxiv.org/pdf/2303.05499.pdf GroundingDino的G…...
分布式锁-redission锁的MutiLock原理
5.5 分布式锁-redission锁的MutiLock原理 为了提高redis的可用性,我们会搭建集群或者主从,现在以主从为例 此时我们去写命令,写在主机上, 主机会将数据同步给从机,但是假设在主机还没有来得及把数据写入到从机去的时…...
MySQL索引、B+树相关知识汇总
MySQL索引、B树相关知识汇总 一、有一个查询需求,MySQL中有两个表,一个表1000W数据,另一个表只有几千数据,要做一个关联查询,如何优化?1、为关联字段建立索引二、小表驱动大表 二、b树和b树的区别1、更高的…...
相机模型浅析
相机模型 文章目录 相机模型四个坐标系针孔相机模型世界坐标系到相机坐标系相机坐标系到图像坐标系图像坐标到像素坐标 四个坐标系 ①世界坐标系:是客观三维世界的绝对坐标系,也称客观坐标系。因为数码相机安放在三维空间中,我们需要世界坐标…...
国芯科技(C*Core)双芯片汽车安全气囊解决方案
汽车安全气囊是20世纪汽车上的十大发明之一,是目前汽车的法定标准配置,成为汽车驾乘人员生命安全的保护神。随着人们对汽车安全性要求的进一步提高,已形成前排驾驶员气囊、前排副驾驶员气囊、前排侧气囊、后排侧气囊、膝部气囊、安全气帘等等…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
Unity VR/MR开发-VR开发与传统3D开发的差异
视频讲解链接:【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
Linux操作系统共享Windows操作系统的文件
目录 一、共享文件 二、挂载 一、共享文件 点击虚拟机选项-设置 点击选项,设置文件夹共享为总是启用,点击添加,可添加需要共享的文件夹 查询是否共享成功 ls /mnt/hgfs 如果显示Download(这是我共享的文件夹)&…...
EasyRTC音视频实时通话功能在WebRTC与智能硬件整合中的应用与优势
一、WebRTC与智能硬件整合趋势 随着物联网和实时通信需求的爆发式增长,WebRTC作为开源实时通信技术,为浏览器与移动应用提供免插件的音视频通信能力,在智能硬件领域的融合应用已成必然趋势。智能硬件不再局限于单一功能,对实时…...
C++ 类基础:封装、继承、多态与多线程模板实现
前言 C 是一门强大的面向对象编程语言,而类(Class)作为其核心特性之一,是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性,包括封装、继承和多态,同时讨论类中的权限控制,并展示如何使用类…...
Axure零基础跟我学:展开与收回
亲爱的小伙伴,如有帮助请订阅专栏!跟着老师每课一练,系统学习Axure交互设计课程! Axure产品经理精品视频课https://edu.csdn.net/course/detail/40420 课程主题:Axure菜单展开与收回 课程视频:...
