打印

分享一种由硬件检查栈溢出的小技巧(转)

[复制链接]
4307|27
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一.需求
    1.造成主栈(MSP)溢出的原因有很多,如过多的定义局部变量,递归调用,中断嵌套等都有可能会导致主栈溢出。
    2.主栈溢出后会对其他内存空间做恶意修改,导致不可预料的后果
    3.stm32不具备MPU,没有对内存进行保护的硬件机制
    4.软件检测栈溢出有其局限性

二.实现原理
    1.将主栈的栈底定位在0x20000000处,当主栈溢出时将导致数据被push到低于0x20000000的非法区域,从而触发HardFault异常
    2.由于进入HardFault_Handler后栈有可能已经溢出了,C语言的运行环境可能已不存在,故HardFault_Handler需要改用汇编来实现,在HardFault_Handler
      中先根据LR的值判断当前使用的栈是主MSP还是PSP,然后检查BFSR==0x00000092 SP<0x20000000
      这三个条件是否同时满足,如果同时满足则可判断主栈确实溢出了,否则可判断出不是因为栈溢出而触发的HardFault异常可以调用C语言实现的
      HardFaultShow函数,打印出相关的寄存器的值以及其每个含义位的值帮助我们分析产生HardFault的其他原因

三.添加步骤
1.将STM32F10x.s中的
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
改为
                AREA |.ARM.__AT_0x20000000|, DATA, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size

2.在STM32F10x.s中增加一个RO段
                AREA STKV, DATA, READONLY
STK_OV_MSG        DCB "MAIN STACK OVERFLOW!",0

3.将STM32F10x.s中的
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler         [WEAK]
                B       .
                ENDP
改为
BFSR       EQU 0xE000ED29
STKVBFSR   EQU 0x00000092
ERRSP      EQU 0x20000000
UART0_FLAG EQU 0x40013800
UART0_DATA EQU 0x40013804

HardFault_Handler PROC
           IMPORT HardFaultShow        
           ;Get info        
           TST LR, #4
           ITE EQ
           MRSEQ R0, MSP
           MRSNE R0, PSP
           ;Check stack memory overflow
           ;if(BFSR==0x00000092 && MMAR==0x1FFFFFFC && SP<0x20000000),is stack overflow,output deubg msg to uart.
           ;else goto HardFaultShow
           LDR         R1,=ERRSP        
           CMP         R0, R1
           BGE         HardFaultShow ;
           LDR         R1,=BFSR
           LDR         R1,[R1]
           LDR         R2,=STKVBFSR
           CMP          R1,R2
           BNE         HardFaultShow
           ;Yes,Stack is overflow,we must output the msg for programmer.
           LDR         R1,=STK_OV_MSG
           LDR R2,=UART0_FLAG
           LDR R3,=UART0_DATA
PutsLoop2
           LDRB R0, [R1], #1
           CBZ R0, Loop
PutcWaitLoop2
           LDR R4, [R2]
           TST R4, #0x80
           BEQ PutcWaitLoop2
           STRB R0, [R3]        
           B PutsLoop2        
Loop         
           B .
           ENDP

