1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license 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 __gmsx86_h__
#define __gmsx86_h__
#define __gmsx86_h__
#ifdef _DEBUG
class HelperMethodFrame;
struct MachState;
EXTERN_C MachState* STDCALL HelperMethodFrameConfirmState(HelperMethodFrame* frame, void* esiVal, void* ediVal, void* ebxVal, void* ebpVal);
#endif
// 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* pEdi() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(dac_cast<TADDR>(_pEdi) != INVALID_POINTER_CC); return(_pEdi); }
TADDR* pEsi() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(dac_cast<TADDR>(_pEsi) != INVALID_POINTER_CC); return(_pEsi); }
TADDR* pEbx() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(dac_cast<TADDR>(_pEbx) != INVALID_POINTER_CC); return(_pEbx); }
TADDR* pEbp() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(dac_cast<TADDR>(_pEbp) != INVALID_POINTER_CC); return(_pEbp); }
TADDR esp() { LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(isValid()); return(_esp); }
PTR_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) { LIMITED_METHOD_CONTRACT; _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:
// Note the fields are layed out to make generating a
// MachState structure from assembly code very easy
// The state of all the callee saved registers.
// If the register has been spill to the stack p<REG>
// points at this location, otherwise it points
// at the field <REG> field itself
PTR_TADDR _pEdi;
TADDR _edi;
PTR_TADDR _pEsi;
TADDR _esi;
PTR_TADDR _pEbx;
TADDR _ebx;
PTR_TADDR _pEbp;
TADDR _ebp;
TADDR _esp; // stack pointer after the function returns
PTR_TADDR _pRetAddr; // The address of the stored IP address (points into the stack)
};
/********************************************************************/
/* 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 */
struct LazyMachState;
typedef DPTR(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,
int funCallDepth = 1,
HostCallPreference hostCallPreference = AllowHostCalls);
friend class HelperMethodFrame;
friend class CheckAsmOffsets;
private:
TADDR captureEbp; // Ebp at the time of capture
TADDR captureEsp; // Esp at the time of capture
TADDR captureEip; // Eip at the time of capture
};
inline void LazyMachState::setLazyStateFromUnwind(MachState* copy)
{
// _pRetAddr has to be the last thing updated when we make the copy (because its
// is the the _pRetAddr becoming non-zero that flips this from invalid to valid.
// we assert that it is the last field in the struct.
static_assert_no_msg(offsetof(MachState, _pRetAddr) + sizeof(_pRetAddr) == sizeof(MachState));
memcpy(this, copy, offsetof(MachState, _pRetAddr));
// this has to be last
VolatileStore((TADDR*)&_pRetAddr, dac_cast<TADDR>(copy->_pRetAddr));
}
// 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 int __fastcall 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.
// Note that the return is never taken, is is there for epilog walking
#define CAPTURE_STATE(machState, ret) \
if (LazyMachStateCaptureState(machState)) ret
#endif
|