Skip to content

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base - #131243

Merged
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva
Jul 23, 2026
Merged

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base#131243
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva

Conversation

@lewing

Copy link
Copy Markdown
Member

Summary

Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled managed code participates in a stackwalk (exception unwind, GetCallersAssembly, Assembly.Load, GetExecutingAssembly, …).

1. Inline P/Invoke sentinel resolution (stackwalk.cpp, frames.h, helpers.cpp)

An R2R inline P/Invoke InlinedCallFrame 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 (this infrastructure — the sentinel assignment and InlinedCallFrame::UpdateRegDisplay_Impl — already exists on main). But StackFrameIterator::NextRaw fed that sentinel straight to ProcessIp, which cannot recognize it as managed code, so the same active frame repeated forever — an infinite loop in any stackwalk that crosses an R2R inline-P/Invoke frame.

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 moves from a helpers.cpp#define to a typed static constexpr in frames.h so the stack walker can reference it (existing helpers.cpp uses are unchanged semantically).

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 — unwind-frame RVAs decoded against the wrong base. Use the R2R image base (ReadyToRunInfo()->GetImage()->GetBase()), correct for both composite and standalone R2R.

Validation

  • WASI CoreCLR build (clr -os wasi -c Release): 0W/0E; stackwalk.cpp/helpers.cpp objects recompile clean.
  • Validated end-to-end against minimal Assembly.Load / GetExecutingAssembly R2R repros that previously looped forever in StackFrameIterator.

Note

This PR was authored with the assistance of GitHub Copilot.

…ge 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
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

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

CopilotAI left a comment

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.

Pull request overview

This PR makes two WebAssembly-only correctness fixes for ReadyToRun (R2R) stack walking: (1) properly handling the sentinel “return address” used by inline P/Invoke frames in R2R, and (2) using the correct image base when mapping unwind RVAs for composite R2R images.

Changes:

  • Resolve the wasm R2R inline-P/Invoke sentinel return address to a real virtual IP before calling ProcessIp during stack walking.
  • Move the sentinel constant to a typed static constexpr in frames.h so it’s accessible to stack-walk code.
  • Fix wasm JitTokenToModuleRVABase to use the R2R image base (not the IL module base) for RANGE_SECTION_VIRTUALIP sections.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

FileDescription
src/coreclr/vm/wasm/helpers.cppRemoves the local macro sentinel definition; continues using the sentinel for active inline P/Invoke frames.
src/coreclr/vm/stackwalk.cppAdds wasm-specific handling to translate the inline P/Invoke sentinel to a virtual IP prior to ProcessIp.
src/coreclr/vm/frames.hIntroduces a typed sentinel constant and declares GetWasmVirtualIPFromStackPointer for wasm.
src/coreclr/vm/codeman.inlUpdates wasm virtual-IP RVA base resolution to use the R2R image base address.

Comment threadsrc/coreclr/vm/stackwalk.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 23, 2026 00:02

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment threadsrc/coreclr/vm/stackwalk.cpp
@lewing
lewing requested a review from AndyAyersMSJuly 23, 2026 01:07
@lewing
lewing marked this pull request as ready for review July 23, 2026 01:13
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

CopilotAI review requested due to automatic review settings July 23, 2026 15:11

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@lewing
lewing enabled auto-merge (squash) July 23, 2026 17:31
@lewing

Copy link
Copy Markdown
MemberAuthor

/ba-g native-aot infra failures are not related

@lewing
lewing merged commit 309ac56 into dotnet:mainJul 23, 2026
118 of 127 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Jul 24, 2026
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
…ge RVA base (dotnet#131243)
## Summary
Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled
managed code participates in a stackwalk (exception unwind,
`GetCallersAssembly`, `Assembly.Load`, `GetExecutingAssembly`, …).
### 1. Inline P/Invoke sentinel resolution (`stackwalk.cpp`, `frames.h`,
`helpers.cpp`)
An R2R inline P/Invoke `InlinedCallFrame` 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`
(this infrastructure — the sentinel assignment and
`InlinedCallFrame::UpdateRegDisplay_Impl` — already exists on `main`).
But `StackFrameIterator::NextRaw` fed that sentinel straight to
`ProcessIp`, which cannot recognize it as managed code, so the same
active frame repeated forever — an infinite loop in any stackwalk that
crosses an R2R inline-P/Invoke frame.
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 moves
from a `helpers.cpp` `#define` to a typed `static constexpr` in
`frames.h` so the stack walker can reference it (existing `helpers.cpp`
uses are unchanged semantically).
### 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 — unwind-frame RVAs decoded against the wrong base. Use
the R2R image base (`ReadyToRunInfo()->GetImage()->GetBase()`), correct
for both composite and standalone R2R.
## Validation
- WASI CoreCLR build (`clr -os wasi -c Release`): 0W/0E;
`stackwalk.cpp`/`helpers.cpp` objects recompile clean.
- Validated end-to-end against minimal `Assembly.Load` /
`GetExecutingAssembly` R2R repros that previously looped forever in
`StackFrameIterator`.
> [!NOTE]
> This PR was authored with the assistance of GitHub Copilot.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@lewing@davidwrighton
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base by lewing · Pull Request #131243 · dotnet/runtime · GitHub
Skip to content

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base - #131243

