mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-31 17:31:42 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			439 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| {
 | |
|     This file is part of the Free Pascal run time library.
 | |
|     Copyright (c) 2013 by Free Pascal development team
 | |
| 
 | |
|     Support for 32-bit Windows exception handling
 | |
| 
 | |
|     See the file COPYING.FPC, included in this distribution,
 | |
|     for details about the copyright.
 | |
| 
 | |
|     This program 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.
 | |
| 
 | |
|  **********************************************************************}
 | |
| 
 | |
| const
 | |
|   EXCEPTION_UNWIND = EXCEPTION_UNWINDING or EXCEPTION_EXIT_UNWIND;
 | |
| 
 | |
| type
 | |
|   TDispatcherContext=record
 | |
|   end;
 | |
| 
 | |
|   PSEHFrame=^TSEHFrame;
 | |
|   TSEHFrame=record
 | |
|     Next: PSEHFrame;
 | |
|     Addr: Pointer;
 | |
|     _EBP: PtrUint;
 | |
|     HandlerArg: Pointer;
 | |
|   end;
 | |
| 
 | |
| 
 | |
| procedure RtlUnwind(
 | |
|   TargetFrame: Pointer;
 | |
|   TargetIp: Pointer;
 | |
|   ExceptionRecord: PExceptionRecord;
 | |
|   ReturnValue: Pointer);
 | |
|   stdcall; external 'kernel32.dll' name 'RtlUnwind';
 | |
| 
 | |
| {$ifdef FPC_USE_WIN32_SEH}
 | |
