Subterranean IL: Exception handling 2
Posted
by Simon Cooper
on Simple Talk
See other posts from Simple Talk
or by Simon Cooper
Published on Mon, 17 Jan 2011 15:14:00 GMT
Indexed on
2011/01/17
22:57 UTC
Read the original article
Hit count: 274
Control flow in and around exception handlers is tightly controlled, due to the various ways the handler blocks can be executed. To start off with, I'll describe what SEH does when an exception is thrown.
Handling exceptions
When an exception is thrown, the CLR stops program execution at the throw
statement and searches up the call stack looking for an appropriate handler; catch clauses are analyzed, and filter blocks are executed (I'll be looking at filter blocks in a later post). Then, when an appropriate catch or filter handler is found, the stack is unwound to that handler, executing successive finally and fault handlers in their own stack contexts along the way, and program execution continues at the start of the catch handler.
Because catch, fault, finally and filter blocks can be executed essentially out of the blue by the SEH mechanism, without any reference to preceding instructions, you can't use arbitary branches in and out of exception handler blocks. Instead, you need to use specific instructions for control flow out of handler blocks: leave
, endfinally
/endfault
, and endfilter
.
Exception handler control flow
try
blocksYou cannot branch into or out of a try block or its handler using normal control flow instructions. The only way of entering a try block is by either falling through from preceding instructions, or by branching to the first instruction in the block. Once you are inside a try block, you can only leave it by throwing an exception or using the
leave <label>
instruction to jump to somewhere outside the block and its handler. Theleave
instructions signals the CLR to execute any finally handlers around the block.Most importantly, you cannot fall out of the block, and you cannot use a
ret
to return from the containing method (unlike in C#); you have to useleave
to branch to aret
elsewhere in the method. As a side effect,leave
empties the stack.catch
blocksThe only way of entering a catch block is if it is run by the SEH. At the start of the block execution, the thrown exception will be the only thing on the stack. The only way of leaving a catch block is to use
throw
,rethrow
, orleave
, in a similar way to try blocks. However, one thing you can do is use aleave
to branch back to an arbitary place in the handler's try block! In other words, you can do this:.try { // ... newobj instance void [mscorlib]System.Exception::.ctor() throw MidTry: // ... leave.s RestOfMethod } catch [mscorlib]System.Exception { // ... leave.s MidTry } RestOfMethod: // ...
As far as I know, this mechanism is not exposed in C# or VB.
finally
/fault
blocksThe only way of entering a finally or fault block is via the SEH, either as the result of a
leave
instruction in the corresponding try block, or as part of handling an exception. The only way to leave a finally or fault block is to useendfinally
orendfault
(both compile to the same binary representation), which continues execution after the finally/fault block, or, if the block was executed as part of handling an exception, signals that the SEH can continue walking the stack.filter
blocksI'll be covering filters in a separate blog posts. They're quite different to the others, and have their own special semantics.
Phew! Complicated stuff, but it's important to know if you're writing or outputting exception handlers in IL. Dealing with the C# compiler is probably best saved for the next post.
© Simple Talk or respective owner