1 //===-- RegisterContextWindows.cpp ----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "lldb/Host/windows/HostThreadWindows.h"
10 #include "lldb/Host/windows/windows.h"
11 #include "lldb/Utility/DataBufferHeap.h"
12 #include "lldb/Utility/Status.h"
13 #include "lldb/lldb-private-types.h"
14
15 #include "ProcessWindowsLog.h"
16 #include "RegisterContextWindows.h"
17 #include "TargetThreadWindows.h"
18
19 #include "llvm/ADT/STLExtras.h"
20 #include "lldb/Target/Target.h"
21
22 using namespace lldb;
23 using namespace lldb_private;
24
25 const DWORD kWinContextFlags = CONTEXT_ALL;
26
27 // Constructors and Destructors
RegisterContextWindows(Thread & thread,uint32_t concrete_frame_idx)28 RegisterContextWindows::RegisterContextWindows(Thread &thread,
29 uint32_t concrete_frame_idx)
30 : RegisterContext(thread, concrete_frame_idx), m_context(),
31 m_context_stale(true) {}
32
~RegisterContextWindows()33 RegisterContextWindows::~RegisterContextWindows() {}
34
InvalidateAllRegisters()35 void RegisterContextWindows::InvalidateAllRegisters() {
36 m_context_stale = true;
37 }
38
ReadAllRegisterValues(lldb::DataBufferSP & data_sp)39 bool RegisterContextWindows::ReadAllRegisterValues(
40 lldb::DataBufferSP &data_sp) {
41
42 if (!CacheAllRegisterValues())
43 return false;
44
45 data_sp.reset(new DataBufferHeap(sizeof(CONTEXT), 0));
46 memcpy(data_sp->GetBytes(), &m_context, sizeof(m_context));
47
48 return true;
49 }
50
WriteAllRegisterValues(const lldb::DataBufferSP & data_sp)51 bool RegisterContextWindows::WriteAllRegisterValues(
52 const lldb::DataBufferSP &data_sp) {
53 assert(data_sp->GetByteSize() >= sizeof(m_context));
54 memcpy(&m_context, data_sp->GetBytes(), sizeof(m_context));
55
56 return ApplyAllRegisterValues();
57 }
58
ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind,uint32_t num)59 uint32_t RegisterContextWindows::ConvertRegisterKindToRegisterNumber(
60 lldb::RegisterKind kind, uint32_t num) {
61 const uint32_t num_regs = GetRegisterCount();
62
63 assert(kind < kNumRegisterKinds);
64 for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
65 const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
66
67 if (reg_info->kinds[kind] == num)
68 return reg_idx;
69 }
70
71 return LLDB_INVALID_REGNUM;
72 }
73
HardwareSingleStep(bool enable)74 bool RegisterContextWindows::HardwareSingleStep(bool enable) { return false; }
75
AddHardwareBreakpoint(uint32_t slot,lldb::addr_t address,uint32_t size,bool read,bool write)76 bool RegisterContextWindows::AddHardwareBreakpoint(uint32_t slot,
77 lldb::addr_t address,
78 uint32_t size, bool read,
79 bool write) {
80 if (slot >= NUM_HARDWARE_BREAKPOINT_SLOTS)
81 return false;
82
83 switch (size) {
84 case 1:
85 case 2:
86 case 4:
87 #if defined(_WIN64)
88 case 8:
89 #endif
90 break;
91 default:
92 return false;
93 }
94
95 if (!CacheAllRegisterValues())
96 return false;
97
98 #if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_AMD64)
99 unsigned shift = 2 * slot;
100 m_context.Dr7 |= 1ULL << shift;
101
102 (&m_context.Dr0)[slot] = address;
103
104 shift = 18 + 4 * slot;
105 m_context.Dr7 &= ~(3ULL << shift);
106 m_context.Dr7 |= (size == 8 ? 2ULL : size - 1) << shift;
107
108 shift = 16 + 4 * slot;
109 m_context.Dr7 &= ~(3ULL << shift);
110 m_context.Dr7 |= (read ? 3ULL : (write ? 1ULL : 0)) << shift;
111
112 return ApplyAllRegisterValues();
113
114 #else
115 Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
116 LLDB_LOG(log, "hardware breakpoints not currently supported on this arch");
117 return false;
118 #endif
119 }
120
RemoveHardwareBreakpoint(uint32_t slot)121 bool RegisterContextWindows::RemoveHardwareBreakpoint(uint32_t slot) {
122 if (slot >= NUM_HARDWARE_BREAKPOINT_SLOTS)
123 return false;
124
125 if (!CacheAllRegisterValues())
126 return false;
127
128 #if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_AMD64)
129 unsigned shift = 2 * slot;
130 m_context.Dr7 &= ~(1ULL << shift);
131
132 return ApplyAllRegisterValues();
133 #else
134 return false;
135 #endif
136 }
137
GetTriggeredHardwareBreakpointSlotId()138 uint32_t RegisterContextWindows::GetTriggeredHardwareBreakpointSlotId() {
139 if (!CacheAllRegisterValues())
140 return LLDB_INVALID_INDEX32;
141
142 #if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_AMD64)
143 for (unsigned i = 0UL; i < NUM_HARDWARE_BREAKPOINT_SLOTS; i++)
144 if (m_context.Dr6 & (1ULL << i))
145 return i;
146 #endif
147
148 return LLDB_INVALID_INDEX32;
149 }
150
CacheAllRegisterValues()151 bool RegisterContextWindows::CacheAllRegisterValues() {
152 Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
153 if (!m_context_stale)
154 return true;
155
156 TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread);
157 memset(&m_context, 0, sizeof(m_context));
158 m_context.ContextFlags = kWinContextFlags;
159 if (::SuspendThread(
160 wthread.GetHostThread().GetNativeThread().GetSystemHandle()) ==
161 (DWORD)-1) {
162 return false;
163 }
164 if (!::GetThreadContext(
165 wthread.GetHostThread().GetNativeThread().GetSystemHandle(),
166 &m_context)) {
167 LLDB_LOG(
168 log,
169 "GetThreadContext failed with error {0} while caching register values.",
170 ::GetLastError());
171 return false;
172 }
173 if (::ResumeThread(
174 wthread.GetHostThread().GetNativeThread().GetSystemHandle()) ==
175 (DWORD)-1) {
176 return false;
177 }
178 LLDB_LOG(log, "successfully updated the register values.");
179 m_context_stale = false;
180 return true;
181 }
182
ApplyAllRegisterValues()183 bool RegisterContextWindows::ApplyAllRegisterValues() {
184 TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread);
185 return ::SetThreadContext(
186 wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context);
187 }
188