Skip to content

[cDAC][wasm] Keep caller SP for non-R2R callers and restore SoftwareExceptionFrame FP - #135494

Merged
lewing merged 7 commits into
dotnet:mainfrom
lewing:lewing-cdac-wasm-reverse-pinvoke-unwind
Oct 20, 2026
Merged

lewing merged 7 commits into
dotnet:mainfrom
lewing:lewing-cdac-wasm-reverse-pinvoke-unwind

Conversation

@lewing

@lewing lewing commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Aligns the wasm cDAC R2R unwinder with native WasmUnwindStackFrameCore and UpdateRegDisplay_Impl:

  • Non-R2R caller: keep the caller SP with a null IP and FP instead of clearing SP. Otherwise an R2R method whose active InlinedCallFrame lives in its own frame is reported repeatedly, because the walker never gets past that frame.
  • SoftwareExceptionFrame: restore FP along with IP and SP. FP (InterpreterFP) is wasm's only callee-saved register, but the CalleeSavedRegisters descriptor is empty on wasm, so FP was left over from the previous frame.
  • Inactive InlinedCallFrame: return before writing the interpreter-frame stash, as native does.

StackWalk.md is updated. The earlier reverse P/Invoke check was dropped after #135440 identified those roots from the tagged frame identity.

Not included

Note

This PR description was generated with GitHub Copilot.

lewing and others added 4 commits October 9, 2026 16:01
Mirror native RtlVirtualUnwind and WasmUnwindStackFrameCore when unwinding a
WASM ReadyToRun frame:

- Always advance SP to the caller's stack pointer. When the caller is not R2R
  code, report a null IP and FP instead of nulling SP, so the walk continues
  through the Frame chain from the right stack position.
- When the frame being unwound has a reverse P/Invoke frame in its GC info and
  is not a funclet, its caller is native code. Report no R2R caller rather than
  reading the native caller's stack bytes as an R2R frame record.

Expose GCInfoHeader.HasReversePInvokeFrame from both the common decoder and the
x86 decoder.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ntexts

Mirror native SoftwareExceptionFrame::UpdateRegDisplay_Impl and
InlinedCallFrame::UpdateRegDisplay_Impl on WASM:

- WASM's callee-saved register set is InterpreterFP, so restore IP, SP, and FP
  from a SoftwareExceptionFrame's saved context. The CalleeSavedRegisters data
  descriptor is empty on WASM, so the base handler left FP from the previous
  frame.
- Return before any update for an inactive InlinedCallFrame, including the
  interpreter-frame stash, as native does.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Follow the AMD64Context pattern: WasmContext.Unwind delegates to
WasmUnwinder.Unwind(ref WasmContext). Remove the TryUnwindOneFrame
overload without a control PC, which only tests used.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 5 pipeline(s).
11 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: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

lewing and others added 2 commits October 13, 2026 17:26
…vel test

Native RtlVirtualUnwind asserts that the control PC is an R2R virtual IP,
so WasmUnwinder.TryUnwindOneFrame now throws when it is null instead of
silently skipping the reverse P/Invoke check. Every production caller
already unwinds a managed context. Tests pass a real control PC.

Add a StackWalk test for an R2R method whose caller is not R2R code and
whose active InlinedCallFrame lives in its own frame. Keeping the caller
SP lets the walk pass the frame and report the method once; with the SP
nulled, the walk reports the method repeatedly.

Also reattach the StashesInterpreterFrame test comment that the previous
commit separated from its test.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cf17d923-d419-4c13-bcb4-094d592f8243
Main now identifies reverse P/Invoke roots from the tagged frame identity, so
the GC-info based HasReversePInvokeFrame check is dropped. What remains: keep
the caller SP when the caller is not R2R code, restore FP from a WASM
SoftwareExceptionFrame, and skip an inactive InlinedCallFrame.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lewing
lewing marked this pull request as ready for review October 16, 2026 23:51
@lewing
lewing requested review from steveisok and thaystg October 16, 2026 23:52
@lewing
lewing requested review from max-charlamb and rcj1 October 17, 2026 01:30
…andler

Drop TryUnwindOneFrame_CallerWithoutVirtualIp_PreservesCallerStackPointer: the
WasmContext_UnwindCallerWithoutVirtualIp test sets up the same frames and asserts the
same SP and IP plus FP. State the invariant in one or two lines on the new StackWalk
test and on HandleSoftwareExceptionFrame.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lewing lewing changed the title [cDAC][wasm] Stop R2R unwinding at reverse P/Invoke frames and restore exception-frame FP [cDAC][wasm] Keep caller SP for non-R2R callers and restore SoftwareExceptionFrame FP Oct 20, 2026
lewing added a commit to lewing/runtime that referenced this pull request Oct 20, 2026
Decode WASM register locations (localIndex + WASM_LOCAL_REGNUM_BASE) into
DebugVarInfo.WasmLocal/WasmLocal2 and report them as the WasmLocal and
WasmLocalPair kinds. Resolve VLT_STK / VLT_STK2 against the logical frame
pointer in the stack-walk context. Engine-private locals report no native
locations, and an unreadable or null WASM VLT_STK_BYREF indirection keeps
its location but fails reads instead of reading linear address 0.

Frame context, funclet frame pointers and function identity are provided
by dotnet#135140 (merged), dotnet#135494 and dotnet#135495.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lewing
lewing enabled auto-merge (squash) October 20, 2026 16:12
@lewing

lewing commented Oct 20, 2026

Copy link
Copy Markdown
Member Author

/ba-g ba is stuck

@lewing
lewing merged commit def2794 into dotnet:main Oct 20, 2026
86 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants