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 mov
ing 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
rel
and assemblinglea rbx, [values + rdi * 8]
, which would require the problematic relocation. The address ofvalues
would have to go in the 32-bit displacement field of the instruction's memory operand, but that is impossible sincevalues
need 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.)