【问题标题】:How to get a call stack from a SoftWare Interrupt exception如何从软件中断异常中获取调用堆栈
【发布时间】:2013-02-16 00:55:44
【问题描述】:

我正在尝试使用gdb 调试在裸机 ARM 平台上运行的程序。在某些时候会生成SWI(软件中断)异常。但是,如您所见,回溯并没有显示产生异常的原因:

(gdb)c

Continuing. 
^C 
Program received signal SIGTRAP, Trace/breakpoint trap. 0xffff0008 in ?? ()
(gdb) bt
#0  0xffff0008 in ?? ()

在ARM上运行的程序是用arm-none-linux-gnueabi-gcc -O2 -c -ggdb编译的,我也试过用-O0得到同样的结果。

如何获得有意义的调用堆栈?是否有其他方法可以找到产生此异常的原因?

【问题讨论】:

  • 可以打印收据吗?另外,这是裸机还是你有操作系统?
  • 我可以打印寄存器,但是程序计数器是0xffff0008,我应该尝试分析寄存器中的哪些信息?实际上,在设置了一些断点并单步执行汇编指令之后,我能够看到我的代码正在跳转到经过我的程序的内存地址,其中包含未定义的指令,从而导致 SWI 异常。这是裸机,没有操作系统。

标签: gdb arm bare-metal


【解决方案1】:

要了解gdb 产生堆栈跟踪的问题,您首先必须了解gdb 如何产生堆栈跟踪。编译器在创建C 函数时使用标准的prologueepilogue,这是函数入口和出口处的汇编程序 代码。其中一部分是将lr 保存在堆栈上,为局部变量保留空间链接前一个帧指针fp。这些堆栈帧提供了一种以fproot-ed且通常以结束的链表类型。这取决于 ABI(请参阅 -mabi)。每种 ARM ABI 类型的具体细节略有不同,但概念相似。

因此,当SWI(或任何异常发生)时,它会完全中断C 编译代码的流程,并且帧指针 列表可能难以解码,尤其是在堆栈损坏的情况。此外,gdb 不会解码程序的上下文。某些系统可能会更改帧指针并立即从exception mode 切换到system/supervisor mode。异常的sp 甚至可以用作暂存寄存器。当输入SWI 时,处理程序的部分工作将是保存user 寄存器(r0-r12)。在多任务操作系统的情况下,这可能会导致user 堆栈从一个任务完全更改到另一个任务。

您始终可以通过在异常模式下检查lr 来确定错误/导致指令。这在ARM Architechure 中指定,对于任何ARM CPU 都是相同的。 0xfff0008 是默认的SWI 处理程序地址(使用高内存向量表时)。 ARM ARM(架构参考手册)中的SWI 除外,


A2.6.4 软件中断异常

软件中断指令 (SWI) 进入超级用户模式以请求特定超级用户(操作系统)功能。执行 SWI 时,会执行以下操作:

   R14_svc    = address of next instruction after the SWI instruction
   SPSR_svc   = CPSR
   CPSR[4:0]  = 0b10011                 /* Enter Supervisor mode */
   CPSR[5]    = 0                       /* Execute in ARM state */
                                        /* CPSR[6] is unchanged */
   CPSR[7]    = 1                       /* Disable normal interrupts */
                                        /* CPSR[8] is unchanged */
   CPSR[9]    = CP15_reg1_EEbit         /* Endianness on exception entry */
   if high vectors configured then
       PC     = 0xFFFF0008
   else
       PC     = 0x00000008

要在执行SWI操作后返回,请使用以下指令恢复PC(来自R14_svc)和CPSR(来自SPSR_svc)并返回到SWI之后的指令:

       MOVS PC,R14

如您所见,R14_svc 是在超级用户模式下的lr,设置为SWI 指令+4。这样正常返回会重新启动下面的指令。通过检查lr 的位置,您可以确定SWI 发生的位置。如果用户堆栈没有损坏,您可以使用编译器通过fp 使用的ABI 将您的supervisor 堆栈链接到user 堆栈。如果您这样做,那么gdb 可以提供堆栈跟踪。但是,在讨论的系统中,没有SWI支持代码。

您也可以编写gdb 宏来在这种情况下提供堆栈跟踪。但是,我希望很明显gdb 通常很难做到这一点。

【讨论】:

  • 感谢您非常详细的回答!
  • 我经常遇到 SWI,其中 PC=0xFFFF0008 和 LR=0xFFFF0008 也是,有没有办法知道在这种情况下执行的最后一行?
  • 我会检查您所处的模式。检查 SPSR_svc 和用户/模式 LR。您是否可以从 SWI 处理程序 执行SWI。你的意思是 CSPR 说“超级”模式,PCLR 都是 0xFFFF0008?其他模式有阴影LR,但只有切换模式才能看到。您也许可以使用 JTAG 调试器来做到这一点。我想这就是你正在使用的。 用户 LR 会告诉你大概在哪里。
  • 是的,我的意思是在 SWI 发生并且 ARM 处于超级用户模式之后,LR 和 SWI 为 0xffff0008。再次感谢您的回答,我不知道可以使用 JTAG 调试器更改 ARM 模式。使用 gdb "set $cpsr=0xd0" 完成,然后我可以看到用户 LR。
猜你喜欢
  • 2010-10-19
  • 1970-01-01
  • 1970-01-01
  • 2014-04-23
  • 1970-01-01
  • 2010-12-20
  • 1970-01-01
  • 2017-04-15
  • 1970-01-01
相关资源
最近更新 更多