【问题标题】:why can't i access an array with a register as offset? [duplicate]为什么我不能使用寄存器作为偏移量访问数组? [复制]
【发布时间】:2018-08-29 15:26:06
【问题描述】:

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

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 没有任何错误,但是当我尝试使用 ld 链接和创建共享库时:

relocation R_X86_64_32S 对 `.data' 在制作时不能使用 共享对象;用 -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

【问题讨论】:

  • 相关:stackoverflow.com/questions/43367427/…:PIE 可执行文件对使用 [array + rax] 有相同的限制,因为它要求地址适合 32 位符号扩展值。正如 Jester 所说,奇怪的是 NASM 没有在 [rel array + rax] 上发出警告,因为 rel 不是一个选项,所以它被编码为 [abs array + rax]。顺便说一句,您可以使用default rel 让 NASM 尽可能使用 RIP-relative。

标签: arrays assembly shared-libraries x86-64 position-independent-code


【解决方案1】:

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

mov  al, BYTE [rel array + 3]

汇编器生成一个内存操作数

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

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

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

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

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

【讨论】:

  • 如果 nasm 没有为 [rel array + rax] 生成错误,那么这是一个错误。它应该告诉你这是一个无效的寻址模式。
  • @fuz thx 为答案,但只是要知道,如果您使用 gcc 制作二进制文件,它会起作用,为什么会这样? gcc 需要进行链接并且应该失败?
  • [array + 3 - $ + rip] 也不完全正确。 $ 是当前指令的开始,但 RIP 相对寻址是相对于指令的结尾(即下一条的开始)。
  • @PeterCordes 我知道。但是,添加它会有点复杂且难以解释。
  • @PeterCordes 我的回答是“这种新的寻址模式取代了以前的disp32 寻址模式。”这到底是怎么回事?我还明确地说,rip-relative 寻址模式仅在没有 SIB 的情况下可用。请再次阅读我的回答,您可能误解了倒数第二段。
猜你喜欢
  • 2018-04-03
  • 2020-03-10
  • 2021-01-16
  • 2018-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-28
相关资源
最近更新 更多