Delphi 的 Exception 机制浅探(3)

2019-08-30 15:37

MOV EAX,[ESP+4] // EAX 是 TExceptionRecord 的指针 TEST [EAX].TExceptionRecord.ExceptionFlags,cUnwindInProgress JNE @@exit // ZF = 0 (结果是 !0 )

// 如果是 Delphi Exception,跳至 Delphi Exception 处理过程 CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException JE @@DelphiException

CLD

CALL _FpuInit // 重置浮点数处理器 (猜想)

// 映射操作系统异常至 Delphi 异常类

MOV EDX,ExceptClsProc // (ExceptClsProc 指向 GetExceptionClass) TEST EDX,EDX // 如果 ExceptClsProc 为 nil 则退出 JE @@exit CALL EDX

TEST EAX,EAX // 检查是否找到相应的 ExceptionClass JNE @@common // ExceptionClass 映射成功,跳到 common JMP @@exit // 映射不成功,退出

// 通过 TExceptionRecord 结构获得 Exception 对象指针 @@DelphiException:

MOV EAX,[EAX].TExceptionRecord.ExceptObject

MOV EAX,[EAX] { load vtable of exception object }

// (EAX 是当前 Exception 对象的 VMT ptr) @@common:

MOV EDX,[ESP+8] // EDX <- TExcFrame 的地址

// 保存 4 个寄存器 PUSH EBX PUSH ESI PUSH EDI PUSH EBP

MOV ECX,[EDX].TExcFrame.desc // ECX <- TExcFrame.desc 地址 MOV EBX,[ECX].TExcDesc.cnt // EBX <- 异常描述个数

LEA ESI,[ECX].TExcDesc.excTab { point ECX to exc descriptor table } // ESI <- 异常描述的地址

MOV EBP,EAX { load vtable of exception object } // EBP <- 当前异常对象的 VMT ptr

// 匹配当前异常和异常描述表中的异常(并递归匹配父类) @@innerLoop:

MOV EAX,[ESI].TExcDescEntry.vTable

// EAX <- 异常描述表中的 VMT 指针的 指针(注意,二级指针) TEST EAX,EAX { catch all clause? } // 是 0 则处理所有异常

JE @@doHandler { yes: go execute handler }

MOV EDI,EBP { load vtable of exception object } JMP @@haveVMT

@@vtLoop:

MOV EDI,[EDI] // EDX <- 当前异常对象的 VMT @@haveVMT:

MOV EAX,[EAX] // EAX <- 异常描述表中的 VMT

CMP EAX,EDI // 如果 VMT 相同,跳到 @@doHandler JE @@doHandler

// 为什么要比较以下项?

MOV ECX,[EAX].vmtInstanceSize // 比较对象实例大小,如果不同 CMP ECX,[EDI].vmtInstanceSize // 则跳到 @@parent JNE @@parent

MOV EAX,[EAX].vmtClassName // 比较 ClassName,如果不同 MOV EDX,[EDI].vmtClassName // 则跳到 @@parent,相同则 // 跳至 @@doHandler XOR ECX,ECX MOV CL,[EAX] CMP CL,[EDX] JNE @@parent

INC EAX INC EDX

CALL _AStrCmp JE @@doHandler

@@parent:

MOV EDI,[EDI].vmtParent { load vtable of parent } MOV EAX,[ESI].TExcDescEntry.vTable // 获取当前异常的父类 VTM TEST EDI,EDI // 如果父类 VMT 不等于 0, JNE @@vtLoop // 则重新比较 VMT 是否相同

ADD ESI,8 // 步进到异常描述表的一下项 DEC EBX // 减少计数

JNZ @@innerLoop // 重新进行异常匹配

POP EBP // 恢复堆栈,退出 POP EDI POP ESI POP EBX JMP @@exit

@@doHandler:

MOV EAX,[ESP+4+4*4] // (4*4 是上面 PUSH 的四个寄存器) // 如果是 Delphi 异常,则从 TExceptionRecord 结构中取得异常对象 CMP [EAX].TExceptionRecord.ExceptionCode,cDelphiException MOV EDX,[EAX].TExceptionRecord.ExceptObject MOV ECX,[EAX].TExceptionRecord.ExceptAddr JE @@haveObject

// 否则通过 ExceptObjProc 取得异常对象 CALL ExceptObjProc

// 以下是 Delphi IDE 调试的集成处理 (未阅)

MOV EDX,[ESP+12+4*4] // EDX <- CONTEXT ptr

CALL NotifyNonDelphiException // 向 Delphi Debuger 发送异常通知 CMP BYTE PTR JITEnable,0 JBE @@NoJIT

CMP BYTE PTR DebugHook,0

JA @@noJIT { Do not JIT if debugging }

// 调用 UnhandledExceptionFilter API LEA ECX,[ESP+4]

PUSH EAX // 保存当前 EAX 值 PUSH ECX

CALL UnhandledExceptionFilter

CMP EAX,EXCEPTION_CONTINUE_SEARCH

POP EAX // 恢复当前 EAX 值 JE @@exit

@@noJIT:

MOV EDX,EAX // EDX <- 异常对象指针 MOV EAX,[ESP+4+4*4] // ECX <- 异常发生时的地址 MOV ECX,[EAX].TExceptionRecord.ExceptionAddress JMP @@GoUnwind

@@haveObject:

// 有 Delphi Exception 对象时进行的 IDE Debug 设置 CMP BYTE PTR JITEnable,1 JBE @@GoUnwind

CMP BYTE PTR DebugHook,0 JA @@GoUnwind PUSH EAX

LEA EAX,[ESP+8] PUSH EDX PUSH ECX PUSH EAX

CALL UnhandledExceptionFilter

CMP EAX,EXCEPTION_CONTINUE_SEARCH POP ECX POP EDX POP EAX JE @@exit

@@GoUnwind:

XOR EBX,EBX

MOV EBX,FS:[EBX]

PUSH EBX { Save topmost frame } PUSH EAX { Save exception record } PUSH EDX { Save exception object } PUSH ECX { Save exception address }

MOV EDX,[ESP+8+8*4]

OR [EAX].TExceptionRecord.ExceptionFlags,cUnwinding

PUSH ESI { Save handler entry }

// ESI <- TExcDescEntry.vTable 指针 // 用于下段 EBX 获得 handler

PUSH 0 // 可能是参数个数

PUSH EAX // EAX <- TExceptionRecord 指针 PUSH offset @@returnAddress // 可能是 RtlUnwind 返回地址 PUSH EDX // EDX <- TExcFrame 指针 CALL RtlUnwindProc // 调用 RtlUnwind API

@@returnAddress:

POP EBX { Restore handler entry }

MOV EDI,[ESP+8+8*4] // EDI <- TExcFrame 指针

{ Make the RaiseList entry on the stack }

CALL SysInit.@GetTLS // 在堆栈中构造 RaiseList PUSH [EAX].RaiseListPtr

MOV [EAX].RaiseListPtr,ESP

MOV EBP,[EDI].TExcFrame.hEBP // 恢复 try 语句时的 EBP MOV [EDI].TExcFrame.desc,offset @@exceptFinally

// 设置异常处理地址?? // 向 IDE 发通知

MOV EAX,[ESP].TRaiseFrame.ExceptObject CALL NotifyOnExcept

JMP [EBX].TExcDescEntry.handler // 调用异常处理过程

@@exceptFinally:

JMP _HandleFinally

@@destroyExcept:

{ we come here if an exception handler has thrown yet another exception } { we need to destroy the exception object and pop the raise list. }

CALL SysInit.@GetTLS

MOV ECX,[EAX].RaiseListPtr

MOV EDX,[ECX].TRaiseFrame.NextRaise MOV [EAX].RaiseListPtr,EDX

MOV EAX,[ECX].TRaiseFrame.ExceptObject JMP TObject.Free @@exit:

MOV EAX,1 end; {

The following NotifyXXXX routines are used to \ as a signaling mechanism to an interested debugger. If the debugger sets the DebugHook flag to 1 or 2, then all exception processing is tracked by raising these special exceptions. The debugger *MUST* respond to the debug event with DBG_CONTINUE so that normal processing will occur. }

DebugHook: Byte platform = 0; { 1 to notify debugger of non-Delphi exceptions

>1 to notify debugger of exception unwinding } JITEnable: Byte platform = 0; { 1 to call UnhandledExceptionFilter if the exception

is not a Pascal exception.

>1 to call UnhandledExceptionFilter for all exceptions }

function MapException(P: PExceptionRecord): TRuntimeError; begin

case P.ExceptionCode of

STATUS_INTEGER_DIVIDE_BY_ZERO: Result := System.reDivByZero; STATUS_ARRAY_BOUNDS_EXCEEDED: Result := System.reRangeError; STATUS_INTEGER_OVERFLOW:

Result := System.reIntOverflow; STATUS_FLOAT_INEXACT_RESULT, STATUS_FLOAT_INVALID_OPERATION,

STATUS_FLOAT_STACK_CHECK:

Result := System.reInvalidOp; STATUS_FLOAT_DIVIDE_BY_ZERO:

Result := System.reZeroDivide; STATUS_FLOAT_OVERFLOW:

Result := System.reOverflow; STATUS_FLOAT_UNDERFLOW,

STATUS_FLOAT_DENORMAL_OPERAND: Result := System.reUnderflow; STATUS_ACCESS_VIOLATION:

Result := System.reAccessViolation; STATUS_PRIVILEGED_INSTRUCTION:

Result := System.rePrivInstruction; STATUS_CONTROL_C_EXIT:

Result := System.reControlBreak; STATUS_STACK_OVERFLOW:

Result := System.reStackOverflow; else

Result := System.reExternalException; end; end;

// 注意前三个异常类型没有异常数组定义

TRuntimeError = (reNone, reOutOfMemory, reInvalidPtr, reDivByZero, reRangeError, reIntOverflow, reInvalidOp, reZeroDivide, reOverflow, reUnderflow, reInvalidCast, reAccessViolation, rePrivInstruction, reControlBreak, reStackOverflow, { reVar* used in Variants.pas } reVarTypeCast, reVarInvalidOp,

reVarDispatch, reVarArrayCreate, reVarNotArray, reVarArrayBounds, reAssertionFailed,

reExternalException, { not used here; in SysUtils } reIntfCastError, reSafeCallError );

{$NODEFINE TRuntimeError}

const

ExceptMap: array[Ord(reDivByZero)..Ord(High(TRuntimeError))] of TExceptRec = ( (EClass: EDivByZero; EIdent: SDivByZero), (EClass: ERangeError; EIdent: SRangeError), (EClass: EIntOverflow; EIdent: SIntOverflow), (EClass: EInvalidOp; EIdent: SInvalidOp), (EClass: EZeroDivide; EIdent: SZeroDivide), (EClass: EOverflow; EIdent: SOverflow), (EClass: EUnderflow; EIdent: SUnderflow), (EClass: EInvalidCast; EIdent: SInvalidCast),

(EClass: EAccessViolation; EIdent: SAccessViolationNoArg), (EClass: EPrivilege; EIdent: SPrivilege), (EClass: EControlC; EIdent: SControlC),

(EClass: EStackOverflow; EIdent: SStackOverflow), (EClass: EVariantError; EIdent: SInvalidVarCast), (EClass: EVariantError; EIdent: SInvalidVarOp), (EClass: EVariantError; EIdent: SDispatchError), (EClass: EVariantError; EIdent: SVarArrayCreate), (EClass: EVariantError; EIdent: SVarInvalid), (EClass: EVariantError; EIdent: SVarArrayBounds),

(EClass: EAssertionFailed; EIdent: SAssertionFailed),

(EClass: EExternalException; EIdent: SExternalException), (EClass: EIntfCastError; EIdent: SIntfCastError),


Delphi 的 Exception 机制浅探(3).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:滇池调研报告

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: