CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/MIPS/MIPSStackWalk.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "Core/MemMap.h"18#include "Core/Debugger/SymbolMap.h"19#include "Core/MIPS/MIPSCodeUtils.h"20#include "Core/MIPS/MIPSStackWalk.h"2122#define _RS ((op >> 21) & 0x1F)23#define _RT ((op >> 16) & 0x1F)24#define _RD ((op >> 11) & 0x1F)25#define _IMM16 ((signed short)(op & 0xFFFF))2627#define MIPSTABLE_IMM_MASK 0xFC00000028#define MIPSTABLE_SPECIAL_MASK 0xFC00003F2930namespace MIPSStackWalk {31using namespace MIPSCodeUtils;3233// In the worst case, we scan this far above the pc for an entry.34const int MAX_FUNC_SIZE = 32768 * 4;35// After this we assume we're stuck.36const size_t MAX_DEPTH = 1024;3738static u32 GuessEntry(u32 pc) {39SymbolInfo info;40if (g_symbolMap->GetSymbolInfo(&info, pc)) {41return info.address;42}43return INVALIDTARGET;44}4546bool IsSWInstr(MIPSOpcode op) {47return (op & MIPSTABLE_IMM_MASK) == 0xAC000000;48}4950bool IsAddImmInstr(MIPSOpcode op) {51return (op & MIPSTABLE_IMM_MASK) == 0x20000000 || (op & MIPSTABLE_IMM_MASK) == 0x24000000;52}5354bool IsMovRegsInstr(MIPSOpcode op) {55// TODO: There are more options here. Let's assume addu for now.56if ((op & MIPSTABLE_SPECIAL_MASK) == 0x00000021) {57return _RS == 0 || _RT == 0;58}59return false;60}6162bool ScanForAllocaSignature(u32 pc) {63// In God Eater Burst, for example, after 0880E750, there's what looks like an alloca().64// It's surrounded by "mov fp, sp" and "mov sp, fp", which is unlikely to be used for other reasons.6566// It ought to be pretty close.67u32 stop = pc - 32 * 4;68for (; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {69MIPSOpcode op = Memory::Read_Instruction(pc, true);7071// We're looking for a "mov fp, sp" close by a "addiu sp, sp, -N".72if (IsMovRegsInstr(op) && _RD == MIPS_REG_FP && (_RS == MIPS_REG_SP || _RT == MIPS_REG_SP)) {73return true;74}75}76return false;77}7879bool ScanForEntry(StackFrame &frame, u32 entry, u32 &ra) {80// Let's hope there are no > 1MB functions on the PSP, for the sake of humanity...81const u32 LONGEST_FUNCTION = 1024 * 1024;82// TODO: Check if found entry is in the same symbol? Might be wrong sometimes...8384if (entry != INVALIDTARGET && frame.pc == entry) {85// This happens when we're at the start of a function. Our ra is already correct.86frame.entry = entry;87// This function may consume stack, but the frame hasn't used it yet.88frame.stackSize = 0;89return true;90}9192int ra_offset = -1;93// Start one instruction before the current frame pc, as that hasn't run yet.94const u32 start = frame.pc - 4;95u32 stop = entry;96if (entry == INVALIDTARGET) {97if (start >= PSP_GetUserMemoryBase()) {98stop = PSP_GetUserMemoryBase();99} else if (start >= PSP_GetKernelMemoryBase()) {100stop = PSP_GetKernelMemoryBase();101} else if (start >= PSP_GetScratchpadMemoryBase()) {102stop = PSP_GetScratchpadMemoryBase();103}104}105106if (!Memory::IsValidAddress(start)) {107return false;108}109110if (stop < start - LONGEST_FUNCTION) {111stop = start - LONGEST_FUNCTION;112}113for (u32 pc = start; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {114MIPSOpcode op = Memory::Read_Instruction(pc, true);115116// Here's where they store the ra address.117if (IsSWInstr(op) && _RT == MIPS_REG_RA && _RS == MIPS_REG_SP) {118ra_offset = _IMM16;119}120121if (IsAddImmInstr(op) && _RT == MIPS_REG_SP && _RS == MIPS_REG_SP) {122// A positive imm either means alloca() or we went too far.123if (_IMM16 > 0) {124// TODO: Maybe check for any alloca() signature and bail?125continue;126}127if (ScanForAllocaSignature(pc)) {128continue;129}130131frame.entry = pc;132frame.stackSize = -_IMM16;133if (ra_offset != -1 && Memory::IsValidAddress(frame.sp + ra_offset)) {134ra = Memory::Read_U32(frame.sp + ra_offset);135}136return true;137}138}139return false;140}141142bool DetermineFrameInfo(StackFrame &frame, u32 possibleEntry, u32 threadEntry, u32 &ra) {143if (ScanForEntry(frame, possibleEntry, ra)) {144// Awesome, found one that looks right.145return true;146} else if (ra != INVALIDTARGET && possibleEntry != INVALIDTARGET) {147// Let's just assume it's a leaf.148frame.entry = possibleEntry;149frame.stackSize = 0;150return true;151}152153// Okay, we failed to get one. Our possibleEntry could be wrong, it often is.154// Let's just scan upward.155u32 newPossibleEntry = frame.pc > threadEntry ? threadEntry : frame.pc - MAX_FUNC_SIZE;156if (ScanForEntry(frame, newPossibleEntry, ra)) {157return true;158} else {159return false;160}161}162163std::vector<StackFrame> Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop) {164std::vector<StackFrame> frames;165StackFrame current;166current.pc = pc;167current.sp = sp;168current.entry = INVALIDTARGET;169current.stackSize = -1;170171u32 prevEntry = INVALIDTARGET;172while (pc != threadEntry) {173u32 possibleEntry = GuessEntry(current.pc);174if (DetermineFrameInfo(current, possibleEntry, threadEntry, ra)) {175frames.push_back(current);176if (current.entry == threadEntry || GuessEntry(current.entry) == threadEntry) {177break;178}179if (current.entry == prevEntry || frames.size() >= MAX_DEPTH) {180// Recursion, means we're screwed. Let's just give up.181break;182}183prevEntry = current.entry;184185current.pc = ra;186current.sp += current.stackSize;187ra = INVALIDTARGET;188current.entry = INVALIDTARGET;189current.stackSize = -1;190} else {191// Well, we got as far as we could.192current.entry = possibleEntry;193current.stackSize = 0;194frames.push_back(current);195break;196}197}198199return frames;200}201};202203204