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
22using namespace lldb;
23using namespace lldb_private;
24
25const DWORD kWinContextFlags = CONTEXT_ALL;
26
27// Constructors and Destructors
28RegisterContextWindows::RegisterContextWindows(Thread &thread,
29 uint32_t concrete_frame_idx)
30 : RegisterContext(thread, concrete_frame_idx), m_context(),
31 m_context_stale(true) {}
32
33RegisterContextWindows::~RegisterContextWindows() {}
34
35void RegisterContextWindows::InvalidateAllRegisters() {
36 m_context_stale = true;
37}
38
39bool RegisterContextWindows::ReadAllRegisterValues(
40 lldb::WritableDataBufferSP &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
51bool 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
59uint32_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
74bool RegisterContextWindows::HardwareSingleStep(bool enable) { return false; }
75
76bool 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 = GetLog(WindowsLog::Registers);
116 LLDB_LOG(log, "hardware breakpoints not currently supported on this arch");
117 return false;
118#endif
119}
120
121bool 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
138uint32_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
151bool RegisterContextWindows::CacheAllRegisterValues() {
152 Log *log = GetLog(mask: WindowsLog::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
183bool RegisterContextWindows::ApplyAllRegisterValues() {
184 TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread);
185 return ::SetThreadContext(
186 wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context);
187}
188

source code of lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp