diff options
Diffstat (limited to 'src/vm/amd64/gmscpu.h')
-rw-r--r-- | src/vm/amd64/gmscpu.h | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/src/vm/amd64/gmscpu.h b/src/vm/amd64/gmscpu.h new file mode 100644 index 0000000000..ff84d18daf --- /dev/null +++ b/src/vm/amd64/gmscpu.h @@ -0,0 +1,186 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/**************************************************************/ +/* gmscpu.h */ +/**************************************************************/ +/* HelperFrame is defines 'GET_STATE(machState)' macro, which + figures out what the state of the machine will be when the + current method returns. It then stores the state in the + JIT_machState structure. */ + +/**************************************************************/ + +#ifndef __gmsAMD64_h__ +#define __gmsAMD64_h__ + +#ifdef _DEBUG +class HelperMethodFrame; +struct MachState; +EXTERN_C MachState* __stdcall HelperMethodFrameConfirmState(HelperMethodFrame* frame, void* esiVal, void* ediVal, void* ebxVal, void* ebpVal); +#endif // _DEBUG + +// A MachState indicates the register state of the processor at some point in time (usually +// just before or after a call is made). It can be made one of two ways. Either explicitly +// (when you for some reason know the values of all the registers), or implicitly using the +// GET_STATE macros. + +typedef DPTR(struct MachState) PTR_MachState; +struct MachState +{ + MachState() + { + LIMITED_METHOD_DAC_CONTRACT; + INDEBUG(memset(this, 0xCC, sizeof(MachState));) + } + + bool isValid() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(dac_cast<TADDR>(_pRetAddr) != INVALID_POINTER_CC); return(_pRetAddr != 0); } + TADDR* pRetAddr() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(isValid()); return(_pRetAddr); } + TADDR GetRetAddr() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(isValid()); return *_pRetAddr; } +#ifndef DACCESS_COMPILE + void SetRetAddr(TADDR* addr) { _ASSERTE(isValid()); _pRetAddr = addr; } +#endif + + friend class HelperMethodFrame; + friend class CheckAsmOffsets; + friend struct LazyMachState; +#ifdef _DEBUG + friend MachState* __stdcall HelperMethodFrameConfirmState(HelperMethodFrame* frame, void* esiVal, void* ediVal, void* ebxVal, void* ebpVal); +#endif + +protected: + PCODE m_Rip; + TADDR m_Rsp; + + // + // These "capture" fields are READ ONLY once initialized by + // LazyMachStateCaptureState because we are racing to update + // the MachState when we do a stackwalk so, we must not update + // any state used to initialize the unwind from the captured + // state to the managed caller. + // + // Note also, that these fields need to be in the base struct + // because the context pointers below may point up to these + // fields. + // + CalleeSavedRegisters m_Capture; + + // context pointers for preserved registers + CalleeSavedRegistersPointers m_Ptrs; + + PTR_TADDR _pRetAddr; + +#ifdef FEATURE_PAL + // On PAL, we don't always have the context pointers available due to + // a limitation of an unwinding library. In such case, preserve + // the unwound values. + CalleeSavedRegisters m_Unwound; +#endif +}; + +/********************************************************************/ +/* This allows you to defer the computation of the Machine state + until later. Note that we don't reuse slots, because we want + this to be threadsafe without locks */ + +EXTERN_C void LazyMachStateCaptureState(struct LazyMachState *pState); + +typedef DPTR(struct LazyMachState) PTR_LazyMachState; +struct LazyMachState : public MachState +{ + // compute the machine state of the processor as it will exist just + // after the return after at most'funCallDepth' number of functions. + // if 'testFtn' is non-NULL, the return address is tested at each + // return instruction encountered. If this test returns non-NULL, + // then stack walking stops (thus you can walk up to the point that the + // return address matches some criteria + + // Normally this is called with funCallDepth=1 and testFtn = 0 so that + // it returns the state of the processor after the function that called 'captureState()' + void setLazyStateFromUnwind(MachState* copy); + static void unwindLazyState(LazyMachState* baseState, + MachState* lazyState, + DWORD threadId, + int funCallDepth = 1, + HostCallPreference hostCallPreference = AllowHostCalls); + + friend class HelperMethodFrame; + friend class CheckAsmOffsets; + + // + // These "capture" fields are READ ONLY once initialized by + // LazyMachStateCaptureState because we are racing to update + // the MachState when we do a stackwalk so, we must not update + // any state used to initialize the unwind from the captured + // state to the managed caller. + // + ULONG64 m_CaptureRip; + ULONG64 m_CaptureRsp; +}; + +inline void LazyMachState::setLazyStateFromUnwind(MachState* copy) +{ + LIMITED_METHOD_CONTRACT; + +#if defined(DACCESS_COMPILE) + // This function cannot be called in DAC because DAC cannot update target memory. + DacError(E_FAIL); + return; + +#else // !DACCESS_COMPILE + this->m_Rip = copy->m_Rip; + this->m_Rsp = copy->m_Rsp; + +#ifdef FEATURE_PAL + this->m_Unwound = copy->m_Unwound; +#endif + + // Capture* has already been set, so there is no need to touch it + + // loop over the nonvolatile context pointers and make + // sure to properly copy interior pointers into the + // new struct + + PULONG64* pSrc = (PULONG64 *)©->m_Ptrs; + PULONG64* pDst = (PULONG64 *)&this->m_Ptrs; + + const PULONG64 LowerBoundDst = (PULONG64) this; + const PULONG64 LowerBoundSrc = (PULONG64) copy; + + const PULONG64 UpperBoundSrc = (PULONG64) ((BYTE*)LowerBoundSrc + sizeof(*copy)); + + for (int i = 0; i < NUM_CALLEE_SAVED_REGISTERS; i++) + { + PULONG64 valueSrc = *pSrc++; + + if ((LowerBoundSrc <= valueSrc) && (valueSrc < UpperBoundSrc)) + { + // make any pointer interior to 'src' interior to 'dst' + valueSrc = (PULONG64)((BYTE*)valueSrc - (BYTE*)LowerBoundSrc + (BYTE*)LowerBoundDst); + } + + *pDst++ = valueSrc; + } + + // this has to be last because we depend on write ordering to + // synchronize the race implicit in updating this struct + VolatileStore(&_pRetAddr, (PTR_TADDR)(TADDR)&m_Rip); + +#endif // !DACCESS_COMPILE +} + +// Do the initial capture of the machine state. This is meant to be +// as light weight as possible, as we may never need the state that +// we capture. Thus to complete the process you need to call +// 'getMachState()', which finishes the process +EXTERN_C void LazyMachStateCaptureState(struct LazyMachState *pState); + +// CAPTURE_STATE captures just enough register state so that the state of the +// processor can be deterined just after the the routine that has CAPTURE_STATE in +// it returns. + +#define CAPTURE_STATE(machState, ret) \ + LazyMachStateCaptureState(machState) + +#endif // __gmsAMD64_h__ |