I'm going through the x86-64 tutorial on exercism.org. I'm using NASM on Linux, producing an ELF binary. There is just a bit of C code that invokes my assembly code in a test harness. Their build system specifies -pie in the LDFLAGS and -fPIE in the CFLAGS (among others, but those are the most relevant here I think). Therefore, I need (and would like to understand) a solution that uses PIC, which requires RIP-relative addressing.
I have an index (in rdi) into an array of 8-byte (qword) values called values. I just want to get the address at the offset in order to mov the value it points to into a register. Or I would accept moving the value directly.
I tried this:
lea rbx, [rel values + rdi * 8]
My understanding is that this will look at the address (relative to rip) of values, which is in the data section, then it will add to that the correct offset (rdi * 8) and put that in rbx.
But this produces next error:
/usr/bin/ld: space_age.o: relocation R_X86_64_32S against `.data' can not be used when making a PIE object; recompile with -fPIE
I understand that recompile with -fPIE is the result of the linker thinking that the code was compiled rather than assembled from handwritten assembly. So it seems like NASM is producing a relocation type that is not okay with what it's being linked against, and the linker sees it as not PIE, right?
So I then tried this:
lea rbx, [rdi * 8] ; first compute the offset: index * sizeof(qword value)
lea r8, [rel values] ; then find the address of the values
add rbx, r8 ; rbx = offset of the array + address of base of the array
To me, this is the exact same thing as the first instruction. I don't understand how these differ, but something about the fact that the relocation in the first is R_X86_64_32S seems to suggest that the first instruction only has a signed 32 bit offset, and maybe lea r8, [rel values] creates a different relocation type that's 64 bits, but I'm just guessing there.
Is this possible to do in one instruction?
Edit: this is not a duplicate of this question about addressing modes because it did not explain why NASM is accepting my code but then it fails to link. I now understand thanks to the accepted answer that this addressing mode does not exist, but NASM silently ignores the rel part.
There is no such instruction as
lea rbx, [rel values + rdi * 8], or in other wordslea rbx, [values + rip + rdi * 8]. Therip-relative addressing mode cannot be combined with an index register (with or without scaling). See Referencing the contents of a memory location. (x86 addressing modes)Unfortunately, it looks like nasm handles this by just ignoring the
reland assemblinglea rbx, [values + rdi * 8], which would require the problematic relocation. The address ofvalueswould have to go in the 32-bit displacement field of the instruction's memory operand, but that is impossible sincevaluesneed not be located in the lowest or highest 2 GB of memory; hence the confusing error message.But the correct solution is that you just have to write more instructions. You can get the desired effect in two instructions with
Or if you want to actually do the load:
(The choices of registers are arbitrary.)