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 | |
52 | unsigned dbgStubCtr = 0; |
53 | unsigned dbgStubTrip = 0xFFFFFFFF; |
54 | |
55 | void 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 | |
119 | void __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 | |
159 | bool 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 |
210 | const 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 */ |
227 | PTR_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 | |
245 | void 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 | |
270 | void 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. |
292 | static PtrHashMap* s_pFrameVTables = NULL; |
293 | |
294 | // static |
295 | void 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 |
315 | void 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 |
327 | bool 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 |
367 | PTR_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 | |
385 | VOID 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 | |
399 | VOID 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 | |
435 | VOID 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 | |
449 | VOID 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) |
476 | void 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 | //--------------------------------------------------------------- |
502 | PTR_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 | |
534 | TADDR TransitionFrame::GetAddrOfThis() |
535 | { |
536 | WRAPPER_NO_CONTRACT; |
537 | return GetTransitionBlock() + ArgIterator::GetThisOffset(); |
538 | } |
539 | |
540 | VASigCookie * 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 |
557 | PrestubMethodFrame::PrestubMethodFrame(TransitionBlock * pTransitionBlock, MethodDesc * pMD) |
558 | : FramedMethodFrame(pTransitionBlock, pMD) |
559 | { |
560 | LIMITED_METHOD_CONTRACT; |
561 | } |
562 | #endif // #ifndef DACCESS_COMPILE |
563 | |
564 | BOOL 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 | //----------------------------------------------------------------------- |
594 | StubDispatchFrame::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 | |
610 | MethodDesc* 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 | |
634 | static 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 | |
667 | PTR_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 | |
709 | void 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 | |
731 | BOOL 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 | |
757 | Frame::Interception StubDispatchFrame::GetInterception() |
758 | { |
759 | LIMITED_METHOD_CONTRACT; |
760 | |
761 | return INTERCEPTION_NONE; |
762 | } |
763 | |
764 | #ifndef DACCESS_COMPILE |
765 | ExternalMethodFrame::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 | |
777 | void 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 | |
790 | PTR_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 | |
811 | Frame::Interception ExternalMethodFrame::GetInterception() |
812 | { |
813 | LIMITED_METHOD_CONTRACT; |
814 | |
815 | return INTERCEPTION_NONE; |
816 | } |
817 | |
818 | Frame::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 |
833 | DynamicHelperFrame::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 | |
842 | void 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 | //----------------------------------------------------------------------- |
887 | VOID |
888 | ComPrestubMethodFrame::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 | //-------------------------------------------------------------------- |
908 | GCFrame::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 | |
922 | GCFrame::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 | |
936 | void 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 | |
989 | void 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 | //-------------------------------------------------------------------- |
1012 | VOID 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. |
1026 | InterpreterFrame::InterpreterFrame(Interpreter* interp) |
1027 | : Frame(), m_interp(interp) |
1028 | { |
1029 | Push(); |
1030 | } |
1031 | |
1032 | |
1033 | MethodDesc* InterpreterFrame::GetFunction() |
1034 | { |
1035 | return m_interp->GetMethodDesc(); |
1036 | } |
1037 | |
1038 | void 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 | |
1047 | struct IsProtectedByGCFrameStruct |
1048 | { |
1049 | OBJECTREF *ppObjectRef; |
1050 | UINT count; |
1051 | }; |
1052 | |
1053 | static 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 | |
1071 | BOOL 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 | |
1100 | void 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 | |
1148 | void 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 | |
1183 | void 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 | |
1206 | void 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 | |
1265 | void 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_ |
1316 | UINT 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 | |
1325 | void 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 | |
1403 | void 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 |
1424 | PInvokeCalliFrame::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 |
1437 | ComPlusMethodFrame::ComPlusMethodFrame(TransitionBlock * pTransitionBlock, MethodDesc * pMD) |
1438 | : FramedMethodFrame(pTransitionBlock, pMD) |
1439 | { |
1440 | LIMITED_METHOD_CONTRACT; |
1441 | } |
1442 | #endif // #ifndef DACCESS_COMPILE |
1443 | |
1444 | //virtual |
1445 | void 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 | |
1471 | struct 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 | |
1484 | void 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 | |
1492 | BOOL 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. |
1517 | UINT 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 |
1531 | void 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 | |
1578 | PTR_UMEntryThunk UMThkCallFrame::GetUMEntryThunk() |
1579 | { |
1580 | LIMITED_METHOD_DAC_CONTRACT; |
1581 | return dac_cast<PTR_UMEntryThunk>(GetDatum()); |
1582 | } |
1583 | |
1584 | #ifdef DACCESS_COMPILE |
1585 | void 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. |
1608 | void 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 | |
1641 | void 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 | |
1666 | NOINLINE 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 | |
1685 | NOINLINE 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 | |
1700 | MethodDesc* 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 | |
1742 | BOOL 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 | |
1843 | Assembly* 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 | |
1866 | BOOL 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 | |
1879 | BOOL 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 | |
1947 | VOID 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 | |
1966 | void UnmanagedToManagedFrame::ExceptionUnwind() |
1967 | { |
1968 | WRAPPER_NO_CONTRACT; |
1969 | |
1970 | AppDomain::ExceptionUnwind(this); |
1971 | } |
1972 | |
1973 | #endif // !DACCESS_COMPILE |
1974 | |
1975 | void 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 | |
1995 | PCODE 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 | |