1 //===-- NativeRegisterContextWindows_i386.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 #if defined(__i386__) || defined(_M_IX86)
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 *
CreateRegisterInfoInterface(const ArchSpec & target_arch)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 
GetThreadContextHelper(lldb::thread_t thread_handle,PCONTEXT context_ptr,const DWORD control_flag)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 
SetThreadContextHelper(lldb::thread_t thread_handle,PCONTEXT context_ptr)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>
CreateHostNativeRegisterContextWindows(const ArchSpec & target_arch,NativeThreadProtocol & native_thread)87 NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
88     const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
89   return std::make_unique<NativeRegisterContextWindows_i386>(target_arch,
90                                                              native_thread);
91 }
92 
NativeRegisterContextWindows_i386(const ArchSpec & target_arch,NativeThreadProtocol & native_thread)93 NativeRegisterContextWindows_i386::NativeRegisterContextWindows_i386(
94     const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
95     : NativeRegisterContextWindows(native_thread,
96                                    CreateRegisterInfoInterface(target_arch)) {}
97 
IsGPR(uint32_t reg_index) const98 bool NativeRegisterContextWindows_i386::IsGPR(uint32_t reg_index) const {
99   return (reg_index < k_first_alias_i386);
100 }
101 
IsDR(uint32_t reg_index) const102 bool NativeRegisterContextWindows_i386::IsDR(uint32_t reg_index) const {
103   return (reg_index >= lldb_dr0_i386 && reg_index <= lldb_dr7_i386);
104 }
105 
GetRegisterSetCount() const106 uint32_t NativeRegisterContextWindows_i386::GetRegisterSetCount() const {
107   return k_num_register_sets;
108 }
109 
110 const RegisterSet *
GetRegisterSet(uint32_t set_index) const111 NativeRegisterContextWindows_i386::GetRegisterSet(uint32_t set_index) const {
112   if (set_index >= k_num_register_sets)
113     return nullptr;
114   return &g_reg_sets_i386[set_index];
115 }
116 
GPRRead(const uint32_t reg,RegisterValue & reg_value)117 Status NativeRegisterContextWindows_i386::GPRRead(const uint32_t reg,
118                                                   RegisterValue &reg_value) {
119   ::CONTEXT tls_context;
120   DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
121   Status error =
122       GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag);
123   if (error.Fail())
124     return error;
125 
126   switch (reg) {
127   case lldb_eax_i386:
128     reg_value.SetUInt32(tls_context.Eax);
129     break;
130   case lldb_ebx_i386:
131     reg_value.SetUInt32(tls_context.Ebx);
132     break;
133   case lldb_ecx_i386:
134     reg_value.SetUInt32(tls_context.Ecx);
135     break;
136   case lldb_edx_i386:
137     reg_value.SetUInt32(tls_context.Edx);
138     break;
139   case lldb_edi_i386:
140     reg_value.SetUInt32(tls_context.Edi);
141     break;
142   case lldb_esi_i386:
143     reg_value.SetUInt32(tls_context.Esi);
144     break;
145   case lldb_ebp_i386:
146     reg_value.SetUInt32(tls_context.Ebp);
147     break;
148   case lldb_esp_i386:
149     reg_value.SetUInt32(tls_context.Esp);
150     break;
151   case lldb_eip_i386:
152     reg_value.SetUInt32(tls_context.Eip);
153     break;
154   case lldb_eflags_i386:
155     reg_value.SetUInt32(tls_context.EFlags);
156     break;
157   case lldb_cs_i386:
158     reg_value.SetUInt32(tls_context.SegCs);
159     break;
160   case lldb_fs_i386:
161     reg_value.SetUInt32(tls_context.SegFs);
162     break;
163   case lldb_gs_i386:
164     reg_value.SetUInt32(tls_context.SegGs);
165     break;
166   case lldb_ss_i386:
167     reg_value.SetUInt32(tls_context.SegSs);
168     break;
169   case lldb_ds_i386:
170     reg_value.SetUInt32(tls_context.SegDs);
171     break;
172   case lldb_es_i386:
173     reg_value.SetUInt32(tls_context.SegEs);
174     break;
175   }
176 
177   return error;
178 }
179 
180 Status
GPRWrite(const uint32_t reg,const RegisterValue & reg_value)181 NativeRegisterContextWindows_i386::GPRWrite(const uint32_t reg,
182                                             const RegisterValue &reg_value) {
183   ::CONTEXT tls_context;
184   DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
185   auto thread_handle = GetThreadHandle();
186   Status error =
187       GetThreadContextHelper(thread_handle, &tls_context, context_flag);
188   if (error.Fail())
189     return error;
190 
191   switch (reg) {
192   case lldb_eax_i386:
193     tls_context.Eax = reg_value.GetAsUInt32();
194     break;
195   case lldb_ebx_i386:
196     tls_context.Ebx = reg_value.GetAsUInt32();
197     break;
198   case lldb_ecx_i386:
199     tls_context.Ecx = reg_value.GetAsUInt32();
200     break;
201   case lldb_edx_i386:
202     tls_context.Edx = reg_value.GetAsUInt32();
203     break;
204   case lldb_edi_i386:
205     tls_context.Edi = reg_value.GetAsUInt32();
206     break;
207   case lldb_esi_i386:
208     tls_context.Esi = reg_value.GetAsUInt32();
209     break;
210   case lldb_ebp_i386:
211     tls_context.Ebp = reg_value.GetAsUInt32();
212     break;
213   case lldb_esp_i386:
214     tls_context.Esp = reg_value.GetAsUInt32();
215     break;
216   case lldb_eip_i386:
217     tls_context.Eip = reg_value.GetAsUInt32();
218     break;
219   case lldb_eflags_i386:
220     tls_context.EFlags = reg_value.GetAsUInt32();
221     break;
222   case lldb_cs_i386:
223     tls_context.SegCs = reg_value.GetAsUInt32();
224     break;
225   case lldb_fs_i386:
226     tls_context.SegFs = reg_value.GetAsUInt32();
227     break;
228   case lldb_gs_i386:
229     tls_context.SegGs = reg_value.GetAsUInt32();
230     break;
231   case lldb_ss_i386:
232     tls_context.SegSs = reg_value.GetAsUInt32();
233     break;
234   case lldb_ds_i386:
235     tls_context.SegDs = reg_value.GetAsUInt32();
236     break;
237   case lldb_es_i386:
238     tls_context.SegEs = reg_value.GetAsUInt32();
239     break;
240   }
241 
242   return SetThreadContextHelper(thread_handle, &tls_context);
243 }
244 
DRRead(const uint32_t reg,RegisterValue & reg_value)245 Status NativeRegisterContextWindows_i386::DRRead(const uint32_t reg,
246                                                  RegisterValue &reg_value) {
247   ::CONTEXT tls_context;
248   DWORD context_flag = CONTEXT_DEBUG_REGISTERS;
249   Status error =
250       GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag);
251   if (error.Fail())
252     return error;
253 
254   switch (reg) {
255   case lldb_dr0_i386:
256     reg_value.SetUInt32(tls_context.Dr0);
257     break;
258   case lldb_dr1_i386:
259     reg_value.SetUInt32(tls_context.Dr1);
260     break;
261   case lldb_dr2_i386:
262     reg_value.SetUInt32(tls_context.Dr2);
263     break;
264   case lldb_dr3_i386:
265     reg_value.SetUInt32(tls_context.Dr3);
266     break;
267   case lldb_dr4_i386:
268     return Status("register DR4 is obsolete");
269   case lldb_dr5_i386:
270     return Status("register DR5 is obsolete");
271   case lldb_dr6_i386:
272     reg_value.SetUInt32(tls_context.Dr6);
273     break;
274   case lldb_dr7_i386:
275     reg_value.SetUInt32(tls_context.Dr7);
276     break;
277   }
278 
279   return {};
280 }
281 
282 Status
DRWrite(const uint32_t reg,const RegisterValue & reg_value)283 NativeRegisterContextWindows_i386::DRWrite(const uint32_t reg,
284                                            const RegisterValue &reg_value) {
285   ::CONTEXT tls_context;
286   DWORD context_flag = CONTEXT_DEBUG_REGISTERS;
287   auto thread_handle = GetThreadHandle();
288   Status error =
289       GetThreadContextHelper(thread_handle, &tls_context, context_flag);
290   if (error.Fail())
291     return error;
292 
293   switch (reg) {
294   case lldb_dr0_i386:
295     tls_context.Dr0 = reg_value.GetAsUInt32();
296     break;
297   case lldb_dr1_i386:
298     tls_context.Dr1 = reg_value.GetAsUInt32();
299     break;
300   case lldb_dr2_i386:
301     tls_context.Dr2 = reg_value.GetAsUInt32();
302     break;
303   case lldb_dr3_i386:
304     tls_context.Dr3 = reg_value.GetAsUInt32();
305     break;
306   case lldb_dr4_i386:
307     return Status("register DR4 is obsolete");
308   case lldb_dr5_i386:
309     return Status("register DR5 is obsolete");
310   case lldb_dr6_i386:
311     tls_context.Dr6 = reg_value.GetAsUInt32();
312     break;
313   case lldb_dr7_i386:
314     tls_context.Dr7 = reg_value.GetAsUInt32();
315     break;
316   }
317 
318   return SetThreadContextHelper(thread_handle, &tls_context);
319 }
320 
321 Status
ReadRegister(const RegisterInfo * reg_info,RegisterValue & reg_value)322 NativeRegisterContextWindows_i386::ReadRegister(const RegisterInfo *reg_info,
323                                                 RegisterValue &reg_value) {
324   Status error;
325 
326   if (!reg_info) {
327     error.SetErrorString("reg_info NULL");
328     return error;
329   }
330 
331   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
332   if (reg == LLDB_INVALID_REGNUM) {
333     // This is likely an internal register for lldb use only and should not be
334     // directly queried.
335     error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
336                                    "register, cannot read directly",
337                                    reg_info->name);
338     return error;
339   }
340 
341   if (IsGPR(reg))
342     return GPRRead(reg, reg_value);
343 
344   if (IsDR(reg))
345     return DRRead(reg, reg_value);
346 
347   return Status("unimplemented");
348 }
349 
WriteRegister(const RegisterInfo * reg_info,const RegisterValue & reg_value)350 Status NativeRegisterContextWindows_i386::WriteRegister(
351     const RegisterInfo *reg_info, const RegisterValue &reg_value) {
352   Status error;
353 
354   if (!reg_info) {
355     error.SetErrorString("reg_info NULL");
356     return error;
357   }
358 
359   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
360   if (reg == LLDB_INVALID_REGNUM) {
361     // This is likely an internal register for lldb use only and should not be
362     // directly written.
363     error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
364                                    "register, cannot write directly",
365                                    reg_info->name);
366     return error;
367   }
368 
369   if (IsGPR(reg))
370     return GPRWrite(reg, reg_value);
371 
372   if (IsDR(reg))
373     return DRWrite(reg, reg_value);
374 
375   return Status("unimplemented");
376 }
377 
ReadAllRegisterValues(lldb::DataBufferSP & data_sp)378 Status NativeRegisterContextWindows_i386::ReadAllRegisterValues(
379     lldb::DataBufferSP &data_sp) {
380   const size_t data_size = REG_CONTEXT_SIZE;
381   data_sp = std::make_shared<DataBufferHeap>(data_size, 0);
382   ::CONTEXT tls_context;
383   Status error =
384       GetThreadContextHelper(GetThreadHandle(), &tls_context, CONTEXT_ALL);
385   if (error.Fail())
386     return error;
387 
388   uint8_t *dst = data_sp->GetBytes();
389   ::memcpy(dst, &tls_context, data_size);
390   return error;
391 }
392 
WriteAllRegisterValues(const lldb::DataBufferSP & data_sp)393 Status NativeRegisterContextWindows_i386::WriteAllRegisterValues(
394     const lldb::DataBufferSP &data_sp) {
395   Status error;
396   const size_t data_size = REG_CONTEXT_SIZE;
397   if (!data_sp) {
398     error.SetErrorStringWithFormat(
399         "NativeRegisterContextWindows_i386::%s invalid data_sp provided",
400         __FUNCTION__);
401     return error;
402   }
403 
404   if (data_sp->GetByteSize() != data_size) {
405     error.SetErrorStringWithFormatv(
406         "data_sp contained mismatched data size, expected {0}, actual {1}",
407         data_size, data_sp->GetByteSize());
408     return error;
409   }
410 
411   ::CONTEXT tls_context;
412   memcpy(&tls_context, data_sp->GetBytes(), data_size);
413   return SetThreadContextHelper(GetThreadHandle(), &tls_context);
414 }
415 
IsWatchpointHit(uint32_t wp_index,bool & is_hit)416 Status NativeRegisterContextWindows_i386::IsWatchpointHit(uint32_t wp_index,
417                                                           bool &is_hit) {
418   is_hit = false;
419 
420   if (wp_index >= NumSupportedHardwareWatchpoints())
421     return Status("watchpoint index out of range");
422 
423   RegisterValue reg_value;
424   Status error = DRRead(lldb_dr6_i386, reg_value);
425   if (error.Fail())
426     return error;
427 
428   is_hit = reg_value.GetAsUInt32() & (1 << wp_index);
429 
430   return {};
431 }
432 
GetWatchpointHitIndex(uint32_t & wp_index,lldb::addr_t trap_addr)433 Status NativeRegisterContextWindows_i386::GetWatchpointHitIndex(
434     uint32_t &wp_index, lldb::addr_t trap_addr) {
435   wp_index = LLDB_INVALID_INDEX32;
436 
437   for (uint32_t i = 0; i < NumSupportedHardwareWatchpoints(); i++) {
438     bool is_hit;
439     Status error = IsWatchpointHit(i, is_hit);
440     if (error.Fail())
441       return error;
442 
443     if (is_hit) {
444       wp_index = i;
445       return {};
446     }
447   }
448 
449   return {};
450 }
451 
IsWatchpointVacant(uint32_t wp_index,bool & is_vacant)452 Status NativeRegisterContextWindows_i386::IsWatchpointVacant(uint32_t wp_index,
453                                                              bool &is_vacant) {
454   is_vacant = false;
455 
456   if (wp_index >= NumSupportedHardwareWatchpoints())
457     return Status("Watchpoint index out of range");
458 
459   RegisterValue reg_value;
460   Status error = DRRead(lldb_dr7_i386, reg_value);
461   if (error.Fail())
462     return error;
463 
464   is_vacant = !(reg_value.GetAsUInt32() & (1 << (2 * wp_index)));
465 
466   return error;
467 }
468 
ClearHardwareWatchpoint(uint32_t wp_index)469 bool NativeRegisterContextWindows_i386::ClearHardwareWatchpoint(
470     uint32_t wp_index) {
471   if (wp_index >= NumSupportedHardwareWatchpoints())
472     return false;
473 
474   // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0, 1, 2, or 3 of
475   // the debug status register (DR6)
476 
477   RegisterValue reg_value;
478   Status error = DRRead(lldb_dr6_i386, reg_value);
479   if (error.Fail())
480     return false;
481 
482   uint32_t bit_mask = 1 << wp_index;
483   uint32_t status_bits = reg_value.GetAsUInt32() & ~bit_mask;
484   error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits));
485   if (error.Fail())
486     return false;
487 
488   // for watchpoints 0, 1, 2, or 3, respectively, clear bits {0-1,16-19},
489   // {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} of the debug control register
490   // (DR7)
491 
492   error = DRRead(lldb_dr7_i386, reg_value);
493   if (error.Fail())
494     return false;
495 
496   bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
497   uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask;
498   return DRWrite(lldb_dr7_i386, RegisterValue(control_bits)).Success();
499 }
500 
ClearAllHardwareWatchpoints()501 Status NativeRegisterContextWindows_i386::ClearAllHardwareWatchpoints() {
502   RegisterValue reg_value;
503 
504   // clear bits {0-4} of the debug status register (DR6)
505 
506   Status error = DRRead(lldb_dr6_i386, reg_value);
507   if (error.Fail())
508     return error;
509 
510   uint32_t status_bits = reg_value.GetAsUInt32() & ~0xF;
511   error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits));
512   if (error.Fail())
513     return error;
514 
515   // clear bits {0-7,16-31} of the debug control register (DR7)
516 
517   error = DRRead(lldb_dr7_i386, reg_value);
518   if (error.Fail())
519     return error;
520 
521   uint32_t control_bits = reg_value.GetAsUInt32() & ~0xFFFF00FF;
522   return DRWrite(lldb_dr7_i386, RegisterValue(control_bits));
523 }
524 
SetHardwareWatchpoint(lldb::addr_t addr,size_t size,uint32_t watch_flags)525 uint32_t NativeRegisterContextWindows_i386::SetHardwareWatchpoint(
526     lldb::addr_t addr, size_t size, uint32_t watch_flags) {
527   switch (size) {
528   case 1:
529   case 2:
530   case 4:
531     break;
532   default:
533     return LLDB_INVALID_INDEX32;
534   }
535 
536   if (watch_flags == 0x2)
537     watch_flags = 0x3;
538 
539   if (watch_flags != 0x1 && watch_flags != 0x3)
540     return LLDB_INVALID_INDEX32;
541 
542   for (uint32_t wp_index = 0; wp_index < NumSupportedHardwareWatchpoints();
543        ++wp_index) {
544     bool is_vacant;
545     if (IsWatchpointVacant(wp_index, is_vacant).Fail())
546       return LLDB_INVALID_INDEX32;
547 
548     if (is_vacant) {
549       if (!ClearHardwareWatchpoint(wp_index))
550         return LLDB_INVALID_INDEX32;
551 
552       if (ApplyHardwareBreakpoint(wp_index, addr, size, watch_flags).Fail())
553         return LLDB_INVALID_INDEX32;
554 
555       return wp_index;
556     }
557   }
558   return LLDB_INVALID_INDEX32;
559 }
560 
ApplyHardwareBreakpoint(uint32_t wp_index,lldb::addr_t addr,size_t size,uint32_t flags)561 Status NativeRegisterContextWindows_i386::ApplyHardwareBreakpoint(
562     uint32_t wp_index, lldb::addr_t addr, size_t size, uint32_t flags) {
563   RegisterValue reg_value;
564   auto error = DRRead(lldb_dr7_i386, reg_value);
565   if (error.Fail())
566     return error;
567 
568   // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7
569   uint32_t enable_bit = 1 << (2 * wp_index);
570 
571   // set bits 16-17, 20-21, 24-25, or 28-29
572   // with 0b01 for write, and 0b11 for read/write
573   uint32_t rw_bits = flags << (16 + 4 * wp_index);
574 
575   // set bits 18-19, 22-23, 26-27, or 30-31
576   // with 0b00, 0b01, 0b10, or 0b11
577   // for 1, 2, 8 (if supported), or 4 bytes, respectively
578   uint32_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index);
579 
580   uint32_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
581 
582   uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask;
583   control_bits |= enable_bit | rw_bits | size_bits;
584 
585   error = DRWrite(lldb_dr7_i386, RegisterValue(control_bits));
586   if (error.Fail())
587     return error;
588 
589   error = DRWrite(lldb_dr0_i386 + wp_index, RegisterValue(addr));
590   if (error.Fail())
591     return error;
592 
593   return {};
594 }
595 
596 lldb::addr_t
GetWatchpointAddress(uint32_t wp_index)597 NativeRegisterContextWindows_i386::GetWatchpointAddress(uint32_t wp_index) {
598   if (wp_index >= NumSupportedHardwareWatchpoints())
599     return LLDB_INVALID_ADDRESS;
600 
601   RegisterValue reg_value;
602   if (DRRead(lldb_dr0_i386 + wp_index, reg_value).Fail())
603     return LLDB_INVALID_ADDRESS;
604 
605   return reg_value.GetAsUInt32();
606 }
607 
NumSupportedHardwareWatchpoints()608 uint32_t NativeRegisterContextWindows_i386::NumSupportedHardwareWatchpoints() {
609   return 4;
610 }
611 
612 #endif
613