31 Mar 2011

Weird Delphi 7 compiler bugs (Part 1)

This one I came across only recently.  It is only affected in ASM blocks.


Function  BugTest : Cardinal;
Asm
    Jmp  @Start
  @L1:
    Nop
    Nop
    Nop
    Nop
  @L2:
    Jmp  @L1
    Nop
    Nop
  @L3:  
    DD  Offset L2    // Start address
    DD  Offset L3    // End address
  // Return the distance between labels @L2 and @L3
  @Start:
    Lea ECX, Offset @L3
    Mov EAX, [ECX]   // Get start address
    Mov ECX, [ECX+4] // Get end address
    Sub ECX, EAX     // Sub start from end to get len
    Mov @Result, ECX
End;
Running this code should give a result of 4 (2-byte Jmp and 2 nops) but it doesn't, the function returns 8!

What actually happens is that the start address is taken from the JMP instruction at the @L2 label, meaning that the start address value compiled in the table at @L3 points to @L1 instead of @L2 ..
This doesn't happen with any conditional JCC either, just JMP, and only when pointed to by a DWord table offset..  Very rare condition, but annoying if you don't know what is going on.

So how to stop this? Well, as I said you can alter the JMP to be a JCC or rearrange the instructions so that the first instruction is not a JMP (I added a NOP)