1

Being a newbie in Assembly language I managed to tripped up over this hello-world-like program:

.global _start
_start:
    ldr R0, =val
    mov R7, #1
    svc 0
.data
    val: .word 0xffffffff

My intent is just to load the val word into R0, so its value would be used as the program's exit status (its the least significant byte, to be precise). However, instead of the expected value, ff_16 = 255, I get such an output from bash:

$ ./prog; echo $?
132

Output of disassemblying:

00000000 <_start>:
   0:   e59f0004        ldr     r0, [pc, #4]    ; c <_start+0xc>
   4:   e3a07001        mov     r7, #1
   8:   ef000000        svc     0x00000000
   c:   00000000        .word   0x00000000

It shows the presence of an offset from PC by 4 but the word marked as 4: is not the desired one, is it? On the other hand, even this word does not contain 84_16 = 132 value, so where it springs from?

Some clarification is needed, please.

Architecture: arm7l, OS: raspberrypi 5.4.72-v7+

Ilya Loskutov
  • 1,967
  • 2
  • 20
  • 34
  • 2
    As noted, you have one level of indirection too many. But note that you can get the desired behavior more simply by just doing `ldr R0, =#0xffffffff`, and the compiler will put the value in the literal pool for you, saving you the trouble of defining a `val` for yourself. In fact, `0xffffffff` aka `-1` does not need a memory load at all, as this value can be encoded into an immediate: just do `mov R0, #0xffffffff`. Smaller and faster. – Nate Eldredge Jul 19 '22 at 17:05
  • @NateEldredge thank you for this complement! – Ilya Loskutov Jul 19 '22 at 18:28
  • Combine with [Getting label to address](https://stackoverflow.com/questions/15774581/getting-an-label-address-to-a-register-on-arm), the cited duplicate and this question should answer your issue. – artless noise Jul 20 '22 at 14:06

1 Answers1

1

The instruction

        ldr r0, =val

loads the address of val into R0. It assembles to something like

        ldr r0, foo
        ...
foo:    .int val

To load the value of val, you need

        ldr r0, val

Note that this only works if val is reasonably close by (e.g. closer than about ±1 kB). If val is far away or in another segment (as is in your case), you'll need to first load the address and then the datum:

        ldr r0, =val
        ldr r0, [r0]

Also note that you might want to use a movw/movt pair instead of the ldr r0, =... instruction if supported (ARMv7 has it); it's slightly faster where supported as it eliminates a load:

        movw r0, :lower16:val
        movt r0, :upper16:val

These two instructions can be issued interspersed with other instructions, but if they are given consecutively, they macro fuse on some cores which gives a performance benefit, so try to do that. You can also use a lone movw to load any 16 bit value into a register.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • Oh, I got it that `ldr` just loaded the *address*, not the value. But still, could you clarify why is there `#4` in the `ldr r0, [pc, #4]` dissasembly line? `PC` + four bytes is the address of the `mov` command, not one of the needed word `val`, isn't it? – Ilya Loskutov Jul 19 '22 at 16:33
  • @IlyaLoskutov Recall that the program counter runs ahead of the current instruction by 8 bytes during execution. The address this resolves to is `0xc` as given in the comment on the right. – fuz Jul 19 '22 at 16:39