2002-11-28 Dietmar Maurer <dietmar@ximian.com>
[mono.git] / docs / exceptions
1 Author: Dietmar Maurer (dietmar@ximian.com)
2 (C) 2001 Ximian, Inc.
3
4 Exception implementation (jit):
5 ===============================
6
7 Stack unwinding:
8 ================
9
10 We record the code address (start_address, size) of all methods. That way it is
11 possible to map an instruction pointer (IP) to the method information needed
12 for unwinding the stack:
13
14 We also save a Last Managed Frame (LMF) structure at each call from managed to
15 unmanaged code. That way we can recover from exceptions inside unmanaged code.
16
17 void handle_exception ((struct sigcontext *ctx, gpointer obj)
18 {
19         if (ctx->bp < mono_end_of_stack) {
20                 /* unhandled exception */
21                 abort ();
22         }
23
24         info = mono_jit_info_table_find (mono_jit_info_table, ctx->ip);
25
26         if (info) { // we are inside managed code
27
28                 if (ch =  find_catch_handler ())
29                         execute_catch_handler (ch, ctx, obj); 
30                 
31                 execute_all_finally_handler ();
32
33                 // restore register, including IP and Frame pointer
34                 ctx = restore_caller_saved_registers_from_ctx (ji, ctx);
35
36                 // continue unwinding
37                 handle_exception (ctx, obj);
38
39         } else {
40
41                 lmf = get_last_managed_frame ();
42                 
43                 // restore register, including IP and Frame pointer
44                 ctx = restore_caller_saved_registers_from_lmf (ji, lmf);
45                 
46                 // continue unwinding
47                 handle_exception (ctx, obj);
48         }
49 }
50
51
52 Code generation:
53 ================
54
55 leave: is simply translated into a branch to the target. If the leave
56 instruction is inside a finally block (but not inside another handler)
57 we call the finally handler before we branch to the target.
58
59 finally/endfinally: is translated into subroutine ending with a "return"
60 statement. The subroutine does not save EBP/ESP, because we need access to the
61 local variables of the enclosing method. We have to use a "call"
62 instruction to execute such finally handlers. This makes it possible to
63 execute them inside the stack unwinding code.
64
65 throw: we first save all regs into a sigcontext struct (we pass the
66 exception object in register ECX), and then call the stack unwinding
67 code.
68
69 catch handler: receives the exception object in ECX. They store that
70 object into a local variable, so that rethrow can access the object.
71
72
73 gcc support for Exceptions
74 ==========================
75
76 gcc supports exceptions in files compiled with the -fexception option. gcc
77 generates DWARF exceptions tables in that case, so it is possible to unwind the
78 stack. The method to read those exception tables is contained in libgcc.a, and
79 in newer versions of glibc (glibc 2.2.5 for example), and it is called
80 __frame_state_for(). Another usable glibc function is backtrace_symbols() which
81 returns the function name corresponding to a code address.
82
83 We dynamically check if those features are available using g_module_symbol(),
84 and we use them only when available. If not available we use the LMF as
85 fallback.
86
87 Using gcc exception information prevents us from saving the LMF at each native
88 call, so this is a way to speed up native calls. This is especially valuable
89 for internal calls, because we can make sure that all internal calls are
90 compiled with -fexceptions (we compile the whole mono runtime with that
91 option).
92
93 All native function are able to call function without exception tables, and so
94 we are unable to restore all caller saved registers if an exception is raised
95 in such function. Well, its possible if the previous function already saves all
96 registers. So we only omit the the LMF if a function has an exception table
97 able to restore all caller saved registers.
98
99 One problem is that gcc almost never saves all caller saved registers, because
100 it is just unnecessary in normal situations. But there is a trick forcing gcc
101 to save all register, we just need to call __builtin_unwind_init() at the
102 beginning of a function. That way gcc generates code to save all caller saved
103 register on the stack.
104
105
106
107
108