Hi,

I was bored and I played around with SEH exceptions. It might be useful
in some cases when you can't catch an exception in a dll but there might
also be easier methods in some cases. I though before the code will be
forgotten and deleted again I post it here:

the unit seh.pas contains the handler. This handler is very simple, it
will handle all exceptions and always restore the stack to the state it
was when the handler was installed and use the address that was pushed
to the stack to continue execution.

the file sehtest.pas uses this unit and sets up a try/except construct
with two asm blocks and some labels and then triggers an exception
somewhere deeper in the callstack (to test the proper restoring of the
stack). If FPC had more flexible macros then it could be made a bit
nicer but it fulfills its purpose.

Bernd
unit SEH;

{$mode objfpc}

interface

function handler(
  ExceptionRecord: Pointer;
  EstablisherFrame: Pointer;
  ContextRecord: Pointer;
  DispatcherContext: Pointer): DWORD; stdcall;

implementation
uses
  sysutils;

const // these consts are copy-pasted from system.pp
  EXCEPTION_MAXIMUM_PARAMETERS    = 15;
  MAXIMUM_SUPPORTED_EXTENSION     = 512;

type // these types are copy-pasted from system.pp
  PFloatingSaveArea = ^TFloatingSaveArea;
  TFloatingSaveArea = packed record
          ControlWord : Cardinal;
          StatusWord : Cardinal;
          TagWord : Cardinal;
          ErrorOffset : Cardinal;
          ErrorSelector : Cardinal;
          DataOffset : Cardinal;
          DataSelector : Cardinal;
          RegisterArea : array[0..79] of Byte;
          Cr0NpxState : Cardinal;
  end;

  PContext = ^TContext;
  TContext = packed record
      //
      // The flags values within this flag control the contents of
      // a CONTEXT record.
      //
          ContextFlags : Cardinal;

      //
      // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
      // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT
      // included in CONTEXT_FULL.
      //
          Dr0, Dr1, Dr2,
          Dr3, Dr6, Dr7 : Cardinal;

      //
      // This section is specified/returned if the
      // ContextFlags word contains the flag CONTEXT_FLOATING_POINT.
      //
          FloatSave : TFloatingSaveArea;

      //
      // This section is specified/returned if the
      // ContextFlags word contains the flag CONTEXT_SEGMENTS.
      //
          SegGs, SegFs,
          SegEs, SegDs : Cardinal;

      //
      // This section is specified/returned if the
      // ContextFlags word contains the flag CONTEXT_INTEGER.
      //
          Edi, Esi, Ebx,
          Edx, Ecx, Eax : Cardinal;

      //
      // This section is specified/returned if the
      // ContextFlags word contains the flag CONTEXT_CONTROL.
      //
          Ebp : Cardinal;
          Eip : Cardinal;
          SegCs : Cardinal;
          EFlags, Esp, SegSs : Cardinal;

      //
      // This section is specified/returned if the ContextFlags word
      // contains the flag CONTEXT_EXTENDED_REGISTERS.
      // The format and contexts are processor specific
      //
          ExtendedRegisters : array[0..MAXIMUM_SUPPORTED_EXTENSION-1] of Byte;
  end;

  PExceptionRecord = ^TExceptionRecord;
  TExceptionRecord = packed record
          ExceptionCode   : cardinal;
          ExceptionFlags  : Longint;
          ExceptionRecord : PExceptionRecord;
          ExceptionAddress : Pointer;
          NumberParameters : Longint;
          ExceptionInformation : array[0..EXCEPTION_MAXIMUM_PARAMETERS-1] of Pointer;
  end;

function handler(
  ExceptionRecord: Pointer;
  EstablisherFrame: Pointer;
  ContextRecord: Pointer;
  DispatcherContext: Pointer): DWORD; stdcall;

var
  exceptaddr : PDWORD;

begin
  writeln(format('exception %x', [PExceptionRecord(ExceptionRecord)^.ExceptionCode]));
  exceptaddr := EstablisherFrame + 12;  // 3 pushs earlier (the except label)
  PContext(ContextRecord)^.Eip := exceptaddr^;
  PContext(ContextRecord)^.Esp := DWORD(EstablisherFrame);
  Result := 0;
end;


end.

program sehtest;

{$mode objfpc}{$H+}
{$asmmode intel}

uses
  SEH, sysutils;


procedure foo2(foobaz: integer);
var
  x : integer;
begin
  x := 1 div foobaz;
  //PInteger(nil)^ := foobaz;
end;

procedure foo1(foobaz: integer);
var
  x : integer;
begin
  foo2(foobaz);
end;

procedure foo(foobaz: integer);
var
  x : integer;
begin
  foo1(foobaz);
end;



procedure test;
label
  __except, __finally;
begin
  writeln(IntToHex(PtrUInt(@__except), 8));

  // ****** TRY ****** (push the except address and ebp and then install SEH)
  asm
    lea eax, __except
    push eax
    push ebp
    push handler
    push fs:[0]
    mov fs:[0], esp
  end;
  // ****** TRY ******

  writeln('trying...');
  foo(0); // provoke exception somewhere deeper in the callstack
  goto __finally;

  __except:
  writeln('except');

  __finally:
  // ****** FINALLY ****** (undo the things done in try)
  asm
    pop eax
    mov fs:[0], eax
    pop eax // handler
    pop ebp
    pop eax // except label
  end;
  // ****** FINALLY ******

  writeln('finally');
end;

begin
  test;
end.
_______________________________________________
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply via email to