【问题标题】:Understanding the x86 m* operands (FPU and otherwise)了解 x86 m* 操作数(FPU 和其他)
【发布时间】:2018-09-20 19:31:05
【问题描述】:

我正在尝试制作一个简单的 x86 反汇编程序(目前为 32 位)用于学习目的。

所以英特尔文档是这样的:

但我觉得这很令人困惑。

首先,m8-32 操作数似乎表示ES:(E)DIDS:(E)SI
但尚不清楚在哪种情况下会出现这种情况。 在某些操作码中您有OPCODE m8, m8,在其他操作码中您只有一个操作数m8,在检查了多个操作码后,我得出的结论是没有一般规则。

还有其他这些,简称为memory operand in memory,这让我更加困惑。是否应该有位移,可能是绝对地址或相对偏移量?如果是这样,那还有什么意义,因为我们有moffsrel

后面的有点意思,但是冒号后面的数字是不是位移?
不过,& 符号让我完全一无所知。

除此之外,还有这些m[number][descriptor],据我所知是用于FPU的吗? (我还没有处理 0Fh 转义的操作码)。

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

提前致谢。

【问题讨论】:

  • FPU 向一个非常不同的鼓手进军。这与它开始的方式有很大关系,它曾经是一个不同的芯片(8087),与处理器分开销售。非常不同的数据类型,没有寄存器而是堆栈。很久以后它被集成到同一个芯片中,奔腾是第一个保证它可用的芯片。请记住,它在现代软件开发中变得非常无关紧要,它有 too many quirks 并且现代编译器生成 SSE 代码。
  • @HansPassant:x87 在内存中使用两种与 SSE/SSE2 相同的数据类型:IEEE754 单精度和双精度浮点数。 (m32fp 和 m64fp)。只有当您使用 fld / fstp 的 m80fp 形式时,您才能获得 10 字节的内部格式,即an IEEE754 extended precision format。它比单/双具有更多的位,但除了不使用显着的隐藏/隐式最高位之外,它的工作方式相同。但是 x87 整体来说很糟糕,而且它的寄存器堆栈绝对不是一个好的编译器目标!

标签: assembly x86 disassembly 32-bit machine-code


【解决方案1】:

add 这样可以使用内存操作数的普通指令也适用于寄存器,所以ADD has encodings for add r32, r/m32 and add r/m32, r32add eax, ecx 可以使用编码/操作码(没关系)。

这就是为什么m32(而不是r/m32)通常只是movsdstosd或其他字符串指令的隐式操作数,以及为什么英特尔说他们通常使用ES:(E)DIDS:(E)SI

首先,m8-32 操作数似乎表示 ES:(E)DI 或 DS:(E)SI。 但尚不清楚在哪种情况下会出现这种情况。

m32 表示 32 位内存操作数,不能是寄存器。 查看具体说明的条目以了解如何指定操作数,(例如DS:(E/R)SI is implicit for lodsb/w/d/q),而其他人可能使用 ModR/M 操作数但要求它是内存。 p>

对于 x87,额外的注释告诉您指令如何解释它。例如m32fp 是 32 位 IEEE 单精度 float(例如,fmulfld),而 m32int 是 32 位整数(例如,fimulfild)。


除了 x87,数字只是告诉您操作数大小。就是这样。

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

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

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

汇编器会为您处理这个问题,并在将 mov eax, [symbol] 的代码大小保存为 32 位代码时使用 moffs 编码。通常,只需以正常方式 (Referencing the contents of a memory location. (x86 addressing modes)) 编写寻址模式并让汇编器生成 ModR/M 字节,或者如果您做了一些非法(不可编码)的事情,例如尝试将 movsb 与不同的寄存器一起使用,则会警告您。


有关 x86 asm 的更多信息,请参阅x86 tag wiki。另外,Agner Fog's guides 非常好,尽管他并没有试图涵盖这样的基本内容。但是,阅读 Agner 的指南并查看他对他的简短示例(几条指令长)的看法将帮助您了解 asm 的工作原理。

【讨论】:

  • 我认为问题是(太):使用的是(E)SI 还是(E)DI 中的哪一个?然后我想这有助于提问者知道 SI 代表源索引 (IIRC) 和 DI 代表目标索引,并且这些寄存器中的一个或两个都以对指令有意义的唯一顺序隐式使用。
  • @PeterCordes 也许我不够清楚,但我的问题从来不在于 modrm/sib 字节,或 moffs' 或 rel 或其他什么,这很容易理解,特别是 @ 987654358@whatever 操作数。与此同时,我发现了这个ref.x86asm.net/index.html#Instruction-Operand-Codes,如果你可以通过 BA、BB 和 BD 条目看到,即使英特尔文档将它们全部命名为相同的名称,它们只是真的不一样,它们'是操作码隐式的。而且真的很难弄清楚,因为很少有文档说操作码实际上指定了它,只是 m。我的 q 怎么不清楚?
  • @TrisT:您的 q 大部分时间都很长,其中的一部分我只是略过,因为我很懒。这个答案开始是一个评论,我决定我应该把它作为一个答案发布,然后它变得更长了。我根本没有发现stos 条目不清楚:felixcloutier.com/x86/STOS:STOSB:STOSW:STOSD:STOSQ.html 查看操作码列:只是AA,而不是 + 任何操作数。并且操作数编码都是该表中的NA。并且描述是 100% 明确的:For legacy mode, store EAX at address ES:(E)DI; For 64-bit mode store EAX at address RDI or EDI. 没有指定不同 regs 的空间。
  • @TrisT:或者换一种说法:英特尔的m32 术语甚至没有试图告诉你操作数是如何编码的。这可能是特定于指令的。但是作为 asm 程序员(而不是编写反汇编程序的人)查看文档并查看 m32 很有用。我确切地知道这意味着什么:它是一个 32 位内存操作数。这就是我在文档中寻找的指令形式。
  • @PeterCordes “作为 asm 程序员很有用(而不是编写反汇编程序的人)”我的问题是关于反汇编的。我现在完全理解它是依赖于操作码的,并且我已经将它编码为一个特定的操作数,而不仅仅是“mXYZ”(借助我上一篇评论中提供的链接),但你仍然可以看到它是如何实现的令人困惑且解释性不够,因为它可以是ES:(E)DIDS:(E)SI,除非您对每个操作码进行硬编码,否则真的无法仅从操作数知道。因此,您可以看到文档(或基于它们的 cmets/answers)没有多大帮助。
【解决方案2】:

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

操作码描述为here

geek 版本并不像 coder 那样模棱两可。

不过,如果有人能指导我到哪里可以自己学习,我将不胜感激。我似乎无法在英特尔文档或 x86asm 以外的任何其他地方找到它。

再说一次,我经常错过一些东西,所以如果我找到了我会编辑的东西。

希望我能帮上忙,祝你好运。

【讨论】:

  • 相关位是描述指令操作数的 modr/m 和 sib 字节。我在this answer 中写了一些关于它们的详细信息。我写了另一个可能很有趣的答案here
  • 我也强烈建议您阅读旧版本的 CPU 手册,因为它们通常更容易阅读。
  • @fuz 如果相关操作码不存在 modrm 或 sib,则它们将不起作用。他们从来没有为我的问题服务,这与m 操作数有关。如果您能告诉我更多关于/链接这些手册的信息,那就太好了。
猜你喜欢
  • 2017-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
  • 1970-01-01
  • 2014-04-04
相关资源
最近更新 更多