Merged
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva
Jul 23, 2026
Merged

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base#131243
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva

Conversation

@lewing

Copy link
Copy Markdown
Member

Summary

Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled managed code participates in a stackwalk (exception unwind, GetCallersAssembly, Assembly.Load, GetExecutingAssembly, …).

1. Inline P/Invoke sentinel resolution (stackwalk.cpp, frames.h, helpers.cpp)

An R2R inline P/Invoke InlinedCallFrame 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 (this infrastructure — the sentinel assignment and InlinedCallFrame::UpdateRegDisplay_Impl — already exists on main). But StackFrameIterator::NextRaw fed that sentinel straight to ProcessIp, which cannot recognize it as managed code, so the same active frame repeated forever — an infinite loop in any stackwalk that crosses an R2R inline-P/Invoke frame.

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 moves from a helpers.cpp#define to a typed static constexpr in frames.h so the stack walker can reference it (existing helpers.cpp uses are unchanged semantically).

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 — unwind-frame RVAs decoded against the wrong base. Use the R2R image base (ReadyToRunInfo()->GetImage()->GetBase()), correct for both composite and standalone R2R.

Validation

  • WASI CoreCLR build (clr -os wasi -c Release): 0W/0E; stackwalk.cpp/helpers.cpp objects recompile clean.
  • Validated end-to-end against minimal Assembly.Load / GetExecutingAssembly R2R repros that previously looped forever in StackFrameIterator.

Note

This PR was authored with the assistance of GitHub Copilot.

…ge 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
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

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

CopilotAI left a comment

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.

Pull request overview

This PR makes two WebAssembly-only correctness fixes for ReadyToRun (R2R) stack walking: (1) properly handling the sentinel “return address” used by inline P/Invoke frames in R2R, and (2) using the correct image base when mapping unwind RVAs for composite R2R images.

Changes:

  • Resolve the wasm R2R inline-P/Invoke sentinel return address to a real virtual IP before calling ProcessIp during stack walking.
  • Move the sentinel constant to a typed static constexpr in frames.h so it’s accessible to stack-walk code.
  • Fix wasm JitTokenToModuleRVABase to use the R2R image base (not the IL module base) for RANGE_SECTION_VIRTUALIP sections.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

FileDescription
src/coreclr/vm/wasm/helpers.cppRemoves the local macro sentinel definition; continues using the sentinel for active inline P/Invoke frames.
src/coreclr/vm/stackwalk.cppAdds wasm-specific handling to translate the inline P/Invoke sentinel to a virtual IP prior to ProcessIp.
src/coreclr/vm/frames.hIntroduces a typed sentinel constant and declares GetWasmVirtualIPFromStackPointer for wasm.
src/coreclr/vm/codeman.inlUpdates wasm virtual-IP RVA base resolution to use the R2R image base address.

Comment threadsrc/coreclr/vm/stackwalk.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 23, 2026 00:02

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment threadsrc/coreclr/vm/stackwalk.cpp
@lewing
lewing requested a review from AndyAyersMSJuly 23, 2026 01:07
@lewing
lewing marked this pull request as ready for review July 23, 2026 01:13
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

CopilotAI review requested due to automatic review settings July 23, 2026 15:11

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@lewing
lewing enabled auto-merge (squash) July 23, 2026 17:31
@lewing

Copy link
Copy Markdown
MemberAuthor

/ba-g native-aot infra failures are not related

@lewing
lewing merged commit 309ac56 into dotnet:mainJul 23, 2026
118 of 127 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Jul 24, 2026
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
…ge RVA base (dotnet#131243)
## Summary
Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled
managed code participates in a stackwalk (exception unwind,
`GetCallersAssembly`, `Assembly.Load`, `GetExecutingAssembly`, …).
### 1. Inline P/Invoke sentinel resolution (`stackwalk.cpp`, `frames.h`,
`helpers.cpp`)
An R2R inline P/Invoke `InlinedCallFrame` 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`
(this infrastructure — the sentinel assignment and
`InlinedCallFrame::UpdateRegDisplay_Impl` — already exists on `main`).
But `StackFrameIterator::NextRaw` fed that sentinel straight to
`ProcessIp`, which cannot recognize it as managed code, so the same
active frame repeated forever — an infinite loop in any stackwalk that
crosses an R2R inline-P/Invoke frame.
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 moves
from a `helpers.cpp` `#define` to a typed `static constexpr` in
`frames.h` so the stack walker can reference it (existing `helpers.cpp`
uses are unchanged semantically).
### 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 — unwind-frame RVAs decoded against the wrong base. Use
the R2R image base (`ReadyToRunInfo()->GetImage()->GetBase()`), correct
for both composite and standalone R2R.
## Validation
- WASI CoreCLR build (`clr -os wasi -c Release`): 0W/0E;
`stackwalk.cpp`/`helpers.cpp` objects recompile clean.
- Validated end-to-end against minimal `Assembly.Load` /
`GetExecutingAssembly` R2R repros that previously looped forever in
`StackFrameIterator`.
> [!NOTE]
> This PR was authored with the assistance of GitHub Copilot.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@lewing@davidwrighton
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base by lewing · Pull Request #131243 · dotnet/runtime · GitHub
Skip to content

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base - #131243

Merged
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva
Jul 23, 2026
Merged

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base#131243
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva

Conversation

@lewing

Copy link
Copy Markdown
Member

Summary

Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled managed code participates in a stackwalk (exception unwind, GetCallersAssembly, Assembly.Load, GetExecutingAssembly, …).

1. Inline P/Invoke sentinel resolution (stackwalk.cpp, frames.h, helpers.cpp)

An R2R inline P/Invoke InlinedCallFrame 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 (this infrastructure — the sentinel assignment and InlinedCallFrame::UpdateRegDisplay_Impl — already exists on main). But StackFrameIterator::NextRaw fed that sentinel straight to ProcessIp, which cannot recognize it as managed code, so the same active frame repeated forever — an infinite loop in any stackwalk that crosses an R2R inline-P/Invoke frame.

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 moves from a helpers.cpp#define to a typed static constexpr in frames.h so the stack walker can reference it (existing helpers.cpp uses are unchanged semantically).

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 — unwind-frame RVAs decoded against the wrong base. Use the R2R image base (ReadyToRunInfo()->GetImage()->GetBase()), correct for both composite and standalone R2R.

Validation

  • WASI CoreCLR build (clr -os wasi -c Release): 0W/0E; stackwalk.cpp/helpers.cpp objects recompile clean.
  • Validated end-to-end against minimal Assembly.Load / GetExecutingAssembly R2R repros that previously looped forever in StackFrameIterator.

Note

This PR was authored with the assistance of GitHub Copilot.

…ge 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
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

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

CopilotAI left a comment

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.

Pull request overview

This PR makes two WebAssembly-only correctness fixes for ReadyToRun (R2R) stack walking: (1) properly handling the sentinel “return address” used by inline P/Invoke frames in R2R, and (2) using the correct image base when mapping unwind RVAs for composite R2R images.

Changes:

  • Resolve the wasm R2R inline-P/Invoke sentinel return address to a real virtual IP before calling ProcessIp during stack walking.
  • Move the sentinel constant to a typed static constexpr in frames.h so it’s accessible to stack-walk code.
  • Fix wasm JitTokenToModuleRVABase to use the R2R image base (not the IL module base) for RANGE_SECTION_VIRTUALIP sections.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

FileDescription
src/coreclr/vm/wasm/helpers.cppRemoves the local macro sentinel definition; continues using the sentinel for active inline P/Invoke frames.
src/coreclr/vm/stackwalk.cppAdds wasm-specific handling to translate the inline P/Invoke sentinel to a virtual IP prior to ProcessIp.
src/coreclr/vm/frames.hIntroduces a typed sentinel constant and declares GetWasmVirtualIPFromStackPointer for wasm.
src/coreclr/vm/codeman.inlUpdates wasm virtual-IP RVA base resolution to use the R2R image base address.

Comment threadsrc/coreclr/vm/stackwalk.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 23, 2026 00:02

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment threadsrc/coreclr/vm/stackwalk.cpp
@lewing
lewing requested a review from AndyAyersMSJuly 23, 2026 01:07
@lewing
lewing marked this pull request as ready for review July 23, 2026 01:13
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

CopilotAI review requested due to automatic review settings July 23, 2026 15:11

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@lewing
lewing enabled auto-merge (squash) July 23, 2026 17:31
@lewing

Copy link
Copy Markdown
MemberAuthor

/ba-g native-aot infra failures are not related

@lewing
lewing merged commit 309ac56 into dotnet:mainJul 23, 2026
118 of 127 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Jul 24, 2026
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
…ge RVA base (dotnet#131243)
## Summary
Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled
managed code participates in a stackwalk (exception unwind,
`GetCallersAssembly`, `Assembly.Load`, `GetExecutingAssembly`, …).
### 1. Inline P/Invoke sentinel resolution (`stackwalk.cpp`, `frames.h`,
`helpers.cpp`)
An R2R inline P/Invoke `InlinedCallFrame` 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`
(this infrastructure — the sentinel assignment and
`InlinedCallFrame::UpdateRegDisplay_Impl` — already exists on `main`).
But `StackFrameIterator::NextRaw` fed that sentinel straight to
`ProcessIp`, which cannot recognize it as managed code, so the same
active frame repeated forever — an infinite loop in any stackwalk that
crosses an R2R inline-P/Invoke frame.
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 moves
from a `helpers.cpp` `#define` to a typed `static constexpr` in
`frames.h` so the stack walker can reference it (existing `helpers.cpp`
uses are unchanged semantically).
### 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 — unwind-frame RVAs decoded against the wrong base. Use
the R2R image base (`ReadyToRunInfo()->GetImage()->GetBase()`), correct
for both composite and standalone R2R.
## Validation
- WASI CoreCLR build (`clr -os wasi -c Release`): 0W/0E;
`stackwalk.cpp`/`helpers.cpp` objects recompile clean.
- Validated end-to-end against minimal `Assembly.Load` /
`GetExecutingAssembly` R2R repros that previously looped forever in
`StackFrameIterator`.
> [!NOTE]
> This PR was authored with the assistance of GitHub Copilot.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@lewing@davidwrighton
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base by lewing · Pull Request #131243 · dotnet/runtime · GitHub
Skip to content

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base - #131243

Merged
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva
Jul 23, 2026
Merged

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base#131243
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva

Conversation

@lewing

Copy link
Copy Markdown
Member

