| 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 | |