This commit is contained in:
Johan Lorensson 2025-07-30 15:56:35 +02:00 committed by GitHub
commit 93b2766158
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 226 additions and 34 deletions

View File

@ -111,6 +111,9 @@ typedef enum
#define AMD64_CALLEE_SAVED_REGS ((1<<AMD64_RDI) | (1<<AMD64_RSI) | (1<<AMD64_RBX) | (1<<AMD64_R12) | (1<<AMD64_R13) | (1<<AMD64_R14) | (1<<AMD64_R15) | (1<<AMD64_RBP))
#define AMD64_IS_CALLEE_SAVED_REG(reg) (AMD64_CALLEE_SAVED_REGS & (1 << (reg)))
#define AMD64_CALLEE_SAVED_XREGS ((1<<AMD64_XMM6) | (1<<AMD64_XMM7) | (1<<AMD64_XMM8) | (1<<AMD64_XMM9) | (1<<AMD64_XMM10) | (1<<AMD64_XMM11) | (1<<AMD64_XMM12) | (1<<AMD64_XMM13) | (1<<AMD64_XMM14) | (1<<AMD64_XMM15))
#define AMD64_IS_CALLEE_SAVED_XREG(reg) (AMD64_CALLEE_SAVED_XREGS & (1 << (reg)))
#else
#define AMD64_CALLEE_REGS ((1<<AMD64_RAX) | (1<<AMD64_RCX) | (1<<AMD64_RDX) | (1<<AMD64_RSI) | (1<<AMD64_RDI) | (1<<AMD64_R8) | (1<<AMD64_R9) | (1<<AMD64_R10))
#define AMD64_IS_CALLEE_REG(reg) (AMD64_CALLEE_REGS & (1 << (reg)))

View File

@ -258,13 +258,27 @@ mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG1, 8);
/* Restore all registers except %rip and %r11 */
/* Restore all general registers except %rip and %r11 */
gregs_offset = MONO_STRUCT_OFFSET (MonoContext, gregs);
for (i = 0; i < AMD64_NREG; ++i) {
if (i != AMD64_RIP && i != AMD64_RSP && i != AMD64_R8 && i != AMD64_R9 && i != AMD64_R10 && i != AMD64_R11)
amd64_mov_reg_membase (code, i, AMD64_R11, gregs_offset + (i * 8), 8);
}
#ifdef AMD64_CALLEE_SAVED_XREGS
/* Restore all callee saved XMM registers */
int fregs_offset = MONO_STRUCT_OFFSET (MonoContext, fregs);
for (i = 0; i < AMD64_XMM_NREG; ++i) {
if (AMD64_IS_CALLEE_SAVED_XREG (i)) {
#if defined(MONO_HAVE_SIMD_REG)
amd64_movdqu_reg_membase (code, i, AMD64_R11, fregs_offset + (i * sizeof (MonoContextSimdReg)));
#else
amd64_movsd_reg_membase (code, i, AMD64_R11, fregs_offset + (i * sizeof (double)));
#endif
}
}
#endif
/*
* The context resides on the stack, in the stack frame of the
* caller of this function. The stack pointer that we need to
@ -467,7 +481,11 @@ get_throw_trampoline (MonoTrampInfo **info, gboolean rethrow, gboolean corlib, g
MonoJumpInfo *ji = NULL;
GSList *unwind_ops = NULL;
int i, stack_size, arg_offsets [16], ctx_offset, regs_offset;
#ifdef AMD64_CALLEE_SAVED_XREGS
const int kMaxCodeSize = 300;
#else
const int kMaxCodeSize = 256;
#endif
#ifdef TARGET_WIN32
const int dummy_stack_space = 6 * sizeof (target_mgreg_t); /* Windows expects stack space allocated for all 6 dummy args. */
@ -517,6 +535,20 @@ get_throw_trampoline (MonoTrampInfo **info, gboolean rethrow, gboolean corlib, g
/* Save IP */
amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, stack_size, sizeof (target_mgreg_t));
amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (AMD64_RIP * sizeof (target_mgreg_t)), AMD64_RAX, sizeof (target_mgreg_t));
#ifdef AMD64_CALLEE_SAVED_XREGS
int fregs_offset = ctx_offset + MONO_STRUCT_OFFSET (MonoContext, fregs);
for (i = 0; i < AMD64_XMM_NREG; ++i) {
if (AMD64_IS_CALLEE_SAVED_XREG (i)) {
#if defined(MONO_HAVE_SIMD_REG)
amd64_movdqu_membase_reg (code, AMD64_RSP, fregs_offset + (i * sizeof (MonoContextSimdReg)), i);
#else
amd64_movsd_membase_reg (code, AMD64_RSP, fregs_offset + (i * sizeof (double)), i);
#endif
}
}
#endif
/* Set arg1 == ctx */
amd64_lea_membase (code, AMD64_RAX, AMD64_RSP, ctx_offset);
amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [0], AMD64_RAX, sizeof (target_mgreg_t));
@ -617,6 +649,75 @@ mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
}
#endif /* !DISABLE_JIT */
#if defined(AMD64_CALLEE_SAVED_XREGS) && defined(TARGET_WIN32)
static gboolean
unwind_llvm_frame_win64 (
gpointer ip,
mono_unwind_reg_t *regs,
int nregs,
gboolean readonly_regs,
host_mgreg_t **save_locations,
int save_locations_len)
{
gboolean result = FALSE;
DWORD64 address = (DWORD64)ip;
DWORD64 image_base = 0;
UNWIND_HISTORY_TABLE entry = { 0 };
if (RtlLookupFunctionEntry(address, &image_base, &entry)) {
for (DWORD i = 0; i < entry.Count; i++) {
DWORD64 start = entry.Entry[i].ImageBase + entry.Entry[i].FunctionEntry->BeginAddress;
DWORD64 end = entry.Entry[i].ImageBase + entry.Entry[i].FunctionEntry->EndAddress;
DWORD64 start_offset = address - start;
UNWIND_INFO* unwind_info_address = (UNWIND_INFO*)(entry.Entry[i].ImageBase + entry.Entry[i].FunctionEntry->UnwindInfoAddress);
guint8 *fixed_stack_loc = NULL;
if (!unwind_info_address->FrameRegister) {
g_assert (nregs > AMD64_RSP);
fixed_stack_loc = (guint8 *)(regs [AMD64_RSP]);
} else {
g_assert (nregs > unwind_info_address->FrameRegister);
fixed_stack_loc = (guint8 *)((guint8 *)(regs [unwind_info_address->FrameRegister]) - (unwind_info_address->FrameOffset * 16));
}
g_assert (fixed_stack_loc);
if (start <= address && end >= address) {
for (guchar j = 0; j < unwind_info_address->CountOfCodes; j++) {
if (start_offset > unwind_info_address->UnwindCode[j].CodeOffset) {
if (unwind_info_address->UnwindCode[j].UnwindOp == UWOP_SAVE_XMM128 || unwind_info_address->UnwindCode[j].UnwindOp == UWOP_SAVE_XMM128_FAR) {
int reg = AMD64_NREG + unwind_info_address->UnwindCode[j].OpInfo;
guint8 *offset = fixed_stack_loc;
if (unwind_info_address->UnwindCode[j].UnwindOp == UWOP_SAVE_XMM128) {
g_assert (j + 1 < unwind_info_address->CountOfCodes);
offset = offset + unwind_info_address->UnwindCode[j+1].FrameOffset * 16;
j += 1;
}
if (unwind_info_address->UnwindCode[j].UnwindOp == UWOP_SAVE_XMM128_FAR) {
g_assert (j + 2 < unwind_info_address->CountOfCodes);
offset = offset + (((guint)(unwind_info_address->UnwindCode[j+1].FrameOffset << 16)) | ((guint)unwind_info_address->UnwindCode[j+2].FrameOffset));
j += 2;
}
if (!readonly_regs && regs && (reg < nregs))
regs [reg] = GUINT64_TO_HMREG (*(guint64*)(offset));
if (save_locations && (reg < save_locations_len) && !save_locations [reg])
save_locations [reg] = (host_mgreg_t *)offset;
}
}
}
}
}
result = TRUE;
}
return result;
}
#endif
/*
* mono_arch_unwind_frame:
*
@ -641,7 +742,14 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls,
*new_ctx = *ctx;
if (ji != NULL) {
host_mgreg_t regs [MONO_MAX_IREGS + 1];
#ifdef AMD64_CALLEE_SAVED_XREGS
host_mgreg_t *restored_regs [AMD64_NREG + AMD64_XMM_NREG];
#else
host_mgreg_t *restored_regs [AMD64_NREG];
#endif
const int restored_regs_len = G_N_ELEMENTS (restored_regs);
guint8 *cfa;
guint32 unwind_info_len;
guint8 *unwind_info;
@ -665,19 +773,34 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls,
if (ji->has_arch_eh_info)
epilog = (guint8*)ji->code_start + ji->code_size - mono_jinfo_get_epilog_size (ji);
for (i = 0; i < AMD64_NREG; ++i)
regs [i] = new_ctx->gregs [i];
gboolean success = mono_unwind_frame (unwind_info, unwind_info_len, (guint8 *)ji->code_start,
(guint8*)ji->code_start + ji->code_size,
(guint8 *)ip, epilog ? &epilog : NULL, regs, MONO_MAX_IREGS + 1,
save_locations, MONO_MAX_IREGS, &cfa);
(guint8 *)ip, epilog ? &epilog : NULL,
new_ctx->gregs, AMD64_NREG, TRUE,
restored_regs, restored_regs_len, &cfa);
#if defined(AMD64_CALLEE_SAVED_XREGS) && defined(TARGET_WIN32)
if (ji->from_llvm)
success &= unwind_llvm_frame_win64 ((guint8 *)ip, new_ctx->gregs, AMD64_NREG, TRUE, restored_regs, restored_regs_len);
#endif
if (!success)
return FALSE;
for (i = 0; i < AMD64_NREG; ++i)
new_ctx->gregs [i] = regs [i];
for (i = 0; i < AMD64_NREG; ++i) {
if (i < restored_regs_len && restored_regs [i])
new_ctx->gregs [i] = *(restored_regs [i]);
}
#ifdef AMD64_CALLEE_SAVED_XREGS
for (i = 0; i < AMD64_XMM_NREG; ++i) {
if (AMD64_NREG + i < restored_regs_len && restored_regs [AMD64_NREG + i])
memcpy (&(new_ctx->fregs [i]), restored_regs [AMD64_NREG + i], sizeof (new_ctx->fregs [0]));
}
#endif
if (save_locations)
memcpy (save_locations, restored_regs, MONO_MAX_IREGS * sizeof (restored_regs [0]));
/* The CFA becomes the new SP value */
new_ctx->gregs [AMD64_RSP] = (host_mgreg_t)(gsize)cfa;

