您的位置:首页 > 其它

关于SEH局部展开的一点思考

2009-12-26 20:11 260 查看
最近读了Matt Pietrek的《A Crash Course on the Depths of Win32 Structured Exception Handling》,有如醍醐灌顶,尤其是编译器级的SEH,如果是SEH的初学者,推荐读一下这篇文章,不要先去看《windows核心编程》的异常处理章节,那样只会越搞越乱。如果把SEH帧链和_SCOPETABLE链这两条链搞清了基本就OK了,至于细节部分值得深究的地方还是很多的。但在这篇文章中一个没有具体提到的问题就是SEH的局部展开,也就是_local_unwind函数,由于这是一个与编译器相关的函数,所以对研究系统的SEH没有障碍。后来我在张银奎的《软件调试》补编的异常编译章节中找到了一段该函数的伪码,摘录如下:



这段伪码中最主要的是12行开始的那个while循环,第18行判断pCurScopeEntry->lpfnFilter是否为NULL,原因是在VC中try_finally块和try_except块一样,都对应一个scopetable中的_SCOPETABLE表项,区别在于try_finally对应表项的lpfnFilter值为NULL,也就是说没有过滤表达式。因此以上代码可以看出,局部展开的任务主要是搜索_SCOPETABLE链中的try_finally节点并执行finally处理代码。为了验证这个说法,我写了一小段代码

#include <windows.h>
#include <stdio.h>

void ViewScopeTable(DWORD* pSEHFrame)
{
DWORD* Scopetable = (DWORD*)*(pSEHFrame+2);
DWORD TryLevel = *(pSEHFrame+3);
DWORD* pCurTable = (DWORD*)(Scopetable + TryLevel*3);
while( TRUE )
{
printf("prevTryLevel:%x/nlpfnfilter:%x/nlpfnHandler:%x/n/n",
*pCurTable,
*(pCurTable+1),
*(pCurTable+2)
);
if( ( TryLevel = *(pCurTable) ) == 0xFFFFFFFF)
break;
pCurTable = (DWORD*)(Scopetable + TryLevel*3);
}
}

void ShowSEHFrame()
{
DWORD pSEHHead;
_asm mov eax, fs:[0]
_asm mov pSEHHead, eax
printf("fs:[0]:%x/n/n", pSEHHead);
DWORD* pHead = (DWORD*)pSEHHead;
while(TRUE)
{
printf("Frame addr:%x/nprev:%x/nhandler:%x/nscopetable:%x/ntrylevel:%x/n/n",
Head,
*pHead,
*(pHead+1),
*(pHead+2),
*(pHead+3)
);
ViewScopeTable(pHead);
if( *pHead == 0xFFFFFFFF )
break;
pHead = (DWORD*)*pHead;
}
}

void func1()
{
_try{
_try{
_try{
ShowSEHFrame();
}_except( EXCEPTION_CONTINUE_SEARCH ) //except块
{

}
}_finally //有意设置成finally块

{

}
}_except( EXCEPTION_CONTINUE_SEARCH ) //except块
{

}
}

int main()
{
func1();
}

执行结果如下:



可见try_finally块和try_except一样被插入到_SCOPETABLE链中,只是lpfnFilter为0。

在Matt Pietrek的_except_handler3的伪码中有这样一句:

if ( pRegistrationFrame->scopetable[trylevel].lpfnFilter )

.......

即判断_SCOPETABLE项的lpfnFilter,若是try_finally块则直接略过该节点,查询下一节点。而在上述的_local_unwind2伪码中,若是try_except节点则略过,查询下一节点。由此可以总结出:

try_except节点在发生异常时搜索异常处理块时被使用,而try_finally节点则用于局部展开的时候。

由于VC中的异常handler都指向_except_handler3,不是一个异常处理块对应一个异常处理帧,故没有办法在全局展开时直接用_except_handler3完成清理工作,所以有了try_finally结构,这样看来这个结构的出现也是在情理之中的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: