mirror of https://github.com/red-prig/fpPS4.git
290 lines
7.2 KiB
Plaintext
290 lines
7.2 KiB
Plaintext
unit md_exception;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
implementation
|
|
|
|
uses
|
|
Windows,
|
|
sysutils,
|
|
ntapi,
|
|
machdep,
|
|
md_context,
|
|
md_proc,
|
|
kern_thr,
|
|
trap,
|
|
signal,
|
|
ucontext,
|
|
vm,
|
|
vmparam;
|
|
|
|
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 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;
|
|
|
|
function translate_pageflt_err(v:QWORD):QWORD; inline;
|
|
begin
|
|
Result:=VM_PROT_NONE;
|
|
case v of
|
|
0:Result:=VM_PROT_READ;
|
|
2:Result:=VM_PROT_WRITE;
|
|
8:Result:=VM_PROT_EXECUTE;
|
|
end;
|
|
end;
|
|
|
|
function ProcessException3(p:PExceptionPointers):longint; SysV_ABI_CDecl;
|
|
var
|
|
ExceptionCode:DWORD;
|
|
td:p_kthread;
|
|
tf_addr:QWORD;
|
|
backup:trapframe;
|
|
rv:Integer;
|
|
begin
|
|
Result:=1;
|
|
td:=curkthread;
|
|
|
|
//Context backup to return correctly
|
|
backup:=td^.td_frame;
|
|
td^.td_frame.tf_trapno:=0;
|
|
|
|
ExceptionCode:=p^.ExceptionRecord^.ExceptionCode;
|
|
|
|
rv:=-1;
|
|
|
|
case ExceptionCode of
|
|
STATUS_ACCESS_VIOLATION:
|
|
begin
|
|
tf_addr:=p^.ExceptionRecord^.ExceptionInformation[1];
|
|
|
|
//Writeln(HexStr(p^.ContextRecord^.Rip,16));
|
|
//Writeln(HexStr(Get_pc_addr));
|
|
|
|
_get_frame(p^.ContextRecord,@td^.td_frame,@td^.td_fpstate);
|
|
|
|
td^.td_frame.tf_trapno:=T_PAGEFLT;
|
|
td^.td_frame.tf_err :=translate_pageflt_err(p^.ExceptionRecord^.ExceptionInformation[0]);
|
|
td^.td_frame.tf_addr :=tf_addr;
|
|
|
|
rv:=trap.trap(@td^.td_frame);
|
|
end;
|
|
|
|
else;
|
|
end;
|
|
|
|
if (rv=0) then
|
|
begin
|
|
_set_frame(p^.ContextRecord,@td^.td_frame,@td^.td_fpstate);
|
|
Result:=0;
|
|
end;
|
|
|
|
//simple ret
|
|
td^.pcb_flags:=td^.pcb_flags and (not PCB_FULL_IRET);
|
|
|
|
td^.td_frame:=backup;
|
|
end;
|
|
|
|
function ProcessException2(p:PExceptionPointers):longint; assembler; nostackframe; SysV_ABI_CDecl;
|
|
asm
|
|
//prolog (debugger)
|
|
pushq %rbp
|
|
movq %rsp,%rbp
|
|
|
|
movq ProcessException3,%rax
|
|
call fast_syscall
|
|
|
|
//epilog (debugger)
|
|
popq %rbp
|
|
end;
|
|
|
|
type
|
|
TExceptObjProc=function(code: Longint; const rec: TExceptionRecord): Pointer; { Exception }
|
|
TExceptClsProc=function(code: Longint): Pointer; { ExceptClass }
|
|
|
|
function ProcessException(p:PExceptionPointers):longint; stdcall;
|
|
begin
|
|
Result:=EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
//Writeln('rsp:0x',HexStr(p^.ContextRecord^.Rsp,16));
|
|
//Writeln('rsp:0x',HexStr(get_frame));
|
|
|
|
case p^.ExceptionRecord^.ExceptionCode of
|
|
FPC_EXCEPTION_CODE :Exit;
|
|
EXCEPTION_BREAKPOINT :Exit;
|
|
EXCEPTION_SET_THREADNAME:Exit;
|
|
end;
|
|
|
|
if (curkthread=nil) then Exit;
|
|
|
|
//It looks like there is a small stack inside the exception, so you need to switch the context
|
|
Result:=ProcessException2(p);
|
|
|
|
if (Result=0) then
|
|
begin
|
|
Result:=EXCEPTION_CONTINUE_EXECUTION;
|
|
end else
|
|
begin
|
|
Result:=EXCEPTION_CONTINUE_SEARCH;
|
|
end;
|
|
end;
|
|
|
|
function UnhandledException(p:PExceptionPointers):longint; stdcall;
|
|
var
|
|
td:p_kthread;
|
|
rec:PExceptionRecord;
|
|
code: Longint;
|
|
ExObj:Exception;
|
|
begin
|
|
Result:=EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
case p^.ExceptionRecord^.ExceptionCode of
|
|
FPC_EXCEPTION_CODE :Exit;
|
|
EXCEPTION_BREAKPOINT :Exit;
|
|
EXCEPTION_SET_THREADNAME:Exit;
|
|
end;
|
|
|
|
td:=curkthread;
|
|
if (td=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 (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 _Assert(Const Msg,FName:Shortstring;LineNo:Longint;ErrorAddr:Pointer);
|
|
begin
|
|
If Length(msg)=0 then
|
|
write(stderr,'Assertion failed')
|
|
else
|
|
write(stderr,msg);
|
|
Writeln(stderr,' (',FName,', line ',LineNo,').');
|
|
print_backtrace(stderr,Get_pc_addr,get_frame,0);
|
|
asm
|
|
int3
|
|
end;
|
|
md_halt(217);
|
|
end;
|
|
|
|
var
|
|
VEHandler:Pointer=nil;
|
|
UEHandler:Pointer=nil;
|
|
V2Handler:Pointer=nil;
|
|
|
|
procedure InstallExceptionHandler;
|
|
begin
|
|
AssertErrorProc:=@_Assert;
|
|
UEHandler:=SetUnhandledExceptionFilter(@UnhandledException);
|
|
VEHandler:=AddVectoredExceptionHandler(1,@ProcessException);
|
|
V2Handler:=AddVectoredExceptionHandler(0,@UnhandledException);
|
|
end;
|
|
|
|
procedure UninstallExceptionHandler;
|
|
begin
|
|
SetUnhandledExceptionFilter(UEHandler);
|
|
RemoveVectoredExceptionHandler(VEHandler);
|
|
RemoveVectoredExceptionHandler(V2Handler);
|
|
end;
|
|
|
|
initialization
|
|
InstallExceptionHandler;
|
|
|
|
finalization
|
|
UninstallExceptionHandler;
|
|
|
|
end.
|