首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何展开ARM Cortex M3堆栈

如何展开ARM Cortex M3堆栈
EN

Stack Overflow用户
提问于 2015-11-27 10:49:04
回答 1查看 3.4K关注 0票数 3

ARM Coretex STM32 32的HardFault_Handler在崩溃发生时只能得到几个寄存器值: r0、r1、r2、r3、lr、pc、xPSR。但是栈中没有FP和SP。因此,我无法打开堆栈。有什么解决办法吗?非常感谢。

更新

按照web指令,允许ARMGCC(Keil uvision IDE)通过添加一个编译选项“-use_frame_指针”来生成FP,但是我在堆栈中找不到FP。我是这里真正的新手。下面是我的演示代码:

代码语言:javascript
运行
复制
int test2(int i, int j)
{
    return i/j;
}

int main()
{
    SCB->CCR |= 0x10;
    int a = 10;
    int b = 0;
    int c;
    c = test2(a,b);
}

enum { r0 = 0, r1, r2, r3, r11, r12, lr, pc, psr};
void Hard_Fault_Handler(uint32_t *faultStackAddress)
{
    uint32_t r0_val = faultStackAddress[r0]; 
    uint32_t r1_val = faultStackAddress[r1];
    uint32_t r2_val = faultStackAddress[r2]; 
    uint32_t r3_val = faultStackAddress[r3];
    uint32_t r12_val = faultStackAddress[r12]; 
    uint32_t r11_val = faultStackAddress[r11]; 
    uint32_t lr_val =  faultStackAddress[lr];
    uint32_t pc_val =  faultStackAddress[pc];
    uint32_t psr_val = faultStackAddress[psr];
}

我有两个问题:

1.我不确定堆栈中FP(r11)的索引在哪里,也不确定它是否被推入堆栈中。我假设是在r12之前,因为我在添加选项"--use_frame_pointer“之前和之后比较了汇编源代码。我还比较了从Hard_Fault_Handler读取的值,似乎r11不在堆栈中。因为我读到的r11地址指向一个代码不是我的代码的地方。更新我已经确认FP被推到堆栈中。第二个问题还需要回答。。

请参见下面的代码片段:

没有选项“-框架指针”

代码语言:javascript
运行
复制
test2 PROC
        MOVS     r0,#3
        BX       lr
        ENDP

main PROC
        PUSH     {lr}
        MOVS     r0,#0
        BL       test2
        MOVS     r0,#0
        POP      {pc}
        ENDP

选项“-框架指针”

代码语言:javascript
运行
复制
test2 PROC
        PUSH     {r11,lr}
        ADD      r11,sp,#4
        MOVS     r0,#3
        MOV      sp,r11
        SUB      sp,sp,#4
        POP      {r11,pc}
        ENDP

main PROC
        PUSH     {r11,lr}
        ADD      r11,sp,#4
        MOVS     r0,#0
        BL       test2
        MOVS     r0,#0
        MOV      sp,r11
        SUB      sp,sp,#4
        POP      {r11,pc}
        ENDP

2.似乎FP不在Hard_Fault_Handler()的输入参数faultStackAddress中,在哪里可以让调用方的FP展开堆栈?

再次更新--现在我明白了,最后一个FP(r11)不是存储在堆栈中的。我所需要做的就是读取r11寄存器的值,然后就可以展开整个堆栈。

因此,现在我的最后一个问题是如何使用C.的内联汇编程序读取它--我尝试了下面的代码,但是没有按照http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0472f/Cihfhjhg.html的引用从r11读取正确的值

代码语言:javascript
运行
复制
volatile int top_fp;
__asm
{
    mov top_fp, r11
}

R11值为0x20009DCC top_fp值为0x00000004

下面的更新3是我的全部代码.

代码语言:javascript
运行
复制
int test5(int i, int j, int k)
{
    char a[128] = {0} ;
    a[0] = 'a';
    return i/j;
}
int test2(int i, int j)
{
    char a[18] = {0} ;
    a[0] = 'a';
    return test5(i, j, 0);    
}


int main()
{
    SCB->CCR |= 0x10;
    int a = 10;
    int b = 0;
    int c;
    c = test2(a,b); //create a divide by zero crash
}

/*错误处理程序实现调用一个名为Hard_Fault_Handler()的函数。*/

代码语言:javascript
运行
复制
#if defined(__CC_ARM)
__asm void HardFault_Handler(void)
{
   TST lr, #4
   ITE EQ
   MRSEQ r0, MSP
   MRSNE r0, PSP
   B __cpp(Hard_Fault_Handler)
}
#else
void HardFault_Handler(void)
{
   __asm("TST lr, #4");
   __asm("ITE EQ");
   __asm("MRSEQ r0, MSP");
   __asm("MRSNE r0, PSP");
   __asm("B Hard_Fault_Handler");
}
#endif

void Hard_Fault_Handler(uint32_t *faultStackAddress)
{
   volatile int top_fp;
   __asm
   {
       mov top_fp, r11
   }
   //TODO: use top_fp to unwind the whole stack.

 }

