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: 335
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
tryblocksYou 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. Theleaveinstructions 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
retto return from the containing method (unlike in C#); you have to useleaveto branch to aretelsewhere in the method. As a side effect,leaveempties the stack.catchblocksThe 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 aleaveto 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/faultblocksThe only way of entering a finally or fault block is via the SEH, either as the result of a
leaveinstruction in the corresponding try block, or as part of handling an exception. The only way to leave a finally or fault block is to useendfinallyorendfault(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.filterblocksI'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