43
LEA EAX, [EAX]

I encountered this instruction in a binary compiled with the Microsoft C compiler. It clearly can't change the value of EAX. Then why is it there?

G S
  • 35,511
  • 22
  • 84
  • 118
  • @Potatoswatter: Yes, this is a release version of binary, so optimization should be on. Also, I'm using ollydbg for disassembling. – G S Apr 24 '10 at 06:04
  • Do you have the corresponding C statement for this gem? – Wikser Apr 24 '10 at 06:09
  • @Wikser: No. And this is not a one off case. I've seen one or two others like LEA EBX, [EBX] in the same binary. In fact, I'm look at the last one right now on my screen. Ollydbg shows the op-code for this one (LEA EBX, [EBX] that is) as "8D9B 00000000" – G S Apr 24 '10 at 06:13
  • 1
    If that is a 64-bit binary then it's just a zero out the top 32 bits – phuclv Oct 31 '13 at 01:57

3 Answers3

82

It is a NOP.

The following are typcially used as NOP. They all do the same thing but they result in machine code of different length. Depending on the alignment requirement one of them is chosen:

xchg eax, eax         = 90
mov eax, eax          = 89 C0 
lea eax, [eax + 0x00] = 8D 40 00 
Nathan Fellman
  • 122,701
  • 101
  • 260
  • 319
codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 6
    Actually, it's not strictly a `NOP`, because it introduces a data dependency on `EAX`. Modern CPUs detect this specific pattern as a `NOP` and ignore the data dependency, but some older CPUs might not. – Jörg W Mittag Apr 24 '10 at 14:44
  • 13
    Actually, the opcode for `nop` is `0x90`, which is the same as `xchg eax, eax` – Nathan Fellman Apr 24 '10 at 18:14
  • 1
    well, it is wrong that "modern cpu's detect this specific pattern as a NOP", they still introduce _false_ data dependency chains. There are only some true NOP's out there where the decoders just don't introduce any data-dependencys. – Quonux Jul 14 '10 at 22:48
  • 1
    @Quonux the [multi-byte NOPs](https://software.intel.com/en-us/forums/topic/307174) have been made official long time ago. [This answer](http://stackoverflow.com/a/12564044/995714) has also listed Intel's official NOPs from 2 to 9 bytes – phuclv Jun 24 '14 at 02:25
35

From this article:

This trick is used by MSVC++ compiler to emit the NOP instructions of different length (for padding before jump targets). For example, MSVC++ generates the following code if it needs 4-byte and 6-byte padding:

8d6424 00 lea [ebx+00],ebx ; 4-byte padding 8d9b 00000000
lea [esp+00000000],esp ; 6-byte padding

The first line is marked as "npad 4" in assembly listings generated by the compiler, and the second is "npad 6". The registers (ebx, esp) can be chosen from the rarely used ones to avoid false dependencies in the code.

So this is just a kind of NOP, appearing right before targets of jmp instructions in order to align them.

Interestingly, you can identify the compiler from the characteristic nature of such instructions.

G S
  • 35,511
  • 22
  • 84
  • 118
3
LEA EAX, [EAX]

Indeed doesn't change the value of EAX. As far as I understand, it's identical in function to:

MOV EAX, EAX

Did you see it in optimized code, or unoptimized code?

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
  • It's optimized code alright. But how does that justify/explain this LEA? – G S Apr 24 '10 at 06:05
  • @Frederick: had it not been optimized, I guess it would make sense if the compiler uses LEA for some sort of computation and a special case generated this redundant statement (this happens in unoptimized code) – Eli Bendersky Apr 24 '10 at 06:09
  • This is a binary released to customers by my company. So it has to be the optimized version. – G S Apr 24 '10 at 06:15
  • "So it has to be the optimized version". Not necessarily. What if they forgot to turn on `/O2`? – Alex Budovski Apr 24 '10 at 06:26
  • the LEA instruction could be execute faster on a Pentium 4 than a MOV instruction(and calculations are still today fast using LEA) so i would point this as a reason for LEA. – Quonux Jul 14 '10 at 22:51