Home > Back-end >  Why use adr and ldr x2, =var to get the address of a variable?
Why use adr and ldr x2, =var to get the address of a variable?

Time:09-05

.data
varA: .quad 25
.text
.global main
//main function
main:
    adr x0,varA
    ldr x1, [x0]
    ldr x2,=varA
    ldr x3,[x2]

Will x0 and x2 same? Just memory access difference here as i learnt.

but why we need to again assign it to same or different register using [] to assign value? Isnt adr did it first line of main code?

CodePudding user response:

adr reg, label and ldr reg, =label will both, in principle, place the address of label in the register reg. So the code in your question will result in x0, x2 both containing the address of varA, and x1, x3 both containing the value 25.

However, they work differently under the hood, which is good to understand because it leads to certain restrictions and tradeoffs for each approach.

  • adr reg, label will assemble into the instruction the displacement between the instruction's own address and the address of label. At runtime, it adds this displacement to the value of pc and places the result in reg, like add reg, pc, #(label - .) if such an instruction existed. This has the advantage of being position-independent; if the entire binary is loaded at an arbitrary address in memory, then the same machine code still works, so long as the .text and .data sections remain at the same relative positions.

    The limitation is that the instruction only has space to encode a 19-bit displacement, so if your program is so large that the label and the instruction are more than 1 MB away from each other in either direction, the program will not link. You can increase this to 4 GB by using a two-instruction sequence of adrp and add, see Understanding ARM relocation (example: str x0, [tmp, #:lo12:zbi_paddr]).

  • ldr reg, =label will assemble the absolute address of label into some nearby location in memory (called a literal pool), and emit a load instruction to fetch the address from there. Equivalent to:

    ldr reg, literal_pool
    ...
    literal_pool:
    .quad label
    

    where ldr reg, literal_pool is a "literal load": the displacement between its own address and that of literal_pool is encoded in the instruction, and at runtime it adds the displacement to pc, then loads from the resulting address. The displacement has to be less than 1 MB, but the assembler can usually arrange to put a literal pool within that range.

    The upside is that, in principle, it works without restrictions on the relative position of label, which can be anywhere in the 64-bit address space. The downside is that extra memory is needed: besides the 4 bytes of the ldr reg, literal_pool instruction itself, an additional 8 bytes are needed for the absolute address of label in the literal pool.

    Also, as you can see from the above, ldr reg, =label is not position-independent, since it requires the absolute address of label to go in the literal pool. If the code ends up being loaded at a different address than the linker assumed, such as for address space layout randomization, then the stored address of label will have to be updated when the program is loaded, before it is executed. In some cases the operating system will do this for you automatically (e.g. on Linux) but it does make the loading of your program take a few extra cycles, and the necessary metadata will make your binary a little larger. Other operating systems just don't support this, e.g. MacOS (Why can't I assemble absolute addresses in the .text section on ARM64 MacOS?), in which case ldr reg, =label is simply not usable. And if you are running on bare metal instead of under an existing OS, you would have to write extra code in your loader to do this relocation.

    As a result, although ldr reg, =label might seem like the simplest approach, it is not the most efficient or portable. Compilers normally use the adrp/add combination mentioned above, which provides a position-independent solution that also works in quite large programs.

CodePudding user response:

I finally understood the concept.

x0,x2 just hold the address then we assign the value to x1,x3 from the address.

  • Related