exceptiondelphiassemblyx86-64

Exception Handling in 64bit(Windows) Delphi assembly


I have the following code that is written in pure Delphi.

procedure Test;
var
  i2: Integer;
begin
  i2 := 0;
  i2 := 0 div i2;
end;
try
  Test1;
except
  WriteLn('runtime error');
end;

Then I copied all the assembly code that generated by Delphi compiler in the CPU view under debugger.

procedure Test;
asm
  push rbp
  sub rsp,$10
  mov rbp,rsp
  mov [rbp+$0c],$0000007b
  mov [rbp+$08],$00000000
  mov eax,[rbp+$0c]
  cdq
  idiv dword ptr [rbp+$08]
  mov [rbp+$08],eax
  lea rsp,[rbp+$10]
  pop rbp
end;

try
  Test1;
except
  WriteLn('runtime error');
end;

But the program crashed due to unable unwind exception. But the pure Pascal version works correctly to catch the exception. Why it happens since they are the same assembly code?

Is there any documentation to explain this different behavior? I have found a documentation explaining MacOS inline assembly (PC-Mapped Exceptions - RAD Studio). But I suppose that does not fit Win64.

Is there any special rule to write assembly code in Win64 and properly let Delphi handing runtime error?


Solution

  • Use MASM to do this. Although BASM do provide some Pseudo-Ops similar to MASM, it lacks some important Pseudo-Ops to manage the stack and generate extra info in the prologue (.allocstack, .setframe, etc). https://docwiki.embarcadero.com/RADStudio/Athens/en/Assembly_Procedures_and_Functions https://learn.microsoft.com/en-us/cpp/assembler/masm/dot-setframe?view=msvc-170

    1. Compile with MASM to get obj file. ml64 /c test.asm

    test.asm

    _text SEGMENT
    Test PROC FRAME
       push rbp
    .pushreg rbp
       sub rsp, 010h
    .allocstack 010h
       mov rbp, rsp
    .setframe rbp, 0
    .endprolog
      mov dword ptr [rbp+0Ch], 7Bh
      mov dword ptr [rbp+08h], 0
      mov eax, dword ptr [rbp+0Ch]
      cdq
      idiv dword ptr [rbp+08h]
      mov dword ptr [rbp+08h], eax
      lea rsp, [rbp+10h]
      pop rbp
      ret
    Test ENDP
    _text ENDS
    END
    
    1. Link the obj file with Delphi.

    test.pas

    {$L test.obj}
    procedure Test; external;
    
    1. Then we could catch runtime errors in Delphi code!
    try
      Test;
    except
      WriteLn('Handled!');
    end;