Summary

Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled managed code participates in a stackwalk (exception unwind, GetCallersAssembly, Assembly.Load, GetExecutingAssembly, …).

1. Inline P/Invoke sentinel resolution (stackwalk.cpp, frames.h, helpers.cpp)

An R2R inline P/Invoke InlinedCallFrame 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 (this infrastructure — the sentinel assignment and InlinedCallFrame::UpdateRegDisplay_Impl — already exists on main). But StackFrameIterator::NextRaw fed that sentinel straight to ProcessIp, which cannot recognize it as managed code, so the same active frame repeated forever — an infinite loop in any stackwalk that crosses an R2R inline-P/Invoke frame.

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 moves from a helpers.cpp#define to a typed static constexpr in frames.h so the stack walker can reference it (existing helpers.cpp uses are unchanged semantically).

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 — unwind-frame RVAs decoded against the wrong base. Use the R2R image base (ReadyToRunInfo()->GetImage()->GetBase()), correct for both composite and standalone R2R.

Validation

  • WASI CoreCLR build (clr -os wasi -c Release): 0W/0E; stackwalk.cpp/helpers.cpp objects recompile clean.
  • Validated end-to-end against minimal Assembly.Load / GetExecutingAssembly R2R repros that previously looped forever in StackFrameIterator.

Note

This PR was authored with the assistance of GitHub Copilot.

…ge 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
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

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

CopilotAI left a comment

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.

Pull request overview

This PR makes two WebAssembly-only correctness fixes for ReadyToRun (R2R) stack walking: (1) properly handling the sentinel “return address” used by inline P/Invoke frames in R2R, and (2) using the correct image base when mapping unwind RVAs for composite R2R images.

Changes:

  • Resolve the wasm R2R inline-P/Invoke sentinel return address to a real virtual IP before calling ProcessIp during stack walking.
  • Move the sentinel constant to a typed static constexpr in frames.h so it’s accessible to stack-walk code.
  • Fix wasm JitTokenToModuleRVABase to use the R2R image base (not the IL module base) for RANGE_SECTION_VIRTUALIP sections.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

FileDescription
src/coreclr/vm/wasm/helpers.cppRemoves the local macro sentinel definition; continues using the sentinel for active inline P/Invoke frames.
src/coreclr/vm/stackwalk.cppAdds wasm-specific handling to translate the inline P/Invoke sentinel to a virtual IP prior to ProcessIp.
src/coreclr/vm/frames.hIntroduces a typed sentinel constant and declares GetWasmVirtualIPFromStackPointer for wasm.
src/coreclr/vm/codeman.inlUpdates wasm virtual-IP RVA base resolution to use the R2R image base address.

Comment threadsrc/coreclr/vm/stackwalk.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 23, 2026 00:02

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment threadsrc/coreclr/vm/stackwalk.cpp
@lewing
lewing requested a review from AndyAyersMSJuly 23, 2026 01:07
@lewing
lewing marked this pull request as ready for review July 23, 2026 01:13
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

CopilotAI review requested due to automatic review settings July 23, 2026 15:11

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@lewing
lewing enabled auto-merge (squash) July 23, 2026 17:31
@lewing

Copy link
Copy Markdown
MemberAuthor

/ba-g native-aot infra failures are not related

@lewing
lewing merged commit 309ac56 into dotnet:mainJul 23, 2026
118 of 127 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Jul 24, 2026
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
…ge RVA base (dotnet#131243)
## Summary
Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled
managed code participates in a stackwalk (exception unwind,
`GetCallersAssembly`, `Assembly.Load`, `GetExecutingAssembly`, …).
### 1. Inline P/Invoke sentinel resolution (`stackwalk.cpp`, `frames.h`,
`helpers.cpp`)
An R2R inline P/Invoke `InlinedCallFrame` 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`
(this infrastructure — the sentinel assignment and
`InlinedCallFrame::UpdateRegDisplay_Impl` — already exists on `main`).
But `StackFrameIterator::NextRaw` fed that sentinel straight to
`ProcessIp`, which cannot recognize it as managed code, so the same
active frame repeated forever — an infinite loop in any stackwalk that
crosses an R2R inline-P/Invoke frame.
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 moves
from a `helpers.cpp` `#define` to a typed `static constexpr` in
`frames.h` so the stack walker can reference it (existing `helpers.cpp`
uses are unchanged semantically).
### 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 — unwind-frame RVAs decoded against the wrong base. Use
the R2R image base (`ReadyToRunInfo()->GetImage()->GetBase()`), correct
for both composite and standalone R2R.
## Validation
- WASI CoreCLR build (`clr -os wasi -c Release`): 0W/0E;
`stackwalk.cpp`/`helpers.cpp` objects recompile clean.
- Validated end-to-end against minimal `Assembly.Load` /
`GetExecutingAssembly` R2R repros that previously looped forever in
`StackFrameIterator`.
> [!NOTE]
> This PR was authored with the assistance of GitHub Copilot.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@lewing@davidwrighton
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base by lewing · Pull Request #131243 · dotnet/runtime · GitHub
Skip to content

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base - #131243

Merged
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva
Jul 23, 2026
Merged

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base#131243
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva

Conversation

@lewing

Copy link
Copy Markdown
Member

