Differential D56233 Diff 194380 source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_wow64.cpp
Changeset View
Changeset View
Standalone View
Standalone View
source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_wow64.cpp
- This file was added.
//===-- NativeRegisterContextWindows_wow64.cpp ----- ------------*- C++ -*-===// | |||||
// | |||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||||
// See https://llvm.org/LICENSE.txt for license information. | |||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||||
// | |||||
//===----------------------------------------------------------------------===// | |||||
#if defined(_WIN64) | |||||
#include "NativeRegisterContextWindows_wow64.h" | |||||
#include "NativeThreadWindows.h" | |||||
#include "Plugins/Process/Utility/RegisterContextWindows_wow64.h" | |||||
#include "ProcessWindowsLog.h" | |||||
#include "lldb/Host/HostInfo.h" | |||||
#include "lldb/Host/HostThread.h" | |||||
#include "lldb/Host/Windows/HostThreadWindows.h" | |||||
#include "lldb/Host/windows/windows.h" | |||||
#include "lldb/Utility/Log.h" | |||||
#include "lldb/Utility/RegisterValue.h" | |||||
#include "llvm/ADT/STLExtras.h" | |||||
using namespace lldb; | |||||
using namespace lldb_private; | |||||
namespace { | |||||
static const uint32_t g_gpr_regnums_wow64[] = { | |||||
lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386, | |||||
lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386, | |||||
lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386, | |||||
lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386, | |||||
LLDB_INVALID_REGNUM // Register sets must be terminated with this flag. | |||||
}; | |||||
static size_t k_num_gprs = llvm::array_lengthof(g_gpr_regnums_wow64) - 1; | |||||
static const RegisterSet g_reg_sets_wow64[] = { | |||||
{"General Purpose Registers", "gpr", | |||||
llvm::array_lengthof(g_gpr_regnums_wow64) - 1, g_gpr_regnums_wow64}, | |||||
}; | |||||
static size_t k_num_register_sets = llvm::array_lengthof(g_gpr_regnums_wow64); | |||||
static RegisterInfoInterface * | |||||
CreateRegisterInfoInterface(const ArchSpec &target_arch) { | |||||
assert((target_arch.GetAddressByteSize() == 4 && | |||||
HostInfo::GetArchitecture().GetAddressByteSize() == 8) && | |||||
"Register setting path assumes this is a 64-bit host"); | |||||
return new RegisterContextWindows_wow64(target_arch); | |||||
} | |||||
} // namespace | |||||
#define REG_CONTEXT_SIZE sizeof(::WOW64_CONTEXT) | |||||
NativeRegisterContextWindows_wow64::NativeRegisterContextWindows_wow64( | |||||
const ArchSpec &target_arch, NativeThreadProtocol &native_thread) | |||||
: NativeRegisterContextWindows(native_thread, | |||||
CreateRegisterInfoInterface(target_arch)) {} | |||||
bool NativeRegisterContextWindows_wow64::IsGPR(uint32_t reg_index) const { | |||||
return (reg_index < k_first_alias_i386); | |||||
} | |||||
uint32_t NativeRegisterContextWindows_wow64::GetRegisterSetCount() const { | |||||
return k_num_register_sets; | |||||
} | |||||
const RegisterSet * | |||||
NativeRegisterContextWindows_wow64::GetRegisterSet(uint32_t set_index) const { | |||||
if (set_index >= k_num_register_sets) | |||||
return nullptr; | |||||
return &g_reg_sets_wow64[set_index]; | |||||
} | |||||
Status NativeRegisterContextWindows_wow64::GPRRead(const uint32_t reg, | |||||
RegisterValue ®_value) { | |||||
Status error; | |||||
Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); | |||||
::WOW64_CONTEXT tls_context; | |||||
NativeThreadWindows *wthread = static_cast<NativeThreadWindows *>(&m_thread); | |||||
auto host_thread = wthread->GetHostThread(); | |||||
memset(&tls_context, 0, sizeof(tls_context)); | |||||
tls_context.ContextFlags = | |||||
WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS; | |||||
if (!::Wow64GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), | |||||
&tls_context)) { | |||||
LLDB_LOG(log, | |||||
"GetThreadContext failed with error {0} while caching register " | |||||
"values.", | |||||
::GetLastError()); | |||||
return Status("error"); | |||||
} | |||||
LLDB_LOG(log, "successfully updated the register values."); | |||||
switch (reg) { | |||||
case lldb_eax_i386: | |||||
reg_value.SetUInt32(tls_context.Eax); | |||||
break; | |||||
case lldb_ebx_i386: | |||||
reg_value.SetUInt32(tls_context.Ebx); | |||||
break; | |||||
case lldb_esi_i386: | |||||
reg_value.SetUInt32(tls_context.Esi); | |||||
break; | |||||
case lldb_ebp_i386: | |||||
reg_value.SetUInt32(tls_context.Ebp); | |||||
break; | |||||
case lldb_esp_i386: | |||||
reg_value.SetUInt32(tls_context.Esp); | |||||
break; | |||||
case lldb_eip_i386: | |||||
reg_value.SetUInt32(tls_context.Eip); | |||||
break; | |||||
case lldb_eflags_i386: | |||||
reg_value.SetUInt32(tls_context.EFlags); | |||||
break; | |||||
case lldb_cs_i386: | |||||
reg_value.SetUInt32(tls_context.SegCs); | |||||
break; | |||||
case lldb_fs_i386: | |||||
reg_value.SetUInt32(tls_context.SegFs); | |||||
break; | |||||
case lldb_gs_i386: | |||||
reg_value.SetUInt32(tls_context.SegGs); | |||||
break; | |||||
case lldb_ss_i386: | |||||
reg_value.SetUInt32(tls_context.SegSs); | |||||
break; | |||||
case lldb_ds_i386: | |||||
reg_value.SetUInt32(tls_context.SegDs); | |||||
break; | |||||
case lldb_es_i386: | |||||
reg_value.SetUInt32(tls_context.SegEs); | |||||
break; | |||||
} | |||||
return error; | |||||
} | |||||
Status | |||||
NativeRegisterContextWindows_wow64::GPRWrite(const uint32_t reg, | |||||
const RegisterValue ®_value) { | |||||
Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); | |||||
Status error; | |||||
::WOW64_CONTEXT tls_context; | |||||
memset(&tls_context, 0, sizeof(tls_context)); | |||||
NativeThreadWindows *wthread = static_cast<NativeThreadWindows *>(&m_thread); | |||||
auto host_thread = wthread->GetHostThread(); | |||||
tls_context.ContextFlags = | |||||
WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS; | |||||
if (!::Wow64GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), | |||||
&tls_context)) { | |||||
LLDB_LOG(log, | |||||
"GetThreadContext failed with error {0} while caching register " | |||||
"values.", | |||||
::GetLastError()); | |||||
return Status("error"); | |||||
} | |||||
LLDB_LOG(log, "successfully updated the register values."); | |||||
switch (reg) { | |||||
case lldb_eax_i386: | |||||
tls_context.Eax = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_ebx_i386: | |||||
tls_context.Ebx = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_ecx_i386: | |||||
tls_context.Ecx = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_edx_i386: | |||||
tls_context.Edx = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_edi_i386: | |||||
tls_context.Edi = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_esi_i386: | |||||
tls_context.Esi = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_ebp_i386: | |||||
tls_context.Ebp = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_esp_i386: | |||||
tls_context.Esp = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_eip_i386: | |||||
tls_context.Eip = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_eflags_i386: | |||||
tls_context.EFlags = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_cs_i386: | |||||
tls_context.SegCs = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_fs_i386: | |||||
tls_context.SegFs = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_gs_i386: | |||||
tls_context.SegGs = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_ss_i386: | |||||
tls_context.SegSs = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_ds_i386: | |||||
tls_context.SegDs = reg_value.GetAsUInt32(); | |||||
break; | |||||
case lldb_es_i386: | |||||
tls_context.SegEs = reg_value.GetAsUInt32(); | |||||
break; | |||||
} | |||||
bool success = ::Wow64SetThreadContext( | |||||
host_thread.GetNativeThread().GetSystemHandle(), &tls_context); | |||||
if (!success) | |||||
return Status(::GetLastError(), eErrorTypeWin32); | |||||
return error; | |||||
} | |||||
Status | |||||
NativeRegisterContextWindows_wow64::ReadRegister(const RegisterInfo *reg_info, | |||||
RegisterValue ®_value) { | |||||
Status error; | |||||
if (!reg_info) { | |||||
error.SetErrorString("reg_info NULL"); | |||||
return error; | |||||
} | |||||
const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; | |||||
if (reg == LLDB_INVALID_REGNUM) { | |||||
// This is likely an internal register for lldb use only and should not be | |||||
// directly queried. | |||||
error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " | |||||
"register, cannot read directly", | |||||
reg_info->name); | |||||
return error; | |||||
} | |||||
if (IsGPR(reg)) | |||||
return GPRRead(reg, reg_value); | |||||
return Status("unimplemented"); | |||||
} | |||||
Status NativeRegisterContextWindows_wow64::WriteRegister( | |||||
const RegisterInfo *reg_info, const RegisterValue ®_value) { | |||||
Status error; | |||||
if (!reg_info) { | |||||
error.SetErrorString("reg_info NULL"); | |||||
return error; | |||||
} | |||||
const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; | |||||
if (reg == LLDB_INVALID_REGNUM) { | |||||
// This is likely an internal register for lldb use only and should not be | |||||
// directly queried. | |||||
error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " | |||||
"register, cannot read directly", | |||||
reg_info->name); | |||||
return error; | |||||
} | |||||
if (IsGPR(reg)) | |||||
return GPRWrite(reg, reg_value); | |||||
return Status("unimplemented"); | |||||
} | |||||
Status NativeRegisterContextWindows_wow64::ReadAllRegisterValues( | |||||
lldb::DataBufferSP &data_sp) { | |||||
Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); | |||||
Status error; | |||||
const size_t data_size = REG_CONTEXT_SIZE; | |||||
data_sp.reset(new DataBufferHeap(data_size, 0)); | |||||
if (!data_sp) { | |||||
error.SetErrorStringWithFormat( | |||||
"failed to allocate DataBufferHeap instance of size %" PRIu64, | |||||
data_size); | |||||
return error; | |||||
} | |||||
::WOW64_CONTEXT tls_context; | |||||
memset(&tls_context, 0, sizeof(tls_context)); | |||||
NativeThreadWindows *wthread = static_cast<NativeThreadWindows *>(&m_thread); | |||||
auto host_thread = wthread->GetHostThread(); | |||||
tls_context.ContextFlags = | |||||
WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS; | |||||
if (!::Wow64GetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), | |||||
&tls_context)) { | |||||
LLDB_LOG(log, "GetThreadContext failed with error {0}", ::GetLastError()); | |||||
return Status(::GetLastError(), eErrorTypeWin32); | |||||
} | |||||
LLDB_LOG(log, "successfully updated the register values."); | |||||
uint8_t *dst = data_sp->GetBytes(); | |||||
if (dst == nullptr) { | |||||
error.SetErrorStringWithFormat("DataBufferHeap instance of size %" PRIu64 | |||||
" returned a null pointer", | |||||
data_size); | |||||
return error; | |||||
} | |||||
::memcpy(dst, &tls_context, data_size); | |||||
return error; | |||||
} | |||||
Status NativeRegisterContextWindows_wow64::WriteAllRegisterValues( | |||||
const lldb::DataBufferSP &data_sp) { | |||||
Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); | |||||
Status error; | |||||
const size_t data_size = REG_CONTEXT_SIZE; | |||||
if (!data_sp) { | |||||
error.SetErrorStringWithFormat( | |||||
"NativeRegisterContextWindows::%s invalid data_sp provided", | |||||
__FUNCTION__); | |||||
return error; | |||||
} | |||||
if (data_sp->GetByteSize() != data_size) { | |||||
error.SetErrorStringWithFormatv( | |||||
"data_sp contained mismatched data size, expected {0}, actual {1}", | |||||
data_size, data_sp->GetByteSize()); | |||||
return error; | |||||
} | |||||
uint8_t *src = data_sp->GetBytes(); | |||||
if (src == nullptr) { | |||||
error.SetErrorStringWithFormat("NativeRegisterContextWindows::%s " | |||||
"DataBuffer::GetBytes() returned a null " | |||||
"pointer", | |||||
__FUNCTION__); | |||||
return error; | |||||
} | |||||
::WOW64_CONTEXT tls_context; | |||||
memcpy(&tls_context, data_sp->GetBytes(), data_size); | |||||
NativeThreadWindows *wthread = static_cast<NativeThreadWindows *>(&m_thread); | |||||
auto host_thread = wthread->GetHostThread(); | |||||
if (!::Wow64SetThreadContext(host_thread.GetNativeThread().GetSystemHandle(), | |||||
&tls_context)) { | |||||
LLDB_LOG(log, "SetThreadContext failed with error {0}", ::GetLastError()); | |||||
return Status(::GetLastError(), eErrorTypeWin32); | |||||
} | |||||
return error; | |||||
} | |||||
Status NativeRegisterContextWindows_wow64::IsWatchpointHit(uint32_t wp_index, | |||||
bool &is_hit) { | |||||
return Status("unimplemented"); | |||||
} | |||||
Status NativeRegisterContextWindows_wow64::GetWatchpointHitIndex( | |||||
uint32_t &wp_index, lldb::addr_t trap_addr) { | |||||
return Status("unimplemented"); | |||||
} | |||||
Status NativeRegisterContextWindows_wow64::IsWatchpointVacant(uint32_t wp_index, | |||||
bool &is_vacant) { | |||||
return Status("unimplemented"); | |||||
} | |||||
Status NativeRegisterContextWindows_wow64::SetHardwareWatchpointWithIndex( | |||||
lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { | |||||
return Status("unimplemented"); | |||||
} | |||||
bool NativeRegisterContextWindows_wow64::ClearHardwareWatchpoint( | |||||
uint32_t wp_index) { | |||||
return false; | |||||
} | |||||
Status NativeRegisterContextWindows_wow64::ClearAllHardwareWatchpoints() { | |||||
return Status("unimplemented"); | |||||
} | |||||
uint32_t NativeRegisterContextWindows_wow64::SetHardwareWatchpoint( | |||||
lldb::addr_t addr, size_t size, uint32_t watch_flags) { | |||||
return LLDB_INVALID_INDEX32; | |||||
} | |||||
lldb::addr_t | |||||
NativeRegisterContextWindows_wow64::GetWatchpointAddress(uint32_t wp_index) { | |||||
return LLDB_INVALID_ADDRESS; | |||||
} | |||||
uint32_t NativeRegisterContextWindows_wow64::NumSupportedHardwareWatchpoints() { | |||||
// Not implemented | |||||
return 0; | |||||
} | |||||
#endif // defined(_WIN64) |