2 * exception.c: exception support
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2001 Ximian, Inc.
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>
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
46 * arch_get_restore_context:
48 * Returns a pointer to a method which restores a previously saved sigcontext.
51 arch_get_restore_context (void)
53 static guint8 *start = NULL;
59 /* restore_contect (struct sigcontext *ctx) */
60 /* we do not restore X86_EAX, X86_EDX */
62 start = code = g_malloc (1024);
65 x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
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);
70 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
72 x86_mov_reg_membase (code, X86_EDI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EDI), 4);
74 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
76 x86_mov_reg_membase (code, X86_ESP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESP), 4);
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);
82 /* jump to the saved IP */
83 x86_jump_reg (code, X86_EDX);
89 * arch_get_call_finally:
91 * Returns a pointer to a method which calls a finally handler.
94 arch_get_call_finally (void)
96 static guint8 start [64];
97 static int inited = 0;
104 /* call_finally (struct sigcontext *ctx, unsigned long eip) */
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);
114 x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
116 x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
118 x86_push_reg (code, X86_EBP);
120 x86_mov_reg_membase (code, X86_EBP, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBP), 4);
121 /* restore registers used by global register allocation (EBX & ESI) */
122 x86_mov_reg_membase (code, X86_EBX, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_EBX), 4);
123 x86_mov_reg_membase (code, X86_ESI, X86_EAX, G_STRUCT_OFFSET (struct sigcontext, SC_ESI), 4);
124 /* save the ESP - this is used by endfinally */
125 x86_mov_membase_reg (code, X86_EBP, mono_exc_esp_offset, X86_ESP, 4);
126 /* call the handler */
127 x86_call_reg (code, X86_ECX);
129 x86_pop_reg (code, X86_EBP);
130 /* restore saved regs */
131 x86_pop_reg (code, X86_ESI);
132 x86_pop_reg (code, X86_EDI);
133 x86_pop_reg (code, X86_EBX);
137 g_assert ((code - start) < 64);
142 glist_to_array (GList *list)
144 MonoDomain *domain = mono_domain_get ();
151 len = g_list_length (list);
152 res = mono_array_new (domain, mono_defaults.int_class, len);
154 for (i = 0; list; list = list->next, i++)
155 mono_array_set (res, gpointer, i, list->data);
161 * return TRUE if the exception is catched. It also sets the
162 * stack_trace String.
165 arch_exc_is_caught (MonoDomain *domain, MonoJitTlsData *jit_tls, gpointer ip,
166 gpointer *bp, gpointer obj)
169 gpointer *end_of_stack;
170 MonoLMF *lmf = jit_tls->lmf;
172 GList *trace_ips = NULL;
175 end_of_stack = jit_tls->end_of_stack;
176 g_assert (end_of_stack);
180 ji = mono_jit_info_table_find (domain, ip);
182 if (ji) { /* we are inside managed code */
185 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
187 char *tmp, *tmpsig, *source_location, *tmpaddr, *fname;
188 gint32 address, iloffset;
190 trace_ips = g_list_append (trace_ips, ip);
192 if (!((MonoException*)obj)->stack_trace)
193 strace = g_strdup ("");
195 strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
197 address = (char *)ip - (char *)ji->code_start;
199 source_location = mono_debug_source_location_from_address (m, address, NULL);
200 iloffset = mono_debug_il_offset_from_address (m, address);
203 tmpaddr = g_strdup_printf ("<0x%05x>", address);
205 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
207 tmpsig = mono_signature_get_desc (m->signature, TRUE);
209 fname = mono_method_full_name (m);
212 tmp = g_strdup_printf ("%sin %s (at %s) %s (%s)\n", strace, tmpaddr,
213 source_location, fname, tmpsig);
215 tmp = g_strdup_printf ("%sin %s %s (%s)\n", strace, tmpaddr,
218 g_free (source_location);
222 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
226 if (ji->num_clauses) {
228 g_assert (ji->clauses);
230 for (i = 0; i < ji->num_clauses; i++) {
231 MonoJitExceptionInfo *ei = &ji->clauses [i];
233 if (ei->try_start <= ip && ip <= (ei->try_end)) {
235 if (ei->flags == 0 && (m->is_wrapper ||
236 mono_object_isinst (obj, mono_class_get (m->klass->image, ei->token_or_filter)))) {
237 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
238 g_list_free (trace_ips);
245 /* continue unwinding */
247 ip = (gpointer)(*((int *)bp + 1) - 5);
248 bp = (gpointer)(*((int *)bp));
250 if (bp >= end_of_stack) {
251 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
252 g_list_free (trace_ips);
258 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
259 g_list_free (trace_ips);
263 bp = (gpointer)lmf->ebp;
264 ip = (gpointer)lmf->eip;
268 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
272 trace_ips = g_list_append (trace_ips, lmf->method->info);
274 if (!((MonoException*)obj)->stack_trace)
275 strace = g_strdup ("");
277 strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
279 tmp = g_strdup_printf ("%sin (unmanaged) %s.%s:%s ()\n", strace, m->klass->name_space,
280 m->klass->name, m->name);
284 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
288 lmf = lmf->previous_lmf;
290 if (bp >= end_of_stack) {
291 ((MonoException*)obj)->trace_ips = glist_to_array (trace_ips);
292 g_list_free (trace_ips);
302 ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info)
304 MonoDomain *domain = mono_domain_get ();
306 MonoArray *ta = exc->trace_ips;
309 len = mono_array_length (ta);
311 res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0);
313 for (i = skip; i < len; i++) {
315 MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class);
316 gpointer ip = mono_array_get (ta, gpointer, i);
318 ji = mono_jit_info_table_find (domain, ip);
319 g_assert (ji != NULL);
321 sf->method = mono_method_get_object (domain, ji->method);
322 sf->native_offset = (char *)ip - (char *)ji->code_start;
323 sf->il_offset = mono_debug_il_offset_from_address (ji->method, sf->native_offset);
325 if (need_file_info) {
328 filename = mono_debug_source_location_from_address (ji->method, sf->native_offset, &sf->line);
330 sf->filename = mono_string_new (domain, filename ? filename : "<unknown>");
336 mono_array_set (res, gpointer, i, sf);
343 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info,
344 MonoReflectionMethod **method,
345 gint32 *iloffset, gint32 *native_offset,
346 MonoString **file, gint32 *line, gint32 *column)
348 MonoDomain *domain = mono_domain_get ();
349 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
350 MonoLMF *lmf = jit_tls->lmf;
351 gpointer *sf = (gpointer *)&skip;
352 gpointer ip = sf [-1];
354 gpointer *bp = sf [-2];
355 MonoMethod *m = NULL;
359 addr = -1; /* unknown */
361 if ((ji = mono_jit_info_table_find (domain, ip))) {
363 addr = (char *)ip - (char *)ji->code_start;
364 ip = (gpointer)((char *)bp [1] - 5);
372 bp = (gpointer)lmf->ebp;
373 ip = (gpointer)lmf->eip;
375 lmf = lmf->previous_lmf;
378 if ((unsigned)bp >= (unsigned)jit_tls->end_of_stack)
381 } while (skip-- > 0);
385 *method = mono_method_get_object (domain, m);
386 *iloffset = mono_debug_il_offset_from_address (m, addr);
387 *native_offset = addr;
389 if (need_file_info) {
392 filename = mono_debug_source_location_from_address (m, addr, line);
394 *file = mono_string_new (domain, filename ? filename : "<unknown>");
404 * arch_handle_exception:
405 * @ctx: saved processor state
409 arch_handle_exception (struct sigcontext *ctx, gpointer obj)
411 MonoDomain *domain = mono_domain_get ();
413 gpointer ip = (gpointer)ctx->SC_EIP;
414 static void (*restore_context) (struct sigcontext *);
415 static void (*call_finally) (struct sigcontext *, unsigned long);
416 void (*cleanup) (MonoObject *exc);
417 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
418 gpointer end_of_stack;
420 g_assert (ctx != NULL);
423 MonoException *ex = mono_get_exception_null_reference ();
424 ex->message = mono_string_new (domain,
425 "Object reference not set to an instance of an object");
426 obj = (MonoObject *)ex;
429 ((MonoException *)obj)->stack_trace = NULL;
431 if (!restore_context)
432 restore_context = arch_get_restore_context ();
435 call_finally = arch_get_call_finally ();
437 end_of_stack = jit_tls->end_of_stack;
438 g_assert (end_of_stack);
440 cleanup = jit_tls->abort_func;
442 if (!arch_exc_is_caught (domain, jit_tls, ip, (gpointer *)ctx->SC_EBP, obj)) {
443 if (mono_break_on_exc) {
444 if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
445 mono_debug_make_symbols ();
448 mono_unhandled_exception (obj);
453 ji = mono_jit_info_table_find (domain, ip);
455 if (ji) { /* we are inside managed code */
456 MonoMethod *m = ji->method;
459 if (ji->num_clauses) {
462 g_assert (ji->clauses);
464 for (i = 0; i < ji->num_clauses; i++) {
465 MonoJitExceptionInfo *ei = &ji->clauses [i];
467 if (ei->try_start <= ip && ip <= (ei->try_end)) {
469 if (ei->flags == 0 && (m->is_wrapper ||
470 mono_object_isinst (obj, mono_class_get (m->klass->image, ei->token_or_filter)))) {
472 ctx->SC_EIP = (unsigned long)ei->handler_start;
473 ctx->SC_ECX = (unsigned long)obj;
474 restore_context (ctx);
475 g_assert_not_reached ();
480 /* no handler found - we need to call all finally handlers */
481 for (i = 0; i < ji->num_clauses; i++) {
482 MonoJitExceptionInfo *ei = &ji->clauses [i];
484 if (ei->try_start <= ip && ip < (ei->try_end) &&
485 (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
486 call_finally (ctx, (unsigned long)ei->handler_start);
491 /* continue unwinding */
494 /* restore caller saved registers */
495 if (ji->used_regs & X86_EBX_MASK) {
496 ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
499 if (ji->used_regs & X86_EDI_MASK) {
500 ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
503 if (ji->used_regs & X86_ESI_MASK) {
504 ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
507 ctx->SC_ESP = ctx->SC_EBP;
508 ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 5;
509 ctx->SC_EBP = *((int *)ctx->SC_EBP);
511 ip = (gpointer)ctx->SC_EIP;
513 if (ctx->SC_EBP > (unsigned)end_of_stack) {
516 g_assert_not_reached ();
520 MonoLMF *lmf = jit_tls->lmf;
525 g_assert_not_reached ();
528 jit_tls->lmf = lmf->previous_lmf;
530 ctx->SC_ESI = lmf->esi;
531 ctx->SC_EDI = lmf->edi;
532 ctx->SC_EBX = lmf->ebx;
533 ctx->SC_EBP = lmf->ebp;
534 ctx->SC_EIP = lmf->eip;
535 ctx->SC_ESP = (unsigned long)&lmf->eip;
537 ip = (gpointer)ctx->SC_EIP;
539 if (ctx->SC_EBP >= (unsigned)end_of_stack) {
542 g_assert_not_reached ();
547 g_assert_not_reached ();
551 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
552 unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
553 unsigned long eip, unsigned long esp)
555 struct sigcontext ctx;
557 /* adjust eip so that it point to the call instruction */
570 arch_handle_exception (&ctx, exc);
572 g_assert_not_reached ();
576 * arch_get_throw_exception:
578 * Returns a function pointer which can be used to raise
579 * exceptions. The returned function has the following
580 * signature: void (*func) (MonoException *exc);
581 * For example to raise an arithmetic exception you can use:
583 * x86_push_imm (code, mono_get_exception_arithmetic ());
584 * x86_call_code (code, arch_get_throw_exception ());
588 arch_get_throw_exception (void)
590 static guint8 start [24];
591 static int inited = 0;
600 x86_push_reg (code, X86_ESP);
601 x86_push_membase (code, X86_ESP, 4); /* IP */
602 x86_push_membase (code, X86_ESP, 12); /* exception */
603 x86_push_reg (code, X86_EBP);
604 x86_push_reg (code, X86_EDI);
605 x86_push_reg (code, X86_ESI);
606 x86_push_reg (code, X86_EBX);
607 x86_push_reg (code, X86_EDX);
608 x86_push_reg (code, X86_ECX);
609 x86_push_reg (code, X86_EAX);
610 x86_call_code (code, throw_exception);
611 /* we should never reach this breakpoint */
612 x86_breakpoint (code);
614 g_assert ((code - start) < 24);
619 * arch_get_throw_exception_by_name:
621 * Returns a function pointer which can be used to raise
622 * corlib exceptions. The returned function has the following
623 * signature: void (*func) (char *exc_name);
624 * For example to raise an arithmetic exception you can use:
626 * x86_push_imm (code, "ArithmeticException");
627 * x86_call_code (code, arch_get_throw_exception ());
631 arch_get_throw_exception_by_name ()
633 static guint8 start [32];
634 static int inited = 0;
643 /* fixme: we do not save EAX, EDX, ECD - unsure if we need that */
645 x86_push_membase (code, X86_ESP, 4); /* exception name */
646 x86_push_imm (code, "System");
647 x86_push_imm (code, mono_defaults.exception_class->image);
648 x86_call_code (code, mono_exception_from_name);
649 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
650 /* save the newly create object (overwrite exception name)*/
651 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
652 x86_jump_code (code, arch_get_throw_exception ());
654 g_assert ((code - start) < 32);