我有一些非常简单的汇编代码行,可以移动一些数据,如下所示:
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]
但是,编译器返回
错误A2202:非法使用段寄存器
我觉得这可能是我的编译器或版本的问题,任何指针都会有帮助
答案 0 :(得分:3)
cs
/ ds
/ es
/ ss
段覆盖前缀,但它们对具有内存的指令几乎没有影响操作数。 (例如,如果来自非规范地址的mov eax, ds:[rbp]
故障(高16位不是低48的符号扩展),它仍然是#SS故障(因为基址寄存器是RBP或RSP),而不是#GP。实际上忽略了DS前缀,而不仅仅是等效的,因为所有这些段都具有相同的固定基址= 0.英特尔手册vol.1 3.3.7.1规范寻址。
如果指令使用基址寄存器RSP / RBP并使用段覆盖前缀来指定非SS段,则a 规范错误生成#GP(而不是#SS)。 在64位模式下,只有FS和GS段覆盖适用于这种情况。其他段覆盖前缀(CS,DS,ES和SS)将被忽略。请注意,这也意味着 应用于“非堆栈”寄存器引用的SS段覆盖被忽略。这样的序列仍然产生 规范错误的#GP(而不是#SS)。
该部分的措辞可能仅意味着在这种情况下,为了例外类型,它们会被忽略。
唯一可以区分的另一种方法是,如果有任何加载或存储尝试使用它会导致DS出现故障的段选择器,然后运行mov eax, ds:[rbp]
。但我不确定这是可能的;当我试图在Linux中设置一个非零值时,我正在mov ds, eax
上得到一个段错误。 NULL段选择器为DS和ES“工作”。鉴于NASM和FASM的汇编警告/行为,这些汇编程序的开发人员至少认为段前缀确实毫无意义。
fs
和gs
是在64位模式下执行任何操作的唯一段覆盖。它们可以具有非零的基址。
CS / DS / ES / SS仅用于填充以使指令更长(出于对齐原因)。 What methods can be used to efficiently extend instruction length on modern x86?
请注意ds
已经是除rbp
/ rsp
以外的所有基址寄存器的默认段,因此显式ds
前缀不会改变其含义指令 at all 即使它没有被忽略。如果您的汇编程序拒绝接受mov rcx,qword ptr ds:[rax+20]
,那么在32位模式下拒绝接受mov ecx, dword ptr ds:[eax+20]
也是合理的。
对于某些汇编程序(例如NASM),在asm源代码行中明确提及一个段会在指令上发出一个段前缀,即使它已经是默认值。如果这是MASM的工作方式,那么它就是将其用于“文档”目的并不是一个好主意。
如果您确实要为填充发出DS前缀,只需明确编码:
mov rax, gs:[60]
db 3Eh ; ds prefix
mov rcx, [rax+20]
有趣的是,在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]
mov rcx, [ds: rax+20]
mov rcx, [rax+20]
使用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]