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// FRAMES.CPP
5
6
7
8#include "common.h"
9#include "log.h"
10#include "frames.h"
11#include "threads.h"
12#include "object.h"
13#include "method.hpp"
14#include "class.h"
15#include "excep.h"
16#include "stublink.h"
17#include "fieldmarshaler.h"
18#include "siginfo.hpp"
19#include "gcheaputilities.h"
20#include "dllimportcallback.h"
21#include "stackwalk.h"
22#include "dbginterface.h"
23#include "gms.h"
24#include "eeconfig.h"
25#include "ecall.h"
26#include "clsload.hpp"
27#include "cgensys.h"
28#include "virtualcallstub.h"
29#include "mdaassistants.h"
30#include "dllimport.h"
31#include "gcrefmap.h"
32#include "asmconstants.h"
33
34#ifdef FEATURE_COMINTEROP
35#include "comtoclrcall.h"
36#endif // FEATURE_COMINTEROP
37
38#ifdef FEATURE_INTERPRETER
39#include "interpreter.h"
40#endif // FEATURE_INTERPRETER
41
42#include "argdestination.h"
43
44#define CHECK_APP_DOMAIN 0
45
46//-----------------------------------------------------------------------
47#if _DEBUG
48//-----------------------------------------------------------------------
49
50#ifndef DACCESS_COMPILE
51
52unsigned dbgStubCtr = 0;
53unsigned dbgStubTrip = 0xFFFFFFFF;
54
55void Frame::Log() {
56 WRAPPER_NO_CONTRACT;
57
58 if (!LoggingOn(LF_STUBS, LL_INFO1000000))
59 return;
60
61 dbgStubCtr++;
62 if (dbgStubCtr > dbgStubTrip) {
63 dbgStubCtr++; // basicly a nop to put a breakpoint on.
64 }
65
66 MethodDesc* method = GetFunction();
67
68#ifdef _TARGET_X86_
69 if (GetVTablePtr() == UMThkCallFrame::GetMethodFrameVPtr())
70 method = ((UMThkCallFrame*) this)->GetUMEntryThunk()->GetMethod();
71#endif
72
73 STRESS_LOG3(LF_STUBS, LL_INFO1000000, "STUBS: In Stub with Frame %p assoc Method %pM FrameType = %pV\n", this, method, *((void**) this));
74
75 char buff[64];
76 const char* frameType;
77 if (GetVTablePtr() == PrestubMethodFrame::GetMethodFrameVPtr())
78 frameType = "PreStub";
79#ifdef _TARGET_X86_
80 else if (GetVTablePtr() == UMThkCallFrame::GetMethodFrameVPtr())
81 frameType = "UMThkCallFrame";
82#endif
83 else if (GetVTablePtr() == PInvokeCalliFrame::GetMethodFrameVPtr())
84 {
85 sprintf_s(buff, COUNTOF(buff), "PInvoke CALLI target" FMT_ADDR,
86 DBG_ADDR(((PInvokeCalliFrame*)this)->GetPInvokeCalliTarget()));
87 frameType = buff;
88 }
89 else if (GetVTablePtr() == StubDispatchFrame::GetMethodFrameVPtr())
90 frameType = "StubDispatch";
91 else if (GetVTablePtr() == ExternalMethodFrame::GetMethodFrameVPtr())
92 frameType = "ExternalMethod";
93 else
94 frameType = "Unknown";
95
96 if (method != 0)
97 LOG((LF_STUBS, LL_INFO1000000,
98 "IN %s Stub Method = %s::%s SIG %s ESP of return" FMT_ADDR "\n",
99 frameType,
100 method->m_pszDebugClassName,
101 method->m_pszDebugMethodName,
102 method->m_pszDebugMethodSignature,
103 DBG_ADDR(GetReturnAddressPtr())));
104 else
105 LOG((LF_STUBS, LL_INFO1000000,
106 "IN %s Stub Method UNKNOWN ESP of return" FMT_ADDR "\n",
107 frameType,
108 DBG_ADDR(GetReturnAddressPtr()) ));
109
110 _ASSERTE(GetThread()->PreemptiveGCDisabled());
111}
112
113//-----------------------------------------------------------------------
114// This function is used to log transitions in either direction
115// between unmanaged code and CLR/managed code.
116// This is typically done in a stub that sets up a Frame, which is
117// passed as an argument to this function.
118
119void __stdcall Frame::LogTransition(Frame* frame)
120{
121
122 CONTRACTL {
123 DEBUG_ONLY;
124 NOTHROW;
125 ENTRY_POINT;
126 GC_NOTRIGGER;
127 } CONTRACTL_END;
128
129 BEGIN_ENTRYPOINT_VOIDRET;
130
131#ifdef _TARGET_X86_
132 // On x86, StubLinkerCPU::EmitMethodStubProlog calls Frame::LogTransition
133 // but the caller of EmitMethodStubProlog sets the GSCookie later on.
134 // So the cookie is not initialized by the point we get here.
135#else
136 _ASSERTE(*frame->GetGSCookiePtr() == GetProcessGSCookie());
137#endif
138
139 if (Frame::ShouldLogTransitions())
140 frame->Log();
141
142 END_ENTRYPOINT_VOIDRET;
143} // void Frame::Log()
144
145#endif // #ifndef DACCESS_COMPILE
146
147//-----------------------------------------------------------------------
148#endif // _DEBUG
149//-----------------------------------------------------------------------
150
151
152// TODO [DAVBR]: For the full fix for VsWhidbey 450273, all the below
153// may be uncommented once isLegalManagedCodeCaller works properly
154// with non-return address inputs, and with non-DEBUG builds
155#if 0
156//-----------------------------------------------------------------------
157// returns TRUE if retAddr, is a return address that can call managed code
158
159bool isLegalManagedCodeCaller(PCODE retAddr) {
160 WRAPPER_NO_CONTRACT;
161#ifdef _TARGET_X86_
162
163 // we expect to be called from JITTED code or from special code sites inside
164 // mscorwks like callDescr which we have put a NOP (0x90) so we know that they
165 // are specially blessed.
166 if (!ExecutionManager::IsManagedCode(retAddr) &&
167 (
168#ifdef DACCESS_COMPILE
169 !(PTR_BYTE(retAddr).IsValid()) ||
170#endif
171 ((*PTR_BYTE(retAddr) != 0x90) &&
172 (*PTR_BYTE(retAddr) != 0xcc))))
173 {
174 LOG((LF_GC, LL_INFO10, "Bad caller to managed code: retAddr=0x%08x, *retAddr=0x%x\n",
175 retAddr, *(BYTE*)PTR_BYTE(retAddr)));
176
177 return false;
178 }
179
180 // it better be a return address of some kind
181 TADDR dummy;
182 if (isRetAddr(retAddr, &dummy))
183 return true;
184
185#ifndef DACCESS_COMPILE
186#ifdef DEBUGGING_SUPPORTED
187 // The debugger could have dropped an INT3 on the instruction that made the call
188 // Calls can be 2 to 7 bytes long
189 if (CORDebuggerAttached()) {
190 PTR_BYTE ptr = PTR_BYTE(retAddr);
191 for (int i = -2; i >= -7; --i)
192 if (ptr[i] == 0xCC)
193 return true;
194 return false;
195 }
196#endif // DEBUGGING_SUPPORTED
197#endif // #ifndef DACCESS_COMPILE
198
199 _ASSERTE(!"Bad return address on stack");
200 return false;
201#else // _TARGET_X86_
202 return true;
203#endif // _TARGET_X86_
204}
205#endif //0
206
207
208//-----------------------------------------------------------------------
209// Count of the number of frame types
210const size_t FRAME_TYPES_COUNT =
211#define FRAME_TYPE_NAME(frameType) +1
212#include "frames.h"
213;
214
215#if defined (_DEBUG_IMPL) // _DEBUG and !DAC
216
217//-----------------------------------------------------------------------
218// Implementation of the global table of names. On the DAC side, just the global pointer.
219// On the runtime side, the array of names.
220 #define FRAME_TYPE_NAME(x) {x::GetMethodFrameVPtr(), #x} ,
221 static FrameTypeName FrameTypeNameTable[] = {
222 #include "frames.h"
223 };
224
225
226/* static */
227PTR_CSTR Frame::GetFrameTypeName(TADDR vtbl)
228{
229 LIMITED_METHOD_CONTRACT;
230 for (size_t i=0; i<FRAME_TYPES_COUNT; ++i)
231 {
232 if (vtbl == FrameTypeNameTable[(int)i].vtbl)
233 {
234 return FrameTypeNameTable[(int)i].name;
235 }
236 }
237
238 return NULL;
239} // char* Frame::FrameTypeName()
240
241
242//-----------------------------------------------------------------------
243
244
245void Frame::LogFrame(
246 int LF, // Log facility for this call.
247 int LL) // Log Level for this call.
248{
249 char buf[32];
250 const char *pFrameType;
251 pFrameType = GetFrameTypeName();
252
253 if (pFrameType == NULL)
254 {
255 pFrameType = GetFrameTypeName(GetVTablePtr());
256 }
257
258 if (pFrameType == NULL)
259 {
260 _ASSERTE(!"New Frame type needs to be added to FrameTypeName()");
261 // Pointer is up to 17chars + vtbl@ = 22 chars
262 sprintf_s(buf, COUNTOF(buf), "vtbl@%p", GetVTablePtr());
263 pFrameType = buf;
264 }
265
266 LOG((LF, LL, "FRAME: addr:%p, next:%p, type:%s\n",
267 this, m_Next, pFrameType));
268} // void Frame::LogFrame()
269
270void Frame::LogFrameChain(
271 int LF, // Log facility for this call.
272 int LL) // Log Level for this call.
273{
274 if (!LoggingOn(LF, LL))
275 return;
276
277 Frame *pFrame = this;
278 while (pFrame != FRAME_TOP)
279 {
280 pFrame->LogFrame(LF, LL);
281 pFrame = pFrame->m_Next;
282 }
283} // void Frame::LogFrameChain()
284
285//-----------------------------------------------------------------------
286#endif // _DEBUG_IMPL
287//-----------------------------------------------------------------------
288
289#ifndef DACCESS_COMPILE
290
291// This hashtable contains the vtable value of every Frame type.
292static PtrHashMap* s_pFrameVTables = NULL;
293
294// static
295void Frame::Init()
296{
297 CONTRACTL
298 {
299 THROWS;
300 GC_NOTRIGGER;
301 MODE_ANY;
302 }
303 CONTRACTL_END;
304 // create a table big enough for all the frame types, not in asynchronous mode, and with no lock owner
305 s_pFrameVTables = ::new PtrHashMap;
306 s_pFrameVTables->Init(2 * FRAME_TYPES_COUNT, FALSE, &g_lockTrustMeIAmThreadSafe);
307#define FRAME_TYPE_NAME(frameType) \
308 s_pFrameVTables->InsertValue(frameType::GetMethodFrameVPtr(), \
309 (LPVOID) frameType::GetMethodFrameVPtr());
310#include "frames.h"
311
312} // void Frame::Init()
313
314// static
315void Frame::Term()
316{
317 LIMITED_METHOD_CONTRACT;
318 delete s_pFrameVTables;
319 s_pFrameVTables = NULL;
320}
321
322#endif // DACCESS_COMPILE
323
324// Returns true if the Frame's VTablePtr is valid
325
326// static
327bool Frame::HasValidVTablePtr(Frame * pFrame)
328{
329 WRAPPER_NO_CONTRACT;
330
331 if (pFrame == NULL || pFrame == FRAME_TOP)
332 return false;
333
334#ifndef DACCESS_COMPILE
335 TADDR vptr = pFrame->GetVTablePtr();
336 //
337 // Helper MethodFrame,GCFrame,DebuggerSecurityCodeMarkFrame are the most
338 // common frame types, explicitly check for them.
339 //
340 if (vptr == HelperMethodFrame::GetMethodFrameVPtr())
341 return true;
342
343 if (vptr == GCFrame::GetMethodFrameVPtr())
344 return true;
345
346 if (vptr == DebuggerSecurityCodeMarkFrame::GetMethodFrameVPtr())
347 return true;
348
349 //
350 // otherwise consult the hashtable
351 //
352 if (s_pFrameVTables->LookupValue(vptr, (LPVOID) vptr) == (LPVOID) INVALIDENTRY)
353 return false;
354#endif
355
356 return true;
357}
358
359// Returns the location of the expected GSCookie,
360// Return NULL if the frame's vtable pointer is corrupt
361//
362// Note that Frame::GetGSCookiePtr is a virtual method,
363// and so it cannot be used without first checking if
364// the vtable is valid.
365
366// static
367PTR_GSCookie Frame::SafeGetGSCookiePtr(Frame * pFrame)
368{
369 WRAPPER_NO_CONTRACT;
370
371 _ASSERTE(pFrame != FRAME_TOP);
372
373 if (Frame::HasValidVTablePtr(pFrame))
374 return pFrame->GetGSCookiePtr();
375 else
376 return NULL;
377}
378
379//-----------------------------------------------------------------------
380#ifndef DACCESS_COMPILE
381//-----------------------------------------------------------------------
382// Link and Unlink this frame.
383//-----------------------------------------------------------------------
384
385VOID Frame::Push()
386{
387 CONTRACTL
388 {
389 NOTHROW;
390 GC_NOTRIGGER;
391 MODE_COOPERATIVE;
392 SO_TOLERANT;
393 }
394 CONTRACTL_END;
395
396 Push(GetThread());
397}
398
399VOID Frame::Push(Thread *pThread)
400{
401 CONTRACTL
402 {
403 NOTHROW;
404 GC_NOTRIGGER;
405 MODE_COOPERATIVE;
406 SO_TOLERANT;
407 }
408 CONTRACTL_END;
409
410 _ASSERTE(*GetGSCookiePtr() == GetProcessGSCookie());
411
412 m_Next = pThread->GetFrame();
413
414 // GetOsPageSize() is used to relax the assert for cases where two Frames are
415 // declared in the same source function. We cannot predict the order
416 // in which the C compiler will lay them out in the stack frame.
417 // So GetOsPageSize() is a guess of the maximum stack frame size of any method
418 // with multiple Frames in mscorwks.dll
419 _ASSERTE(((m_Next == FRAME_TOP) ||
420 (PBYTE(m_Next) + (2 * GetOsPageSize())) > PBYTE(this)) &&
421 "Pushing a frame out of order ?");
422
423 _ASSERTE(// If AssertOnFailFast is set, the test expects to do stack overrun
424 // corruptions. In that case, the Frame chain may be corrupted,
425 // and the rest of the assert is not valid.
426 // Note that the corrupted Frame chain will be detected
427 // during stack-walking.
428 !g_pConfig->fAssertOnFailFast() ||
429 (m_Next == FRAME_TOP) ||
430 (*m_Next->GetGSCookiePtr() == GetProcessGSCookie()));
431
432 pThread->SetFrame(this);
433}
434
435VOID Frame::Pop()
436{
437 CONTRACTL
438 {
439 NOTHROW;
440 GC_NOTRIGGER;
441 MODE_COOPERATIVE;
442 SO_TOLERANT;
443 }
444 CONTRACTL_END;
445
446 Pop(GetThread());
447}
448
449VOID Frame::Pop(Thread *pThread)
450{
451 CONTRACTL
452 {
453 NOTHROW;
454 GC_NOTRIGGER;
455 MODE_COOPERATIVE;
456 SO_TOLERANT;
457 }
458 CONTRACTL_END;
459
460 _ASSERTE(pThread->GetFrame() == this && "Popping a frame out of order ?");
461 _ASSERTE(*GetGSCookiePtr() == GetProcessGSCookie());
462 _ASSERTE(// If AssertOnFailFast is set, the test expects to do stack overrun
463 // corruptions. In that case, the Frame chain may be corrupted,
464 // and the rest of the assert is not valid.
465 // Note that the corrupted Frame chain will be detected
466 // during stack-walking.
467 !g_pConfig->fAssertOnFailFast() ||
468 (m_Next == FRAME_TOP) ||
469 (*m_Next->GetGSCookiePtr() == GetProcessGSCookie()));
470
471 pThread->SetFrame(m_Next);
472 m_Next = NULL;
473}
474
475#if defined(FEATURE_PAL) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
476void Frame::PopIfChained()
477{
478 CONTRACTL
479 {
480 NOTHROW;
481 GC_NOTRIGGER;
482 MODE_COOPERATIVE;
483 SO_TOLERANT;
484 }
485 CONTRACTL_END;
486
487 if (m_Next != NULL)
488 {
489 GCX_COOP();
490 // When the frame is destroyed, make sure it is no longer in the
491 // frame chain managed by the Thread.
492 Pop();
493 }
494}
495#endif // FEATURE_PAL && !DACCESS_COMPILE && !CROSSGEN_COMPILE
496
497//-----------------------------------------------------------------------
498#endif // #ifndef DACCESS_COMPILE
499//---------------------------------------------------------------
500// Get the extra param for shared generic code.
501//---------------------------------------------------------------
502PTR_VOID TransitionFrame::GetParamTypeArg()
503{
504 CONTRACTL
505 {
506 NOTHROW;
507 GC_NOTRIGGER;
508 MODE_ANY;
509 SUPPORTS_DAC;
510 }
511 CONTRACTL_END;
512
513 // This gets called while creating stack traces during exception handling.
514 // Using the ArgIterator constructor calls ArgIterator::Init which calls GetInitialOfsAdjust
515 // which calls SizeOfArgStack, which thinks it may load value types.
516 // However all these will have previously been loaded.
517 //
518 // I'm not entirely convinced this is the best places to put this: CrawlFrame::GetExactGenericArgsToken
519 // may be another option.
520 ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
521
522 MethodDesc *pFunction = GetFunction();
523 _ASSERTE (pFunction->RequiresInstArg());
524
525 MetaSig msig(pFunction);
526 ArgIterator argit (&msig);
527
528 INT offs = argit.GetParamTypeArgOffset();
529
530 TADDR taParamTypeArg = *PTR_TADDR(GetTransitionBlock() + offs);
531 return PTR_VOID(taParamTypeArg);
532}
533
534TADDR TransitionFrame::GetAddrOfThis()
535{
536 WRAPPER_NO_CONTRACT;
537 return GetTransitionBlock() + ArgIterator::GetThisOffset();
538}
539
540VASigCookie * TransitionFrame::GetVASigCookie()
541{
542#if defined(_TARGET_X86_)
543 LIMITED_METHOD_CONTRACT;
544 return dac_cast<PTR_VASigCookie>(
545 *dac_cast<PTR_TADDR>(GetTransitionBlock() +
546 sizeof(TransitionBlock)));
547#else
548 WRAPPER_NO_CONTRACT;
549 MetaSig msig(GetFunction());
550 ArgIterator argit(&msig);
551 return PTR_VASigCookie(
552 *dac_cast<PTR_TADDR>(GetTransitionBlock() + argit.GetVASigCookieOffset()));
553#endif
554}
555
556#ifndef DACCESS_COMPILE
557PrestubMethodFrame::PrestubMethodFrame(TransitionBlock * pTransitionBlock, MethodDesc * pMD)
558 : FramedMethodFrame(pTransitionBlock, pMD)
559{
560 LIMITED_METHOD_CONTRACT;
561}
562#endif // #ifndef DACCESS_COMPILE
563
564BOOL PrestubMethodFrame::TraceFrame(Thread *thread, BOOL fromPatch,
565 TraceDestination *trace, REGDISPLAY *regs)
566{
567 WRAPPER_NO_CONTRACT;
568
569 //
570 // We want to set a frame patch, unless we're already at the
571 // frame patch, in which case we'll trace stable entrypoint which
572 // should be set by now.
573 //
574
575 if (fromPatch)
576 {
577 trace->InitForStub(GetFunction()->GetStableEntryPoint());
578 }
579 else
580 {
581 trace->InitForStub(GetPreStubEntryPoint());
582 }
583
584 LOG((LF_CORDB, LL_INFO10000,
585 "PrestubMethodFrame::TraceFrame: ip=" FMT_ADDR "\n", DBG_ADDR(trace->GetAddress()) ));
586
587 return TRUE;
588}
589
590#ifndef DACCESS_COMPILE
591//-----------------------------------------------------------------------
592// A rather specialized routine for the exclusive use of StubDispatch.
593//-----------------------------------------------------------------------
594StubDispatchFrame::StubDispatchFrame(TransitionBlock * pTransitionBlock)
595 : FramedMethodFrame(pTransitionBlock, NULL)
596{
597 LIMITED_METHOD_CONTRACT;
598
599 m_pRepresentativeMT = NULL;
600 m_representativeSlot = 0;
601
602 m_pZapModule = NULL;
603 m_pIndirection = NULL;
604
605 m_pGCRefMap = NULL;
606}
607
608#endif // #ifndef DACCESS_COMPILE
609
610MethodDesc* StubDispatchFrame::GetFunction()
611{
612 CONTRACTL {
613 NOTHROW;
614 GC_NOTRIGGER;
615 SO_TOLERANT;
616 } CONTRACTL_END;
617
618 MethodDesc * pMD = m_pMD;
619
620 if (m_pMD == NULL)
621 {
622 if (m_pRepresentativeMT != NULL)
623 {
624 pMD = m_pRepresentativeMT->GetMethodDescForSlot(m_representativeSlot);
625#ifndef DACCESS_COMPILE
626 m_pMD = pMD;
627#endif
628 }
629 }
630
631 return pMD;
632}
633
634static PTR_BYTE FindGCRefMap(PTR_Module pZapModule, TADDR ptr)
635{
636 LIMITED_METHOD_DAC_CONTRACT;
637
638 PEImageLayout *pNativeImage = pZapModule->GetNativeOrReadyToRunImage();
639
640 RVA rva = pNativeImage->GetDataRva(ptr);
641
642 PTR_CORCOMPILE_IMPORT_SECTION pImportSection = pZapModule->GetImportSectionForRVA(rva);
643 if (pImportSection == NULL)
644 return NULL;
645
646 COUNT_T index = (rva - pImportSection->Section.VirtualAddress) / pImportSection->EntrySize;
647
648 PTR_BYTE pGCRefMap = dac_cast<PTR_BYTE>(pNativeImage->GetRvaData(pImportSection->AuxiliaryData));
649 _ASSERTE(pGCRefMap != NULL);
650
651 // GCRefMap starts with lookup index to limit size of linear scan that follows.
652 PTR_BYTE p = pGCRefMap + dac_cast<PTR_DWORD>(pGCRefMap)[index / GCREFMAP_LOOKUP_STRIDE];
653 COUNT_T remaining = index % GCREFMAP_LOOKUP_STRIDE;
654
655 while (remaining > 0)
656 {
657 while ((*p & 0x80) != 0)
658 p++;
659 p++;
660
661 remaining--;
662 }
663
664 return p;
665}
666
667PTR_BYTE StubDispatchFrame::GetGCRefMap()
668{
669 CONTRACTL
670 {
671 NOTHROW;
672 GC_NOTRIGGER;
673 }
674 CONTRACTL_END;
675
676 PTR_BYTE pGCRefMap = m_pGCRefMap;
677
678 if (pGCRefMap == NULL)
679 {
680 if (m_pIndirection != NULL)
681 {
682 if (m_pZapModule == NULL)
683 {
684 m_pZapModule = ExecutionManager::FindModuleForGCRefMap(m_pIndirection);
685 }
686
687 if (m_pZapModule != NULL)
688 {
689 pGCRefMap = FindGCRefMap(m_pZapModule, m_pIndirection);
690 }
691
692#ifndef DACCESS_COMPILE
693 if (pGCRefMap != NULL)
694 {
695 m_pGCRefMap = pGCRefMap;
696 }
697 else
698 {
699 // Clear the indirection to avoid retrying
700 m_pIndirection = NULL;
701 }
702#endif
703 }
704 }
705
706 return pGCRefMap;
707}
708
709void StubDispatchFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
710{
711 CONTRACTL
712 {
713 NOTHROW;
714 GC_NOTRIGGER;
715 }
716 CONTRACTL_END
717
718 FramedMethodFrame::GcScanRoots(fn, sc);
719
720 PTR_BYTE pGCRefMap = GetGCRefMap();
721 if (pGCRefMap != NULL)
722 {
723 PromoteCallerStackUsingGCRefMap(fn, sc, pGCRefMap);
724 }
725 else
726 {
727 PromoteCallerStack(fn, sc);
728 }
729}
730
731BOOL StubDispatchFrame::TraceFrame(Thread *thread, BOOL fromPatch,
732 TraceDestination *trace, REGDISPLAY *regs)
733{
734 WRAPPER_NO_CONTRACT;
735
736 //
737 // We want to set a frame patch, unless we're already at the
738 // frame patch, in which case we'll trace stable entrypoint which
739 // should be set by now.
740 //
741
742 if (fromPatch)
743 {
744 trace->InitForStub(GetFunction()->GetStableEntryPoint());
745 }
746 else
747 {
748 trace->InitForStub(GetPreStubEntryPoint());
749 }
750
751 LOG((LF_CORDB, LL_INFO10000,
752 "StubDispatchFrame::TraceFrame: ip=" FMT_ADDR "\n", DBG_ADDR(trace->GetAddress()) ));
753
754 return TRUE;
755}
756
757Frame::Interception StubDispatchFrame::GetInterception()
758{
759 LIMITED_METHOD_CONTRACT;
760
761 return INTERCEPTION_NONE;
762}
763
764#ifndef DACCESS_COMPILE
765ExternalMethodFrame::ExternalMethodFrame(TransitionBlock * pTransitionBlock)
766 : FramedMethodFrame(pTransitionBlock, NULL)
767{
768 LIMITED_METHOD_CONTRACT;
769
770 m_pIndirection = NULL;
771 m_pZapModule = NULL;
772
773 m_pGCRefMap = NULL;
774}
775#endif // !DACCESS_COMPILE
776
777void ExternalMethodFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
778{
779 CONTRACTL
780 {
781 NOTHROW;
782 GC_NOTRIGGER;
783 }
784 CONTRACTL_END
785
786 FramedMethodFrame::GcScanRoots(fn, sc);
787 PromoteCallerStackUsingGCRefMap(fn, sc, GetGCRefMap());
788}
789
790PTR_BYTE ExternalMethodFrame::GetGCRefMap()
791{
792 LIMITED_METHOD_DAC_CONTRACT;
793
794 PTR_BYTE pGCRefMap = m_pGCRefMap;
795
796 if (pGCRefMap == NULL)
797 {
798 if (m_pIndirection != NULL)
799 {
800 pGCRefMap = FindGCRefMap(m_pZapModule, m_pIndirection);
801#ifndef DACCESS_COMPILE
802 m_pGCRefMap = pGCRefMap;
803#endif
804 }
805 }
806
807 _ASSERTE(pGCRefMap != NULL);
808 return pGCRefMap;
809}
810
811Frame::Interception ExternalMethodFrame::GetInterception()
812{
813 LIMITED_METHOD_CONTRACT;
814
815 return INTERCEPTION_NONE;
816}
817
818Frame::Interception PrestubMethodFrame::GetInterception()
819{
820 LIMITED_METHOD_DAC_CONTRACT;
821
822 //
823 // The only direct kind of interception done by the prestub
824 // is class initialization.
825 //
826
827 return INTERCEPTION_PRESTUB;
828}
829
830#ifdef FEATURE_READYTORUN
831
832#ifndef DACCESS_COMPILE
833DynamicHelperFrame::DynamicHelperFrame(TransitionBlock * pTransitionBlock, int dynamicHelperFrameFlags)
834 : FramedMethodFrame(pTransitionBlock, NULL)
835{
836 LIMITED_METHOD_CONTRACT;
837
838 m_dynamicHelperFrameFlags = dynamicHelperFrameFlags;
839}
840#endif // !DACCESS_COMPILE
841
842void DynamicHelperFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
843{
844 CONTRACTL
845 {
846 NOTHROW;
847 GC_NOTRIGGER;
848 }
849 CONTRACTL_END
850
851 FramedMethodFrame::GcScanRoots(fn, sc);
852
853 PTR_PTR_Object pArgumentRegisters = dac_cast<PTR_PTR_Object>(GetTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters());
854
855 if (m_dynamicHelperFrameFlags & DynamicHelperFrameFlags_ObjectArg)
856 {
857 TADDR pArgument = GetTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters();
858#ifdef _TARGET_X86_
859 // x86 is special as always
860 pArgument += offsetof(ArgumentRegisters, ECX);
861#endif
862 (*fn)(dac_cast<PTR_PTR_Object>(pArgument), sc, CHECK_APP_DOMAIN);
863 }
864
865 if (m_dynamicHelperFrameFlags & DynamicHelperFrameFlags_ObjectArg2)
866 {
867 TADDR pArgument = GetTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters();
868#ifdef _TARGET_X86_
869 // x86 is special as always
870 pArgument += offsetof(ArgumentRegisters, EDX);
871#else
872 pArgument += sizeof(TADDR);
873#endif
874 (*fn)(dac_cast<PTR_PTR_Object>(pArgument), sc, CHECK_APP_DOMAIN);
875 }
876}
877
878#endif // FEATURE_READYTORUN
879
880
881#ifndef DACCESS_COMPILE
882
883#ifdef FEATURE_COMINTEROP
884//-----------------------------------------------------------------------
885// A rather specialized routine for the exclusive use of the COM PreStub.
886//-----------------------------------------------------------------------
887VOID
888ComPrestubMethodFrame::Init()
889{
890 WRAPPER_NO_CONTRACT;
891
892 // Initializes the frame's VPTR. This assumes C++ puts the vptr
893 // at offset 0 for a class not using MI, but this is no different
894 // than the assumption that COM Classic makes.
895 *((TADDR*)this) = GetMethodFrameVPtr();
896 *GetGSCookiePtr() = GetProcessGSCookie();
897}
898#endif // FEATURE_COMINTEROP
899
900//-----------------------------------------------------------------------
901// GCFrames
902//-----------------------------------------------------------------------
903
904
905//--------------------------------------------------------------------
906// This constructor pushes a new GCFrame on the frame chain.
907//--------------------------------------------------------------------
908GCFrame::GCFrame(OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior)
909{
910 CONTRACTL
911 {
912 NOTHROW;
913 GC_NOTRIGGER;
914 MODE_COOPERATIVE;
915 SO_TOLERANT;
916 }
917 CONTRACTL_END;
918
919 Init(GetThread(), pObjRefs, numObjRefs, maybeInterior);
920}
921
922GCFrame::GCFrame(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior)
923{
924 CONTRACTL
925 {
926 NOTHROW;
927 GC_NOTRIGGER;
928 MODE_COOPERATIVE;
929 SO_TOLERANT;
930 }
931 CONTRACTL_END;
932
933 Init(pThread, pObjRefs, numObjRefs, maybeInterior);
934}
935
936void GCFrame::Init(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior)
937{
938 CONTRACTL
939 {
940 NOTHROW;
941 GC_NOTRIGGER;
942 MODE_COOPERATIVE;
943 SO_TOLERANT;
944 }
945 CONTRACTL_END;
946
947#ifdef USE_CHECKED_OBJECTREFS
948 if (!maybeInterior) {
949 UINT i;
950 for(i = 0; i < numObjRefs; i++)
951 Thread::ObjectRefProtected(&pObjRefs[i]);
952
953 for (i = 0; i < numObjRefs; i++) {
954 pObjRefs[i].Validate();
955 }
956 }
957
958#if 0 // We'll want to restore this goodness check at some time. For now, the fact that we use
959 // this as temporary backstops in our loader exception conversions means we're highly
960 // exposed to infinite stack recursion should the loader be invoked during a stackwalk.
961 // So we'll do without.
962
963 if (g_pConfig->GetGCStressLevel() != 0 && IsProtectedByGCFrame(pObjRefs)) {
964 _ASSERTE(!"This objectref is already protected by a GCFrame. Protecting it twice will corrupt the GC.");
965 }
966#endif
967
968#endif
969
970 m_pObjRefs = pObjRefs;
971 m_numObjRefs = numObjRefs;
972 m_pCurThread = pThread;
973 m_MaybeInterior = maybeInterior;
974
975 Frame::Push(m_pCurThread);
976}
977
978
979//
980// GCFrame Object Scanning
981//
982// This handles scanning/promotion of GC objects that were
983// protected by the programmer explicitly protecting it in a GC Frame
984// via the GCPROTECTBEGIN / GCPROTECTEND facility...
985//
986
987#endif // !DACCESS_COMPILE
988
989void GCFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
990{
991 WRAPPER_NO_CONTRACT;
992
993 PTR_PTR_Object pRefs = dac_cast<PTR_PTR_Object>(m_pObjRefs);
994
995 for (UINT i = 0;i < m_numObjRefs; i++) {
996
997 LOG((LF_GC, INFO3, "GC Protection Frame Promoting" FMT_ADDR "to",
998 DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(m_pObjRefs[i])) ));
999 if (m_MaybeInterior)
1000 PromoteCarefully(fn, pRefs + i, sc, GC_CALL_INTERIOR|CHECK_APP_DOMAIN);
1001 else
1002 (*fn)(pRefs + i, sc, 0);
1003 LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(m_pObjRefs[i])) ));
1004 }
1005}
1006
1007
1008#ifndef DACCESS_COMPILE
1009//--------------------------------------------------------------------
1010// Pops the GCFrame and cancels the GC protection.
1011//--------------------------------------------------------------------
1012VOID GCFrame::Pop()
1013{
1014 WRAPPER_NO_CONTRACT;
1015
1016 Frame::Pop(m_pCurThread);
1017#ifdef _DEBUG
1018 m_pCurThread->EnableStressHeap();
1019 for(UINT i = 0; i < m_numObjRefs; i++)
1020 Thread::ObjectRefNew(&m_pObjRefs[i]); // Unprotect them
1021#endif
1022}
1023
1024#ifdef FEATURE_INTERPRETER
1025// Methods of IntepreterFrame.
1026InterpreterFrame::InterpreterFrame(Interpreter* interp)
1027 : Frame(), m_interp(interp)
1028{
1029 Push();
1030}
1031
1032
1033MethodDesc* InterpreterFrame::GetFunction()
1034{
1035 return m_interp->GetMethodDesc();
1036}
1037
1038void InterpreterFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
1039{
1040 return m_interp->GCScanRoots(fn, sc);
1041}
1042
1043#endif // FEATURE_INTERPRETER
1044
1045#if defined(_DEBUG) && !defined (DACCESS_COMPILE)
1046
1047struct IsProtectedByGCFrameStruct
1048{
1049 OBJECTREF *ppObjectRef;
1050 UINT count;
1051};
1052
1053static StackWalkAction IsProtectedByGCFrameStackWalkFramesCallback(
1054 CrawlFrame *pCF,
1055 VOID *pData
1056)
1057{
1058 DEBUG_ONLY_FUNCTION;
1059 WRAPPER_NO_CONTRACT;
1060
1061 IsProtectedByGCFrameStruct *pd = (IsProtectedByGCFrameStruct*)pData;
1062 Frame *pFrame = pCF->GetFrame();
1063 if (pFrame) {
1064 if (pFrame->Protects(pd->ppObjectRef)) {
1065 pd->count++;
1066 }
1067 }
1068 return SWA_CONTINUE;
1069}
1070
1071BOOL IsProtectedByGCFrame(OBJECTREF *ppObjectRef)
1072{
1073 DEBUG_ONLY_FUNCTION;
1074 WRAPPER_NO_CONTRACT;
1075
1076 // Just report TRUE if GCStress is not on. This satisfies the asserts that use this
1077 // code without the cost of actually determining it.
1078 if (!GCStress<cfg_any>::IsEnabled())
1079 return TRUE;
1080
1081 if (ppObjectRef == NULL) {
1082 return TRUE;
1083 }
1084
1085 CONTRACT_VIOLATION(ThrowsViolation);
1086 ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE ();
1087 IsProtectedByGCFrameStruct d = {ppObjectRef, 0};
1088 GetThread()->StackWalkFrames(IsProtectedByGCFrameStackWalkFramesCallback, &d);
1089 if (d.count > 1) {
1090 _ASSERTE(!"Multiple GCFrames protecting the same pointer. This will cause GC corruption!");
1091 }
1092 return d.count != 0;
1093}
1094#endif // _DEBUG
1095
1096#endif //!DACCESS_COMPILE
1097
1098#ifdef FEATURE_HIJACK
1099
1100void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
1101{
1102 LIMITED_METHOD_CONTRACT;
1103
1104 ReturnKind returnKind = m_Thread->GetHijackReturnKind();
1105 _ASSERTE(IsValidReturnKind(returnKind));
1106
1107 int regNo = 0;
1108 bool moreRegisters = false;
1109
1110 do
1111 {
1112 ReturnKind r = ExtractRegReturnKind(returnKind, regNo, moreRegisters);
1113 PTR_PTR_Object objPtr = dac_cast<PTR_PTR_Object>(&m_Args->ReturnValue[regNo]);
1114
1115 switch (r)
1116 {
1117#ifdef _TARGET_X86_
1118 case RT_Float: // Fall through
1119#endif
1120 case RT_Scalar:
1121 // nothing to report
1122 break;
1123
1124 case RT_Object:
1125 LOG((LF_GC, INFO3, "Hijack Frame Promoting Object" FMT_ADDR "to",
1126 DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
1127 (*fn)(objPtr, sc, CHECK_APP_DOMAIN);
1128 LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
1129 break;
1130
1131 case RT_ByRef:
1132 LOG((LF_GC, INFO3, "Hijack Frame Carefully Promoting pointer" FMT_ADDR "to",
1133 DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
1134 PromoteCarefully(fn, objPtr, sc, GC_CALL_INTERIOR | GC_CALL_CHECK_APP_DOMAIN);
1135 LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(OBJECTREF_TO_UNCHECKED_OBJECTREF(*objPtr))));
1136 break;
1137
1138 default:
1139 _ASSERTE(!"Impossible two bit encoding");
1140 }
1141
1142 regNo++;
1143 } while (moreRegisters);
1144}
1145
1146#endif // FEATURE_HIJACK
1147
1148void ProtectByRefsFrame::GcScanRoots(promote_func *fn, ScanContext *sc)
1149{
1150 CONTRACTL
1151 {
1152 NOTHROW;
1153 GC_NOTRIGGER;
1154 }
1155 CONTRACTL_END
1156
1157 ByRefInfo *pByRefInfos = m_brInfo;
1158 while (pByRefInfos)
1159 {
1160 if (!CorIsPrimitiveType(pByRefInfos->typ))
1161 {
1162 TADDR pData = PTR_HOST_MEMBER_TADDR(ByRefInfo, pByRefInfos, data);
1163
1164 if (pByRefInfos->typeHandle.IsValueType())
1165 {
1166 ReportPointersFromValueType(fn, sc, pByRefInfos->typeHandle.GetMethodTable(), PTR_VOID(pData));
1167 }
1168 else
1169 {
1170 PTR_PTR_Object ppObject = PTR_PTR_Object(pData);
1171
1172 LOG((LF_GC, INFO3, "ProtectByRefs Frame Promoting" FMT_ADDR "to ", DBG_ADDR(*ppObject)));
1173
1174 (*fn)(ppObject, sc, CHECK_APP_DOMAIN);
1175
1176 LOG((LF_GC, INFO3, FMT_ADDR "\n", DBG_ADDR(*ppObject) ));
1177 }
1178 }
1179 pByRefInfos = pByRefInfos->pNext;
1180 }
1181}
1182
1183void ProtectValueClassFrame::GcScanRoots(promote_func *fn, ScanContext *sc)
1184{
1185 CONTRACTL
1186 {
1187 NOTHROW;
1188 GC_NOTRIGGER;
1189 }
1190 CONTRACTL_END
1191
1192 ValueClassInfo *pVCInfo = m_pVCInfo;
1193 while (pVCInfo != NULL)
1194 {
1195 _ASSERTE(pVCInfo->pMT->IsValueType());
1196 ReportPointersFromValueType(fn, sc, pVCInfo->pMT, pVCInfo->pData);
1197 pVCInfo = pVCInfo->pNext;
1198 }
1199}
1200
1201//
1202// Promote Caller Stack
1203//
1204//
1205
1206void TransitionFrame::PromoteCallerStack(promote_func* fn, ScanContext* sc)
1207{
1208 WRAPPER_NO_CONTRACT;
1209
1210 // I believe this is the contract:
1211 //CONTRACTL
1212 //{
1213 // INSTANCE_CHECK;
1214 // NOTHROW;
1215 // GC_NOTRIGGER;
1216 // FORBID_FAULT;
1217 // MODE_ANY;
1218 //}
1219 //CONTRACTL_END
1220
1221 MethodDesc *pFunction;
1222
1223 LOG((LF_GC, INFO3, " Promoting method caller Arguments\n" ));
1224
1225 // We're going to have to look at the signature to determine
1226 // which arguments a are pointers....First we need the function
1227 pFunction = GetFunction();
1228 if (pFunction == NULL)
1229 return;
1230
1231 // Now get the signature...
1232 Signature callSignature = pFunction->GetSignature();
1233 if (callSignature.IsEmpty())
1234 {
1235 return;
1236 }
1237
1238 //If not "vararg" calling convention, assume "default" calling convention
1239 if (!MetaSig::IsVarArg(pFunction->GetModule(), callSignature))
1240 {
1241 SigTypeContext typeContext(pFunction);
1242 PCCOR_SIGNATURE pSig;
1243 DWORD cbSigSize;
1244 pFunction->GetSig(&pSig, &cbSigSize);
1245
1246 MetaSig msig(pSig, cbSigSize, pFunction->GetModule(), &typeContext);
1247
1248 if (pFunction->RequiresInstArg() && !SuppressParamTypeArg())
1249 msig.SetHasParamTypeArg();
1250
1251 PromoteCallerStackHelper (fn, sc, pFunction, &msig);
1252 }
1253 else
1254 {
1255 VASigCookie *varArgSig = GetVASigCookie();
1256
1257 //Note: no instantiations needed for varargs
1258 MetaSig msig(varArgSig->signature,
1259 varArgSig->pModule,
1260 NULL);
1261 PromoteCallerStackHelper (fn, sc, pFunction, &msig);
1262 }
1263}
1264
1265void TransitionFrame::PromoteCallerStackHelper(promote_func* fn, ScanContext* sc,
1266 MethodDesc *pFunction, MetaSig *pmsig)
1267{
1268 WRAPPER_NO_CONTRACT;
1269 // I believe this is the contract:
1270 //CONTRACTL
1271 //{
1272 // INSTANCE_CHECK;
1273 // NOTHROW;
1274 // GC_NOTRIGGER;
1275 // FORBID_FAULT;
1276 // MODE_ANY;
1277 //}
1278 //CONTRACTL_END
1279
1280 ArgIterator argit(pmsig);
1281
1282 TADDR pTransitionBlock = GetTransitionBlock();
1283
1284 // promote 'this' for non-static methods
1285 if (argit.HasThis() && pFunction != NULL)
1286 {
1287 BOOL interior = pFunction->GetMethodTable()->IsValueType() && !pFunction->IsUnboxingStub();
1288
1289 PTR_PTR_VOID pThis = dac_cast<PTR_PTR_VOID>(pTransitionBlock + argit.GetThisOffset());
1290 LOG((LF_GC, INFO3,
1291 " 'this' Argument at " FMT_ADDR "promoted from" FMT_ADDR "\n",
1292 DBG_ADDR(pThis), DBG_ADDR(*pThis) ));
1293
1294 if (interior)
1295 PromoteCarefully(fn, PTR_PTR_Object(pThis), sc, GC_CALL_INTERIOR|CHECK_APP_DOMAIN);
1296 else
1297 (fn)(PTR_PTR_Object(pThis), sc, CHECK_APP_DOMAIN);
1298 }
1299
1300 if (argit.HasRetBuffArg())
1301 {
1302 PTR_PTR_VOID pRetBuffArg = dac_cast<PTR_PTR_VOID>(pTransitionBlock + argit.GetRetBuffArgOffset());
1303 LOG((LF_GC, INFO3, " ret buf Argument promoted from" FMT_ADDR "\n", DBG_ADDR(*pRetBuffArg) ));
1304 PromoteCarefully(fn, PTR_PTR_Object(pRetBuffArg), sc, GC_CALL_INTERIOR|CHECK_APP_DOMAIN);
1305 }
1306
1307 int argOffset;
1308 while ((argOffset = argit.GetNextOffset()) != TransitionBlock::InvalidOffset)
1309 {
1310 ArgDestination argDest(dac_cast<PTR_VOID>(pTransitionBlock), argOffset, argit.GetArgLocDescForStructInRegs());
1311 pmsig->GcScanRoots(&argDest, fn, sc);
1312 }
1313}
1314
1315#ifdef _TARGET_X86_
1316UINT TransitionFrame::CbStackPopUsingGCRefMap(PTR_BYTE pGCRefMap)
1317{
1318 LIMITED_METHOD_CONTRACT;
1319
1320 GCRefMapDecoder decoder(pGCRefMap);
1321 return decoder.ReadStackPop() * sizeof(TADDR);
1322}
1323#endif
1324
1325void TransitionFrame::PromoteCallerStackUsingGCRefMap(promote_func* fn, ScanContext* sc, PTR_BYTE pGCRefMap)
1326{
1327 WRAPPER_NO_CONTRACT;
1328
1329 GCRefMapDecoder decoder(pGCRefMap);
1330
1331#ifdef _TARGET_X86_
1332 // Skip StackPop
1333 decoder.ReadStackPop();
1334#endif
1335
1336 TADDR pTransitionBlock = GetTransitionBlock();
1337
1338 while (!decoder.AtEnd())
1339 {
1340 int pos = decoder.CurrentPos();
1341 int token = decoder.ReadToken();
1342
1343 int ofs;
1344
1345#ifdef _TARGET_X86_
1346 ofs = (pos < NUM_ARGUMENT_REGISTERS) ?
1347 (TransitionBlock::GetOffsetOfArgumentRegisters() + ARGUMENTREGISTERS_SIZE - (pos + 1) * sizeof(TADDR)) :
1348 (TransitionBlock::GetOffsetOfArgs() + (pos - NUM_ARGUMENT_REGISTERS) * sizeof(TADDR));
1349#else
1350 ofs = TransitionBlock::GetOffsetOfArgumentRegisters() + pos * sizeof(TADDR);
1351#endif
1352
1353 PTR_TADDR ppObj = dac_cast<PTR_TADDR>(pTransitionBlock + ofs);
1354
1355 switch (token)
1356 {
1357 case GCREFMAP_SKIP:
1358 break;
1359 case GCREFMAP_REF:
1360 fn(dac_cast<PTR_PTR_Object>(ppObj), sc, CHECK_APP_DOMAIN);
1361 break;
1362 case GCREFMAP_INTERIOR:
1363 PromoteCarefully(fn, dac_cast<PTR_PTR_Object>(ppObj), sc, GC_CALL_INTERIOR | GC_CALL_CHECK_APP_DOMAIN);
1364 break;
1365 case GCREFMAP_METHOD_PARAM:
1366 if (sc->promotion)
1367 {
1368#ifndef DACCESS_COMPILE
1369 MethodDesc *pMDReal = dac_cast<PTR_MethodDesc>(*ppObj);
1370 if (pMDReal != NULL)
1371 GcReportLoaderAllocator(fn, sc, pMDReal->GetLoaderAllocator());
1372#endif
1373 }
1374 break;
1375 case GCREFMAP_TYPE_PARAM:
1376 if (sc->promotion)
1377 {
1378#ifndef DACCESS_COMPILE
1379 MethodTable *pMTReal = dac_cast<PTR_MethodTable>(*ppObj);
1380 if (pMTReal != NULL)
1381 GcReportLoaderAllocator(fn, sc, pMTReal->GetLoaderAllocator());
1382#endif
1383 }
1384 break;
1385 case GCREFMAP_VASIG_COOKIE:
1386 {
1387 VASigCookie *varArgSig = dac_cast<PTR_VASigCookie>(*ppObj);
1388
1389 //Note: no instantiations needed for varargs
1390 MetaSig msig(varArgSig->signature,
1391 varArgSig->pModule,
1392 NULL);
1393 PromoteCallerStackHelper (fn, sc, NULL, &msig);
1394 }
1395 break;
1396 default:
1397 _ASSERTE(!"Unknown GCREFMAP token");
1398 break;
1399 }
1400 }
1401}
1402
1403void PInvokeCalliFrame::PromoteCallerStack(promote_func* fn, ScanContext* sc)
1404{
1405 WRAPPER_NO_CONTRACT;
1406
1407 LOG((LF_GC, INFO3, " Promoting CALLI caller Arguments\n" ));
1408
1409 // get the signature
1410 VASigCookie *varArgSig = GetVASigCookie();
1411 if (varArgSig->signature.IsEmpty())
1412 {
1413 return;
1414 }
1415
1416 // no instantiations needed for varargs
1417 MetaSig msig(varArgSig->signature,
1418 varArgSig->pModule,
1419 NULL);
1420 PromoteCallerStackHelper(fn, sc, NULL, &msig);
1421}
1422
1423#ifndef DACCESS_COMPILE
1424PInvokeCalliFrame::PInvokeCalliFrame(TransitionBlock * pTransitionBlock, VASigCookie * pVASigCookie, PCODE pUnmanagedTarget)
1425 : FramedMethodFrame(pTransitionBlock, NULL)
1426{
1427 LIMITED_METHOD_CONTRACT;
1428
1429 m_pVASigCookie = pVASigCookie;
1430 m_pUnmanagedTarget = pUnmanagedTarget;
1431}
1432#endif // #ifndef DACCESS_COMPILE
1433
1434#ifdef FEATURE_COMINTEROP
1435
1436#ifndef DACCESS_COMPILE
1437ComPlusMethodFrame::ComPlusMethodFrame(TransitionBlock * pTransitionBlock, MethodDesc * pMD)
1438 : FramedMethodFrame(pTransitionBlock, pMD)
1439{
1440 LIMITED_METHOD_CONTRACT;
1441}
1442#endif // #ifndef DACCESS_COMPILE
1443
1444//virtual
1445void ComPlusMethodFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
1446{
1447 WRAPPER_NO_CONTRACT;
1448
1449 // ComPlusMethodFrame is only used in the event call / late bound call code path where we do not have IL stub
1450 // so we need to promote the arguments and return value manually.
1451
1452 FramedMethodFrame::GcScanRoots(fn, sc);
1453 PromoteCallerStack(fn, sc);
1454
1455 MetaSig::RETURNTYPE returnType = GetFunction()->ReturnsObject();
1456
1457 // Promote the returned object
1458 if(returnType == MetaSig::RETOBJ)
1459 (*fn)(GetReturnObjectPtr(), sc, CHECK_APP_DOMAIN);
1460 else if (returnType == MetaSig::RETBYREF)
1461 PromoteCarefully(fn, GetReturnObjectPtr(), sc, GC_CALL_INTERIOR|CHECK_APP_DOMAIN);
1462}
1463#endif // FEATURE_COMINTEROP
1464
1465#if defined (_DEBUG) && !defined (DACCESS_COMPILE)
1466// For IsProtectedByGCFrame, we need to know whether a given object ref is protected
1467// by a ComPlusMethodFrame or a ComMethodFrame. Since GCScanRoots for those frames are
1468// quite complicated, we don't want to duplicate their logic so we call GCScanRoots with
1469// IsObjRefProtected (a fake promote function) and an extended ScanContext to do the checking.
1470
1471struct IsObjRefProtectedScanContext : public ScanContext
1472{
1473 OBJECTREF * oref_to_check;
1474 BOOL oref_protected;
1475 IsObjRefProtectedScanContext (OBJECTREF * oref)
1476 {
1477 thread_under_crawl = GetThread ();
1478 promotion = TRUE;
1479 oref_to_check = oref;
1480 oref_protected = FALSE;
1481 }
1482};
1483
1484void IsObjRefProtected (Object** ppObj, ScanContext* sc, uint32_t)
1485{
1486 LIMITED_METHOD_CONTRACT;
1487 IsObjRefProtectedScanContext * orefProtectedSc = (IsObjRefProtectedScanContext *)sc;
1488 if (ppObj == (Object **)(orefProtectedSc->oref_to_check))
1489 orefProtectedSc->oref_protected = TRUE;
1490}
1491
1492BOOL TransitionFrame::Protects(OBJECTREF * ppORef)
1493{
1494 WRAPPER_NO_CONTRACT;
1495 IsObjRefProtectedScanContext sc (ppORef);
1496 // Set the stack limit for the scan to the SP of the managed frame above the transition frame
1497 sc.stack_limit = GetSP();
1498 GcScanRoots (IsObjRefProtected, &sc);
1499 return sc.oref_protected;
1500}
1501#endif //defined (_DEBUG) && !defined (DACCESS_COMPILE)
1502
1503//+----------------------------------------------------------------------------
1504//
1505// Method: TPMethodFrame::GcScanRoots public
1506//
1507// Synopsis: GC protects arguments on the stack
1508//
1509
1510//
1511//+----------------------------------------------------------------------------
1512
1513#ifdef FEATURE_COMINTEROP
1514
1515#ifdef _TARGET_X86_
1516// Return the # of stack bytes pushed by the unmanaged caller.
1517UINT ComMethodFrame::GetNumCallerStackBytes()
1518{
1519 WRAPPER_NO_CONTRACT;
1520 SUPPORTS_DAC;
1521
1522 ComCallMethodDesc* pCMD = PTR_ComCallMethodDesc((TADDR)GetDatum());
1523 PREFIX_ASSUME(pCMD != NULL);
1524 // assumes __stdcall
1525 // compute the callee pop stack bytes
1526 return pCMD->GetNumStackBytes();
1527}
1528#endif // _TARGET_X86_
1529
1530#ifndef DACCESS_COMPILE
1531void ComMethodFrame::DoSecondPassHandlerCleanup(Frame * pCurFrame)
1532{
1533 LIMITED_METHOD_CONTRACT;
1534
1535 // Find ComMethodFrame, noting any ContextTransitionFrame along the way
1536
1537 while ((pCurFrame != FRAME_TOP) &&
1538 (pCurFrame->GetVTablePtr() != ComMethodFrame::GetMethodFrameVPtr()))
1539 {
1540 if (pCurFrame->GetVTablePtr() == ContextTransitionFrame::GetMethodFrameVPtr())
1541 {
1542 // If there is a context transition before we find a ComMethodFrame, do nothing. Expect that
1543 // the AD transition code will perform the corresponding work after it pops its context
1544 // transition frame and before it rethrows the exception.
1545 return;
1546 }
1547 pCurFrame = pCurFrame->PtrNextFrame();
1548 }
1549
1550 if (pCurFrame == FRAME_TOP)
1551 return;
1552
1553 ComMethodFrame * pComMethodFrame = (ComMethodFrame *)pCurFrame;
1554
1555 _ASSERTE(pComMethodFrame != NULL);
1556 Thread * pThread = GetThread();
1557 GCX_COOP_THREAD_EXISTS(pThread);
1558 // Unwind the frames till the entry frame (which was ComMethodFrame)
1559 pCurFrame = pThread->GetFrame();
1560 while ((pCurFrame != NULL) && (pCurFrame <= pComMethodFrame))
1561 {
1562 pCurFrame->ExceptionUnwind();
1563 pCurFrame = pCurFrame->PtrNextFrame();
1564 }
1565
1566 // At this point, pCurFrame would be the ComMethodFrame's predecessor frame
1567 // that we need to reset to.
1568 _ASSERTE((pCurFrame != NULL) && (pComMethodFrame->PtrNextFrame() == pCurFrame));
1569 pThread->SetFrame(pCurFrame);
1570}
1571#endif // !DACCESS_COMPILE
1572
1573#endif // FEATURE_COMINTEROP
1574
1575
1576#ifdef _TARGET_X86_
1577
1578PTR_UMEntryThunk UMThkCallFrame::GetUMEntryThunk()
1579{
1580 LIMITED_METHOD_DAC_CONTRACT;
1581 return dac_cast<PTR_UMEntryThunk>(GetDatum());
1582}
1583
1584#ifdef DACCESS_COMPILE
1585void UMThkCallFrame::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
1586{
1587 WRAPPER_NO_CONTRACT;
1588 UnmanagedToManagedFrame::EnumMemoryRegions(flags);
1589
1590 // Pieces of the UMEntryThunk need to be saved.
1591 UMEntryThunk *pThunk = GetUMEntryThunk();
1592 DacEnumMemoryRegion(dac_cast<TADDR>(pThunk), sizeof(UMEntryThunk));
1593
1594 UMThunkMarshInfo *pMarshInfo = pThunk->GetUMThunkMarshInfo();
1595 DacEnumMemoryRegion(dac_cast<TADDR>(pMarshInfo), sizeof(UMThunkMarshInfo));
1596}
1597#endif
1598
1599#endif // _TARGET_X86_
1600
1601#ifndef DACCESS_COMPILE
1602
1603#if defined(_MSC_VER) && defined(_TARGET_X86_)
1604#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
1605#endif
1606
1607// Initialization of HelperMethodFrame.
1608void HelperMethodFrame::Push()
1609{
1610 CONTRACTL {
1611 if (m_Attribs & FRAME_ATTR_NO_THREAD_ABORT) NOTHROW; else THROWS;
1612 GC_TRIGGERS;
1613 MODE_COOPERATIVE;
1614 SO_TOLERANT;
1615 } CONTRACTL_END;
1616
1617 //
1618 // Finish initialization
1619 //
1620
1621 // Compiler would not inline GetGSCookiePtr() because of it is virtual method.
1622 // Inline it manually and verify that it gives same result.
1623 _ASSERTE(GetGSCookiePtr() == (((GSCookie *)(this)) - 1));
1624 *(((GSCookie *)(this)) - 1) = GetProcessGSCookie();
1625
1626 _ASSERTE(!m_MachState.isValid());
1627
1628 Thread * pThread = ::GetThread();
1629 m_pThread = pThread;
1630
1631 // Push the frame
1632 Frame::Push(pThread);
1633
1634 if (!pThread->HasThreadStateOpportunistic(Thread::TS_AbortRequested))
1635 return;
1636
1637 // Outline the slow path for better perf
1638 PushSlowHelper();
1639}
1640
1641void HelperMethodFrame::Pop()
1642{
1643 CONTRACTL {
1644 if (m_Attribs & FRAME_ATTR_NO_THREAD_ABORT) NOTHROW; else THROWS;
1645 GC_TRIGGERS;
1646 MODE_COOPERATIVE;
1647 SO_TOLERANT;
1648 } CONTRACTL_END;
1649
1650 Thread * pThread = m_pThread;
1651
1652 if ((m_Attribs & FRAME_ATTR_NO_THREAD_ABORT) || !pThread->HasThreadStateOpportunistic(Thread::TS_AbortInitiated))
1653 {
1654 Frame::Pop(pThread);
1655 return;
1656 }
1657
1658 // Outline the slow path for better perf
1659 PopSlowHelper();
1660}
1661
1662#if defined(_MSC_VER) && defined(_TARGET_X86_)
1663#pragma optimize("", on) // Go back to command line default optimizations
1664#endif
1665
1666NOINLINE void HelperMethodFrame::PushSlowHelper()
1667{
1668 CONTRACTL {
1669 if (m_Attribs & FRAME_ATTR_NO_THREAD_ABORT) NOTHROW; else THROWS;
1670 GC_TRIGGERS;
1671 MODE_COOPERATIVE;
1672 SO_TOLERANT;
1673 } CONTRACTL_END;
1674
1675 if (!(m_Attribs & FRAME_ATTR_NO_THREAD_ABORT))
1676 {
1677 if (m_pThread->IsAbortRequested())
1678 {
1679 m_pThread->HandleThreadAbort();
1680 }
1681
1682 }
1683}
1684
1685NOINLINE void HelperMethodFrame::PopSlowHelper()
1686{
1687 CONTRACTL {
1688 THROWS;
1689 GC_TRIGGERS;
1690 MODE_COOPERATIVE;
1691 SO_TOLERANT;
1692 } CONTRACTL_END;
1693
1694 m_pThread->HandleThreadAbort();
1695 Frame::Pop(m_pThread);
1696}
1697
1698#endif // #ifndef DACCESS_COMPILE
1699
1700MethodDesc* HelperMethodFrame::GetFunction()
1701{
1702 WRAPPER_NO_CONTRACT;
1703
1704#ifndef DACCESS_COMPILE
1705 InsureInit(false, NULL);
1706 return m_pMD;
1707#else
1708 if (m_MachState.isValid())
1709 {
1710 return m_pMD;
1711 }
1712 else
1713 {
1714 return ECall::MapTargetBackToMethod(m_FCallEntry);
1715 }
1716#endif
1717}
1718
1719//---------------------------------------------------------------------------------------
1720//
1721// Ensures the HelperMethodFrame gets initialized, if not already.
1722//
1723// Arguments:
1724// * initialInit -
1725// * true: ensure the simple, first stage of initialization has been completed.
1726// This is used when the HelperMethodFrame is first created.
1727// * false: complete any initialization that was left to do, if any.
1728// * unwindState - [out] DAC builds use this to return the unwound machine state.
1729// * hostCallPreference - (See code:HelperMethodFrame::HostCallPreference.)
1730//
1731// Return Value:
1732// Normally, the function always returns TRUE meaning the initialization succeeded.
1733//
1734// However, if hostCallPreference is NoHostCalls, AND if a callee (like
1735// LazyMachState::unwindLazyState) needed to acquire a JIT reader lock and was unable
1736// to do so (lest it re-enter the host), then InsureInit will abort and return FALSE.
1737// So any callers that specify hostCallPreference = NoHostCalls (which is not the
1738// default), should check for FALSE return, and refuse to use the HMF in that case.
1739// Currently only asynchronous calls made by profilers use that code path.
1740//
1741
1742BOOL HelperMethodFrame::InsureInit(bool initialInit,
1743 MachState * unwindState,
1744 HostCallPreference hostCallPreference /* = AllowHostCalls */)
1745{
1746 CONTRACTL {
1747 NOTHROW;
1748 GC_NOTRIGGER;
1749 SO_TOLERANT;
1750 if ((hostCallPreference == AllowHostCalls) && !m_MachState.isValid()) { HOST_CALLS; } else { HOST_NOCALLS; }
1751 SUPPORTS_DAC;
1752 } CONTRACTL_END;
1753
1754 if (m_MachState.isValid())
1755 {
1756 return TRUE;
1757 }
1758
1759 _ASSERTE(m_Attribs != 0xCCCCCCCC);
1760
1761#ifndef DACCESS_COMPILE
1762 if (!initialInit)
1763 {
1764 m_pMD = ECall::MapTargetBackToMethod(m_FCallEntry);
1765
1766 // if this is an FCall, we should find it
1767 _ASSERTE(m_FCallEntry == 0 || m_pMD != 0);
1768 }
1769#endif
1770
1771 // Because TRUE FCalls can be called from via reflection, com-interop, etc.,
1772 // we can't rely on the fact that we are called from jitted code to find the
1773 // caller of the FCALL. Thus FCalls must erect the frame directly in the
1774 // FCall. For JIT helpers, however, we can rely on this, and so they can
1775 // be sneakier and defer the HelperMethodFrame setup to a called worker method.
1776
1777 // Work with a copy so that we only write the values once.
1778 // this avoids race conditions.
1779 LazyMachState* lazy = &m_MachState;
1780 DWORD threadId = m_pThread->GetOSThreadId();
1781 MachState unwound;
1782
1783 if (!initialInit &&
1784 m_FCallEntry == 0 &&
1785 !(m_Attribs & Frame::FRAME_ATTR_EXACT_DEPTH)) // Jit Helper
1786 {
1787 LazyMachState::unwindLazyState(
1788 lazy,
1789 &unwound,
1790 threadId,
1791 0,
1792 hostCallPreference);
1793
1794#if !defined(DACCESS_COMPILE)
1795 if (!unwound.isValid())
1796 {
1797 // This only happens if LazyMachState::unwindLazyState had to abort as a
1798 // result of failing to take a reader lock (because we told it not to yield,
1799 // but the writer lock was already held). Since we've not yet updated
1800 // m_MachState, this HelperMethodFrame will still be considered not fully
1801 // initialized (so a future call into InsureInit() will attempt to complete
1802 // initialization again).
1803 //
1804 // Note that, in DAC builds, the contract with LazyMachState::unwindLazyState
1805 // is a bit different, and it's expected that LazyMachState::unwindLazyState
1806 // will commonly return an unwound state with _pRetAddr==NULL (which counts
1807 // as an "invalid" MachState). So have DAC builds deliberately fall through
1808 // rather than aborting when unwound is invalid.
1809 _ASSERTE(hostCallPreference == NoHostCalls);
1810 return FALSE;
1811 }
1812#endif // !defined(DACCESS_COMPILE)
1813 }
1814 else if (!initialInit &&
1815 (m_Attribs & Frame::FRAME_ATTR_CAPTURE_DEPTH_2) != 0)
1816 {
1817 // explictly told depth
1818 LazyMachState::unwindLazyState(lazy, &unwound, threadId, 2);
1819 }
1820 else
1821 {
1822 // True FCall
1823 LazyMachState::unwindLazyState(lazy, &unwound, threadId, 1);
1824 }
1825
1826 _ASSERTE(unwound.isValid());
1827
1828#if !defined(DACCESS_COMPILE)
1829 lazy->setLazyStateFromUnwind(&unwound);
1830#else // DACCESS_COMPILE
1831 if (unwindState)
1832 {
1833 *unwindState = unwound;
1834 }
1835#endif // DACCESS_COMPILE
1836
1837 return TRUE;
1838}
1839
1840
1841#include "comdelegate.h"
1842
1843Assembly* SecureDelegateFrame::GetAssembly()
1844{
1845 WRAPPER_NO_CONTRACT;
1846
1847#if !defined(DACCESS_COMPILE)
1848 // obtain the frame off the delegate pointer
1849 DELEGATEREF delegate = (DELEGATEREF) GetThis();
1850 _ASSERTE(delegate);
1851 if (!delegate->IsWrapperDelegate())
1852 {
1853 MethodDesc* pMethod = (MethodDesc*) delegate->GetMethodPtrAux();
1854 Assembly* pAssembly = pMethod->GetAssembly();
1855 _ASSERTE(pAssembly != NULL);
1856 return pAssembly;
1857 }
1858 else
1859 return NULL;
1860#else
1861 DacNotImpl();
1862 return NULL;
1863#endif
1864}
1865
1866BOOL SecureDelegateFrame::TraceFrame(Thread *thread, BOOL fromPatch, TraceDestination *trace, REGDISPLAY *regs)
1867{
1868 WRAPPER_NO_CONTRACT;
1869
1870 _ASSERTE(!fromPatch);
1871
1872 // Unlike multicast delegates, secure delegates only call one method. So, we should just return false here
1873 // and let the step out logic continue to the caller of the secure delegate stub.
1874 LOG((LF_CORDB, LL_INFO1000, "SDF::TF: return FALSE\n"));
1875
1876 return FALSE;
1877}
1878
1879BOOL MulticastFrame::TraceFrame(Thread *thread, BOOL fromPatch,
1880 TraceDestination *trace, REGDISPLAY *regs)
1881{
1882 CONTRACTL
1883 {
1884 THROWS;
1885 GC_NOTRIGGER;
1886 MODE_COOPERATIVE;
1887 }
1888 CONTRACTL_END;
1889
1890 _ASSERTE(!fromPatch);
1891
1892#ifdef DACCESS_COMPILE
1893 return FALSE;
1894
1895#else // !DACCESS_COMPILE
1896 LOG((LF_CORDB,LL_INFO10000, "MulticastFrame::TF FromPatch:0x%x, at 0x%x\n", fromPatch, GetControlPC(regs)));
1897
1898 // At this point we have no way to recover the Stub object from the control pc. We can't use the MD stored
1899 // in the MulticastFrame because it points to the dummy Invoke() method, not the method we want to call.
1900
1901 BYTE *pbDel = NULL;
1902 int delegateCount = 0;
1903
1904#if defined(_TARGET_X86_)
1905 // At this point the counter hasn't been incremented yet.
1906 delegateCount = *regs->GetEdiLocation() + 1;
1907 pbDel = *(BYTE **)( (size_t)*regs->GetEsiLocation() + GetOffsetOfTransitionBlock() + ArgIterator::GetThisOffset());
1908#elif defined(_TARGET_AMD64_)
1909 // At this point the counter hasn't been incremented yet.
1910 delegateCount = (int)regs->pCurrentContext->Rdi + 1;
1911 pbDel = *(BYTE **)( (size_t)(regs->pCurrentContext->Rsi) + GetOffsetOfTransitionBlock() + ArgIterator::GetThisOffset());
1912#elif defined(_TARGET_ARM_)
1913 // At this point the counter has not yet been incremented. Counter is in R7, frame pointer in R4.
1914 delegateCount = regs->pCurrentContext->R7 + 1;
1915 pbDel = *(BYTE **)( (size_t)(regs->pCurrentContext->R4) + GetOffsetOfTransitionBlock() + ArgIterator::GetThisOffset());
1916#else
1917 delegateCount = 0;
1918 PORTABILITY_ASSERT("MulticastFrame::TraceFrame (frames.cpp)");
1919#endif
1920
1921 int totalDelegateCount = (int)*(size_t*)(pbDel + DelegateObject::GetOffsetOfInvocationCount());
1922
1923 _ASSERTE( COMDelegate::IsTrueMulticastDelegate( ObjectToOBJECTREF((Object*)pbDel) ) );
1924
1925 if (delegateCount == totalDelegateCount)
1926 {
1927 LOG((LF_CORDB, LL_INFO1000, "MF::TF: Executed all stubs, should return\n"));
1928 // We've executed all the stubs, so we should return
1929 return FALSE;
1930 }
1931 else
1932 {
1933 // We're going to execute stub delegateCount next, so go and grab it.
1934 BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationList());
1935
1936 pbDel = *(BYTE**)( ((ArrayBase *)pbDelInvocationList)->GetDataPtr() +
1937 ((ArrayBase *)pbDelInvocationList)->GetComponentSize()*delegateCount);
1938
1939 _ASSERTE(pbDel);
1940 return DelegateInvokeStubManager::TraceDelegateObject(pbDel, trace);
1941 }
1942#endif // !DACCESS_COMPILE
1943}
1944
1945#ifndef DACCESS_COMPILE
1946
1947VOID InlinedCallFrame::Init()
1948{
1949 WRAPPER_NO_CONTRACT;
1950
1951 *((TADDR *)this) = GetMethodFrameVPtr();
1952
1953 // GetGSCookiePtr contains a virtual call and this is a perf critical method so we don't want to call it in ret builds
1954 GSCookie *ptrGS = (GSCookie *)((BYTE *)this - sizeof(GSCookie));
1955 _ASSERTE(ptrGS == GetGSCookiePtr());
1956
1957 *ptrGS = GetProcessGSCookie();
1958
1959 m_Datum = NULL;
1960 m_pCallSiteSP = NULL;
1961 m_pCallerReturnAddress = NULL;
1962}
1963
1964
1965
1966void UnmanagedToManagedFrame::ExceptionUnwind()
1967{
1968 WRAPPER_NO_CONTRACT;
1969
1970 AppDomain::ExceptionUnwind(this);
1971}
1972
1973#endif // !DACCESS_COMPILE
1974
1975void ContextTransitionFrame::GcScanRoots(promote_func *fn, ScanContext* sc)
1976{
1977 WRAPPER_NO_CONTRACT;
1978
1979 // Don't check app domains here - m_LastThrownObjectInParentContext is in the parent frame's app domain
1980 (*fn)(dac_cast<PTR_PTR_Object>(PTR_HOST_MEMBER_TADDR(ContextTransitionFrame, this, m_LastThrownObjectInParentContext)), sc, 0);
1981 LOG((LF_GC, INFO3, " " FMT_ADDR "\n", DBG_ADDR(m_LastThrownObjectInParentContext) ));
1982
1983 // don't need to worry about the object moving as it is stored in a weak handle
1984 // but do need to report it so it doesn't get collected if the only reference to
1985 // it is in this frame. So only do something if are in promotion phase. And if are
1986 // in reloc phase this could cause invalid refs as the object may have been moved.
1987 if (! sc->promotion)
1988 return;
1989
1990 // The dac only cares about strong references at the moment. Since this is always
1991 // in a weak ref, we don't report it here.
1992}
1993
1994
1995PCODE UnmanagedToManagedFrame::GetReturnAddress()
1996{
1997 WRAPPER_NO_CONTRACT;
1998
1999 PCODE pRetAddr = Frame::GetReturnAddress();
2000
2001 if (InlinedCallFrame::FrameHasActiveCall(m_Next) &&
2002 pRetAddr == m_Next->GetReturnAddress())
2003 {
2004 // there's actually no unmanaged code involved - we were called directly
2005 // from managed code using an InlinedCallFrame
2006 return NULL;
2007 }
2008 else
2009 {
2010 return pRetAddr;
2011 }
2012}
2013