2002-06-18 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 #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         /* save the ESP - this is used by endfinally */
122         x86_mov_membase_reg (code, X86_EBP, -16, X86_ESP, 4);
123         /* call the handler */
124         x86_call_reg (code, X86_ECX);
125         /* restore EBP */
126         x86_pop_reg (code, X86_EBP);
127         /* restore saved regs */
128         x86_pop_reg (code, X86_ESI);
129         x86_pop_reg (code, X86_EDI);
130         x86_pop_reg (code, X86_EBX);
131         x86_leave (code);
132         x86_ret (code);
133
134         g_assert ((code - start) < 28);
135         return start;
136 }
137
138 /*
139  * return TRUE if the exception is catched. It also sets the 
140  * stack_trace String. 
141  */
142 static gboolean
143 arch_exc_is_caught (MonoDomain *domain, MonoJitTlsData *jit_tls, gpointer ip, 
144                     gpointer *bp, gpointer obj)
145 {
146         MonoJitInfo *ji;
147         gpointer *end_of_stack;
148         MonoLMF *lmf = jit_tls->lmf;
149         MonoMethod *m;
150         int i;
151
152         end_of_stack = jit_tls->end_of_stack;
153         g_assert (end_of_stack);
154
155         while (1) {
156
157                 ji = mono_jit_info_table_find (domain, ip);
158
159                 if (ji) { /* we are inside managed code */
160                         m = ji->method;
161                         
162                         if (mono_object_isinst (obj, mono_defaults.exception_class)) {
163                                 char    *strace;
164                                 char    *tmp, *tmpsig, *source_location, *tmpaddr;
165                                 gint32   address, iloffset;
166
167                                 if (!((MonoException*)obj)->stack_trace)
168                                         strace = g_strdup ("");
169                                 else
170                                         strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
171
172                                 address = (char *)ip - (char *)ji->code_start;
173
174                                 source_location = mono_debug_source_location_from_address (m, address);
175                                 iloffset = mono_debug_il_offset_from_address (m, address);
176
177                                 if (iloffset < 0)
178                                         tmpaddr = g_strdup_printf ("<0x%05x>", address);
179                                 else
180                                         tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
181
182                                 tmpsig = mono_signature_get_desc(m->signature, TRUE);
183                                 if (source_location)
184                                         tmp = g_strdup_printf ("%sin %s (at %s) %s.%s:%s (%s)\n", strace, tmpaddr,
185                                                                source_location, m->klass->name_space, m->klass->name,
186                                                                m->name, tmpsig);
187                                 else
188                                         tmp = g_strdup_printf ("%sin %s %s.%s:%s (%s)\n", strace, tmpaddr,
189                                                                m->klass->name_space, m->klass->name, m->name, tmpsig);
190                                 g_free (source_location);
191                                 g_free (tmpsig);
192                                 g_free (strace);
193
194                                 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
195                                 g_free (tmp);
196                         }
197
198                         if (ji->num_clauses) {
199
200                                 g_assert (ji->clauses);
201
202                                 for (i = 0; i < ji->num_clauses; i++) {
203                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
204                                 
205                                         if (ei->try_start <= ip && ip <= (ei->try_end)) { 
206                                                 /* catch block */
207                                                 if (ei->flags == 0 && mono_object_isinst (obj, 
208                                                         mono_class_get (m->klass->image, ei->token_or_filter))) {
209                                                         return TRUE;
210                                                 }
211                                         }
212                                 }
213                         }
214
215                         /* continue unwinding */
216
217                         ip = (gpointer)(*((int *)bp + 1) - 5);
218                         bp = (gpointer)(*((int *)bp));
219
220                         if (bp >= end_of_stack) {
221                                 if (!jit_tls->env)
222                                         return FALSE;
223                                 return TRUE;
224                         }
225         
226                 } else {
227                         if (!lmf)
228                                 return FALSE;
229
230                         bp = (gpointer)lmf->ebp;
231                         ip = (gpointer)lmf->eip;
232
233                         m = lmf->method;
234
235                         if (mono_object_isinst (obj, mono_defaults.exception_class)) {
236                                 char  *strace; 
237                                 char  *tmp;
238
239                                 if (!((MonoException*)obj)->stack_trace)
240                                         strace = g_strdup ("");
241                                 else
242                                         strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
243
244                                 tmp = g_strdup_printf ("%sin (unmanaged) %s.%s:%s ()\n", strace, m->klass->name_space,  
245                                                        m->klass->name, m->name);
246
247                                 g_free (strace);
248
249                                 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
250                                 g_free (tmp);
251                         }
252
253                         lmf = lmf->previous_lmf;
254
255                         if (bp >= end_of_stack) {
256                                 if (!jit_tls->env)
257                                         return FALSE;
258                                 return TRUE;
259                         }
260                 }
261         }
262         
263         return FALSE;
264 }
265
266
267 MonoBoolean
268 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, 
269                           MonoReflectionMethod **method, 
270                           gint32 *iloffset, gint32 *native_offset,
271                           MonoString **file, gint32 *line, gint32 *column)
272 {
273         MonoDomain *domain = mono_domain_get ();
274         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
275         MonoLMF *lmf = jit_tls->lmf;
276         gpointer *sf = (gpointer *)&skip;
277         gpointer ip = sf [-1];
278         int addr;
279         gpointer *bp = sf [-2];
280         MonoMethod *m = NULL;
281
282         do {
283                 MonoJitInfo *ji;
284                 addr = 0;
285
286                 if ((ji = mono_jit_info_table_find (domain, ip))) {
287                         m = ji->method;
288                         addr = (char *)ip - (char *)ji->code_start;
289                         ip = (gpointer)((char *)bp [1] - 5);
290                         bp = bp [0];
291                 } else {
292                         if (!lmf)
293                                 g_assert_not_reached ();
294                         
295                         m = lmf->method;
296
297                         bp = (gpointer)lmf->ebp;
298                         ip = (gpointer)lmf->eip;
299
300                         lmf = lmf->previous_lmf;
301                 }
302
303                 if ((unsigned)bp >= (unsigned)jit_tls->end_of_stack)
304                         return FALSE;
305
306         } while (skip-- > 0);
307
308         g_assert (m);
309
310         *method = mono_method_get_object (domain, m);
311         *iloffset = mono_debug_il_offset_from_address (m, addr);
312         *native_offset = addr;
313
314         if (need_file_info) {
315                 *file = mono_string_new (domain, m->klass->image->name);
316                 *column = 0; // fixme
317                 *line = 0; // fixme
318         }
319
320         return TRUE;
321 }
322
323 /**
324  * arch_handle_exception:
325  * @ctx: saved processor state
326  * @obj:
327  */
328 void
329 arch_handle_exception (struct sigcontext *ctx, gpointer obj)
330 {
331         MonoDomain *domain = mono_domain_get ();
332         MonoJitInfo *ji;
333         gpointer ip = (gpointer)ctx->SC_EIP;
334         static void (*restore_context) (struct sigcontext *);
335         static void (*call_finally) (struct sigcontext *, unsigned long);
336         void (*cleanup) (MonoObject *exc);
337         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
338         gpointer end_of_stack;
339
340         g_assert (ctx != NULL);
341
342         if (!obj) {
343                 MonoException *ex = mono_get_exception_null_reference ();
344                 ex->message = mono_string_new (domain, 
345                         "Object reference not set to an instance of an object");
346                 obj = (MonoObject *)ex;
347         } 
348
349         ((MonoException *)obj)->stack_trace = NULL;
350
351         if (!restore_context)
352                 restore_context = arch_get_restore_context ();
353         
354         if (!call_finally)
355                 call_finally = arch_get_call_finally ();
356
357         end_of_stack = jit_tls->end_of_stack;
358         g_assert (end_of_stack);
359
360         cleanup = jit_tls->abort_func;
361
362         if (!arch_exc_is_caught (domain, jit_tls, ip, (gpointer *)ctx->SC_EBP, obj)) {
363                 if (mono_break_on_exc) {
364                         if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
365                                 mono_debug_make_symbols ();
366                         G_BREAKPOINT ();
367                 }
368                 mono_unhandled_exception (obj);
369         }
370
371         while (1) {
372
373                 ji = mono_jit_info_table_find (domain, ip);
374         
375                 if (ji) { /* we are inside managed code */
376                         MonoMethod *m = ji->method;
377                         int offset;
378
379                         if (ji->num_clauses) {
380                                 int i;
381                                 
382                                 g_assert (ji->clauses);
383                         
384                                 for (i = 0; i < ji->num_clauses; i++) {
385                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
386
387                                         if (ei->try_start <= ip && ip <= (ei->try_end)) { 
388                                                 /* catch block */
389                                                 if (ei->flags == 0 && mono_object_isinst (obj, 
390                                                         mono_class_get (m->klass->image, ei->token_or_filter))) {
391                                         
392                                                         ctx->SC_EIP = (unsigned long)ei->handler_start;
393                                                         ctx->SC_ECX = (unsigned long)obj;
394                                                         restore_context (ctx);
395                                                         g_assert_not_reached ();
396                                                 }
397                                         }
398                                 }
399
400                                 /* no handler found - we need to call all finally handlers */
401                                 for (i = 0; i < ji->num_clauses; i++) {
402                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
403
404                                         if (ei->try_start <= ip && ip < (ei->try_end) &&
405                                             (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
406                                                 call_finally (ctx, (unsigned long)ei->handler_start);
407                                         }
408                                 }
409                         }
410
411                         /* continue unwinding */
412
413                         offset = -1;
414                         /* restore caller saved registers */
415                         if (ji->used_regs & X86_EBX_MASK) {
416                                 ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
417                                 offset--;
418                         }
419                         if (ji->used_regs & X86_EDI_MASK) {
420                                 ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
421                                 offset--;
422                         }
423                         if (ji->used_regs & X86_ESI_MASK) {
424                                 ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
425                         }
426
427                         ctx->SC_ESP = ctx->SC_EBP;
428                         ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 5;
429                         ctx->SC_EBP = *((int *)ctx->SC_EBP);
430
431                         ip = (gpointer)ctx->SC_EIP;
432
433                         if (ctx->SC_EBP > (unsigned)end_of_stack) {
434                                 g_assert (cleanup);
435                                 cleanup (obj);
436                                 g_assert_not_reached ();
437                         }
438         
439                 } else {
440                         MonoLMF *lmf = jit_tls->lmf;
441
442                         if (!lmf) {
443                                 g_assert (cleanup);
444                                 cleanup (obj);
445                                 g_assert_not_reached ();
446                         }
447
448                         jit_tls->lmf = lmf->previous_lmf;
449
450                         ctx->SC_ESI = lmf->esi;
451                         ctx->SC_EDI = lmf->edi;
452                         ctx->SC_EBX = lmf->ebx;
453                         ctx->SC_EBP = lmf->ebp;
454                         ctx->SC_EIP = lmf->eip;
455                         ctx->SC_ESP = (unsigned long)&lmf->eip;
456
457                         ip = (gpointer)ctx->SC_EIP;
458
459                         if (ctx->SC_EBP >= (unsigned)end_of_stack) {
460                                 g_assert (cleanup);
461                                 cleanup (obj);
462                                 g_assert_not_reached ();
463                         }
464                 }
465         }
466
467         g_assert_not_reached ();
468 }
469
470 static void
471 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
472                  unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
473                  unsigned long eip,  unsigned long esp)
474 {
475         struct sigcontext ctx;
476
477         /* adjust eip so that it point to the call instruction */
478         eip -= 5;
479
480         ctx.SC_ESP = esp;
481         ctx.SC_EIP = eip;
482         ctx.SC_EBP = ebp;
483         ctx.SC_EDI = edi;
484         ctx.SC_ESI = esi;
485         ctx.SC_EBX = ebx;
486         ctx.SC_EDX = edx;
487         ctx.SC_ECX = ecx;
488         ctx.SC_EAX = eax;
489         
490         arch_handle_exception (&ctx, exc);
491
492         g_assert_not_reached ();
493 }
494
495 /**
496  * arch_get_throw_exception:
497  *
498  * Returns a function pointer which can be used to raise 
499  * exceptions. The returned function has the following 
500  * signature: void (*func) (MonoException *exc); 
501  * For example to raise an arithmetic exception you can use:
502  *
503  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
504  * x86_call_code (code, arch_get_throw_exception ()); 
505  *
506  */
507 gpointer 
508 arch_get_throw_exception (void)
509 {
510         static guint8 start [24];
511         static int inited = 0;
512         guint8 *code;
513
514         if (inited)
515                 return start;
516
517         inited = 1;
518         code = start;
519
520         x86_push_reg (code, X86_ESP);
521         x86_push_membase (code, X86_ESP, 4); /* IP */
522         x86_push_membase (code, X86_ESP, 12); /* exception */
523         x86_push_reg (code, X86_EBP);
524         x86_push_reg (code, X86_EDI);
525         x86_push_reg (code, X86_ESI);
526         x86_push_reg (code, X86_EBX);
527         x86_push_reg (code, X86_EDX);
528         x86_push_reg (code, X86_ECX);
529         x86_push_reg (code, X86_EAX);
530         x86_call_code (code, throw_exception);
531         /* we should never reach this breakpoint */
532         x86_breakpoint (code);
533
534         g_assert ((code - start) < 24);
535         return start;
536 }
537
538 /**
539  * arch_get_throw_exception_by_name:
540  *
541  * Returns a function pointer which can be used to raise 
542  * corlib exceptions. The returned function has the following 
543  * signature: void (*func) (char *exc_name); 
544  * For example to raise an arithmetic exception you can use:
545  *
546  * x86_push_imm (code, "ArithmeticException"); 
547  * x86_call_code (code, arch_get_throw_exception ()); 
548  *
549  */
550 gpointer 
551 arch_get_throw_exception_by_name ()
552 {
553         static guint8 start [32];
554         static int inited = 0;
555         guint8 *code;
556
557         if (inited)
558                 return start;
559
560         inited = 1;
561         code = start;
562
563         /* fixme: we do not save EAX, EDX, ECD - unsure if we need that */
564
565         x86_push_membase (code, X86_ESP, 4); /* exception name */
566         x86_push_imm (code, "System");
567         x86_push_imm (code, mono_defaults.exception_class->image);
568         x86_call_code (code, mono_exception_from_name);
569         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
570         /* save the newly create object (overwrite exception name)*/
571         x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
572         x86_jump_code (code, arch_get_throw_exception ());
573
574         g_assert ((code - start) < 32);
575
576         return start;
577 }