Pārlūkot izejas kodu

* basic helpers for DWARF/PSABI EH-based exception handling (based on
GCC 4.2.1's libstdc++/libsupc++)
- compile RTL with -dFPC_USE_PSEABIEH to include
- the x86-64 compiler currently crashes if it has been compiled with
optimizations (the eh_return_data_regno function from cpubase
probably triggers mantis #34385)

git-svn-id: branches/debug_eh@40071 -

Jonas Maebe 6 gadi atpakaļ
vecāks
revīzija
b2d1ab7f05
6 mainītis faili ar 920 papildinājumiem un 1 dzēšanām
  1. 2 0
      .gitattributes
  2. 6 1
      rtl/inc/except.inc
  3. 4 0
      rtl/inc/excepth.inc
  4. 25 0
      rtl/inc/objpash.inc
  5. 822 0
      rtl/inc/psabieh.inc
  6. 61 0
      rtl/inc/psabiehh.inc

+ 2 - 0
.gitattributes

@@ -9503,6 +9503,8 @@ rtl/inc/objcnf.inc svneol=native#text/plain
 rtl/inc/objpas.inc svneol=native#text/plain
 rtl/inc/objpash.inc svneol=native#text/plain
 rtl/inc/pagemem.pp svneol=native#text/plain
+rtl/inc/psabieh.inc svneol=native#text/plain
+rtl/inc/psabiehh.inc svneol=native#text/plain
 rtl/inc/readme -text
 rtl/inc/real2str.inc svneol=native#text/plain
 rtl/inc/resh.inc svneol=native#text/plain

+ 6 - 1
rtl/inc/except.inc

@@ -26,6 +26,10 @@ Var
   ExceptObjectStack : PExceptObject;
   ExceptTryLevel    : ObjpasInt;
 
+{$ifdef FPC_USE_PSEABIEH}
+{$i psabieh.inc}
+{$endif}
+
 Function RaiseList : PExceptObject;
 begin
   RaiseList:=ExceptObjectStack;
@@ -82,7 +86,7 @@ end;
 { This routine is called only from fpc_raiseexception, which uses ExceptTryLevel
   flag to guard against repeated exceptions which can occur due to corrupted stack
   or heap. }
-Procedure PushExceptObject(Obj : TObject; AnAddr : CodePointer; AFrame : Pointer);
+function PushExceptObject(Obj : TObject; AnAddr : CodePointer; AFrame : Pointer): PExceptObject;
 var
   Newobj : PExceptObject;
   _ExceptObjectStack : ^PExceptObject;
@@ -131,6 +135,7 @@ begin
     End;
   NewObj^.framecount:=framecount;
   NewObj^.frames:=frames;
+  Result:=NewObj;
 end;
 
 Procedure DoUnHandledException;

+ 4 - 0
rtl/inc/excepth.inc

@@ -12,6 +12,10 @@
 
  **********************************************************************}
 
+{$if defined(FPC_USE_PSEABIEH)}
+{$i psabiehh.inc}
+{$endif}
+
 Const
   { Type of exception. Currently only one. }
   FPC_EXCEPTION   = 1;

+ 25 - 0
rtl/inc/objpash.inc

@@ -339,6 +339,23 @@
        PPDispatch = ^PDispatch;
        PInterface = PUnknown;
 
+{$ifdef FPC_USE_PSEABIEH}
+       { needed here for TExceptObject (rest is in psabiehh.inc) }
+       FPC_Unwind_Reason_Code = longint; {cint}
+       FPC_Unwind_Action = longint; {cint}
+
+       PFPC_Unwind_Exception = ^FPC_Unwind_Exception;
+
+       FPC_Unwind_Exception_Cleanup_Fn =
+         procedure(reason: FPC_Unwind_Reason_Code; exc: PFPC_Unwind_Exception); cdecl;
+
+       FPC_Unwind_Exception = record
+         exception_class: qword;
+         exception_cleanup: FPC_Unwind_Exception_Cleanup_Fn;
+         private_1: ptruint;
+         private_2: ptruint;
+       end;
+{$endif FPC_USE_PSEABIEH}
 
        TExceptProc = Procedure (Obj : TObject; Addr : CodePointer; FrameCount:Longint; Frame: PCodePointer);
 
@@ -356,6 +373,14 @@
          ExceptRec  : Pointer;
          ReraiseBuf : jmp_buf;
 {$endif FPC_USE_WIN32_SEH}
+{$ifdef FPC_USE_PSEABIEH}
+         { cached info from unwind phase for action phase }
+         handler_switch_value: longint;
+         language_specific_data: PByte;
+         landing_pad: PtrUInt;
+         { libunwind exception handling data (must be last!) }
+         unwind_exception: FPC_Unwind_Exception;
+{$endif FPC_USE_PSEABIEH}
        end;
 
     Const

+ 822 - 0
rtl/inc/psabieh.inc

@@ -0,0 +1,822 @@
+{
+    This file is part of the Free Pascal run time library.
+    Translated to Pascal by Jonas Maebe,
+    member of the Free Pascal development team
+
+    This file is based on the source code of libsupc++ from GCC 4.2.1.
+
+    See below for details about the copyright. While it is GPLv2 rather
+    than LGPLv2 like the rest of the FPC RTL, it has the same linking
+    exception as the rest of the FPC RTL and hence it can be used in the
+    same way.
+
+ **********************************************************************}
+
+// -*- C++ -*- The GNU C++ exception personality routine.
+// Copyright (C) 2001, 2002, 2003, 2006 Free Software Foundation, Inc.
+//
+// This file is part of GCC.
+//
+// GCC is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2, or (at your option)
+// any later version.
+//
+// GCC is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING.  If not, write to
+// the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction.  Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License.  This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+
+{$packrecords c}
+
+{$if (defined(CPUARMEL) or defined(CPUARMHF)) and not defined(darwin)}
+{$define __ARM_EABI_UNWINDER__}
+{$error add ARM EABI unwinder support}
+{$endif}
+
+function FPC_psabieh_GetExceptionWrapper(exceptionObject: PFPC_Unwind_Exception): PExceptObject; inline;
+  begin
+    { go to end of the wrapped exception (it's the last field in PFPC_Unwind_Exception), then to the start }
+    result:=PExceptObject(exceptionObject+1)-1;
+  end;
+
+procedure _Unwind_DeleteException(context:PFPC_Unwind_Context);cdecl;external;
+function _Unwind_GetGR(context:PFPC_Unwind_Context; index:cint):PtrUInt;cdecl;external;
+procedure _Unwind_SetGR(context:PFPC_Unwind_Context; index:cint; new_value:PtrUInt);cdecl;external;
+function _Unwind_GetIP(context:PFPC_Unwind_Context):PtrUInt;cdecl;external;
+procedure _Unwind_SetIP(_para1:PFPC_Unwind_Context; new_value:PtrUInt);cdecl;external;
+function _Unwind_GetRegionStart(context:PFPC_Unwind_Context):PtrUInt;cdecl;external;
+function _Unwind_GetLanguageSpecificData(context:PFPC_Unwind_Context):PtrUInt;cdecl;external;
+
+function _Unwind_GetDataRelBase(context:PFPC_Unwind_Context):PtrUInt;cdecl;external;
+function _Unwind_GetTextRelBase(context:PFPC_Unwind_Context):PtrUInt;cdecl;external;
+
+{ _Unwind_Backtrace() is a gcc extension that walks the stack and calls the  }
+{ _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack }
+{ or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON. }
+{ }
+type
+  FPC_Unwind_Trace_Fn = function (_para1:PFPC_Unwind_Context; _para2:pointer):FPC_Unwind_Reason_Code;cdecl;
+
+function _Unwind_Backtrace(_para1:FPC_Unwind_Trace_Fn; _para2:pointer):FPC_Unwind_Reason_Code;cdecl;weakexternal;
+
+{ _Unwind_GetCFA is a gcc extension that can be called from within a personality  }
+{ handler to get the CFA (stack pointer before call) of current frame. }
+{ }
+function _Unwind_GetCFA(_para1:PFPC_Unwind_Context):PtrUInt;cdecl;weakexternal;
+
+const
+  DW_EH_PE_absptr       = $00;
+  DW_EH_PE_omit         = $ff;
+
+  DW_EH_PE_uleb128      = $01;
+  DW_EH_PE_udata2       = $02;
+  DW_EH_PE_udata4       = $03;
+  DW_EH_PE_udata8       = $04;
+  DW_EH_PE_sleb128      = $09;
+  DW_EH_PE_sdata2       = $0A;
+  DW_EH_PE_sdata4       = $0B;
+  DW_EH_PE_sdata8       = $0C;
+  DW_EH_PE_signed       = $08;
+
+  DW_EH_PE_pcrel        = $10;
+  DW_EH_PE_textrel      = $20;
+  DW_EH_PE_datarel      = $30;
+  DW_EH_PE_funcrel      = $40;
+  DW_EH_PE_aligned      = $50;
+
+  DW_EH_PE_indirect	= $80;
+
+function FPC_psabieh_size_of_encoded_value(encoding: byte): longint;
+  begin
+    if encoding = DW_EH_PE_omit then
+      exit(0);
+
+    case (encoding and 7) of
+      DW_EH_PE_absptr:
+        exit(sizeof(pointer));
+      DW_EH_PE_udata2:
+        exit(2);
+      DW_EH_PE_udata4:
+        exit(4);
+      DW_EH_PE_udata8:
+        exit(8);
+      else
+        halt(217);
+    end
+  end;
+
+{ Given an encoding and an _Unwind_Context, return the base to which
+   the encoding is relative.  This base may then be passed to
+   read_encoded_value_with_base for use when the _Unwind_Context is
+   not available.  }
+
+function FPC_psabieh_base_of_encoded_value (encoding: byte; context: PFPC_Unwind_Context): PtrUInt;
+  begin
+    if encoding = DW_EH_PE_omit then
+      exit(0);
+
+    case (encoding and $70) of
+      DW_EH_PE_absptr,
+      DW_EH_PE_pcrel,
+      DW_EH_PE_aligned:
+        exit(0);
+      DW_EH_PE_textrel:
+        exit(_Unwind_GetTextRelBase(context));
+      DW_EH_PE_datarel:
+        exit(_Unwind_GetDataRelBase(context));
+      DW_EH_PE_funcrel:
+        exit(_Unwind_GetRegionStart(context));
+      else
+        halt(217);
+      end;
+  end;
+
+function fpc_read_uleb128 (p: PByte; out val: PTRUInt): PByte;
+  var
+    shift: longint;
+    b: byte;
+    res: PtrUInt;
+  begin
+    shift:=0;
+
+    res:=0;
+    repeat
+      b:=p^;
+      inc(p);
+      res:=res or (PtrUInt(b and $7f) shl shift);
+      inc(shift,7);
+    until (b and $80)<>0;
+
+    val:=res;
+    result:=p;
+  end;
+
+function fpc_read_sleb128 (p: PByte; out val: PtrInt): PByte;
+  var
+    shift: longint;
+    b: byte;
+    res: PtrUInt;
+  begin
+    shift:=0;
+
+    res:=0;
+    repeat
+      b:=p^;
+      inc(p);
+      res:=res or (PtrUInt(b and $7f) shl shift);
+      inc(shift,7);
+    until (b and $80)<>0;
+    if (shift<8*(sizeof(res))) and
+       ((b and $40)<>0) then
+      res:=res or -(PtrUInt(1) shl shift);
+
+    val:=PTRInt(res);
+    result:=p;
+  end;
+
+function FPC_psabieh_read_encoded_value_with_base (encoding: byte; base: PtrUInt; p: PByte; out val: PtrUInt): PByte;
+  var
+    res: PtrUInt;
+    tmpres: PtrInt;
+    alignedp: PPtrUint;
+  begin
+    if encoding=DW_EH_PE_aligned then
+      begin
+        alignedp:=PPtrUint(align(PtrUInt(p),sizeof(PtrUint)));
+        res:=alignedp^;
+        result:=PByte(alignedp)+sizeof(PtrUInt);
+      end
+    else
+      begin
+        case encoding and $0f of
+	  DW_EH_PE_absptr:
+            begin
+              res:=unaligned(PPtrUint(p)^);
+	      result:=p+sizeof(PtrUInt);
+            end;
+	  DW_EH_PE_uleb128:
+	    begin
+              result:=fpc_read_uleb128(p,res);
+            end;
+	  DW_EH_PE_sleb128:
+	    begin
+              result:=fpc_read_sleb128(p,tmpres);
+	      res:=PtrUInt(tmpres);;
+            end;
+	  DW_EH_PE_udata2:
+            begin
+	      res:=unaligned(pword(p)^);
+	      result:=p+2;
+            end;
+	  DW_EH_PE_udata4:
+            begin
+	      res:=unaligned(pdword(p)^);
+	      result:=p+4;
+	    end;
+	  DW_EH_PE_udata8:
+            begin
+	      res:=unaligned(pqword(p)^);
+	      result:=p+8;
+	    end;
+	  DW_EH_PE_sdata2:
+            begin
+	      res:=PtrUInt(unaligned(psmallint(p)^));
+	      result:=p+2;
+            end;
+	  DW_EH_PE_sdata4:
+            begin
+	      res:=PtrUInt(unaligned(plongint(p)^));
+	      result:=p+4;
+	    end;
+	  DW_EH_PE_sdata8:
+            begin
+	      res:=PtrUInt(unaligned(pint64(p)^));
+	      result:=p+8;
+	    end;
+          else
+            halt(217);
+        end;
+        if res<>0 then
+          begin
+            if (encoding and $70)=DW_EH_PE_pcrel then
+              inc(res,PtrUInt(p))
+            else
+              inc(res, base);
+            if (encoding and DW_EH_PE_indirect)<>0 then
+              res:=PPtrUInt(res)^;
+          end;
+      end;
+    val:=res;
+  end;
+
+function FPC_psabieh_read_encoded_value (context: PFPC_Unwind_Context; encoding: byte; p: PByte; out val: PtrUInt): PByte; inline;
+  begin
+    result:=FPC_psabieh_read_encoded_value_with_base(encoding,FPC_psabieh_base_of_encoded_value(encoding,context),p,val);
+  end;
+
+type
+  FPC_psabieh_lsda_header_info = record
+    Start: PtrUInt;
+    LPStart: PtrUInt;
+    ttype_base: PtrUInt;
+    TType: Pointer;
+    action_table: pointer;
+    ttype_encoding: byte;
+    call_site_encoding: byte;
+  end;
+
+function FPC_psabieh_parse_lsda_header(context: PFPC_Unwind_Context; p: PByte; out info: FPC_psabieh_lsda_header_info): PByte;
+  var
+    tmp: PTRUint;
+    lpstart_encoding: byte;
+  begin
+    if assigned(context) then
+      info.Start:=_Unwind_GetRegionStart(context)
+    else
+      info.Start:=0;
+
+    // Find @LPStart, the base to which landing pad offsets are relative.
+    lpstart_encoding:=p^;
+    inc(p);
+    if lpstart_encoding<>DW_EH_PE_omit then
+      p:=FPC_psabieh_read_encoded_value(context,lpstart_encoding,p,info.LPStart)
+    else
+      info.LPStart:=info.Start;
+
+    // Find @TType, the base of the handler and exception spec type data.
+    info.ttype_encoding:=p^;
+    inc(p);
+    if info.ttype_encoding<>DW_EH_PE_omit then
+      begin
+        p:=fpc_read_uleb128(p,tmp);
+        info.TType:=p+tmp;
+      end
+    else
+      info.TType:=nil;
+
+    // The encoding and length of the call-site table; the action table
+    // immediately follows.
+    info.call_site_encoding:=p^;
+    inc(p);
+    p:=fpc_read_uleb128(p,tmp);
+    info.action_table:=p+tmp;
+
+    result:=p;
+  end;
+
+
+// Return an element from a type table.
+function FPC_psabieh_get_ttype_entry(const info: FPC_psabieh_lsda_header_info; i: PtrUInt): TClass;
+  var
+    ptr: PtrUInt;
+  begin
+    i:=i*FPC_psabieh_size_of_encoded_value(info.ttype_encoding);
+    FPC_psabieh_read_encoded_value_with_base(info.ttype_encoding,info.ttype_base,info.TType-i,ptr);
+    result:=TClass(ptr);
+  end;
+
+function FPC_psabieh_can_catch(catch_type: TClass; thrown: TObject): boolean;
+  begin
+    result:=thrown is catch_type
+  end;
+
+// Return true if THROW_TYPE matches one if the filter types.
+function FPC_psabieh_check_exception_spec(const info: FPC_psabieh_lsda_header_info; thrown: TObject; filter_value: PtrInt): boolean;
+  var
+    e: PByte;
+    catch_type: TClass;
+    tmp: PtrUInt;
+  begin
+    e:=info.TType - filter_value - 1;
+    repeat
+      e:=fpc_read_uleb128(e,tmp);
+      // Zero signals the end of the list.  If we've not found
+      // a match by now, then we've failed the specification.
+      if tmp=0 then
+        exit(false);
+
+      // Match a ttype entry.
+      catch_type:=FPC_psabieh_get_ttype_entry(info,tmp);
+
+    until thrown is catch_type;
+    result:=true;
+  end;
+
+// Save stage1 handler information in the exception object
+procedure FPC_psabieh_save_caught_exception(ue_header: PFPC_Unwind_Exception;
+                      handler_switch_value: longint;
+                      language_specific_data: PByte;
+                      landing_pad: PtrUInt);
+  var
+    xh: PExceptObject;
+  begin
+    xh:=FPC_psabieh_GetExceptionWrapper(ue_header);
+    xh^.handler_switch_value:=handler_switch_value;
+    xh^.language_specific_data:=language_specific_data;
+    xh^.landing_pad:=landing_pad;
+  end;
+
+// Restore the catch handler information saved during phase1.
+procedure FPC_psabieh_restore_caught_exception(ue_header: PFPC_Unwind_Exception;
+                         out handler_switch_value: longint;
+                         out language_specific_data: PByte;
+                         out landing_pad: PtrUInt);
+  var
+    xh: PExceptObject;
+  begin
+    xh:=FPC_psabieh_GetExceptionWrapper(ue_header);
+    handler_switch_value:=xh^.handler_switch_value;
+    language_specific_data:=xh^.language_specific_data;
+    landing_pad:=xh^.landing_pad;
+  end;
+
+function FPC_psabieh_find_action_record(const info: FPC_psabieh_lsda_header_info; var p: PByte; const ip: PTRUint; var landing_pad: PtrUInt; var action_record: PByte): boolean;
+  var
+    cs_start, cs_len, cs_lp: PtrUint{_Unwind_Ptr};
+    cs_action: PtrUInt {_Unwind_Word};
+  begin
+    result:=false;
+    while (p<info.action_table) do
+      begin
+        // Note that all call-site encodings are "absolute" displacements.
+        p:=FPC_psabieh_read_encoded_value (nil, info.call_site_encoding, p, cs_start);
+        p:=FPC_psabieh_read_encoded_value (nil, info.call_site_encoding, p, cs_len);
+        p:=FPC_psabieh_read_encoded_value (nil, info.call_site_encoding, p, cs_lp);
+        p:=FPC_read_uleb128 (p, &cs_action);
+
+        // The table is sorted, so if we've passed the ip, stop.
+        if ip<(info.Start+cs_start) then
+          p:=info.action_table
+        else if ip<(info.Start+cs_start+cs_len) then
+          begin
+            if cs_lp<>0 then
+              landing_pad:=info.LPStart+cs_lp;
+            if cs_action<>0 then
+              action_record:=info.action_table+cs_action-1;
+            result:=true;
+          end;
+      end;
+  end;
+
+
+// Return true if the filter spec is empty, ie throw().
+
+function fpc_psabieh_empty_exception_spec(const info: FPC_psabieh_lsda_header_info; const filter_value: PtrInt {_Unwind_Sword}): boolean;
+  var
+    e: PByte;
+    tmp: PtrUInt;
+  begin
+    e:=PByte(info.ttype - filter_value - 1);
+    e:=fpc_read_uleb128(e,tmp);
+    result:=tmp = 0;
+  end;
+
+type
+  FPC_psabieh_found_handler_type = (
+    found_nothing,
+    found_terminate,
+    found_cleanup,
+    found_handler
+  );
+
+function FPC_psabieh_find_handler(const info: FPC_psabieh_lsda_header_info; const foreign_exception: boolean; actions: FPC_Unwind_Action; thrown: TObject; var action_record: PByte; var handler_switch_value: longint): FPC_psabieh_found_handler_type;
+  var
+    ar_filter, ar_disp: PtrInt;
+    catch_type: TClass;
+    throw_type: TOBject;
+    saw_cleanup, saw_handler: boolean;
+    p: PByte;
+  begin
+    saw_cleanup:=false;
+    saw_handler:=false;
+
+    // During forced unwinding, we only run cleanups.  With a foreign
+    // exception class, there's no exception type.
+    if ((actions and FPC_UA_FORCE_UNWIND)<>0) or
+       foreign_exception then
+      throw_type:=nil
+    else
+      throw_type:=thrown;
+
+    while true do
+      begin
+        p:=action_record;
+        p:=fpc_read_sleb128(p,ar_filter);
+        fpc_read_sleb128(p,ar_disp);
+
+        if ar_filter=0 then
+          begin
+            // Zero filter values are cleanups.
+            saw_cleanup:=true;
+          end
+        else if ar_filter>0 then
+          begin
+            // Positive filter values are handlers.
+            catch_type:=FPC_psabieh_get_ttype_entry(info,ar_filter);
+
+            // Null catch type is a catch-all handler; we can catch foreign
+            // exceptions with this.  Otherwise we must match types.
+            if not assigned(catch_type) or
+               (assigned(throw_type) and
+                (throw_type is catch_type)) then
+              begin
+                saw_handler:=true;
+                break;
+              end
+          end
+        else
+          begin
+            // Negative filter values are exception specifications.
+            // ??? How do foreign exceptions fit in?  As far as I can
+            // see we can't match because there's no __cxa_exception
+            // object to stuff bits in for __cxa_call_unexpected to use.
+            // Allow them iff the exception spec is non-empty.  I.e.
+            // a throw() specification results in __unexpected.
+            if (assigned(throw_type) and
+                not FPC_psabieh_check_exception_spec(info,thrown,ar_filter)) or
+               (not assigned(throw_type) and
+                FPC_psabieh_empty_exception_spec(info,ar_filter)) then
+              begin
+                saw_handler:=true;
+                break;
+              end;
+            end;
+
+        if ar_disp=0 then
+          break;
+        action_record:=p+ar_disp;
+      end;
+
+    if saw_handler then
+      begin
+        handler_switch_value:=ar_filter;
+        result:=found_handler;
+      end
+    else
+      begin
+        if saw_cleanup then
+          result:=found_cleanup
+        else
+          result:=found_nothing;
+      end;
+  end;
+
+{$ifdef FPC_PSABIEH_CPLUSPLUSSUPPORT}
+procedure __gxx_personality_v0(version: cint; actions: FPC_Unwind_Action; exceptionClass: cuint64; libunwind_exception: PFPC_Unwind_Exception; context: PFPC_Unwind_Context); cdecl; external;
+{$endif FPC_PSABIEH_CPLUSPLUSSUPPORT}
+
+function FPC_psabieh_personality_v0(version: cint; actions: FPC_Unwind_Action; exceptionClass: cuint64; libunwind_exception: PFPC_Unwind_Exception; context: PFPC_Unwind_Context): FPC_Unwind_Reason_Code; cdecl;
+  var
+    WrappedException: PExceptObject;
+    found_type: FPC_psabieh_found_handler_type;
+    info: FPC_psabieh_lsda_header_info;
+    language_specific_data: PByte;
+    action_record: PByte;
+    p: PByte;
+    landing_pad, ip: PtrUInt;
+    handler_switch_value: longint;
+    foreign_exception: boolean;
+  begin
+    { unsupported version -> failure }
+    if version<>1 then
+      begin
+        result:=FPC_URC_FATAL_PHASE1_ERROR;
+        exit;
+      end;
+
+    { foreign exception type -> let c++ runtime handle it }
+    foreign_exception:=exceptionClass<>FPC_psabieh_exceptionClass_ID.u;
+{$ifdef FPC_PSABIEH_CPLUSPLUSSUPPORT}
+    if foreign_exception then
+      begin
+        result:=__gxx_personality_v0(version, actions, exceptionClass, libunwind_exception, context)
+        exit;
+      end;
+{$endif FPC_PSABIEH_CPLUSPLUSSUPPORT}
+
+    WrappedException:=FPC_psabieh_GetExceptionWrapper(libunwind_exception);
+
+    // Shortcut for phase 2 found handler for domestic exception.
+    if (actions=(FPC_UA_CLEANUP_PHASE or FPC_UA_HANDLER_FRAME)) and
+       not foreign_exception then
+      begin
+        FPC_psabieh_restore_caught_exception(libunwind_exception,handler_switch_value,
+                                 language_specific_data,landing_pad);
+        if landing_pad<>0 then
+          found_type:=found_handler
+        else
+          found_type:=found_terminate;
+      end
+    else
+      begin
+        language_specific_data:=PByte(_Unwind_GetLanguageSpecificData(context));
+
+        // If no LSDA, then there are no handlers or cleanups.
+        if not assigned(language_specific_data) then
+          begin
+            exit(FPC_URC_CONTINUE_UNWIND);
+          end;
+
+        // Parse the LSDA header.
+        p:=FPC_psabieh_parse_lsda_header(context,language_specific_data,info);
+        info.ttype_base:=FPC_psabieh_base_of_encoded_value(info.ttype_encoding,context);
+        ip:=_Unwind_GetIP(context);
+        dec(ip);
+        landing_pad:=0;
+        action_record:=nil;
+        handler_switch_value:=0;
+
+        // Search the call-site table for the action associated with this IP.
+        if FPC_psabieh_find_action_record(info,p,ip,landing_pad,action_record) then
+          begin
+            if landing_pad=0 then
+              begin
+                // If ip is present, and has a null landing pad, there are
+                // no cleanups or handlers to be run.
+                found_type:=found_nothing;
+              end
+            else if action_record=nil then
+              begin
+                // If ip is present, has a non-null landing pad, and a null
+                // action table offset, then there are only cleanups present.
+                // Cleanups use a zero switch value, as set above.
+                found_type:=found_cleanup;
+              end
+            else
+              begin
+                // Otherwise we have a catch handler or exception specification.
+                found_type:=FPC_psabieh_find_handler(info,foreign_exception,actions,WrappedException^.FObject,action_record,handler_switch_value);
+              end
+          end
+        else
+          begin
+            // If ip is not present in the table, call terminate.  This is for
+            // a destructor inside a cleanup, or a library routine the compiler
+            // was not expecting to throw.
+            found_type:=found_terminate;
+          end;
+
+         if found_type=found_nothing then
+           exit(FPC_URC_CONTINUE_UNWIND);
+
+        if (actions and FPC_UA_SEARCH_PHASE)<>0 then
+          begin
+            if found_type=found_cleanup then
+              exit(FPC_URC_CONTINUE_UNWIND);
+
+            if not foreign_exception then
+              begin
+                // For domestic exceptions, we cache data from phase 1 for phase 2.
+                FPC_psabieh_save_caught_exception(libunwind_exception,
+                                        handler_switch_value,language_specific_data,
+                                        landing_pad);
+              end;
+            exit(FPC_URC_HANDLER_FOUND);
+          end;
+      end;
+
+    if ((actions and FPC_UA_FORCE_UNWIND)<>0) or
+       foreign_exception then
+      begin
+        if found_type=found_terminate then
+          halt(217)
+        { can only perform cleanups when force-unwinding }
+        else if handler_switch_value<0 then
+          begin
+            RunError(217)
+          end
+      end
+    else
+      begin
+        if found_type=found_terminate then
+          halt(217);
+      end;
+    { For targets with pointers smaller than the word size, we must extend the
+       pointer, and this extension is target dependent.  }
+    {$if sizeof(pointer)<>sizeof(SizeInt)}
+      {$error Add support for extending pointer values}
+    {$endif}
+    _Unwind_SetGR(context,fpc_eh_return_data_regno(0),libunwind_exception);
+    _Unwind_SetGR (context,fpc_eh_return_data_regno(1),handler_switch_value);
+    _Unwind_SetIP(context,landing_pad);
+    result:=FPC_URC_INSTALL_CONTEXT;
+  end;
+
+//////////////////////////////
+///// Raising an exception
+//////////////////////////////
+
+procedure FPC_psabieh_ExceptionCleanUp(reason: FPC_Unwind_Reason_Code; exc:PFPC_Unwind_Exception); cdecl;
+  var
+    ExceptWrapper: PExceptObject;
+  begin
+    // If we haven't been caught by a foreign handler, then this is
+    // some sort of unwind error.  In that case just die immediately.
+    // _Unwind_DeleteException in the HP-UX IA64 libunwind library
+    //  returns _URC_NO_REASON and not _URC_FOREIGN_EXCEPTION_CAUGHT
+    // like the GCC _Unwind_DeleteException function does.
+    if (reason<>FPC_URC_FOREIGN_EXCEPTION_CAUGHT) and
+       (reason<>FPC_URC_NO_REASON) then
+      halt(217);
+
+    ExceptWrapper:=FPC_psabieh_GetExceptionWrapper(exc);
+    ExceptWrapper^.FObject.free;
+    ExceptWrapper^.FObject:=nil;
+    Dispose(ExceptWrapper);
+  end;
+
+function PushExceptObject(Obj : TObject; AnAddr : CodePointer; AFrame : Pointer): PExceptObject; forward;
+
+{$define FPC_SYSTEM_HAS_RAISEEXCEPTION}
+procedure fpc_RaiseException(Obj: TObject; AnAddr: CodePointer; AFrame: Pointer); compilerproc;
+var
+  _ExceptObjectStack : PExceptObject;
+  _ExceptAddrstack : PExceptAddr;
+  ExceptWrapper: PExceptObject;
+begin
+{$ifdef excdebug}
+  writeln ('In psabieh RaiseException');
+{$endif}
+  if ExceptTryLevel<>0 then
+    Halt(217);
+  ExceptTryLevel:=1;
+  ExceptWrapper:=PushExceptObject(Obj,AnAddr,AFrame);
+  ExceptWrapper^.unwind_exception.exception_class:=FPC_psabieh_exceptionClass_ID.u;
+  ExceptWrapper^.unwind_exception.exception_cleanup:=@FPC_psabieh_ExceptionCleanUp;
+  { if PushExceptObject causes another exception, the following won't be executed,
+    causing halt upon entering this routine recursively. }
+  ExceptTryLevel:=0;
+  _ExceptObjectStack:=ExceptObjectStack;
+  if (RaiseProc <> nil) and (_ExceptObjectStack <> nil) then
+    with _ExceptObjectStack^ do
+      RaiseProc(FObject,Addr,FrameCount,Frames);
+  _Unwind_RaiseException(@ExceptWrapper^.unwind_exception);
+  // should never return
+  Halt(217);
+end;
+
+{$ifdef FPC_PSABIEH_CPLUSPLUSSUPPORT}
+function __cxa_begin_catch(exc:PFPC_Unwind_Exception): pointer; cdecl; external;
+{$endif FPC_PSABIEH_CPLUSPLUSSUPPORT}
+
+function FPC_psabi_begin_catch(exc:PFPC_Unwind_Exception): pointer; cdecl; compilerproc;
+  var
+    ExceptWrapper: PExceptObject;
+    _ExceptObjectStack : PExceptObject;
+    count: longint;
+  begin
+    _ExceptObjectStack:=ExceptObjectStack;
+    // hand off foreign exceptions to the C++ runtime
+    if exc^.exception_class<>FPC_psabieh_exceptionClass_ID.u then
+      begin
+        // Can't link foreign exceptions with our stack
+        if assigned(_ExceptObjectStack) then
+          halt(217);
+        // This is a wrong conversion, but as long as afterwards we only access
+        // fields of PFPC_Unwind_Exception, it's fine
+        _ExceptObjectStack:=FPC_psabieh_GetExceptionWrapper(exc);
+{$ifdef FPC_PSABIEH_CPLUSPLUSSUPPORT}
+        result:=__cxa_begin_catch(exc);
+{$else}
+         // ??? No sensible value to return; we don't know what the
+         // object is, much less where it is in relation to the header.
+        result:=nil;
+{$endif}
+        exit;
+      end;
+
+    ExceptWrapper:=FPC_psabieh_GetExceptionWrapper(exc);
+
+    count:=ExceptWrapper^.refcount;
+    // Count is less than zero if this exception was rethrown from an
+    // immediately enclosing region.
+    if count < 0 then
+      count:=-count+1
+    else
+      inc(count);
+    ExceptWrapper^.refcount:=count;
+//    globals->uncaughtExceptions -= 1;
+    if _ExceptObjectStack<>ExceptWrapper then
+      begin
+        ExceptWrapper^.Next:=_ExceptObjectStack;
+        _ExceptObjectStack:=ExceptWrapper;
+      end;
+
+    result:= ExceptWrapper^.FObject;
+{$ifdef __ARM_EABI_UNWINDER__}
+    _Unwind_Complete(ExceptWrapper);
+{$endif}
+  end;
+
+{$ifdef FPC_PSABIEH_CPLUSPLUSSUPPORT}
+procedure __cxa_end_catch; cdecl; external;
+{$endif FPC_PSABIEH_CPLUSPLUSSUPPORT}
+
+procedure FPC_psabi_end_catch; cdecl; compilerproc;
+  var
+    _ExceptObjectStack: PExceptObject;
+    refcount: longint;
+  begin
+    _ExceptObjectStack:=ExceptObjectStack;
+    // A rethrow of a foreign exception will be removed from the
+    // the exception stack immediately by __cxa_rethrow.
+    if not assigned(_ExceptObjectStack) then
+      exit;
+
+    // Pass foreign exception to the C++ runtime
+    if _ExceptObjectStack^.unwind_exception.exception_class<>FPC_psabieh_exceptionClass_ID.u then
+      begin
+        { remove foreign exception; since we never link multiple foreign
+          exceptions, we know the stack is now empty }
+        ExceptObjectStack:=nil;
+{$ifdef FPC_PSABIEH_CPLUSPLUSSUPPORT}
+        __cxa_end_catch();
+{$else}
+       _Unwind_DeleteException(@_ExceptObjectStack^.unwind_exception);
+{$endif}
+        exit;
+      end;
+
+    refcount:=_ExceptObjectStack^.refcount;
+    if refcount<0 then
+      begin
+        // This exception was rethrown.  Decrement the (inverted) catch
+        // count and remove it from the chain when it reaches zero.
+        inc(refcount);
+        if refcount = 0 then
+          ExceptObjectStack:=_ExceptObjectStack^.next;
+      end
+    else
+      begin
+        dec(refcount);
+        if refcount=0 then
+          begin
+            // Handling for this exception is complete.  Destroy the object.
+            ExceptObjectStack:=_ExceptObjectStack^.next;
+            _Unwind_DeleteException(@_ExceptObjectStack^.unwind_exception);
+            exit;
+          end
+        else if refcount<0 then
+          begin
+            // A bug in the exception handling library or compiler.
+            halt(217);
+          end;
+      end;
+    _ExceptObjectStack^.refcount:=refcount;
+  end;

+ 61 - 0
rtl/inc/psabiehh.inc

@@ -0,0 +1,61 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2017-2018 by Jonas Maebe,
+    member of the Free Pascal development team
+
+    This file contains support for Itanium psABI EH
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+
+{$packrecords c}
+
+const
+  FPC_URC_NO_REASON = FPC_Unwind_Reason_Code(0);
+  FPC_URC_FOREIGN_EXCEPTION_CAUGHT = FPC_Unwind_Reason_Code(1);
+  FPC_URC_FATAL_PHASE2_ERROR = FPC_Unwind_Reason_Code(2);
+  FPC_URC_FATAL_PHASE1_ERROR = FPC_Unwind_Reason_Code(3);
+  FPC_URC_NORMAL_STOP = FPC_Unwind_Reason_Code(4);
+  FPC_URC_END_OF_STACK = FPC_Unwind_Reason_Code(5);
+  FPC_URC_HANDLER_FOUND = FPC_Unwind_Reason_Code(6);
+  FPC_URC_INSTALL_CONTEXT = FPC_Unwind_Reason_Code(7);
+  FPC_URC_CONTINUE_UNWIND = FPC_Unwind_Reason_Code(8);
+
+const
+  FPC_UA_SEARCH_PHASE = FPC_Unwind_Action(1);
+  FPC_UA_CLEANUP_PHASE = FPC_Unwind_Action(2);
+  FPC_UA_HANDLER_FRAME = FPC_Unwind_Action(4);
+  FPC_UA_FORCE_UNWIND = FPC_Unwind_Action(8);
+  FPC_UA_END_OF_STACK = FPC_Unwind_Action(16);
+
+type
+  PFPC_Unwind_Context = ^FPC_Unwind_Context;
+  FPC_Unwind_Context = record
+  end;
+
+  procedure _Unwind_RaiseException(exception_object: PFPC_Unwind_Exception); cdecl; external;
+  procedure _Unwind_Resume(exception_object: PFPC_Unwind_Exception); cdecl; external;
+
+type
+  TFPC_psabieh_exceptionClass = record
+    case byte of
+      0: (u: qword); {cuint64}
+      1: (a: array[0..7] of char);
+  end;
+
+{$push}
+{$j-}
+const
+  FPC_psabieh_exceptionClass_ID: TFPC_psabieh_exceptionClass =
+    (a: 'FPC1PAS'#0);
+{$pop}
+
+
+{$packrecords default}