From 3c316d0cde259ae14c09a6b23e78395a0f8f3032 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 20 Apr 2026 14:11:27 -0700 Subject: [PATCH 1/5] Logic to load R2R files on WebAssembly - Add better error checking to libCorerun.js - Add handling of relocs/tableBase to WebCIL v1 images - Actually handle PortableEntryPoint correctly in the ReadyToRun code - Generate WASM_TABLE_INDEX_I32 relocs in the the RuntimeFunctionsTableNode - Adjust DoesSlotCallPrestub to return false and not assert all the time on Wasm. In the absence of code versioning, returning false appears to be adequate. --- src/coreclr/hosts/corerun/wasm/libCorerun.js | 19 +- src/coreclr/inc/webcildecoder.h | 27 ++- .../ReadyToRun/RuntimeFunctionsTableNode.cs | 17 +- src/coreclr/utilcode/webcildecoder.cpp | 172 ++++++++++++++++-- src/coreclr/vm/peimagelayout.cpp | 20 +- src/coreclr/vm/peimagelayout.h | 4 +- src/coreclr/vm/peimagelayout.inl | 21 ++- src/coreclr/vm/precode_portable.cpp | 4 +- src/coreclr/vm/readytoruninfo.cpp | 11 ++ 9 files changed, 257 insertions(+), 38 deletions(-) diff --git a/src/coreclr/hosts/corerun/wasm/libCorerun.js b/src/coreclr/hosts/corerun/wasm/libCorerun.js index a47e145144f663..333a456c4948fd 100644 --- a/src/coreclr/hosts/corerun/wasm/libCorerun.js +++ b/src/coreclr/hosts/corerun/wasm/libCorerun.js @@ -178,7 +178,15 @@ function libCoreRunFactory() { } catch (e) { return false; } - const wasmModule = new WebAssembly.Module(wasmBytes); + let wasmModule; + try { + wasmModule = new WebAssembly.Module(wasmBytes); + } catch (e) { + const errormessage = e instanceof Error ? e.message : String(e); + console.error("Failed to compile WebAssembly module for Webcil image:", {wasmPath, errormessage}); + return false; + } + const tableStartIndex = wasmTable.length; var payloadSize = 0; @@ -195,7 +203,14 @@ function libCoreRunFactory() { console.error("Webcil payload size is 0; cannot load image"); return false; } - wasmTable.grow(tableSize); + + try { + wasmTable.grow(tableSize); + } catch (e) { + const errormessage = e instanceof Error ? e.message : String(e); + console.error("Failed to grow WebAssembly table for Webcil image:", {wasmPath, errormessage}); + return false; + } var payloadPtr = 0; var wasmInstance; diff --git a/src/coreclr/inc/webcildecoder.h b/src/coreclr/inc/webcildecoder.h index a83e5d33875aca..4082b0506bac13 100644 --- a/src/coreclr/inc/webcildecoder.h +++ b/src/coreclr/inc/webcildecoder.h @@ -197,15 +197,26 @@ class WebcilDecoder void *GetNativeEntryPoint() const { return NULL; } // ------------------------------------------------------------ - // R2R — not supported for Webcil + // R2R // ------------------------------------------------------------ - BOOL HasReadyToRunHeader() const { return FALSE; } + BOOL HasReadyToRunHeader() const; BOOL IsComponentAssembly() const { return FALSE; } - READYTORUN_HEADER *GetReadyToRunHeader() const { return NULL; } - BOOL IsNativeMachineFormat() const { return FALSE; } - PTR_CVOID GetNativeManifestMetadata(COUNT_T *pSize = NULL) const; + READYTORUN_HEADER *GetReadyToRunHeader() const; + BOOL IsNativeMachineFormat() const { return TRUE; } + PTR_CVOID GetNativeManifestMetadata(COUNT_T *pSize) const; + CHECK CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags = 0, IsNullOK ok = NULL_NOT_OK) const; + TADDR GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir) const; + SSIZE_T GetTableBaseOffset() const + { + if (m_pHeader == NULL) + return 0; + return m_pHeader->VersionMajor >= WEBCIL_VERSION_MAJOR_1 ? ((const WebcilHeader_1 *)m_pHeader)->TableBase : 0; + } +private: + READYTORUN_HEADER *FindReadyToRunHeader() const; +public: // ------------------------------------------------------------ // RVA operations (remaining private) // ------------------------------------------------------------ @@ -274,12 +285,14 @@ class WebcilDecoder // Instance members TADDR m_base; COUNT_T m_size; - BOOL m_hasContents; - BOOL m_relocated = FALSE; + bool m_relocated = FALSE; + bool m_hasContents; + mutable bool m_hasNoReadyToRunHeader; const WebcilHeader *m_pHeader; const WebcilSectionHeader *m_sections; mutable IMAGE_COR20_HEADER *m_pCorHeader; + mutable PTR_READYTORUN_HEADER m_pReadyToRunHeader; }; #endif // FEATURE_WEBCIL diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs index bd2f062fc59be4..063caae405075c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/RuntimeFunctionsTableNode.cs @@ -124,12 +124,19 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) symbol = method; } - runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ADDR32NB, delta: frameInfo.StartOffset + _nodeFactory.Target.CodeDelta); - if (!relocsOnly && _nodeFactory.Target.Architecture == TargetArchitecture.X64) + if (_nodeFactory.Target.Architecture == TargetArchitecture.Wasm32) { - // On Amd64, the 2nd word contains the EndOffset of the runtime function - Debug.Assert(frameInfo.StartOffset != frameInfo.EndOffset); - runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ADDR32NB, delta: frameInfo.EndOffset); + runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.WASM_TABLE_INDEX_I32, frameIndex); + } + else + { + runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ADDR32NB, delta: frameInfo.StartOffset + _nodeFactory.Target.CodeDelta); + if (!relocsOnly && _nodeFactory.Target.Architecture == TargetArchitecture.X64) + { + // On Amd64, the 2nd word contains the EndOffset of the runtime function + Debug.Assert(frameInfo.StartOffset != frameInfo.EndOffset); + runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_ADDR32NB, delta: frameInfo.EndOffset); + } } runtimeFunctionsBuilder.EmitReloc(factory.RuntimeFunctionsGCInfo, RelocType.IMAGE_REL_BASED_ADDR32NB, funcletOffsets[frameIndex]); runtimeFunctionIndex++; diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index 475801ab4be5a4..e4a1011eb0a706 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -34,10 +34,12 @@ bool WebcilDecoder::DetectWebcilFormat(const void* data, COUNT_T size) WebcilDecoder::WebcilDecoder() : m_base(0), m_size(0), - m_hasContents(FALSE), + m_hasContents(false), + m_hasNoReadyToRunHeader(false), m_pHeader(NULL), m_sections(NULL), - m_pCorHeader(NULL) + m_pCorHeader(NULL), + m_pReadyToRunHeader(NULL) { LIMITED_METHOD_CONTRACT; } @@ -82,7 +84,7 @@ void WebcilDecoder::Reset() LIMITED_METHOD_CONTRACT; m_base = 0; m_size = 0; - m_hasContents = FALSE; + m_hasContents = false; m_pHeader = NULL; m_sections = NULL; m_pCorHeader = NULL; @@ -470,14 +472,6 @@ ULONG WebcilDecoder::GetEntryPointToken() const // R2R / native manifest metadata // ------------------------------------------------------------ -PTR_CVOID WebcilDecoder::GetNativeManifestMetadata(COUNT_T *pSize) const -{ - LIMITED_METHOD_CONTRACT; - if (pSize != NULL) - *pSize = 0; - return NULL; -} - // ------------------------------------------------------------ // RVA operations // ------------------------------------------------------------ @@ -864,7 +858,7 @@ TADDR WebcilDecoder::GetDirectoryEntryData(int entry, COUNT_T *pSize) const return (TADDR)0; } - const WebcilSectionHeader *sections = (const WebcilSectionHeader *)(m_base + sizeof(WebcilHeader)); + const WebcilSectionHeader *sections = m_sections; const WebcilSectionHeader *relocSection = §ions[sectionIndex]; if (pSize != NULL) @@ -916,6 +910,160 @@ CHECK WebcilDecoder::CheckILMethod(RVA rva) return CorDecoderHelpers::CheckILMethod(*this, rva); } +BOOL WebcilDecoder::HasReadyToRunHeader() const +{ + CONTRACTL + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + SUPPORTS_DAC; + } + CONTRACTL_END; + + if (m_hasNoReadyToRunHeader) + return FALSE; + + if (m_pReadyToRunHeader != NULL) + return TRUE; + + return FindReadyToRunHeader() != NULL; +} + +READYTORUN_HEADER * WebcilDecoder::GetReadyToRunHeader() const +{ + CONTRACT(READYTORUN_HEADER *) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(HasCorHeader()); + PRECONDITION(HasReadyToRunHeader()); + NOTHROW; + GC_NOTRIGGER; + POSTCONDITION(CheckPointer(RETVAL)); + SUPPORTS_DAC; + CANNOT_TAKE_LOCK; + } + CONTRACT_END; + + if (m_pReadyToRunHeader != NULL) + RETURN m_pReadyToRunHeader; + + RETURN FindReadyToRunHeader(); +} + +PTR_CVOID WebcilDecoder::GetNativeManifestMetadata(COUNT_T *pSize) const +{ + CONTRACT(PTR_CVOID) + { + INSTANCE_CHECK; + PRECONDITION(HasReadyToRunHeader()); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // TBD - may not store metadata for IJW + NOTHROW; + GC_NOTRIGGER; + } + CONTRACT_END; + + IMAGE_DATA_DIRECTORY *pDir = NULL; + { + READYTORUN_HEADER * pHeader = GetReadyToRunHeader(); + + PTR_READYTORUN_SECTION pSections = dac_cast(dac_cast(pHeader) + sizeof(READYTORUN_HEADER)); + for (DWORD i = 0; i < pHeader->CoreHeader.NumberOfSections; i++) + { + // Verify that section types are sorted + _ASSERTE(i == 0 || (pSections[i - 1].Type < pSections[i].Type)); + + READYTORUN_SECTION * pSection = pSections + i; + if (pSection->Type == ReadyToRunSectionType::ManifestMetadata) + { + // Set pDir to the address of the manifest metadata section + pDir = &pSection->Section; + break; + } + } + + // ReadyToRun file without large version bubble support doesn't have the ManifestMetadata + if (pDir == NULL) + { + if (pSize != NULL) + { + *pSize = 0; + } + + RETURN NULL; + } + } + + if (pSize != NULL) + *pSize = VAL32(pDir->Size); + + RETURN dac_cast(GetDirectoryData(pDir)); +} + +READYTORUN_HEADER * WebcilDecoder::FindReadyToRunHeader() const +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACTL_END; + + IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader; + + if (VAL32(pDir->Size) >= sizeof(READYTORUN_HEADER) && CheckDirectory(pDir)) + { + PTR_READYTORUN_HEADER pHeader = PTR_READYTORUN_HEADER((TADDR)GetDirectoryData(pDir)); + if (pHeader->Signature == READYTORUN_SIGNATURE) + { + m_pReadyToRunHeader = pHeader; + return pHeader; + } + } + + m_hasNoReadyToRunHeader = true; + return NULL; +} + +TADDR WebcilDecoder::GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir) const +{ + CONTRACT(TADDR) + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckDirectory(pDir, 0, NULL_OK)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + POSTCONDITION(CheckPointer((void *)RETVAL, NULL_OK)); + CANNOT_TAKE_LOCK; + } + CONTRACT_END; + + RETURN GetRvaData(VAL32(pDir->VirtualAddress)); +} + +CHECK WebcilDecoder::CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags, IsNullOK ok) const +{ + CONTRACT_CHECK + { + INSTANCE_CHECK; + PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckPointer(pDir)); + NOTHROW; + GC_NOTRIGGER; + SUPPORTS_DAC; + } + CONTRACT_CHECK_END; + + CHECK(CheckRva(VAL32(pDir->VirtualAddress), VAL32(pDir->Size), forbiddenFlags, ok)); + + CHECK_OK; +} + // ------------------------------------------------------------ // DAC support // ------------------------------------------------------------ diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 5442b4d17e9cfe..8ef26225d8777f 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -210,6 +210,8 @@ void PEImageLayout::InitDecoders(void* data, COUNT_T size) { m_format = FORMAT_WEBCIL; m_webcilDecoder.Init(data, size); + if (HasBaseRelocations()) + ApplyBaseRelocations(true); m_peDecoder.Init(data, size); // Initialize base/size/flags for cDAC } else @@ -245,7 +247,7 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) SSIZE_T delta = (SIZE_T) GetBase() - (SIZE_T) GetPreferredBase(); #ifdef FEATURE_WEBCIL - SSIZE_T tableBaseDelta = m_tableBaseOffset; + SSIZE_T tableBaseDelta = GetTableBaseOffset(); #endif // FEATURE_WEBCIL // Nothing to do - image is loaded at preferred base and no table base offset @@ -275,12 +277,26 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) const SIZE_T cbPageSize = 4096; COUNT_T dirPos = 0; +#ifdef TARGET_WASM + // WASM will padd out the reloc size to the next 16 byte boundary, so we need to validate we can safely read the IMAGE_BASE_RELOCATION struct before processing each entry. + while (dirPos < (dirSize - sizeof(IMAGE_BASE_RELOCATION))) +#else while (dirPos < dirSize) +#endif { PIMAGE_BASE_RELOCATION r = (PIMAGE_BASE_RELOCATION)(dir + dirPos); COUNT_T fixupsSize = VAL32(r->SizeOfBlock); +#ifdef TARGET_WASM + if (fixupsSize == 0) + { + // Since WASM will pad the reloc block to the next 16 byte boundary with 0's we need to allow for a SizeOfBlock being zero. + // This can only happen for the last block in the relocation list, so we can break here instead of continue. + break; + } +#endif + USHORT *fixups = (USHORT *) (r + 1); _ASSERTE(fixupsSize > sizeof(IMAGE_BASE_RELOCATION)); @@ -415,7 +431,7 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) dirPos += fixupsSize; } - _ASSERTE(dirSize == dirPos); + _ASSERTE(dirSize == dirPos || !IsPEFormat()); if (dwOldProtection != 0) { diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index 542ec86216569b..89c66cc4b63d30 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -77,9 +77,7 @@ class PEImageLayout void ApplyBaseRelocations(bool relocationMustWriteCopy); #ifdef FEATURE_WEBCIL -// TODO-WASM: These can be removed very soon, and we can fetch this from the webcil header itself - void SetTableBaseOffset(SSIZE_T tableBaseOffset) { m_tableBaseOffset = tableBaseOffset; } - SSIZE_T GetTableBaseOffset() const { return m_tableBaseOffset; } + SSIZE_T GetTableBaseOffset() const; #endif // ------------------------------------------------------------ diff --git a/src/coreclr/vm/peimagelayout.inl b/src/coreclr/vm/peimagelayout.inl index bd0d1a8c2b5f28..83703fde9220ec 100644 --- a/src/coreclr/vm/peimagelayout.inl +++ b/src/coreclr/vm/peimagelayout.inl @@ -65,9 +65,6 @@ inline PEImageLayout::~PEImageLayout() inline PEImageLayout::PEImageLayout() : m_refCount(1) , m_format(FORMAT_PE) -#ifdef FEATURE_WEBCIL - , m_tableBaseOffset(0) -#endif , m_pOwner(NULL) { LIMITED_METHOD_CONTRACT; @@ -279,6 +276,18 @@ inline BOOL PEImageLayout::HasBaseRelocations() const DECODER_DISPATCH(HasBaseRelocations()) } +#ifdef FEATURE_WEBCIL +inline SSIZE_T PEImageLayout::GetTableBaseOffset() const +{ + WRAPPER_NO_CONTRACT; + if (IsWebcilFormat()) + { + return m_webcilDecoder.GetTableBaseOffset(); + } + return 0; +} +#endif + inline const void *PEImageLayout::GetPreferredBase() const { WRAPPER_NO_CONTRACT; @@ -579,7 +588,7 @@ inline IMAGE_COR_VTABLEFIXUP *PEImageLayout::GetVTableFixups(COUNT_T *pCount) co inline BOOL PEImageLayout::IsNativeMachineFormat() const { WRAPPER_NO_CONTRACT; - PE_OR_WEBCIL(IsNativeMachineFormat(), FALSE) + PE_OR_WEBCIL(IsNativeMachineFormat(), TRUE) } inline BOOL PEImageLayout::IsI386() const @@ -634,13 +643,13 @@ inline BOOL PEImageLayout::IsComponentAssembly() const inline BOOL PEImageLayout::HasReadyToRunHeader() const { LIMITED_METHOD_DAC_CONTRACT; - PE_OR_WEBCIL(HasReadyToRunHeader(), FALSE) + DECODER_DISPATCH(HasReadyToRunHeader()) } inline READYTORUN_HEADER *PEImageLayout::GetReadyToRunHeader() const { WRAPPER_NO_CONTRACT; - PE_OR_WEBCIL(GetReadyToRunHeader(), NULL) + DECODER_DISPATCH(GetReadyToRunHeader()) } inline BOOL PEImageLayout::HasNativeEntryPoint() const diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp index 3330d548ea845d..fff4654e8db9be 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -238,7 +238,9 @@ void FlushCacheForDynamicMappedStub(void* code, SIZE_T size) BOOL DoesSlotCallPrestub(PCODE pCode) { LIMITED_METHOD_CONTRACT; - _ASSERTE(!"DoesSlotCallPrestub is not supported with Portable EntryPoints"); + /* On WASM slots never directly call the prestub. Instead we have the R2R to interpreter thunks + which serve as the PreStub, but the characteristics are slightly different, and it appears this + isn't necessary. */ return FALSE; } diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index a6eb8e6e742e16..2f4f364555c082 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1367,7 +1367,18 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig } _ASSERTE(id < m_nRuntimeFunctions); +#ifndef FEATURE_PORTABLE_ENTRYPOINTS pEntryPoint = dac_cast(GetImage()->GetBase()) + m_pRuntimeFunctions[id].BeginAddress; +#else + // When we have portable entrypoints enable, the R2R image contains actual entrypoints. +#ifdef FEATURE_TIERED_COMPILATION +#error "Portable entry points are not currently supported with tiered compilation, as the interaction between the two is not yet fully worked out." +#endif + PCODE actualEntryPoint; + actualEntryPoint = m_pRuntimeFunctions[id].BeginAddress; + pEntryPoint = pMD->GetTemporaryEntryPoint(); + PortableEntryPoint::SetActualCode(pEntryPoint, actualEntryPoint); +#endif m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(pEntryPoint, pMD); #ifdef PROFILING_SUPPORTED From 765fa857e1bc5076849e75a1f1f7e09e470e533f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 21 Apr 2026 17:16:55 -0700 Subject: [PATCH 2/5] Respond to feedback --- src/coreclr/hosts/corerun/wasm/libCorerun.js | 4 ++-- src/coreclr/inc/webcildecoder.h | 2 +- src/coreclr/utilcode/webcildecoder.cpp | 6 +++--- src/coreclr/vm/peimagelayout.cpp | 4 ++-- src/coreclr/vm/peimagelayout.h | 1 - src/coreclr/vm/precode_portable.cpp | 5 +++++ src/coreclr/vm/readytoruninfo.cpp | 2 +- 7 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/coreclr/hosts/corerun/wasm/libCorerun.js b/src/coreclr/hosts/corerun/wasm/libCorerun.js index 333a456c4948fd..1c23ebd8045dcd 100644 --- a/src/coreclr/hosts/corerun/wasm/libCorerun.js +++ b/src/coreclr/hosts/corerun/wasm/libCorerun.js @@ -182,8 +182,8 @@ function libCoreRunFactory() { try { wasmModule = new WebAssembly.Module(wasmBytes); } catch (e) { - const errormessage = e instanceof Error ? e.message : String(e); - console.error("Failed to compile WebAssembly module for Webcil image:", {wasmPath, errormessage}); + const errorMessage = e instanceof Error ? e.message : String(e); + console.error("Failed to compile WebAssembly module for Webcil image:", {wasmPath, errorMessage}); return false; } diff --git a/src/coreclr/inc/webcildecoder.h b/src/coreclr/inc/webcildecoder.h index 4082b0506bac13..8b8684b2948560 100644 --- a/src/coreclr/inc/webcildecoder.h +++ b/src/coreclr/inc/webcildecoder.h @@ -203,7 +203,7 @@ class WebcilDecoder BOOL HasReadyToRunHeader() const; BOOL IsComponentAssembly() const { return FALSE; } READYTORUN_HEADER *GetReadyToRunHeader() const; - BOOL IsNativeMachineFormat() const { return TRUE; } + BOOL IsNativeMachineFormat() const { return true; } // This can only be loaded on a Wasm runtime which matches the necessary load environment, which means these are always in the native machine format. PTR_CVOID GetNativeManifestMetadata(COUNT_T *pSize) const; CHECK CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags = 0, IsNullOK ok = NULL_NOT_OK) const; diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index e4a1011eb0a706..2721c5ef6bd3f7 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -936,7 +936,7 @@ READYTORUN_HEADER * WebcilDecoder::GetReadyToRunHeader() const CONTRACT(READYTORUN_HEADER *) { INSTANCE_CHECK; - PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckWebcilHeaders()); PRECONDITION(HasCorHeader()); PRECONDITION(HasReadyToRunHeader()); NOTHROW; @@ -1033,7 +1033,7 @@ TADDR WebcilDecoder::GetDirectoryData(IMAGE_DATA_DIRECTORY *pDir) const CONTRACT(TADDR) { INSTANCE_CHECK; - PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckWebcilHeaders()); PRECONDITION(CheckDirectory(pDir, 0, NULL_OK)); NOTHROW; GC_NOTRIGGER; @@ -1051,7 +1051,7 @@ CHECK WebcilDecoder::CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFla CONTRACT_CHECK { INSTANCE_CHECK; - PRECONDITION(CheckNTHeaders()); + PRECONDITION(CheckWebcilHeaders()); PRECONDITION(CheckPointer(pDir)); NOTHROW; GC_NOTRIGGER; diff --git a/src/coreclr/vm/peimagelayout.cpp b/src/coreclr/vm/peimagelayout.cpp index 8ef26225d8777f..3c87f1b1d06ab3 100644 --- a/src/coreclr/vm/peimagelayout.cpp +++ b/src/coreclr/vm/peimagelayout.cpp @@ -278,8 +278,8 @@ void PEImageLayout::ApplyBaseRelocations(bool relocationMustWriteCopy) COUNT_T dirPos = 0; #ifdef TARGET_WASM - // WASM will padd out the reloc size to the next 16 byte boundary, so we need to validate we can safely read the IMAGE_BASE_RELOCATION struct before processing each entry. - while (dirPos < (dirSize - sizeof(IMAGE_BASE_RELOCATION))) + // WASM will pad out the reloc size to the next 16 byte boundary, so we need to validate we can safely read the IMAGE_BASE_RELOCATION struct before processing each entry. + while (dirPos + sizeof(IMAGE_BASE_RELOCATION) <= dirSize) #else while (dirPos < dirSize) #endif diff --git a/src/coreclr/vm/peimagelayout.h b/src/coreclr/vm/peimagelayout.h index 89c66cc4b63d30..d5d9855d6a369b 100644 --- a/src/coreclr/vm/peimagelayout.h +++ b/src/coreclr/vm/peimagelayout.h @@ -242,7 +242,6 @@ class PEImageLayout PEDecoder m_peDecoder; #ifdef FEATURE_WEBCIL WebcilDecoder m_webcilDecoder; - SSIZE_T m_tableBaseOffset; #endif public: diff --git a/src/coreclr/vm/precode_portable.cpp b/src/coreclr/vm/precode_portable.cpp index fff4654e8db9be..934b5fb3c1e6ba 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -238,10 +238,15 @@ void FlushCacheForDynamicMappedStub(void* code, SIZE_T size) BOOL DoesSlotCallPrestub(PCODE pCode) { LIMITED_METHOD_CONTRACT; +#ifdef TARGET_WASM /* On WASM slots never directly call the prestub. Instead we have the R2R to interpreter thunks which serve as the PreStub, but the characteristics are slightly different, and it appears this isn't necessary. */ return FALSE; +#else + _ASSERTE(!"DoesSlotCallPrestub is not yet implemented for non-WASM portable entrypoints"); + return FALSE; +#endif } #endif // FEATURE_PORTABLE_ENTRYPOINTS diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 2f4f364555c082..ceb7d3473712b5 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -1370,7 +1370,7 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig #ifndef FEATURE_PORTABLE_ENTRYPOINTS pEntryPoint = dac_cast(GetImage()->GetBase()) + m_pRuntimeFunctions[id].BeginAddress; #else - // When we have portable entrypoints enable, the R2R image contains actual entrypoints. + // When we have portable entrypoints enabled, the R2R image contains actual entrypoints. #ifdef FEATURE_TIERED_COMPILATION #error "Portable entry points are not currently supported with tiered compilation, as the interaction between the two is not yet fully worked out." #endif From 98c76e70ac7b8378a2427341e475f7b7bc27591f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 22 Apr 2026 10:19:49 -0700 Subject: [PATCH 3/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/coreclr/hosts/corerun/wasm/libCorerun.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/hosts/corerun/wasm/libCorerun.js b/src/coreclr/hosts/corerun/wasm/libCorerun.js index 1c23ebd8045dcd..f8a554a0ce2b78 100644 --- a/src/coreclr/hosts/corerun/wasm/libCorerun.js +++ b/src/coreclr/hosts/corerun/wasm/libCorerun.js @@ -207,8 +207,8 @@ function libCoreRunFactory() { try { wasmTable.grow(tableSize); } catch (e) { - const errormessage = e instanceof Error ? e.message : String(e); - console.error("Failed to grow WebAssembly table for Webcil image:", {wasmPath, errormessage}); + const errorMessage = e instanceof Error ? e.message : String(e); + console.error("Failed to grow WebAssembly table for Webcil image:", {wasmPath, errorMessage}); return false; } From f9aeb16ea9d55d9d09b78a506b66801c4d491c80 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 22 Apr 2026 10:54:15 -0700 Subject: [PATCH 4/5] Remove unused WebcilDecoder::Reset() method The Reset() method on WebcilDecoder was declared and defined but never called anywhere in the codebase. Remove the dead code. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/inc/webcildecoder.h | 1 - src/coreclr/utilcode/webcildecoder.cpp | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/src/coreclr/inc/webcildecoder.h b/src/coreclr/inc/webcildecoder.h index 8b8684b2948560..f91bf891f2d2da 100644 --- a/src/coreclr/inc/webcildecoder.h +++ b/src/coreclr/inc/webcildecoder.h @@ -110,7 +110,6 @@ class WebcilDecoder WebcilDecoder(); void Init(void *flatBase, COUNT_T size); - void Reset(); // ------------------------------------------------------------ // COR header — public for CorDecoderHelpers access diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index 2721c5ef6bd3f7..507e5917adc008 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -79,18 +79,6 @@ void WebcilDecoder::Init(void *flatBase, COUNT_T size) m_relocated = FALSE; } -void WebcilDecoder::Reset() -{ - LIMITED_METHOD_CONTRACT; - m_base = 0; - m_size = 0; - m_hasContents = false; - m_pHeader = NULL; - m_sections = NULL; - m_pCorHeader = NULL; - m_relocated = FALSE; -} - // ------------------------------------------------------------ // Basic properties // ------------------------------------------------------------ From 29fc37f86f6ef34ec7dc3c707b11281376a02c26 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 22 Apr 2026 13:11:08 -0700 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Adam Perlin --- src/coreclr/hosts/corerun/wasm/libCorerun.js | 2 +- src/coreclr/utilcode/webcildecoder.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/hosts/corerun/wasm/libCorerun.js b/src/coreclr/hosts/corerun/wasm/libCorerun.js index f8a554a0ce2b78..683ec9241d45fb 100644 --- a/src/coreclr/hosts/corerun/wasm/libCorerun.js +++ b/src/coreclr/hosts/corerun/wasm/libCorerun.js @@ -183,7 +183,7 @@ function libCoreRunFactory() { wasmModule = new WebAssembly.Module(wasmBytes); } catch (e) { const errorMessage = e instanceof Error ? e.message : String(e); - console.error("Failed to compile WebAssembly module for Webcil image:", {wasmPath, errorMessage}); + console.error("Failed to construct WebAssembly module for Webcil image:", {wasmPath, errorMessage}); return false; } diff --git a/src/coreclr/utilcode/webcildecoder.cpp b/src/coreclr/utilcode/webcildecoder.cpp index 507e5917adc008..59460a21a0f447 100644 --- a/src/coreclr/utilcode/webcildecoder.cpp +++ b/src/coreclr/utilcode/webcildecoder.cpp @@ -972,7 +972,7 @@ PTR_CVOID WebcilDecoder::GetNativeManifestMetadata(COUNT_T *pSize) const } } - // ReadyToRun file without large version bubble support doesn't have the ManifestMetadata + // Handle the no ManifestMetadata case, which will happen in a ReadyToRun file without large version bubble support. if (pDir == NULL) { if (pSize != NULL)