This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

undef handler

Hi all,

If the processor tries to go back from the test() function to the main() function, the program counter gets always the wrong address - so that an undef handler occurs.... (processor is searching for an programm code in the sdram...). The whole programm is stored in an external nor flash device (0x10000000).

void test(void)
{
   //do some stuff...
   return;
}

void main(void)
{
   test();
}


The user stack size is 0x800. That's a short part of the disassembly where the jump to the sdram occurs (the next step after 0x100012D4).

According to the map file, the function test() is using the memory space until 0x1000012D8 and the next function starts at 0x100001304. I'm not sure about the space within these two points? All these values are also stored in the nor flash device...


   522: return;
0x100012C8  E7980185  LDR       R0,[R8,R5,LSL #3]
0x100012CC  E3C00001  BIC       R0,R0,#0x00000001
0x100012D0  E7880185  STR       R0,[R8,R5,LSL #3]
   523: }
0x100012D4  E8BD8FF8  LDMIA     R13!,{R3-R11,PC}
0x100012D8  544F5250  DD        0x544F5250
0x100012DC  0050495F  DD        0x0050495F
0x100012E0  0A50490A  DD        0x0A50490A
0x100012E4  00000000  DD        0x00000000
0x100012E8  606E6F64  DD        0x606E6F64
0x100012EC  6E6B2074  DD        0x6E6B2074
0x100012F0  7420776F  DD        0x7420776F
0x100012F4  20736968  DD        0x20736968
0x100012F8  6B636170  DD        0x6B636170
0x100012FC  000A7465  DD        0x000A7465
0x10001300  68746520  DD        0x68746520
0x10001304  00000020  DD        0x00000020
    39:                 pbuf->hdr->flag = used;



best regards
Howard

Parents
  • Do you know if the printf() function use the stack or the heap section?

    I don't know. I suspect that printf() doesn't use heap, because there are applications where you'd use printf() and would want to avoid using heap, and the standard library should support that.

    But how could I inspect the stack with Keil?

    I think that's what the Call Stack Unwinder is for. You could also analyze the disassembly and derive expected stack usage from that.

    It seems that the area isn't correct initialized...

    This seems normal. You would expect the used part of the stack to contain non-zeros, and the yet untuched part to be all-zeros.

    Moreover do you know if it is possible to see if the size of the different stacks (irq, user, supervisor stack...) are big enough?

    One way is to let the program run for a while (stress-test it,) then stop it and inspect stack space. The untouched part of the stack is all-zeros. This should give you an idea of how much stack was used.

Reply
  • Do you know if the printf() function use the stack or the heap section?

    I don't know. I suspect that printf() doesn't use heap, because there are applications where you'd use printf() and would want to avoid using heap, and the standard library should support that.

    But how could I inspect the stack with Keil?

    I think that's what the Call Stack Unwinder is for. You could also analyze the disassembly and derive expected stack usage from that.

    It seems that the area isn't correct initialized...

    This seems normal. You would expect the used part of the stack to contain non-zeros, and the yet untuched part to be all-zeros.

    Moreover do you know if it is possible to see if the size of the different stacks (irq, user, supervisor stack...) are big enough?

    One way is to let the program run for a while (stress-test it,) then stop it and inspect stack space. The untouched part of the stack is all-zeros. This should give you an idea of how much stack was used.

Children

  • I think that's what the Call Stack Unwinder is for. You could also analyze the disassembly and derive expected stack usage from that.

    Could you show me a small example? The Call Stack Unwinder only displays the c-functions with their variables (and their values... there's no error) - but I can't find the relation between the Stack Unwinder window and the memory window displaying the top of the stack.


    One way is to let the program run for a while (stress-test it,) then stop it and inspect stack space

    The test() function will be called if a new packet was received by ethernet. If this happens, the error occurs.... If I reduce the code within the test() function everything seems to work. Therefore I think there would be something wrong with the stack - but the stack size seems to be big enough. I'm not sure what a common size of the stack would be... maybe the printf() function is using a huge amount of the stack...

    best regards
    Howard

  • maybe the printf() function is using a huge amount of the stack...

    That's probably it. I would expect printf() to easily consume 1K bytes of stack (I don't have data to back this up, though.)
    As for required stack size for a typical program, it really strongly depends on the program. Deeply nested function calls and lots of large automatic variables can consume tons of stack space.


  • That's probably it. I would expect printf() to easily consume 1K bytes of stack (I don't have data to back this up, though.)

    That's a lot.... Do you know a better way to transmit messages to the debug (rs232) interface containing the values of variables?

    I also have installed two large buffers with 256 Bytes - one for a small receive ethernet frame and the other one to transmit the next ethernet frame....

    Provided that, these huge buffers and the printf() function will overflow the stack - which possibilities do I have to avoid this? If I call another function, I always deliver only an pointer to the buffer - not the buffer. My stack size is 0x1000 = 4kByte - which is also a lot....

    best regards
    Howard

  • ok, now I was able to do some tests. The printf() function uses a lot of the stack size but that's not the problem.

    I open the Call Stack Window, where the main() function is shown at the beginning. When the processor calls the testfunction() function and after that another function (testfunction2) where a memcmp instruction is executed - all previous values will be overwriten (e.g. in the main function)....

    Call Stack Window:
    
    testfunction2()
    testfunction()
    main()
    
    ARM_LIB_HEAP 0x312000 EMPTY 0x1000 {
    }
    
    ARM_LIB_STACK 0x314000 EMPTY -0x1000 {
    }
    

    So the stack size is huge enough, but the programm is only using 0x200 of the whole stack (start at 0x314000). If the programm needs more space for the stack, the programm will start at the beginning of the stack 0x314000 and overwrite all values (located for example in the first function main()).

    That means, when the programm tries to go back from the testfunction() to the main() function - the correct address for this jump is no longer located in the stack.

    I hope you could give me some advice to solve this problem.

    best regards
    Howard

  • ...another function (testfunction2) where a memcmp instruction is executed - all previous values will be overwriten

    I'm not sure why you are mentioning memcmp - this function compares two memory regions, it shouldn't alter their contents.
    But it sounds like you almost found the exact place in the program where stack corruption takes place. Try locating the exact line of the source code which does it.


  • But it sounds like you almost found the exact place in the program where stack corruption takes place.

    It is only a example - nearly every command which uses some space (stack) will occur that other variables will be overwriten.

    I think there is something wrong that the processor doesn't add the new stack variables at the end of the actual stack location. For example I've a simple unsigned int i variable in the main() function, which is 0x00, but when the processor do some stuff in the testfunction2() the value is 0x20000041....

    It is also a little bit strange, that I couldn't see any value in the memory window (start of the stack location) which is similar to the values shown in the stack window. I only see that the area from 0x314000 to 0x313F00 will change its values)

    best regards
    Howard

  • I think there is something wrong that the processor doesn't add the new stack variables at the end of the actual stack location.

    It could be stack pointer corruption then. If at some point in the program the stack pointer register is not updated properly (or updated improperly) then all subsequent stack operations would reference wrong memory locations.
    I don't think it's possible to corrupt the stack pointer by means of the C language only. It would involve pieces written in assembly, or improper handling of CPU mode switching, improper handling of interrupts etc...

  • I'm using the startup file from Atmel instead of the one from Keil, because it was easier to add some instructions to boot from an external flash device.

    ;------------------------------------------------------------------------------
    ; After a reset, execution starts here, the mode is ARM, supervisor
    ; with interrupts disabled.
    ; Initializes the chip and branches to the main() function.
    ;------------------------------------------------------------------------------
    
                    AREA  cstartup, CODE
                    ENTRY        ; Entry point for the application
    
    
    ; Reset Handler
    
            EXPORT  resetHandler
            IMPORT  |Image$$Fixed_region$$Limit|
            IMPORT  |Image$$Relocate_region$$Base|
            IMPORT  |Image$$Relocate_region$$ZI$$Base|
            IMPORT  |Image$$Relocate_region$$ZI$$Limit|
            IMPORT  |Image$$ARM_LIB_STACK$$Base|
            IMPORT  |Image$$ARM_LIB_STACK$$ZI$$Limit|
    
                    ; Perform low-level initialization of the chip using LowLevelInit()
                    IMPORT  LowLevelInit
    
    resetHandler
    
    
            ; Set pc to actual code location (i.e. not in remap zone)
                LDR     pc, =label
    label
    
    
                    ; Set up temporary stack (Top of the SRAM)
                    LDR     r0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|
            MOV     sp, r0
                    ; Call Low level init
                LDR     r0, =LowLevelInit
            MOV     lr, pc
            BX      r0
    
    
    ;Initialize the Relocate_region segment
                    LDR     r0, = |Image$$Fixed_region$$Limit|
                    LDR     r1, = |Image$$Relocate_region$$Base|
                    LDR     r3, = |Image$$Relocate_region$$ZI$$Base|
    
                CMP     r0, r1
            BEQ     %1
    
    
            ; Copy init data
    0       CMP     r1, r3
            LDRCC   r2, [r0], #4
            STRCC   r2, [r1], #4
            BCC     %0
    
    1       LDR     r1, =|Image$$Relocate_region$$ZI$$Limit|
            MOV     r2, #0
    2       CMP     r3, r1
            STRCC   r2, [r3], #4
            BCC     %2
    
    
    ; Cache Setup ------------------------------------------------------------------
    
    ; Constants
    ICACHE_ENABLE   EQU     (1<<12)         ; Instruction Cache Enable Bit
    
                    MRC     p15, 0, R0, c1, c0, 0   ; Enable Instruction Cache
                    ORR     R0, R0, #ICACHE_ENABLE
                    MCR     p15, 0, R0, c1, c0, 0
    
    
    
    ; Setup Stack for each mode
    
            LDR     R0, = |Image$$ARM_LIB_STACK$$ZI$$Limit|
    
    
    
    ;  Enter Undefined Instruction Mode and set its Stack Pointer
                    MSR     CPSR_c, #ARM_MODE_UND:OR:I_BIT:OR:F_BIT
                    MOV     SP, R0
                    SUB     R0, R0, #UND_Stack_Size
    
    ;  Enter Abort Mode and set its Stack Pointer
                    MSR     CPSR_c, #ARM_MODE_ABT:OR:I_BIT:OR:F_BIT
                    MOV     SP, R0
                    SUB     R0, R0, #ABT_Stack_Size
    
    ;  Enter FIQ Mode and set its Stack Pointer
                    MSR     CPSR_c, #ARM_MODE_FIQ:OR:I_BIT:OR:F_BIT
                    MOV     SP, R0
                    SUB     R0, R0, #FIQ_Stack_Size
    
    
    
    ;  Enter IRQ Mode and set its Stack Pointer
            MSR     CPSR_c, #ARM_MODE_IRQ:OR:I_BIT:OR:F_BIT
            MOV     SP, R0
    ;        SUB     R4, SP, #IRQ_Stack_Size
            SUB     R0, SP, #IRQ_Stack_Size
    
    
    ; Supervisor mode (interrupts enabled)
            MSR     CPSR_c, #ARM_MODE_SVC:OR:I_BIT:OR:F_BIT
    ;        MOV     SP, R4
                    MOV     SP, R0
    ;        SUB     R4, SP, #SVC_Stack_Size
            SUB     R0, SP, #SVC_Stack_Size
    
    
    ;  Enter User Mode and set its Stack Pointer
            MSR     CPSR_c, #ARM_MODE_USR
            MOV     SP, R0
            SUB     SL, SP, #USR_Stack_Size
    
    
    ; Enter the C code
    
            IMPORT  __main
            LDR     R0, =__main
            BX      R0
    
            END
    

    I've added four of the instruction modes (undefined, abort mode and FIQ mode as well as the user mode).

    Which parts of the startup file will affect the stack pointer?

    best regards
    Howard

  • here is the first part of the startup file:

    ; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs
    
    ARM_MODE_USR        EQU     0x10
    ARM_MODE_FIQ        EQU     0x11
    ARM_MODE_IRQ        EQU     0x12
    ARM_MODE_SVC        EQU     0x13
    ARM_MODE_ABT        EQU     0x17
    ARM_MODE_UND        EQU     0x1B
    ARM_MODE_SYS        EQU     0x1F
    
    I_BIT               EQU     0x80            ; when I bit is set, IRQ is disabled
    F_BIT               EQU     0x40            ; when F bit is set, FIQ is disabled
    
    AT91C_BASE_AIC      EQU     0xFFFFF000
    AIC_IVR             EQU     0x100
    AIC_EOICR           EQU     0x130
    
    UND_Stack_Size      EQU     0x00000000
    SVC_Stack_Size      EQU     0x00000008
    ABT_Stack_Size      EQU     0x00000000
    FIQ_Stack_Size      EQU     0x00000000
    IRQ_Stack_Size      EQU     0x00000100
    USR_Stack_Size      EQU     0x00000800
    
            PRESERVE8
    
    ; Area Definition and Entry Point
    ; Startup Code must be linked first at Address at which it expects to run.
    
            AREA    VECTOR, CODE
            ARM
    
    ; Exception Vectors
    
    Vectors
                                    LDR     pc,=resetHandler
                                    LDR     PC,Undef_Addr
                    LDR     PC,SWI_Addr
                    LDR     PC,PAbt_Addr
                    LDR     PC,DAbt_Addr
                                    LDR     PC,[PC,#-0xF20] ; Vector From AIC_IVR
                    LDR     PC,[PC,#-0xF20] ; Vector From AIC_FVR
    
    Reset_Addr      DCD     resetHandler
    Undef_Addr      DCD     undefVector
    SWI_Addr        DCD     swiVector
    PAbt_Addr       DCD     prefetchAbortVector
    DAbt_Addr       DCD     dataAbortVector
                    DCD     0               ; Reserved Address
    IRQ_Addr        DCD     irqVector
    FIQ_Addr        DCD     fiqVector
    
    undefVector
                                b           undefVector             ; Undefined instruction
    swiVector
                            b       swiVector               ; Software interrupt
    prefetchAbortVector
                            b       prefetchAbortVector     ; Prefetch abort
    dataAbortVector
                            b       dataAbortVector         ; Data abort
    reservedVector
                            b       reservedVector          ; Reserved for future use
    irqVector
            b       irqHandler              ; Interrupt
    fiqVector
                                            ; Fast interrupt
    
    ;------------------------------------------------------------------------------
    ; Handles a fast interrupt request by branching to the address defined in the
    ; AIC.
    ;------------------------------------------------------------------------------
    fiqHandler
            b       fiqHandler
    
    ;------------------------------------------------------------------------------
    ; Handles incoming interrupt requests by branching to the corresponding
    ; handler, as defined in the AIC. Supports interrupt nesting.
    ;------------------------------------------------------------------------------
    irqHandler
            ;  Save interrupt context on the stack to allow nesting */
            SUB     lr, lr, #4
            STMFD   sp!, {lr}
            MRS     lr, SPSR
            STMFD   sp!, {r0,r1,lr}
    
            ; Write in the IVR to support Protect Mode */
            LDR     lr, =AT91C_BASE_AIC
            LDR     r0, [r14, #AIC_IVR]
            STR     lr, [r14, #AIC_IVR]
    
            ; Branch to interrupt handler in Supervisor mode */
            MSR     CPSR_c, #ARM_MODE_SVC
            STMFD   sp!, {r1-r4, r12, lr}
            MOV     lr, pc
            BX      r0
            LDMIA   sp!, {r1-r4, r12, lr}
            MSR     CPSR_c, #ARM_MODE_IRQ | I_BIT
    
            ; Acknowledge interrupt */
            LDR     lr, =AT91C_BASE_AIC
            STR     lr, [r14, #AIC_EOICR]
    
            ; Restore interrupt context and branch back to calling code
            LDMIA   sp!, {r0,r1,lr}
            MSR     SPSR_cxsf, lr
            LDMIA   sp!, {pc}^
    
    ;------------------------------------------------------------------------------
    ; After a reset, execution starts here, the mode is ARM, supervisor
    ; with interrupts disabled.
    ; Initializes the chip and branches to the main() function.
    ;------------------------------------------------------------------------------
    
                    AREA  cstartup, CODE
                    ENTRY        ; Entry point for the application
    
    
    ; Reset Handler
    
            EXPORT  resetHandler
            IMPORT  |Image$$Fixed_region$$Limit|
            IMPORT  |Image$$Relocate_region$$Base|
            IMPORT  |Image$$Relocate_region$$ZI$$Base|
            IMPORT  |Image$$Relocate_region$$ZI$$Limit|
            IMPORT  |Image$$ARM_LIB_STACK$$Base|
            IMPORT  |Image$$ARM_LIB_STACK$$ZI$$Limit|
    
                    ; Perform low-level initialization of the chip using LowLevelInit()
                    IMPORT  LowLevelInit
    
    resetHandler
    

    and that will be my scatter-loading file:

    Load_region 0x10000000 0x4000000 {
    
    
    Fixed_region 0x10000000 { *.o (VECTOR, +First) *(cstartup) .ANY (+RO) }
    ; Relocate_region +0 { Relocate_region 0x300000 0x10000 { ether.o (+RW +ZI) }
    SDRAM 0x20000000 0x1000000 { .ANY (+RW +ZI) }
    ARM_LIB_HEAP 0x312000 EMPTY 0x1000 { }
    ARM_LIB_STACK 0x314000 EMPTY -0x1000 { } }

    Howard

  • SVC_Stack_Size EQU 0x00000008
    ...
    ; Branch to interrupt handler in Supervisor mode */

    Apparently, interrupt handlers are run in Supervisor mode, and only 8 bytes are allocated for Supervisor mode stack. If you have any interrupt handlers at all, 8 bytes would not be enough.
    The straightforward solution is to increase Supervisor stack size. Another solution is to run the interrupt handlers in User or System mode (shares stack with main program, saves memory.)
    I could be missing something, of course...

  • thank you for the hint. I was always looking at the last definition in the startup file

    MSR     CPSR_c, #ARM_MODE_SVC:OR:I_BIT:OR:F_BIT
    


    where the interrupts are disabled in the supervisor mode. I've one question about the supervisor mode: in which mode will be the lowlevelinit function called in the startup file?

    The straightforward solution is to increase Supervisor stack size. Another solution is to run the interrupt handlers in User or System mode (shares stack with main program, saves memory.)
    

    The definition of the user mode (at the end of the startup file)

    MSR     CPSR_c, #ARM_MODE_USR
    


    will allow all kind of interrupts (in the user mode). I'm starting the main function in the user mode, when a interrupt occurs the irqhandler in the startup file will be executed and will change the original settings of the supervisor mode (interrupts disabled)? And all the values of the interupt (+ jump back addr) will be stored in the SVC_Stack_Size?

    ; Branch to interrupt handler in Supervisor mode */
            MSR     CPSR_c, #ARM_MODE_SVC
            STMFD   sp!, {r1-r4, r12, lr}
            MOV     lr, pc
            BX      r0
            LDMIA   sp!, {r1-r4, r12, lr}
            MSR     CPSR_c, #ARM_MODE_IRQ | I_BIT
    


    Could you explain me the above part a little bit?

    So if I will replace the line above with

    ; Branch to interrupt handler in Supervisor mode */
    MSR     CPSR_c, #ARM_MODE_USR
    


    the interupts will be executed in the user mode?

    best regards
    Howard

  • I've changed the size of the supervisor stack to 0x100 and the same error occurs.

    The stack size, which the processor use, starts from 0x314000 and ends at 0x313E40.

    The interrupt method is very small - only checking the status registers (emac receive, transmit) and setting the value of a volatile global variable, which inidicates that a new ethernet frame received.

    best regards
    Howard

  • Too many questions, and all of them are answered in the ARM Architecture Reference Manual.
    I have no knowledge of the AIC, so I'm afraid my advice could be completely wrong. All I know is that with the startup file you presented, you'll definitely overflow the Supervisor mode stack. That could explain all your troubles.


  • All I know is that with the startup file you presented, you'll definitely overflow the Supervisor mode stack. That could explain all your troubles.

    Could you please tell me which configuration I have to made to avoid supervisor mode stack overflow?

    ; Branch to interrupt handler in User mode */
            MSR     CPSR_c, #ARM_MODE_USER
            STMFD   sp!, {r1-r4, r12, lr}
            MOV     lr, pc
            BX      r0
            LDMIA   sp!, {r1-r4, r12, lr}
            MSR     CPSR_c, #ARM_MODE_IRQ | I_BIT
    

    Howard

  • This is exactly what I would try first. Of course, the problem could be caused by something completely unrelated to this...