为什么我不能访问以寄存器作为偏移量的数组?

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

我正在编写一些汇编代码 (intel),但我不明白为什么当我尝试创建共享库时这段代码不起作用:

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

然而,如果您通过用数字更改寄存器来修改带有 "mov" 的行,它会起作用:

mov  al, BYTE [rel array + 3]

我对 nasm 没有任何错误,但是当我尝试 link 并使用 ld 创建共享库时:

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

我找到了 "R_X86_64_32S" 错误的答案:How does C++ linking work in practice?

但是我不明白为什么我不能使用 "rax" 作为偏移量,而我可以使用数字。

有没有办法浏览数组?

这是我用来创建共享库的命令:

nasm -f elf64 test.s
ld -shared test.o -o test.so

对于长模式(64 位模式),AMD 引入了 rip x86 的相对寻址。如果你输入

mov  al, BYTE [rel array + 3]

汇编程序生成一个内存操作数,效果为

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

这意味着当机器代码被加载到不同的地址时,内存操作数仍然会到达正确的位置,因为只有array相对于它被引用的指令的相对偏移量被编码,而不是array 的绝对地址,在 link 时未知。

现在使用变址寄存器时linking失败的原因是这种新的寻址模式取代了以前的disp32寻址模式(modr/m byte 05 +r) .它不适用于 SIB (scale/index/base) 寻址模式(事实上,之前的 disp32 寻址模式仍然可以通过既没有基数也没有索引的 SIB 操作数使用),因此汇编器无法生成位置无关代码的适当内存操作数。

解决方案是首先使用leaarray的绝对地址加载到某个寄存器中,然后访问相对于刚刚加载的地址的数组成员:

lea rbx, [rel array]
mov al, byte [rbx + rax]