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 | |
6 | #include "common.h" |
7 | #include "stubmgr.h" |
8 | #include "virtualcallstub.h" |
9 | #include "dllimportcallback.h" |
10 | #include "stubhelpers.h" |
11 | #include "asmconstants.h" |
12 | #ifdef FEATURE_COMINTEROP |
13 | #include "olecontexthelpers.h" |
14 | #endif |
15 | |
16 | #ifdef LOGGING |
17 | const char *GetTType( TraceType tt) |
18 | { |
19 | LIMITED_METHOD_CONTRACT; |
20 | |
21 | switch( tt ) |
22 | { |
23 | case TRACE_ENTRY_STUB: return "TRACE_ENTRY_STUB" ; |
24 | case TRACE_STUB: return "TRACE_STUB" ; |
25 | case TRACE_UNMANAGED: return "TRACE_UNMANAGED" ; |
26 | case TRACE_MANAGED: return "TRACE_MANAGED" ; |
27 | case TRACE_FRAME_PUSH: return "TRACE_FRAME_PUSH" ; |
28 | case TRACE_MGR_PUSH: return "TRACE_MGR_PUSH" ; |
29 | case TRACE_OTHER: return "TRACE_OTHER" ; |
30 | case TRACE_UNJITTED_METHOD: return "TRACE_UNJITTED_METHOD" ; |
31 | } |
32 | return "TRACE_REALLY_WACKED" ; |
33 | } |
34 | |
35 | void LogTraceDestination(const char * szHint, PCODE stubAddr, TraceDestination * pTrace) |
36 | { |
37 | LIMITED_METHOD_CONTRACT; |
38 | if (pTrace->GetTraceType() == TRACE_UNJITTED_METHOD) |
39 | { |
40 | MethodDesc * md = pTrace->GetMethodDesc(); |
41 | LOG((LF_CORDB, LL_INFO10000, "'%s' yields '%s' to method 0x%p for input 0x%p.\n" , |
42 | szHint, GetTType(pTrace->GetTraceType()), |
43 | md, stubAddr)); |
44 | } |
45 | else |
46 | { |
47 | LOG((LF_CORDB, LL_INFO10000, "'%s' yields '%s' to address 0x%p for input 0x%p.\n" , |
48 | szHint, GetTType(pTrace->GetTraceType()), |
49 | pTrace->GetAddress(), stubAddr)); |
50 | } |
51 | } |
52 | #endif |
53 | |
54 | #ifdef _DEBUG |
55 | // Get a string representation of this TraceDestination |
56 | // Uses the supplied buffer to store the memory (or may return a string literal). |
57 | const WCHAR * TraceDestination::DbgToString(SString & buffer) |
58 | { |
59 | CONTRACTL |
60 | { |
61 | NOTHROW; |
62 | GC_NOTRIGGER; |
63 | MODE_ANY; |
64 | } |
65 | CONTRACTL_END; |
66 | |
67 | const WCHAR * pValue = W("unknown" ); |
68 | |
69 | #ifndef DACCESS_COMPILE |
70 | if (!StubManager::IsStubLoggingEnabled()) |
71 | { |
72 | return W("<unavailable while native-debugging>" ); |
73 | } |
74 | // Now that we know we're not interop-debugging, we can safely call new. |
75 | SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; |
76 | |
77 | |
78 | FAULT_NOT_FATAL(); |
79 | |
80 | EX_TRY |
81 | { |
82 | switch(this->type) |
83 | { |
84 | case TRACE_ENTRY_STUB: |
85 | buffer.Printf("TRACE_ENTRY_STUB(addr=0x%p)" , GetAddress()); |
86 | pValue = buffer.GetUnicode(); |
87 | break; |
88 | |
89 | case TRACE_STUB: |
90 | buffer.Printf("TRACE_STUB(addr=0x%p)" , GetAddress()); |
91 | pValue = buffer.GetUnicode(); |
92 | break; |
93 | |
94 | case TRACE_UNMANAGED: |
95 | buffer.Printf("TRACE_UNMANAGED(addr=0x%p)" , GetAddress()); |
96 | pValue = buffer.GetUnicode(); |
97 | break; |
98 | |
99 | case TRACE_MANAGED: |
100 | buffer.Printf("TRACE_MANAGED(addr=0x%p)" , GetAddress()); |
101 | pValue = buffer.GetUnicode(); |
102 | break; |
103 | |
104 | case TRACE_UNJITTED_METHOD: |
105 | { |
106 | MethodDesc * md = this->GetMethodDesc(); |
107 | buffer.Printf("TRACE_UNJITTED_METHOD(md=0x%p, %s::%s)" , md, md->m_pszDebugClassName, md->m_pszDebugMethodName); |
108 | pValue = buffer.GetUnicode(); |
109 | } |
110 | break; |
111 | |
112 | case TRACE_FRAME_PUSH: |
113 | buffer.Printf("TRACE_FRAME_PUSH(addr=0x%p)" , GetAddress()); |
114 | pValue = buffer.GetUnicode(); |
115 | break; |
116 | |
117 | case TRACE_MGR_PUSH: |
118 | buffer.Printf("TRACE_MGR_PUSH(addr=0x%p, sm=%s)" , GetAddress(), this->GetStubManager()->DbgGetName()); |
119 | pValue = buffer.GetUnicode(); |
120 | break; |
121 | |
122 | case TRACE_OTHER: |
123 | pValue = W("TRACE_OTHER" ); |
124 | break; |
125 | } |
126 | } |
127 | EX_CATCH |
128 | { |
129 | pValue = W("(OOM while printing TD)" ); |
130 | } |
131 | EX_END_CATCH(SwallowAllExceptions); |
132 | #endif |
133 | return pValue; |
134 | } |
135 | #endif |
136 | |
137 | |
138 | void TraceDestination::InitForUnjittedMethod(MethodDesc * pDesc) |
139 | { |
140 | CONTRACTL |
141 | { |
142 | GC_NOTRIGGER; |
143 | NOTHROW; |
144 | MODE_ANY; |
145 | |
146 | PRECONDITION(CheckPointer(pDesc)); |
147 | } |
148 | CONTRACTL_END; |
149 | |
150 | _ASSERTE(pDesc->SanityCheck()); |
151 | |
152 | { |
153 | // If this is a wrapper stub, then find the real method that it will go to and patch that. |
154 | // This is more than just a convenience - converted wrapper MD to real MD is required for correct behavior. |
155 | // Wrapper MDs look like unjitted MethodDescs. So when the debugger patches one, |
156 | // it won't actually bind + apply the patch (it'll wait for the jit-complete instead). |
157 | // But if the wrapper MD is for prejitted code, then we'll never get the Jit-complete. |
158 | // Thus it'll miss the patch completely. |
159 | if (pDesc->IsWrapperStub()) |
160 | { |
161 | MethodDesc * pNewDesc = NULL; |
162 | |
163 | FAULT_NOT_FATAL(); |
164 | |
165 | |
166 | #ifndef DACCESS_COMPILE |
167 | EX_TRY |
168 | { |
169 | pNewDesc = pDesc->GetExistingWrappedMethodDesc(); |
170 | } |
171 | EX_CATCH |
172 | { |
173 | // In case of an error, we'll just stick w/ the original method desc. |
174 | } EX_END_CATCH(SwallowAllExceptions) |
175 | #else |
176 | // @todo - DAC needs this too, but the method is currently not DACized. |
177 | // However, we don't throw here b/c the error may not be fatal. |
178 | // DacNotImpl(); |
179 | #endif |
180 | |
181 | if (pNewDesc != NULL) |
182 | { |
183 | pDesc = pNewDesc; |
184 | |
185 | LOG((LF_CORDB, LL_INFO10000, "TD::UnjittedMethod: wrapper md: %p --> %p" , pDesc, pNewDesc)); |
186 | |
187 | } |
188 | } |
189 | } |
190 | |
191 | |
192 | this->type = TRACE_UNJITTED_METHOD; |
193 | this->pDesc = pDesc; |
194 | this->stubManager = NULL; |
195 | } |
196 | |
197 | |
198 | // Initialize statics. |
199 | #ifdef _DEBUG |
200 | SString * StubManager::s_pDbgStubManagerLog = NULL; |
201 | CrstStatic StubManager::s_DbgLogCrst; |
202 | |
203 | #endif |
204 | |
205 | SPTR_IMPL(StubManager, StubManager, g_pFirstManager); |
206 | |
207 | CrstStatic StubManager::s_StubManagerListCrst; |
208 | |
209 | //----------------------------------------------------------- |
210 | // For perf reasons, the stub managers are now kept in a two |
211 | // tier system: all stub managers but the VirtualStubManagers |
212 | // are in the first tier. A VirtualStubManagerManager takes |
213 | // care of all VirtualStubManagers, and is iterated last of |
214 | // all. It does a smarter job of looking up the owning |
215 | // manager for virtual stubs, checking the current and shared |
216 | // appdomains before checking the remaining managers. |
217 | // |
218 | // Thus, this iterator will run the regular list until it |
219 | // hits the end, then it will check the VSMM, then it will |
220 | // end. |
221 | //----------------------------------------------------------- |
222 | class StubManagerIterator |
223 | { |
224 | public: |
225 | StubManagerIterator(); |
226 | ~StubManagerIterator(); |
227 | |
228 | void Reset(); |
229 | BOOL Next(); |
230 | PTR_StubManager Current(); |
231 | |
232 | protected: |
233 | enum SMI_State |
234 | { |
235 | SMI_START, |
236 | SMI_NORMAL, |
237 | SMI_VIRTUALCALLSTUBMANAGER, |
238 | SMI_END |
239 | }; |
240 | |
241 | SMI_State m_state; |
242 | PTR_StubManager m_pCurMgr; |
243 | SimpleReadLockHolder m_lh; |
244 | }; |
245 | |
246 | //----------------------------------------------------------- |
247 | // Ctor |
248 | //----------------------------------------------------------- |
249 | StubManagerIterator::StubManagerIterator() |
250 | { |
251 | WRAPPER_NO_CONTRACT; |
252 | SUPPORTS_DAC; |
253 | |
254 | Reset(); |
255 | } |
256 | |
257 | void StubManagerIterator::Reset() |
258 | { |
259 | LIMITED_METHOD_DAC_CONTRACT; |
260 | m_pCurMgr = NULL; |
261 | m_state = SMI_START; |
262 | } |
263 | |
264 | //----------------------------------------------------------- |
265 | // Ctor |
266 | //----------------------------------------------------------- |
267 | StubManagerIterator::~StubManagerIterator() |
268 | { |
269 | LIMITED_METHOD_DAC_CONTRACT; |
270 | } |
271 | |
272 | //----------------------------------------------------------- |
273 | // Move to the next element. Iterators are created at |
274 | // start-1, so must call Next before using Current |
275 | //----------------------------------------------------------- |
276 | BOOL StubManagerIterator::Next() |
277 | { |
278 | CONTRACTL |
279 | { |
280 | NOTHROW; |
281 | GC_NOTRIGGER; |
282 | MODE_ANY; |
283 | #ifndef DACCESS_COMPILE |
284 | CAN_TAKE_LOCK; // because of m_lh.Assign() |
285 | #else |
286 | CANNOT_TAKE_LOCK; |
287 | #endif |
288 | } |
289 | CONTRACTL_END; |
290 | |
291 | SUPPORTS_DAC; |
292 | |
293 | do { |
294 | if (m_state == SMI_START) { |
295 | m_state = SMI_NORMAL; |
296 | m_pCurMgr = StubManager::g_pFirstManager; |
297 | } |
298 | else if (m_state == SMI_NORMAL) { |
299 | if (m_pCurMgr != NULL) { |
300 | m_pCurMgr = m_pCurMgr->m_pNextManager; |
301 | } |
302 | else { |
303 | // If we've reached the end of the regular list of stub managers, then we |
304 | // set the VirtualCallStubManagerManager is the current item (effectively |
305 | // forcing it to always be the last manager checked). |
306 | m_state = SMI_VIRTUALCALLSTUBMANAGER; |
307 | VirtualCallStubManagerManager *pVCSMMgr = VirtualCallStubManagerManager::GlobalManager(); |
308 | m_pCurMgr = PTR_StubManager(pVCSMMgr); |
309 | #ifndef DACCESS_COMPILE |
310 | m_lh.Assign(&pVCSMMgr->m_RWLock); |
311 | #endif |
312 | } |
313 | } |
314 | else if (m_state == SMI_VIRTUALCALLSTUBMANAGER) { |
315 | m_state = SMI_END; |
316 | m_pCurMgr = NULL; |
317 | #ifndef DACCESS_COMPILE |
318 | m_lh.Clear(); |
319 | #endif |
320 | } |
321 | } while (m_state != SMI_END && m_pCurMgr == NULL); |
322 | |
323 | CONSISTENCY_CHECK(m_state == SMI_END || m_pCurMgr != NULL); |
324 | return (m_state != SMI_END); |
325 | } |
326 | |
327 | //----------------------------------------------------------- |
328 | // Get the current contents of the iterator |
329 | //----------------------------------------------------------- |
330 | PTR_StubManager StubManagerIterator::Current() |
331 | { |
332 | LIMITED_METHOD_DAC_CONTRACT; |
333 | CONSISTENCY_CHECK(m_state != SMI_START); |
334 | CONSISTENCY_CHECK(m_state != SMI_END); |
335 | CONSISTENCY_CHECK(CheckPointer(m_pCurMgr)); |
336 | |
337 | return m_pCurMgr; |
338 | } |
339 | |
340 | #ifndef DACCESS_COMPILE |
341 | //----------------------------------------------------------- |
342 | //----------------------------------------------------------- |
343 | StubManager::StubManager() |
344 | : m_pNextManager(NULL) |
345 | { |
346 | LIMITED_METHOD_CONTRACT; |
347 | } |
348 | |
349 | //----------------------------------------------------------- |
350 | //----------------------------------------------------------- |
351 | StubManager::~StubManager() |
352 | { |
353 | CONTRACTL { |
354 | NOTHROW; |
355 | GC_NOTRIGGER; |
356 | CAN_TAKE_LOCK; // StubManager::UnlinkStubManager uses a crst |
357 | PRECONDITION(CheckPointer(this)); |
358 | } CONTRACTL_END; |
359 | |
360 | UnlinkStubManager(this); |
361 | } |
362 | #endif // #ifndef DACCESS_COMPILE |
363 | |
364 | #ifdef _DEBUG_IMPL |
365 | //----------------------------------------------------------- |
366 | // Verify that the stub is owned by the given stub manager |
367 | // and no other stub manager. If a stub is claimed by multiple managers, |
368 | // then the wrong manager may claim ownership and improperly trace the stub. |
369 | //----------------------------------------------------------- |
370 | BOOL StubManager::IsSingleOwner(PCODE stubAddress, StubManager * pOwner) |
371 | { |
372 | STATIC_CONTRACT_NOTHROW; |
373 | STATIC_CONTRACT_GC_NOTRIGGER; |
374 | STATIC_CONTRACT_FORBID_FAULT; |
375 | STATIC_CONTRACT_CAN_TAKE_LOCK; // courtesy StubManagerIterator |
376 | |
377 | // ensure this stubmanager owns it. |
378 | _ASSERTE(pOwner != NULL); |
379 | |
380 | // ensure nobody else does. |
381 | bool ownerFound = false; |
382 | int count = 0; |
383 | StubManagerIterator it; |
384 | while (it.Next()) |
385 | { |
386 | // Callers would have iterated till pOwner. |
387 | if (!ownerFound && it.Current() != pOwner) |
388 | continue; |
389 | |
390 | if (it.Current() == pOwner) |
391 | ownerFound = true; |
392 | |
393 | if (it.Current()->CheckIsStub_Worker(stubAddress)) |
394 | { |
395 | // If you hit this assert, you can tell what 2 stub managers are conflicting by inspecting their vtable. |
396 | CONSISTENCY_CHECK_MSGF((it.Current() == pOwner), ("Stub at 0x%p is owner by multiple managers (0x%p, 0x%p)" , |
397 | (void*) stubAddress, pOwner, it.Current())); |
398 | count++; |
399 | } |
400 | else |
401 | { |
402 | _ASSERTE(it.Current() != pOwner); |
403 | } |
404 | } |
405 | |
406 | _ASSERTE(ownerFound); |
407 | |
408 | // We expect pOwner to be the only one to own this stub. |
409 | return (count == 1); |
410 | } |
411 | #endif |
412 | |
413 | |
414 | |
415 | //----------------------------------------------------------- |
416 | //----------------------------------------------------------- |
417 | BOOL StubManager::CheckIsStub_Worker(PCODE stubStartAddress) |
418 | { |
419 | CONTRACTL |
420 | { |
421 | NOTHROW; |
422 | CAN_TAKE_LOCK; // CheckIsStub_Internal can enter SimpleRWLock |
423 | GC_NOTRIGGER; |
424 | SO_TOLERANT; |
425 | } |
426 | CONTRACTL_END; |
427 | |
428 | SUPPORTS_DAC; |
429 | |
430 | // @todo - consider having a single check for null right up front. |
431 | // Though this may cover bugs where stub-managers don't handle bad addresses. |
432 | // And someone could just as easily pass (0x01) as NULL. |
433 | if (stubStartAddress == NULL) |
434 | { |
435 | return FALSE; |
436 | } |
437 | |
438 | CONTRACT_VIOLATION(SOToleranceViolation); |
439 | // @todo : this might not have a thread |
440 | // BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return FALSE); |
441 | |
442 | struct Param |
443 | { |
444 | BOOL fIsStub; |
445 | StubManager *pThis; |
446 | TADDR stubStartAddress; |
447 | } param; |
448 | param.fIsStub = FALSE; |
449 | param.pThis = this; |
450 | param.stubStartAddress = stubStartAddress; |
451 | |
452 | // This may be called from DAC, and DAC + non-DAC have very different |
453 | // exception handling. |
454 | #ifdef DACCESS_COMPILE |
455 | PAL_TRY(Param *, pParam, ¶m) |
456 | #else |
457 | Param *pParam = ¶m; |
458 | EX_TRY |
459 | #endif |
460 | { |
461 | SUPPORTS_DAC; |
462 | |
463 | #ifndef DACCESS_COMPILE |
464 | // Use CheckIsStub_Internal may AV. That's ok. |
465 | AVInRuntimeImplOkayHolder AVOkay; |
466 | #endif |
467 | |
468 | // Make a Polymorphic call to derived stub manager. |
469 | // Try to see if this address is for a stub. If the address is |
470 | // completely bogus, then this might fault, so we protect it |
471 | // with SEH. |
472 | pParam->fIsStub = pParam->pThis->CheckIsStub_Internal(pParam->stubStartAddress); |
473 | } |
474 | #ifdef DACCESS_COMPILE |
475 | PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) |
476 | #else |
477 | EX_CATCH |
478 | #endif |
479 | { |
480 | LOG((LF_CORDB, LL_INFO10000, "D::GASTSI: exception indicated addr is bad.\n" )); |
481 | |
482 | param.fIsStub = FALSE; |
483 | } |
484 | #ifdef DACCESS_COMPILE |
485 | PAL_ENDTRY |
486 | #else |
487 | EX_END_CATCH(SwallowAllExceptions); |
488 | #endif |
489 | |
490 | //END_SO_INTOLERANT_CODE; |
491 | |
492 | return param.fIsStub; |
493 | } |
494 | |
495 | //----------------------------------------------------------- |
496 | // stubAddress may be an invalid address. |
497 | //----------------------------------------------------------- |
498 | PTR_StubManager StubManager::FindStubManager(PCODE stubAddress) |
499 | { |
500 | CONTRACTL |
501 | { |
502 | NOTHROW; |
503 | GC_NOTRIGGER; |
504 | CAN_TAKE_LOCK; // courtesy StubManagerIterator |
505 | } |
506 | CONTRACTL_END; |
507 | |
508 | SUPPORTS_DAC; |
509 | |
510 | StubManagerIterator it; |
511 | while (it.Next()) |
512 | { |
513 | if (it.Current()->CheckIsStub_Worker(stubAddress)) |
514 | { |
515 | _ASSERTE_IMPL(IsSingleOwner(stubAddress, it.Current())); |
516 | return it.Current(); |
517 | } |
518 | } |
519 | |
520 | return NULL; |
521 | } |
522 | |
523 | //----------------------------------------------------------- |
524 | // Given an address, figure out a TraceDestination describing where |
525 | // the instructions at that address will eventually transfer execution to. |
526 | //----------------------------------------------------------- |
527 | BOOL StubManager::TraceStub(PCODE stubStartAddress, TraceDestination *trace) |
528 | { |
529 | WRAPPER_NO_CONTRACT; |
530 | |
531 | StubManagerIterator it; |
532 | while (it.Next()) |
533 | { |
534 | StubManager * pCurrent = it.Current(); |
535 | if (pCurrent->CheckIsStub_Worker(stubStartAddress)) |
536 | { |
537 | LOG((LF_CORDB, LL_INFO10000, |
538 | "StubManager::TraceStub: addr 0x%p claimed by mgr " |
539 | "0x%p.\n" , stubStartAddress, pCurrent)); |
540 | |
541 | _ASSERTE_IMPL(IsSingleOwner(stubStartAddress, pCurrent)); |
542 | |
543 | BOOL fValid = pCurrent->DoTraceStub(stubStartAddress, trace); |
544 | #ifdef _DEBUG |
545 | if (IsStubLoggingEnabled()) |
546 | { |
547 | DbgWriteLog("Doing TraceStub for Address 0x%p, claimed by '%s' (0x%p)\n" , stubStartAddress, pCurrent->DbgGetName(), pCurrent); |
548 | if (fValid) |
549 | { |
550 | SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; |
551 | FAULT_NOT_FATAL(); |
552 | SString buffer; |
553 | DbgWriteLog(" td=%S\n" , trace->DbgToString(buffer)); |
554 | } |
555 | else |
556 | { |
557 | DbgWriteLog(" stubmanager returned false. Does not expect to call managed code\n" ); |
558 | |
559 | } |
560 | } // logging |
561 | #endif |
562 | return fValid; |
563 | } |
564 | } |
565 | |
566 | if (ExecutionManager::IsManagedCode(stubStartAddress)) |
567 | { |
568 | trace->InitForManaged(stubStartAddress); |
569 | |
570 | #ifdef _DEBUG |
571 | DbgWriteLog("Doing TraceStub for Address 0x%p is jitted code claimed by codemanager\n" , stubStartAddress); |
572 | #endif |
573 | |
574 | LOG((LF_CORDB, LL_INFO10000, |
575 | "StubManager::TraceStub: addr 0x%p is managed code\n" , |
576 | stubStartAddress)); |
577 | |
578 | return TRUE; |
579 | } |
580 | |
581 | LOG((LF_CORDB, LL_INFO10000, |
582 | "StubManager::TraceStub: addr 0x%p unknown. TRACE_OTHER...\n" , |
583 | stubStartAddress)); |
584 | |
585 | #ifdef _DEBUG |
586 | DbgWriteLog("Doing TraceStub for Address 0x%p is unknown!!!\n" , stubStartAddress); |
587 | #endif |
588 | |
589 | trace->InitForOther(stubStartAddress); |
590 | return FALSE; |
591 | } |
592 | |
593 | //----------------------------------------------------------- |
594 | //----------------------------------------------------------- |
595 | BOOL StubManager::FollowTrace(TraceDestination *trace) |
596 | { |
597 | STATIC_CONTRACT_NOTHROW; |
598 | STATIC_CONTRACT_GC_NOTRIGGER; |
599 | STATIC_CONTRACT_FORBID_FAULT; |
600 | |
601 | while (trace->GetTraceType() == TRACE_STUB) |
602 | { |
603 | LOG((LF_CORDB, LL_INFO10000, |
604 | "StubManager::FollowTrace: TRACE_STUB for 0x%p\n" , |
605 | trace->GetAddress())); |
606 | |
607 | if (!TraceStub(trace->GetAddress(), trace)) |
608 | { |
609 | // |
610 | // No stub manager claimed it - it must be an EE helper or something. |
611 | // |
612 | |
613 | trace->InitForOther(trace->GetAddress()); |
614 | } |
615 | } |
616 | |
617 | LOG_TRACE_DESTINATION(trace, NULL, "StubManager::FollowTrace" ); |
618 | |
619 | return trace->GetTraceType() != TRACE_OTHER; |
620 | } |
621 | |
622 | #ifndef DACCESS_COMPILE |
623 | |
624 | //----------------------------------------------------------- |
625 | //----------------------------------------------------------- |
626 | void StubManager::AddStubManager(StubManager *mgr) |
627 | { |
628 | WRAPPER_NO_CONTRACT; |
629 | CONSISTENCY_CHECK(CheckPointer(g_pFirstManager, NULL_OK)); |
630 | CONSISTENCY_CHECK(CheckPointer(mgr)); |
631 | |
632 | GCX_COOP_NO_THREAD_BROKEN(); |
633 | |
634 | CrstHolder ch(&s_StubManagerListCrst); |
635 | |
636 | if (g_pFirstManager == NULL) |
637 | { |
638 | g_pFirstManager = mgr; |
639 | } |
640 | else |
641 | { |
642 | mgr->m_pNextManager = g_pFirstManager; |
643 | g_pFirstManager = mgr; |
644 | } |
645 | |
646 | LOG((LF_CORDB, LL_EVERYTHING, "StubManager::AddStubManager - 0x%p (vptr %x%p)\n" , mgr, (*(PVOID*)mgr))); |
647 | } |
648 | |
649 | //----------------------------------------------------------- |
650 | // NOTE: The runtime MUST be suspended to use this in a |
651 | // truly safe manner. |
652 | //----------------------------------------------------------- |
653 | void StubManager::UnlinkStubManager(StubManager *mgr) |
654 | { |
655 | STATIC_CONTRACT_GC_NOTRIGGER; |
656 | STATIC_CONTRACT_NOTHROW; |
657 | STATIC_CONTRACT_CAN_TAKE_LOCK; |
658 | CONSISTENCY_CHECK(CheckPointer(g_pFirstManager, NULL_OK)); |
659 | CONSISTENCY_CHECK(CheckPointer(mgr)); |
660 | |
661 | CrstHolder ch(&s_StubManagerListCrst); |
662 | |
663 | StubManager **m = &g_pFirstManager; |
664 | while (*m != NULL) |
665 | { |
666 | if (*m == mgr) |
667 | { |
668 | *m = (*m)->m_pNextManager; |
669 | return; |
670 | } |
671 | m = &(*m)->m_pNextManager; |
672 | } |
673 | } |
674 | |
675 | #endif // #ifndef DACCESS_COMPILE |
676 | |
677 | #ifdef DACCESS_COMPILE |
678 | |
679 | //----------------------------------------------------------- |
680 | //----------------------------------------------------------- |
681 | void |
682 | StubManager::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
683 | { |
684 | SUPPORTS_DAC; |
685 | // Report the global list head. |
686 | DacEnumMemoryRegion(DacGlobalBase() + |
687 | g_dacGlobals.StubManager__g_pFirstManager, |
688 | sizeof(TADDR)); |
689 | |
690 | // |
691 | // Report the list contents. |
692 | // |
693 | |
694 | StubManagerIterator it; |
695 | while (it.Next()) |
696 | { |
697 | it.Current()->DoEnumMemoryRegions(flags); |
698 | } |
699 | } |
700 | |
701 | //----------------------------------------------------------- |
702 | //----------------------------------------------------------- |
703 | void |
704 | StubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
705 | { |
706 | SUPPORTS_DAC; |
707 | DAC_ENUM_VTHIS(); |
708 | EMEM_OUT(("MEM: %p StubManager base\n" , dac_cast<TADDR>(this))); |
709 | } |
710 | |
711 | #endif // #ifdef DACCESS_COMPILE |
712 | |
713 | //----------------------------------------------------------- |
714 | // Initialize the global stub manager service. |
715 | //----------------------------------------------------------- |
716 | void StubManager::InitializeStubManagers() |
717 | { |
718 | #if !defined(DACCESS_COMPILE) |
719 | |
720 | #if defined(_DEBUG) |
721 | s_DbgLogCrst.Init(CrstDebuggerHeapLock, (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN)); |
722 | #endif |
723 | s_StubManagerListCrst.Init(CrstDebuggerHeapLock, (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN)); |
724 | |
725 | #endif // !DACCESS_COMPILE |
726 | } |
727 | |
728 | //----------------------------------------------------------- |
729 | // Terminate the global stub manager service. |
730 | //----------------------------------------------------------- |
731 | void StubManager::TerminateStubManagers() |
732 | { |
733 | #if !defined(DACCESS_COMPILE) |
734 | |
735 | #if defined(_DEBUG) |
736 | DbgFinishLog(); |
737 | s_DbgLogCrst.Destroy(); |
738 | #endif |
739 | |
740 | s_StubManagerListCrst.Destroy(); |
741 | #endif // !DACCESS_COMPILE |
742 | } |
743 | |
744 | #ifdef _DEBUG |
745 | |
746 | //----------------------------------------------------------- |
747 | // Should stub-manager logging be enabled? |
748 | //----------------------------------------------------------- |
749 | bool StubManager::IsStubLoggingEnabled() |
750 | { |
751 | // Our current logging impl uses SString, which uses new(), which can't be called |
752 | // on the helper thread. (B/c it may deadlock. See SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE) |
753 | |
754 | // We avoid this by just not logging when native-debugging. |
755 | if (IsDebuggerPresent()) |
756 | { |
757 | return false; |
758 | } |
759 | |
760 | return true; |
761 | } |
762 | |
763 | |
764 | //----------------------------------------------------------- |
765 | // Call to reset the log. This is used at the start of a new step-operation. |
766 | // pThread is the managed thread doing the stepping. |
767 | // It should either be the current thread or the helper thread. |
768 | //----------------------------------------------------------- |
769 | void StubManager::DbgBeginLog(TADDR addrCallInstruction, TADDR addrCallTarget) |
770 | { |
771 | #ifndef DACCESS_COMPILE |
772 | CONTRACTL |
773 | { |
774 | NOTHROW; |
775 | GC_NOTRIGGER; |
776 | MODE_ANY; |
777 | } |
778 | CONTRACTL_END; |
779 | |
780 | |
781 | // We can't call new() if another thread holds the heap lock and is then suspended by |
782 | // an interop-debugging. Since this is debug-only logging code, we'll just skip |
783 | // it under those cases. |
784 | if (!IsStubLoggingEnabled()) |
785 | { |
786 | return; |
787 | } |
788 | // Now that we know we're not interop-debugging, we can safely call new. |
789 | SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; |
790 | FAULT_NOT_FATAL(); |
791 | |
792 | { |
793 | CrstHolder ch(&s_DbgLogCrst); |
794 | EX_TRY |
795 | { |
796 | if (s_pDbgStubManagerLog == NULL) |
797 | { |
798 | s_pDbgStubManagerLog = new SString(); |
799 | } |
800 | s_pDbgStubManagerLog->Clear(); |
801 | } |
802 | EX_CATCH |
803 | { |
804 | DbgFinishLog(); |
805 | } |
806 | EX_END_CATCH(SwallowAllExceptions); |
807 | } |
808 | |
809 | DbgWriteLog("Beginning Step-in. IP after Call instruction is at 0x%p, call target is at 0x%p\n" , |
810 | addrCallInstruction, addrCallTarget); |
811 | #endif |
812 | } |
813 | |
814 | //----------------------------------------------------------- |
815 | // Finish logging for this thread. |
816 | // pThread is the managed thread doing the stepping. |
817 | // It should either be the current thread or the helper thread. |
818 | //----------------------------------------------------------- |
819 | void StubManager::DbgFinishLog() |
820 | { |
821 | #ifndef DACCESS_COMPILE |
822 | CONTRACTL |
823 | { |
824 | NOTHROW; |
825 | GC_NOTRIGGER; |
826 | MODE_ANY; |
827 | } |
828 | CONTRACTL_END; |
829 | |
830 | CrstHolder ch(&s_DbgLogCrst); |
831 | |
832 | // Since this is just a tool for debugging, we don't care if we call new. |
833 | SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; |
834 | FAULT_NOT_FATAL(); |
835 | |
836 | delete s_pDbgStubManagerLog; |
837 | s_pDbgStubManagerLog = NULL; |
838 | |
839 | |
840 | #endif |
841 | } |
842 | |
843 | |
844 | //----------------------------------------------------------- |
845 | // Write an arbitrary string to the log. |
846 | //----------------------------------------------------------- |
847 | void StubManager::DbgWriteLog(const CHAR *format, ...) |
848 | { |
849 | #ifndef DACCESS_COMPILE |
850 | CONTRACTL |
851 | { |
852 | NOTHROW; |
853 | GC_NOTRIGGER; |
854 | MODE_ANY; |
855 | } |
856 | CONTRACTL_END; |
857 | |
858 | |
859 | if (!IsStubLoggingEnabled()) |
860 | { |
861 | return; |
862 | } |
863 | |
864 | // Since this is just a tool for debugging, we don't care if we call new. |
865 | SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; |
866 | FAULT_NOT_FATAL(); |
867 | |
868 | CrstHolder ch(&s_DbgLogCrst); |
869 | |
870 | if (s_pDbgStubManagerLog == NULL) |
871 | { |
872 | return; |
873 | } |
874 | |
875 | // Suppress asserts about lossy encoding conversion in SString::Printf |
876 | CHECK chk; |
877 | BOOL fEntered = chk.EnterAssert(); |
878 | |
879 | EX_TRY |
880 | { |
881 | va_list args; |
882 | va_start(args, format); |
883 | s_pDbgStubManagerLog->AppendVPrintf(format, args); |
884 | va_end(args); |
885 | } |
886 | EX_CATCH |
887 | { |
888 | } |
889 | EX_END_CATCH(SwallowAllExceptions); |
890 | |
891 | if (fEntered) chk.LeaveAssert(); |
892 | #endif |
893 | } |
894 | |
895 | |
896 | |
897 | //----------------------------------------------------------- |
898 | // Get the log as a string. |
899 | //----------------------------------------------------------- |
900 | void StubManager::DbgGetLog(SString * pStringOut) |
901 | { |
902 | #ifndef DACCESS_COMPILE |
903 | CONTRACTL |
904 | { |
905 | NOTHROW; |
906 | GC_NOTRIGGER; |
907 | MODE_ANY; |
908 | |
909 | PRECONDITION(CheckPointer(pStringOut)); |
910 | } |
911 | CONTRACTL_END; |
912 | |
913 | if (!IsStubLoggingEnabled()) |
914 | { |
915 | return; |
916 | } |
917 | |
918 | // Since this is just a tool for debugging, we don't care if we call new. |
919 | SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; |
920 | FAULT_NOT_FATAL(); |
921 | |
922 | CrstHolder ch(&s_DbgLogCrst); |
923 | |
924 | if (s_pDbgStubManagerLog == NULL) |
925 | { |
926 | return; |
927 | } |
928 | |
929 | EX_TRY |
930 | { |
931 | pStringOut->Set(*s_pDbgStubManagerLog); |
932 | } |
933 | EX_CATCH |
934 | { |
935 | } |
936 | EX_END_CATCH(SwallowAllExceptions); |
937 | #endif |
938 | } |
939 | |
940 | |
941 | #endif // _DEBUG |
942 | |
943 | extern "C" void STDCALL ThePreStubPatchLabel(void); |
944 | |
945 | //----------------------------------------------------------- |
946 | //----------------------------------------------------------- |
947 | BOOL ThePreStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) |
948 | { |
949 | CONTRACTL |
950 | { |
951 | NOTHROW; |
952 | GC_NOTRIGGER; |
953 | MODE_ANY; |
954 | |
955 | PRECONDITION(stubStartAddress != NULL); |
956 | PRECONDITION(CheckPointer(trace)); |
957 | } |
958 | CONTRACTL_END; |
959 | |
960 | // |
961 | // We cannot tell where the stub will end up |
962 | // until after the prestub worker has been run. |
963 | // |
964 | |
965 | trace->InitForFramePush(GetEEFuncEntryPoint(ThePreStubPatchLabel)); |
966 | |
967 | return TRUE; |
968 | } |
969 | |
970 | //----------------------------------------------------------- |
971 | BOOL ThePreStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
972 | { |
973 | LIMITED_METHOD_DAC_CONTRACT; |
974 | return stubStartAddress == GetPreStubEntryPoint(); |
975 | |
976 | } |
977 | |
978 | |
979 | // ------------------------------------------------------- |
980 | // Stub manager functions & globals |
981 | // ------------------------------------------------------- |
982 | |
983 | SPTR_IMPL(PrecodeStubManager, PrecodeStubManager, g_pManager); |
984 | |
985 | #ifndef DACCESS_COMPILE |
986 | |
987 | /* static */ |
988 | void PrecodeStubManager::Init() |
989 | { |
990 | CONTRACTL |
991 | { |
992 | THROWS; |
993 | GC_NOTRIGGER; |
994 | MODE_ANY; |
995 | } |
996 | CONTRACTL_END |
997 | |
998 | g_pManager = new PrecodeStubManager(); |
999 | StubManager::AddStubManager(g_pManager); |
1000 | } |
1001 | |
1002 | #endif // #ifndef DACCESS_COMPILE |
1003 | |
1004 | /* static */ |
1005 | BOOL PrecodeStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
1006 | { |
1007 | CONTRACTL |
1008 | { |
1009 | THROWS; // address may be bad, so we may AV. |
1010 | GC_NOTRIGGER; |
1011 | SUPPORTS_DAC; |
1012 | } |
1013 | CONTRACTL_END; |
1014 | |
1015 | // Forwarded to from RangeSectionStubManager |
1016 | return FALSE; |
1017 | } |
1018 | |
1019 | BOOL PrecodeStubManager::DoTraceStub(PCODE stubStartAddress, |
1020 | TraceDestination *trace) |
1021 | { |
1022 | CONTRACTL |
1023 | { |
1024 | INSTANCE_CHECK; |
1025 | NOTHROW; |
1026 | GC_NOTRIGGER; |
1027 | MODE_ANY; |
1028 | FORBID_FAULT; |
1029 | } |
1030 | CONTRACTL_END |
1031 | |
1032 | LOG((LF_CORDB, LL_EVERYTHING, "PrecodeStubManager::DoTraceStub called\n" )); |
1033 | |
1034 | MethodDesc* pMD = NULL; |
1035 | |
1036 | #ifdef HAS_COMPACT_ENTRYPOINTS |
1037 | if (MethodDescChunk::IsCompactEntryPointAtAddress(stubStartAddress)) |
1038 | { |
1039 | pMD = MethodDescChunk::GetMethodDescFromCompactEntryPoint(stubStartAddress); |
1040 | } |
1041 | else |
1042 | #endif // HAS_COMPACT_ENTRYPOINTS |
1043 | { |
1044 | Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(stubStartAddress); |
1045 | PREFIX_ASSUME(pPrecode != NULL); |
1046 | |
1047 | switch (pPrecode->GetType()) |
1048 | { |
1049 | case PRECODE_STUB: |
1050 | break; |
1051 | |
1052 | #ifdef HAS_NDIRECT_IMPORT_PRECODE |
1053 | case PRECODE_NDIRECT_IMPORT: |
1054 | #ifndef DACCESS_COMPILE |
1055 | trace->InitForUnmanaged(GetEEFuncEntryPoint(NDirectImportThunk)); |
1056 | #else |
1057 | trace->InitForOther(NULL); |
1058 | #endif |
1059 | LOG_TRACE_DESTINATION(trace, stubStartAddress, "PrecodeStubManager::DoTraceStub - NDirect import" ); |
1060 | return TRUE; |
1061 | #endif // HAS_NDIRECT_IMPORT_PRECODE |
1062 | |
1063 | #ifdef HAS_FIXUP_PRECODE |
1064 | case PRECODE_FIXUP: |
1065 | break; |
1066 | #endif // HAS_FIXUP_PRECODE |
1067 | |
1068 | #ifdef HAS_THISPTR_RETBUF_PRECODE |
1069 | case PRECODE_THISPTR_RETBUF: |
1070 | break; |
1071 | #endif // HAS_THISPTR_RETBUF_PRECODE |
1072 | |
1073 | default: |
1074 | _ASSERTE_IMPL(!"DoTraceStub: Unexpected precode type" ); |
1075 | break; |
1076 | } |
1077 | |
1078 | PCODE target = pPrecode->GetTarget(); |
1079 | |
1080 | // check if the method has been jitted |
1081 | if (!pPrecode->IsPointingToPrestub(target)) |
1082 | { |
1083 | trace->InitForStub(target); |
1084 | LOG_TRACE_DESTINATION(trace, stubStartAddress, "PrecodeStubManager::DoTraceStub - code" ); |
1085 | return TRUE; |
1086 | } |
1087 | |
1088 | pMD = pPrecode->GetMethodDesc(); |
1089 | } |
1090 | |
1091 | PREFIX_ASSUME(pMD != NULL); |
1092 | |
1093 | // If the method is not IL, then we patch the prestub because no one will ever change the call here at the |
1094 | // MethodDesc. If, however, this is an IL method, then we are at risk to have another thread backpatch the call |
1095 | // here, so we'd miss if we patched the prestub. Therefore, we go right to the IL method and patch IL offset 0 |
1096 | // by using TRACE_UNJITTED_METHOD. |
1097 | if (!pMD->IsIL()) |
1098 | { |
1099 | trace->InitForStub(GetPreStubEntryPoint()); |
1100 | } |
1101 | else |
1102 | { |
1103 | trace->InitForUnjittedMethod(pMD); |
1104 | } |
1105 | |
1106 | LOG_TRACE_DESTINATION(trace, stubStartAddress, "PrecodeStubManager::DoTraceStub - prestub" ); |
1107 | return TRUE; |
1108 | } |
1109 | |
1110 | #ifndef DACCESS_COMPILE |
1111 | BOOL PrecodeStubManager::TraceManager(Thread *thread, |
1112 | TraceDestination *trace, |
1113 | T_CONTEXT *pContext, |
1114 | BYTE **pRetAddr) |
1115 | { |
1116 | CONTRACTL |
1117 | { |
1118 | NOTHROW; |
1119 | GC_NOTRIGGER; |
1120 | MODE_ANY; |
1121 | PRECONDITION(CheckPointer(thread, NULL_OK)); |
1122 | PRECONDITION(CheckPointer(trace)); |
1123 | PRECONDITION(CheckPointer(pContext)); |
1124 | PRECONDITION(CheckPointer(pRetAddr)); |
1125 | } |
1126 | CONTRACTL_END; |
1127 | |
1128 | _ASSERTE(!"Unexpected call to PrecodeStubManager::TraceManager" ); |
1129 | return FALSE; |
1130 | } |
1131 | #endif |
1132 | |
1133 | // ------------------------------------------------------- |
1134 | // StubLinkStubManager |
1135 | // ------------------------------------------------------- |
1136 | |
1137 | SPTR_IMPL(StubLinkStubManager, StubLinkStubManager, g_pManager); |
1138 | |
1139 | #ifndef DACCESS_COMPILE |
1140 | |
1141 | /* static */ |
1142 | void StubLinkStubManager::Init() |
1143 | { |
1144 | CONTRACTL |
1145 | { |
1146 | THROWS; |
1147 | GC_NOTRIGGER; |
1148 | MODE_ANY; |
1149 | } |
1150 | CONTRACTL_END |
1151 | |
1152 | g_pManager = new StubLinkStubManager(); |
1153 | StubManager::AddStubManager(g_pManager); |
1154 | } |
1155 | |
1156 | #endif // #ifndef DACCESS_COMPILE |
1157 | |
1158 | BOOL StubLinkStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
1159 | { |
1160 | WRAPPER_NO_CONTRACT; |
1161 | SUPPORTS_DAC; |
1162 | |
1163 | return GetRangeList()->IsInRange(stubStartAddress); |
1164 | } |
1165 | |
1166 | |
1167 | BOOL StubLinkStubManager::DoTraceStub(PCODE stubStartAddress, |
1168 | TraceDestination *trace) |
1169 | { |
1170 | CONTRACTL |
1171 | { |
1172 | INSTANCE_CHECK; |
1173 | NOTHROW; |
1174 | GC_NOTRIGGER; |
1175 | MODE_ANY; |
1176 | } |
1177 | CONTRACTL_END |
1178 | |
1179 | LOG((LF_CORDB, LL_INFO10000, |
1180 | "StubLinkStubManager::DoTraceStub: stubStartAddress=0x%08x\n" , |
1181 | stubStartAddress)); |
1182 | |
1183 | Stub *stub = Stub::RecoverStub(stubStartAddress); |
1184 | |
1185 | LOG((LF_CORDB, LL_INFO10000, |
1186 | "StubLinkStubManager::DoTraceStub: stub=0x%08x\n" , stub)); |
1187 | |
1188 | // |
1189 | // If this is an intercept stub, we may be able to step |
1190 | // into the intercepted stub. |
1191 | // |
1192 | // <TODO>!!! Note that this case should not be necessary, it's just |
1193 | // here until I get all of the patch offsets & frame patch |
1194 | // methods in place.</TODO> |
1195 | // |
1196 | TADDR pRealAddr = 0; |
1197 | if (stub->IsIntercept()) |
1198 | { |
1199 | InterceptStub *is = dac_cast<PTR_InterceptStub>(stub); |
1200 | |
1201 | if (*is->GetInterceptedStub() == NULL) |
1202 | { |
1203 | pRealAddr = *is->GetRealAddr(); |
1204 | LOG((LF_CORDB, LL_INFO10000, "StubLinkStubManager::DoTraceStub" |
1205 | " Intercept stub, no following stub, real addr:0x%x\n" , |
1206 | pRealAddr)); |
1207 | } |
1208 | else |
1209 | { |
1210 | stub = *is->GetInterceptedStub(); |
1211 | |
1212 | pRealAddr = stub->GetEntryPoint(); |
1213 | |
1214 | LOG((LF_CORDB, LL_INFO10000, |
1215 | "StubLinkStubManager::DoTraceStub: intercepted " |
1216 | "stub=0x%08x, ep=0x%08x\n" , |
1217 | stub, stub->GetEntryPoint())); |
1218 | } |
1219 | _ASSERTE( pRealAddr ); |
1220 | |
1221 | // !!! will push a frame??? |
1222 | return TraceStub(pRealAddr, trace); |
1223 | } |
1224 | else if (stub->IsMulticastDelegate()) |
1225 | { |
1226 | LOG((LF_CORDB, LL_INFO10000, |
1227 | "StubLinkStubManager(MCDel)::DoTraceStub: stubStartAddress=0x%08x\n" , |
1228 | stubStartAddress)); |
1229 | |
1230 | LOG((LF_CORDB, LL_INFO10000, |
1231 | "StubLinkStubManager(MCDel)::DoTraceStub: stub=0x%08x MGR_PUSH to entrypoint:0x%x\n" , stub, |
1232 | stub->GetEntryPoint())); |
1233 | |
1234 | // If it's a MC delegate, then we want to set a BP & do a context-ful |
1235 | // manager push, so that we can figure out if this call will be to a |
1236 | // single multicast delegate or a multi multicast delegate |
1237 | trace->InitForManagerPush(stubStartAddress, this); |
1238 | |
1239 | return TRUE; |
1240 | } |
1241 | else if (stub->GetPatchOffset() == 0) |
1242 | { |
1243 | LOG((LF_CORDB, LL_INFO10000, "StubLinkStubManager::DoTraceStub: patch offset is 0!\n" )); |
1244 | |
1245 | return FALSE; |
1246 | } |
1247 | else |
1248 | { |
1249 | trace->InitForFramePush((PCODE)stub->GetPatchAddress()); |
1250 | |
1251 | LOG_TRACE_DESTINATION(trace, stubStartAddress, "StubLinkStubManager::DoTraceStub" ); |
1252 | |
1253 | return TRUE; |
1254 | } |
1255 | } |
1256 | |
1257 | #ifndef DACCESS_COMPILE |
1258 | |
1259 | BOOL StubLinkStubManager::TraceManager(Thread *thread, |
1260 | TraceDestination *trace, |
1261 | T_CONTEXT *pContext, |
1262 | BYTE **pRetAddr) |
1263 | { |
1264 | CONTRACTL |
1265 | { |
1266 | INSTANCE_CHECK; |
1267 | THROWS; |
1268 | GC_TRIGGERS; |
1269 | MODE_ANY; |
1270 | INJECT_FAULT(return FALSE;); |
1271 | } |
1272 | CONTRACTL_END |
1273 | |
1274 | // NOTE that we're assuming that this will be called if and ONLY if |
1275 | // we're examing a multicast delegate stub. Otherwise, we'll have to figure out |
1276 | // what we're looking iat |
1277 | |
1278 | BYTE *pbDel = 0; |
1279 | |
1280 | LPVOID pc = (LPVOID)GetIP(pContext); |
1281 | |
1282 | *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext); |
1283 | |
1284 | pbDel = (BYTE *)StubManagerHelpers::GetThisPtr(pContext); |
1285 | |
1286 | LOG((LF_CORDB,LL_INFO10000, "SLSM:TM at 0x%x, retAddr is 0x%x\n" , pc, (*pRetAddr))); |
1287 | |
1288 | return DelegateInvokeStubManager::TraceDelegateObject(pbDel, trace); |
1289 | } |
1290 | |
1291 | #endif // #ifndef DACCESS_COMPILE |
1292 | |
1293 | // ------------------------------------------------------- |
1294 | // Stub manager for thunks. |
1295 | // |
1296 | // Note, the only reason we have this stub manager is so that we can recgonize UMEntryThunks for IsTransitionStub. If it |
1297 | // turns out that having a full-blown stub manager for these things causes problems else where, then we can just attach |
1298 | // a range list to the thunk heap and have IsTransitionStub check that after checking with the main stub manager. |
1299 | // ------------------------------------------------------- |
1300 | |
1301 | SPTR_IMPL(ThunkHeapStubManager, ThunkHeapStubManager, g_pManager); |
1302 | |
1303 | #ifndef DACCESS_COMPILE |
1304 | |
1305 | /* static */ |
1306 | void ThunkHeapStubManager::Init() |
1307 | { |
1308 | CONTRACTL |
1309 | { |
1310 | THROWS; |
1311 | GC_NOTRIGGER; |
1312 | MODE_ANY; |
1313 | INJECT_FAULT(COMPlusThrowOM()); |
1314 | } |
1315 | CONTRACTL_END; |
1316 | |
1317 | g_pManager = new ThunkHeapStubManager(); |
1318 | StubManager::AddStubManager(g_pManager); |
1319 | } |
1320 | |
1321 | #endif // !DACCESS_COMPILE |
1322 | |
1323 | BOOL ThunkHeapStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
1324 | { |
1325 | WRAPPER_NO_CONTRACT; |
1326 | SUPPORTS_DAC; |
1327 | |
1328 | // Its a stub if its in our heaps range. |
1329 | return GetRangeList()->IsInRange(stubStartAddress); |
1330 | } |
1331 | |
1332 | BOOL ThunkHeapStubManager::DoTraceStub(PCODE stubStartAddress, |
1333 | TraceDestination *trace) |
1334 | { |
1335 | LIMITED_METHOD_CONTRACT; |
1336 | // We never trace through these stubs when stepping through managed code. The only reason we have this stub manager |
1337 | // is so that IsTransitionStub can recgonize UMEntryThunks. |
1338 | return FALSE; |
1339 | } |
1340 | |
1341 | // ------------------------------------------------------- |
1342 | // JumpStub stubs |
1343 | // |
1344 | // Stub manager for jump stubs created by ExecutionManager::jumpStub() |
1345 | // These are currently used only on the 64-bit targets IA64 and AMD64 |
1346 | // |
1347 | // ------------------------------------------------------- |
1348 | |
1349 | SPTR_IMPL(JumpStubStubManager, JumpStubStubManager, g_pManager); |
1350 | |
1351 | #ifndef DACCESS_COMPILE |
1352 | /* static */ |
1353 | void JumpStubStubManager::Init() |
1354 | { |
1355 | CONTRACTL |
1356 | { |
1357 | THROWS; |
1358 | GC_NOTRIGGER; |
1359 | MODE_ANY; |
1360 | } |
1361 | CONTRACTL_END |
1362 | |
1363 | g_pManager = new JumpStubStubManager(); |
1364 | StubManager::AddStubManager(g_pManager); |
1365 | } |
1366 | #endif // #ifndef DACCESS_COMPILE |
1367 | |
1368 | BOOL JumpStubStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
1369 | { |
1370 | WRAPPER_NO_CONTRACT; |
1371 | SUPPORTS_DAC; |
1372 | |
1373 | // Forwarded to from RangeSectionStubManager |
1374 | return FALSE; |
1375 | } |
1376 | |
1377 | BOOL JumpStubStubManager::DoTraceStub(PCODE stubStartAddress, |
1378 | TraceDestination *trace) |
1379 | { |
1380 | LIMITED_METHOD_CONTRACT; |
1381 | |
1382 | PCODE jumpTarget = decodeBackToBackJump(stubStartAddress); |
1383 | trace->InitForStub(jumpTarget); |
1384 | |
1385 | LOG_TRACE_DESTINATION(trace, stubStartAddress, "JumpStubStubManager::DoTraceStub" ); |
1386 | |
1387 | return TRUE; |
1388 | } |
1389 | |
1390 | // |
1391 | // Stub manager for code sections. It forwards the query to the more appropriate |
1392 | // stub manager, or handles the query itself. |
1393 | // |
1394 | |
1395 | SPTR_IMPL(RangeSectionStubManager, RangeSectionStubManager, g_pManager); |
1396 | |
1397 | #ifndef DACCESS_COMPILE |
1398 | /* static */ |
1399 | void RangeSectionStubManager::Init() |
1400 | { |
1401 | CONTRACTL |
1402 | { |
1403 | THROWS; |
1404 | GC_NOTRIGGER; |
1405 | MODE_ANY; |
1406 | } |
1407 | CONTRACTL_END |
1408 | |
1409 | g_pManager = new RangeSectionStubManager(); |
1410 | StubManager::AddStubManager(g_pManager); |
1411 | } |
1412 | #endif // #ifndef DACCESS_COMPILE |
1413 | |
1414 | BOOL RangeSectionStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
1415 | { |
1416 | WRAPPER_NO_CONTRACT; |
1417 | SUPPORTS_DAC; |
1418 | |
1419 | switch (GetStubKind(stubStartAddress)) |
1420 | { |
1421 | case STUB_CODE_BLOCK_PRECODE: |
1422 | case STUB_CODE_BLOCK_JUMPSTUB: |
1423 | case STUB_CODE_BLOCK_STUBLINK: |
1424 | case STUB_CODE_BLOCK_VIRTUAL_METHOD_THUNK: |
1425 | case STUB_CODE_BLOCK_EXTERNAL_METHOD_THUNK: |
1426 | case STUB_CODE_BLOCK_METHOD_CALL_THUNK: |
1427 | return TRUE; |
1428 | default: |
1429 | break; |
1430 | } |
1431 | |
1432 | return FALSE; |
1433 | } |
1434 | |
1435 | BOOL RangeSectionStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) |
1436 | { |
1437 | CONTRACTL |
1438 | { |
1439 | INSTANCE_CHECK; |
1440 | NOTHROW; |
1441 | GC_NOTRIGGER; |
1442 | MODE_ANY; |
1443 | FORBID_FAULT; |
1444 | } |
1445 | CONTRACTL_END |
1446 | |
1447 | switch (GetStubKind(stubStartAddress)) |
1448 | { |
1449 | case STUB_CODE_BLOCK_PRECODE: |
1450 | return PrecodeStubManager::g_pManager->DoTraceStub(stubStartAddress, trace); |
1451 | |
1452 | case STUB_CODE_BLOCK_JUMPSTUB: |
1453 | return JumpStubStubManager::g_pManager->DoTraceStub(stubStartAddress, trace); |
1454 | |
1455 | case STUB_CODE_BLOCK_STUBLINK: |
1456 | return StubLinkStubManager::g_pManager->DoTraceStub(stubStartAddress, trace); |
1457 | |
1458 | #ifdef FEATURE_PREJIT |
1459 | case STUB_CODE_BLOCK_VIRTUAL_METHOD_THUNK: |
1460 | { |
1461 | PCODE pTarget = GetMethodThunkTarget(stubStartAddress); |
1462 | if (pTarget == ExecutionManager::FindZapModule(stubStartAddress)-> |
1463 | GetNGenLayoutInfo()->m_pVirtualImportFixupJumpStub) |
1464 | { |
1465 | #ifdef DACCESS_COMPILE |
1466 | DacNotImpl(); |
1467 | #else |
1468 | trace->InitForManagerPush(GetEEFuncEntryPoint(VirtualMethodFixupPatchLabel), this); |
1469 | #endif |
1470 | } |
1471 | else |
1472 | { |
1473 | trace->InitForStub(pTarget); |
1474 | } |
1475 | return TRUE; |
1476 | } |
1477 | #endif |
1478 | |
1479 | case STUB_CODE_BLOCK_EXTERNAL_METHOD_THUNK: |
1480 | { |
1481 | PCODE pTarget = GetMethodThunkTarget(stubStartAddress); |
1482 | if (pTarget != ExecutionManager::FindZapModule(stubStartAddress)-> |
1483 | GetNGenLayoutInfo()->m_pExternalMethodFixupJumpStub) |
1484 | { |
1485 | trace->InitForStub(pTarget); |
1486 | return TRUE; |
1487 | } |
1488 | } |
1489 | |
1490 | __fallthrough; |
1491 | |
1492 | case STUB_CODE_BLOCK_METHOD_CALL_THUNK: |
1493 | #ifdef DACCESS_COMPILE |
1494 | DacNotImpl(); |
1495 | #else |
1496 | trace->InitForManagerPush(GetEEFuncEntryPoint(ExternalMethodFixupPatchLabel), this); |
1497 | #endif |
1498 | return TRUE; |
1499 | |
1500 | default: |
1501 | break; |
1502 | } |
1503 | |
1504 | return FALSE; |
1505 | } |
1506 | |
1507 | #ifndef DACCESS_COMPILE |
1508 | BOOL RangeSectionStubManager::TraceManager(Thread *thread, |
1509 | TraceDestination *trace, |
1510 | CONTEXT *pContext, |
1511 | BYTE **pRetAddr) |
1512 | { |
1513 | CONTRACTL |
1514 | { |
1515 | NOTHROW; |
1516 | GC_NOTRIGGER; |
1517 | MODE_ANY; |
1518 | } |
1519 | CONTRACTL_END; |
1520 | |
1521 | // Both virtual and external import thunks have the same structure. We can use |
1522 | // common code to handle them. |
1523 | _ASSERTE(GetIP(pContext) == GetEEFuncEntryPoint(VirtualMethodFixupPatchLabel) |
1524 | || GetIP(pContext) == GetEEFuncEntryPoint(ExternalMethodFixupPatchLabel)); |
1525 | |
1526 | *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext); |
1527 | |
1528 | PCODE target = StubManagerHelpers::GetTailCallTarget(pContext); |
1529 | trace->InitForStub(target); |
1530 | return TRUE; |
1531 | } |
1532 | #endif |
1533 | |
1534 | PCODE RangeSectionStubManager::GetMethodThunkTarget(PCODE stubStartAddress) |
1535 | { |
1536 | WRAPPER_NO_CONTRACT; |
1537 | |
1538 | #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) |
1539 | return rel32Decode(stubStartAddress+1); |
1540 | #elif defined(_TARGET_ARM_) |
1541 | TADDR pInstr = PCODEToPINSTR(stubStartAddress); |
1542 | return *dac_cast<PTR_PCODE>(pInstr + 2 * sizeof(DWORD)); |
1543 | #else |
1544 | PORTABILITY_ASSERT("RangeSectionStubManager::GetMethodThunkTarget" ); |
1545 | return NULL; |
1546 | #endif |
1547 | } |
1548 | |
1549 | #ifdef DACCESS_COMPILE |
1550 | LPCWSTR RangeSectionStubManager::GetStubManagerName(PCODE addr) |
1551 | { |
1552 | WRAPPER_NO_CONTRACT; |
1553 | |
1554 | switch (GetStubKind(addr)) |
1555 | { |
1556 | case STUB_CODE_BLOCK_PRECODE: |
1557 | return W("MethodDescPrestub" ); |
1558 | |
1559 | case STUB_CODE_BLOCK_JUMPSTUB: |
1560 | return W("JumpStub" ); |
1561 | |
1562 | case STUB_CODE_BLOCK_STUBLINK: |
1563 | return W("StubLinkStub" ); |
1564 | |
1565 | case STUB_CODE_BLOCK_VIRTUAL_METHOD_THUNK: |
1566 | return W("VirtualMethodThunk" ); |
1567 | |
1568 | case STUB_CODE_BLOCK_EXTERNAL_METHOD_THUNK: |
1569 | return W("ExternalMethodThunk" ); |
1570 | |
1571 | case STUB_CODE_BLOCK_METHOD_CALL_THUNK: |
1572 | return W("MethodCallThunk" ); |
1573 | |
1574 | default: |
1575 | break; |
1576 | } |
1577 | |
1578 | return W("UnknownRangeSectionStub" ); |
1579 | } |
1580 | #endif // DACCESS_COMPILE |
1581 | |
1582 | StubCodeBlockKind |
1583 | RangeSectionStubManager::GetStubKind(PCODE stubStartAddress) |
1584 | { |
1585 | CONTRACTL |
1586 | { |
1587 | NOTHROW; |
1588 | GC_NOTRIGGER; |
1589 | SO_TOLERANT; |
1590 | MODE_ANY; |
1591 | } |
1592 | CONTRACTL_END; |
1593 | |
1594 | RangeSection * pRS = ExecutionManager::FindCodeRange(stubStartAddress, ExecutionManager::ScanReaderLock); |
1595 | if (pRS == NULL) |
1596 | return STUB_CODE_BLOCK_UNKNOWN; |
1597 | |
1598 | return pRS->pjit->GetStubCodeBlockKind(pRS, stubStartAddress); |
1599 | } |
1600 | |
1601 | // |
1602 | // This is the stub manager for IL stubs. |
1603 | // |
1604 | |
1605 | #ifndef DACCESS_COMPILE |
1606 | |
1607 | /* static */ |
1608 | void ILStubManager::Init() |
1609 | { |
1610 | CONTRACTL |
1611 | { |
1612 | THROWS; |
1613 | GC_NOTRIGGER; |
1614 | MODE_ANY; |
1615 | } |
1616 | CONTRACTL_END |
1617 | |
1618 | StubManager::AddStubManager(new ILStubManager()); |
1619 | } |
1620 | |
1621 | #endif // #ifndef DACCESS_COMPILE |
1622 | |
1623 | BOOL ILStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
1624 | { |
1625 | WRAPPER_NO_CONTRACT; |
1626 | SUPPORTS_DAC; |
1627 | |
1628 | MethodDesc *pMD = ExecutionManager::GetCodeMethodDesc(stubStartAddress); |
1629 | |
1630 | return (pMD != NULL) && pMD->IsILStub(); |
1631 | } |
1632 | |
1633 | BOOL ILStubManager::DoTraceStub(PCODE stubStartAddress, |
1634 | TraceDestination *trace) |
1635 | { |
1636 | LIMITED_METHOD_CONTRACT; |
1637 | |
1638 | LOG((LF_CORDB, LL_EVERYTHING, "ILStubManager::DoTraceStub called\n" )); |
1639 | |
1640 | #ifndef DACCESS_COMPILE |
1641 | |
1642 | PCODE traceDestination = NULL; |
1643 | |
1644 | #ifdef FEATURE_MULTICASTSTUB_AS_IL |
1645 | MethodDesc* pStubMD = ExecutionManager::GetCodeMethodDesc(stubStartAddress); |
1646 | if (pStubMD != NULL && pStubMD->AsDynamicMethodDesc()->IsMulticastStub()) |
1647 | { |
1648 | traceDestination = GetEEFuncEntryPoint(StubHelpers::MulticastDebuggerTraceHelper); |
1649 | } |
1650 | else |
1651 | #endif // FEATURE_MULTICASTSTUB_AS_IL |
1652 | { |
1653 | // This call is going out to unmanaged code, either through pinvoke or COM interop. |
1654 | traceDestination = stubStartAddress; |
1655 | } |
1656 | |
1657 | trace->InitForManagerPush(traceDestination, this); |
1658 | LOG_TRACE_DESTINATION(trace, traceDestination, "ILStubManager::DoTraceStub" ); |
1659 | |
1660 | return TRUE; |
1661 | |
1662 | #else // !DACCESS_COMPILE |
1663 | trace->InitForOther(NULL); |
1664 | return FALSE; |
1665 | |
1666 | #endif // !DACCESS_COMPILE |
1667 | } |
1668 | |
1669 | #ifndef DACCESS_COMPILE |
1670 | #ifdef FEATURE_COMINTEROP |
1671 | PCODE ILStubManager::GetCOMTarget(Object *pThis, ComPlusCallInfo *pComPlusCallInfo) |
1672 | { |
1673 | CONTRACTL |
1674 | { |
1675 | THROWS; |
1676 | GC_TRIGGERS; |
1677 | MODE_COOPERATIVE; |
1678 | } |
1679 | CONTRACTL_END; |
1680 | |
1681 | // calculate the target interface pointer |
1682 | SafeComHolder<IUnknown> pUnk; |
1683 | |
1684 | OBJECTREF oref = ObjectToOBJECTREF(pThis); |
1685 | GCPROTECT_BEGIN(oref); |
1686 | pUnk = ComObject::GetComIPFromRCWThrowing(&oref, pComPlusCallInfo->m_pInterfaceMT); |
1687 | GCPROTECT_END(); |
1688 | |
1689 | LPVOID *lpVtbl = *(LPVOID **)(IUnknown *)pUnk; |
1690 | |
1691 | PCODE target = (PCODE)lpVtbl[pComPlusCallInfo->m_cachedComSlot]; |
1692 | return target; |
1693 | } |
1694 | |
1695 | // This function should return the same result as StubHelpers::GetWinRTFactoryObject followed by |
1696 | // ILStubManager::GetCOMTarget. The difference is that it does not allocate managed memory, so it |
1697 | // does not trigger GC. |
1698 | // |
1699 | // The reason why GC (and potentially a stack walk for other purposes, such as exception handling) |
1700 | // would be problematic is that we are stopped at the first instruction of an IL stub which is |
1701 | // not a GC-safe point. Technically, the function still has the GC_TRIGGERS contract but we should |
1702 | // not see GC in practice here without allocating. |
1703 | // |
1704 | // Note that the GC suspension logic detects the debugger-is-attached-at-GC-unsafe-point case and |
1705 | // will back off and retry. This means that it suffices to ensure that this thread does not trigger |
1706 | // GC, allocations on other threads will wait and not cause major trouble. |
1707 | PCODE ILStubManager::GetWinRTFactoryTarget(ComPlusCallMethodDesc *pCMD) |
1708 | { |
1709 | CONTRACTL |
1710 | { |
1711 | THROWS; |
1712 | GC_TRIGGERS; |
1713 | MODE_COOPERATIVE; |
1714 | } |
1715 | CONTRACTL_END; |
1716 | |
1717 | MethodTable *pMT = pCMD->GetMethodTable(); |
1718 | |
1719 | // GetComClassFactory could load types and trigger GC, get class name manually |
1720 | InlineSString<DEFAULT_NONSTACK_CLASSNAME_SIZE> ssClassName; |
1721 | pMT->_GetFullyQualifiedNameForClass(ssClassName); |
1722 | |
1723 | IID iid; |
1724 | pCMD->m_pComPlusCallInfo->m_pInterfaceMT->GetGuid(&iid, FALSE, FALSE); |
1725 | |
1726 | SafeComHolder<IInspectable> pFactory; |
1727 | { |
1728 | GCX_PREEMP(); |
1729 | if (SUCCEEDED(RoGetActivationFactory(WinRtStringRef(ssClassName.GetUnicode(), ssClassName.GetCount()), iid, &pFactory))) |
1730 | { |
1731 | LPVOID *lpVtbl = *(LPVOID **)(IUnknown *)pFactory; |
1732 | return (PCODE)lpVtbl[pCMD->m_pComPlusCallInfo->m_cachedComSlot]; |
1733 | } |
1734 | } |
1735 | |
1736 | return NULL; |
1737 | } |
1738 | #endif // FEATURE_COMINTEROP |
1739 | |
1740 | #ifndef CROSSGEN_COMPILE |
1741 | BOOL ILStubManager::TraceManager(Thread *thread, |
1742 | TraceDestination *trace, |
1743 | T_CONTEXT *pContext, |
1744 | BYTE **pRetAddr) |
1745 | { |
1746 | // See code:ILStubCache.CreateNewMethodDesc for the code that sets flags on stub MDs |
1747 | |
1748 | PCODE stubIP = GetIP(pContext); |
1749 | *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext); |
1750 | |
1751 | #ifdef FEATURE_MULTICASTSTUB_AS_IL |
1752 | if (stubIP == GetEEFuncEntryPoint(StubHelpers::MulticastDebuggerTraceHelper)) |
1753 | { |
1754 | stubIP = (PCODE)*pRetAddr; |
1755 | *pRetAddr = (BYTE*)StubManagerHelpers::GetRetAddrFromMulticastILStubFrame(pContext); |
1756 | } |
1757 | #endif |
1758 | |
1759 | DynamicMethodDesc *pStubMD = Entry2MethodDesc(stubIP, NULL)->AsDynamicMethodDesc(); |
1760 | |
1761 | TADDR arg = StubManagerHelpers::GetHiddenArg(pContext); |
1762 | |
1763 | Object * pThis = StubManagerHelpers::GetThisPtr(pContext); |
1764 | |
1765 | // See code:ILStubCache.CreateNewMethodDesc for the code that sets flags on stub MDs |
1766 | PCODE target; |
1767 | |
1768 | #ifdef FEATURE_MULTICASTSTUB_AS_IL |
1769 | if(pStubMD->IsMulticastStub()) |
1770 | { |
1771 | _ASSERTE(GetIP(pContext) == GetEEFuncEntryPoint(StubHelpers::MulticastDebuggerTraceHelper)); |
1772 | |
1773 | int delegateCount = (int)StubManagerHelpers::GetSecondArg(pContext); |
1774 | |
1775 | int totalDelegateCount = (int)*(size_t*)((BYTE*)pThis + DelegateObject::GetOffsetOfInvocationCount()); |
1776 | |
1777 | if (delegateCount == totalDelegateCount) |
1778 | { |
1779 | LOG((LF_CORDB, LL_INFO1000, "MF::TF: Executed all stubs, should return\n" )); |
1780 | // We've executed all the stubs, so we should return |
1781 | return FALSE; |
1782 | } |
1783 | else |
1784 | { |
1785 | // We're going to execute stub delegateCount next, so go and grab it. |
1786 | BYTE *pbDelInvocationList = *(BYTE **)((BYTE*)pThis + DelegateObject::GetOffsetOfInvocationList()); |
1787 | |
1788 | BYTE* pbDel = *(BYTE**)( ((ArrayBase *)pbDelInvocationList)->GetDataPtr() + |
1789 | ((ArrayBase *)pbDelInvocationList)->GetComponentSize()*delegateCount); |
1790 | |
1791 | _ASSERTE(pbDel); |
1792 | return DelegateInvokeStubManager::TraceDelegateObject(pbDel, trace); |
1793 | } |
1794 | |
1795 | } |
1796 | else |
1797 | #endif // FEATURE_MULTICASTSTUB_AS_IL |
1798 | if (pStubMD->IsReverseStub()) |
1799 | { |
1800 | if (pStubMD->IsStatic()) |
1801 | { |
1802 | // This is reverse P/Invoke stub, the argument is UMEntryThunk |
1803 | UMEntryThunk *pEntryThunk = (UMEntryThunk *)arg; |
1804 | target = pEntryThunk->GetManagedTarget(); |
1805 | |
1806 | LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Reverse P/Invoke case 0x%x\n" , target)); |
1807 | } |
1808 | else |
1809 | { |
1810 | // This is COM-to-CLR stub, the argument is the target |
1811 | target = (PCODE)arg; |
1812 | LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: COM-to-CLR case 0x%x\n" , target)); |
1813 | } |
1814 | trace->InitForManaged(target); |
1815 | } |
1816 | else if (pStubMD->IsDelegateStub()) |
1817 | { |
1818 | // This is forward delegate P/Invoke stub, the argument is undefined |
1819 | DelegateObject *pDel = (DelegateObject *)pThis; |
1820 | target = pDel->GetMethodPtrAux(); |
1821 | |
1822 | LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Forward delegate P/Invoke case 0x%x\n" , target)); |
1823 | trace->InitForUnmanaged(target); |
1824 | } |
1825 | else if (pStubMD->IsCALLIStub()) |
1826 | { |
1827 | // This is unmanaged CALLI stub, the argument is the target |
1828 | target = (PCODE)arg; |
1829 | |
1830 | // The value is mangled on 64-bit |
1831 | #ifdef _TARGET_AMD64_ |
1832 | target = target >> 1; // call target is encoded as (addr << 1) | 1 |
1833 | #endif // _TARGET_AMD64_ |
1834 | |
1835 | LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Unmanaged CALLI case 0x%x\n" , target)); |
1836 | trace->InitForUnmanaged(target); |
1837 | } |
1838 | #ifdef FEATURE_COMINTEROP |
1839 | else if (pStubMD->IsDelegateCOMStub()) |
1840 | { |
1841 | // This is a delegate, but the target is COM. |
1842 | DelegateObject *pDel = (DelegateObject *)pThis; |
1843 | DelegateEEClass *pClass = (DelegateEEClass *)pDel->GetMethodTable()->GetClass(); |
1844 | |
1845 | target = GetCOMTarget(pThis, pClass->m_pComPlusCallInfo); |
1846 | |
1847 | LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: CLR-to-COM via delegate case 0x%x\n" , target)); |
1848 | trace->InitForUnmanaged(target); |
1849 | } |
1850 | #endif // FEATURE_COMINTEROP |
1851 | else |
1852 | { |
1853 | // This is either direct forward P/Invoke or a CLR-to-COM call, the argument is MD |
1854 | MethodDesc *pMD = (MethodDesc *)arg; |
1855 | |
1856 | if (pMD->IsNDirect()) |
1857 | { |
1858 | target = (PCODE)((NDirectMethodDesc *)pMD)->GetNativeNDirectTarget(); |
1859 | LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Forward P/Invoke case 0x%x\n" , target)); |
1860 | trace->InitForUnmanaged(target); |
1861 | } |
1862 | #ifdef FEATURE_COMINTEROP |
1863 | else |
1864 | { |
1865 | _ASSERTE(pMD->IsComPlusCall()); |
1866 | ComPlusCallMethodDesc *pCMD = (ComPlusCallMethodDesc *)pMD; |
1867 | |
1868 | if (pCMD->IsStatic() || pCMD->IsCtor()) |
1869 | { |
1870 | // pThis is not the object we'll be calling, we need to get the factory object instead |
1871 | MethodTable *pMTOfTypeToCreate = pCMD->GetMethodTable(); |
1872 | pThis = OBJECTREFToObject(GetAppDomain()->LookupWinRTFactoryObject(pMTOfTypeToCreate, GetCurrentCtxCookie())); |
1873 | |
1874 | if (pThis == NULL) |
1875 | { |
1876 | // If we don't have an RCW of the factory object yet, don't create it. We would |
1877 | // risk triggering GC which is not safe here because the IL stub is not at a GC |
1878 | // safe point. Instead, query WinRT directly and release the factory immediately. |
1879 | target = GetWinRTFactoryTarget(pCMD); |
1880 | |
1881 | if (target != NULL) |
1882 | { |
1883 | LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: CLR-to-COM WinRT factory RCW-does-not-exist-yet case 0x%x\n" , target)); |
1884 | trace->InitForUnmanaged(target); |
1885 | } |
1886 | } |
1887 | } |
1888 | |
1889 | if (pThis != NULL) |
1890 | { |
1891 | target = GetCOMTarget(pThis, pCMD->m_pComPlusCallInfo); |
1892 | |
1893 | LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: CLR-to-COM case 0x%x\n" , target)); |
1894 | trace->InitForUnmanaged(target); |
1895 | } |
1896 | } |
1897 | #endif // FEATURE_COMINTEROP |
1898 | } |
1899 | |
1900 | return TRUE; |
1901 | } |
1902 | #endif // !CROSSGEN_COMPILE |
1903 | #endif //!DACCESS_COMPILE |
1904 | |
1905 | // This is used to recognize GenericComPlusCallStub, VarargPInvokeStub, and GenericPInvokeCalliHelper. |
1906 | |
1907 | #ifndef DACCESS_COMPILE |
1908 | |
1909 | /* static */ |
1910 | void InteropDispatchStubManager::Init() |
1911 | { |
1912 | CONTRACTL |
1913 | { |
1914 | THROWS; |
1915 | GC_NOTRIGGER; |
1916 | MODE_ANY; |
1917 | } |
1918 | CONTRACTL_END |
1919 | |
1920 | StubManager::AddStubManager(new InteropDispatchStubManager()); |
1921 | } |
1922 | |
1923 | #endif // #ifndef DACCESS_COMPILE |
1924 | |
1925 | PCODE TheGenericComplusCallStub(); // clrtocom.cpp |
1926 | |
1927 | #ifndef DACCESS_COMPILE |
1928 | static BOOL IsVarargPInvokeStub(PCODE stubStartAddress) |
1929 | { |
1930 | LIMITED_METHOD_CONTRACT; |
1931 | |
1932 | if (stubStartAddress == GetEEFuncEntryPoint(VarargPInvokeStub)) |
1933 | return TRUE; |
1934 | |
1935 | #if !defined(_TARGET_X86_) && !defined(_TARGET_ARM64_) |
1936 | if (stubStartAddress == GetEEFuncEntryPoint(VarargPInvokeStub_RetBuffArg)) |
1937 | return TRUE; |
1938 | #endif |
1939 | |
1940 | return FALSE; |
1941 | } |
1942 | #endif // #ifndef DACCESS_COMPILE |
1943 | |
1944 | BOOL InteropDispatchStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
1945 | { |
1946 | WRAPPER_NO_CONTRACT; |
1947 | //@dbgtodo dharvey implement DAC suport |
1948 | |
1949 | #ifndef DACCESS_COMPILE |
1950 | #ifdef FEATURE_COMINTEROP |
1951 | if (stubStartAddress == GetEEFuncEntryPoint(GenericComPlusCallStub)) |
1952 | { |
1953 | return true; |
1954 | } |
1955 | #endif // FEATURE_COMINTEROP |
1956 | |
1957 | if (IsVarargPInvokeStub(stubStartAddress)) |
1958 | { |
1959 | return true; |
1960 | } |
1961 | |
1962 | if (stubStartAddress == GetEEFuncEntryPoint(GenericPInvokeCalliHelper)) |
1963 | { |
1964 | return true; |
1965 | } |
1966 | |
1967 | #endif // !DACCESS_COMPILE |
1968 | return false; |
1969 | } |
1970 | |
1971 | BOOL InteropDispatchStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) |
1972 | { |
1973 | LIMITED_METHOD_CONTRACT; |
1974 | |
1975 | LOG((LF_CORDB, LL_EVERYTHING, "InteropDispatchStubManager::DoTraceStub called\n" )); |
1976 | |
1977 | #ifndef DACCESS_COMPILE |
1978 | _ASSERTE(CheckIsStub_Internal(stubStartAddress)); |
1979 | |
1980 | trace->InitForManagerPush(stubStartAddress, this); |
1981 | |
1982 | LOG_TRACE_DESTINATION(trace, stubStartAddress, "InteropDispatchStubManager::DoTraceStub" ); |
1983 | |
1984 | return TRUE; |
1985 | |
1986 | #else // !DACCESS_COMPILE |
1987 | trace->InitForOther(NULL); |
1988 | return FALSE; |
1989 | |
1990 | #endif // !DACCESS_COMPILE |
1991 | } |
1992 | |
1993 | #ifndef DACCESS_COMPILE |
1994 | |
1995 | BOOL InteropDispatchStubManager::TraceManager(Thread *thread, |
1996 | TraceDestination *trace, |
1997 | T_CONTEXT *pContext, |
1998 | BYTE **pRetAddr) |
1999 | { |
2000 | CONTRACTL |
2001 | { |
2002 | THROWS; |
2003 | GC_TRIGGERS; |
2004 | MODE_COOPERATIVE; |
2005 | } |
2006 | CONTRACTL_END; |
2007 | |
2008 | *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext); |
2009 | |
2010 | TADDR arg = StubManagerHelpers::GetHiddenArg(pContext); |
2011 | |
2012 | // IL stub may not exist at this point so we init directly for the target (TODO?) |
2013 | |
2014 | if (IsVarargPInvokeStub(GetIP(pContext))) |
2015 | { |
2016 | NDirectMethodDesc *pNMD = (NDirectMethodDesc *)arg; |
2017 | _ASSERTE(pNMD->IsNDirect()); |
2018 | PCODE target = (PCODE)pNMD->GetNDirectTarget(); |
2019 | |
2020 | LOG((LF_CORDB, LL_INFO10000, "IDSM::TraceManager: Vararg P/Invoke case 0x%x\n" , target)); |
2021 | trace->InitForUnmanaged(target); |
2022 | } |
2023 | else if (GetIP(pContext) == GetEEFuncEntryPoint(GenericPInvokeCalliHelper)) |
2024 | { |
2025 | PCODE target = (PCODE)arg; |
2026 | LOG((LF_CORDB, LL_INFO10000, "IDSM::TraceManager: Unmanaged CALLI case 0x%x\n" , target)); |
2027 | trace->InitForUnmanaged(target); |
2028 | } |
2029 | #ifdef FEATURE_COMINTEROP |
2030 | else |
2031 | { |
2032 | ComPlusCallMethodDesc *pCMD = (ComPlusCallMethodDesc *)arg; |
2033 | _ASSERTE(pCMD->IsComPlusCall()); |
2034 | |
2035 | Object * pThis = StubManagerHelpers::GetThisPtr(pContext); |
2036 | |
2037 | { |
2038 | if (!pCMD->m_pComPlusCallInfo->m_pInterfaceMT->IsComEventItfType() && (pCMD->m_pComPlusCallInfo->m_pILStub != NULL)) |
2039 | { |
2040 | // Early-bound CLR->COM call - continue in the IL stub |
2041 | trace->InitForStub(pCMD->m_pComPlusCallInfo->m_pILStub); |
2042 | } |
2043 | else |
2044 | { |
2045 | // Late-bound CLR->COM call - continue in target's IDispatch::Invoke |
2046 | OBJECTREF oref = ObjectToOBJECTREF(pThis); |
2047 | GCPROTECT_BEGIN(oref); |
2048 | |
2049 | MethodTable *pItfMT = pCMD->m_pComPlusCallInfo->m_pInterfaceMT; |
2050 | _ASSERTE(pItfMT->GetComInterfaceType() == ifDispatch); |
2051 | |
2052 | SafeComHolder<IUnknown> pUnk = ComObject::GetComIPFromRCWThrowing(&oref, pItfMT); |
2053 | LPVOID *lpVtbl = *(LPVOID **)(IUnknown *)pUnk; |
2054 | |
2055 | PCODE target = (PCODE)lpVtbl[6]; // DISPATCH_INVOKE_SLOT; |
2056 | LOG((LF_CORDB, LL_INFO10000, "CPSM::TraceManager: CLR-to-COM late-bound case 0x%x\n" , target)); |
2057 | trace->InitForUnmanaged(target); |
2058 | |
2059 | GCPROTECT_END(); |
2060 | } |
2061 | } |
2062 | } |
2063 | #endif // FEATURE_COMINTEROP |
2064 | |
2065 | return TRUE; |
2066 | } |
2067 | #endif //!DACCESS_COMPILE |
2068 | |
2069 | // |
2070 | // Since we don't generate delegate invoke stubs at runtime on IA64, we |
2071 | // can't use the StubLinkStubManager for these stubs. Instead, we create |
2072 | // an additional DelegateInvokeStubManager instead. |
2073 | // |
2074 | SPTR_IMPL(DelegateInvokeStubManager, DelegateInvokeStubManager, g_pManager); |
2075 | |
2076 | #ifndef DACCESS_COMPILE |
2077 | |
2078 | // static |
2079 | void DelegateInvokeStubManager::Init() |
2080 | { |
2081 | CONTRACTL |
2082 | { |
2083 | THROWS; |
2084 | GC_NOTRIGGER; |
2085 | MODE_ANY; |
2086 | } |
2087 | CONTRACTL_END |
2088 | |
2089 | g_pManager = new DelegateInvokeStubManager(); |
2090 | StubManager::AddStubManager(g_pManager); |
2091 | } |
2092 | |
2093 | BOOL DelegateInvokeStubManager::AddStub(Stub* pStub) |
2094 | { |
2095 | WRAPPER_NO_CONTRACT; |
2096 | PCODE start = pStub->GetEntryPoint(); |
2097 | |
2098 | // We don't really care about the size here. We only stop in these stubs at the first instruction, |
2099 | // so we'll never be asked to claim an address in the middle of a stub. |
2100 | return GetRangeList()->AddRange((BYTE *)start, (BYTE *)start + 1, (LPVOID)start); |
2101 | } |
2102 | |
2103 | void DelegateInvokeStubManager::RemoveStub(Stub* pStub) |
2104 | { |
2105 | WRAPPER_NO_CONTRACT; |
2106 | PCODE start = pStub->GetEntryPoint(); |
2107 | |
2108 | // We don't really care about the size here. We only stop in these stubs at the first instruction, |
2109 | // so we'll never be asked to claim an address in the middle of a stub. |
2110 | GetRangeList()->RemoveRanges((LPVOID)start); |
2111 | } |
2112 | |
2113 | #endif |
2114 | |
2115 | BOOL DelegateInvokeStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
2116 | { |
2117 | LIMITED_METHOD_DAC_CONTRACT; |
2118 | |
2119 | bool fIsStub = false; |
2120 | |
2121 | #ifndef DACCESS_COMPILE |
2122 | #ifndef _TARGET_X86_ |
2123 | fIsStub = fIsStub || (stubStartAddress == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub)); |
2124 | #endif |
2125 | #endif // !DACCESS_COMPILE |
2126 | |
2127 | fIsStub = fIsStub || GetRangeList()->IsInRange(stubStartAddress); |
2128 | |
2129 | return fIsStub; |
2130 | } |
2131 | |
2132 | BOOL DelegateInvokeStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) |
2133 | { |
2134 | LIMITED_METHOD_CONTRACT; |
2135 | |
2136 | LOG((LF_CORDB, LL_EVERYTHING, "DelegateInvokeStubManager::DoTraceStub called\n" )); |
2137 | |
2138 | _ASSERTE(CheckIsStub_Internal(stubStartAddress)); |
2139 | |
2140 | // If it's a MC delegate, then we want to set a BP & do a context-ful |
2141 | // manager push, so that we can figure out if this call will be to a |
2142 | // single multicast delegate or a multi multicast delegate |
2143 | trace->InitForManagerPush(stubStartAddress, this); |
2144 | |
2145 | LOG_TRACE_DESTINATION(trace, stubStartAddress, "DelegateInvokeStubManager::DoTraceStub" ); |
2146 | |
2147 | return TRUE; |
2148 | } |
2149 | |
2150 | #if !defined(DACCESS_COMPILE) |
2151 | |
2152 | BOOL DelegateInvokeStubManager::TraceManager(Thread *thread, TraceDestination *trace, |
2153 | T_CONTEXT *pContext, BYTE **pRetAddr) |
2154 | { |
2155 | CONTRACTL |
2156 | { |
2157 | MODE_COOPERATIVE; |
2158 | } |
2159 | CONTRACTL_END; |
2160 | |
2161 | PCODE destAddr; |
2162 | |
2163 | PCODE pc; |
2164 | pc = ::GetIP(pContext); |
2165 | |
2166 | BYTE* pThis; |
2167 | pThis = NULL; |
2168 | |
2169 | // Retrieve the this pointer from the context. |
2170 | #if defined(_TARGET_X86_) |
2171 | (*pRetAddr) = *(BYTE **)(size_t)(pContext->Esp); |
2172 | |
2173 | pThis = (BYTE*)(size_t)(pContext->Ecx); |
2174 | |
2175 | destAddr = *(PCODE*)(pThis + DelegateObject::GetOffsetOfMethodPtrAux()); |
2176 | |
2177 | #elif defined(_TARGET_AMD64_) |
2178 | |
2179 | // <TODO> |
2180 | // We need to check whether the following is the correct return address. |
2181 | // </TODO> |
2182 | (*pRetAddr) = *(BYTE **)(size_t)(pContext->Rsp); |
2183 | |
2184 | LOG((LF_CORDB, LL_INFO10000, "DISM:TM at 0x%p, retAddr is 0x%p\n" , pc, (*pRetAddr))); |
2185 | |
2186 | DELEGATEREF orDelegate; |
2187 | if (GetEEFuncEntryPoint(SinglecastDelegateInvokeStub) == pc) |
2188 | { |
2189 | LOG((LF_CORDB, LL_INFO10000, "DISM::TraceManager: isSingle\n" )); |
2190 | |
2191 | orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext)); |
2192 | |
2193 | // _methodPtr is where we are going to next. However, in ngen cases, we may have a shuffle thunk |
2194 | // burned into the ngen image, in which case the shuffle thunk is not added to the range list of |
2195 | // the DelegateInvokeStubManager. So we use _methodPtrAux as a fallback. |
2196 | destAddr = orDelegate->GetMethodPtr(); |
2197 | if (StubManager::TraceStub(destAddr, trace)) |
2198 | { |
2199 | LOG((LF_CORDB,LL_INFO10000, "DISM::TM: ppbDest: 0x%p\n" , destAddr)); |
2200 | LOG((LF_CORDB,LL_INFO10000, "DISM::TM: res: 1, result type: %d\n" , trace->GetTraceType())); |
2201 | return TRUE; |
2202 | } |
2203 | } |
2204 | else |
2205 | { |
2206 | // We get here if we are stopped at the beginning of a shuffle thunk. |
2207 | // The next address we are going to is _methodPtrAux. |
2208 | Stub* pStub = Stub::RecoverStub(pc); |
2209 | |
2210 | // We use the patch offset field to indicate whether the stub has a hidden return buffer argument. |
2211 | // This field is set in SetupShuffleThunk(). |
2212 | if (pStub->GetPatchOffset() != 0) |
2213 | { |
2214 | // This stub has a hidden return buffer argument. |
2215 | orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetSecondArg(pContext)); |
2216 | } |
2217 | else |
2218 | { |
2219 | orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext)); |
2220 | } |
2221 | } |
2222 | |
2223 | destAddr = orDelegate->GetMethodPtrAux(); |
2224 | #elif defined(_TARGET_ARM_) |
2225 | (*pRetAddr) = (BYTE *)(size_t)(pContext->Lr); |
2226 | pThis = (BYTE*)(size_t)(pContext->R0); |
2227 | |
2228 | // Could be in the singlecast invoke stub (in which case the next destination is in _methodPtr) or a |
2229 | // shuffle thunk (destination in _methodPtrAux). |
2230 | int offsetOfNextDest; |
2231 | if (pc == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub)) |
2232 | offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtr(); |
2233 | else |
2234 | offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtrAux(); |
2235 | destAddr = *(PCODE*)(pThis + offsetOfNextDest); |
2236 | #elif defined(_TARGET_ARM64_) |
2237 | (*pRetAddr) = (BYTE *)(size_t)(pContext->Lr); |
2238 | pThis = (BYTE*)(size_t)(pContext->X0); |
2239 | |
2240 | // Could be in the singlecast invoke stub (in which case the next destination is in _methodPtr) or a |
2241 | // shuffle thunk (destination in _methodPtrAux). |
2242 | int offsetOfNextDest; |
2243 | if (pc == GetEEFuncEntryPoint(SinglecastDelegateInvokeStub)) |
2244 | offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtr(); |
2245 | else |
2246 | offsetOfNextDest = DelegateObject::GetOffsetOfMethodPtrAux(); |
2247 | destAddr = *(PCODE*)(pThis + offsetOfNextDest); |
2248 | #else |
2249 | PORTABILITY_ASSERT("DelegateInvokeStubManager::TraceManager" ); |
2250 | destAddr = NULL; |
2251 | #endif |
2252 | |
2253 | LOG((LF_CORDB,LL_INFO10000, "DISM::TM: ppbDest: 0x%p\n" , destAddr)); |
2254 | |
2255 | BOOL res = StubManager::TraceStub(destAddr, trace); |
2256 | LOG((LF_CORDB,LL_INFO10000, "DISM::TM: res: %d, result type: %d\n" , res, trace->GetTraceType())); |
2257 | |
2258 | return res; |
2259 | } |
2260 | |
2261 | // static |
2262 | BOOL DelegateInvokeStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *trace) |
2263 | { |
2264 | CONTRACTL |
2265 | { |
2266 | NOTHROW; |
2267 | GC_NOTRIGGER; |
2268 | MODE_ANY; |
2269 | } |
2270 | CONTRACTL_END; |
2271 | BYTE **ppbDest = NULL; |
2272 | // If we got here, then we're here b/c we're at the start of a delegate stub |
2273 | // need to figure out the kind of delegates we are dealing with |
2274 | |
2275 | BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationList()); |
2276 | |
2277 | LOG((LF_CORDB,LL_INFO10000, "DISM::TMI: invocationList: 0x%x\n" , pbDelInvocationList)); |
2278 | |
2279 | if (pbDelInvocationList == NULL) |
2280 | { |
2281 | // null invocationList can be one of the following: |
2282 | // Instance closed, Instance open non-virt, Instance open virtual, Static closed, Static opened, Unmanaged FtnPtr |
2283 | // Instance open virtual is complex and we need to figure out what to do (TODO). |
2284 | // For the others the logic is the following: |
2285 | // if _methodPtrAux is 0 the target is in _methodPtr, otherwise the taret is _methodPtrAux |
2286 | |
2287 | ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); |
2288 | |
2289 | if (*ppbDest == NULL) |
2290 | { |
2291 | ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtr()); |
2292 | |
2293 | if (*ppbDest == NULL) |
2294 | { |
2295 | // it's not looking good, bail out |
2296 | LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: can't trace into it\n" )); |
2297 | return FALSE; |
2298 | } |
2299 | |
2300 | } |
2301 | |
2302 | LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: ppbDest: 0x%x *ppbDest:0x%x\n" , ppbDest, *ppbDest)); |
2303 | |
2304 | BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace); |
2305 | |
2306 | LOG((LF_CORDB,LL_INFO10000, "DISM(MCDel)::TM: res: %d, result type: %d\n" , res, trace->GetTraceType())); |
2307 | |
2308 | return res; |
2309 | } |
2310 | |
2311 | // invocationList is not null, so it can be one of the following: |
2312 | // Multicast, Static closed (special sig), Secure |
2313 | |
2314 | // rule out the static with special sig |
2315 | BYTE *pbCount = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationCount()); |
2316 | |
2317 | if (!pbCount) |
2318 | { |
2319 | // it's a static closed, the target lives in _methodAuxPtr |
2320 | ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); |
2321 | |
2322 | if (*ppbDest == NULL) |
2323 | { |
2324 | // it's not looking good, bail out |
2325 | LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: can't trace into it\n" )); |
2326 | return FALSE; |
2327 | } |
2328 | |
2329 | LOG((LF_CORDB,LL_INFO10000, "DISM(DelegateStub)::TM: ppbDest: 0x%x *ppbDest:0x%x\n" , ppbDest, *ppbDest)); |
2330 | |
2331 | BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace); |
2332 | |
2333 | LOG((LF_CORDB,LL_INFO10000, "DISM(MCDel)::TM: res: %d, result type: %d\n" , res, trace->GetTraceType())); |
2334 | |
2335 | return res; |
2336 | } |
2337 | |
2338 | MethodTable *pType = *(MethodTable**)pbDelInvocationList; |
2339 | if (pType->IsDelegate()) |
2340 | { |
2341 | // this is a secure deelgate. The target is hidden inside this field, so recurse in and pray... |
2342 | return TraceDelegateObject(pbDelInvocationList, trace); |
2343 | } |
2344 | |
2345 | // Otherwise, we're going for the first invoke of the multi case. |
2346 | // In order to go to the correct spot, we have just have to fish out |
2347 | // slot 0 of the invocation list, and figure out where that's going to, |
2348 | // then put a breakpoint there... |
2349 | pbDel = *(BYTE**)(((ArrayBase *)pbDelInvocationList)->GetDataPtr()); |
2350 | return TraceDelegateObject(pbDel, trace); |
2351 | } |
2352 | |
2353 | #endif // DACCESS_COMPILE |
2354 | |
2355 | |
2356 | #if !defined(DACCESS_COMPILE) |
2357 | |
2358 | // static |
2359 | void TailCallStubManager::Init() |
2360 | { |
2361 | CONTRACTL |
2362 | { |
2363 | THROWS; |
2364 | GC_NOTRIGGER; |
2365 | MODE_ANY; |
2366 | } |
2367 | CONTRACTL_END |
2368 | |
2369 | StubManager::AddStubManager(new TailCallStubManager()); |
2370 | } |
2371 | |
2372 | bool TailCallStubManager::IsTailCallStubHelper(PCODE code) |
2373 | { |
2374 | LIMITED_METHOD_CONTRACT; |
2375 | |
2376 | return code == GetEEFuncEntryPoint(JIT_TailCall); |
2377 | } |
2378 | |
2379 | #endif // !DACCESS_COMPILED |
2380 | |
2381 | BOOL TailCallStubManager::CheckIsStub_Internal(PCODE stubStartAddress) |
2382 | { |
2383 | LIMITED_METHOD_DAC_CONTRACT; |
2384 | |
2385 | bool fIsStub = false; |
2386 | |
2387 | #if !defined(DACCESS_COMPILE) |
2388 | fIsStub = IsTailCallStubHelper(stubStartAddress); |
2389 | #endif // !DACCESS_COMPILE |
2390 | |
2391 | return fIsStub; |
2392 | } |
2393 | |
2394 | #if !defined(DACCESS_COMPILE) |
2395 | |
2396 | #if defined(_TARGET_X86_) |
2397 | EXTERN_C void STDCALL JIT_TailCallLeave(); |
2398 | EXTERN_C void STDCALL JIT_TailCallVSDLeave(); |
2399 | #endif // _TARGET_X86_ |
2400 | |
2401 | BOOL TailCallStubManager::TraceManager(Thread * pThread, |
2402 | TraceDestination * pTrace, |
2403 | T_CONTEXT * pContext, |
2404 | BYTE ** ppRetAddr) |
2405 | { |
2406 | WRAPPER_NO_CONTRACT; |
2407 | #if defined(_TARGET_X86_) |
2408 | TADDR esp = GetSP(pContext); |
2409 | TADDR ebp = GetFP(pContext); |
2410 | |
2411 | // Check if we are stopped at the beginning of JIT_TailCall(). |
2412 | if (GetIP(pContext) == GetEEFuncEntryPoint(JIT_TailCall)) |
2413 | { |
2414 | // There are two cases in JIT_TailCall(). The first one is a normal tail call. |
2415 | // The second one is a tail call to a virtual method. |
2416 | *ppRetAddr = *(reinterpret_cast<BYTE **>(ebp + sizeof(SIZE_T))); |
2417 | |
2418 | // Check whether this is a VSD tail call. |
2419 | SIZE_T flags = *(reinterpret_cast<SIZE_T *>(esp + JIT_TailCall_StackOffsetToFlags)); |
2420 | if (flags & 0x2) |
2421 | { |
2422 | // This is a VSD tail call. |
2423 | pTrace->InitForManagerPush(GetEEFuncEntryPoint(JIT_TailCallVSDLeave), this); |
2424 | return TRUE; |
2425 | } |
2426 | else |
2427 | { |
2428 | // This is not a VSD tail call. |
2429 | pTrace->InitForManagerPush(GetEEFuncEntryPoint(JIT_TailCallLeave), this); |
2430 | return TRUE; |
2431 | } |
2432 | } |
2433 | else |
2434 | { |
2435 | if (GetIP(pContext) == GetEEFuncEntryPoint(JIT_TailCallLeave)) |
2436 | { |
2437 | // This is the simple case. The tail call goes directly to the target. There won't be an |
2438 | // explicit frame on the stack. We should be right at the return instruction which branches to |
2439 | // the call target. The return address is stored in the second leafmost stack slot. |
2440 | *ppRetAddr = *(reinterpret_cast<BYTE **>(esp + sizeof(SIZE_T))); |
2441 | } |
2442 | else |
2443 | { |
2444 | _ASSERTE(GetIP(pContext) == GetEEFuncEntryPoint(JIT_TailCallVSDLeave)); |
2445 | |
2446 | // This is the VSD case. The tail call goes through a assembly helper function which sets up |
2447 | // and tears down an explicit frame. In this case, the return address is at the same place |
2448 | // as on entry to JIT_TailCall(). |
2449 | *ppRetAddr = *(reinterpret_cast<BYTE **>(ebp + sizeof(SIZE_T))); |
2450 | } |
2451 | |
2452 | // In both cases, the target address is stored in the leafmost stack slot. |
2453 | pTrace->InitForStub((PCODE)*reinterpret_cast<SIZE_T *>(esp)); |
2454 | return TRUE; |
2455 | } |
2456 | |
2457 | #elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM_) |
2458 | |
2459 | _ASSERTE(GetIP(pContext) == GetEEFuncEntryPoint(JIT_TailCall)); |
2460 | |
2461 | // The target address is the second argument |
2462 | #ifdef _TARGET_AMD64_ |
2463 | PCODE target = (PCODE)pContext->Rdx; |
2464 | #else |
2465 | PCODE target = (PCODE)pContext->R1; |
2466 | #endif |
2467 | *ppRetAddr = reinterpret_cast<BYTE *>(target); |
2468 | pTrace->InitForStub(target); |
2469 | return TRUE; |
2470 | |
2471 | #else // !_TARGET_X86_ && !_TARGET_AMD64_ && !_TARGET_ARM_ |
2472 | |
2473 | _ASSERTE(!"TCSM::TM - TailCallStubManager should not be necessary on this platform" ); |
2474 | return FALSE; |
2475 | |
2476 | #endif // _TARGET_X86_ || _TARGET_AMD64_ |
2477 | } |
2478 | |
2479 | #endif // !DACCESS_COMPILE |
2480 | |
2481 | BOOL TailCallStubManager::DoTraceStub(PCODE stubStartAddress, TraceDestination *trace) |
2482 | { |
2483 | WRAPPER_NO_CONTRACT; |
2484 | |
2485 | LOG((LF_CORDB, LL_EVERYTHING, "TailCallStubManager::DoTraceStub called\n" )); |
2486 | |
2487 | BOOL fResult = FALSE; |
2488 | |
2489 | // Make sure we are stopped at the beginning of JIT_TailCall(). |
2490 | _ASSERTE(CheckIsStub_Internal(stubStartAddress)); |
2491 | trace->InitForManagerPush(stubStartAddress, this); |
2492 | fResult = TRUE; |
2493 | |
2494 | LOG_TRACE_DESTINATION(trace, stubStartAddress, "TailCallStubManager::DoTraceStub" ); |
2495 | return fResult; |
2496 | } |
2497 | |
2498 | |
2499 | #ifdef DACCESS_COMPILE |
2500 | |
2501 | void |
2502 | PrecodeStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2503 | { |
2504 | SUPPORTS_DAC; |
2505 | WRAPPER_NO_CONTRACT; |
2506 | DAC_ENUM_VTHIS(); |
2507 | EMEM_OUT(("MEM: %p PrecodeStubManager\n" , dac_cast<TADDR>(this))); |
2508 | } |
2509 | |
2510 | void |
2511 | StubLinkStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2512 | { |
2513 | SUPPORTS_DAC; |
2514 | WRAPPER_NO_CONTRACT; |
2515 | DAC_ENUM_VTHIS(); |
2516 | EMEM_OUT(("MEM: %p StubLinkStubManager\n" , dac_cast<TADDR>(this))); |
2517 | GetRangeList()->EnumMemoryRegions(flags); |
2518 | } |
2519 | |
2520 | void |
2521 | ThunkHeapStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2522 | { |
2523 | SUPPORTS_DAC; |
2524 | WRAPPER_NO_CONTRACT; |
2525 | DAC_ENUM_VTHIS(); |
2526 | EMEM_OUT(("MEM: %p ThunkHeapStubManager\n" , dac_cast<TADDR>(this))); |
2527 | GetRangeList()->EnumMemoryRegions(flags); |
2528 | } |
2529 | |
2530 | void |
2531 | JumpStubStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2532 | { |
2533 | SUPPORTS_DAC; |
2534 | WRAPPER_NO_CONTRACT; |
2535 | DAC_ENUM_VTHIS(); |
2536 | EMEM_OUT(("MEM: %p JumpStubStubManager\n" , dac_cast<TADDR>(this))); |
2537 | } |
2538 | |
2539 | #ifdef FEATURE_PREJIT |
2540 | void |
2541 | RangeSectionStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2542 | { |
2543 | SUPPORTS_DAC; |
2544 | WRAPPER_NO_CONTRACT; |
2545 | DAC_ENUM_VTHIS(); |
2546 | EMEM_OUT(("MEM: %p RangeSectionStubManager\n" , dac_cast<TADDR>(this))); |
2547 | } |
2548 | #endif |
2549 | |
2550 | void |
2551 | ILStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2552 | { |
2553 | SUPPORTS_DAC; |
2554 | WRAPPER_NO_CONTRACT; |
2555 | DAC_ENUM_VTHIS(); |
2556 | EMEM_OUT(("MEM: %p ILStubManager\n" , dac_cast<TADDR>(this))); |
2557 | } |
2558 | |
2559 | void |
2560 | InteropDispatchStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2561 | { |
2562 | SUPPORTS_DAC; |
2563 | WRAPPER_NO_CONTRACT; |
2564 | DAC_ENUM_VTHIS(); |
2565 | EMEM_OUT(("MEM: %p InteropDispatchStubManager\n" , dac_cast<TADDR>(this))); |
2566 | } |
2567 | |
2568 | void |
2569 | DelegateInvokeStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2570 | { |
2571 | SUPPORTS_DAC; |
2572 | WRAPPER_NO_CONTRACT; |
2573 | DAC_ENUM_VTHIS(); |
2574 | EMEM_OUT(("MEM: %p DelegateInvokeStubManager\n" , dac_cast<TADDR>(this))); |
2575 | GetRangeList()->EnumMemoryRegions(flags); |
2576 | } |
2577 | |
2578 | void |
2579 | VirtualCallStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2580 | { |
2581 | SUPPORTS_DAC; |
2582 | WRAPPER_NO_CONTRACT; |
2583 | DAC_ENUM_VTHIS(); |
2584 | EMEM_OUT(("MEM: %p VirtualCallStubManager\n" , dac_cast<TADDR>(this))); |
2585 | GetLookupRangeList()->EnumMemoryRegions(flags); |
2586 | GetResolveRangeList()->EnumMemoryRegions(flags); |
2587 | GetDispatchRangeList()->EnumMemoryRegions(flags); |
2588 | GetCacheEntryRangeList()->EnumMemoryRegions(flags); |
2589 | } |
2590 | |
2591 | void TailCallStubManager::DoEnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2592 | { |
2593 | SUPPORTS_DAC; |
2594 | WRAPPER_NO_CONTRACT; |
2595 | DAC_ENUM_VTHIS(); |
2596 | EMEM_OUT(("MEM: %p TailCallStubManager\n" , dac_cast<TADDR>(this))); |
2597 | } |
2598 | |
2599 | #endif // #ifdef DACCESS_COMPILE |
2600 | |
2601 | |