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>

因此最终的函数栈构成了下图所示。那我怎么感觉文章开始的那张图片是错的呢。。。。

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

 

 后面看了一下这几个变量可以得到代码段、数据段等的结束地址。这几个符号都是由ld链接器产生的。感觉回溯结束可以依据这几个变量

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

 

 另外用arm-linux-gnueabi-addr2line解析出来的行号也是准确的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/73648.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rpc异步日志模块

Rpc异步日志模块作用 在一个大型分布式系统中&#xff0c;任何部署的分布式节点都可能发生崩溃&#xff0c;试想如果用普通的办法&#xff0c;即先排查哪个节点down掉了&#xff0c;找到down掉的节点后采取调试工具gdb调试该节点&#xff0c;进而排查宕机的原因。这中排查方法…

文心一言 VS 讯飞星火 VS chatgpt (70)-- 算法导论6.5 9题

九、请设计一个时间复杂度为 (n lgk)的算法&#xff0c;它能够将 k 个有序链表合并为一个有序链表&#xff0c;这里 n 是所有输入链表包含的总的元素个数。(提示:使用最小堆来完成 k 路归并。 文心一言&#xff1a; 要设计一个时间复杂度为 O(n log k) 的算法&#xff0c;将 …

leetcode----JavaScript 详情题解(1)

目录 2618. 检查是否是类的对象实例 2619. 数组原型对象的最后一个元素 2620. 计数器 2621. 睡眠函数 2622. 有时间限制的缓存 2623. 记忆函数 2625. 扁平化嵌套数组 2626. 数组归约运算 2627. 函数防抖 2618. 检查是否是类的对象实例 请你编写一个函数&#xff0c;…

Python 程序设计入门(001)—— 安装 Python(Windows 操作系统)

Python 程序设计入门&#xff08;001&#xff09;—— 安装 Python&#xff08;Windows 操作系统&#xff09; 目录 Python 程序设计入门&#xff08;001&#xff09;—— 安装 Python&#xff08;Windows 操作系统&#xff09;一、下载 Python 安装包二、安装 Python三、测试&…

Android四大组件之服务

为什么要使用服务呢&#xff1f; 从上面的文字说&#xff0c;我们知道这个服务是用于执行长期后台运行的操作。有些时候&#xff0c;我们没有界面&#xff0c;但是程序仍然需要工作。比如说&#xff0c;我们播放音乐&#xff0c;在后台播放音乐。比如说&#xff0c;我们下载任…

SpringBoot内嵌的Tomcat:

SpringBoot内嵌Tomcat源码&#xff1a; 1、调用启动类SpringbootdemoApplication中的SpringApplication.run()方法。 SpringBootApplication public class SpringbootdemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootdemoApplicat…

机器学习十大经典算法

机器学习算法是计算机科学和人工智能领域的关键组成部分&#xff0c;它们用于从数据中学习模式并作出预测或做出决策。本文将为大家介绍十大经典机器学习算法&#xff0c;其中包括了线性回归、逻辑回归、支持向量机、朴素贝叶斯、决策树等算法&#xff0c;每种算法都在特定的领…

基于深度学习的高精度80类动物目标检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度80类动物目标检测识别系统可用于日常生活中或野外来检测与定位80类动物目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的80类动物目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YO…

STM32入门学习之外部中断

1.STM32的IO口可以作为外部中断输入口。本文通过按键按下作为外部中断的输入&#xff0c;点亮LED灯。在STM32的19个外部中断中&#xff0c;0-15为外部IO口的中断输入口。STM32的引脚分别对应着0-15的外部中断线。比如&#xff0c;外部中断线0对应着GPIOA.0-GPIOG.0&#xff0c;…

WEB:xff_referer

前提知识 xxf referer 题目 直接在请求头里添加&#xff0c;然后重放后显示内容为 修改referer payload Referer:https://www.google.com 得到flag

AcWing244. 谜一样的牛(树状数组)

输入样例&#xff1a; 5 1 2 1 0输出样例&#xff1a; 2 4 5 3 1 解析&#xff1a; 从后往前遍历&#xff0c;每次需要在剩余的数中&#xff0c;找到第 h[ i ]1 大的数即为当前牛的身高。 每次二分&#xff0c;然后求前缀和。 #include<bits/stdc.h> using namespace …

回归预测 | MATLAB实现SO-CNN-GRU蛇群算法优化卷积门控循环单元多输入单输出回归预测

回归预测 | MATLAB实现SO-CNN-GRU蛇群算法优化卷积门控循环单元多输入单输出回归预测 目录 回归预测 | MATLAB实现SO-CNN-GRU蛇群算法优化卷积门控循环单元多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现SO-CNN-GRU蛇群算法优化卷积门控循…

消息队列——rabbitmq的不同工作模式

目录 Work queues 工作队列模式 Pub/Sub 订阅模式 Routing路由模式 Topics通配符模式 工作模式总结 Work queues 工作队列模式 C1和C2属于竞争关系&#xff0c;一个消息只有一个消费者可以取到。 代码部分只需要用两个消费者进程监听同一个队里即可。 两个消费者呈现竞争关…

【1.2】Java微服务:SpringCloud概论

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。 &#x1f34e;个人主页&#xff1a;Meteors.的博客 &#x1f49e;当前专栏&#xff1a; 微服务 ✨特色专栏&#xff1a; 知识分享 &#x…

纯css实现登录表单动效

效果图&#xff1a; 代码展示 // 我这边用的是elementUI表单校验&#xff0c;更改的样式。 <el-form:model"form":rules"rules"ref"fromList":hide-required-asterisk"true"><el-form-item prop"account"><…

【phaser微信抖音小游戏开发005】画布上添加图片

特别注意&#xff1a;真机模拟的时候&#xff0c;尽量使用网络图片资源&#xff0c;不要在小程序源文件里面使用图片&#xff0c;会出现真机加载不成功&#xff0c;小程序包体积过大的问题。我们学习过程中&#xff0c;只是作为演示使用。 推荐使用场景&#xff1a; 背景图片…

vue3引入video.js

一.引入video.js yarn add video.js videojs-player/vue --save 或者 npm install video.js videojs-player/vue --save 二.vue3项目main.js引入 import VueVideoPlayer from "videojs-player/vue" import "video.js/dist/video-js.css" const app cr…

W6100-EVB-PICO做DNS Client进行域名解析

前言 在上一章节中我们用W6100-EVB-PICO通过dhcp获取ip地址&#xff08;网关&#xff0c;子网掩码&#xff0c;dns服务器&#xff09;等信息&#xff0c;给我们的开发板配置网络信息&#xff0c;成功的接入网络中&#xff0c;那么本章将教大家如何让我们的开发板进行DNS域名解…

AP5179 高端电流采样降压恒流驱动IC SOP8 LED车灯电源驱动

产品描述 AP5179是一款连续电感电流导通模式的降压恒流源&#xff0c;用于驱动一颗或多颗串联LED输入电压范围从 5 V 到 60V&#xff0c;输出电流 最大可达 2.0A 。根据不同的输入电压和外部器件&#xff0c; 可以驱动高达数十瓦的 LED。内置功率开关&#xff0c;采用高端电流…

MySQL性能优化

索引下推是什么&#xff1f; 索引下推是索引下推是 MySQL 5.6 及以上版本上推出的&#xff0c;用于对查询进行优化。 索引下推是把本应该在 server 层进行筛选的条件&#xff0c;下推到存储引擎层来进行筛选判断&#xff0c;这样能有效减少回表。 举例说明&#xff1a; 首先…