3

I am making some assembly code (intel) and I don't understand why this code does not work when I try to create a shared library :

BITS 64
SECTION .text
GLOBAL test
test:
push rbp
mov  rbp, rsp

mov rax, 3
mov  al, BYTE [rel array + rax]

pop  rbp
ret

SECTION  .data
array times 256 db 0

Whereas if you modify the line with the "mov" by changing the register with a number, it works :

mov  al, BYTE [rel array + 3]

I do not have any error with nasm, but when i try to link and create a shared library with ld :

relocation R_X86_64_32S against `.data' can not be used when making a shared object; recompile with -fPIC

I found this answer for the "R_X86_64_32S" error : How does C++ linking work in practice?

But what I do not understand why I can not use "rax" as an offset, whereas I can with a number.

Is there a way to browse thought the array ?

This is the commands I use to create the shared library :

nasm -f elf64 test.s
ld -shared test.o -o test.so
Jester
  • 56,577
  • 4
  • 81
  • 125
  • Related: https://stackoverflow.com/questions/43367427/32-bit-absolute-addresses-no-longer-allowed-in-x86-64-linux: PIE executables have the same limitation against using `[array + rax]`, because it requires the address to fit in a 32-bit sign-extended value. As Jester says, it's weird that NASM doesn't warn on `[rel array + rax]`, because rel isn't an option so it's encoded as `[abs array + rax]`. And BTW, you can use `default rel` to make NASM use RIP-relative wherever possible. – Peter Cordes Mar 20 '18 at 22:12

1 Answers1

3

With long mode (64 bit mode), AMD introduced rip relative addressing to x86. If you type

mov  al, BYTE [rel array + 3]

the assembler generates a memory operand to the effect of

mov  al, BYTE [array + 3 - $ + rip]

This means that when the machine code is loaded to a different address, the memory operand still goes to the right place as only the relative offset of array from the instruction it was referenced from is encoded, not the absolute address of array, which isn't known at link time.

Now the point why linking fails when using an index register is that this new addressing mode replaces the previous disp32 adressing mode (modr/m byte 05 +r). It is not available with SIB (scale/index/base) adressing modes (in fact, the previous disp32 adressing mode is still available through a SIB operand with neither base nor index), so the assembler is unable to generate an appropriate memory operand for position independent code.

The solution is to first load the absolute address of array into some register using lea and then to access array members relative to the address just loaded:

lea rbx, [rel array]
mov al, byte [rbx + rax]
fuz
  • 88,405
  • 25
  • 200
  • 352
  • 3
    If nasm doesn't generate an error for `[rel array + rax]` then it's a bug. It should tell you that it is an invalid addressing mode. – Jester Mar 20 '18 at 17:46
  • @fuz thx for the answer, but just to know, it works if you use gcc to make a binary, why is that ? gcc need to do the linking and should fail ? – Michael Vouriz Mar 20 '18 at 18:57
  • `[array + 3 - $ + rip]` isn't quite right, either. `$` is the start of the current instruction, but RIP-relative addressing is relative to the end of the instruction (i.e. start of the next). – Peter Cordes Mar 20 '18 at 19:21
  • @PeterCordes I know. However, adding this would have been a bit complicated and tedious to explain. – fuz Mar 20 '18 at 20:31
  • @PeterCordes My answer says “this new addressing mode replaces the previous `disp32` adressing mode.” How exactly is that wrong? I also say explicitly that the rip-relative addressing mode is only available without a SIB. Please read my answer again, you might have misunderstood the second to last paragraph. – fuz Mar 20 '18 at 20:33
  • I think I read it backwards, like changing to `[array + rax]` *replaces* .... But you were talking about x86-64 replacing one form of x86-32's `[disp32]` addressing mode. And the SIB / no-SIB part is correct, IDK how I misread that. I had just woken up, and apparently didn't read the question either before closing it as the wrong duplicate. Fixed now. – Peter Cordes Mar 20 '18 at 22:09