4.将stm32f10x_it.c中的
void HardFault_Handler(void)
{
    /* Go to infinite loop when Hard Fault exception occurs */
    while (1)
    {
    }
}
改为
void HardFaultShow(unsigned int * hardfault_args,unsigned int *sp)
{
    unsigned int stacked_r0;
    unsigned int stacked_r1;
    unsigned int stacked_r2;
    unsigned int stacked_r3;
    unsigned int stacked_r12;
    unsigned int stacked_lr;
    unsigned int stacked_pc;
    unsigned int stacked_psr;
    unsigned int CFSR;
    unsigned int HFSR;
    stacked_r0 = ((unsigned long) hardfault_args[0]);
    stacked_r1 = ((unsigned long) hardfault_args[1]);
    stacked_r2 = ((unsigned long) hardfault_args[2]);
    stacked_r3 = ((unsigned long) hardfault_args[3]);
    stacked_r12 = ((unsigned long) hardfault_args[4]);
    stacked_lr = ((unsigned long) hardfault_args[5]);
    stacked_pc = ((unsigned long) hardfault_args[6]);
    stacked_psr = ((unsigned long) hardfault_args[7]);
    printf ("################Hard fault handler]################\r\n");
    printf ("R0   = 0x%08x\r\n", stacked_r0);
    printf ("R1   = 0x%08x\r\n", stacked_r1);
    printf ("R2   = 0x%08x\r\n", stacked_r2);
    printf ("R3   = 0x%08x\r\n", stacked_r3);
    printf ("R12  = 0x%08x\r\n", stacked_r12);
    printf ("SP   = 0x%08x\r\n",(unsigned int)sp);
    printf ("LR   = 0x%08x\r\n", stacked_lr);
    printf ("PC   = 0x%08x\r\n", stacked_pc);
    printf ("PSR  = 0x%08x\r\n", stacked_psr);
    CFSR = *((volatile unsigned long *)(0xE000ED28));
    printf ("CFSR = 0x%08x\r\n", CFSR);
    printf (" BFARVALID   :%d\r\n",CFSR&(1<<15)?1:0);
    printf (" STKERR      :%d\r\n",CFSR&(1<<12)?1:0);
    printf (" UNSTKERR    :%d\r\n",CFSR&(1<<11)?1:0);
    printf (" IMPRECISERR :%d\r\n",CFSR&(1<<10)?1:0);
    printf (" PRECISERR   :%d\r\n",CFSR&(1<<9)?1:0);
    printf (" IBUSERR     :%d\r\n",CFSR&(1<<8)?1:0);
    printf (" MMARVALID   :%d\r\n",CFSR&(1<<7)?1:0);
    printf (" MSTKERR     :%d\r\n",CFSR&(1<<4)?1:0);
    printf (" MUNSTKERR   :%d\r\n",CFSR&(1<<3)?1:0);
    printf (" DACCVIOL    :%d\r\n",CFSR&(1<<1)?1:0);
    printf (" IACCVIOL    :%d\r\n",CFSR&(1<<0)?1:0);
    printf (" DIVBYZERO   :%d\r\n",CFSR&(1<<25)?1:0);
    printf (" UNALIGNED   :%d\r\n",CFSR&(1<<24)?1:0);
    printf (" NOCP        :%d\r\n",CFSR&(1<<19)?1:0);
    printf (" INVPC       :%d\r\n",CFSR&(1<<18)?1:0);
    printf (" INVSTATE    :%d\r\n",CFSR&(1<<17)?1:0);
    printf (" UNDEFINSTR  :%d\r\n",CFSR&(1<<16)?1:0);
    HFSR = *((volatile unsigned long *)(0xE000ED2C));
    printf ("HFSR = 0x%08x\r\n", HFSR);
    printf (" DEBUGEVT    :%d\r\n",HFSR&((u32)1<<31)?1:0);
    printf (" FORCED      :%d\r\n",HFSR&(1<<30)?1:0);
    printf (" VECTBL      :%d\r\n",HFSR&(1<<1)?1:0);
    printf ("DFSR = 0x%08x\r\n", (*((volatile unsigned long *)(0xE000ED30))));
    printf ("AFSR = 0x%08x\r\n", (*((volatile unsigned long *)(0xE000ED3C))));
    printf ("BFAR = 0x%08x\r\n", (*((volatile unsigned long *)(0xE000ED38))));
    printf ("MMAR = 0x%08x\r\n", (*((volatile unsigned long *)(0xE000ED34))));
    while(1);
}

一.需求
    1.造成主栈(MSP)溢出的原因有很多,如过多的定义局部变量,递归调用,中断嵌套等都有可能会导致主栈溢出。
    2.主栈溢出后会对其他内存空间做恶意修改,导致不可预料的后果
    3.stm32不具备MPU,没有对内存进行保护的硬件机制
    4.软件检测栈溢出有其局限性

二.实现原理
    1.将主栈的栈底定位在0x20000000处,当主栈溢出时将导致数据被push到低于0x20000000的非法区域,从而触发HardFault异常
    2.由于进入HardFault_Handler后栈有可能已经溢出了,C语言的运行环境可能已不存在,故HardFault_Handler需要改用汇编来实现,在HardFault_Handler
      中先根据LR的值判断当前使用的栈是主MSP还是PSP,然后检查BFSR==0x00000092 SP<0x20000000
      这三个条件是否同时满足,如果同时满足则可判断主栈确实溢出了,否则可判断出不是因为栈溢出而触发的HardFault异常可以调用C语言实现的
      HardFaultShow函数,打印出相关的寄存器的值以及其每个含义位的值帮助我们分析产生HardFault的其他原因

三.添加步骤
1.将STM32F10x.s中的
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
改为
                AREA |.ARM.__AT_0x20000000|, DATA, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size

