2002-05-23 Martin Baulig <martin@gnome.org>
[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 #include <mono/metadata/threads.h>
17 #include <mono/metadata/debug-helpers.h>
18
19 #include "jit.h"
20 #include "codegen.h"
21 #include "debug.h"
22
23 #ifdef __FreeBSD__
24 # define SC_EAX sc_eax
25 # define SC_EBX sc_ebx
26 # define SC_ECX sc_ecx
27 # define SC_EDX sc_edx
28 # define SC_EBP sc_ebp
29 # define SC_EIP sc_eip
30 # define SC_ESP sc_esp
31 # define SC_EDI sc_edi
32 # define SC_ESI sc_esi
33 #else
34 # define SC_EAX eax
35 # define SC_EBX ebx
36 # define SC_ECX ecx
37 # define SC_EDX edx
38 # define SC_EBP ebp
39 # define SC_EIP eip
40 # define SC_ESP esp
41 # define SC_EDI edi
42 # define SC_ESI esi
43 #endif
44
45 /*
46  * arch_get_restore_context:
47  *
48  * Returns a pointer to a method which restores a previously saved sigcontext.
49  */
50 static gpointer
51 arch_get_restore_context (void)
52 {
53         static guint8 *start = NULL;
54         guint8 *code;
55
56         if (start)
57                 return start;
58
59         /* restore_contect (struct sigcontext *ctx) */
60         /* we do not restore X86_EAX, X86_EDX */
61
62         start = code = g_malloc (1024);
63         
64         /* load ctx */
65         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
66
67         /* get return address, stored in EDX */
68         x86_mov_reg_membase (code, X86_EDX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
69         /* restore EBX */
70         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
71         /* restore EDI */
72         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
73         /* restore ESI */
74         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
75         /* restore ESP */
76         x86_mov_reg_membase (code, X86_ESP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
77         /* restore EBP */
78         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
79         /* restore ECX. the exception object is passed here to the catch handler */
80         x86_mov_reg_membase (code, X86_ECX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ECX), 4);
81
82         /* jump to the saved IP */
83         x86_jump_reg (code, X86_EDX);
84
85         return start;
86 }
87
88 /*
89  * arch_get_call_finally:
90  *
91  * Returns a pointer to a method which calls a finally handler.
92  */
93 static gpointer
94 arch_get_call_finally (void)
95 {
96         static guint8 start [28];
97         static int inited = 0;
98         guint8 *code;
99
100         if (inited)
101                 return start;
102
103         inited = 1;
104         /* call_finally (struct sigcontext *ctx, unsigned long eip) */
105         code = start;
106
107         x86_push_reg (code, X86_EBP);
108         x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
109         x86_push_reg (code, X86_EBX);
110         x86_push_reg (code, X86_EDI);
111         x86_push_reg (code, X86_ESI);
112
113         /* load ctx */
114         x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
115         /* load eip */
116         x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
117         /* save EBP */
118         x86_push_reg (code, X86_EBP);
119         /* set new EBP */
120         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
121         /* call the handler */
122         x86_call_reg (code, X86_ECX);
123         /* restore EBP */
124         x86_pop_reg (code, X86_EBP);
125         /* restore saved regs */
126         x86_pop_reg (code, X86_ESI);
127         x86_pop_reg (code, X86_EDI);
128         x86_pop_reg (code, X86_EBX);
129         x86_leave (code);
130         x86_ret (code);
131
132         g_assert ((code - start) < 28);
133         return start;
134 }
135
136 /*
137  * return TRUE if the exception is catched. It also sets the 
138  * stack_trace String. 
139  */
140 static gboolean
141 arch_exc_is_catched (MonoDomain *domain, MonoJitTlsData *jit_tls, gpointer ip, 
142                      gpointer *bp, gpointer obj)
143 {
144         MonoJitInfo *ji;
145         gpointer *end_of_stack;
146         MonoLMF *lmf = jit_tls->lmf;
147         MonoMethod *m;
148         int i;
149
150         end_of_stack = jit_tls->end_of_stack;
151         g_assert (end_of_stack);
152
153         while (1) {
154
155                 ji = mono_jit_info_table_find (domain, ip);
156
157                 if (ji) { /* we are inside managed code */
158                         m = ji->method;
159                         
160                         if (mono_object_isinst (obj, mono_defaults.exception_class)) {
161                                 char    *strace;
162                                 char    *tmp, *tmpsig, *source_location, *tmpaddr;
163                                 gint32   address, iloffset;
164
165                                 if (!((MonoException*)obj)->stack_trace)
166                                         strace = g_strdup ("");
167                                 else
168                                         strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
169
170                                 address = (char *)ip - (char *)ji->code_start;
171
172                                 source_location = mono_debug_source_location_from_address (m, address);
173                                 iloffset = mono_debug_il_offset_from_address (m, address);
174
175                                 if (iloffset < 0)
176                                         tmpaddr = g_strdup_printf ("<0x%05x>", address);
177                                 else
178                                         tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
179
180                                 tmpsig = mono_signature_get_desc(m->signature, TRUE);
181                                 if (source_location)
182                                         tmp = g_strdup_printf ("%sin %s (at %s) %s.%s:%s (%s)\n", strace, tmpaddr,
183                                                                source_location, m->klass->name_space, m->klass->name,
184                                                                m->name, tmpsig);
185                                 else
186                                         tmp = g_strdup_printf ("%sin %s %s.%s:%s (%s)\n", strace, tmpaddr,
187                                                                m->klass->name_space, m->klass->name, m->name, tmpsig);
188                                 g_free (source_location);
189                                 g_free (tmpsig);
190                                 g_free (strace);
191
192                                 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
193                                 g_free (tmp);
194                         }
195
196                         if (ji->num_clauses) {
197
198                                 g_assert (ji->clauses);
199
200                                 for (i = 0; i < ji->num_clauses; i++) {
201                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
202                                 
203                                         if (ei->try_start <= ip && ip <= (ei->try_end)) { 
204                                                 /* catch block */
205                                                 if (ei->flags == 0 && mono_object_isinst (obj, 
206                                                         mono_class_get (m->klass->image, ei->token_or_filter))) {
207                                                         return TRUE;
208                                                 }
209                                         }
210                                 }
211                         }
212
213                         /* continue unwinding */
214
215                         ip = (gpointer)(*((int *)bp + 1) - 5);
216                         bp = (gpointer)(*((int *)bp));
217
218                         if (bp >= end_of_stack)
219                                 return FALSE;
220         
221                 } else {
222                         if (!lmf) 
223                                 return FALSE;
224
225                         bp = (gpointer)lmf->ebp;
226                         ip = (gpointer)lmf->eip;
227
228                         m = lmf->method;
229
230                         if (mono_object_isinst (obj, mono_defaults.exception_class)) {
231                                 char  *strace; 
232                                 char  *tmp;
233
234                                 if (!((MonoException*)obj)->stack_trace)
235                                         strace = g_strdup ("");
236                                 else
237                                         strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
238
239                                 tmp = g_strdup_printf ("%sin (unmanaged) %s.%s:%s ()\n", strace, m->klass->name_space,  
240                                                        m->klass->name, m->name);
241
242                                 g_free (strace);
243
244                                 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
245                                 g_free (tmp);
246                         }
247
248                         lmf = lmf->previous_lmf;
249
250                         if (bp >= end_of_stack)
251                                 return FALSE;
252                 }
253         }
254         
255         return FALSE;
256 }
257
258 /**
259  * arch_handle_exception:
260  * @ctx: saved processor state
261  * @obj:
262  */
263 void
264 arch_handle_exception (struct sigcontext *ctx, gpointer obj)
265 {
266         MonoDomain *domain = mono_domain_get ();
267         MonoJitInfo *ji;
268         gpointer ip = (gpointer)ctx->SC_EIP;
269         static void (*restore_context) (struct sigcontext *);
270         static void (*call_finally) (struct sigcontext *, unsigned long);
271         void (*cleanup) (MonoObject *exc);
272         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
273         gpointer end_of_stack;
274
275         g_assert (ctx != NULL);
276
277         if (!obj) {
278                 MonoException *ex = mono_get_exception_null_reference ();
279                 ex->message = mono_string_new (domain, 
280                         "Object reference not set to an instance of an object");
281                 obj = (MonoObject *)ex;
282         } 
283
284         ((MonoException *)obj)->stack_trace = NULL;
285
286         if (!restore_context)
287                 restore_context = arch_get_restore_context ();
288         
289         if (!call_finally)
290                 call_finally = arch_get_call_finally ();
291
292         end_of_stack = jit_tls->end_of_stack;
293         g_assert (end_of_stack);
294
295         cleanup = jit_tls->abort_func;
296
297         if (!arch_exc_is_catched (domain, jit_tls, ip, (gpointer *)ctx->SC_EBP, obj)) {
298                 if (mono_debug_format != MONO_DEBUG_FORMAT_NONE) {
299                         mono_debug_make_symbols ();
300                 }
301                 mono_unhandled_exception (obj);
302         }
303
304         while (1) {
305
306                 ji = mono_jit_info_table_find (domain, ip);
307         
308                 if (ji) { /* we are inside managed code */
309                         MonoMethod *m = ji->method;
310                         int offset;
311
312                         if (ji->num_clauses) {
313                                 int i;
314                                 
315                                 g_assert (ji->clauses);
316                         
317                                 for (i = 0; i < ji->num_clauses; i++) {
318                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
319
320                                         if (ei->try_start <= ip && ip <= (ei->try_end)) { 
321                                                 /* catch block */
322                                                 if (ei->flags == 0 && mono_object_isinst (obj, 
323                                                         mono_class_get (m->klass->image, ei->token_or_filter))) {
324                                         
325                                                         ctx->SC_EIP = (unsigned long)ei->handler_start;
326                                                         ctx->SC_ECX = (unsigned long)obj;
327                                                         restore_context (ctx);
328                                                         g_assert_not_reached ();
329                                                 }
330                                         }
331                                 }
332
333                                 /* no handler found - we need to call all finally handlers */
334                                 for (i = 0; i < ji->num_clauses; i++) {
335                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
336
337                                         if (ei->try_start <= ip && ip < (ei->try_end) &&
338                                             (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
339                                                 call_finally (ctx, (unsigned long)ei->handler_start);
340                                         }
341                                 }
342                         }
343
344                         /* continue unwinding */
345
346                         offset = -1;
347                         /* restore caller saved registers */
348                         if (ji->used_regs & X86_ESI_MASK) {
349                                 ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
350                                 offset--;
351                         }
352                         if (ji->used_regs & X86_EDI_MASK) {
353                                 ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
354                                 offset--;
355                         }
356                         if (ji->used_regs & X86_EBX_MASK) {
357                                 ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
358                         }
359
360                         ctx->SC_ESP = ctx->SC_EBP;
361                         ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 5;
362                         ctx->SC_EBP = *((int *)ctx->SC_EBP);
363
364                         ip = (gpointer)ctx->SC_EIP;
365
366                         if (ctx->SC_EBP > (unsigned)end_of_stack) {
367                                 g_assert (cleanup);
368                                 cleanup (obj);
369                                 g_assert_not_reached ();
370                         }
371         
372                 } else {
373                         MonoLMF *lmf = jit_tls->lmf;
374
375                         if (!lmf) {
376                                 g_assert (cleanup);
377                                 cleanup (obj);
378                                 g_assert_not_reached ();
379                         }
380
381                         jit_tls->lmf = lmf->previous_lmf;
382
383                         ctx->SC_ESI = lmf->esi;
384                         ctx->SC_EDI = lmf->edi;
385                         ctx->SC_EBX = lmf->ebx;
386                         ctx->SC_EBP = lmf->ebp;
387                         ctx->SC_EIP = lmf->eip;
388                         ctx->SC_ESP = (unsigned long)&lmf->eip;
389
390                         ip = (gpointer)ctx->SC_EIP;
391
392                         if (ctx->SC_EBP >= (unsigned)end_of_stack) {
393                                 g_assert (cleanup);
394                                 cleanup (obj);
395                                 g_assert_not_reached ();
396                         }
397                 }
398         }
399
400         g_assert_not_reached ();
401 }
402
403 static void
404 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
405                  unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
406                  unsigned long eip,  unsigned long esp)
407 {
408         struct sigcontext ctx;
409
410         /* adjust eip so that it point to the call instruction */
411         eip -= 5;
412
413         ctx.SC_ESP = esp;
414         ctx.SC_EIP = eip;
415         ctx.SC_EBP = ebp;
416         ctx.SC_EDI = edi;
417         ctx.SC_ESI = esi;
418         ctx.SC_EBX = ebx;
419         ctx.SC_EDX = edx;
420         ctx.SC_ECX = ecx;
421         ctx.SC_EAX = eax;
422         
423         arch_handle_exception (&ctx, exc);
424
425         g_assert_not_reached ();
426 }
427
428 /**
429  * arch_get_throw_exception:
430  *
431  * Returns a function pointer which can be used to raise 
432  * exceptions. The returned function has the following 
433  * signature: void (*func) (MonoException *exc); 
434  * For example to raise an arithmetic exception you can use:
435  *
436  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
437  * x86_call_code (code, arch_get_throw_exception ()); 
438  *
439  */
440 gpointer 
441 arch_get_throw_exception (void)
442 {
443         static guint8 start [24];
444         static int inited = 0;
445         guint8 *code;
446
447         if (inited)
448                 return start;
449
450         inited = 1;
451         code = start;
452
453         x86_push_reg (code, X86_ESP);
454         x86_push_membase (code, X86_ESP, 4); /* IP */
455         x86_push_membase (code, X86_ESP, 12); /* exception */
456         x86_push_reg (code, X86_EBP);
457         x86_push_reg (code, X86_EDI);
458         x86_push_reg (code, X86_ESI);
459         x86_push_reg (code, X86_EBX);
460         x86_push_reg (code, X86_EDX);
461         x86_push_reg (code, X86_ECX);
462         x86_push_reg (code, X86_EAX);
463         x86_call_code (code, throw_exception);
464         /* we should never reach this breakpoint */
465         x86_breakpoint (code);
466
467         g_assert ((code - start) < 24);
468         return start;
469 }
470
471 /**
472  * arch_get_throw_exception_by_name:
473  *
474  * Returns a function pointer which can be used to raise 
475  * corlib exceptions. The returned function has the following 
476  * signature: void (*func) (char *exc_name); 
477  * For example to raise an arithmetic exception you can use:
478  *
479  * x86_push_imm (code, "ArithmeticException"); 
480  * x86_call_code (code, arch_get_throw_exception ()); 
481  *
482  */
483 gpointer 
484 arch_get_throw_exception_by_name ()
485 {
486         static guint8 start [32];
487         static int inited = 0;
488         guint8 *code;
489
490         if (inited)
491                 return start;
492
493         inited = 1;
494         code = start;
495
496         /* fixme: we do not save EAX, EDX, ECD - unsure if we need that */
497
498         x86_push_membase (code, X86_ESP, 4); /* exception name */
499         x86_push_imm (code, "System");
500         x86_push_imm (code, mono_defaults.exception_class->image);
501         x86_call_code (code, mono_exception_from_name);
502         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
503         /* save the newly create object (overwrite exception name)*/
504         x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
505         x86_jump_code (code, arch_get_throw_exception ());
506
507         g_assert ((code - start) < 32);
508
509         return start;
510 }