2003-01-16 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 #include <signal.h>
13
14 #include <mono/arch/x86/x86-codegen.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/debug-helpers.h>
19
20 #include "jit.h"
21 #include "codegen.h"
22 #include "debug.h"
23
24 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
25 # define SC_EAX sc_eax
26 # define SC_EBX sc_ebx
27 # define SC_ECX sc_ecx
28 # define SC_EDX sc_edx
29 # define SC_EBP sc_ebp
30 # define SC_EIP sc_eip
31 # define SC_ESP sc_esp
32 # define SC_EDI sc_edi
33 # define SC_ESI sc_esi
34 #else
35 # define SC_EAX eax
36 # define SC_EBX ebx
37 # define SC_ECX ecx
38 # define SC_EDX edx
39 # define SC_EBP ebp
40 # define SC_EIP eip
41 # define SC_ESP esp
42 # define SC_EDI edi
43 # define SC_ESI esi
44 #endif
45
46 typedef struct sigcontext MonoContext;
47
48 #define MONO_CONTEXT_SET_IP(ctx,ip) do { (ctx)->SC_EIP = (long)ip; } while (0); 
49 #define MONO_CONTEXT_SET_BP(ctx,bp) do { (ctx)->SC_EBP = (long)bp; } while (0); 
50 #define MONO_CONTEXT_SET_EXCREG(ctx,exc) do { (ctx)->SC_ECX = (long)exc; } while (0); 
51
52 #define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->SC_EIP))
53 #define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->SC_EBP))
54
55 #ifdef MONO_USE_EXC_TABLES
56
57 /*************************************/
58 /*    STACK UNWINDING STUFF          */
59 /*************************************/
60
61 /* These definitions are from unwind-dw2.c in glibc 2.2.5 */
62
63 /* For x86 */
64 #define DWARF_FRAME_REGISTERS 17
65
66 typedef struct frame_state
67 {
68   void *cfa;
69   void *eh_ptr;
70   long cfa_offset;
71   long args_size;
72   long reg_or_offset[DWARF_FRAME_REGISTERS+1];
73   unsigned short cfa_reg;
74   unsigned short retaddr_column;
75   char saved[DWARF_FRAME_REGISTERS+1];
76 } frame_state;
77
78 static long
79 get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum)
80 {
81         switch (dwarf_regnum) {
82         case X86_EAX:
83                 return ctx->eax;
84         case X86_EBX:
85                 return ctx->ebx;
86         case X86_ECX:
87                 return ctx->ecx;
88         case X86_EDX:
89                 return ctx->edx;
90         case X86_ESI:
91                 return ctx->esi;
92         case X86_EDI:
93                 return ctx->edi;
94         case X86_EBP:
95                 return ctx->ebp;
96         case X86_ESP:
97                 return ctx->esp;
98         default:
99                 g_assert_not_reached ();
100         }
101
102         return 0;
103 }
104
105 static void
106 set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value)
107 {
108         switch (dwarf_regnum) {
109         case X86_EAX:
110                 ctx->eax = value;
111                 break;
112         case X86_EBX:
113                 ctx->ebx = value;
114                 break;
115         case X86_ECX:
116                 ctx->ecx = value;
117                 break;
118         case X86_EDX:
119                 ctx->edx = value;
120                 break;
121         case X86_ESI:
122                 ctx->esi = value;
123                 break;
124         case X86_EDI:
125                 ctx->edi = value;
126                 break;
127         case X86_EBP:
128                 ctx->ebp = value;
129                 break;
130         case X86_ESP:
131                 ctx->esp = value;
132                 break;
133         case 8:
134                 ctx->eip = value;
135                 break;
136         default:
137                 g_assert_not_reached ();
138         }
139 }
140
141 typedef struct frame_state * (*framesf) (void *, struct frame_state *);
142
143 static framesf frame_state_for = NULL;
144
145 static gboolean inited = FALSE;
146
147 typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size);
148
149 static get_backtrace_symbols_type get_backtrace_symbols = NULL;
150
151 static void
152 init_frame_state_for (void)
153 {
154         GModule *module;
155
156         /*
157          * There are two versions of __frame_state_for: one in libgcc.a and the
158          * other in glibc.so. We need the version from glibc.
159          * For more info, see this:
160          * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html
161          */
162         if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) {
163         
164                 if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for))
165                         frame_state_for = NULL;
166
167                 if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) {
168                         get_backtrace_symbols = NULL;
169                         frame_state_for = NULL;
170                 }
171
172                 g_module_close (module);
173         }
174
175         inited = TRUE;
176 }
177
178 /* mono_has_unwind_info:
179  *
180  * Tests if a function has an DWARF exception table able to restore
181  * all caller saved registers. 
182  */
183 gboolean
184 mono_has_unwind_info (MonoMethod *method)
185 {
186         struct frame_state state_in;
187         struct frame_state *res;
188
189         if (!inited) 
190                 init_frame_state_for ();
191         
192         if (!frame_state_for)
193                 return FALSE;
194
195         g_assert (method->addr);
196
197         memset (&state_in, 0, sizeof (state_in));
198
199         /* offset 10 is just a guess, but it works for all methods tested */
200         if ((res = frame_state_for ((char *)method->addr + 10, &state_in))) {
201
202                 if (res->saved [X86_EBX] != 1 ||
203                     res->saved [X86_EDI] != 1 ||
204                     res->saved [X86_EBP] != 1 ||
205                     res->saved [X86_ESI] != 1) {
206                         return FALSE;
207                 }
208                 return TRUE;
209         } else 
210                 return FALSE;
211 }
212
213 struct stack_frame
214 {
215   void *next;
216   void *return_address;
217 };
218
219 static MonoJitInfo *
220 x86_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, struct sigcontext *ctx, 
221                          struct sigcontext *new_ctx, MonoLMF *lmf, char **trace)
222 {
223         struct stack_frame *frame;
224         gpointer max_stack;
225         MonoJitInfo *ji;
226         struct frame_state state_in;
227         struct frame_state *res;
228
229         if (trace)
230                 *trace = NULL;
231
232         if (!inited) 
233                 init_frame_state_for ();
234
235         if (!frame_state_for)
236                 return FALSE;
237
238         frame = MONO_CONTEXT_GET_BP (ctx);
239
240         max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack;
241
242         *new_ctx = *ctx;
243
244         memset (&state_in, 0, sizeof (state_in));
245
246         while ((gpointer)frame->next < (gpointer)max_stack) {
247                 gpointer ip, addr = frame->return_address;
248                 void *cfa;
249                 char *tmp, **symbols;
250
251                 if (trace) {
252                         ip = MONO_CONTEXT_GET_IP (new_ctx);
253                         symbols = get_backtrace_symbols (&ip, 1);
254                         if (*trace)
255                                 tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]);
256                         else
257                                 tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]);
258
259                         free (symbols);
260                         g_free (*trace);
261                         *trace = tmp;
262                 }
263
264                 if ((res = frame_state_for (addr, &state_in))) {        
265                         int i;
266
267                         cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset);
268                         frame = (struct stack_frame *)((gint8*)cfa - 8);
269                         for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) {
270                                 int how = res->saved[i];
271                                 long val;
272                                 g_assert ((how == 0) || (how == 1));
273                         
274                                 if (how == 1) {
275                                         val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]);
276                                         set_sigcontext_reg (new_ctx, i, val);
277                                 }
278                         }
279                         new_ctx->esp = (long)cfa;
280
281                         if (res->saved [X86_EBX] == 1 &&
282                             res->saved [X86_EDI] == 1 &&
283                             res->saved [X86_EBP] == 1 &&
284                             res->saved [X86_ESI] == 1 &&
285                             (ji = mono_jit_info_table_find (domain, frame->return_address))) {
286                                 //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE));
287                                 return ji;
288                         }
289
290                 } else {
291                         //printf ("FRAME %p %p %p\n", frame, MONO_CONTEXT_GET_IP (new_ctx), mono_jit_info_table_find (domain, MONO_CONTEXT_GET_IP (new_ctx)));
292
293                         MONO_CONTEXT_SET_IP (new_ctx, frame->return_address);
294                         frame = frame->next;
295                         MONO_CONTEXT_SET_BP (new_ctx, frame);
296                         
297                         /* stop if !frame or when we detect an unexpected managed frame */
298                         if (!frame || mono_jit_info_table_find (domain, frame->return_address)) {
299                                 if (trace) {
300                                         g_free (*trace);
301                                         *trace = NULL;
302                                 }
303                                 return NULL;
304                         }
305                 }
306         }
307
308         //if (!lmf)
309         //g_assert_not_reached ();
310                 
311         if (trace) {
312                 g_free (*trace);
313                 *trace = NULL;
314         }
315         return NULL;
316 }
317
318 #endif
319
320 /*
321  * arch_get_restore_context:
322  *
323  * Returns a pointer to a method which restores a previously saved sigcontext.
324  */
325 static gpointer
326 arch_get_restore_context (void)
327 {
328         static guint8 *start = NULL;
329         guint8 *code;
330
331         if (start)
332                 return start;
333
334         /* restore_contect (struct sigcontext *ctx) */
335         /* we do not restore X86_EAX, X86_EDX */
336
337         start = code = g_malloc (1024);
338         
339         /* load ctx */
340         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
341
342         /* get return address, stored in EDX */
343         x86_mov_reg_membase (code, X86_EDX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
344         /* restore EBX */
345         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
346         /* restore EDI */
347         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
348         /* restore ESI */
349         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
350         /* restore ESP */
351         x86_mov_reg_membase (code, X86_ESP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
352         /* restore EBP */
353         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
354         /* restore ECX. the exception object is passed here to the catch handler */
355         x86_mov_reg_membase (code, X86_ECX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ECX), 4);
356
357         /* jump to the saved IP */
358         x86_jump_reg (code, X86_EDX);
359
360         return start;
361 }
362
363 /*
364  * arch_get_call_filter:
365  *
366  * Returns a pointer to a method which calls an exception filter. We
367  * also use this function to call finally handlers (we pass NULL as 
368  * @exc object in this case).
369  */
370 static gpointer
371 arch_get_call_filter (void)
372 {
373         static guint8 start [64];
374         static int inited = 0;
375         guint8 *code;
376
377         if (inited)
378                 return start;
379
380         inited = 1;
381         /* call_filter (struct sigcontext *ctx, unsigned long eip, gpointer exc) */
382         code = start;
383
384         x86_push_reg (code, X86_EBP);
385         x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
386         x86_push_reg (code, X86_EBX);
387         x86_push_reg (code, X86_EDI);
388         x86_push_reg (code, X86_ESI);
389
390         /* load ctx */
391         x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
392         /* load eip */
393         x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
394         /* save EBP */
395         x86_push_reg (code, X86_EBP);
396         /* push exc */
397         x86_push_membase (code, X86_EBP, 16);
398         /* set new EBP */
399         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
400         /* restore registers used by global register allocation (EBX & ESI) */
401         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
402         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
403         /* save the ESP - this is used by endfinally */
404         x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4);
405         /* call the handler */
406         x86_call_reg (code, X86_ECX);
407         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
408         /* restore EBP */
409         x86_pop_reg (code, X86_EBP);
410         /* restore saved regs */
411         x86_pop_reg (code, X86_ESI);
412         x86_pop_reg (code, X86_EDI);
413         x86_pop_reg (code, X86_EBX);
414         x86_leave (code);
415         x86_ret (code);
416
417         g_assert ((code - start) < 64);
418         return start;
419 }
420
421 static void
422 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
423                  unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
424                  unsigned long eip,  unsigned long esp)
425 {
426         static void (*restore_context) (struct sigcontext *);
427         struct sigcontext ctx;
428
429         if (!restore_context)
430                 restore_context = arch_get_restore_context ();
431
432         /* adjust eip so that it point into the call instruction */
433         eip -= 1;
434
435         ctx.SC_ESP = esp;
436         ctx.SC_EIP = eip;
437         ctx.SC_EBP = ebp;
438         ctx.SC_EDI = edi;
439         ctx.SC_ESI = esi;
440         ctx.SC_EBX = ebx;
441         ctx.SC_EDX = edx;
442         ctx.SC_ECX = ecx;
443         ctx.SC_EAX = eax;
444         
445         arch_handle_exception (&ctx, exc, FALSE);
446         restore_context (&ctx);
447
448         g_assert_not_reached ();
449 }
450
451 /**
452  * arch_get_throw_exception:
453  *
454  * Returns a function pointer which can be used to raise 
455  * exceptions. The returned function has the following 
456  * signature: void (*func) (MonoException *exc); 
457  * For example to raise an arithmetic exception you can use:
458  *
459  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
460  * x86_call_code (code, arch_get_throw_exception ()); 
461  *
462  */
463 gpointer 
464 arch_get_throw_exception (void)
465 {
466         static guint8 start [24];
467         static int inited = 0;
468         guint8 *code;
469
470         if (inited)
471                 return start;
472
473         inited = 1;
474         code = start;
475
476         x86_push_reg (code, X86_ESP);
477         x86_push_membase (code, X86_ESP, 4); /* IP */
478         x86_push_membase (code, X86_ESP, 12); /* exception */
479         x86_push_reg (code, X86_EBP);
480         x86_push_reg (code, X86_EDI);
481         x86_push_reg (code, X86_ESI);
482         x86_push_reg (code, X86_EBX);
483         x86_push_reg (code, X86_EDX);
484         x86_push_reg (code, X86_ECX);
485         x86_push_reg (code, X86_EAX);
486         x86_call_code (code, throw_exception);
487         /* we should never reach this breakpoint */
488         x86_breakpoint (code);
489
490         g_assert ((code - start) < 24);
491         return start;
492 }
493
494 /**
495  * arch_get_throw_exception_by_name:
496  *
497  * Returns a function pointer which can be used to raise 
498  * corlib exceptions. The returned function has the following 
499  * signature: void (*func) (char *exc_name); 
500  * For example to raise an arithmetic exception you can use:
501  *
502  * x86_push_imm (code, "ArithmeticException"); 
503  * x86_call_code (code, arch_get_throw_exception ()); 
504  *
505  */
506 gpointer 
507 arch_get_throw_exception_by_name ()
508 {
509         static guint8 start [32];
510         static int inited = 0;
511         guint8 *code;
512
513         if (inited)
514                 return start;
515
516         inited = 1;
517         code = start;
518
519         /* fixme: we do not save EAX, EDX, ECD - unsure if we need that */
520
521         x86_push_membase (code, X86_ESP, 4); /* exception name */
522         x86_push_imm (code, "System");
523         x86_push_imm (code, mono_defaults.exception_class->image);
524         x86_call_code (code, mono_exception_from_name);
525         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
526         /* save the newly create object (overwrite exception name)*/
527         x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
528         x86_jump_code (code, arch_get_throw_exception ());
529
530         g_assert ((code - start) < 32);
531
532         return start;
533 }       
534
535 static MonoArray *
536 glist_to_array (GList *list) 
537 {
538         MonoDomain *domain = mono_domain_get ();
539         MonoArray *res;
540         int len, i;
541
542         if (!list)
543                 return NULL;
544
545         len = g_list_length (list);
546         res = mono_array_new (domain, mono_defaults.int_class, len);
547
548         for (i = 0; list; list = list->next, i++)
549                 mono_array_set (res, gpointer, i, list->data);
550
551         return res;
552 }
553
554 /* mono_arch_find_jit_info:
555  *
556  * This function is used to gather information from @ctx. It return the 
557  * MonoJitInfo of the corresponding function, unwinds one stack frame and
558  * stores the resulting context into @new_ctx. It also stores a string 
559  * describing the stack location into @trace (if not NULL), and modifies
560  * the @lmf if necessary. @native_offset return the IP offset from the 
561  * start of the function or -1 if that info is not available.
562  */
563 static MonoJitInfo *
564 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *ctx, 
565                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
566                          gboolean *managed)
567 {
568         MonoJitInfo *ji;
569         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
570
571         ji = mono_jit_info_table_find (domain, ip);
572
573         if (trace)
574                 *trace = NULL;
575
576         if (native_offset)
577                 *native_offset = -1;
578
579         if (managed)
580                 *managed = FALSE;
581
582         if (ji != NULL) {
583                 char *source_location, *tmpaddr, *fname;
584                 gint32 address, iloffset;
585                 int offset;
586
587                 *new_ctx = *ctx;
588
589                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
590                         /* remove any unused lmf */
591                         *lmf = (*lmf)->previous_lmf;
592                 }
593
594                 address = (char *)ip - (char *)ji->code_start;
595
596                 if (native_offset)
597                         *native_offset = address;
598
599                 if (managed)
600                         if (!ji->method->wrapper_type)
601                                 *managed = TRUE;
602
603                 if (trace) {
604                         if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
605                                 mono_debug_make_symbols ();
606
607                         source_location = mono_debug_source_location_from_address (ji->method, address, NULL);
608                         iloffset = mono_debug_il_offset_from_address (ji->method, address);
609
610                         if (iloffset < 0)
611                                 tmpaddr = g_strdup_printf ("<0x%05x>", address);
612                         else
613                                 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
614                 
615                         fname = mono_method_full_name (ji->method, TRUE);
616
617                         if (source_location)
618                                 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
619                         else
620                                 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
621
622                         g_free (fname);
623                         g_free (source_location);
624                         g_free (tmpaddr);
625                 }
626                                 
627                 offset = -1;
628                 /* restore caller saved registers */
629                 if (ji->used_regs & X86_EBX_MASK) {
630                         new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
631                         offset--;
632                 }
633                 if (ji->used_regs & X86_EDI_MASK) {
634                         new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
635                         offset--;
636                 }
637                 if (ji->used_regs & X86_ESI_MASK) {
638                         new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
639                 }
640
641                 new_ctx->SC_ESP = ctx->SC_EBP;
642                 /* we substract 1, so that the IP points into the call instruction */
643                 new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
644                 new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
645
646                 return ji;
647 #ifdef MONO_USE_EXC_TABLES
648         } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
649                 return ji;
650 #endif
651         } else if (*lmf) {
652                 
653                 *new_ctx = *ctx;
654
655                 if (!(*lmf)->method)
656                         return (gpointer)-1;
657
658                 if (trace)
659                         *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
660                 
661                 ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip);
662                 g_assert (ji != NULL);
663
664                 new_ctx->SC_ESI = (*lmf)->esi;
665                 new_ctx->SC_EDI = (*lmf)->edi;
666                 new_ctx->SC_EBX = (*lmf)->ebx;
667                 new_ctx->SC_EBP = (*lmf)->ebp;
668                 new_ctx->SC_EIP = (*lmf)->eip;
669                 /* the lmf is always stored on the stack, so the following
670                  * expression points to a stack location which can be used as ESP */
671                 new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
672
673                 *lmf = (*lmf)->previous_lmf;
674
675                 return ji;
676                 
677         }
678
679         return NULL;
680 }
681
682 MonoArray *
683 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
684 {
685         MonoDomain *domain = mono_domain_get ();
686         MonoArray *res;
687         MonoArray *ta = exc->trace_ips;
688         int i, len;
689         
690         len = mono_array_length (ta);
691
692         res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
693
694         for (i = skip; i < len; i++) {
695                 MonoJitInfo *ji;
696                 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
697                 gpointer ip = mono_array_get (ta, gpointer, i);
698
699                 ji = mono_jit_info_table_find (domain, ip);
700                 g_assert (ji != NULL);
701
702                 sf->method = mono_method_get_object (domain, ji->method, NULL);
703                 sf->native_offset = (char *)ip - (char *)ji->code_start;
704                 sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset);
705
706                 if (need_file_info) {
707                         gchar *filename;
708
709                         filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line);
710
711                         sf->filename = mono_string_new (domain, filename ? filename : "<unknown>");
712                         sf->column = 0;
713
714                         g_free (filename);
715                 }
716
717                 mono_array_set (res, gpointer, i, sf);
718         }
719
720         return res;
721 }
722
723 void
724 mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
725         MonoDomain *domain = mono_domain_get ();
726         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
727         MonoLMF *lmf = jit_tls->lmf;
728         MonoJitInfo *ji;
729         gint native_offset, il_offset;
730         gboolean managed;
731
732         MonoContext ctx, new_ctx;
733
734         MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0));
735         MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (1));
736
737         while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
738                 
739                 ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
740                 g_assert (ji);
741
742                 if (ji == (gpointer)-1)
743                         return;
744
745                 il_offset = mono_debug_il_offset_from_address (ji->method, native_offset);
746
747                 if (func (ji->method, native_offset, il_offset, managed, user_data))
748                         return;
749                 
750                 ctx = new_ctx;
751         }
752 }
753
754 MonoBoolean
755 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, 
756                           MonoReflectionMethod **method, 
757                           gint32 *iloffset, gint32 *native_offset,
758                           MonoString **file, gint32 *line, gint32 *column)
759 {
760         MonoDomain *domain = mono_domain_get ();
761         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
762         MonoLMF *lmf = jit_tls->lmf;
763         MonoJitInfo *ji;
764         MonoContext ctx, new_ctx;
765
766         MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info);
767         MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
768
769         skip++;
770
771         do {
772                 ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
773                 ctx = new_ctx;
774                 
775                 if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
776                         return FALSE;
777
778                 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE)
779                         continue;
780                 
781                 skip--;
782
783         } while (skip >= 0);
784
785         *method = mono_method_get_object (domain, ji->method, NULL);
786         *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset);
787
788         if (need_file_info) {
789                 gchar *filename;
790
791                 filename = mono_debug_source_location_from_address (ji->method, *native_offset, line);
792
793                 *file = mono_string_new (domain, filename ? filename : "<unknown>");
794                 *column = 0;
795
796                 g_free (filename);
797         }
798
799         return TRUE;
800 }
801
802 /**
803  * arch_handle_exception:
804  * @ctx: saved processor state
805  * @obj: the exception object
806  * @test_only: only test if the exception is caught, but dont call handlers
807  *
808  *
809  */
810 gboolean
811 arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
812 {
813         MonoDomain *domain = mono_domain_get ();
814         MonoJitInfo *ji;
815         static int (*call_filter) (MonoContext *, gpointer, gpointer) = NULL;
816         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
817         MonoLMF *lmf = jit_tls->lmf;            
818         GList *trace_ips = NULL;
819
820         g_assert (ctx != NULL);
821         if (!obj) {
822                 MonoException *ex = mono_get_exception_null_reference ();
823                 ex->message = mono_string_new (domain, 
824                         "Object reference not set to an instance of an object");
825                 obj = (MonoObject *)ex;
826         } 
827
828         g_assert (mono_object_isinst (obj, mono_defaults.exception_class));
829
830         if (!call_filter)
831                 call_filter = arch_get_call_filter ();
832
833         g_assert (jit_tls->end_of_stack);
834         g_assert (jit_tls->abort_func);
835
836         if (!test_only) {
837                 MonoContext ctx_cp = *ctx;
838                 if (!arch_handle_exception (&ctx_cp, obj, TRUE)) {
839                         if (mono_break_on_exc) {
840                                 if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
841                                         mono_debug_make_symbols ();
842                                 G_BREAKPOINT ();
843                         }
844                         mono_unhandled_exception (obj);
845                 }
846         }
847
848         while (1) {
849                 MonoContext new_ctx;
850                 char *trace = NULL;
851                 
852                 ji = mono_arch_find_jit_info (domain, jit_tls, ctx, &new_ctx, 
853                                               test_only ? &trace : NULL, &lmf, NULL, NULL);
854
855                 if (!ji) {
856                         g_warning ("Exception inside function without unwind info");
857                         g_assert_not_reached ();
858                 }
859
860                 if (ji != (gpointer)-1) {
861                         if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) {
862                                 char *tmp, *strace;
863
864                                 trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
865
866                                 if (!((MonoException*)obj)->stack_trace)
867                                         strace = g_strdup ("");
868                                 else
869                                         strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
870                         
871                                 tmp = g_strdup_printf ("%s%s\n", strace, trace);
872                                 g_free (strace);
873
874                                 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
875
876                                 g_free (tmp);
877                         }
878
879                         if (ji->num_clauses) {
880                                 int i;
881                                 
882                                 g_assert (ji->clauses);
883                         
884                                 for (i = 0; i < ji->num_clauses; i++) {
885                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
886
887                                         if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
888                                             MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) { 
889                                                 /* catch block */
890                                                 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE && 
891                                                      mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) ||
892                                                     ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
893                                                       call_filter (ctx, ei->data.filter, obj)))) {
894                                                         if (test_only) {
895                                                                 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
896                                                                 g_list_free (trace_ips);
897                                                                 return TRUE;
898                                                         }
899                                                         MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
900                                                         MONO_CONTEXT_SET_EXCREG (ctx, obj);
901                                                         jit_tls->lmf = lmf;
902                                                         return 0;
903                                                 }
904                                         }
905                                 }
906
907                                 /* no handler found - we need to call all finally handlers */
908                                 if (!test_only) {
909                                         for (i = 0; i < ji->num_clauses; i++) {
910                                                 MonoJitExceptionInfo *ei = &ji->clauses [i];
911                                                 
912                                                 if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
913                                                     MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
914                                                     (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
915                                                         call_filter (ctx, ei->handler_start, NULL);
916                                                 }
917                                         }
918                                 }
919                         }
920                 }
921
922                 g_free (trace);
923                         
924                 *ctx = new_ctx;
925
926                 if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
927                         if (!test_only) {
928                                 jit_tls->lmf = lmf;
929                                 jit_tls->abort_func (obj);
930                                 g_assert_not_reached ();
931                         } else {
932                                 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
933                                 g_list_free (trace_ips);
934                                 return FALSE;
935                         }
936                 }
937         }
938
939         g_assert_not_reached ();
940 }
941