NXP - 用MDK建立基于arm-none-eabi工具链的工程框架 - 解决单步调试问题 - 实践
文章目录
NXP - 用MDK建立基于arm-none-eabi工具链的工程框架 - 应对单步调试问题
概述
前面做了一个实验(NXP - 用MDK建立基于arm-none-eabi工具链的工程框架),用来解决如何在MDK下做一个基于arm-none-eabi设备链的工程框架,但是不能单步调试。
不能单步调试后面隐含的问题,可能是不能正常运行。
自己又琢磨了一天,想出了几个调试思路。一个一个的落地,终于有个思路是可以的,单步调试问题搞定了。
不能运行的。就是果真啊,前面那种实验尽管编译过了,经过这个实验验证,确实
一个可以正常运行的脚本 就是现在这个版本,处理了调试问题,能够单步调试,就
笔记
解决单步障碍的前置条件
你得会点反汇编知识,如果不会,这个难题真不好弄。
因为用MDK自带的ARMCLANG工具链+.sct分散加载记录,编译后,生成的.axf确实是能单步调试的。
那么用MDK切换到自己的arm-gcc工具链 + .ld分散加载文件,编译后,不能单步调试,区别在哪里呢?
这就很考验调试思路了。
如果不懂点反汇编知识和反汇编手段,这挑战基本没得搞。
上个实验,通过试错法和对比法,基本将bug锁定在.ld书写的内容不对上。
不过哪里不对?这不去看生成的axf, elf, 不通过生成的汇编代码,是不能确定障碍的。
用IDA64反汇编正常的.axf和不正常的.elf, 看出来了。
很明显的是,程序的开始地址和内存的开始地址不一样。
正常程序的ROM开始地址是0,不正常的ROM开始地址是0x4000
内存地址正常的是0x10000000, 不正常的内存地址是0x1000008C
这明显就是.ld中指定的地址,改过.ld,编译过后,再单步调试就ok了。
等能单步调试了,才回过味来。
因为我用的是Smoothieware工程自带的gcc-arm的LPC1768.ld和对应的startup_x.s. 冰沙工程不是一个能够单独烧录可运行的主程序,而是通过丢到mbedU盘中,通过bootloader烧录到FLASH中,由bootloader程序启动的程序。所以中断向量表必然不在0x0的地方,而是由bootloader启动后,二次指定的中断向量表。一般固件IAP工程,都是这么玩的。
mdk+arm-gcc无法在UI中设置地址
下图是:在mdk+arm-gcc环境下,UI上就没有可以设置地址的地方。只能是由.ld指定。
UI就不用做输入地址的界面。就是也许就是arm-gcc软件链下,只能由.ld指定地址,于
下图是:mdk+armclang的默认设备链,在UI上是能指定ROM地址和RAM地址的。
看看正常程序编译后的反汇编完成


看看不正常程序编译后的反汇编实现
行看到,程序的elf的中断向量表的地址是0x4000, 这就离谱了。
因为MCU硬件复位后,会去0x0的地址找中断向量表,这是硬件决定的。
如果大家的程序的中断向量表不在0x0地址,那程序实现就都不对了。
通过没有再去实验了。就是内存地址我感觉问题不大,不修改估计也能够。可
主要问题是中断向量表地址不为0x0引起的问题。
单步调试失败时,是有汇编代码的,看汇编代码的地址,如果此时有觉悟,也会感觉是中断向量表不对。
因为这是连SystemInit都没进,一单步调试,就读取内存失败,且读取内存的地址是0x200之下的地址。很像中断向量表的地址读取不了引起的。
修改.ld
.ld文件语法不支持C风格的单行注释,只能是用C风格的多行注释。
如何写.ld,在ARM官方文档中有。
也去下载了ARM官方的Arm-Architecture-ABI-rel-2025Q1文档集合,文档太多了。这个bug(不能单步调试)根据自己的调试经验和调试感觉,根本就能搞定,就只看了开篇bsabi32.pdf,就没继续再看了。等以后有刚需的时候再看。
/* Linker script for mbed LPC1768 */
MEMORY
{
/* FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 512K */
/* err */
/* FLASH (rx) : ORIGIN = 16K, LENGTH = (512K - 16K) */
/* ok, modify this */
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 512K
/* err */
/* RAM (rwx) : ORIGIN = 0x100000C8, LENGTH = (32K - 0xC8) */
/* ok, modify this */
RAM (rwx) : ORIGIN = 0x10000000, LENGTH = 32K
USB_RAM(rwx) : ORIGIN = 0x2007C000, LENGTH = 16K
ETH_RAM(rwx) : ORIGIN = 0x20080000, LENGTH = 16K
}
编译过,再看一眼反汇编。

因为工具链不同,编译出来的程序稍有区别。
不过ROM开始地址是0x0, 中断向量表就对了。
看看单步的效果
这次允许单步调试。
且可以在startup_LPC17xx.s中单步。
可能和调试设置有关,这回第一次停下来,就在Reset_Handler中。
F10/F11, 就允许走到my_main.cpp中。


再单步,就可以进入_start(), 相当于main().
一个函数,_start()干了活之后,才调用的main.就是MDK + ARMCLANG的工具链中,_start函数和main不
要自己实现的,就相当于main函数。就是但是对于固件来说,工具链不同,稍有区别是正常的。对于MDK + arm-gcc工具链,_start函数


到此,障碍(能编译过,但是不能单步调试)就完美克服完了
备注
至此,将一个命令行环境(gcc4mbed编译的固件工程, e.g. Smoothieware)迁移到MDK中,可以正常编译,单步调试的愿望,所需要的知识点,基础都搞定了。
时间,大把的。就是下一步,就是开开心心的将开源工程移植到MDK工程中(就135个实现档案 + 根据报错,将数量不详的头文件加进来)。对于工程师来说,只要路子是对的,不怕费时间。咱有的
经过这次实验,感觉还可以将工程迁移到MCUXpresso IDE v25.6.136或者EclipseCPP, 因为在这2种IDE中,是能够直接引入makefile工程的。
前面做过MCUXpresso中编译冰沙工程的实现,结果编译过了,因为不能单步调试,就放弃了。现在想起来,大概率也是因为中断向量表不对引起的。然而当时没有现在的觉悟,还有一个原因,就是工具大了,有bug就不好弄(如果自己是作者,这都不是问题。关键工程不是咱写的,人家遇到的坑咱不知道啊)。要排查问题,还是从一个小demo开始排查,比较容易一些。
通过等后续再试验一下,如果用基于Eclipse的IDE能够的话,那移植的工作量就更小了。

浙公网安备 33010602011771号