brintos

brintos / llvm-project-archived public Read only

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