最后,我完成了更新4。我的解决方案:

注意:要访问r11,我们必须使用嵌入式汇编程序(参见这里 ),这需要我花很长时间才能弄清楚。

代码语言:javascript
运行
复制
//we have to use embedded assembler.     
__asm int getRegisterR11()
{
    mov r0,r11
    BX LR
}

//call it from Hard_Fault_Handler function.
/*
Function call stack frame:
   FP1(r11) ->    | lr |(High Address)
                  | FP2|(prev FP)
                  | ...| 
Current FP(r11) ->| lr |
                  | FP1|(prev FP)
                  | ...|(Low Address)

    With FP, we can access lr(link register) which is the address to return when the current functions returns(where you were).
    Then (current FP - 1) points to prev FP.
    Thus we can unwind the stack.
*/
void unwindBacktrace(uint32_t topFp, uint16_t* backtrace)
{
    uint32_t nextFp = topFp;
    int j = 0;

    //#define BACK_TRACE_DEPTH 5
    //loop backtrace using FP(r11), save lr into an uint16_t array.
    for(int i = 0; i < BACK_TRACE_DEPTH; i++)
    {
        uint32_t lr = *((uint32_t*)nextFp);
        if ((lr >= 0x08000000) && (lr <= 0x08FFFFFF))
        {
            backtrace[j*2] = LOW_16_BITS(lr);
            backtrace[j*2 + 1] = HIGH_16_BITS(lr);
            j += 1;
        }
        nextFp = *((uint32_t*)nextFp - 1);
        if (nextFp == 0)
        {
            break;
        }
    }
}

#if defined(__CC_ARM)
__asm void HardFault_Handler(void)
{
   TST lr, #4
   ITE EQ
   MRSEQ r0, MSP
   MRSNE r0, PSP
   B __cpp(Hard_Fault_Handler)
}
#else
void HardFault_Handler(void)
{
   __asm("TST lr, #4");
   __asm("ITE EQ");
   __asm("MRSEQ r0, MSP");
   __asm("MRSNE r0, PSP");
   __asm("B Hard_Fault_Handler");
}
#endif

void Hard_Fault_Handler(uint32_t *faultStackAddress)
{
       //get back trace
    int topFp = getRegisterR11();

    unwindBacktrace(topFp, persistentData.faultStack.back_trace);
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-12-02 20:48:05

在这种情况下,非常原始的解除堆栈的方法是读取在arm-none-eabi-addr2line.时看到的SP上的所有堆栈内存,并使用HardFault_Handler进行处理。保存在堆栈中的所有链接寄存器条目都将转换为源行(请记住,实际的代码路径在LR指向该行之前就行了)。注意,如果中间的函数是使用分支指令(b)而不是分支和链接(bl)调用的,您将不会看到它们使用这种方法。

(我没有足够的声誉点来写评论,所以我正在编辑我的答案):

问题2的更新:

你为什么认为Hard_Fault_Handler会有任何争论?Hard_Fault_Handler通常是一个将地址存储在向量(异常)表中的函数。当处理器异常发生时,将执行Hard_Fault_Handler。没有任何争论涉及到这样做。但是,在故障发生时,所有寄存器都会被保存下来。具体来说,如果您编译时没有省略框架指针,您可以只读取R11的值(或拇指-2模式下的R7 )。然而,为了确保代码中的实际上是一个真正的硬错误处理程序,请查看startup.s代码,看看Hard_Fault_Handler是否位于向量表的第三个条目。如果有其他函数,则意味着从该函数中显式地调用Hard_Fault_Handler。有关详细信息,请参阅此文章。您还可以阅读我的博客 :)有一个关于堆栈的章节是基于Android示例的,但是很多事情都是一样的。

还要注意,很可能在faultStackAddress中应该存储堆栈指针,而不是帧指针。

更新2

好吧,让我们澄清一些事情。首先,请粘贴您调用Hard_Fault_Handler的代码。其次,我想您是从实际的HardFault异常处理程序中调用它的。在这种情况下,您不能期望R11会在faultStackAddressr11。你已经在问题的第一句中提到过了。只有,r0-r3,r12,lr,pc和psr.

你还写道:

但是栈中没有FP和SP。因此,我无法打开堆栈。有什么解决办法吗?

SP不是“在堆栈中”,因为它已经在一个堆栈寄存器(msp或psp)中。再看看这篇文章。另外,FP对于展开堆栈并不重要,因为您可以不使用它(通过保存的链接寄存器“导航”)。另一件事是,如果您在SP下面转储内存,那么如果您真的需要它,您可以期望FP就在保存的LR旁边。

回答最后一个问题:我现在不知道如何验证这段代码以及如何调用它(您需要粘贴完整的代码)。你可以查看这个函数的集合,看看引擎盖下面发生了什么。您可以做的另一件事是遵循这个职位作为模板。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33955719

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档