gpt4 book ai didi

assembly - x86 多字节 NOP 和指令前缀

转载 作者:行者123 更新时间:2023-12-02 03:28:13 39 4
gpt4 key购买 nike

作为一个小记忆,x86 架构将 0x0F 0x1F [mod R/M] 定义为多字节 NOP。

现在我正在研究 8 字节 NOP 的具体情况:我已经得到了

0x0F 0x1F 0x84 0x__ 0x__ 0x__ 0x__ 0x__

其中最后 5 个字节具有任意值。

第三个字节,[mod R/M],分割后给出:

modrm

  • mod = 10b:参数为 reg1 + DWORD 大小的位移
  • reg2 = 000b:(我们不在乎)
  • reg1 = 100b:表示参数是 SIB 字节 + DWORD 大小的位移。

现在,作为一个具体的例子,如果我采取

0x0F 0x1F 0x84 0x12 0x34 0x56 0x78 0x9A

我有

  • SIB = 0x12
  • 位移 = 0x9A785634:一个 DWORD

现在我添加 0x66 指令前缀来指示位移应该是 WORD 而不是 DWORD:

0x66 0x0F 0x1F 0x84 0x12 0x34 0x56 0x78 0x9A

我希望0x78 0x9A被“切断”并被视为一条新指令。然而,当编译它并在生成的可执行文件上运行 objdump 时,它仍然使用所有 4 个字节(一个 DWORD)作为位移。

我是否误解了“位移”在这种情况下的含义?或者 0x66 前缀对多字节 NOP 指令没有任何影响?

最佳答案

66H 前缀将操作数的大小覆盖为 16 位。
如果您希望使用67H,它不会覆盖地址的大小。 (但不要在 32 位代码中;这往往会导致 LCP stall ,特别是如果您使用 nopw [bx+0x1111] 与 disp16 而不是您从相同的 ModRM 编码,没有 67H 前缀。)

以下是所有传统 x86 前缀(不是 REX 或 VEX/EVEX)的列表。

        F0h = LOCK  -- locks memory reads/writes
String prefixes
F3h = REP, REPE
F2h = REPNE
Segment overrides
2Eh = CS
36h = SS
3Eh = DS
26h = ES
64h = FS
65h = GS
Operand override
66h. Changes size of data expected to 16-bit
Address override
67h. Changes size of address expected to 16-bit (in 32-bit mode)
<小时/>

但是,最好不要创建自己的 NOP 指令,而应遵循推荐的(多字节)NOP。

AMD 建议如下:
表 4-9。推荐的 NOP 指令多字节序列

bytes  sequence                encoding

1 90H NOP
2 66 90H 66 NOP
3 0F 1F 00H NOP DWORD ptr [EAX]
4 0F 1F 40 00H NOP DWORD ptr [EAX + 00H]
5 0F 1F 44 00 00H NOP DWORD ptr [EAX + EAX*1 + 00H]
6 66 0F 1F 44 00 00H NOP DWORD ptr [AX + AX*1 + 00H]
7 0F 1F 80 00 00 00 00H NOP DWORD ptr [EAX + 00000000H]
8 0F 1F 84 00 00 00 00 00H NOP DWORD ptr [AX + AX*1 + 00000000H]
9 66 0F 1F 84 00 00 00 00 00H NOP DWORD ptr [AX + AX*1 + 00000000H]

(此表中的反汇编是错误的:它在使用 66H 前缀的指令中显示 [AX]。该前缀设置操作数-size 为 16,但地址大小未修改。如果在 32 位模式下使用 67H 前缀,则 AX 在 16 位寻址模式下不可编码。并且 16位地址大小意味着 2 字节位移而不是 4 字节,并且消除了 SIB 字节的可能性。这就是 67H 在 Intel CPU 上的 32 位模式下解码缓慢的部分原因,给出 false LCP stalls 。)

Intel 不介意最多 3 个冗余前缀,因此 nop 最多 11 个字节可以像这样构造。

 10     66 66 0F 1F 84 00 00 00 00 00H     NOP DWORD ptr [AX + AX*1 + 00000000H] 
11 66 66 66 0F 1F 84 00 00 00 00 00H NOP DWORD ptr [AX + AX*1 + 00000000H]
<小时/>

您还可以通过在正常指令前添加冗余前缀来消除 NOP。例如

rep mov reg,reg //one extra byte

rep 通常被忽略,但新的 CPU 扩展经常使用 rep 使旧的操作码具有不同的含义。更安全的选择是前缀对于该操作码有效,但对寄存器操作数没有影响,就像段覆盖一样。或者像 DS 前缀(当它已经是默认值时),或者在 64 位模式下,其中 CS/DS/ES/SS 基数均为 0。

或者选择需要 REX 前缀的寄存器,因此汇编器必须使用同一指令的更长版本。

test r8d,r8d is one byte longer than: test edx,edx

带有立即操作数的指令有短版本和长版本(test 除外)。

and edx,7        //short imm8
and edx,0000007 //long imm32

大多数汇编器都会帮助您缩短所有指令,因此您必须使用 db 或 NASM/YASM 和 edx,严格 dword 7 自己编写较长的指令。请参阅 What methods can be used to efficiently extend instruction length on modern x86? 了解更多有关此内容的一般信息。

将它们散布在战略位置可以帮助您对齐跳跃目标,而不必因 NOP 的解码或执行而产生延迟。

请记住,在大多数 CPU 上执行 NOP 仍然会消耗资源。前端解码/uop-cache/问题槽,并在 ROB 中跟踪它直到退役。填充其他指令会占用相同的额外 I-cache 空间,但不会产生其他成本。

当您为 Skylake 中 JCC 勘误的 Intel 微代码解决方法引入的性能缺陷启用解决方法时,这些扩展指令的技术有时会自动使用:请参阅 How can I mitigate the impact of the Intel jcc erratum on gcc?这可能需要扩展内部循环内的指令,而您确实不想在内部循环中使用 NOP,因此英特尔建议扩展早期的指令。

关于assembly - x86 多字节 NOP 和指令前缀,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27714524/

39 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com