diff --git a/src/coreclr/hosts/corerun/wasm/libCorerun.js b/src/coreclr/hosts/corerun/wasm/libCorerun.js index a47e145144f663..1c23ebd8045dcd 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..8b8684b2948560 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; } // 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; + 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..2721c5ef6bd3f7 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(CheckWebcilHeaders()); + 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(CheckWebcilHeaders()); + 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(CheckWebcilHeaders()); + 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..3c87f1b1d06ab3 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 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 { 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..d5d9855d6a369b 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 // ------------------------------------------------------------ @@ -244,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/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..934b5fb3c1e6ba 100644 --- a/src/coreclr/vm/precode_portable.cpp +++ b/src/coreclr/vm/precode_portable.cpp @@ -238,8 +238,15 @@ void FlushCacheForDynamicMappedStub(void* code, SIZE_T size) BOOL DoesSlotCallPrestub(PCODE pCode) { LIMITED_METHOD_CONTRACT; - _ASSERTE(!"DoesSlotCallPrestub is not supported with Portable EntryPoints"); +#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 a6eb8e6e742e16..ceb7d3473712b5 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 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 + PCODE actualEntryPoint; + actualEntryPoint = m_pRuntimeFunctions[id].BeginAddress; + pEntryPoint = pMD->GetTemporaryEntryPoint(); + PortableEntryPoint::SetActualCode(pEntryPoint, actualEntryPoint); +#endif m_pCompositeInfo->SetMethodDescForEntryPointInNativeImage(pEntryPoint, pMD); #ifdef PROFILING_SUPPORTED