.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, labelwill 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 ofpcand 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.textand.datasections 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
adrpandadd, see Understanding ARM relocation (example: str x0, [tmp, #:lo12:zbi_paddr]).ldr reg, =labelwill assemble the absolute address oflabelinto 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 labelwhere
ldr reg, literal_poolis a "literal load": the displacement between its own address and that ofliteral_poolis 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_poolinstruction itself, an additional 8 bytes are needed for the absolute address oflabelin the literal pool.Also, as you can see from the above,
ldr reg, =labelis not position-independent, since it requires the absolute address oflabelto 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 oflabelwill 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, =labelis 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, =labelmight seem like the simplest approach, it is not the most efficient or portable. Compilers normally use theadrp/addcombination 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.
