2004-04-29 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mono / mini / exceptions-ppc.c
1 /*
2  * exceptions-ppc.c: exception support for PowerPC
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *   Paolo Molaro (lupus@ximian.com)
7  *
8  * (C) 2001 Ximian, Inc.
9  */
10
11 #include <config.h>
12 #include <glib.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <ucontext.h>
16
17 #include <mono/arch/ppc/ppc-codegen.h>
18 #include <mono/metadata/appdomain.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/threads.h>
21 #include <mono/metadata/debug-helpers.h>
22 #include <mono/metadata/exception.h>
23 #include <mono/metadata/mono-debug.h>
24
25 #include "mini.h"
26 #include "mini-ppc.h"
27
28 static gboolean arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only);
29
30 /*
31
32 struct sigcontext {
33     int      sc_onstack;     // sigstack state to restore 
34     int      sc_mask;        // signal mask to restore 
35     int      sc_ir;          // pc 
36     int      sc_psw;         // processor status word 
37     int      sc_sp;          // stack pointer if sc_regs == NULL 
38     void    *sc_regs;        // (kernel private) saved state 
39 };
40
41 struct ucontext {
42         int             uc_onstack;
43         sigset_t        uc_sigmask;     // signal mask used by this context 
44         stack_t         uc_stack;       // stack used by this context 
45         struct ucontext *uc_link;       // pointer to resuming context 
46         size_t          uc_mcsize;      // size of the machine context passed in 
47         mcontext_t      uc_mcontext;    // machine specific context 
48 };
49
50 typedef struct ppc_exception_state {
51         unsigned long dar;      // Fault registers for coredump 
52         unsigned long dsisr;
53         unsigned long exception;// number of powerpc exception taken 
54         unsigned long pad0;     // align to 16 bytes 
55
56         unsigned long pad1[4];  // space in PCB "just in case" 
57 } ppc_exception_state_t;
58
59 typedef struct ppc_vector_state {
60         unsigned long   save_vr[32][4];
61         unsigned long   save_vscr[4];
62         unsigned int    save_pad5[4];
63         unsigned int    save_vrvalid;                   // VRs that have been saved 
64         unsigned int    save_pad6[7];
65 } ppc_vector_state_t;
66
67 typedef struct ppc_float_state {
68         double  fpregs[32];
69
70         unsigned int fpscr_pad; // fpscr is 64 bits, 32 bits of rubbish 
71         unsigned int fpscr;     // floating point status register 
72 } ppc_float_state_t;
73
74 typedef struct ppc_thread_state {
75         unsigned int srr0;      // Instruction address register (PC) 
76         unsigned int srr1;      // Machine state register (supervisor) 
77         unsigned int r0;
78         unsigned int r1;
79         unsigned int r2;
80         ... 
81         unsigned int r31;
82         unsigned int cr;        // Condition register 
83         unsigned int xer;       // User's integer exception register 
84         unsigned int lr;        // Link register 
85         unsigned int ctr;       // Count register 
86         unsigned int mq;        // MQ register (601 only) 
87
88         unsigned int vrsave;    // Vector Save Register 
89 } ppc_thread_state_t;
90
91 struct mcontext {
92         ppc_exception_state_t   es;
93         ppc_thread_state_t      ss;
94         ppc_float_state_t       fs;
95         ppc_vector_state_t      vs;
96 };
97
98 typedef struct mcontext  * mcontext_t;
99
100 Linux/PPC instead has:
101 struct sigcontext {
102         unsigned long   _unused[4];
103         int             signal;
104         unsigned long   handler;
105         unsigned long   oldmask;
106         struct pt_regs  *regs;
107 };
108 struct pt_regs {
109         unsigned long gpr[32];
110         unsigned long nip;
111         unsigned long msr;
112         unsigned long orig_gpr3;        // Used for restarting system calls 
113         unsigned long ctr;
114         unsigned long link;
115         unsigned long xer;
116         unsigned long ccr;
117         unsigned long mq;               // 601 only (not used at present) 
118                                         // Used on APUS to hold IPL value. 
119         unsigned long trap;             // Reason for being here 
120         // N.B. for critical exceptions on 4xx, the dar and dsisr
121         // fields are overloaded to hold srr0 and srr1. 
122         unsigned long dar;              // Fault registers 
123         unsigned long dsisr;            // on 4xx/Book-E used for ESR 
124         unsigned long result;           // Result of a system call 
125 };
126 struct mcontext {
127         elf_gregset_t   mc_gregs;
128         elf_fpregset_t  mc_fregs;
129         unsigned long   mc_pad[2];
130         elf_vrregset_t  mc_vregs __attribute__((__aligned__(16)));
131 };
132
133 struct ucontext {
134         unsigned long    uc_flags;
135         struct ucontext *uc_link;
136         stack_t          uc_stack;
137         int              uc_pad[7];
138         struct mcontext *uc_regs;       // points to uc_mcontext field 
139         sigset_t         uc_sigmask;
140         // glibc has 1024-bit signal masks, ours are 64-bit 
141         int              uc_maskext[30];
142         int              uc_pad2[3];
143         struct mcontext  uc_mcontext;
144 };
145
146 #define ELF_NGREG       48      // includes nip, msr, lr, etc. 
147 #define ELF_NFPREG      33      // includes fpscr 
148
149 // General registers 
150 typedef unsigned long elf_greg_t;
151 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
152
153 // Floating point registers 
154 typedef double elf_fpreg_t;
155 typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
156
157
158 */
159
160 /* we have the stack pointer, not the base pointer in sigcontext */
161 #define MONO_CONTEXT_SET_IP(ctx,ip) do { (ctx)->sc_ir = (int)ip; } while (0); 
162 #define MONO_CONTEXT_SET_BP(ctx,bp) do { (ctx)->sc_sp = (int)bp; } while (0); 
163
164 #define MONO_CONTEXT_GET_IP(ctx) ((gpointer)((ctx)->sc_ir))
165 #define MONO_CONTEXT_GET_BP(ctx) ((gpointer)((ctx)->sc_sp))
166
167 #ifdef __APPLE__
168
169 typedef struct {
170         unsigned long sp;
171         unsigned long unused1;
172         unsigned long lr;
173 } MonoPPCStackFrame;
174
175 #else
176
177 typedef struct {
178         unsigned long sp;
179         unsigned long lr;
180 } MonoPPCStackFrame;
181
182 #endif
183
184 /* disbale this for now */
185 #undef MONO_USE_EXC_TABLES
186
187 #ifdef MONO_USE_EXC_TABLES
188
189 /*************************************/
190 /*    STACK UNWINDING STUFF          */
191 /*************************************/
192
193 /* These definitions are from unwind-dw2.c in glibc 2.2.5 */
194
195 /* For x86 */
196 #define DWARF_FRAME_REGISTERS 17
197
198 typedef struct frame_state
199 {
200   void *cfa;
201   void *eh_ptr;
202   long cfa_offset;
203   long args_size;
204   long reg_or_offset[DWARF_FRAME_REGISTERS+1];
205   unsigned short cfa_reg;
206   unsigned short retaddr_column;
207   char saved[DWARF_FRAME_REGISTERS+1];
208 } frame_state;
209
210 #if 0
211
212 static long
213 get_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum)
214 {
215         switch (dwarf_regnum) {
216         case X86_EAX:
217                 return ctx->SC_EAX;
218         case X86_EBX:
219                 return ctx->SC_EBX;
220         case X86_ECX:
221                 return ctx->SC_ECX;
222         case X86_EDX:
223                 return ctx->SC_EDX;
224         case X86_ESI:
225                 return ctx->SC_ESI;
226         case X86_EDI:
227                 return ctx->SC_EDI;
228         case X86_EBP:
229                 return ctx->SC_EBP;
230         case X86_ESP:
231                 return ctx->SC_ESP;
232         default:
233                 g_assert_not_reached ();
234         }
235
236         return 0;
237 }
238
239 static void
240 set_sigcontext_reg (struct sigcontext *ctx, int dwarf_regnum, long value)
241 {
242         switch (dwarf_regnum) {
243         case X86_EAX:
244                 ctx->SC_EAX = value;
245                 break;
246         case X86_EBX:
247                 ctx->SC_EBX = value;
248                 break;
249         case X86_ECX:
250                 ctx->SC_ECX = value;
251                 break;
252         case X86_EDX:
253                 ctx->SC_EDX = value;
254                 break;
255         case X86_ESI:
256                 ctx->SC_ESI = value;
257                 break;
258         case X86_EDI:
259                 ctx->SC_EDI = value;
260                 break;
261         case X86_EBP:
262                 ctx->SC_EBP = value;
263                 break;
264         case X86_ESP:
265                 ctx->SC_ESP = value;
266                 break;
267         case 8:
268                 ctx->SC_EIP = value;
269                 break;
270         default:
271                 g_assert_not_reached ();
272         }
273 }
274
275 typedef struct frame_state * (*framesf) (void *, struct frame_state *);
276
277 static framesf frame_state_for = NULL;
278
279 static gboolean inited = FALSE;
280
281 typedef char ** (*get_backtrace_symbols_type) (void *__const *__array, int __size);
282
283 static get_backtrace_symbols_type get_backtrace_symbols = NULL;
284
285 static void
286 init_frame_state_for (void)
287 {
288         GModule *module;
289
290         /*
291          * There are two versions of __frame_state_for: one in libgcc.a and the
292          * other in glibc.so. We need the version from glibc.
293          * For more info, see this:
294          * http://gcc.gnu.org/ml/gcc/2002-08/msg00192.html
295          */
296         if ((module = g_module_open ("libc.so.6", G_MODULE_BIND_LAZY))) {
297         
298                 if (!g_module_symbol (module, "__frame_state_for", (gpointer*)&frame_state_for))
299                         frame_state_for = NULL;
300
301                 if (!g_module_symbol (module, "backtrace_symbols", (gpointer*)&get_backtrace_symbols)) {
302                         get_backtrace_symbols = NULL;
303                         frame_state_for = NULL;
304                 }
305
306                 g_module_close (module);
307         }
308
309         inited = TRUE;
310 }
311
312 #endif
313
314 /* mono_arch_has_unwind_info:
315  *
316  * Tests if a function has an DWARF exception table able to restore
317  * all caller saved registers. 
318  */
319 gboolean
320 mono_arch_has_unwind_info (MonoMethod *method)
321 {
322 #if 0
323         struct frame_state state_in;
324         struct frame_state *res;
325
326         if (!inited) 
327                 init_frame_state_for ();
328         
329         if (!frame_state_for)
330                 return FALSE;
331
332         g_assert (method->addr);
333
334         memset (&state_in, 0, sizeof (state_in));
335
336         /* offset 10 is just a guess, but it works for all methods tested */
337         if ((res = frame_state_for ((char *)method->addr + 10, &state_in))) {
338
339                 if (res->saved [X86_EBX] != 1 ||
340                     res->saved [X86_EDI] != 1 ||
341                     res->saved [X86_EBP] != 1 ||
342                     res->saved [X86_ESI] != 1) {
343                         return FALSE;
344                 }
345                 return TRUE;
346         } else {
347                 return FALSE;
348         }
349 #else
350         return FALSE;
351 #endif
352 }
353
354 struct stack_frame
355 {
356   void *next;
357   void *return_address;
358 };
359
360 static MonoJitInfo *
361 ppc_unwind_native_frame (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *ctx, 
362                          MonoContext *new_ctx, MonoLMF *lmf, char **trace)
363 {
364 #if 0
365         struct stack_frame *frame;
366         gpointer max_stack;
367         MonoJitInfo *ji;
368         struct frame_state state_in;
369         struct frame_state *res;
370
371         if (trace)
372                 *trace = NULL;
373
374         if (!inited) 
375                 init_frame_state_for ();
376
377         if (!frame_state_for)
378                 return FALSE;
379
380         frame = MONO_CONTEXT_GET_BP (ctx);
381
382         max_stack = lmf && lmf->method ? lmf : jit_tls->end_of_stack;
383
384         *new_ctx = *ctx;
385
386         memset (&state_in, 0, sizeof (state_in));
387
388         while ((gpointer)frame->next < (gpointer)max_stack) {
389                 gpointer ip, addr = frame->return_address;
390                 void *cfa;
391                 char *tmp, **symbols;
392
393                 if (trace) {
394                         ip = MONO_CONTEXT_GET_IP (new_ctx);
395                         symbols = get_backtrace_symbols (&ip, 1);
396                         if (*trace)
397                                 tmp = g_strdup_printf ("%s\nin (unmanaged) %s", *trace, symbols [0]);
398                         else
399                                 tmp = g_strdup_printf ("in (unmanaged) %s", symbols [0]);
400
401                         free (symbols);
402                         g_free (*trace);
403                         *trace = tmp;
404                 }
405
406                 if ((res = frame_state_for (addr, &state_in))) {        
407                         int i;
408
409                         cfa = (gint8*) (get_sigcontext_reg (new_ctx, res->cfa_reg) + res->cfa_offset);
410                         frame = (struct stack_frame *)((gint8*)cfa - 8);
411                         for (i = 0; i < DWARF_FRAME_REGISTERS + 1; i++) {
412                                 int how = res->saved[i];
413                                 long val;
414                                 g_assert ((how == 0) || (how == 1));
415                         
416                                 if (how == 1) {
417                                         val = * (long*) ((gint8*)cfa + res->reg_or_offset[i]);
418                                         set_sigcontext_reg (new_ctx, i, val);
419                                 }
420                         }
421                         new_ctx->SC_ESP = (long)cfa;
422
423                         if (res->saved [X86_EBX] == 1 &&
424                             res->saved [X86_EDI] == 1 &&
425                             res->saved [X86_EBP] == 1 &&
426                             res->saved [X86_ESI] == 1 &&
427                             (ji = mono_jit_info_table_find (domain, frame->return_address))) {
428                                 //printf ("FRAME CFA %s\n", mono_method_full_name (ji->method, TRUE));
429                                 return ji;
430                         }
431
432                 } else {
433                         //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)));
434
435                         MONO_CONTEXT_SET_IP (new_ctx, frame->return_address);
436                         frame = frame->next;
437                         MONO_CONTEXT_SET_BP (new_ctx, frame);
438
439                         /* stop if !frame or when we detect an unexpected managed frame */
440                         if (!frame || mono_jit_info_table_find (domain, frame->return_address)) {
441                                 if (trace) {
442                                         g_free (*trace);
443                                         *trace = NULL;
444                                 }
445                                 return NULL;
446                         }
447                 }
448         }
449
450         //if (!lmf)
451         //g_assert_not_reached ();
452
453         if (trace) {
454                 g_free (*trace);
455                 *trace = NULL;
456         }
457 #endif
458         return NULL;
459 }
460
461 #endif
462
463         /* FIXME: restore the rest of the registers */
464 #define restore_regs_from_context(ctx_reg,ip_reg,tmp_reg) do {  \
465                 ppc_lwz (code, ip_reg, G_STRUCT_OFFSET (MonoContext, sc_ir), ctx_reg);  \
466                 ppc_lwz (code, ppc_sp, G_STRUCT_OFFSET (MonoContext, sc_sp), ctx_reg);  \
467                 ppc_lmw (code, ppc_r13, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs));  \
468         } while (0)
469
470 /* nothing to do */
471 #define setup_context(ctx)
472
473 /*
474  * arch_get_restore_context:
475  *
476  * Returns a pointer to a method which restores a previously saved sigcontext.
477  * The first argument in r3 is the pointer to the context.
478  */
479 static gpointer
480 arch_get_restore_context (void)
481 {
482         guint8 *code;
483         static guint8 start [64];
484         static int inited = 0;
485
486         if (inited)
487                 return start;
488         inited = 1;
489
490         code = start;
491         restore_regs_from_context (ppc_r3, ppc_r4, ppc_r5);
492         //ppc_break (code);
493         /* jump to the saved IP */
494         ppc_mtctr (code, ppc_r4);
495         ppc_bcctr (code, PPC_BR_ALWAYS, 0);
496         /* never reached */
497         ppc_break (code);
498
499         g_assert ((code - start) < sizeof(start));
500         return start;
501 }
502
503 /*
504  * arch_get_call_filter:
505  *
506  * Returns a pointer to a method which calls an exception filter. We
507  * also use this function to call finally handlers (we pass NULL as 
508  * @exc object in this case).
509  */
510 static gpointer
511 arch_get_call_filter (void)
512 {
513         static guint8 start [256];
514         static int inited = 0;
515         guint8 *code;
516         int alloc_size, pos, i;
517
518         if (inited)
519                 return start;
520
521         inited = 1;
522         /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
523         code = start;
524
525         ppc_mflr (code, ppc_r0);
526         ppc_stw (code, ppc_r0, PPC_RET_ADDR_OFFSET, ppc_sp);
527         alloc_size = 32 + PPC_MINIMAL_STACK_SIZE + (sizeof (gulong) * 19 + sizeof (gdouble) * 18);
528         // align to PPC_STACK_ALIGNMENT bytes
529         if (alloc_size & (PPC_STACK_ALIGNMENT - 1))
530                 alloc_size += PPC_STACK_ALIGNMENT - (alloc_size & (PPC_STACK_ALIGNMENT - 1));
531         g_assert ((alloc_size & (PPC_STACK_ALIGNMENT-1)) == 0);
532         ppc_stwu (code, ppc_sp, -alloc_size, ppc_sp);
533         /* save all the regs on the stack */
534         pos = 32 + PPC_MINIMAL_STACK_SIZE;
535         ppc_stmw (code, ppc_r13, ppc_sp, pos);
536         pos += sizeof (gulong) * 19;
537         for (i = 14; i < 32; ++i) {
538                 ppc_stfd (code, i, pos, ppc_sp);
539                 pos += sizeof (gdouble);
540         }
541         /* restore all the regs from ctx (in r3) */
542         /* FIXME: calling the filter code must not restore the stack pointer */
543         restore_regs_from_context (ppc_r3, ppc_r6, ppc_r7);
544         /* call handler at eip (r4) and set the first arg with the exception (r5) */
545         ppc_mtctr (code, ppc_r4);
546         ppc_mr (code, ppc_r3, ppc_r5);
547         ppc_bcctrl (code, PPC_BR_ALWAYS, 0);
548         /* restore all the regs from the stack */
549         pos = 32 + PPC_MINIMAL_STACK_SIZE;
550         ppc_lmw (code, ppc_r13, ppc_sp, pos);
551         pos += sizeof (gulong) * 19;
552         for (i = 14; i < 32; ++i) {
553                 ppc_lfd (code, i, pos, ppc_sp);
554                 pos += sizeof (gdouble);
555         }
556         ppc_lwz (code, ppc_r0, alloc_size + PPC_RET_ADDR_OFFSET, ppc_sp);
557         ppc_mtlr (code, ppc_r0);
558         ppc_addic (code, ppc_sp, ppc_sp, alloc_size);
559         ppc_blr (code);
560
561         g_assert ((code - start) < sizeof(start));
562         return start;
563 }
564
565 static void
566 throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
567 {
568         static void (*restore_context) (MonoContext *);
569         MonoContext ctx;
570
571         if (!restore_context)
572                 restore_context = arch_get_restore_context ();
573
574         /* adjust eip so that it point into the call instruction */
575         eip -= 4;
576
577         setup_context (&ctx);
578
579         /*printf ("stack in throw: %p\n", esp);*/
580         MONO_CONTEXT_SET_BP (&ctx, esp);
581         MONO_CONTEXT_SET_IP (&ctx, eip);
582         memcpy (&ctx.regs, int_regs, sizeof (gulong) * 19);
583
584         arch_handle_exception (&ctx, exc, FALSE);
585         restore_context (&ctx);
586
587         g_assert_not_reached ();
588 }
589
590 /**
591  * arch_get_throw_exception_generic:
592  *
593  * Returns a function pointer which can be used to raise 
594  * exceptions. The returned function has the following 
595  * signature: void (*func) (MonoException *exc); or
596  * void (*func) (char *exc_name);
597  *
598  */
599 static gpointer 
600 mono_arch_get_throw_exception_generic (guint8 *start, int size, int by_name)
601 {
602         guint8 *code;
603         int alloc_size, pos, i;
604
605         code = start;
606
607         ppc_mflr (code, ppc_r0);
608         ppc_stw (code, ppc_r0, PPC_RET_ADDR_OFFSET, ppc_sp);
609         alloc_size = 32 + PPC_MINIMAL_STACK_SIZE + (sizeof (gulong) * 19 + sizeof (gdouble) * 18);
610         // align to PPC_STACK_ALIGNMENT bytes
611         if (alloc_size & (PPC_STACK_ALIGNMENT - 1)) {
612                 alloc_size += PPC_STACK_ALIGNMENT - 1;
613                 alloc_size &= ~(PPC_STACK_ALIGNMENT - 1);
614         }
615         /*g_print ("alloc size = %d\n", alloc_size);*/
616         g_assert ((alloc_size & (PPC_STACK_ALIGNMENT-1)) == 0);
617         ppc_stwu (code, ppc_sp, -alloc_size, ppc_sp);
618         //ppc_break (code);
619         if (by_name) {
620                 ppc_mr (code, ppc_r5, ppc_r3);
621                 ppc_load (code, ppc_r3, mono_defaults.corlib);
622                 ppc_load (code, ppc_r4, "System");
623                 ppc_bl (code, 0);
624                 ppc_patch (code - 4, mono_exception_from_name);
625         }
626         /* save all the regs on the stack */
627         pos = 32 + PPC_MINIMAL_STACK_SIZE;
628         ppc_stmw (code, ppc_r13, ppc_sp, pos);
629         pos += sizeof (gulong) * 19;
630         /* align for doubles */
631         pos += 7;
632         pos &= ~7;
633         for (i = 14; i < 32; ++i) {
634                 ppc_stfd (code, i, pos, ppc_sp);
635                 pos += sizeof (gdouble);
636         }
637         /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */
638         /* caller sp */
639         ppc_lwz (code, ppc_r5, 0, ppc_sp); 
640         /* exc is already in place in r3 */
641         if (by_name)
642                 ppc_lwz (code, ppc_r4, PPC_RET_ADDR_OFFSET, ppc_r5); 
643         else
644                 ppc_mr (code, ppc_r4, ppc_r0); /* caller ip */
645         /* pointer to the saved int regs */
646         pos = 32 + PPC_MINIMAL_STACK_SIZE;
647         ppc_addi (code, ppc_r6, ppc_sp, pos);
648         /* pointer to the saved fp regs */
649         pos += sizeof (gulong) * 19;
650         ppc_addi (code, ppc_r7, ppc_sp, pos);
651         ppc_bl (code, 0);
652         ppc_patch (code - 4, throw_exception);
653         /* we should never reach this breakpoint */
654         ppc_break (code);
655         g_assert ((code - start) < size);
656         return start;
657 }
658
659 /**
660  * arch_get_throw_exception:
661  *
662  * Returns a function pointer which can be used to raise 
663  * exceptions. The returned function has the following 
664  * signature: void (*func) (MonoException *exc); 
665  * For example to raise an arithmetic exception you can use:
666  *
667  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
668  * x86_call_code (code, arch_get_throw_exception ()); 
669  *
670  */
671 gpointer 
672 mono_arch_get_throw_exception (void)
673 {
674         static guint8 start [128];
675         static int inited = 0;
676
677         if (inited)
678                 return start;
679         mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE);
680         inited = 1;
681         return start;
682 }
683
684 /**
685  * arch_get_throw_exception_by_name:
686  *
687  * Returns a function pointer which can be used to raise 
688  * corlib exceptions. The returned function has the following 
689  * signature: void (*func) (char *exc_name); 
690  * For example to raise an arithmetic exception you can use:
691  *
692  * x86_push_imm (code, "ArithmeticException"); 
693  * x86_call_code (code, arch_get_throw_exception_by_name ()); 
694  *
695  */
696 gpointer 
697 mono_arch_get_throw_exception_by_name (void)
698 {
699         static guint8 start [160];
700         static int inited = 0;
701
702         if (inited)
703                 return start;
704         mono_arch_get_throw_exception_generic (start, sizeof (start), TRUE);
705         inited = 1;
706         return start;
707 }       
708
709 static MonoArray *
710 glist_to_array (GList *list) 
711 {
712         MonoDomain *domain = mono_domain_get ();
713         MonoArray *res;
714         int len, i;
715
716         if (!list)
717                 return NULL;
718
719         len = g_list_length (list);
720         res = mono_array_new (domain, mono_defaults.int_class, len);
721
722         for (i = 0; list; list = list->next, i++)
723                 mono_array_set (res, gpointer, i, list->data);
724
725         return res;
726 }
727
728 /* mono_arch_find_jit_info:
729  *
730  * This function is used to gather information from @ctx. It return the 
731  * MonoJitInfo of the corresponding function, unwinds one stack frame and
732  * stores the resulting context into @new_ctx. It also stores a string 
733  * describing the stack location into @trace (if not NULL), and modifies
734  * the @lmf if necessary. @native_offset return the IP offset from the 
735  * start of the function or -1 if that info is not available.
736  */
737 MonoJitInfo *
738 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji,
739                          MonoContext *ctx, MonoContext *new_ctx, char **trace, MonoLMF **lmf,
740                          int *native_offset, gboolean *managed)
741 {
742         MonoJitInfo *ji;
743         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
744         unsigned long *ptr;
745         char *p;
746         MonoPPCStackFrame *sframe;
747
748         /* Avoid costly table lookup during stack overflow */
749         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
750                 ji = prev_ji;
751         else
752                 ji = mono_jit_info_table_find (domain, ip);
753
754         if (trace)
755                 *trace = NULL;
756
757         if (native_offset)
758                 *native_offset = -1;
759
760         if (managed)
761                 *managed = FALSE;
762
763         if (ji != NULL) {
764                 char *source_location, *tmpaddr, *fname;
765                 gint32 address, iloffset;
766                 int offset, i;
767                 gulong *ctx_regs;
768
769                 *new_ctx = *ctx;
770                 setup_context (new_ctx);
771
772                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
773                         /* remove any unused lmf */
774                         *lmf = (*lmf)->previous_lmf;
775                 }
776
777                 address = (char *)ip - (char *)ji->code_start;
778
779                 if (native_offset)
780                         *native_offset = address;
781
782                 if (managed)
783                         if (!ji->method->wrapper_type)
784                                 *managed = TRUE;
785
786                 if (trace) {
787                         source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
788                         iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
789
790                         if (iloffset < 0)
791                                 tmpaddr = g_strdup_printf ("<0x%05x>", address);
792                         else
793                                 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
794                 
795                         fname = mono_method_full_name (ji->method, TRUE);
796
797                         if (source_location)
798                                 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
799                         else
800                                 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
801
802                         g_free (fname);
803                         g_free (source_location);
804                         g_free (tmpaddr);
805                 }
806 #if 0                           
807                 offset = -1;
808                 /* restore caller saved registers */
809                 if (ji->used_regs & X86_EBX_MASK) {
810                         new_ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
811                         offset--;
812                 }
813                 if (ji->used_regs & X86_EDI_MASK) {
814                         new_ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
815                         offset--;
816                 }
817                 if (ji->used_regs & X86_ESI_MASK) {
818                         new_ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
819                 }
820
821                 new_ctx->SC_ESP = ctx->SC_EBP;
822                 /* we substract 1, so that the IP points into the call instruction */
823                 new_ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 1;
824                 new_ctx->SC_EBP = *((int *)ctx->SC_EBP);
825 #endif
826                 sframe = (MonoPPCStackFrame*)MONO_CONTEXT_GET_BP (ctx);
827                 MONO_CONTEXT_SET_BP (new_ctx, sframe->sp);
828                 if (ji->used_regs) {
829                         /* keep updated with emit_prolog in mini-ppc.c */
830                         offset = 0;
831                         for (i = 13; i < 32; ++i) {
832                                 if (ji->used_regs & (1 << i)) {
833                                         offset += sizeof (gulong);
834                                         new_ctx->regs [i] = *(gulong*)((char*)sframe->sp - offset);
835                                 }
836                         }
837                 }
838                 /* the calling IP is in the parent frame */
839                 sframe = (MonoPPCStackFrame*)sframe->sp;
840                 /* we substract 4, so that the IP points into the call instruction */
841                 MONO_CONTEXT_SET_IP (new_ctx, sframe->lr - 4);
842                 *res = *ji;
843                 return res;
844 #ifdef MONO_USE_EXC_TABLES
845         } else if ((ji = ppc_unwind_native_frame (domain, jit_tls, ctx, new_ctx, *lmf, trace))) {
846                 *res = *ji;
847                 return res;
848 #endif
849         } else if (*lmf) {
850                 
851                 *new_ctx = *ctx;
852                 setup_context (new_ctx);
853
854                 if (!(*lmf)->method)
855                         return (gpointer)-1;
856
857                 if (trace)
858                         *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
859                 
860                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->eip))) {
861                         *res = *ji;
862                 } else {
863                         memset (res, 0, sizeof (MonoJitInfo));
864                         res->method = (*lmf)->method;
865                 }
866
867 #if 0
868                 new_ctx->SC_ESI = (*lmf)->esi;
869                 new_ctx->SC_EDI = (*lmf)->edi;
870                 new_ctx->SC_EBX = (*lmf)->ebx;
871                 new_ctx->SC_EBP = (*lmf)->ebp;
872                 new_ctx->SC_EIP = (*lmf)->eip;
873                 /* the lmf is always stored on the stack, so the following
874                  * expression points to a stack location which can be used as ESP */
875                 new_ctx->SC_ESP = (unsigned long)&((*lmf)->eip);
876 #endif
877                 /*sframe = (MonoPPCStackFrame*)MONO_CONTEXT_GET_BP (ctx);
878                 MONO_CONTEXT_SET_BP (new_ctx, sframe->sp);
879                 MONO_CONTEXT_SET_IP (new_ctx, sframe->lr);*/
880                 MONO_CONTEXT_SET_BP (new_ctx, (*lmf)->ebp);
881                 MONO_CONTEXT_SET_IP (new_ctx, (*lmf)->eip);
882                 memcpy (&new_ctx->regs, (*lmf)->iregs, sizeof (gulong) * 19);
883                 *lmf = (*lmf)->previous_lmf;
884
885                 return res;
886                 
887         }
888
889         return NULL;
890 }
891
892 MonoArray *
893 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
894 {
895         MonoDomain *domain = mono_domain_get ();
896         MonoArray *res;
897         MonoArray *ta = exc->trace_ips;
898         int i, len;
899         
900         len = mono_array_length (ta);
901
902         res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
903
904         for (i = skip; i < len; i++) {
905                 MonoJitInfo *ji;
906                 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
907                 gpointer ip = mono_array_get (ta, gpointer, i);
908
909                 ji = mono_jit_info_table_find (domain, ip);
910                 g_assert (ji != NULL);
911
912                 sf->method = mono_method_get_object (domain, ji->method, NULL);
913                 sf->native_offset = (char *)ip - (char *)ji->code_start;
914
915                 sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset, domain);
916
917                 if (need_file_info) {
918                         gchar *filename;
919                         
920                         filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line, domain);
921
922                         sf->filename = filename? mono_string_new (domain, filename): NULL;
923                         sf->column = 0;
924
925                         g_free (filename);
926                 }
927
928                 mono_array_set (res, gpointer, i, sf);
929         }
930
931         return res;
932 }
933
934 void
935 mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
936         MonoDomain *domain = mono_domain_get ();
937         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
938         MonoLMF *lmf = jit_tls->lmf;
939         MonoJitInfo *ji, rji;
940         gint native_offset, il_offset;
941         gboolean managed;
942         MonoPPCStackFrame *sframe;
943
944         MonoContext ctx, new_ctx;
945
946         setup_context (&ctx);
947         setup_context (&new_ctx);
948
949 #ifdef __APPLE__
950         __asm__ volatile("lwz   %0,0(r1)" : "=r" (sframe));
951 #else
952         __asm__ volatile("lwz   %0,0(1)" : "=r" (sframe));
953 #endif
954         MONO_CONTEXT_SET_IP (&ctx, sframe->lr);
955         MONO_CONTEXT_SET_BP (&ctx, sframe->sp);
956
957         while (MONO_CONTEXT_GET_BP (&ctx) < jit_tls->end_of_stack) {
958                 
959                 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &rji, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
960                 g_assert (ji);
961
962                 if (ji == (gpointer)-1)
963                         return;
964
965                 il_offset = mono_debug_il_offset_from_address (ji->method, native_offset, domain);
966
967                 if (func (ji->method, native_offset, il_offset, managed, user_data))
968                         return;
969                 
970                 ctx = new_ctx;
971                 setup_context (&ctx);
972         }
973 }
974
975 MonoBoolean
976 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, 
977                           MonoReflectionMethod **method, 
978                           gint32 *iloffset, gint32 *native_offset,
979                           MonoString **file, gint32 *line, gint32 *column)
980 {
981         MonoDomain *domain = mono_domain_get ();
982         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
983         MonoLMF *lmf = jit_tls->lmf;
984         MonoJitInfo *ji, rji;
985         MonoContext ctx, new_ctx;
986
987         MONO_CONTEXT_SET_IP (&ctx, ves_icall_get_frame_info);
988         MONO_CONTEXT_SET_BP (&ctx, __builtin_frame_address (0));
989
990         skip++;
991
992         do {
993                 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &rji, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL);
994
995                 ctx = new_ctx;
996                 
997                 if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_BP (&ctx) >= jit_tls->end_of_stack)
998                         return FALSE;
999
1000                 /* skip all wrappers ??*/
1001                 if (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE ||
1002                     ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK ||
1003                     ji->method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE)
1004                         continue;
1005
1006                 skip--;
1007
1008         } while (skip >= 0);
1009
1010         *method = mono_method_get_object (domain, ji->method, NULL);
1011         *iloffset = mono_debug_il_offset_from_address (ji->method, *native_offset, domain);
1012
1013         if (need_file_info) {
1014                 gchar *filename;
1015
1016                 filename = mono_debug_source_location_from_address (ji->method, *native_offset, line, domain);
1017
1018                 *file = filename? mono_string_new (domain, filename): NULL;
1019                 *column = 0;
1020
1021                 g_free (filename);
1022         }
1023
1024         return TRUE;
1025 }
1026
1027 /*
1028  * This is the function called from the signal handler
1029  */
1030 #ifdef __APPLE__
1031 gboolean
1032 mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only)
1033 {
1034         struct ucontext *uc = ctx;
1035         MonoContext mctx;
1036         gboolean result;
1037         
1038         mctx.sc_ir = uc->uc_mcontext->ss.srr0;
1039         mctx.sc_sp = uc->uc_mcontext->ss.r1;
1040         memcpy (&mctx.regs, &uc->uc_mcontext->ss.r13, sizeof (gulong) * 19);
1041         memcpy (&mctx.fregs, &uc->uc_mcontext->fs.fpregs [14], sizeof (double) * 20);
1042
1043         result = arch_handle_exception (&mctx, obj, test_only);
1044         /* restore the context so that returning from the signal handler will invoke
1045          * the catch clause 
1046          */
1047         uc->uc_mcontext->ss.srr0 = mctx.sc_ir;
1048         uc->uc_mcontext->ss.r1 = mctx.sc_sp;
1049         memcpy (&uc->uc_mcontext->ss.r13, &mctx.regs, sizeof (gulong) * 19);
1050         memcpy (&uc->uc_mcontext->fs.fpregs [14], &mctx.fregs, sizeof (double) * 20);
1051         return result;
1052 }
1053 #else
1054 /* Linux */
1055 gboolean
1056 mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only)
1057 {
1058         struct ucontext *uc = ctx;
1059         MonoContext mctx;
1060         gboolean result;
1061         
1062         mctx.sc_ir = uc->uc_mcontext.uc_regs->gregs [PT_NIP];
1063         mctx.sc_sp = uc->uc_mcontext.uc_regs->gregs [PT_R1];
1064         memcpy (&mctx.regs, &uc->uc_mcontext.uc_regs->gregs [PT_R13], sizeof (gulong) * 19);
1065         memcpy (&mctx.fregs, &uc->uc_mcontext.uc_regs->fpregs.fpregs [14], sizeof (double) * 20);
1066
1067         result = arch_handle_exception (&mctx, obj, test_only);
1068         /* restore the context so that returning from the signal handler will invoke
1069          * the catch clause 
1070          */
1071         uc->uc_mcontext.uc_regs->gregs [PT_NIP] = mctx.sc_ir;
1072         uc->uc_mcontext.uc_regs->gregs [PT_R1] = mctx.sc_sp;
1073         memcpy (&uc->uc_mcontext.uc_regs->gregs [PT_R13], &mctx.regs, sizeof (gulong) * 19);
1074         memcpy (&uc->uc_mcontext.uc_regs->fpregs.fpregs [14], &mctx.fregs, sizeof (double) * 20);
1075         return result;
1076 }
1077 #endif
1078
1079 /**
1080  * arch_handle_exception:
1081  * @ctx: saved processor state
1082  * @obj: the exception object
1083  * @test_only: only test if the exception is caught, but dont call handlers
1084  *
1085  *
1086  */
1087 static gboolean
1088 arch_handle_exception (MonoContext *ctx, gpointer obj, gboolean test_only)
1089 {
1090         MonoDomain *domain = mono_domain_get ();
1091         MonoJitInfo *ji, rji;
1092         static int (*call_filter) (MonoContext *, gpointer, gpointer) = NULL;
1093         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
1094         MonoLMF *lmf = jit_tls->lmf;            
1095         GList *trace_ips = NULL;
1096         MonoException *mono_ex;
1097         MonoString *initial_stack_trace;
1098
1099         g_assert (ctx != NULL);
1100         if (!obj) {
1101                 MonoException *ex = mono_get_exception_null_reference ();
1102                 ex->message = mono_string_new (domain, 
1103                         "Object reference not set to an instance of an object");
1104                 obj = (MonoObject *)ex;
1105         } 
1106
1107         if (mono_object_isinst (obj, mono_defaults.exception_class)) {
1108                 mono_ex = (MonoException*)obj;
1109                 initial_stack_trace = mono_ex->stack_trace;
1110         } else {
1111                 mono_ex = NULL;
1112         }
1113
1114
1115         if (!call_filter)
1116                 call_filter = arch_get_call_filter ();
1117
1118         g_assert (jit_tls->end_of_stack);
1119         g_assert (jit_tls->abort_func);
1120
1121         if (!test_only) {
1122                 MonoContext ctx_cp = *ctx;
1123                 setup_context (&ctx_cp);
1124                 if (mono_jit_trace_calls != NULL)
1125                         g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name);
1126                 if (!arch_handle_exception (&ctx_cp, obj, TRUE)) {
1127                         if (mono_break_on_exc)
1128                                 G_BREAKPOINT ();
1129                         mono_unhandled_exception (obj);
1130                 }
1131         }
1132
1133         while (1) {
1134                 MonoContext new_ctx;
1135                 char *trace = NULL;
1136                 
1137                 setup_context (&new_ctx);
1138                 ji = mono_arch_find_jit_info (domain, jit_tls, &rji, &rji, ctx, &new_ctx, 
1139                                               test_only ? &trace : NULL, &lmf, NULL, NULL);
1140                 if (!ji) {
1141                         g_warning ("Exception inside function without unwind info");
1142                         g_assert_not_reached ();
1143                 }
1144
1145                 if (ji != (gpointer)-1) {
1146                         
1147                         if (test_only && ji->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
1148                                 char *tmp, *strace;
1149
1150                                 if (!initial_stack_trace) {
1151                                         trace_ips = g_list_append (trace_ips, MONO_CONTEXT_GET_IP (ctx));
1152
1153                                         if (!mono_ex->stack_trace)
1154                                                 strace = g_strdup ("");
1155                                         else
1156                                                 strace = mono_string_to_utf8 (mono_ex->stack_trace);
1157                         
1158                                         tmp = g_strdup_printf ("%s%s\n", strace, trace);
1159                                         g_free (strace);
1160
1161                                         mono_ex->stack_trace = mono_string_new (domain, tmp);
1162
1163                                         g_free (tmp);
1164                                 }
1165                         }
1166
1167                         if (ji->num_clauses) {
1168                                 int i;
1169                                 
1170                                 g_assert (ji->clauses);
1171                         
1172                                 for (i = 0; i < ji->num_clauses; i++) {
1173                                         MonoJitExceptionInfo *ei = &ji->clauses [i];
1174
1175                                         if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
1176                                             MONO_CONTEXT_GET_IP (ctx) <= ei->try_end) { 
1177                                                 /* catch block */
1178                                                 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE && 
1179                                                      mono_object_isinst (obj, mono_class_get (ji->method->klass->image, ei->data.token))) ||
1180                                                     ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
1181                                                       call_filter (ctx, ei->data.filter, obj)))) {
1182                                                         if (test_only) {
1183                                                                 if (mono_ex)
1184                                                                         mono_ex->trace_ips = glist_to_array (trace_ips);
1185                                                                 g_list_free (trace_ips);
1186                                                                 g_free (trace);
1187                                                                 return TRUE;
1188                                                         }
1189                                                         if (mono_jit_trace_calls != NULL)
1190                                                                 g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1191                                                         /*printf ("stack for catch: %p\n", MONO_CONTEXT_GET_BP (ctx));*/
1192                                                         MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
1193                                                         *((gpointer *)((char *)MONO_CONTEXT_GET_BP (ctx) + ji->exvar_offset)) = obj;
1194                                                         jit_tls->lmf = lmf;
1195                                                         g_free (trace);
1196                                                         return 0;
1197                                                 }
1198                                                 if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && 
1199                                                     MONO_CONTEXT_GET_IP (ctx) < ei->try_end &&
1200                                                     (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
1201                                                         if (mono_jit_trace_calls != NULL)
1202                                                                 g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE));
1203                                                         call_filter (ctx, ei->handler_start, NULL);
1204                                                 }
1205                                                 
1206                                         }
1207                                 }
1208                         }
1209                 }
1210
1211                 g_free (trace);
1212                         
1213                 *ctx = new_ctx;
1214                 setup_context (ctx);
1215
1216                 if ((ji == (gpointer)-1) || MONO_CONTEXT_GET_BP (ctx) >= jit_tls->end_of_stack) {
1217                         if (!test_only) {
1218                                 jit_tls->lmf = lmf;
1219                                 jit_tls->abort_func (obj);
1220                                 g_assert_not_reached ();
1221                         } else {
1222                                 if (mono_ex)
1223                                         mono_ex->trace_ips = glist_to_array (trace_ips);
1224                                 g_list_free (trace_ips);
1225                                 return FALSE;
1226                         }
1227                 }
1228         }
1229
1230         g_assert_not_reached ();
1231 }
1232
1233 gboolean
1234 mono_arch_has_unwind_info (gconstpointer addr)
1235 {
1236         return FALSE;
1237 }
1238