ARM ADR vs. LDR
Since both the LDR
pseudo-instruction and the ADRP
instruction can load the address of a label, and the LDR
pseudo-instruction can address the 64-bit address space, and the addressing range of the ADRP
instruction is the current PC address ±4GB, then what is the necessity of having an ADRP
instruction when there's already an LDR
pseudo-instruction?
ADR faster than indirect LDR#
In Arm GNU Toolchain and ARM64 System calls, we call svc #0
to request Linux system call to write a character string to standard output device. It uses a combination of adr
, ldr
and mov
instructions.
Modern Arm Assembly Language Programming: Covers Armv8-A 32-bit, 64-bit, and SIMD | Chapter 9: Armv8-32 SIMD Floating-Point Programming - Packed Floating-Point Arithmetic - Conversions
The next instruction, adr r3,CvtTab
(form PC relative address), loads the address of CvtTab into R3
. The ADR
instruction is used here instead of a ldr r3,=CvtTab
instruction since the target label is located within ±4095 bytes of the PC. Using an ADR
instruction is also slightly faster since it eliminates the extra memory read cycle that the LDR
instruction needs to load the target address from a literal pool as explained in Chapter 2. Following the ADR
instruction is a LDR r3,[r3,r2,lsl #2]
instruction that loads the correct label address from CvtTab.
The range of ADR
is just as limited as an unconditional b
or a bl
. To address a label that is a greater distance away, yet within 4 GB in either direction, the ADRP
instruction can be used in AArch64.
LDR or ADR? non-PIC or PIC?#
An address loaded using the LDR
pseudo-instruction is fixed at link time, so the code is not position-independent(non-PIC). The address holding the constant remains valid regardless of where the linker places the ELF section containing the LDR
instruction.
Meanwhile, ADR
produces position-independent(PIC) code, because the assembler generates an instruction that adds or subtracts a value to the PC
. The expansive ADRL
pseudo-instruction produces position-independent code too, because the address is PC-relative or register-relative.
The label used with ADR
or ADRL
must be within the same code section. If a label is out of range in the same section, the assembler faults the reference. As an aside, if a label is out of range in other code sections, the linker faults the reference.
if you plan to reference labels in other sections of code, or you know that a literal table will exist and you don't mind the extra cycles used to fetch the literal from memory. Use the same caution with literal pools that you would for the construct LDR <Rd>, = constant
.
Arm Compiler armasm User Guide | 7. Writing A32/T32 Assembly Language - 7.12 Load addresses to a register using LDR Rd, =label
The example of string copy also shows how, unlike the ADR
and ADRL
pseudo-instructions, you can use the LDR
pseudo-instruction with labels that are outside the current section. The assembler places a relocation directive in the object code when the source file is assembled. The relocation directive instructs the linker to resolve the address at link time. The address remains valid wherever the linker places the section containing the LDR
and the literal pool.
ARM 64-Bit Assembly Language | 3 Load/store and branch instructions - 3.5 Branch instructions - 3.5.5 Form PC-relative address
The ADR
instruction is helpful for calculating the address of labels at run-time. This is particularly useful when the address of a label must be passed to a function as an argument(see head.S later), but the address cannot be determined at compile time. For example, the address of some system libraries may not be set by the linker, but are set when the program is loaded and prepared to run. The addresses of labels in these libraries cannot be loaded with the ldr Rx,=label
syntax, because the assembler and linker cannot predict the location of the label. The ADR
instruction provides a way to get the address of the label.