+ Exception Implementation in the Mono Runtime
+ Dietmar Maurer (dietmar@ximian.com)
+ (C) 2001 Ximian, Inc.
+
Exception implementation (jit):
===============================
possible to map an instruction pointer (IP) to the method information needed
for unwinding the stack:
+We also save a Last Managed Frame (LMF) structure at each call from managed to
+unmanaged code. That way we can recover from exceptions inside unmanaged code.
+
void handle_exception ((struct sigcontext *ctx, gpointer obj)
{
+ if (ctx->bp < mono_end_of_stack) {
+ /* unhandled exception */
+ abort ();
+ }
info = mono_jit_info_table_find (mono_jit_info_table, ctx->ip);
execute_all_finally_handler ();
// restore register, including IP and Frame pointer
- restore_caller_saved_registers (ji, ctx);
+ ctx = restore_caller_saved_registers_from_ctx (ji, ctx);
// continue unwinding
handle_exception (ctx, obj);
} else {
- // not implemented
+ lmf = get_last_managed_frame ();
+
+ // restore register, including IP and Frame pointer
+ ctx = restore_caller_saved_registers_from_lmf (ji, lmf);
+
+ // continue unwinding
+ handle_exception (ctx, obj);
}
}
instruction is inside a finally block (but not inside another handler)
we call the finally handler before we branch to the target.
-finally/endfinally: is translated into subroutine ending with a "return"
-statement. The subroutine does not save EBP/ESP, because we need access to the
-local variables of the enclosing method. We have to use a "call"
-instruction to execute such finally handlers. This makes it possible to
-execute them inside the stack unwinding code.
+finally/endfinally, filter/endfilter: is translated into subroutine ending with
+a "return" statement. The subroutine does not save EBP, because we need access
+to the local variables of the enclosing method. Its is possible that
+instructions inside those handlers modify the stack pointer, thus we save the
+stack pointer at the start of the handler, and restore it at the end. We have
+to use a "call" instruction to execute such finally handlers. This makes it
+also possible to execute them inside the stack unwinding code. The exception
+object for filters is passed in a local variable (cfg->exvar).
+
+throw: we first save all regs into a sigcontext struct and then call the stack
+unwinding code.
+
+catch handler: catch hanlders are always called from the stack unwinding
+code. The exception object is passed in a local variable (cfg->exvar).
+
+gcc support for Exceptions
+==========================
+
+gcc supports exceptions in files compiled with the -fexception option. gcc
+generates DWARF exceptions tables in that case, so it is possible to unwind the
+stack. The method to read those exception tables is contained in libgcc.a, and
+in newer versions of glibc (glibc 2.2.5 for example), and it is called
+__frame_state_for(). Another usable glibc function is backtrace_symbols() which
+returns the function name corresponding to a code address.
+
+We dynamically check if those features are available using g_module_symbol(),
+and we use them only when available. If not available we use the LMF as
+fallback.
+
+Using gcc exception information prevents us from saving the LMF at each native
+call, so this is a way to speed up native calls. This is especially valuable
+for internal calls, because we can make sure that all internal calls are
+compiled with -fexceptions (we compile the whole mono runtime with that
+option).
+
+All native function are able to call function without exception tables, and so
+we are unable to restore all caller saved registers if an exception is raised
+in such function. Well, its possible if the previous function already saves all
+registers. So we only omit the the LMF if a function has an exception table
+able to restore all caller saved registers.
+
+One problem is that gcc almost never saves all caller saved registers, because
+it is just unnecessary in normal situations. But there is a trick forcing gcc
+to save all register, we just need to call __builtin_unwind_init() at the
+beginning of a function. That way gcc generates code to save all caller saved
+register on the stack.
+
+
-throw: we first save all regs into a sigcontext struct (we pass the
-exception object in register ECX), and then call the stack unwinding
-code.
-catch handler: receives the exception object in ECX. They store that
-object into a local variable, so that rethrow can access the object.
+
\ No newline at end of file