From 94a37da6bb45784c999faf3874cf0e6184e38bd5 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Thu, 17 Jan 2008 10:43:53 +0000 Subject: [PATCH] 2008-01-17 Zoltan Varga * mini-amd64.h (MONO_ARCH_HAVE_NOTIFY_PENDING_EXC): Turn on this for amd64. * exceptions-amd64.c (mono_arch_notify_pending_exc): New function to process async exceptions received while in unmanaged code. svn path=/trunk/mono/; revision=93143 --- mono/mini/ChangeLog | 5 ++ mono/mini/exceptions-amd64.c | 132 +++++++++++++++++++++++++++++++++++ mono/mini/mini-amd64.h | 1 + 3 files changed, 138 insertions(+) diff --git a/mono/mini/ChangeLog b/mono/mini/ChangeLog index 0eca60af1a0..a5ce0852fc4 100644 --- a/mono/mini/ChangeLog +++ b/mono/mini/ChangeLog @@ -1,5 +1,10 @@ 2008-01-17 Zoltan Varga + * mini-amd64.h (MONO_ARCH_HAVE_NOTIFY_PENDING_EXC): Turn on this for amd64. + + * exceptions-amd64.c (mono_arch_notify_pending_exc): New function to + process async exceptions received while in unmanaged code. + * mini.c (mini_init): Install a callback with the runtime which will be called when a thread receives an async exception while in unmanaged code. diff --git a/mono/mini/exceptions-amd64.c b/mono/mini/exceptions-amd64.c index f599082d8a1..0036a9531c3 100644 --- a/mono/mini/exceptions-amd64.c +++ b/mono/mini/exceptions-amd64.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -886,3 +887,134 @@ mono_arch_handle_altstack_exception (void *sigctx, gpointer fault_addr, gboolean #endif } +static guint64 +get_original_ip (void) +{ + MonoLMF *lmf = mono_get_lmf (); + + g_assert (lmf); + + /* Reset the change to previous_lmf */ + lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf & ~1); + + return lmf->rip; +} + +static gpointer +get_throw_pending_exception (void) +{ + static guint8* start; + static gboolean inited = FALSE; + guint8 *code; + guint8 *br[1]; + gpointer throw_trampoline; + + if (inited) + return start; + + start = code = mono_global_codeman_reserve (128); + + /* We are in the frame of a managed method after a call */ + /* + * We would like to throw the pending exception in such a way that it looks to + * be thrown from the managed method. + */ + + /* Save registers which might contain the return value of the call */ + amd64_push_reg (code, AMD64_RAX); + amd64_push_reg (code, AMD64_RDX); + + amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8); + amd64_movsd_membase_reg (code, AMD64_RSP, 0, AMD64_XMM0); + + /* Align stack */ + amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8); + + /* Obtain the pending exception */ + amd64_mov_reg_imm (code, AMD64_R11, mono_thread_get_and_clear_pending_exception); + amd64_call_reg (code, AMD64_R11); + + /* Check if it is NULL, and branch */ + amd64_alu_reg_imm (code, X86_CMP, AMD64_RAX, 0); + br[0] = code; x86_branch8 (code, X86_CC_EQ, 0, FALSE); + + /* exc != NULL branch */ + + /* Save the exc on the stack */ + amd64_push_reg (code, AMD64_RAX); + /* Align stack */ + amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8); + + /* Obtain the original ip and clear the flag in previous_lmf */ + amd64_mov_reg_imm (code, AMD64_R11, get_original_ip); + amd64_call_reg (code, AMD64_R11); + + /* Load exc */ + amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, 8, 8); + + /* Pop saved stuff from the stack */ + amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 6 * 8); + + /* Setup arguments for the throw trampoline */ + /* Exception */ + amd64_mov_reg_reg (code, AMD64_ARG_REG1, AMD64_R11, 8); + /* The trampoline expects the caller ip to be pushed on the stack */ + amd64_push_reg (code, AMD64_RAX); + + /* Call the throw trampoline */ + throw_trampoline = mono_arch_get_throw_exception (); + amd64_mov_reg_imm (code, AMD64_R11, throw_trampoline); + /* We use a jump instead of a call so we can push the original ip on the stack */ + amd64_jump_reg (code, AMD64_R11); + + /* ex == NULL branch */ + mono_amd64_patch (br [0], code); + + /* Obtain the original ip and clear the flag in previous_lmf */ + amd64_mov_reg_imm (code, AMD64_R11, get_original_ip); + amd64_call_reg (code, AMD64_R11); + amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, 8); + + /* Restore registers */ + amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8); + amd64_movsd_reg_membase (code, AMD64_XMM0, AMD64_RSP, 0); + amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8); + amd64_pop_reg (code, AMD64_RDX); + amd64_pop_reg (code, AMD64_RAX); + + /* Return to original code */ + amd64_jump_reg (code, AMD64_R11); + + g_assert ((code - start) < 128); + + inited = TRUE; + + return start; +} + +/* + * Called when a thread receives an async exception while executing unmanaged code. + * Instead of checking for this exception in the managed-to-native wrapper, we hijack + * the return address on the stack to point to a helper routine which throws the + * exception. + */ +void +mono_arch_notify_pending_exc (void) +{ + MonoLMF *lmf = mono_get_lmf (); + + if (lmf->rsp == 0) + /* Initial LMF */ + return; + + if ((guint64)lmf->previous_lmf & 1) + /* Already hijacked or trampoline LMF entry */ + return; + + /* lmf->rsp is set just before making the call which transitions to unmanaged code */ + lmf->rip = *(guint64*)(lmf->rsp - 8); + /* Signal that lmf->rip is set */ + lmf->previous_lmf = (gpointer)((guint64)lmf->previous_lmf | 1); + + *(gpointer*)(lmf->rsp - 8) = get_throw_pending_exception (); +} diff --git a/mono/mini/mini-amd64.h b/mono/mini/mini-amd64.h index 7411dd1c5ff..3955cdeb12a 100644 --- a/mono/mini/mini-amd64.h +++ b/mono/mini/mini-amd64.h @@ -278,6 +278,7 @@ typedef struct { #define MONO_ARCH_IMT_REG AMD64_R11 #define MONO_ARCH_VTABLE_REG AMD64_R11 #define MONO_ARCH_COMMON_VTABLE_TRAMPOLINE 1 +#define MONO_ARCH_HAVE_NOTIFY_PENDING_EXC 1 #define MONO_ARCH_AOT_SUPPORTED 1 -- 2.25.1