Search code examples
arraysassemblyshared-librariesx86-64position-independent-code

why can't i access an array with a register as offset?


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

Solution

  • 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]