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 #ifdef FEATURE_READYTORUN
936 void DynamicHelperFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
939 UpdateRegDisplayHelper(pRD, 0);
941 #endif // FEATURE_READYTORUN
943 //------------------------------------------------------------------------
944 // This is declared as returning WORD instead of PRD_TYPE because of
945 // header issues with cgencpu.h including dbginterface.h.
946 WORD GetUnpatchedCodeData(LPCBYTE pAddr)
949 #error Make sure this works before porting to platforms other than x86.
954 PRECONDITION(CORDebuggerAttached());
955 PRECONDITION(CheckPointer(pAddr));
959 // Ordering is because x86 is little-endien.
960 BYTE bLow = pAddr[0];
961 BYTE bHigh = pAddr[1];
963 #ifndef DACCESS_COMPILE
964 // Need to make sure that the code we're reading is free of breakpoint patches.
965 PRD_TYPE unpatchedOpcode;
966 if (g_pDebugInterface->CheckGetPatchedOpcode((CORDB_ADDRESS_TYPE *)pAddr,
969 // PRD_TYPE is supposed to be an opaque debugger structure representing data to remove a patch.
970 // Although PRD_TYPE is currently typedef'ed to be a DWORD_PTR, it's actually semantically just a BYTE.
971 // (since a patch on x86 is just an 0xCC instruction).
972 // Ideally, the debugger subsystem would expose a patch-code stripper that returns BYTE/WORD/etc, and
973 // not force us to crack it ourselves here.
974 bLow = (BYTE) unpatchedOpcode;
979 WORD w = bLow + (bHigh << 8);
984 #ifndef DACCESS_COMPILE
986 #if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
987 //-------------------------------------------------------------------------
988 // One-time creation of special prestub to initialize UMEntryThunks.
989 //-------------------------------------------------------------------------
990 Stub *GenerateUMThunkPrestub()
995 POSTCONDITION(CheckPointer(RETVAL));
1000 CPUSTUBLINKER *psl = &sl;
1002 CodeLabel* rgRareLabels[] = { psl->NewCodeLabel(),
1003 psl->NewCodeLabel(),
1008 CodeLabel* rgRejoinLabels[] = { psl->NewCodeLabel(),
1009 psl->NewCodeLabel(),
1013 // emit the initial prolog
1014 psl->EmitComMethodStubProlog(UMThkCallFrame::GetMethodFrameVPtr(), rgRareLabels, rgRejoinLabels, FALSE /*Don't profile*/);
1016 // mov ecx, [esi+UMThkCallFrame.pUMEntryThunk]
1017 psl->X86EmitIndexRegLoad(kECX, kESI, UMThkCallFrame::GetOffsetOfUMEntryThunk());
1019 // The call conv is a __stdcall
1020 psl->X86EmitPushReg(kECX);
1022 // call UMEntryThunk::DoRunTimeInit
1023 psl->X86EmitCall(psl->NewExternalCodeLabel((LPVOID)UMEntryThunk::DoRunTimeInit), 4);
1025 // mov ecx, [esi+UMThkCallFrame.pUMEntryThunk]
1026 psl->X86EmitIndexRegLoad(kEAX, kESI, UMThkCallFrame::GetOffsetOfUMEntryThunk());
1028 // lea eax, [eax + UMEntryThunk.m_code] // point to fixedup UMEntryThunk
1029 psl->X86EmitOp(0x8d, kEAX, kEAX,
1030 UMEntryThunk::GetCodeOffset() + UMEntryThunkCode::GetEntryPointOffset());
1032 psl->EmitComMethodStubEpilog(UMThkCallFrame::GetMethodFrameVPtr(), rgRareLabels, rgRejoinLabels, FALSE /*Don't profile*/);
1034 RETURN psl->Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap());
1036 #endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
1038 Stub *GenerateInitPInvokeFrameHelper()
1043 POSTCONDITION(CheckPointer(RETVAL));
1048 CPUSTUBLINKER *psl = &sl;
1050 CORINFO_EE_INFO::InlinedCallFrameInfo FrameInfo;
1051 InlinedCallFrame::GetEEInfo(&FrameInfo);
1053 // EDI contains address of the frame on stack (the frame ptr, not its negspace)
1054 unsigned negSpace = FrameInfo.offsetOfFrameVptr;
1056 // mov esi, GetThread()
1057 psl->X86EmitCurrentThreadFetch(kESI, (1<<kEDI)|(1<<kEBX)|(1<<kECX)|(1<<kEDX));
1059 // mov [edi + FrameInfo.offsetOfGSCookie], GetProcessGSCookie()
1060 psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfGSCookie - negSpace);
1061 psl->Emit32(GetProcessGSCookie());
1063 // mov [edi + FrameInfo.offsetOfFrameVptr], InlinedCallFrame::GetFrameVtable()
1064 psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfFrameVptr - negSpace);
1065 psl->Emit32(InlinedCallFrame::GetMethodFrameVPtr());
1067 // mov eax, [esi + offsetof(Thread, m_pFrame)]
1068 // mov [edi + FrameInfo.offsetOfFrameLink], eax
1069 psl->X86EmitIndexRegLoad(kEAX, kESI, offsetof(Thread, m_pFrame));
1070 psl->X86EmitIndexRegStore(kEDI, FrameInfo.offsetOfFrameLink - negSpace, kEAX);
1072 // mov [edi + FrameInfo.offsetOfCalleeSavedEbp], ebp
1073 psl->X86EmitIndexRegStore(kEDI, FrameInfo.offsetOfCalleeSavedFP - negSpace, kEBP);
1075 // mov [edi + FrameInfo.offsetOfReturnAddress], 0
1076 psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfReturnAddress - negSpace);
1079 // mov [esi + offsetof(Thread, m_pFrame)], edi
1080 psl->X86EmitIndexRegStore(kESI, offsetof(Thread, m_pFrame), kEDI);
1082 // leave current Thread in ESI
1083 psl->X86EmitReturn(0);
1085 // A single process-wide stub that will never unload
1086 RETURN psl->Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap());
1091 #ifdef MDA_SUPPORTED
1093 //-----------------------------------------------------------------------------
1094 Stub *NDirectMethodDesc::GenerateStubForMDA(LPVOID pNativeTarget, Stub *pInnerStub, BOOL fCalledByStub)
1096 STANDARD_VM_CONTRACT;
1099 sl.X86EmitPushEBPframe();
1101 DWORD callConv = (DWORD)(IsThisCall() ? pmCallConvThiscall : (IsStdCall() ? pmCallConvStdcall : pmCallConvCdecl));
1102 _ASSERTE((callConv & StackImbalanceCookie::HAS_FP_RETURN_VALUE) == 0);
1105 if (msig.HasFPReturn())
1107 // check for the HRESULT swapping impl flag
1109 IfFailThrow(GetMDImport()->GetMethodImplProps(GetMemberDef(), NULL, &dwImplFlags));
1111 if (dwImplFlags & miPreserveSig)
1113 // pass a flag to PInvokeStackImbalanceHelper that it should save & restore FPU return value
1114 callConv |= StackImbalanceCookie::HAS_FP_RETURN_VALUE;
1118 // init StackImbalanceCookie
1119 sl.X86EmitPushReg(kEAX); // m_dwSavedEsp (just making space)
1120 sl.X86EmitPushImm32(callConv); // m_callConv
1124 // Re-push the return address as an argument to GetStackSizeForVarArgCall()
1127 // We will be called by another stub that doesn't know the stack size,
1128 // so we need to skip a frame to get to the managed caller.
1129 sl.X86EmitIndexRegLoad(kEAX, kEBP, 0);
1130 sl.X86EmitIndexPush(kEAX, 4);
1134 sl.X86EmitIndexPush(kEBP, 4);
1137 // This will return the number of stack arguments (in DWORDs)
1138 sl.X86EmitCall(sl.NewExternalCodeLabel((LPVOID)GetStackSizeForVarArgCall), 4);
1144 sl.X86EmitPushReg(kEAX); // m_dwStackArgSize
1148 sl.X86EmitPushImm32(GetStackArgumentSize()); // m_dwStackArgSize
1151 LPVOID pTarget = (pInnerStub != NULL ? (LPVOID)pInnerStub->GetEntryPoint() : pNativeTarget);
1152 sl.X86EmitPushImmPtr(pTarget); // m_pTarget
1153 sl.X86EmitPushImmPtr(this); // m_pMD
1155 // stack layout at this point
1158 // | stack arguments | EBP + 8
1159 // +-----------------------+
1160 // | return address | EBP + 4
1161 // +-----------------------+
1162 // | saved EBP | EBP + 0
1163 // +-----------------------+
1164 // | SIC::m_dwSavedEsp |
1165 // | SIC::m_callConv |
1166 // | SIC::m_dwStackArgSize |
1167 // | SIC::m_pTarget |
1168 // | SIC::m_pMD | EBP - 20
1169 // ------------------------
1172 sl.X86EmitCall(sl.NewExternalCodeLabel(PInvokeStackImbalanceHelper), sizeof(StackImbalanceCookie));
1174 // pop StackImbalanceCookie
1175 sl.X86EmitMovSPReg(kEBP);
1177 sl.X86EmitPopReg(kEBP);
1178 sl.X86EmitReturn((IsStdCall() || IsThisCall()) ? GetStackArgumentSize() : 0);
1182 return sl.LinkInterceptor(GetLoaderAllocator()->GetStubHeap(), pInnerStub, pNativeTarget);
1186 return sl.Link(GetLoaderAllocator()->GetStubHeap());
1190 //-----------------------------------------------------------------------------
1192 Stub *COMDelegate::GenerateStubForMDA(MethodDesc *pInvokeMD, MethodDesc *pStubMD, LPVOID pNativeTarget, Stub *pInnerStub)
1194 STANDARD_VM_CONTRACT;
1196 WORD wStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize();
1198 // get unmanaged calling convention from pInvokeMD's metadata
1199 PInvokeStaticSigInfo sigInfo(pInvokeMD);
1200 DWORD callConv = (DWORD)sigInfo.GetCallConv();
1201 _ASSERTE((callConv & StackImbalanceCookie::HAS_FP_RETURN_VALUE) == 0);
1203 MetaSig msig(pInvokeMD);
1204 if (msig.HasFPReturn())
1206 // pass a flag to PInvokeStackImbalanceHelper that it should save & restore FPU return value
1207 callConv |= StackImbalanceCookie::HAS_FP_RETURN_VALUE;
1211 sl.X86EmitPushEBPframe();
1213 LPVOID pTarget = (pInnerStub != NULL ? (LPVOID)pInnerStub->GetEntryPoint() : pNativeTarget);
1215 // init StackImbalanceCookie
1216 sl.X86EmitPushReg(kEAX); // m_dwSavedEsp (just making space)
1217 sl.X86EmitPushImm32(callConv); // m_callConv
1218 sl.X86EmitPushImm32(wStackArgSize); // m_dwStackArgSize
1219 sl.X86EmitPushImmPtr(pTarget); // m_pTarget
1220 sl.X86EmitPushImmPtr(pInvokeMD); // m_pMD
1222 // stack layout at this point
1225 // | stack arguments | EBP + 8
1226 // +-----------------------+
1227 // | return address | EBP + 4
1228 // +-----------------------+
1229 // | saved EBP | EBP + 0
1230 // +-----------------------+
1231 // | SIC::m_dwSavedEsp |
1232 // | SIC::m_callConv |
1233 // | SIC::m_dwStackArgSize |
1234 // | SIC::m_pTarget |
1235 // | SIC::m_pMD | EBP - 20
1236 // ------------------------
1239 sl.X86EmitCall(sl.NewExternalCodeLabel(PInvokeStackImbalanceHelper), sizeof(StackImbalanceCookie));
1241 // pop StackImbalanceCookie
1242 sl.X86EmitMovSPReg(kEBP);
1244 sl.X86EmitPopReg(kEBP);
1245 sl.X86EmitReturn(callConv == pmCallConvCdecl ? 0 : wStackArgSize);
1247 if (pInnerStub != NULL)
1249 return sl.LinkInterceptor(pInnerStub, pNativeTarget);
1253 return sl.Link(); // don't use loader heap as we want to be able to free the stub
1257 #endif // MDA_SUPPORTED
1259 extern "C" VOID STDCALL StubRareEnableWorker(Thread *pThread)
1261 WRAPPER_NO_CONTRACT;
1263 //printf("RareEnable\n");
1264 pThread->RareEnablePreemptiveGC();
1270 // Disable when calling into managed code from a place that fails via Exceptions
1271 extern "C" VOID STDCALL StubRareDisableTHROWWorker(Thread *pThread)
1273 STATIC_CONTRACT_THROWS;
1274 STATIC_CONTRACT_GC_TRIGGERS;
1276 // Do not add a CONTRACT here. We haven't set up SEH. We rely
1277 // on HandleThreadAbort and COMPlusThrowBoot dealing with this situation properly.
1280 // when we start executing here, we are actually in cooperative mode. But we
1281 // haven't synchronized with the barrier to reentry yet. So we are in a highly
1282 // dangerous mode. If we call managed code, we will potentially be active in
1283 // the GC heap, even as GC's are occuring!
1285 // Check for ShutDown scenario. This happens only when we have initiated shutdown
1286 // and someone is trying to call in after the CLR is suspended. In that case, we
1287 // must either raise an unmanaged exception or return an HRESULT, depending on the
1288 // expectations of our caller.
1289 if (!CanRunManagedCode())
1291 // DO NOT IMPROVE THIS EXCEPTION! It cannot be a managed exception. It
1292 // cannot be a real exception object because we cannot execute any managed
1294 pThread->m_fPreemptiveGCDisabled = 0;
1295 COMPlusThrowBoot(E_PROCESS_SHUTDOWN_REENTRY);
1298 // We must do the following in this order, because otherwise we would be constructing
1299 // the exception for the abort without synchronizing with the GC. Also, we have no
1300 // CLR SEH set up, despite the fact that we may throw a ThreadAbortException.
1301 pThread->RareDisablePreemptiveGC();
1302 pThread->HandleThreadAbort();
1306 // Note that this logic is copied below, in PopSEHRecords
1308 VOID __cdecl PopSEHRecords(LPVOID pTargetSP)
1310 // No CONTRACT possible on naked functions
1311 STATIC_CONTRACT_NOTHROW;
1312 STATIC_CONTRACT_GC_NOTRIGGER;
1315 mov ecx, [esp+4] ;; ecx <- pTargetSP
1316 mov eax, fs:[0] ;; get current SEH record
1320 mov eax, [eax] ;; get next SEH record
1327 #endif // FEATURE_PAL
1329 //////////////////////////////////////////////////////////////////////////////
1333 //////////////////////////////////////////////////////////////////////////////
1335 /*********************************************************************/
1336 #ifdef EnC_SUPPORTED
1337 #pragma warning (disable : 4731)
1338 void ResumeAtJit(PCONTEXT pContext, LPVOID oldESP)
1340 // No CONTRACT here, because we can't run the risk of it pushing any SEH into the
1343 STATIC_CONTRACT_NOTHROW;
1344 STATIC_CONTRACT_GC_NOTRIGGER;
1348 __asm mov curESP, esp
1353 _ASSERTE(curESP < (DWORD)(size_t)oldESP);
1354 // should have popped the SEH records by now as stack has been overwritten
1355 _ASSERTE(GetCurrentSEHRecord() > oldESP);
1358 // For the "push Eip, ..., ret"
1359 _ASSERTE(curESP < pContext->Esp - sizeof(DWORD));
1360 pContext->Esp -= sizeof(DWORD);
1365 // Push Eip onto the targetESP, so that the final "ret" will consume it
1366 mov ecx, [ebp]CONTEXT.Esp
1367 mov edx, [ebp]CONTEXT.Eip
1370 // Restore all registers except Esp, Ebp, Eip
1371 mov eax, [ebp]CONTEXT.Eax
1372 mov ebx, [ebp]CONTEXT.Ebx
1373 mov ecx, [ebp]CONTEXT.Ecx
1374 mov edx, [ebp]CONTEXT.Edx
1375 mov esi, [ebp]CONTEXT.Esi
1376 mov edi, [ebp]CONTEXT.Edi
1378 push [ebp]CONTEXT.Esp // pContext->Esp is (targetESP-sizeof(DWORD))
1379 push [ebp]CONTEXT.Ebp
1383 // esp is (targetESP-sizeof(DWORD)), and [esp] is the targetEIP.
1384 // The ret will set eip to targetEIP and esp will be automatically
1385 // incremented to targetESP
1390 #pragma warning (default : 4731)
1391 #endif // !EnC_SUPPORTED
1395 #pragma warning(push)
1396 #pragma warning(disable: 4035)
1397 extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16])
1399 LIMITED_METHOD_CONTRACT
1417 // The following function uses Deterministic Cache Parameter leafs to determine the cache hierarchy information on Prescott & Above platforms.
1418 // This function takes 3 arguments:
1419 // Arg1 is an input to ECX. Used as index to specify which cache level to return infoformation on by CPUID.
1420 // Arg2 is an input to EAX. For deterministic code enumeration, we pass in 4H in arg2.
1421 // Arg3 is a pointer to the return buffer
1422 // No need to check whether or not CPUID is supported because we have already called CPUID with success to come here.
1424 extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16])
1426 LIMITED_METHOD_CONTRACT
1445 extern "C" DWORD __stdcall xmmYmmStateSupport()
1448 STATIC_CONTRACT_NOTHROW;
1449 STATIC_CONTRACT_GC_NOTRIGGER;
1453 mov ecx, 0 ; Specify xcr0
1454 xgetbv ; result in EDX:EAX
1456 cmp eax, 06H ; check OS has enabled both XMM and YMM state support
1466 #pragma warning(pop)
1468 #else // !FEATURE_PAL
1470 extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16])
1473 __asm(" xor %%ecx, %%ecx\n" \
1475 " mov %%eax, 0(%[result])\n" \
1476 " mov %%ebx, 4(%[result])\n" \
1477 " mov %%ecx, 8(%[result])\n" \
1478 " mov %%edx, 12(%[result])\n" \
1479 : "=a"(eax) /*output in eax*/\
1480 : "a"(arg), [result]"r"(result) /*inputs - arg in eax, result in any register*/\
1481 : "eax", "ebx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */
1486 extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16])
1490 " mov %%eax, 0(%[result])\n" \
1491 " mov %%ebx, 4(%[result])\n" \
1492 " mov %%ecx, 8(%[result])\n" \
1493 " mov %%edx, 12(%[result])\n" \
1494 : "=a"(eax) /*output in eax*/\
1495 : "c"(arg1), "a"(arg2), [result]"r"(result) /*inputs - arg1 in ecx, arg2 in eax, result in any register*/\
1496 : "eax", "ebx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */
1501 extern "C" DWORD __stdcall xmmYmmStateSupport()
1505 : "=a"(eax) /*output in eax*/\
1506 : "c"(0) /*inputs - 0 in ecx*/\
1507 : "eax", "edx" /* registers that are clobbered*/
1509 // check OS has enabled both XMM and YMM state support
1510 return ((eax & 0x06) == 0x06) ? 1 : 0;
1513 #endif // !FEATURE_PAL
1515 // This function returns the number of logical processors on a given physical chip. If it cannot
1516 // determine the number of logical cpus, or the machine is not populated uniformly with the same
1517 // type of processors, this function returns 1.
1518 DWORD GetLogicalCpuCount()
1520 // No CONTRACT possible because GetLogicalCpuCount uses SEH
1522 STATIC_CONTRACT_THROWS;
1523 STATIC_CONTRACT_GC_NOTRIGGER;
1525 static DWORD val = 0;
1527 // cache value for later re-use
1533 struct Param : DefaultCatchFilterParam
1537 param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER;
1540 PAL_TRY(Param *, pParam, ¶m)
1542 unsigned char buffer[16];
1543 DWORD* dwBuffer = NULL;
1545 DWORD maxCpuId = getcpuid(0, buffer);
1550 dwBuffer = (DWORD*)buffer;
1552 if (dwBuffer[1] == 'uneG') {
1553 if (dwBuffer[3] == 'Ieni') {
1554 if (dwBuffer[2] == 'letn') { // get SMT/multicore enumeration for Intel EM64T
1556 // TODO: Currently GetLogicalCpuCountFromOS() and GetLogicalCpuCountFallback() are broken on
1557 // multi-core processor, but we never call into those two functions since we don't halve the
1558 // gen0size when it's prescott and above processor. We keep the old version here for earlier
1559 // generation system(Northwood based), perf data suggests on those systems, halve gen0 size
1560 // still boost the performance(ex:Biztalk boosts about 17%). So on earlier systems(Northwood)
1561 // based, we still go ahead and halve gen0 size. The logic in GetLogicalCpuCountFromOS()
1562 // and GetLogicalCpuCountFallback() works fine for those earlier generation systems.
1563 // If it's a Prescott and above processor or Multi-core, perf data suggests not to halve gen0
1564 // size at all gives us overall better performance.
1565 // This is going to be fixed with a new version in orcas time frame.
1567 if( (maxCpuId > 3) && (maxCpuId < 0x80000000) )
1570 val = GetLogicalCpuCountFromOS(); //try to obtain HT enumeration from OS API
1573 pParam->retVal = val; // OS API HT enumeration successful, we are Done
1577 val = GetLogicalCpuCountFallback(); // OS API failed, Fallback to HT enumeration using CPUID
1579 pParam->retVal = val;
1585 PAL_EXCEPT_FILTER(DefaultCatchFilter)
1595 return param.retVal;
1598 void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
1600 LIMITED_METHOD_CONTRACT;
1603 m_alignpad[0] = X86_INSTR_INT3;
1604 m_alignpad[1] = X86_INSTR_INT3;
1606 m_movEAX = X86_INSTR_MOV_EAX_IMM32;
1607 m_uet = pvSecretParam;
1608 m_jmp = X86_INSTR_JMP_REL32;
1609 m_execstub = (BYTE*) ((pTargetCode) - (4+((BYTE*)&m_execstub)));
1611 FlushInstructionCache(GetCurrentProcess(),GetEntryPoint(),sizeof(UMEntryThunkCode));
1614 UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback)
1616 LIMITED_METHOD_CONTRACT;
1618 if (*((BYTE*)pCallback) != X86_INSTR_MOV_EAX_IMM32 ||
1619 ( ((size_t)pCallback) & 3) != 2) {
1622 return *(UMEntryThunk**)( 1 + (BYTE*)pCallback );
1625 BOOL DoesSlotCallPrestub(PCODE pCode)
1631 PRECONDITION(pCode != NULL);
1632 PRECONDITION(pCode != GetPreStubEntryPoint());
1635 // x86 has the following possible sequences for prestub logic:
1636 // 1. slot -> temporary entrypoint -> prestub
1637 // 2. slot -> precode -> prestub
1638 // 3. slot -> precode -> jumprel32 (NGEN case) -> prestub
1640 #ifdef HAS_COMPACT_ENTRYPOINTS
1641 if (MethodDescChunk::GetMethodDescFromCompactEntryPoint(pCode, TRUE) != NULL)
1645 #endif // HAS_COMPACT_ENTRYPOINTS
1647 if (!IS_ALIGNED(pCode, PRECODE_ALIGNMENT))
1652 #ifdef HAS_FIXUP_PRECODE
1653 if (*PTR_BYTE(pCode) == X86_INSTR_CALL_REL32)
1655 // Note that call could have been patched to jmp in the meantime
1656 pCode = rel32Decode(pCode+1);
1659 if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) {
1660 pCode = rel32Decode(pCode+1);
1663 return pCode == (TADDR)PrecodeFixupThunk;
1667 if (*PTR_BYTE(pCode) != X86_INSTR_MOV_EAX_IMM32 ||
1668 *PTR_BYTE(pCode+5) != X86_INSTR_MOV_RM_R ||
1669 *PTR_BYTE(pCode+7) != X86_INSTR_JMP_REL32)
1673 pCode = rel32Decode(pCode+8);
1676 if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) {
1677 pCode = rel32Decode(pCode+1);
1680 return pCode == GetPreStubEntryPoint();
1683 //==========================================================================================
1684 // In NGen image, virtual slots inherited from cross-module dependencies point to jump thunks.
1685 // These jump thunk initially point to VirtualMethodFixupStub which transfers control here.
1686 // This method 'VirtualMethodFixupWorker' will patch the jump thunk to point to the actual
1687 // inherited method body after we have execute the precode and a stable entry point.
1689 EXTERN_C PVOID STDCALL VirtualMethodFixupWorker(Object * pThisPtr, CORCOMPILE_VIRTUAL_IMPORT_THUNK *pThunk)
1700 _ASSERTE(pThisPtr != NULL);
1701 VALIDATEOBJECT(pThisPtr);
1703 MethodTable * pMT = pThisPtr->GetTrueMethodTable();
1705 WORD slotNumber = pThunk->slotNum;
1706 _ASSERTE(slotNumber != (WORD)-1);
1708 PCODE pCode = pMT->GetRestoredSlot(slotNumber);
1710 if (!DoesSlotCallPrestub(pCode))
1712 // Skip fixup precode jump for better perf
1713 PCODE pDirectTarget = Precode::TryToSkipFixupPrecode(pCode);
1714 if (pDirectTarget != NULL)
1715 pCode = pDirectTarget;
1717 INT64 oldValue = *(INT64*)pThunk;
1718 BYTE* pOldValue = (BYTE*)&oldValue;
1720 if (pOldValue[0] == X86_INSTR_CALL_REL32)
1722 INT64 newValue = oldValue;
1723 BYTE* pNewValue = (BYTE*)&newValue;
1724 pNewValue[0] = X86_INSTR_JMP_REL32;
1726 INT_PTR pcRelOffset = (BYTE*)pCode - &pThunk->callJmp[5];
1727 *(INT32 *)(&pNewValue[1]) = (INT32) pcRelOffset;
1729 _ASSERTE(IS_ALIGNED(pThunk, sizeof(INT64)));
1730 if (EnsureWritableExecutablePagesNoThrow(pThunk, sizeof(INT64)))
1731 FastInterlockCompareExchangeLong((INT64*)pThunk, newValue, oldValue);
1733 FlushInstructionCache(GetCurrentProcess(), pThunk, 8);
1737 return PVOID(pCode);
1741 #ifdef FEATURE_READYTORUN
1744 // Allocation of dynamic helpers
1747 #define DYNAMIC_HELPER_ALIGNMENT sizeof(TADDR)
1749 #define BEGIN_DYNAMIC_HELPER_EMIT(size) \
1751 SIZE_T cbAligned = ALIGN_UP(cb, DYNAMIC_HELPER_ALIGNMENT); \
1752 BYTE * pStart = (BYTE *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \
1755 #define END_DYNAMIC_HELPER_EMIT() \
1756 _ASSERTE(pStart + cb == p); \
1757 while (p < pStart + cbAligned) *p++ = X86_INSTR_INT3; \
1758 ClrFlushInstructionCache(pStart, cbAligned); \
1759 return (PCODE)pStart
1761 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1763 STANDARD_VM_CONTRACT;
1765 BEGIN_DYNAMIC_HELPER_EMIT(10);
1767 *p++ = 0xB9; // mov ecx, XXXXXX
1768 *(INT32 *)p = (INT32)arg;
1771 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1772 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1775 END_DYNAMIC_HELPER_EMIT();
1778 void DynamicHelpers::EmitHelperWithArg(BYTE*& p, LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1783 PRECONDITION(p != NULL && target != NULL);
1787 // Move an an argument into the second argument register and jump to a target function.
1789 *p++ = 0xBA; // mov edx, XXXXXX
1790 *(INT32 *)p = (INT32)arg;
1793 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1794 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1798 PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1800 BEGIN_DYNAMIC_HELPER_EMIT(10);
1802 EmitHelperWithArg(p, pAllocator, arg, target);
1804 END_DYNAMIC_HELPER_EMIT();
1807 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1809 BEGIN_DYNAMIC_HELPER_EMIT(15);
1811 *p++ = 0xB9; // mov ecx, XXXXXX
1812 *(INT32 *)p = (INT32)arg;
1815 *p++ = 0xBA; // mov edx, XXXXXX
1816 *(INT32 *)p = (INT32)arg2;
1819 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1820 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1823 END_DYNAMIC_HELPER_EMIT();
1826 PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1828 BEGIN_DYNAMIC_HELPER_EMIT(12);
1830 *(UINT16 *)p = 0xD18B; // mov edx, ecx
1833 *p++ = 0xB9; // mov ecx, XXXXXX
1834 *(INT32 *)p = (INT32)arg;
1837 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1838 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1841 END_DYNAMIC_HELPER_EMIT();
1844 PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator)
1846 BEGIN_DYNAMIC_HELPER_EMIT(1);
1850 END_DYNAMIC_HELPER_EMIT();
1853 PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg)
1855 BEGIN_DYNAMIC_HELPER_EMIT(6);
1857 *p++ = 0xB8; // mov eax, XXXXXX
1858 *(INT32 *)p = (INT32)arg;
1863 END_DYNAMIC_HELPER_EMIT();
1866 PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset)
1868 BEGIN_DYNAMIC_HELPER_EMIT((offset != 0) ? 9 : 6);
1870 *p++ = 0xA1; // mov eax, [XXXXXX]
1871 *(INT32 *)p = (INT32)arg;
1876 // add eax, <offset>
1884 END_DYNAMIC_HELPER_EMIT();
1887 EXTERN_C VOID DynamicHelperArgsStub();
1889 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1892 BEGIN_DYNAMIC_HELPER_EMIT(18);
1894 BEGIN_DYNAMIC_HELPER_EMIT(12);
1915 *(INT32 *)p = target;
1922 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1924 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, (PCODE)DynamicHelperArgsStub);
1926 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1930 END_DYNAMIC_HELPER_EMIT();
1933 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1936 BEGIN_DYNAMIC_HELPER_EMIT(23);
1938 BEGIN_DYNAMIC_HELPER_EMIT(17);
1964 *(INT32 *)p = target;
1971 *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1973 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, (PCODE)DynamicHelperArgsStub);
1975 *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1979 END_DYNAMIC_HELPER_EMIT();
1982 PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule)
1984 STANDARD_VM_CONTRACT;
1986 PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
1987 GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
1988 GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
1990 GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
1991 pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
1992 pArgs->signature = pLookup->signature;
1993 pArgs->module = (CORINFO_MODULE_HANDLE)pModule;
1995 // It's available only via the run-time helper function
1996 if (pLookup->indirections == CORINFO_USEHELPER)
1998 BEGIN_DYNAMIC_HELPER_EMIT(10);
2000 // ecx contains the generic context parameter
2002 // jmp helperAddress
2003 EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2005 END_DYNAMIC_HELPER_EMIT();
2009 int indirectionsSize = 0;
2010 for (WORD i = 0; i < pLookup->indirections; i++)
2011 indirectionsSize += (pLookup->offsets[i] >= 0x80 ? 6 : 3);
2013 int codeSize = indirectionsSize + (pLookup->testForNull ? 21 : 3);
2015 BEGIN_DYNAMIC_HELPER_EMIT(codeSize);
2017 if (pLookup->testForNull)
2019 // ecx contains the generic context parameter. Save a copy of it in the eax register
2021 *(UINT16*)p = 0xc889; p += 2;
2024 for (WORD i = 0; i < pLookup->indirections; i++)
2026 // mov ecx,qword ptr [ecx+offset]
2027 if (pLookup->offsets[i] >= 0x80)
2029 *(UINT16*)p = 0x898b; p += 2;
2030 *(UINT32*)p = (UINT32)pLookup->offsets[i]; p += 4;
2034 *(UINT16*)p = 0x498b; p += 2;
2035 *p++ = (BYTE)pLookup->offsets[i];
2039 // No null test required
2040 if (!pLookup->testForNull)
2042 // No fixups needed for R2R
2045 *(UINT16*)p = 0xc889; p += 2;
2050 // ecx contains the value of the dictionary slot entry
2052 _ASSERTE(pLookup->indirections != 0);
2055 *(UINT16*)p = 0xc985; p += 2;
2057 // je 'HELPER_CALL' (a jump of 3 bytes)
2058 *(UINT16*)p = 0x0374; p += 2;
2061 *(UINT16*)p = 0xc889; p += 2;
2066 // Put the generic context back into rcx (was previously saved in eax)
2068 *(UINT16*)p = 0xc189; p += 2;
2071 // jmp helperAddress
2072 EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2076 END_DYNAMIC_HELPER_EMIT();
2080 #endif // FEATURE_READYTORUN
2083 #endif // DACCESS_COMPILE