Summary

Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled managed code participates in a stackwalk (exception unwind, GetCallersAssembly, Assembly.Load, GetExecutingAssembly, …).

1. Inline P/Invoke sentinel resolution (stackwalk.cpp, frames.h, helpers.cpp)

An R2R inline P/Invoke InlinedCallFrame 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 (this infrastructure — the sentinel assignment and InlinedCallFrame::UpdateRegDisplay_Impl — already exists on main). But StackFrameIterator::NextRaw fed that sentinel straight to ProcessIp, which cannot recognize it as managed code, so the same active frame repeated forever — an infinite loop in any stackwalk that crosses an R2R inline-P/Invoke frame.

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 moves from a helpers.cpp#define to a typed static constexpr in frames.h so the stack walker can reference it (existing helpers.cpp uses are unchanged semantically).

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 — unwind-frame RVAs decoded against the wrong base. Use the R2R image base (ReadyToRunInfo()->GetImage()->GetBase()), correct for both composite and standalone R2R.

Validation

  • WASI CoreCLR build (clr -os wasi -c Release): 0W/0E; stackwalk.cpp/helpers.cpp objects recompile clean.
  • Validated end-to-end against minimal Assembly.Load / GetExecutingAssembly R2R repros that previously looped forever in StackFrameIterator.

Note

This PR was authored with the assistance of GitHub Copilot.

…ge 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
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

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

CopilotAI left a comment

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.

Pull request overview

This PR makes two WebAssembly-only correctness fixes for ReadyToRun (R2R) stack walking: (1) properly handling the sentinel “return address” used by inline P/Invoke frames in R2R, and (2) using the correct image base when mapping unwind RVAs for composite R2R images.

Changes:

  • Resolve the wasm R2R inline-P/Invoke sentinel return address to a real virtual IP before calling ProcessIp during stack walking.
  • Move the sentinel constant to a typed static constexpr in frames.h so it’s accessible to stack-walk code.
  • Fix wasm JitTokenToModuleRVABase to use the R2R image base (not the IL module base) for RANGE_SECTION_VIRTUALIP sections.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

FileDescription
src/coreclr/vm/wasm/helpers.cppRemoves the local macro sentinel definition; continues using the sentinel for active inline P/Invoke frames.
src/coreclr/vm/stackwalk.cppAdds wasm-specific handling to translate the inline P/Invoke sentinel to a virtual IP prior to ProcessIp.
src/coreclr/vm/frames.hIntroduces a typed sentinel constant and declares GetWasmVirtualIPFromStackPointer for wasm.
src/coreclr/vm/codeman.inlUpdates wasm virtual-IP RVA base resolution to use the R2R image base address.

Comment threadsrc/coreclr/vm/stackwalk.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 23, 2026 00:02

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment threadsrc/coreclr/vm/stackwalk.cpp
@lewing
lewing requested a review from AndyAyersMSJuly 23, 2026 01:07
@lewing
lewing marked this pull request as ready for review July 23, 2026 01:13
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

CopilotAI review requested due to automatic review settings July 23, 2026 15:11

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@lewing
lewing enabled auto-merge (squash) July 23, 2026 17:31
@lewing

Copy link
Copy Markdown
MemberAuthor

/ba-g native-aot infra failures are not related

@lewing
lewing merged commit 309ac56 into dotnet:mainJul 23, 2026
118 of 127 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Jul 24, 2026
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
…ge RVA base (dotnet#131243)
## Summary
Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled
managed code participates in a stackwalk (exception unwind,
`GetCallersAssembly`, `Assembly.Load`, `GetExecutingAssembly`, …).
### 1. Inline P/Invoke sentinel resolution (`stackwalk.cpp`, `frames.h`,
`helpers.cpp`)
An R2R inline P/Invoke `InlinedCallFrame` 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`
(this infrastructure — the sentinel assignment and
`InlinedCallFrame::UpdateRegDisplay_Impl` — already exists on `main`).
But `StackFrameIterator::NextRaw` fed that sentinel straight to
`ProcessIp`, which cannot recognize it as managed code, so the same
active frame repeated forever — an infinite loop in any stackwalk that
crosses an R2R inline-P/Invoke frame.
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 moves
from a `helpers.cpp` `#define` to a typed `static constexpr` in
`frames.h` so the stack walker can reference it (existing `helpers.cpp`
uses are unchanged semantically).
### 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 — unwind-frame RVAs decoded against the wrong base. Use
the R2R image base (`ReadyToRunInfo()->GetImage()->GetBase()`), correct
for both composite and standalone R2R.
## Validation
- WASI CoreCLR build (`clr -os wasi -c Release`): 0W/0E;
`stackwalk.cpp`/`helpers.cpp` objects recompile clean.
- Validated end-to-end against minimal `Assembly.Load` /
`GetExecutingAssembly` R2R repros that previously looped forever in
`StackFrameIterator`.
> [!NOTE]
> This PR was authored with the assistance of GitHub Copilot.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@lewing@davidwrighton
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base by lewing · Pull Request #131243 · dotnet/runtime · GitHub
Skip to content

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base - #131243

Merged
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva
Jul 23, 2026
Merged

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base#131243
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva

Conversation

@lewing

Copy link
Copy Markdown
Member

Summary

Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled managed code participates in a stackwalk (exception unwind, GetCallersAssembly, Assembly.Load, GetExecutingAssembly, …).

1. Inline P/Invoke sentinel resolution (stackwalk.cpp, frames.h, helpers.cpp)

An R2R inline P/Invoke InlinedCallFrame 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 (this infrastructure — the sentinel assignment and InlinedCallFrame::UpdateRegDisplay_Impl — already exists on main). But StackFrameIterator::NextRaw fed that sentinel straight to ProcessIp, which cannot recognize it as managed code, so the same active frame repeated forever — an infinite loop in any stackwalk that crosses an R2R inline-P/Invoke frame.

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 moves from a helpers.cpp#define to a typed static constexpr in frames.h so the stack walker can reference it (existing helpers.cpp uses are unchanged semantically).

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 — unwind-frame RVAs decoded against the wrong base. Use the R2R image base (ReadyToRunInfo()->GetImage()->GetBase()), correct for both composite and standalone R2R.

Validation

  • WASI CoreCLR build (clr -os wasi -c Release): 0W/0E; stackwalk.cpp/helpers.cpp objects recompile clean.
  • Validated end-to-end against minimal Assembly.Load / GetExecutingAssembly R2R repros that previously looped forever in StackFrameIterator.

Note

This PR was authored with the assistance of GitHub Copilot.

…ge 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
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

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

CopilotAI left a comment

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.

Pull request overview

This PR makes two WebAssembly-only correctness fixes for ReadyToRun (R2R) stack walking: (1) properly handling the sentinel “return address” used by inline P/Invoke frames in R2R, and (2) using the correct image base when mapping unwind RVAs for composite R2R images.

Changes:

  • Resolve the wasm R2R inline-P/Invoke sentinel return address to a real virtual IP before calling ProcessIp during stack walking.
  • Move the sentinel constant to a typed static constexpr in frames.h so it’s accessible to stack-walk code.
  • Fix wasm JitTokenToModuleRVABase to use the R2R image base (not the IL module base) for RANGE_SECTION_VIRTUALIP sections.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

FileDescription
src/coreclr/vm/wasm/helpers.cppRemoves the local macro sentinel definition; continues using the sentinel for active inline P/Invoke frames.
src/coreclr/vm/stackwalk.cppAdds wasm-specific handling to translate the inline P/Invoke sentinel to a virtual IP prior to ProcessIp.
src/coreclr/vm/frames.hIntroduces a typed sentinel constant and declares GetWasmVirtualIPFromStackPointer for wasm.
src/coreclr/vm/codeman.inlUpdates wasm virtual-IP RVA base resolution to use the R2R image base address.

Comment threadsrc/coreclr/vm/stackwalk.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 23, 2026 00:02

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment threadsrc/coreclr/vm/stackwalk.cpp
@lewing
lewing requested a review from AndyAyersMSJuly 23, 2026 01:07
@lewing
lewing marked this pull request as ready for review July 23, 2026 01:13
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

CopilotAI review requested due to automatic review settings July 23, 2026 15:11

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@lewing
lewing enabled auto-merge (squash) July 23, 2026 17:31
@lewing

Copy link
Copy Markdown
MemberAuthor

/ba-g native-aot infra failures are not related

@lewing
lewing merged commit 309ac56 into dotnet:mainJul 23, 2026
118 of 127 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Jul 24, 2026
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
…ge RVA base (dotnet#131243)
## Summary
Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled
managed code participates in a stackwalk (exception unwind,
`GetCallersAssembly`, `Assembly.Load`, `GetExecutingAssembly`, …).
### 1. Inline P/Invoke sentinel resolution (`stackwalk.cpp`, `frames.h`,
`helpers.cpp`)
An R2R inline P/Invoke `InlinedCallFrame` 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`
(this infrastructure — the sentinel assignment and
`InlinedCallFrame::UpdateRegDisplay_Impl` — already exists on `main`).
But `StackFrameIterator::NextRaw` fed that sentinel straight to
`ProcessIp`, which cannot recognize it as managed code, so the same
active frame repeated forever — an infinite loop in any stackwalk that
crosses an R2R inline-P/Invoke frame.
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 moves
from a `helpers.cpp` `#define` to a typed `static constexpr` in
`frames.h` so the stack walker can reference it (existing `helpers.cpp`
uses are unchanged semantically).
### 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 — unwind-frame RVAs decoded against the wrong base. Use
the R2R image base (`ReadyToRunInfo()->GetImage()->GetBase()`), correct
for both composite and standalone R2R.
## Validation
- WASI CoreCLR build (`clr -os wasi -c Release`): 0W/0E;
`stackwalk.cpp`/`helpers.cpp` objects recompile clean.
- Validated end-to-end against minimal `Assembly.Load` /
`GetExecutingAssembly` R2R repros that previously looped forever in
`StackFrameIterator`.
> [!NOTE]
> This PR was authored with the assistance of GitHub Copilot.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@lewing@davidwrighton
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base by lewing · Pull Request #131243 · dotnet/runtime · GitHub
Skip to content

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base - #131243

Merged
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva
Jul 23, 2026
Merged

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base#131243
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva

Conversation

@lewing

Copy link
Copy Markdown
Member

Summary

Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled managed code participates in a stackwalk (exception unwind, GetCallersAssembly, Assembly.Load, GetExecutingAssembly, …).

