新闻  |   论坛  |   博客  |   在线研讨会
一个半小时,远程解决一个 APP跳转 BUG
鱼鹰谈单片机 | 2022-03-19 10:34:58    阅读:521   发布文章

昨天有一位读者刚关注鱼鹰,并且很快加了鱼鹰微信,描述了以下问题:

图片

图片

当时也没有细看问题,只是大概看了一下,然后简单的提供了一个思路给对方,毕竟鱼鹰也比较忙,不可能花太多时间在别人的问题上。


而且也是看对方的问题描述比较清楚,比较有诚意,所以才会提供一个思路,否则可能懒得回复了。所以大家问问题的时候一定要直接详细描述,否则没人不愿意回答。毕竟很多人问完并回复之后,可能一句谢谢都没有就没有下文了,这种事情见多了。好像别人帮助你是理所应当的。


只是没想到,对方会提出给报酬,所以看他挺有诚意的,就开玩笑的问问多少钱。然后开始报价,并且对方也同意我的报价,并愿意多给一倍。

图片


既然他那么有诚意,晚上也没啥事情,所以还是按自己的报价(有原则的鱼鹰)确定下来 20 点左右开始远程解决问题。


8 点半左右开始正式远程解决,原本以为半小时能搞定,实际上花了一个半小时。这里稍微总结一下。


首先是让对方复现问题,然后和正常的情况一并复现,这里花了几分钟,确定了好的代码与不好的代码不同现象。


这里可能有点奇怪,为啥有好的代码(boot 能正常跳转到 APP),还要找鱼鹰解决,鱼鹰开始也有点纳闷,后来才知道,原来好的代码只要稍微改点代码,就不能正常跳转了,这肯定是不行的,因此找上了鱼鹰(其实描述也说了,只是鱼鹰没太在意)。


根据目前了解的情况来看,怎么说也是 APP 的问题,毕竟 Boot 有成功跳转的情况。


开始的时候,鱼鹰的方向确实在 APP 上,还仔细的看了看 APP 的编译警告问题(很多人不注意警告,总是在工程里面遗留很多警告,虽然很多情况下,这些警告不会有问题,但是万一呢。所以建议大家尽量解决工程的警告, 0 error 0 warning还是相当舒服的),这里也花了几分钟确认,发现确实无关大雅。

图片


之后又是尝试了几次好代码与坏代码的差别,发现在跳转代码部分,没有任何区别,跳转地址: 0x00010000 栈顶地址: 0xE000ED08(通过汇编代码分析)。


但这个反常的现象却引起了鱼鹰的警惕, 因为据鱼鹰了解到的 Cortex-M3  内核知识《STM32固件升级之基础知识(一)》,两个不同的 APP 这两个地址大概率是不同的。


虽然它是 M33 的内核,但也不应该差别那么大。


而且内存地址也不应该是 0xE000 开头的,所以又是查 map 文件,又是看链接文件,又是上网(鱼鹰对 M33 的跳转不熟悉,所以需要了解情况,否则解决时间可以缩短不少),又是和提问者沟通,最终得到以下信息:


1、两个代码确实不同(这个可以确定编译没有问题),通过 map 可以分析出来,但为什么一份可以,一份不可以?boot的跳转地址和栈顶地址还一样?不符合常理!


2、调试过程中发现跳转时不会运行到启动文件(*.s),这有悖常理!


3、内存地址实际从 0x20000000 开始;


4、APP 链接文件修改的地方由 NXP 的 FAE 告知,跳转到 APP 部分的代码也是 FAE 提供的(一开始就了解了这个情况,所以没有一开始就怀疑这部分代码)。


5、通过查看 flash 0x0001 0000 地址处的内容,发现两份代码有不同,确定下载没有问题。


结合上面的两个地址(跳转地址和栈顶地址(先假设是这样,因为 M3 内核就是这样,M33 内核估计差不了多少)),鱼鹰开始重点查看这个跳转代码(NXP FAE 提供)




















#define ApplicationAddress    0x10000
void JumpToUserApplication(unsigned long userSP, unsigned long userStartup){ // set up stack pointer  __asm("msr msp, r0");  __asm("msr psp, r0");
 // Jump to PC (r1)  __asm("mov pc, r1"); }
void StartUp(void){ // relocate vector table SCB->VTOR = ApplicationAddress;   // Jump to user application JumpToUserApplication(*((unsigned long*)ApplicationAddress), *((unsigned long*)(ApplicationAddress+4)));    }


乍看,和鱼鹰理解的 M3 类似,都是在跳转时取出 APP 首地址处的 8 个字节作为 栈顶指针 和 PC 指针


但是从 C 语言的角度,你看不出任何毛病,很正常,但是它最终到 JumpToUserApplication 时却是 0x0001 0000 和 0xE000ED08,这个和前面的分析完全不符。


没办法,只能拿出鱼鹰的杀手锏,汇编


之后就发现,这个嵌入式汇编在跳转时,并没有取出 0x0001 0000 地址的 8个数据,而是直接沿用前面的 SCB->VTOR = ApplicationAddress;  运行后的 R0、 R1作为参数传递进去,最终导致跳转失败。


也就是说,问题出在 BOOT 上,而不是 APP 里面,所谓的好代码只是凑巧赶上了,这也就能解释为什么跳转时不会运行到启动文件了。


找到问题,但因为对汇编不熟,又是一顿改写,测试,总算成功跳转了。也算是完成了任务。

图片

从这次解决 BUG 的经历中,各位道友可以发现,这里运用了非常多的基础知识(内存分配、向量表、map文件、链接文件、汇编等等),正因为有这些知识的积累,此案才能顺利告破。


解 BUG,就像一个破案的警察,不仅需要从蛛丝马迹中寻找线索,还需要很多理论基础支撑,足够敏感,才能顺利解决,否则只能干瞪眼了。


但个人觉得,自己的行为像一个杀手,你出钱,我办事,各取所需。


这次是鱼鹰第一次有偿且亲自远程解决疑难问题,觉得挺有意义的,故此记录。


另外如果其他道友有难解的 BUG,同时又愿意付费解决的话,欢迎咨询鱼鹰,鱼鹰视情况收费(可能不低,但可以节省时间,项目紧急的话欢迎找鱼鹰),当然也可以雇佣鱼鹰作为 MCU 技术顾问,帮助公司解决一些难解问题。


在此感谢大家对鱼鹰的关注与支持!


*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客