spsr 的恢复出错,导致 thumb 指令集的 it 条件运行指令运行异常,清晰的调试思路帮助快速解决问题

记一次调试过程

这是一个在 arm 架构上的 RTOS 上的调试过程。问题现象为使用 thumb 指令集的 libgcc 库的情况下,浮点运算随机出错。经过一番追踪调试,逐步缩小问题范围,最后定位问题,成功解决。

场景

在某款的国产 RTOS 上,由于客户应用需要,使用了thumb 指令集编译的 libgcc 的库,导致了同时运行了 arm 指令集和 thumb 指令集的代码。原本的 RTOS 未设计运行 thumb 指令的代码,也没有严格测试过,所以本次暴露了浮点运算偶尔出错的问题。

测试代码

客户精简过的暴露问题代码如下

unsigned long long   ulTa = 0x100;
unsigned long long   ulTb = 0x2001;
double dRea, dReb;
double dRes;while (1) {= (double)ulTa;dReb = (double)ulTb;dRes = dRea / dReb;if ((dRea == 0.0) || (dReab == 0.0) || (c <= 0.01f)) {printf("error!!\r\n");}}

这段代码的逻辑是强转了两个 unsigned long long 类型,然后进行除法运行,最后判断各个变量的结果是否符合预期,否则就报 error。

问题分析

抓取问题中的关键点:

  1. 和浮点运算有关,是不是我们的RTOS对于浮点运算的寄存器没有处理好
  2. 简单的代码反复运行只是偶尔出问题,而不是固定每次出问题,那么考虑其他外部的影响,首先考虑的是中断。

问题调试

bug调试的指导思想是,仔细观察问题剖析问题,提出怀疑一些原因,并修改代码验证

步骤1 剖析问题

靠近问题的实质,分析问题:既然计算出错,那么看看出错的时候的变量是什么样的。经过一些调试打印发现 3 个 double 类型的变量都出错过。出错的时候,打印这些变量对应地址存储的值,发现这些内存的值有 0 存在。因为 double 变量使用 8 bytes 的空间存放的,这些空间里存放的不是实际值,而是分符号位,指数位等。
根据这个现象,我有理由怀疑是某些时候的强转没有成功。

步骤2 证明和中断的相关性

进行关中断测试,在关闭中断的情况下,长时间跑了测试,时间足够长,发现并没有出错。那么证明的确和中断有关系。

步骤3 证明测试代码的逻辑是没有问题

排查其他问题:使用 arm 指令集的 libgcc 进行测试,使用同样的代码,时间足够长,也没有出错。那么证明测试代码的逻辑是没有问题的。

陷入了第一个瓶颈

确定中断有问题,那么推测偶尔出问题肯定是恰好某个时刻的中断踩到了特殊位置。修改代码关注是否是中断返回立即出错,打印分析被中断打断处的指令,观察是否是固定的某个指令。经过漫长的调试,观察到的确有规律。
分析长时间的出错前的 PC 指针的位置,很多是靠近 thumb 指令集里面的 IT block 指令。必须敏锐地抓住这个点。类似下面的 it eq 指令。

001007d4 <__aeabi_ul2d>:1007d4:	ea50 0201 	orrs.w	r2, r0, r11007d8:	bf08      	it	eq1007da:	4770      	bxeq	lr1007dc:	b530      	push	{r4, r5, lr}1007de:	f04f 0500 	mov.w	r5, #01007e2:	e00a      	b.n	1007fa <__aeabi_l2d+0x16>

简化问题

看手册学习 it block 指令。
// 学习过程不表

证明是 it 指令的问题

调试指导思想是:缩小问题的范围
想办法直接运行这个指令,观察是否出错。我使用内联汇编实现,使用伪代码解释代码逻辑

r1 = 0x10;
r2 = 0x10;
r3 = 0x20;
r4 = 0x40;r4 = r4 - r2;  //运行后 r4 = 0x30// itte ge , 上一行指令会改变 cpsr 的 flag 标志位,
// 若 ge: r1 = r1 + (r4 << 1)  //运行后 r1 = 0x70;
// 若 ge: r2 = r2 + 0x10;
// 若 lt: r4 = r4 + 0x10;

