2 * exception.c: exception support
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2001 Ximian, Inc.
14 #include <mono/arch/x86/x86-codegen.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/debug-helpers.h>
25 # define SC_EAX sc_eax
26 # define SC_EBX sc_ebx
27 # define SC_ECX sc_ecx
28 # define SC_EDX sc_edx
29 # define SC_EBP sc_ebp
30 # define SC_EIP sc_eip
31 # define SC_ESP sc_esp
32 # define SC_EDI sc_edi
33 # define SC_ESI sc_esi
47 * arch_get_restore_context:
49 * Returns a pointer to a method which restores a previously saved sigcontext.
52 arch_get_restore_context (void)
54 static guint8 *start = NULL;
60 /* restore_contect (struct sigcontext *ctx) */
61 /* we do not restore X86_EAX, X86_EDX */
63 start = code = g_malloc (1024);
66 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
68 /* get return address, stored in EDX */
69 x86_mov_reg_membase (code, X86_EDX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EIP), 4);
71 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
73 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
75 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
77 x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
79 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
80 /* restore ECX. the exception object is passed here to the catch handler */
81 x86_mov_reg_membase (code, X86_ECX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ECX), 4);
83 /* jump to the saved IP */
84 x86_jump_reg (code, X86_EDX);
90 * arch_get_call_filter:
92 * Returns a pointer to a method which calls an exception filter.
95 arch_get_call_filter (void)
97 static guint8 start [64];
98 static int inited = 0;
105 /* call_finally (struct sigcontext *ctx, unsigned long eip, gpointer exc) */
108 x86_push_reg (code, X86_EBP);
109 x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
110 x86_push_reg (code, X86_EBX);
111 x86_push_reg (code, X86_EDI);
112 x86_push_reg (code, X86_ESI);
115 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
117 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
119 x86_push_reg (code, X86_EBP);
121 x86_push_membase (code, X86_EBP, 16);
123 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
124 /* restore registers used by global register allocation (EBX & ESI) */
125 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
126 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
127 /* save the ESP - this is used by endfinally */
128 x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4);
129 /* call the handler */
130 x86_call_reg (code, X86_ECX);
131 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
133 x86_pop_reg (code, X86_EBP);
134 /* restore saved regs */
135 x86_pop_reg (code, X86_ESI);
136 x86_pop_reg (code, X86_EDI);
137 x86_pop_reg (code, X86_EBX);
141 g_assert ((code - start) < 64);
146 * arch_get_call_finally:
148 * Returns a pointer to a method which calls a finally handler.
151 arch_get_call_finally (void)
153 static guint8 start [64];
154 static int inited = 0;
161 /* call_finally (struct sigcontext *ctx, unsigned long eip) */
164 x86_push_reg (code, X86_EBP);
165 x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
166 x86_push_reg (code, X86_EBX);
167 x86_push_reg (code, X86_EDI);
168 x86_push_reg (code, X86_ESI);
171 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
173 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
175 x86_push_reg (code, X86_EBP);
177 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
178 /* restore registers used by global register allocation (EBX & ESI) */
179 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
180 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
181 /* save the ESP - this is used by endfinally */
182 x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4);
183 /* call the handler */
184 x86_call_reg (code, X86_ECX);
186 x86_pop_reg (code, X86_EBP);
187 /* restore saved regs */
188 x86_pop_reg (code, X86_ESI);
189 x86_pop_reg (code, X86_EDI);
190 x86_pop_reg (code, X86_EBX);
194 g_assert ((code - start) < 64);
199 glist_to_array (GList *list)
201 MonoDomain *domain = mono_domain_get ();
208 len = g_list_length (list);
209 res = mono_array_new (domain, mono_defaults.int_class, len);
211 for (i = 0; list; list = list->next, i++)
212 mono_array_set (res, gpointer, i, list->data);
218 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
220 MonoDomain *domain = mono_domain_get ();
222 MonoArray *ta = exc->trace_ips;
225 len = mono_array_length (ta);
227 res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
229 for (i = skip; i < len; i++) {
231 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
232 gpointer ip = mono_array_get (ta, gpointer, i);
234 ji = mono_jit_info_table_find (domain, ip);
235 g_assert (ji != NULL);
237 sf->method = mono_method_get_object (domain, ji->method, NULL);
238 sf->native_offset = (char *)ip - (char *)ji->code_start;
239 sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset);
241 if (need_file_info) {
244 filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line);
246 sf->filename = mono_string_new (domain, filename ? filename : "<unknown>");
252 mono_array_set (res, gpointer, i, sf);
259 mono_jit_walk_stack (MonoStackWalk func, gpointer user_data) {
260 MonoDomain *domain = mono_domain_get ();
261 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
262 MonoLMF *lmf = jit_tls->lmf;
267 gint native_offset, il_offset;
269 bp = (gpointer *)&func;
273 while ((unsigned)bp < (unsigned)jit_tls->end_of_stack) {
274 if ((ji = mono_jit_info_table_find (domain, ip))) {
276 native_offset = (char *)ip - (char *)ji->code_start;
277 ip = (gpointer)((char *)bp [1] - 5);
279 il_offset = mono_debug_il_offset_from_address (m, native_offset);
285 bp = (gpointer)lmf->ebp;
286 ip = (gpointer)lmf->eip;
287 lmf = lmf->previous_lmf;
289 native_offset = il_offset = -1;
291 if (func (m, native_offset, il_offset, user_data))
297 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
298 MonoReflectionMethod **method,
299 gint32 *iloffset, gint32 *native_offset,
300 MonoString **file, gint32 *line, gint32 *column)
302 MonoDomain *domain = mono_domain_get ();
303 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
304 MonoLMF *lmf = jit_tls->lmf;
305 gpointer *sf = (gpointer *)&skip;
306 gpointer ip = sf [-1];
308 gpointer *bp = sf [-2];
309 MonoMethod *m = NULL;
313 addr = -1; /* unknown */
315 if ((ji = mono_jit_info_table_find (domain, ip))) {
317 addr = (char *)ip - (char *)ji->code_start;
318 ip = (gpointer)((char *)bp [1] - 5);
326 bp = (gpointer)lmf->ebp;
327 ip = (gpointer)lmf->eip;
329 lmf = lmf->previous_lmf;
332 if ((unsigned)bp >= (unsigned)jit_tls->end_of_stack)
335 } while (skip-- > 0);
339 *method = mono_method_get_object (domain, m, NULL);
340 *iloffset = mono_debug_il_offset_from_address (m, addr);
341 *native_offset = addr;
343 if (need_file_info) {
346 filename = mono_debug_source_location_from_address (m, addr, line);
348 *file = mono_string_new (domain, filename ? filename : "<unknown>");
358 * arch_handle_exception:
359 * @ctx: saved processor state
363 arch_handle_exception (struct sigcontext *ctx, gpointer obj, gboolean test_only)
365 MonoDomain *domain = mono_domain_get ();
367 static void (*restore_context) (struct sigcontext *);
368 static void (*call_finally) (struct sigcontext *, unsigned long);
369 static int (*call_filter) (struct sigcontext *, gpointer, gpointer);
370 void (*cleanup) (MonoObject *exc);
371 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
372 gpointer end_of_stack;
373 struct sigcontext ctx_cp;
374 MonoLMF *lmf = jit_tls->lmf;
375 GList *trace_ips = NULL;
378 g_assert (ctx != NULL);
380 MonoException *ex = mono_get_exception_null_reference ();
381 ex->message = mono_string_new (domain,
382 "Object reference not set to an instance of an object");
383 obj = (MonoObject *)ex;
386 g_assert (mono_object_isinst (obj, mono_defaults.exception_class));
388 ((MonoException *)obj)->stack_trace = NULL;
390 if (!restore_context)
391 restore_context = arch_get_restore_context ();
394 call_finally = arch_get_call_finally ();
397 call_filter = arch_get_call_filter ();
399 end_of_stack = jit_tls->end_of_stack;
400 g_assert (end_of_stack);
402 cleanup = jit_tls->abort_func;
407 if (!arch_handle_exception (&ctx_cp, obj, TRUE)) {
408 if (mono_break_on_exc) {
409 if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
410 mono_debug_make_symbols ();
413 mono_unhandled_exception (obj);
419 ji = mono_jit_info_table_find (domain, (gpointer)ctx->SC_EIP);
421 /* we are inside managed code if ji != NULL */
426 if (m == mono_start_method) {
430 g_assert_not_reached ();
432 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
433 g_list_free (trace_ips);
440 char *tmp, *source_location, *tmpaddr, *fname;
441 gint32 address, iloffset;
443 trace_ips = g_list_append (trace_ips, (gpointer)ctx->SC_EIP);
445 if (!((MonoException*)obj)->stack_trace)
446 strace = g_strdup ("");
448 strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
450 address = (char *)ctx->SC_EIP - (char *)ji->code_start;
452 source_location = mono_debug_source_location_from_address (m, address, NULL);
453 iloffset = mono_debug_il_offset_from_address (m, address);
456 tmpaddr = g_strdup_printf ("<0x%05x>", address);
458 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
460 fname = mono_method_full_name (m, TRUE);
463 tmp = g_strdup_printf ("%sin %s (at %s) %s\n", strace, tmpaddr,
464 source_location, fname);
466 tmp = g_strdup_printf ("%sin %s %s\n", strace, tmpaddr,
469 g_free (source_location);
473 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
478 if (ji->num_clauses) {
481 g_assert (ji->clauses);
483 for (i = 0; i < ji->num_clauses; i++) {
484 MonoJitExceptionInfo *ei = &ji->clauses [i];
486 if (ei->try_start <= (gpointer)ctx->SC_EIP &&
487 (gpointer)ctx->SC_EIP <= ei->try_end) {
489 if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
490 mono_object_isinst (obj, mono_class_get (m->klass->image, ei->data.token))) ||
491 ((ei->flags == MONO_EXCEPTION_CLAUSE_FILTER &&
492 call_filter (ctx, ei->data.filter, obj)))) {
494 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
495 g_list_free (trace_ips);
498 ctx->SC_EIP = (unsigned long)ei->handler_start;
499 ctx->SC_ECX = (unsigned long)obj;
504 restore_context (ctx);
505 g_assert_not_reached ();
511 /* no handler found - we need to call all finally handlers */
513 for (i = 0; i < ji->num_clauses; i++) {
514 MonoJitExceptionInfo *ei = &ji->clauses [i];
516 if (ei->try_start <= (gpointer)ctx->SC_EIP &&
517 (gpointer)ctx->SC_EIP < ei->try_end &&
518 (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
519 call_finally (ctx, (unsigned long)ei->handler_start);
525 /* continue unwinding */
528 /* restore caller saved registers */
529 if (ji->used_regs & X86_EBX_MASK) {
530 ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
533 if (ji->used_regs & X86_EDI_MASK) {
534 ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
537 if (ji->used_regs & X86_ESI_MASK) {
538 ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
541 ctx->SC_ESP = ctx->SC_EBP;
542 ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 5;
543 ctx->SC_EBP = *((int *)ctx->SC_EBP);
545 if (ctx->SC_EBP > (unsigned)end_of_stack) {
549 g_assert_not_reached ();
551 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
552 g_list_free (trace_ips);
562 g_assert_not_reached ();
564 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
565 g_list_free (trace_ips);
576 trace_ips = g_list_append (trace_ips, lmf->method->info);
578 if (!((MonoException*)obj)->stack_trace)
579 strace = g_strdup ("");
581 strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
583 tmp = g_strdup_printf ("%sin (unmanaged) %s\n", strace, mono_method_full_name (m, TRUE));
587 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
591 ctx->SC_ESI = lmf->esi;
592 ctx->SC_EDI = lmf->edi;
593 ctx->SC_EBX = lmf->ebx;
594 ctx->SC_EBP = lmf->ebp;
595 ctx->SC_EIP = lmf->eip;
596 ctx->SC_ESP = (unsigned long)&lmf->eip;
598 lmf = lmf->previous_lmf;
600 if (ctx->SC_EBP >= (unsigned)end_of_stack) {
604 g_assert_not_reached ();
606 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
607 g_list_free (trace_ips);
614 g_assert_not_reached ();
618 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
619 unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
620 unsigned long eip, unsigned long esp)
622 static void (*restore_context) (struct sigcontext *);
623 struct sigcontext ctx;
625 if (!restore_context)
626 restore_context = arch_get_restore_context ();
628 /* adjust eip so that it point to the call instruction */
641 arch_handle_exception (&ctx, exc, FALSE);
642 restore_context (&ctx);
644 g_assert_not_reached ();
648 * arch_get_throw_exception:
650 * Returns a function pointer which can be used to raise
651 * exceptions. The returned function has the following
652 * signature: void (*func) (MonoException *exc);
653 * For example to raise an arithmetic exception you can use:
655 * x86_push_imm (code, mono_get_exception_arithmetic ());
656 * x86_call_code (code, arch_get_throw_exception ());
660 arch_get_throw_exception (void)
662 static guint8 start [24];
663 static int inited = 0;
672 x86_push_reg (code, X86_ESP);
673 x86_push_membase (code, X86_ESP, 4); /* IP */
674 x86_push_membase (code, X86_ESP, 12); /* exception */
675 x86_push_reg (code, X86_EBP);
676 x86_push_reg (code, X86_EDI);
677 x86_push_reg (code, X86_ESI);
678 x86_push_reg (code, X86_EBX);
679 x86_push_reg (code, X86_EDX);
680 x86_push_reg (code, X86_ECX);
681 x86_push_reg (code, X86_EAX);
682 x86_call_code (code, throw_exception);
683 /* we should never reach this breakpoint */
684 x86_breakpoint (code);
686 g_assert ((code - start) < 24);
691 * arch_get_throw_exception_by_name:
693 * Returns a function pointer which can be used to raise
694 * corlib exceptions. The returned function has the following
695 * signature: void (*func) (char *exc_name);
696 * For example to raise an arithmetic exception you can use:
698 * x86_push_imm (code, "ArithmeticException");
699 * x86_call_code (code, arch_get_throw_exception ());
703 arch_get_throw_exception_by_name ()
705 static guint8 start [32];
706 static int inited = 0;
715 /* fixme: we do not save EAX, EDX, ECD - unsure if we need that */
717 x86_push_membase (code, X86_ESP, 4); /* exception name */
718 x86_push_imm (code, "System");
719 x86_push_imm (code, mono_defaults.exception_class->image);
720 x86_call_code (code, mono_exception_from_name);
721 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
722 /* save the newly create object (overwrite exception name)*/
723 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
724 x86_jump_code (code, arch_get_throw_exception ());
726 g_assert ((code - start) < 32);