为什么在 64 位模式下使用“DS:”段覆盖是非法的?

2 assembly x86-64 masm

我有几行非常简单的汇编代码,可以移动一些数据,如下所示:

mov rax,qword ptr gs:[60]                         
mov rcx,qword ptr ds:[rax+20]                                         
mov rax,qword ptr gs:[60]                         
mov rcx,qword ptr ds:[rax+20]                     
mov rbx,qword ptr ds:[rcx+28] 
Run Code Online (Sandbox Code Playgroud)

但是,编译器返回

错误 A2202:非法使用段寄存器

我有一种感觉,这可能是我的编译器或版本的问题,任何指针都会有所帮助

Pet*_*des 5

cs/ ds/ es/ss段重载前缀允许在机器代码,在64位模式。但是一些组装商选择不允许它们。

但请注意,它们实际上对带有内存操作数的指令没有影响。这可能就是为什么 masm 显然选择不允许它的原因,以避免在此类前缀上浪费空间的“错误”。其他汇编程序,如 NASM,确实允许这样做。

所有这些段都具有相同的固定基址 = 0,甚至不更改将引发的异常。

英特尔手册 vol.1 3.3.7.1 规范寻址:

如果指令使用基址寄存器 RSP/RBP 并使用段覆盖前缀来指定非 SS 段,则规范错误会生成 #GP(而不是 #SS)。在 64 位模式下,只有 FS 和 GS 段覆盖适用于这种情况。忽略其他段覆盖前缀(CS、DS、ES 和 SS)。请注意,这也意味着将忽略应用于“非堆栈”寄存器引用的 SS 段覆盖。这样的序列仍然会为规范错误生成#GP(而不是#SS)。

例如,如果mov eax, ds:[rbp]来自非规范地址的错误(高 16 位不是低 48 的符号扩展),它仍然是 #SS 错误(因为基址寄存器是 RBP 或 RSP),而不是 #GP。DS 前缀真的被忽略了。

您可以分辨出差异的唯一其他方法是,是否可以为 DS 提供一个段选择器,如果任何加载或存储尝试使用它,该段选择器就会出错,然后运行mov eax, [ds:rbp]. 但我不确定这是否可能;mov ds, eax尝试在 Linux 中设置非零值时,我立即遇到了段错误。NULL 段选择器适用于 DS 和 ES。鉴于 NASM 和 FASM 的汇编器警告/行为,这些汇编器的开发人员至少认为段前缀确实毫无意义。


fs并且gs是唯一在 64 位模式下执行任何操作的段覆盖。

它们可以具有非零基地址。

CS/DS/ES/SS 仅用于填充以使指令更长(出于对齐原因)可以使用哪些方法有效地扩展现代 x86 上的指令长度?

请注意,ds已经是比其他所有的基址寄存器的默认段rbp/ rsp,所以一个明确的ds前缀不会改变指令的含义在所有即使它没有被忽略。如果您的汇编程序拒绝接受mov rcx,qword ptr ds:[rax+20],那么mov ecx, dword ptr ds:[eax+20]在 32 位模式下也拒绝接受也是合理的。


对于某些汇编程序(例如 NASM),在 asm 源代码行中明确提及段会在指令上发出段前缀,即使它已经是默认值。 如果这就是 MASM 的工作方式,那么将其用于“文档”目的并不是一个好主意。

如果您确实想为填充发出 DS 前缀,只需显式编码它们:

mov   rax, gs:[60]
db    3Eh   ; ds prefix
mov   rcx, [rax+20]
Run Code Online (Sandbox Code Playgroud)

其他汇编器(GAS、NASM)

有趣的是,在 GAS 中.intel_syntax noprefix(类似于 MASM),ds:[rax+20]在没有显式前缀的情况下进行组装(因为它已经是默认值),但ss:[rax+20]确实会发出前缀。因此,即使在 64 位代码中,GAS 也不假设 cs/ds/es/ss 是等效的。

NASM 警告: segments.asm:5: warning: ds segment base generated, but will be ignored in 64-bit mode

mov rax, [gs: 60]             ; NASM requires the seg: to be
mov rcx, [ds: rax+20]         ;  inside the [] if present
mov rcx, [rax+20]
Run Code Online (Sandbox Code Playgroud)

用 NASM 组装到这个(objdump -drwC -Mintel输出):

  4000b0:       65 48 8b 04 25 3c 00 00 00      mov    rax,QWORD PTR gs:0x3c
  4000b9:       3e 48 8b 48 14          mov    rcx,QWORD PTR ds:[rax+0x14]
  4000be:       48 8b 48 14             mov    rcx,QWORD PTR [rax+0x14]
Run Code Online (Sandbox Code Playgroud)