// Find the code manager containing the given address, which might be a return address from a managed function. The // address may be to another managed function, or it may be to an unmanaged function, or it may be to a GC // hijack. The address may also refer to an EEType if we've been called from RhpGetClasslibFunction. If it is // a GC hijack, we will recognize that and use the real return address. static ICodeManager * FindCodeManagerRespectingReturnAddressHijacks(void * address) { RuntimeInstance * pRI = GetRuntimeInstance(); // Try looking up the code manager assuming the address is for code first. This is expected to be most common. ICodeManager * pCodeManager = pRI->FindCodeManagerByAddress(address); if (pCodeManager != NULL) return pCodeManager; // @TODO: CORERT: Do we need to make this work for CoreRT? // Less common, we will look for the address in any of the sections of the module. This is slower, but is // necessary for EEType pointers and jump stubs. Module * pModule = pRI->FindModuleByAddress(address); if (pModule != NULL) return pModule; // Corner-case: The thread might be hijacked -- @TODO: this is a bit brittle because there is no validation that // the hijacked return address from the thread is actually related to place where the caller got the hijack // target. Thread * pCurThread = ThreadStore::GetCurrentThread(); if (pCurThread->IsHijacked() && Thread::IsHijackTarget(address)) { ICodeManager * pCodeManagerForHijack = pRI->FindCodeManagerByAddress(pCurThread->GetHijackedReturnAddress()); ASSERT_MSG(pCodeManagerForHijack != NULL, "expected to find the module for a hijacked return address"); return pCodeManagerForHijack; } return NULL; }
// this generic type or method is not a duplicate - enter it into the hash table bool GenericUnificationHashtable::EnterDesc(GenericUnificationDesc *pDesc, UIntTarget *pIndirCells) { if (m_tableSize < m_entryCount) { if (!GrowTable(m_entryCount)) return false; } m_entryCount++; UInt32 hashCode = pDesc->m_hashCode & m_hashMask; Entry *pEntry = new (nothrow) Entry(m_table[hashCode], pDesc, pIndirCells); if (pEntry == nullptr) return false; m_table[hashCode] = pEntry; if (pDesc->m_flags & GUF_GC_STATICS) { UInt32 indirCellIndex = pDesc->GetIndirCellIndex(GUF_GC_STATICS); UInt8 *pGcStaticData = (UInt8 *)pIndirCells[indirCellIndex]; StaticGcDesc *pGcStaticsDesc = (StaticGcDesc *)pIndirCells[indirCellIndex + 1]; if (!GetRuntimeInstance()->AddDynamicGcStatics(pGcStaticData, pGcStaticsDesc)) return false; } if (pDesc->m_flags & GUF_THREAD_STATICS) { UInt32 indirCellIndex = pDesc->GetIndirCellIndex(GUF_THREAD_STATICS); UInt32 tlsIndex = *(UInt32 *)pIndirCells[indirCellIndex]; UInt32 tlsOffset = (UInt32)pIndirCells[indirCellIndex + 1]; StaticGcDesc *pGcStaticsDesc = (StaticGcDesc *)pIndirCells[indirCellIndex + 2]; if (!GetRuntimeInstance()->AddDynamicThreadStaticGcData(tlsIndex, tlsOffset, pGcStaticsDesc)) return false; } return true; }
// Find the code manager containing the given address, which might be a return address from a managed function. The // address may be to another managed function, or it may be to an unmanaged function. The address may also refer to // an EEType. static ICodeManager * FindCodeManagerForClasslibFunction(void * address) { RuntimeInstance * pRI = GetRuntimeInstance(); // Try looking up the code manager assuming the address is for code first. This is expected to be most common. ICodeManager * pCodeManager = pRI->FindCodeManagerByAddress(address); if (pCodeManager != NULL) return pCodeManager; // @TODO: CORERT: Do we need to make this work for CoreRT? // Less common, we will look for the address in any of the sections of the module. This is slower, but is // necessary for EEType pointers and jump stubs. Module * pModule = pRI->FindModuleByAddress(address); if (pModule != NULL) return pModule; ASSERT_MSG(!Thread::IsHijackTarget(address), "not expected to be called with hijacked return address"); return NULL; }
// Find the module containing the given address, which is a return address from a managed function. The // address may be to another managed function, or it may be to an unmanaged function, or it may be to a GC // hijack. The address may also refer to an EEType if we've been called from RhpGetClasslibFunction. If it is // a GC hijack, we will recgonize that and use the real return address, updating the address passed in. static Module * FindModuleRespectingReturnAddressHijacks(void ** pAddress) { RuntimeInstance * pRI = GetRuntimeInstance(); // Try looking up the module assuming the address is for code first. Fall back to a read-only data looukp // if that fails. If we have data indicating that the data case is more common then we can reverse the // order of checks. Finally check read/write data: generic EETypes live there since they need to be fixed // up at runtime to support unification. Module * pModule = pRI->FindModuleByCodeAddress(*pAddress); if (pModule == NULL) { pModule = pRI->FindModuleByReadOnlyDataAddress(*pAddress); if (pModule == NULL) pModule = pRI->FindModuleByDataAddress(*pAddress); if (pModule == NULL) { // Hmmm... we didn't find a managed module for the given PC. We have a return address in unmanaged // code, but it could be because the thread is hijacked for GC suspension. If it is then we should // get the real return address and try again. Thread * pCurThread = ThreadStore::GetCurrentThread(); if (!pCurThread->IsHijacked()) { // The PC isn't in a managed module, and there is no hijack in place, so we have no EH info. return NULL; } // Update the PC passed in to reflect the correct return address. *pAddress = pCurThread->GetHijackedReturnAddress(); pModule = pRI->FindModuleByCodeAddress(*pAddress); } } return pModule; }
//--------------------------------------------------------------------------------------- // // Sends a raw managed debug event to the debugger. // // Arguments: // pPayload - managed debug event data // // // Notes: // The entire process will get frozen by the debugger once we send. The debugger // needs to resume the process. It may detach as well. // See CordbProcess::DecodeEvent in mscordbi for decoding this event. These methods must stay in sync. // //--------------------------------------------------------------------------------------- void DebugEventSource::SendRawEvent(DebugEventPayload* pPayload) { #ifdef _MSC_VER // We get to send an array of void* as data with the notification. // The debugger can then use ReadProcessMemory to read through this array. UInt64 rgData [] = { (UInt64) CLRDBG_EXCEPTION_DATA_CHECKSUM, (UInt64) GetRuntimeInstance()->GetPalInstance(), (UInt64) pPayload }; // // Physically send the event via an OS Exception. We're using exceptions as a notification // mechanism on top of the OS native debugging pipeline. // __try { const UInt32 dwFlags = 0; // continuable (eg, Debugger can continue GH) // RaiseException treats arguments as pointer sized values, but we encoded 3 QWORDS. // On 32 bit platforms we have 6 elements, on 64 bit platforms we have 3 elements RaiseException(CLRDBG_NOTIFICATION_EXCEPTION_CODE, dwFlags, 3*sizeof(UInt64)/sizeof(UInt32*), (UInt32*)rgData); // If debugger continues "GH" (DBG_CONTINUE), then we land here. // This is the expected path for a well-behaved ICorDebug debugger. } __except(1) { // We can get here if: // An ICorDebug aware debugger enabled the debug events AND // a) the debugger detached during the event OR // b) the debugger continues "GN" (DBG_EXCEPTION_NOT_HANDLED) - this would be considered a badly written debugger // // there is no great harm in reaching here but it is a needless perf-cost } #endif // _MSC_VER }
ThreadStore * GetThreadStore() { return GetRuntimeInstance()->GetThreadStore(); }
void EnumAllStaticGCRefs(EnumGcRefCallbackFunc * fn, EnumGcRefScanContext * sc) { GetRuntimeInstance()->EnumAllStaticGCRefs(fn, sc); }
void EnumAllStaticGCRefs(EnumGcRefCallbackFunc * fn, EnumGcRefScanContext * sc) { GetRuntimeInstance()->EnumAllStaticGCRefs(reinterpret_cast<void*>(fn), sc); }
void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CONTEXT pCtx, UInt32 dwFlags) { ASSERT((dwFlags & MethodStateCalculated) == 0); m_pThread = pThreadToWalk; m_pInstance = GetRuntimeInstance(); m_ControlPC = 0; m_pCodeManager = NULL; m_pHijackedReturnValue = NULL; m_HijackedReturnValueKind = GCRK_Unknown; m_pConservativeStackRangeLowerBound = NULL; m_pConservativeStackRangeUpperBound = NULL; m_dwFlags = dwFlags; m_pNextExInfo = pThreadToWalk->GetCurExInfo(); // We need to walk the ExInfo chain in parallel with the stackwalk so that we know when we cross over // exception throw points. So we must find our initial point in the ExInfo chain here so that we can // properly walk it in parallel. ResetNextExInfoForSP(pCtx->GetSp()); PTR_VOID ControlPC = dac_cast<PTR_VOID>(pCtx->GetIp()); if (dwFlags & ApplyReturnAddressAdjustment) ControlPC = AdjustReturnAddressBackward(ControlPC); // If our control PC indicates that we're in one of the thunks we use to make managed callouts from the // runtime we need to adjust the frame state to that of the managed method that previously called into the // runtime (i.e. skip the intervening unmanaged frames). HandleManagedCalloutThunk(ControlPC, pCtx->GetFp()); // This codepath is used by the hijack stackwalk and we can get arbitrary ControlPCs from there. If this // context has a non-managed control PC, then we're done. if (!m_pInstance->FindCodeManagerByAddress(ControlPC)) return; // // control state // m_ControlPC = ControlPC; m_RegDisplay.SP = pCtx->GetSp(); m_RegDisplay.IP = pCtx->GetIp(); m_RegDisplay.pIP = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, IP); #ifdef TARGET_ARM // // preserved regs // m_RegDisplay.pR4 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R4); m_RegDisplay.pR5 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R5); m_RegDisplay.pR6 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R6); m_RegDisplay.pR7 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R7); m_RegDisplay.pR8 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R8); m_RegDisplay.pR9 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R9); m_RegDisplay.pR10 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R10); m_RegDisplay.pR11 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R11); m_RegDisplay.pLR = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, LR); // // preserved vfp regs // for (Int32 i = 0; i < 16 - 8; i++) { m_RegDisplay.D[i] = pCtx->D[i]; } // // scratch regs // m_RegDisplay.pR0 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R0); #else // TARGET_ARM // // preserved regs // m_RegDisplay.pRbp = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rbp); m_RegDisplay.pRsi = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rsi); m_RegDisplay.pRdi = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rdi); m_RegDisplay.pRbx = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rbx); #ifdef TARGET_AMD64 m_RegDisplay.pR12 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R12); m_RegDisplay.pR13 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R13); m_RegDisplay.pR14 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R14); m_RegDisplay.pR15 = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, R15); // // preserved xmm regs // memcpy(m_RegDisplay.Xmm, &pCtx->Xmm6, sizeof(m_RegDisplay.Xmm)); #endif // TARGET_AMD64 // // scratch regs // m_RegDisplay.pRax = PTR_TO_MEMBER(PAL_LIMITED_CONTEXT, pCtx, Rax); m_RegDisplay.pRcx = NULL; m_RegDisplay.pRdx = NULL; #ifdef TARGET_AMD64 m_RegDisplay.pR8 = NULL; m_RegDisplay.pR9 = NULL; m_RegDisplay.pR10 = NULL; m_RegDisplay.pR11 = NULL; #endif // TARGET_AMD64 #endif // TARGET_ARM }
void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PInvokeTransitionFrame pFrame) { m_pThread = pThreadToWalk; m_pInstance = GetRuntimeInstance(); m_pCodeManager = NULL; m_pHijackedReturnValue = NULL; m_HijackedReturnValueKind = GCRK_Unknown; m_pConservativeStackRangeLowerBound = NULL; m_pConservativeStackRangeUpperBound = NULL; m_dwFlags = CollapseFunclets | RemapHardwareFaultsToSafePoint; // options for GC stack walk m_pNextExInfo = pThreadToWalk->GetCurExInfo(); if (pFrame == TOP_OF_STACK_MARKER) { m_ControlPC = 0; return; } memset(&m_RegDisplay, 0, sizeof(m_RegDisplay)); // We need to walk the ExInfo chain in parallel with the stackwalk so that we know when we cross over // exception throw points. So we must find our initial point in the ExInfo chain here so that we can // properly walk it in parallel. ResetNextExInfoForSP((UIntNative)dac_cast<TADDR>(pFrame)); m_RegDisplay.SetIP((PCODE)pFrame->m_RIP); m_RegDisplay.SetAddrOfIP((PTR_PCODE)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP)); PTR_UIntNative pPreservedRegsCursor = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_PreservedRegs); #ifdef TARGET_ARM m_RegDisplay.pLR = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP); m_RegDisplay.pR11 = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_ChainPointer); if (pFrame->m_dwFlags & PTFF_SAVE_R4) { m_RegDisplay.pR4 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R5) { m_RegDisplay.pR5 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R6) { m_RegDisplay.pR6 = pPreservedRegsCursor++; } ASSERT(!(pFrame->m_dwFlags & PTFF_SAVE_R7)); // R7 should never contain a GC ref because we require // a frame pointer for methods with pinvokes if (pFrame->m_dwFlags & PTFF_SAVE_R8) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R9) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_SP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } m_RegDisplay.pR7 = (PTR_UIntNative) PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_FramePointer); if (pFrame->m_dwFlags & PTFF_SAVE_R0) { m_RegDisplay.pR0 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R1) { m_RegDisplay.pR1 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R2) { m_RegDisplay.pR2 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R3) { m_RegDisplay.pR3 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_R0_IS_GCREF) { m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pR0; m_HijackedReturnValueKind = GCRK_Object; } if (pFrame->m_dwFlags & PTFF_R0_IS_BYREF) { m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pR0; m_HijackedReturnValueKind = GCRK_Byref; } m_ControlPC = dac_cast<PTR_VOID>(*(m_RegDisplay.pIP)); #else // TARGET_ARM if (pFrame->m_dwFlags & PTFF_SAVE_RBX) { m_RegDisplay.pRbx = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_RSI) { m_RegDisplay.pRsi = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_RDI) { m_RegDisplay.pRdi = pPreservedRegsCursor++; } ASSERT(!(pFrame->m_dwFlags & PTFF_SAVE_RBP)); // RBP should never contain a GC ref because we require // a frame pointer for methods with pinvokes #ifdef TARGET_AMD64 if (pFrame->m_dwFlags & PTFF_SAVE_R12) { m_RegDisplay.pR12 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R13) { m_RegDisplay.pR13 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R14) { m_RegDisplay.pR14 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R15) { m_RegDisplay.pR15 = pPreservedRegsCursor++; } #endif // TARGET_AMD64 m_RegDisplay.pRbp = (PTR_UIntNative) PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_FramePointer); if (pFrame->m_dwFlags & PTFF_SAVE_RSP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_RAX) { m_RegDisplay.pRax = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_RCX) { m_RegDisplay.pRcx = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_RDX) { m_RegDisplay.pRdx = pPreservedRegsCursor++; } #ifdef TARGET_AMD64 if (pFrame->m_dwFlags & PTFF_SAVE_R8 ) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R9 ) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } if (pFrame->m_dwFlags & PTFF_SAVE_R11) { m_RegDisplay.pR11 = pPreservedRegsCursor++; } #endif // TARGET_AMD64 if (pFrame->m_dwFlags & PTFF_RAX_IS_GCREF) { m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pRax; m_HijackedReturnValueKind = GCRK_Object; } if (pFrame->m_dwFlags & PTFF_RAX_IS_BYREF) { m_pHijackedReturnValue = (PTR_RtuObjectRef) m_RegDisplay.pRax; m_HijackedReturnValueKind = GCRK_Byref; } m_ControlPC = dac_cast<PTR_VOID>(*(m_RegDisplay.pIP)); #endif // TARGET_ARM // @TODO: currently, we always save all registers -- how do we handle the onese we don't save once we // start only saving those that weren't already saved? // If our control PC indicates that we're in one of the thunks we use to make managed callouts from the // runtime we need to adjust the frame state to that of the managed method that previously called into the // runtime (i.e. skip the intervening unmanaged frames). HandleManagedCalloutThunk(); STRESS_LOG1(LF_STACKWALK, LL_INFO10000, " %p\n", m_ControlPC); }