[amd64] Use soft breakpoints in aot mode.
authorZoltan Varga <vargaz@gmail.com>
Thu, 2 Apr 2015 23:56:10 +0000 (19:56 -0400)
committerZoltan Varga <vargaz@gmail.com>
Thu, 2 Apr 2015 23:56:25 +0000 (19:56 -0400)
mono/mini/cpu-amd64.md
mono/mini/debugger-agent.c
mono/mini/mini-amd64.c
mono/mini/mini-amd64.h
mono/mini/mini-trampolines.c
mono/mini/mini.h
mono/mini/tramp-amd64.c

index b24b0e5f5005cc1e299c132c099c663b234950c3..00b01e9a47c575bdf5f2ab9447d42122280ead0d 100755 (executable)
@@ -65,7 +65,7 @@ break: len:2
 tailcall: len:120 clob:c
 br: len:6
 label: len:0
-seq_point: len:31 clob:c
+seq_point: len:46 clob:c
 il_seq_point: len:0
 
 long_add: dest:i src1:i src2:i len:3 clob:1 nacl:6
index e7776b004d27ce97ee1a7e0146bbf49aba08f68b..5ea057983fe28652d2708e869f6a6c5d0c8f31ca 100644 (file)
@@ -4727,7 +4727,7 @@ breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
 }
 
 static void
