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: primitives.h |
6 | // |
7 | |
8 | // |
9 | // Platform-specific debugger primitives |
10 | // |
11 | //***************************************************************************** |
12 | |
13 | #ifndef PRIMITIVES_H_ |
14 | #define PRIMITIVES_H_ |
15 | |
16 | #ifndef CORDB_ADDRESS_TYPE |
17 | typedef const BYTE CORDB_ADDRESS_TYPE; |
18 | typedef DPTR(CORDB_ADDRESS_TYPE) PTR_CORDB_ADDRESS_TYPE; |
19 | #endif |
20 | //This is an abstraction to keep x86/ia64 patch data separate |
21 | #ifndef PRD_TYPE |
22 | #define PRD_TYPE DWORD_PTR |
23 | #endif |
24 | |
25 | typedef M128A FPRegister64; |
26 | |
27 | // From section 1.1 of AMD64 Programmers Manual Vol 3. |
28 | #define MAX_INSTRUCTION_LENGTH 15 |
29 | |
30 | // Given a return address retrieved during stackwalk, |
31 | // this is the offset by which it should be decremented to lend somewhere in a call instruction. |
32 | #define STACKWALK_CONTROLPC_ADJUST_OFFSET 1 |
33 | |
34 | #define CORDbg_BREAK_INSTRUCTION_SIZE 1 |
35 | #define CORDbg_BREAK_INSTRUCTION (BYTE)0xCC |
36 | |
37 | inline CORDB_ADDRESS GetPatchEndAddr(CORDB_ADDRESS patchAddr) |
38 | { |
39 | LIMITED_METHOD_DAC_CONTRACT; |
40 | return patchAddr + CORDbg_BREAK_INSTRUCTION_SIZE; |
41 | } |
42 | |
43 | |
44 | #define InitializePRDToBreakInst(_pPRD) *(_pPRD) = CORDbg_BREAK_INSTRUCTION |
45 | #define PRDIsBreakInst(_pPRD) (*(_pPRD) == CORDbg_BREAK_INSTRUCTION) |
46 | |
47 | #define CORDbgGetInstructionEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \ |
48 | CORDbgGetInstruction((CORDB_ADDRESS_TYPE *)((_buffer) + ((_patchAddr) - (_requestedAddr)))); |
49 | |
50 | #define CORDbgSetInstructionEx(_buffer, _requestedAddr, _patchAddr, _opcode, _dummy2) \ |
51 | CORDbgSetInstruction((CORDB_ADDRESS_TYPE *)((_buffer) + ((_patchAddr) - (_requestedAddr))), (_opcode)); |
52 | |
53 | #define CORDbgInsertBreakpointEx(_buffer, _requestedAddr, _patchAddr, _dummy1, _dummy2) \ |
54 | CORDbgInsertBreakpoint((CORDB_ADDRESS_TYPE *)((_buffer) + ((_patchAddr) - (_requestedAddr)))); |
55 | |
56 | |
57 | SELECTANY const CorDebugRegister g_JITToCorDbgReg[] = |
58 | { |
59 | REGISTER_AMD64_RAX, |
60 | REGISTER_AMD64_RCX, |
61 | REGISTER_AMD64_RDX, |
62 | REGISTER_AMD64_RBX, |
63 | REGISTER_AMD64_RSP, |
64 | REGISTER_AMD64_RBP, |
65 | REGISTER_AMD64_RSI, |
66 | REGISTER_AMD64_RDI, |
67 | REGISTER_AMD64_R8, |
68 | REGISTER_AMD64_R9, |
69 | REGISTER_AMD64_R10, |
70 | REGISTER_AMD64_R11, |
71 | REGISTER_AMD64_R12, |
72 | REGISTER_AMD64_R13, |
73 | REGISTER_AMD64_R14, |
74 | REGISTER_AMD64_R15 |
75 | }; |
76 | |
77 | // |
78 | // Mapping from ICorDebugInfo register numbers to CorDebugRegister |
79 | // numbers. Note: this must match the order in corinfo.h. |
80 | // |
81 | inline CorDebugRegister ConvertRegNumToCorDebugRegister(ICorDebugInfo::RegNum reg) |
82 | { |
83 | _ASSERTE(reg >= 0); |
84 | _ASSERTE(static_cast<size_t>(reg) < _countof(g_JITToCorDbgReg)); |
85 | return g_JITToCorDbgReg[reg]; |
86 | } |
87 | |
88 | |
89 | // |
90 | // inline function to access/modify the CONTEXT |
91 | // |
92 | inline LPVOID CORDbgGetIP(DT_CONTEXT* context) |
93 | { |
94 | CONTRACTL |
95 | { |
96 | NOTHROW; |
97 | GC_NOTRIGGER; |
98 | |
99 | PRECONDITION(CheckPointer(context)); |
100 | } |
101 | CONTRACTL_END; |
102 | |
103 | return (LPVOID) context->Rip; |
104 | } |
105 | |
106 | inline void CORDbgSetIP(DT_CONTEXT* context, LPVOID rip) |
107 | { |
108 | CONTRACTL |
109 | { |
110 | NOTHROW; |
111 | GC_NOTRIGGER; |
112 | |
113 | PRECONDITION(CheckPointer(context)); |
114 | } |
115 | CONTRACTL_END; |
116 | |
117 | context->Rip = (DWORD64) rip; |
118 | } |
119 | |
120 | inline LPVOID CORDbgGetSP(const DT_CONTEXT * context) |
121 | { |
122 | CONTRACTL |
123 | { |
124 | NOTHROW; |
125 | GC_NOTRIGGER; |
126 | |
127 | PRECONDITION(CheckPointer(context)); |
128 | } |
129 | CONTRACTL_END; |
130 | |
131 | return (LPVOID)context->Rsp; |
132 | } |
133 | inline void CORDbgSetSP(DT_CONTEXT *context, LPVOID rsp) |
134 | { |
135 | CONTRACTL |
136 | { |
137 | NOTHROW; |
138 | GC_NOTRIGGER; |
139 | |
140 | PRECONDITION(CheckPointer(context)); |
141 | } |
142 | CONTRACTL_END; |
143 | |
144 | context->Rsp = (UINT_PTR)rsp; |
145 | } |
146 | |
147 | // AMD64 has no frame pointer stored in RBP |
148 | #define CORDbgSetFP(context, rbp) |
149 | #define CORDbgGetFP(context) 0 |
150 | |
151 | // compare the RIP, RSP, and RBP |
152 | inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * pCtx2) |
153 | { |
154 | LIMITED_METHOD_DAC_CONTRACT; |
155 | |
156 | if ((pCtx1->Rip == pCtx2->Rip) && |
157 | (pCtx1->Rsp == pCtx2->Rsp) && |
158 | (pCtx1->Rbp == pCtx2->Rbp)) |
159 | { |
160 | return TRUE; |
161 | } |
162 | else |
163 | { |
164 | return FALSE; |
165 | } |
166 | } |
167 | |
168 | /* ========================================================================= */ |
169 | // |
170 | // Routines used by debugger support functions such as codepatch.cpp or |
171 | // exception handling code. |
172 | // |
173 | // GetInstruction, InsertBreakpoint, and SetInstruction all operate on |
174 | // a _single_ byte of memory. This is really important. If you only |
175 | // save one byte from the instruction stream before placing a breakpoint, |
176 | // you need to make sure to only replace one byte later on. |
177 | // |
178 | |
179 | |
180 | inline PRD_TYPE CORDbgGetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address) |
181 | { |
182 | LIMITED_METHOD_CONTRACT; |
183 | |
184 | return *address; // retrieving only one byte is important |
185 | } |
186 | |
187 | inline void CORDbgInsertBreakpoint(UNALIGNED CORDB_ADDRESS_TYPE *address) |
188 | { |
189 | LIMITED_METHOD_CONTRACT; |
190 | |
191 | *((unsigned char*)address) = 0xCC; // int 3 (single byte patch) |
192 | FlushInstructionCache(GetCurrentProcess(), address, 1); |
193 | |
194 | } |
195 | |
196 | inline void CORDbgSetInstruction(UNALIGNED CORDB_ADDRESS_TYPE* address, |
197 | PRD_TYPE instruction) |
198 | { |
199 | // In a DAC build, this function assumes the input is an host address. |
200 | LIMITED_METHOD_DAC_CONTRACT; |
201 | |
202 | *((unsigned char*)address) = |
203 | (unsigned char) instruction; // setting one byte is important |
204 | FlushInstructionCache(GetCurrentProcess(), address, 1); |
205 | |
206 | } |
207 | |
208 | |
209 | inline void CORDbgAdjustPCForBreakInstruction(DT_CONTEXT* pContext) |
210 | { |
211 | LIMITED_METHOD_CONTRACT; |
212 | |
213 | pContext->Rip -= 1; |
214 | } |
215 | |
216 | inline bool AddressIsBreakpoint(CORDB_ADDRESS_TYPE *address) |
217 | { |
218 | LIMITED_METHOD_CONTRACT; |
219 | |
220 | return *address == CORDbg_BREAK_INSTRUCTION; |
221 | } |
222 | |
223 | inline BOOL IsRunningOnWin95() { |
224 | return false; |
225 | } |
226 | |
227 | inline void SetSSFlag(DT_CONTEXT *pContext) |
228 | { |
229 | _ASSERTE(pContext != NULL); |
230 | pContext->EFlags |= 0x100; |
231 | } |
232 | |
233 | inline void UnsetSSFlag(DT_CONTEXT *pContext) |
234 | { |
235 | _ASSERTE(pContext != NULL); |
236 | pContext->EFlags &= ~0x100; |
237 | } |
238 | |
239 | inline bool IsSSFlagEnabled(DT_CONTEXT * context) |
240 | { |
241 | _ASSERTE(context != NULL); |
242 | return (context->EFlags & 0x100) != 0; |
243 | } |
244 | |
245 | |
246 | inline bool PRDIsEqual(PRD_TYPE p1, PRD_TYPE p2){ |
247 | return p1 == p2; |
248 | } |
249 | inline void InitializePRD(PRD_TYPE *p1) { |
250 | *p1 = 0; |
251 | } |
252 | |
253 | inline bool PRDIsEmpty(PRD_TYPE p1) { |
254 | return p1 == 0; |
255 | } |
256 | |
257 | #endif // PRIMITIVES_H_ |
258 | |