1 //===-- RegisterContextWindows.cpp ------------------------------*- C++ -*-===//
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
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 
33 RegisterContextWindows::~RegisterContextWindows() {}
34 
35 void RegisterContextWindows::InvalidateAllRegisters() {
36   m_context_stale = true;
37 }
38 
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 
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 
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 
74 bool RegisterContextWindows::HardwareSingleStep(bool enable) { return false; }
75 
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(__x86_64__) || defined(_M_AMD64)
88   case 8:
89 #endif
90     break;
91   default:
92     return false;
93   }
94 
95   if (!CacheAllRegisterValues())
96     return false;
97 
98   unsigned shift = 2 * slot;
99   m_context.Dr7 |= 1ULL << shift;
100 
101   (&m_context.Dr0)[slot] = address;
102 
103   shift = 18 + 4 * slot;
104   m_context.Dr7 &= ~(3ULL << shift);
105   m_context.Dr7 |= (size == 8 ? 2ULL : size - 1) << shift;
106 
107   shift = 16 + 4 * slot;
108   m_context.Dr7 &= ~(3ULL << shift);
109   m_context.Dr7 |= (read ? 3ULL : (write ? 1ULL : 0)) << shift;
110 
111   return ApplyAllRegisterValues();
112 }
113 
114 bool RegisterContextWindows::RemoveHardwareBreakpoint(uint32_t slot) {
115   if (slot >= NUM_HARDWARE_BREAKPOINT_SLOTS)
116     return false;
117 
118   if (!CacheAllRegisterValues())
119     return false;
120 
121   unsigned shift = 2 * slot;
122   m_context.Dr7 &= ~(1ULL << shift);
123 
124   return ApplyAllRegisterValues();
125 }
126 
127 uint32_t RegisterContextWindows::GetTriggeredHardwareBreakpointSlotId() {
128   if (!CacheAllRegisterValues())
129     return LLDB_INVALID_INDEX32;
130 
131   for (unsigned i = 0UL; i < NUM_HARDWARE_BREAKPOINT_SLOTS; i++)
132     if (m_context.Dr6 & (1ULL << i))
133       return i;
134 
135   return LLDB_INVALID_INDEX32;
136 }
137 
138 bool RegisterContextWindows::CacheAllRegisterValues() {
139   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
140   if (!m_context_stale)
141     return true;
142 
143   TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread);
144   uint8_t buffer[2048];
145   memset(buffer, 0, sizeof(buffer));
146   PCONTEXT tmpContext = NULL;
147   DWORD contextLength = (DWORD)sizeof(buffer);
148   if (!::InitializeContext(buffer, kWinContextFlags, &tmpContext,
149                            &contextLength)) {
150     return false;
151   }
152   memcpy(&m_context, tmpContext, sizeof(m_context));
153   if (::SuspendThread(
154           wthread.GetHostThread().GetNativeThread().GetSystemHandle()) ==
155       (DWORD)-1) {
156     return false;
157   }
158   if (!::GetThreadContext(
159           wthread.GetHostThread().GetNativeThread().GetSystemHandle(),
160           &m_context)) {
161     LLDB_LOG(
162         log,
163         "GetThreadContext failed with error {0} while caching register values.",
164         ::GetLastError());
165     return false;
166   }
167   if (::ResumeThread(
168           wthread.GetHostThread().GetNativeThread().GetSystemHandle()) ==
169       (DWORD)-1) {
170     return false;
171   }
172   LLDB_LOG(log, "successfully updated the register values.");
173   m_context_stale = false;
174   return true;
175 }
176 
177 bool RegisterContextWindows::ApplyAllRegisterValues() {
178   TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread);
179   return ::SetThreadContext(
180       wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context);
181 }
182