为什么nopl
x86 中的指令采用操作数?不要nops只是做,好吧,什么都没有?
nopl 0x0(%rax)
许多处理器的二进制指令集有多种方式来表示功能相同的指令。例如,原始的 ARM 指令集包括用任何形式的值加载 R0 的指令,b << n
其中b
0 到 255 的值和n
0 到 24 的偶数。如果想用值 256 加载 R0,可以加载使用 加载它的指令1<<8
,或者可以将指令用于4<<6
、16<<4
或64<<2
。加载这些不同值的指令都具有不同的二进制编码,即使所有四个指令具有相同的效果。
一些编译器的汇编器不遗余力地提供方法来请求一段代码应该使用哪些看似相同的指令。虽然这通常并不重要,但有时可能希望避免在一段代码中使用某些字节值,或者有时对一段代码中的某些字节的修改应该具有特定效果。例如,上述 ARM 指令中的 8 位用于指定 的值b
。如果代码要用值 12 覆盖b
上述指令之一的部分,则加载到 R0 中的值将取决于使用了原始四个指令中的哪一个;它可以是 0x0C00、0x0300、0x00C0 或 0x0030。
尽管 8x86 的汇编器通常不能明确区分所有可能的指令编码,但在某些情况下,能够指定指令中应包含哪些字节值可能会有所帮助。例如,处理异常的一种方法是进行例行检查,当异常发生时,返回地址处的指令是否是某种特定形式的 NOP,如果是,则将其操作数解释为数据结构的地址持有异常相关的信息。在实践中,大多数支持异常的 8x86 语言都使用其他方法来处理它们,但是上述方法会减慢正常函数返回的速度,因为它需要获取和执行长 NOP 所需的时间,
有时我在调试时使用 nops。如果我知道出了什么问题,但它需要数千个断点断点才能发现我编写了测试它的代码。它可能看起来像这样(C 风格的代码):
if (condition_occurred)
{
asm("nop");
}
当我在“asm”行设置断点时,调试器将使用 nop 的线性(物理)地址(对应于虚拟地址)设置 DRx 寄存器。当到达此位置时,将发生断点中断,您将进入调试器。如果您在没有调试器的情况下执行 nop 将被处理(没有任何反应)。所以在这里我想要一个完全不做任何事情的指令,并且它做(不做)是有道理的。
这是一个“什么都不做” nop 指令实际上做某事的示例……尽管是间接的。
请参阅本文的第 8 页,并注意示例 3 中循环的第一条(顶部)指令(这是示例 2 的扩展)。还有页面右下角的脚注。
作者暗示额外的 nop 可能会进一步加快这个过程。
所以 nops 肯定有它们的用途。