2.在STM32F10x.s中增加一个RO段
                AREA STKV, DATA, READONLY
STK_OV_MSG        DCB "MAIN STACK OVERFLOW!",0

3.将STM32F10x.s中的
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler         [WEAK]
                B       .
                ENDP
改为
BFSR       EQU 0xE000ED29
STKVBFSR   EQU 0x00000092
ERRSP      EQU 0x20000000
UART0_FLAG EQU 0x40013800
UART0_DATA EQU 0x40013804

HardFault_Handler PROC
           IMPORT HardFaultShow        
           ;Get info        
           TST LR, #4
           ITE EQ
           MRSEQ R0, MSP
           MRSNE R0, PSP
           ;Check stack memory overflow
           ;if(BFSR==0x00000092 && MMAR==0x1FFFFFFC && SP<0x20000000),is stack overflow,output deubg msg to uart.
           ;else goto HardFaultShow
           LDR         R1,=ERRSP        
           CMP         R0, R1
           BGE         HardFaultShow ;
           LDR         R1,=BFSR
           LDR         R1,[R1]
           LDR         R2,=STKVBFSR
           CMP          R1,R2
           BNE         HardFaultShow
           ;Yes,Stack is overflow,we must output the msg for programmer.
           LDR         R1,=STK_OV_MSG
           LDR R2,=UART0_FLAG
           LDR R3,=UART0_DATA
PutsLoop2
           LDRB R0, [R1], #1
           CBZ R0, Loop
PutcWaitLoop2
           LDR R4, [R2]
           TST R4, #0x80
           BEQ PutcWaitLoop2
           STRB R0, [R3]        
           B PutsLoop2        
Loop         
           B .
           ENDP

4.将stm32f10x_it.c中的
void HardFault_Handler(void)
{
    /* Go to infinite loop when Hard Fault exception occurs */
    while (1)
    {
    }
}
改为
void HardFaultShow(unsigned int * hardfault_args,unsigned int *sp)
{
    unsigned int stacked_r0;
    unsigned int stacked_r1;
    unsigned int stacked_r2;
    unsigned int stacked_r3;
    unsigned int stacked_r12;
    unsigned int stacked_lr;
    unsigned int stacked_pc;
    unsigned int stacked_psr;
    unsigned int CFSR;
    unsigned int HFSR;
    stacked_r0 = ((unsigned long) hardfault_args[0]);
    stacked_r1 = ((unsigned long) hardfault_args[1]);
    stacked_r2 = ((unsigned long) hardfault_args[2]);
    stacked_r3 = ((unsigned long) hardfault_args[3]);
    stacked_r12 = ((unsigned long) hardfault_args[4]);
    stacked_lr = ((unsigned long) hardfault_args[5]);
    stacked_pc = ((unsigned long) hardfault_args[6]);
    stacked_psr = ((unsigned long) hardfault_args[7]);
    printf ("################Hard fault handler]################\r\n");
    printf ("R0   = 0x%08x\r\n", stacked_r0);
    printf ("R1   = 0x%08x\r\n", stacked_r1);
    printf ("R2   = 0x%08x\r\n", stacked_r2);
    printf ("R3   = 0x%08x\r\n", stacked_r3);
    printf ("R12  = 0x%08x\r\n", stacked_r12);
    printf ("SP   = 0x%08x\r\n",(unsigned int)sp);
    printf ("LR   = 0x%08x\r\n", stacked_lr);
    printf ("PC   = 0x%08x\r\n", stacked_pc);
    printf ("PSR  = 0x%08x\r\n", stacked_psr);
    CFSR = *((volatile unsigned long *)(0xE000ED28));
    printf ("CFSR = 0x%08x\r\n", CFSR);
    printf (" BFARVALID   :%d\r\n",CFSR&(1<<15)?1:0);
    printf (" STKERR      :%d\r\n",CFSR&(1<<12)?1:0);
    printf (" UNSTKERR    :%d\r\n",CFSR&(1<<11)?1:0);
    printf (" IMPRECISERR :%d\r\n",CFSR&(1<<10)?1:0);
    printf (" PRECISERR   :%d\r\n",CFSR&(1<<9)?1:0);
    printf (" IBUSERR     :%d\r\n",CFSR&(1<<8)?1:0);
    printf (" MMARVALID   :%d\r\n",CFSR&(1<<7)?1:0);
    printf (" MSTKERR     :%d\r\n",CFSR&(1<<4)?1:0);
    printf (" MUNSTKERR   :%d\r\n",CFSR&(1<<3)?1:0);
    printf (" DACCVIOL    :%d\r\n",CFSR&(1<<1)?1:0);
    printf (" IACCVIOL    :%d\r\n",CFSR&(1<<0)?1:0);
    printf (" DIVBYZERO   :%d\r\n",CFSR&(1<<25)?1:0);
    printf (" UNALIGNED   :%d\r\n",CFSR&(1<<24)?1:0);
    printf (" NOCP        :%d\r\n",CFSR&(1<<19)?1:0);
    printf (" INVPC       :%d\r\n",CFSR&(1<<18)?1:0);
    printf (" INVSTATE    :%d\r\n",CFSR&(1<<17)?1:0);
    printf (" UNDEFINSTR  :%d\r\n",CFSR&(1<<16)?1:0);
    HFSR = *((volatile unsigned long *)(0xE000ED2C));
    printf ("HFSR = 0x%08x\r\n", HFSR);
    printf (" DEBUGEVT    :%d\r\n",HFSR&((u32)1<<31)?1:0);
    printf (" FORCED      :%d\r\n",HFSR&(1<<30)?1:0);
    printf (" VECTBL      :%d\r\n",HFSR&(1<<1)?1:0);
    printf ("DFSR = 0x%08x\r\n", (*((volatile unsigned long *)(0xE000ED30))));
    printf ("AFSR = 0x%08x\r\n", (*((volatile unsigned long *)(0xE000ED3C))));
    printf ("BFAR = 0x%08x\r\n", (*((volatile unsigned long *)(0xE000ED38))));
    printf ("MMAR = 0x%08x\r\n", (*((volatile unsigned long *)(0xE000ED34))));
    while(1);
}