代码如下:

__attribute__((target("thumb"))) void thumb_ins(void);
void thumb_ins(void) {int ia = 0;int ib = 0;int ic = 0;int  cnt = 0;while (1) {ia = 0;ib = 0;ic = 0;asm volatile ("mov r1, #0x10");asm volatile ("mov r3, #0x20");asm volatile ("mov r2, #0x10");asm volatile ("mov r4, #0x40");asm volatile ("subs	r4, r4, r2");asm volatile ("itte  ge");asm volatile ("addge.w	r1, r1, r4, lsl #1");asm volatile ("addge r2, r2, #0x10");asm volatile ("addlt  r4, r4, #0x10");asm volatile ("mov %0, r1" : "=r" (ia));asm volatile ("mov %0, r2" : "=r" (ib));asm volatile ("mov %0, r4" : "=r" (ic));if ((ia != 0x70) || (ib != 0x20) || (ic != 0x30)) {printf("error!!!");printf(" r1 [0x%x] r2 [0x%x] r4[0x%x]\r\n", ia, ib, ic);}if (cnt ++ > 9000000) {printf(" r1 [0x%x] r2 [0x%x] r4[0x%x]\r\n", ia, ib, ic);cnt = 0;}}
}

经过运行测试,这个代码运行时,也会打印出 error;关闭中断进行测试,则运行不会打印 error。则完全证明了是 it 指令执行的问题。并且发现了出错时, it block 控制的条件允许代码全部没有执行,包括2条判断条件相反的指令都没有执行。

陷入了第二个瓶颈 分析 it 指令执行出错的原因

arm 手册上关于这个 it 指令从异常返回的描述:
On a branch or an exception return, if PSTATE.IT is set to a value that is not consistent with the instruction stream being branched to or returned to, then instruction execution is CONSTRAINED UNPREDICTABLE.
我们需要关注到异常返回的地址和异常返回时 PSTATE.IT 的标志状态。

  • 经过调试分析,已经确认我们的 RTOS 在异常保存上下文中 cpsr 的值是正确的。使用 msr 将其恢复也是正确的。也确认了我们RTOS配置的返回地址也是正确的。满足手册中提到的注意事项,问题陷入僵局。
  • 试图对比 Linux 源码中的异常保存和恢复过程中,对于 thumb 指令是否有特殊处理,然而并没有收获。

问题突破

经过艰难的调试之后,终于有了新的突破
我们知道,在异常返回时,是由硬件自动将 spsr 寄存器中的值恢复到 cpsr。我在设置PC跳转之前,读出 spsr 的值,保存到内存中,然后有了惊人的发现:此时 spsr 寄存器中的内容和上下文结构体中的值不一致,而且是关键的 IT block 的信息丢失。那么断定是恢复 spsr 的时候出错了。
经过简单调试,和 Linux 进行对比,发现了问题的根源。

问题解决

我们的 RTOS 恢复 spsr 使用的指令为 msr spsr, r1 ,编译生成的指令为 83ce0ae8: e169f001 msr SPSR_fc, r1。而 Linux 使用 msr spsr_cxsf, r1, 编译生成的指令为 83ce0ae8: e16ff001 msr SPSR_fsxc, r1。我的 RTOS 在恢复 spsr 中,丢失了关键的 s 和 x 域的内容:

  • c 指 CPSR中的control field ( PSR[7:0])
  • f 指 flag field (PSR[31:24])
  • x 指 extend field (PSR[15:8])
  • s 指 status field ( PSR[23:16])
    而恰好 IT block 信息存放在 [26:25] + [15:10]
    it block
IT[1:0], bits [26:25] 
IT block state bits for the T32 IT (If-Then) instruction. See IT[7:2] for explanation of this field.
IT[7:2], bits [15:10] 
IT block state bits for the T32 IT (If-Then) instruction. This field must be interpreted in two parts. • IT[7:5] holds the base condition for the IT block. The base condition is the top 3 bits of the 
condition code specified by the first condition field of the IT instruction.
• IT[4:0] encodes the size of the IT block, which is the number of instructions that are to be 
conditionally executed, by the position of the least significant 1 in this field. It also encodes the value of the least significant bit of the condition code for each instruction in the block.
The IT field is 0b00000000 when no IT block is active.

