From 948852130a4187d12049d0d4c2c6d0fc06994b4d Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Wed, 22 Jul 2026 18:31:52 -0500 Subject: [PATCH 1/2] [wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base Two wasm-only stackwalk correctness fixes, needed once R2R-compiled managed code participates in a stackwalk (exception unwind, GetCallersAssembly, Assembly.Load): 1. Inline-P/Invoke sentinel resolution (stackwalk.cpp, frames.h, helpers.cpp): An R2R inline P/Invoke frame carries the sentinel return address INLINED_PINVOKE_FROM_R2R (== 1) instead of a real native address; the real caller virtual IP is derived from m_pCallSiteSP. StackFrameIterator::NextRaw fed that sentinel straight to ProcessIp, which cannot recognize it as managed code, so the same active frame repeated forever. Resolve the sentinel to the caller virtual IP via GetWasmVirtualIPFromStackPointer before ProcessIp, failing the walk (SWA_FAILED) if the lookup returns null. The sentinel constant is moved from a helpers.cpp #define into frames.h so the stack walker can reference it. 2. Composite R2R image RVA base (codeman.inl): JitTokenToModuleRVABase resolved wasm virtual-IP ranges against the IL module base (GetModuleBaseAddress()), which is wrong for a composite R2R (WbIL) image. Use the R2R image base (ReadyToRunInfo()->GetImage()->GetBase()) so unwind-frame RVAs decode against the correct composite base. Verified: WASI CoreCLR build (clr -os wasi -c Release) 0W/0E; validated end-to-end against minimal Assembly.Load / GetExecutingAssembly R2R repros that previously looped forever in StackFrameIterator. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae --- src/coreclr/vm/codeman.inl | 2 +- src/coreclr/vm/frames.h | 3 +++ src/coreclr/vm/stackwalk.cpp | 12 ++++++++++++ src/coreclr/vm/wasm/helpers.cpp | 1 - 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/codeman.inl b/src/coreclr/vm/codeman.inl index 099ff1cc13128d..9f70457a42a3e3 100644 --- a/src/coreclr/vm/codeman.inl +++ b/src/coreclr/vm/codeman.inl @@ -13,7 +13,7 @@ inline TADDR IJitManager::JitTokenToModuleRVABase(const METHODTOKEN& MethodToken { #ifdef TARGET_WASM if (MethodToken.m_pRangeSection->_flags & RangeSection::RANGE_SECTION_VIRTUALIP) - return (TADDR)MethodToken.m_pRangeSection->_pR2RModule->GetModuleBaseAddress(); + return dac_cast(MethodToken.m_pRangeSection->_pR2RModule->GetReadyToRunInfo()->GetImage()->GetBase()); #endif // For non-wasm, the rva base is always the same as the range base. return MethodToken.m_pRangeSection->_range.RangeStart(); diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index dc1a954604369f..856a9270d0fffd 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -791,6 +791,9 @@ inline CONTEXT * GETREDIRECTEDCONTEXT(Thread * thread) { LIMITED_METHOD_CONTRACT typedef DPTR(class TransitionFrame) PTR_TransitionFrame; #ifdef TARGET_WASM +// Wasm has no native return address for an R2R inline P/Invoke. This sentinel marks the frame as +// active; stack walkers recover the caller virtual IP from m_pCallSiteSP. +static constexpr TADDR INLINED_PINVOKE_FROM_R2R = 1; TADDR GetWasmVirtualIPFromStackPointer(TADDR sp); #endif diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 6cce2efdd31108..a42e0711b1f523 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -2330,6 +2330,18 @@ StackWalkAction StackFrameIterator::NextRaw(void) _ASSERTE(!pInlinedFrame || adr); +#ifdef TARGET_WASM + if ((pInlinedFrame != NULL) && (adr == (PCODE)INLINED_PINVOKE_FROM_R2R)) + { + adr = GetWasmVirtualIPFromStackPointer((TADDR)((InlinedCallFrame*)pInlinedFrame)->m_pCallSiteSP); + if (adr == (PCODE)NULL) + { + retVal = SWA_FAILED; + goto Cleanup; + } + } +#endif // TARGET_WASM + if (adr) { ProcessIp(adr); diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index 5295261aa3b27d..dbfb436c627c25 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -13,7 +13,6 @@ #define WASM_STRINGIFY_HELPER(value) #value #define WASM_STRINGIFY(value) WASM_STRINGIFY_HELPER(value) -#define INLINED_PINVOKE_FROM_R2R 1 void ExecuteInterpretedMethodWithArgs_PortableEntryPoint(PCODE portableEntrypoint, TransitionBlock* block, size_t argsSize, int8_t* retBuff); From 93836f451f78896360d8000e82721aff8cb3d46b Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Wed, 22 Jul 2026 19:02:05 -0500 Subject: [PATCH 2/2] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/coreclr/vm/stackwalk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index a42e0711b1f523..cda43ae7f97cdb 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -2333,7 +2333,7 @@ StackWalkAction StackFrameIterator::NextRaw(void) #ifdef TARGET_WASM if ((pInlinedFrame != NULL) && (adr == (PCODE)INLINED_PINVOKE_FROM_R2R)) { - adr = GetWasmVirtualIPFromStackPointer((TADDR)((InlinedCallFrame*)pInlinedFrame)->m_pCallSiteSP); + adr = GetWasmVirtualIPFromStackPointer(dac_cast(((InlinedCallFrame*)pInlinedFrame)->GetCallSiteSP())); if (adr == (PCODE)NULL) { retVal = SWA_FAILED;