1. Inline P/Invoke sentinel resolution (stackwalk.cpp, frames.h, helpers.cpp)

An R2R inline P/Invoke InlinedCallFrame 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 (this infrastructure — the sentinel assignment and InlinedCallFrame::UpdateRegDisplay_Impl — already exists on main). But StackFrameIterator::NextRaw fed that sentinel straight to ProcessIp, which cannot recognize it as managed code, so the same active frame repeated forever — an infinite loop in any stackwalk that crosses an R2R inline-P/Invoke frame.

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 moves from a helpers.cpp#define to a typed static constexpr in frames.h so the stack walker can reference it (existing helpers.cpp uses are unchanged semantically).

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 — unwind-frame RVAs decoded against the wrong base. Use the R2R image base (ReadyToRunInfo()->GetImage()->GetBase()), correct for both composite and standalone R2R.

Validation

  • WASI CoreCLR build (clr -os wasi -c Release): 0W/0E; stackwalk.cpp/helpers.cpp objects recompile clean.
  • Validated end-to-end against minimal Assembly.Load / GetExecutingAssembly R2R repros that previously looped forever in StackFrameIterator.

Note

This PR was authored with the assistance of GitHub Copilot.

…ge 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
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

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

CopilotAI left a comment

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.

Pull request overview

This PR makes two WebAssembly-only correctness fixes for ReadyToRun (R2R) stack walking: (1) properly handling the sentinel “return address” used by inline P/Invoke frames in R2R, and (2) using the correct image base when mapping unwind RVAs for composite R2R images.

Changes:

  • Resolve the wasm R2R inline-P/Invoke sentinel return address to a real virtual IP before calling ProcessIp during stack walking.
  • Move the sentinel constant to a typed static constexpr in frames.h so it’s accessible to stack-walk code.
  • Fix wasm JitTokenToModuleRVABase to use the R2R image base (not the IL module base) for RANGE_SECTION_VIRTUALIP sections.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

FileDescription
src/coreclr/vm/wasm/helpers.cppRemoves the local macro sentinel definition; continues using the sentinel for active inline P/Invoke frames.
src/coreclr/vm/stackwalk.cppAdds wasm-specific handling to translate the inline P/Invoke sentinel to a virtual IP prior to ProcessIp.
src/coreclr/vm/frames.hIntroduces a typed sentinel constant and declares GetWasmVirtualIPFromStackPointer for wasm.
src/coreclr/vm/codeman.inlUpdates wasm virtual-IP RVA base resolution to use the R2R image base address.

Comment threadsrc/coreclr/vm/stackwalk.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 23, 2026 00:02

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment threadsrc/coreclr/vm/stackwalk.cpp
@lewing
lewing requested a review from AndyAyersMSJuly 23, 2026 01:07
@lewing
lewing marked this pull request as ready for review July 23, 2026 01:13
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

CopilotAI review requested due to automatic review settings July 23, 2026 15:11

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@lewing
lewing enabled auto-merge (squash) July 23, 2026 17:31
@lewing

Copy link
Copy Markdown
MemberAuthor

/ba-g native-aot infra failures are not related

@lewing
lewing merged commit 309ac56 into dotnet:mainJul 23, 2026
118 of 127 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Jul 24, 2026
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
…ge RVA base (dotnet#131243)
## Summary
Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled
managed code participates in a stackwalk (exception unwind,
`GetCallersAssembly`, `Assembly.Load`, `GetExecutingAssembly`, …).
### 1. Inline P/Invoke sentinel resolution (`stackwalk.cpp`, `frames.h`,
`helpers.cpp`)
An R2R inline P/Invoke `InlinedCallFrame` 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`
(this infrastructure — the sentinel assignment and
`InlinedCallFrame::UpdateRegDisplay_Impl` — already exists on `main`).
But `StackFrameIterator::NextRaw` fed that sentinel straight to
`ProcessIp`, which cannot recognize it as managed code, so the same
active frame repeated forever — an infinite loop in any stackwalk that
crosses an R2R inline-P/Invoke frame.
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 moves
from a `helpers.cpp` `#define` to a typed `static constexpr` in
`frames.h` so the stack walker can reference it (existing `helpers.cpp`
uses are unchanged semantically).
### 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 — unwind-frame RVAs decoded against the wrong base. Use
the R2R image base (`ReadyToRunInfo()->GetImage()->GetBase()`), correct
for both composite and standalone R2R.
## Validation
- WASI CoreCLR build (`clr -os wasi -c Release`): 0W/0E;
`stackwalk.cpp`/`helpers.cpp` objects recompile clean.
- Validated end-to-end against minimal `Assembly.Load` /
`GetExecutingAssembly` R2R repros that previously looped forever in
`StackFrameIterator`.
> [!NOTE]
> This PR was authored with the assistance of GitHub Copilot.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@lewing@davidwrighton
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base by lewing · Pull Request #131243 · dotnet/runtime · GitHub
Skip to content

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base - #131243

Merged
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva
Jul 23, 2026
Merged

[wasm] Fix R2R stackwalk for inline P/Invoke frames and composite-image RVA base#131243
lewing merged 3 commits into
dotnet:mainfrom
lewing:wasi-r2r-stackwalk-rva

Conversation

@lewing

Copy link
Copy Markdown
Member

