首页 文章

为什么x86 nopl指令采用操作数?

提问于
浏览
13

为什么x86可执行文件中的 nopl 指令采用操作数?不要只是做,好吧,什么都没有?

nopl 0x0(%rax)

2 回答

  • 7

    许多处理器的二进制指令集具有多种表示功能相同的指令的方式 . 例如,原始ARM指令集包含使用 b << n 形式的任何值加载R0的指令,其中 b 是0到255之间的值, n 是0到24之间的偶数 . 如果想要加载值为256的R0 ,可以加载用 1<<8 加载它的指令,或者可以使用 4<<616<<464<<2 的指令 . 加载这些不同值的指令都具有不同的二进制编码,即使所有四个指令具有相同的效果 .

    某些编译器的汇编程序不遗余力地提供一种代码应该使用哪些看似相同的指令的方法 . 虽然这通常并不重要,但有时可能需要避免在一段代码中使用某些字节值,或者有时候对一段代码中某些字节的修改应该具有特定的效果 . 例如,前述ARM指令中的8位用于指定 b 的值 . 如果代码用值12覆盖上述指令之一的 b 部分,则加载到R0中的值将取决于使用了哪四个原始指令;它可以是0x0C00,0x0300,0x00C0或0x0030 .

    尽管8x86的汇编器通常不能明确区分所有可能的指令编码,但是可能存在一些能够指定应该在指令中包括哪些字节值的上下文可能是有帮助的 . 例如,处理异常的一种方法是在发生异常时进行例行检查,返回地址处的指令是否是某种特定形式的NOP,如果是,则将其操作数解释为数据结构的地址持有与例外相关的信息 . 实际上,支持异常的大多数8x86语言都使用其他处理异常的方法,但前面提到的方法会使获取和执行长NOP所需的时间减慢正常函数返回,但能够相对有效地处理异常退出(大多数)语言使用较慢的方法来处理中断,以避免在无异常情况下执行NOP的成本,但其他语言可以选择以不同方式执行操作 .

  • 2

    有时我在调试时会使用nops . 如果我知道出了什么问题,但它需要成千上万的断点来发现我编写的代码来测试它 . 它可能看起来像这样(C风格的代码):

    if (condition_occurred)
    {
      asm("nop");
    }
    

    当我在“asm”行上设置断点时,调试器将使用nop的线性(物理)地址(对应于虚拟地址)设置DRx寄存器 . 到达此位置时,会发生断点中断并进入调试器 . 如果你在没有调试器的情况下执行,则会处理nop(没有任何反应) . 所以在这里我想要一个完全没有做任何事情的指令,它有意义(它没有) .


    下面是一个“无所事事”的nop指令实际上做某事的例子......虽然是间接的 .

    请参见this paper中的第8页,并注意示例3中循环的第一条(顶部)指令(这是示例2的开发) . 也是页面右下角的脚注 .

    作者暗示,额外的nops可能会进一步加快这一过程 .

    所以nops肯定有它们的用途 .

相关问题