fpc/rtl/inc/psabieh.inc
2019-09-15 17:26:21 +00:00

1251 lines
43 KiB
PHP

{
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}
{$ifdef linux}
{$linklib c}
{$linklib libgcc_s}
{$endif}
{$ifdef __ARM_EABI_UNWINDER__}
{$define PSABIEH_NO_SIZEOF_ENCODED_VALUE}
{$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;
function _Unwind_Resume_or_Rethrow (context:PFPC_Unwind_Context): FPC_Unwind_Reason_Code;cdecl;external;
procedure _Unwind_DeleteException(context:PFPC_Unwind_Context);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;
{$ifdef __ARM_EABI_UNWINDER__}
procedure _Unwind_Complete(exceptionObject: PFPC_Unwind_Exception);cdecl;external;
function __gnu_unwind_frame(exception:PFPC_Unwind_Exception;context:PFPC_Unwind_Context):FPC_Unwind_Reason_Code;cdecl;external;
type
FPC_Unwind_VRS_RegClass = UInt32;
const
FPC_UVRSC_CORE = FPC_Unwind_VRS_RegClass(0); // integer register
FPC_UVRSC_VFP = FPC_Unwind_VRS_RegClass(1); // vfp
FPC_UVRSC_FPA = FPC_Unwind_VRS_RegClass(2); // fpa
FPC_UVRSC_WMMXD = FPC_Unwind_VRS_RegClass(3); // Intel WMMX data register
FPC_UVRSC_WMMXC = FPC_Unwind_VRS_RegClass(4); // Intel WMMX control register
type
FPC_Unwind_VRS_DataRepresentation = UInt32;
const
FPC_UVRSD_UINT32 = FPC_Unwind_VRS_DataRepresentation(0);
FPC_UVRSD_VFPX = FPC_Unwind_VRS_DataRepresentation(1);
FPC_UVRSD_FPAX = FPC_Unwind_VRS_DataRepresentation(2);
FPC_UVRSD_UINT64 = FPC_Unwind_VRS_DataRepresentation(3);
FPC_UVRSD_FLOAT = FPC_Unwind_VRS_DataRepresentation(4);
FPC_UVRSD_DOUBLE = FPC_Unwind_VRS_DataRepresentation(5);
type
FPC_Unwind_VRS_Result = UInt32;
const
FPC_UVRSR_OK = FPC_Unwind_VRS_Result(0);
FPC_UVRSR_NOT_IMPLEMENTED = FPC_Unwind_VRS_Result(1);
FPC_UVRSR_FAILED = FPC_Unwind_VRS_Result(2);
Function _Unwind_VRS_Set(context: PFPC_Unwind_Context; regclass: FPC_Unwind_VRS_RegClass;
regnr: PTRUint {uw}; repr: FPC_Unwind_VRS_DataRepresentation;
value: pointer): FPC_Unwind_VRS_Result; cdecl; external;
function _Unwind_VRS_Get(context: PFPC_Unwind_Context; regclass: FPC_Unwind_VRS_RegClass;
regnr: PTRUint {uw}; repr: FPC_Unwind_VRS_DataRepresentation;
value: pointer): FPC_Unwind_VRS_Result; cdecl; external;
procedure _Unwind_SetGR(context:PFPC_Unwind_Context;index:cint; new_value:PtrUInt); inline;
begin
_Unwind_VRS_Set(context,FPC_UVRSC_CORE, index, FPC_UVRSD_UINT32, @new_Value);
end;
function _Unwind_GetGR(context:PFPC_Unwind_Context;index:cint):PtrUInt; inline;
begin
_Unwind_VRS_Get(context,FPC_UVRSC_CORE, index, FPC_UVRSD_UINT32, @result);
end;
procedure _Unwind_SetIP(context:PFPC_Unwind_Context;new_value:PtrUInt); inline;
begin
_Unwind_SetGR(context,15,new_value or (_Unwind_GetGR(context,15) and 1));
end;
function _Unwind_GetIP(context:PFPC_Unwind_Context):PtrUInt;cdecl; inline;
begin
result:=_Unwind_GetGR(context,15) and not(1);
end;
{$else}
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(context:PFPC_Unwind_Context; new_value:PtrUInt);cdecl;external;
{$endif}
procedure DoUnHandledException; forward;
{ _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;
{$ifndef PSABIEH_NO_SIZEOF_ENCODED_VALUE}
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
begin
{$ifdef excdebug}
writeln('Unsupported encoding: $', hexstr(encoding,sizeof(encoding)*2));
{$endif}
halt(217);
end;
end
end;
{$endif PSABIEH_NO_SIZEOF_ENCODED_VALUE}
{ 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
begin
{$ifdef excdebug}
writeln('Unsupported base of encoding: $', hexstr(encoding,sizeof(encoding)*2));
{$endif}
halt(217);
end;
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
begin
{$ifdef excdebug}
writeln('Unsupported encoding of value with base: $', hexstr(encoding,sizeof(encoding)*2));
{$endif}
halt(217);
end;
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;
{$ifdef excdebug}
writeln('lsda header');
writeln(' * start: $',hexstr(info.start,sizeof(info.start)*2));
writeln(' * lp_start encoding: $',hexstr(lpstart_encoding,sizeof(lpstart_encoding)*2));
writeln(' * lp_start: $',hexstr(info.LPStart,sizeof(info.LPStart)*2));
writeln(' * ttype_encoding: $',hexstr(info.ttype_encoding,sizeof(info.ttype_encoding)*2));
writeln(' * ttype base: $',hexstr(info.TType));
writeln(' * call_site_encoding: $',hexstr(info.call_site_encoding,sizeof(info.call_site_encoding)*2));
writeln(' * action table: $', hexstr(p),' (offset: ',tmp,')');
{$endif}
result:=p;
end;
{$ifdef __ARM_EABI_UNWINDER__}
function FPC_psabieh_Unwind_decode_target2(ptr: PtrUInt {_Unwind_Word}): PtrUInt {_Unwind_Word}; inline;
begin
result:=PPtrUInt(ptr)^;
// Zero values are always NULL.
if result<>0 then
begin
{$if defined(linux) or defined(netbsd)}
// Pc-relative indirect.
inc(result,ptr);
result:=PPtrUint(result)^;
{$else}
// Pc-relative pointer.
inc(result,ptr);
{$endif}
end;
end;
{$endif __ARM_EABI_UNWINDER__}
// Return an element from a type table.
{$ifdef __ARM_EABI_UNWINDER__}
function FPC_psabieh_get_ttype_entry(const info: FPC_psabieh_lsda_header_info; i: PtrUInt {_Unwind_Word}): TClass;
var
ptr: PtrUInt {_Unwind_Word};
begin
ptr:=PtrUInt(info.TType)-(i*4);
ptr:=FPC_psabieh_Unwind_decode_target2(ptr);
result:=TClass(ptr);
end;
{$else}
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;
{$endif}
// 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);
{$ifdef __ARM_EABI_UNWINDER__}
tmp:=FPC_psabieh_Unwind_decode_target2(PtrUInt(e)); {_Unwind_Word}
{$endif}
// Match a ttype entry.
catch_type:=FPC_psabieh_get_ttype_entry(info,tmp);
until thrown is catch_type;
result:=true;
end;
{$ifdef __ARM_EABI_UNWINDER__}
// Save stage1 handler information in the exception object
procedure FPC_psabieh_save_caught_exception(ue_header: PFPC_Unwind_Exception;
context: PFPC_Unwind_Context;
handler_switch_value: longint;
language_specific_data: PByte;
landing_pad: PtrUInt);
begin
with ue_header^.barrier_cache do
begin
sp:=_Unwind_GetGR(context,13);
{ bitpattern[0] is assigned but never used in the original code }
bitpattern[1]:=handler_switch_value;
bitpattern[2]:=PtrUInt(language_specific_data);
bitpattern[3]:=landing_pad;
end;
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);
begin
with ue_header^.barrier_cache do
begin
handler_switch_value:=longint(bitpattern[1]);
language_specific_data:=PByte(bitpattern[2]);
landing_pad:=bitpattern[3];
end;
end;
{$else __ARM_EABI_UNWINDER__}
// Save stage1 handler information in the exception object
procedure FPC_psabieh_save_caught_exception(ue_header: PFPC_Unwind_Exception;
context: PFPC_Unwind_Context;
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;
{$endif __ARM_EABI_UNWINDER__}
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;
{$ifdef excdebug}
writeln('find action record start: p: $',hexstr(p),'; lsda covered code start: $',hexstr(info.Start,sizeof(info.start)*2),'; lsda action table: $',hexstr(info.action_table),'; lsda call site encoding: $',hexstr(info.call_site_encoding,2),'; ip: $', hexstr(ip,sizeof(ip)*2));
{$endif}
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);
{$ifdef excdebug}
writeln('find action record: cs_start: $',hexstr(cs_start,sizeof(cs_start)*2),', len: ',cs_len,
' (ip=$',hexstr(info.Start+cs_start,sizeof(PtrUInt)*2),'...$',hexstr(info.Start+cs_start+cs_len,sizeof(PtrUInt)*2),')',
', lp: ', cs_lp,' action ofs: ',cs_action);
{$endif}
// 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;
{$ifdef excdebug}
writeln('action record result: action_record: $',hexstr(cs_start,sizeof(cs_start)*2),', len: ',cs_len,', lp: ', cs_lp,
',landing_pad: $',hexstr(landing_pad,sizeof(landing_pad)*2));
{$endif}
result:=true;
exit;
end;
end;
{$ifdef excdebug}
writeln('find action record failed');
{$endif}
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}
{$ifdef __ARM_EABI_UNWINDER__}
function continue_unwinding(libunwind_exception: PFPC_Unwind_Exception; context: PFPC_Unwind_Context): FPC_Unwind_Reason_Code; inline;
begin
if __gnu_unwind_frame(libunwind_exception, context)<>FPC_URC_OK then
result:=FPC_URC_FAILURE
else
result:=FPC_URC_CONTINUE_UNWIND;
end;
function _FPC_psabieh_personality_v0(state: FPC_Unwind_State; libunwind_exception: PFPC_Unwind_Exception; context: PFPC_Unwind_Context): FPC_Unwind_Reason_Code; cdecl;
{$else}
function continue_unwinding(libunwind_exception: PFPC_Unwind_Exception; context: PFPC_Unwind_Context): FPC_Unwind_Reason_Code; inline;
begin
result:=FPC_URC_CONTINUE_UNWIND;
end;
function _FPC_psabieh_personality_v0(version: longint; actions: FPC_Unwind_Action; exceptionClass: qword; libunwind_exception: PFPC_Unwind_Exception; context: PFPC_Unwind_Context): FPC_Unwind_Reason_Code; cdecl;
{$endif}
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; { _Unwind_Ptr }
handler_switch_value: longint;
foreign_exception: boolean;
{$ifdef __ARM_EABI_UNWINDER__}
actions: FPC_Unwind_Action;
{$endif}
begin
{$ifdef __ARM_EABI_UNWINDER__}
{ convert the state flags to FPC_Unwind_Action flags so we can share the rest of the code }
case (state and FPC_US_ACTION_MASK) of
FPC_US_VIRTUAL_UNWIND_FRAME:
begin
actions:=FPC_UA_SEARCH_PHASE;
end;
FPC_US_UNWIND_FRAME_STARTING:
begin
actions:=FPC_UA_CLEANUP_PHASE;
if ((state and FPC_US_FORCE_UNWIND)<>0) and
(libunwind_exception^.barrier_cache.sp=_Unwind_GetGR(context,13)) then
actions:=actions or FPC_UA_HANDLER_FRAME;
end;
FPC_US_UNWIND_FRAME_RESUME:
begin
result:=continue_unwinding(libunwind_exception,context);
exit;
end;
end;
actions:=actions or (state and FPC_US_FORCE_UNWIND);
// The dwarf unwinder assumes the context structure holds things like the
// function and LSDA pointers. The ARM implementation caches these in
// the exception header (UCB). To avoid rewriting everything we make the
// virtual IP register point at the UCB.
ip:=PtrUInt(libunwind_exception);
_Unwind_SetGR(context, 12, ip);
{ foreign exception type -> let c++ runtime handle it }
foreign_exception:=libunwind_exception^.exception_class<>FPC_psabieh_exceptionClass_ID.u;
{$else __ARM_EABI_UNWINDER__}
{ 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;
{$endif __ARM_EABI_UNWINDER__}
{$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);
{$ifdef excdebug}
writeln('Personality (version = ',{$ifndef __ARM_EABI_UNWINDER__}version{$else}0{$endif},', actions = $',hexstr(actions,4),') started for wrapper ',hexstr(WrappedException),' = fpc exc ',hexstr(WrappedException^.FObject),
', refcount is now ',WrappedException^.refcount);
writeln(' ip=$',hexstr(_Unwind_GetIP(context),sizeof(pointer)*2));
{$endif}
// 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
{$ifdef excdebug}
writeln('restoring caught exception');
{$endif}
FPC_psabieh_restore_caught_exception(libunwind_exception,handler_switch_value,
language_specific_data,landing_pad);
{$ifdef excdebug}
writeln('restoring caught exception, landing_pad = $',hexstr(landing_pad,sizeof(landing_pad)*2));
{$endif}
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
{$ifdef excdebug}
writeln('did not find lsda for ip $',hexstr(_Unwind_GetIP(context),sizeof(pointer)*2));
{$endif}
exit(continue_unwinding(libunwind_exception,context));
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
{$ifdef excdebug}
writeln('found action record for ip ',hexstr(_Unwind_GetIP(context),sizeof(pointer)*2));
{$endif}
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;
{$ifdef excdebug}
writeln('find_handler: ',found_type);
{$endif}
if found_type=found_nothing then
exit(continue_unwinding(libunwind_exception,context));
if (actions and FPC_UA_SEARCH_PHASE)<>0 then
begin
if found_type=found_cleanup then
exit(continue_unwinding(libunwind_exception,context));
if not foreign_exception then
begin
{$ifdef excdebug}
writeln('saving native exception: $',hexstr(landing_pad,sizeof(landing_pad)*2));
{$endif}
// For domestic exceptions, we cache data from phase 1 for phase 2.
FPC_psabieh_save_caught_exception(libunwind_exception,context,
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
begin
{$ifdef excdebug}
writeln('foreign exception or force unwind, and found type = found terminate; actions = $',hexstr(actions,sizeof(actions)*2),'; foreign exception ', foreign_exception);
{$endif}
DoUnHandledException;
end
{ can only perform cleanups when force-unwinding }
else if handler_switch_value<0 then
begin
{$ifdef excdebug}
writeln('foreign exception or force unwind, handler_switch_value < 0: ', handler_switch_value);
{$endif}
DoUnHandledException;
end
end
else
begin
if found_type=found_terminate then
begin
{$ifdef excdebug}
writeln('native exception and no force unwind, and force_terminate');
{$endif}
DoUnHandledException;
end
else if handler_switch_value<0 then
begin
{ C++ calls __cxa_call_unexpected in this case }
{$ifdef excdebug}
writeln('native exception and no force unwind, and handler_switch_value<0: ', handler_switch_value);
{$endif}
DoUnHandledException;
end;
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}
{$ifdef excdebug}
writeln('returning exception $',hexstr(libunwind_exception),' with switch value ',handler_switch_value);
{$endif}
_Unwind_SetGR(context,fpc_eh_return_data_regno(0),PtrUInt(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
begin
{$ifdef excdebug}
writeln('exception cleanup and reason not foreign exception or no reason, reason = $',hexstr(reason,sizeof(reason)*2));
{$endif}
halt(217);
end;
ExceptWrapper:=FPC_psabieh_GetExceptionWrapper(exc);
{$ifdef excdebug}
writeln('exception cleanup: deleting wrapper ',hexstr(ExceptWrapper),' and fpc exception ',hexstr(ExceptWrapper^.FObject));
{$endif}
ExceptWrapper^.FObject.free;
ExceptWrapper^.FObject:=nil;
if assigned(ExceptWrapper^.frames) then
freemem(ExceptWrapper^.frames);
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;
RaiseResult: FPC_Unwind_Reason_Code;
begin
{$ifdef excdebug}
writeln ('In psabieh RaiseException for object ',hexstr(obj),' of class type ',obj.classname);
{$endif}
if ExceptTryLevel<>0 then
begin
{$ifdef excdebug}
writeln('exception while raising exception, aborting');
{$endif}
Halt(217);
end;
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);
RaiseResult:=_Unwind_RaiseException(@ExceptWrapper^.unwind_exception);
// Only returns if there is no exception catching block anymore
{$ifdef excdebug}
writeln('_Unwind_RaiseException returned: ',RaiseResult);
{$endif}
DoUnHandledException;
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; compilerproc;
var
ExceptWrapper: PExceptObject;
_ExceptObjectStack : PExceptObject;
count: longint;
begin
{$ifdef excdebug}
writeln('start begin_catch unwind exception ',hexstr(exc));
{$endif}
_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
begin
{$ifdef excdebug}
writeln('begin catch for nested foreign exception');
{$endif}
DoUnHandledException;
end;
// 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;
{$ifdef excdebug}
writeln('stop begin_catch for wrapper ',hexstr(ExceptWrapper),' = fpc exc ',hexstr(ExceptWrapper^.FObject),', refcount is now ',count);
{$endif}
result:= ExceptWrapper^.FObject;
{$ifdef __ARM_EABI_UNWINDER__}
_Unwind_Complete(exc);
{$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;
{$ifdef excdebug}
writeln('start end_catch unwind exception ',hexstr(@_ExceptObjectStack^.unwind_exception));
{$endif}
// A rethrow of a foreign exception will be removed from the
// the exception stack immediately by __cxa_rethrow -> stack could be empty here
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;
{$ifdef excdebug}
writeln('middle end_catch for wrapper ',hexstr(_ExceptObjectStack),' = fpc exception ',hexstr(_ExceptObjectStack^.FObject),' with refcount ',refcount);
{$endif}
if refcount<0 then
begin
{ Can happen in the original glibc code, but not for us. When re-raising an
exception, we always immediately do this to an outer frame }
halt(217);
end
else
begin
dec(refcount);
{$ifdef excdebug}
writeln('stop end_catch, not rethrown, new refcount: ',refcount);
{$endif}
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.
{$ifdef excdebug}
writeln('refcount for exception is negative in end catch');
{$endif}
RunError(217);
end;
end;
_ExceptObjectStack^.refcount:=refcount;
end;
{$ifdef FPC_PSABIEH_CPLUSPLUSSUPPORT}
procedure __cxa_rethrow; cdecl; external; noreturn;
{$endif FPC_PSABIEH_CPLUSPLUSSUPPORT}
{$define FPC_SYSTEM_HAS_RERAISE}
procedure fpc_ReRaise; [public,alias:'FPC_RERAISE']; compilerproc;
var
_ExceptObjectStack: PExceptObject;
refcount: longint;
reraise_error: FPC_Unwind_Reason_Code;
begin
_ExceptObjectStack:=ExceptObjectStack;
// globals->uncaughtExceptions += 1;
{$ifdef excdebug}
writeln('start reraise for wrapper ',hexstr(_ExceptObjectStack));
{$endif}
// Watch for luser rethrowing with no active exception.
if assigned(_ExceptObjectStack) then
begin
// Tell __cxa_end_catch this is a rethrow.
if _ExceptObjectStack^.unwind_exception.exception_class<>FPC_psabieh_exceptionClass_ID.u then
{$ifdef FPC_PSABIEH_CPLUSPLUSSUPPORT}
begin
{ remove foreign exception; since we never link multiple foreign
exceptions, we know the stack is now empty }
ExceptObjectStack:=nil;
__cxa_rethrow;
{ should never be reached }
DoUnHandledException;
end
{$endif FPC_PSABIEH_CPLUSPLUSSUPPORT}
else
begin
{ reraise has to reset the refcount, this is also what the
generic exception handling does }
_ExceptObjectStack^.refcount := 0;
end;
{$ifdef excdebug}
writeln('Stop reraise, new refcount = ',_ExceptObjectStack^.refcount);
{$endif}
// #ifdef _GLIBCXX_SJLJ_EXCEPTIONS
// _Unwind_SjLj_Resume_or_Rethrow (&header->unwindHeader);
// #else
// #if defined(_LIBUNWIND_STD_ABI)
// _Unwind_RaiseException (@_ExceptObjectStack^.unwind_exception);
// #else
reraise_error:=_Unwind_Resume_or_Rethrow (@_ExceptObjectStack^.unwind_exception);
{$ifdef excdebug}
writeln('reraise failed, error = ',reraise_error);
{$endif}
// #endif
// #endif
// Some sort of unwinding error.
DoUnHandledException;
end;
DoUnHandledException;
end;
{$define FPC_SYSTEM_HAS_RAISENESTED}
procedure fpc_raise_nested;compilerproc;
var
hp, _ExceptObjectStack: PExceptObject;
begin
_ExceptObjectStack:=ExceptObjectStack;
if not(assigned(_ExceptObjectStack)) or
not(assigned(_ExceptObjectStack^.next)) then
begin
{$ifdef excdebug}
writeln ('raise_nested: At end of ExceptionObjectStack');
{$endif}
halt(1);
end;
if _ExceptObjectStack^.unwind_exception.exception_class<>FPC_psabieh_exceptionClass_ID.u then
begin
{$ifdef excdebug}
writeln ('raise_nested: top of stack contains foreign exception');
{$endif}
halt(1);
end;
hp:=_ExceptObjectStack^.next;
_ExceptObjectStack^.next:=hp^.next;
{$ifdef excdebug}
writeln('raise_nested: raising nested wrapper ',hexstr(_ExceptObjectStack),' = fpc exception ',hexstr(_ExceptObjectStack^.FObject),' with refcount ',_ExceptObjectStack^.refcount{,' (will increase to ',_ExceptObjectStack^.refcount+1,')'});
writeln('raise_nested: previous exception ',hexstr(hp),' = fpc exception ',hexstr(hp^.FObject),' with refcount ',hp^.refcount,' (will delete if refcount = 1, otherwise decrease to',hp^.refcount-1,')');
{$endif}
if hp^.refcount=1 then
{ we need to free the original exception object if its refcount=1
(means it was not acquired, only refcount increase by begin_catch) }
_Unwind_DeleteException(@hp^.unwind_exception)
else
dec(hp^.refcount);
_Unwind_RaiseException(@_ExceptObjectStack^.unwind_exception);
DoUnHandledException;
end;
procedure FPC_DummyPotentialRaise; nostackframe; assembler;
asm
end;