2002-04-30 Dietmar Maurer <dietmar@ximian.com>
[mono.git] / mono / jit / exception.c
1 /*
2  * exception.c: exception support
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/arch/x86/x86-codegen.h>
14 #include <mono/metadata/appdomain.h>
15 #include <mono/metadata/tabledefs.h>
16
17 #include "jit.h"
18 #include "codegen.h"
19
20 #ifdef __FreeBSD__
21 # define SC_EAX sc_eax
22 # define SC_EBX sc_ebx
23 # define SC_ECX sc_ecx
24 # define SC_EDX sc_edx
25 # define SC_EBP sc_ebp
26 # define SC_EIP sc_eip
27 # define SC_ESP sc_esp
28 # define SC_EDI sc_edi
29 # define SC_ESI sc_esi
30 #else
31 # define SC_EAX eax
32 # define SC_EBX ebx
33 # define SC_ECX ecx
34 # define SC_EDX edx
35 # define SC_EBP ebp
36 # define SC_EIP eip
37 # define SC_ESP esp
38 # define SC_EDI edi
39 # define SC_ESI esi
40 #endif
41
42 /*
43  * arch_get_restore_context:
44  *
45  * Returns a pointer to a method which restores a previously saved sigcontext.
46  */
47 static gpointer
48 arch_get_restore_context (void)
49 {
50         static guint8 *start = NULL;
51         guint8 *code;
52
53         if (start)
54                 return start;
55
56         /* restore_contect (struct sigcontext *ctx) */
57         /* we do not restore X86_EAX, X86_EDX */
58
59         start = code = g_malloc (1024);
60         
61         /* load ctx */
62         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
63
64         /* get return address, stored in EDX */
65         x86_mov_reg_membase (code, X86_EDX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
66         /* restore EBX */
67         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
68         /* restore EDI */
69         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
70         /* restore ESI */
71         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
72         /* restore ESP */
73         x86_mov_reg_membase (code, X86_ESP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
74         /* restore EBP */
75         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
76         /* restore ECX. the exception object is passed here to the catch handler */
77         x86_mov_reg_membase (code, X86_ECX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ECX), 4);
78
79         /* jump to the saved IP */
80         x86_jump_reg (code, X86_EDX);
81
82         return start;
83 }
84
85 /*
86  * arch_get_call_finally:
87  *
88  * Returns a pointer to a method which calls a finally handler.
89  */
90 static gpointer
91 arch_get_call_finally (void)
92 {
93         static guint8 start [28];
94         static int inited = 0;
95         guint8 *code;
96
97         if (inited)
98                 return start;
99
100         inited = 1;
101         /* call_finally (struct sigcontext *ctx, unsigned long eip) */
102         code = start;
103
104         x86_push_reg (code, X86_EBP);
105         x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
106         x86_push_reg (code, X86_EBX);
107         x86_push_reg (code, X86_EDI);
108         x86_push_reg (code, X86_ESI);
109
110         /* load ctx */
111         x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
112         /* load eip */
113         x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
114         /* save EBP */
115         x86_push_reg (code, X86_EBP);
116         /* set new EBP */
117         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
118         /* call the handler */
119         x86_call_reg (code, X86_ECX);
120         /* restore EBP */
121         x86_pop_reg (code, X86_EBP);
122         /* restore saved regs */
123         x86_pop_reg (code, X86_ESI);
124         x86_pop_reg (code, X86_EDI);
125         x86_pop_reg (code, X86_EBX);
126         x86_leave (code);
127         x86_ret (code);
128
129         g_assert ((code - start) < 28);
130         return start;
131 }
132
133
134 /**
135  * arch_handle_exception:
136  * @ctx: saved processor state
137  * @obj:
138  */
139 void
140 arch_handle_exception (struct sigcontext *ctx, gpointer obj)
141 {
142         MonoDomain *domain = mono_domain_get ();
143         MonoJitInfo *ji;
144         gpointer ip = (gpointer)ctx->SC_EIP;
145         static void (*restore_context) (struct sigcontext *);
146         static void (*call_finally) (struct sigcontext *, unsigned long);
147         void (*cleanup) (MonoObject *exc);
148
149         g_assert (ctx != NULL);
150         g_assert (obj != NULL);
151
152         ji = mono_jit_info_table_find (domain, ip);
153
154         if (!restore_context)
155                 restore_context = arch_get_restore_context ();
156         
157         if (!call_finally)
158                 call_finally = arch_get_call_finally ();
159
160         cleanup = TlsGetValue (exc_cleanup_id);
161
162         if (ji) { /* we are inside managed code */
163                 MonoMethod *m = ji->method;
164                 int offset = 2;
165
166                 if (ji->num_clauses) {
167                         int i;
168
169                         g_assert (ji->clauses);
170                         
171                         for (i = 0; i < ji->num_clauses; i++) {
172                                 MonoJitExceptionInfo *ei = &ji->clauses [i];
173
174                                 if (ei->try_start <= ip && ip <= (ei->try_end)) { 
175                                         /* catch block */
176                                         if (ei->flags == 0 && mono_object_isinst (obj, 
177                                                 mono_class_get (m->klass->image, ei->token_or_filter))) {
178                                         
179                                                 ctx->SC_EIP = (unsigned long)ei->handler_start;
180                                                 ctx->SC_ECX = (unsigned long)obj;
181                                                 restore_context (ctx);
182                                                 g_assert_not_reached ();
183                                         }
184                                 }
185                         }
186
187                         /* no handler found - we need to call all finally handlers */
188                         for (i = 0; i < ji->num_clauses; i++) {
189                                 MonoJitExceptionInfo *ei = &ji->clauses [i];
190
191                                 if (ei->try_start <= ip && ip < (ei->try_end) &&
192                                     (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
193                                         call_finally (ctx, (unsigned long)ei->handler_start);
194                                 }
195                         }
196                 }
197
198                 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
199                         char  *strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
200                         char  *tmp;
201
202                         if (!strcmp (strace, "TODO: implement stack traces")){
203                                 g_free (strace);
204                                 strace = g_strdup ("");
205                         }
206
207                         tmp = g_strdup_printf ("%sin %s.%s:%s ()\n", strace, m->klass->name_space,  
208                                                m->klass->name, m->name);
209
210                         g_free (strace);
211
212                         ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
213                         g_free (tmp);
214                 }
215
216                 /* continue unwinding */
217
218                 /* restore caller saved registers */
219                 if (ji->used_regs & X86_ESI_MASK) {
220                         ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
221                         offset++;
222                 }
223                 if (ji->used_regs & X86_EDI_MASK) {
224                         ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
225                         offset++;
226                 }
227                 if (ji->used_regs & X86_EBX_MASK) {
228                         ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
229                 }
230
231                 ctx->SC_ESP = ctx->SC_EBP;
232                 ctx->SC_EIP = *((int *)ctx->SC_EBP + 1);
233                 ctx->SC_EBP = *((int *)ctx->SC_EBP);
234                 
235                 if (ctx->SC_EBP < (unsigned)mono_end_of_stack)
236                         arch_handle_exception (ctx, obj);
237                 else {
238                         g_assert (cleanup);
239                         cleanup (obj);
240                 }
241         
242         } else {
243                 gpointer *lmf_addr = TlsGetValue (lmf_thread_id);
244                 MonoLMF *lmf;
245                 MonoMethod *m;
246
247                 g_assert (lmf_addr);
248                 lmf = *((MonoLMF **)lmf_addr);
249
250                 if (!lmf) {
251                         g_assert (cleanup);
252                         cleanup (obj);
253                 }
254                 m = lmf->method;
255
256                 *lmf_addr = lmf->previous_lmf;
257
258                 ctx->SC_ESI = lmf->esi;
259                 ctx->SC_EDI = lmf->edi;
260                 ctx->SC_EBX = lmf->ebx;
261                 ctx->SC_EBP = lmf->ebp;
262                 ctx->SC_EIP = lmf->eip;
263                 ctx->SC_ESP = (unsigned long)&lmf->eip;
264
265                 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
266                         char  *strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
267                         char  *tmp;
268
269                         if (!strcmp (strace, "TODO: implement stack traces"))
270                                 strace = g_strdup ("");
271
272                         tmp = g_strdup_printf ("%sin (unmanaged) %s.%s:%s ()\n", strace, m->klass->name_space,  
273                                                m->klass->name, m->name);
274
275                         g_free (strace);
276
277                         ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
278                         g_free (tmp);
279                 }
280
281                 if (ctx->SC_EBP < (unsigned)mono_end_of_stack)
282                         arch_handle_exception (ctx, obj);
283                 else {
284                         g_assert (cleanup);
285                         cleanup (obj);
286                 }
287         }
288
289         g_assert_not_reached ();
290 }
291
292 static void
293 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
294                  unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
295                  unsigned long eip,  unsigned long esp)
296 {
297         struct sigcontext ctx;
298
299         ctx.SC_ESP = esp;
300         ctx.SC_EIP = eip;
301         ctx.SC_EBP = ebp;
302         ctx.SC_EDI = edi;
303         ctx.SC_ESI = esi;
304         ctx.SC_EBX = ebx;
305         ctx.SC_EDX = edx;
306         ctx.SC_ECX = ecx;
307         ctx.SC_EAX = eax;
308         
309         arch_handle_exception (&ctx, exc);
310
311         g_assert_not_reached ();
312 }
313
314 /**
315  * arch_get_throw_exception:
316  *
317  * Returns a function pointer which can be used to raise 
318  * exceptions. The returned function has the following 
319  * signature: void (*func) (MonoException *exc); 
320  * For example to raise an arithmetic exception you can use:
321  *
322  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
323  * x86_call_code (code, arch_get_throw_exception ()); 
324  *
325  */
326 gpointer 
327 arch_get_throw_exception (void)
328 {
329         static guint8 start [24];
330         static int inited = 0;
331         guint8 *code;
332
333         if (inited)
334                 return start;
335
336         inited = 1;
337         code = start;
338
339         x86_push_reg (code, X86_ESP);
340         x86_push_membase (code, X86_ESP, 4); /* IP */
341         x86_push_membase (code, X86_ESP, 12); /* exception */
342         x86_push_reg (code, X86_EBP);
343         x86_push_reg (code, X86_EDI);
344         x86_push_reg (code, X86_ESI);
345         x86_push_reg (code, X86_EBX);
346         x86_push_reg (code, X86_EDX);
347         x86_push_reg (code, X86_ECX);
348         x86_push_reg (code, X86_EAX);
349         x86_call_code (code, throw_exception);
350         /* we should never reach this breakpoint */
351         x86_breakpoint (code);
352
353         g_assert ((code - start) < 24);
354         return start;
355 }
356
357 /**
358  * arch_get_throw_exception_by_name:
359  *
360  * Returns a function pointer which can be used to raise 
361  * corlib exceptions. The returned function has the following 
362  * signature: void (*func) (char *exc_name); 
363  * For example to raise an arithmetic exception you can use:
364  *
365  * x86_push_imm (code, "ArithmeticException"); 
366  * x86_call_code (code, arch_get_throw_exception ()); 
367  *
368  */
369 gpointer 
370 arch_get_throw_exception_by_name ()
371 {
372         static guint8 start [32];
373         static int inited = 0;
374         guint8 *code;
375
376         if (inited)
377                 return start;
378
379         inited = 1;
380         code = start;
381
382         /* fixme: we do not save EAX, EDX, ECD - unsure if we need that */
383
384         x86_push_membase (code, X86_ESP, 4); /* exception name */
385         x86_push_imm (code, "System");
386         x86_push_imm (code, mono_defaults.exception_class->image);
387         x86_call_code (code, mono_exception_from_name);
388         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
389         /* save the newly create object (overwrite exception name)*/
390         x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
391         x86_jump_code (code, arch_get_throw_exception ());
392
393         g_assert ((code - start) < 32);
394
395         return start;
396 }