Skip to content

Logic to load R2R files on WebAssembly#127229

Open
davidwrighton wants to merge 1 commit intodotnet:mainfrom
davidwrighton:LoadR2R_Core
Open

Logic to load R2R files on WebAssembly#127229
davidwrighton wants to merge 1 commit intodotnet:mainfrom
davidwrighton:LoadR2R_Core

Conversation

@davidwrighton
Copy link
Copy Markdown
Member

  • 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.

- 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.
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enables loading and executing ReadyToRun (R2R) code from WebAssembly/Webcil images by extending Webcil decoding/relocation support and wiring portable entrypoints + runtime function table relocs for Wasm.

Changes:

  • Extend Webcil decoding and PEImageLayout dispatch so Webcil images can expose R2R headers, manifest metadata, and apply base/table relocs.
  • Update R2R runtime entrypoint lookup to correctly use portable entrypoints (temporary entrypoint + “actual code”).
  • Emit Wasm table-index relocations for the runtime functions table and improve libCorerun.js error handling around module compile/table growth.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/coreclr/vm/readytoruninfo.cpp Uses portable entrypoints to route R2R entrypoint resolution via temporary entrypoints.
src/coreclr/vm/precode_portable.cpp Makes DoesSlotCallPrestub return false under portable entrypoints (Wasm scenario).
src/coreclr/vm/peimagelayout.inl Adds Webcil GetTableBaseOffset forwarding + changes Webcil dispatch behavior for R2R header queries.
src/coreclr/vm/peimagelayout.h Removes legacy table-base setter/getter in favor of decoder-derived offset.
src/coreclr/vm/peimagelayout.cpp Applies relocations for Webcil at init time and adds Wasm-specific relocation parsing logic.
src/coreclr/utilcode/webcildecoder.cpp Adds R2R header discovery + manifest metadata access for Webcil.
src/coreclr/inc/webcildecoder.h Exposes new Webcil R2R surface area and table-base offset from Webcil v1 header.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/.../RuntimeFunctionsTableNode.cs Emits WASM_TABLE_INDEX_I32 relocs for Wasm runtime function BeginAddress values.
src/coreclr/hosts/corerun/wasm/libCorerun.js Adds error handling for Wasm module compilation and table growth failures.
Comments suppressed due to low confidence (1)

src/coreclr/utilcode/webcildecoder.cpp:92

  • WebcilDecoder::Reset() doesn't reset the new ReadyToRun header caches (m_hasNoReadyToRunHeader and m_pReadyToRunHeader). If the decoder instance is reused (e.g., via Reset/Init), stale cached state can incorrectly force HasReadyToRunHeader() to return false or return a pointer from a previous image. Reset should clear these fields (and any other R2R-related cached state) to the same defaults as the constructor.
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;
}

Comment on lines 279 to +285
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
Comment on lines +127 to 140
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);
}
}
#ifndef FEATURE_PORTABLE_ENTRYPOINTS
pEntryPoint = dac_cast<TADDR>(GetImage()->GetBase()) + m_pRuntimeFunctions[id].BeginAddress;
#else
// When we have portable entrypoints enable, the R2R image contains actual entrypoints.
Comment on lines +934 to +943
READYTORUN_HEADER * WebcilDecoder::GetReadyToRunHeader() const
{
CONTRACT(READYTORUN_HEADER *)
{
INSTANCE_CHECK;
PRECONDITION(CheckNTHeaders());
PRECONDITION(HasCorHeader());
PRECONDITION(HasReadyToRunHeader());
NOTHROW;
GC_NOTRIGGER;
Comment on lines +1031 to +1056
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;

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.
#ifdef FEATURE_WEBCIL
, m_tableBaseOffset(0)
#endif
, m_pOwner(NULL)
Comment on lines +185 to +186
const errormessage = e instanceof Error ? e.message : String(e);
console.error("Failed to compile WebAssembly module for Webcil image:", {wasmPath, errormessage});
{
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be guarded with a #ifdef TARGET_WASM? I'm just wondering if we could have other platforms with portable entrypoints where this behavior may be different.

BOOL IsNativeMachineFormat() const { return FALSE; }
PTR_CVOID GetNativeManifestMetadata(COUNT_T *pSize = NULL) const;
READYTORUN_HEADER *GetReadyToRunHeader() const;
BOOL IsNativeMachineFormat() const { return TRUE; }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TRUE only when you found R2R ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants