diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index df637a400259e7..00e6673561eb2e 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -56,11 +56,9 @@ endif(CLR_CMAKE_HOST_WIN32) if (NOT (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_UNIX)) add_compile_definitions(EnC_SUPPORTED) endif() -if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM64) - if(CLR_CMAKE_TARGET_WIN32) - add_compile_definitions(FEATURE_ENC_SUPPORTED) - endif(CLR_CMAKE_TARGET_WIN32) -endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM64) +if(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_WIN32)) + add_compile_definitions(FEATURE_ENC_SUPPORTED) +endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR (CLR_CMAKE_TARGET_ARCH_I386 AND CLR_CMAKE_TARGET_WIN32)) if(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64) add_compile_definitions(OUT_OF_PROCESS_SETTHREADCONTEXT) diff --git a/src/coreclr/debug/di/rsthread.cpp b/src/coreclr/debug/di/rsthread.cpp index 5edd2dbd36aa14..4cc387235910c9 100644 --- a/src/coreclr/debug/di/rsthread.cpp +++ b/src/coreclr/debug/di/rsthread.cpp @@ -2135,11 +2135,6 @@ HRESULT CordbThread::InterceptCurrentException(ICorDebugFrame * pFrame) FAIL_IF_NEUTERED(this); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); -#if defined(FEATURE_DBGIPC_TRANSPORT_DI) - // Continuable exceptions are not implemented on Unix-like platforms. - return E_NOTIMPL; - -#else // !FEATURE_DBGIPC_TRANSPORT_DI HRESULT hr = S_OK; EX_TRY { @@ -2216,7 +2211,6 @@ HRESULT CordbThread::InterceptCurrentException(ICorDebugFrame * pFrame) } EX_CATCH_HRESULT(hr); return hr; -#endif // FEATURE_DBGIPC_TRANSPORT_DI } //--------------------------------------------------------------------------------------- diff --git a/src/coreclr/inc/eetwain.h b/src/coreclr/inc/eetwain.h index bb5bf893b99ee7..91bb4c85c85a4b 100644 --- a/src/coreclr/inc/eetwain.h +++ b/src/coreclr/inc/eetwain.h @@ -332,7 +332,7 @@ virtual void LeaveCatch(GCInfoToken gcInfoToken, PCONTEXT pCtx)=0; #endif // FEATURE_EH_FUNCLETS -#ifdef EnC_SUPPORTED +#ifdef FEATURE_ENC_SUPPORTED /* Last chance for the runtime support to do fixups in the context @@ -347,7 +347,7 @@ virtual HRESULT FixContextForEnC(PCONTEXT pCtx, const ICorDebugInfo::NativeVarInfo * newMethodVars, SIZE_T newMethodVarsCount) = 0; -#endif // EnC_SUPPORTED +#endif // FEATURE_ENC_SUPPORTED #endif // #ifndef DACCESS_COMPILE @@ -597,7 +597,7 @@ virtual void LeaveCatch(GCInfoToken gcInfoToken, PCONTEXT pCtx); #endif // FEATURE_EH_FUNCLETS -#ifdef EnC_SUPPORTED +#ifdef FEATURE_ENC_SUPPORTED /* Last chance for the runtime support to do fixups in the context before execution continues inside an EnC updated function. @@ -610,7 +610,7 @@ HRESULT FixContextForEnC(PCONTEXT pCtx, EECodeInfo * pNewCodeInfo, const ICorDebugInfo::NativeVarInfo * newMethodVars, SIZE_T newMethodVarsCount); -#endif // EnC_SUPPORTED +#endif // FEATURE_ENC_SUPPORTED #endif // #ifndef DACCESS_COMPILE diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 23c79087d4f8c6..5847b40ffd32e2 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -8910,7 +8910,7 @@ void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize // -saved bool for synchronized methods // slots for ret address + FP + EnC callee-saves - int preservedAreaSize = (2 + genCountBits(RBM_ENC_CALLEE_SAVED)) * REGSIZE_BYTES; + int preservedAreaSize = (2 + genCountBits((uint64_t)RBM_ENC_CALLEE_SAVED)) * REGSIZE_BYTES; if (compiler->info.compFlags & CORINFO_FLG_SYNCH) { diff --git a/src/coreclr/jit/targetamd64.h b/src/coreclr/jit/targetamd64.h index 6d3f92fb6da901..4abe71984b57cf 100644 --- a/src/coreclr/jit/targetamd64.h +++ b/src/coreclr/jit/targetamd64.h @@ -303,6 +303,9 @@ /* NOTE: Sync with variable name defined in compiler.h */ #define REG_CALLEE_SAVED_ORDER REG_EBX,REG_ETW_FRAMED_EBP_LIST REG_R12,REG_R13,REG_R14,REG_R15 #define RBM_CALLEE_SAVED_ORDER RBM_EBX,RBM_ETW_FRAMED_EBP_LIST RBM_R12,RBM_R13,RBM_R14,RBM_R15 + + // For SysV we have more volatile registers so we do not save any callee saves for EnC. + #define RBM_ENC_CALLEE_SAVED 0 #else // !UNIX_AMD64_ABI #define CNT_CALLEE_SAVED (7 + REG_ETW_FRAMED_EBP_COUNT) #define CNT_CALLEE_TRASH (7) @@ -314,6 +317,9 @@ /* NOTE: Sync with variable name defined in compiler.h */ #define REG_CALLEE_SAVED_ORDER REG_EBX,REG_ESI,REG_EDI,REG_ETW_FRAMED_EBP_LIST REG_R12,REG_R13,REG_R14,REG_R15 #define RBM_CALLEE_SAVED_ORDER RBM_EBX,RBM_ESI,RBM_EDI,RBM_ETW_FRAMED_EBP_LIST RBM_R12,RBM_R13,RBM_R14,RBM_R15 + + // Callee-preserved registers we always save and allow use of for EnC code, since there are quite few volatile registers. + #define RBM_ENC_CALLEE_SAVED (RBM_RSI | RBM_RDI) #endif // !UNIX_AMD64_ABI #define CNT_CALLEE_TRASH_FLOAT get_CNT_CALLEE_TRASH_FLOAT() @@ -327,9 +333,6 @@ #define CALLEE_SAVED_REG_MAXSZ (CNT_CALLEE_SAVED*REGSIZE_BYTES) #define CALLEE_SAVED_FLOAT_MAXSZ (CNT_CALLEE_SAVED_FLOAT*16) - // callee-preserved registers we always save and allow use of for EnC code - #define RBM_ENC_CALLEE_SAVED (RBM_RSI | RBM_RDI) - // register to hold shift amount #define REG_SHIFT REG_ECX #define RBM_SHIFT RBM_ECX diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index d5541621f1fd2e..e691ee5e06d352 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -4490,6 +4490,7 @@ struct PAL_SEHException ExceptionPointers.ExceptionRecord = ex.ExceptionPointers.ExceptionRecord; ExceptionPointers.ContextRecord = ex.ExceptionPointers.ContextRecord; TargetFrameSp = ex.TargetFrameSp; + TargetIp = ex.TargetIp; RecordsOnStack = ex.RecordsOnStack; IsExternal = ex.IsExternal; ManagedToNativeExceptionCallback = ex.ManagedToNativeExceptionCallback; @@ -4512,6 +4513,8 @@ struct PAL_SEHException EXCEPTION_POINTERS ExceptionPointers; // Target frame stack pointer set before the 2nd pass. SIZE_T TargetFrameSp; + SIZE_T TargetIp; + SIZE_T ReturnValue; bool RecordsOnStack; // The exception is a hardware exception coming from a native code out of // the well known runtime helpers @@ -4525,6 +4528,7 @@ struct PAL_SEHException ExceptionPointers.ExceptionRecord = pExceptionRecord; ExceptionPointers.ContextRecord = pContextRecord; TargetFrameSp = NoTargetFrameSp; + TargetIp = 0; RecordsOnStack = onStack; IsExternal = false; ManagedToNativeExceptionCallback = NULL; @@ -4564,6 +4568,7 @@ struct PAL_SEHException ExceptionPointers.ExceptionRecord = NULL; ExceptionPointers.ContextRecord = NULL; TargetFrameSp = NoTargetFrameSp; + TargetIp = 0; RecordsOnStack = false; IsExternal = false; ManagedToNativeExceptionCallback = NULL; diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index 1e16cb811bc6f9..bb06173bca5237 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -792,7 +792,7 @@ bool VarIsInReg(ICorDebugInfo::VarLoc varLoc) } } -#ifdef EnC_SUPPORTED +#ifdef FEATURE_ENC_SUPPORTED /***************************************************************************** * Last chance for the runtime support to do fixups in the context * before execution continues inside an EnC updated function. @@ -831,7 +831,6 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx, T_CONTEXT oldCtx = *pCtx; #if defined(TARGET_X86) - LOG((LF_CORDB, LL_INFO100, "EECM::FixContextForEnC\n")); /* Extract the necessary information from the info block header */ @@ -997,22 +996,29 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx, _ASSERTE(pOldCodeInfo->HasFrameRegister()); _ASSERTE(pNewCodeInfo->HasFrameRegister()); - LOG((LF_CORDB, LL_INFO100, "EECM::FixContextForEnC: Old and new fixed stack sizes are %u and %u\n", oldFixedStackSize, newFixedStackSize)); - - // x64: SP == FP before localloc - if (oldStackBase != GetFP(&oldCtx)) - return E_FAIL; #elif defined(TARGET_ARM64) DWORD oldFixedStackSize = oldGcDecoder.GetSizeOfEditAndContinueFixedStackFrame(); DWORD newFixedStackSize = newGcDecoder.GetSizeOfEditAndContinueFixedStackFrame(); +#else + PORTABILITY_ASSERT("Edit-and-continue not enabled on this platform."); +#endif LOG((LF_CORDB, LL_INFO100, "EECM::FixContextForEnC: Old and new fixed stack sizes are %u and %u\n", oldFixedStackSize, newFixedStackSize)); - // ARM64: FP + 16 == SP + oldFixedStackSize before localloc - if (GetFP(&oldCtx) + 16 != oldStackBase + oldFixedStackSize) +#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS) + // win-x64: SP == FP before localloc + if (oldStackBase != GetFP(&oldCtx)) + { return E_FAIL; + } #else - PORTABILITY_ASSERT("Edit-and-continue not enabled on this platform."); + // All other 64-bit targets use frame chaining with the FP stored right below the + // return address (LR is always pushed on arm64). FP + 16 == SP + oldFixedStackSize + // gives the caller's SP before stack alloc. + if (GetFP(&oldCtx) + 16 != oldStackBase + oldFixedStackSize) + { + return E_FAIL; + } #endif // EnC remap inside handlers is not supported @@ -1028,14 +1034,15 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx, TADDR callerSP = oldStackBase + oldFixedStackSize; #ifdef _DEBUG - // If the old method has a PSPSym, then its value should == FP for x64 and callerSP for arm64 + // If the old method has a PSPSym, then its value should == initial-SP (i.e. + // oldStackBase) for x64 and callerSP for arm64 INT32 nOldPspSymStackSlot = oldGcDecoder.GetPSPSymStackSlot(); if (nOldPspSymStackSlot != NO_PSP_SYM) { #if defined(TARGET_AMD64) TADDR oldPSP = *PTR_TADDR(oldStackBase + nOldPspSymStackSlot); - _ASSERTE(oldPSP == GetFP(&oldCtx)); -#elif defined(TARGET_ARM64) + _ASSERTE(oldPSP == oldStackBase); +#else TADDR oldPSP = *PTR_TADDR(callerSP + nOldPspSymStackSlot); _ASSERTE(oldPSP == callerSP); #endif @@ -1222,8 +1229,7 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx, #if defined(TARGET_X86) // Zero out all the registers as some may hold new variables. - pCtx->Eax = pCtx->Ecx = pCtx->Edx = pCtx->Ebx = - pCtx->Esi = pCtx->Edi = 0; + pCtx->Eax = pCtx->Ecx = pCtx->Edx = pCtx->Ebx = pCtx->Esi = pCtx->Edi = 0; // 3) zero out the stack frame - this'll initialize _all_ variables @@ -1240,7 +1246,7 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx, _ASSERTE( frameHeaderSize <= oldInfo.stackSize ); _ASSERTE( GetSizeOfFrameHeaderForEnC( &oldInfo ) == frameHeaderSize ); -#elif defined(TARGET_AMD64) +#elif defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI) // Next few statements zero out all registers that may end up holding new variables. @@ -1276,7 +1282,8 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx, // For EnC-compliant x64 code, FP == SP. Since SP changed above, update FP now pCtx->Rbp = newStackBase; -#elif defined(TARGET_ARM64) +#else +#if defined(TARGET_ARM64) // Zero out volatile part of stack frame // x0-x17 memset(&pCtx->X[0], 0, sizeof(pCtx->X[0]) * 18); @@ -1284,6 +1291,15 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx, memset(&pCtx->V[0], 0, sizeof(pCtx->V[0]) * 8); // v16-v31 memset(&pCtx->V[16], 0, sizeof(pCtx->V[0]) * 16); +#elif defined(TARGET_AMD64) + // SysV ABI + pCtx->Rax = pCtx->Rdi = pCtx->Rsi = pCtx->Rdx = pCtx->Rcx = pCtx->R8 = pCtx->R9 = 0; + + // volatile float registers + memset(&pCtx->Xmm0, 0, sizeof(pCtx->Xmm0) * 16); +#else + PORTABILITY_ASSERT("Edit-and-continue not enabled on this platform."); +#endif TADDR newStackBase = callerSP - newFixedStackSize; @@ -1293,12 +1309,9 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx, _ASSERTE(frameHeaderSize <= oldFixedStackSize); _ASSERTE(frameHeaderSize <= newFixedStackSize); - // EnC prolog saves only fp,lr and does it at sp-16. It should already - // be set up from previous version. + // EnC prolog saves only FP (and LR on arm64), and FP points to saved FP for frame chaining. + // These should already be set up from previous version. _ASSERTE(GetFP(pCtx) == callerSP - 16); - -#else // !X86 - PORTABILITY_ASSERT("Edit-and-continue not enabled on this platform."); #endif // Perform some debug-only sanity checks on stack variables. Some checks are @@ -1416,7 +1429,7 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx, if (nNewPspSymStackSlot != NO_PSP_SYM) { #if defined(TARGET_AMD64) - *PTR_TADDR(newStackBase + nNewPspSymStackSlot) = GetFP(pCtx); + *PTR_TADDR(newStackBase + nNewPspSymStackSlot) = newStackBase; #elif defined(TARGET_ARM64) *PTR_TADDR(callerSP + nNewPspSymStackSlot) = callerSP; #else diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp index 56500271314b57..1dcfb8bf091f4c 100644 --- a/src/coreclr/vm/encee.cpp +++ b/src/coreclr/vm/encee.cpp @@ -733,9 +733,11 @@ HRESULT EditAndContinueModule::ResumeInUpdatedFunction( // Win32 handlers on the stack so cannot ever return from this function. EEPOLICY_HANDLE_FATAL_ERROR(CORDBG_E_ENC_INTERNAL_ERROR); return E_FAIL; -#endif // #if defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) +#endif // #if defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) + } +#ifdef FEATURE_ENC_SUPPORTED //--------------------------------------------------------------------------------------- // // FixContextAndResume - Modify the thread context for EnC remap and resume execution @@ -871,6 +873,8 @@ NOINLINE void EditAndContinueModule::FixContextAndResume( LOG((LF_ENC, LL_ERROR, "**Error** EnCModule::ResumeInUpdatedFunction returned from ResumeAtJit")); _ASSERTE(!"Should not return from ResumeAtJit()"); } +#endif // #ifdef FEATURE_ENC_SUPPORTED + #endif // #ifndef DACCESS_COMPILE //--------------------------------------------------------------------------------------- diff --git a/src/coreclr/vm/encee.h b/src/coreclr/vm/encee.h index ae3ed4a0158be7..0fe0a9d8d8229b 100644 --- a/src/coreclr/vm/encee.h +++ b/src/coreclr/vm/encee.h @@ -247,12 +247,14 @@ class EditAndContinueModule : public Module SIZE_T newILOffset, T_CONTEXT *pContext); +#ifdef FEATURE_ENC_SUPPORTED // Modify the thread context for EnC remap and resume execution void FixContextAndResume(MethodDesc *pMD, void *oldDebuggerFuncHandle, T_CONTEXT *pContext, EECodeInfo *pOldCodeInfo, EECodeInfo *pNewCodeInfo); +#endif // FEATURE_ENC_SUPPORTED // Get a pointer to the value of a field added by EnC or return NULL if it doesn't exist PTR_CBYTE ResolveField(OBJECTREF thisPointer, diff --git a/src/coreclr/vm/excep.h b/src/coreclr/vm/excep.h index 9030331474c73e..082ba7b91a4c9c 100644 --- a/src/coreclr/vm/excep.h +++ b/src/coreclr/vm/excep.h @@ -710,10 +710,9 @@ bool IsInterceptableException(Thread *pThread); // perform simple checking to see if the current exception is intercepted bool CheckThreadExceptionStateForInterception(); -#ifndef TARGET_UNIX -// Currently, only Windows supports ClrUnwindEx (used inside ClrDebuggerDoUnwindAndIntercept) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_X86) #define DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED -#endif // !TARGET_UNIX +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_X86) #ifdef DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED // Intercept the current exception and start an unwind. This function may never return. diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index cec1175c9c022e..0621ae03efe4f4 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -46,6 +46,10 @@ ClrUnwindEx(EXCEPTION_RECORD* pExceptionRecord, UINT_PTR TargetFrameSp); #endif // !TARGET_UNIX +#if defined(TARGET_UNIX) && !defined(DACCESS_COMPILE) +VOID UnwindManagedExceptionPass2(PAL_SEHException& ex, CONTEXT* unwindStartContext); +#endif // TARGET_UNIX && !DACCESS_COMPILE + #ifdef USE_CURRENT_CONTEXT_IN_FILTER inline void CaptureNonvolatileRegisters(PKNONVOLATILE_CONTEXT pNonvolatileContext, PCONTEXT pContext) { @@ -4210,7 +4214,16 @@ EXCEPTION_DISPOSITION ClrDebuggerDoUnwindAndIntercept(X86_FIRST_ARG(EXCEPTION_RE (PBYTE*)&uInterceptStackFrame, NULL, NULL); +#ifdef TARGET_UNIX + CONTEXT *pContext = pThread->GetExceptionState()->GetContextRecord(); + PAL_SEHException ex(pThread->GetExceptionState()->GetExceptionRecord(), pContext, /* onStack */ false); + ex.TargetIp = INVALID_RESUME_ADDRESS; + ex.TargetFrameSp = uInterceptStackFrame; + ex.ReturnValue = (UINT_PTR)pThread; + UnwindManagedExceptionPass2(ex, pContext); +#else // TARGET_UNIX ClrUnwindEx(pExceptionRecord, (UINT_PTR)pThread, INVALID_RESUME_ADDRESS, uInterceptStackFrame); +#endif // TARGET_UNIX UNREACHABLE(); } @@ -4554,6 +4567,29 @@ VOID UnwindManagedExceptionPass2(PAL_SEHException& ex, CONTEXT* unwindStartConte } while (Thread::IsAddressInCurrentStack(sp) && (establisherFrame != ex.TargetFrameSp)); +#ifdef DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED + if ((establisherFrame == ex.TargetFrameSp) && (ex.TargetIp != 0)) + { +#ifdef TARGET_AMD64 +#define RETURN_REG Rax +#elif defined(TARGET_ARM64) +#define RETURN_REG X0 +#elif defined(TARGET_ARM) +#define RETURN_REG R0 +#elif defined(TARGET_X86) +#define RETURN_REG Eax +#else +#error Missing definition of RETURN_REG for the current architecture +#endif + currentFrameContext->RETURN_REG = ex.ReturnValue; + if (ex.GetExceptionRecord()->ExceptionCode != STATUS_UNWIND_CONSOLIDATE) + { + SetIP(currentFrameContext, ex.TargetIp); + } + ExceptionTracker::ResumeExecution(currentFrameContext); + } +#endif // DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED + _ASSERTE(!"UnwindManagedExceptionPass2: Unwinding failed. Reached the end of the stack"); EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); }