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