【问题标题】:Is LEA the only instruction in x86 with a memory operand that doesn't access memory?LEA 是 x86 中唯一具有不访问内存的内存操作数的指令吗?
【发布时间】:2014-01-04 13:25:32
【问题描述】:

我正在使用libdis,来自the bastard 的x86 反汇编程序库,我正在尝试找出哪些指令访问内存。

参考这两条指令:

mov eax, [ebx + 10]
lea eax, [ebx + 10]

在libdis 中,两者都以指令类型insn_mov 列出,并且地址操作数在两种情况下具有相同的标志。所以我能判断内存是否被访问的唯一方法是查看指令助记符。

因此我的问题是:LEA 是唯一使用实际上不访问内存的内存操作数的指令吗?任何指向参考的链接都会很好。

【问题讨论】:

  • 我想是的……不过不确定。

标签: assembly x86 instructions memory-access


【解决方案1】:

有效地址不需要

prefetch / prefetchw 和 nop 如其他答案所述。

任何带有全零掩码的 AVX512 掩码加载或存储,例如 vmovaps [rdi]{k1}, zmm1。或 AVX vmaskmovps / vpmaskmovd。 AVX2 gather / AVX512 gather 或使用全零掩码散布。这些都对无效地址进行故障抑制。 (慢,但没有实际的内存访问。)

invlpg m8 采用 ModRM 指定虚拟地址。 (特权指令)。它不是从该地址加载,而是使该地址的 TLB 条目以及缓存在 page walker 中的更高级别的页面目录条目无效。

verr / verw — 验证段以进行读取或写入:他们采用 ModRM 寻址模式并根据段限制检查地址,设置 FLAGS。 (以及最近的微码更新。verw also clears internal CPU buffers so OSes can use it to mitigate L1TF / MDS vulnerabilities)。

rep cmpsb 或其他 RCX = 0 的字符串指令执行零迭代,不访问 [RDI] 或 [RSI] 隐式内存操作数。我认为这意味着即使地址错误也不会出错。微码肯定够慢

cldemote(Intel 中的新功能Tremont)——与预取相反;将数据推送到共享 L3 以加快从另一个内核的首次访问的性能提示。它在没有该功能的情况下解码为硬件上的 NOP。预取不会在无效地址上出错(尽管它们在使用微码辅助来抑制故障时可能会很慢);对于cldemote,该手册不是 100% 清楚的,但确实称其为推测性提示。

在某些处理器实现中,CLDEMOTE 指令可以设置页表中的A 位,但不能设置D 位。

如果在缓存中没有找到该行,则该指令将被视为 NOP。

MPX bndcl bnd, r/m64 / bndcu / bndcn / bndmk - 内存源表单有一个内置的 LEA:操作部分的伪代码甚至是 TEMP ← LEA(mem);。寄存器源形式只是直接使用寄存器值作为地址。正如手册所说,此指令不会导致任何内存访问,也不会读取或写入任何标志。(它会在越界时引发#BR 异常)。请注意,不推荐使用 MPX。


需要有效地址,但本身不是加载或存储。

clflush / clflushopt / clwb 都采用内存操作数来指定要刷新或写回 DRAM 的高速缓存行,可用于非易失性 DIMM 以确保提交到 NV 存储(与cldemote 不同,这些不是只是暗示如果忙或找不到地址,CPU 可能会下降)。它们确实需要一个有效的虚拟地址,并且确实会影响相应高速缓存行的 MESI 状态。但是如果缓存行不存在于 L1d 缓存中,它不会被引入然后再次刷新。我认为它会从所有内核的缓存中逐出,因此一个内核在一行上发送垃圾邮件clflush 会影响另一个内核读/写它。

The CLFLUSHOPT instruction 可以在所有特权级别使用,并且受到所有权限检查和与字节加载相关的错误(此外,允许 CLFLUSHOPT 指令刷新线性地址只执行段)。与加载一样,CLFLUSHOPT 指令设置页表中的 A 位但不设置 D 位。

MONITOR 将内存地址视为隐式DS:RAX/EAX/AX,而不是在 ModRM 中编码。它实际上并没有从中加载,而只是设置核心以通知另一个核心何时更改该内存。但是,它确实像负载一样工作。 (大概是让线路进入 MESI 共享状态,以便它可以在另一个内核在写入之前使其无效时注意到。)

MONITOR 指令被排序为相对于其他内存事务的加载操作。该指令受到与字节加载相关的权限检查和故障的影响。像负载一样,MONITOR 设置页表中的 A 位而不是 D 位。

umonitor(用户空间版本)是一样的。

【讨论】:

    【解决方案2】:

    英特尔有一条multi-byte "NOP" 指令,其操作码为0F 1F /0,它采用内存寻址操作数。来自英特尔的手册:

    多字节 NOP 指令不会改变寄存器的内容,也不会发出内存操作

    cmets 中的讨论是关于将nop 的操作码字节放在未映射页面的末尾,如果无法读取包括 ModR/M 和位移字节在内的完整指令,则代码提取错误。这与这个问题是正交的。


    您可以认为 long-NOP 的工作方式如下:

    • 指令解码硬件知道如何找到采用 ModRM 的指令的结尾(这意味着可选的 SIB 和/或位移)。
    • 基于该操作码特别是 NOP,CPU 的其余部分不会对 ModRM 编码的寻址模式执行任何操作。

    这使得软件可以通过使用各种寻址模式和前缀来编码多字节 NOP。 CPU 可以设计为无需任何特殊硬件即可处理它,而无需将另外一个操作码识别为nop。整体指令格式与大多数相同。

    【讨论】:

    • 似乎他们确实访问了内存 according to Peter Ferrie:“有趣的是,尽管它的名字,如果 Mod/RM 字节告诉它,它确实访问内存,所以这个“无操作”可能导致页面错误。毕竟不是一个 NOP。”
    • 真的吗?唔。我将不得不返回并重新访问该代码;如果不在缓存中,内存接触可能会很慢。
    • 我似乎得到了一个不同的故事:mail.openjdk.java.net/pipermail/hotspot-compiler-dev/… 我的代码生成器在 EAX 可以是任意值的上下文中生成涉及 EAX 的建议代码。如果这些真的读取内存,我会遇到陷阱,但我对此的麻烦为零。 Ferrie 或 Dabbs 之一是错误的。
    • @peter ferrie:这只是意味着 CPU 有关于在执行指令之前获取整个指令的规则,这是一个正交主题。问题是,如果 NOP 试图获取由 mod/rm 字节形成的地址。除了你刚才举的例子,你还有什么具体的证据吗?
    • @IgorSkochinsky:幸运的是,英特尔的手册清除了该误导性引用所带来的任何疑问。我编辑了 Ira 的答案以澄清。
    【解决方案3】:

    prefetch 系列指令(prefetcht1、prefetcht2、prefetcht3、prefetchnta)要求处理器将这些内存线拉入缓存,因为它们很快就会被需要。但是,英特尔的文档清楚地表明,传递给预取的错误地址不会导致任何故障。这样一来,软件就可以将潜在的越界地址传递给预取,而无需先检查它们,从而在执行这些检查时数据可以在传输中。

    与LEA 不同,预取也没有“输出”。

    【讨论】:

      猜你喜欢
      • 2012-02-27
      • 2019-02-19
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      • 2017-12-11
      • 2017-04-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多