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