From 024bed60658bd6534e405693860b00dcc1786e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 19 Oct 2026 11:04:57 +0200 Subject: [PATCH] Stack walk: Report a function's symbol start as its entry, not its prologue The scan for the prologue took the addiu sp as the entry, which is past the start when the function does something first (a lui, a move). Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/MIPS/MIPSStackWalk.cpp | 5 ++++- unittest/UnitTest.cpp | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Core/MIPS/MIPSStackWalk.cpp b/Core/MIPS/MIPSStackWalk.cpp index 46d42e8245..09c2e96e93 100644 --- a/Core/MIPS/MIPSStackWalk.cpp +++ b/Core/MIPS/MIPSStackWalk.cpp @@ -145,7 +145,10 @@ namespace MIPSStackWalk { continue; } - frame.entry = pc; + // The prologue often isn't the first instruction (a lui or move can come first), so + // the function's symbol, if it has one, gives the real entry. + const u32 symbolEntry = GuessEntry(pc); + frame.entry = symbolEntry != INVALIDTARGET ? symbolEntry : pc; frame.stackSize = -_IMM16; if (ra_offset != -1 && Memory::IsValid4AlignedAddress(frame.sp + ra_offset)) { ra = Memory::ReadUnchecked_U32(frame.sp + ra_offset); diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index fe424fc8c0..825baf0baf 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -2501,6 +2501,29 @@ static bool CheckStackWalk() { EXPECT_EQ_HEX(frames[1].pc, caller + 0x10); EXPECT_EQ_HEX(frames[1].entry, caller); EXPECT_EQ_INT(frames[1].stackSize, 16); + + // With a symbol, the entry is where it starts, even when the prologue isn't first. + const u32 func = 0x08805000; + const u32 funcCode[] = { + 0x3C080000, // func: lui t0, 0 + 0x27BDFFF0, // addiu sp, sp, -16 + 0xAFBF000C, // sw ra, 12(sp) + 0x00000000, // nop + }; + for (size_t i = 0; i < ARRAY_SIZE(funcCode); i++) { + Memory::WriteUnchecked_U32(funcCode[i], func + (u32)i * 4); + } + Memory::WriteUnchecked_U32(caller + 0x10, sp + 12); + symbolMap.AddFunction("func", func, sizeof(funcCode)); + symbolMap.SortSymbols(); + g_symbolMap = &symbolMap; + frames = MIPSStackWalk::Walk(func + 0xC, caller + 0x10, sp, caller, 0x09F00000); + g_symbolMap = nullptr; + + EXPECT_EQ_INT((int)frames.size(), 2); + EXPECT_EQ_HEX(frames[0].entry, func); + EXPECT_EQ_INT(frames[0].stackSize, 16); + EXPECT_EQ_HEX(frames[1].pc, caller + 0x10); return true; }