在“ 帮助解决硬故障的处理器专家组件 ”中,我使用了带有一些汇编代码的 C 处理程序,这些代码是使用处理器专家创建的,以帮助我调试 ARM Cortex-M 上的硬故障。受 此处 GNU gdb 脚本的启发,我现在有了另一种方法。由于此方法使用 GDB 命令行方法,因此它既适用于 Eclipse GUI,也适用于仅在命令行模式下使用 GDB :-)。
-- 用于调试 ARM 硬故障的 GDB 脚本
这个想法是:
- 在硬故障异常处理程序中设置断点
- 当发生硬故障时,CPU 将调用硬故障异常处理程序,调试器将停止目标
- 在 GDB 中执行“armex”(ARM 异常)脚本/命令以转储堆栈寄存器以显示发生问题的程序计数器。
.gdbinit 脚本
有几种方法可以使用自己的命令扩展 GDB。一种简单的方法是将额外的函数添加到 GDB 在启动时加载的 .gdbinit 脚本中。
我已将以下内容添加到我的 .gdbinit 文件中以定义我的“armex”命令:
define armex
printf "EXEC_RETURN (LR):\n",
info registers $lr
if $lr & 0x4 == 0x4
printf "Uses MSP 0x%x return.\n", $MSP
set $armex_base = $MSP
else
printf "Uses PSP 0x%x return.\n", $PSP
set $armex_base = $PSP
end
printf "xPSR 0x%x\n", *($armex_base+28)
printf "ReturnAddress 0x%x\n", *($armex_base+24)
printf "LR (R14) 0x%x\n", *($armex_base+20)
printf "R12 0x%x\n", *($armex_base+16)
printf "R3 0x%x\n", *($armex_base+12)
printf "R2 0x%x\n", *($armex_base+8)
printf "R1 0x%x\n", *($armex_base+4)
printf "R0 0x%x\n", *($armex_base)
printf "Return instruction:\n"
x/i *($armex_base+24)
printf "LR instruction:\n"
x/i *($armex_base+20)
end
document armex
ARMv7 Exception entry behavior.
xPSR, ReturnAddress, LR (R14), R12, R3, R2, R1, and R0
end
您可以将 .gdbinit 文件放在任何地方。我将其放置在我的 gdb 位于 Freescale Kinetis Design Studio 中的位置 (C:\Freescale\KDS_3.0.0\toolchain\bin)。
为了确保 GDB 找到 .gdbinit,我在 Eclipse 工作区首选项中指定了它的路径:
-- Eclipse 工作区首选项中的 GDB 命令文件
调试硬故障
为了调试硬故障,我在硬故障中断处理程序中设置了一个断点,以便在故障发生时停止调试器:
-- 因硬故障停止
为了找出问题发生的位置,我现在在 gdb 控制台中使用“armex”命令:
使用控制台的“三角”菜单切换到 arm-none-eabi-gdb 视图
-- gdb 控制台中的 armex 命令
armex 命令列出堆栈寄存器(与我在“ 调试 ARM Cortex-M 上的硬故障 ”中显示的处理程序相同)。重要信息要么是返回指令,要么是LR指令信息。我可以在反汇编视图中输入该地址以找出问题发生的位置:
硬故障原因拆解图
在上面的示例中,LR(链接寄存器或返回地址)是 0xbd2(设置了 Thumb 位的 0xbd3)。在反汇编视图中,这是处理程序将返回到的地址,因此问题一定就在这之前。检查汇编代码有间接分支寄存器
define armex
printf "EXEC_RETURN (LR):\n",
info registers $lr
if $lr & 0x4 == 0x4
printf "Uses MSP 0x%x return.\n", $MSP
set $armex_base = $MSP
else
printf "Uses PSP 0x%x return.\n", $PSP
set $armex_base = $PSP
end
printf "xPSR 0x%x\n", *($armex_base+28)
printf "ReturnAddress 0x%x\n", *($armex_base+24)
printf "LR (R14) 0x%x\n", *($armex_base+20)
printf "R12 0x%x\n", *($armex_base+16)
printf "R3 0x%x\n", *($armex_base+12)
printf "R2 0x%x\n", *($armex_base+8)
printf "R1 0x%x\n", *($armex_base+4)
printf "R0 0x%x\n", *($armex_base)
printf "Return instruction:\n"
x/i *($armex_base+24)
printf "LR instruction:\n"
x/i *($armex_base+20)
end
document armex
ARMv7 Exception entry behavior.
xPSR, ReturnAddress, LR (R14), R12, R3, R2, R1, and R0
end
堆栈寄存器显示
define armex
printf "EXEC_RETURN (LR):\n",
info registers $lr
if $lr & 0x4 == 0x4
printf "Uses MSP 0x%x return.\n", $MSP
set $armex_base = $MSP
else
printf "Uses PSP 0x%x return.\n", $PSP
set $armex_base = $PSP
end
printf "xPSR 0x%x\n", *($armex_base+28)
printf "ReturnAddress 0x%x\n", *($armex_base+24)
printf "LR (R14) 0x%x\n", *($armex_base+20)
printf "R12 0x%x\n", *($armex_base+16)
printf "R3 0x%x\n", *($armex_base+12)
printf "R2 0x%x\n", *($armex_base+8)
printf "R1 0x%x\n", *($armex_base+4)
printf "R0 0x%x\n", *($armex_base)
printf "Return instruction:\n"
x/i *($armex_base+24)
printf "LR instruction:\n"
x/i *($armex_base+20)
end
document armex
ARMv7 Exception entry behavior.
xPSR, ReturnAddress, LR (R14), R12, R3, R2, R1, and R0
end
这导致了硬故障。如果问题不是那么清楚,那么只需在该位置周围设置一个断点并重新启动应用程序以调试在触发硬故障之前发生的情况。有了这个,应该很容易找到并解决问题。
概括
我现在有另一种方法来调试我的硬故障:使用我的自定义 gdb 命令转储堆栈寄存器。与我之前的解决方案相比,这种方法的优点是它不需要目标上的任何额外资源(代码中没有额外的处理程序,也没有变量)。额外的好处是现在我知道如何使用我的自定义命令扩展 GDB :-)。