汇编程序何时更好地使用符号扩展重定位(如R_X86_64_32S)而不是像R_X86_64_32那样的零扩展?

Cir*_*四事件 2 assembly x86-64 gnu-assembler nasm elf

作为一个具体的例子,在GAS 2.24上移动地址:

mov $s, %eax
s:
Run Code Online (Sandbox Code Playgroud)

后:

as --64 -o a.o a.S
objdump -Sr a.o
Run Code Online (Sandbox Code Playgroud)

使用零扩展:

0000000000000000 <s-0x5>:
   0:   b8 00 00 00 00          mov    $0x0,%eax
                        1: R_X86_64_32  .text+0x5
Run Code Online (Sandbox Code Playgroud)

但内存访问:

mov s, %eax
s:
Run Code Online (Sandbox Code Playgroud)

编译以签署扩展名:

0000000000000000 <s-0x7>:
   0:   8b 04 25 00 00 00 00    mov    0x0,%eax
                        3: R_X86_64_32S .text+0x7
Run Code Online (Sandbox Code Playgroud)

在这种特定情况下,或者一般情况下,是否有理由使用?我不明白汇编程序如何能够对任何一种情况做出更好的假设.

NASM 2.10.09仅R_X86_64_32用于上述两种情况.更新:在2.11之后的一个边缘nasm commit 6377180产生相同的Gas输出,这看起来像Ross提到的一个bug.

我已经解释了我认为我理解的内容R_X86_64_32S:https://stackoverflow.com/a/33289761/895245

Ros*_*dge 6

不同之处在于符号的允许地址s.在使用R_X86_64_32的第一种情况下,符号必须在0x00000000'00000000到0x00000000'FFFFFFFF的范围内.在使用R_X86_64_32S的第二种情况下,符号的地址必须在0xFFFFFFFF'80000000和0x00000000'7FFFFFFF之间.如果s最终的地址超出这些范围,则链接器将给出错误.

这对应于CPU如何将s编码的32位值解释为两个指令.在第一条指令中,它是一个立即操作数,32位值被零扩展到RAX中.在第二条指令中,32位值是存储器操作数中的位移,因此符号扩展为64位地址.

NASM不应该对第二条指令使用无符号R_X86_64_32重定位.这不是一个更好的问题,使用R_X86_64_32这里是不正确的.NASM允许地址为s0x00000000'80000000,但CPU最终会访问0xFFFFFFFF'80000000.