[Lldb-commits] [lldb] 9bf3e61 - [lldb][AArch64] Fix arm64 hardware breakpoint/watchpoint to arm32 process. (#147198)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Jul 29 02:05:17 PDT 2025
Author: b10902118
Date: 2025-07-29T10:05:13+01:00
New Revision: 9bf3e615a2c6db6e2a00ee2004ebcb21daf1334b
URL: https://github.com/llvm/llvm-project/commit/9bf3e615a2c6db6e2a00ee2004ebcb21daf1334b
DIFF: https://github.com/llvm/llvm-project/commit/9bf3e615a2c6db6e2a00ee2004ebcb21daf1334b.diff
LOG: [lldb][AArch64] Fix arm64 hardware breakpoint/watchpoint to arm32 process. (#147198)
When debugging arm32 process on arm64 machine, arm64 lldb-server will
use `NativeRegisterContextLinux_arm`, but the server should keep using
64-bit ptrace commands for hardware watchpoint/breakpoint, even when
debugging a 32-bit tracee.
See:
https://github.com/torvalds/linux/commit/5d220ff9420f8b1689805ba2d938bedf9e0860a4
There have been many conditional compilation handling arm32 tracee on
arm64, but this one is missed out.
To reuse the 64-bit implementation, I separate the shared code from
`NativeRegisterContextLinux_arm64.cpp` to
`NativeRegisterContextLinux_arm64dbreg.cpp`, with other adjustments to
share data structures of debug registers.
Added:
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64dbreg.cpp
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64dbreg.h
Modified:
lldb/source/Plugins/Process/Linux/CMakeLists.txt
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h
Removed:
################################################################################
diff --git a/lldb/source/Plugins/Process/Linux/CMakeLists.txt b/lldb/source/Plugins/Process/Linux/CMakeLists.txt
index 33af2e24dedd4..bb69016702cdf 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
+ NativeRegisterContextLinux_arm64dbreg.cpp
NativeRegisterContextLinux_loongarch64.cpp
NativeRegisterContextLinux_ppc64le.cpp
NativeRegisterContextLinux_riscv64.cpp
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
index dc7fb103e87c0..fdafacf410d64 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
@@ -23,13 +23,19 @@
#include <elf.h>
#include <sys/uio.h>
+#if defined(__arm64__) || defined(__aarch64__)
+#include "NativeRegisterContextLinux_arm64dbreg.h"
+#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
@@ -342,7 +348,8 @@ NativeRegisterContextLinux_arm::SetHardwareBreakpoint(lldb::addr_t addr,
m_hbr_regs[bp_index].control = control_value;
// PTRACE call to set corresponding hardware breakpoint register.
- error = WriteHardwareDebugRegs(eDREGTypeBREAK, bp_index);
+ error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK,
+ bp_index);
if (error.Fail()) {
m_hbr_regs[bp_index].address = 0;
@@ -375,7 +382,8 @@ bool NativeRegisterContextLinux_arm::ClearHardwareBreakpoint(uint32_t hw_idx) {
m_hbr_regs[hw_idx].address = 0;
// PTRACE call to clear corresponding hardware breakpoint register.
- error = WriteHardwareDebugRegs(eDREGTypeBREAK, hw_idx);
+ error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK,
+ hw_idx);
if (error.Fail()) {
m_hbr_regs[hw_idx].control = tempControl;
@@ -435,7 +443,8 @@ Status NativeRegisterContextLinux_arm::ClearAllHardwareBreakpoints() {
m_hbr_regs[i].address = 0;
// Ptrace call to update hardware debug registers
- error = WriteHardwareDebugRegs(eDREGTypeBREAK, i);
+ error =
+ WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, i);
if (error.Fail()) {
m_hbr_regs[i].control = tempControl;
@@ -555,7 +564,8 @@ uint32_t NativeRegisterContextLinux_arm::SetHardwareWatchpoint(
m_hwp_regs[wp_index].control = control_value;
// PTRACE call to set corresponding watchpoint register.
- error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index);
+ error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH,
+ wp_index);
if (error.Fail()) {
m_hwp_regs[wp_index].address = 0;
@@ -590,7 +600,8 @@ bool NativeRegisterContextLinux_arm::ClearHardwareWatchpoint(
m_hwp_regs[wp_index].address = 0;
// Ptrace call to update hardware debug registers
- error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index);
+ error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH,
+ wp_index);
if (error.Fail()) {
m_hwp_regs[wp_index].control = tempControl;
@@ -623,7 +634,8 @@ Status NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints() {
m_hwp_regs[i].address = 0;
// Ptrace call to update hardware debug registers
- error = WriteHardwareDebugRegs(eDREGTypeWATCH, i);
+ error =
+ WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, i);
if (error.Fail()) {
m_hwp_regs[i].control = tempControl;
@@ -723,6 +735,7 @@ Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() {
return Status();
}
+#ifdef __arm__
unsigned int cap_val;
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(),
@@ -737,16 +750,21 @@ Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() {
m_refresh_hwdebug_info = false;
return error;
+#else // __aarch64__
+ return arm64::ReadHardwareDebugInfo(m_thread.GetID(), m_max_hwp_supported,
+ m_max_hbp_supported);
+#endif // ifdef __arm__
}
-Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType,
- int hwb_index) {
+Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(
+ NativeRegisterContextDBReg::DREGType hwbType, int hwb_index) {
Status error;
+#ifdef __arm__
lldb::addr_t *addr_buf;
uint32_t *ctrl_buf;
- if (hwbType == eDREGTypeWATCH) {
+ if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) {
addr_buf = &m_hwp_regs[hwb_index].address;
ctrl_buf = &m_hwp_regs[hwb_index].control;
@@ -781,6 +799,17 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType,
}
return error;
+#else // __aarch64__
+ uint32_t max_supported =
+ (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH)
+ ? m_max_hwp_supported
+ : m_max_hbp_supported;
+ auto ®s = (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH)
+ ? m_hwp_regs
+ : m_hbr_regs;
+ return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported,
+ regs);
+#endif // ifdef __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 420e866dbd635..3a31d68d7a3c4 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,9 +75,6 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux {
bool WatchpointIsEnabled(uint32_t wp_index);
- // Debug register type select
- enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK };
-
protected:
Status DoReadRegisterValue(uint32_t offset, const char *reg_name,
uint32_t size, RegisterValue &value) override;
@@ -102,17 +100,10 @@ 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.
- };
-
- struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints
- struct DREG m_hwp_regs[16]; // Arm native linux hardware watchpoints
+ std::array<NativeRegisterContextDBReg::DREG, 16>
+ m_hbr_regs; // Arm native linux hardware breakpoints
+ std::array<NativeRegisterContextDBReg::DREG, 16>
+ m_hwp_regs; // Arm native linux hardware watchpoints
uint32_t m_max_hwp_supported;
uint32_t m_max_hbp_supported;
@@ -124,7 +115,8 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux {
Status ReadHardwareDebugInfo();
- Status WriteHardwareDebugRegs(int hwbType, int hwb_index);
+ Status WriteHardwareDebugRegs(NativeRegisterContextDBReg::DREGType hwbType,
+ int hwb_index);
uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
index b1c7421bef8d5..294a446686f22 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -8,8 +8,9 @@
#if defined(__arm64__) || defined(__aarch64__)
-#include "NativeRegisterContextLinux_arm.h"
#include "NativeRegisterContextLinux_arm64.h"
+#include "NativeRegisterContextLinux_arm.h"
+#include "NativeRegisterContextLinux_arm64dbreg.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/common/NativeProcessProtocol.h"
@@ -1146,29 +1147,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);
-
+ Status error = arm64::ReadHardwareDebugInfo(tid, m_max_hwp_supported,
+ m_max_hbp_supported);
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);
-
- if (error.Fail())
- return error.ToError();
-
- m_max_hbp_supported = dreg_state.dbg_info & 0xff;
m_refresh_hwdebug_info = false;
return llvm::Error::success();
@@ -1176,38 +1159,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/Linux/NativeRegisterContextLinux_arm64dbreg.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64dbreg.cpp
new file mode 100644
index 0000000000000..53ee8fdb2b482
--- /dev/null
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64dbreg.cpp
@@ -0,0 +1,77 @@
+//===-- NativeRegisterContextLinux_arm64dbreg.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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__arm64__) || defined(__aarch64__)
+
+#include "NativeRegisterContextLinux_arm64dbreg.h"
+#include "lldb/Host/linux/Ptrace.h"
+
+#include <asm/ptrace.h>
+// System includes - They have to be included after framework includes because
+// they define some macros which collide with variable names in other modules
+#include <sys/uio.h>
+// NT_PRSTATUS and NT_FPREGSET definition
+#include <elf.h>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_linux;
+
+static 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;
+}
+
+Status lldb_private::process_linux::arm64::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 lldb_private::process_linux::arm64::WriteHardwareDebugRegs(
+ int hwbType, ::pid_t tid, uint32_t max_supported,
+ const std::array<NativeRegisterContextDBReg::DREG, 16> ®s) {
+ int regset = hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH
+ ? NT_ARM_HW_WATCH
+ : NT_ARM_HW_BREAK;
+
+ struct user_hwdebug_state dreg_state;
+ memset(&dreg_state, 0, sizeof(dreg_state));
+ 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;
+ }
+
+ struct iovec ioVec;
+ 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);
+
+ return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, ®set,
+ &ioVec, ioVec.iov_len);
+}
+
+#endif // defined (__arm64__) || defined (__aarch64__)
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64dbreg.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64dbreg.h
new file mode 100644
index 0000000000000..bca217851858d
--- /dev/null
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64dbreg.h
@@ -0,0 +1,31 @@
+//===-- NativeRegisterContextLinux_arm64dbreg.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
+//
+//===----------------------------------------------------------------------===//
+
+// When debugging 32-bit processes, Arm64 lldb-server should use 64-bit ptrace
+// interfaces. 32-bit ptrace interfaces should only be used by 32-bit server.
+// These functions are split out to be reused in both 32-bit and 64-bit register
+// context for 64-bit server.
+
+#include "Plugins/Process/Linux/NativeProcessLinux.h"
+#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"
+#include "lldb/Utility/Status.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
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
More information about the lldb-commits
mailing list