2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mono / mini / exceptions-arm.c
1 /*
2  * exceptions-arm.c: exception support for ARM
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/arm/arm-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-arm.h"
27
28 /*
29
30 struct sigcontext {
31         unsigned long trap_no;
32         unsigned long error_code;
33         unsigned long oldmask;
34         unsigned long arm_r0;
35         unsigned long arm_r1;
36         unsigned long arm_r2;
37         unsigned long arm_r3;
38         unsigned long arm_r4;
39         unsigned long arm_r5;
40         unsigned long arm_r6;
41         unsigned long arm_r7;
42         unsigned long arm_r8;
43         unsigned long arm_r9;
44         unsigned long arm_r10;
45         unsigned long arm_fp;
46         unsigned long arm_ip;
47         unsigned long arm_sp;
48         unsigned long arm_lr;
49         unsigned long arm_pc;
50         unsigned long arm_cpsr;
51         unsigned long fault_address;
52 };
53
54 gregs below is this struct
55 struct user_regs {
56         unsigned long int uregs[18];
57 };
58
59 the companion user_fpregs has just 8 double registers
60 (it's valid for FPA mode, will need changes for VFP)
61
62 typedef struct {
63         gregset_t gregs;
64         fpregset_t fpregs;
65 } mcontext_t;
66             
67 typedef struct ucontext {
68         unsigned long int uc_flags;
69         struct ucontext *uc_link;
70         __sigset_t uc_sigmask;
71         stack_t uc_stack;
72         mcontext_t uc_mcontext;
73         long int uc_filler[5];
74 } ucontext_t;
75
76 */
77
78 /*
79  * So, it turns out that the ucontext struct defined by libc is incorrect.
80  * We define our own version here and use it instead.
81  */
82
83 #if __APPLE__
84 #define my_ucontext ucontext_t
85 #else
86 typedef struct my_ucontext {
87         unsigned long       uc_flags;
88         struct my_ucontext *uc_link;
89         struct {
90                 void *p;
91                 int flags;
92                 size_t size;
93         } sstack_data;
94         struct sigcontext sig_ctx;
95         /* some 2.6.x kernel has fp data here after a few other fields
96          * we don't use them for now...
97          */
98 } my_ucontext;
99 #endif
100
101 /*
102  * arch_get_restore_context:
103  *
104  * Returns a pointer to a method which restores a previously saved sigcontext.
105  * The first argument in r0 is the pointer to the context.
106  */
107 gpointer
108 mono_arch_get_restore_context_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
109 {
110         guint8 *code;
111         guint8 *start;
112         int ctx_reg;
113
114         *ji = NULL;
115
116         start = code = mono_global_codeman_reserve (128);
117
118         /* 
119          * Move things to their proper place so we can restore all the registers with
120          * one instruction.
121          */
122
123         ctx_reg = ARMREG_R0;
124
125         /* move eip to PC */
126         ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, eip));
127         ARM_STR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_PC * 4));
128         /* move sp to SP */
129         ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, esp));
130         ARM_STR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * 4));
131         /* move ebp to FP */
132         ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, ebp));
133         ARM_STR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 4));
134
135         /* restore everything */
136         ARM_ADD_REG_IMM8 (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs));
137         ARM_LDM (code, ARMREG_IP, 0xffff);
138
139         /* never reached */
140         ARM_DBRK (code);
141
142         g_assert ((code - start) < 128);
143
144         mono_arch_flush_icache (start, code - start);
145
146         *code_size = code - start;
147
148         return start;
149 }
150
151 /*
152  * arch_get_call_filter:
153  *
154  * Returns a pointer to a method which calls an exception filter. We
155  * also use this function to call finally handlers (we pass NULL as 
156  * @exc object in this case).
157  */
158 gpointer
159 mono_arch_get_call_filter_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
160 {
161         guint8 *code;
162         guint8* start;
163         int ctx_reg;
164
165         *ji = NULL;
166
167         /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
168         start = code = mono_global_codeman_reserve (320);
169
170         /* save all the regs on the stack */
171         ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
172         ARM_PUSH (code, MONO_ARM_REGSAVE_MASK);
173
174         /* restore all the regs from ctx (in r0), but not sp, the stack pointer */
175         ctx_reg = ARMREG_R0;
176         ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, eip));
177         ARM_ADD_REG_IMM8 (code, ARMREG_LR, ctx_reg, G_STRUCT_OFFSET(MonoContext, regs) + (4 * 4));
178         ARM_LDM (code, ARMREG_LR, MONO_ARM_REGSAVE_MASK);
179         /* call handler at eip (r1) and set the first arg with the exception (r2) */
180         ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R2);
181         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
182         ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1);
183
184         /* epilog */
185         ARM_POP_NWB (code, 0xff0 | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));
186
187         g_assert ((code - start) < 320);
188
189         mono_arch_flush_icache (start, code - start);
190
191         *code_size = code - start;
192
193         return start;
194 }
195
196 void
197 mono_arm_throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
198 {
199         static void (*restore_context) (MonoContext *);
200         MonoContext ctx;
201         gboolean rethrow = eip & 1;
202
203         if (!restore_context)
204                 restore_context = mono_get_restore_context ();
205
206         eip &= ~1; /* clear the optional rethrow bit */
207         /* adjust eip so that it point into the call instruction */
208         eip -= 4;
209
210         /*printf ("stack in throw: %p\n", esp);*/
211         MONO_CONTEXT_SET_BP (&ctx, int_regs [ARMREG_FP - 4]);
212         MONO_CONTEXT_SET_SP (&ctx, esp);
213         MONO_CONTEXT_SET_IP (&ctx, eip);
214         memcpy (((guint8*)&ctx.regs) + (4 * 4), int_regs, sizeof (gulong) * 8);
215         /* memcpy (&ctx.fregs, fp_regs, sizeof (double) * MONO_SAVED_FREGS); */
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, (gpointer)(eip + 4), FALSE);
223         restore_context (&ctx);
224         g_assert_not_reached ();
225 }
226
227 void
228 mono_arm_throw_exception_by_token (guint32 type_token, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs)
229 {
230         mono_arm_throw_exception ((MonoObject*)mono_exception_from_token (mono_defaults.corlib, type_token), eip, esp, int_regs, fp_regs);
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) (guint32 ex_token, guint8* ip);
240  *
241  */
242 static gpointer 
243 mono_arch_get_throw_exception_generic (int size, int by_token, gboolean rethrow, guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
244 {
245         guint8 *start;
246         guint8 *code;
247
248         *ji = NULL;
249
250         code = start = mono_global_codeman_reserve (size);
251
252         /* save all the regs on the stack */
253         ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
254         ARM_PUSH (code, MONO_ARM_REGSAVE_MASK);
255
256         /* call throw_exception (exc, ip, sp, int_regs, fp_regs) */
257         /* caller sp */
258         ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, 10 * 4); /* 10 saved regs */
259         /* exc is already in place in r0 */
260         if (by_token) {
261                 /* The caller ip is already in R1 */
262         } else {
263                 ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_LR); /* caller ip */
264         }
265         /* FIXME: pointer to the saved fp regs */
266         /*pos = alloc_size - sizeof (double) * MONO_SAVED_FREGS;
267         ppc_addi (code, ppc_r7, ppc_sp, pos);*/
268         /* pointer to the saved int regs */
269         ARM_MOV_REG_REG (code, ARMREG_R3, ARMREG_SP); /* the pushed regs */
270         /* we encode rethrow in the ip, so we avoid args on the stack */
271         ARM_ORR_REG_IMM8 (code, ARMREG_R1, ARMREG_R1, rethrow);
272
273         if (aot) {
274                 *ji = mono_patch_info_list_prepend (*ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, by_token ? "mono_arm_throw_exception_by_token" : "mono_arm_throw_exception");
275                 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
276                 ARM_B (code, 0);
277                 *(gpointer*)(gpointer)code = NULL;
278                 code += 4;
279                 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
280         } else {
281                 code = mono_arm_emit_load_imm (code, ARMREG_IP, GPOINTER_TO_UINT (by_token ? (gpointer)mono_arm_throw_exception_by_token : (gpointer)mono_arm_throw_exception));
282         }
283         ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
284         ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
285         /* we should never reach this breakpoint */
286         ARM_DBRK (code);
287         g_assert ((code - start) < size);
288         mono_arch_flush_icache (start, code - start);
289
290         *code_size = code - start;
291
292         return start;
293 }
294
295 /**
296  * mono_arch_get_rethrow_exception:
297  *
298  * Returns a function pointer which can be used to rethrow 
299  * exceptions. The returned function has the following 
300  * signature: void (*func) (MonoException *exc); 
301  *
302  */
303 gpointer
304 mono_arch_get_rethrow_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
305 {
306         return mono_arch_get_throw_exception_generic (132, FALSE, TRUE, code_size, ji, aot);
307 }
308
309 /**
310  * arch_get_throw_exception:
311  *
312  * Returns a function pointer which can be used to raise 
313  * exceptions. The returned function has the following 
314  * signature: void (*func) (MonoException *exc); 
315  * For example to raise an arithmetic exception you can use:
316  *
317  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
318  * x86_call_code (code, arch_get_throw_exception ()); 
319  *
320  */
321 gpointer 
322 mono_arch_get_throw_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
323 {
324         return mono_arch_get_throw_exception_generic (132, FALSE, FALSE, code_size, ji, aot);
325 }
326
327 /**
328  * mono_arch_get_throw_corlib_exception:
329  *
330  * Returns a function pointer which can be used to raise 
331  * corlib exceptions. The returned function has the following 
332  * signature: void (*func) (guint32 ex_token, guint32 offset); 
333  * Here, offset is the offset which needs to be substracted from the caller IP 
334  * to get the IP of the throw. Passing the offset has the advantage that it 
335  * needs no relocations in the caller.
336  * On ARM, the ip is passed instead of an offset.
337  */
338 gpointer 
339 mono_arch_get_throw_corlib_exception_full (guint32 *code_size, MonoJumpInfo **ji, gboolean aot)
340 {
341         return mono_arch_get_throw_exception_generic (168, TRUE, FALSE, code_size, ji, aot);
342 }       
343
344 /* 
345  * mono_arch_find_jit_info_ext:
346  *
347  * See exceptions-amd64.c for docs;
348  */
349 gboolean
350 mono_arch_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, 
351                                                          MonoJitInfo *ji, MonoContext *ctx, 
352                                                          MonoContext *new_ctx, MonoLMF **lmf, 
353                                                          StackFrameInfo *frame)
354 {
355         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
356
357         memset (frame, 0, sizeof (StackFrameInfo));
358         frame->ji = ji;
359         frame->managed = FALSE;
360
361         *new_ctx = *ctx;
362
363         if (ji != NULL) {
364                 int i;
365                 gssize regs [MONO_MAX_IREGS + 1];
366                 guint8 *cfa;
367                 guint32 unwind_info_len;
368                 guint8 *unwind_info;
369
370                 frame->type = FRAME_TYPE_MANAGED;
371
372                 if (!ji->method->wrapper_type || ji->method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
373                         frame->managed = TRUE;
374
375                 if (ji->from_aot)
376                         unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
377                 else
378                         unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len);
379
380                 for (i = 0; i < 16; ++i)
381                         regs [i] = new_ctx->regs [i];
382                 regs [ARMREG_SP] = new_ctx->esp;
383
384                 mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
385                                                    (guint8*)ji->code_start + ji->code_size,
386                                                    ip, regs, MONO_MAX_IREGS, &cfa);
387
388                 for (i = 0; i < 16; ++i)
389                         new_ctx->regs [i] = regs [i];
390                 new_ctx->eip = regs [ARMREG_LR];
391                 new_ctx->esp = (gsize)cfa;
392                 new_ctx->ebp = new_ctx->esp;
393
394                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
395                         /* remove any unused lmf */
396                         *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
397                 }
398
399                 /* we substract 1, so that the IP points into the call instruction */
400                 new_ctx->eip--;
401
402                 return TRUE;
403         } else if (*lmf) {
404
405                 if (((gsize)(*lmf)->previous_lmf) & 2) {
406                         /* 
407                          * This LMF entry is created by the soft debug code to mark transitions to
408                          * managed code done during invokes.
409                          */
410                         MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
411
412                         g_assert (ext->debugger_invoke);
413
414                         memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
415
416                         *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
417
418                         frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
419
420                         return TRUE;
421                 }
422
423                 frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
424                 
425                 if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) {
426                         frame->ji = ji;
427                 } else {
428                         if (!(*lmf)->method)
429                                 return FALSE;
430                         frame->method = (*lmf)->method;
431                 }
432
433                 memcpy (&new_ctx->regs [0], &(*lmf)->iregs [0], sizeof (gulong) * 13);
434                 /* SP is skipped */
435                 new_ctx->regs [ARMREG_LR] = (*lmf)->iregs [ARMREG_LR - 1];
436                 /* This is the sp for the current frame */
437                 new_ctx->esp = (*lmf)->iregs [ARMREG_FP];
438                 new_ctx->eip = (*lmf)->iregs [13];
439                 new_ctx->ebp = new_ctx->esp;
440
441                 *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
442
443                 return TRUE;
444         }
445
446         return FALSE;
447 }
448
449 void
450 mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
451 {
452 #if BROKEN_LINUX
453         g_assert_not_reached ();
454 #else
455         my_ucontext *my_uc = sigctx;
456
457         mctx->eip = UCONTEXT_REG_PC (my_uc);
458         mctx->esp = UCONTEXT_REG_SP (my_uc);
459         memcpy (&mctx->regs, &UCONTEXT_REG_R0 (my_uc), sizeof (gulong) * 16);
460 #endif
461         mctx->ebp = mctx->regs [ARMREG_FP];
462 }
463
464 void
465 mono_arch_monoctx_to_sigctx (MonoContext *mctx, void *ctx)
466 {
467 #if BROKEN_LINUX
468         g_assert_not_reached ();
469 #else
470         my_ucontext *my_uc = ctx;
471
472         UCONTEXT_REG_PC (my_uc) = mctx->eip;
473         UCONTEXT_REG_SP (my_uc) = mctx->ebp;
474         /* The upper registers are not guaranteed to be valid */
475         memcpy (&UCONTEXT_REG_R0 (my_uc), &mctx->regs, sizeof (gulong) * 12);
476 #endif
477 }
478
479 /*
480  * This is the function called from the signal handler
481  */
482 gboolean
483 mono_arch_handle_exception (void *ctx, gpointer obj, gboolean test_only)
484 {
485         MonoContext mctx;
486         gboolean result;
487
488         mono_arch_sigctx_to_monoctx (ctx, &mctx);
489
490         result = mono_handle_exception (&mctx, obj, (gpointer)mctx.eip, test_only);
491         /* restore the context so that returning from the signal handler will invoke
492          * the catch clause 
493          */
494         mono_arch_monoctx_to_sigctx (&mctx, ctx);
495         return result;
496 }
497
498 gpointer
499 mono_arch_ip_from_context (void *sigctx)
500 {
501 #if BROKEN_LINUX
502         g_assert_not_reached ();
503 #else
504         my_ucontext *my_uc = sigctx;
505         return (void*) UCONTEXT_REG_PC (my_uc);
506 #endif
507 }
508
509 gboolean
510 mono_arch_has_unwind_info (gconstpointer addr)
511 {
512         return FALSE;
513 }
514