调试经验总结

  1. 分析问题,缩小问题范围
  2. 敏锐观察,抓住灵感
  3. 验证寄存器的值是否正确不能读中间状态,必须紧贴使用之前进行验证
  4. 相信硬件,在计算机的世界没有上帝

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

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

相关文章

使用Flask构建POST请求的Web应用

文章目录 准备工作创建路由处理POST请求创建表单页面运行应用结论 在Web开发中&#xff0c;处理POST请求是一项常见任务&#xff0c;特别是在构建表单提交、用户注册和数据提交等功能时。Flask是一个简单而强大的Python Web框架&#xff0c;它提供了方便的工具来处理HTTP请求&a…

Spring编程使用DDD的小把戏

场景 现在流行充血领域层&#xff0c;在原本只存储对象的java类中&#xff0c;增加一些方法去替代原本写在service层的crud&#xff0c; 但是例如service这种一般都是托管给spring的&#xff0c;我们使用的ORM也都托管给spring&#xff0c;这样方便在service层调用mybatis的m…

数据结构绪论

1.数据&#xff1a;是客观事物的符号表示&#xff0c;是一切能够输入到计算机中&#xff0c;并被计算机处理的符号的总称。 数据元素&#xff1a;数据的基本单位。 数据项&#xff1a;是数据元素中独立的&#xff0c;最小的单位。 数据对象&#xff1a;是性质相同的数据元素…

OSTE-Web-Log-Analyzer:基于Python的Web服务器日志自动化分析工具

关于OSTE-Web-Log-Analyzer OSTE-Web-Log-Analyzer是一款功能强大的Web服务器日志自动化分析工具&#xff0c;该工具专为安全研究人员设计&#xff0c;能够使用Python Web日志分析工具&#xff08;Python Web Log Analyzer&#xff09;帮助广大研究人员以自动化的形式实现Web服…

云动态摘要 2024-05-12

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 [免费试用]即刻畅享自研SaaS产品 腾讯云 2024-04-25 涵盖办公协同、营销拓客、上云安全保障、数据分析处理等多场景 云服务器ECS试用产品续用 阿里云 2024-04-14 云服务器ECS试用产品续用…

Linux学习笔记7---仿STM32自建寄存器库

为了开发方便&#xff0c;ST 官方为 STM32F103 编写了一个叫做 stm32f10x.h 的文件&#xff0c;在这个文件里面定义了 STM32F103 所有外设寄存器。而有些芯片是没有这种寄存器库的&#xff0c;在没有的情况下要学会自己建立一个寄存器库。NXP 官方并没有为 I.MX6UL 编写类似 st…

Linux与windows网络管理

文章目录 一、TCP/IP1.1、TCP/IP概念TCP/IP是什么TCP/IP的作用TCP/IP的特点TCP/IP的工作原理 1.2、TCP/IP网络发展史1.3、OSI网络模型1.4、TCP/IP网络模型1.5、linux中配置网络网络配置文件位置DNS配置文件主机名配置文件常用网络查看命令 1.6、windows中配置网络CMD中网络常用…

springboot中mybatisplus注意事项

使用代码生成工具CodeGenerator 需要修改的内容 dsc.setUsername(“root”); mysql账号dsc.setPassword(“root”); mysql密码strategy.setInclude(“crm_edu”); 表名pc.setModuleName(“eduservice”); //模块名 package com.test.demo;import com.baomidou.mybatisplus.a…

Flink checkpoint 源码分析- Checkpoint snapshot 处理流程

背景 在上一篇博客中我们分析了代码中barrier的是如何流动传递的。Flink checkpoint 源码分析- Checkpoint barrier 传递源码分析-CSDN博客 最后跟踪到了代码org.apache.flink.streaming.runtime.io.checkpointing.CheckpointedInputGate#handleEvent 现在我们接着跟踪相应…

面向电商家居行业3D室内场景合成中的空间感知