-process_breakpoint_inner (DebuggerTlsData *tls)
+process_breakpoint_inner (DebuggerTlsData *tls, gboolean from_signal)
 {
        MonoJitInfo *ji;
        guint8 *ip;
@@ -4757,7 +4757,8 @@ process_breakpoint_inner (DebuggerTlsData *tls)
        /* 
         * Skip the instruction causing the breakpoint signal.
         */
-       mono_arch_skip_breakpoint (ctx, ji);
+       if (from_signal)
+               mono_arch_skip_breakpoint (ctx, ji);
 
        if (method->wrapper_type || tls->disable_breakpoints)
                return;
@@ -4850,7 +4851,7 @@ process_breakpoint_inner (DebuggerTlsData *tls)
 
 /* Process a breakpoint/single step event after resuming from a signal handler */
 static void
-process_signal_event (void (*func) (DebuggerTlsData*))
+process_signal_event (void (*func) (DebuggerTlsData*, gboolean))
 {
        DebuggerTlsData *tls;
        MonoThreadUnwindState orig_restore_state;
@@ -4861,7 +4862,7 @@ process_signal_event (void (*func) (DebuggerTlsData*))
        memcpy (&orig_restore_state, &tls->restore_state, sizeof (MonoThreadUnwindState));
        mono_thread_state_init_from_monoctx (&tls->restore_state, &tls->handler_ctx);
 
-       func (tls);
+       func (tls, TRUE);
 
        /* This is called when resuming from a signal handler, so it shouldn't return */
        memcpy (&ctx, &tls->restore_state.ctx, sizeof (MonoContext));
@@ -4973,7 +4974,7 @@ ss_depth_to_string (StepDepth depth)
 }
 
 static void
-process_single_step_inner (DebuggerTlsData *tls)
+process_single_step_inner (DebuggerTlsData *tls, gboolean from_signal)
 {
        MonoJitInfo *ji;
        guint8 *ip;
@@ -4989,7 +4990,8 @@ process_single_step_inner (DebuggerTlsData *tls)
        ip = MONO_CONTEXT_GET_IP (ctx);
 
        /* Skip the instruction causing the single step */
-       mono_arch_skip_single_step (ctx);
+       if (from_signal)
+               mono_arch_skip_single_step (ctx);
 
        if (suspend_count > 0) {
                process_suspend (tls, ctx);
@@ -5112,7 +5114,7 @@ debugger_agent_single_step_from_context (MonoContext *ctx)
        memcpy (&orig_restore_state, &tls->restore_state, sizeof (MonoThreadUnwindState));
        mono_thread_state_init_from_monoctx (&tls->restore_state, ctx);
 
-       process_single_step_inner (tls);
+       process_single_step_inner (tls, FALSE);
 
        memcpy (ctx, &tls->restore_state.ctx, sizeof (MonoContext));
        memcpy (&tls->restore_state, &orig_restore_state, sizeof (MonoThreadUnwindState));
@@ -5129,7 +5131,7 @@ debugger_agent_breakpoint_from_context (MonoContext *ctx)
        memcpy (&orig_restore_state, &tls->restore_state, sizeof (MonoContext));
        mono_thread_state_init_from_monoctx (&tls->restore_state, ctx);
 
-       process_breakpoint_inner (tls);
+       process_breakpoint_inner (tls, FALSE);
 
        memcpy (ctx, &tls->restore_state.ctx, sizeof (MonoContext));
        memcpy (&tls->restore_state, &orig_restore_state, sizeof (MonoThreadUnwindState));
index 3bf75530531dd00ac9fd3aba074ea57addbb350d..cd1070c42f07a43b8b3447bf66e3069cf31b6be9 100755 (executable)
@@ -85,6 +85,9 @@ static int breakpoint_fault_size;
 /* The size of the single step instruction causing the actual fault */
 static int single_step_fault_size;
 
+/* The single step trampoline */
+static gpointer ss_trampoline;
+
 /* Offset between fp and the first argument in the callee */
 #define ARGS_OFFSET 16
 #define GP_SCRATCH_REG AMD64_R11
@@ -2012,6 +2015,10 @@ mono_arch_create_vars (MonoCompile *cfg)
                        MonoInst *ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
                        ins->flags |= MONO_INST_VOLATILE;
                        cfg->arch.seq_point_info_var = ins;
+
+                       ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+                       ins->flags |= MONO_INST_VOLATILE;
+                       cfg->arch.ss_tramp_var = ins;
                }
 
            ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
@@ -4169,17 +4176,33 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_SEQ_POINT: {
                        int i;
 
-                       /* 
-                        * Read from the single stepping trigger page. This will cause a
-                        * SIGSEGV when single stepping is enabled.
-                        * We do this _before_ the breakpoint, so single stepping after
-                        * a breakpoint is hit will step to the next IL offset.
-                        */
                        if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
-                               MonoInst *var = cfg->arch.ss_trigger_page_var;
+                               if (cfg->compile_aot) {
+                                       MonoInst *var = cfg->arch.ss_tramp_var;
+                                       guint8 *label;
+
+                                       /* Load ss_tramp_var */
+                                       amd64_mov_reg_membase (code, AMD64_R11, var->inst_basereg, var->inst_offset, 8);
+                                       /* Load the trampoline address */
+                                       amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, 8);
+                                       /* Call it if it is non-null */
+                                       amd64_test_reg_reg (code, AMD64_R11, AMD64_R11);
+                                       label = code;
+                                       amd64_branch8 (code, X86_CC_Z, 0, FALSE);
+                                       amd64_call_reg (code, AMD64_R11);
+                                       amd64_patch (label, code);
+                               } else {
+                                       /* 
+                                        * Read from the single stepping trigger page. This will cause a
+                                        * SIGSEGV when single stepping is enabled.
+                                        * We do this _before_ the breakpoint, so single stepping after
+                                        * a breakpoint is hit will step to the next IL offset.
+                                        */
+                                       MonoInst *var = cfg->arch.ss_trigger_page_var;
 
-                               amd64_mov_reg_membase (code, AMD64_R11, var->inst_basereg, var->inst_offset, 8);
-                               amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R11, 0, 0, 4);
+                                       amd64_mov_reg_membase (code, AMD64_R11, var->inst_basereg, var->inst_offset, 8);
+                                       amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R11, 0, 0, 4);
+                               }
                        }
 
                        /* 
@@ -4191,13 +4214,19 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                guint32 offset = code - cfg->native_code;
                                guint32 val;
                                MonoInst *info_var = cfg->arch.seq_point_info_var;
+                               guint8 *label;
 
                                /* Load info var */
                                amd64_mov_reg_membase (code, AMD64_R11, info_var->inst_basereg, info_var->inst_offset, 8);
                                val = ((offset) * sizeof (guint8*)) + MONO_STRUCT_OFFSET (SeqPointInfo, bp_addrs);
-                               /* Load the info->bp_addrs [offset], which is either a valid address or the address of a trigger page */
+                               /* Load the info->bp_addrs [offset], which is either NULL or the address of the breakpoint trampoline */
                                amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, val, 8);
-                               amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, 8);
+                               amd64_test_reg_reg (code, AMD64_R11, AMD64_R11);
+                               label = code;
+                               amd64_branch8 (code, X86_CC_Z, 0, FALSE);
+                               /* Call the trampoline */
+                               amd64_call_reg (code, AMD64_R11);
+                               amd64_patch (label, code);
                        } else {
                                /* 
                                 * A placeholder for a possible breakpoint inserted by
@@ -7197,18 +7226,23 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                        amd64_mov_membase_reg (code, info_var->inst_basereg, info_var->inst_offset, AMD64_R11, 8);
                }
 
-               /* Initialize ss_trigger_page_var */
-               ins = cfg->arch.ss_trigger_page_var;
-
-               g_assert (ins->opcode == OP_REGOFFSET);
-
                if (cfg->compile_aot) {
+                       /* Initialize ss_tramp_var */
+                       ins = cfg->arch.ss_tramp_var;
+                       g_assert (ins->opcode == OP_REGOFFSET);
+
                        amd64_mov_reg_membase (code, AMD64_R11, info_var->inst_basereg, info_var->inst_offset, 8);
-                       amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (SeqPointInfo, ss_trigger_page), 8);
+                       amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, MONO_STRUCT_OFFSET (SeqPointInfo, ss_tramp_addr), 8);
+                       amd64_mov_membase_reg (code, ins->inst_basereg, ins->inst_offset, AMD64_R11, 8);
                } else {
+                       /* Initialize ss_trigger_page_var */
+                       ins = cfg->arch.ss_trigger_page_var;
+
+                       g_assert (ins->opcode == OP_REGOFFSET);
+
                        amd64_mov_reg_imm (code, AMD64_R11, (guint64)ss_trigger_page);
+                       amd64_mov_membase_reg (code, ins->inst_basereg, ins->inst_offset, AMD64_R11, 8);
                }
-               amd64_mov_membase_reg (code, ins->inst_basereg, ins->inst_offset, AMD64_R11, 8);
        }
 
        cfg->code_len = code - cfg->native_code;
@@ -8483,7 +8517,7 @@ mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
                SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
 
                g_assert (info->bp_addrs [native_offset] == 0);
-               info->bp_addrs [native_offset] = bp_trigger_page;
+               info->bp_addrs [native_offset] = mini_get_breakpoint_trampoline ();
        } else {
                /* 
                 * In production, we will use int3 (has to fix the size in the md 
@@ -8517,8 +8551,7 @@ mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
                guint32 native_offset = ip - (guint8*)ji->code_start;
                SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
 
-               g_assert (info->bp_addrs [native_offset] == 0);
-               info->bp_addrs [native_offset] = info;
+               info->bp_addrs [native_offset] = NULL;
        } else {
                for (i = 0; i < breakpoint_size; ++i)
                        x86_nop (code);
@@ -8554,8 +8587,7 @@ void
 mono_arch_skip_breakpoint (MonoContext *ctx, MonoJitInfo *ji)
 {
        if (ji->from_aot) {
-               /* amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, 8) */
-               MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 3);
+               /* The breakpoint instruction is a call */
        } else {
                MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + breakpoint_fault_size);
        }
@@ -8570,6 +8602,7 @@ void
 mono_arch_start_single_stepping (void)
 {
        mono_mprotect (ss_trigger_page, mono_pagesize (), 0);
+       ss_trampoline = mini_get_single_step_trampoline ();
 }
        
 /*
@@ -8581,6 +8614,7 @@ void
 mono_arch_stop_single_stepping (void)
 {
        mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ);
+       ss_trampoline = NULL;
 }
 
 /*
@@ -8631,7 +8665,6 @@ mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
 {
        SeqPointInfo *info;
        MonoJitInfo *ji;
-       int i;
 
        // FIXME: Add a free function
 
@@ -8647,11 +8680,7 @@ mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
                // FIXME: Optimize the size
                info = g_malloc0 (sizeof (SeqPointInfo) + (ji->code_size * sizeof (gpointer)));
 
-               info->ss_trigger_page = ss_trigger_page;
-               info->bp_trigger_page = bp_trigger_page;
-               /* Initialize to a valid address */
-               for (i = 0; i < ji->code_size; ++i)
-                       info->bp_addrs [i] = info;
+               info->ss_tramp_addr = &ss_trampoline;
 
                mono_domain_lock (domain);
                g_hash_table_insert (domain_jit_info (domain)->arch_seq_points,
index f6eb5f1aeed781a3d8a52d0b1ca326795ce6fbdb..1b48c14caa69bef3097b7a9522a7042518c86b93 100644 (file)
@@ -207,6 +207,7 @@ typedef struct MonoCompileArch {
 #endif
        gpointer seq_point_info_var;
        gpointer ss_trigger_page_var;
+       gpointer ss_tramp_var;
        gpointer lmf_var;
 } MonoCompileArch;
 
@@ -220,8 +221,7 @@ typedef struct MonoCompileArch {
 
 /* Structure used by the sequence points in AOTed code */
 typedef struct {
-       gpointer ss_trigger_page;
-       gpointer bp_trigger_page;
+       gpointer ss_tramp_addr;
        gpointer bp_addrs [MONO_ZERO_LEN_ARRAY];
 } SeqPointInfo;
 
@@ -354,6 +354,7 @@ typedef struct {
 #define MONO_ARCH_HAVE_OP_TAIL_CALL 1
 #define MONO_ARCH_HAVE_TRANSLATE_TLS_OFFSET 1
 #define MONO_ARCH_HAVE_DUMMY_INIT 1
+#define MONO_ARCH_HAVE_SDB_TRAMPOLINES 1
 
 #if defined(TARGET_OSX) || defined(__linux__)
 #define MONO_ARCH_HAVE_TLS_GET_REG 1
index daac4fe90e764558871b516545a22a6ad3db0f79..51d7e12dca05ecdd20289adb319c25e6d20c8e23 100644 (file)
@@ -1866,3 +1866,67 @@ mini_get_nullified_class_init_trampoline (void)
 
        return nullified_class_init_trampoline;
 }
+
+/*
+ * mini_get_single_step_trampoline:
+ *
+ *   Return a trampoline which calls debugger_agent_single_step_from_context ().
+ */
+gpointer
+mini_get_single_step_trampoline (void)
+{
+       static gpointer trampoline;
+
+       if (!trampoline) {
+               gpointer tramp;
+               MonoTrampInfo *info;
+
+               if (mono_aot_only) {
+                       tramp = mono_aot_get_trampoline ("sdb_single_step_trampoline");
+               } else {
+#ifdef MONO_ARCH_HAVE_SDB_TRAMPOLINES
+                       tramp = mono_arch_create_sdb_trampoline (TRUE, &info, FALSE);
+                       mono_tramp_info_register (info);
+#else
+                       tramp = NULL;
+                       g_assert_not_reached ();
+#endif
+               }
+               mono_memory_barrier ();
+               trampoline = tramp;
+       }
+
+       return trampoline;
+}
+
+/*
+ * mini_get_breakpoint_trampoline:
+ *
+ *   Return a trampoline which calls debugger_agent_breakpoint_from_context ().
+ */
+gpointer
+mini_get_breakpoint_trampoline (void)
+{
+       static gpointer trampoline;
+
+       if (!trampoline) {
+               gpointer tramp;
+               MonoTrampInfo *info;
+
+               if (mono_aot_only) {
+                       tramp = mono_aot_get_trampoline ("sdb_breakpoint_trampoline");
+               } else {
+#ifdef MONO_ARCH_HAVE_SDB_TRAMPOLINES
+                       tramp = mono_arch_create_sdb_trampoline (FALSE, &info, FALSE);
+                       mono_tramp_info_register (info);
+#else
+                       tramp = NULL;
+                       g_assert_not_reached ();
+#endif
+               }
+               mono_memory_barrier ();
+               trampoline = tramp;
+       }
+
+       return trampoline;
+}
index 427b39bb679d40cd7823457e1687c46c8f0dcaae..4f57200d16d3c902f55e050f1b9c8e584e4e51f5 100644 (file)
@@ -2364,6 +2364,8 @@ const char*       mono_get_generic_trampoline_simple_name (MonoTrampolineType tr
 char*             mono_get_generic_trampoline_name (MonoTrampolineType tramp_type);
 char*             mono_get_rgctx_fetch_trampoline_name (int slot);
 gpointer          mini_get_nullified_class_init_trampoline (void);
+gpointer          mini_get_single_step_trampoline (void);
+gpointer          mini_get_breakpoint_trampoline (void);
 gpointer          mini_add_method_trampoline (MonoMethod *orig_method, MonoMethod *m, gpointer compiled_method, gboolean add_static_rgctx_tramp, gboolean add_unbox_tramp);
 gboolean          mini_jit_info_is_gsharedvt (MonoJitInfo *ji);
 
@@ -2437,6 +2439,7 @@ gpointer  mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampI
 gpointer  mono_arch_create_general_rgctx_lazy_fetch_trampoline (MonoTrampInfo **info, gboolean aot);
 gpointer  mono_arch_create_generic_class_init_trampoline (MonoTrampInfo **info, gboolean aot);
 gpointer  mono_arch_get_nullified_class_init_trampoline (MonoTrampInfo **info);
+guint8*   mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot);
 gpointer  mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean is_v4, gboolean aot);
 gpointer  mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot);
 guint8   *mono_arch_create_llvm_native_thunk     (MonoDomain *domain, guint8* addr) MONO_LLVM_INTERNAL;
