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.
6 // Various helper routines for generating x86 assembly code.
19 #include "dllimport.h"
20 #include "comdelegate.h"
23 #include "comdelegate.h"
25 #include "jitinterface.h"
27 #include "dbginterface.h"
28 #include "eeprofinterfaces.h"
30 #include "asmconstants.h"
32 #include "virtualcallstub.h"
33 #include "mdaassistants.h"
34 #include "jitinterface.h"
36 #ifdef FEATURE_COMINTEROP
37 #include "comtoclrcall.h"
38 #include "runtimecallablewrapper.h"
40 #include "olevariant.h"
41 #endif // FEATURE_COMINTEROP
47 #include "stublink.inl"
49 extern "C" DWORD STDCALL GetSpecificCpuTypeAsm(void);
50 extern "C" DWORD STDCALL GetSpecificCpuFeaturesAsm(DWORD *pInfo);
52 // NOTE on Frame Size C_ASSERT usage in this file
53 // if the frame size changes then the stubs have to be revisited for correctness
54 // kindly revist the logic and then update the constants so that the C_ASSERT will again fire
55 // if someone changes the frame size. You are expected to keep this hard coded constant
56 // up to date so that changes in the frame size trigger errors at compile time if the code is not altered
58 void generate_noref_copy (unsigned nbytes, StubLinkerCPU* sl);
60 #ifdef WIN64EXCEPTIONS
61 void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * regs)
63 LIMITED_METHOD_CONTRACT;
65 T_CONTEXT * pContext = pRD->pCurrentContext;
66 #define CALLEE_SAVED_REGISTER(regname) pContext->regname = regs->regname;
67 ENUM_CALLEE_SAVED_REGISTERS();
68 #undef CALLEE_SAVED_REGISTER
70 KNONVOLATILE_CONTEXT_POINTERS * pContextPointers = pRD->pCurrentContextPointers;
71 #define CALLEE_SAVED_REGISTER(regname) pContextPointers->regname = (DWORD*)®s->regname;
72 ENUM_CALLEE_SAVED_REGISTERS();
73 #undef CALLEE_SAVED_REGISTER
76 void ClearRegDisplayArgumentAndScratchRegisters(REGDISPLAY * pRD)
78 LIMITED_METHOD_CONTRACT;
80 #define ARGUMENT_AND_SCRATCH_REGISTER(regname) pRD->pCurrentContextPointers->regname = NULL;
81 ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
82 #undef ARGUMENT_AND_SCRATCH_REGISTER
84 #endif // WIN64EXCEPTIONS
86 #ifndef DACCESS_COMPILE
88 //=============================================================================
89 // Runtime test to see if the OS has enabled support for the SSE2 instructions
92 BOOL Runtime_Test_For_SSE2()
94 #ifdef FEATURE_CORESYSTEM
98 BOOL result = IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
103 // **********************************************************************
105 // *** IMPORTANT NOTE: ***
107 // *** All of these RunningOnXXX APIs return true when ***
108 // *** the OS that you are running on is that OS or later. ***
109 // *** For example RunningOnWin2003() will return true ***
110 // *** when you are running on Win2k3, Vista, Win7 or later. ***
112 // **********************************************************************
115 // Windows 7 and later should alwys be using SSE2 instructions
116 // this is true for both for native and Wow64
121 if (RunningInWow64())
123 // There is an issue with saving/restoring the SSE2 registers under wow64
124 // So we figure out if we are running on an impacted OS and Service Pack level
125 // See DevDiv Bugs 89587 for the wow64 bug.
128 _ASSERTE(ExOSInfoAvailable()); // This is always available on Vista and later
131 // The issue is fixed in Windows Server 2008 or Vista/SP1
133 // It is not fixed in Vista/RTM, so check for that case
135 if ((ExOSInfoRunningOnServer() == FALSE))
137 OSVERSIONINFOEX osvi;
139 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
140 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
141 osvi.wServicePackMajor = 0;
143 DWORDLONG dwlConditionMask = 0;
144 VER_SET_CONDITION( dwlConditionMask, CLR_VER_SERVICEPACKMAJOR, VER_EQUAL);
146 if (VerifyVersionInfo(&osvi, CLR_VER_SERVICEPACKMAJOR, dwlConditionMask))
155 //---------------------------------------------------------------
156 // Returns the type of CPU (the value of x of x86)
157 // (Please note, that it returns 6 for P5-II)
158 //---------------------------------------------------------------
159 void GetSpecificCpuInfo(CORINFO_CPU * cpuInfo)
161 LIMITED_METHOD_CONTRACT;
163 static CORINFO_CPU val = { 0, 0, 0 };
172 tempVal.dwCPUType = GetSpecificCpuTypeAsm(); // written in ASM & doesn't participate in contracts
173 _ASSERTE(tempVal.dwCPUType);
177 SO_NOT_MAINLINE_REGION();
179 /* Set Family+Model+Stepping string (eg., x690 for Banias, or xF30 for P4 Prescott)
180 * instead of Family only
183 const DWORD cpuDefault = 0xFFFFFFFF;
184 static ConfigDWORD cpuFamily;
185 DWORD configCpuFamily = cpuFamily.val_DontUse_(CLRConfig::INTERNAL_CPUFamily, cpuDefault);
186 if (configCpuFamily != cpuDefault)
188 assert((configCpuFamily & 0xFFF) == configCpuFamily);
189 tempVal.dwCPUType = (tempVal.dwCPUType & 0xFFFF0000) | configCpuFamily;
194 tempVal.dwFeatures = GetSpecificCpuFeaturesAsm(&tempVal.dwExtendedFeatures); // written in ASM & doesn't participate in contracts
198 SO_NOT_MAINLINE_REGION();
200 /* Set the 32-bit feature mask
203 const DWORD cpuFeaturesDefault = 0xFFFFFFFF;
204 static ConfigDWORD cpuFeatures;
205 DWORD configCpuFeatures = cpuFeatures.val_DontUse_(CLRConfig::INTERNAL_CPUFeatures, cpuFeaturesDefault);
206 if (configCpuFeatures != cpuFeaturesDefault)
208 tempVal.dwFeatures = configCpuFeatures;
213 val = *cpuInfo = tempVal;
216 #endif // #ifndef DACCESS_COMPILE
219 #ifndef WIN64EXCEPTIONS
220 //---------------------------------------------------------------------------------------
222 // Initialize the EHContext using the resume PC and the REGDISPLAY. The EHContext is currently used in two
223 // scenarios: to store the register state before calling an EH clause, and to retrieve the ambient SP of a
224 // particular stack frame. resumePC means different things in the two scenarios. In the former case, it
225 // is the IP at which we are going to resume execution when we call an EH clause. In the latter case, it
226 // is just the current IP.
229 // resumePC - refer to the comment above
230 // regs - This is the REGDISPLAY obtained from the CrawlFrame used in the stackwalk. It represents the
231 // stack frame of the method containing the EH clause we are about to call. For getting the
232 // ambient SP, this is the stack frame we are interested in.
235 void EHContext::Setup(PCODE resumePC, PREGDISPLAY regs)
237 LIMITED_METHOD_DAC_CONTRACT;
239 // EAX ECX EDX are scratch
240 this->Esp = regs->SP;
241 this->Ebx = *regs->pEbx;
242 this->Esi = *regs->pEsi;
243 this->Edi = *regs->pEdi;
244 this->Ebp = *regs->pEbp;
246 this->Eip = (ULONG)(size_t)resumePC;
250 // Update the registers using new context
252 // This is necessary to reflect GC pointer changes during the middle of a unwind inside a
253 // finally clause, because:
254 // 1. GC won't see the part of stack inside try (which has thrown an exception) that is already
255 // unwinded and thus GC won't update GC pointers for this portion of the stack, but rather the
256 // call stack in finally.
257 // 2. upon return of finally, the unwind process continues and unwinds stack based on the part
258 // of stack inside try and won't see the updated values in finally.
259 // As a result, we need to manually update the context using register values upon return of finally
261 // Note that we only update the registers for finally clause because
262 // 1. For filter handlers, stack walker is able to see the whole stack (including the try part)
263 // with the help of ExceptionFilterFrame as filter handlers are called in first pass
264 // 2. For catch handlers, the current unwinding is already finished
266 void EHContext::UpdateFrame(PREGDISPLAY regs)
268 LIMITED_METHOD_CONTRACT;
270 // EAX ECX EDX are scratch.
271 // No need to update ESP as unwinder takes care of that for us
273 LOG((LF_EH, LL_INFO1000, "Updating saved EBX: *%p= %p\n", regs->pEbx, this->Ebx));
274 LOG((LF_EH, LL_INFO1000, "Updating saved ESI: *%p= %p\n", regs->pEsi, this->Esi));
275 LOG((LF_EH, LL_INFO1000, "Updating saved EDI: *%p= %p\n", regs->pEdi, this->Edi));
276 LOG((LF_EH, LL_INFO1000, "Updating saved EBP: *%p= %p\n", regs->pEbp, this->Ebp));
278 *regs->pEbx = this->Ebx;
279 *regs->pEsi = this->Esi;
280 *regs->pEdi = this->Edi;
281 *regs->pEbp = this->Ebp;
283 #endif // WIN64EXCEPTIONS
285 void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
297 ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
299 MethodDesc * pFunc = GetFunction();
300 _ASSERTE(pFunc != NULL);
302 UpdateRegDisplayHelper(pRD, pFunc->CbStackPop());
304 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
309 void TransitionFrame::UpdateRegDisplayHelper(const PREGDISPLAY pRD, UINT cbStackPop)
321 CalleeSavedRegisters* regs = GetCalleeSavedRegisters();
323 pRD->PCTAddr = GetReturnAddressPtr();
325 #ifdef WIN64EXCEPTIONS
327 DWORD CallerSP = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
329 pRD->IsCallerContextValid = FALSE;
330 pRD->IsCallerSPValid = FALSE;
332 pRD->pCurrentContext->Eip = *PTR_PCODE(pRD->PCTAddr);;
333 pRD->pCurrentContext->Esp = CallerSP;
335 UpdateRegDisplayFromCalleeSavedRegisters(pRD, regs);
336 ClearRegDisplayArgumentAndScratchRegisters(pRD);
338 SyncRegDisplayToCurrentContext(pRD);
340 #else // WIN64EXCEPTIONS
342 // reset pContext; it's only valid for active (top-most) frame
343 pRD->pContext = NULL;
345 #define CALLEE_SAVED_REGISTER(regname) pRD->p##regname = (DWORD*) ®s->regname;
346 ENUM_CALLEE_SAVED_REGISTERS();
347 #undef CALLEE_SAVED_REGISTER
349 pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
350 pRD->SP = (DWORD)(pRD->PCTAddr + sizeof(TADDR) + cbStackPop);
352 #endif // WIN64EXCEPTIONS
357 void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
365 PRECONDITION(m_MachState.isValid()); // InsureInit has been called
370 ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
372 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK HelperMethodFrame::UpdateRegDisplay cached ip:%p, sp:%p\n", m_MachState.GetRetAddr(), m_MachState.esp()));
374 pRD->PCTAddr = dac_cast<TADDR>(m_MachState.pRetAddr());
376 #ifdef WIN64EXCEPTIONS
378 pRD->IsCallerContextValid = FALSE;
379 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
381 #ifdef DACCESS_COMPILE
382 PORTABILITY_ASSERT("HelperMethodFrame::UpdateRegDisplay");
383 #endif // DACCESS_COMPILE
385 pRD->pCurrentContext->Eip = pRD->ControlPC = m_MachState.GetRetAddr();
386 pRD->pCurrentContext->Esp = pRD->SP = (DWORD) m_MachState.esp();
388 #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContext->regname = *((DWORD*) m_MachState.p##regname());
389 ENUM_CALLEE_SAVED_REGISTERS();
390 #undef CALLEE_SAVED_REGISTER
392 #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = (DWORD*) m_MachState.p##regname();
393 ENUM_CALLEE_SAVED_REGISTERS();
394 #undef CALLEE_SAVED_REGISTER
397 // Clear all knowledge of scratch registers. We're skipping to any
398 // arbitrary point on the stack, and frames aren't required to preserve or
399 // keep track of these anyways.
402 ClearRegDisplayArgumentAndScratchRegisters(pRD);
404 #else // WIN64EXCEPTIONS
406 // reset pContext; it's only valid for active (top-most) frame
407 pRD->pContext = NULL;
409 #ifdef DACCESS_COMPILE
412 // In the dac case we may have gotten here
413 // without the frame being initialized, so
414 // try and initialize on the fly.
417 if (!m_MachState.isValid())
419 MachState unwindState;
421 InsureInit(false, &unwindState);
422 pRD->PCTAddr = dac_cast<TADDR>(unwindState.pRetAddr());
423 pRD->ControlPC = unwindState.GetRetAddr();
424 pRD->SP = unwindState._esp;
426 // Get some special host instance memory
427 // so we have a place to point to.
428 // This host memory has no target address
429 // and so won't be looked up or used for
431 MachState* thisState = (MachState*)
432 DacAllocHostOnlyInstance(sizeof(*thisState), true);
434 thisState->_edi = unwindState._edi;
435 pRD->pEdi = (DWORD *)&thisState->_edi;
436 thisState->_esi = unwindState._esi;
437 pRD->pEsi = (DWORD *)&thisState->_esi;
438 thisState->_ebx = unwindState._ebx;
439 pRD->pEbx = (DWORD *)&thisState->_ebx;
440 thisState->_ebp = unwindState._ebp;
441 pRD->pEbp = (DWORD *)&thisState->_ebp;
443 // InsureInit always sets m_RegArgs to zero
444 // in the real code. I'm not sure exactly
445 // what should happen in the on-the-fly case,
446 // but go with what would happen from an InsureInit.
451 #endif // #ifdef DACCESS_COMPILE
453 // DACCESS: The MachState pointers are kept as PTR_TADDR so
454 // the host pointers here refer to the appropriate size and
455 // these casts are not a problem.
456 pRD->pEdi = (DWORD*) m_MachState.pEdi();
457 pRD->pEsi = (DWORD*) m_MachState.pEsi();
458 pRD->pEbx = (DWORD*) m_MachState.pEbx();
459 pRD->pEbp = (DWORD*) m_MachState.pEbp();
461 pRD->ControlPC = m_MachState.GetRetAddr();
462 pRD->SP = (DWORD) m_MachState.esp();
464 #endif // WIN64EXCEPTIONS
470 // Confirm that if the machine state was not initialized, then
471 // any unspilled callee saved registers did not change
472 EXTERN_C MachState* STDCALL HelperMethodFrameConfirmState(HelperMethodFrame* frame, void* esiVal, void* ediVal, void* ebxVal, void* ebpVal)
483 MachState* state = frame->MachineState();
485 // if we've already executed this check once for this helper method frame then
486 // we don't do the check again because it is very expensive.
487 if (frame->HaveDoneConfirmStateCheck())
492 // probe to avoid a kazillion violations in the code that follows.
493 BEGIN_DEBUG_ONLY_CODE;
494 if (!state->isValid())
496 frame->InsureInit(false, NULL);
497 _ASSERTE(state->_pEsi != &state->_esi || state->_esi == (TADDR)esiVal);
498 _ASSERTE(state->_pEdi != &state->_edi || state->_edi == (TADDR)ediVal);
499 _ASSERTE(state->_pEbx != &state->_ebx || state->_ebx == (TADDR)ebxVal);
500 _ASSERTE(state->_pEbp != &state->_ebp || state->_ebp == (TADDR)ebpVal);
504 // set that we have executed this check once for this helper method frame.
505 frame->SetHaveDoneConfirmStateCheck();
511 void ExternalMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
523 UpdateRegDisplayHelper(pRD, CbStackPopUsingGCRefMap(GetGCRefMap()));
525 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK ExternalMethodFrane::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
531 void StubDispatchFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
543 PTR_BYTE pGCRefMap = GetGCRefMap();
544 if (pGCRefMap != NULL)
546 UpdateRegDisplayHelper(pRD, CbStackPopUsingGCRefMap(pGCRefMap));
549 if (GetFunction() != NULL)
551 FramedMethodFrame::UpdateRegDisplay(pRD);
555 UpdateRegDisplayHelper(pRD, 0);
557 // If we do not have owning MethodDesc, we need to pretend that
558 // the call happened on the call instruction to get the ESP unwound properly.
560 // This path is hit when we are throwing null reference exception from
561 // code:VSD_ResolveWorker or code:StubDispatchFixupWorker
562 pRD->ControlPC = GetAdjustedCallAddress(pRD->ControlPC);
565 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK StubDispatchFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
570 PCODE StubDispatchFrame::GetReturnAddress()
579 PCODE retAddress = FramedMethodFrame::GetReturnAddress();
580 if (GetFunction() == NULL && GetGCRefMap() == NULL)
582 // See comment in code:StubDispatchFrame::UpdateRegDisplay
583 retAddress = GetAdjustedCallAddress(retAddress);
588 void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
600 pRD->PCTAddr = GetReturnAddressPtr();
602 #ifdef WIN64EXCEPTIONS
604 memcpy(pRD->pCurrentContext, &m_ctx, sizeof(CONTEXT));
607 pRD->ControlPC = m_ctx.Eip;
609 #define ARGUMENT_AND_SCRATCH_REGISTER(regname) pRD->pCurrentContextPointers->regname = &m_ctx.regname;
610 ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
611 #undef ARGUMENT_AND_SCRATCH_REGISTER
613 #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = &m_ctx.regname;
614 ENUM_CALLEE_SAVED_REGISTERS();
615 #undef CALLEE_SAVED_REGISTER
617 pRD->IsCallerContextValid = FALSE;
618 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
620 #else // WIN64EXCEPTIONS
622 // reset pContext; it's only valid for active (top-most) frame
623 pRD->pContext = NULL;
625 CalleeSavedRegisters* regs = GetCalleeSavedRegisters();
627 #define CALLEE_SAVED_REGISTER(regname) pRD->p##regname = (DWORD*) ®s->regname;
628 ENUM_CALLEE_SAVED_REGISTERS();
629 #undef CALLEE_SAVED_REGISTER
632 pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
634 #endif // WIN64EXCEPTIONS
636 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK FaultingExceptionFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
641 void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
647 // We should skip over InlinedCallFrame if it is not active.
648 // It will be part of a JITed method's frame, and the stack-walker
649 // can handle such a case.
650 #ifdef PROFILING_SUPPORTED
651 PRECONDITION(CORProfilerStackSnapshotEnabled() || InlinedCallFrame::FrameHasActiveCall(this));
659 // @TODO: Remove this after the debugger is fixed to avoid stack-walks from bad places
660 // @TODO: This may be still needed for sampling profilers
661 if (!InlinedCallFrame::FrameHasActiveCall(this))
663 LOG((LF_CORDB, LL_ERROR, "WARNING: InlinedCallFrame::UpdateRegDisplay called on inactive frame %p\n", this));
667 DWORD stackArgSize = (DWORD) dac_cast<TADDR>(m_Datum);
669 if (stackArgSize & ~0xFFFF)
671 NDirectMethodDesc * pMD = PTR_NDirectMethodDesc(m_Datum);
673 /* if this is not an NDirect frame, something is really wrong */
675 _ASSERTE(pMD->SanityCheck() && pMD->IsNDirect());
677 stackArgSize = pMD->GetStackArgumentSize();
680 /* The return address is just above the "ESP" */
681 pRD->PCTAddr = PTR_HOST_MEMBER_TADDR(InlinedCallFrame, this,
682 m_pCallerReturnAddress);
684 #ifdef WIN64EXCEPTIONS
686 pRD->IsCallerContextValid = FALSE;
687 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
689 pRD->pCurrentContext->Eip = *PTR_PCODE(pRD->PCTAddr);
690 pRD->pCurrentContext->Esp = (DWORD) dac_cast<TADDR>(m_pCallSiteSP);
691 pRD->pCurrentContext->Ebp = (DWORD) m_pCalleeSavedFP;
693 ClearRegDisplayArgumentAndScratchRegisters(pRD);
695 #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = NULL;
696 ENUM_CALLEE_SAVED_REGISTERS();
697 #undef CALLEE_SAVED_REGISTER
699 pRD->pCurrentContextPointers->Ebp = (DWORD*) &m_pCalleeSavedFP;
701 SyncRegDisplayToCurrentContext(pRD);
703 #else // WIN64EXCEPTIONS
705 // reset pContext; it's only valid for active (top-most) frame
706 pRD->pContext = NULL;
708 pRD->pEbp = (DWORD*) &m_pCalleeSavedFP;
710 pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
711 /* Now we need to pop off the outgoing arguments */
712 pRD->SP = (DWORD) dac_cast<TADDR>(m_pCallSiteSP) + stackArgSize;
714 #endif // WIN64EXCEPTIONS
716 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK InlinedCallFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
721 #ifdef FEATURE_HIJACK
722 //==========================
723 // Resumable Exception Frame
725 TADDR ResumableFrame::GetReturnAddressPtr()
727 LIMITED_METHOD_DAC_CONTRACT;
728 return dac_cast<TADDR>(m_Regs) + offsetof(CONTEXT, Eip);
731 void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
743 pRD->PCTAddr = dac_cast<TADDR>(m_Regs) + offsetof(CONTEXT, Eip);
745 #ifdef WIN64EXCEPTIONS
747 CopyMemory(pRD->pCurrentContext, m_Regs, sizeof(T_CONTEXT));
749 pRD->SP = m_Regs->Esp;
750 pRD->ControlPC = m_Regs->Eip;
752 #define ARGUMENT_AND_SCRATCH_REGISTER(reg) pRD->pCurrentContextPointers->reg = &m_Regs->reg;
753 ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
754 #undef ARGUMENT_AND_SCRATCH_REGISTER
756 #define CALLEE_SAVED_REGISTER(reg) pRD->pCurrentContextPointers->reg = &m_Regs->reg;
757 ENUM_CALLEE_SAVED_REGISTERS();
758 #undef CALLEE_SAVED_REGISTER
760 pRD->IsCallerContextValid = FALSE;
761 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
763 #else // WIN64EXCEPTIONS
765 // reset pContext; it's only valid for active (top-most) frame
766 pRD->pContext = NULL;
768 CONTEXT* pUnwoundContext = m_Regs;
770 #if !defined(DACCESS_COMPILE)
771 // "pContextForUnwind" field is only used on X86 since not only is it initialized just for it,
772 // but its used only under the confines of STACKWALKER_MAY_POP_FRAMES preprocessor define,
773 // which is defined for x86 only (refer to its definition in stackwalk.cpp).
774 if (pRD->pContextForUnwind != NULL)
776 pUnwoundContext = pRD->pContextForUnwind;
778 pUnwoundContext->Eax = m_Regs->Eax;
779 pUnwoundContext->Ecx = m_Regs->Ecx;
780 pUnwoundContext->Edx = m_Regs->Edx;
782 pUnwoundContext->Edi = m_Regs->Edi;
783 pUnwoundContext->Esi = m_Regs->Esi;
784 pUnwoundContext->Ebx = m_Regs->Ebx;
785 pUnwoundContext->Ebp = m_Regs->Ebp;
786 pUnwoundContext->Eip = m_Regs->Eip;
788 #endif // !defined(DACCESS_COMPILE)
790 pRD->pEax = &pUnwoundContext->Eax;
791 pRD->pEcx = &pUnwoundContext->Ecx;
792 pRD->pEdx = &pUnwoundContext->Edx;
794 pRD->pEdi = &pUnwoundContext->Edi;
795 pRD->pEsi = &pUnwoundContext->Esi;
796 pRD->pEbx = &pUnwoundContext->Ebx;
797 pRD->pEbp = &pUnwoundContext->Ebp;
799 pRD->ControlPC = pUnwoundContext->Eip;
801 pRD->SP = m_Regs->Esp;
803 #endif // !WIN64EXCEPTIONS
805 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK ResumableFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
810 // The HijackFrame has to know the registers that are pushed by OnHijackTripThread
811 // -> HijackFrame::UpdateRegDisplay should restore all the registers pushed by OnHijackTripThread
812 void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
822 pRD->PCTAddr = dac_cast<TADDR>(m_Args) + offsetof(HijackArgs, Eip);
824 #ifdef WIN64EXCEPTIONS
826 pRD->IsCallerContextValid = FALSE;
827 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
829 pRD->pCurrentContext->Eip = *PTR_PCODE(pRD->PCTAddr);
830 pRD->pCurrentContext->Esp = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
832 #define RESTORE_REG(reg) { pRD->pCurrentContext->reg = m_Args->reg; pRD->pCurrentContextPointers->reg = &m_Args->reg; }
833 #define CALLEE_SAVED_REGISTER(reg) RESTORE_REG(reg)
834 ENUM_CALLEE_SAVED_REGISTERS();
835 #undef CALLEE_SAVED_REGISTER
837 #define ARGUMENT_AND_SCRATCH_REGISTER(reg) RESTORE_REG(reg)
838 ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
839 #undef ARGUMENT_AND_SCRATCH_REGISTER
842 SyncRegDisplayToCurrentContext(pRD);
844 #else // WIN64EXCEPTIONS
846 // This only describes the top-most frame
847 pRD->pContext = NULL;
849 #define RESTORE_REG(reg) { pRD->p##reg = &m_Args->reg; }
850 #define CALLEE_SAVED_REGISTER(reg) RESTORE_REG(reg)
851 ENUM_CALLEE_SAVED_REGISTERS();
852 #undef CALLEE_SAVED_REGISTER
854 #define ARGUMENT_AND_SCRATCH_REGISTER(reg) RESTORE_REG(reg)
855 ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
856 #undef ARGUMENT_AND_SCRATCH_REGISTER
859 pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
860 pRD->SP = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
862 #endif // WIN64EXCEPTIONS
864 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK HijackFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
867 #endif // FEATURE_HIJACK
869 void PInvokeCalliFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
881 VASigCookie *pVASigCookie = GetVASigCookie();
882 UpdateRegDisplayHelper(pRD, pVASigCookie->sizeOfArgs+sizeof(int));
884 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK PInvokeCalliFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
889 void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
901 pRD->PCTAddr = GetReturnAddressPtr();
903 #ifdef WIN64EXCEPTIONS
905 pRD->IsCallerContextValid = FALSE;
906 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
908 pRD->pCurrentContext->Eip = *PTR_PCODE(pRD->PCTAddr);
909 pRD->pCurrentContext->Esp = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
911 UpdateRegDisplayFromCalleeSavedRegisters(pRD, &m_regs);
912 ClearRegDisplayArgumentAndScratchRegisters(pRD);
914 SyncRegDisplayToCurrentContext(pRD);
918 // reset pContext; it's only valid for active (top-most) frame
919 pRD->pContext = NULL;
921 #define CALLEE_SAVED_REGISTER(regname) pRD->p##regname = (DWORD*) &m_regs.regname;
922 ENUM_CALLEE_SAVED_REGISTERS();
923 #undef CALLEE_SAVED_REGISTER
925 pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
926 pRD->SP = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
930 LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TailCallFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
935 //------------------------------------------------------------------------
936 // This is declared as returning WORD instead of PRD_TYPE because of
937 // header issues with cgencpu.h including dbginterface.h.
938 WORD GetUnpatchedCodeData(LPCBYTE pAddr)
941 #error Make sure this works before porting to platforms other than x86.
946 PRECONDITION(CORDebuggerAttached());
947 PRECONDITION(CheckPointer(pAddr));
951 // Ordering is because x86 is little-endien.
952 BYTE bLow = pAddr[0];
953 BYTE bHigh = pAddr[1];
955 #ifndef DACCESS_COMPILE
956 // Need to make sure that the code we're reading is free of breakpoint patches.
957 PRD_TYPE unpatchedOpcode;
958 if (g_pDebugInterface->CheckGetPatchedOpcode((CORDB_ADDRESS_TYPE *)pAddr,
961 // PRD_TYPE is supposed to be an opaque debugger structure representing data to remove a patch.
962 // Although PRD_TYPE is currently typedef'ed to be a DWORD_PTR, it's actually semantically just a BYTE.
963 // (since a patch on x86 is just an 0xCC instruction).
964 // Ideally, the debugger subsystem would expose a patch-code stripper that returns BYTE/WORD/etc, and
965 // not force us to crack it ourselves here.
966 bLow = (BYTE) unpatchedOpcode;
971 WORD w = bLow + (bHigh << 8);
976 #ifndef DACCESS_COMPILE
978 #if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
979 //-------------------------------------------------------------------------
980 // One-time creation of special prestub to initialize UMEntryThunks.
981 //-------------------------------------------------------------------------
982 Stub *GenerateUMThunkPrestub()
987 POSTCONDITION(CheckPointer(RETVAL));
992 CPUSTUBLINKER *psl = &sl;
994 CodeLabel* rgRareLabels[] = { psl->NewCodeLabel(),
1000 CodeLabel* rgRejoinLabels[] = { psl->NewCodeLabel(),
1001 psl->NewCodeLabel(),
1005 // emit the initial prolog
1006 psl->EmitComMethodStubProlog(UMThkCallFrame::GetMethodFrameVPtr(), rgRareLabels, rgRejoinLabels, FALSE /*Don't profile*/);
1008 // mov ecx, [esi+UMThkCallFrame.pUMEntryThunk]
1009 psl->X86EmitIndexRegLoad(kECX, kESI, UMThkCallFrame::GetOffsetOfUMEntryThunk());
1011 // The call conv is a __stdcall
1012 psl->X86EmitPushReg(kECX);
1014 // call UMEntryThunk::DoRunTimeInit
1015 psl->X86EmitCall(psl->NewExternalCodeLabel((LPVOID)UMEntryThunk::DoRunTimeInit), 4);
1017 // mov ecx, [esi+UMThkCallFrame.pUMEntryThunk]
1018 psl->X86EmitIndexRegLoad(kEAX, kESI, UMThkCallFrame::GetOffsetOfUMEntryThunk());
1020 // lea eax, [eax + UMEntryThunk.m_code] // point to fixedup UMEntryThunk
1021 psl->X86EmitOp(0x8d, kEAX, kEAX,
1022 UMEntryThunk::GetCodeOffset() + UMEntryThunkCode::GetEntryPointOffset());
1024 psl->EmitComMethodStubEpilog(UMThkCallFrame::GetMethodFrameVPtr(), rgRareLabels, rgRejoinLabels, FALSE /*Don't profile*/);
1026 RETURN psl->Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap());
1028 #endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
1030 Stub *GenerateInitPInvokeFrameHelper()
1035 POSTCONDITION(CheckPointer(RETVAL));
1040 CPUSTUBLINKER *psl = &sl;
1042 CORINFO_EE_INFO::InlinedCallFrameInfo FrameInfo;
1043 InlinedCallFrame::GetEEInfo(&FrameInfo);
1045 // EDI contains address of the frame on stack (the frame ptr, not its negspace)
1046 unsigned negSpace = FrameInfo.offsetOfFrameVptr;
1048 // mov esi, GetThread()
1049 psl->X86EmitCurrentThreadFetch(kESI, (1<<kEDI)|(1<<kEBX)|(1<<kECX)|(1<<kEDX));
1051 // mov [edi + FrameInfo.offsetOfGSCookie], GetProcessGSCookie()
1052 psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfGSCookie - negSpace);
1053 psl->Emit32(GetProcessGSCookie());
1055 // mov [edi + FrameInfo.offsetOfFrameVptr], InlinedCallFrame::GetFrameVtable()
1056 psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfFrameVptr - negSpace);
1057 psl->Emit32(InlinedCallFrame::GetMethodFrameVPtr());
1059 // mov eax, [esi + offsetof(Thread, m_pFrame)]
1060 // mov [edi + FrameInfo.offsetOfFrameLink], eax
1061 psl->X86EmitIndexRegLoad(kEAX, kESI, offsetof(Thread, m_pFrame));
1062 psl->X86EmitIndexRegStore(kEDI, FrameInfo.offsetOfFrameLink - negSpace, kEAX);
1064 // mov [edi + FrameInfo.offsetOfCalleeSavedEbp], ebp
1065 psl->X86EmitIndexRegStore(kEDI, FrameInfo.offsetOfCalleeSavedFP - negSpace, kEBP);
1067 // mov [edi + FrameInfo.offsetOfReturnAddress], 0
1068 psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfReturnAddress - negSpace);
1071 // mov [esi + offsetof(Thread, m_pFrame)], edi
1072 psl->X86EmitIndexRegStore(kESI, offsetof(Thread, m_pFrame), kEDI);
1074 // leave current Thread in ESI
1075 psl->X86EmitReturn(0);
1077 // A single process-wide stub that will never unload
1078 RETURN psl->Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap());
1083 #ifdef MDA_SUPPORTED
1085 //-----------------------------------------------------------------------------
1086 Stub *NDirectMethodDesc::GenerateStubForMDA(LPVOID pNativeTarget, Stub *pInnerStub, BOOL fCalledByStub)
1088 STANDARD_VM_CONTRACT;
1091 sl.X86EmitPushEBPframe();
1093 DWORD callConv = (DWORD)(IsThisCall() ? pmCallConvThiscall : (IsStdCall() ? pmCallConvStdcall : pmCallConvCdecl));
1094 _ASSERTE((callConv & StackImbalanceCookie::HAS_FP_RETURN_VALUE) == 0);
1097 if (msig.HasFPReturn())
1099 // check for the HRESULT swapping impl flag
1101 IfFailThrow(GetMDImport()->GetMethodImplProps(GetMemberDef(), NULL, &dwImplFlags));
1103 if (dwImplFlags & miPreserveSig)
1105 // pass a flag to PInvokeStackImbalanceHelper that it should save & restore FPU return value
1106 callConv |= StackImbalanceCookie::HAS_FP_RETURN_VALUE;
1110 // init StackImbalanceCookie
1111 sl.X86EmitPushReg(kEAX); // m_dwSavedEsp (just making space)
1112 sl.X86EmitPushImm32(callConv); // m_callConv
1116 // Re-push the return address as an argument to GetStackSizeForVarArgCall()
1119 // We will be called by another stub that doesn't know the stack size,
1120 // so we need to skip a frame to get to the managed caller.
1121 sl.X86EmitIndexRegLoad(kEAX, kEBP, 0);
1122 sl.X86EmitIndexPush(kEAX, 4);
1126 sl.X86EmitIndexPush(kEBP, 4);
1129 // This will return the number of stack arguments (in DWORDs)
1130 sl.X86EmitCall(sl.NewExternalCodeLabel((LPVOID)GetStackSizeForVarArgCall), 4);
1136 sl.X86EmitPushReg(kEAX); // m_dwStackArgSize
1140 sl.X86EmitPushImm32(GetStackArgumentSize()); // m_dwStackArgSize
1143 LPVOID pTarget = (pInnerStub != NULL ? (LPVOID)pInnerStub->GetEntryPoint() : pNativeTarget);
1144 sl.X86EmitPushImmPtr(pTarget); // m_pTarget
1145 sl.X86EmitPushImmPtr(this); // m_pMD
1147 // stack layout at this point
1150 // | stack arguments | EBP + 8
1151 // +-----------------------+
1152 // | return address | EBP + 4
1153 // +-----------------------+
1154 // | saved EBP | EBP + 0
1155 // +-----------------------+
1156 // | SIC::m_dwSavedEsp |
1157 // | SIC::m_callConv |
1158 // | SIC::m_dwStackArgSize |
1159 // | SIC::m_pTarget |
1160 // | SIC::m_pMD | EBP - 20
1161 // ------------------------
1164 sl.X86EmitCall(sl.NewExternalCodeLabel(PInvokeStackImbalanceHelper), sizeof(StackImbalanceCookie));
1166 // pop StackImbalanceCookie
1167 sl.X86EmitMovSPReg(kEBP);
1169 sl.X86EmitPopReg(kEBP);
1170 sl.X86EmitReturn((IsStdCall() || IsThisCall()) ? GetStackArgumentSize() : 0);
1174 return sl.LinkInterceptor(GetLoaderAllocator()->GetStubHeap(), pInnerStub, pNativeTarget);
1178 return sl.Link(GetLoaderAllocator()->GetStubHeap());
1182 //-----------------------------------------------------------------------------
1184 Stub *COMDelegate::GenerateStubForMDA(MethodDesc *pInvokeMD, MethodDesc *pStubMD, LPVOID pNativeTarget, Stub *pInnerStub)
1186 STANDARD_VM_CONTRACT;
1188 WORD wStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize();
1190 // get unmanaged calling convention from pInvokeMD's metadata
1191 PInvokeStaticSigInfo sigInfo(pInvokeMD);
1192 DWORD callConv = (DWORD)sigInfo.GetCallConv();
1193 _ASSERTE((callConv & StackImbalanceCookie::HAS_FP_RETURN_VALUE) == 0);
1195 MetaSig msig(pInvokeMD);
1196 if (msig.HasFPReturn())
1198 // pass a flag to PInvokeStackImbalanceHelper that it should save & restore FPU return value
1199 callConv |= StackImbalanceCookie::HAS_FP_RETURN_VALUE;
1203 sl.X86EmitPushEBPframe();
1205 LPVOID pTarget = (pInnerStub != NULL ? (LPVOID)pInnerStub->GetEntryPoint() : pNativeTarget);
1207 // init StackImbalanceCookie
1208 sl.X86EmitPushReg(kEAX); // m_dwSavedEsp (just making space)
1209 sl.X86EmitPushImm32(callConv); // m_callConv
1210 sl.X86EmitPushImm32(wStackArgSize); // m_dwStackArgSize
1211 sl.X86EmitPushImmPtr(pTarget); // m_pTarget
1212 sl.X86EmitPushImmPtr(pInvokeMD); // m_pMD
1214 // stack layout at this point
1217 // | stack arguments | EBP + 8
1218 // +-----------------------+
1219 // | return address | EBP + 4
1220 // +-----------------------+
1221 // | saved EBP | EBP + 0
1222 // +-----------------------+
1223 // | SIC::m_dwSavedEsp |
1224 // | SIC::m_callConv |
1225 // | SIC::m_dwStackArgSize |
1226 // | SIC::m_pTarget |
1227 // | SIC::m_pMD | EBP - 20
1228 // ------------------------
1231 sl.X86EmitCall(sl.NewExternalCodeLabel(PInvokeStackImbalanceHelper), sizeof(StackImbalanceCookie));
1233 // pop StackImbalanceCookie
1234 sl.X86EmitMovSPReg(kEBP);
1236 sl.X86EmitPopReg(kEBP);
1237 sl.X86EmitReturn(callConv == pmCallConvCdecl ? 0 : wStackArgSize);
1239 if (pInnerStub != NULL)
1241 return sl.LinkInterceptor(pInnerStub, pNativeTarget);
1245 return sl.Link(); // don't use loader heap as we want to be able to free the stub
1249 #endif // MDA_SUPPORTED
1251 extern "C" VOID STDCALL StubRareEnableWorker(Thread *pThread)
1253 WRAPPER_NO_CONTRACT;
1255 //printf("RareEnable\n");
1256 pThread->RareEnablePreemptiveGC();
1262 // Disable when calling into managed code from a place that fails via Exceptions
1263 extern "C" VOID STDCALL StubRareDisableTHROWWorker(Thread *pThread)
1265 STATIC_CONTRACT_THROWS;
1266 STATIC_CONTRACT_GC_TRIGGERS;
1268 // Do not add a CONTRACT here. We haven't set up SEH. We rely
1269 // on HandleThreadAbort and COMPlusThrowBoot dealing with this situation properly.
1272 // when we start executing here, we are actually in cooperative mode. But we
1273 // haven't synchronized with the barrier to reentry yet. So we are in a highly
1274 // dangerous mode. If we call managed code, we will potentially be active in
1275 // the GC heap, even as GC's are occuring!
1277 // Check for ShutDown scenario. This happens only when we have initiated shutdown
1278 // and someone is trying to call in after the CLR is suspended. In that case, we
1279 // must either raise an unmanaged exception or return an HRESULT, depending on the
1280 // expectations of our caller.
1281 if (!CanRunManagedCode())
1283 // DO NOT IMPROVE THIS EXCEPTION! It cannot be a managed exception. It
1284 // cannot be a real exception object because we cannot execute any managed
1286 pThread->m_fPreemptiveGCDisabled = 0;
1287 COMPlusThrowBoot(E_PROCESS_SHUTDOWN_REENTRY);
1290 // We must do the following in this order, because otherwise we would be constructing
1291 // the exception for the abort without synchronizing with the GC. Also, we have no
1292 // CLR SEH set up, despite the fact that we may throw a ThreadAbortException.
1293 pThread->RareDisablePreemptiveGC();
1294 pThread->HandleThreadAbort();
1298 // Note that this logic is copied below, in PopSEHRecords
1300 VOID __cdecl PopSEHRecords(LPVOID pTargetSP)
1302 // No CONTRACT possible on naked functions
1303 STATIC_CONTRACT_NOTHROW;
1304 STATIC_CONTRACT_GC_NOTRIGGER;
1307 mov ecx, [esp+4] ;; ecx <- pTargetSP
1308 mov eax, fs:[0] ;; get current SEH record
1312 mov eax, [eax] ;; get next SEH record
1319 #endif // FEATURE_PAL
1321 //////////////////////////////////////////////////////////////////////////////
1325 //////////////////////////////////////////////////////////////////////////////
1327 /*********************************************************************/
1328 #ifdef EnC_SUPPORTED
1329 #pragma warning (disable : 4731)
1330 void ResumeAtJit(PCONTEXT pContext, LPVOID oldESP)
1332 // No CONTRACT here, because we can't run the risk of it pushing any SEH into the
1335 STATIC_CONTRACT_NOTHROW;
1336 STATIC_CONTRACT_GC_NOTRIGGER;
1340 __asm mov curESP, esp
1345 _ASSERTE(curESP < (DWORD)(size_t)oldESP);
1346 // should have popped the SEH records by now as stack has been overwritten
1347 _ASSERTE(GetCurrentSEHRecord() > oldESP);
1350 // For the "push Eip, ..., ret"
1351 _ASSERTE(curESP < pContext->Esp - sizeof(DWORD));
1352 pContext->Esp -= sizeof(DWORD);
1357 // Push Eip onto the targetESP, so that the final "ret" will consume it
1358 mov ecx, [ebp]CONTEXT.Esp
1359 mov edx, [ebp]CONTEXT.Eip
1362 // Restore all registers except Esp, Ebp, Eip
1363 mov eax, [ebp]CONTEXT.Eax
1364 mov ebx, [ebp]CONTEXT.Ebx
1365 mov ecx, [ebp]CONTEXT.Ecx
1366 mov edx, [ebp]CONTEXT.Edx
1367 mov esi, [ebp]CONTEXT.Esi
1368 mov edi, [ebp]CONTEXT.Edi
1370 push [ebp]CONTEXT.Esp // pContext->Esp is (targetESP-sizeof(DWORD))
1371 push [ebp]CONTEXT.Ebp
1375 // esp is (targetESP-sizeof(DWORD)), and [esp] is the targetEIP.
1376 // The ret will set eip to targetEIP and esp will be automatically
1377 // incremented to targetESP
1382 #pragma warning (default : 4731)
1383 #endif // !EnC_SUPPORTED
1387 #pragma warning(push)
1388 #pragma warning(disable: 4035)
1389 extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16])
1391 LIMITED_METHOD_CONTRACT
1409 // The following function uses Deterministic Cache Parameter leafs to determine the cache hierarchy information on Prescott & Above platforms.
1410 // This function takes 3 arguments:
1411 // Arg1 is an input to ECX. Used as index to specify which cache level to return infoformation on by CPUID.
1412 // Arg2 is an input to EAX. For deterministic code enumeration, we pass in 4H in arg2.
1413 // Arg3 is a pointer to the return buffer
1414 // No need to check whether or not CPUID is supported because we have already called CPUID with success to come here.
1416 extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16])
1418 LIMITED_METHOD_CONTRACT
1437 extern "C" DWORD __stdcall xmmYmmStateSupport()
1440 STATIC_CONTRACT_NOTHROW;
1441 STATIC_CONTRACT_GC_NOTRIGGER;
1445 mov ecx, 0 ; Specify xcr0
1446 xgetbv ; result in EDX:EAX
1448 cmp eax, 06H ; check OS has enabled both XMM and YMM state support
1458 #pragma warning(pop)
1460 #else // !FEATURE_PAL
1462 extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16])
1465 __asm(" xor %%ecx, %%ecx\n" \
1467 " mov %%eax, 0(%[result])\n" \
1468 " mov %%ebx, 4(%[result])\n" \
1469 " mov %%ecx, 8(%[result])\n" \
1470 " mov %%edx, 12(%[result])\n" \
1471 : "=a"(eax) /*output in eax*/\
1472 : "a"(arg), [result]"r"(result) /*inputs - arg in eax, result in any register*/\
1473 : "eax", "ebx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */
1478 extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16])
1482 " mov %%eax, 0(%[result])\n" \
1483 " mov %%ebx, 4(%[result])\n" \
1484 " mov %%ecx, 8(%[result])\n" \
1485 " mov %%edx, 12(%[result])\n" \
1486 : "=a"(eax) /*output in eax*/\
1487 : "c"(arg1), "a"(arg2), [result]"r"(result) /*inputs - arg1 in ecx, arg2 in eax, result in any register*/\
1488 : "eax", "ebx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */
1493 extern "C" DWORD __stdcall xmmYmmStateSupport()
1497 : "=a"(eax) /*output in eax*/\
1498 : "c"(0) /*inputs - 0 in ecx*/\
1499 : "eax", "edx" /* registers that are clobbered*/
1501 // check OS has enabled both XMM and YMM state support
1502 return ((eax & 0x06) == 0x06) ? 1 : 0;
1505 #endif // !FEATURE_PAL
1507 // This function returns the number of logical processors on a given physical chip. If it cannot
1508 // determine the number of logical cpus, or the machine is not populated uniformly with the same
1509 // type of processors, this function returns 1.
1510 DWORD GetLogicalCpuCount()
1512 // No CONTRACT possible because GetLogicalCpuCount uses SEH
1514 STATIC_CONTRACT_THROWS;
1515 STATIC_CONTRACT_GC_NOTRIGGER;
1517 static DWORD val = 0;
1519 // cache value for later re-use
1525 struct Param : DefaultCatchFilterParam
1529 param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER;
1532 PAL_TRY(Param *, pParam, ¶m)
1534 unsigned char buffer[16];
1535 DWORD* dwBuffer = NULL;
1537 DWORD maxCpuId = getcpuid(0, buffer);
1542 dwBuffer = (DWORD*)buffer;
1544 if (dwBuffer[1] == 'uneG') {
1545 if (dwBuffer[3] == 'Ieni') {
1546 if (dwBuffer[2] == 'letn') { // get SMT/multicore enumeration for Intel EM64T
1548 // TODO: Currently GetLogicalCpuCountFromOS() and GetLogicalCpuCountFallback() are broken on
1549 // multi-core processor, but we never call into those two functions since we don't halve the
1550 // gen0size when it's prescott and above processor. We keep the old version here for earlier
1551 // generation system(Northwood based), perf data suggests on those systems, halve gen0 size
1552 // still boost the performance(ex:Biztalk boosts about 17%). So on earlier systems(Northwood)
1553 // based, we still go ahead and halve gen0 size. The logic in GetLogicalCpuCountFromOS()
1554 // and GetLogicalCpuCountFallback() works fine for those earlier generation systems.
1555 // If it's a Prescott and above processor or Multi-core, perf data suggests not to halve gen0
1556 // size at all gives us overall better performance.
1557 // This is going to be fixed with a new version in orcas time frame.
1559 if( (maxCpuId > 3) && (maxCpuId < 0x80000000) )
1562 val = GetLogicalCpuCountFromOS(); //try to obtain HT enumeration from OS API
1565 pParam->retVal = val; // OS API HT enumeration successful, we are Done
1569 val = GetLogicalCpuCountFallback(); // OS API failed, Fallback to HT enumeration using CPUID
1571 pParam->retVal = val;
1577 PAL_EXCEPT_FILTER(DefaultCatchFilter)
1587 return param.retVal;
1590 void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
1592 LIMITED_METHOD_CONTRACT;
1595 m_alignpad[0] = X86_INSTR_INT3;
1596 m_alignpad[1] = X86_INSTR_INT3;
1598 m_movEAX = X86_INSTR_MOV_EAX_IMM32;
1599 m_uet = pvSecretParam;
1600 m_jmp = X86_INSTR_JMP_REL32;
1601 m_execstub = (BYTE*) ((pTargetCode) - (4+((BYTE*)&m_execstub)));
1603 FlushInstructionCache(GetCurrentProcess(),GetEntryPoint(),sizeof(UMEntryThunkCode));
1606 UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback)
1608 LIMITED_METHOD_CONTRACT;
1610 if (*((BYTE*)pCallback) != X86_INSTR_MOV_EAX_IMM32 ||
1611 ( ((size_t)pCallback) & 3) != 2) {
1614 return *(UMEntryThunk**)( 1 + (BYTE*)pCallback );
1617 BOOL DoesSlotCallPrestub(PCODE pCode)
1623 PRECONDITION(pCode != NULL);
1624 PRECONDITION(pCode != GetPreStubEntryPoint());
1627 // x86 has the following possible sequences for prestub logic:
1628 // 1. slot -> temporary entrypoint -> prestub
1629 // 2. slot -> precode -> prestub
1630 // 3. slot -> precode -> jumprel32 (NGEN case) -> prestub
1632 #ifdef HAS_COMPACT_ENTRYPOINTS
1633 if (MethodDescChunk::GetMethodDescFromCompactEntryPoint(pCode, TRUE) != NULL)
1637 #endif // HAS_COMPACT_ENTRYPOINTS
1639 if (!IS_ALIGNED(pCode, PRECODE_ALIGNMENT))
1644 #ifdef HAS_FIXUP_PRECODE
1645 if (*PTR_BYTE(pCode) == X86_INSTR_CALL_REL32)
1647 // Note that call could have been patched to jmp in the meantime
1648 pCode = rel32Decode(pCode+1);
1651 if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) {
1652 pCode = rel32Decode(pCode+1);
1655 return pCode == (TADDR)PrecodeFixupThunk;
1659 if (*PTR_BYTE(pCode) != X86_INSTR_MOV_EAX_IMM32 ||
1660 *PTR_BYTE(pCode+5) != X86_INSTR_MOV_RM_R ||
1661 *PTR_BYTE(pCode+7) != X86_INSTR_JMP_REL32)
1665 pCode = rel32Decode(pCode+8);
1668 if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) {
1669 pCode = rel32Decode(pCode+1);
1672 return pCode == GetPreStubEntryPoint();
1675 //==========================================================================================
1676 // In NGen image, virtual slots inherited from cross-module dependencies point to jump thunks.
1677 // These jump thunk initially point to VirtualMethodFixupStub which transfers control here.
1678 // This method 'VirtualMethodFixupWorker' will patch the jump thunk to point to the actual
1679 // inherited method body after we have execute the precode and a stable entry point.
1681 EXTERN_C PVOID STDCALL VirtualMethodFixupWorker(Object * pThisPtr, CORCOMPILE_VIRTUAL_IMPORT_THUNK *pThunk)
1692 _ASSERTE(pThisPtr != NULL);
1693 VALIDATEOBJECT(pThisPtr);
1695 MethodTable * pMT = pThisPtr->GetTrueMethodTable();
1697 WORD slotNumber = pThunk->slotNum;
1698 _ASSERTE(slotNumber != (WORD)-1);
1700 PCODE pCode = pMT->GetRestoredSlot(slotNumber);
1702 if (!DoesSlotCallPrestub(pCode))
1704 // Skip fixup precode jump for better perf
1705 PCODE pDirectTarget = Precode::TryToSkipFixupPrecode(pCode);
1706 if (pDirectTarget != NULL)
1707 pCode = pDirectTarget;
1709 INT64 oldValue = *(INT64*)pThunk;
1710 BYTE* pOldValue = (BYTE*)&oldValue;
1712 if (pOldValue[0] == X86_INSTR_CALL_REL32)
1714 INT64 newValue = oldValue;
1715 BYTE* pNewValue = (BYTE*)&newValue;
1716 pNewValue[0] = X86_INSTR_JMP_REL32;
1718 INT_PTR pcRelOffset = (BYTE*)pCode - &pThunk->callJmp[5];
1719 *(INT32 *)(&pNewValue[1]) = (INT32) pcRelOffset;
1721 _ASSERTE(IS_ALIGNED(pThunk, sizeof(INT64)));
1722 if (EnsureWritableExecutablePagesNoThrow(pThunk, sizeof(INT64)))
1723 FastInterlockCompareExchangeLong((INT64*)pThunk, newValue, oldValue);
1725 FlushInstructionCache(GetCurrentProcess(), pThunk, 8);
1729 return PVOID(pCode);
1733 #ifdef FEATURE_READYTORUN
1736 // Allocation of dynamic helpers
1739 #define DYNAMIC_HELPER_ALIGNMENT sizeof(TADDR)
1741 #define BEGIN_DYNAMIC_HELPER_EMIT(size) \
1743 SIZE_T cbAligned = ALIGN_UP(cb, DYNAMIC_HELPER_ALIGNMENT); \
1744 BYTE * pStart = (BYTE *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \
1747 #define END_DYNAMIC_HELPER_EMIT() \
1748 _ASSERTE(pStart + cb == p); \
1749 while (p < pStart + cbAligned) *p++ = X86_INSTR_INT3; \
1750 ClrFlushInstructionCache(pStart, cbAligned); \
1751 return (PCODE)pStart
1753 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1755 STANDARD_VM_CONTRACT;
1757 BEGIN_DYNAMIC_HELPER_EMIT(10);
1759 *p++ = 0xB9; // mov ecx, XXXXXX
1760 *(INT32 *)p = (INT32)arg;
1763 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1764 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1767 END_DYNAMIC_HELPER_EMIT();
1770 void DynamicHelpers::EmitHelperWithArg(BYTE*& p, LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1775 PRECONDITION(p != NULL && target != NULL);
1779 // Move an an argument into the second argument register and jump to a target function.
1781 *p++ = 0xBA; // mov edx, XXXXXX
1782 *(INT32 *)p = (INT32)arg;
1785 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1786 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1790 PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1792 BEGIN_DYNAMIC_HELPER_EMIT(10);
1794 EmitHelperWithArg(p, pAllocator, arg, target);
1796 END_DYNAMIC_HELPER_EMIT();
1799 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1801 BEGIN_DYNAMIC_HELPER_EMIT(15);
1803 *p++ = 0xB9; // mov ecx, XXXXXX
1804 *(INT32 *)p = (INT32)arg;
1807 *p++ = 0xBA; // mov edx, XXXXXX
1808 *(INT32 *)p = (INT32)arg2;
1811 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1812 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1815 END_DYNAMIC_HELPER_EMIT();
1818 PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1820 BEGIN_DYNAMIC_HELPER_EMIT(12);
1822 *(UINT16 *)p = 0xD18B; // mov edx, ecx
1825 *p++ = 0xB9; // mov ecx, XXXXXX
1826 *(INT32 *)p = (INT32)arg;
1829 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1830 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1833 END_DYNAMIC_HELPER_EMIT();
1836 PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator)
1838 BEGIN_DYNAMIC_HELPER_EMIT(1);
1842 END_DYNAMIC_HELPER_EMIT();
1845 PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg)
1847 BEGIN_DYNAMIC_HELPER_EMIT(6);
1849 *p++ = 0xB8; // mov eax, XXXXXX
1850 *(INT32 *)p = (INT32)arg;
1855 END_DYNAMIC_HELPER_EMIT();
1858 PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset)
1860 BEGIN_DYNAMIC_HELPER_EMIT((offset != 0) ? 9 : 6);
1862 *p++ = 0xA1; // mov eax, [XXXXXX]
1863 *(INT32 *)p = (INT32)arg;
1868 // add eax, <offset>
1876 END_DYNAMIC_HELPER_EMIT();
1879 EXTERN_C VOID DynamicHelperArgsStub();
1881 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1884 BEGIN_DYNAMIC_HELPER_EMIT(18);
1886 BEGIN_DYNAMIC_HELPER_EMIT(12);
1907 *(INT32 *)p = target;
1914 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1916 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, (PCODE)DynamicHelperArgsStub);
1918 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1922 END_DYNAMIC_HELPER_EMIT();
1925 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1928 BEGIN_DYNAMIC_HELPER_EMIT(23);
1930 BEGIN_DYNAMIC_HELPER_EMIT(17);
1956 *(INT32 *)p = target;
1963 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1965 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, (PCODE)DynamicHelperArgsStub);
1967 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1971 END_DYNAMIC_HELPER_EMIT();
1974 PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule)
1976 STANDARD_VM_CONTRACT;
1978 PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
1979 GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
1980 GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
1982 GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
1983 pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
1984 pArgs->signature = pLookup->signature;
1985 pArgs->module = (CORINFO_MODULE_HANDLE)pModule;
1987 // It's available only via the run-time helper function
1988 if (pLookup->indirections == CORINFO_USEHELPER)
1990 BEGIN_DYNAMIC_HELPER_EMIT(10);
1992 // ecx contains the generic context parameter
1994 // jmp helperAddress
1995 EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
1997 END_DYNAMIC_HELPER_EMIT();
2001 int indirectionsSize = 0;
2002 for (WORD i = 0; i < pLookup->indirections; i++)
2003 indirectionsSize += (pLookup->offsets[i] >= 0x80 ? 6 : 3);
2005 int codeSize = indirectionsSize + (pLookup->testForNull ? 21 : 3);
2007 BEGIN_DYNAMIC_HELPER_EMIT(codeSize);
2009 if (pLookup->testForNull)
2011 // ecx contains the generic context parameter. Save a copy of it in the eax register
2013 *(UINT16*)p = 0xc889; p += 2;
2016 for (WORD i = 0; i < pLookup->indirections; i++)
2018 // mov ecx,qword ptr [ecx+offset]
2019 if (pLookup->offsets[i] >= 0x80)
2021 *(UINT16*)p = 0x898b; p += 2;
2022 *(UINT32*)p = (UINT32)pLookup->offsets[i]; p += 4;
2026 *(UINT16*)p = 0x498b; p += 2;
2027 *p++ = (BYTE)pLookup->offsets[i];
2031 // No null test required
2032 if (!pLookup->testForNull)
2034 // No fixups needed for R2R
2037 *(UINT16*)p = 0xc889; p += 2;
2042 // ecx contains the value of the dictionary slot entry
2044 _ASSERTE(pLookup->indirections != 0);
2047 *(UINT16*)p = 0xc985; p += 2;
2049 // je 'HELPER_CALL' (a jump of 3 bytes)
2050 *(UINT16*)p = 0x0374; p += 2;
2053 *(UINT16*)p = 0xc889; p += 2;
2058 // Put the generic context back into rcx (was previously saved in eax)
2060 *(UINT16*)p = 0xc189; p += 2;
2063 // jmp helperAddress
2064 EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2068 END_DYNAMIC_HELPER_EMIT();
2072 #endif // FEATURE_READYTORUN
2075 #endif // DACCESS_COMPILE