I know that both adr
and ldr
pseudo instructions loads addr of a label. The difference between them is that ldr
is absolute but adr
is PC-relative.
But when should I use which?
I always use ldr
to get the address when writing a bare-metal assembly progress. Can you give an explanation and example of using the adr
instruction? Is it related to PIC(Position-Independent-Code)?
adr
generates a PC-relative address. It takes the address of the instruction itself and adds a signed offset that is encoded in the instruction to it.
The ldr
pseudo-instruction (that is, ldr x0, =label
) loads a pre-generated absolute address from a PC-relative position. At compile time, the absolute address of label
is generated and embedded somewhere in the binary, and the (actual PC-relative) ldr
instruction then loads that value from that place.
If we take a sample program (_main
because I'm on macOS):
.globl _main
.p2align 2
_main:
adr x1, label
ldr x2, =label
mov w0, 0
ret
label:
mov w0, 0x1234
ret
Compiling this and looking at it in a disassembler yields:
;-- _main:
0x100003f98 81000010 adr x1, sym.func.100003fa8
0x100003f9c a2000058 ldr x2, 0x100003fb0
0x100003fa0 00008052 mov w0, 0
0x100003fa4 c0035fd6 ret
;-- func.100003fa8:
0x100003fa8 80468252 mov w0, 0x1234
0x100003fac c0035fd6 ret
0x100003fb0 a83f0000 invalid
0x100003fb4 01000000 invalid
Thus, the above program is equivalent to:
.globl _main
.p2align 2
_main:
adr x1, label
ldr x2, addressof_label
mov w0, 0
ret
label:
mov w0, 0x1234
ret
.p2align 3
addressof_label:
.8byte label
This adds one level of indirection, and means that in an environment with ASLR, the dynamic linker must add the ASLR slide to addressof_label
.