https://github.com/b10902118 updated https://github.com/llvm/llvm-project/pull/147198
>From 102a2f59c07dfb63cc2aabae66a0afcf60388602 Mon Sep 17 00:00:00 2001 From: b10902118 <b10902...@linux1.csie.ntu.edu.tw> Date: Sun, 6 Jul 2025 23:50:54 +0800 Subject: [PATCH 1/2] [lldb][AArch64] Fix arm64 hardware breakpoint/watchpoint to arm32 process. This bug skips the test because the wrong ptrace call to detect avaliable hardware/breakpoint number that resuls in 0. After tracing linux's compat_ptrace in arch/arm64/kernel/ptrace.c, found that arm64 lldb-server should just keep using the ptrace commands for 64bit tracees. See: https://github.com/torvalds/linux/commit/5d220ff9420f8b1689805ba2d938bedf9e0860a4 So the solution is copying the implementation in NativeRegisterContextLinux_arm64.cpp. --- .../Linux/NativeRegisterContextLinux_arm.cpp | 75 ++++++++++++++++++- .../Linux/NativeRegisterContextLinux_arm.h | 2 +- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp index dc7fb103e87c0..9123a577008bd 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -23,13 +23,18 @@ #include <elf.h> #include <sys/uio.h> +#if defined(__arm64__) || defined(__aarch64__) +#include "lldb/Host/linux/Ptrace.h" +#include <asm/ptrace.h> +#endif + #define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(m_fpr)) #ifndef PTRACE_GETVFPREGS #define PTRACE_GETVFPREGS 27 #define PTRACE_SETVFPREGS 28 #endif -#ifndef PTRACE_GETHBPREGS +#if defined(__arm__) && !defined(PTRACE_GETHBPREGS) #define PTRACE_GETHBPREGS 29 #define PTRACE_SETHBPREGS 30 #endif @@ -723,6 +728,7 @@ Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() { return Status(); } +#ifdef __arm__ unsigned int cap_val; error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(), @@ -737,12 +743,43 @@ Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() { m_refresh_hwdebug_info = false; return error; +#else // __aarch64__ + ::pid_t tid = m_thread.GetID(); + + int regset = NT_ARM_HW_WATCH; + struct iovec ioVec; + struct user_hwdebug_state dreg_state; + + 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 & 0xff; + + regset = NT_ARM_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 & 0xff; + m_refresh_hwdebug_info = false; + + return error; +#endif // __arm__ } -Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType, +Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(DREGType hwbType, int hwb_index) { Status error; +#ifdef __arm__ lldb::addr_t *addr_buf; uint32_t *ctrl_buf; @@ -781,6 +818,40 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType, } return error; +#else // __aarch64__ + struct iovec ioVec; + struct user_hwdebug_state dreg_state; + int regset; + + memset(&dreg_state, 0, sizeof(dreg_state)); + ioVec.iov_base = &dreg_state; + + switch (hwbType) { + case eDREGTypeWATCH: + regset = NT_ARM_HW_WATCH; + ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + + (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_ARM_HW_BREAK; + ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + + (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_hbr_regs[i].address; + dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control; + } + break; + } + + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), + ®set, &ioVec, ioVec.iov_len); +#endif // __arm__ } uint32_t NativeRegisterContextLinux_arm::CalculateFprOffset( diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h index 15b46609c286b..6d0ad38d7eb75 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -125,7 +125,7 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux { Status ReadHardwareDebugInfo(); - Status WriteHardwareDebugRegs(int hwbType, int hwb_index); + Status WriteHardwareDebugRegs(DREGType hwbType, int hwb_index); uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; >From 78f8d7167cb5d49977e881c0b2d50a5c213421ff Mon Sep 17 00:00:00 2001 From: b10902118 <b10902...@linux1.csie.ntu.edu.tw> Date: Sun, 20 Jul 2025 15:45:13 +0800 Subject: [PATCH 2/2] [lldb][AArch64] Fix hardware breakpoint/watchpoint to arm32 debugee on arm64. When debugging arm32 process on arm64 machine, arm64 lldb-server will use `NativeRegisterContextLinux_arm`, but it should use 64-bit ptrace commands for hardware watchpoint/breakpoint. See: https://github.com/torvalds/linux/commit/5d220ff9420f8b1689805ba2d938bedf9e0860a4 There have been many conditional compilation handling arm32 debuggees on arm64, but this one is missed out. To reuse the 64-bit implementation, I separate the shared code from `NativeRegisterContextLinux_arm64.cpp` to `NativeRegisterContextLinuxArm64Shared.cpp`, with other adjustments to share data structures of debug registers. --- .../Plugins/Process/Linux/CMakeLists.txt | 1 + .../NativeRegisterContextLinuxArm64Shared.cpp | 63 +++++++++++++++++ .../NativeRegisterContextLinuxArm64Shared.h | 23 +++++++ .../Linux/NativeRegisterContextLinux_arm.cpp | 68 +++---------------- .../Linux/NativeRegisterContextLinux_arm.h | 21 ++---- .../NativeRegisterContextLinux_arm64.cpp | 60 +++------------- .../Utility/NativeRegisterContextDBReg.h | 2 +- 7 files changed, 111 insertions(+), 127 deletions(-) create mode 100644 lldb/source/Plugins/Process/Linux/NativeRegisterContextLinuxArm64Shared.cpp create mode 100644 lldb/source/Plugins/Process/Linux/NativeRegisterContextLinuxArm64Shared.h diff --git a/lldb/source/Plugins/Process/Linux/CMakeLists.txt b/lldb/source/Plugins/Process/Linux/CMakeLists.txt index 33af2e24dedd4..f3493859467a2 100644 --- a/lldb/source/Plugins/Process/Linux/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Linux/CMakeLists.txt @@ -8,6 +8,7 @@ add_lldb_library(lldbPluginProcessLinux NativeRegisterContextLinux.cpp NativeRegisterContextLinux_arm.cpp NativeRegisterContextLinux_arm64.cpp + NativeRegisterContextLinuxArm64Shared.cpp NativeRegisterContextLinux_loongarch64.cpp NativeRegisterContextLinux_ppc64le.cpp NativeRegisterContextLinux_riscv64.cpp diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinuxArm64Shared.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinuxArm64Shared.cpp new file mode 100644 index 0000000000000..a7dff0aed43be --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinuxArm64Shared.cpp @@ -0,0 +1,63 @@ +#include "NativeRegisterContextLinuxArm64Shared.h" + +using namespace lldb_private::process_linux::arm64; + +namespace lldb_private { +namespace process_linux { +namespace arm64 { + +namespace { +Status ReadHardwareDebugInfoHelper(int regset, ::pid_t tid, + uint32_t &max_supported) { + struct iovec ioVec; + struct user_hwdebug_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; + + max_supported = dreg_state.dbg_info & 0xff; + return error; +} +} // namespace + +Status ReadHardwareDebugInfo(::pid_t tid, uint32_t &max_hwp_supported, + uint32_t &max_hbp_supported) { + Status error = + ReadHardwareDebugInfoHelper(NT_ARM_HW_WATCH, tid, max_hwp_supported); + + if (error.Fail()) + return error; + + return ReadHardwareDebugInfoHelper(NT_ARM_HW_BREAK, tid, max_hbp_supported); +} + +Status WriteHardwareDebugRegs( + int hwbType, ::pid_t tid, uint32_t max_supported, + const std::array<NativeRegisterContextDBReg::DREG, 16> ®s) { + struct iovec ioVec; + struct user_hwdebug_state dreg_state; + int regset = hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH + ? NT_ARM_HW_WATCH + : NT_ARM_HW_BREAK; + memset(&dreg_state, 0, sizeof(dreg_state)); + ioVec.iov_base = &dreg_state; + ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + + (sizeof(dreg_state.dbg_regs[0]) * max_supported); + for (uint32_t i = 0; i < max_supported; i++) { + dreg_state.dbg_regs[i].addr = regs[i].address; + dreg_state.dbg_regs[i].ctrl = regs[i].control; + } + + return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, ®set, + &ioVec, ioVec.iov_len); +} + +} // namespace arm64 +} // namespace process_linux +} // namespace lldb_private diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinuxArm64Shared.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinuxArm64Shared.h new file mode 100644 index 0000000000000..1e8319ca7da4c --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinuxArm64Shared.h @@ -0,0 +1,23 @@ +#include "Plugins/Process/Linux/NativeProcessLinux.h" +#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h" +#include "lldb/Utility/Status.h" +#include <asm/ptrace.h> +#include <cstdint> +#include <elf.h> +#include <sys/ptrace.h> +#include <sys/uio.h> + +namespace lldb_private { +namespace process_linux { +namespace arm64 { + +Status ReadHardwareDebugInfo(::pid_t tid, uint32_t &max_hwp_supported, + uint32_t &max_hbp_supported); + +Status WriteHardwareDebugRegs( + int hwbType, ::pid_t tid, uint32_t max_supported, + const std::array<NativeRegisterContextDBReg::DREG, 16> ®s); + +} // namespace arm64 +} // namespace process_linux +} // namespace lldb_private \ No newline at end of file diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp index 9123a577008bd..3e92a320ae95c 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -9,6 +9,7 @@ #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) #include "NativeRegisterContextLinux_arm.h" +#include "NativeRegisterContextLinuxArm64Shared.h" #include "Plugins/Process/Linux/NativeProcessLinux.h" #include "Plugins/Process/Linux/Procfs.h" @@ -744,34 +745,8 @@ Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() { return error; #else // __aarch64__ - ::pid_t tid = m_thread.GetID(); - - int regset = NT_ARM_HW_WATCH; - struct iovec ioVec; - struct user_hwdebug_state dreg_state; - - 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 & 0xff; - - regset = NT_ARM_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 & 0xff; - m_refresh_hwdebug_info = false; - - return error; + return arm64::ReadHardwareDebugInfo(m_thread.GetID(), m_max_hwp_supported, + m_max_hbp_supported); #endif // __arm__ } @@ -819,38 +794,11 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(DREGType hwbType, return error; #else // __aarch64__ - struct iovec ioVec; - struct user_hwdebug_state dreg_state; - int regset; - - memset(&dreg_state, 0, sizeof(dreg_state)); - ioVec.iov_base = &dreg_state; - - switch (hwbType) { - case eDREGTypeWATCH: - regset = NT_ARM_HW_WATCH; - ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + - (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_ARM_HW_BREAK; - ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + - (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_hbr_regs[i].address; - dreg_state.dbg_regs[i].ctrl = m_hbr_regs[i].control; - } - break; - } - - return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(), - ®set, &ioVec, ioVec.iov_len); + uint32_t max_supported = + (hwbType == eDREGTypeWATCH) ? m_max_hwp_supported : m_max_hbp_supported; + auto ®s = (hwbType == eDREGTypeWATCH) ? m_hwp_regs : m_hbr_regs; + return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported, + regs); #endif // __arm__ } diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h index 6d0ad38d7eb75..aa3e6b6aaba79 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -12,6 +12,7 @@ #define lldb_NativeRegisterContextLinux_arm_h #include "Plugins/Process/Linux/NativeRegisterContextLinux.h" +#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h" #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h" #include "Plugins/Process/Utility/lldb-arm-register-enums.h" @@ -74,8 +75,10 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux { bool WatchpointIsEnabled(uint32_t wp_index); - // Debug register type select - enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK }; + using DREGType = NativeRegisterContextDBReg::DREGType; + static const DREGType eDREGTypeBREAK = DREGType::eDREGTypeBREAK; + static const DREGType eDREGTypeWATCH = DREGType::eDREGTypeWATCH; + using DREG = NativeRegisterContextDBReg::DREG; protected: Status DoReadRegisterValue(uint32_t offset, const char *reg_name, @@ -102,18 +105,8 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux { uint32_t m_gpr_arm[k_num_gpr_registers_arm]; RegisterInfoPOSIX_arm::FPU m_fpr; - // 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. - uint32_t refcount; // Serves as enable/disable and reference counter. - }; - - struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints - struct DREG m_hwp_regs[16]; // Arm native linux hardware watchpoints + std::array<DREG, 16> m_hbr_regs; // Arm native linux hardware breakpoints + std::array<DREG, 16> m_hwp_regs; // Arm native linux hardware watchpoints uint32_t m_max_hwp_supported; uint32_t m_max_hbp_supported; diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp index 884c7d4b9e359..9d869a8975821 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "NativeRegisterContextLinuxArm64Shared.h" #if defined(__arm64__) || defined(__aarch64__) #include "NativeRegisterContextLinux_arm.h" @@ -1143,29 +1144,11 @@ llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { ::pid_t tid = m_thread.GetID(); - int regset = NT_ARM_HW_WATCH; - struct iovec ioVec; - struct user_hwdebug_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.ToError(); - - m_max_hwp_supported = dreg_state.dbg_info & 0xff; - - regset = NT_ARM_HW_BREAK; - error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, ®set, - &ioVec, ioVec.iov_len); - + Status error = arm64::ReadHardwareDebugInfo(tid, m_max_hwp_supported, + m_max_hbp_supported); if (error.Fail()) return error.ToError(); - m_max_hbp_supported = dreg_state.dbg_info & 0xff; m_refresh_hwdebug_info = false; return llvm::Error::success(); @@ -1173,38 +1156,11 @@ llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { llvm::Error NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(DREGType hwbType) { - struct iovec ioVec; - struct user_hwdebug_state dreg_state; - int regset; - - memset(&dreg_state, 0, sizeof(dreg_state)); - ioVec.iov_base = &dreg_state; - - switch (hwbType) { - case eDREGTypeWATCH: - regset = NT_ARM_HW_WATCH; - ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + - (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_ARM_HW_BREAK; - ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) + - (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) + uint32_t max_supported = + (hwbType == eDREGTypeWATCH) ? m_max_hwp_supported : m_max_hbp_supported; + auto ®s = (hwbType == eDREGTypeWATCH) ? m_hwp_regs : m_hbp_regs; + return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported, + regs) .ToError(); } diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h index e17a700f7dad7..9b6ecd382c3f3 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h @@ -51,7 +51,6 @@ class NativeRegisterContextDBReg lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; -protected: // Debug register type select enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK }; @@ -64,6 +63,7 @@ class NativeRegisterContextDBReg uint32_t control; // Breakpoint/watchpoint control value. }; +protected: std::array<struct DREG, 16> m_hbp_regs; // hardware breakpoints std::array<struct DREG, 16> m_hwp_regs; // hardware watchpoints _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits