Remove some unused arguments from mono_arch_handle_exception () / mono_handle_excepti...
[mono.git] / mono / mini / exceptions-mips.c
1 /*
2  * exceptions-mips.c: exception support for MIPS
3  *
4  * Authors:
5  *   Mark Mason (mason@broadcom.com)
6  *
7  * Based on exceptions-ppc.c by:
8  *   Dietmar Maurer (dietmar@ximian.com)
9  *   Paolo Molaro (lupus@ximian.com)
10  *
11  * (C) 2006 Broadcom
12  * (C) 2001 Ximian, Inc.
13  */
14
15 #include <config.h>
16 #include <glib.h>
17 #include <signal.h>
18 #include <string.h>
19 #include <ucontext.h>
20
21 #include <mono/arch/mips/mips-codegen.h>
22 #include <mono/metadata/appdomain.h>
23 #include <mono/metadata/tabledefs.h>
24 #include <mono/metadata/threads.h>
25 #include <mono/metadata/debug-helpers.h>
26 #include <mono/metadata/exception.h>
27 #include <mono/metadata/mono-debug.h>
28
29 #include "mini.h"
30 #include "mini-mips.h"
31
32 #define GENERIC_EXCEPTION_SIZE 256
33
34 /* XXX */
35 #if 1
36 #define restore_regs_from_context(ctx_reg,ip_reg,tmp_reg) do {  \
37         } while (0)
38 #else
39 #define restore_regs_from_context(ctx_reg,pc,tmp_reg) do {      \
40                 int reg;        \
41                 ppc_lwz (code, pc, G_STRUCT_OFFSET (MonoContext, sc_pc), ctx_reg);      \
42                 ppc_lmw (code, ppc_r13, ctx_reg, G_STRUCT_OFFSET (MonoContext, sc_regs));       \
43                 for (reg = 0; reg < MONO_SAVED_FREGS; ++reg) {  \
44                         ppc_lfd (code, (14 + reg), G_STRUCT_OFFSET(MonoLMF, sc_fpregs) + reg * sizeof (gdouble), ctx_reg);      \
45                 }       \
46         } while (0)
47 #endif
48
49 /* nothing to do */
50 #define setup_context(ctx) do { \
51                 memset ((ctx), 0, sizeof(*(ctx)));      \
52         } while (0);
53
54 /*
55  * mono_arch_get_restore_context:
56  *
57  * Returns a pointer to a method which restores a previously saved MonoContext.
58  * The first argument in a0 is the pointer to the MonoContext.
59  */
60 gpointer
61 mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
62 {
63         int i;
64         guint8 *code;
65         static guint8 start [128];
66         static int inited = 0;
67         guint32 iregs_to_restore;
68
69         g_assert (!aot);
70         if (info)
71                 *info = NULL;
72
73         if (inited)
74                 return start;
75         inited = 1;
76         code = start;
77
78         iregs_to_restore = (MONO_ARCH_CALLEE_SAVED_REGS \
79                             | (1 << mips_sp) | (1 << mips_ra));
80         for (i = 0; i < MONO_SAVED_GREGS; ++i) {
81                 if (iregs_to_restore & (1 << i)) {
82                         MIPS_LW (code, i, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[i]));
83                 }
84         }
85
86         /* Get the address to return to */
87         mips_lw (code, mips_t9, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_pc));
88
89         /* jump to the saved IP */
90         mips_jr (code, mips_t9);
91         mips_nop (code);
92
93         /* never reached */
94         mips_break (code, 0xff);
95
96         g_assert ((code - start) < sizeof(start));
97         mono_arch_flush_icache (start, code - start);
98         return start;
99 }
100
101 /*
102  * mono_arch_get_call_filter:
103  *
104  * Returns a pointer to a method which calls an exception filter. We
105  * also use this function to call finally handlers (we pass NULL as 
106  * @exc object in this case).
107  *
108  * This function is invoked as
109  *      call_handler (MonoContext *ctx, handler)
110  *
111  * Where 'handler' is a function to be invoked as:
112  *      handler (void)
113  */
114 gpointer
115 mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
116 {
117         static guint8 start [320];
118         static int inited = 0;
119         guint8 *code;
120         int alloc_size;
121         int offset;
122
123         g_assert (!aot);
124         if (info)
125                 *info = NULL;
126
127         if (inited)
128                 return start;
129
130         inited = 1;
131         code = start;
132
133         alloc_size = 64;
134         g_assert ((alloc_size & (MIPS_STACK_ALIGNMENT-1)) == 0);
135
136         mips_addiu (code, mips_sp, mips_sp, -alloc_size);
137         mips_sw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET);
138
139         /* Save global registers on stack (s0 - s7) */
140         offset = 16;
141         MIPS_SW (code, mips_s0, mips_sp, offset); offset += IREG_SIZE;
142         MIPS_SW (code, mips_s1, mips_sp, offset); offset += IREG_SIZE;
143         MIPS_SW (code, mips_s2, mips_sp, offset); offset += IREG_SIZE;
144         MIPS_SW (code, mips_s3, mips_sp, offset); offset += IREG_SIZE;
145         MIPS_SW (code, mips_s4, mips_sp, offset); offset += IREG_SIZE;
146         MIPS_SW (code, mips_s5, mips_sp, offset); offset += IREG_SIZE;
147         MIPS_SW (code, mips_s6, mips_sp, offset); offset += IREG_SIZE;
148         MIPS_SW (code, mips_s7, mips_sp, offset); offset += IREG_SIZE;
149         MIPS_SW (code, mips_fp, mips_sp, offset); offset += IREG_SIZE;
150
151         /* Restore global registers from MonoContext, including the frame pointer */
152         MIPS_LW (code, mips_s0, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s0]));
153         MIPS_LW (code, mips_s1, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s1]));
154         MIPS_LW (code, mips_s2, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s2]));
155         MIPS_LW (code, mips_s3, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s3]));
156         MIPS_LW (code, mips_s4, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s4]));
157         MIPS_LW (code, mips_s5, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s5]));
158         MIPS_LW (code, mips_s6, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s6]));
159         MIPS_LW (code, mips_s7, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_s7]));
160         MIPS_LW (code, mips_fp, mips_a0, G_STRUCT_OFFSET (MonoContext, sc_regs[mips_fp]));
161
162         /* a1 is the handler to call */
163         mips_move (code, mips_t9, mips_a1);
164
165         /* jump to the saved IP */
166         mips_jalr (code, mips_t9, mips_ra);
167         mips_nop (code);
168
169         /* restore all regs from the stack */
170         offset = 16;
171         MIPS_LW (code, mips_s0, mips_sp, offset); offset += IREG_SIZE;
172         MIPS_LW (code, mips_s1, mips_sp, offset); offset += IREG_SIZE;
173         MIPS_LW (code, mips_s2, mips_sp, offset); offset += IREG_SIZE;
174         MIPS_LW (code, mips_s3, mips_sp, offset); offset += IREG_SIZE;
175         MIPS_LW (code, mips_s4, mips_sp, offset); offset += IREG_SIZE;
176         MIPS_LW (code, mips_s5, mips_sp, offset); offset += IREG_SIZE;
177         MIPS_LW (code, mips_s6, mips_sp, offset); offset += IREG_SIZE;
178         MIPS_LW (code, mips_s7, mips_sp, offset); offset += IREG_SIZE;
179         MIPS_LW (code, mips_fp, mips_sp, offset); offset += IREG_SIZE;
180
181         /* epilog */
182         mips_lw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET);
183         mips_addiu (code, mips_sp, mips_sp, alloc_size);
184         mips_jr (code, mips_ra);
185         mips_nop (code);
186
187         g_assert ((code - start) < sizeof(start));
188         mono_arch_flush_icache (start, code - start);
189         return start;
190 }
191
192 static void
193 throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gboolean rethrow)
194 {
195         static void (*restore_context) (MonoContext *);
196         MonoContext ctx;
197
198 #ifdef DEBUG_EXCEPTIONS
199         g_print ("throw_exception: exc=%p eip=%p esp=%p rethrow=%d\n",
200                  exc, (void *)eip, (void *) esp, rethrow);
201 #endif
202
203         if (!restore_context)
204                 restore_context = mono_get_restore_context ();
205
206         /* adjust eip so that it point into the call instruction */
207         eip -= 8;
208
209         setup_context (&ctx);
210
211         /*g_print  ("stack in throw: %p\n", esp);*/
212         memcpy (&ctx.sc_regs, (void *)(esp + MIPS_STACK_PARAM_OFFSET),
213                 sizeof (gulong) * MONO_SAVED_GREGS);
214         memset (&ctx.sc_fpregs, 0, sizeof (mips_freg) * MONO_SAVED_FREGS);
215         MONO_CONTEXT_SET_IP (&ctx, eip);
216
217         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
218                 MonoException *mono_ex = (MonoException*)exc;
219                 if (!rethrow)
220                         mono_ex->stack_trace = NULL;
221         }
222         mono_handle_exception (&ctx, exc);
223 #ifdef DEBUG_EXCEPTIONS
224         g_print ("throw_exception: restore to pc=%p sp=%p fp=%p ctx=%p\n",
225                  (void *) ctx.sc_pc, (void *) ctx.sc_regs[mips_sp],
226                  (void *) ctx.sc_regs[mips_fp], &ctx);
227 #endif
228         restore_context (&ctx);
229
230         g_assert_not_reached ();
231 }
232
233 /**
234  * arch_get_throw_exception_generic:
235  *
236  * Returns a function pointer which can be used to raise 
237  * exceptions. The returned function has the following 
238  * signature: void (*func) (MonoException *exc); or
239  * void (*func) (char *exc_name);
240  *
241  */
242 static gpointer 
243 mono_arch_get_throw_exception_generic (guint8 *start, int size, int corlib, gboolean rethrow)
244 {
245         guint8 *code;
246         int alloc_size, pos, i;
247
248         code = start;
249
250         //g_print ("mono_arch_get_throw_exception_generic: code=%p\n", code);
251
252         pos = 0;
253         /* XXX - save all the FP regs on the stack ? */
254
255         pos += MONO_MAX_IREGS * sizeof(guint32);
256
257         alloc_size = MIPS_MINIMAL_STACK_SIZE + pos + 64;
258         // align to MIPS_STACK_ALIGNMENT bytes
259         alloc_size += MIPS_STACK_ALIGNMENT - 1;
260         alloc_size &= ~(MIPS_STACK_ALIGNMENT - 1);
261
262         g_assert ((alloc_size & (MIPS_STACK_ALIGNMENT-1)) == 0);
263         mips_addiu (code, mips_sp, mips_sp, -alloc_size);
264         mips_sw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET);
265
266         /* Save all the regs on the stack */
267         for (i = 0; i < MONO_MAX_IREGS; i++) {
268                 if (i != mips_sp)
269                         MIPS_SW (code, i, mips_sp, i*IREG_SIZE + MIPS_STACK_PARAM_OFFSET);
270                 else {
271                         mips_addiu (code, mips_at, mips_sp, alloc_size);
272                         MIPS_SW (code, mips_at, mips_sp, i*IREG_SIZE + MIPS_STACK_PARAM_OFFSET);
273                 }
274         }
275
276         if (corlib) {
277                 mips_move (code, mips_a1, mips_a0);
278                 mips_load (code, mips_a0, mono_defaults.corlib);
279                 mips_load (code, mips_t9, mono_exception_from_token);
280                 mips_jalr (code, mips_t9, mips_ra);
281                 mips_nop (code);
282                 mips_move (code, mips_a0, mips_v0);
283         }
284         /* call throw_exception (exc, ip, sp, rethrow) */
285
286         /* exc is already in place in a0 */
287
288         /* pointer to ip */
289         if (corlib)
290                 mips_lw (code, mips_a1, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET);
291         else
292                 mips_move (code, mips_a1, mips_ra);
293
294         /* current sp & rethrow */
295         mips_move (code, mips_a2, mips_sp);
296         mips_addiu (code, mips_a3, mips_zero, rethrow);
297
298         mips_load (code, mips_t9, throw_exception);
299         mips_jr (code, mips_t9);
300         mips_nop (code);
301         /* we should never reach this breakpoint */
302         mips_break (code, 0xfe);
303
304         g_assert ((code - start) < size);
305         mono_arch_flush_icache (start, code - start);
306         return start;
307 }
308
309 /**
310  * mono_arch_get_rethrow_exception:
311  *
312  * Returns a function pointer which can be used to rethrow 
313  * exceptions. The returned function has the following 
314  * signature: void (*func) (MonoException *exc); 
315  *
316  */
317 gpointer
318 mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
319 {
320         static guint8 start [GENERIC_EXCEPTION_SIZE];
321         static int inited = 0;
322
323         g_assert (!aot);
324         if (info)
325                 *info = NULL;
326
327         if (inited)
328                 return start;
329         mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, TRUE);
330         inited = 1;
331         return start;
332 }
333
334 /**
335  * arch_get_throw_exception:
336  *
337  * Returns a function pointer which can be used to raise 
338  * exceptions. The returned function has the following 
339  * signature: void (*func) (MonoException *exc); 
340  * For example to raise an arithmetic exception you can use:
341  *
342  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
343  * x86_call_code (code, arch_get_throw_exception ()); 
344  *
345  */
346 gpointer
347 mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
348 {
349         static guint8 start [GENERIC_EXCEPTION_SIZE];
350         static int inited = 0;
351
352         g_assert (!aot);
353         if (info)
354                 *info = NULL;
355
356         if (inited)
357                 return start;
358         mono_arch_get_throw_exception_generic (start, sizeof (start), FALSE, FALSE);
359         inited = 1;
360         return start;
361 }
362
363 gpointer 
364 mono_arch_get_throw_exception_by_name (void)
365 {
366         guint8 *start, *code;
367         int size = 64;
368
369         /* Not used on MIPS */  
370         start = code = mono_global_codeman_reserve (size);
371         mips_break (code, 0xfd);
372         mono_arch_flush_icache (start, code - start);
373         return start;
374 }
375
376 /**
377  * mono_arch_get_throw_corlib_exception:
378  *
379  * Returns a function pointer which can be used to raise 
380  * corlib exceptions. The returned function has the following 
381  * signature: void (*func) (guint32 ex_token, guint32 offset); 
382  * On MIPS, the offset argument is missing.
383  */
384 gpointer
385 mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
386 {
387         static guint8 start [GENERIC_EXCEPTION_SIZE];
388         static int inited = 0;
389
390         g_assert (!aot);
391         if (info)
392                 *info = NULL;
393
394         if (inited)
395                 return start;
396         mono_arch_get_throw_exception_generic (start, sizeof (start), TRUE, FALSE);
397         inited = 1;
398         return start;
399 }
400
401 /*
402  * mono_arch_find_jit_info:
403  *
404  * This function is used to gather information from @ctx, and store it in @frame_info.
405  * It unwinds one stack frame, and stores the resulting context into @new_ctx. @lmf
406  * is modified if needed.
407  * Returns TRUE on success, FALSE otherwise.
408  */
409 gboolean
410 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, 
411                                                          MonoJitInfo *ji, MonoContext *ctx, 
412                                                          MonoContext *new_ctx, MonoLMF **lmf, 
413                                                          mgreg_t **save_locations,
414                                                          StackFrameInfo *frame)
415 {
416         memset (frame, 0, sizeof (StackFrameInfo));
417         frame->ji = ji;
418
419         *new_ctx = *ctx;
420
421         if (ji != NULL) {
422                 gint32 address;
423                 gpointer ip = MONO_CONTEXT_GET_IP (ctx);
424                 gpointer fp = MONO_CONTEXT_GET_BP (ctx);
425                 guint32 sp;
426
427                 frame->type = FRAME_TYPE_MANAGED;
428
429                 if (*lmf && (fp >= (gpointer)(*lmf)->ebp)) {
430                         /* remove any unused lmf */
431                         *lmf = (*lmf)->previous_lmf;
432                 }
433
434                 address = (char *)ip - (char *)ji->code_start;
435
436                 /* Compute the previous stack frame, assuming method
437                  * starts with addiu sp, sp, <offset>. */
438                 sp = (guint32)(fp) - (short)(*(guint32 *)(ji->code_start));
439
440                 /* Sanity check the frame */
441                 if (!sp || (sp == 0xffffffff)
442                     || (sp & 0x07) || (sp < 64*1024)) {
443 #ifdef DEBUG_EXCEPTIONS
444                         g_print ("mono_arch_find_jit_info: bad stack sp=%p\n", (void *) sp);
445 #endif
446                         return FALSE;
447                 }
448
449                 if (ji->method->save_lmf && 0) {
450                         /* only enable this when prologue stops emitting
451                          * normal save of s-regs when save_lmf is true.
452                          * Will have to sync with prologue code at that point.
453                          */
454                         memcpy (&new_ctx->sc_fpregs,
455                                 (char*)sp - sizeof (float) * MONO_SAVED_FREGS,
456                                 sizeof (float) * MONO_SAVED_FREGS);
457                         memcpy (&new_ctx->sc_regs,
458                                 (char*)sp - sizeof (float) * MONO_SAVED_FREGS - sizeof (gulong) * MONO_SAVED_GREGS,
459                                 sizeof (gulong) * MONO_SAVED_GREGS);
460                 } else if (ji->used_regs) {
461                         guint32 *insn;
462                         guint32 mask = ji->used_regs;
463
464                         /* these all happen before adjustment of fp */
465                         /* Look for sw ??, ????(sp) */
466                         insn = ((guint32 *)ji->code_start) + 1;
467                         while (!*insn || ((*insn & 0xffe00000) == 0xafa00000) || ((*insn & 0xffe00000) == 0xffa00000)) {
468                                 int reg = (*insn >> 16) & 0x1f;
469                                 guint32 addr = (((guint32)fp) + (short)(*insn & 0x0000ffff));
470
471                                 mask &= ~(1 << reg);
472                                 if ((*insn & 0xffe00000) == 0xafa00000)
473                                         new_ctx->sc_regs [reg] = *(guint32 *)addr;
474                                 else
475                                         new_ctx->sc_regs [reg] = *(guint64 *)addr;
476                                 insn++;
477                         }
478                         MONO_CONTEXT_SET_SP (new_ctx, sp);
479                         MONO_CONTEXT_SET_BP (new_ctx, sp);
480                         /* assert that we found all registers we were supposed to */
481                         g_assert (!mask);
482                 }
483                 /* we substract 8, so that the IP points into the call instruction */
484                 MONO_CONTEXT_SET_IP (new_ctx, new_ctx->sc_regs[mips_ra] - 8);
485
486                 /* Sanity check -- we should have made progress here */
487                 g_assert (MONO_CONTEXT_GET_BP (new_ctx) != MONO_CONTEXT_GET_BP (ctx));
488                 return TRUE;
489         } else if (*lmf) {
490                 if (!(*lmf)->method) {
491 #ifdef DEBUG_EXCEPTIONS
492                         g_print ("mono_arch_find_jit_info: bad lmf @ %p\n", (void *) *lmf);
493 #endif
494                         return FALSE;
495                 }
496                 g_assert (((*lmf)->magic == MIPS_LMF_MAGIC1) || ((*lmf)->magic == MIPS_LMF_MAGIC2));
497
498                 ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL);
499                 if (!ji) {
500                         // FIXME: This can happen with multiple appdomains (bug #444383)
501                         return FALSE;
502                 }
503
504                 frame->ji = ji;
505                 frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
506
507                 memcpy (&new_ctx->sc_regs, (*lmf)->iregs, sizeof (gulong) * MONO_SAVED_GREGS);
508                 memcpy (&new_ctx->sc_fpregs, (*lmf)->fregs, sizeof (float) * MONO_SAVED_FREGS);
509                 MONO_CONTEXT_SET_IP (new_ctx, (*lmf)->eip);
510                 /* ensure that we've made progress */
511                 g_assert (new_ctx->sc_pc != ctx->sc_pc);
512                 *lmf = (*lmf)->previous_lmf;
513
514                 return TRUE;
515         }
516
517         return FALSE;
518 }
519
520 void
521 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
522 {
523         int i;
524         struct sigcontext *ctx = (struct sigcontext *)sigctx;
525
526         mctx->sc_pc = ctx->sc_pc;
527         for (i = 0; i < 32; ++i) {
528                 mctx->sc_regs[i] = ctx->sc_regs[i];
529                 mctx->sc_fpregs[i] = ctx->sc_fpregs[i];
530         }
531 }
532
533 void
534 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
535 {
536         int i;
537         struct sigcontext *ctx = (struct sigcontext *)sigctx;
538
539         ctx->sc_pc = mctx->sc_pc;
540         for (i = 0; i < 32; ++i) {
541                 ctx->sc_regs[i] = mctx->sc_regs[i];
542                 ctx->sc_fpregs[i] = mctx->sc_fpregs[i];
543         }
544 }       
545
546 gpointer
547 mono_arch_ip_from_context (void *sigctx)
548 {
549         struct sigcontext *ctx = (struct sigcontext *)sigctx;
550         return (gpointer)(guint32)ctx->sc_pc;
551 }
552
553 /*
554  * This is the function called from the signal handler
555  */
556 gboolean
557 mono_arch_handle_exception (void *ctx, gpointer obj)
558 {
559         MonoContext mctx;
560         gboolean result;
561         
562         mono_arch_sigctx_to_monoctx (ctx, &mctx);
563 #ifdef DEBUG_EXCEPTIONS
564         g_print ("mono_arch_handle_exception: pc=%p\n", (void *) mctx.sc_pc);
565 #endif
566         mono_handle_exception (&mctx, obj);
567         result = TRUE;
568
569 #ifdef DEBUG_EXCEPTIONS
570         g_print ("mono_arch_handle_exception: restore pc=%p\n", (void *)mctx.sc_pc);
571 #endif
572         /* restore the context so that returning from the signal handler
573          * will invoke the catch clause 
574          */
575         mono_arch_monoctx_to_sigctx (&mctx, ctx);
576
577         return result;
578 }