Invalid constant after fixup?

Kristina Brooks picture Kristina Brooks · Apr 21, 2012 · Viewed 23.7k times · Source

For some reason, when I try to compile this bit of code, the compiler says syscall.s:72:invalid constant (0x172) after fixup:

.globl _mach_msg_trap$MACH
_mach_msg_trap$MACH:
    stmfd sp!, {r4,r7}
    mov r7, #370 /* this is line 72 */
    svc 0
    ldmfd sp!, {r4, r7}
    bx lr

I don't know why it's doing it. When I put a smaller constant into r7, it works fine. But with higher numbers, it spits out this error. I've temporary fixed it by doing mov r7, #300 and add r7, #70, which achieves the desired effect. Still not sure what caused the error though.

Answer

Nico Erfurth picture Nico Erfurth · Apr 21, 2012

The ARM Instruction Set, up to ARMv5, is only able to use a limited range of immediate values. The problem is that the value has to be encoded in the instruction itself. As all ARM Instructions are 32-bit wide, the original instruction-set up to ARMv5 only had a total of 8+4 bits to encode immediates. With the first 8-bit being able to load any 8-bit value in the range of 0-255 and the 4 bit being a right rotate in steps of 2 between 0 and 30.

So you can load values like:

#0
#122
#121 ror #24 = 30976
#230 ror #12 = 241172480

But, #370 is not loadable with this scheme, it would require something like #185 ror #31 which is not possible.

There are two ways to get your immediate value loaded.

  1. How you already solved it by constructing the value in multiple steps.
  2. By loading the value from memory with ldr: ldr r7,=#370 The assembler then will create a constant-pool and load the value from there via pc-relative addressing.

Usually you should prefer to construct constants with up to 2 instructions, if thats not possible (or the value has to be relocatable) use ldr.

Starting with ARMv7 you can also use movw to load any 16 bit value in the lower half of a register while zeroing the top-half and movt to load another 16bit value to the upper half without touching the lower half.