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