index 0c7f8b2b13f58b7e8a3e2165c2c7556bb012a34c..784d5c2635e04b1e6d3487ed99cfed1553bfbd5a 100755 (executable)
 
 #include "mini.h"
 #include "mini-amd64.h"
+#include "debugger-agent.h"
 
 #if defined(__native_client_codegen__) && defined(__native_client__)
 #include <malloc.h>
 #include <nacl/nacl_dyncode.h>
 #endif
 
+#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
+
 #define IS_REX(inst) (((inst) >= 0x40) && ((inst) <= 0x4f))
 
 /*
@@ -1358,3 +1361,102 @@ mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code)
        return *(guint32*)(plt_entry + 6);
 #endif
 }
+
+/*
+ * mono_arch_create_sdb_trampoline:
+ *
+ *   Return a trampoline which captures the current context, passes it to
+ * debugger_agent_single_step_from_context ()/debugger_agent_breakpoint_from_context (),
+ * then restores the (potentially changed) context.
+ */
+guint8*
+mono_arch_create_sdb_trampoline (gboolean single_step, MonoTrampInfo **info, gboolean aot)
+{
+       int tramp_size = 256;
+       int framesize, ctx_offset;
+       guint8 *code, *buf;
+       GSList *unwind_ops = NULL;
+       MonoJumpInfo *ji = NULL;
+
+       g_assert (!aot);
+
+       code = buf = mono_global_codeman_reserve (tramp_size);
+
+       framesize = sizeof (MonoContext);
+       framesize = ALIGN_TO (framesize, MONO_ARCH_FRAME_ALIGNMENT);
+
+       // FIXME: Unwind info
+       amd64_push_reg (code, AMD64_RBP);
+       amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
+       amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
+
+       ctx_offset = 0;
+
+       /* Initialize a MonoContext structure on the stack */
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rax), AMD64_RAX, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rbx), AMD64_RBX, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rcx), AMD64_RCX, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rdx), AMD64_RDX, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rsi), AMD64_RSI, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rdi), AMD64_RDI, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r8), AMD64_R8, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r9), AMD64_R9, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r10), AMD64_R10, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r11), AMD64_R11, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r12), AMD64_R12, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r13), AMD64_R13, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r14), AMD64_R14, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r15), AMD64_R15, sizeof (mgreg_t));
+
+       amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, 0, sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rbp), AMD64_R11, sizeof (mgreg_t));
+       amd64_lea_membase (code, AMD64_R11, AMD64_RBP, 2 * sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rsp), AMD64_R11, sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, sizeof (mgreg_t), sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rip), AMD64_R11, sizeof (mgreg_t));
+
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+       /* Call the single step/breakpoint function in sdb */
+       amd64_lea_membase (code, AMD64_ARG_REG1, AMD64_RSP, ctx_offset);
+       if (single_step)
+               amd64_call_code (code, debugger_agent_single_step_from_context);
+       else
+               amd64_call_code (code, debugger_agent_breakpoint_from_context);
+#else
+       g_assert_not_reached ();
+#endif
+
+       /* Restore registers from ctx */
+       amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rax), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_RBX, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rbx), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_RCX, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rcx), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_RDX, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rdx), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_RSI, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rsi), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_RDI, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rdi), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R8, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r8), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R9, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r9), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R10, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r10), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r11), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R12, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r12), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R13, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r13), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R14, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r14), sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R15, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, r15), sizeof (mgreg_t));
+
+       amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rbp), sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RBP, 0, AMD64_R11, sizeof (mgreg_t));
+       amd64_mov_reg_membase (code, AMD64_R11, AMD64_RSP, ctx_offset + G_STRUCT_OFFSET (MonoContext, rip), sizeof (mgreg_t));
+       amd64_mov_membase_reg (code, AMD64_RBP, sizeof (mgreg_t), AMD64_R11, sizeof (mgreg_t));
+
+       amd64_leave (code);
+       amd64_ret (code);
+
+       mono_arch_flush_icache (code, code - buf);
+       g_assert (code - buf <= tramp_size);
+
+       if (info) {
+               const char *tramp_name = single_step ? "sdb_single_step_trampoline" : "sdb_breakpoint_trampoline";
+               *info = mono_tramp_info_create (tramp_name, buf, code - buf, ji, unwind_ops);
+       }
+
+       return buf;
+}