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
54enum 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
68class StubManager;
69class SString;
70
71class DebuggerRCThread;
72
73enum 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.
78class TraceDestination
79{
80public:
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 }
192private:
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
207typedef VPTR(class StubManager) PTR_StubManager;
208
209class 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
278public:
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
309protected:
310 // Implement log as a SString.
311 static SString * s_pDbgStubManagerLog;
312
313 static CrstStatic s_DbgLogCrst;
314
315#endif
316
317
318protected:
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
335public:
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
340private:
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
353class 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//-----------------------------------------------------------
401class 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
434typedef VPTR(class PrecodeStubManager) PTR_PrecodeStubManager;
435
436class 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
487typedef VPTR(class StubLinkStubManager) PTR_StubLinkStubManager;
488
489class 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
543typedef VPTR(class ThunkHeapStubManager) PTR_ThunkHeapStubManager;
544
545class 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//
592typedef VPTR(class JumpStubStubManager) PTR_JumpStubStubManager;
593
594class 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//
631typedef VPTR(class RangeSectionStubManager) PTR_RangeSectionStubManager;
632
633class 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//
680typedef VPTR(class ILStubManager) PTR_ILStubManager;
681
682#ifdef FEATURE_COMINTEROP
683struct ComPlusCallInfo;
684#endif // FEATURE_COMINTEROP
685
686class 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()
744typedef VPTR(class InteropDispatchStubManager) PTR_InteropDispatchStubManager;
745
746class 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//
789typedef VPTR(class DelegateInvokeStubManager) PTR_DelegateInvokeStubManager;
790
791class 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
852typedef VPTR(class TailCallStubManager) PTR_TailCallStubManager;
853
854class TailCallStubManager : public StubManager
855{
856 VPTR_VTABLE_CLASS(TailCallStubManager, StubManager)
857
858public:
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
876private:
877 virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination * pTrace);
878
879#if defined(DACCESS_COMPILE)
880 virtual void DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags);
881
882protected:
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//
890class StubManagerHelpers
891{
892public:
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