2005-07-06 Zoltan Varga <vargaz@freemail.hu>
[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 mono_create_ftnptr (gpointer ptr)
47 {
48         gpointer *desc = g_malloc (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_alloc (code, local0 + 0, local0 - in0, out0 - local0, nout, 0);
117         ia64_mov_from_br (code, local0 + 1, IA64_B0);
118
119         /* FIXME: This depends on the current instruction emitter */
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_B0, in0 + 1);
135         /* Return address */
136         ia64_mov_from_ip (code, GP_SCRATCH_REG);
137         ia64_adds_imm (code, GP_SCRATCH_REG, 3 * 16, GP_SCRATCH_REG);
138
139         /* Call the filter */
140         ia64_br_cond_reg (code, IA64_B0);
141
142         /* R8 contains the result of the filter */
143         ia64_mov_to_ar_i (code, IA64_PFS, local0 + 0);
144         ia64_mov_ret_to_br (code, IA64_B0, local0 + 1);
145         ia64_br_ret_reg (code, IA64_B0);
146
147         ia64_codegen_close (code);
148
149         g_assert ((code.buf - start) <= 256);
150
151         mono_arch_flush_icache (start, code.buf - start);
152
153         di = g_malloc0 (sizeof (unw_dyn_info_t));
154         di->start_ip = (unw_word_t) start;
155         di->end_ip = (unw_word_t) code.buf;
156         di->gp = 0;
157         di->format = UNW_INFO_FORMAT_DYNAMIC;
158         di->u.pi.name_ptr = (unw_word_t)"throw_trampoline";
159         di->u.pi.regions = r_pro;
160
161         _U_dyn_register (di);
162
163         return mono_create_ftnptr (start);
164 }
165
166 static int
167 call_filter (MonoContext *ctx, gpointer ip)
168 {
169         static int (*filter) (MonoContext *, gpointer) = NULL;
170         gpointer fp = MONO_CONTEXT_GET_BP (ctx);
171
172         /* FIXME: thread safety */
173         if (!filter)
174                 filter = get_real_call_filter ();
175
176         return filter (fp, ip);
177 }
178
179 /*
180  * mono_arch_get_call_filter:
181  *
182  * Returns a pointer to a method which calls an exception filter. We
183  * also use this function to call finally handlers (we pass NULL as 
184  * @exc object in this case).
185  */
186 gpointer
187 mono_arch_get_call_filter (void)
188 {
189         return call_filter;
190 }
191
192 static void
193 throw_exception (MonoObject *exc, guint64 rethrow)
194 {
195         unw_context_t unw_ctx;
196         MonoContext ctx;
197         MonoJitInfo *ji;
198         unw_word_t ip;
199         int res;
200
201         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
202                 MonoException *mono_ex = (MonoException*)exc;
203                 if (!rethrow)
204                         mono_ex->stack_trace = NULL;
205         }
206
207         res = unw_getcontext (&unw_ctx);
208         g_assert (res == 0);
209         res = unw_init_local (&ctx.cursor, &unw_ctx);
210         g_assert (res == 0);
211
212         /* 
213          * Unwind until the first managed frame. This is needed since 
214          * mono_handle_exception expects the variables in the original context to
215          * correspond to the method returned by mono_find_jit_info.
216          */
217         while (TRUE) {
218                 res = unw_get_reg (&ctx.cursor, UNW_IA64_IP, &ip);
219                 g_assert (res == 0);
220
221                 ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)ip);
222
223                 if (ji)
224                         break;
225
226                 res = unw_step (&ctx.cursor);
227
228                 if (res == 0) {
229                         /*
230                          * This means an unhandled exception during the compilation of a
231                          * topmost method like Main
232                          */
233                         break;
234                 }
235                 g_assert (res >= 0);
236         }
237
238         mono_handle_exception (&ctx, exc, (gpointer)(ip), FALSE);
239         restore_context (&ctx);
240
241         g_assert_not_reached ();
242 }
243
244 static gpointer
245 get_throw_trampoline (gboolean rethrow)
246 {
247         guint8* start;
248         Ia64CodegenState code;
249         gpointer ptr = throw_exception;
250         int i, in0, local0, out0;
251         unw_dyn_info_t *di;
252         unw_dyn_region_info_t *r_pro;
253
254         start = mono_global_codeman_reserve (256);
255
256         in0 = 32;
257         local0 = in0 + 1;
258         out0 = local0 + 2;
259
260         ia64_codegen_init (code, start);
261         ia64_alloc (code, local0 + 0, local0 - in0, out0 - local0, 3, 0);
262         ia64_mov_from_br (code, local0 + 1, IA64_B0);   
263
264         /* FIXME: This depends on the current instruction emitter */
265
266         r_pro = g_malloc0 (_U_dyn_region_info_size (2));
267         r_pro->op_count = 2;
268         r_pro->insn_count = 6;
269         i = 0;
270         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 2,
271                                                 /* reg=*/ UNW_IA64_AR_PFS, /* dst=*/ UNW_IA64_GR + local0 + 0);
272         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 5,
273                                                 /* reg=*/ UNW_IA64_RP, /* dst=*/ UNW_IA64_GR + local0 + 1);
274         g_assert ((unsigned) i <= r_pro->op_count);     
275
276         /* Set args */
277         ia64_mov (code, out0 + 0, in0 + 0);
278         ia64_adds_imm (code, out0 + 1, rethrow, IA64_R0);
279
280         /* Call throw_exception */
281         ia64_movl (code, GP_SCRATCH_REG, ptr);
282         ia64_ld8_inc_imm (code, GP_SCRATCH_REG2, GP_SCRATCH_REG, 8);
283         ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG2);
284         ia64_ld8 (code, IA64_GP, GP_SCRATCH_REG);
285         ia64_br_call_reg (code, IA64_B0, IA64_B6);
286
287         /* Not reached */
288         ia64_break_i (code, 1000);
289         ia64_codegen_close (code);
290
291         g_assert ((code.buf - start) <= 256);
292
293         mono_arch_flush_icache (start, code.buf - start);
294
295         di = g_malloc0 (sizeof (unw_dyn_info_t));
296         di->start_ip = (unw_word_t) start;
297         di->end_ip = (unw_word_t) code.buf;
298         di->gp = 0;
299         di->format = UNW_INFO_FORMAT_DYNAMIC;
300         di->u.pi.name_ptr = (unw_word_t)"throw_trampoline";
301         di->u.pi.regions = r_pro;
302
303         _U_dyn_register (di);
304
305         return mono_create_ftnptr (start);
306 }
307
308 /**
309  * mono_arch_get_throw_exception:
310  *
311  * Returns a function pointer which can be used to raise 
312  * exceptions. The returned function has the following 
313  * signature: void (*func) (MonoException *exc); 
314  *
315  */
316 gpointer 
317 mono_arch_get_throw_exception (void)
318 {
319         static guint8* start;
320         static gboolean inited = FALSE;
321
322         if (inited)
323                 return start;
324
325         start = get_throw_trampoline (FALSE);
326
327         inited = TRUE;
328
329         return start;
330 }
331
332 gpointer 
333 mono_arch_get_rethrow_exception (void)
334 {
335         static guint8* start;
336         static gboolean inited = FALSE;
337
338         if (inited)
339                 return start;
340
341         start = get_throw_trampoline (TRUE);
342
343         inited = TRUE;
344
345         return start;
346 }
347
348 gpointer 
349 mono_arch_get_throw_exception_by_name (void)
350 {       
351         guint8* start;
352         Ia64CodegenState code;
353
354         start = mono_global_codeman_reserve (64);
355
356         /* Not used on ia64 */
357         ia64_codegen_init (code, start);
358         ia64_break_i (code, 1001);
359         ia64_codegen_close (code);
360
361         g_assert ((code.buf - start) <= 256);
362
363         mono_arch_flush_icache (start, code.buf - start);
364
365         return start;
366 }
367
368 /**
369  * mono_arch_get_throw_corlib_exception:
370  *
371  * Returns a function pointer which can be used to raise 
372  * corlib exceptions. The returned function has the following 
373  * signature: void (*func) (guint32 ex_token, guint32 offset); 
374  * Here, offset is the offset which needs to be substracted from the caller IP 
375  * to get the IP of the throw. Passing the offset has the advantage that it 
376  * needs no relocations in the caller.
377  */
378 gpointer 
379 mono_arch_get_throw_corlib_exception (void)
380 {
381         static guint8* start;
382         static gboolean inited = FALSE;
383         gpointer ptr;
384         int i, in0, local0, out0, nout;
385         Ia64CodegenState code;
386         unw_dyn_info_t *di;
387         unw_dyn_region_info_t *r_pro;
388
389         if (inited)
390                 return start;
391
392         start = mono_global_codeman_reserve (1024);
393
394         in0 = 32;
395         local0 = in0 + 2;
396         out0 = local0 + 4;
397         nout = 3;
398
399         ia64_codegen_init (code, start);
400         ia64_alloc (code, local0 + 0, local0 - in0, out0 - local0, nout, 0);
401         ia64_mov_from_br (code, local0 + 1, IA64_RP);
402
403         r_pro = g_malloc0 (_U_dyn_region_info_size (2));
404         r_pro->op_count = 2;
405         r_pro->insn_count = 6;
406         i = 0;
407         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 2,
408                                                 /* reg=*/ UNW_IA64_AR_PFS, /* dst=*/ UNW_IA64_GR + local0 + 0);
409         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 5,
410                                                 /* reg=*/ UNW_IA64_RP, /* dst=*/ UNW_IA64_GR + local0 + 1);
411         g_assert ((unsigned) i <= r_pro->op_count);     
412
413         /* Call exception_from_token */
414         ia64_movl (code, out0 + 0, mono_defaults.exception_class->image);
415         ia64_mov (code, out0 + 1, in0 + 0);
416         ptr = mono_exception_from_token;
417         ia64_movl (code, GP_SCRATCH_REG, ptr);
418         ia64_ld8_inc_imm (code, GP_SCRATCH_REG2, GP_SCRATCH_REG, 8);
419         ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG2);
420         ia64_ld8 (code, IA64_GP, GP_SCRATCH_REG);
421         ia64_br_call_reg (code, IA64_B0, IA64_B6);
422         ia64_mov (code, local0 + 3, IA64_R8);
423
424         /* Compute throw ip */
425         ia64_mov (code, local0 + 2, local0 + 1);
426         ia64_sub (code, local0 + 2, local0 + 2, in0 + 1);
427
428         /* Trick the unwind library into using throw_ip as the IP in the caller frame */
429         ia64_mov (code, local0 + 1, local0 + 2);
430
431         /* Set args */
432         ia64_mov (code, out0 + 0, local0 + 3);
433         ia64_mov (code, out0 + 1, IA64_R0);
434
435         /* Call throw_exception */
436         ptr = throw_exception;
437         ia64_movl (code, GP_SCRATCH_REG, ptr);
438         ia64_ld8_inc_imm (code, GP_SCRATCH_REG2, GP_SCRATCH_REG, 8);
439         ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG2);
440         ia64_ld8 (code, IA64_GP, GP_SCRATCH_REG);
441         ia64_br_call_reg (code, IA64_B0, IA64_B6);
442
443         ia64_break_i (code, 1002);
444         ia64_codegen_close (code);
445
446         g_assert ((code.buf - start) <= 1024);
447
448         di = g_malloc0 (sizeof (unw_dyn_info_t));
449         di->start_ip = (unw_word_t) start;
450         di->end_ip = (unw_word_t) code.buf;
451         di->gp = 0;
452         di->format = UNW_INFO_FORMAT_DYNAMIC;
453         di->u.pi.name_ptr = (unw_word_t)"throw_corlib_exception_trampoline";
454         di->u.pi.regions = r_pro;
455
456         _U_dyn_register (di);
457
458         mono_arch_flush_icache (start, code.buf - start);
459
460         return mono_create_ftnptr (start);
461 }
462
463 /* mono_arch_find_jit_info:
464  *
465  * This function is used to gather information from @ctx. It return the 
466  * MonoJitInfo of the corresponding function, unwinds one stack frame and
467  * stores the resulting context into @new_ctx. It also stores a string 
468  * describing the stack location into @trace (if not NULL), and modifies
469  * the @lmf if necessary. @native_offset return the IP offset from the 
470  * start of the function or -1 if that info is not available.
471  */
472 MonoJitInfo *
473 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
474                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
475                          gboolean *managed)
476 {
477         MonoJitInfo *ji;
478         int err;
479         unw_word_t ip;
480
481         *new_ctx = *ctx;
482
483         while (TRUE) {
484                 err = unw_get_reg (&new_ctx->cursor, UNW_IA64_IP, &ip);
485                 g_assert (err == 0);
486
487                 /* Avoid costly table lookup during stack overflow */
488                 if (prev_ji && ((guint8*)ip > (guint8*)prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
489                         ji = prev_ji;
490                 else
491                         ji = mono_jit_info_table_find (domain, (gpointer)ip);
492
493                 if (managed)
494                         *managed = FALSE;
495
496                 /*
497                 {
498                         char name[256];
499                         unw_word_t off;
500
501                         unw_get_proc_name (&new_ctx->cursor, name, 256, &off);
502                         printf ("F: %s\n", name);
503                 }
504                 */
505
506                 if (ji != NULL) {
507                         if (managed)
508                                 if (!ji->method->wrapper_type)
509                                         *managed = TRUE;
510
511                         /*
512                          * Some managed methods like pinvoke wrappers might have save_lmf set.
513                          * In this case, register save/restore code is not generated by the 
514                          * JIT, so we have to restore callee saved registers from the lmf.
515                          */
516                         if (ji->method->save_lmf) {
517                         }
518                         else {
519                         }
520
521                         if (*lmf && (MONO_CONTEXT_GET_BP (new_ctx) >= (gpointer)(*lmf)->ebp)) {
522                                 /* remove any unused lmf */
523                                 *lmf = (*lmf)->previous_lmf;
524                         }
525
526                         break;
527                 }
528
529                 /* This is an unmanaged frame, so just unwind through it */
530                 err = unw_step (&new_ctx->cursor);
531                 g_assert (err >= 0);
532
533                 if (err == 0)
534                         break;
535         }
536
537         if (ji) {
538                 err = unw_step (&new_ctx->cursor);
539                 g_assert (err >= 0);
540
541                 return ji;
542         }
543         else
544                 return (gpointer)(gssize)-1;
545 }
546
547 /**
548  * mono_arch_handle_exception:
549  *
550  * @ctx: saved processor state
551  * @obj: the exception object
552  */
553 gboolean
554 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
555 {
556         /* libunwind takes care of this */
557         unw_context_t unw_ctx;
558         MonoContext ctx;
559         MonoJitInfo *ji;
560         unw_word_t ip;
561         int res;
562
563         res = unw_getcontext (&unw_ctx);
564         g_assert (res == 0);
565         res = unw_init_local (&ctx.cursor, &unw_ctx);
566         g_assert (res == 0);
567
568         /* 
569          * Unwind until the first managed frame. This skips the signal handler frames
570          * too.
571          */
572         while (TRUE) {
573                 res = unw_get_reg (&ctx.cursor, UNW_IA64_IP, &ip);
574                 g_assert (res == 0);
575
576                 ji = mono_jit_info_table_find (mono_domain_get (), (gpointer)ip);
577
578                 if (ji)
579                         break;
580
581                 res = unw_step (&ctx.cursor);
582                 g_assert (res >= 0);
583         }
584
585         mono_handle_exception (&ctx, obj, (gpointer)ip, test_only);
586
587         restore_context (&ctx);
588
589         g_assert_not_reached ();
590 }
591
592 gpointer
593 mono_arch_ip_from_context (void *sigctx)
594 {
595         NOT_IMPLEMENTED;
596         return NULL;
597 }