1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | //***************************************************************************** |
5 | // File: walker.h |
6 | // |
7 | |
8 | // |
9 | // Debugger code stream analysis routines |
10 | // |
11 | //***************************************************************************** |
12 | |
13 | #ifndef WALKER_H_ |
14 | #define WALKER_H_ |
15 | |
16 | |
17 | /* ========================================================================= */ |
18 | |
19 | /* ------------------------------------------------------------------------- * |
20 | * Constants |
21 | * ------------------------------------------------------------------------- */ |
22 | |
23 | enum WALK_TYPE |
24 | { |
25 | WALK_NEXT, |
26 | WALK_BRANCH, |
27 | WALK_COND_BRANCH, |
28 | WALK_CALL, |
29 | WALK_RETURN, |
30 | WALK_BREAK, |
31 | WALK_THROW, |
32 | WALK_META, |
33 | WALK_UNKNOWN |
34 | }; |
35 | |
36 | // struct holding information for the instruction being skipped over |
37 | struct InstructionAttribute |
38 | { |
39 | bool m_fIsCall; // is this a call instruction? |
40 | bool m_fIsCond; // is this a conditional jump? |
41 | bool m_fIsAbsBranch; // is this an absolute branch (either a call or a jump)? |
42 | bool m_fIsRelBranch; // is this a relative branch (either a call or a jump)? |
43 | bool m_fIsWrite; // does the instruction write to an address? |
44 | |
45 | |
46 | DWORD m_cbInstr; // the size of the instruction |
47 | DWORD m_cbDisp; // the size of the displacement |
48 | DWORD m_dwOffsetToDisp; // the offset from the beginning of the instruction |
49 | // to the beginning of the displacement |
50 | BYTE m_cOperandSize; // the size of the operand |
51 | |
52 | void Reset() |
53 | { |
54 | m_fIsCall = false; |
55 | m_fIsCond = false; |
56 | m_fIsAbsBranch = false; |
57 | m_fIsRelBranch = false; |
58 | m_fIsWrite = false; |
59 | m_cbInstr = 0; |
60 | m_cbDisp = 0; |
61 | m_dwOffsetToDisp = 0; |
62 | m_cOperandSize = 0; |
63 | } |
64 | }; |
65 | |
66 | /* ------------------------------------------------------------------------- * |
67 | * Classes |
68 | * ------------------------------------------------------------------------- */ |
69 | |
70 | class Walker |
71 | { |
72 | protected: |
73 | Walker() |
74 | : m_type(WALK_UNKNOWN), m_registers(NULL), m_ip(0), m_skipIP(0), m_nextIP(0), m_isAbsoluteBranch(false) |
75 | {LIMITED_METHOD_CONTRACT; } |
76 | |
77 | public: |
78 | |
79 | virtual void Init(const BYTE *ip, REGDISPLAY *pregisters) |
80 | { |
81 | PREFIX_ASSUME(pregisters != NULL); |
82 | _ASSERTE(GetControlPC(pregisters) == (PCODE)ip); |
83 | |
84 | m_registers = pregisters; |
85 | SetIP(ip); |
86 | } |
87 | |
88 | const BYTE *GetIP() |
89 | { return m_ip; } |
90 | |
91 | WALK_TYPE GetOpcodeWalkType() |
92 | { return m_type; } |
93 | |
94 | const BYTE *GetSkipIP() |
95 | { return m_skipIP; } |
96 | |
97 | bool IsAbsoluteBranch() |
98 | { return m_isAbsoluteBranch; } |
99 | |
100 | const BYTE *GetNextIP() |
101 | { return m_nextIP; } |
102 | |
103 | // We don't currently keep the registers up to date |
104 | // <TODO> Check if it really works on IA64. </TODO> |
105 | virtual void Next() { m_registers = NULL; SetIP(m_nextIP); } |
106 | virtual void Skip() { m_registers = NULL; LOG((LF_CORDB, LL_INFO10000, "skipping over to %p \n" , m_skipIP)); SetIP(m_skipIP); } |
107 | |
108 | // Decode the instruction |
109 | virtual void Decode() = 0; |
110 | |
111 | private: |
112 | void SetIP(const BYTE *ip) |
113 | { m_ip = ip; Decode(); } |
114 | |
115 | protected: |
116 | WALK_TYPE m_type; // Type of instructions |
117 | REGDISPLAY *m_registers; // Registers |
118 | const BYTE *m_ip; // Current IP |
119 | const BYTE *m_skipIP; // IP if we skip the instruction |
120 | const BYTE *m_nextIP; // IP if the instruction is taken |
121 | bool m_isAbsoluteBranch; // Is it an obsolute branch or not |
122 | }; |
123 | |
124 | #ifdef _TARGET_X86_ |
125 | |
126 | class NativeWalker : public Walker |
127 | { |
128 | public: |
129 | void Init(const BYTE *ip, REGDISPLAY *pregisters) |
130 | { |
131 | m_opcode = 0; |
132 | Walker::Init(ip, pregisters); |
133 | } |
134 | |
135 | DWORD GetOpcode() |
136 | { return m_opcode; } |
137 | /* |
138 | void SetRegDisplay(REGDISPLAY *registers) |
139 | { m_registers = registers; } |
140 | */ |
141 | REGDISPLAY *GetRegDisplay() |
142 | { return m_registers; } |
143 | |
144 | void Decode(); |
145 | void DecodeModRM(BYTE mod, BYTE reg, BYTE rm, const BYTE *ip); |
146 | static void DecodeInstructionForPatchSkip(const BYTE *address, InstructionAttribute * pInstrAttrib); |
147 | |
148 | private: |
149 | DWORD GetRegisterValue(int registerNumber); |
150 | |
151 | DWORD m_opcode; // Current instruction or opcode |
152 | }; |
153 | |
154 | #elif defined (_TARGET_ARM_) |
155 | |
156 | class NativeWalker : public Walker |
157 | { |
158 | public: |
159 | void Init(const BYTE *ip, REGDISPLAY *pregisters) |
160 | { |
161 | Walker::Init(ip, pregisters); |
162 | } |
163 | |
164 | void Decode(); |
165 | |
166 | private: |
167 | bool ConditionHolds(DWORD cond); |
168 | DWORD GetReg(DWORD reg); |
169 | }; |
170 | |
171 | #elif defined(_TARGET_AMD64_) |
172 | |
173 | class NativeWalker : public Walker |
174 | { |
175 | public: |
176 | void Init(const BYTE *ip, REGDISPLAY *pregisters) |
177 | { |
178 | m_opcode = 0; |
179 | Walker::Init(ip, pregisters); |
180 | } |
181 | |
182 | DWORD GetOpcode() |
183 | { return m_opcode; } |
184 | /* |
185 | void SetRegDisplay(REGDISPLAY *registers) |
186 | { m_registers = registers; } |
187 | */ |
188 | REGDISPLAY *GetRegDisplay() |
189 | { return m_registers; } |
190 | |
191 | void Decode(); |
192 | void DecodeModRM(BYTE mod, BYTE reg, BYTE rm, const BYTE *ip); |
193 | static void DecodeInstructionForPatchSkip(const BYTE *address, InstructionAttribute * pInstrAttrib); |
194 | |
195 | private: |
196 | UINT64 GetRegisterValue(int registerNumber); |
197 | |
198 | DWORD m_opcode; // Current instruction or opcode |
199 | }; |
200 | #elif defined (_TARGET_ARM64_) |
201 | #include "controller.h" |
202 | class NativeWalker : public Walker |
203 | { |
204 | public: |
205 | void Init(const BYTE *ip, REGDISPLAY *pregisters) |
206 | { |
207 | Walker::Init(ip, pregisters); |
208 | } |
209 | void Decode(); |
210 | static void NativeWalker::DecodeInstructionForPatchSkip(const BYTE *address, InstructionAttribute * pInstrAttrib) |
211 | { |
212 | pInstrAttrib->Reset(); |
213 | } |
214 | static BOOL NativeWalker::DecodePCRelativeBranchInst(PT_CONTEXT context,const PRD_TYPE& opcode, PCODE& offset, WALK_TYPE& walk); |
215 | static BOOL NativeWalker::DecodeCallInst(const PRD_TYPE& opcode, int& RegNum, WALK_TYPE& walk); |
216 | static BYTE* SetupOrSimulateInstructionForPatchSkip(T_CONTEXT * context, SharedPatchBypassBuffer * m_pSharedPatchBypassBuffer, const BYTE *address, PRD_TYPE opcode); |
217 | |
218 | }; |
219 | #else |
220 | PORTABILITY_WARNING("NativeWalker not implemented on this platform" ); |
221 | class NativeWalker : public Walker |
222 | { |
223 | public: |
224 | void Init(const BYTE *ip, REGDISPLAY *pregisters) |
225 | { |
226 | m_opcode = 0; |
227 | Walker::Init(ip, pregisters); |
228 | } |
229 | DWORD GetOpcode() |
230 | { return m_opcode; } |
231 | void Next() |
232 | { Walker::Next(); } |
233 | void Skip() |
234 | { Walker::Skip(); } |
235 | |
236 | void Decode() |
237 | { |
238 | PORTABILITY_ASSERT("NativeWalker not implemented on this platform" ); |
239 | m_type = WALK_UNKNOWN; |
240 | m_skipIP = m_ip++; |
241 | m_nextIP = m_ip++; |
242 | } |
243 | |
244 | static void DecodeInstructionForPatchSkip(const BYTE *address, InstructionAttribute * pInstrAttrib) |
245 | { |
246 | PORTABILITY_ASSERT("NativeWalker not implemented on this platform" ); |
247 | |
248 | } |
249 | |
250 | private: |
251 | DWORD m_opcode; // Current instruction or opcode |
252 | }; |
253 | #endif |
254 | |
255 | #endif // WALKER_H_ |
256 | |