X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=docs%2Fexceptions;h=d5ecaeead43a634984b2701a5c5bd5acaa8ff019;hb=cf1a6829b1553118ad66aaea2cea385fbab0e93f;hp=ab0f2dec94ad67ae47efeea238dd671a02b28553;hpb=7b52efd545cc7baa20750bd061f5ca15c98331c8;p=mono.git diff --git a/docs/exceptions b/docs/exceptions index ab0f2dec94a..d5ecaeead43 100644 --- a/docs/exceptions +++ b/docs/exceptions @@ -1,3 +1,7 @@ + Exception Implementation in the Mono Runtime + Dietmar Maurer (dietmar@ximian.com) + (C) 2001 Ximian, Inc. + Exception implementation (jit): =============================== @@ -8,8 +12,15 @@ We record the code address (start_address, size) of all methods. That way it is 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); @@ -21,14 +32,20 @@ void handle_exception ((struct sigcontext *ctx, gpointer obj) 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); } } @@ -40,15 +57,54 @@ leave: is simply translated into a branch to the target. If the leave 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