unit seh64; {$mode objfpc}{$H+} interface implementation uses Windows, ntapi, SysConst, SysUtils, hamt, ps4libdoc, sys_types, sys_kernel, ps4_program; function AddVectoredExceptionHandler(FirstHandler: DWORD; VectoredHandler: pointer): pointer; stdcall; external 'kernel32.dll' name 'AddVectoredExceptionHandler'; function RemoveVectoredExceptionHandler(VectoredHandlerHandle: pointer): ULONG; stdcall; external 'kernel32.dll' name 'RemoveVectoredExceptionHandler'; function GetModuleHandleEx(dwFlags: DWORD; lpModuleName: pointer; var hModule: THandle): BOOL; stdcall; external 'kernel32.dll' name 'GetModuleHandleExA'; // sysutils.GetModuleName(); const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2; GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 4; function GetModuleByAdr(adr:Pointer):THandle; var Flags:DWORD; begin Flags:=GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS or GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; Result:=0; GetModuleHandleEx(Flags,adr,Result); end; function RunErrorCode(const rec: TExceptionRecord): longint; begin { negative result means 'FPU reset required' } case rec.ExceptionCode of STATUS_INTEGER_DIVIDE_BY_ZERO: result := 200; { reDivByZero } STATUS_FLOAT_DIVIDE_BY_ZERO: result := -208; { !!reZeroDivide } STATUS_ARRAY_BOUNDS_EXCEEDED: result := 201; { reRangeError } STATUS_STACK_OVERFLOW: result := 202; { reStackOverflow } STATUS_FLOAT_OVERFLOW: result := -205; { reOverflow } STATUS_FLOAT_DENORMAL_OPERAND, STATUS_FLOAT_UNDERFLOW: result := -206; { reUnderflow } STATUS_FLOAT_INEXACT_RESULT, STATUS_FLOAT_INVALID_OPERATION, STATUS_FLOAT_STACK_CHECK: result := -207; { reInvalidOp } STATUS_INTEGER_OVERFLOW: result := 215; { reIntOverflow } STATUS_ILLEGAL_INSTRUCTION: result := -216; STATUS_ACCESS_VIOLATION: result := 216; { reAccessViolation } STATUS_CONTROL_C_EXIT: result := 217; { reControlBreak } STATUS_PRIVILEGED_INSTRUCTION: result := 218; { rePrivilegedInstruction } STATUS_FLOAT_MULTIPLE_TRAPS, STATUS_FLOAT_MULTIPLE_FAULTS: result := -255; { indicate FPU reset } else result := 255; { reExternalException } end; end; procedure TranslateMxcsr(mxcsr: longword; var code: longint); begin { we can return only one value, further one's are lost } { InvalidOp } if (mxcsr and 1)<>0 then code:=-207 { Denormal } else if (mxcsr and 2)<>0 then code:=-206 { !!reZeroDivide } else if (mxcsr and 4)<>0 then code:=-208 { reOverflow } else if (mxcsr and 8)<>0 then code:=-205 { Underflow } else if (mxcsr and 16)<>0 then code:=-206 { Precision } else if (mxcsr and 32)<>0 then code:=-207 else { this should not happen } code:=-255 end; function RunErrorCodex64(const rec: TExceptionRecord; const context: TContext): Longint; begin Result:=RunErrorCode(rec); if (Result=-255) then TranslateMxcsr(context.MxCsr,result); end; type _TElf_node=class(TElf_node) end; PTLQRec=^TLQRec; TLQRec=record pAddr:Pointer; ExceptAddr:Pointer; LastAdr:Pointer; LastNid:QWORD; end; procedure trav_proc(data,userdata:Pointer); var adr:Pointer; nid:QWORD; begin if (data=nil) then Exit; safe_move_ptr(PPointer(data)[0],adr); safe_move_ptr(PPointer(data)[1],nid); if (adr>=PTLQRec(userdata)^.pAddr) then if (adr<=PTLQRec(userdata)^.ExceptAddr) then if (adr>PTLQRec(userdata)^.LastAdr) then begin PTLQRec(userdata)^.LastAdr:=adr; PTLQRec(userdata)^.LastNid:=nid; end; end; function IsSubTrie64(n:PHAMTNode64):Boolean; inline; var BaseValue:PtrUint; begin safe_move_ptr(n^.BaseValue,BaseValue); Result:=(BaseValue and 1)<>0; end; function GetBitMapSize64(n:PHAMTNode64):QWORD; inline; var BitMapKey:QWORD; begin safe_move_ptr(n^.BitMapKey,BitMapKey); Result:=PopCnt(BitMapKey); Result:=Result and HAMT64.node_mask; if (Result=0) then Result:=HAMT64.node_size; end; function GetSubTrie64(n:PHAMTNode64):PHAMTNode64; inline; var BaseValue:PtrUint; begin safe_move_ptr(n^.BaseValue,BaseValue); PtrUint(Result):=(BaseValue or 1) xor 1; end; function GetValue64(n:PHAMTNode64):Pointer; inline; begin safe_move_ptr(n^.BaseValue,Result); end; procedure HAMT_traverse_trie64(node:PHAMTNode64;cb:Tfree_data_cb;userdata:Pointer); inline; type PStackNode=^TStackNode; TStackNode=packed record cnode,enode:PHAMTNode64; end; var curr:PStackNode; data:array[0..HAMT64.stack_max] of TStackNode; Size:QWORD; begin if IsSubTrie64(node) then begin curr:=@data; Size:=GetBitMapSize64(node); With curr^ do begin cnode:=GetSubTrie64(node); enode:=@cnode[Size]; end; repeat if (curr^.cnode>=curr^.enode) then begin if (curr=@data) then Break; Dec(curr); Inc(curr^.cnode); Continue; end; if IsSubTrie64(curr^.cnode) then begin node:=curr^.cnode; Inc(curr); Size:=GetBitMapSize64(node); With curr^ do begin cnode:=GetSubTrie64(node); enode:=@cnode[Size]; end; end else begin if (cb<>nil) then cb(GetValue64(curr^.cnode),userdata); Inc(curr^.cnode); end; until false; end else begin if (cb<>nil) then cb(GetValue64(node),userdata); end; end; function HAMT_traverse64(hamt:THAMT;cb:Tfree_data_cb;userdata:Pointer):Boolean; var i:Integer; node:PHAMTNode64; begin if (hamt=nil) then Exit(False); For i:=0 to HAMT64.root_mask do begin node:=@PHAMTNode64(hamt)[i]; HAMT_traverse_trie64(node,cb,userdata); end; Result:=True; end; Function FindLQProc(node:TElf_node;r:PTLQRec):Boolean; var i,l:SizeInt; lib:PLIBRARY; MapSymbol:THAMT; Import:Boolean; begin Result:=false; l:=Length(_TElf_node(node).aLibs); if (l<>0) then begin r^.LastAdr:=nil; r^.LastNid:=0; For i:=0 to l-1 do begin safe_move_ptr(_TElf_node(node).aLibs[i],lib); if (lib<>nil) then begin Import:=True; safe_move(lib^.Import,Import,SizeOf(Boolean)); if not Import then begin safe_move_ptr(lib^.MapSymbol,MapSymbol); HAMT_traverse64(MapSymbol,@trav_proc,r); end; end; end; Result:=(r^.LastAdr<>nil); end; end; Procedure WriteErr(Const s:shortstring); var num:DWORD; begin WriteConsole(GetStdHandle(STD_ERROR_HANDLE),@s[1],ord(s[0]),num,nil); end; function IntToStr(Value:longint): shortstring; begin System.Str(Value,result); end; function GetModuleName(Module:HMODULE): shortstring; var Len:DWORD; Buffer:array[0..MAX_PATH] of WideChar; P:PWideChar; begin Len:=GetModuleFileNameW(Module,@Buffer,MAX_PATH); P:=@Buffer[Len]; While (P<>@Buffer) do begin if (P^='\') then begin Inc(P); Break; end; Dec(P); end; Len:=@Buffer[Len]-P; Len:=UnicodeToUtf8(@Result[1],255,P,Len); Byte(Result[0]):=Len; end; Procedure DumpException(node:TElf_node;code:Longint;ExceptAddr:Pointer;ContextRecord:PCONTEXT); var Report:shortstring; pFileName:PChar; Mem:TMemChunk; top,rbp:PPointer; procedure print_adr; var r:TLQRec; begin Report:=' $'+hexstr(ExceptAddr); if (node<>nil) then begin Mem:=node.GetCodeFrame; if (Mem.pAddr<>nil) and (Mem.nSize<>0) then begin safe_move_ptr(node.pFileName,pFileName); Report:=Report+' offset $'+hexstr(ExceptAddr-Mem.pAddr,8)+' '+safe_str(pFileName); r.pAddr:=Mem.pAddr; r.ExceptAddr:=ExceptAddr; if FindLQProc(node,@r) then begin Report:=Report+':'+ps4libdoc.GetFunctName(r.LastNid)+'+$'+hexstr(ExceptAddr-r.LastAdr,8); end else if (node.GetEntryPoint<>nil) then begin Report:=Report+':EntryPoint+$'+hexstr(ExceptAddr-node.GetEntryPoint,8); end; end; end; Report:=Report+#13#10; WriteErr(Report); end; procedure print_adr2; begin Report:=' $'+hexstr(ExceptAddr); Report:=Report+' '+GetModuleName(GetModuleByAdr(ExceptAddr)); Report:=Report+#13#10; WriteErr(Report); end; begin Report:=''; Report:=Report+'Message: '+SysConst.GetRunError(abs(code)); Report:=Report+' ('+IntToStr(longint(code))+')'; Report:=Report+#13#10; WriteErr(Report); print_adr; if (node<>nil) then node.Release; top:=Pointer(ContextRecord^.Rbp); //if (top>StackBottom) and (topnil) then begin node:=ps4_app.AcqureFileByCodeAdr(ExceptAddr); if (node<>nil) then begin print_adr; node.Release; end else begin print_adr2; end; end; until (node=nil) {or (rbp>top) or (rbpGetModuleByAdr(@ProcessException)) then Exit(EXCEPTION_CONTINUE_SEARCH); code:=RunErrorCodex64(p^.ExceptionRecord^,p^.ContextRecord^); DumpException(node,code,p^.ExceptionRecord^.ExceptionAddress,P^.ContextRecord); halt; end; var VEHandler: pointer = Nil; procedure InstallExceptionHandler; begin VEHandler := AddVectoredExceptionHandler(1, @ProcessException); end; procedure UninstallExceptionHandler; begin if Assigned(VEHandler) then begin RemoveVectoredExceptionHandler(VEHandler); VEHandler := Nil; end; end; initialization InstallExceptionHandler; finalization UninstallExceptionHandler; end.