Question

Why does a nopl instruction in an x86 take an operand? Don't nops just do, well, nothing?

nopl   0x0(%rax)
Was it helpful?

Solution

Many processors' binary instruction sets have multiple ways of representing functionally-identical instructions. For example, the original ARM instruction set includes instructions to load R0 with any value of the form b << n where b is a value from 0 to 255 and n is an even number from 0 to 24. If one wanted to load R0 with the value 256, one could load the instruction which loads it with 1<<8, or one could use the instruction for 4<<6, 16<<4, or 64<<2. The instructions to load those different values all have different binary encodings, even though all four instructions have the same effect.

The assemblers for some compilers go out of their way to provide means of requesting which of the seemingly-identical instructions a piece of code should use. While this is normally not important, there are times when it may be desirable to avoid using certain byte values within a piece of code, or there may be times when modifications to certain bytes within a piece of code should have a particular effect. For example, eight bits in the aformentioned ARM instructions are used to specify the value of b. If code were to overwrite the b part of one of the above instructions with the value 12, the value loaded into R0 would depend upon which of the original four instructions had been used; it could be 0x0C00, 0x0300, 0x00C0, or 0x0030.

Although assemblers for the 8x86 do not generally make it possible to explicitly distinguish between all possible instruction codings, there may be some contexts where being able to specify what byte values should be included within an instruction may be helpful. For example, one approach to handling exceptions would be to have a routine check, when an exception occurs, whether the instruction at the return address is some particular form of NOP and, if it is, interpret its operand as the address of a data structure holding exception-related information. In practice, most 8x86 languages that support exceptions use other means of handling them, but the aforementioned method would slow down normal function returns by the time required to fetch and execute a long NOP, but would be able to handle exceptional exits relatively efficiently (most languages use a slower approach for handling interrupts in the interest of avoiding the cost of executing the NOP in the no-exception case, but other languages could opt to do things differently).

OTHER TIPS

Sometimes I use nops when debugging. If I know how something goes wrong but it requires thousands of breakpoint breaks to discover I write code that tests for it. It may look something like this (C-style code):

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

When I set a breakpoint on the "asm" line the debugger will set up a DRx register with the linear (physical) address (corresponding to the virtual address) of the nop. When this location is reached a breakpoint interrupt occurs and you enter the debugger. If you're executing without the debugger the nop will be processed (nothing happens). So here I want an instruction that does exactly nothing and it makes sense that it does (doesn't).


Here is an example of where a "do nothing" nop instruction actually does something ... though indirectly.

See page 8 in this paper and notice the first (top) instruction of the loop in example 3 (which is a development of example 2). Also the footnote at the bottom right of the page.

The author hints that additional nops might speed up the process further.

So nops definitely have their uses.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top