找到cortex-m4发生中断的地方

时间:2016-08-02 15:27:25

标签: gdb arm cortex-m

我试图在我的代码中找到特定中断发生的位置。在这种情况下,它位于stm32f4微控制器上,中断是SysTick_Handler。

我想要的基本上是弄清楚systick中断发生的地方。我正在使用arm-none-eabi-gdb来尝试查找回溯,但我从那里得到的唯一信息是:

(gdb) bt
#0  SysTick_Handler () at modules/profiling.c:66
#1  <signal handler called>
#2  0x55555554 in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?)

如何在中断发布之前获得有关程序所在位置的一些信息?

查看arm文档here,似乎我应该能够读取堆栈指针,并从那里获取PC。但那么这正是GDB中的解开者所做的不是吗?

4 个答案:

答案 0 :(得分:2)

在问题结束时你走在正确的轨道上。 ARM Cortex-M内核有两个堆栈指针,主堆栈指针(MSP,用于中断)和进程堆栈指针(PSP,用于任务)。

当有优先级的中断进入时,当前寄存器值(对于大多数寄存器)被压入当前堆栈(如果中断后台应用程序则为PSP,如果中断较低优先级中断则为MSP),然后是堆栈切换到MSP(如果还没有)。

当您第一次输入中断时,链接寄存器(LR,返回地址)将具有一个大部分为F而不是实际返回地址的值。该值告诉核心分支时如何退出。通常,如果后台任务被中断,您将看到0xFFFFFFFD的值,如果中断较低优先级的中断,您将看到0xFFFFFFF1。如果使用浮点单元,这些值将有所不同。但是,这个值的神奇之处在于第2位(0x4)告诉您堆栈帧是在PSP还是MSP上。

一旦确定了帧所在的堆栈,就可以通过查看相应的堆栈指针减去24(6个32位位置)来找到正在执行的地址。请参见链接中的图2.3。这将指向您被中断的PC。

答案 1 :(得分:1)

我们一直以各种形式看到这个问题,人们一直说有两个堆栈。所以我用systick自己试了一下。

文档说我们处于重置的线程模式,如果你用openocd停止它就说

target halted due to debug-request, current mode: Thread 

我有一些转储寄存器的代码:

20000000 APSR
00000000 IPSR
00000000 EPSR
00000000 CONTROL
00000000 SP_PROCESS
20000D00 SP_PROCESS after I modified it
20000FF0 SP_MAIN
20000FF0 mov r0,sp  
then I dump the stack up to 0x20001000 which is where I know my stack started
20000FF0 00000000 
20000FF4 00000000 
20000FF8 00000000 
20000FFC 0100005F 

我设置并等待一个systick中断,处理程序转储寄存器和ram,然后进入无限循环。一般不好的做法,但只是在这里调试/学习。在中断之前,我准备了一些寄存器:

.thumb_func
.globl iwait
iwait:
    mov r0,#1
    mov r1,#2
    mov r2,#3
    mov r3,#4
    mov r4,#13
    mov r12,r4
    mov r4,#15
    mov r14,r4
    b .

在我看到的处理程序中

20000000 APSR
0000000F IPSR
00000000 EPSR
00000000 CONTROL
20000D00 SP_PROCESS
20000FC0 SP_MAIN
20000FC0 mov r0,sp
20000FC0 0000000F 
20000FC4 20000FFF 
20000FC8 00000000 
20000FCC FFFFFFF9  this is our special lr (not one rjp mentioned)
20000FD0 00000001  this is r0
20000FD4 00000002  this is r1
20000FD8 00000003  this is r2
20000FDC 00000004  this is r3
20000FE0 0000000D  this is r12
20000FE4 0000000F  this is r14/lr
20000FE8 01000074  and this is where we were interrupted from
20000FEC 21000000  this is probably the xpsr mentioned
20000FF0 00000000  stuff that was there before
20000FF4 00000000 
20000FF8 00000000 
20000FFC 0100005F 


