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 [28];
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 /* save the ESP - this is used by endfinally */
122 x86_mov_membase_reg (code, X86_EBP, -16, X86_ESP, 4);
123 /* call the handler */
124 x86_call_reg (code, X86_ECX);
126 x86_pop_reg (code, X86_EBP);
127 /* restore saved regs */
128 x86_pop_reg (code, X86_ESI);
129 x86_pop_reg (code, X86_EDI);
130 x86_pop_reg (code, X86_EBX);
134 g_assert ((code - start) < 28);
139 * return TRUE if the exception is catched. It also sets the
140 * stack_trace String.
143 arch_exc_is_caught (MonoDomain *domain, MonoJitTlsData *jit_tls, gpointer ip,
144 gpointer *bp, gpointer obj)
147 gpointer *end_of_stack;
148 MonoLMF *lmf = jit_tls->lmf;
152 end_of_stack = jit_tls->end_of_stack;
153 g_assert (end_of_stack);
157 ji = mono_jit_info_table_find (domain, ip);
159 if (ji) { /* we are inside managed code */
162 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
164 char *tmp, *tmpsig, *source_location, *tmpaddr;
165 gint32 address, iloffset;
167 if (!((MonoException*)obj)->stack_trace)
168 strace = g_strdup ("");
170 strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
172 address = (char *)ip - (char *)ji->code_start;
174 source_location = mono_debug_source_location_from_address (m, address);
175 iloffset = mono_debug_il_offset_from_address (m, address);
178 tmpaddr = g_strdup_printf ("<0x%05x>", address);
180 tmpaddr = g_strdup_printf ("[0x%05x]", iloffset);
182 tmpsig = mono_signature_get_desc(m->signature, TRUE);
184 tmp = g_strdup_printf ("%sin %s (at %s) %s.%s:%s (%s)\n", strace, tmpaddr,
185 source_location, m->klass->name_space, m->klass->name,
188 tmp = g_strdup_printf ("%sin %s %s.%s:%s (%s)\n", strace, tmpaddr,
189 m->klass->name_space, m->klass->name, m->name, tmpsig);
190 g_free (source_location);
194 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
198 if (ji->num_clauses) {
200 g_assert (ji->clauses);
202 for (i = 0; i < ji->num_clauses; i++) {
203 MonoJitExceptionInfo *ei = &ji->clauses [i];
205 if (ei->try_start <= ip && ip <= (ei->try_end)) {
207 if (ei->flags == 0 && mono_object_isinst (obj,
208 mono_class_get (m->klass->image, ei->token_or_filter))) {
215 /* continue unwinding */
217 ip = (gpointer)(*((int *)bp + 1) - 5);
218 bp = (gpointer)(*((int *)bp));
220 if (bp >= end_of_stack) {
230 bp = (gpointer)lmf->ebp;
231 ip = (gpointer)lmf->eip;
235 if (mono_object_isinst (obj, mono_defaults.exception_class)) {
239 if (!((MonoException*)obj)->stack_trace)
240 strace = g_strdup ("");
242 strace = mono_string_to_utf8 (((MonoException*)obj)->stack_trace);
244 tmp = g_strdup_printf ("%sin (unmanaged) %s.%s:%s ()\n", strace, m->klass->name_space,
245 m->klass->name, m->name);
249 ((MonoException*)obj)->stack_trace = mono_string_new (domain, tmp);
253 lmf = lmf->previous_lmf;
255 if (bp >= end_of_stack) {
267 * arch_handle_exception:
268 * @ctx: saved processor state
272 arch_handle_exception (struct sigcontext *ctx, gpointer obj)
274 MonoDomain *domain = mono_domain_get ();
276 gpointer ip = (gpointer)ctx->SC_EIP;
277 static void (*restore_context) (struct sigcontext *);
278 static void (*call_finally) (struct sigcontext *, unsigned long);
279 void (*cleanup) (MonoObject *exc);
280 MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id);
281 gpointer end_of_stack;
283 g_assert (ctx != NULL);
286 MonoException *ex = mono_get_exception_null_reference ();
287 ex->message = mono_string_new (domain,
288 "Object reference not set to an instance of an object");
289 obj = (MonoObject *)ex;
292 ((MonoException *)obj)->stack_trace = NULL;
294 if (!restore_context)
295 restore_context = arch_get_restore_context ();
298 call_finally = arch_get_call_finally ();
300 end_of_stack = jit_tls->end_of_stack;
301 g_assert (end_of_stack);
303 cleanup = jit_tls->abort_func;
305 if (!arch_exc_is_caught (domain, jit_tls, ip, (gpointer *)ctx->SC_EBP, obj)) {
306 if (mono_break_on_exc) {
307 if (mono_debug_format != MONO_DEBUG_FORMAT_NONE)
308 mono_debug_make_symbols ();
311 mono_unhandled_exception (obj);
316 ji = mono_jit_info_table_find (domain, ip);
318 if (ji) { /* we are inside managed code */
319 MonoMethod *m = ji->method;
322 if (ji->num_clauses) {
325 g_assert (ji->clauses);
327 for (i = 0; i < ji->num_clauses; i++) {
328 MonoJitExceptionInfo *ei = &ji->clauses [i];
330 if (ei->try_start <= ip && ip <= (ei->try_end)) {
332 if (ei->flags == 0 && mono_object_isinst (obj,
333 mono_class_get (m->klass->image, ei->token_or_filter))) {
335 ctx->SC_EIP = (unsigned long)ei->handler_start;
336 ctx->SC_ECX = (unsigned long)obj;
337 restore_context (ctx);
338 g_assert_not_reached ();
343 /* no handler found - we need to call all finally handlers */
344 for (i = 0; i < ji->num_clauses; i++) {
345 MonoJitExceptionInfo *ei = &ji->clauses [i];
347 if (ei->try_start <= ip && ip < (ei->try_end) &&
348 (ei->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
349 call_finally (ctx, (unsigned long)ei->handler_start);
354 /* continue unwinding */
357 /* restore caller saved registers */
358 if (ji->used_regs & X86_ESI_MASK) {
359 ctx->SC_EBX = *((int *)ctx->SC_EBP + offset);
362 if (ji->used_regs & X86_EDI_MASK) {
363 ctx->SC_EDI = *((int *)ctx->SC_EBP + offset);
366 if (ji->used_regs & X86_EBX_MASK) {
367 ctx->SC_ESI = *((int *)ctx->SC_EBP + offset);
370 ctx->SC_ESP = ctx->SC_EBP;
371 ctx->SC_EIP = *((int *)ctx->SC_EBP + 1) - 5;
372 ctx->SC_EBP = *((int *)ctx->SC_EBP);
374 ip = (gpointer)ctx->SC_EIP;
376 if (ctx->SC_EBP > (unsigned)end_of_stack) {
379 g_assert_not_reached ();
383 MonoLMF *lmf = jit_tls->lmf;
388 g_assert_not_reached ();
391 jit_tls->lmf = lmf->previous_lmf;
393 ctx->SC_ESI = lmf->esi;
394 ctx->SC_EDI = lmf->edi;
395 ctx->SC_EBX = lmf->ebx;
396 ctx->SC_EBP = lmf->ebp;
397 ctx->SC_EIP = lmf->eip;
398 ctx->SC_ESP = (unsigned long)&lmf->eip;
400 ip = (gpointer)ctx->SC_EIP;
402 if (ctx->SC_EBP >= (unsigned)end_of_stack) {
405 g_assert_not_reached ();
410 g_assert_not_reached ();
414 throw_exception (unsigned long eax, unsigned long ecx, unsigned long edx, unsigned long ebx,
415 unsigned long esi, unsigned long edi, unsigned long ebp, MonoObject *exc,
416 unsigned long eip, unsigned long esp)
418 struct sigcontext ctx;
420 /* adjust eip so that it point to the call instruction */
433 arch_handle_exception (&ctx, exc);
435 g_assert_not_reached ();
439 * arch_get_throw_exception:
441 * Returns a function pointer which can be used to raise
442 * exceptions. The returned function has the following
443 * signature: void (*func) (MonoException *exc);
444 * For example to raise an arithmetic exception you can use:
446 * x86_push_imm (code, mono_get_exception_arithmetic ());
447 * x86_call_code (code, arch_get_throw_exception ());
451 arch_get_throw_exception (void)
453 static guint8 start [24];
454 static int inited = 0;
463 x86_push_reg (code, X86_ESP);
464 x86_push_membase (code, X86_ESP, 4); /* IP */
465 x86_push_membase (code, X86_ESP, 12); /* exception */
466 x86_push_reg (code, X86_EBP);
467 x86_push_reg (code, X86_EDI);
468 x86_push_reg (code, X86_ESI);
469 x86_push_reg (code, X86_EBX);
470 x86_push_reg (code, X86_EDX);
471 x86_push_reg (code, X86_ECX);
472 x86_push_reg (code, X86_EAX);
473 x86_call_code (code, throw_exception);
474 /* we should never reach this breakpoint */
475 x86_breakpoint (code);
477 g_assert ((code - start) < 24);
482 * arch_get_throw_exception_by_name:
484 * Returns a function pointer which can be used to raise
485 * corlib exceptions. The returned function has the following
486 * signature: void (*func) (char *exc_name);
487 * For example to raise an arithmetic exception you can use:
489 * x86_push_imm (code, "ArithmeticException");
490 * x86_call_code (code, arch_get_throw_exception ());
494 arch_get_throw_exception_by_name ()
496 static guint8 start [32];
497 static int inited = 0;
506 /* fixme: we do not save EAX, EDX, ECD - unsure if we need that */
508 x86_push_membase (code, X86_ESP, 4); /* exception name */
509 x86_push_imm (code, "System");
510 x86_push_imm (code, mono_defaults.exception_class->image);
511 x86_call_code (code, mono_exception_from_name);
512 x86_alu_reg_imm (code, X86_ADD, X86_ESP, 12);
513 /* save the newly create object (overwrite exception name)*/
514 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
515 x86_jump_code (code, arch_get_throw_exception ());
517 g_assert ((code - start) < 32);