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