ldr r0, #0x28

What is the ldr instruction ? Does it load a string from some offset? How can I find the string/value that is actually loaded ?


LDR loads a 32-bit constant (LDRH (halfword): 16 bit, LDRB (byte): 8 bit) from memory into the specified target register (r0 in your example).

Since 32-bit constants cannot be encoded in 32-bit opcodes (or 16-bit for Thumb instructions), the assembler stores the constant in the text segment close to the referencing instruction and then references the value using (usually) PC-relative addressing, i.e. some offset from r15.

Thus, ldr is in fact a pseudo instruction. The following code

    .code 32
    ldr r0, =0x12345678
    bx lr

is translated by the assembler into

00000000 <main>:
       0:   e51f0000    ldr r0, [pc, #-0]   ; 8 <main+0x8>
       4:   e12fff1e    bx  lr
       8:   12345678    .word   0x12345678

As you can see, the constant referenced in the original ldr instruction is in fact stored at address 0x8 instead of in the instruction itself. The ldr instruction at address 0 then references this value using PC-relative addressing. The offset to the PC is 0 (instead of 8), since the actual PC value is always the address of the current instruction + 8 - this is an effect of the early ARM processor pipeline which has to be preserved for compatibility.


This can be simply translated into:

r0 = 0x28;

In ARM assembly, the # marks the immediate values and the r0, r1, ... are registers. The ldr instruction can take the following syntactic forms (yours is the first line):

LDR{type}{cond} Rt, [Rn {, #offset}] ; immediate offset
LDR{type}{cond} Rt, [Rn, #offset]! ; pre-indexed
LDR{type}{cond} Rt, [Rn], #offset ; post-indexed
LDRD{cond} Rt, Rt2, [Rn {, #offset}] ; immediate offset, doubleword
LDRD{cond} Rt, Rt2, [Rn, #offset]! ; pre-indexed, doubleword
LDRD{cond} Rt, Rt2, [Rn], #offset ; post-indexed, doubleword 

ldr without = does PC relative loads

This is true for both labels and numbers.

But you will of course rarely use numbers directly in your assembly. Maybe you have provided some disassembly without labels?

Both of the following work in GNU GAS ARMv8. With a label:

    ldr x0, pc_relative_ldr
    b 1f
    .quad 0x123456789ABCDEF0
    /* x0 == 0x123456789ABCDEF0 */

with an offset:

    ldr x0, 0x8
    b 1f
    .quad 0x123456789ABCDEF0
    /* x0 == 0x123456789ABCDEF0 */

Both are equivalent. The assembler just happens to convert the label into the correct offset for you.

GitHub upstream with assertions.

STR does not have PC-relative addressing like LDR in ARMv8, you just have to calculate the address into registers first: https://stackoverflow.com/questions/28638981/howto-write-pc-relative-adressing-on-arm-asm/54480999#54480999

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.