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 | // StubMgr.h |
5 | // |
6 | |
7 | // |
8 | // The stub manager exists so that the debugger can accurately step through |
9 | // the myriad stubs & wrappers which exist in the EE, without imposing undue |
10 | // overhead on the stubs themselves. |
11 | // |
12 | // Each type of stub (except those which the debugger can treat as atomic operations) |
13 | // needs to have a stub manager to represent it. The stub manager is responsible for |
14 | // (a) identifying the stub as such, and |
15 | // (b) tracing into the stub & reporting what the stub will call. This |
16 | // report can consist of |
17 | // (i) a managed code address |
18 | // (ii) an unmanaged code address |
19 | // (iii) another stub address |
20 | // (iv) a "frame patch" address - that is, an address in the stub, |
21 | // which the debugger can patch. When the patch is hit, the debugger |
22 | // will query the topmost frame to trace itself. (Thus this is |
23 | // a way of deferring the trace logic to the frame which the stub |
24 | // will push.) |
25 | // |
26 | // The set of stub managers is extensible, but should be kept to a reasonable number |
27 | // as they are currently linearly searched & queried for each stub. |
28 | // |
29 | // |
30 | // IMPORTANT IMPLEMENTATION NOTE: Due to code versioning, tracing through a jitted code |
31 | // call is a speculative exercise. A trace could predict that calling method Foo would run |
32 | // jitted code at address 0x1234, however afterwards code versioning redirects Foo to call |
33 | // an alternate jitted code body at address 0x5678. To handle this stub managers should |
34 | // either: |
35 | // a) stop tracing at offset zero of the newly called jitted code. The debugger knows |
36 | // to treat offset 0 in jitted code as potentially being any jitted code instance |
37 | // b) trace all the way through the jitted method such that regardless of which jitted |
38 | // code instance gets called the trace will still end at the predicted location. |
39 | // |
40 | // If we wanted to be more rigorous about this we should probably have different trace |
41 | // results for intra-jitted and inter-jitted trace results but given the relative |
42 | // stability of this part of the code I haven't attacked that problem right now. It does |
43 | // work as-is. |
44 | // |
45 | |
46 | |
47 | #ifndef __stubmgr_h__ |
48 | #define __stubmgr_h__ |
49 | |
50 | #include "simplerwlock.hpp" |
51 | |
52 | // When 'TraceStub' returns, it gives the address of where the 'target' is for a stub' |
53 | // TraceType indicates what this 'target' is |
54 | enum TraceType |
55 | { |
56 | TRACE_ENTRY_STUB, // Stub goes to an unmanaged entry stub |
57 | TRACE_STUB, // Stub goes to another stub |
58 | TRACE_UNMANAGED, // Stub goes to unmanaged code |
59 | TRACE_MANAGED, // Stub goes to Jitted code |
60 | TRACE_UNJITTED_METHOD, // Is the prestub, since there is no code, the address will actually be a MethodDesc* |
61 | |
62 | TRACE_FRAME_PUSH, // Don't know where stub goes, stop at address, and then ask the frame that is on the stack |
63 | TRACE_MGR_PUSH, // Don't know where stub goes, stop at address then call TraceManager() below to find out |
64 | |
65 | TRACE_OTHER // We are going somewhere you can't step into (eg. ee helper function) |
66 | }; |
67 | |
68 | class StubManager; |
69 | class SString; |
70 | |
71 | class DebuggerRCThread; |
72 | |
73 | enum StubCodeBlockKind : int; |
74 | |
75 | // A TraceDestination describes where code is going to call. This can be used by the Debugger's Step-In functionality |
76 | // to skip through stubs and place a patch directly at a call's target. |
77 | // TD are supplied by the stubmanagers. |
78 | class TraceDestination |
79 | { |
80 | public: |
81 | friend class DebuggerRCThread; |
82 | |
83 | TraceDestination() { } |
84 | |
85 | #ifdef _DEBUG |
86 | // Get a string representation of this TraceDestination |
87 | // Uses the supplied buffer to store the memory (or may return a string literal). |
88 | // This will also print the TD's arguments. |
89 | const WCHAR * DbgToString(SString &buffer); |
90 | #endif |
91 | |
92 | // Initialize for unmanaged code. |
93 | // The addr is in unmanaged code. Used for Step-in from managed to native. |
94 | void InitForUnmanaged(PCODE addr) |
95 | { |
96 | STATIC_CONTRACT_SO_TOLERANT; |
97 | this->type = TRACE_UNMANAGED; |
98 | this->address = addr; |
99 | this->stubManager = NULL; |
100 | } |
101 | |
102 | // The addr is inside jitted code (eg, there's a JitManaged that will claim it) |
103 | void InitForManaged(PCODE addr) |
104 | { |
105 | STATIC_CONTRACT_SO_TOLERANT; |
106 | this->type = TRACE_MANAGED; |
107 | this->address = addr; |
108 | this->stubManager = NULL; |
109 | } |
110 | |
111 | // Initialize for an unmanaged entry stub. |
112 | void InitForUnmanagedStub(PCODE addr) |
113 | { |
114 | STATIC_CONTRACT_SO_TOLERANT; |
115 | this->type = TRACE_ENTRY_STUB; |
116 | this->address = addr; |
117 | this->stubManager = NULL; |
118 | } |
119 | |
120 | // Initialize for a stub. |
121 | void InitForStub(PCODE addr) |
122 | { |
123 | STATIC_CONTRACT_SO_TOLERANT; |
124 | this->type = TRACE_STUB; |
125 | this->address = addr; |
126 | this->stubManager = NULL; |
127 | } |
128 | |
129 | // Init for a managed unjitted method. |
130 | // This will place an IL patch that will get bound when the debugger gets a Jit complete |
131 | // notification for this method. |
132 | // If pDesc is a wrapper methoddesc, we will unwrap it. |
133 | void InitForUnjittedMethod(MethodDesc * pDesc); |
134 | |
135 | // Place a patch at the given addr, and then when it's hit, |
136 | // call pStubManager->TraceManager() to get the next TraceDestination. |
137 | void InitForManagerPush(PCODE addr, StubManager * pStubManager) |
138 | { |
139 | STATIC_CONTRACT_SO_TOLERANT; |
140 | this->type = TRACE_MGR_PUSH; |
141 | this->address = addr; |
142 | this->stubManager = pStubManager; |
143 | } |
144 | |
145 | // Place a patch at the given addr, and then when it's hit |
146 | // call GetThread()->GetFrame()->TraceFrame() to get the next TraceDestination. |
147 | // This address must be safe to run a callstack at. |
148 | void InitForFramePush(PCODE addr) |
149 | { |
150 | this->type = TRACE_FRAME_PUSH; |
151 | this->address = addr; |
152 | this->stubManager = NULL; |
153 | } |
154 | |
155 | // Nobody recognized the target address. We will not be able to step-in to it. |
156 | // This is ok if the target just calls into mscorwks (such as an Fcall) because |
157 | // there's no managed code to step in to, and we don't support debugging the CLR |
158 | // itself, so there's no native code to step into either. |
159 | void InitForOther(PCODE addr) |
160 | { |
161 | this->type = TRACE_OTHER; |
162 | this->address = addr; |
163 | this->stubManager = NULL; |
164 | } |
165 | |
166 | // Accessors |
167 | TraceType GetTraceType() { return type; } |
168 | PCODE GetAddress() |
169 | { |
170 | LIMITED_METHOD_CONTRACT; |
171 | _ASSERTE(type != TRACE_UNJITTED_METHOD); |
172 | return address; |
173 | } |
174 | MethodDesc* GetMethodDesc() |
175 | { |
176 | LIMITED_METHOD_CONTRACT; |
177 | _ASSERTE(type == TRACE_UNJITTED_METHOD); |
178 | return pDesc; |
179 | } |
180 | |
181 | StubManager * GetStubManager() |
182 | { |
183 | return stubManager; |
184 | } |
185 | |
186 | // Expose this b/c DebuggerPatchTable::AddPatchForAddress() needs it. |
187 | // Ideally we'd get rid of this. |
188 | void Bad_SetTraceType(TraceType t) |
189 | { |
190 | this->type = t; |
191 | } |
192 | private: |
193 | TraceType type; // The kind of code the stub is going to |
194 | PCODE address; // Where the stub is going |
195 | StubManager *stubManager; // The manager that claims this stub |
196 | MethodDesc *pDesc; |
197 | }; |
198 | |
199 | // For logging |
200 | #ifdef LOGGING |
201 | void LogTraceDestination(const char * szHint, PCODE stubAddr, TraceDestination * pTrace); |
202 | #define LOG_TRACE_DESTINATION(_tracedestination, stubAddr, _stHint) LogTraceDestination(_stHint, stubAddr, _tracedestination) |
203 | #else |
204 | #define LOG_TRACE_DESTINATION(_tracedestination, stubAddr, _stHint) |
205 | #endif |
206 | |
207 | typedef VPTR(class StubManager) PTR_StubManager; |
208 | |
209 | class StubManager |
210 | { |
211 | #ifndef CROSSGEN_COMPILE |
212 | friend class StubManagerIterator; |
213 | |
214 | VPTR_BASE_VTABLE_CLASS(StubManager) |
215 | |
216 | public: |
217 | // Startup and shutdown the global stubmanager service. |
218 | static void InitializeStubManagers(); |
219 | static void TerminateStubManagers(); |
220 | |
221 | // Does any sub manager recognise this EIP? |
222 | static BOOL IsStub(PCODE stubAddress) |
223 | { |
224 | WRAPPER_NO_CONTRACT; |
225 | return FindStubManager(stubAddress) != NULL; |
226 | } |
227 | |
228 | // Find stub manager for given code address |
229 | static PTR_StubManager FindStubManager(PCODE stubAddress); |
230 | |
231 | // Look for stubAddress, if found return TRUE, and set 'trace' to |
232 | static BOOL TraceStub(PCODE stubAddress, TraceDestination *trace); |
233 | |
234 | // if 'trace' indicates TRACE_STUB, keep calling TraceStub on 'trace', until you get out of all stubs |
235 | // returns true if successfull |
236 | static BOOL FollowTrace(TraceDestination *trace); |
237 | |
238 | #ifdef DACCESS_COMPILE |
239 | static void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
240 | #endif |
241 | |
242 | static void AddStubManager(StubManager *mgr); |
243 | |
244 | // NOTE: Very important when using this. It is not thread safe, except in this very |
245 | // limited scenario: the thread must have the runtime suspended. |
246 | static void UnlinkStubManager(StubManager *mgr); |
247 | |
248 | #ifndef DACCESS_COMPILE |
249 | StubManager(); |
250 | virtual ~StubManager(); |
251 | #endif |
252 | |
253 | |
254 | #ifdef _DEBUG |
255 | // Debug helper to help identify stub-managers. Make it pure to force stub managers to implement it. |
256 | virtual const char * DbgGetName() = 0; |
257 | #endif |
258 | |
259 | // Only Stubmanagers that return 'TRACE_MGR_PUSH' as a trace type need to implement this function |
260 | // Fills in 'trace' (the target), and 'pRetAddr' (the method that called the stub) (this is needed |
261 | // as a 'fall back' so that the debugger can at least stop when the stub returns. |
262 | virtual BOOL TraceManager(Thread *thread, TraceDestination *trace, |
263 | T_CONTEXT *pContext, BYTE **pRetAddr) |
264 | { |
265 | LIMITED_METHOD_CONTRACT; |
266 | |
267 | _ASSERTE(!"Default impl of TraceManager should never be called!" ); |
268 | return FALSE; |
269 | } |
270 | |
271 | // The worker for IsStub. This calls CheckIsStub_Internal, but wraps it w/ |
272 | // a try-catch. |
273 | BOOL CheckIsStub_Worker(PCODE stubStartAddress); |
274 | |
275 | |
276 | |
277 | #ifdef _DEBUG |
278 | public: |
279 | //----------------------------------------------------------------------------- |
280 | // Debugging Stubmanager bugs is very painful. You need to figure out |
281 | // how you go to where you got and which stub-manager is at fault. |
282 | // To help with this, we track a rolling log so that we can give very |
283 | // informative asserts. this log is not thread-safe, but we really only expect |
284 | // a single stub-manager usage at a time. |
285 | // |
286 | // A stub manager for a step-in operation may be used across |
287 | // both the helper thread and then the managed thread doing the step-in. |
288 | // These threads will coordinate to have exclusive access (helper will only access |
289 | // when stopped; and managed thread will only access when running). |
290 | // |
291 | // It's also possible (but rare) for a single thread to have multiple step-in operations. |
292 | // Since that's so rare, no present need to expand our logging to support it. |
293 | //----------------------------------------------------------------------------- |
294 | |
295 | |
296 | static bool IsStubLoggingEnabled(); |
297 | |
298 | // Call to reset the log. This is used at the start of a new step-operation. |
299 | static void DbgBeginLog(TADDR addrCallInstruction, TADDR addrCallTarget); |
300 | static void DbgFinishLog(); |
301 | |
302 | // Log arbitrary string. This is a nop if it's outside the Begin/Finish window. |
303 | // We could consider making each log entry type-safe (and thus avoid the string operations). |
304 | static void DbgWriteLog(const CHAR *format, ...); |
305 | |
306 | // Get the log as a string. |
307 | static void DbgGetLog(SString * pStringOut); |
308 | |
309 | protected: |
310 | // Implement log as a SString. |
311 | static SString * s_pDbgStubManagerLog; |
312 | |
313 | static CrstStatic s_DbgLogCrst; |
314 | |
315 | #endif |
316 | |
317 | |
318 | protected: |
319 | |
320 | // Each stubmanaged implements this. |
321 | // This may throw, AV, etc depending on the implementation. This should not |
322 | // be called directly unless you know exactly what you're doing. |
323 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress) = 0; |
324 | |
325 | // The worker for TraceStub |
326 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) = 0; |
327 | |
328 | #ifdef _DEBUG_IMPL |
329 | static BOOL IsSingleOwner(PCODE stubAddress, StubManager * pOwner); |
330 | #endif |
331 | |
332 | #ifdef DACCESS_COMPILE |
333 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
334 | |
335 | public: |
336 | // This is used by DAC to provide more information on who owns a stub. |
337 | virtual LPCWSTR GetStubManagerName(PCODE addr) = 0; |
338 | #endif |
339 | |
340 | private: |
341 | SPTR_DECL(StubManager, g_pFirstManager); |
342 | PTR_StubManager m_pNextManager; |
343 | |
344 | static CrstStatic s_StubManagerListCrst; |
345 | #endif // !CROSSGEN_COMPILE |
346 | }; |
347 | |
348 | // ------------------------------------------------------- |
349 | // This just wraps the RangeList methods in a read or |
350 | // write lock depending on the operation. |
351 | // ------------------------------------------------------- |
352 | |
353 | class LockedRangeList : public RangeList |
354 | { |
355 | public: |
356 | VPTR_VTABLE_CLASS(LockedRangeList, RangeList) |
357 | |
358 | LockedRangeList() : RangeList(), m_RangeListRWLock(COOPERATIVE_OR_PREEMPTIVE, LOCK_TYPE_DEFAULT) |
359 | { |
360 | LIMITED_METHOD_CONTRACT; |
361 | } |
362 | |
363 | ~LockedRangeList() |
364 | { |
365 | LIMITED_METHOD_CONTRACT; |
366 | } |
367 | |
368 | protected: |
369 | |
370 | virtual BOOL AddRangeWorker(const BYTE *start, const BYTE *end, void *id) |
371 | { |
372 | WRAPPER_NO_CONTRACT; |
373 | SimpleWriteLockHolder lh(&m_RangeListRWLock); |
374 | return RangeList::AddRangeWorker(start,end,id); |
375 | } |
376 | |
377 | virtual void RemoveRangesWorker(void *id, const BYTE *start = NULL, const BYTE *end = NULL) |
378 | { |
379 | WRAPPER_NO_CONTRACT; |
380 | SimpleWriteLockHolder lh(&m_RangeListRWLock); |
381 | RangeList::RemoveRangesWorker(id,start,end); |
382 | } |
383 | |
384 | virtual BOOL IsInRangeWorker(TADDR address, TADDR *pID = NULL) |
385 | { |
386 | WRAPPER_NO_CONTRACT; |
387 | SUPPORTS_DAC; |
388 | SimpleReadLockHolder lh(&m_RangeListRWLock); |
389 | return RangeList::IsInRangeWorker(address, pID); |
390 | } |
391 | |
392 | SimpleRWLock m_RangeListRWLock; |
393 | }; |
394 | |
395 | #ifndef CROSSGEN_COMPILE |
396 | |
397 | //----------------------------------------------------------- |
398 | // Stub manager for the prestub. Although there is just one, it has |
399 | // unique behavior so it gets its own stub manager. |
400 | //----------------------------------------------------------- |
401 | class ThePreStubManager : public StubManager |
402 | { |
403 | VPTR_VTABLE_CLASS(ThePreStubManager, StubManager) |
404 | |
405 | public: |
406 | #ifndef DACCESS_COMPILE |
407 | ThePreStubManager() { LIMITED_METHOD_CONTRACT; } |
408 | #endif |
409 | |
410 | #ifdef _DEBUG |
411 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "ThePreStubManager" ; } |
412 | #endif |
413 | |
414 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
415 | |
416 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); |
417 | |
418 | #ifndef DACCESS_COMPILE |
419 | static void Init(void); |
420 | #endif |
421 | |
422 | #ifdef DACCESS_COMPILE |
423 | protected: |
424 | virtual LPCWSTR GetStubManagerName(PCODE addr) |
425 | { LIMITED_METHOD_CONTRACT; return W("ThePreStub" ); } |
426 | #endif |
427 | }; |
428 | |
429 | // ------------------------------------------------------- |
430 | // Stub manager classes for method desc prestubs & normal |
431 | // frame-pushing, StubLinker created stubs |
432 | // ------------------------------------------------------- |
433 | |
434 | typedef VPTR(class PrecodeStubManager) PTR_PrecodeStubManager; |
435 | |
436 | class PrecodeStubManager : public StubManager |
437 | { |
438 | VPTR_VTABLE_CLASS(PrecodeStubManager, StubManager) |
439 | |
440 | public: |
441 | |
442 | SPTR_DECL(PrecodeStubManager, g_pManager); |
443 | |
444 | #ifdef _DEBUG |
445 | // Debug helper to help identify stub-managers. |
446 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "PrecodeStubManager" ; } |
447 | #endif |
448 | |
449 | |
450 | static void Init(); |
451 | |
452 | #ifndef DACCESS_COMPILE |
453 | PrecodeStubManager() {LIMITED_METHOD_CONTRACT;} |
454 | ~PrecodeStubManager() {WRAPPER_NO_CONTRACT;} |
455 | #endif |
456 | |
457 | public: |
458 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
459 | |
460 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); |
461 | #ifndef DACCESS_COMPILE |
462 | virtual BOOL TraceManager(Thread *thread, |
463 | TraceDestination *trace, |
464 | T_CONTEXT *pContext, |
465 | BYTE **pRetAddr); |
466 | #endif |
467 | |
468 | #ifdef DACCESS_COMPILE |
469 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
470 | |
471 | protected: |
472 | virtual LPCWSTR GetStubManagerName(PCODE addr) |
473 | { LIMITED_METHOD_CONTRACT; return W("MethodDescPrestub" ); } |
474 | #endif |
475 | }; |
476 | |
477 | // Note that this stub was written by a debugger guy, and thus when he refers to 'multicast' |
478 | // stub, he really means multi or single cast stub. This was done b/c the same stub |
479 | // services both types of stub. |
480 | // Note from the debugger guy: the way to understand what this manager does is to |
481 | // first grok EmitMulticastInvoke for the platform you're working on (right now, just x86). |
482 | // Then return here, and understand that (for x86) the only way we know which method |
483 | // we're going to invoke next is by inspecting EDI when we've got the debuggee stopped |
484 | // in the stub, and so our trace frame will either (FRAME_PUSH) put a breakpoint |
485 | // in the stub, or (if we hit the BP) examine EDI, etc, & figure out where we're going next. |
486 | |
487 | typedef VPTR(class StubLinkStubManager) PTR_StubLinkStubManager; |
488 | |
489 | class StubLinkStubManager : public StubManager |
490 | { |
491 | VPTR_VTABLE_CLASS(StubLinkStubManager, StubManager) |
492 | |
493 | public: |
494 | |
495 | #ifdef _DEBUG |
496 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "StubLinkStubManager" ; } |
497 | #endif |
498 | |
499 | |
500 | SPTR_DECL(StubLinkStubManager, g_pManager); |
501 | |
502 | static void Init(); |
503 | |
504 | #ifndef DACCESS_COMPILE |
505 | StubLinkStubManager() : StubManager(), m_rangeList() {LIMITED_METHOD_CONTRACT;} |
506 | ~StubLinkStubManager() {WRAPPER_NO_CONTRACT;} |
507 | #endif |
508 | |
509 | protected: |
510 | LockedRangeList m_rangeList; |
511 | public: |
512 | // Get dac-ized pointer to rangelist. |
513 | PTR_RangeList GetRangeList() |
514 | { |
515 | SUPPORTS_DAC; |
516 | |
517 | TADDR addr = PTR_HOST_MEMBER_TADDR(StubLinkStubManager, this, m_rangeList); |
518 | return PTR_RangeList(addr); |
519 | } |
520 | |
521 | |
522 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
523 | |
524 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); |
525 | #ifndef DACCESS_COMPILE |
526 | virtual BOOL TraceManager(Thread *thread, |
527 | TraceDestination *trace, |
528 | T_CONTEXT *pContext, |
529 | BYTE **pRetAddr); |
530 | #endif |
531 | |
532 | #ifdef DACCESS_COMPILE |
533 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
534 | |
535 | protected: |
536 | virtual LPCWSTR GetStubManagerName(PCODE addr) |
537 | { LIMITED_METHOD_CONTRACT; return W("StubLinkStub" ); } |
538 | #endif |
539 | } ; |
540 | |
541 | // Stub manager for thunks. |
542 | |
543 | typedef VPTR(class ThunkHeapStubManager) PTR_ThunkHeapStubManager; |
544 | |
545 | class ThunkHeapStubManager : public StubManager |
546 | { |
547 | VPTR_VTABLE_CLASS(ThunkHeapStubManager, StubManager) |
548 | |
549 | public: |
550 | |
551 | SPTR_DECL(ThunkHeapStubManager, g_pManager); |
552 | |
553 | static void Init(); |
554 | |
555 | #ifndef DACCESS_COMPILE |
556 | ThunkHeapStubManager() : StubManager(), m_rangeList() { LIMITED_METHOD_CONTRACT; } |
557 | ~ThunkHeapStubManager() {WRAPPER_NO_CONTRACT;} |
558 | #endif |
559 | |
560 | #ifdef _DEBUG |
561 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "ThunkHeapStubManager" ; } |
562 | #endif |
563 | |
564 | protected: |
565 | LockedRangeList m_rangeList; |
566 | public: |
567 | // Get dac-ized pointer to rangelist. |
568 | PTR_RangeList GetRangeList() |
569 | { |
570 | SUPPORTS_DAC; |
571 | TADDR addr = PTR_HOST_MEMBER_TADDR(ThunkHeapStubManager, this, m_rangeList); |
572 | return PTR_RangeList(addr); |
573 | } |
574 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
575 | |
576 | private: |
577 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); |
578 | |
579 | #ifdef DACCESS_COMPILE |
580 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
581 | |
582 | protected: |
583 | virtual LPCWSTR GetStubManagerName(PCODE addr) |
584 | { LIMITED_METHOD_CONTRACT; return W("ThunkHeapStub" ); } |
585 | #endif |
586 | }; |
587 | |
588 | // |
589 | // Stub manager for jump stubs created by ExecutionManager::jumpStub() |
590 | // These are currently used only on the 64-bit targets IA64 and AMD64 |
591 | // |
592 | typedef VPTR(class JumpStubStubManager) PTR_JumpStubStubManager; |
593 | |
594 | class JumpStubStubManager : public StubManager |
595 | { |
596 | VPTR_VTABLE_CLASS(JumpStubStubManager, StubManager) |
597 | |
598 | public: |
599 | |
600 | SPTR_DECL(JumpStubStubManager, g_pManager); |
601 | |
602 | static void Init(); |
603 | |
604 | #ifndef DACCESS_COMPILE |
605 | JumpStubStubManager() {LIMITED_METHOD_CONTRACT;} |
606 | ~JumpStubStubManager() {WRAPPER_NO_CONTRACT;} |
607 | |
608 | #endif |
609 | |
610 | #ifdef _DEBUG |
611 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "JumpStubStubManager" ; } |
612 | #endif |
613 | |
614 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
615 | |
616 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); |
617 | |
618 | #ifdef DACCESS_COMPILE |
619 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
620 | |
621 | protected: |
622 | virtual LPCWSTR GetStubManagerName(PCODE addr) |
623 | { LIMITED_METHOD_CONTRACT; return W("JumpStub" ); } |
624 | #endif |
625 | }; |
626 | |
627 | // |
628 | // Stub manager for code sections. It forwards the query to the more appropriate |
629 | // stub manager, or handles the query itself. |
630 | // |
631 | typedef VPTR(class RangeSectionStubManager) PTR_RangeSectionStubManager; |
632 | |
633 | class RangeSectionStubManager : public StubManager |
634 | { |
635 | VPTR_VTABLE_CLASS(RangeSectionStubManager, StubManager) |
636 | |
637 | public: |
638 | SPTR_DECL(RangeSectionStubManager, g_pManager); |
639 | |
640 | static void Init(); |
641 | |
642 | #ifndef DACCESS_COMPILE |
643 | RangeSectionStubManager() {LIMITED_METHOD_CONTRACT;} |
644 | ~RangeSectionStubManager() {WRAPPER_NO_CONTRACT;} |
645 | #endif |
646 | |
647 | static StubCodeBlockKind GetStubKind(PCODE stubStartAddress); |
648 | |
649 | static PCODE GetMethodThunkTarget(PCODE stubStartAddress); |
650 | |
651 | public: |
652 | #ifdef _DEBUG |
653 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "RangeSectionStubManager" ; } |
654 | #endif |
655 | |
656 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
657 | |
658 | private: |
659 | |
660 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); |
661 | |
662 | #ifndef DACCESS_COMPILE |
663 | virtual BOOL TraceManager(Thread *thread, |
664 | TraceDestination *trace, |
665 | T_CONTEXT *pContext, |
666 | BYTE **pRetAddr); |
667 | #endif |
668 | |
669 | #ifdef DACCESS_COMPILE |
670 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
671 | |
672 | protected: |
673 | virtual LPCWSTR GetStubManagerName(PCODE addr); |
674 | #endif |
675 | }; |
676 | |
677 | // |
678 | // This is the stub manager for IL stubs. |
679 | // |
680 | typedef VPTR(class ILStubManager) PTR_ILStubManager; |
681 | |
682 | #ifdef FEATURE_COMINTEROP |
683 | struct ComPlusCallInfo; |
684 | #endif // FEATURE_COMINTEROP |
685 | |
686 | class ILStubManager : public StubManager |
687 | { |
688 | VPTR_VTABLE_CLASS(ILStubManager, StubManager) |
689 | |
690 | public: |
691 | static void Init(); |
692 | |
693 | #ifndef DACCESS_COMPILE |
694 | ILStubManager() : StubManager() {WRAPPER_NO_CONTRACT;} |
695 | ~ILStubManager() |
696 | { |
697 | CONTRACTL |
698 | { |
699 | NOTHROW; |
700 | GC_NOTRIGGER; |
701 | CAN_TAKE_LOCK; // StubManager::UnlinkStubManager uses a crst |
702 | } |
703 | CONTRACTL_END; |
704 | } |
705 | #endif |
706 | |
707 | public: |
708 | |
709 | #ifdef _DEBUG |
710 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "ILStubManager" ; } |
711 | #endif |
712 | |
713 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
714 | |
715 | private: |
716 | |
717 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); |
718 | |
719 | #ifndef DACCESS_COMPILE |
720 | #ifdef FEATURE_COMINTEROP |
721 | static PCODE GetCOMTarget(Object *pThis, ComPlusCallInfo *pComPlusCallInfo); |
722 | static PCODE GetWinRTFactoryTarget(ComPlusCallMethodDesc *pCMD); |
723 | #endif // FEATURE_COMINTEROP |
724 | |
725 | virtual BOOL TraceManager(Thread *thread, |
726 | TraceDestination *trace, |
727 | T_CONTEXT *pContext, |
728 | BYTE **pRetAddr); |
729 | #endif |
730 | |
731 | #ifdef DACCESS_COMPILE |
732 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
733 | |
734 | protected: |
735 | virtual LPCWSTR GetStubManagerName(PCODE addr) |
736 | { LIMITED_METHOD_CONTRACT; return W("ILStub" ); } |
737 | #endif |
738 | }; |
739 | |
740 | // This is used to recognize |
741 | // GenericComPlusCallStub() |
742 | // VarargPInvokeStub() |
743 | // GenericPInvokeCalliHelper() |
744 | typedef VPTR(class InteropDispatchStubManager) PTR_InteropDispatchStubManager; |
745 | |
746 | class InteropDispatchStubManager : public StubManager |
747 | { |
748 | VPTR_VTABLE_CLASS(InteropDispatchStubManager, StubManager) |
749 | |
750 | public: |
751 | static void Init(); |
752 | |
753 | #ifndef DACCESS_COMPILE |
754 | InteropDispatchStubManager() : StubManager() {WRAPPER_NO_CONTRACT;} |
755 | ~InteropDispatchStubManager() {WRAPPER_NO_CONTRACT;} |
756 | #endif |
757 | |
758 | #ifdef _DEBUG |
759 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "InteropDispatchStubManager" ; } |
760 | #endif |
761 | |
762 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
763 | |
764 | private: |
765 | |
766 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); |
767 | |
768 | #ifndef DACCESS_COMPILE |
769 | virtual BOOL TraceManager(Thread *thread, |
770 | TraceDestination *trace, |
771 | T_CONTEXT *pContext, |
772 | BYTE **pRetAddr); |
773 | #endif |
774 | |
775 | #ifdef DACCESS_COMPILE |
776 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
777 | |
778 | protected: |
779 | virtual LPCWSTR GetStubManagerName(PCODE addr) |
780 | { LIMITED_METHOD_CONTRACT; return W("InteropDispatchStub" ); } |
781 | #endif |
782 | }; |
783 | |
784 | // |
785 | // Since we don't generate delegate invoke stubs at runtime on WIN64, we |
786 | // can't use the StubLinkStubManager for these stubs. Instead, we create |
787 | // an additional DelegateInvokeStubManager instead. |
788 | // |
789 | typedef VPTR(class DelegateInvokeStubManager) PTR_DelegateInvokeStubManager; |
790 | |
791 | class DelegateInvokeStubManager : public StubManager |
792 | { |
793 | VPTR_VTABLE_CLASS(DelegateInvokeStubManager, StubManager) |
794 | |
795 | public: |
796 | |
797 | SPTR_DECL(DelegateInvokeStubManager, g_pManager); |
798 | |
799 | static void Init(); |
800 | |
801 | #if !defined(DACCESS_COMPILE) |
802 | DelegateInvokeStubManager() : StubManager(), m_rangeList() {LIMITED_METHOD_CONTRACT;} |
803 | ~DelegateInvokeStubManager() {WRAPPER_NO_CONTRACT;} |
804 | #endif // DACCESS_COMPILE |
805 | |
806 | BOOL AddStub(Stub* pStub); |
807 | void RemoveStub(Stub* pStub); |
808 | |
809 | #ifdef _DEBUG |
810 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "DelegateInvokeStubManager" ; } |
811 | #endif |
812 | |
813 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
814 | |
815 | #if !defined(DACCESS_COMPILE) |
816 | virtual BOOL TraceManager(Thread *thread, TraceDestination *trace, T_CONTEXT *pContext, BYTE **pRetAddr); |
817 | static BOOL TraceDelegateObject(BYTE *orDel, TraceDestination *trace); |
818 | #endif // DACCESS_COMPILE |
819 | |
820 | private: |
821 | |
822 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); |
823 | |
824 | protected: |
825 | LockedRangeList m_rangeList; |
826 | public: |
827 | // Get dac-ized pointer to rangelist. |
828 | PTR_RangeList GetRangeList() |
829 | { |
830 | SUPPORTS_DAC; |
831 | |
832 | TADDR addr = PTR_HOST_MEMBER_TADDR(DelegateInvokeStubManager, this, m_rangeList); |
833 | return PTR_RangeList(addr); |
834 | } |
835 | |
836 | |
837 | #ifdef DACCESS_COMPILE |
838 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
839 | |
840 | protected: |
841 | virtual LPCWSTR GetStubManagerName(PCODE addr) |
842 | { LIMITED_METHOD_CONTRACT; return W("DelegateInvokeStub" ); } |
843 | #endif |
844 | }; |
845 | |
846 | //--------------------------------------------------------------------------------------- |
847 | // |
848 | // This is the stub manager to help the managed debugger step into a tail call. |
849 | // It helps the debugger trace through JIT_TailCall(). |
850 | // |
851 | |
852 | typedef VPTR(class TailCallStubManager) PTR_TailCallStubManager; |
853 | |
854 | class TailCallStubManager : public StubManager |
855 | { |
856 | VPTR_VTABLE_CLASS(TailCallStubManager, StubManager) |
857 | |
858 | public: |
859 | static void Init(); |
860 | |
861 | #if !defined(DACCESS_COMPILE) |
862 | TailCallStubManager() : StubManager() {WRAPPER_NO_CONTRACT;} |
863 | ~TailCallStubManager() {WRAPPER_NO_CONTRACT;} |
864 | |
865 | virtual BOOL TraceManager(Thread * pThread, TraceDestination * pTrace, T_CONTEXT * pContext, BYTE ** ppRetAddr); |
866 | |
867 | static bool IsTailCallStubHelper(PCODE code); |
868 | #endif // DACCESS_COMPILE |
869 | |
870 | #if defined(_DEBUG) |
871 | virtual const char * DbgGetName() { LIMITED_METHOD_CONTRACT; return "TailCallStubManager" ; } |
872 | #endif // _DEBUG |
873 | |
874 | virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); |
875 | |
876 | private: |
877 | virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination * pTrace); |
878 | |
879 | #if defined(DACCESS_COMPILE) |
880 | virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags); |
881 | |
882 | protected: |
883 | virtual LPCWSTR GetStubManagerName(PCODE addr) {LIMITED_METHOD_CONTRACT; return W("TailCallStub" );} |
884 | #endif // !DACCESS_COMPILE |
885 | }; |
886 | |
887 | // |
888 | // Helpers for common value locations in stubs to make stub managers more portable |
889 | // |
890 | class StubManagerHelpers |
891 | { |
892 | public: |
893 | static PCODE GetReturnAddress(T_CONTEXT * pContext) |
894 | { |
895 | #if defined(_TARGET_X86_) |
896 | return *dac_cast<PTR_PCODE>(pContext->Esp); |
897 | #elif defined(_TARGET_AMD64_) |
898 | return *dac_cast<PTR_PCODE>(pContext->Rsp); |
899 | #elif defined(_TARGET_ARM_) |
900 | return pContext->Lr; |
901 | #elif defined(_TARGET_ARM64_) |
902 | return pContext->Lr; |
903 | #else |
904 | PORTABILITY_ASSERT("StubManagerHelpers::GetReturnAddress" ); |
905 | return NULL; |
906 | #endif |
907 | } |
908 | |
909 | static PTR_Object GetThisPtr(T_CONTEXT * pContext) |
910 | { |
911 | #if defined(_TARGET_X86_) |
912 | return dac_cast<PTR_Object>(pContext->Ecx); |
913 | #elif defined(_TARGET_AMD64_) |
914 | #ifdef UNIX_AMD64_ABI |
915 | return dac_cast<PTR_Object>(pContext->Rdi); |
916 | #else |
917 | return dac_cast<PTR_Object>(pContext->Rcx); |
918 | #endif |
919 | #elif defined(_TARGET_ARM_) |
920 | return dac_cast<PTR_Object>((TADDR)pContext->R0); |
921 | #elif defined(_TARGET_ARM64_) |
922 | return dac_cast<PTR_Object>(pContext->X0); |
923 | #else |
924 | PORTABILITY_ASSERT("StubManagerHelpers::GetThisPtr" ); |
925 | return NULL; |
926 | #endif |
927 | } |
928 | |
929 | static PCODE GetTailCallTarget(T_CONTEXT * pContext) |
930 | { |
931 | #if defined(_TARGET_X86_) |
932 | return pContext->Eax; |
933 | #elif defined(_TARGET_AMD64_) |
934 | return pContext->Rax; |
935 | #elif defined(_TARGET_ARM_) |
936 | return pContext->R12; |
937 | #elif defined(_TARGET_ARM64_) |
938 | return pContext->X12; |
939 | #else |
940 | PORTABILITY_ASSERT("StubManagerHelpers::GetTailCallTarget" ); |
941 | return NULL; |
942 | #endif |
943 | } |
944 | |
945 | static TADDR GetHiddenArg(T_CONTEXT * pContext) |
946 | { |
947 | #if defined(_TARGET_X86_) |
948 | return pContext->Eax; |
949 | #elif defined(_TARGET_AMD64_) |
950 | return pContext->R10; |
951 | #elif defined(_TARGET_ARM_) |
952 | return pContext->R12; |
953 | #elif defined(_TARGET_ARM64_) |
954 | return pContext->X12; |
955 | #else |
956 | PORTABILITY_ASSERT("StubManagerHelpers::GetHiddenArg" ); |
957 | return NULL; |
958 | #endif |
959 | } |
960 | |
961 | #ifndef CROSSGEN_COMPILE |
962 | static PCODE GetRetAddrFromMulticastILStubFrame(T_CONTEXT * pContext) |
963 | { |
964 | /* |
965 | Following is the callstack corresponding to context received by ILStubManager::TraceManager. |
966 | This function returns the return address (user code address) where control should return after all |
967 | delegates in multicast delegate have been executed. |
968 | |
969 | StubHelpers::MulticastDebuggerTraceHelper |
970 | IL_STUB_MulticastDelegate_Invoke |
971 | UserCode which invokes multicast delegate <--- |
972 | */ |
973 | |
974 | #if defined(_TARGET_X86_) |
975 | return *((PCODE *)pContext->Ebp + 1); |
976 | #elif defined(_TARGET_AMD64_) |
977 | T_CONTEXT context(*pContext); |
978 | Thread::VirtualUnwindCallFrame(&context); |
979 | Thread::VirtualUnwindCallFrame(&context); |
980 | |
981 | return context.Rip; |
982 | #elif defined(_TARGET_ARM_) |
983 | return *((PCODE *)((TADDR)pContext->R11) + 1); |
984 | #elif defined(_TARGET_ARM64_) |
985 | return *((PCODE *)pContext->Fp + 1); |
986 | #else |
987 | PORTABILITY_ASSERT("StubManagerHelpers::GetRetAddrFromMulticastILStubFrame" ); |
988 | return NULL; |
989 | #endif |
990 | } |
991 | #endif // !CROSSGEN_COMPILE |
992 | |
993 | static TADDR GetSecondArg(T_CONTEXT * pContext) |
994 | { |
995 | #if defined(_TARGET_X86_) |
996 | return pContext->Edx; |
997 | #elif defined(_TARGET_AMD64_) |
998 | #ifdef UNIX_AMD64_ABI |
999 | return pContext->Rsi; |
1000 | #else |
1001 | return pContext->Rdx; |
1002 | #endif |
1003 | #elif defined(_TARGET_ARM_) |
1004 | return pContext->R1; |
1005 | #elif defined(_TARGET_ARM64_) |
1006 | return pContext->X1; |
1007 | #else |
1008 | PORTABILITY_ASSERT("StubManagerHelpers::GetSecondArg" ); |
1009 | return NULL; |
1010 | #endif |
1011 | } |
1012 | |
1013 | }; |
1014 | |
1015 | #endif // !CROSSGEN_COMPILE |
1016 | #endif // !__stubmgr_h__ |
1017 | |