https://github.com/wangleiat updated https://github.com/llvm/llvm-project/pull/118043
>From a7cba7ef089a6f2004b1879d30675652729370e5 Mon Sep 17 00:00:00 2001 From: wanglei <wang...@loongson.cn> Date: Fri, 29 Nov 2024 10:43:31 +0800 Subject: [PATCH 1/2] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?= =?UTF-8?q?itial=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.5-bogner --- ...NativeRegisterContextLinux_loongarch64.cpp | 484 ++++++++++++++++++ .../NativeRegisterContextLinux_loongarch64.h | 60 +++ .../GDBRemoteCommunicationServerCommon.cpp | 3 +- lldb/source/Target/Process.cpp | 3 +- 4 files changed, 548 insertions(+), 2 deletions(-) diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp index f4d1bb297049da..1f73bd0467962d 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp @@ -11,7 +11,9 @@ #include "NativeRegisterContextLinux_loongarch64.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Host/linux/Ptrace.h" #include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/Status.h" @@ -32,6 +34,19 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_linux; +// CTRL_PLV3_ENABLE, used to enable breakpoint/watchpoint +constexpr uint32_t g_enable_bit = 0x10; + +// Returns appropriate control register bits for the specified size +// size encoded: +// case 1 : 0b11 +// case 2 : 0b10 +// case 4 : 0b01 +// case 8 : 0b00 +static inline uint64_t GetSizeBits(int size) { + return (3 - llvm::Log2_32(size)) << 10; +} + std::unique_ptr<NativeRegisterContextLinux> NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( const ArchSpec &target_arch, NativeThreadLinux &native_thread) { @@ -61,6 +76,8 @@ NativeRegisterContextLinux_loongarch64::NativeRegisterContextLinux_loongarch64( NativeRegisterContextLinux(native_thread) { ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr, 0, sizeof(m_gpr)); + ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); + ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); m_gpr_is_valid = false; m_fpu_is_valid = false; @@ -337,4 +354,471 @@ NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters( return expedited_reg_nums; } +uint32_t +NativeRegisterContextLinux_loongarch64::NumSupportedHardwareBreakpoints() { + Log *log = GetLog(LLDBLog::Breakpoints); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) { + LLDB_LOG(log, "failed to read debug registers"); + return 0; + } + + LLDB_LOG(log, "{0}", m_max_hbp_supported); + return m_max_hbp_supported; +} + +uint32_t +NativeRegisterContextLinux_loongarch64::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + Log *log = GetLog(LLDBLog::Breakpoints); + LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) { + LLDB_LOG(log, "unable to set breakpoint: failed to read debug registers"); + return LLDB_INVALID_INDEX32; + } + + uint32_t bp_index = 0; + + // Check if size has a valid hardware breakpoint length. + if (size != 4) + return LLDB_INVALID_INDEX32; // Invalid size for a LoongArch hardware + // breakpoint + + // Check 4-byte alignment for hardware breakpoint target address. + if (addr & 0x03) + return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned. + + // Iterate over stored breakpoints and find a free bp_index + bp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + if (!BreakpointIsEnabled(i)) + bp_index = i; // Mark last free slot + else if (m_hbp_regs[i].address == addr) + return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints. + } + + if (bp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Update breakpoint in local cache + m_hbp_regs[bp_index].address = addr; + m_hbp_regs[bp_index].control = g_enable_bit; + + // PTRACE call to set corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) { + m_hbp_regs[bp_index].address = 0; + m_hbp_regs[bp_index].control = 0; + + LLDB_LOG(log, "unable to set breakpoint: failed to write debug registers"); + return LLDB_INVALID_INDEX32; + } + + return bp_index; +} +bool NativeRegisterContextLinux_loongarch64::ClearHardwareBreakpoint( + uint32_t hw_idx) { + Log *log = GetLog(LLDBLog::Breakpoints); + LLDB_LOG(log, "hw_idx: {0}", hw_idx); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) { + LLDB_LOG(log, "unable to clear breakpoint: failed to read debug registers"); + return false; + } + + if (hw_idx >= m_max_hbp_supported) + return false; + + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address; + uint32_t tempControl = m_hbp_regs[hw_idx].control; + + m_hbp_regs[hw_idx].control = 0; + m_hbp_regs[hw_idx].address = 0; + + // PTRACE call to clear corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) { + m_hbp_regs[hw_idx].control = tempControl; + m_hbp_regs[hw_idx].address = tempAddr; + + LLDB_LOG(log, + "unable to clear breakpoint: failed to write debug registers"); + return false; + } + + return true; +} +Status NativeRegisterContextLinux_loongarch64::GetHardwareBreakHitIndex( + uint32_t &bp_index, lldb::addr_t trap_addr) { + Log *log = GetLog(LLDBLog::Breakpoints); + + LLDB_LOGF(log, "NativeRegisterContextLinux_loongarch64::%s()", __FUNCTION__); + + lldb::addr_t break_addr; + + for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { + break_addr = m_hbp_regs[bp_index].address; + + if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) { + m_hbp_regs[bp_index].hit_addr = trap_addr; + return Status(); + } + } + + bp_index = LLDB_INVALID_INDEX32; + return Status(); +} +Status NativeRegisterContextLinux_loongarch64::ClearAllHardwareBreakpoints() { + Log *log = GetLog(LLDBLog::Breakpoints); + + LLDB_LOGF(log, "NativeRegisterContextLinux_loongarch64::%s()", __FUNCTION__); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) + return error; + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + if (!BreakpointIsEnabled(i)) + continue; + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbp_regs[i].address; + uint32_t tempControl = m_hbp_regs[i].control; + + // Clear watchpoints in local cache + m_hbp_regs[i].control = 0; + m_hbp_regs[i].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) { + m_hbp_regs[i].control = tempControl; + m_hbp_regs[i].address = tempAddr; + + return error; + } + } + + return Status(); +} +bool NativeRegisterContextLinux_loongarch64::BreakpointIsEnabled( + uint32_t bp_index) { + Log *log = GetLog(LLDBLog::Breakpoints); + LLDB_LOG(log, "bp_index: {0}", bp_index); + return ((m_hbp_regs[bp_index].control & g_enable_bit) != 0); +} + +uint32_t +NativeRegisterContextLinux_loongarch64::NumSupportedHardwareWatchpoints() { + Log *log = GetLog(LLDBLog::Watchpoints); + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) { + LLDB_LOG(log, "failed to read debug registers"); + return 0; + } + + return m_max_hwp_supported; +} + +uint32_t NativeRegisterContextLinux_loongarch64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, + watch_flags); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) { + LLDB_LOG(log, "unable to set watchpoint: failed to read debug registers"); + return LLDB_INVALID_INDEX32; + } + + uint32_t control_value = 0, wp_index = 0; + + // Check if we are setting watchpoint other than read/write/access Update + // watchpoint flag to match loongarch64 write-read bit configuration. + switch (watch_flags) { + case eWatchpointKindWrite: + watch_flags = 2; + break; + case eWatchpointKindRead: + watch_flags = 1; + break; + case (eWatchpointKindRead | eWatchpointKindWrite): + break; + default: + return LLDB_INVALID_INDEX32; + } + + // Check if size has a valid hardware watchpoint length. + if (size != 1 && size != 2 && size != 4 && size != 8) + return LLDB_INVALID_INDEX32; + + // Setup control value + control_value = g_enable_bit | GetSizeBits(size); + control_value |= watch_flags << 8; + + // Iterate over stored watchpoints and find a free wp_index + wp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if (!WatchpointIsEnabled(i)) { + wp_index = i; // Mark last free slot + } else if (m_hwp_regs[i].address == addr) { + return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control = 0; + + LLDB_LOG(log, "unable to set watchpoint: failed to write debug registers"); + return LLDB_INVALID_INDEX32; + } + + return wp_index; +} + +bool NativeRegisterContextLinux_loongarch64::ClearHardwareWatchpoint( + uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) { + LLDB_LOG(log, "unable to clear watchpoint: failed to read debug registers"); + return false; + } + + if (wp_index >= m_max_hwp_supported) + return false; + + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control = 0; + m_hwp_regs[wp_index].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + + LLDB_LOG(log, "unable to clear watchpoint: failed to read debug registers"); + return false; + } + + return true; +} + +Status NativeRegisterContextLinux_loongarch64::ClearAllHardwareWatchpoints() { + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) + return error; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if (!WatchpointIsEnabled(i)) + continue; + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[i].address; + uint32_t tempControl = m_hwp_regs[i].control; + + // Clear watchpoints in local cache + m_hwp_regs[i].control = 0; + m_hwp_regs[i].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) { + m_hwp_regs[i].control = tempControl; + m_hwp_regs[i].address = tempAddr; + + return error; + } + } + + return Status(); +} + +uint32_t +NativeRegisterContextLinux_loongarch64::GetWatchpointSize(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + switch ((m_hwp_regs[wp_index].control >> 10) & 0x3) { + case 0x0: + return 8; + case 0x1: + return 4; + case 0x2: + return 2; + case 0x3: + return 1; + default: + return 0; + } +} + +bool NativeRegisterContextLinux_loongarch64::WatchpointIsEnabled( + uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + return ((m_hwp_regs[wp_index].control & g_enable_bit) != 0); +} + +Status NativeRegisterContextLinux_loongarch64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) + return error; + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { + watch_size = GetWatchpointSize(wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && + trap_addr <= watch_addr + watch_size) { + m_hwp_regs[wp_index].hit_addr = trap_addr; + return Status(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +lldb::addr_t NativeRegisterContextLinux_loongarch64::GetWatchpointAddress( + uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].address; + + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t NativeRegisterContextLinux_loongarch64::GetWatchpointHitAddress( + uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].hit_addr; + + return LLDB_INVALID_ADDRESS; +} + +Status NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) + return Status(); + + ::pid_t tid = m_thread.GetID(); + + int regset = NT_LOONGARCH_HW_WATCH; + struct iovec ioVec; + struct user_watch_state dreg_state; + Status error; + + ioVec.iov_base = &dreg_state; + ioVec.iov_len = sizeof(dreg_state); + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, + &ioVec, ioVec.iov_len); + if (error.Fail()) + return error; + + m_max_hwp_supported = dreg_state.dbg_info & 0x3f; + + regset = NT_LOONGARCH_HW_BREAK; + error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, + &ioVec, ioVec.iov_len); + if (error.Fail()) + return error; + + m_max_hbp_supported = dreg_state.dbg_info & 0x3f; + + m_refresh_hwdebug_info = false; + + return error; +} + +Status NativeRegisterContextLinux_loongarch64::WriteHardwareDebugRegs( + DREGType hwbType) { + struct iovec ioVec; + struct user_watch_state dreg_state; + int regset; + + memset(&dreg_state, 0, sizeof(dreg_state)); + ioVec.iov_base = &dreg_state; + + switch (hwbType) { + case eDREGTypeWATCH: + regset = NT_LOONGARCH_HW_WATCH; + ioVec.iov_len = sizeof(dreg_state.dbg_info) + + (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported); + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control; + } + break; + case eDREGTypeBREAK: + regset = NT_LOONGARCH_HW_BREAK; + ioVec.iov_len = sizeof(dreg_state.dbg_info) + + (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported); + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control; + } + break; + } + + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), + ®set, &ioVec, ioVec.iov_len); +} #endif // defined(__loongarch__) && __loongarch_grlen == 64 diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h index 0a6084ff4206db..6599677924d41d 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h @@ -51,6 +51,42 @@ class NativeRegisterContextLinux_loongarch64 bool RegisterOffsetIsDynamic() const override { return true; } + // Hardware breakpoints management functions + + uint32_t NumSupportedHardwareBreakpoints() override; + + uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + + Status ClearAllHardwareBreakpoints() override; + + Status GetHardwareBreakHitIndex(uint32_t &bp_index, + lldb::addr_t trap_addr) override; + + bool BreakpointIsEnabled(uint32_t bp_index); + + // Hardware watchpoints management functions + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t GetWatchpointSize(uint32_t wp_index); + + bool WatchpointIsEnabled(uint32_t wp_index); + protected: Status ReadGPR() override; @@ -83,6 +119,30 @@ class NativeRegisterContextLinux_loongarch64 uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; const RegisterInfoPOSIX_loongarch64 &GetRegisterInfo() const; + + // Debug register type select + enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK }; + + Status ReadHardwareDebugInfo(); + Status WriteHardwareDebugRegs(DREGType hwbType); + + // Debug register info for hardware watchpoints management. + struct DREG { + lldb::addr_t address; // Breakpoint/watchpoint address value. + lldb::addr_t hit_addr; // Address at which last watchpoint trigger + // exception occurred. + uint32_t control; // Breakpoint/watchpoint control value. + }; + + std::array<struct DREG, 14> m_hbp_regs; // hardware breakpoints + std::array<struct DREG, 14> m_hwp_regs; // hardware watchpoints + + // Refer to: + // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints + // 14 is just a maximum value, query hardware for actual watchpoint count. + uint32_t m_max_hwp_supported = 14; + uint32_t m_max_hbp_supported = 14; + bool m_refresh_hwdebug_info = true; }; } // namespace process_linux diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp index 324db3db7eb4c7..c2fe05cad566ef 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -231,7 +231,8 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo( host_arch.GetMachine() == llvm::Triple::aarch64_32 || host_arch.GetMachine() == llvm::Triple::aarch64_be || host_arch.GetMachine() == llvm::Triple::arm || - host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS()) + host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS() || + host_arch.GetTriple().isLoongArch()) response.Printf("watchpoint_exceptions_received:before;"); else response.Printf("watchpoint_exceptions_received:after;"); diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index db33525978a16a..68485a40a3fcce 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2517,7 +2517,8 @@ bool Process::GetWatchpointReportedAfter() { llvm::Triple triple = arch.GetTriple(); if (triple.isMIPS() || triple.isPPC64() || triple.isRISCV() || - triple.isAArch64() || triple.isArmMClass() || triple.isARM()) + triple.isAArch64() || triple.isArmMClass() || triple.isARM() || + triple.isLoongArch()) reported_after = false; return reported_after; >From 94596521121634dcfe5e357e1444c0fbe725ef63 Mon Sep 17 00:00:00 2001 From: wanglei <wang...@loongson.cn> Date: Thu, 5 Dec 2024 17:11:56 +0800 Subject: [PATCH 2/2] Introduce NativeRegisterContextDBReg class Created using spr 1.3.5-bogner --- .../NativeRegisterContextLinux_arm64.cpp | 21 +- .../Linux/NativeRegisterContextLinux_arm64.h | 4 +- ...NativeRegisterContextLinux_loongarch64.cpp | 484 ------------------ .../NativeRegisterContextLinux_loongarch64.h | 60 --- .../Plugins/Process/Utility/CMakeLists.txt | 1 + .../Utility/NativeRegisterContextDBReg.cpp | 271 ++++++++++ .../Utility/NativeRegisterContextDBReg.h | 76 +++ .../NativeRegisterContextDBReg_arm64.cpp | 342 ++----------- .../NativeRegisterContextDBReg_arm64.h | 68 +-- .../GDBRemoteCommunicationServerCommon.cpp | 3 +- lldb/source/Target/Process.cpp | 3 +- 11 files changed, 402 insertions(+), 931 deletions(-) create mode 100644 lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp create mode 100644 lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp index 6056f3001fed6e..55dd0583890403 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -202,6 +202,9 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64( m_max_hwp_supported = 16; m_max_hbp_supported = 16; + // E (bit 0), used to enable breakpoint/watchpoint + m_hw_dbg_enable_bit = 1; + m_refresh_hwdebug_info = true; m_gpr_is_valid = false; @@ -1067,10 +1070,9 @@ bool NativeRegisterContextLinux_arm64::IsFPMR(unsigned reg) const { return GetRegisterInfo().IsFPMRReg(reg); } -llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { - if (!m_refresh_hwdebug_info) { - return llvm::Error::success(); - } +Status NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) + return Status(); ::pid_t tid = m_thread.GetID(); @@ -1085,7 +1087,7 @@ llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { &ioVec, ioVec.iov_len); if (error.Fail()) - return error.ToError(); + return error; m_max_hwp_supported = dreg_state.dbg_info & 0xff; @@ -1094,15 +1096,15 @@ llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { &ioVec, ioVec.iov_len); if (error.Fail()) - return error.ToError(); + return error; m_max_hbp_supported = dreg_state.dbg_info & 0xff; m_refresh_hwdebug_info = false; - return llvm::Error::success(); + return Status(); } -llvm::Error +Status NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(DREGType hwbType) { struct iovec ioVec; struct user_hwdebug_state dreg_state; @@ -1135,8 +1137,7 @@ NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(DREGType hwbType) { } return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), - ®set, &ioVec, ioVec.iov_len) - .ToError(); + ®set, &ioVec, ioVec.iov_len); } Status NativeRegisterContextLinux_arm64::ReadGPR() { diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h index 16190b5492582b..2730a61df8bcf8 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -234,9 +234,9 @@ class NativeRegisterContextLinux_arm64 size_t GetFPMRBufferSize() { return sizeof(m_fpmr_reg); } - llvm::Error ReadHardwareDebugInfo() override; + Status ReadHardwareDebugInfo() override; - llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; + Status WriteHardwareDebugRegs(DREGType hwbType) override; uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp index 1f73bd0467962d..f4d1bb297049da 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.cpp @@ -11,9 +11,7 @@ #include "NativeRegisterContextLinux_loongarch64.h" #include "lldb/Host/HostInfo.h" -#include "lldb/Host/linux/Ptrace.h" #include "lldb/Utility/DataBufferHeap.h" -#include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/Status.h" @@ -34,19 +32,6 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_linux; -// CTRL_PLV3_ENABLE, used to enable breakpoint/watchpoint -constexpr uint32_t g_enable_bit = 0x10; - -// Returns appropriate control register bits for the specified size -// size encoded: -// case 1 : 0b11 -// case 2 : 0b10 -// case 4 : 0b01 -// case 8 : 0b00 -static inline uint64_t GetSizeBits(int size) { - return (3 - llvm::Log2_32(size)) << 10; -} - std::unique_ptr<NativeRegisterContextLinux> NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux( const ArchSpec &target_arch, NativeThreadLinux &native_thread) { @@ -76,8 +61,6 @@ NativeRegisterContextLinux_loongarch64::NativeRegisterContextLinux_loongarch64( NativeRegisterContextLinux(native_thread) { ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr, 0, sizeof(m_gpr)); - ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); - ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); m_gpr_is_valid = false; m_fpu_is_valid = false; @@ -354,471 +337,4 @@ NativeRegisterContextLinux_loongarch64::GetExpeditedRegisters( return expedited_reg_nums; } -uint32_t -NativeRegisterContextLinux_loongarch64::NumSupportedHardwareBreakpoints() { - Log *log = GetLog(LLDBLog::Breakpoints); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) { - LLDB_LOG(log, "failed to read debug registers"); - return 0; - } - - LLDB_LOG(log, "{0}", m_max_hbp_supported); - return m_max_hbp_supported; -} - -uint32_t -NativeRegisterContextLinux_loongarch64::SetHardwareBreakpoint(lldb::addr_t addr, - size_t size) { - Log *log = GetLog(LLDBLog::Breakpoints); - LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - if (error.Fail()) { - LLDB_LOG(log, "unable to set breakpoint: failed to read debug registers"); - return LLDB_INVALID_INDEX32; - } - - uint32_t bp_index = 0; - - // Check if size has a valid hardware breakpoint length. - if (size != 4) - return LLDB_INVALID_INDEX32; // Invalid size for a LoongArch hardware - // breakpoint - - // Check 4-byte alignment for hardware breakpoint target address. - if (addr & 0x03) - return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned. - - // Iterate over stored breakpoints and find a free bp_index - bp_index = LLDB_INVALID_INDEX32; - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - if (!BreakpointIsEnabled(i)) - bp_index = i; // Mark last free slot - else if (m_hbp_regs[i].address == addr) - return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints. - } - - if (bp_index == LLDB_INVALID_INDEX32) - return LLDB_INVALID_INDEX32; - - // Update breakpoint in local cache - m_hbp_regs[bp_index].address = addr; - m_hbp_regs[bp_index].control = g_enable_bit; - - // PTRACE call to set corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(eDREGTypeBREAK); - - if (error.Fail()) { - m_hbp_regs[bp_index].address = 0; - m_hbp_regs[bp_index].control = 0; - - LLDB_LOG(log, "unable to set breakpoint: failed to write debug registers"); - return LLDB_INVALID_INDEX32; - } - - return bp_index; -} -bool NativeRegisterContextLinux_loongarch64::ClearHardwareBreakpoint( - uint32_t hw_idx) { - Log *log = GetLog(LLDBLog::Breakpoints); - LLDB_LOG(log, "hw_idx: {0}", hw_idx); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - if (error.Fail()) { - LLDB_LOG(log, "unable to clear breakpoint: failed to read debug registers"); - return false; - } - - if (hw_idx >= m_max_hbp_supported) - return false; - - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address; - uint32_t tempControl = m_hbp_regs[hw_idx].control; - - m_hbp_regs[hw_idx].control = 0; - m_hbp_regs[hw_idx].address = 0; - - // PTRACE call to clear corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(eDREGTypeBREAK); - - if (error.Fail()) { - m_hbp_regs[hw_idx].control = tempControl; - m_hbp_regs[hw_idx].address = tempAddr; - - LLDB_LOG(log, - "unable to clear breakpoint: failed to write debug registers"); - return false; - } - - return true; -} -Status NativeRegisterContextLinux_loongarch64::GetHardwareBreakHitIndex( - uint32_t &bp_index, lldb::addr_t trap_addr) { - Log *log = GetLog(LLDBLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_loongarch64::%s()", __FUNCTION__); - - lldb::addr_t break_addr; - - for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { - break_addr = m_hbp_regs[bp_index].address; - - if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) { - m_hbp_regs[bp_index].hit_addr = trap_addr; - return Status(); - } - } - - bp_index = LLDB_INVALID_INDEX32; - return Status(); -} -Status NativeRegisterContextLinux_loongarch64::ClearAllHardwareBreakpoints() { - Log *log = GetLog(LLDBLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_loongarch64::%s()", __FUNCTION__); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - if (error.Fail()) - return error; - - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - if (!BreakpointIsEnabled(i)) - continue; - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hbp_regs[i].address; - uint32_t tempControl = m_hbp_regs[i].control; - - // Clear watchpoints in local cache - m_hbp_regs[i].control = 0; - m_hbp_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(eDREGTypeBREAK); - - if (error.Fail()) { - m_hbp_regs[i].control = tempControl; - m_hbp_regs[i].address = tempAddr; - - return error; - } - } - - return Status(); -} -bool NativeRegisterContextLinux_loongarch64::BreakpointIsEnabled( - uint32_t bp_index) { - Log *log = GetLog(LLDBLog::Breakpoints); - LLDB_LOG(log, "bp_index: {0}", bp_index); - return ((m_hbp_regs[bp_index].control & g_enable_bit) != 0); -} - -uint32_t -NativeRegisterContextLinux_loongarch64::NumSupportedHardwareWatchpoints() { - Log *log = GetLog(LLDBLog::Watchpoints); - Status error = ReadHardwareDebugInfo(); - if (error.Fail()) { - LLDB_LOG(log, "failed to read debug registers"); - return 0; - } - - return m_max_hwp_supported; -} - -uint32_t NativeRegisterContextLinux_loongarch64::SetHardwareWatchpoint( - lldb::addr_t addr, size_t size, uint32_t watch_flags) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, - watch_flags); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) { - LLDB_LOG(log, "unable to set watchpoint: failed to read debug registers"); - return LLDB_INVALID_INDEX32; - } - - uint32_t control_value = 0, wp_index = 0; - - // Check if we are setting watchpoint other than read/write/access Update - // watchpoint flag to match loongarch64 write-read bit configuration. - switch (watch_flags) { - case eWatchpointKindWrite: - watch_flags = 2; - break; - case eWatchpointKindRead: - watch_flags = 1; - break; - case (eWatchpointKindRead | eWatchpointKindWrite): - break; - default: - return LLDB_INVALID_INDEX32; - } - - // Check if size has a valid hardware watchpoint length. - if (size != 1 && size != 2 && size != 4 && size != 8) - return LLDB_INVALID_INDEX32; - - // Setup control value - control_value = g_enable_bit | GetSizeBits(size); - control_value |= watch_flags << 8; - - // Iterate over stored watchpoints and find a free wp_index - wp_index = LLDB_INVALID_INDEX32; - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - if (!WatchpointIsEnabled(i)) { - wp_index = i; // Mark last free slot - } else if (m_hwp_regs[i].address == addr) { - return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. - } - } - - if (wp_index == LLDB_INVALID_INDEX32) - return LLDB_INVALID_INDEX32; - - // Update watchpoint in local cache - m_hwp_regs[wp_index].address = addr; - m_hwp_regs[wp_index].control = control_value; - - // PTRACE call to set corresponding watchpoint register. - error = WriteHardwareDebugRegs(eDREGTypeWATCH); - - if (error.Fail()) { - m_hwp_regs[wp_index].address = 0; - m_hwp_regs[wp_index].control = 0; - - LLDB_LOG(log, "unable to set watchpoint: failed to write debug registers"); - return LLDB_INVALID_INDEX32; - } - - return wp_index; -} - -bool NativeRegisterContextLinux_loongarch64::ClearHardwareWatchpoint( - uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) { - LLDB_LOG(log, "unable to clear watchpoint: failed to read debug registers"); - return false; - } - - if (wp_index >= m_max_hwp_supported) - return false; - - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; - uint32_t tempControl = m_hwp_regs[wp_index].control; - - // Update watchpoint in local cache - m_hwp_regs[wp_index].control = 0; - m_hwp_regs[wp_index].address = 0; - - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(eDREGTypeWATCH); - - if (error.Fail()) { - m_hwp_regs[wp_index].control = tempControl; - m_hwp_regs[wp_index].address = tempAddr; - - LLDB_LOG(log, "unable to clear watchpoint: failed to read debug registers"); - return false; - } - - return true; -} - -Status NativeRegisterContextLinux_loongarch64::ClearAllHardwareWatchpoints() { - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - if (error.Fail()) - return error; - - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - if (!WatchpointIsEnabled(i)) - continue; - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hwp_regs[i].address; - uint32_t tempControl = m_hwp_regs[i].control; - - // Clear watchpoints in local cache - m_hwp_regs[i].control = 0; - m_hwp_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(eDREGTypeWATCH); - - if (error.Fail()) { - m_hwp_regs[i].control = tempControl; - m_hwp_regs[i].address = tempAddr; - - return error; - } - } - - return Status(); -} - -uint32_t -NativeRegisterContextLinux_loongarch64::GetWatchpointSize(uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - switch ((m_hwp_regs[wp_index].control >> 10) & 0x3) { - case 0x0: - return 8; - case 0x1: - return 4; - case 0x2: - return 2; - case 0x3: - return 1; - default: - return 0; - } -} - -bool NativeRegisterContextLinux_loongarch64::WatchpointIsEnabled( - uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - return ((m_hwp_regs[wp_index].control & g_enable_bit) != 0); -} - -Status NativeRegisterContextLinux_loongarch64::GetWatchpointHitIndex( - uint32_t &wp_index, lldb::addr_t trap_addr) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - if (error.Fail()) - return error; - - uint32_t watch_size; - lldb::addr_t watch_addr; - - for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { - watch_size = GetWatchpointSize(wp_index); - watch_addr = m_hwp_regs[wp_index].address; - - if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && - trap_addr <= watch_addr + watch_size) { - m_hwp_regs[wp_index].hit_addr = trap_addr; - return Status(); - } - } - - wp_index = LLDB_INVALID_INDEX32; - return Status(); -} - -lldb::addr_t NativeRegisterContextLinux_loongarch64::GetWatchpointAddress( - uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if (wp_index >= m_max_hwp_supported) - return LLDB_INVALID_ADDRESS; - - if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].address; - - return LLDB_INVALID_ADDRESS; -} - -lldb::addr_t NativeRegisterContextLinux_loongarch64::GetWatchpointHitAddress( - uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if (wp_index >= m_max_hwp_supported) - return LLDB_INVALID_ADDRESS; - - if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].hit_addr; - - return LLDB_INVALID_ADDRESS; -} - -Status NativeRegisterContextLinux_loongarch64::ReadHardwareDebugInfo() { - if (!m_refresh_hwdebug_info) - return Status(); - - ::pid_t tid = m_thread.GetID(); - - int regset = NT_LOONGARCH_HW_WATCH; - struct iovec ioVec; - struct user_watch_state dreg_state; - Status error; - - ioVec.iov_base = &dreg_state; - ioVec.iov_len = sizeof(dreg_state); - error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, - &ioVec, ioVec.iov_len); - if (error.Fail()) - return error; - - m_max_hwp_supported = dreg_state.dbg_info & 0x3f; - - regset = NT_LOONGARCH_HW_BREAK; - error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, - &ioVec, ioVec.iov_len); - if (error.Fail()) - return error; - - m_max_hbp_supported = dreg_state.dbg_info & 0x3f; - - m_refresh_hwdebug_info = false; - - return error; -} - -Status NativeRegisterContextLinux_loongarch64::WriteHardwareDebugRegs( - DREGType hwbType) { - struct iovec ioVec; - struct user_watch_state dreg_state; - int regset; - - memset(&dreg_state, 0, sizeof(dreg_state)); - ioVec.iov_base = &dreg_state; - - switch (hwbType) { - case eDREGTypeWATCH: - regset = NT_LOONGARCH_HW_WATCH; - ioVec.iov_len = sizeof(dreg_state.dbg_info) + - (sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported); - - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address; - dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control; - } - break; - case eDREGTypeBREAK: - regset = NT_LOONGARCH_HW_BREAK; - ioVec.iov_len = sizeof(dreg_state.dbg_info) + - (sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported); - - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address; - dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control; - } - break; - } - - return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), - ®set, &ioVec, ioVec.iov_len); -} #endif // defined(__loongarch__) && __loongarch_grlen == 64 diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h index 6599677924d41d..0a6084ff4206db 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_loongarch64.h @@ -51,42 +51,6 @@ class NativeRegisterContextLinux_loongarch64 bool RegisterOffsetIsDynamic() const override { return true; } - // Hardware breakpoints management functions - - uint32_t NumSupportedHardwareBreakpoints() override; - - uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; - - bool ClearHardwareBreakpoint(uint32_t hw_idx) override; - - Status ClearAllHardwareBreakpoints() override; - - Status GetHardwareBreakHitIndex(uint32_t &bp_index, - lldb::addr_t trap_addr) override; - - bool BreakpointIsEnabled(uint32_t bp_index); - - // Hardware watchpoints management functions - uint32_t NumSupportedHardwareWatchpoints() override; - - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, - uint32_t watch_flags) override; - - bool ClearHardwareWatchpoint(uint32_t hw_index) override; - - Status ClearAllHardwareWatchpoints() override; - - Status GetWatchpointHitIndex(uint32_t &wp_index, - lldb::addr_t trap_addr) override; - - lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; - - lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; - - uint32_t GetWatchpointSize(uint32_t wp_index); - - bool WatchpointIsEnabled(uint32_t wp_index); - protected: Status ReadGPR() override; @@ -119,30 +83,6 @@ class NativeRegisterContextLinux_loongarch64 uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; const RegisterInfoPOSIX_loongarch64 &GetRegisterInfo() const; - - // Debug register type select - enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK }; - - Status ReadHardwareDebugInfo(); - Status WriteHardwareDebugRegs(DREGType hwbType); - - // Debug register info for hardware watchpoints management. - struct DREG { - lldb::addr_t address; // Breakpoint/watchpoint address value. - lldb::addr_t hit_addr; // Address at which last watchpoint trigger - // exception occurred. - uint32_t control; // Breakpoint/watchpoint control value. - }; - - std::array<struct DREG, 14> m_hbp_regs; // hardware breakpoints - std::array<struct DREG, 14> m_hwp_regs; // hardware watchpoints - - // Refer to: - // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints - // 14 is just a maximum value, query hardware for actual watchpoint count. - uint32_t m_max_hwp_supported = 14; - uint32_t m_max_hbp_supported = 14; - bool m_refresh_hwdebug_info = true; }; } // namespace process_linux diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt index 308ea29e31ad77..0526c95503175e 100644 --- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -9,6 +9,7 @@ add_lldb_library(lldbPluginProcessUtility LinuxSignals.cpp MemoryTagManagerAArch64MTE.cpp NativeProcessSoftwareSingleStep.cpp + NativeRegisterContextDBReg.cpp NativeRegisterContextDBReg_arm64.cpp NativeRegisterContextDBReg_x86.cpp NativeRegisterContextRegisterInfo.cpp diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp new file mode 100644 index 00000000000000..9a0f17ba57bdfa --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp @@ -0,0 +1,271 @@ +//===-- NativeRegisterContextDBReg.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 "NativeRegisterContextDBReg.h" + +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" + +using namespace lldb_private; + +uint32_t NativeRegisterContextDBReg::NumSupportedHardwareBreakpoints() { + Log *log = GetLog(LLDBLog::Breakpoints); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + + if (error.Fail()) { + LLDB_LOG(log, "failed to read debug registers"); + return 0; + } + + LLDB_LOG(log, "{0}", m_max_hbp_supported); + return m_max_hbp_supported; +} + +bool NativeRegisterContextDBReg::ClearHardwareBreakpoint(uint32_t hw_idx) { + Log *log = GetLog(LLDBLog::Breakpoints); + LLDB_LOG(log, "hw_idx: {0}", hw_idx); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) { + LLDB_LOG(log, + "unable to clear breakpoint: failed to read debug registers: {0}"); + return false; + } + + if (hw_idx >= m_max_hbp_supported) + return false; + + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address; + uint32_t tempControl = m_hbp_regs[hw_idx].control; + + m_hbp_regs[hw_idx].control &= ~m_hw_dbg_enable_bit; + m_hbp_regs[hw_idx].address = 0; + + // PTRACE call to clear corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) { + m_hbp_regs[hw_idx].control = tempControl; + m_hbp_regs[hw_idx].address = tempAddr; + + LLDB_LOG(log, + "unable to clear breakpoint: failed to write debug registers"); + return false; + } + + return true; +} + +Status +NativeRegisterContextDBReg::GetHardwareBreakHitIndex(uint32_t &bp_index, + lldb::addr_t trap_addr) { + Log *log = GetLog(LLDBLog::Breakpoints); + + LLDB_LOGF(log, "NativeRegisterContextDBReg::%s()", __FUNCTION__); + + lldb::addr_t break_addr; + + for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { + break_addr = m_hbp_regs[bp_index].address; + + if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) { + m_hbp_regs[bp_index].hit_addr = trap_addr; + return Status(); + } + } + + bp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +Status NativeRegisterContextDBReg::ClearAllHardwareBreakpoints() { + Log *log = GetLog(LLDBLog::Breakpoints); + + LLDB_LOGF(log, "NativeRegisterContextDBReg::%s()", __FUNCTION__); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) + return error; + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + if (!BreakpointIsEnabled(i)) + continue; + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbp_regs[i].address; + uint32_t tempControl = m_hbp_regs[i].control; + + // Clear watchpoints in local cache + m_hbp_regs[i].control &= ~m_hw_dbg_enable_bit; + m_hbp_regs[i].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error.Fail()) { + m_hbp_regs[i].control = tempControl; + m_hbp_regs[i].address = tempAddr; + + return error; + } + } + + return Status(); +} + +bool NativeRegisterContextDBReg::BreakpointIsEnabled(uint32_t bp_index) { + return ((m_hbp_regs[bp_index].control & m_hw_dbg_enable_bit) != 0); +} + +uint32_t NativeRegisterContextDBReg::NumSupportedHardwareWatchpoints() { + Log *log = GetLog(LLDBLog::Watchpoints); + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) { + LLDB_LOG(log, "failed to read debug registers"); + return 0; + } + + return m_max_hwp_supported; +} + +bool NativeRegisterContextDBReg::ClearHardwareWatchpoint(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) { + LLDB_LOG(log, "unable to set watchpoint: failed to read debug registers"); + return LLDB_INVALID_INDEX32; + } + + if (wp_index >= m_max_hwp_supported) + return false; + + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= ~m_hw_dbg_enable_bit; + m_hwp_regs[wp_index].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + + LLDB_LOG(log, "unable to clear watchpoint: failed to read debug registers"); + return false; + } + + return true; +} + +Status NativeRegisterContextDBReg::ClearAllHardwareWatchpoints() { + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) + return error; + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if (!WatchpointIsEnabled(i)) + continue; + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[i].address; + uint32_t tempControl = m_hwp_regs[i].control; + + // Clear watchpoints in local cache + m_hwp_regs[i].control = 0; + m_hwp_regs[i].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error.Fail()) { + m_hwp_regs[i].control = tempControl; + m_hwp_regs[i].address = tempAddr; + + return error; + } + } + + return Status(); +} + +Status +NativeRegisterContextDBReg::GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); + + // Read hardware breakpoint and watchpoint information. + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) + return error; + + // Mask off ignored bits from watchpoint trap address. + // aarch64 + trap_addr = FixWatchpointHitAddress(trap_addr); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { + watch_size = GetWatchpointSize(wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && + trap_addr < watch_addr + watch_size) { + m_hwp_regs[wp_index].hit_addr = trap_addr; + return Status(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +bool NativeRegisterContextDBReg::WatchpointIsEnabled(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + return ((m_hwp_regs[wp_index].control & m_hw_dbg_enable_bit) != 0); +} + +lldb::addr_t +NativeRegisterContextDBReg::GetWatchpointAddress(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].real_addr; + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +NativeRegisterContextDBReg::GetWatchpointHitAddress(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].hit_addr; + return LLDB_INVALID_ADDRESS; +} diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h new file mode 100644 index 00000000000000..bbd23bddbea25d --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h @@ -0,0 +1,76 @@ +//===-- NativeRegisterContextDBReg.h ----------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeRegisterContextDBReg_h +#define lldb_NativeRegisterContextDBReg_h + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" + +#include <array> + +namespace lldb_private { + +class NativeRegisterContextDBReg + : public virtual NativeRegisterContextRegisterInfo { +public: + uint32_t NumSupportedHardwareBreakpoints() override; + + bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + + Status ClearAllHardwareBreakpoints() override; + + Status GetHardwareBreakHitIndex(uint32_t &bp_index, + lldb::addr_t trap_addr) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + +protected: + // Debug register type select + enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK }; + + /// Debug register info for hardware breakpoints and watchpoints management. + struct DREG { + lldb::addr_t address; // Breakpoint/watchpoint address value. + lldb::addr_t hit_addr; // Address at which last watchpoint trigger exception + // occurred. + lldb::addr_t real_addr; // Address value that should cause target to stop. + uint32_t control; // Breakpoint/watchpoint control value. + }; + + std::array<struct DREG, 16> m_hbp_regs; // hardware breakpoints + std::array<struct DREG, 16> m_hwp_regs; // hardware watchpoints + + uint32_t m_max_hbp_supported; + uint32_t m_max_hwp_supported; + uint32_t m_hw_dbg_enable_bit; + + bool WatchpointIsEnabled(uint32_t wp_index); + bool BreakpointIsEnabled(uint32_t bp_index); + + virtual uint32_t GetWatchpointSize(uint32_t wp_index) = 0; + virtual Status ReadHardwareDebugInfo() = 0; + virtual Status WriteHardwareDebugRegs(DREGType hwbType) = 0; + virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) { + return hit_addr; + } +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextDBReg_h diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp index f1d0756b3ed9c5..101fbe692a2b09 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp @@ -14,8 +14,6 @@ using namespace lldb_private; -// E (bit 0), used to enable breakpoint/watchpoint -constexpr uint32_t g_enable_bit = 1; // PAC (bits 2:1): 0b10 constexpr uint32_t g_pac_bits = (2 << 1); @@ -29,18 +27,6 @@ static constexpr inline uint64_t GetSizeBits(int size) { return ((1 << size) - 1) << 5; } -uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareBreakpoints() { - Log *log = GetLog(LLDBLog::Breakpoints); - llvm::Error error = ReadHardwareDebugInfo(); - if (error) { - LLDB_LOG_ERROR(log, std::move(error), - "failed to read debug registers: {0}"); - return 0; - } - - return m_max_hbp_supported; -} - uint32_t NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { @@ -48,11 +34,10 @@ NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr, LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size); // Read hardware breakpoint and watchpoint information. - llvm::Error error = ReadHardwareDebugInfo(); - if (error) { - LLDB_LOG_ERROR( - log, std::move(error), - "unable to set breakpoint: failed to read debug registers: {0}"); + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) { + LLDB_LOG(log, + "unable to set breakpoint: failed to read debug registers: {0}"); return LLDB_INVALID_INDEX32; } @@ -68,7 +53,7 @@ NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr, return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned. // Setup control value - control_value = g_enable_bit | g_pac_bits | GetSizeBits(size); + control_value = m_hw_dbg_enable_bit | g_pac_bits | GetSizeBits(size); // Iterate over stored breakpoints and find a free bp_index bp_index = LLDB_INVALID_INDEX32; @@ -90,132 +75,35 @@ NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr, // PTRACE call to set corresponding hardware breakpoint register. error = WriteHardwareDebugRegs(eDREGTypeBREAK); - if (error) { + if (error.Fail()) { m_hbp_regs[bp_index].address = 0; - m_hbp_regs[bp_index].control &= ~1; + m_hbp_regs[bp_index].control &= ~m_hw_dbg_enable_bit; - LLDB_LOG_ERROR( - log, std::move(error), - "unable to set breakpoint: failed to write debug registers: {0}"); + LLDB_LOG(log, + "unable to set breakpoint: failed to write debug registers: {0}"); return LLDB_INVALID_INDEX32; } return bp_index; } -bool NativeRegisterContextDBReg_arm64::ClearHardwareBreakpoint( - uint32_t hw_idx) { - Log *log = GetLog(LLDBLog::Breakpoints); - LLDB_LOG(log, "hw_idx: {0}", hw_idx); - - // Read hardware breakpoint and watchpoint information. - llvm::Error error = ReadHardwareDebugInfo(); - if (error) { - LLDB_LOG_ERROR( - log, std::move(error), - "unable to clear breakpoint: failed to read debug registers: {0}"); - return false; - } - - if (hw_idx >= m_max_hbp_supported) - return false; - - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address; - uint32_t tempControl = m_hbp_regs[hw_idx].control; - - m_hbp_regs[hw_idx].control &= ~g_enable_bit; - m_hbp_regs[hw_idx].address = 0; - - // PTRACE call to clear corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(eDREGTypeBREAK); - - if (error) { - m_hbp_regs[hw_idx].control = tempControl; - m_hbp_regs[hw_idx].address = tempAddr; - - LLDB_LOG_ERROR( - log, std::move(error), - "unable to clear breakpoint: failed to write debug registers: {0}"); - return false; - } - - return true; -} - -Status NativeRegisterContextDBReg_arm64::GetHardwareBreakHitIndex( - uint32_t &bp_index, lldb::addr_t trap_addr) { - Log *log = GetLog(LLDBLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__); - - lldb::addr_t break_addr; - - for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { - break_addr = m_hbp_regs[bp_index].address; - - if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) { - m_hbp_regs[bp_index].hit_addr = trap_addr; - return Status(); - } - } - - bp_index = LLDB_INVALID_INDEX32; - return Status(); -} - -Status NativeRegisterContextDBReg_arm64::ClearAllHardwareBreakpoints() { - Log *log = GetLog(LLDBLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__); - - // Read hardware breakpoint and watchpoint information. - llvm::Error error = ReadHardwareDebugInfo(); - if (error) - return Status::FromError(std::move(error)); - - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - if (BreakpointIsEnabled(i)) { - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hbp_regs[i].address; - uint32_t tempControl = m_hbp_regs[i].control; - - // Clear watchpoints in local cache - m_hbp_regs[i].control &= ~g_enable_bit; - m_hbp_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(eDREGTypeBREAK); - - if (error) { - m_hbp_regs[i].control = tempControl; - m_hbp_regs[i].address = tempAddr; - - return Status::FromError(std::move(error)); - } - } - } - - return Status(); -} - -bool NativeRegisterContextDBReg_arm64::BreakpointIsEnabled(uint32_t bp_index) { - if ((m_hbp_regs[bp_index].control & g_enable_bit) != 0) - return true; - else - return false; -} - -uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareWatchpoints() { +uint32_t +NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) { Log *log = GetLog(LLDBLog::Watchpoints); - llvm::Error error = ReadHardwareDebugInfo(); - if (error) { - LLDB_LOG_ERROR(log, std::move(error), - "failed to read debug registers: {0}"); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) { + case 0x01: + return 1; + case 0x03: + return 2; + case 0x0f: + return 4; + case 0xff: + return 8; + default: return 0; } - - return m_max_hwp_supported; } uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint( @@ -225,11 +113,10 @@ uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint( watch_flags); // Read hardware breakpoint and watchpoint information. - llvm::Error error = ReadHardwareDebugInfo(); - if (error) { - LLDB_LOG_ERROR( - log, std::move(error), - "unable to set watchpoint: failed to read debug registers: {0}"); + Status error = ReadHardwareDebugInfo(); + if (error.Fail()) { + LLDB_LOG(log, + "unable to set watchpoint: failed to read debug registers: {0}"); return LLDB_INVALID_INDEX32; } @@ -239,13 +126,13 @@ uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint( // Check if we are setting watchpoint other than read/write/access Also // update watchpoint flag to match AArch64 write-read bit configuration. switch (watch_flags) { - case 1: + case lldb::eWatchpointKindWrite: watch_flags = 2; break; - case 2: + case lldb::eWatchpointKindRead: watch_flags = 1; break; - case 3: + case (lldb::eWatchpointKindRead | lldb::eWatchpointKindWrite): break; default: return LLDB_INVALID_INDEX32; @@ -274,7 +161,7 @@ uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint( } // Setup control value - control_value = g_enable_bit | g_pac_bits | GetSizeBits(size); + control_value = m_hw_dbg_enable_bit | g_pac_bits | GetSizeBits(size); control_value |= watch_flags << 3; // Iterate over stored watchpoints and find a free wp_index @@ -298,173 +185,14 @@ uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint( // PTRACE call to set corresponding watchpoint register. error = WriteHardwareDebugRegs(eDREGTypeWATCH); - if (error) { + if (error.Fail()) { m_hwp_regs[wp_index].address = 0; - m_hwp_regs[wp_index].control &= ~g_enable_bit; + m_hwp_regs[wp_index].control &= ~m_hw_dbg_enable_bit; - LLDB_LOG_ERROR( - log, std::move(error), - "unable to set watchpoint: failed to write debug registers: {0}"); + LLDB_LOG(log, + "unable to set watchpoint: failed to write debug registers: {0}"); return LLDB_INVALID_INDEX32; } return wp_index; } - -bool NativeRegisterContextDBReg_arm64::ClearHardwareWatchpoint( - uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - // Read hardware breakpoint and watchpoint information. - llvm::Error error = ReadHardwareDebugInfo(); - if (error) { - LLDB_LOG_ERROR( - log, std::move(error), - "unable to clear watchpoint: failed to read debug registers: {0}"); - return false; - } - - if (wp_index >= m_max_hwp_supported) - return false; - - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; - uint32_t tempControl = m_hwp_regs[wp_index].control; - - // Update watchpoint in local cache - m_hwp_regs[wp_index].control &= ~g_enable_bit; - m_hwp_regs[wp_index].address = 0; - - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(eDREGTypeWATCH); - - if (error) { - m_hwp_regs[wp_index].control = tempControl; - m_hwp_regs[wp_index].address = tempAddr; - - LLDB_LOG_ERROR( - log, std::move(error), - "unable to clear watchpoint: failed to write debug registers: {0}"); - return false; - } - - return true; -} - -Status NativeRegisterContextDBReg_arm64::ClearAllHardwareWatchpoints() { - // Read hardware breakpoint and watchpoint information. - llvm::Error error = ReadHardwareDebugInfo(); - if (error) - return Status::FromError(std::move(error)); - - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - if (WatchpointIsEnabled(i)) { - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hwp_regs[i].address; - uint32_t tempControl = m_hwp_regs[i].control; - - // Clear watchpoints in local cache - m_hwp_regs[i].control &= ~g_enable_bit; - m_hwp_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(eDREGTypeWATCH); - - if (error) { - m_hwp_regs[i].control = tempControl; - m_hwp_regs[i].address = tempAddr; - - return Status::FromError(std::move(error)); - } - } - } - - return Status(); -} - -uint32_t -NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) { - case 0x01: - return 1; - case 0x03: - return 2; - case 0x0f: - return 4; - case 0xff: - return 8; - default: - return 0; - } -} - -bool NativeRegisterContextDBReg_arm64::WatchpointIsEnabled(uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if ((m_hwp_regs[wp_index].control & g_enable_bit) != 0) - return true; - else - return false; -} - -Status NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex( - uint32_t &wp_index, lldb::addr_t trap_addr) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); - - // Read hardware breakpoint and watchpoint information. - llvm::Error error = ReadHardwareDebugInfo(); - if (error) - return Status::FromError(std::move(error)); - - // Mask off ignored bits from watchpoint trap address. - trap_addr = FixWatchpointHitAddress(trap_addr); - - uint32_t watch_size; - lldb::addr_t watch_addr; - - for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { - watch_size = GetWatchpointSize(wp_index); - watch_addr = m_hwp_regs[wp_index].address; - - if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && - trap_addr < watch_addr + watch_size) { - m_hwp_regs[wp_index].hit_addr = trap_addr; - return Status(); - } - } - - wp_index = LLDB_INVALID_INDEX32; - return Status(); -} - -lldb::addr_t -NativeRegisterContextDBReg_arm64::GetWatchpointAddress(uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if (wp_index >= m_max_hwp_supported) - return LLDB_INVALID_ADDRESS; - - if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].real_addr; - return LLDB_INVALID_ADDRESS; -} - -lldb::addr_t -NativeRegisterContextDBReg_arm64::GetWatchpointHitAddress(uint32_t wp_index) { - Log *log = GetLog(LLDBLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if (wp_index >= m_max_hwp_supported) - return LLDB_INVALID_ADDRESS; - - if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].hit_addr; - return LLDB_INVALID_ADDRESS; -} diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h index f8246ff4d71873..9142a0a685bea3 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h @@ -9,79 +9,19 @@ #ifndef lldb_NativeRegisterContextDBReg_arm64_h #define lldb_NativeRegisterContextDBReg_arm64_h -#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" - -#include <array> +#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h" namespace lldb_private { -class NativeRegisterContextDBReg_arm64 - : public virtual NativeRegisterContextRegisterInfo { +class NativeRegisterContextDBReg_arm64 : public NativeRegisterContextDBReg { public: - uint32_t NumSupportedHardwareBreakpoints() override; - uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; - bool ClearHardwareBreakpoint(uint32_t hw_idx) override; - - Status ClearAllHardwareBreakpoints() override; - - Status GetHardwareBreakHitIndex(uint32_t &bp_index, - lldb::addr_t trap_addr) override; - - bool BreakpointIsEnabled(uint32_t bp_index); - - uint32_t NumSupportedHardwareWatchpoints() override; - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags) override; - bool ClearHardwareWatchpoint(uint32_t hw_index) override; - - Status ClearAllHardwareWatchpoints() override; - - Status GetWatchpointHitIndex(uint32_t &wp_index, - lldb::addr_t trap_addr) override; - - lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; - - lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; - - uint32_t GetWatchpointSize(uint32_t wp_index); - - bool WatchpointIsEnabled(uint32_t wp_index); - - // Debug register type select - enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK }; - -protected: - /// Debug register info for hardware breakpoints and watchpoints management. - /// Watchpoints: For a user requested size 4 at addr 0x1004, where BAS - /// watchpoints are at doubleword (8-byte) alignment. - /// \a real_addr is 0x1004 - /// \a address is 0x1000 - /// size is 8 - /// If a one-byte write to 0x1006 is the most recent watchpoint trap, - /// \a hit_addr is 0x1006 - struct DREG { - lldb::addr_t address; // Breakpoint/watchpoint address value. - lldb::addr_t hit_addr; // Address at which last watchpoint trigger exception - // occurred. - lldb::addr_t real_addr; // Address value that should cause target to stop. - uint32_t control; // Breakpoint/watchpoint control value. - }; - - std::array<struct DREG, 16> m_hbp_regs; // hardware breakpoints - std::array<struct DREG, 16> m_hwp_regs; // hardware watchpoints - - uint32_t m_max_hbp_supported; - uint32_t m_max_hwp_supported; - - virtual llvm::Error ReadHardwareDebugInfo() = 0; - virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0; - virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) { - return hit_addr; - } +private: + uint32_t GetWatchpointSize(uint32_t wp_index) override; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp index c2fe05cad566ef..324db3db7eb4c7 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -231,8 +231,7 @@ GDBRemoteCommunicationServerCommon::Handle_qHostInfo( host_arch.GetMachine() == llvm::Triple::aarch64_32 || host_arch.GetMachine() == llvm::Triple::aarch64_be || host_arch.GetMachine() == llvm::Triple::arm || - host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS() || - host_arch.GetTriple().isLoongArch()) + host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS()) response.Printf("watchpoint_exceptions_received:before;"); else response.Printf("watchpoint_exceptions_received:after;"); diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 68485a40a3fcce..db33525978a16a 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2517,8 +2517,7 @@ bool Process::GetWatchpointReportedAfter() { llvm::Triple triple = arch.GetTriple(); if (triple.isMIPS() || triple.isPPC64() || triple.isRISCV() || - triple.isAArch64() || triple.isArmMClass() || triple.isARM() || - triple.isLoongArch()) + triple.isAArch64() || triple.isArmMClass() || triple.isARM()) reported_after = false; return reported_after; _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits