Merge pull request #101 from occ/master
[mono.git] / mono / mini / exceptions-s390.c
1 /*------------------------------------------------------------------*/
2 /*                                                                  */
3 /* Name        - exceptions-s390.c                                  */
4 /*                                                                  */
5 /* Function    - Exception support for S/390.                       */
6 /*                                                                  */
7 /* Name        - Neale Ferguson (Neale.Ferguson@SoftwareAG-usa.com) */
8 /*                                                                  */
9 /* Date        - January, 2004                                      */
10 /*                                                                  */
11 /* Derivation  - From exceptions-x86 & exceptions-ppc               */
12 /*               Paolo Molaro (lupus@ximian.com)                    */
13 /*               Dietmar Maurer (dietmar@ximian.com)                */
14 /*                                                                  */
15 /* Copyright   - 2001 Ximian, Inc.                                  */
16 /*                                                                  */
17 /*------------------------------------------------------------------*/
18
19 /*------------------------------------------------------------------*/
20 /*                 D e f i n e s                                    */
21 /*------------------------------------------------------------------*/
22
23 #define S390_CALLFILTER_INTREGS         S390_MINIMAL_STACK_SIZE
24 #define S390_CALLFILTER_FLTREGS         S390_CALLFILTER_INTREGS+(16*sizeof(gulong))
25 #define S390_CALLFILTER_ACCREGS         S390_CALLFILTER_FLTREGS+(16*sizeof(gdouble))
26 #define S390_CALLFILTER_SIZE            (S390_CALLFILTER_ACCREGS+(16*sizeof(gulong)))
27
28 #define S390_THROWSTACK_ACCPRM          S390_MINIMAL_STACK_SIZE
29 #define S390_THROWSTACK_FPCPRM          S390_THROWSTACK_ACCPRM+sizeof(gpointer)
30 #define S390_THROWSTACK_RETHROW         S390_THROWSTACK_FPCPRM+sizeof(gulong)
31 #define S390_THROWSTACK_INTREGS         S390_THROWSTACK_RETHROW+sizeof(gboolean)
32 #define S390_THROWSTACK_FLTREGS         S390_THROWSTACK_INTREGS+(16*sizeof(gulong))
33 #define S390_THROWSTACK_ACCREGS         S390_THROWSTACK_FLTREGS+(16*sizeof(gdouble))
34 #define S390_THROWSTACK_SIZE            (S390_THROWSTACK_ACCREGS+(16*sizeof(gulong)))
35
36 #define SZ_THROW        384
37
38 /*========================= End of Defines =========================*/
39
40 /*------------------------------------------------------------------*/
41 /*                 I n c l u d e s                                  */
42 /*------------------------------------------------------------------*/
43
44 #include <config.h>
45 #include <glib.h>
46 #include <signal.h>
47 #include <string.h>
48 #include <ucontext.h>
49
50 #include <mono/arch/s390/s390-codegen.h>
51 #include <mono/metadata/appdomain.h>
52 #include <mono/metadata/tabledefs.h>
53 #include <mono/metadata/threads.h>
54 #include <mono/metadata/debug-helpers.h>
55 #include <mono/metadata/exception.h>
56 #include <mono/metadata/mono-debug.h>
57
58 #include "mini.h"
59 #include "mini-s390.h"
60
61 /*========================= End of Includes ========================*/
62
63 /*------------------------------------------------------------------*/
64 /*                   P r o t o t y p e s                            */
65 /*------------------------------------------------------------------*/
66
67 gboolean mono_arch_handle_exception (void     *ctx,
68                                      gpointer obj, 
69                                      gboolean test_only);
70
71 /*========================= End of Prototypes ======================*/
72
73 /*------------------------------------------------------------------*/
74 /*                 G l o b a l   V a r i a b l e s                  */
75 /*------------------------------------------------------------------*/
76
77 /*====================== End of Global Variables ===================*/
78
79 /*------------------------------------------------------------------*/
80 /*                                                                  */
81 /* Name         - mono_arch_get_call_filter                         */
82 /*                                                                  */
83 /* Function     - Return a pointer to a method which calls an       */
84 /*                exception filter. We also use this function to    */
85 /*                call finally handlers (we pass NULL as @exc       */
86 /*                object in this case).                             */
87 /*                                                                  */
88 /*------------------------------------------------------------------*/
89
90 gpointer
91 mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
92 {
93         static guint8 *start;
94         static int inited = 0;
95         guint8 *code;
96         int alloc_size, pos, i;
97
98         g_assert (!aot);
99         if (info)
100                 *info = NULL;
101
102         if (inited)
103                 return start;
104
105         inited = 1;
106         /* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
107         code = start = mono_global_codeman_reserve (512);
108
109         s390_stm (code, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
110         s390_lr  (code, s390_r14, STK_BASE);
111         alloc_size = S390_ALIGN(S390_CALLFILTER_SIZE, S390_STACK_ALIGNMENT);
112         s390_ahi (code, STK_BASE, -alloc_size);
113         s390_st  (code, s390_r14, 0, STK_BASE, 0);
114
115         /*------------------------------------------------------*/
116         /* save general registers on stack                      */
117         /*------------------------------------------------------*/
118         s390_stm (code, s390_r0, s390_r13, STK_BASE, S390_CALLFILTER_INTREGS);
119
120         /*------------------------------------------------------*/
121         /* save floating point registers on stack               */
122         /*------------------------------------------------------*/
123 //      pos = S390_CALLFILTER_FLTREGS;
124 //      for (i = 0; i < 16; ++i) {
125 //              s390_std (code, i, 0, STK_BASE, pos);
126 //              pos += sizeof (gdouble);
127 //      }
128
129         /*------------------------------------------------------*/
130         /* save access registers on stack                       */
131         /*------------------------------------------------------*/
132 //      s390_stam (code, s390_a0, s390_a15, STK_BASE, S390_CALLFILTER_ACCREGS);
133
134         /*------------------------------------------------------*/
135         /* Get A(Context)                                       */
136         /*------------------------------------------------------*/
137         s390_lr   (code, s390_r13, s390_r2);
138
139         /*------------------------------------------------------*/
140         /* Get A(Handler Entry Point)                           */
141         /*------------------------------------------------------*/
142         s390_lr   (code, s390_r0, s390_r3);
143
144         /*------------------------------------------------------*/
145         /* Set parameter register with Exception                */
146         /*------------------------------------------------------*/
147         s390_lr   (code, s390_r2, s390_r4);
148
149         /*------------------------------------------------------*/
150         /* Load all registers with values from the context      */
151         /*------------------------------------------------------*/
152         s390_lm   (code, s390_r3, s390_r12, s390_r13, 
153                    G_STRUCT_OFFSET(MonoContext, uc_mcontext.gregs[3]));
154         pos = G_STRUCT_OFFSET(MonoContext, uc_mcontext.fpregs.fprs[0]);
155         for (i = 0; i < 16; ++i) {
156                 s390_ld  (code, i, 0, s390_r13, pos);
157                 pos += sizeof(gdouble);
158         }
159         
160         /*------------------------------------------------------*/
161         /* Point at the copied stack frame and call the filter  */
162         /*------------------------------------------------------*/
163         s390_lr   (code, s390_r1, s390_r0);
164         s390_basr (code, s390_r14, s390_r1);
165
166         /*------------------------------------------------------*/
167         /* Save return value                                    */
168         /*------------------------------------------------------*/
169         s390_lr   (code, s390_r14, s390_r2);
170
171         /*------------------------------------------------------*/
172         /* Restore all the regs from the stack                  */
173         /*------------------------------------------------------*/
174         s390_lm (code, s390_r0, s390_r13, STK_BASE, S390_CALLFILTER_INTREGS);
175 //      pos = S390_CALLFILTER_FLTREGS;
176 //      for (i = 0; i < 16; ++i) {
177 //              s390_ld (code, i, 0, STK_BASE, pos);
178 //              pos += sizeof (gdouble);
179 //      }
180
181         s390_lr   (code, s390_r2, s390_r14);
182 //      s390_lam  (code, s390_a0, s390_a15, STK_BASE, S390_CALLFILTER_ACCREGS);
183         s390_ahi  (code, s390_r15, alloc_size);
184         s390_lm   (code, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
185         s390_br   (code, s390_r14);
186
187         g_assert ((code - start) < SZ_THROW); 
188         return start;
189 }
190
191 /*========================= End of Function ========================*/
192
193 /*------------------------------------------------------------------*/
194 /*                                                                  */
195 /* Name         - throw_exception.                                  */
196 /*                                                                  */
197 /* Function     - Raise an exception based on the parameters passed.*/
198 /*                                                                  */
199 /*------------------------------------------------------------------*/
200
201 static void
202 throw_exception (MonoObject *exc, unsigned long ip, unsigned long sp, 
203                  gulong *int_regs, gdouble *fp_regs, gulong *acc_regs, 
204                  guint fpc, gboolean rethrow)
205 {
206         MonoContext ctx;
207         int iReg;
208         
209         memset(&ctx, 0, sizeof(ctx));
210
211         getcontext(&ctx);
212
213         /* adjust eip so that it point into the call instruction */
214         ip -= 6;
215
216         for (iReg = 0; iReg < 16; iReg++) {
217                 ctx.uc_mcontext.gregs[iReg]         = int_regs[iReg];
218                 ctx.uc_mcontext.fpregs.fprs[iReg].d = fp_regs[iReg];
219                 ctx.uc_mcontext.aregs[iReg]         = acc_regs[iReg];
220         }
221
222         ctx.uc_mcontext.fpregs.fpc = fpc;
223
224         MONO_CONTEXT_SET_BP (&ctx, sp);
225         MONO_CONTEXT_SET_IP (&ctx, ip);
226         
227         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
228                 MonoException *mono_ex = (MonoException*)exc;
229                 if (!rethrow)
230                         mono_ex->stack_trace = NULL;
231         }
232         mono_arch_handle_exception (&ctx, exc, FALSE);
233         setcontext(&ctx);
234
235         g_assert_not_reached ();
236 }
237
238 /*========================= End of Function ========================*/
239
240 /*------------------------------------------------------------------*/
241 /*                                                                  */
242 /* Name         - get_throw_exception_generic                       */
243 /*                                                                  */
244 /* Function     - Return a function pointer which can be used to    */
245 /*                raise exceptions. The returned function has the   */
246 /*                following signature:                              */
247 /*                void (*func) (MonoException *exc); or,            */
248 /*                void (*func) (char *exc_name);                    */
249 /*                                                                  */
250 /*------------------------------------------------------------------*/
251
252 static gpointer 
253 get_throw_exception_generic (guint8 *start, int size, 
254                              int by_name, gboolean rethrow)
255 {
256         guint8 *code;
257         int alloc_size, pos, i, offset;
258
259         code = start;
260
261         s390_stm (code, s390_r6, s390_r14, STK_BASE, S390_REG_SAVE_OFFSET);
262         alloc_size = S390_ALIGN(S390_THROWSTACK_SIZE, S390_STACK_ALIGNMENT);
263         s390_lr   (code, s390_r14, STK_BASE);
264         s390_ahi  (code, STK_BASE, -alloc_size);
265         s390_st   (code, s390_r14, 0, STK_BASE, 0);
266         if (by_name) {
267                 s390_lr   (code, s390_r4, s390_r2);
268                 s390_bras (code, s390_r13, 6);
269                 s390_word (code, mono_defaults.corlib);
270                 s390_word (code, "System");
271                 s390_l    (code, s390_r2, 0, s390_r13, 0);
272                 s390_l    (code, s390_r3, 0, s390_r13, 4);
273                 offset = (guint32) S390_RELATIVE(mono_exception_from_name, code);
274                 s390_brasl(code, s390_r14, offset);
275         }
276         /*------------------------------------------------------*/
277         /* save the general registers on the stack              */
278         /*------------------------------------------------------*/
279         s390_stm (code, s390_r0, s390_r13, STK_BASE, S390_THROWSTACK_INTREGS);
280
281         s390_lr  (code, s390_r1, STK_BASE);
282         s390_ahi (code, s390_r1, alloc_size);
283         /*------------------------------------------------------*/
284         /* save the return address in the parameter register    */
285         /*------------------------------------------------------*/
286         s390_l   (code, s390_r3, 0, s390_r1, S390_RET_ADDR_OFFSET);
287
288         /*------------------------------------------------------*/
289         /* save the floating point registers                    */
290         /*------------------------------------------------------*/
291         pos = S390_THROWSTACK_FLTREGS;
292         for (i = 0; i < 16; ++i) {
293                 s390_std (code, i, 0,STK_BASE, pos);
294                 pos += sizeof (gdouble);
295         }
296         /*------------------------------------------------------*/
297         /* save the access registers                            */
298         /*------------------------------------------------------*/
299         s390_stam (code, s390_r0, s390_r15, STK_BASE, S390_THROWSTACK_ACCREGS);
300
301         /*------------------------------------------------------*/
302         /* call throw_exception (exc, ip, sp, gr, fr, ar)       */
303         /* exc is already in place in r2                        */
304         /*------------------------------------------------------*/
305         s390_lr   (code, s390_r4, s390_r1);        /* caller sp */
306         /*------------------------------------------------------*/
307         /* pointer to the saved int regs                        */
308         /*------------------------------------------------------*/
309         s390_la   (code, s390_r5, 0, STK_BASE, S390_THROWSTACK_INTREGS);
310         s390_la   (code, s390_r6, 0, STK_BASE, S390_THROWSTACK_FLTREGS);
311         s390_la   (code, s390_r7, 0, STK_BASE, S390_THROWSTACK_ACCREGS);
312         s390_st   (code, s390_r7, 0, STK_BASE, S390_THROWSTACK_ACCPRM);
313         s390_stfpc(code, STK_BASE, S390_THROWSTACK_FPCPRM);
314         s390_lhi  (code, s390_r7, rethrow);
315         s390_st   (code, s390_r7, 0, STK_BASE, S390_THROWSTACK_RETHROW);
316         offset = (guint32) S390_RELATIVE(throw_exception, code);
317         s390_brasl(code, s390_r14, offset);
318         /* we should never reach this breakpoint */
319         s390_break (code);
320         g_assert ((code - start) < size);
321         return start;
322 }
323
324 /*========================= End of Function ========================*/
325
326 /*------------------------------------------------------------------*/
327 /*                                                                  */
328 /* Name         - arch_get_throw_exception                          */
329 /*                                                                  */
330 /* Function     - Return a function pointer which can be used to    */
331 /*                raise exceptions. The returned function has the   */
332 /*                following signature:                              */
333 /*                void (*func) (MonoException *exc);                */
334 /*                                                                  */
335 /*------------------------------------------------------------------*/
336
337 gpointer
338 mono_arch_get_throw_exception (MonoTrampInfo **info, gboolean aot)
339 {
340         static guint8 *start;
341         static int inited = 0;
342
343         g_assert (!aot);
344         if (info)
345                 *info = NULL;
346
347         if (inited)
348                 return start;
349         start = mono_global_codeman_reserve (SZ_THROW);
350         get_throw_exception_generic (start, SZ_THROW, FALSE, FALSE);
351         inited = 1;
352         return start;
353 }
354
355 /*========================= End of Function ========================*/
356
357 /*------------------------------------------------------------------*/
358 /*                                                                  */
359 /* Name         - arch_get_rethrow_exception                        */
360 /*                                                                  */
361 /* Function     - Return a function pointer which can be used to    */
362 /*                raise exceptions. The returned function has the   */
363 /*                following signature:                              */
364 /*                void (*func) (MonoException *exc);                */
365 /*                                                                  */
366 /*------------------------------------------------------------------*/
367
368 gpointer 
369 mono_arch_get_rethrow_exception (MonoTrampInfo **info, gboolean aot)
370 {
371         static guint8 *start;
372         static int inited = 0;
373
374         g_assert (!aot);
375         if (info)
376                 *info = NULL;
377
378         if (inited)
379                 return start;
380         start = mono_global_codeman_reserve (SZ_THROW);
381         get_throw_exception_generic (start, SZ_THROW, FALSE, TRUE);
382         inited = 1;
383         return start;
384 }
385
386 /*========================= End of Function ========================*/
387
388 /*------------------------------------------------------------------*/
389 /*                                                                  */
390 /* Name         - arch_get_throw_exception_by_name                  */
391 /*                                                                  */
392 /* Function     - Return a function pointer which can be used to    */
393 /*                raise corlib exceptions. The return function has  */
394 /*                the following signature:                          */
395 /*                void (*func) (char *exc_name);                    */
396 /*                                                                  */
397 /*------------------------------------------------------------------*/
398
399 gpointer 
400 mono_arch_get_throw_exception_by_name (void)
401 {
402         static guint8 *start;
403         static int inited = 0;
404
405         if (inited)
406                 return start;
407         start = mono_global_codeman_reserve (SZ_THROW);
408         get_throw_exception_generic (start, SZ_THROW, TRUE, FALSE);
409         inited = 1;
410         return start;
411 }       
412
413 /*========================= End of Function ========================*/
414
415 /*------------------------------------------------------------------*/
416 /*                                                                  */
417 /* Name         - mono_arch_find_jit_info                           */
418 /*                                                                  */
419 /* Function     - See exceptions-amd64.c for docs.                      */
420 /*                                                                  */
421 /*------------------------------------------------------------------*/
422
423 gboolean
424 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, 
425                                                          MonoJitInfo *ji, MonoContext *ctx, 
426                                                          MonoContext *new_ctx, MonoLMF **lmf,
427                                                          mgreg_t **save_locations,
428                                                          StackFrameInfo *frame)
429 {
430         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
431         MonoS390StackFrame *sframe;
432
433         memset (frame, 0, sizeof (StackFrameInfo));
434         frame->ji = ji;
435
436         *new_ctx = *ctx;
437
438         if (ji != NULL) {
439                 gint32 address;
440
441                 frame->type = FRAME_TYPE_MANAGED;
442
443                 if (*lmf && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->ebp)) {
444                         /* remove any unused lmf */
445                         *lmf = (*lmf)->previous_lmf;
446                 }
447
448                 address = (char *)ip - (char *)ji->code_start;
449
450                 sframe = (MonoS390StackFrame *) MONO_CONTEXT_GET_SP (ctx);
451                 MONO_CONTEXT_SET_BP (new_ctx, sframe->prev);
452                 sframe = (MonoS390StackFrame *) sframe->prev;
453                 MONO_CONTEXT_SET_IP (new_ctx, (guint8*)sframe->return_address - 2);
454                 memcpy (&new_ctx->uc_mcontext.gregs[6], sframe->regs, (8*sizeof(gint32)));
455                 return TRUE;
456         } else if (*lmf) {
457                 if (!(*lmf)->method)
458                         return FALSE;
459
460                 ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL);
461                 if (!ji) {
462                         // FIXME: This can happen with multiple appdomains (bug #444383)
463                         return FALSE;
464                 }
465
466                 frame->ji = ji;
467                 frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
468
469                 memcpy(new_ctx->uc_mcontext.gregs, (*lmf)->gregs, sizeof((*lmf)->gregs));
470                 memcpy(new_ctx->uc_mcontext.fpregs.fprs, (*lmf)->fregs, sizeof((*lmf)->fregs));
471
472                 MONO_CONTEXT_SET_BP (new_ctx, (*lmf)->ebp);
473                 MONO_CONTEXT_SET_IP (new_ctx, (*lmf)->eip - 2);
474                 *lmf = (*lmf)->previous_lmf;
475
476                 return TRUE;
477         }
478
479         return FALSE;
480 }
481
482 /*========================= End of Function ========================*/
483
484 /*------------------------------------------------------------------*/
485 /*                                                                  */
486 /* Name         - mono_arch_handle_exception                        */
487 /*                                                                  */
488 /* Function     - Handle an exception raised by the JIT code.       */
489 /*                                                                  */
490 /* Parameters   - ctx       - Saved processor state                 */
491 /*                obj       - The exception object                  */
492 /*                test_only - Only test if the exception is caught, */
493 /*                            but don't call handlers               */
494 /*                                                                  */
495 /*------------------------------------------------------------------*/
496
497 gboolean
498 mono_arch_handle_exception (void *uc, gpointer obj, gboolean test_only)
499 {
500         return mono_handle_exception (uc, obj, mono_arch_ip_from_context(uc), test_only);
501 }
502
503 /*========================= End of Function ========================*/
504
505 /*------------------------------------------------------------------*/
506 /*                                                                  */
507 /* Name         - mono_arch_ip_from_context                         */
508 /*                                                                  */
509 /* Function     - Return the instruction pointer from the context.  */
510 /*                                                                  */
511 /* Parameters   - sigctx    - Saved processor state                 */
512 /*                                                                  */
513 /*------------------------------------------------------------------*/
514
515 gpointer
516 mono_arch_ip_from_context (void *sigctx)
517 {
518         return context_get_ip (sigctx);
519 }
520
521
522 /*========================= End of Function ========================*/
523
524 /*------------------------------------------------------------------*/
525 /*                                                                  */
526 /* Name         - mono_arch_get_restore_context                    */
527 /*                                                                  */
528 /* Function     - Return the address of the routine that will rest- */
529 /*                ore the context.                                  */
530 /*                                                                  */
531 /*------------------------------------------------------------------*/
532
533 gpointer
534 mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
535 {
536         g_assert (!aot);
537         if (info)
538                 *info = NULL;
539
540         return setcontext;
541 }
542
543
544 /*========================= End of Function ========================*/
545
546 /*------------------------------------------------------------------*/
547 /*                                                                  */
548 /* Name         - mono_arch_is_int_overflow                         */
549 /*                                                                  */
550 /* Function     - Inspect the code that raised the SIGFPE signal    */
551 /*                to see if the DivideByZero or Arithmetic exception*/
552 /*                should be raised.                                 */
553 /*                                                                  */
554 /*------------------------------------------------------------------*/
555
556 gboolean
557 mono_arch_is_int_overflow (void *uc, void *info)
558 {
559         MonoContext *ctx;
560         guint8      *code;
561         guint32     *operand;
562         gboolean    arithExc = TRUE;
563         gint        regNo,
564                     offset;
565
566         ctx  = (MonoContext *) uc;
567         code =  (guint8 *) ((siginfo_t *)info)->si_addr;
568         /*----------------------------------------------------------*/
569         /* Divide operations are the only ones that will give the   */
570         /* divide by zero exception so just check for these ops.    */
571         /*----------------------------------------------------------*/
572         switch (code[0]) {
573                 case 0x1d :             /* Divide Register          */
574                         regNo = code[1] & 0x0f; 
575                         if (ctx->uc_mcontext.gregs[regNo] == 0)
576                                 arithExc = FALSE;
577                 break;
578                 case 0x5d :             /* Divide                   */
579                         regNo   = (code[2] & 0xf0 >> 8);        
580                         offset  = *((guint16 *) code+2) & 0x0fff;
581                         operand = (guint32*)(ctx->uc_mcontext.gregs[regNo] + offset);
582                         if (*operand == 0)
583                                 arithExc = FALSE; 
584                 break;
585                 case 0xb9 :             /* Divide logical Register? */
586                         if (code[1] == 0x97) {
587                                 regNo = (code[2] & 0xf0 >> 8);  
588                                 if (ctx->uc_mcontext.gregs[regNo] == 0)
589                                         arithExc = FALSE;
590                         }
591                 break;
592                 case 0xe3 :             /* Divide logical?          */
593                         if (code[1] == 0x97) {  
594                                 regNo   = (code[2] & 0xf0 >> 8);        
595                                 offset  = *((guint32 *) code+1) & 0x000fffff;
596                                 operand = (guint32*)(ctx->uc_mcontext.gregs[regNo] + offset);
597                                 if (*operand == 0)
598                                         arithExc = FALSE; 
599                         }
600                 break;
601                 default:
602                         arithExc = TRUE;
603         }
604         ctx->uc_mcontext.psw.addr = (guint32)code;
605         return (arithExc);
606 }
607
608 /*========================= End of Function ========================*/