-2

Consider the following function which is written in x86-assembly

foo:
        rep
        nop
        ret

Using NASM to assemble the code and disassemble it with gdb we have:

(gdb) disas foo
Dump of assembler code for function foo:
   0x0000000000000610 <+0>:     pause  ;pause ????
   0x0000000000000612 <+2>:     ret    
   0x0000000000000613 <+3>:     nop    WORD PTR cs:[rax+rax*1+0x0]
   0x000000000000061d <+13>:    nop    DWORD PTR [rax]
End of assembler dump.

It uses the pause instruction which was not presented in the original assembly. Why does it work this way? Is that an intentional documented behavior of NASM? So in case some earlier x86 cpus do not have pause we can just use rep nop?

St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • 2
    Yes, Intel chose `rep nop` as the encoding for `pause` so it could be used without checking for CPU support; older CPUs decode it as `nop`. Read the manual for pause. https://www.felixcloutier.com/x86/pause – Peter Cordes Oct 25 '19 at 05:23
  • @PeterCordes Manual entry for `rep` does not even specify it to be used with `nop`. Precisely _The REP prefix can be added to the INS, OUTS, MOVS, LODS, and STOS instructions, and the REPE, REPNE, REPZ, and REPNZ prefixes can be added to the CMPS and SCAS instructions._ So that was the reason I considered the `rep nop` as a feature of NASM. – St.Antario Oct 27 '19 at 04:58
  • @PeterCordes Moreover it was stated that _The F3H prefix is defined for the following instructions and undefined for the rest: F3H as REP/REPE/REPZ for string and input/output instruction. F3H is a mandatory prefix for POPCNT, LZCNT, and ADOX._ which did not mention `nop`. – St.Antario Oct 27 '19 at 05:04
  • 4
    Someone deleted my 2nd comment, IDK why. I said when I google for `rep nop pause` the linked duplicate is the first hit, it fully explains the situation. Anyway, `rep` isn't a valid prefix for `nop`, that's *why* its ignored by CPUs that don't recognize it as part of the encoding for another instruction. That list of instructions that have `F3` as a mandatory prefix is not exhaustive; it's missing `tzcnt` and `pause`, and is also used as the [`xrelease` prefix](https://www.felixcloutier.com/x86/xacquire:xrelease) for HLE (again because CPUs that don't recognize it ignore it). – Peter Cordes Oct 27 '19 at 05:11
  • 3
    You told NASM to output a REP prefix byte, that is a byte with the value 0xF3, followed by a NOP opcode byte, that is a byte with the value 0x90. You told NASM to output the byte sequence `F3 90` so that's what it did. NASM did not at any point consider whether or not this was a valid instruction sequence or not. It did not change `rep` `nop` from a invalid instruction sequence to an valid one. It just happened to be one, whether you intended it to be or not. – Ross Ridge Oct 27 '19 at 05:57

1 Answers1

5

It's not so much NASM assembling REP NOP into a PAUSE instruction as GDB decoding the F3 and 90 bytes that make up the REP prefix and the NOP instruction respectively as the PAUSE instruction.

That's because the PAUSE instruction is also encoded as F3 90. This is intentional documented behaviour of x86 CPUS, so that CPUs without the PAUSE instruction will treat it as a NOP. It has nothing to with NASM or GDB other than they assemble and disassemble instructions according to the CPU documentation.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • But the concern is that `rep` is not specified to be used with `nop`: [rep docs](https://www.felixcloutier.com/x86/rep:repe:repz:repne:repnz). Percisely _The F3H prefix is defined for the following instructions and **undefined for the rest**: F3H as REP/REPE/REPZ for string and input/output instruction. F3H is a mandatory prefix for POPCNT, LZCNT, and ADOX._ – St.Antario Oct 27 '19 at 05:10
  • 3
    @St.Antario: As part of the encoding for `pause`, it's *not* acting as a `rep` prefix. – Peter Cordes Oct 27 '19 at 05:13
  • @BeeOnRope The details of prefixes application is blurred across the manual. But searching through the manual VOLs I did not find that if a prefix is not used with an instruction then it is ignored. Moreover `lock` is also invalid prefix for `nop`, but it is explicitly stated that `#UD` is raised if `lock nop` is encountered. – St.Antario Oct 27 '19 at 05:47
  • 5
    You don't have to look all through the manual. You can look at just [one page for pause itself](https://www.felixcloutier.com/x86/pause) and see that it is encoded F3 90, i.e. the "rep" and "nop" bytes. Whether you consider this a prefix or just part of the encoding is just semantics. A good principle is that specific wins over broad: if you find a statement that says pause is encoded like this, that's likely to be right as opposed to a general description of rep prefixes, _espcially_ because the primary purpose (the repeating thing) of rep is different. – BeeOnRope Oct 27 '19 at 05:55
  • 1
    @St.Antario: For the record, inapplicable prefixes are not in general *guaranteed* to be ignored. For example, `rep bsr` is `lzcnt`, which works differently, so if old code had been using `rep` as padding on a `bsr`, it would break on new CPUs. But once a new instruction is introduced that uses `rep` as a mandatory prefix, Intel documents how *older* CPUs run that machine-code sequence (by ignoring the `rep` prefix), only then documenting what was already in practice true on real hardware. – Peter Cordes Sep 22 '22 at 02:34