View File

@ -499,7 +499,7 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls,
gboolean success = mono_unwind_frame (unwind_info, unwind_info_len, (guint8*)ji->code_start,
(guint8*)ji->code_start + ji->code_size,
(guint8*)ip, NULL, regs, MONO_MAX_IREGS + 8,
(guint8*)ip, NULL, regs, MONO_MAX_IREGS + 8, FALSE,
save_locations, MONO_MAX_IREGS, &cfa);
if (!success)

View File

@ -461,7 +461,7 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls,
gpointer ip = MINI_FTNPTR_TO_ADDR (MONO_CONTEXT_GET_IP (ctx));
gboolean success = mono_unwind_frame (unwind_info, unwind_info_len, (guint8*)ji->code_start,
(guint8*)ji->code_start + ji->code_size,
(guint8*)ip, NULL, regs, MONO_MAX_IREGS + 8,
(guint8*)ip, NULL, regs, MONO_MAX_IREGS + 8, FALSE,
save_locations, MONO_MAX_IREGS, (guint8**)&cfa);
if (!success)

View File

@ -583,7 +583,7 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls,
gboolean success = mono_unwind_frame (unwind_info, unwind_info_len, (guint8*)ji->code_start,
(guint8*)ji->code_start + ji->code_size,
(guint8*)ip, NULL, regs, ppc_lr + 1,
(guint8*)ip, NULL, regs, ppc_lr + 1, FALSE,
save_locations, MONO_MAX_IREGS, &cfa);
if (!success)

View File

@ -430,7 +430,7 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls, MonoJitInfo *ji,
gboolean success = mono_unwind_frame (unwind_info, unwind_info_len, (guint8 *)ji->code_start,
(guint8 *)ji->code_start + ji->code_size, (guint8 *)ip, NULL, regs,
MONO_MAX_IREGS + 12 + 1, save_locations, MONO_MAX_IREGS, (guint8 **)&cfa);
MONO_MAX_IREGS + 12 + 1, FALSE, save_locations, MONO_MAX_IREGS, (guint8 **)&cfa);
if (!success)
return FALSE;

View File

@ -534,7 +534,7 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls,
memcpy (&regs[16], &ctx->uc_mcontext.fpregs.fprs, 16 * sizeof(host_mgreg_t));
gboolean success = mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start,
(guint8 *) ji->code_start + ji->code_size,
ip, epilog ? &epilog : NULL, regs, 32, save_locations,
ip, epilog ? &epilog : NULL, regs, 32, FALSE, save_locations,
MONO_MAX_IREGS, &cfa);
if (!success)

View File

@ -839,7 +839,7 @@ mono_arch_unwind_frame (MonoJitTlsData *jit_tls,
gboolean success = mono_unwind_frame ((guint8*)unwind_info, unwind_info_len, (guint8*)ji->code_start,
(guint8*)ji->code_start + ji->code_size,
(guint8*)ip, NULL, regs, MONO_MAX_IREGS + 1,
(guint8*)ip, NULL, regs, MONO_MAX_IREGS + 1, FALSE,
save_locations, MONO_MAX_IREGS, &cfa);
if (!success)

View File

@ -1991,6 +1991,23 @@ mono_arch_allocate_vars (MonoCompile *cfg)
if (AMD64_IS_CALLEE_SAVED_REG (i) && (cfg->arch.saved_iregs & (1 << i))) {
offset += sizeof (target_mgreg_t);
}
#if defined(AMD64_CALLEE_SAVED_XREGS)
if (cfg->method->save_lmf) {
#if defined(MONO_HAVE_SIMD_REG)
int xreg_size = sizeof (MonoContextSimdReg);
#else
int xreg_size = sizeof (double);
#endif
offset = ALIGN_TO (offset, xreg_size);
for (guint i = 0; i < AMD64_XMM_NREG; ++i) {
if (AMD64_IS_CALLEE_SAVED_XREG (i)) {
offset += xreg_size;
}
}
}
#endif
if (!cfg->arch.omit_fp)
cfg->arch.reg_save_area_offset = -offset;
@ -8422,6 +8439,38 @@ MONO_RESTORE_WARNING
}
}
#if defined(AMD64_CALLEE_SAVED_XREGS)
if (method->save_lmf) {
#if defined(MONO_HAVE_SIMD_REG)
int xreg_size = sizeof (MonoContextSimdReg);
#else
int xreg_size = sizeof (double);
#endif
save_area_offset = ALIGN_TO (save_area_offset, xreg_size);
for (guint16 i = 0; i < AMD64_XMM_NREG; ++i) {
if (AMD64_IS_CALLEE_SAVED_XREG (i)) {
#if defined(MONO_HAVE_SIMD_REG)
amd64_movdqu_membase_reg (code, cfg->frame_reg, save_area_offset, i);
#else
amd64_movsd_membase_reg (code, cfg->frame_reg, save_area_offset, i);
#endif
if (cfg->arch.omit_fp) {
mono_emit_unwind_op_offset (cfg, code, AMD64_NREG + i, - (cfa_offset - save_area_offset));
/* These are handled automatically by the stack marking code */
mini_gc_set_slot_type_from_cfa (cfg, - (cfa_offset - save_area_offset), SLOT_NOREF);
} else {
mono_emit_unwind_op_offset (cfg, code, AMD64_NREG + i, - (-save_area_offset + (2 * 8)));
// FIXME: GC
}
save_area_offset += xreg_size;
async_exc_point (code);
}
}
}
#endif
/* store runtime generic context */
if (cfg->rgctx_var) {
g_assert (cfg->rgctx_var->opcode == OP_REGOFFSET &&

View File

@ -545,15 +545,7 @@ mono_amd64_get_exception_trampolines (gboolean aot);
int
mono_amd64_get_tls_gs_offset (void);
#if defined(TARGET_WIN32) && !defined(DISABLE_JIT)
#define MONO_ARCH_HAVE_UNWIND_TABLE 1
#define MONO_ARCH_HAVE_CODE_CHUNK_TRACKING 1
#ifdef ENABLE_CHECKED_BUILD
#define ENABLE_CHECKED_BUILD_UNWINDINFO
#endif
#if defined(TARGET_WIN32)
#define MONO_MAX_UNWIND_CODES 22
typedef enum _UNWIND_OP_CODES {
@ -563,7 +555,7 @@ typedef enum _UNWIND_OP_CODES {
UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */
UWOP_SAVE_XMM128 = 8, /* info == XMM reg number, offset in next slot */
UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
} UNWIND_CODE_OPS;
@ -593,6 +585,15 @@ typedef struct _UNWIND_INFO {
* OPTIONAL ULONG ExceptionData[]; */
} UNWIND_INFO, *PUNWIND_INFO;
#if !defined(DISABLE_JIT)
#define MONO_ARCH_HAVE_UNWIND_TABLE 1
#define MONO_ARCH_HAVE_CODE_CHUNK_TRACKING 1
#ifdef ENABLE_CHECKED_BUILD
#define ENABLE_CHECKED_BUILD_UNWINDINFO
#endif
static inline guint
mono_arch_unwindinfo_get_size (guchar code_count)
{
@ -634,7 +635,8 @@ mono_arch_code_chunk_new (void *chunk, int size);
void
mono_arch_code_chunk_destroy (void *chunk);
#endif /* defined(TARGET_WIN32) && !defined(DISABLE_JIT) */
#endif /* !defined(DISABLE_JIT) */
#endif /* defined(TARGET_WIN32) */
#ifdef MONO_ARCH_HAVE_UNWIND_TABLE
// Allocate additional size for max 3 unwind ops (push + fp or sp small|large) + unwind info struct trailing code buffer.

View File

@ -180,7 +180,7 @@ mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len);
gboolean
mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
guint8 *start_ip, guint8 *end_ip, guint8 *ip, guint8 **mark_locations,
mono_unwind_reg_t *regs, int nregs,
mono_unwind_reg_t *regs, int nregs, gboolean readonly_regs,
host_mgreg_t **save_locations, int save_locations_len,
guint8 **out_cfa);

View File

@ -46,8 +46,18 @@ static int unwind_info_size;
#define unwind_unlock() mono_os_mutex_unlock (&unwind_mutex)
#ifdef TARGET_AMD64
static int map_hw_reg_to_dwarf_reg [] = { 0, 2, 1, 3, 7, 6, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
static int map_hw_reg_to_dwarf_reg [] = {
0, 2, 1, 3, 7, 6, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16
#ifdef AMD64_CALLEE_SAVED_XREGS
, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
#endif
};
#ifdef AMD64_CALLEE_SAVED_XREGS
#define NUM_DWARF_REGS (AMD64_NREG + AMD64_XMM_NREG)
#else
#define NUM_DWARF_REGS AMD64_NREG
#endif
#define DWARF_DATA_ALIGN (-8)
#define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (AMD64_RIP))
#elif defined(TARGET_ARM)
@ -545,7 +555,7 @@ typedef struct {
gboolean
mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
guint8 *start_ip, guint8 *end_ip, guint8 *ip, guint8 **mark_locations,
mono_unwind_reg_t *regs, int nregs,
mono_unwind_reg_t *regs, int nregs, gboolean readonly_regs,
host_mgreg_t **save_locations, int save_locations_len,
guint8 **out_cfa)
{
@ -587,6 +597,7 @@ mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
reg_saved [hwreg] = TRUE;
locations [hwreg].loc_type = LOC_OFFSET;
locations [hwreg].offset = decode_uleb128 (p, &p) * DWARF_DATA_ALIGN;
break;
case 0: {
int ext_op = *p;
@ -694,14 +705,18 @@ mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
for (hwreg = 0; hwreg < NUM_HW_REGS; ++hwreg) {
if (reg_saved [hwreg] && locations [hwreg].loc_type == LOC_OFFSET) {
int dwarfreg = mono_hw_reg_to_dwarf_reg (hwreg);
if (hwreg >= nregs) {
if (!readonly_regs && hwreg >= nregs) {
mono_runtime_printf_err ("Unwind failure. Assertion at %s %d\n.", __FILE__, __LINE__);
return FALSE;
}
if (IS_DOUBLE_REG (dwarfreg))
regs [hwreg] = GUINT64_TO_HMREG (*(guint64*)(cfa_val + locations [hwreg].offset));
else
regs [hwreg] = *(host_mgreg_t*)(cfa_val + locations [hwreg].offset);
if (!readonly_regs) {
if (IS_DOUBLE_REG (dwarfreg))
regs [hwreg] = GUINT64_TO_HMREG (*(guint64*)(cfa_val + locations [hwreg].offset));
else
regs [hwreg] = *(host_mgreg_t*)(cfa_val + locations [hwreg].offset);
}
if (save_locations && hwreg < save_locations_len)
save_locations [hwreg] = (host_mgreg_t*)(cfa_val + locations [hwreg].offset);
}