1 //===-- NativeRegisterContextWindows_i386.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 #if defined(_WIN32) && !defined(_WIN64)
10 
11 #include "NativeRegisterContextWindows_i386.h"
12 
13 #include "NativeThreadWindows.h"
14 #include "Plugins/Process/Utility/RegisterContextWindows_i386.h"
15 #include "ProcessWindowsLog.h"
16 #include "lldb/Host/HostInfo.h"
17 #include "lldb/Host/HostThread.h"
18 #include "lldb/Host/Windows/HostThreadWindows.h"
19 #include "lldb/Host/windows/windows.h"
20 
21 #include "lldb/Utility/Log.h"
22 #include "lldb/Utility/RegisterValue.h"
23 #include "llvm/ADT/STLExtras.h"
24 
25 using namespace lldb;
26 using namespace lldb_private;
27 
28 #define REG_CONTEXT_SIZE sizeof(::CONTEXT)
29 
30 namespace {
31 static const uint32_t g_gpr_regnums_i386[] = {
32     lldb_eax_i386,      lldb_ebx_i386,    lldb_ecx_i386, lldb_edx_i386,
33     lldb_edi_i386,      lldb_esi_i386,    lldb_ebp_i386, lldb_esp_i386,
34     lldb_eip_i386,      lldb_eflags_i386, lldb_cs_i386,  lldb_fs_i386,
35     lldb_gs_i386,       lldb_ss_i386,     lldb_ds_i386,  lldb_es_i386,
36     LLDB_INVALID_REGNUM // Register sets must be terminated with this flag.
37 };
38 
39 static const RegisterSet g_reg_sets_i386[] = {
40     {"General Purpose Registers", "gpr",
41      llvm::array_lengthof(g_gpr_regnums_i386) - 1, g_gpr_regnums_i386},
42 };
43 
44 enum { k_num_register_sets = 1 };
45 
46 } // namespace
47 
48 static RegisterInfoInterface *
49 CreateRegisterInfoInterface(const ArchSpec &target_arch) {
50   assert((HostInfo::GetArchitecture().GetAddressByteSize() == 4) &&
51          "Register setting path assumes this is a 32-bit host");
52   return new RegisterContextWindows_i386(target_arch);
53 }
54 
55 static Status GetThreadContextHelper(lldb::thread_t thread_handle,
56                                      PCONTEXT context_ptr,
57                                      const DWORD control_flag) {
58   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
59   Status error;
60 
61   memset(context_ptr, 0, sizeof(::CONTEXT));
62   context_ptr->ContextFlags = control_flag;
63   if (!::GetThreadContext(thread_handle, context_ptr)) {
64     error.SetError(GetLastError(), eErrorTypeWin32);
65     LLDB_LOG(log, "{0} GetThreadContext failed with error {1}", __FUNCTION__,
66              error);
67     return error;
68   }
69   return Status();
70 }
71 
72 static Status SetThreadContextHelper(lldb::thread_t thread_handle,
73                                      PCONTEXT context_ptr) {
74   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
75   Status error;
76 
77   if (!::SetThreadContext(thread_handle, context_ptr)) {
78     error.SetError(GetLastError(), eErrorTypeWin32);
79     LLDB_LOG(log, "{0} SetThreadContext failed with error {1}", __FUNCTION__,
80              error);
81     return error;
82   }
83   return Status();
84 }
85 
86 std::unique_ptr<NativeRegisterContextWindows>
87 NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
88     const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
89   return llvm::make_unique<NativeRegisterContextWindows_i386>(target_arch,
90                                                               native_thread);
91 }
92 
93 NativeRegisterContextWindows_i386::NativeRegisterContextWindows_i386(
94     const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
95     : NativeRegisterContextWindows(native_thread,
96                                    CreateRegisterInfoInterface(target_arch)) {}
97 
98 bool NativeRegisterContextWindows_i386::IsGPR(uint32_t reg_index) const {
99   return (reg_index < k_first_alias_i386);
100 }
101 
102 uint32_t NativeRegisterContextWindows_i386::GetRegisterSetCount() const {
103   return k_num_register_sets;
104 }
105 
106 const RegisterSet *
107 NativeRegisterContextWindows_i386::GetRegisterSet(uint32_t set_index) const {
108   if (set_index >= k_num_register_sets)
109     return nullptr;
110   return &g_reg_sets_i386[set_index];
111 }
112 
113 Status NativeRegisterContextWindows_i386::GPRRead(const uint32_t reg,
114                                                   RegisterValue &reg_value) {
115   ::CONTEXT tls_context;
116   DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
117   Status error =
118       GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag);
119   if (error.Fail())
120     return error;
121 
122   switch (reg) {
123   case lldb_eax_i386:
124     reg_value.SetUInt32(tls_context.Eax);
125     break;
126   case lldb_ebx_i386:
127     reg_value.SetUInt32(tls_context.Ebx);
128     break;
129   case lldb_ecx_i386:
130     reg_value.SetUInt32(tls_context.Ecx);
131     break;
132   case lldb_edx_i386:
133     reg_value.SetUInt32(tls_context.Edx);
134     break;
135   case lldb_edi_i386:
136     reg_value.SetUInt32(tls_context.Edi);
137     break;
138   case lldb_esi_i386:
139     reg_value.SetUInt32(tls_context.Esi);
140     break;
141   case lldb_ebp_i386:
142     reg_value.SetUInt32(tls_context.Ebp);
143     break;
144   case lldb_esp_i386:
145     reg_value.SetUInt32(tls_context.Esp);
146     break;
147   case lldb_eip_i386:
148     reg_value.SetUInt32(tls_context.Eip);
149     break;
150   case lldb_eflags_i386:
151     reg_value.SetUInt32(tls_context.EFlags);
152     break;
153   case lldb_cs_i386:
154     reg_value.SetUInt32(tls_context.SegCs);
155     break;
156   case lldb_fs_i386:
157     reg_value.SetUInt32(tls_context.SegFs);
158     break;
159   case lldb_gs_i386:
160     reg_value.SetUInt32(tls_context.SegGs);
161     break;
162   case lldb_ss_i386:
163     reg_value.SetUInt32(tls_context.SegSs);
164     break;
165   case lldb_ds_i386:
166     reg_value.SetUInt32(tls_context.SegDs);
167     break;
168   case lldb_es_i386:
169     reg_value.SetUInt32(tls_context.SegEs);
170     break;
171   }
172 
173   return error;
174 }
175 
176 Status
177 NativeRegisterContextWindows_i386::GPRWrite(const uint32_t reg,
178                                             const RegisterValue &reg_value) {
179   ::CONTEXT tls_context;
180   DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
181   auto thread_handle = GetThreadHandle();
182   Status error =
183       GetThreadContextHelper(thread_handle, &tls_context, context_flag);
184   if (error.Fail())
185     return error;
186 
187   switch (reg) {
188   case lldb_eax_i386:
189     tls_context.Eax = reg_value.GetAsUInt32();
190     break;
191   case lldb_ebx_i386:
192     tls_context.Ebx = reg_value.GetAsUInt32();
193     break;
194   case lldb_ecx_i386:
195     tls_context.Ecx = reg_value.GetAsUInt32();
196     break;
197   case lldb_edx_i386:
198     tls_context.Edx = reg_value.GetAsUInt32();
199     break;
200   case lldb_edi_i386:
201     tls_context.Edi = reg_value.GetAsUInt32();
202     break;
203   case lldb_esi_i386:
204     tls_context.Esi = reg_value.GetAsUInt32();
205     break;
206   case lldb_ebp_i386:
207     tls_context.Ebp = reg_value.GetAsUInt32();
208     break;
209   case lldb_esp_i386:
210     tls_context.Esp = reg_value.GetAsUInt32();
211     break;
212   case lldb_eip_i386:
213     tls_context.Eip = reg_value.GetAsUInt32();
214     break;
215   case lldb_eflags_i386:
216     tls_context.EFlags = reg_value.GetAsUInt32();
217     break;
218   case lldb_cs_i386:
219     tls_context.SegCs = reg_value.GetAsUInt32();
220     break;
221   case lldb_fs_i386:
222     tls_context.SegFs = reg_value.GetAsUInt32();
223     break;
224   case lldb_gs_i386:
225     tls_context.SegGs = reg_value.GetAsUInt32();
226     break;
227   case lldb_ss_i386:
228     tls_context.SegSs = reg_value.GetAsUInt32();
229     break;
230   case lldb_ds_i386:
231     tls_context.SegDs = reg_value.GetAsUInt32();
232     break;
233   case lldb_es_i386:
234     tls_context.SegEs = reg_value.GetAsUInt32();
235     break;
236   }
237 
238   return SetThreadContextHelper(thread_handle, &tls_context);
239 }
240 
241 Status
242 NativeRegisterContextWindows_i386::ReadRegister(const RegisterInfo *reg_info,
243                                                 RegisterValue &reg_value) {
244   Status error;
245   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
246 
247   if (!reg_info) {
248     error.SetErrorString("reg_info NULL");
249     return error;
250   }
251 
252   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
253   if (reg == LLDB_INVALID_REGNUM) {
254     // This is likely an internal register for lldb use only and should not be
255     // directly queried.
256     error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
257                                    "register, cannot read directly",
258                                    reg_info->name);
259     return error;
260   }
261 
262   if (IsGPR(reg))
263     return GPRRead(reg, reg_value);
264 
265   return Status("unimplemented");
266 }
267 
268 Status NativeRegisterContextWindows_i386::WriteRegister(
269     const RegisterInfo *reg_info, const RegisterValue &reg_value) {
270   Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
271   Status error;
272 
273   if (!reg_info) {
274     error.SetErrorString("reg_info NULL");
275     return error;
276   }
277 
278   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
279   if (reg == LLDB_INVALID_REGNUM) {
280     // This is likely an internal register for lldb use only and should not be
281     // directly queried.
282     error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
283                                    "register, cannot read directly",
284                                    reg_info->name);
285     return error;
286   }
287 
288   if (IsGPR(reg))
289     return GPRRead(reg, reg_value);
290 
291   return Status("unimplemented");
292 }
293 
294 Status NativeRegisterContextWindows_x86_64::ReadAllRegisterValues(
295     lldb::DataBufferSP &data_sp) {
296   const size_t data_size = REG_CONTEXT_SIZE;
297   data_sp = std::make_shared<DataBufferHeap>(data_size, 0);
298   ::CONTEXT tls_context;
299   Status error =
300       GetThreadContextHelper(GetThreadHandle(), &tls_context, CONTEXT_ALL);
301   if (error.Fail())
302     return error;
303 
304   uint8_t *dst = data_sp->GetBytes();
305   ::memcpy(dst, &tls_context, data_size);
306   return error;
307 }
308 
309 Status NativeRegisterContextWindows_i386::WriteAllRegisterValues(
310     const lldb::DataBufferSP &data_sp) {
311   Status error;
312   const size_t data_size = REG_CONTEXT_SIZE;
313   if (!data_sp) {
314     error.SetErrorStringWithFormat(
315         "NativeRegisterContextWindows_i386::%s invalid data_sp provided",
316         __FUNCTION__);
317     return error;
318   }
319 
320   if (data_sp->GetByteSize() != data_size) {
321     error.SetErrorStringWithFormatv(
322         "data_sp contained mismatched data size, expected {0}, actual {1}",
323         data_size, data_sp->GetByteSize());
324     return error;
325   }
326 
327   ::CONTEXT tls_context;
328   memcpy(&tls_context, data_sp->GetBytes(), data_size);
329   return SetThreadContextHelper(GetThreadHandle(), &tls_context);
330 }
331 
332 Status NativeRegisterContextWindows_i386::IsWatchpointHit(uint32_t wp_index,
333                                                           bool &is_hit) {
334   return Status("unimplemented");
335 }
336 
337 Status NativeRegisterContextWindows_i386::GetWatchpointHitIndex(
338     uint32_t &wp_index, lldb::addr_t trap_addr) {
339   return Status("unimplemented");
340 }
341 
342 Status NativeRegisterContextWindows_i386::IsWatchpointVacant(uint32_t wp_index,
343                                                              bool &is_vacant) {
344   return Status("unimplemented");
345 }
346 
347 Status NativeRegisterContextWindows_i386::SetHardwareWatchpointWithIndex(
348     lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
349   return Status("unimplemented");
350 }
351 
352 bool NativeRegisterContextWindows_i386::ClearHardwareWatchpoint(
353     uint32_t wp_index) {
354   return false;
355 }
356 
357 Status NativeRegisterContextWindows_i386::ClearAllHardwareWatchpoints() {
358   return Status("unimplemented");
359 }
360 
361 uint32_t NativeRegisterContextWindows_i386::SetHardwareWatchpoint(
362     lldb::addr_t addr, size_t size, uint32_t watch_flags) {
363   return LLDB_INVALID_INDEX32;
364 }
365 
366 lldb::addr_t
367 NativeRegisterContextWindows_i386::GetWatchpointAddress(uint32_t wp_index) {
368   return LLDB_INVALID_ADDRESS;
369 }
370 
371 uint32_t NativeRegisterContextWindows_i386::NumSupportedHardwareWatchpoints() {
372   // Not implemented
373   return 0;
374 }
375 
376 #endif
377