!C++异常的实现_ZH.doc_第1页
!C++异常的实现_ZH.doc_第2页
!C++异常的实现_ZH.doc_第3页
!C++异常的实现_ZH.doc_第4页
!C++异常的实现_ZH.doc_第5页
已阅读5页,还剩2页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

/htm_data/87/0407/72938.htmlhow a c+ compiler implements exception handling有人应该知道有这么一个同名的文章介绍vc 6怎么实现c+异常的文章.在这里小弟不才.借用下这个名字,介绍 2003怎么实现这个技术. 先说这些代码的由来,首先呢,你得写一个c+得程序,然后选择静态链接,然后用ida 反汇编就ok,用 vc 本身作调试器就ok,softice这种家伙,也没有用得必要了. 当然你也许能发现eh.obj这样的文件,其实代码就在那几个obj里面. 要想继续看下去,你得了解windows得seh技术,得熟练掌握32位汇编,因为这里并没有什么源代码给你看,全部是反汇编得结果.自然对c+异常的语法,以及c+异常规范,你必须要有足够的了解才行. 因为我ida反汇编以后加上注释的那几个idb文件已经不见了,只是剩下了自己用汇编写的代码,所以这里出现的代码都是nasm格式的汇编代码,同时搭配使用c32.mac这个文件,这个文件我有修改增加其宏的功能,名字都很简单明了,希望那些宏不会对大家的阅读产生障碍. 然后呢,要说说一些基本的概念. 你应该要知道_stdcall,_cdecl,_thiscall这些的调用与被调用规则,应该要知道在一个c函数里面哪些寄存器的值是要保护的,哪些寄存器是不用保护的.应该要明白mov fs:0,eax是在干什么,你要明白windows的seh是怎么完成的.有了足够的准备以后往下看吧. seh是windows平台上的一种异常处理方式,通过预先安装一个处理函数,到发生异常的时候跳转到处理函数里面的方式完成异常处理,这里不要把_try,_except等同到seh,他们只是一种简单的处理模型,并不代表了seh本身,seh本身只是在异常发生的时候跳转到一个函数而已. 当我们写下throw 0;这样的语句的时候,编译器会为我们产生下面的这些代码 mov ebp-10,0 lea eax,ebp-10 push _TI1H push eax call _CxxThrowException8 上面这些代码调用了一个叫_CxxThrowException8的函数 看它的名字就知道它应该是一个使用stdcall调用法则,有两个参数的函数.它的原型 void _stdcall _CxxThrowException(void *pObject,_s_ThrowInfo const* pThrowInfo) 第一个参数就是throw 后面的那个object的地址,第二个参数是一个叫throwInfo的结构,它的定义如下(能在vc的watch窗口里面看到,你输入(_s_ThrowInfo*)0就能看到它的成员定义了) ; throw info struc _s_ThrowInfo .attributes : resd 1 ; properites .pmfnUnwind : resd 1 ; destructor for thrown object .pForwardCompat : resd 1 ; compat handler address .pCatchableTypeArray : resd 1 ; catchable type array endstruc 这个结构定义是使用nasm的语法,resd表示它是dword大小的,后面的1表示一个dword. 上面看到的那个_TI1H就是这个结构. 关于它的几个成员.我后面都有注释,唯一要说的就是最后一个成员,它表示这个throw出来的object它能转换成哪些其他的object以便被catch语句捕获,你应该要知道catch语句捕获的原则. _CxxThrowException函数是一个外部的函数,它应该由c+的lib提供,它在设置好一些工作以后应该调用os提供的RaiseException的函数,然后os取得控制权,交到应用程序安装的seh handler上面, 那么vc为我们安装的那个函数是什么呢? 它会是一个诸如_ehhandler$_main:这样的函数 它简单的mov eax,_TI1H+48h 然后jmp到一个叫_CxxFrameHandler的函数 这个函数原型如下 int _CxxFrameHandler(struct EHExceptionRecord * pExcept,struct EHRegistrationNode * pRN, ; struct _CONTEXT * pContext,void * pDC) 这个函数使用什么样子的调用法则是无关紧要的,因为它要么不返回,要么os会另外的设置esp,所以你使用cdcel还是stdcall都没有关系,唯一要保证的就是它的名字,它也应该要由c+的lib提供(当然我假定你是这个lib的提供者). 它使用的几个参数,首先是eax,它是一个_s_FuncInfo的指针,然后是一个EEHExceptionRecord的指针,它其实跟seh使用的ExceptionRecord差不多,只是vc在它里面加入了些其他的成员,剩下的参数也同它一样,你应该要知道这些大都是seh传递给你的参数,你也应该要知道,他们会在你调用RaiseException的时候设置好. 先给出这些结构的定义. ;exception record struc EHExceptionRecord .ExceptionCode : resd 1 ; exception code .ExceptionFlags : resd 1 ; exception flags .ExceptionRecord : resd 1 ; exception record .ExceptionAddress : resd 1 ; exception address .NumberParameters : resd 1 ; number of param .magicNumber : resd 1 ; magic number .pExceptionObject : resd 1 ; exception object pointer .pThrowInfo : resd 1 ; throw info pointer endstruc ;function info struc _s_FuncInfo .magicNumber : resd 1 ; magic number .maxState : resd 1 ; max state in the function .pUnwindMap : resd 1 ; unwind function list .nTryBlocks : resd 1 ; how many try blocks .pTryBlockMap : resd 1 ; try block list endstruc ;registration node struc EHRegistrationNode .pNext : resd 1 ; prev exception registration node .frameHandler : resd 1 ; this frame handler .state : resd 1 ; current state endstruc 上面的几个结构呢.首先要说的是ExceptionRecord,你应该要知道它里面由一个Parameters的数组,vc使用了里面的3个成员,分别赋予了不同的意思.这个很容易理解. 然后是RegisterationNode,vc多加入了一个叫state的域,如果你对_try,_except很熟悉的话,你会会心一笑,他其实跟tryLevel差不多,用来表示抛出这个异常时,程序运行到什么地方了. 至于function info,他表示的是你当前处于的这个函数的信息,他收集了发生异常的时候必须要完成的操作所需要的信息. 我希望你看到这里的时候还没有头晕,我选择先把这些东西一古老全抖出来然后再解释他的机制的方式,希望你还挺得住. 到这里,我要描述具体得实现思路了. vc实现得c+异常是一种编译时行为,并不能归结到运行时行为,他通过编译器收集足够的信息来完成异常处理. 对于每个函数,vc收集一份他里面的try语句的位置信息,catch语句能catch住的object type信息,以及每个可能发生异常的情况下要完成的object destructor调用的信息,这些信息共同的构成了上面的function info. 同时vc在throw语句执行的时候,抓取到(也是在编译时完成的)那个object的地址,收集到这个obj能转换的类型(catch判断的时候有用),然后移交控制权,同时vc还在运行时候记录当前执行到的语句的位置,这个数据会用来得到当前语句属于哪个try,当前这个地方要调用哪些destructor. 看个具体的例子 假设我有定义A,B两个class code void test() / 这里设置当前位置为-1,表示不处于任何一个try语句里面 try / 这里设置当前位置0,表示进入了第一个try里面 A testA; / 这里设置当前位置1,表示如果在这里出现了问题,要调用类A的destructor MayThrowException1; try / 这里设置当前位置2,表示进入第二个try B testB; / 这里设置为3,要调用B的destructor MayThrowException2; /这里会插入 B类的ddestructor调用,同时设置当前位置为1,表示退出了第二个try catch(.) / 这里也会有修改到当前位置的操作,因为在catch里面也会定义c+对象 / 这里同样插入A的destructor调用,设置当前位置-1 catch(.) /code 这个例子程序非常的简单,大家可以体会下这个当前位置的值的变换情况,他不仅仅是要表示try的位置,也要表示你必须要调用的destructor的信息.你也许要问这个当前位置保存到什么地方的?就在那个EHRegisterationNode里面,state就是用来保存这个的,你应该要知道他的位置就是ebp-4,这个部分你要熟悉seh才能明白为什么是ebp-4 假想这么一个情况,上面的函数发生了一个c+异常,进入到异常处理函数里面,函数通过查看EHRegisterationNode-state的值,得到了当前程序运行的位置,先通过function info得到了当前所在的try block,这个是通过function info里面的try block list完成的, 他其实是一个数组,每个成员又是一个结构定义如下 ;try block entry struc _s_TryBlockMapEntry .tryLow : resd 1 ; begin state .tryHi : resd 1 ; end state .catchHi : resd 1 ; catch end state .nCatches : resd 1 ; how many catches .pHandlerArray : resd 1 ; cathe block entry list endstruc tryHi跟tryLow定义了try语句所跨越的位置,比如上面的第一个try 的范围就是0到3,第二个是2到3,在list的array里面排列的顺序是从里到外的,所以,经过一些比较以后就能确定当前位于哪个try里面 知道了这个以后,从上面的_s_TryBlockMapEntry结构里面能得到跟这个try关联的catch语句信息,他们又构成了一个数组,通过比较那个throw object的类型跟catch语句的类型就能找到合适的catch语句跳转进去,如果没有找到,那么就应该回到上一个tryblock继续查找,这个任务的实现是靠编译器合理的布局try block map entry数组完成的,实际的工作只是需要遍历这个数组一一判断state是否落在tryLow还是tryHi里面,如果不是就继续下一项,如果是就查找catch语句,执行. 对于catch语句,vc也生成了一份信息,定义如下 ; catch block entry struc _s_HandlerType .adjectives : resd 1 ; properites .pType : resd 1 ; type_info pointer .dispCatchObj : resd 1 ; offset from ebp .addressOfHandler : resd 1 ; handler address endstruc 最主要的就是那个pType成员,他是一个type_info指针,你应该要知道这个是个什么东西,addressOfHandler就是catch语句开始的指针. 现在我找到了异常发生的时候的程序运行位置,也知道了我程序写下的catch语句的信息,这个时候我就要开始一一比较我所throw出来的obj跟catch语句能catch的是否一致了. 这个部分比较复杂,涉及c+很多的语法 比如一个const的不能被非const的引用所catch等等 这些的判断就要借助_s_HandlerType.adjectives属性来判断了,vc收集好catch语句要求的obj的信息,const,volatile等等属性,你应该要知道这些属性并不能通过type_info来获取,你也应该知道非const的指针是能被const指针catch的等等语法.如果你明白了这些就能明白adjectives用来干什么了. catch语句这边的信息已经足够了. 那throw出来的object呢,他能转换成什么?这个就落在了_s_ThrowInfo.pCatchableTypeArray上面了,是一个结构定义如下 ; catchable type array struc _s_CatchableTypeArray .nCatchableTypes : resd 1 ; how many types .arrayOfCatchableTypes : resd 1 ; types list endstruc 最后一个成员是一个数组,成员定义如下 ; catchable type struc _s_CatchableType .properites : resd 1 ; properites .pType : resd 1 ; type_info pointer .mdisp : resd 1 ; PMD .pdisp : resd 1 .vdisp : resd 1 .sizeOrOffset : resd 1 ; size or offset .copyFunction : resd 1 ; copy constructor endstruc 看到这个properites你就应该想到他是和catch的adjectives配合使用的,yes,you are right! 同样你也看到了type_info,中间的几个变量你也许要觉得诡异了,干什么用的,这个先放放,先只要知道这个是用来作this指针调整的就够了,然后是一个sizeOrOffset,标记object的大小,然后是一个copy constructor,这个,呃,你应该知道catch(A a)这样的语句是有一个copy动作的. 然后要提到的就是local object的distructor调用,这个是通过_s_FuncInfo.pUnwindMap完成的,他指向一个unwindmap的数组,定义如下 ; unwind map struc _s_UnwindMapEntry .toState : resd 1 ; linked field .action : resd 1 ; unwind function endstruc vc 把每个要调用destructor的local object都收集起来,放到一个list里面,然后用toState链接起来,通过当前state作为下标索引这个list,然后调用action函数(这个action并不是destructor本身,因为action是一个无参数的,而destructor是要有this参数的,在action里面会有mov ecx,xxx,call xxx),然后用toState成员再作下标索引,直到toState变成-

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论