nasm有内置的方法来发出给定长度的long-nop(又称多字节nop)指令吗?
结论似乎是:没有官方的方法在nasm1中直接发出这些长nop指令。
因此,我根据Intel手册2中推荐的序列编写了自己的宏来生成1到9个字节的nop指令:
;; long-nop instructions: nopX inserts a nop of X bytes
;; see "Table 4-12. Recommended Multi-Byte Sequence of NOP Instruction" in
;; "Intel® 64 and IA-32 Architectures Software Developer’s Manual" (325383-061US)
%define nop1 nop ; just a nop, included for completeness
%define nop2 db 0x66, 0x90 ; 66 NOP
%define nop3 db 0x0F, 0x1F, 0x00 ; NOP DWORD ptr [EAX]
%define nop4 db 0x0F, 0x1F, 0x40, 0x00 ; NOP DWORD ptr [EAX + 00H]
%define nop5 db 0x0F, 0x1F, 0x44, 0x00, 0x00 ; NOP DWORD ptr [EAX + EAX*1 + 00H]
%define nop6 db 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 ; 66 NOP DWORD ptr [EAX + EAX*1 + 00H]
%define nop7 db 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 ; NOP DWORD ptr [EAX + 00000000H]
%define nop8 db 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 ; NOP DWORD ptr [EAX + EAX*1 + 00000000H]
%define nop9 db 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 ; 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H]
我还将它们添加到nasm-utils项目中,因此,如果您有相同的需求,这是一种获取它们的方法。
1尽管正如Jester 所指出的,您可以深入了解内部实现,找到用于实现“智能对齐”功能的一些宏。
2值得一提的是,我认为这些首先出现在AMD手册中,并且最终英特尔采用了相同的推荐序列。
WORD PTR
而不是 DWORD PTR
。 - Sep RolandWORD
隐含了。基本上,66H, NOP DWORD ptr [EAX + EAX*1 + 00H]
和NOP WORD ptr [EAX + EAX*1 + 00H]
是写同一件事的两种方式,如果你明确地编码一个db 0x66
,然后跟着DWORD
版本,你会得到相同的字节。我上面展示的形式是从英特尔手册中逐字复制的。请注意,注释甚至对于64字节模式也不正确:如果您实际上组装了带有地址的nop,则所有地址都比注释版本长1个字节,因为它们将具有... - BeeOnRope0F
转义字节被计算为前缀的CPU(Silvermont)上,它仍然只有3个总前缀。 - Peter Cordes1-byte: XCHG EAX, EAX
2-byte: 66 NOP
3-byte: LEA REG, 0 (REG) (8-bit displacement)
4-byte: NOP DWORD PTR [EAX + 0] (8-bit displacement)
5-byte: NOP DWORD PTR [EAX + EAX*1 + 0] (8-bit displacement)
6-byte: LEA REG, 0 (REG) (32-bit displacement)
7-byte: NOP DWORD PTR [EAX + 0] (32-bit displacement)
8-byte: NOP DWORD PTR [EAX + EAX*1 + 0] (32-bit displacement)
9-byte: NOP WORD PTR [EAX + EAX*1 + 0] (32-bit displacement)
• Select the smallest number of NOPs and pseudo-NOPs to provide the desired padding.
• Select NOPs that are least likely to execute on slower execution unit clusters.
• Select the register arguments of NOPs to reduce dependencies.
0F 1F modrm ...
,而是使用lea
。以这种方式使用LEA在体系结构上是“真正的nop”,但在微体系结构上不是,而0F 1F
与90
短NOP相同,不使用任何执行端口并且不会延长涉及任何寄存器的dep链。在x86-64代码中,您应该始终使用0F 1F
NOPs而不是LEA,或者在还使用CMOV或其他P6功能的32位代码中也可以使用。 - Peter CordesNOP
指令。它的代码是0x90,只有一个字节。XCHG
。例如,对于"2字节NOP
",您可以编写:XCHG AL, AL
这被编码为:
86 C0
因此,您可以编写宏以获取任何所需的大小。找到所有这些“什么也不做”的指令需要一些工作。此外,在大多数情况下,编译器会尝试对表达式进行优化。这就是输入代码可能是必需的地方。
我知道的最长编码将使用LEA
指令。这是可以优化地址偏移量大小的地方,因为它们将是零,许多零,并且它们应该被优化。
正如Jester提到的那样,您可以使用现有的宏。互联网上有该文件的副本。
https://github.com/letolabs/nasm/blob/master/macros/smartalign.mac
解码所有这些指令并查看它们是令人兴奋的。
例如,他们使用MOV %si, %si
来创建一个2字节的NOP
。
nop
。 - Jongwarenop +size
或类似的东西。 - Alexis Wilkemov al,[eax+1]
与人为延长的 mov al,[eax+0x0000001]
是一样的。反汇编器过去会显示底层字节码的逐字节文字表示,但现在这种方法已经不再有效了。 - Jongwarexchg ax,ax
。它不是“官方”的NOP,只是Intel将其标记为无操作指令。 - David Hoelzer0x90
是真正的NOP指令。如果它被解释为xchg eax, eax
,那么它将把EAX零扩展为RAX。如果您写入xchg eax, eax
,编码就不能使用90
,因为NOP指令已经占用了0x90
xchg-with-eax操作码。但其他寄存器仍然可以使用短编码,例如0x91
表示xchg eax和ecx。我不确定汇编器在32位或64位模式下如何选择编码xchg ax, ax
。66 90
也是合法的,尽管它是真正的NOP,而不是3-uop xchg
。(虽然LEA编码用作NOP不是真正的NOP) - Peter Cordes
db __ALIGN_32BIT_2B__
可以在 32 位代码中插入一个 2 字节的 NOP。 - Jesternop [rm16/rm32]
。如果nasm接受这种表示法,你可以自己创建一组宏,这样就不需要依赖于“未经记录”的外部宏。(或者-哎呀,迟来的想法-复制它们的定义...) - Jongware