blob: 099bae6b0f6190dd652b2dde71b0ad3da47d51a2 [file] [log] [blame]
//===-- RegisterContextWindows.cpp ----------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "lldb/Host/windows/HostThreadWindows.h"
#include "lldb/Host/windows/windows.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/Status.h"
#include "lldb/lldb-private-types.h"
#include "ProcessWindowsLog.h"
#include "RegisterContextWindows.h"
#include "TargetThreadWindows.h"
#include "llvm/ADT/STLExtras.h"
#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
const DWORD kWinContextFlags = CONTEXT_ALL;
// Constructors and Destructors
RegisterContextWindows::RegisterContextWindows(Thread &thread,
uint32_t concrete_frame_idx)
: RegisterContext(thread, concrete_frame_idx), m_context(),
m_context_stale(true) {}
RegisterContextWindows::~RegisterContextWindows() {}
void RegisterContextWindows::InvalidateAllRegisters() {
m_context_stale = true;
}
bool RegisterContextWindows::ReadAllRegisterValues(
lldb::DataBufferSP &data_sp) {
if (!CacheAllRegisterValues())
return false;
data_sp.reset(new DataBufferHeap(sizeof(CONTEXT), 0));
memcpy(data_sp->GetBytes(), &m_context, sizeof(m_context));
return true;
}
bool RegisterContextWindows::WriteAllRegisterValues(
const lldb::DataBufferSP &data_sp) {
assert(data_sp->GetByteSize() >= sizeof(m_context));
memcpy(&m_context, data_sp->GetBytes(), sizeof(m_context));
return ApplyAllRegisterValues();
}
uint32_t RegisterContextWindows::ConvertRegisterKindToRegisterNumber(
lldb::RegisterKind kind, uint32_t num) {
const uint32_t num_regs = GetRegisterCount();
assert(kind < kNumRegisterKinds);
for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx);
if (reg_info->kinds[kind] == num)
return reg_idx;
}
return LLDB_INVALID_REGNUM;
}
bool RegisterContextWindows::HardwareSingleStep(bool enable) { return false; }
bool RegisterContextWindows::AddHardwareBreakpoint(uint32_t slot,
lldb::addr_t address,
uint32_t size, bool read,
bool write) {
if (slot >= NUM_HARDWARE_BREAKPOINT_SLOTS)
return false;
switch (size) {
case 1:
case 2:
case 4:
#if defined(_WIN64)
case 8:
#endif
break;
default:
return false;
}
if (!CacheAllRegisterValues())
return false;
#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_AMD64)
unsigned shift = 2 * slot;
m_context.Dr7 |= 1ULL << shift;
(&m_context.Dr0)[slot] = address;
shift = 18 + 4 * slot;
m_context.Dr7 &= ~(3ULL << shift);
m_context.Dr7 |= (size == 8 ? 2ULL : size - 1) << shift;
shift = 16 + 4 * slot;
m_context.Dr7 &= ~(3ULL << shift);
m_context.Dr7 |= (read ? 3ULL : (write ? 1ULL : 0)) << shift;
return ApplyAllRegisterValues();
#else
Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
LLDB_LOG(log, "hardware breakpoints not currently supported on this arch");
return false;
#endif
}
bool RegisterContextWindows::RemoveHardwareBreakpoint(uint32_t slot) {
if (slot >= NUM_HARDWARE_BREAKPOINT_SLOTS)
return false;
if (!CacheAllRegisterValues())
return false;
#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_AMD64)
unsigned shift = 2 * slot;
m_context.Dr7 &= ~(1ULL << shift);
return ApplyAllRegisterValues();
#else
return false;
#endif
}
uint32_t RegisterContextWindows::GetTriggeredHardwareBreakpointSlotId() {
if (!CacheAllRegisterValues())
return LLDB_INVALID_INDEX32;
#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_AMD64)
for (unsigned i = 0UL; i < NUM_HARDWARE_BREAKPOINT_SLOTS; i++)
if (m_context.Dr6 & (1ULL << i))
return i;
#endif
return LLDB_INVALID_INDEX32;
}
bool RegisterContextWindows::CacheAllRegisterValues() {
Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS);
if (!m_context_stale)
return true;
TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread);
memset(&m_context, 0, sizeof(m_context));
m_context.ContextFlags = kWinContextFlags;
if (::SuspendThread(
wthread.GetHostThread().GetNativeThread().GetSystemHandle()) ==
(DWORD)-1) {
return false;
}
if (!::GetThreadContext(
wthread.GetHostThread().GetNativeThread().GetSystemHandle(),
&m_context)) {
LLDB_LOG(
log,
"GetThreadContext failed with error {0} while caching register values.",
::GetLastError());
return false;
}
if (::ResumeThread(
wthread.GetHostThread().GetNativeThread().GetSystemHandle()) ==
(DWORD)-1) {
return false;
}
LLDB_LOG(log, "successfully updated the register values.");
m_context_stale = false;
return true;
}
bool RegisterContextWindows::ApplyAllRegisterValues() {
TargetThreadWindows &wthread = static_cast<TargetThreadWindows &>(m_thread);
return ::SetThreadContext(
wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context);
}