From 4d92682e9c664f45b119a24da5e1865cf75cc368 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 11 Aug 2026 14:43:55 -0700 Subject: [PATCH] Fix WASM R2R virtual IP initialization Register composite virtual IP ranges before eager fixups publish entry points, and assert that virtual IP bases are initialized before use. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: e77a1005-f20a-4fbd-9d4a-0a0b36f3a542 --- src/coreclr/vm/ceeload.cpp | 21 +++++++++--- src/coreclr/vm/readytoruninfo.cpp | 53 +++++++++++++++++-------------- src/coreclr/vm/readytoruninfo.h | 18 ++++++++++- 3 files changed, 62 insertions(+), 30 deletions(-) diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index c8e7e987be3f7a..91490382dd4070 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -3512,8 +3512,10 @@ void Module::RunEagerFixups() COUNT_T nSections; PTR_READYTORUN_IMPORT_SECTION pSections = GetImportSections(&nSections); +#ifndef TARGET_WASM if (nSections == 0) return; +#endif // !TARGET_WASM #ifdef _DEBUG // Loading types during eager fixup is not a tested scenario. Make bugs out of any attempts to do so in a @@ -3548,6 +3550,12 @@ void Module::RunEagerFixups() // For composite images, multiple modules may request initializing eager fixups // from multiple threads so we need to lock their resolution. CrstHolder compositeEagerFixups(compositeNativeImage->EagerFixupsLock()); +#ifdef TARGET_WASM + GetReadyToRunInfo()->RegisterVirtualIPRange(this); + if (nSections == 0) + return; +#endif // TARGET_WASM + if (compositeNativeImage->EagerFixupsHaveRun()) { if (compositeNativeImage->ReadyToRunCodeDisabled()) @@ -3562,6 +3570,12 @@ void Module::RunEagerFixups() else { // Per-module eager fixups don't need locking +#ifdef TARGET_WASM + GetReadyToRunInfo()->RegisterVirtualIPRange(this); + if (nSections == 0) + return; +#endif // TARGET_WASM + RunEagerFixupsUnlocked(); } } @@ -3607,10 +3621,7 @@ void Module::RunEagerFixupsUnlocked() } } -#ifdef TARGET_WASM - // For WASM, register virtual IP ranges instead of real code address ranges. - GetReadyToRunInfo()->RegisterVirtualIPRange(this); -#else +#ifndef TARGET_WASM TADDR base = dac_cast(pNativeImage->GetBase()); ExecutionManager::AddCodeRange( @@ -3618,7 +3629,7 @@ void Module::RunEagerFixupsUnlocked() ExecutionManager::GetReadyToRunJitManager(), RangeSection::RANGE_SECTION_NONE, this /* pHeapListOrZapModule */); -#endif // TARGET_WASM +#endif // !TARGET_WASM } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 9c8584bc4c89ca..12618b19863f28 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -2902,30 +2902,35 @@ void ReadyToRunInfo::RegisterVirtualIPRange(Module* pModule) if (m_nRuntimeFunctions == 0) return; - TADDR imageBase = dac_cast(m_pComposite->GetLayout()->GetBase()); - - // The last RUNTIME_FUNCTION entry's BeginAddress is the virtual IP index of that entry. - // Total virtual IPs = lastEntry.BeginAddress + virtualIPCount(lastEntry) - T_RUNTIME_FUNCTION* pLastEntry = &m_pRuntimeFunctions[m_nRuntimeFunctions - 1]; - UINT32 lastEntryVirtualIPIndex = RUNTIME_FUNCTION__BeginAddress(pLastEntry); - - // Decode the virtual IP count from the last entry's unwind data. - // Unwind format: ULEB128(frameSize) ULEB128(virtualIPCount) - PTR_BYTE pUnwindData = dac_cast(imageBase + pLastEntry->UnwindData); - DecodeULEB128AsU32(&pUnwindData); // skip frame size - UINT32 lastEntryVIPCount = DecodeULEB128AsU32(&pUnwindData) * 2; // Multiply by 2 to force all virtual IPs to be an even number. - - UINT32 totalVirtualIPs = lastEntryVirtualIPIndex + lastEntryVIPCount; - - m_minVirtualIP = ExecutionManager::AddVirtualIPRange( - totalVirtualIPs, - ExecutionManager::GetReadyToRunJitManager(), - pModule); - - ExecutionManager::AddFunctionTableIndexRange( - m_minFunctionTableIndex, - m_nRuntimeFunctions, - pModule); + if (!m_pComposite->MinVirtualIPSet()) + { + TADDR imageBase = dac_cast(m_pComposite->GetLayout()->GetBase()); + + // The last RUNTIME_FUNCTION entry's BeginAddress is the virtual IP index of that entry. + // Total virtual IPs = lastEntry.BeginAddress + virtualIPCount(lastEntry) + T_RUNTIME_FUNCTION* pLastEntry = &m_pRuntimeFunctions[m_nRuntimeFunctions - 1]; + UINT32 lastEntryVirtualIPIndex = RUNTIME_FUNCTION__BeginAddress(pLastEntry); + + // Decode the virtual IP count from the last entry's unwind data. + // Unwind format: ULEB128(frameSize) ULEB128(virtualIPCount) + PTR_BYTE pUnwindData = dac_cast(imageBase + pLastEntry->UnwindData); + DecodeULEB128AsU32(&pUnwindData); // skip frame size + UINT32 lastEntryVIPCount = DecodeULEB128AsU32(&pUnwindData) * 2; // Multiply by 2 to force all virtual IPs to be an even number. + + UINT32 totalVirtualIPs = lastEntryVirtualIPIndex + lastEntryVIPCount; + + m_pComposite->SetMinVirtualIP(ExecutionManager::AddVirtualIPRange( + totalVirtualIPs, + ExecutionManager::GetReadyToRunJitManager(), + pModule)); + + ExecutionManager::AddFunctionTableIndexRange( + m_minFunctionTableIndex, + m_nRuntimeFunctions, + pModule); + } + + m_minVirtualIP = m_pComposite->GetMinVirtualIP(); } #endif // TARGET_WASM diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index 60c7cd3ecc96d8..c63f5cc1a3dedc 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -29,6 +29,9 @@ class ReadyToRunCoreInfo private: PTR_ReadyToRunLoadedImage m_pLayout; PTR_READYTORUN_CORE_HEADER m_pCoreHeader; +#ifdef TARGET_WASM + TADDR m_minVirtualIP = 0; +#endif // TARGET_WASM Volatile m_fForbidLoadILBodyFixups; friend struct ::cdac_data; @@ -40,6 +43,15 @@ class ReadyToRunCoreInfo IMAGE_DATA_DIRECTORY * FindSection(ReadyToRunSectionType type) const; void ForbidProcessMoreILBodyFixups() { m_fForbidLoadILBodyFixups = true; } bool IsForbidProcessMoreILBodyFixups() { return m_fForbidLoadILBodyFixups; } +#ifdef TARGET_WASM + bool MinVirtualIPSet() const { return m_minVirtualIP != 0; } + void SetMinVirtualIP(TADDR minVirtualIP) { m_minVirtualIP = minVirtualIP; } + TADDR GetMinVirtualIP() const + { + _ASSERTE(MinVirtualIPSet()); + return m_minVirtualIP; + } +#endif // TARGET_WASM PTR_ReadyToRunLoadedImage GetImage() const { @@ -223,7 +235,11 @@ class ReadyToRunInfo #ifdef TARGET_WASM DWORD GetMinFunctionTableIndex() const { return m_minFunctionTableIndex; } - TADDR GetMinVirtualIP() const { return m_minVirtualIP; } + TADDR GetMinVirtualIP() const + { + _ASSERTE(m_minVirtualIP != 0); + return m_minVirtualIP; + } PCODE R2RRelativeFunctionIndexToVirtualIP(DWORD r2rFunctionIndex) const { LIMITED_METHOD_CONTRACT;