~ppsspp/ppsspp/ppsspp_1.3.0

« back to all changes in this revision

Viewing changes to Core/MIPS/MIPSStackWalk.cpp

  • Committer: Sérgio Benjamim
  • Date: 2017-01-02 00:12:05 UTC
  • Revision ID: sergio_br2@yahoo.com.br-20170102001205-cxbta9za203nmjwm
1.3.0 source (from ppsspp_1.3.0-r160.p5.l1762.a165.t83~56~ubuntu16.04.1.tar.xz).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2012- PPSSPP Project.
 
2
 
 
3
// This program is free software: you can redistribute it and/or modify
 
4
// it under the terms of the GNU General Public License as published by
 
5
// the Free Software Foundation, version 2.0 or later versions.
 
6
 
 
7
// This program is distributed in the hope that it will be useful,
 
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
// GNU General Public License 2.0 for more details.
 
11
 
 
12
// A copy of the GPL 2.0 should have been included with the program.
 
13
// If not, see http://www.gnu.org/licenses/
 
14
 
 
15
// Official git repository and contact information can be found at
 
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
 
17
 
 
18
#include "Core/MemMap.h"
 
19
#include "Core/Debugger/SymbolMap.h"
 
20
#include "Core/MIPS/MIPSCodeUtils.h"
 
21
#include "Core/MIPS/MIPSStackWalk.h"
 
22
 
 
23
#define _RS ((op >> 21) & 0x1F)
 
24
#define _RT ((op >> 16) & 0x1F)
 
25
#define _RD ((op >> 11) & 0x1F)
 
26
#define _IMM16 ((signed short)(op & 0xFFFF))
 
27
 
 
28
#define MIPSTABLE_IMM_MASK 0xFC000000
 
29
#define MIPSTABLE_SPECIAL_MASK 0xFC00003F
 
