理解x86 m*操作数(包括FPU和其他操作数)

3
我正在尝试制作一个简单的 x86 反汇编器(目前是 32 位)以便学习。
因此,英特尔文档如下:

enter image description here

但我觉得这非常让人困惑。
首先,m8-32操作数似乎表示ES:(E)DIDS:(E)SI。但是无法确定在哪种情况下会出现其中的一种。在某些操作码中,您有OPCODE m8,m8,而在其他操作码中,您只有一个操作数m8,经过多次检查后,我得出结论,没有普遍规则。
然后还有这些其他的,简单地描述为“内存操作数在内存中”,这让我更加困惑。可能应该有一个位移、绝对地址或相对偏移吗?如果有,那么这有什么意义呢,既然我们已经有了moffsrel
接下来的一些操作码有些道理,但是冒号后面的数字是位移吗?&符号的那些则让我完全不知所措。
除此之外,还有这些m[number][descriptor],据我所见,它们是用于FPU的?(我还没有处理0Fh转义操作码)。

enter image description here

enter image description here

enter image description here

抱歉,我可能错过了一些非常明显的东西,就像我经常做的那样。提前谢谢。

FPU走的节奏与众不同。这与它的起源有很大关系,它曾经是一颗单独销售的芯片(8087),与处理器分开出售。非常不同的数据类型,没有寄存器只有一个栈。直到很晚才被集成到同一颗芯片中,至于Pentium是第一个保证具备此功能的处理器。请记住,在现代软件开发中它已经变得相当无关紧要,它有太多怪癖,并且现代编译器生成SSE代码。 - undefined
@HansPassant: x87在内存中使用与SSE/SSE2相同的两种数据类型:IEEE754单精度和双精度浮点数(m32fp和m64fp)。只有当您使用fld/fstp的m80fp形式时,才会得到10字节的内部格式,它是IEEE754扩展精度格式。它比单精度/双精度多了一些位,但除了不使用隐藏/隐含的最高有效位之外,其工作方式相同。但是作为一个整体,x87很糟糕,并且绝对不是一个适合编译器的目标,因为它使用寄存器栈! - undefined
2个回答

4
常规指令,如可使用内存操作数的add指令也适用于寄存器,因此ADD有add r32, r/m32add r/m32, r32的编码add eax, ecx可以使用任何一种编码/操作码(无关紧要)。
这就是为什么m32(而不是r/m32)通常只是movsdstosd等字符串指令的隐式操作数,并且Intel表示它们通常使用ES:(E)DIDS:(E)SI

首先,m8-32操作数似乎表明ES:(E)DI或DS:(E)SI。 但是,在哪种情况下使用其中之一却无从得知。

m32表示32位内存操作数,不能代替寄存器。 查看特定指令的条目以查看如何指定操作数(例如,lodsb/w/d/qDS:(E/R)SI是隐式的),而其他指令可能使用ModR/M操作数但需要它为内存。
对于x87,额外的注释告诉您指令如何解释它。例如,m32fp是32位IEEE单精度float(例如用于fmulfld),而m32int是32位整数(例如用于fimulfild)。

除了x87之外,数字仅告诉您操作数大小。这就是全部。

通常使用通常的ModR/M +可选SIB指定内存操作数。唯一的例外是隐式寻址模式(例如pop rax读取qword [rsp]或字符串指令),或MOV的moffs形式,它们跳过ModR/M字节并只使用16/32/64位偏移量(与地址大小相同)。

mov al/ax/eax/rax, [moffs8/16/32/64] (或者store形式)是唯一一个可以直接使用64位绝对地址的指令,不需要先将其放入寄存器中。

注意,moffs8是8位操作数,而不是8位立即数地址。指令的地址大小属性(在64位模式下默认为64位,可通过使用0x67地址大小前缀进行覆盖)决定了多少个字节的绝对地址跟随操作码。

汇编器会为您处理此问题,并在32位代码中为mov eax,[symbol]保存代码大小时使用moffs编码。通常,只需按照正常方式编写寻址模式(参见引用内存位置的内容。 (x86寻址模式)),让汇编器生成ModR/M字节,或者警告您如果您做了非法的事情(如尝试使用不同的寄存器执行movsb)。


有关x86汇编更多信息,请参见x86标签wiki。此外,Agner Fog的指南非常好,尽管他不尝试涵盖这样的基础知识。然而,阅读Agner的指南并查看他对短例子(几个指令长)的说法将帮助您理解汇编是如何工作的。


我认为问题是:使用的是(E)SI还是(E)DI?然后我猜这会帮助提问者知道SI代表源索引(如果我没记错的话),DI代表目标索引,而且这两个寄存器都隐含地在指令中以唯一合理的顺序使用。 - undefined
@PeterCordes 或许我没有表达清楚,但我的问题从来都不是与modrm/sib字节、moffs或rels等有关的,那些很容易理解。我的问题主要是与m这样的操作数有关。与此同时,我找到了这个网址http://ref.x86asm.net/index.html#Instruction-Operand-Codes,如果你看一下BA、BB和BD条目,即使英特尔文档将它们都称为相同的名称,实际上它们并不是同一回事,它们是隐含在操作码中的。而且很难弄清楚,因为很少有文档说明操作码实际上指定了它,只是说是m。我的问题哪里不清楚呢? - undefined
@TrisT: 大部分你的问题只是过长了,我只是懒得仔细阅读其中的一些部分。这个回答最初是作为评论开始的,后来我决定将其发布为一个回答,然后它变得越来越长。我并没有觉得stos的条目不清楚:http://felixcloutier.com/x86/STOS:STOSB:STOSW:STOSD:STOSQ.html看看操作码列:只有`AA`,没有其他操作数。而且在该表中,操作数编码全都是`NA`。描述也非常明确:`对于传统模式,在地址ES:(E)DI处存储EAX;对于64位模式,在地址RDI或EDI处存储EAX。`没有指定不同寄存器的空间。 - undefined
@TrisT:或者换句话说,Intel的m32术语甚至没有试图告诉你操作数是如何编码的。这可能是与指令相关的。但对于汇编程序员(而不是写反汇编器的人)来说,查看文档并看到m32是有用的。我清楚地知道这意味着什么:它是一个32位的内存操作数。所以在文档中我要找的就是这种形式的指令。 - undefined
@PeterCordes "作为一个汇编程序员很有用(不是写反汇编器的人)" 嗯,我的问题是关于反汇编的。我现在完全明白它是依赖于操作码的,并且我已经将其编码为特定的操作数,而不仅仅是“mXYZ”(在我之前的评论中提供的链接的帮助下),但是你可以看到它可能会让人感到困惑,并且并不真正解释清楚,因为它可以是ES:(E)DI或者DS:(E)SI,除非你为每个操作码硬编码,否则仅凭操作数是无法知道的。所以你可以看到文档(或基于文档的注释/答案)并没有太大帮助。 - undefined
@TrisT:是的,我能理解文档中那个部分对你来说没有帮助,但任何使用m32的具体指令都描述了它如何编码操作数,或者如果是隐含的话,它们是什么。我试图解释文档撰写者可能持有的观点,以及他们所针对的受众可能会发现该文本有用。它只是试图解释并提供一些关于将在每个指令文档中用作占位符的符号的背景知识,并不是要说m32是特定的指令编码方面的东西。 - undefined

0

我刚刚发现ref.x86asm.net有一个“极客”版本的表格。

这里描述了操作码here

“极客”版本不像“编码者”那样含糊不清。

不过,如果有人能告诉我在哪里可以自学这个,我将非常感激。我似乎无法在英特尔文档或除了x86asm之外的任何地方找到它。

再次提醒,我经常会错过一些东西,所以如果我找到了什么,我会进行编辑。

希望我能帮到你,祝你愉快。


相关部分是modr/m字节和sib字节,它们描述了指令的操作数。我在这个答案中详细介绍了它们的细节。我还在这里写了另一个可能有趣的答案。 - undefined
我还强烈建议你阅读旧版本的CPU手册,因为它们通常更容易理解。 - undefined
@fuz 如果对应的操作码中不存在 modrm 或 sib,那么它们就没有任何作用。对于我的问题来说,它们从来没有起到过任何作用,因为我的问题与“m”操作数有关。 如果你能告诉我更多关于这些手册的信息或者给我提供链接,那就太好了。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接