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; }