修改原因:去掉了 MMAR==0x1FFFFFFC 这个非必要的条件
专营STM8/32
QQ 940436962
http://y-ec.taobao.com/

相关帖子

沙发
lvjing880907| | 2012-8-13 16:03 | 只看该作者
好长!

使用特权

评论回复
板凳
fuqing5542|  楼主 | 2012-8-29 09:43 | 只看该作者
支持下自己

使用特权

评论回复
地板
ken_zq| | 2012-8-29 18:00 | 只看该作者
曾做过SD卡读写时出现了堆栈溢出,当时没有想到要这样检测。

使用特权

评论回复
5
fuqing5542|  楼主 | 2012-8-31 09:21 | 只看该作者
嗯哼哈哈哈

使用特权

评论回复
6
fuqing5542|  楼主 | 2012-9-10 14:22 | 只看该作者
楼下的跟上啊

使用特权

评论回复
7
fuqing5542|  楼主 | 2012-9-11 10:29 | 只看该作者
:Q

使用特权

评论回复
8
fuqing5542|  楼主 | 2012-9-12 11:09 | 只看该作者
顶自己 顶21

使用特权

评论回复
9
fuqing5542|  楼主 | 2012-9-19 10:01 | 只看该作者
那是相当的长啊啊啊啊

使用特权

评论回复
10
fuqing5542|  楼主 | 2012-9-27 09:26 | 只看该作者
:victory:

使用特权

评论回复
11
fuqing5542|  楼主 | 2012-9-28 09:29 | 只看该作者
:sleepy:

使用特权

评论回复
12
cool_coder| | 2012-9-28 11:00 | 只看该作者
不错

使用特权

评论回复
13
fuqing5542|  楼主 | 2012-9-28 15:48 | 只看该作者
哈哈 不错

使用特权

评论回复
14
sedatefire| | 2012-9-28 22:58 | 只看该作者
问题是,程序是动态的,可能跑很久哦,栈都未必触底呢

使用特权

评论回复
15
Wxy8030| | 2012-9-29 20:37 | 只看该作者
有空看看!

使用特权

评论回复
16
fuqing5542|  楼主 | 2012-10-8 09:29 | 只看该作者
嗯 有空看看

使用特权

评论回复
17
fuqing5542|  楼主 | 2012-10-19 09:17 | 只看该作者
:lol

使用特权

评论回复
18
fuqing5542|  楼主 | 2012-10-22 10:08 | 只看该作者
:shutup:

使用特权

评论回复
19
fuqing5542|  楼主 | 2012-10-23 09:31 | 只看该作者
好长啊

使用特权

评论回复
20
fuqing5542|  楼主 | 2012-10-24 09:08 | 只看该作者
mark

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

0

主题

362

帖子

1

粉丝