Summary

Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled managed code participates in a stackwalk (exception unwind, GetCallersAssembly, Assembly.Load, GetExecutingAssembly, …).

1. Inline P/Invoke sentinel resolution (stackwalk.cpp, frames.h, helpers.cpp)

An R2R inline P/Invoke InlinedCallFrame 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 (this infrastructure — the sentinel assignment and InlinedCallFrame::UpdateRegDisplay_Impl — already exists on main). But StackFrameIterator::NextRaw fed that sentinel straight to ProcessIp, which cannot recognize it as managed code, so the same active frame repeated forever — an infinite loop in any stackwalk that crosses an R2R inline-P/Invoke frame.

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 moves from a helpers.cpp#define to a typed static constexpr in frames.h so the stack walker can reference it (existing helpers.cpp uses are unchanged semantically).

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 — unwind-frame RVAs decoded against the wrong base. Use the R2R image base (ReadyToRunInfo()->GetImage()->GetBase()), correct for both composite and standalone R2R.

Validation

  • WASI CoreCLR build (clr -os wasi -c Release): 0W/0E; stackwalk.cpp/helpers.cpp objects recompile clean.
  • Validated end-to-end against minimal Assembly.Load / GetExecutingAssembly R2R repros that previously looped forever in StackFrameIterator.

Note

This PR was authored with the assistance of GitHub Copilot.

…ge 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
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

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

CopilotAI left a comment

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.

Pull request overview

This PR makes two WebAssembly-only correctness fixes for ReadyToRun (R2R) stack walking: (1) properly handling the sentinel “return address” used by inline P/Invoke frames in R2R, and (2) using the correct image base when mapping unwind RVAs for composite R2R images.

Changes:

  • Resolve the wasm R2R inline-P/Invoke sentinel return address to a real virtual IP before calling ProcessIp during stack walking.
  • Move the sentinel constant to a typed static constexpr in frames.h so it’s accessible to stack-walk code.
  • Fix wasm JitTokenToModuleRVABase to use the R2R image base (not the IL module base) for RANGE_SECTION_VIRTUALIP sections.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

FileDescription
src/coreclr/vm/wasm/helpers.cppRemoves the local macro sentinel definition; continues using the sentinel for active inline P/Invoke frames.
src/coreclr/vm/stackwalk.cppAdds wasm-specific handling to translate the inline P/Invoke sentinel to a virtual IP prior to ProcessIp.
src/coreclr/vm/frames.hIntroduces a typed sentinel constant and declares GetWasmVirtualIPFromStackPointer for wasm.
src/coreclr/vm/codeman.inlUpdates wasm virtual-IP RVA base resolution to use the R2R image base address.

Comment threadsrc/coreclr/vm/stackwalk.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 23, 2026 00:02

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment threadsrc/coreclr/vm/stackwalk.cpp
@lewing
lewing requested a review from AndyAyersMSJuly 23, 2026 01:07
@lewing
lewing marked this pull request as ready for review July 23, 2026 01:13
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

CopilotAI review requested due to automatic review settings July 23, 2026 15:11

CopilotAI left a comment

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@lewing
lewing enabled auto-merge (squash) July 23, 2026 17:31
@lewing

Copy link
Copy Markdown
MemberAuthor

/ba-g native-aot infra failures are not related

@lewing
lewing merged commit 309ac56 into dotnet:mainJul 23, 2026
118 of 127 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc1 milestone Jul 24, 2026
hez2010 pushed a commit to hez2010/runtime that referenced this pull request Jul 26, 2026
…ge RVA base (dotnet#131243)
## Summary
Two wasm-only R2R stackwalk correctness fixes, needed once R2R-compiled
managed code participates in a stackwalk (exception unwind,
`GetCallersAssembly`, `Assembly.Load`, `GetExecutingAssembly`, …).
### 1. Inline P/Invoke sentinel resolution (`stackwalk.cpp`, `frames.h`,
`helpers.cpp`)
An R2R inline P/Invoke `InlinedCallFrame` 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`
(this infrastructure — the sentinel assignment and
`InlinedCallFrame::UpdateRegDisplay_Impl` — already exists on `main`).
But `StackFrameIterator::NextRaw` fed that sentinel straight to
`ProcessIp`, which cannot recognize it as managed code, so the same
active frame repeated forever — an infinite loop in any stackwalk that
crosses an R2R inline-P/Invoke frame.
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 moves
from a `helpers.cpp` `#define` to a typed `static constexpr` in
`frames.h` so the stack walker can reference it (existing `helpers.cpp`
uses are unchanged semantically).
### 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 — unwind-frame RVAs decoded against the wrong base. Use
the R2R image base (`ReadyToRunInfo()->GetImage()->GetBase()`), correct
for both composite and standalone R2R.
## Validation
- WASI CoreCLR build (`clr -os wasi -c Release`): 0W/0E;
`stackwalk.cpp`/`helpers.cpp` objects recompile clean.
- Validated end-to-end against minimal `Assembly.Load` /
`GetExecutingAssembly` R2R repros that previously looped forever in
`StackFrameIterator`.
> [!NOTE]
> This PR was authored with the assistance of GitHub Copilot.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot-Session: 2902374a-f352-4c46-bcec-8a35c97733ae
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Aug 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@lewing@davidwrighton