try..except..finally
块,但我无所适从,发现了代码中的“正常”路线以及except
和(可能)finally
块是什么。汇编看起来像这样:
782CFC 33 C0 xor eax, eax
782CFE 55 push ebp
782CFF 68 (782E37) push _FINALLY_A_0_782E37
782D04 64 FF 30 push dword ptr fs:[eax]
782D07 64 89 20 mov dword ptr fs:[eax], esp
_try_0_782D0A:
782D0A 8B D3 mov edx, ebx
782D0C 8B C6 mov eax, esi
782D0E E8 D1 F3 FF FF call ...unrelated...
782D13 8D 56 1C lea edx, [esi+1Ch]
.. lots of regular code here ..
.. ending with ..
782E17 8B 18 mov ebx, dword ptr [eax]
782E19 FF 53 20 call dword ptr [ebx+20h]
finally_1_782E1C:
782E1C 33 C0 xor eax, eax
782E1E 5A pop edx
782E1F 59 pop ecx
782E20 59 pop ecx
782E21 64 89 10 mov dword ptr fs:[eax], edx
782E24 68 (782E3E) push _end_1_782E3E
@block_L:
782E29 8D 45 F4 lea eax, [ebp + local_0C]
782E2C BA 02 00 00 00 mov edx, 2
782E31 E8 12 E3 F7 FF call System.@LStrArrayClr
782E36 C3 retn
_FINALLY_A_0_782E37:
782E37 E9 B4 E2 F7 FF jmp System.@HandleFinally
_FINALLY_B_0_782E3C:
782E3C EB EB jmp @block_L
; -------
_end_1_782E3E:
782E3E 5F pop edi
782E3F 5E pop esi
782E40 5B pop ebx
782E41 8B E5 mov esp, ebp
782E43 5D pop ebp
782E44 C3 retn
-这是我自己的反汇编程序的输出,但是我认为其中没有错误。标签已经自动命名,但是我仍然无法从一个块到下一个块遵循“逻辑”(如果有)。尤其是函数结尾之前的下半部分使我感到困惑。看完伊戈尔的回答:是的。请考虑以下流程图:左图,是对try / finally块进行特殊处理之前的原始图,右图是之后的。
到另一个作为链接,并且代码流在每个
try
停止。可以清楚地区分finally
(E-(F)-K)和retn
(G-H / I-J)结构。但是,推动返回地址和异常处理的其他“技巧”,可以克服这一点,如悬空的块N和O所示(它们从无处进入),还有一个单独的块“ M”, 在右侧,我将异常块的初始化与主代码分离(添加了一个新块B),并将finalize结构串联为一个新块(M),最终跳到AFTER_TRY(恰好是最后一个Exit块)。现在很明显,在序言之后,立即启动了
if
; 所有代码都结束于
if-else
块M,该块总是存在该代码在单个固定点上。#1 楼
Delphi通过使用Win32结构化异常处理程序(SEH)实现try
/ except
/ finally
。 SEH的基础知识在Matt Pietrek的经典文章中进行了解释,因此我将跳过仅与Delphi相关的详细信息。1。
try
入口try
块或保护退出时需要销毁的自动变量的块(例如字符串)的入口看起来像以下内容:xor eax, eax
push ebp
push offset SEH_HANDLER
push dword ptr fs:[eax]
mov fs:[eax], esp
这是设置SEH框架的典型方法。运行后,堆栈顶部将如下所示:
+-----------+
ESP+00 | next | <- fs:[0] points here
+-----------+
ESP+04 | handler |
+-----------+
ESP+08 | saved_ebp |
+-----------+
指向该结构的指针将传递给SEH处理程序。
2。
try
出口在
try
块的末尾,SEH框架被拆下: xor eax, eax
pop edx ; pop 'next' into edx
pop ecx ; pop handler
pop ecx ; pop saved_ebp
mov fs:[eax], edx ; move 'next' into fs:[0]
如果有
finally
处理程序或自动析构函数,然后像这样继续: push offset AFTER_TRY ; make it so the 'ret' will jump to AFTER_TRY
FINALLY_HANDLER:
<destruct automatic variables created in the try block>
<finally handler body>
ret ; jumps to AFTER_TRY
否则有一个简单的跳转:
jmp AFTER_TRY
3 。
finally
处理程序如果程序使用
finally
语句,或者如果编译器添加了try..finally
以保护自动变量,则SEH处理程序如下所示:SEH_HANDLER:
jmp _HandleFinally
jmp FINALLY_HANDLER
4。
except
处理程序如果程序使用
except
处理程序捕获所有异常,则代码看起来有些不同:SEH_HANDLER:
jmp _HandleAnyException
<handler code>
call _DoneExcept
5。
except on
处理程序如果程序使用
except on...
来匹配所捕获的异常,则编译器将生成一个表,其中包含一个或多个可能的异常类以及相应的处理程序:SEH_HANDLER:
jmp _HandleOnException
dd <numExceptions>
dd offset ExceptionClass1
dd offset OnException1_handler
dd offset ExceptionClass2
dd offset OnException2_handler
<...>
OnException1_handler:
<handler code>
call _DoneExcept
OnException2_handler:
<handler code>
call _DoneExcept
可能会有一些变化,但是我想我涵盖了大部分。
_HandleFinally
,_HandleAnyException
,_HandleOnException
,_DoneExcept
的源代码以及其他一些与异常相关的功能可以在VCL源中的system.pas
中找到。