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