//===-- 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(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(m_thread); return ::SetThreadContext( wthread.GetHostThread().GetNativeThread().GetSystemHandle(), &m_context); }