27

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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Kristina
  • 15,859
  • 29
  • 111
  • 181

1 Answers1

51

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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Nico Erfurth
  • 3,362
  • 22
  • 26
  • I have the same problem, but using armv4. I want to load 0x0000ffff in a register. movw and movt are not supported in this arm mode. And I can't either use ldr r3, =#0000ffff – Trouble-lling Apr 02 '14 at 08:45
  • 1
    You have to load ldr r3, =#0x0000ffff not =#0000ffff. The fixup is down by the assembler. You can also just mov r3, #255 - orr r3,r3, #65280 – Nico Erfurth Apr 02 '14 at 09:06
  • I can't put that value in the register. It's not a direction, it's a constant I want to store in memory, in a position given by v1 variable (in C the instruction is: iowrite32(v1, 0x0000FFFF). The message is: Unable to handle kernel paging request at virtual address 0000ffff – Trouble-lling Apr 02 '14 at 09:25
  • Sorry, I don't know what exactly you want to do there, please write up a complete question about that on SO. Comments are not meant for that kind of discussion but only to further the actual answer or question they are attached to. – Nico Erfurth Apr 02 '14 at 09:30
  • http://stackoverflow.com/questions/22782510/this-assembly-code-makes-nothing This is my question. Where it says edited, I explain the problem with iowrite32. Thanks – Trouble-lling Apr 02 '14 at 09:35
  • This rotated-8-bit immediate encoding isn't unique to `mov` is it? Or does `and reg, reg, #imm` have different limitations? Your first sentence doesn't need to be so specific, and could make this a better duplicate target for questions like [Invalid constant after fixup (and mask)](https://stackoverflow.com/q/63038207) where the not-encodeable instruction was something other than `mov`. – Peter Cordes Jul 22 '20 at 17:01