2

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.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Hut8
  • 6,080
  • 4
  • 42
  • 59
  • Does this answer your question? [Referencing the contents of a memory location. (x86 addressing modes)](https://stackoverflow.com/questions/34058101/referencing-the-contents-of-a-memory-location-x86-addressing-modes) – Nate Eldredge Apr 17 '22 at 21:31
  • 1
    You can't use `[rel values]`, which is to say `[rip + values]`, together with an index register. No such addressing mode is supported by the CPU. This is not about relocations; `lea rbx, [rel values + rdi * 8]` is simply not an encodable instruction. You can have `[disp + base + index * scale]`, or `[rip + disp]`, but not both together. – Nate Eldredge Apr 17 '22 at 21:32
  • @NateEldredge if that's not encodable instruction, then how does nasm assemble it? nasm runs fine on that code, it just fails during linking. – Hut8 Apr 17 '22 at 21:34
  • 1
    Yeah, good question. It looks like nasm just ignores the `rel` and treats it as `lea rbx, [values + rdi * 8]`, which is encodable but is not what you want, and in particular would require the problematic relocation. – Nate Eldredge Apr 17 '22 at 21:36
  • @NateEldredge: The best duplicate about how addressing modes work is [Can rip be used with another register with RIP-relative addressing?](https://stackoverflow.com/q/48124293) but that's using GAS `.intel_syntax`, so it doesn't mention NASM. I agree with Hut8 that it would be better if NASM at least warned when `rel` is used explicitly in an addressing mode it can't support, rather than just `default rel` globally. – Peter Cordes Apr 17 '22 at 21:53

1 Answers1

4

There is no such instruction as lea rbx, [rel values + rdi * 8], or in other words lea rbx, [values + rip + rdi * 8]. The rip-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 assembling lea rbx, [values + rdi * 8], which would require the problematic relocation. The address of values would have to go in the 32-bit displacement field of the instruction's memory operand, but that is impossible since values 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

lea rbx, [rel values]
lea rbx, [rbx + rdi * 8]

Or if you want to actually do the load:

lea rbx, [rel values]
mov rcx, [rbx + rdi * 8]

(The choices of registers are arbitrary.)

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82