30
 
 
31
namespace MIPSStackWalk {
 
32
        using namespace MIPSCodeUtils;
 
33
 
 
34
        // In the worst case, we scan this far above the pc for an entry.
 
35
        const int MAX_FUNC_SIZE = 32768 * 4;
 
36
        // After this we assume we're stuck.
 
37
        const size_t MAX_DEPTH = 1024;
 
38
 
 
39
        static u32 GuessEntry(u32 pc) {
 
40
                SymbolInfo info;
 
41
                if (g_symbolMap->GetSymbolInfo(&info, pc)) {
 
42
                        return info.address;
 
43
                }
 
44
                return INVALIDTARGET;
 
45
        }
 
46
 
 
47
        bool IsSWInstr(MIPSOpcode op) {
 
48
                return (op & MIPSTABLE_IMM_MASK) == 0xAC000000;
 
49
        }
 
50
 
 
51
        bool IsAddImmInstr(MIPSOpcode op) {
 
52
                return (op & MIPSTABLE_IMM_MASK) == 0x20000000 || (op & MIPSTABLE_IMM_MASK) == 0x24000000;
 
53
        }
 
54
 
 
55
        bool IsMovRegsInstr(MIPSOpcode op) {
 
56
                // TODO: There are more options here.  Let's assume addu for now.
 
57
                if ((op & MIPSTABLE_SPECIAL_MASK) == 0x00000021) {
 
58
                        return _RS == 0 || _RT == 0;
 
59
                }
 
60
                return false;
 
61
        }
 
62
 
 
63
        bool ScanForAllocaSignature(u32 pc) {
 
64
                // In God Eater Burst, for example, after 0880E750, there's what looks like an alloca().
 
65
                // It's surrounded by "mov fp, sp" and "mov sp, fp", which is unlikely to be used for other reasons.
 
66
 
 
67
                // It ought to be pretty close.
 
68
                u32 stop = pc - 32 * 4;
 
69
                for (; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
 
70
                        MIPSOpcode op = Memory::Read_Instruction(pc, true);
 
71
 
 
72
                        // We're looking for a "mov fp, sp" close by a "addiu sp, sp, -N".
 
73
                        if (IsMovRegsInstr(op) && _RD == MIPS_REG_FP && (_RS == MIPS_REG_SP || _RT == MIPS_REG_SP)) {
 
74
                                return true;
 
75
                        }
 
76
                }
 
77
                return false;
 
78
        }
 
79
 
 
80
        bool ScanForEntry(StackFrame &frame, u32 entry, u32 &ra) {
 
81
                // Let's hope there are no > 1MB functions on the PSP, for the sake of humanity...
 
82
                const u32 LONGEST_FUNCTION = 1024 * 1024;
 
83
                // TODO: Check if found entry is in the same symbol?  Might be wrong sometimes...
 
84
 
 
85
                int ra_offset = -1;
 
86
                const u32 start = frame.pc;
 
87
                u32 stop = entry;
 
88
                if (entry == INVALIDTARGET) {
 
89
                        if (start >= PSP_GetUserMemoryBase()) {
 
90
                                stop = PSP_GetUserMemoryBase();
 
91
                        } else if (start >= PSP_GetKernelMemoryBase()) {
 
92
                                stop = PSP_GetKernelMemoryBase();
 
93
                        } else if (start >= PSP_GetScratchpadMemoryBase()) {
 
94
                                stop = PSP_GetScratchpadMemoryBase();
 
95
                        }
 
96
                }
 
97
                if (stop < start - LONGEST_FUNCTION) {
 
98
                        stop = start - LONGEST_FUNCTION;
 
99
                }
 
100
                for (u32 pc = start; Memory::IsValidAddress(pc) && pc >= stop; pc -= 4) {
 
101
                        MIPSOpcode op = Memory::Read_Instruction(pc, true);
 
102
 
 
103
                        // Here's where they store the ra address.
 
104
                        if (IsSWInstr(op) && _RT == MIPS_REG_RA && _RS == MIPS_REG_SP) {
 
105
                                ra_offset = _IMM16;
 
106
                        }
 
107
 
 
108
                        if (IsAddImmInstr(op) && _RT == MIPS_REG_SP && _RS == MIPS_REG_SP) {
 
109
                                // A positive imm either means alloca() or we went too far.
 
110
                                if (_IMM16 > 0) {
 
111
                                        // TODO: Maybe check for any alloca() signature and bail?
 
112
                                        continue;
 
113
                                }
 
114
                                if (ScanForAllocaSignature(pc)) {
 
115
                                        continue;
 
116
                                }
 
117
 
 
118
                                frame.entry = pc;
 
119
                                frame.stackSize = -_IMM16;
 
120
                                if (ra_offset != -1 && Memory::IsValidAddress(frame.sp + ra_offset)) {
 
121
                                        ra = Memory::Read_U32(frame.sp + ra_offset);
 
122
                                }
 
123
                                return true;
 
124
                        }
 
125
                }
 
126
                return false;
 
127
        }
 
128
 
 
129
        bool DetermineFrameInfo(StackFrame &frame, u32 possibleEntry, u32 threadEntry, u32 &ra) {
 
130
                if (ScanForEntry(frame, possibleEntry, ra)) {
 
131
                        // Awesome, found one that looks right.
 
132
                        return true;
 
133
                } else if (ra != INVALIDTARGET && possibleEntry != INVALIDTARGET) {
 
134
                        // Let's just assume it's a leaf.
 
135
                        frame.entry = possibleEntry;
 
136
                        frame.stackSize = 0;
 
137
                        return true;
 
138
                }
 
139
 
 
140
                // Okay, we failed to get one.  Our possibleEntry could be wrong, it often is.
 
141
                // Let's just scan upward.
 
142
                u32 newPossibleEntry = frame.pc > threadEntry ? threadEntry : frame.pc - MAX_FUNC_SIZE;
 
143
                if (ScanForEntry(frame, newPossibleEntry, ra)) {
 
144
                        return true;
 
145
                } else {
 
146
                        return false;
 
147
                }
 
148
        }
 
149
 
 
150
        std::vector<StackFrame> Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop) {
 
151
                std::vector<StackFrame> frames;
 
152
                StackFrame current;
 
153
                current.pc = pc;
 
154
                current.sp = sp;
 
155
                current.entry = INVALIDTARGET;
 
156
                current.stackSize = -1;
 
157
 
 
158
                u32 prevEntry = INVALIDTARGET;
 
159
                while (pc != threadEntry) {
 
160
                        u32 possibleEntry = GuessEntry(current.pc);
 
161
                        if (DetermineFrameInfo(current, possibleEntry, threadEntry, ra)) {
 
162
                                frames.push_back(current);
 
163
                                if (current.entry == threadEntry || GuessEntry(current.entry) == threadEntry) {
 
164
                                        break;
 
165
                                }
 
166
                                if (current.entry == prevEntry || frames.size() >= MAX_DEPTH) {
 
167
                                        // Recursion, means we're screwed.  Let's just give up.
 
168
                                        break;
 
169
                                }
 
170
                                prevEntry = current.entry;
 
171
 
 
172
                                current.pc = ra;
 
173
                                current.sp += current.stackSize;
 
174
                                ra = INVALIDTARGET;
 
175
                                current.entry = INVALIDTARGET;
 
176
                                current.stackSize = -1;
 
177
                        } else {
 
178
                                // Well, we got as far as we could.
 
179
                                current.entry = possibleEntry;
 
180
                                current.stackSize = 0;
 
181
                                frames.push_back(current);
 
182
                                break;
 
183
                        }
 
184
                }
 
185
 
 
186
                return frames;
 
187
        }
 
188
};
 
 
b'\\ No newline at end of file'