arm 函数栈回溯

news2024/11/26 19:30:09

 大概意思就是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里面保存的是还未压栈的sp
    printf("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, sp
    8504:	e92dd810 	push	{r4, fp, ip, lr, pc}
    8508:	e24cb004 	sub	fp, ip, #4
    850c:	e24dd00c 	sub	sp, sp, #12
......................................

000085c0 <f3>:

void f3(int c) {
    85c0:	e1a0c00d 	mov	ip, sp
    85c4:	e92dd800 	push	{fp, ip, lr, pc}
    85c8:	e24cb004 	sub	fp, ip, #4
    85cc:	e24dd008 	sub	sp, sp, #8
........................................
    85dc:	ebffff6b 	bl	8390 <_init+0x20>
	backtrace();
    85e0:	ebffffc6 	bl	8500 <backtrace>
}
    85e4:	e24bd00c 	sub	sp, fp, #12
    85e8:	e89da800 	ldm	sp, {fp, sp, pc}
    85ec:	0000878c 	.word	0x0000878c

000085f0 <f2>:
void f2(int b) {
    85f0:	e1a0c00d 	mov	ip, sp
    85f4:	e92dd800 	push	{fp, ip, lr, pc}
    85f8:	e24cb004 	sub	fp, ip, #4
    85fc:	e24dd008 	sub	sp, sp, #8
    8600:	e50b0010 	str	r0, [fp, #-16]
	f3(b);
    8604:	e51b0010 	ldr	r0, [fp, #-16]
    8608:	ebffffec 	bl	85c0 <f3>
}
    860c:	e24bd00c 	sub	sp, fp, #12
    8610:	e89da800 	ldm	sp, {fp, sp, pc}

00008614 <f1>:
void f1(int a) {
    8614:	e1a0c00d 	mov	ip, sp
    8618:	e92dd800 	push	{fp, ip, lr, pc}
    861c:	e24cb004 	sub	fp, ip, #4
    8620:	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, sp
    8674:	e92dd800 	push	{fp, ip, lr, pc}
    8678:	e24cb004 	sub	fp, ip, #4
    867c:	e24dd008 	sub	sp, sp, #8
    8680:	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, r3
    8698:	ebffff3c 	bl	8390 <_init+0x20>
	f1(1);
    869c:	e3a00001 	mov	r0, #1
    86a0:	ebffffdb 	bl	8614 <f1>
	return 0;
    86a4:	e3a03000 	mov	r3, #0
}

 上面是样例代码对应的汇编代码截取。在函数的最开头都存在如下代码

    8500:	e1a0c00d 	mov	ip, sp
    8504:	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, sp
    85f4:	e92dd800 	push	{fp, ip, lr, pc}
    85f8:	e24cb004 	sub	fp, ip, #4
    85fc:	e24dd008 	sub	sp, sp, #8
    8600:	e50b0010 	str	r0, [fp, #-16]
	f3(b);
    8604:	e51b0010 	ldr	r0, [fp, #-16]
    8608:	ebffffec 	bl	85c0 <f3>
}
    860c:	e24bd00c 	sub	sp, fp, #12
    8610:	e89da800 	ldm	sp, {fp, sp, pc}

00008614 <f1>:
void f1(int a) {
    8614:	e1a0c00d 	mov	ip, sp
    8618:	e92dd800 	push	{fp, ip, lr, pc}
    861c:	e24cb004 	sub	fp, ip, #4
    8620:	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解析出来的行号也是准确的

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Scaling Instruction-Finetuned Language Models

Paper name Scaling Instruction-Finetuned Language Models Paper Reading Note Paper URL: https://arxiv.org/pdf/2210.11416.pdf TL;DR 2022 年谷歌出的文章&#xff0c;对指令微调的影响因素进行分析&#xff0c;提出了一些提升指令微调效果的方案。与该文章一起出品…

AI学习笔记四:yolov5训练自己的数据集

若该文为原创文章&#xff0c;转载请注明原文出处。 一般情况下&#xff0c;大部分人的电脑都是没有cpu的&#xff0c;cpu也是可以训练的&#xff0c;但花费的时间太长&#xff0c;实际200张图片&#xff0c;使用CPU训练300轮花了3天&#xff0c;本章记录使用云服务器来训练自…

【无废话解决bug】python dash库 127.0.0.1 拒绝了我们的连接请求

无废话。 Q: python dash库在127.0.0.1无法打开–访问本地IP时显示拒绝访问 在python3写好了代码&#xff0c;打算运行代码后在浏览器127.0.0.1查看&#xff0c;浏览器提示&#xff1a;127.0.0.1 拒绝了我们的连接请求。 A:【本解决方法对dash库问题适用&#xff0c;其他涉及…

【前端知识】React 基础巩固(三十四)——组件中的异步操作及优化

React 基础巩固(三十四)——组件中的异步操作及优化 一、待优化的异步请求流程 通过组件的生命周期来完成网络请求&#xff0c;网络请求的异步代码直接放在组件中 import React, { PureComponent } from "react"; import { connect } from "react-redux"…

OLED透明屏的安装价格与安装步骤

OLED透明屏的安装价格因各种因素而异&#xff0c;例如屏幕尺寸、分辨率、透明度等。一般来说&#xff0c;安装OLED透明屏需要考虑到以下几个步骤&#xff1a; 准备表面&#xff1a;首先&#xff0c;需要清理屏幕表面以确保透明度。然后&#xff0c;需要钻孔以安装屏幕。 安装框…

Java在线OJ项目(一)、多进程编程实现 做题代码的编译和运行

在线OJ项目&#xff08;一&#xff09;、多进程编程实现 做题代码的编译和运行 一、回顾线程和进程二、进程比线程的优势三、多进程编程样例四、多进程思想 实现对代码 的编译 以及 运行两个功能CommandUtil 由于我们是在线oj&#xff0c;所以得编译用户的代码不仅编译 还需要 …

TEE GP(Global Platform)安全认证方案

TEE之GP(Global Platform)认证汇总 一、安全认证介绍 GlobalPlatform的安全认证计划通过独立的安全评估&#xff0c;确认安全组件是否符合通用标准认可的Protection Profile。它确保安全组件满足为特定服务定义的所需安全级别&#xff0c;使服务提供商能够自信有效地管理风险并…

技术干货|详解威胁情报在WAF类产品中如何发挥最大价值?

在产业互联网发展的过程中&#xff0c;企业也将面临越来越多的安全风险和挑战&#xff0c;威胁情报在企业安全建设中的参考权重大幅上升。早期安全产品对威胁的鉴定是“一维”的&#xff1a;鉴定文件是黑&#xff08;恶意文件&#xff09;、白&#xff08;正常文件&#xff09;…

数据结构初阶--带头双向循环链表

目录 一.带头双向循环链表的定义 二.带头双向循环链表的功能实现 2.1.带头双向循环链表的定义 2.2.带头双向循环链表的结点创建 2.3.带头双向循环链表的初始化 2.4.带头双向循环链表的打印 2.5.带头双向循环链表的判空 2.6.带头双向循环链表的尾插 2.7.带头双向循环链…

要做接口并发性能测试,总得先学会分析吧!

引言 这篇是我3月份在公司内部做的技术分享内容&#xff0c;由于我在公司内部分享的内容较多以及一些特殊性&#xff0c;我摘取了接口并发测试从设计思路整理→测试方案设计→设计分析→代码编写这套流程&#xff08;我不会承认我把40多页PPT的内容都放在这篇博文里&#xff0…

高压放大器在无线电能传输中的作用和用途

高压放大器是一种电子放大器&#xff0c;主要用于放大输入信号的幅度&#xff0c;通过输出端输出一个较大的电压信号&#xff0c;以达到强化原始信号的功能。在无线电能传输中&#xff0c;高压放大器扮演着非常重要的角色。本文将从无线电能传输的定义、高压放大器的特性以及高…

mysql数据备份与还原进阶操作

创建以下表 创建普通用户backup并给权限 备份数据库school到/backup目录 mysqldump -u backup -p school > /backup/school.sql备份MySQL数据库为带删除表的格式&#xff0c;能够让该备份覆盖已有数据库而不需要手动删除原有数据库 mysqldump -u backup -p --add-drop-d…

mysql mha高可用

目录 1.MHA是什么以及作用 2.工作原理 3.案例 1.MHA是什么以及作用 MHA(Master High Availability) MySQL高可用环境下故障切换和主从复制的软件 MHA 的出现就是解决MySQL单点故障 MySQL故障切换过程中 MHA能做到0-30秒内自动完成故障切换操作 MHA能在故障切换的过程中最大程…

行业追踪,2023-07-26,如果主力不骗人,化工原料和磷化工有第一波机会

自动复盘 2023-07-26 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

1.java入门(基础概念与常识)

文章目录 1.1.java语言有哪些特点&#xff1f;1.2.关于JVM JDK和JRE最详细通俗的解答1.2.1JVM1.2.2 JDK和JRE 1.3. Oracle JDK 和 OpenJDK 的对比1.4. Java 和 C的区别?1.5. import java 和 javax 有什么区别&#xff1f;1.6. 为什么说 Java 语言“编译与解释并存”&#xff1…

Docker仓库

Docker仓库 一、docker Harbor简介 Harbor 是 VMware 公司开源的企业级 Docker Registry 项目&#xff0c;其目标是帮助用户迅速搭建一个企业级的 Docker Registry 服务。Harbor以 Docker 公司开源的 Registry 为基础&#xff0c;提供了图形管理 UI 、基于角色的访问控制(Rol…

Python 基础(十八):异常处理

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录 一、异常是什么&#xff1f;二、异常处理的基本语法三、捕获特定的异常类型四、finall…

P3183 [HAOI2016] 食物链

题目描述 如图所示为某生态系统的食物网示意图&#xff0c;据图回答第 1 小题 现在给你 n 个物种和 m 条能量流动关系&#xff0c;求其中的食物链条数。物种的名称为从 1 到 n 编号 M 条能量流动关系形如 1,2,3​,…,am−1​,bm−1​,am​,bm​。其中 ai​ 和 bi​ 表示能量从物…

《零基础入门学习Python》第067讲:GUI的终极选择:Tkinter4

今天我们来学习 Entry 组件&#xff0c;也就是我们平时所说的 输入框。 输入框是跟程序打交道的途径&#xff0c;比如 程序要求你输入 账号 和 密码。那么它就要提供两个输入框&#xff0c;并且接收密码的输入框还会用 星号 * 将实际的内容给隐藏起来。 我们学了还几个 tkint…

uniapp 微信小程序 navigationBarBackgroundColor 标题栏颜色渐变

大体思路&#xff1a; 第一步&#xff1a;“navigationStyle”:“custom” 第二步&#xff1a; template内 重点&#xff1a;给view添加ref“top” 第三步&#xff1a;添加渐变色样式 1、pages.json {"path" : "pages/user/user","style" : …