arm 函数栈回溯

大概意思就是arm每个函数开始都会将PC、LR、SP以及FP四个寄存器入栈。
下面我们看一下这四个寄存器里面保存的是什么内存
arm-linux-gnueabi-gcc unwind.c -mapcs -w -g -o unwind(需要加上-mapcs才会严格按照上面说的入栈)
#include <stdio.h>
#include <stdlib.h>struct stackframe {unsigned long fp;//低地址unsigned long sp;unsigned long lr;unsigned long pc;//高地址
};
void backtrace() {struct stackframe *frame = NULL;unsigned long *sp = NULL;asm volatile ("mov %0, ip" : "=g"(sp));//ip里面保存的是还未压栈的spprintf("sp poniter 0x%lx\n", sp);frame = (char*)sp - sizeof(struct stackframe);printf("fp 0x%lx, pc 0x%lx, sp 0x%lx\n", frame->fp,frame->pc, frame->sp);//通过打印栈帧里面的sp确实和ip里面的一样的/* 不知道怎么结束循环.... */for (; frame->fp < 0xdeadbeef; frame = frame->fp - sizeof(struct stackframe) + sizeof(unsigned long)) {printf("Function enter at [<%08x>] from [<%08x>]\n", frame->pc, frame->lr);}
}void f3(int c) {printf("%d\n", c);backtrace();
}
void f2(int b) {f3(b);
}
void f1(int a) {char arr[5] = {0};f2(a);
}int main(int argc, char *argv[]) {printf("programe %s\n", argv[0]);f1(1);return 0;
}
arm-linux-gnueabi-objdump -S unwind > objdump
void backtrace() {8500: e1a0c00d mov ip, sp8504: e92dd810 push {r4, fp, ip, lr, pc}8508: e24cb004 sub fp, ip, #4850c: e24dd00c sub sp, sp, #12
......................................000085c0 <f3>:void f3(int c) {85c0: e1a0c00d mov ip, sp85c4: e92dd800 push {fp, ip, lr, pc}85c8: e24cb004 sub fp, ip, #485cc: e24dd008 sub sp, sp, #8
........................................85dc: ebffff6b bl 8390 <_init+0x20>backtrace();85e0: ebffffc6 bl 8500 <backtrace>
}85e4: e24bd00c sub sp, fp, #1285e8: e89da800 ldm sp, {fp, sp, pc}85ec: 0000878c .word 0x0000878c000085f0 <f2>:
void f2(int b) {85f0: e1a0c00d mov ip, sp85f4: e92dd800 push {fp, ip, lr, pc}85f8: e24cb004 sub fp, ip, #485fc: e24dd008 sub sp, sp, #88600: e50b0010 str r0, [fp, #-16]f3(b);8604: e51b0010 ldr r0, [fp, #-16]8608: ebffffec bl 85c0 <f3>
}860c: e24bd00c sub sp, fp, #128610: e89da800 ldm sp, {fp, sp, pc}00008614 <f1>:
void f1(int a) {8614: e1a0c00d mov ip, sp8618: e92dd800 push {fp, ip, lr, pc}861c: e24cb004 sub fp, ip, #48620: e24dd018 sub sp, sp, #24
..........................................f2(a);8644: e51b0020 ldr r0, [fp, #-32]8648: ebffffe8 bl 85f0 <f2>
}864c: e59f3018 ldr r3, [pc, #24] ; 866c <f1+0x58>00008670 <main>:int main(int argc, char *argv[]) {8670: e1a0c00d mov ip, sp8674: e92dd800 push {fp, ip, lr, pc}8678: e24cb004 sub fp, ip, #4867c: e24dd008 sub sp, sp, #88680: e50b0010 str r0, [fp, #-16]8684: e50b1014 str r1, [fp, #-20]printf("programe %s\n", argv[0]);8688: e51b3014 ldr r3, [fp, #-20]868c: e5933000 ldr r3, [r3]8690: e59f001c ldr r0, [pc, #28] ; 86b4 <main+0x44>8694: e1a01003 mov r1, r38698: ebffff3c bl 8390 <_init+0x20>f1(1);869c: e3a00001 mov r0, #186a0: ebffffdb bl 8614 <f1>return 0;86a4: e3a03000 mov r3, #0
}
上面是样例代码对应的汇编代码截取。在函数的最开头都存在如下代码
8500: e1a0c00d mov ip, sp8504: e92dd810 push {r4, fp, ip, lr, pc}8508: e24cb004 sub fp, ip, #4
就是文章最开始说的函数一开始都会将fp、sp、lr以及pc压栈。那这几个寄存器里面的内容是什么呢?
sp即栈顶指针,sp里面记录的是当前函数的栈顶位置;并且从汇编代码里面能看到先是将sp给ip,然后将ip入栈。因此栈中记录的sp位置是压栈之前的

lr用于保存函数的返回地址(若f2调用f3,那在样例代码中对应的位置就是这一行8558: e89da800 ldm sp, {fp, sp, pc})
pc指针,程序计数器,用于记录当前执行到哪条指令。但是由于ARM采用流水线机制。当正确读取PC时,该值为当前指令(正在执行的指令)地址+8个字节。即PC执行当前指令的下两条地址。所以这就解释了样例代码的打印是0000850c
void backtrace() {
8500: e1a0c00d mov ip, sp
8504: e92dd810 push {r4, fp, ip, lr, pc}//执行到这里时,pc里面记录的是下面两条指令
8508: e24cb004 sub fp, ip, #4
850c: e24dd00c sub sp, sp, #12
......................................具体可以查看这篇文章
ARM体系结构相关杂记_这个我好像学过的博客-CSDN博客
fp:frame pointer:同样也是这段代码。sub fp, ip, #4// fp = ip - 4。那fp其实保存的就是上一个函数的函数栈起始位置-4。这也是for循环里面下一个函数栈需要写为
for (;; frame = frame->fp - sizeof(struct stackframe) + sizeof(unsigned long))
即下一个函数栈是fp + 4 - 12
为什么是上一个函数栈呢?
我们看下面的代码f1调用f2。函数f2最开始压入的fp,这个fp寄存器里面记录的是什么值呢。它里面其实就是上一个函数里面的sub fp, ip, #4得到的啊。ip里面又是上一个函数f1的函数栈开始位置。
000085f0 <f2>:
void f2(int b) {85f0: e1a0c00d mov ip, sp85f4: e92dd800 push {fp, ip, lr, pc}85f8: e24cb004 sub fp, ip, #485fc: e24dd008 sub sp, sp, #88600: e50b0010 str r0, [fp, #-16]f3(b);8604: e51b0010 ldr r0, [fp, #-16]8608: ebffffec bl 85c0 <f3>
}860c: e24bd00c sub sp, fp, #128610: e89da800 ldm sp, {fp, sp, pc}00008614 <f1>:
void f1(int a) {8614: e1a0c00d mov ip, sp8618: e92dd800 push {fp, ip, lr, pc}861c: e24cb004 sub fp, ip, #48620: e24dd018 sub sp, sp, #24
..........................................f2(a);8644: e51b0020 ldr r0, [fp, #-32]8648: ebffffe8 bl 85f0 <f2>
}864c: e59f3018 ldr r3, [pc, #24] ; 866c <f1+0x58>
因此最终的函数栈构成了下图所示。那我怎么感觉文章开始的那张图片是错的呢。。。。

最后样例代码运行结果如下图。由于不知道怎么算回溯结束,所以程序报错了

另外程序打印出来的地址也会汇编代码吻合.具体可以看汇编信息


另外用arm-linux-gnueabi-addr2line解析出来的行号也是准确的
相关文章:
arm 函数栈回溯
大概意思就是arm每个函数开始都会将PC、LR、SP以及FP四个寄存器入栈。 下面我们看一下这四个寄存器里面保存的是什么内存 arm-linux-gnueabi-gcc unwind.c -mapcs -w -g -o unwind(需要加上-mapcs才会严格按照上面说的入栈) #include <stdio.h> …...
30个前端开发中常用的JavaScript函数
🧑💻作者名称:DaenCode 🎤作者简介:啥技术都喜欢捣鼓捣鼓,喜欢分享技术、经验、生活。 😎人生感悟:尝尽人生百味,方知世间冷暖。 前言 在前端开发中通常会用到校验函数…...
基于量子同态加密的改进多方量子私有比较
摘要量子同态加密在隐私保护方面具有明显的优势。本文提出了一种改进的基于量子同态加密的多方量子私钥比较协议。首先,引入可信密钥中心,安全辅助加密密钥的分发和解密密钥的更新,同时防止恶意服务器发布虚假结果的攻击;在保证所有参与者得到…...
解决mysqld服务启动失败
原因如下: 1、进程占用 首先查看下mysql进程: ps -aux | grep mysql有进程号占用了,kill 这个进程号 再重启服务 2、所有者和所属组为mysql 查看/usr/local/MySQL/data/mysqld.pid所有者和所属组是否为mysql 原来是权限有问题,…...
【前端知识】React 基础巩固(四十)——Navigate导航
React 基础巩固(四十)——Navigate导航 一、Navigate的基本使用 新建Login页面,在Login中引入Navigate,实现点击登陆按钮跳转至/home路径下: import React, { PureComponent } from "react"; import { Navigate } from "reac…...
文件IO练习
一、用read函数完成文件大小计算 #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc, const char *argv[]) {int fd open("./1.tx…...
初识FreeRTOS入门,对FreeRTOS简介、任务调度、内存管理、通信机制以及IO操作,控制两个led不同频率闪烁
当代嵌入式系统的开发越来越复杂,实时性要求也越来越高。为了满足这些需求,开发者需要使用实时操作系统(RTOS),其中一个流行的选择是FreeRTOS(Free Real-Time Operating System)。本篇博客将详细…...
STM32CUBUMX配置FLASH(W25Q128)--保姆级教程
———————————————————————————————————— ⏩ 大家好哇!我是小光,嵌入式爱好者,一个想要成为系统架构师的大三学生。 ⏩最近在开发一个STM32H723ZGT6的板子,使用STM32CUBEMX做了很多驱动&#x…...
【Golang 接口自动化04】 解析接口返回JSON串
目录 前言 解析到结构体 json数据与struct字段是如何相匹配的呢? 解析到interface Go类型和JSON类型 实例代码 simpleJson 总结 资料获取方法 前言 上一次我们一起学习了如何解析接口返回的XML数据,这一次我们一起来学习JSON的解析方法。 JSO…...
EPPlus与Microsoft.Office.Interop.Excel的使用区别
文章目录 代码的使用区别EPPlus的工作原理Microsoft.Office.Interop.Excel的使用原理代码的使用区别 static void ExportToExcel(List<(string, double, double)> list, string outputFilePath){//Microsoft.Office.Interop.Excel的使用 /* Excel.Application excelAp…...
ncrack工具使用说明
介绍 网络认证破解工具。 Ncrack是用于网络身份验证破解的开源工具。 它设计为使用可适应不同网络情况的动态引擎进行高速并行破解。 Ncrack还可以针对特殊情况进行广泛的微调,尽管默认参数的通用性足以覆盖几乎所有情况。 它建立在模块化架构上,可以轻松扩展以支持其他协议…...
第二章:进程管理(处理机/CPU管理)
文章目录 2.1 进程与线程1.进程(1)进程的概念、进程的组成、进程的组织(2)进程控制块PCB(3)进程的状态与转换:五状态模型(4)进程控制(5)进程间的通信①共享存储②消息传递③管道通信(6)父进程与子进程(7)进程的内存空间2.线程 Thread(1)线程的概念(2)线程的实现方式:用户级线…...
MySQL中锁的简介——表级锁-元数据锁、意向锁
1.元数据锁 查看元数据锁 select object_type,object_scheme,object_name,lock_type,lock_duration from perfomance_scheme.metadata_locks;2.意向锁 线程A开启事务后在执行update更新语句时候,会给数据加上行锁,加上行锁以后,会对整张表加…...
React几种避免子组件无效刷新的方案
您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~ 前言 一个很常见的场景,React中父组件和子组件在一起,子组件不依赖于父组件任何数据,但是会一起发生变化。 在探究原…...
分享亿款好用的PDF编辑工具
所周知,PDF文件是不能够像word/excel/ppt等文件一样,可以被随意编辑的,PDF文件往往只能够被查看,我们无法对它进行编辑,或者对上面的文字进行复制,也不能任意删除上面的页面。但是很多时候,我们…...
AI生成式视频技术来临:Runway Gen-2文本生成视频
Runway Gen-2的官方网站提供了一种文本生成视频的工具。以下是对该工具的介绍: 文本生成视频:Runway Gen-2是一个创新的在线工具,可以将文本转化为视频。用户只需输入文本描述或句子,Runway Gen-2就能自动生成相应的视频内容。这…...
react钩子函数
React组件的生命周期包括多个阶段和方法,用于在组件不同的生命周期时执行特定的操作。以下是React类组件中常见的生命周期方法: 挂载阶段(Mounting Phase): constructor:组件实例化时调用,用于初…...
RISC-V公测平台发布 · 如何在SG2042上玩转k3s
前言 Kubernetes是一个开源的容器管理平台,通过Kubernetes的跨集群管理功能,用户可以方便地进行应用程序的复制、迁移和跨云平台的部署。 而k3s作为Kubernetes的轻量级发行版,相比传统的Kubernetes具有更小的二进制文件大小和更低的资源消耗…...
Linux系统常见小问题
1、新系统在输入命令时,不会自动提示,按上箭头(↑)和下向下箭头(↓)不会匹配之前的输入 以CentOS 为例,可以通过配置 ~/.bashrc 文件来实现按向上箭头显示最相近的命令。以下是具体的实现步骤 …...
WEB:mfw
背景知识 Git泄露 Githack使用 命令执行漏洞 题目 这里页面里有Git,猜测是Git泄露 先用dirsearch扫一下 确实存在.git目录,可以尝试访问一下 使用Githack来下载并恢复.git文件 这里记得使用的时候关闭杀毒软件 结果会自动保存 点进去先看一下flag这个…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
