Search code examples
gccarminline-assemblycortex-m

Programmatically cause Undefined Instruction exception


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?


Solution

  • 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;
    }