mirror of https://github.com/red-prig/fpPS4.git
497 lines
13 KiB
Plaintext
497 lines
13 KiB
Plaintext
unit md_exception;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
implementation
|
|
|
|
uses
|
|
Windows,
|
|
sysutils,
|
|
ntapi,
|
|
machdep,
|
|
md_context,
|
|
md_proc,
|
|
sys_bootparam,
|
|
kern_exit,
|
|
kern_thr,
|
|
subr_backtrace,
|
|
trap,
|
|
signal,
|
|
ucontext,
|
|
vm,
|
|
vm_map,
|
|
vm_pmap,
|
|
vm_pmap_prot,
|
|
vm_tracking_map,
|
|
kern_proc,
|
|
kern_jit_ctx,
|
|
kern_jit_dynamic;
|
|
|
|
const
|
|
EXCEPTION_SET_THREADNAME = $406D1388;
|
|
DBG_PRINTEXCEPTION_C = $40010006;
|
|
DBG_PRINTEXCEPTION_WIDE_C = $4001000A;
|
|
|
|
type
|
|
LPTOP_LEVEL_EXCEPTION_FILTER=function(excep:PEXCEPTION_POINTERS):longint; stdcall;
|
|
|
|
function SetUnhandledExceptionFilter(lpTopLevelExceptionFilter:Pointer):Pointer; stdcall; external kernel32 name 'SetUnhandledExceptionFilter';
|
|
function AddVectoredExceptionHandler(FirstHandler:DWORD;VectoredHandler:Pointer):Pointer; stdcall; external kernel32 name 'AddVectoredExceptionHandler';
|
|
function RemoveVectoredExceptionHandler(VectoredHandlerHandle:Pointer): ULONG; stdcall; external kernel32 name 'RemoveVectoredExceptionHandler';
|
|
function GetModuleHandleEx(dwFlags:DWORD;lpModuleName:Pointer;var hModule:THandle):BOOL; stdcall; external kernel32 name 'GetModuleHandleExA';
|
|
function IsDebuggerPresent: BOOL; stdcall; external kernel32;
|
|
|
|
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 RunErrorCodeSEH(const rec:TExceptionRecord;const context:TContext):Longint;
|
|
begin
|
|
Result:=RunErrorCode(rec);
|
|
if (Result=-255) then
|
|
begin
|
|
TranslateMxcsr(context.MxCsr,result);
|
|
end;
|
|
end;
|
|
|
|
const
|
|
FPC_EXCEPTION_CODE=$E0465043;
|
|
FPC_SET_EH_HANDLER=$E0465044;
|
|
|
|
function translate_pageflt_err(v:QWORD):Byte; inline;
|
|
begin
|
|
Result:=VM_PROT_NONE;
|
|
case v of
|
|
0:Result:=VM_PROT_READ;
|
|
1:Result:=VM_PROT_WRITE;
|
|
8:Result:=VM_PROT_EXECUTE;
|
|
end;
|
|
end;
|
|
|
|
function get_pageflt_err(p:PExceptionPointers):Byte; inline;
|
|
begin
|
|
Result:=translate_pageflt_err(p^.ExceptionRecord^.ExceptionInformation[0]);
|
|
end;
|
|
|
|
function get_pageflt_addr(p:PExceptionPointers):QWORD; inline;
|
|
begin
|
|
Result:=p^.ExceptionRecord^.ExceptionInformation[1];
|
|
end;
|
|
|
|
function get_exception(p:PExceptionPointers):DWORD; inline;
|
|
begin
|
|
Result:=p^.ExceptionRecord^.ExceptionCode;
|
|
end;
|
|
|
|
procedure jit_save_to_sys_save(td:p_kthread); external;
|
|
procedure sys_save_to_jit_save(td:p_kthread); external;
|
|
|
|
function ProcessException3(td:p_kthread;p:PExceptionPointers):longint; SysV_ABI_CDecl;
|
|
var
|
|
tf_addr:QWORD;
|
|
rv:Integer;
|
|
is_jit:Boolean;
|
|
begin
|
|
Result:=1;
|
|
if (td=nil) then Exit;
|
|
|
|
thread_suspend_all(td);
|
|
|
|
tf_addr:=0;
|
|
is_jit:=exist_jit_host(p^.ExceptionRecord^.ExceptionAddress,@tf_addr);
|
|
|
|
Writeln('cr_rip:0x',HexStr(p^.ContextRecord^.Rip,16));
|
|
Writeln('cr_rsp:0x',HexStr(p^.ContextRecord^.Rsp,16));
|
|
Writeln('cr_rbp:0x',HexStr(p^.ContextRecord^.Rbp,16));
|
|
|
|
Writeln('jitcall:0x',HexStr(td^.td_teb^.jitcall));
|
|
print_frame(stderr,td^.td_teb^.jitcall);
|
|
|
|
Writeln('tf_rip:0x',HexStr(tf_addr,16));
|
|
|
|
_get_frame(p^.ContextRecord,@td^.td_frame,{@td^.td_fpstate}nil,is_jit);
|
|
|
|
if (is_jit) then
|
|
begin
|
|
jit_save_to_sys_save(td);
|
|
td^.td_frame.tf_rip:=tf_addr;
|
|
end;
|
|
|
|
Writeln('tf_rsp:0x',HexStr(td^.td_frame.tf_rsp,16));
|
|
Writeln('tf_rbp:0x',HexStr(td^.td_frame.tf_rbp,16));
|
|
|
|
print_backtrace_td(stderr);
|
|
|
|
td^.td_frame.tf_trapno:=0;
|
|
|
|
rv:=-1;
|
|
|
|
case get_exception(p) of
|
|
STATUS_ACCESS_VIOLATION:
|
|
begin
|
|
tf_addr:=get_pageflt_addr(p);
|
|
|
|
Writeln('tf_addr:0x',HexStr(tf_addr,16));
|
|
|
|
//Writeln(HexStr(p^.ContextRecord^.Rip,16));
|
|
//Writeln(HexStr(Get_pc_addr));
|
|
|
|
//_get_frame(p^.ContextRecord,@td^.td_frame,{@td^.td_fpstate}nil);
|
|
|
|
td^.td_frame.tf_trapno:=T_PAGEFLT;
|
|
td^.td_frame.tf_err :=get_pageflt_err(p);
|
|
td^.td_frame.tf_addr :=tf_addr;
|
|
|
|
rv:=trap.trap(@td^.td_frame,is_jit);
|
|
end;
|
|
|
|
else;
|
|
end;
|
|
|
|
if (is_jit) then
|
|
begin
|
|
sys_save_to_jit_save(td);
|
|
end;
|
|
|
|
if (rv=0) then
|
|
begin
|
|
//_set_frame(p^.ContextRecord,@td^.td_frame,{@td^.td_fpstate}nil);
|
|
Result:=0;
|
|
end;
|
|
|
|
thread_resume_all(td);
|
|
|
|
//simple ret
|
|
//td^.pcb_flags:=td^.pcb_flags and (not PCB_FULL_IRET);
|
|
|
|
//td^.td_frame:=backup;
|
|
end;
|
|
|
|
type
|
|
TExceptObjProc=function(code: Longint; const rec: TExceptionRecord): Pointer; { Exception }
|
|
TExceptClsProc=function(code: Longint): Pointer; { ExceptClass }
|
|
|
|
function IS_SYSTEM_STACK(td:p_kthread;rsp:qword):Boolean; inline;
|
|
begin
|
|
Result:=(rsp<=QWORD(td^.td_kstack.stack)) and (rsp>(QWORD(td^.td_kstack.sttop)));
|
|
end;
|
|
|
|
function IsDefaultExceptions(ExceptionCode:DWORD):Boolean; inline;
|
|
begin
|
|
case ExceptionCode of
|
|
EXCEPTION_ACCESS_VIOLATION,
|
|
EXCEPTION_BREAKPOINT,
|
|
EXCEPTION_DATATYPE_MISALIGNMENT,
|
|
EXCEPTION_SINGLE_STEP,
|
|
EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
|
|
EXCEPTION_FLT_DENORMAL_OPERAND,
|
|
EXCEPTION_FLT_DIVIDE_BY_ZERO,
|
|
EXCEPTION_FLT_INEXACT_RESULT,
|
|
EXCEPTION_FLT_INVALID_OPERATION,
|
|
EXCEPTION_FLT_OVERFLOW,
|
|
EXCEPTION_FLT_STACK_CHECK,
|
|
EXCEPTION_FLT_UNDERFLOW,
|
|
EXCEPTION_INT_DIVIDE_BY_ZERO,
|
|
EXCEPTION_INT_OVERFLOW,
|
|
EXCEPTION_INVALID_HANDLE,
|
|
EXCEPTION_PRIV_INSTRUCTION,
|
|
EXCEPTION_NONCONTINUABLE_EXCEPTION,
|
|
EXCEPTION_STACK_OVERFLOW,
|
|
EXCEPTION_INVALID_DISPOSITION,
|
|
EXCEPTION_IN_PAGE_ERROR,
|
|
EXCEPTION_ILLEGAL_INSTRUCTION,
|
|
EXCEPTION_POSSIBLE_DEADLOCK:
|
|
Result:=True;
|
|
else
|
|
Result:=False;
|
|
end;
|
|
end;
|
|
|
|
function ProcessException(p:PExceptionPointers):longint; stdcall;
|
|
var
|
|
instr:t_instruction_info;
|
|
begin
|
|
Result:=EXCEPTION_CONTINUE_SEARCH;
|
|
if (curkthread=nil) then Exit;
|
|
|
|
//Writeln('ProcessException:0x',HexStr(get_exception(p),8));
|
|
|
|
case get_exception(p) of
|
|
FPC_EXCEPTION_CODE :Exit;
|
|
FPC_SET_EH_HANDLER :Exit(EXCEPTION_CONTINUE_EXECUTION);
|
|
EXCEPTION_BREAKPOINT :Exit;
|
|
EXCEPTION_SINGLE_STEP :Exit;
|
|
EXCEPTION_SET_THREADNAME :Exit;
|
|
DBG_PRINTEXCEPTION_C :Exit(EXCEPTION_CONTINUE_EXECUTION);
|
|
DBG_PRINTEXCEPTION_WIDE_C:Exit(EXCEPTION_CONTINUE_EXECUTION); //RenderDoc issuse
|
|
|
|
STATUS_ACCESS_VIOLATION:
|
|
begin
|
|
instr:=get_instruction_info(Pointer(p^.ContextRecord^.Rip));
|
|
|
|
if pmap_danger_zone(vm_map_t(p_proc.p_vmspace)^.pmap,
|
|
get_pageflt_addr(p),
|
|
instr.mema_size
|
|
) then
|
|
begin
|
|
Exit(EXCEPTION_CONTINUE_EXECUTION);
|
|
end;
|
|
|
|
case get_pageflt_err(p) of
|
|
VM_PROT_READ:
|
|
begin
|
|
if ((ppmap_get_prot(get_pageflt_addr(p),instr.mema_size) and VM_PROT_READ)<>0) then
|
|
begin
|
|
Writeln(stderr,'Unhandled VM_PROT_READ');
|
|
end;
|
|
end;
|
|
VM_PROT_WRITE:
|
|
begin
|
|
if ((ppmap_get_prot(get_pageflt_addr(p),instr.mema_size) and VM_PROT_WRITE)<>0) then
|
|
begin
|
|
//Writeln('TRACK_WRITE:',HexStr(get_pageflt_addr(p),11));
|
|
|
|
//trigger and restore
|
|
vm_map_track_trigger(p_proc.p_vmspace,
|
|
get_pageflt_addr(p),
|
|
get_pageflt_addr(p)+instr.mema_size,
|
|
nil,
|
|
M_CPU_WRITE);
|
|
//
|
|
Exit(EXCEPTION_CONTINUE_EXECUTION);
|
|
end;
|
|
end;
|
|
else;
|
|
end;
|
|
|
|
//if not usermode
|
|
if (curkthread^.pcb_onfault<>nil) then
|
|
begin
|
|
p^.ContextRecord^.Rip:=QWORD(curkthread^.pcb_onfault);
|
|
Exit(EXCEPTION_CONTINUE_EXECUTION);
|
|
end;
|
|
|
|
end;
|
|
|
|
else
|
|
if not IsDefaultExceptions(get_exception(p)) then
|
|
begin
|
|
Writeln(stderr,'Unknow ExceptionCode:0x',HexStr(get_exception(p),8));
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
Result:=ProcessException3(curkthread,p);
|
|
|
|
if (Result=0) then
|
|
begin
|
|
Result:=EXCEPTION_CONTINUE_EXECUTION;
|
|
end else
|
|
begin
|
|
Writeln(stderr,'ExceptionCode:0x',HexStr(get_exception(p),8));
|
|
Result:=EXCEPTION_CONTINUE_SEARCH;
|
|
end;
|
|
end;
|
|
|
|
function UnhandledException(p:PExceptionPointers):longint; stdcall;
|
|
var
|
|
rec:PExceptionRecord;
|
|
code: Longint;
|
|
ExObj:Exception;
|
|
begin
|
|
Result:=EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
{
|
|
case get_exception(p) of
|
|
DBG_PRINTEXCEPTION_C :;
|
|
DBG_PRINTEXCEPTION_WIDE_C:;
|
|
else
|
|
Writeln('UnhandledException:0x',HexStr(get_exception(p),8));
|
|
end;
|
|
}
|
|
|
|
case get_exception(p) of
|
|
FPC_EXCEPTION_CODE :Exit;
|
|
FPC_SET_EH_HANDLER :Exit(EXCEPTION_CONTINUE_EXECUTION);
|
|
EXCEPTION_BREAKPOINT :Exit;
|
|
EXCEPTION_SET_THREADNAME :Exit;
|
|
DBG_PRINTEXCEPTION_C :Exit(EXCEPTION_CONTINUE_EXECUTION);
|
|
DBG_PRINTEXCEPTION_WIDE_C:Exit(EXCEPTION_CONTINUE_EXECUTION); //RenderDoc issuse
|
|
end;
|
|
|
|
if (curkthread=nil) then Exit;
|
|
|
|
rec:=p^.ExceptionRecord;
|
|
|
|
code:=abs(RunErrorCodeSEH(rec^,p^.ContextRecord^));
|
|
|
|
ExObj:=nil;
|
|
|
|
if (rec^.ExceptionCode=FPC_EXCEPTION_CODE) then
|
|
begin
|
|
ExObj:=Exception(rec^.ExceptionInformation[1])
|
|
end else
|
|
if Assigned(ExceptObjProc) then
|
|
begin
|
|
ExObj:=Exception(TExceptObjProc(ExceptObjProc)(abs(code),rec^));
|
|
end;
|
|
|
|
if (curkthread<>nil) then
|
|
begin
|
|
Writeln('curkthread^.td_name:',curkthread^.td_name);
|
|
end;
|
|
|
|
if (ExObj=nil) then
|
|
begin
|
|
Writeln(stderr,'Runtime error ',code,' at $',hexstr(rec^.ExceptionAddress));
|
|
end else
|
|
begin
|
|
Writeln(stderr,'An unhandled exception occurred at $',hexstr(rec^.ExceptionAddress));
|
|
Writeln(stderr,ExObj.ClassName,': ',ExObj.Message);
|
|
end;
|
|
|
|
print_backtrace(stderr,
|
|
Pointer(p^.ContextRecord^.Rip),
|
|
Pointer(p^.ContextRecord^.Rbp),0);
|
|
|
|
ErrorCode:=word(code);
|
|
md_halt(code);
|
|
end;
|
|
|
|
procedure ExceptionDispatcher(p:PExceptionPointers); stdcall;
|
|
begin
|
|
if (ProcessException(p)=EXCEPTION_CONTINUE_EXECUTION) then
|
|
begin
|
|
NtContinue(p^.ContextRecord,False);
|
|
end else
|
|
begin
|
|
UnhandledException(p);
|
|
end;
|
|
end;
|
|
|
|
function _get_msg(Const Msg:Shortstring):Shortstring; inline;
|
|
begin
|
|
Result:=Msg;
|
|
if (Result='') then
|
|
begin
|
|
Result:='Assertion failed';
|
|
end;
|
|
end;
|
|
|
|
var
|
|
prev_assert:TAssertErrorProc;
|
|
|
|
Procedure _Assert(Const Msg,FName:Shortstring;LineNo:Longint;ErrorAddr:Pointer);
|
|
begin
|
|
if (curkthread=nil) then
|
|
begin
|
|
prev_assert(Msg,FName,LineNo,ErrorAddr);
|
|
Exit;
|
|
end;
|
|
|
|
Writeln(stderr,_get_msg(Msg),' (',FName,', line ',LineNo,').');
|
|
p_host_ipc.error(_get_msg(Msg)+' ('+FName+', line '+IntToStr(LineNo)+').');
|
|
|
|
print_backtrace(stderr,Get_pc_addr,get_frame,2);
|
|
|
|
if IsDebuggerPresent then
|
|
asm
|
|
int3
|
|
end;
|
|
|
|
kern_exit.exit1(217);
|
|
end;
|
|
|
|
var
|
|
VEHandler:Pointer=nil;
|
|
UEHandler:Pointer=nil;
|
|
V2Handler:Pointer=nil;
|
|
|
|
type
|
|
PEH_HANDLER_INFO=^EH_HANDLER_INFO;
|
|
EH_HANDLER_INFO=record
|
|
ptr:Pointer;
|
|
end;
|
|
|
|
procedure RaiseException(dwExceptionCode:DWORD;
|
|
dwExceptionFlags:DWORD;
|
|
nNumberOfArguments:DWORD;
|
|
lpArguments:Pointer); external 'kernel32';
|
|
|
|
procedure InstallExceptionHandler;
|
|
var
|
|
eh:EH_HANDLER_INFO;
|
|
begin
|
|
prev_assert:=AssertErrorProc;
|
|
AssertErrorProc:=@_Assert;
|
|
UEHandler:=SetUnhandledExceptionFilter(@UnhandledException);
|
|
VEHandler:=AddVectoredExceptionHandler(1,@ProcessException);
|
|
V2Handler:=AddVectoredExceptionHandler(0,@UnhandledException);
|
|
eh.ptr:=@ExceptionDispatcher;
|
|
//RaiseException(FPC_SET_EH_HANDLER,0,2,@eh);
|
|
end;
|
|
|
|
procedure UninstallExceptionHandler;
|
|
begin
|
|
//SetUnhandledExceptionFilter(UEHandler);
|
|
RemoveVectoredExceptionHandler(VEHandler);
|
|
RemoveVectoredExceptionHandler(V2Handler);
|
|
end;
|
|
|
|
initialization
|
|
InstallExceptionHandler;
|
|
|
|
finalization
|
|
UninstallExceptionHandler;
|
|
|
|
end.
|