Arm Cortex-M Exception Example – Usage Fault

This example demonstrates an exception being called. We can use the example startup code from the introduction with a minor modification to introduce a Usage Fault.

First lets record the disassembly of main() from the original startup code using objdump:

arm-none-eabi-objdump -d startup.elf 

startup.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <reset_handler-0x40>:
   0:    20001000     .word    0x20001000
   4:    00000041     .word    0x00000041
   8:    00000049     .word    0x00000049
   c:    00000049     .word    0x00000049
  10:    00000049     .word    0x00000049
  14:    00000049     .word    0x00000049
  18:    0000004d     .word    0x0000004d
  1c:    00000049     .word    0x00000049
  20:    00000049     .word    0x00000049
  24:    00000049     .word    0x00000049
  28:    00000049     .word    0x00000049
  2c:    00000049     .word    0x00000049
  30:    00000049     .word    0x00000049
  34:    00000049     .word    0x00000049
  38:    00000049     .word    0x00000049
  3c:    00000049     .word    0x00000049

00000040 <reset_handler>:
  40:    f000 f806     bl    50 <main>

00000044 <halt>:
  44:    f7ff fffe     bl    44 <halt>

00000048 <default_handler>:
  48:    f7ff fffe     bl    48 <default_handler>

0000004c <usage_handler>:
  4c:    f7ff fffe     bl    4c <usage_handler>

00000050 <main>:
  50:    b480          push    {r7}
  52:    b083          sub    sp, #12
  54:    af00          add    r7, sp, #0
  56:    f64a 33cd     movw    r3, #43981    ; 0xabcd
  5a:    f2c1 2334     movt    r3, #4660    ; 0x1234
  5e:    607b          str    r3, [r7, #4]
  60:    2300          movs    r3, #0
  62:    4618          mov    r0, r3
  64:    370c          adds    r7, #12
  66:    46bd          mov    sp, r7
  68:    f85d 7b04     ldr.w    r7, [sp], #4
  6c:    4770          bx    lr
  6e:    bf00          nop

Now alter the Makfile as follows(changes highlighted):

TARGET=startup.elf
OBJ := vector_table.o main.o
LDSCRIPT=startup.ld
CROSS=arm-none-eabi-
#CFLAGS := -nostdlib -g -mcpu=cortex-m3 -mthumb
CFLAGS := -nostdlib -g 
LDFLAGS := -g -o
ASMFLAGS := -g -o

TTY := $(shell tty)
MACH := lm3s6965evb
CDEV := tty,id=any,path=$(TTY)

QEMU_FLAGS := -S -gdb tcp::1234 -nographic -kernel

all: $(TARGET)

%.o: %.s
        $(CROSS)as $(ASMFLAGS) $@ $<

%.o: %.c
        $(CROSS)gcc $(CFLAGS) -c $<

$(TARGET): $(OBJ) $(LDSCRIPT)
        $(CROSS)ld $(LDFLAGS) $@ -T $(LDSCRIPT) $(OBJ)

clean:
        $(RM) *.o *.elf

run: $(TARGET)
        qemu-system-arm -machine $(MACH) $(QEMU_FLAGS) $< -chardev $(CDEV)      

debug: $(TARGET)
        $(CROSS)gdb -x gdbinit

Rebuild and now run the objdump command again:

arm-none-eabi-objdump -d startup.elf 

startup.elf:     file format elf32-littlearm


Disassembly of section .text:

00000000 <reset_handler-0x40>:
   0:    20001000     .word    0x20001000
   4:    00000041     .word    0x00000041
   8:    00000049     .word    0x00000049
   c:    00000049     .word    0x00000049
  10:    00000049     .word    0x00000049
  14:    00000049     .word    0x00000049
  18:    0000004d     .word    0x0000004d
  1c:    00000049     .word    0x00000049
  20:    00000049     .word    0x00000049
  24:    00000049     .word    0x00000049
  28:    00000049     .word    0x00000049
  2c:    00000049     .word    0x00000049
  30:    00000049     .word    0x00000049
  34:    00000049     .word    0x00000049
  38:    00000049     .word    0x00000049
  3c:    00000049     .word    0x00000049

00000040 <reset_handler>:
  40:    f000 f806     bl    50 <main>

00000044 <halt>:
  44:    f7ff fffe     bl    44 <halt>

00000048 <default_handler>:
  48:    f7ff fffe     bl    48 <default_handler>

0000004c <usage_handler>:
  4c:    f7ff fffe     bl    4c <usage_handler>

00000050 <main>:
  50:    e52db004     push    {fp}        ; (str fp, [sp, #-4]!)
  54:    e28db000     add    fp, sp, #0
  58:    e24dd00c     sub    sp, sp, #12
  5c:    e59f3014     ldr    r3, [pc, #20]    ; 78 <main+0x28>
  60:    e50b3008     str    r3, [fp, #-8]
  64:    e3a03000     mov    r3, #0
  68:    e1a00003     mov    r0, r3
  6c:    e24bd000     sub    sp, fp, #0
  70:    e49db004     pop    {fp}        ; (ldr fp, [sp], #4)
  74:    e12fff1e     bx    lr
  78:    1234abcd     .word    0x1234abcd

Now compare the disassembly from the two main() functions, and you can see they are different the first output has 16-bit instructions (known as Thumb mode on the Cortex-M CPU) and the second output consists of 32-bit “standard” ARM instructions.

The ARM Cortex-M3 only supports the Thumb instruction set, so what happens if we try to run this code on the QEMU virtual machine.

Let’s try, run make run in a terminal window as follows:

make run
arm-none-eabi-as -g -o vector_table.o vector_table.s
arm-none-eabi-gcc -nostdlib -g  -c main.c
arm-none-eabi-ld -g -o startup.elf -T startup.ld vector_table.o main.o
qemu-system-arm -machine lm3s6965evb -gdb tcp::1234 -nographic -kernel startup.elf -chardev tty,id=any,path=/dev/pts/1

and in another terminal run make debug as follows:

make debug
arm-none-eabi-gdb -x gdbinit
GNU gdb (7.6.50.20131218-0ubuntu1+1) 7.6.50.20131218-cvs
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
0x0000004c in ?? ()
Loading section .text, size 0x7c lma 0x0
Start address 0x0, load size 124
Transfer rate: 992 bits in <1 sec, 124 bytes/write.
Breakpoint 1 at 0x40: file vector_table.s, line 29.

Breakpoint 1, reset_handler () at vector_table.s:29
29        bl main
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             0x20001010    0x20001010
lr             0x51    81
pc             0x40    0x40 <reset_handler>
cpsr           0x40000173    1073742195
(gdb)

Break on main():

(gdb) b main

Breakpoint 2 at 0x5c: file main.c, line 3.

And continue, at this point the debugger does not break at any point, so to force a break type CTRL-C

(gdb) cont
Continuing.
^C
Program received signal SIGINT, Interrupt.
usage_handler () at vector_table.s:43
43        bl usage_handler
(gdb)

At this point we can see the program is now in the usage_handler() exception code.

This is what we expect as an unknown instruction causes a usage fault.In this case it’s because we intentionally modified the build to produce incorrect machine instructions, but another cause could be a branch to memory location containing data rather than code.

 

 

Advertisements