Description
On WebAssembly targets, IRuntimeTypeSystem.GetMethodDescHandle(ptr) throws ArgumentException: Invalid method desc pointer for valid MethodDescs. In practice that means no MethodDesc can be named on WASM, including every MethodDesc that IStackWalk.GetMethodDescPtr / IExecutionManager.GetMethodDesc returns.
Root cause
MethodValidation.ValidateMethodDescPointer checks the temporary entry point:
TargetCodePointer temporaryEntryPoint = GetTemporaryEntryPointIfExists(umd);
if (temporaryEntryPoint != TargetCodePointer.Null)
{
Contracts.IPrecodeStubs precode = _target.Contracts.PrecodeStubs;
TargetPointer methodDesc = precode.GetMethodDescFromStubAddress(temporaryEntryPoint);
...
PrecodeStubs_1.GetMethodDescFromStubAddress needs the PrecodeMachineDescriptor data type, which the descriptor compiles out under FEATURE_PORTABLE_ENTRYPOINTS:
// src/coreclr/vm/datadescriptor/datadescriptor.inc
#ifndef FEATURE_PORTABLE_ENTRYPOINTS
CDAC_TYPE_BEGIN(PrecodeMachineDescriptor)
The lookup throws InvalidOperationException: Field not found in any layout (names=[PrecodeMachineDescriptor]). The blanket catch (System.Exception) in ValidateMethodDescPointer swallows that and returns false, so every MethodDesc that has a temporary entry point is rejected.
I confirmed this by instrumenting each return false / catch in a local build. Every frame MethodDesc on a live browser target failed with exactly this exception at the temporary-entry-point check.
Expected
With portable entry points, the temporary entry point is a PortableEntryPoint whose MethodDesc field is the owner, matching the native FEATURE_PORTABLE_ENTRYPOINTS path. IExecutionManager already reads Data.PortableEntryPoint for GetDiagnosticCodeStartFromEntryPoint (#134827). Validation should take the same path when portable entry points are in use, instead of going through precode stubs.
A secondary concern: the blanket catch converts a reader/descriptor mismatch into "invalid pointer", which hid the root cause here.
Repro
- Target: CoreCLR browser-wasm, nightly
12.0.0-alpha.1.26480.103 (runtime cd42bb5).
- Reader: cDAC built from
main @ bb1b237b389.
- At any CDP pause, walk a thread with
IStackWalk, then call RuntimeTypeSystem.GetMethodDescHandle(GetMethodDescPtr(frame)) on any frameless frame. It throws Invalid method desc pointer.
Notes
As a workaround, Blazor-Playground/nesm overrides PrecodeStubs c1 on WASM targets. The override resolves GetMethodDescFromStubAddress via PortableEntryPoint.MethodDesc. With it, R2R and interpreted frames on a live target resolve to names that match an independent static ReadyToRun (MethodDefEntryPoints) name oracle.
Found while validating a live IStackWalk consumer in Blazor-Playground/nesm; related to #130988 / #134827.
Description
On WebAssembly targets,
IRuntimeTypeSystem.GetMethodDescHandle(ptr)throwsArgumentException: Invalid method desc pointerfor valid MethodDescs. In practice that means no MethodDesc can be named on WASM, including every MethodDesc thatIStackWalk.GetMethodDescPtr/IExecutionManager.GetMethodDescreturns.Root cause
MethodValidation.ValidateMethodDescPointerchecks the temporary entry point:PrecodeStubs_1.GetMethodDescFromStubAddressneeds thePrecodeMachineDescriptordata type, which the descriptor compiles out underFEATURE_PORTABLE_ENTRYPOINTS:The lookup throws
InvalidOperationException: Field not found in any layout (names=[PrecodeMachineDescriptor]). The blanketcatch (System.Exception)inValidateMethodDescPointerswallows that and returnsfalse, so every MethodDesc that has a temporary entry point is rejected.I confirmed this by instrumenting each
return false/catchin a local build. Every frame MethodDesc on a live browser target failed with exactly this exception at the temporary-entry-point check.Expected
With portable entry points, the temporary entry point is a
PortableEntryPointwhoseMethodDescfield is the owner, matching the nativeFEATURE_PORTABLE_ENTRYPOINTSpath.IExecutionManageralready readsData.PortableEntryPointforGetDiagnosticCodeStartFromEntryPoint(#134827). Validation should take the same path when portable entry points are in use, instead of going through precode stubs.A secondary concern: the blanket catch converts a reader/descriptor mismatch into "invalid pointer", which hid the root cause here.
Repro
12.0.0-alpha.1.26480.103(runtimecd42bb5).main@bb1b237b389.IStackWalk, then callRuntimeTypeSystem.GetMethodDescHandle(GetMethodDescPtr(frame))on any frameless frame. It throwsInvalid method desc pointer.Notes
As a workaround, Blazor-Playground/nesm overrides
PrecodeStubsc1on WASM targets. The override resolvesGetMethodDescFromStubAddressviaPortableEntryPoint.MethodDesc. With it, R2R and interpreted frames on a live target resolve to names that match an independent static ReadyToRun (MethodDefEntryPoints) name oracle.Found while validating a live
IStackWalkconsumer in Blazor-Playground/nesm; related to #130988 / #134827.