"); //-->
昨天有一位读者刚关注鱼鹰,并且很快加了鱼鹰微信,描述了以下问题:
当时也没有细看问题,只是大概看了一下,然后简单的提供了一个思路给对方,毕竟鱼鹰也比较忙,不可能花太多时间在别人的问题上。
而且也是看对方的问题描述比较清楚,比较有诚意,所以才会提供一个思路,否则可能懒得回复了。所以大家问问题的时候一定要直接详细描述,否则没人不愿意回答。毕竟很多人问完并回复之后,可能一句谢谢都没有就没有下文了,这种事情见多了。好像别人帮助你是理所应当的。
只是没想到,对方会提出给报酬,所以看他挺有诚意的,就开玩笑的问问多少钱。然后开始报价,并且对方也同意我的报价,并愿意多给一倍。
既然他那么有诚意,晚上也没啥事情,所以还是按自己的报价(有原则的鱼鹰)确定下来 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 技术顾问,帮助公司解决一些难解问题。
在此感谢大家对鱼鹰的关注与支持!
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。