qgi
qgi

Reputation: 95

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?

Upvotes: 1

Views: 168

Answers (2)

qgi
qgi

Reputation: 95

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;

Upvotes: 3

Marco van de Voort
Marco van de Voort

Reputation: 26361

The term is SEH, Structured Exception Handling. The compiler generates a table on a fixed offset of the procedure entry point, and the unwinder can find it using the exception address (and possibly also using the parent frame).

In short: the assembler you have for the procedure, is not all that is generated.

To get a feeling for what is involved, compile the short example with Free Pascal with the -al parameter. That shows all generated data as assembler source, and you can also see section information and other tables.

Upvotes: 3

Related Questions