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