| function NullHandler(
 | |
|   var rec: TExceptionRecord;
 | |
|   var frame: TSEHFrame;
 | |
|   var context: TContext;
 | |
|   var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl;
 | |
| begin
 | |
|   result:=ExceptionContinueSearch;
 | |
| end;
 | |
| 
 | |
| 
 | |
| function GetBacktrace(Context: TContext; StartingFrame: Pointer; out Frames: PPointer): Longint;
 | |
| var
 | |
|   FrameCount: Longint;
 | |
|   oldebp: Cardinal;
 | |
| begin
 | |
|   Frames:=AllocMem(RaiseMaxFrameCount*sizeof(pointer));
 | |
|   FrameCount:=0;
 | |
|   repeat
 | |
|     oldebp:=context.ebp;
 | |
|     { get_caller_stackinfo checks against StackTop on i386 }
 | |
|     get_caller_stackinfo(pointer(Context.Ebp),codepointer(Context.Eip));
 | |
|     if (Context.ebp<=oldebp) or (FrameCount>=RaiseMaxFrameCount) then
 | |
|       break;
 | |
|     if (Pointer(Context.ebp)>StartingFrame) or (FrameCount>0) then
 | |
|     begin
 | |
|       Frames[FrameCount]:=Pointer(Context.eip);
 | |
|       Inc(FrameCount);
 | |
|     end;
 | |
|   until False;
 | |
|   result:=FrameCount;
 | |
| end;
 | |
| 
 | |
| 
 | |
| function RunErrorCode386(const rec: TExceptionRecord; const context: TContext): Longint;
 | |
| begin
 | |
|   result:=RunErrorCode(rec);
 | |
|   { deal with SSE exceptions }
 | |
|   if (result=-255) and ((context.ContextFlags and CONTEXT_EXTENDED_REGISTERS)<>0) then
 | |
|     TranslateMxcsr(PLongword(@context.ExtendedRegisters[24])^,result);
 | |
| end;
 | |
| 
 | |
| 
 | |
| procedure fpc_RaiseException(Obj: TObject; AnAddr,AFrame: Pointer); [public,alias: 'FPC_RAISEEXCEPTION']; compilerproc;
 | |
| var
 | |
|   ctx: TContext;
 | |
|   args: array[0..4] of PtrUint;
 | |
| begin
 | |
|   ctx.Ebp:=Cardinal(AFrame);
 | |
|   ctx.Eip:=Cardinal(AnAddr);
 | |
|   args[0]:=PtrUint(AnAddr);
 | |
|   args[1]:=PtrUint(Obj);
 | |
|   args[2]:=GetBacktrace(ctx,AFrame,PPointer(args[3]));
 | |
|   args[4]:=PtrUInt(AFrame);
 | |
|   RaiseException(FPC_EXCEPTION_CODE,EXCEPTION_NONCONTINUABLE,5,@args[0]);
 | |
| end;
 | |
| 
 | |
| 
 | |
| procedure fpc_reraise; [public,alias:'FPC_RERAISE']; compilerproc;
 | |
| var
 | |
|   hp: PExceptObject;
 | |
| begin
 | |
|   hp:=ExceptObjectStack;
 | |
|   ExceptObjectStack:=hp^.next;
 | |
|   { Since we're going to 'reraise' the original OS exception (or, more exactly, pretend
 | |
|     it wasn't handled), we must revert action of CommonHandler. }
 | |
|   if TExceptionRecord(hp^.ExceptRec^).ExceptionCode<>FPC_EXCEPTION_CODE then
 | |
|   begin
 | |
|     if assigned(hp^.frames) then
 | |
|       freemem(hp^.frames);
 | |
|     if hp^.refcount=0 then
 | |
|       hp^.FObject.Free;
 | |
|   end;
 | |
|   TSEHFrame(hp^.SEHFrame^).Addr:=@NullHandler;
 | |
|   longjmp(hp^.ReraiseBuf,1);
 | |
| end;
 | |
| 
 | |
| 
 | |
| { Parameters are dummy and used to force "ret 16" at the end;
 | |
|   this removes a TSEHFrame record from the stack }
 | |
| procedure _fpc_leave(a1,a2,a3,a4:pointer); [public,alias:'_FPC_leave']; stdcall; compilerproc; assembler; nostackframe;
 | |
| asm
 | |
|      movl   4(%esp),%eax
 | |
|      movl   %eax,%fs:(0)
 | |
|      movl   %ebp,%eax
 | |
|      call   16(%esp)
 | |
| end;
 | |
| 
 | |
| 
 | |
| function PopObjectStack: PExceptObject;
 | |
| var
 | |
|   hp: PExceptObject;
 | |
| begin
 | |
|   hp:=ExceptObjectStack;
 | |
|   if hp=nil then
 | |
|     halt(255)
 | |
|   else
 | |
|   begin
 | |
|     ExceptObjectStack:=hp^.next;
 | |
|     if assigned(hp^.frames) then
 | |
|       freemem(hp^.frames);
 | |
|   end;
 | |
|   result:=hp;
 | |
| end;
 | |
| 
 | |
| 
 | |
| function __FPC_finally_handler(
 | |
|   var rec: TExceptionRecord;
 | |
|   var frame: TSEHFrame;
 | |
|   var context: TContext;
 | |
|   var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_finally_handler'];
 | |
| begin
 | |
|   if (rec.ExceptionFlags and EXCEPTION_UNWIND)<>0 then
 | |
|   begin
 | |
|     { prevent endless loop if things go bad in user routine }
 | |
|     frame.Addr:=@NullHandler;
 | |
|     TUnwindProc(frame.HandlerArg)(frame._EBP);
 | |
|   end;
 | |
|   result:=ExceptionContinueSearch;
 | |
| end;
 | |
| 
 | |
| 
 | |
| function __FPC_default_handler(
 | |
|   var rec: TExceptionRecord;
 | |
|   var frame: TSEHFrame;
 | |
|   var context: TContext;
 | |
|   var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_DEFAULT_HANDLER'];
 | |
| var
 | |
|   code: longint;
 | |
|   Obj: TObject;
 | |
|   Adr: Pointer;
 | |
|   Frames: PCodePointer;
 | |
|   FrameCount: Longint;
 | |
| begin
 | |
|   if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
 | |
|   begin
 | |
|     { Athlon prefetch bug? }
 | |
|     if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and is_prefetch(pointer(context.eip)) then
 | |
|     begin
 | |
|       result:=ExceptionContinueExecution;
 | |
|       exit;
 | |
|     end
 | |
|     else if (rec.ExceptionCode=STATUS_ILLEGAL_INSTRUCTION) and sse_check then
 | |
|     begin
 | |
|       os_supports_sse:=False;
 | |
|       { skip the offending movaps %xmm7,%xmm6 instruction }
 | |
|       inc(context.eip,3);
 | |
|       result:=ExceptionContinueExecution;
 | |
|       exit;
 | |
|     end;
 | |
| 
 | |
|     RtlUnwind(@frame,nil,@rec,nil);
 | |
|     asm
 | |
|       { RtlUnwind destroys nonvolatile registers, this assembler block prevents
 | |
|         regvar optimizations. }
 | |
|     end ['ebx','esi','edi'];
 | |
| 
 | |
|     if rec.ExceptionCode<>FPC_EXCEPTION_CODE then
 | |
|     begin
 | |
|       code:=RunErrorCode386(rec,context);
 | |
|       if code<0 then
 | |
|         SysResetFPU;
 | |
|       code:=abs(code);
 | |
|       Adr:=rec.ExceptionAddress;
 | |
|       Obj:=nil;
 | |
|       if Assigned(ExceptObjProc) then
 | |
|         Obj:=TObject(TExceptObjProc(ExceptObjProc)(code,rec));
 | |
|       if Obj=nil then
 | |
|       begin
 | |
|         { This works because RtlUnwind does not actually unwind the stack on i386
 | |
|           (and only on i386) }
 | |
|         errorcode:=word(code);
 | |
|         errorbase:=pointer(context.Ebp);
 | |
|         erroraddr:=pointer(context.Eip);
 | |
|         Halt(code);
 | |
|       end;
 | |
|       FrameCount:=GetBacktrace(context,nil,Frames);
 | |
|     end
 | |
|     else
 | |
|     begin
 | |
|       Obj:=TObject(rec.ExceptionInformation[1]);
 | |
|       Adr:=rec.ExceptionInformation[0];
 | |
|       Frames:=PCodePointer(rec.ExceptionInformation[3]);
 | |
|       FrameCount:=ptruint(rec.ExceptionInformation[2]);
 | |
|       code:=217;
 | |
|     end;
 | |
|     if Assigned(ExceptProc) then
 | |
|     begin
 | |
|       ExceptProc(Obj,Adr,FrameCount,Frames);
 | |
|       Halt(217);
 | |
|     end
 | |
|     else
 | |
|     begin
 | |
|       errorcode:=word(code);
 | |
|       errorbase:=pointer(rec.ExceptionInformation[4]);
 | |
|       erroraddr:=pointer(Adr);
 | |
|       Halt(code);
 | |
|     end;
 | |
|   end;
 | |
|   result:=ExceptionContinueExecution;
 | |
| end;
 | |
| 
 | |
| 
 | |
| function NestedHandler(
 | |
|   var rec: TExceptionRecord;
 | |
|   var frame: TSEHFrame;
 | |
|   var context: TContext;
 | |
|   var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl;
 | |
| var
 | |
|   hp: PExceptObject;
 | |
| begin
 | |
|   if (rec.ExceptionFlags and EXCEPTION_UNWIND)<>0 then
 | |
|   begin
 | |
|     hp:=PopObjectStack;
 | |
|     if hp^.refcount=0 then
 | |
|       hp^.FObject.Free;
 | |
|   end;
 | |
|   result:=ExceptionContinueSearch;
 | |
| end;
 | |
| 
 | |
| function __FPC_except_safecall(
 | |
|   var rec: TExceptionRecord;
 | |
|   var frame: TSEHFrame;
 | |
|   var context: TContext;
 | |
|   var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; forward;
 | |
| 
 | |
| procedure CommonHandler(
 | |
|   var rec: TExceptionRecord;
 | |
|   var frame: TSEHFrame;
 | |
|   var context: TContext;
 | |
|   TargetAddr: Pointer);
 | |
| var
 | |
|   Exc: TExceptObject;
 | |
|   code: Longint;
 | |
| begin
 | |
|   if rec.ExceptionCode<>FPC_EXCEPTION_CODE then
 | |
|   begin
 | |
|     Exc.FObject:=nil;
 | |
|     code:=RunErrorCode386(rec,context);
 | |
|     if Assigned(ExceptObjProc) then
 | |
|       Exc.FObject:=TObject(TExceptObjProc(ExceptObjProc)(abs(code),rec));
 | |
|     if (Exc.FObject=nil) and (frame.Addr<>Pointer(@__FPC_except_safecall)) then
 | |
|       Exit;
 | |
|     Exc.Addr:=rec.ExceptionAddress;
 | |
|     Exc.FrameCount:=GetBacktrace(context,nil,Exc.Frames);
 | |
|     if code<0 then
 | |
|       SysResetFPU;
 | |
|   end
 | |
|   else
 | |
|   begin
 | |
|     Exc.Addr:=rec.ExceptionInformation[0];
 | |
|     Exc.FObject:=TObject(rec.ExceptionInformation[1]);
 | |
|     Exc.Framecount:=Longint(PtrUInt(rec.ExceptionInformation[2]));
 | |
|     Exc.Frames:=rec.ExceptionInformation[3];
 | |
|   end;
 | |
| 
 | |
|   RtlUnwind(@frame,nil,@rec,nil);
 | |
| 
 | |
|   Exc.Refcount:=0;
 | |
|   Exc.SEHFrame:=@frame;
 | |
|   Exc.ExceptRec:=@rec;
 | |
|   { link to ExceptObjectStack }
 | |
|   Exc.Next:=ExceptObjectStack;
 | |
|   ExceptObjectStack:=@Exc;
 | |
| 
 | |
|   frame.Addr:=@NestedHandler;
 | |
|   if setjmp(Exc.ReraiseBuf)=0 then
 | |
|   asm
 | |
|       movl   Exc.FObject,%eax
 | |
|       movl   frame,%edx
 | |
|       movl   TargetAddr,%ecx              // load ebp-based var before changing ebp
 | |
|       movl   TSEHFrame._EBP(%edx),%ebp
 | |
|       jmpl   *%ecx
 | |
|   end;
 | |
|   { control comes here if exception is re-raised }
 | |
|   rec.ExceptionFlags:=rec.ExceptionFlags and (not EXCEPTION_UNWINDING);
 | |
| end;
 | |
| 
 | |
| 
 | |
| function __FPC_except_handler(
 | |
|   var rec: TExceptionRecord;
 | |
|   var frame: TSEHFrame;
 | |
|   var context: TContext;
 | |
|   var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_except_handler'];
 | |
| begin
 | |
|   if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
 | |
|   begin
 | |
|     { Athlon prefetch bug? }
 | |
|     if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and
 | |
|       is_prefetch(pointer(Context.eip)) then
 | |
|     begin
 | |
|       result:=ExceptionContinueExecution;
 | |
|       exit;
 | |
|     end;
 | |
|     CommonHandler(rec,frame,context,frame.HandlerArg);
 | |
|   end;
 | |
|   result:=ExceptionContinueSearch;
 | |
| end;
 | |
| 
 | |
| { Safecall procedures are expected to handle OS exceptions even if they cannot be
 | |
|   converted to language exceptions. This is indicated by distinct handler address. }
 | |
| function __FPC_except_safecall(
 | |
|   var rec: TExceptionRecord;
 | |
|   var frame: TSEHFrame;
 | |
|   var context: TContext;
 | |
|   var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_except_safecall']; assembler; nostackframe;
 | |
| asm
 | |
|     jmp  __FPC_except_handler
 | |
| end;
 | |
| 
 | |
| 
 | |
| function __FPC_on_handler(
 | |
|   var rec: TExceptionRecord;
 | |
|   var frame: TSEHFrame;
 | |
|   var context: TContext;
 | |
|   var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_on_handler'];
 | |
| var
 | |
|   TargetAddr: Pointer;
 | |
| begin
 | |
|   if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
 | |
|   begin
 | |
|     { Athlon prefetch bug? }
 | |
|     if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and
 | |
|       is_prefetch(pointer(Context.eip)) then
 | |
|     begin
 | |
|       result:=ExceptionContinueExecution;
 | |
|       exit;
 | |
|     end;
 | |
|     { Are we going to catch it? }
 | |
|     TargetAddr:=FilterException(rec,0,PtrUInt(frame.HandlerArg),abs(RunErrorCode386(rec,context)));
 | |
|     if assigned(TargetAddr) then
 | |
|       CommonHandler(rec,frame,context,TargetAddr);
 | |
|   end;
 | |
|   result:=ExceptionContinueSearch;
 | |
| end;
 | |
| 
 | |
| 
 | |
| function fpc_safecallhandler(obj: TObject): HResult; [public,alias:'FPC_SAFECALLHANDLER']; compilerproc;
 | |
| var
 | |
|   hp: PExceptObject;
 | |
|   exc: TObject;
 | |
| begin
 | |
|   hp:=PopObjectStack;
 | |
|   exc:=hp^.FObject;
 | |
|   if Assigned(obj) and Assigned(exc) then
 | |
|     result:=obj.SafeCallException(exc,hp^.Addr)
 | |
|   else
 | |
|     result:=E_UNEXPECTED;
 | |
|   if hp^.refcount=0 then
 | |
|     exc.Free;
 | |
|   asm
 | |
|        movl   %ebp,%edx                             // save current frame
 | |
|        movl   hp,%ecx
 | |
|        movl   TExceptObject.SEHFrame(%ecx),%ecx     // target ESP minus sizeof(TSEHFrame)
 | |
|        movl   (%ecx),%eax
 | |
|        movl   %eax,%fs:(0)                          // restore SEH chain
 | |
|        movl   __RESULT,%eax
 | |
|        movl   TSEHFrame._EBP(%ecx),%ebp             // restore EBP
 | |
|        leal   16(%ecx),%esp                         // restore ESP past the SEH frame
 | |
|        jmpl   4(%edx)                               // jump to caller
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| 
 | |
| procedure fpc_doneexception;[public,alias:'FPC_DONEEXCEPTION'] compilerproc;
 | |
| var
 | |
|   hp: PExceptObject;
 | |
| begin
 | |
|   hp:=PopObjectStack;
 | |
|   if hp^.refcount=0 then
 | |
|     hp^.FObject.Free;
 | |
|   erroraddr:=nil;
 | |
|     asm
 | |
|         movl   %ebp,%edx                             // save current frame
 | |
|         movl   hp,%eax
 | |
|         movl   TExceptObject.SEHFrame(%eax),%eax     // target ESP minus sizeof(TSEHFrame)
 | |
|         movl   (%eax),%ecx
 | |
|         movl   %ecx,%fs:(0)                          // restore SEH chain
 | |
|         movl   TSEHFrame._EBP(%eax),%ebp             // restore EBP
 | |
|         leal   16(%eax),%esp                         // restore ESP, removing SEH frame
 | |
|         jmpl   4(%edx)                               // jump to caller
 | |
|     end;
 | |
| end;
 | |
| 
 | |
| 
 | |
| function main_wrapper(arg: Pointer; proc: Pointer): ptrint; assembler; nostackframe;
 | |
| asm
 | |
|     xorl    %ecx,%ecx
 | |
|     pushl   $__FPC_default_handler
 | |
|     pushl   %fs:(%ecx)
 | |
|     movl    %esp,%fs:(%ecx)
 | |
|     call    *%edx
 | |
|     xorl    %ecx,%ecx
 | |
|     popl    %edx
 | |
|     movl    %edx,%fs:(%ecx)
 | |
|     popl    %ecx
 | |
| end;
 | |
| 
 | |
| {$endif FPC_USE_WIN32_SEH}
 | |
| 
 | 
