FPPS4/sys/md/md_exception.pas

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); SysV_ABI_CDecl; external;
procedure sys_save_to_jit_save(td:p_kthread); SysV_ABI_CDecl; 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.