2002-05-14 Dietmar Maurer <dietmar@ximian.com>
[mono.git] / mono / jit / exception.c
1 /*
2  * exception.c: exception support
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/arch/x86/x86-codegen.h>
14 #include <mono/metadata/appdomain.h>
15 #include <mono/metadata/tabledefs.h>
16 #include <mono/metadata/threads.h>
17 #include <mono/metadata/debug-helpers.h>
18
19 #include "jit.h"
20 #include "codegen.h"
21
22 #ifdef __FreeBSD__
23 # define SC_EAX sc_eax
24 # define SC_EBX sc_ebx
25 # define SC_ECX sc_ecx
26 # define SC_EDX sc_edx
27 # define SC_EBP sc_ebp
28 # define SC_EIP sc_eip
29 # define SC_ESP sc_esp
30 # define SC_EDI sc_edi
31 # define SC_ESI sc_esi
32 #else
33 # define SC_EAX eax
34 # define SC_EBX ebx
35 # define SC_ECX ecx
36 # define SC_EDX edx
37 # define SC_EBP ebp
38 # define SC_EIP eip
39 # define SC_ESP esp
40 # define SC_EDI edi
41 # define SC_ESI esi
42 #endif
43
44 /*
45  * arch_get_restore_context:
46  *
47  * Returns a pointer to a method which restores a previously saved sigcontext.
48  */
49 static gpointer
50 arch_get_restore_context (void)
51 {
52         static guint8 *start = NULL;
53         guint8 *code;
54
55         if (start)
56                 return start;
57
58         /* restore_contect (struct sigcontext *ctx) */
59         /* we do not restore X86_EAX, X86_EDX */
60
61         start = code = g_malloc (1024);
62         
63         /* load ctx */
64         x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
65
66         /* get return address, stored in EDX */
67         x86_mov_reg_membase (code, X86_EDX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
68         /* restore EBX */
69         x86_mov_reg_membase (code, X86_EBX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
70         /* restore EDI */
71         x86_mov_reg_membase (code, X86_EDI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
72         /* restore ESI */
73         x86_mov_reg_membase (code, X86_ESI, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
74         /* restore ESP */
75         x86_mov_reg_membase (code, X86_ESP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
76         /* restore EBP */
77         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
78         /* restore ECX. the exception object is passed here to the catch handler */
79         x86_mov_reg_membase (code, X86_ECX, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_ECX), 4);
80
81         /* jump to the saved IP */
82         x86_jump_reg (code, X86_EDX);
83
84         return start;
85 }
86
87 /*
88  * arch_get_call_finally:
89  *
90  * Returns a pointer to a method which calls a finally handler.
91  */
92 static gpointer
93 arch_get_call_finally (void)
94 {
95         static guint8 start [28];
96         static int inited = 0;
97         guint8 *code;
98
99         if (inited)
100                 return start;
101
102         inited = 1;
103         /* call_finally (struct sigcontext *ctx, unsigned long eip) */
104         code = start;
105
106         x86_push_reg (code, X86_EBP);
107         x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
108         x86_push_reg (code, X86_EBX);
109         x86_push_reg (code, X86_EDI);
110         x86_push_reg (code, X86_ESI);
111
112         /* load ctx */
113         x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
114         /* load eip */
115         x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
116         /* save EBP */
117         x86_push_reg (code, X86_EBP);
118         /* set new EBP */
119         x86_mov_reg_membase (code, X86_EBP, X86_EAX,  G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
120         /* call the handler */
121         x86_call_reg (code, X86_ECX);
122         /* restore EBP */
123         x86_pop_reg (code, X86_EBP);
124         /* restore saved regs */
125         x86_pop_reg (code, X86_ESI);
126         x86_pop_reg (code, X86_EDI);
127         x86_pop_reg (code, X86_EBX);
128         x86_leave (code);
129         x86_ret (code);
130
131         g_assert ((code - start) < 28);
132         return start;
133 }
134
135
136 /**
137  * arch_handle_exception:
138  * @ctx: saved processor state
139  * @obj:
140  */
141 void
142 arch_handle_exception (struct sigcontext *ctx, gpointer obj)
143 {
144         MonoDomain *domain = mono_domain_get ();
145         MonoJitInfo *ji;
146         gpointer ip = (gpointer)ctx->SC_EIP;
147         static void (*restore_context) (struct sigcontext *);
148         static void (*call_finally) (struct sigcontext *, unsigned long);
149         void (*cleanup) (MonoObject *exc);
150         MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
151         gpointer end_of_stack;
152
153         g_assert (ctx != NULL);
154         g_assert (obj != NULL);
155
156         ji = mono_jit_info_table_find (domain, ip);
157         
158         end_of_stack = jit_tls->end_of_stack;
159         g_assert (end_of_stack);
160
161         if (!restore_context)
162                 restore_context = arch_get_restore_context ();
163         
164         if (!call_finally)
165                 call_finally = arch_get_call_finally ();
166
167         cleanup = jit_tls->abort_func;
168
169         if (ji) { /* we are inside managed code */
170                 MonoMethod *m = ji->method;
171                 int offset = 2;
172
173                 if (ji->num_clauses) {
174                         int i;
175
176                         g_assert (ji->clauses);
177                         
178                         for (i = 0; i < ji->num_clauses; i++) {
179                                 MonoJitExceptionInfo *ei = &ji->clauses [i];
180
181                                 if (ei->try_start <= ip && ip <= (ei->try_end)) { 
182                                         /* catch block */
183                                         if (ei->flags == 0 && mono_object_isinst (obj, 
184                                                 mono_class_get (m->klass->image, ei->token_or_filter))) {
185                                         
186                                                 ctx->SC_EIP = (unsigned long)ei->handler_start;
187                                                 ctx->SC_ECX = (unsigned long)obj;
188                                                 restore_context (ctx);
189                                                 g_assert_not_reached ();
190                                         }
191                                 }
192                         }
193
194                         /* no handler found - we need to call all finally handlers */
195                         for (i = 0; i < ji->num_clauses; i++) {
196                                 MonoJitExceptionInfo *ei = &ji->clauses [i];
197
198                                 if (ei->try_start <= ip && ip < (ei->try_end) &&
199                                     (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
200                                         call_finally (ctx, (unsigned long)ei->handler_start);
201                                 }
202                         }
203                 }
204
205                 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
206                         char  *strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
207                         char  *tmp, *tmpsig;
208
209                         if (!strcmp (strace, "TODO: implement stack traces")){
210                                 g_free (strace);
211                                 strace = g_strdup ("");
212                         }
213
214                         tmpsig = mono_signature_get_desc(m->signature, TRUE);
215                         tmp = g_strdup_printf ("%sin %04x %s.%s:%s (%s)\n", strace, 
216                                                (char *)ip - (char *)ji->code_start,
217                                                m->klass->name_space, m->klass->name, m->name, tmpsig);
218                         g_free (tmpsig);
219                         g_free (strace);
220
221                         ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
222                         g_free (tmp);
223                 }
224
225                 /* continue unwinding */
226
227                 /* restore caller saved registers */
228                 if (ji->used_regs & X86_ESI_MASK) {
229                         ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
230                         offset++;
231                 }
232                 if (ji->used_regs & X86_EDI_MASK) {
233                         ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
234                         offset++;
235                 }
236                 if (ji->used_regs & X86_EBX_MASK) {
237                         ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
238                 }
239
240                 ctx->SC_ESP = ctx->SC_EBP;
241                 ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 5;
242                 ctx->SC_EBP = *((int *)ctx->SC_EBP);
243                 
244                 if (ctx->SC_EBP < (unsigned)end_of_stack)
245                         arch_handle_exception (ctx, obj);
246                 else {
247                         g_assert (cleanup);
248                         cleanup (obj);
249                 }
250         
251         } else {
252                 MonoLMF *lmf = jit_tls->lmf;
253                 MonoMethod *m;
254
255                 if (!lmf) {
256                         g_assert (cleanup);
257                         cleanup (obj);
258                 }
259                 m = lmf->method;
260
261                 jit_tls->lmf = lmf->previous_lmf;
262
263                 ctx->SC_ESI = lmf->esi;
264                 ctx->SC_EDI = lmf->edi;
265                 ctx->SC_EBX = lmf->ebx;
266                 ctx->SC_EBP = lmf->ebp;
267                 ctx->SC_EIP = lmf->eip;
268                 ctx->SC_ESP = (unsigned long)&lmf->eip;
269
270                 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
271                         char  *strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
272                         char  *tmp;
273
274                         if (!strcmp (strace, "TODO: implement stack traces"))
275                                 strace = g_strdup ("");
276
277                         tmp = g_strdup_printf ("%sin (unmanaged) %s.%s:%s ()\n", strace, m->klass->name_space,  
278                                                m->klass->name, m->name);
279
280                         g_free (strace);
281
282                         ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
283                         g_free (tmp);
284                 }
285
286                 if (ctx->SC_EBP < (unsigned)end_of_stack)
287                         arch_handle_exception (ctx, obj);
288                 else {
289                         g_assert (cleanup);
290                         cleanup (obj);
291                 }
292         }
293
294         g_assert_not_reached ();
295 }
296
297 static void
298 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
299                  unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
300                  unsigned long eip,  unsigned long esp)
301 {
302         struct sigcontext ctx;
303
304         /* adjust eip so that it point to the call instruction */
305         eip -= 5;
306
307         ctx.SC_ESP = esp;
308         ctx.SC_EIP = eip;
309         ctx.SC_EBP = ebp;
310         ctx.SC_EDI = edi;
311         ctx.SC_ESI = esi;
312         ctx.SC_EBX = ebx;
313         ctx.SC_EDX = edx;
314         ctx.SC_ECX = ecx;
315         ctx.SC_EAX = eax;
316         
317         arch_handle_exception (&ctx, exc);
318
319         g_assert_not_reached ();
320 }
321
322 /**
323  * arch_get_throw_exception:
324  *
325  * Returns a function pointer which can be used to raise 
326  * exceptions. The returned function has the following 
327  * signature: void (*func) (MonoException *exc); 
328  * For example to raise an arithmetic exception you can use:
329  *
330  * x86_push_imm (code, mono_get_exception_arithmetic ()); 
331  * x86_call_code (code, arch_get_throw_exception ()); 
332  *
333  */
334 gpointer 
335 arch_get_throw_exception (void)
336 {
337         static guint8 start [24];
338         static int inited = 0;
339         guint8 *code;
340
341         if (inited)
342                 return start;
343
344         inited = 1;
345         code = start;
346
347         x86_push_reg (code, X86_ESP);
348         x86_push_membase (code, X86_ESP, 4); /* IP */
349         x86_push_membase (code, X86_ESP, 12); /* exception */
350         x86_push_reg (code, X86_EBP);
351         x86_push_reg (code, X86_EDI);
352         x86_push_reg (code, X86_ESI);
353         x86_push_reg (code, X86_EBX);
354         x86_push_reg (code, X86_EDX);
355         x86_push_reg (code, X86_ECX);
356         x86_push_reg (code, X86_EAX);
357         x86_call_code (code, throw_exception);
358         /* we should never reach this breakpoint */
359         x86_breakpoint (code);
360
361         g_assert ((code - start) < 24);
362         return start;
363 }
364
365 /**
366  * arch_get_throw_exception_by_name:
367  *
368  * Returns a function pointer which can be used to raise 
369  * corlib exceptions. The returned function has the following 
370  * signature: void (*func) (char *exc_name); 
371  * For example to raise an arithmetic exception you can use:
372  *
373  * x86_push_imm (code, "ArithmeticException"); 
374  * x86_call_code (code, arch_get_throw_exception ()); 
375  *
376  */
377 gpointer 
378 arch_get_throw_exception_by_name ()
379 {
380         static guint8 start [32];
381         static int inited = 0;
382         guint8 *code;
383
384         if (inited)
385                 return start;
386
387         inited = 1;
388         code = start;
389
390         /* fixme: we do not save EAX, EDX, ECD - unsure if we need that */
391
392         x86_push_membase (code, X86_ESP, 4); /* exception name */
393         x86_push_imm (code, "System");
394         x86_push_imm (code, mono_defaults.exception_class->image);
395         x86_call_code (code, mono_exception_from_name);
396         x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
397         /* save the newly create object (overwrite exception name)*/
398         x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
399         x86_jump_code (code, arch_get_throw_exception ());
400
401         g_assert ((code - start) < 32);
402
403         return start;
404 }