2002-11-28 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 : 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 we detect an unexpected managed frame */
298                         if (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 {
567         MonoJitInfo *ji;
568         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
569
570         ji = mono_jit_info_table_find (domain, ip);
571
572         if (trace)
573                 *trace = NULL;
574
575         if (native_offset)
576                 *native_offset = -1;
577
578         if (ji != NULL) {
579                 char *source_location, *tmpaddr, *fname;
580                 gint32 address, iloffset;
581                 int offset;
582
583                 *new_ctx = *ctx;
584
585                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
586                         /* remove any unused lmf */
587                         *lmf = (*lmf)->previous_lmf;
588                 }
589
590                 address = (char *)ip - (char *)ji->code_start;
591
592                 if (native_offset)
593                         *native_offset = address;
594
595                 if (trace) {
596                         if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
597                                 mono_debug_make_symbols ();
598
599                         source_location = mono_debug_source_location_from_address (ji->method, address, NULL);
600                         iloffset = mono_debug_il_offset_from_address (ji->method, address);
601
602                         if (iloffset < 0)
603                                 tmpaddr = g_strdup_printf ("<0x%05x>", address);
604                         else
605                                 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
606                 
607                         fname = mono_method_full_name (ji->method, TRUE);
608
609                         if (source_location)
610                                 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
611                         else
612                                 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
613
614                         g_free (fname);
615                         g_free (source_location);
616                         g_free (tmpaddr);
617                 }
618                                 
619                 offset = -1;
620                 /* restore caller saved registers */
621                 if (ji->used_regs & X86_EBX_MASK) {
622                         new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
623                         offset--;
624                 }
625                 if (ji->used_regs & X86_EDI_MASK) {
626                         new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
627                         offset--;
628                 }
629                 if (ji->used_regs & X86_ESI_MASK) {
630                         new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
631                 }
632
633                 new_ctx->SC_ESP = ctx->SC_EBP;
634                 /* we substract 1, so that the IP points into the call instruction */
635                 new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
636                 new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
637
638                 return ji;
639 #ifdef MONO_USE_EXC_TABLES
640         } else if ((ji = x86_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
641                 return ji;
642 #endif
643         } else if (*lmf) {
644                 
645                 *new_ctx = *ctx;
646
647                 if (trace)
648                         *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
649                 
650                 ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip);
651                 g_assert (ji != NULL);
652
653                 new_ctx->SC_ESI = (*lmf)->esi;
654                 new_ctx->SC_EDI = (*lmf)->edi;
655                 new_ctx->SC_EBX = (*lmf)->ebx;
656                 new_ctx->SC_EBP = (*lmf)->ebp;
657                 new_ctx->SC_EIP = (*lmf)->eip;
658                 /* the lmf is always stored on the stack, so the following
659                  * expression points to a stack location which can be used as ESP */
660                 new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
661
662                 *lmf = (*lmf)->previous_lmf;
663
664                 return ji;
665                 
666         }
667
668         return NULL;
669 }
670
671 MonoArray *
672 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
673 {
674         MonoDomain *domain = mono_domain_get ();
675         MonoArray *res;
676         MonoArray *ta = exc->trace_ips;
677         int i, len;
678         
679         len = mono_array_length (ta);
680
681         res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
682
683         for (i = skip; i < len; i++) {
684                 MonoJitInfo *ji;
685                 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
686                 gpointer ip = mono_array_get (ta, gpointer, i);
687
688                 ji = mono_jit_info_table_find (domain, ip);
689                 g_assert (ji != NULL);
690
691                 sf->method = mono_method_get_object (domain, ji->method, NULL);
692                 sf->native_offset = (char *)ip - (char *)ji->code_start;
693                 sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset);
694
695                 if (need_file_info) {
696                         gchar *filename;
697
698                         filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line);
699
700                         sf->filename = mono_string_new (domain, filename ? filename : "<unknown>");
701                         sf->column = 0;
702
703                         g_free (filename);
704                 }
705
706                 mono_array_set (res, gpointer, i, sf);
707         }
708
709         return res;
710 }
711
712 void
713 mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
714         MonoDomain *domain = mono_domain_get ();
715         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
716         MonoLMF *lmf = jit_tls->lmf;
717         MonoJitInfo *ji;
718         gint native_offset, il_offset;
719
720         MonoContext ctx, new_ctx;
721
722         MONO_CONTEXT_SET_IP (&ctx, __builtin_return_address (0));
723         MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (1));
724
725         while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
726                 
727                 ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, &native_offset);
728                 g_assert (ji);
729                 
730                 il_offset = mono_debug_il_offset_from_address (ji->method, native_offset);
731
732                 if (func (ji->method, native_offset, il_offset, user_data))
733                         return;
734                 
735                 ctx = new_ctx;
736         }
737 }
738
739 MonoBoolean
740 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, 
741                           MonoReflectionMethod **method, 
742                           gint32 *iloffset, gint32 *native_offset,
743                           MonoString **file, gint32 *line, gint32 *column)
744 {
745         MonoDomain *domain = mono_domain_get ();
746         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
747         MonoLMF *lmf = jit_tls->lmf;
748         MonoJitInfo *ji;
749         MonoContext ctx, new_ctx;
750
751         MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info);
752         MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
753
754         do {
755                 ji = mono_arch_find_jit_info (domain, jit_tls, &ctx, &new_ctx, NULL, &lmf, native_offset);
756                 g_assert (ji);
757
758                 ctx = new_ctx;
759                 
760                 if (MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
761                         return FALSE;
762
763         } while (skip-- > 0);
764
765         *method = mono_method_get_object (domain, ji->method, NULL);
766         *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset);
767
768         if (need_file_info) {
769                 gchar *filename;
770
771                 filename = mono_debug_source_location_from_address (ji->method, *native_offset, line);
772
773                 *file = mono_string_new (domain, filename ? filename : "<unknown>");
774                 *column = 0;
775
776                 g_free (filename);
777         }
778
779         return TRUE;
780 }
781
782 /**
783  * arch_handle_exception:
784  * @ctx: saved processor state
785  * @obj: the exception object
786  * @test_only: only test if the exception is caught, but dont call handlers
787  *
788  *
789  */
790 gboolean
791 arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
792 {
793         MonoDomain *domain = mono_domain_get ();
794         MonoJitInfo *ji;
795         static int (*call_filter) (MonoContext *, gpointer, gpointer) = NULL;
796         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
797         MonoLMF *lmf = jit_tls->lmf;            
798         GList *trace_ips = NULL;
799
800         g_assert (ctx != NULL);
801         if (!obj) {
802                 MonoException *ex = mono_get_exception_null_reference ();
803                 ex->message = mono_string_new (domain, 
804                         "Object reference not set to an instance of an object");
805                 obj = (MonoObject *)ex;
806         } 
807
808         g_assert (mono_object_isinst (obj, mono_defaults.exception_class));
809
810         if (!call_filter)
811                 call_filter = arch_get_call_filter ();
812
813         g_assert (jit_tls->end_of_stack);
814         g_assert (jit_tls->abort_func);
815
816         if (!test_only) {
817                 MonoContext ctx_cp = *ctx;
818                 if (!arch_handle_exception (&ctx_cp, obj, TRUE)) {
819                         if (mono_break_on_exc) {
820                                 if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
821                                         mono_debug_make_symbols ();
822                                 G_BREAKPOINT ();
823                         }
824                         mono_unhandled_exception (obj);
825                 }
826         }
827
828         while (1) {
829                 MonoContext new_ctx;
830                 char *trace = NULL;
831                 
832                 ji = mono_arch_find_jit_info (domain, jit_tls, ctx, &new_ctx, 
833                                               test_only ? &trace : NULL, &lmf, NULL);
834                 if (!ji) {
835                         g_warning ("Exception insinde function without unwind info");
836                         g_assert_not_reached ();
837                 }
838
839                 if (ji->method != mono_start_method) {
840                         
841                         if (test_only) {
842                                 char *tmp, *strace;
843
844                                 trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
845
846                                 if (!((MonoException*)obj)->stack_trace)
847                                         strace = g_strdup ("");
848                                 else
849                                         strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
850
851                                 tmp = g_strdup_printf ("%s%s\n", strace, trace);
852                                 g_free (strace);
853
854                                 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
855
856                                 g_free (tmp);
857                         }
858
859                         if (ji->num_clauses) {
860                                 int i;
861                                 
862                                 g_assert (ji->clauses);
863                         
864                                 for (i = 0; i < ji->num_clauses; i++) {
865                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
866
867                                         if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
868                                             MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) { 
869                                                 /* catch block */
870                                                 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE && 
871                                                      mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) ||
872                                                     ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
873                                                       call_filter (ctx, ei->data.filter, obj)))) {
874                                                         if (test_only) {
875                                                                 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
876                                                                 g_list_free (trace_ips);
877                                                                 return TRUE;
878                                                         }
879                                                         MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
880                                                         MONO_CONTEXT_SET_EXCREG (ctx, obj);
881                                                         jit_tls->lmf = lmf;
882                                                         return 0;
883                                                 }
884                                         }
885                                 }
886
887                                 /* no handler found - we need to call all finally handlers */
888                                 if (!test_only) {
889                                         for (i = 0; i < ji->num_clauses; i++) {
890                                                 MonoJitExceptionInfo *ei = &ji->clauses [i];
891                                                 
892                                                 if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
893                                                     MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
894                                                     (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
895                                                         call_filter (ctx, ei->handler_start, NULL);
896                                                 }
897                                         }
898                                 }
899                         }
900                 }
901
902                 g_free (trace);
903                         
904                 *ctx = new_ctx;
905
906                 if (ji->method == mono_start_method || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
907                         if (!test_only) {
908                                 jit_tls->lmf = lmf;
909                                 jit_tls->abort_func (obj);
910                                 g_assert_not_reached ();
911                         } else {
912                                 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
913                                 g_list_free (trace_ips);
914                                 return FALSE;
915                         }
916                 }
917         }
918
919         g_assert_not_reached ();
920 }
921