本文主要介绍了3D场景合成技术在电商领域&#xff0c;尤其是家居家装行业的应用。它解释了如何使用3D场景合成创建逼真的室内设计&#xff0c;让消费者能够交互式地查看和体验产品&#xff0c;提高购物的趣味性和效率。文章提到了两种主要的3D室内场景生成算法&#xff1a;传统…

Electron | 桌面应用的开发神器

初探 Electron 教程将介绍 Electron 打包应用的全过程&#xff0c;从本地测试&#xff0c;打包&#xff0c;到 GitHub 自动化。讲解 Electron Forge 和 Electron Builder 的用法&#xff0c;以及如何在 GitHub Actions 中自动化生成和发布应用。 官方资源 Electron Document…

Vue.js【路由】

初识路由 提到路由&#xff08;Route&#xff09;&#xff0c;一般我们会联想到网络中常见的路由器&#xff08;Router&#xff09;&#xff0c;那么路由和路由器之间有什么关联呢&#xff1f;路由是指路由器从一个接口接收到数据&#xff0c;根据数据的目的地址将数据定向传送…

Linux开发--Linux内核开发移植

Linux内核开发移植 Linux内核版本变迁及其获得 Linux是最受欢迎的自由电脑操作系统内核&#xff0c; 是一个用C语言写成&#xff0c; 并且符合POSIX标准的类Unix操作系统 Linux是由芬兰黑客Linus Torvalds开发的&#xff0c; 目的是尝试在英特尔x86架构上提供自由免费的类Un…

IDEA及Maven配置代理及Maven中央仓库配置详解

一、配置代理 首先&#xff0c;需要本地开启代理入口&#xff0c;如图。 这个跟你使用代理软件有关。像我使用的是qv2ray。 其次&#xff0c;idea配置代理&#xff0c;如图。 1.1 idea配置代理 打开Settings&#xff0c;如图 1.2 maven配置代理 maven配置代理&#xff0c;修…

[单机]完美国际_V155_GM工具_VM虚拟机

[端游] 完美国际单机版V155一键端PC电脑网络游戏完美世界幻海凌云家园 本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是…

易图讯三维电子沙盘-大数据处理服务

易图讯科技10名高级大数据工程师&#xff0c;高效、快速进行POI、DEM、高清卫星影像、地形地貌、路网、矢量地图等海量大数据处理服务。 免费专业提供POI、AOI、DEM、高清卫星影像、地形地貌、路网、矢量地图等海量大数据处理服务。 1年更新2次POI、高清卫星影像。

Unity基础

概述 基础知识 3D教学 数学计算公共类Mathf 练习: 三角函数 练习&#xff1a; Unity中的坐标系 Vector3向量 向量模长和单位向量 向量加减乘除 练习&#xff1a; 向量点乘 向量叉乘 向量插值运算 Quaternion四元数 为何要使用四元数 四元数是什么 四元数常用方法 四元数计算 练…

怎么制作流程图?介绍制作方法

怎么制作流程图&#xff1f;在日常生活和工作中&#xff0c;流程图已经成为我们不可或缺的工具。无论是项目规划、流程优化&#xff0c;还是学习理解复杂系统&#xff0c;流程图都能帮助我们更直观地理解和表达信息。然而&#xff0c;很多人可能并不清楚&#xff0c;其实制作流…

cmu15445 2023fall project3 详细过程(下)QUERY EXECUTION

QUERY EXECUTION task3/task4 Task #3 - HashJoin Executor and Optimization1、HashJoin1.1 思路1.2 代码 2 NestedLoopJoin优化为HashJoin2.1 思路2.2 代码 Task #4 Sort Limit Executors Top-N Optimization Window Functions1、Sort1.1 思路1.2 代码 2、Limit Executors2…

CCPD车牌检测识别数据集

CCPD 是一个在开源免费的中国城市车牌识别数据集。 1. 介绍 CCPD (Chinese City Parking Dataset, ECCV)是中国城市车牌数据集&#xff0c;共有两个CCPD2019和CCPD2020 数据集&#xff0c;总数据量约35W左右&#xff0c;可用于车牌检测和识别模型算法开发。 CCPD 发表的论文:…