.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 oflabel
. At runtime, it adds this displacement to the value ofpc
and places the result inreg
, likeadd 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
andadd
, see Understanding ARM relocation (example: str x0, [tmp, #:lo12:zbi_paddr]).ldr reg, =label
will assemble the absolute address oflabel
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 ofliteral_pool
is encoded in the instruction, and at runtime it adds the displacement topc
, 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 theldr reg, literal_pool
instruction itself, an additional 8 bytes are needed for the absolute address oflabel
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 oflabel
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 oflabel
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 caseldr 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 theadrp/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.