The processor enters "undefined mode" when it encounters an invalid instruction.
Not all possible bit patterns are valid instructions, and contrary to older processors where at least something happens, the processor detects these patterns and then generates an exception.
The handler for this exception is executed normally, but the processor mode is changed so that the R13_und
and R14_und
registers are mapped to R13
and R14
. This is necessary because the handler requires a local stack and information about the faulting address.
The stack for the handler needs to be separate because it is not guaranteed that the code currently running has a valid stack. Simply using whatever R13
contained at this point would be a security issue, as the handler executes with supervisor privilege. Thus, the operating system at start reserves stack space for the Undefined handler and places the stack top address in R13_und
(by switching to UND mode briefly):
mrs r0, cpsr
bic r1, r0, #0x1f
orr r1, r1, #MODE_UND
msr cpsr_c, r1
ldr r13, =_und_stack_top
msr cpsr_c, r0
The R14
register is also shadowed, because it is overwritten at exception entry, but the handler may decide to emulate the offending instruction and return to regular program flow, which would require restoring R14
(for which a stack would be required, which again is not guaranteed).
For example,
bl do_something
...
do_something:
vstm r13!, {s3}
...
vldm r13!, {s3}
The do_something
function stores s3
on the stack, because it modifies it and wishes to restore it at the end, but doesn't save r14
, because it doesn't call any subroutines. If the CPU doesn't have an FPU, the Undefined handler gets called for the vstm
instruction.
Actual exception vector:
undefined:
b undefined_handler
Handler:
undefined_handler:
stm r13!, {r0-r4,r14} // this goes on the UND handler stack
ldr r0, -8[r14] // look at the offending instruction
... // emulate instruction
ldm r13!, {r0-r4,r14} // restore everything from stack
subs r15, r14, #8 // return from exception
The r14
value for do_something
was hidden in the r14_USR
register, which is not accessible from Undefined mode -- if the handler were interested in that, it would change to System mode (which is a privileged mode with access to USR registers), copy the value to another register and switch back to UND mode in order to get access to its own stack back.