svn path=/branches/mono-1-1-9/mcs/; revision=51207
[mono.git] / mono / mini / exceptions-ia64.c
1 /*
2  * exceptions-ia64.c: exception support for IA64
3  *
4  * Authors:
5  *   Zoltan Varga (vargaz@gmail.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 /*
11  * We implement exception handling with the help of the libuwind library:
12  * 
13  * http://www.hpl.hp.com/research/linux/libunwind/
14  *
15  *  Under IA64 all functions are assumed to have unwind info, we do not need to save
16  * the machine state in the LMF. But we have to generate unwind info for all 
17  * dynamically generated code.
18  */
19
20 #include <config.h>
21 #include <glib.h>
22 #include <signal.h>
23 #include <string.h>
24 #include <sys/ucontext.h>
25
26 #include <mono/arch/ia64/ia64-codegen.h>
27 #include <mono/metadata/appdomain.h>
28 #include <mono/metadata/tabledefs.h>
29 #include <mono/metadata/threads.h>
30 #include <mono/metadata/debug-helpers.h>
31 #include <mono/metadata/exception.h>
32 #include <mono/metadata/gc-internal.h>
33 #include <mono/metadata/mono-debug.h>
34
35 #include "mini.h"
36 #include "mini-ia64.h"
37
38 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
39
40 #define NOT_IMPLEMENTED g_assert_not_reached ()
41
42 #define GP_SCRATCH_REG 31
43 #define GP_SCRATCH_REG2 30
44
45 static gpointer
46 ia64_create_ftnptr (gpointer ptr)
47 {
48         gpointer *desc = mono_global_codeman_reserve (2 * sizeof (gpointer));
49         desc [0] = ptr;
50         desc [1] = NULL;
51
52         return desc;
53 }
54
55 static void
56 restore_context (MonoContext *ctx)
57 {
58         int res;
59
60         /* Set this to 0 to tell OP_START_HANDLER that it doesn't have to set the frame pointer */
61         res = unw_set_reg (&ctx->cursor, UNW_IA64_GR + 15, 0);
62         g_assert (res == 0);
63
64         unw_resume (&ctx->cursor);
65 }
66
67 /*
68  * mono_arch_get_restore_context:
69  *
70  * Returns a pointer to a method which restores a previously saved sigcontext.
71  */
72 gpointer
73 mono_arch_get_restore_context (void)
74 {
75         return restore_context;
76 }
77
78 static gpointer
79 get_real_call_filter (void)
80 {
81         static guint8 *start;
82         static gboolean inited = FALSE;
83         Ia64CodegenState code;
84         int i, in0, local0, out0, nout;
85         unw_dyn_info_t *di;
86         unw_dyn_region_info_t *r_pro;
87
88         if (inited)
89                 return start;
90
91         start = mono_global_codeman_reserve (1024);
92
93         /* int call_filter (guint64 fp, guint64 ip) */
94
95         /*
96          * We have to create a register+stack frame similar to the frame which contains
97          * the filter. 
98          * - setting fp
99          * - setting up a register stack frame
100          * These cannot be set up in this function, because the fp register is a stacked
101          * register which is different in each method. Also, the register stack frame is
102          * different in each method. So we pass the FP value in a a non-stacked
103          * register and the code generated by the OP_START_HANDLER opcode will copy it
104          * to the appropriate register after setting up the register stack frame.
105          * The stacked registers are not need to be set since variables used in
106          * handler registers are never allocated to registers.
107          */
108
109         in0 = 32;
110         local0 = in0 + 2;
111         out0 = local0 + 4;
112         nout = 0;
113
114         ia64_codegen_init (code, start);
115
116         ia64_codegen_set_one_ins_per_bundle (code, TRUE);
117
118         ia64_alloc (code, local0 + 0, local0 - in0, out0 - local0, nout, 0);
119         ia64_mov_from_br (code, local0 + 1, IA64_B0);
120
121         r_pro = g_malloc0 (_U_dyn_region_info_size (2));
122         r_pro->op_count = 2;
123         r_pro->insn_count = 6;
124         i = 0;
125         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 2,
126                                                 /* reg=*/ UNW_IA64_AR_PFS, /* dst=*/ UNW_IA64_GR + local0 + 0);
127         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 5,
128                                                 /* reg=*/ UNW_IA64_RP, /* dst=*/ UNW_IA64_GR + local0 + 1);
129         g_assert ((unsigned) i <= r_pro->op_count);     
130
131         /* Frame pointer */
132         ia64_mov (code, IA64_R15, in0 + 0);
133         /* Target ip */
134         ia64_mov_to_br (code, IA64_B6, in0 + 1);
135
136         /* Return address */
137         ia64_mov_from_ip (code, GP_SCRATCH_REG);
138         ia64_adds_imm (code, GP_SCRATCH_REG, 3 * 16, GP_SCRATCH_REG);
139
140         /* Call the filter */
141         ia64_br_call_reg (code, IA64_B0, IA64_B6);
142
143         /* R8 contains the result of the filter */
144         /* R9 contains the saved apr_pfs value */
145
146         /* The filter returns using br_cond_reg, so have to do another return */
147         ia64_mov_to_ar_i (code, IA64_PFS, IA64_R9);
148         ia64_mov_from_ip (code, GP_SCRATCH_REG);        
149         ia64_adds_imm (code, GP_SCRATCH_REG, 4 * 16, GP_SCRATCH_REG);
150         ia64_mov_to_br (code, IA64_B0, GP_SCRATCH_REG);
151         ia64_br_ret_reg (code, IA64_B0);
152
153         ia64_mov_to_ar_i (code, IA64_PFS, local0 + 0);
154         ia64_mov_ret_to_br (code, IA64_B0, local0 + 1);
155         ia64_br_ret_reg (code, IA64_B0);
156
157         ia64_codegen_set_one_ins_per_bundle (code, FALSE);
158
159         ia64_codegen_close (code);
160
161         g_assert ((code.buf - start) <= 256);
162
163         mono_arch_flush_icache (start, code.buf - start);
164
165         di = g_malloc0 (sizeof (unw_dyn_info_t));
166         di->start_ip = (unw_word_t) start;
167         di->end_ip = (unw_word_t) code.buf;
168         di->gp = 0;
169         di->format = UNW_INFO_FORMAT_DYNAMIC;
170         di->u.pi.name_ptr = (unw_word_t)"throw_trampoline";
171         di->u.pi.regions = r_pro;
172
173         _U_dyn_register (di);
174
175         return ia64_create_ftnptr (start);
176 }
177
178 static int
179 call_filter (MonoContext *ctx, gpointer ip)
180 {
181         static int (*filter) (MonoContext *, gpointer) = NULL;
182         gpointer fp = MONO_CONTEXT_GET_BP (ctx);
183
184         /* FIXME: thread safety */
185         if (!filter)
186                 filter = get_real_call_filter ();
187
188         return filter (fp, ip);
189 }
190
191 /*
192  * mono_arch_get_call_filter:
193  *
194  * Returns a pointer to a method which calls an exception filter. We
195  * also use this function to call finally handlers (we pass NULL as 
196  * @exc object in this case).
197  */
198 gpointer
199 mono_arch_get_call_filter (void)
200 {
201         return call_filter;
202 }
203
204 static void
205 throw_exception (MonoObject *exc, guint64 rethrow)
206 {
207         unw_context_t unw_ctx;
208         MonoContext ctx;
209         MonoJitInfo *ji;
210         unw_word_t ip;
211         int res;
212
213         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
214                 MonoException *mono_ex = (MonoException*)exc;
215                 if (!rethrow)
216                         mono_ex->stack_trace = NULL;
217         }
218
219         res = unw_getcontext (&unw_ctx);
220         g_assert (res == 0);
221         res = unw_init_local (&ctx.cursor, &unw_ctx);
222         g_assert (res == 0);
223
224         /* 
225          * Unwind until the first managed frame. This is needed since 
226          * mono_handle_exception expects the variables in the original context to
227          * correspond to the method returned by mono_find_jit_info.
228          */
229         while (TRUE) {
230                 res = unw_get_reg (&ctx.cursor, UNW_IA64_IP, &ip);
231                 g_assert (res == 0);
232
233                 ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)ip);
234
235                 if (ji)
236                         break;
237
238                 res = unw_step (&ctx.cursor);
239
240                 if (res == 0) {
241                         /*
242                          * This means an unhandled exception during the compilation of a
243                          * topmost method like Main
244                          */
245                         break;
246                 }
247                 g_assert (res >= 0);
248         }
249
250         mono_handle_exception (&ctx, exc, (gpointer)(ip), FALSE);
251         restore_context (&ctx);
252
253         g_assert_not_reached ();
254 }
255
256 static gpointer
257 get_throw_trampoline (gboolean rethrow)
258 {
259         guint8* start;
260         Ia64CodegenState code;
261         gpointer ptr = throw_exception;
262         int i, in0, local0, out0;
263         unw_dyn_info_t *di;
264         unw_dyn_region_info_t *r_pro;
265
266         start = mono_global_codeman_reserve (256);
267
268         in0 = 32;
269         local0 = in0 + 1;
270         out0 = local0 + 2;
271
272         ia64_codegen_init (code, start);
273         ia64_alloc (code, local0 + 0, local0 - in0, out0 - local0, 3, 0);
274         ia64_mov_from_br (code, local0 + 1, IA64_B0);   
275
276         /* FIXME: This depends on the current instruction emitter */
277
278         r_pro = g_malloc0 (_U_dyn_region_info_size (2));
279         r_pro->op_count = 2;
280         r_pro->insn_count = 6;
281         i = 0;
282         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 2,
283                                                 /* reg=*/ UNW_IA64_AR_PFS, /* dst=*/ UNW_IA64_GR + local0 + 0);
284         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 5,
285                                                 /* reg=*/ UNW_IA64_RP, /* dst=*/ UNW_IA64_GR + local0 + 1);
286         g_assert ((unsigned) i <= r_pro->op_count);     
287
288         /* Set args */
289         ia64_mov (code, out0 + 0, in0 + 0);
290         ia64_adds_imm (code, out0 + 1, rethrow, IA64_R0);
291
292         /* Call throw_exception */
293         ia64_movl (code, GP_SCRATCH_REG, ptr);
294         ia64_ld8_inc_imm (code, GP_SCRATCH_REG2, GP_SCRATCH_REG, 8);
295         ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG2);
296         ia64_ld8 (code, IA64_GP, GP_SCRATCH_REG);
297         ia64_br_call_reg (code, IA64_B0, IA64_B6);
298
299         /* Not reached */
300         ia64_break_i (code, 1000);
301         ia64_codegen_close (code);
302
303         g_assert ((code.buf - start) <= 256);
304
305         mono_arch_flush_icache (start, code.buf - start);
306
307         di = g_malloc0 (sizeof (unw_dyn_info_t));
308         di->start_ip = (unw_word_t) start;
309         di->end_ip = (unw_word_t) code.buf;
310         di->gp = 0;
311         di->format = UNW_INFO_FORMAT_DYNAMIC;
312         di->u.pi.name_ptr = (unw_word_t)"throw_trampoline";
313         di->u.pi.regions = r_pro;
314
315         _U_dyn_register (di);
316
317         return ia64_create_ftnptr (start);
318 }
319
320 /**
321  * mono_arch_get_throw_exception:
322  *
323  * Returns a function pointer which can be used to raise 
324  * exceptions. The returned function has the following 
325  * signature: void (*func) (MonoException *exc); 
326  *
327  */
328 gpointer 
329 mono_arch_get_throw_exception (void)
330 {
331         static guint8* start;
332         static gboolean inited = FALSE;
333
334         if (inited)
335                 return start;
336
337         start = get_throw_trampoline (FALSE);
338
339         inited = TRUE;
340
341         return start;
342 }
343
344 gpointer 
345 mono_arch_get_rethrow_exception (void)
346 {
347         static guint8* start;
348         static gboolean inited = FALSE;
349
350         if (inited)
351                 return start;
352
353         start = get_throw_trampoline (TRUE);
354
355         inited = TRUE;
356
357         return start;
358 }
359
360 gpointer 
361 mono_arch_get_throw_exception_by_name (void)
362 {       
363         guint8* start;
364         Ia64CodegenState code;
365
366         start = mono_global_codeman_reserve (64);
367
368         /* Not used on ia64 */
369         ia64_codegen_init (code, start);
370         ia64_break_i (code, 1001);
371         ia64_codegen_close (code);
372
373         g_assert ((code.buf - start) <= 256);
374
375         mono_arch_flush_icache (start, code.buf - start);
376
377         return start;
378 }
379
380 /**
381  * mono_arch_get_throw_corlib_exception:
382  *
383  * Returns a function pointer which can be used to raise 
384  * corlib exceptions. The returned function has the following 
385  * signature: void (*func) (guint32 ex_token, guint32 offset); 
386  * Here, offset is the offset which needs to be substracted from the caller IP 
387  * to get the IP of the throw. Passing the offset has the advantage that it 
388  * needs no relocations in the caller.
389  */
390 gpointer 
391 mono_arch_get_throw_corlib_exception (void)
392 {
393         static guint8* start;
394         static gboolean inited = FALSE;
395         gpointer ptr;
396         int i, in0, local0, out0, nout;
397         Ia64CodegenState code;
398         unw_dyn_info_t *di;
399         unw_dyn_region_info_t *r_pro;
400
401         if (inited)
402                 return start;
403
404         start = mono_global_codeman_reserve (1024);
405
406         in0 = 32;
407         local0 = in0 + 2;
408         out0 = local0 + 4;
409         nout = 3;
410
411         ia64_codegen_init (code, start);
412         ia64_alloc (code, local0 + 0, local0 - in0, out0 - local0, nout, 0);
413         ia64_mov_from_br (code, local0 + 1, IA64_RP);
414
415         r_pro = g_malloc0 (_U_dyn_region_info_size (2));
416         r_pro->op_count = 2;
417         r_pro->insn_count = 6;
418         i = 0;
419         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 2,
420                                                 /* reg=*/ UNW_IA64_AR_PFS, /* dst=*/ UNW_IA64_GR + local0 + 0);
421         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 5,
422                                                 /* reg=*/ UNW_IA64_RP, /* dst=*/ UNW_IA64_GR + local0 + 1);
423         g_assert ((unsigned) i <= r_pro->op_count);     
424
425         /* Call exception_from_token */
426         ia64_movl (code, out0 + 0, mono_defaults.exception_class->image);
427         ia64_mov (code, out0 + 1, in0 + 0);
428         ptr = mono_exception_from_token;
429         ia64_movl (code, GP_SCRATCH_REG, ptr);
430         ia64_ld8_inc_imm (code, GP_SCRATCH_REG2, GP_SCRATCH_REG, 8);
431         ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG2);
432         ia64_ld8 (code, IA64_GP, GP_SCRATCH_REG);
433         ia64_br_call_reg (code, IA64_B0, IA64_B6);
434         ia64_mov (code, local0 + 3, IA64_R8);
435
436         /* Compute throw ip */
437         ia64_mov (code, local0 + 2, local0 + 1);
438         ia64_sub (code, local0 + 2, local0 + 2, in0 + 1);
439
440         /* Trick the unwind library into using throw_ip as the IP in the caller frame */
441         ia64_mov (code, local0 + 1, local0 + 2);
442
443         /* Set args */
444         ia64_mov (code, out0 + 0, local0 + 3);
445         ia64_mov (code, out0 + 1, IA64_R0);
446
447         /* Call throw_exception */
448         ptr = throw_exception;
449         ia64_movl (code, GP_SCRATCH_REG, ptr);
450         ia64_ld8_inc_imm (code, GP_SCRATCH_REG2, GP_SCRATCH_REG, 8);
451         ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG2);
452         ia64_ld8 (code, IA64_GP, GP_SCRATCH_REG);
453         ia64_br_call_reg (code, IA64_B0, IA64_B6);
454
455         ia64_break_i (code, 1002);
456         ia64_codegen_close (code);
457
458         g_assert ((code.buf - start) <= 1024);
459
460         di = g_malloc0 (sizeof (unw_dyn_info_t));
461         di->start_ip = (unw_word_t) start;
462         di->end_ip = (unw_word_t) code.buf;
463         di->gp = 0;
464         di->format = UNW_INFO_FORMAT_DYNAMIC;
465         di->u.pi.name_ptr = (unw_word_t)"throw_corlib_exception_trampoline";
466         di->u.pi.regions = r_pro;
467
468         _U_dyn_register (di);
469
470         mono_arch_flush_icache (start, code.buf - start);
471
472         return ia64_create_ftnptr (start);
473 }
474
475 /* mono_arch_find_jit_info:
476  *
477  * This function is used to gather information from @ctx. It return the 
478  * MonoJitInfo of the corresponding function, unwinds one stack frame and
479  * stores the resulting context into @new_ctx. It also stores a string 
480  * describing the stack location into @trace (if not NULL), and modifies
481  * the @lmf if necessary. @native_offset return the IP offset from the 
482  * start of the function or -1 if that info is not available.
483  */
484 MonoJitInfo *
485 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
486                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
487                          gboolean *managed)
488 {
489         MonoJitInfo *ji;
490         int err;
491         unw_word_t ip;
492
493         *new_ctx = *ctx;
494
495         while (TRUE) {
496                 err = unw_get_reg (&new_ctx->cursor, UNW_IA64_IP, &ip);
497                 g_assert (err == 0);
498
499                 /* Avoid costly table lookup during stack overflow */
500                 if (prev_ji && ((guint8*)ip > (guint8*)prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
501                         ji = prev_ji;
502                 else
503                         ji = mono_jit_info_table_find (domain, (gpointer)ip);
504
505                 if (managed)
506                         *managed = FALSE;
507
508                 /*
509                 {
510                         char name[256];
511                         unw_word_t off;
512
513                         unw_get_proc_name (&new_ctx->cursor, name, 256, &off);
514                         printf ("F: %s\n", name);
515                 }
516                 */
517
518                 if (ji != NULL) {
519                         if (managed)
520                                 if (!ji->method->wrapper_type)
521                                         *managed = TRUE;
522
523                         break;
524                 }
525
526                 /* This is an unmanaged frame, so just unwind through it */
527                 /* FIXME: This returns -3 for the __clone2 frame in libc */
528                 err = unw_step (&new_ctx->cursor);
529                 if (err < 0)
530                         break;
531
532                 if (err == 0)
533                         break;
534         }
535
536         if (ji) {
537                 err = unw_step (&new_ctx->cursor);
538                 g_assert (err >= 0);
539
540                 return ji;
541         }
542         else
543                 return (gpointer)(gssize)-1;
544 }
545
546 /**
547  * mono_arch_handle_exception:
548  *
549  * @ctx: saved processor state
550  * @obj: the exception object
551  */
552 gboolean
553 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
554 {
555         /* libunwind takes care of this */
556         unw_context_t unw_ctx;
557         MonoContext ctx;
558         MonoJitInfo *ji;
559         unw_word_t ip;
560         int res;
561
562         res = unw_getcontext (&unw_ctx);
563         g_assert (res == 0);
564         res = unw_init_local (&ctx.cursor, &unw_ctx);
565         g_assert (res == 0);
566
567         /* 
568          * Unwind until the first managed frame. This skips the signal handler frames
569          * too.
570          */
571         while (TRUE) {
572                 res = unw_get_reg (&ctx.cursor, UNW_IA64_IP, &ip);
573                 g_assert (res == 0);
574
575                 ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)ip);
576
577                 if (ji)
578                         break;
579
580                 res = unw_step (&ctx.cursor);
581                 g_assert (res >= 0);
582         }
583
584         mono_handle_exception (&ctx, obj, (gpointer)ip, test_only);
585
586         restore_context (&ctx);
587
588         g_assert_not_reached ();
589 }
590
591 gpointer
592 mono_arch_ip_from_context (void *sigctx)
593 {
594         /* On IA64, these two are equal */
595         unw_context_t *ctx = (unw_context_t*)sigctx;
596         unw_cursor_t cursor;
597         int res;
598         unw_word_t w;
599
600         res = unw_init_local (&cursor, ctx);
601         g_assert (res == 0);
602         res = unw_get_reg (&cursor, UNW_IA64_IP, &w);
603         g_assert (res == 0);
604
605         return (gpointer)w;
606 }