I want to cause an ARM Cortex-M3 Undefined Instruction exception for the test of my test fixture. The IAR compiler supports this with inline assembly like this:
asm("udf.w #0");
Unfortunately the GNU CC inline assembler does not know this opcode for the NXP LPC177x8x. It writes the diagnostic:
ccw3kZ46.s:404: Error: bad instruction `udf.w #0'
How can I create a function that causes a Undefined Instruction exception?
Some extra info...
One of GCC's builtins is
void __builtin_trap (void)
This function causes the program to exit abnormally. GCC implements this function by using a target-dependent mechanism (such as intentionally executing an illegal instruction) or by calling abort. The mechanism used may vary from release to release so you should not rely on any particular implementation.
Its implementation for ARMv7 is:
(define_insn "trap"
[(trap_if (const_int 1) (const_int 0))]
""
"*
if (TARGET_ARM)
return \".inst\\t0xe7f000f0\";
else
return \".inst\\t0xdeff\";
"
[(set (attr "length")
(if_then_else (eq_attr "is_thumb" "yes")
(const_int 2)
(const_int 4)))
(set_attr "type" "trap")
(set_attr "conds" "unconditional")]
)
So for ARM mode gcc will generate 0x7f000f0 (f0 00 f0 07)
and for other modes 0xdeff (ff de)
(comes handy when disassembling / debugging).
Also note that:
these encodings match the UDF instruction that is defined in the most
recent edition of the ARM architecture reference manual.
Thumb: 0xde00 | imm8 (we chose 0xff for the imm8)
ARM: 0xe7f000f0 | (imm12 << 8) | imm4 (we chose to use 0 for both imms)
For LLVM __builtin_trap
values generated are 0xe7ffdefe
and 0xdefe
:
case ARM::TRAP: {
// Non-Darwin binutils don't yet support the "trap" mnemonic.
// FIXME: Remove this special case when they do.
if (!Subtarget->isTargetDarwin()) {
//.long 0xe7ffdefe @ trap
uint32_t Val = 0xe7ffdefeUL;
OutStreamer.AddComment("trap");
OutStreamer.EmitIntValue(Val, 4);
return;
}
break;
}
case ARM::tTRAP: {
// Non-Darwin binutils don't yet support the "trap" mnemonic.
// FIXME: Remove this special case when they do.
if (!Subtarget->isTargetDarwin()) {
//.short 57086 @ trap
uint16_t Val = 0xdefe;
OutStreamer.AddComment("trap");
OutStreamer.EmitIntValue(Val, 2);
return;
}
break;
}
There are different official undefined instructions in [1] (look for UDF; .
below can be replaced with any hex digit):
0xe7f...f.
- ARM or A1 encoding (ARMv4T, ARMv5T, ARMv6, ARMv7, ARMv8)0xde..
- Thumb or T1 encoding (ARMv4T, ARMv5T, ARMv6, ARMv7, ARMv8)0xf7f.a...
- Thumb2 or T2 encoding (ARMv6T2, ARMv7, ARMv8)0x0000....
- ARMv8-A permanently undefinedThere are others, but these are probably as future-proof as it gets.
As for generating the code that triggers it, you can just use .short
or .word
, like this:
asm volatile (".short 0xde00\n");
/* thumb illegal instruction */asm volatile (".word 0xe7f000f0\n");
/* arm illegal instruction */asm volatile (".word 0xe7f0def0\n");
/* arm+thumb illegal instruction */Note that the last one will decode to 0xdef0
, 0xe7f0
in thumb context, and therefore also cause an undefined instruction exception.
[1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html
[2] DDI0487D_b_armv8_arm.pdf
Building on Masta79's answer:
There is a "permanently undefined" encoding listed in the ARMv7-M architecture reference manual - ARM DDI 0403D (documentation placeholder, registration required). The encoding is 0xf7fXaXXX (where 'X' is ignored). Of course instruction fetches are little-endian, so (without testing):
asm volatile (".word 0xf7f0a000\n");
should yield a guaranteed undefined instruction on any ARMv7-M or later processor.
The thumb-16 version of the permanently undefined instruction is 0xDExx
. So you can do this in your code to trigger the exception:
.inst 0xde00
Reference: ARMv7-M Architecture Reference Manual, section A5.2.6.
(Note that 0xF7Fx, 0xAxxx encoding is also permanently undefined, but is a 32-bit instruction.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With