Arm Cortex-M Exception Example – Stack Frame

In this example we are going to examine the stack frame when an exception is triggered.

When the Cortex M3 processor triggers an exception certain registers are pushed onto the stack as follows:

Address    Register

 sp + 32   xPSR
 sp + 24   PC
 sp + 20   LR
 sp + 16   R12
 sp + 12   R3
 sp + 8    R2
 sp + 4    R1
 sp + 0    R0

Change vector_table.s from the original startup code to the following:

.syntax unified

.cpu cortex-m3

/* Vector Table */

.word 0x20001000        /* Stack Pointer */
.word reset_handler    /* 1 Reset Handler */
.word default_handler   /* 2 NMI */
.word default_handler   /* 3 Hard Fault */
.word default_handler   /* 4 Mem Management Fault */
.word default_handler   /* 5 Bus Fault */
.word usage_handler     /* 6 Usage Fault */
.word default_handler   /* 7 RESERVED */
.word default_handler   /* 8 RESERVED */
.word default_handler   /* 9 RESERVED*/
.word default_handler   /* 10 RESERVED */
.word default_handler   /* 11 SVCall */
.word default_handler   /* 12 Debug Monitor */
.word default_handler   /* 13 RESERVED */
.word default_handler   /* 14 PendSV */
.word default_handler   /* 15 SysTick */

.global reset_handler
.type reset_handler, STT_FUNC
    mov r0, #0
    mov r1, #1
    mov r2, #2
    mov r3, #3
    mov r4, #4
    mov r5, #5
    mov r6, #6
    mov r7, #7
    mov r8, #8
    mov r9, #9
    mov r10, #10
    mov r11, #11
    mov r12, #12
    .word 0xffffffff

    bl halt

.global default_handler
.type default_handler, STT_FUNC
    bl default_handler

.global usage_handler
.type usage_handler, STT_FUNC
    bl usage_handler

The reset_handler function now sets each register to an immediate value corresponding to the register number, i.e r0 is set to 0, r1 is set to 1 etc. This is to provide an easy visual method to verify which registers have been saved onto the exception stack frame.

The code highlighted in red is an illegal instruction, we declare an opcode of 0xffffffff which will trigger a usage fault (exception) and then we can examine the stack frame upon entering this exception via gdb.

In one terminal window use make run to start the QEMU instance, in another terminal window use make debug as follows:

make debug
arm-none-eabi-gdb -x gdbinit
GNU gdb (
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-linux-gnu --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
Find the GDB manual and other documentation resources online at:
For help, type "help".
Type "apropos word" to search for commands related to "word".
0x00000040 in ?? ()
Loading section .text, size 0xa4 lma 0x0
Start address 0x0, load size 164
Transfer rate: 1312 bits in <1 sec, 164 bytes/write.
Breakpoint 1 at 0x44: file vector_table.s, line 30.

Breakpoint 1, reset_handler () at vector_table.s:30
30        mov r1, #1
r0             0x0    0
r1             0x0    0
r2             0x0    0
r3             0x0    0
r4             0x0    0
r5             0x0    0
r6             0x0    0
r7             0x0    0
r8             0x0    0
r9             0x0    0
r10            0x0    0
r11            0x0    0
r12            0x0    0
sp             0x20001000    0x20001000
lr             0x0    0
pc             0x44    0x44 <reset_handler+4>
cpsr           0x40000173    1073742195

Continue execution, then CTRL-C, as follows:

(gdb) cont
Program received signal SIGINT, Interrupt.
usage_handler () at vector_table.s:57
57        bl usage_handler

We are now in the usage_handler exception code, lets examine the registers and also the stack, we instruct the debugger to examine the 8, words of memory pointed to by the stack pointer sp which is the exception stack frame as follows:

(gdb) info reg
r0             0x0    0
r1             0x1    1
r2             0x2    2
r3             0x3    3
r4             0x4    4
r5             0x5    5
r6             0x6    6
r7             0x7    7
r8             0x8    8
r9             0x9    9
r10            0xa    10
r11            0xb    11
r12            0xc    12
sp             0x20000fe0    0x20000fe0
lr             0x85    133
pc             0x80    0x80 <usage_handler>
cpsr           0x40000173    1073742195
(gdb) x /8wx $sp
0x20000fe0:    0x00000000    0x00000001    0x00000002    0x00000003
0x20000ff0:    0x0000000c    0x00000000    0x00000074    0x41000000 


The table below shows the values on the stack in terms of registers, note the text highlighted in green is the pc register, this represents the address of the instruction which triggered the exception.

Address    Register    Value

 sp + 32   xPSR        0x41000000
 sp + 24   PC          0x00000074
 sp + 20   LR          0x00000000
 sp + 16   R12         0x0000000c
 sp + 12   R3          0x00000003
 sp + 8    R2          0x00000002
 sp + 4    R1          0x00000001
 sp + 0    R0          0x00000000

We can ask the debugger to disassemble this address as follows:

(gdb) disassemble 0x00000074
Dump of assembler code for function reset_handler:
   0x00000040 <+0>:    mov.w    r0, #0
   0x00000044 <+4>:    mov.w    r1, #1
   0x00000048 <+8>:    mov.w    r2, #2
   0x0000004c <+12>:    mov.w    r3, #3
   0x00000050 <+16>:    mov.w    r4, #4
   0x00000054 <+20>:    mov.w    r5, #5
   0x00000058 <+24>:    mov.w    r6, #6
   0x0000005c <+28>:    mov.w    r7, #7
   0x00000060 <+32>:    mov.w    r8, #8
   0x00000064 <+36>:    mov.w    r9, #9
   0x00000068 <+40>:    mov.w    r10, #10
   0x0000006c <+44>:    mov.w    r11, #11
   0x00000070 <+48>:    mov.w    r12, #12
   0x00000074 <+52>:            ; <UNDEFINED> instruction: 0xffffffff
End of assembler dump.

From this ouput we can see that the instruction is indeed an illegal instruction.

This example demonstrates an instance of how the information stored in the exception stack frame can be used to track down an illegal instruction.