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