fix(stepping): Step Over no longer runs past a procedure's own prologue - #25
Merged
ClarionLive merged 2 commits intoAug 21, 2026
Conversation
…rionLive#19, ClarionLive#21) BuildSymbols previously rejected nameRef == 0 as invalid, silently dropping _main's definition (its record legitimately points at pool offset 0) and leaving stack frames in _main mis-bound to the preceding module's last symbol, with a phantom SELF local appearing. Also adds a name-recovery heuristic for GROUP members whose nameRef is zeroed by the compiler despite a valid pool string existing (e.g. StringTheory.Value), recovering ~99.5% of confirmed cases by reading backward from a known-good neighbor. Adds temporary diagnostic CLI commands (scanrva, scanname, typemembers, typechain, poolback, scanmissingnames, surveymissingnames) used to investigate both issues, plus related locals/stack-walker/eval fixes and debugger.html/webview tweaks. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both StepMode.Over (source-level) and StepMode.OverInstr (disassembly
"nexti") gated their stop decision on `esp + ESP_SLACK >= _startEsp`,
with ESP_SLACK a fixed 0x10 bytes -- enough only for alignment noise
between statements at the same frame depth. When a step began exactly
at (or within a callee's PROLOGUE_WINDOW of) a procedure's raw entry --
precisely where a prior Step Into lands, since Into's own stop check is
ESP-agnostic -- the callee's own prologue (whether spread across
`push ebp/mov ebp,esp/sub esp,N`, or folded into a single `ENTER N,0`)
legitimately drops ESP by N bytes reserving its local frame, almost
always far more than 0x10. That made the ESP gate reject every
candidate stop point inside the procedure body, so Over/OverInstr never
found a valid stop and the call-skip machinery (meant to hop over
local-object constructor calls) ran the whole procedure to completion
instead.
Added `_startAtProcEntry`, computed once in BeginStep via
ResolveSymbol + a PROLOGUE_WINDOW-fuzzy range check (not strict
entry-address equality, since a symbol's EntryRva can sit below its
first genuine line record). Both Over's and OverInstr's stop checks now
bypass the ESP gate for the one step session that began inside a
procedure's own prologue.
Separately, fix a real breakpoint left un-restored: the disassembly
view's raw single-instruction step ("stepi", StepMode-independent)
paused via PausedWait directly, without the "landed exactly on an armed
breakpoint -> restore original byte + reschedule rearm" handling every
other stepping path gets via StopStepAndPause. When stepping into a
procedure whose entry also carries a user breakpoint, the still-planted
0xCC then fired as a genuine EXCEPTION_BREAKPOINT on the very next
step/resume, reporting reason "breakpoint" instead of "stepi" and
spuriously jumping the host UI to source. Extracted the restore logic
into a shared RestoreIfArmed(tid, va) helper used by both
StopStepAndPause and the stepi path.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
geircodes
force-pushed
the
fix/step-over-prologue-and-armed-bp-restore
branch
from
July 27, 2026 14:12
2e57587 to
fef16da
Compare
ClarionLive
pushed a commit
that referenced
this pull request
Aug 21, 2026
…#19, #21) Lands the commit shared by PRs #23, #24 and #25 on its own, so each of those three collapses to just its unique commit instead of carrying ~1100 duplicated lines. Fixes: - #19 phantom SELF local in main() — BuildSymbols' `nameRef < 1` guard dropped _main's definition record (which legitimately points at pool offset 0), leaving _main a symbol-less hole that stack frames mis-bound to the previous module's last method. Adds ResolveSymbolVerified, cross-checking a candidate against the +0x1C line table rather than the non-comparable +0x28 moduleIdx. - #21 (unnamed+N) GROUP members, e.g. StringTheory.Value — recovers the name by reading backward in the symbol pool from the group's smallest-mNameRef member, with duplicate / '@' / '$' rejection. ~99.5% of confirmed cases. Also carries temporary diagnostic CLI commands used in the investigation, related locals/stack-walker/eval fixes, and debugger.html/webview tweaks. Original author: Geir Stale Eidissen <ML-DEV-GSE@ml.no> Verified: ClarionDbg.Cli + ClarionDebugger.Addin build clean (0 warnings). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix Step Over running past a procedure's own prologue, and a stale breakpoint byte on
stepiSummary
stepoverand disassemblynexti) ran an entire procedure to completion instead of stopping at the next line/instruction, whenever the step began exactly at a procedure's raw entry point (i.e. right after a prior Step Into landed there). Root cause: the ESP-depth gate in IsStepStop used a fixed 0x10-byte slack, far smaller than the local-frame reservation a procedure's own prologue performs (push ebp/mov ebp,esp/sub esp,N, or a foldedENTER N,0), so every statement in the body looked "too deep" and never qualified as a stop.Changes
BeginStepcomputes_startAtProcEntry(ResolveSymbol + a PROLOGUE_WINDOW-fuzzy range check against the entry RVA).IsStepStop'sStepMode.OverandStepMode.OverInstrcases bypass the ESP gate when_startAtProcEntryis set.RestoreIfArmed(tid, va), used by bothStopStepAndPauseand the rawstepipath inOnSingleStep.Files:
src/ClarionDbg.Cli/DebugEngine.cs,src/ClarionDbg.Cli/DebugEngine.Stepping.csTest plan
dotnet buildon ClarionDbg.Cli)ENTER N,0prologue, confirmed Step Over now stops per line/instruction instead of running to completion