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
|
// 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.
//
//
// Emulate hardware single-step on ARM64.
//
#ifndef __ARM64_SINGLE_STEPPER_H__
#define __ARM64_SINGLE_STEPPER_H__
// Class that encapsulates the context needed to single step one thread.
class Arm64SingleStepper
{
public:
Arm64SingleStepper();
~Arm64SingleStepper();
// Given the context with which a thread will be resumed, modify that context such that resuming the
// thread will execute a single instruction before raising an EXCEPTION_BREAKPOINT. The thread context
// must be cleaned up via the Fixup method below before any further exception processing can occur (at
// which point the caller can behave as though EXCEPTION_SINGLE_STEP was raised).
void Enable();
void Bypass(uint64_t ip, uint32_t opcode);
void Apply(T_CONTEXT *pCtx);
// Disables the single stepper.
void Disable();
// Returns whether or not the stepper is enabled.
inline bool IsEnabled() const
{
return m_state == Enabled || m_state == Applied;
}
// When called in response to an exception (preferably in a first chance vectored handler before anyone
// else has looked at the thread context) this method will (a) determine whether this exception was raised
// by a call to Enable() above, in which case true will be returned and (b) perform final fixup of the
// thread context passed in to complete the emulation of a hardware single step. Note that this routine
// must be called even if the exception code is not EXCEPTION_BREAKPOINT since the instruction stepped
// might have raised its own exception (e.g. A/V) and we still need to fix the thread context in this
// case.
bool Fixup(T_CONTEXT *pCtx, DWORD dwExceptionCode);
private:
enum
{
kMaxCodeBuffer = 2, // max slots in our redirect buffer
// 1 for current instruction
// 1 for final breakpoint
#ifdef __linux__
kBreakpointOp = 0xD4200000 + (0x11E1 << 5), // Opcode for the breakpoint instruction used on ARM64 Linux
#else
#error Arm64SingleStepper is only expected to be used for linux
#endif
};
// Bit numbers of the condition flags in the CPSR.
enum APSRBits
{
APSR_N = 31,
APSR_Z = 30,
APSR_C = 29,
APSR_V = 28,
};
enum StepperState
{
Disabled,
Enabled,
Applied
};
uint64_t m_originalPc; // PC value before stepping
uint64_t m_targetPc; // Final PC value after stepping if no exception is raised
uint32_t *m_rgCode; // Buffer execution is redirected to during the step
StepperState m_state; // Tracks whether the stepper is Enabled, Disabled, or enabled and applied to a context
uint32_t m_opcodes[1];
bool m_fEmulate;
bool m_fBypass;
// Initializes m_rgCode. Not thread safe.
void Init();
// Returns true if the current context indicates the ARM condition specified holds.
bool ConditionHolds(T_CONTEXT *pCtx, uint64_t cond);
// Get the current value of a register.
uint64_t GetReg(T_CONTEXT *pCtx, uint64_t reg);
// Set the current value of a register.
void SetReg(T_CONTEXT *pCtx, uint64_t reg, uint64_t value);
// Set the current value of a FP register.
void SetFPReg(T_CONTEXT *pCtx, uint64_t reg, uint64_t valueLo, uint64_t valueHi = 0);
// Attempt to read a 4, or 8 byte value from memory, zero or sign extend it to a 8-byte value and place
// that value into the buffer pointed at by pdwResult. Returns false if attempting to read the location
// caused a fault.
bool GetMem(uint64_t *pdwResult, uint8_t* pAddress, int cbSize, bool fSignExtend);
// Parse the instruction opcode. If the instruction reads or writes the PC it will be emulated by updating
// the thread context appropriately and true will be returned. If the instruction is not one of those cases
// (or it is but we faulted trying to read memory during the emulation) no state is updated and false is
// returned instead.
bool TryEmulate(T_CONTEXT *pCtx, uint32_t opcode, bool execute);
};
#endif // !__ARM64_SINGLE_STEPPER_H__
|