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