01000064 <iwait>:
 1000064:   2001        movs    r0, #1
 1000066:   2102        movs    r1, #2
 1000068:   2203        movs    r2, #3
 100006a:   2304        movs    r3, #4
 100006c:   240d        movs    r4, #13
 100006e:   46a4        mov ip, r4
 1000070:   240f        movs    r4, #15
 1000072:   46a6        mov lr, r4
 1000074:   e7fe        b.n 1000074 <iwait+0x10>
 1000076:   bf00        nop

因此,在这种情况下,直接使用ARM文档,它没有使用sp_process,而是使用sp_main。它正在推动手册说它正在推送的项目,包括中断/返回地址,即0x1000074。

现在,如果我将SPSEL位置1(注意先设置PSP),应用/线程模式下的mov r0,sp似乎使用PSP而不是MSP。但是然后处理程序使用msp作为mov r0,sp但似乎放了

在线程/前景

之前
20000000 APSR
00000000 IPSR
00000000 EPSR
00000000 SP_PROCESS
20000D00 SP_PROCESS modified
00000000 CONTROL
00000002 CONTROL modified
20000FF0 SP_MAIN
20000D00 mov r0,sp

现在在处理程序

20000000 APSR
0000000F IPSR
00000000 EPSR
00000000 CONTROL (interesting!)
20000CE0 SP_PROCESS
20000FE0 SP_MAIN
20000FE0 mov r0,sp
dump of that stack
20000FE0 0000000F 
20000FE4 20000CFF 
20000FE8 00000000 
20000FEC FFFFFFFD 
20000FF0 00000000 
20000FF4 00000000 
20000FF8 00000000 
20000FFC 0100005F 
dump of sp_process stack
20000CE0 00000001 
20000CE4 00000002 
20000CE8 00000003 
20000CEC 00000004 
20000CF0 0000000D 
20000CF4 0000000F 
20000CF8 01000074 our return value
20000CFC 21000000 

因此,要处理人们一直提到的备用堆栈的这个位置,你必须把自己放在那个位置(或者你依赖的代码)。为什么你想要为简单的裸机程序做这件事,谁知道,所有零的控制寄存器都很好,很容易,可以共享一个堆栈。

我不使用gdb,但是你需要让它根据你找到的内容转储所有寄存器sp_process和sp_main,然后在每个寄存器中转储十几个字,你应该看到0xFFFFFFFx作为标记然后倒数从那看到返回地址。您可以让您的处理程序读取两个堆栈指针,然后您可以查看gprs。随着gnu汇编程序mrs rX,psp; mrs rX,msp;对于进程和主堆栈指针。

答案 2 :(得分:1)

正如你们许多人评论的那样,PC会处于两个不同的堆栈中,我解决它的方式是通过在程序集中实际找到HardFault_Handling代码并从那里获取我需要的东西。要正确获取PC值,我使用以下代码。

register int *r0 __asm("r0");

__asm(  "TST lr, #4\n"
        "ITE EQ\n"
        "MRSEQ r0, MSP\n"
        "MRSNE r0, PSP\n" // stack pointer now in r0
        "ldr r0, [r0, #0x18]\n" // stored pc now in r0
        //"add r0, r0, #6\n" // address to stored pc now in r0
     );

现在可以通过

访问中断发生位置的值
uint32_t PC = *r0;

现在可以用于我想要的任何东西。不幸的是,我没有设法让GDB为我自动解除堆栈。但至少我发现了中断发射的位置,这就是目标。

答案 3 :(得分:-5)

这叫做DEBUGGING。最简单的入门方法是在整个代码中粘贴一堆printf()调用。运行程序。如果打印出来:

指出A
得到B点 得点C

并且死了,然后你知道它在“C”和“D”之间死亡。现在,您可以通过使用更紧密间隔的printf()调用将“C”和“D”之间的代码装扮得更好来进一步优化。

这是初学者入门的最佳方式。许多经验丰富的专家也更喜欢printf()进行调试。调试器可能会妨碍。