xref: /llvm-project/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp (revision 6ae657b08d624f9634fa6ebbf5d6fd7a22dc3b4d)
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", std::size(g_gpr_regnums_i386) - 1,
41      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 = GetLog(WindowsLog::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 = Status(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 = GetLog(WindowsLog::Registers);
75   Status error;
76 
77   if (!::SetThreadContext(thread_handle, context_ptr)) {
78     error = Status(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 std::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 bool NativeRegisterContextWindows_i386::IsDR(uint32_t reg_index) const {
103   return (reg_index >= lldb_dr0_i386 && reg_index <= lldb_dr7_i386);
104 }
105 
106 uint32_t NativeRegisterContextWindows_i386::GetRegisterSetCount() const {
107   return k_num_register_sets;
108 }
109 
110 const RegisterSet *
111 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 
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
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 
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::FromErrorString("register DR4 is obsolete");
269   case lldb_dr5_i386:
270     return Status::FromErrorString("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
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::FromErrorString("register DR4 is obsolete");
308   case lldb_dr5_i386:
309     return Status::FromErrorString("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
322 NativeRegisterContextWindows_i386::ReadRegister(const RegisterInfo *reg_info,
323                                                 RegisterValue &reg_value) {
324   Status error;
325 
326   if (!reg_info) {
327     error = Status::FromErrorString("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 = Status::FromErrorStringWithFormat(
336         "register \"%s\" is an internal-only lldb "
337         "register, cannot read directly",
338         reg_info->name);
339     return error;
340   }
341 
342   if (IsGPR(reg))
343     return GPRRead(reg, reg_value);
344 
345   if (IsDR(reg))
346     return DRRead(reg, reg_value);
347 
348   return Status::FromErrorString("unimplemented");
349 }
350 
351 Status NativeRegisterContextWindows_i386::WriteRegister(
352     const RegisterInfo *reg_info, const RegisterValue &reg_value) {
353   Status error;
354 
355   if (!reg_info) {
356     error = Status::FromErrorString("reg_info NULL");
357     return error;
358   }
359 
360   const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
361   if (reg == LLDB_INVALID_REGNUM) {
362     // This is likely an internal register for lldb use only and should not be
363     // directly written.
364     error = Status::FromErrorStringWithFormat(
365         "register \"%s\" is an internal-only lldb "
366         "register, cannot write directly",
367         reg_info->name);
368     return error;
369   }
370 
371   if (IsGPR(reg))
372     return GPRWrite(reg, reg_value);
373 
374   if (IsDR(reg))
375     return DRWrite(reg, reg_value);
376 
377   return Status::FromErrorString("unimplemented");
378 }
379 
380 Status NativeRegisterContextWindows_i386::ReadAllRegisterValues(
381     lldb::WritableDataBufferSP &data_sp) {
382   const size_t data_size = REG_CONTEXT_SIZE;
383   data_sp = std::make_shared<DataBufferHeap>(data_size, 0);
384   ::CONTEXT tls_context;
385   Status error =
386       GetThreadContextHelper(GetThreadHandle(), &tls_context, CONTEXT_ALL);
387   if (error.Fail())
388     return error;
389 
390   uint8_t *dst = data_sp->GetBytes();
391   ::memcpy(dst, &tls_context, data_size);
392   return error;
393 }
394 
395 Status NativeRegisterContextWindows_i386::WriteAllRegisterValues(
396     const lldb::DataBufferSP &data_sp) {
397   Status error;
398   const size_t data_size = REG_CONTEXT_SIZE;
399   if (!data_sp) {
400     error = Status::FromErrorStringWithFormat(
401         "NativeRegisterContextWindows_i386::%s invalid data_sp provided",
402         __FUNCTION__);
403     return error;
404   }
405 
406   if (data_sp->GetByteSize() != data_size) {
407     error = Status::FromErrorStringWithFormatv(
408         "data_sp contained mismatched data size, expected {0}, actual {1}",
409         data_size, data_sp->GetByteSize());
410     return error;
411   }
412 
413   ::CONTEXT tls_context;
414   memcpy(&tls_context, data_sp->GetBytes(), data_size);
415   return SetThreadContextHelper(GetThreadHandle(), &tls_context);
416 }
417 
418 Status NativeRegisterContextWindows_i386::IsWatchpointHit(uint32_t wp_index,
419                                                           bool &is_hit) {
420   is_hit = false;
421 
422   if (wp_index >= NumSupportedHardwareWatchpoints())
423     return Status::FromErrorString("watchpoint index out of range");
424 
425   RegisterValue reg_value;
426   Status error = DRRead(lldb_dr6_i386, reg_value);
427   if (error.Fail())
428     return error;
429 
430   is_hit = reg_value.GetAsUInt32() & (1 << wp_index);
431 
432   return {};
433 }
434 
435 Status NativeRegisterContextWindows_i386::GetWatchpointHitIndex(
436     uint32_t &wp_index, lldb::addr_t trap_addr) {
437   wp_index = LLDB_INVALID_INDEX32;
438 
439   for (uint32_t i = 0; i < NumSupportedHardwareWatchpoints(); i++) {
440     bool is_hit;
441     Status error = IsWatchpointHit(i, is_hit);
442     if (error.Fail())
443       return error;
444 
445     if (is_hit) {
446       wp_index = i;
447       return {};
448     }
449   }
450 
451   return {};
452 }
453 
454 Status NativeRegisterContextWindows_i386::IsWatchpointVacant(uint32_t wp_index,
455                                                              bool &is_vacant) {
456   is_vacant = false;
457 
458   if (wp_index >= NumSupportedHardwareWatchpoints())
459     return Status::FromErrorString("Watchpoint index out of range");
460 
461   RegisterValue reg_value;
462   Status error = DRRead(lldb_dr7_i386, reg_value);
463   if (error.Fail())
464     return error;
465 
466   is_vacant = !(reg_value.GetAsUInt32() & (1 << (2 * wp_index)));
467 
468   return error;
469 }
470 
471 bool NativeRegisterContextWindows_i386::ClearHardwareWatchpoint(
472     uint32_t wp_index) {
473   if (wp_index >= NumSupportedHardwareWatchpoints())
474     return false;
475 
476   // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0, 1, 2, or 3 of
477   // the debug status register (DR6)
478 
479   RegisterValue reg_value;
480   Status error = DRRead(lldb_dr6_i386, reg_value);
481   if (error.Fail())
482     return false;
483 
484   uint32_t bit_mask = 1 << wp_index;
485   uint32_t status_bits = reg_value.GetAsUInt32() & ~bit_mask;
486   error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits));
487   if (error.Fail())
488     return false;
489 
490   // for watchpoints 0, 1, 2, or 3, respectively, clear bits {0-1,16-19},
491   // {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} of the debug control register
492   // (DR7)
493 
494   error = DRRead(lldb_dr7_i386, reg_value);
495   if (error.Fail())
496     return false;
497 
498   bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
499   uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask;
500   return DRWrite(lldb_dr7_i386, RegisterValue(control_bits)).Success();
501 }
502 
503 Status NativeRegisterContextWindows_i386::ClearAllHardwareWatchpoints() {
504   RegisterValue reg_value;
505 
506   // clear bits {0-4} of the debug status register (DR6)
507 
508   Status error = DRRead(lldb_dr6_i386, reg_value);
509   if (error.Fail())
510     return error;
511 
512   uint32_t status_bits = reg_value.GetAsUInt32() & ~0xF;
513   error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits));
514   if (error.Fail())
515     return error;
516 
517   // clear bits {0-7,16-31} of the debug control register (DR7)
518 
519   error = DRRead(lldb_dr7_i386, reg_value);
520   if (error.Fail())
521     return error;
522 
523   uint32_t control_bits = reg_value.GetAsUInt32() & ~0xFFFF00FF;
524   return DRWrite(lldb_dr7_i386, RegisterValue(control_bits));
525 }
526 
527 uint32_t NativeRegisterContextWindows_i386::SetHardwareWatchpoint(
528     lldb::addr_t addr, size_t size, uint32_t watch_flags) {
529   switch (size) {
530   case 1:
531   case 2:
532   case 4:
533     break;
534   default:
535     return LLDB_INVALID_INDEX32;
536   }
537 
538   if (watch_flags == 0x2)
539     watch_flags = 0x3;
540 
541   if (watch_flags != 0x1 && watch_flags != 0x3)
542     return LLDB_INVALID_INDEX32;
543 
544   for (uint32_t wp_index = 0; wp_index < NumSupportedHardwareWatchpoints();
545        ++wp_index) {
546     bool is_vacant;
547     if (IsWatchpointVacant(wp_index, is_vacant).Fail())
548       return LLDB_INVALID_INDEX32;
549 
550     if (is_vacant) {
551       if (!ClearHardwareWatchpoint(wp_index))
552         return LLDB_INVALID_INDEX32;
553 
554       if (ApplyHardwareBreakpoint(wp_index, addr, size, watch_flags).Fail())
555         return LLDB_INVALID_INDEX32;
556 
557       return wp_index;
558     }
559   }
560   return LLDB_INVALID_INDEX32;
561 }
562 
563 Status NativeRegisterContextWindows_i386::ApplyHardwareBreakpoint(
564     uint32_t wp_index, lldb::addr_t addr, size_t size, uint32_t flags) {
565   RegisterValue reg_value;
566   auto error = DRRead(lldb_dr7_i386, reg_value);
567   if (error.Fail())
568     return error;
569 
570   // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7
571   uint32_t enable_bit = 1 << (2 * wp_index);
572 
573   // set bits 16-17, 20-21, 24-25, or 28-29
574   // with 0b01 for write, and 0b11 for read/write
575   uint32_t rw_bits = flags << (16 + 4 * wp_index);
576 
577   // set bits 18-19, 22-23, 26-27, or 30-31
578   // with 0b00, 0b01, 0b10, or 0b11
579   // for 1, 2, 8 (if supported), or 4 bytes, respectively
580   uint32_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index);
581 
582   uint32_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index));
583 
584   uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask;
585   control_bits |= enable_bit | rw_bits | size_bits;
586 
587   error = DRWrite(lldb_dr7_i386, RegisterValue(control_bits));
588   if (error.Fail())
589     return error;
590 
591   error = DRWrite(lldb_dr0_i386 + wp_index, RegisterValue(addr));
592   if (error.Fail())
593     return error;
594 
595   return {};
596 }
597 
598 lldb::addr_t
599 NativeRegisterContextWindows_i386::GetWatchpointAddress(uint32_t wp_index) {
600   if (wp_index >= NumSupportedHardwareWatchpoints())
601     return LLDB_INVALID_ADDRESS;
602 
603   RegisterValue reg_value;
604   if (DRRead(lldb_dr0_i386 + wp_index, reg_value).Fail())
605     return LLDB_INVALID_ADDRESS;
606 
607   return reg_value.GetAsUInt32();
608 }
609 
610 uint32_t NativeRegisterContextWindows_i386::NumSupportedHardwareWatchpoints() {
611   return 4;
612 }
613 
614 #endif
615