如何从SoftWare中断异常中获取调用堆栈

时间:2013-03-01 13:40:31

标签: gdb arm bare-metal

我正在尝试使用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得到相同的结果。

如何获得有意义的调用堆栈?有没有其他方法可以找到产生此异常的内容?

1 个答案:

答案 0 :(得分:4)

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

因此,当SWI(或任何异常发生)时,它会完全中断C编译代码的流程,并且帧指针列表可能难以解码,特别是在堆栈损坏的情况下。此外,gdb不会解码程序的上下文。某些系统可能会更改帧指针并立即从exception mode切换到system/supervisor mode。异常的sp甚至可以用作临时寄存器。输入SWI后,处理程序的部分工作将是保存user寄存器(r0-r12)。在多任务O / S 的情况下,这可能导致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的位置。如果用户堆栈未损坏,您可以使用编译器通过supervisor使用的 ABI user堆栈链接到fp堆栈。如果这样做,那么gdb可以给出堆栈跟踪。但是,在正在讨论的系统中,没有SWI支持代码。

在这种情况下,您还可以编写gdb宏来提供堆栈跟踪。但是,我希望很明显,gdb通常会有一项艰巨的任务。