Mon May 10 17:21:00 CEST 2004 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / mini / exceptions-sparc.c
1 /*
2  * exceptions-sparc.c: exception support for 64 bit sparc
3  *
4  * Authors:
5  *   Mark Crichton (crichton@gimp.org)
6  *   Dietmar Maurer (dietmar@ximian.com)
7  *
8  * (C) 2003 Ximian, Inc.
9  */
10
11 #include <config.h>
12 #include <glib.h>
13 #include <signal.h>
14 #include <string.h>
15 #include <sys/ucontext.h>
16
17 #include <mono/arch/sparc/sparc-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 #include <mono/metadata/gc-internal.h>
25
26 #include "mini.h"
27 #include "mini-sparc.h"
28
29 /*
30  * mono_arch_get_restore_context:
31  *
32  * Returns a pointer to a method which restores a previously saved sigcontext.
33  */
34 gpointer
35 mono_arch_get_restore_context (void)
36 {
37         static guint32 start [32];
38         static int inited = 0;
39         guint32 *code;
40
41         if (inited)
42                 return start;
43
44         code = start;
45
46         sparc_ld_imm (code, sparc_o0, G_STRUCT_OFFSET (MonoContext, ip), sparc_i7);
47         sparc_ld_imm (code, sparc_o0, G_STRUCT_OFFSET (MonoContext, sp), sparc_i6);
48
49         sparc_jmpl_imm (code, sparc_i7, 0, sparc_g0);
50         /* FIXME: This does not return to the correct window */
51         sparc_restore_imm (code, sparc_g0, 0, sparc_g0);
52
53         g_assert ((code - start) < 32);
54
55         inited = 1;
56
57         return start;
58 }
59
60 /*
61  * mono_arch_get_call_filter:
62  *
63  * Returns a pointer to a method which calls an exception filter. We
64  * also use this function to call finally handlers (we pass NULL as 
65  * @exc object in this case).
66  *
67  * call_filter (MonoContext *ctx, gpointer ip)
68  */
69 gpointer
70 mono_arch_get_call_filter (void)
71 {
72         static guint32 start [64];
73         static int inited = 0;
74         guint32 *code;
75         int i;
76
77         if (inited)
78                 return start;
79
80         code = start;
81
82         /*
83          * There are two frames here:
84          * - the first frame is used by call_filter
85          * - the second frame is used to run the filter code
86          */
87
88         /* Create first frame */
89         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
90
91         sparc_mov_reg_reg (code, sparc_i1, sparc_o0);
92         sparc_ld_imm (code, sparc_i0, G_STRUCT_OFFSET (MonoContext, sp), sparc_o1);
93
94         /* Create second frame */
95         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
96
97         sparc_mov_reg_reg (code, sparc_i0, sparc_o0);
98         sparc_mov_reg_reg (code, sparc_i1, sparc_o1);
99
100         /*
101          * We need to change %fp to point to the stack frame of the method
102          * containing the filter. But changing %fp also changes the %sp of
103          * the parent frame (the first frame), so if the OS saves the first frame,
104          * it saves it to the stack frame of the method, which is not good.
105          * So flush all register windows to memory before changing %fp.
106          */
107         sparc_flushw (code);
108
109         sparc_mov_reg_reg (code, sparc_fp, sparc_o7);
110
111         /* 
112          * Modify the second frame so it is identical to the one used in the
113          * method containing the filter.
114          */
115         for (i = 0; i < 16; ++i)
116                 sparc_ld_imm (code, sparc_o1, i * 4, sparc_l0 + i);
117
118         /* Save %fp to a location reserved in mono_arch_allocate_vars */
119         sparc_st_imm (code, sparc_o7, sparc_fp, -4);
120
121         /* Call the filter code, after this returns, %o0 will hold the result */
122         sparc_call_imm (code, sparc_o0, 0);
123         sparc_nop (code);
124
125         /* Restore original %fp */
126         sparc_ld_imm (code, sparc_fp, -4, sparc_fp);
127
128         sparc_mov_reg_reg (code, sparc_o0, sparc_i0);
129
130         /* Return to first frame */
131         sparc_restore (code, sparc_g0, sparc_g0, sparc_g0);
132
133         /* FIXME: Save locals to the stack */
134
135         /* Return to caller */
136         sparc_ret (code);
137         /* Return result in delay slot */
138         sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
139
140         g_assert ((code - start) < 64);
141
142         inited = 1;
143
144         return start;
145 }
146
147 static void
148 throw_exception (MonoObject *ex, guint32 sp, guint32 ip)
149 {
150         MonoContext ctx;
151         static void (*restore_context) (MonoContext *);
152
153         if (!restore_context)
154                 restore_context = mono_arch_get_restore_context ();
155
156         ctx.sp = (guint32*)sp;
157         ctx.ip = ip;
158         ctx.fp = (guint32*)ctx.sp [sparc_i6 - 16];
159
160         mono_handle_exception (&ctx, ex, FALSE);
161         restore_context (&ctx);
162
163         g_assert_not_reached ();
164 }
165
166 /**
167  * mono_arch_get_throw_exception_by_name:
168  *
169  * Returns a function pointer which can be used to raise exceptions.
170  * The returned function has the following 
171  * signature: void (*func) (char *exc_name); 
172  */
173 gpointer 
174 mono_arch_get_throw_exception (void)
175 {
176         static guint32 start [32];
177         static int inited = 0;
178         guint32 *code;
179
180         if (inited)
181                 return start;
182
183         inited = 1;
184         code = start;
185
186         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
187
188         sparc_flushw (code);
189         sparc_mov_reg_reg (code, sparc_i0, sparc_o0);
190         sparc_mov_reg_reg (code, sparc_fp, sparc_o1);
191         sparc_mov_reg_reg (code, sparc_i7, sparc_o2);
192         sparc_set (code, throw_exception, sparc_o7);
193         sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
194         sparc_nop (code);
195
196         g_assert ((code - start) < 32);
197
198         return start;
199 }
200
201 /**
202  * mono_arch_get_throw_exception_by_name:
203  *
204  * Returns a function pointer which can be used to raise 
205  * corlib exceptions. The returned function has the following 
206  * signature: void (*func) (char *exc_name, gpointer ip); 
207  */
208 gpointer 
209 mono_arch_get_throw_exception_by_name (void)
210 {
211         static guint32 start [32];
212         static int inited = 0;
213         guint32 *code;
214
215         if (inited)
216                 return start;
217
218         inited = 1;
219         code = start;
220
221         sparc_save_imm (code, sparc_sp, -160, sparc_sp);
222
223         sparc_mov_reg_reg (code, sparc_i0, sparc_o2);
224         sparc_set (code, mono_defaults.corlib, sparc_o0);
225         sparc_set (code, "System", sparc_o1);
226         sparc_set (code, mono_exception_from_name, sparc_o7);
227         sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite);
228         sparc_nop (code);
229
230         /* Return to the caller, so exception handling does not see this frame */
231         sparc_restore (code, sparc_o0, sparc_g0, sparc_o0);
232
233         /* Put original return address into %o7 */
234         sparc_mov_reg_reg (code, sparc_o1, sparc_o7);
235         sparc_set (code, mono_arch_get_throw_exception (), sparc_g1);
236         /* Use a jmp instead of a call so o7 is preserved */
237         sparc_jmpl_imm (code, sparc_g1, 0, sparc_g0);
238         sparc_nop (code);
239
240         g_assert ((code - start) < 32);
241
242         return start;
243 }       
244
245 /* mono_arch_find_jit_info:
246  *
247  * This function is used to gather information from @ctx. It return the 
248  * MonoJitInfo of the corresponding function, unwinds one stack frame and
249  * stores the resulting context into @new_ctx. It also stores a string 
250  * describing the stack location into @trace (if not NULL), and modifies
251  * the @lmf if necessary. @native_offset return the IP offset from the 
252  * start of the function or -1 if that info is not available.
253  */
254 MonoJitInfo *
255 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
256                          MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset,
257                          gboolean *managed)
258 {
259         MonoJitInfo *ji;
260         gpointer ip = MONO_CONTEXT_GET_IP (ctx);
261         guint32 *window;
262
263         /* Avoid costly table lookup during stack overflow */
264         if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
265                 ji = prev_ji;
266         else
267                 ji = mono_jit_info_table_find (domain, ip);
268
269         if (trace)
270                 *trace = NULL;
271
272         if (native_offset)
273                 *native_offset = -1;
274
275         if (managed)
276                 *managed = FALSE;
277
278         if (ji != NULL) {
279                 char *source_location, *tmpaddr, *fname;
280                 gint32 address, iloffset;
281
282                 *new_ctx = *ctx;
283
284                 address = (char *)ip - (char *)ji->code_start;
285
286                 if (native_offset)
287                         *native_offset = address;
288
289                 if (managed)
290                         if (!ji->method->wrapper_type)
291                                 *managed = TRUE;
292
293                 if (trace) {
294                         source_location = mono_debug_source_location_from_address (ji->method, address, NULL, domain);
295                         iloffset = mono_debug_il_offset_from_address (ji->method, address, domain);
296
297                         if (iloffset < 0)
298                                 tmpaddr = g_strdup_printf ("<0x%05x>", address);
299                         else
300                                 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
301                 
302                         fname = mono_method_full_name (ji->method, TRUE);
303
304                         if (source_location)
305                                 *trace = g_strdup_printf ("in %s (at %s) %s", tmpaddr, source_location, fname);
306                         else
307                                 *trace = g_strdup_printf ("in %s %s", tmpaddr, fname);
308
309                         g_free (fname);
310                         g_free (source_location);
311                         g_free (tmpaddr);
312                 }
313
314                 if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
315                         /* remove any unused lmf */
316                         *lmf = (*lmf)->previous_lmf;
317                 }
318
319                 /* Restore ip and sp from the saved register window */
320                 window = (guint32*)ctx->sp;
321                 new_ctx->ip = window [sparc_i7 - 16];
322                 new_ctx->sp = (guint32*)(window [sparc_i6 - 16]);
323                 new_ctx->fp = (guint32*)(new_ctx->sp [sparc_i6 - 16]);
324
325                 *res = *ji;
326                 return res;
327         }
328         else {
329                 if (!(*lmf))
330                         return NULL;
331
332                 *new_ctx = *ctx;
333
334                 if (!(*lmf)->method)
335                         return (gpointer)-1;
336
337                 if (trace)
338                         *trace = g_strdup_printf ("in (unmanaged) %s", mono_method_full_name ((*lmf)->method, TRUE));
339                 
340                 if ((ji = mono_jit_info_table_find (domain, (gpointer)(*lmf)->ip))) {
341                         *res = *ji;
342                 } else {
343                         memset (res, 0, sizeof (MonoJitInfo));
344                         res->method = (*lmf)->method;
345                 }
346
347                 new_ctx->ip = (*lmf)->ip;
348                 new_ctx->sp = (*lmf)->sp;
349                 new_ctx->fp = (*lmf)->ebp;
350
351                 *lmf = (*lmf)->previous_lmf;
352
353                 return res;
354         }
355 }
356
357 gboolean
358 mono_arch_has_unwind_info (gconstpointer addr)
359 {
360         return FALSE;
361 }
362
363 gboolean
364 mono_arch_handle_exception (void *sigctx, gpointer obj, gboolean test_only)
365 {
366         MonoContext mctx;
367         ucontext_t *ctx = (ucontext_t*)sigctx;
368
369         /*
370          * Access to the machine state using the ucontext_t parameter is somewhat
371          * under documented under solaris. The code below seems to work under
372          * Solaris 9.
373          */
374         g_assert (!ctx->uc_mcontext.gwins);
375
376         mctx.ip = ctx->uc_mcontext.gregs [REG_PC];
377         mctx.sp = ctx->uc_mcontext.gregs [REG_SP];
378         mctx.fp = mctx.sp [sparc_fp - 16];
379
380         mono_handle_exception (&mctx, obj, test_only);
381         
382         /* We can't use restore_context to return from a signal handler */
383         ctx->uc_mcontext.gregs [REG_PC] = mctx.ip;
384         ctx->uc_mcontext.gregs [REG_nPC] = mctx.ip + 4;
385         ctx->uc_mcontext.gregs [REG_SP] = mctx.sp;
386         mctx.sp [sparc_fp - 16] = mctx.fp;
387
388         return TRUE;
389 }