Skip to content

Commit 9bf3e61

Browse files
authored
[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: torvalds/linux@5d220ff 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.
1 parent c59cc54 commit 9bf3e61

7 files changed

+165
-79
lines changed

lldb/source/Plugins/Process/Linux/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_lldb_library(lldbPluginProcessLinux
88
NativeRegisterContextLinux.cpp
99
NativeRegisterContextLinux_arm.cpp
1010
NativeRegisterContextLinux_arm64.cpp
11+
NativeRegisterContextLinux_arm64dbreg.cpp
1112
NativeRegisterContextLinux_loongarch64.cpp
1213
NativeRegisterContextLinux_ppc64le.cpp
1314
NativeRegisterContextLinux_riscv64.cpp

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@
2323
#include <elf.h>
2424
#include <sys/uio.h>
2525

26+
#if defined(__arm64__) || defined(__aarch64__)
27+
#include "NativeRegisterContextLinux_arm64dbreg.h"
28+
#include "lldb/Host/linux/Ptrace.h"
29+
#include <asm/ptrace.h>
30+
#endif
31+
2632
#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(m_fpr))
2733

2834
#ifndef PTRACE_GETVFPREGS
2935
#define PTRACE_GETVFPREGS 27
3036
#define PTRACE_SETVFPREGS 28
3137
#endif
32-
#ifndef PTRACE_GETHBPREGS
38+
#if defined(__arm__) && !defined(PTRACE_GETHBPREGS)
3339
#define PTRACE_GETHBPREGS 29
3440
#define PTRACE_SETHBPREGS 30
3541
#endif
@@ -342,7 +348,8 @@ NativeRegisterContextLinux_arm::SetHardwareBreakpoint(lldb::addr_t addr,
342348
m_hbr_regs[bp_index].control = control_value;
343349

344350
// PTRACE call to set corresponding hardware breakpoint register.
345-
error = WriteHardwareDebugRegs(eDREGTypeBREAK, bp_index);
351+
error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK,
352+
bp_index);
346353

347354
if (error.Fail()) {
348355
m_hbr_regs[bp_index].address = 0;
@@ -375,7 +382,8 @@ bool NativeRegisterContextLinux_arm::ClearHardwareBreakpoint(uint32_t hw_idx) {
375382
m_hbr_regs[hw_idx].address = 0;
376383

377384
// PTRACE call to clear corresponding hardware breakpoint register.
378-
error = WriteHardwareDebugRegs(eDREGTypeBREAK, hw_idx);
385+
error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK,
386+
hw_idx);
379387

380388
if (error.Fail()) {
381389
m_hbr_regs[hw_idx].control = tempControl;
@@ -435,7 +443,8 @@ Status NativeRegisterContextLinux_arm::ClearAllHardwareBreakpoints() {
435443
m_hbr_regs[i].address = 0;
436444

437445
// Ptrace call to update hardware debug registers
438-
error = WriteHardwareDebugRegs(eDREGTypeBREAK, i);
446+
error =
447+
WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, i);
439448

440449
if (error.Fail()) {
441450
m_hbr_regs[i].control = tempControl;
@@ -555,7 +564,8 @@ uint32_t NativeRegisterContextLinux_arm::SetHardwareWatchpoint(
555564
m_hwp_regs[wp_index].control = control_value;
556565

557566
// PTRACE call to set corresponding watchpoint register.
558-
error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index);
567+
error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH,
568+
wp_index);
559569

560570
if (error.Fail()) {
561571
m_hwp_regs[wp_index].address = 0;
@@ -590,7 +600,8 @@ bool NativeRegisterContextLinux_arm::ClearHardwareWatchpoint(
590600
m_hwp_regs[wp_index].address = 0;
591601

592602
// Ptrace call to update hardware debug registers
593-
error = WriteHardwareDebugRegs(eDREGTypeWATCH, wp_index);
603+
error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH,
604+
wp_index);
594605

595606
if (error.Fail()) {
596607
m_hwp_regs[wp_index].control = tempControl;
@@ -623,7 +634,8 @@ Status NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints() {
623634
m_hwp_regs[i].address = 0;
624635

625636
// Ptrace call to update hardware debug registers
626-
error = WriteHardwareDebugRegs(eDREGTypeWATCH, i);
637+
error =
638+
WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, i);
627639

628640
if (error.Fail()) {
629641
m_hwp_regs[i].control = tempControl;
@@ -723,6 +735,7 @@ Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() {
723735
return Status();
724736
}
725737

738+
#ifdef __arm__
726739
unsigned int cap_val;
727740

728741
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(),
@@ -737,16 +750,21 @@ Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() {
737750
m_refresh_hwdebug_info = false;
738751

739752
return error;
753+
#else // __aarch64__
754+
return arm64::ReadHardwareDebugInfo(m_thread.GetID(), m_max_hwp_supported,
755+
m_max_hbp_supported);
756+
#endif // ifdef __arm__
740757
}
741758

742-
Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType,
743-
int hwb_index) {
759+
Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(
760+
NativeRegisterContextDBReg::DREGType hwbType, int hwb_index) {
744761
Status error;
745762

763+
#ifdef __arm__
746764
lldb::addr_t *addr_buf;
747765
uint32_t *ctrl_buf;
748766

749-
if (hwbType == eDREGTypeWATCH) {
767+
if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) {
750768
addr_buf = &m_hwp_regs[hwb_index].address;
751769
ctrl_buf = &m_hwp_regs[hwb_index].control;
752770

@@ -781,6 +799,17 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(int hwbType,
781799
}
782800

783801
return error;
802+
#else // __aarch64__
803+
uint32_t max_supported =
804+
(hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH)
805+
? m_max_hwp_supported
806+
: m_max_hbp_supported;
807+
auto &regs = (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH)
808+
? m_hwp_regs
809+
: m_hbr_regs;
810+
return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported,
811+
regs);
812+
#endif // ifdef __arm__
784813
}
785814

786815
uint32_t NativeRegisterContextLinux_arm::CalculateFprOffset(

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define lldb_NativeRegisterContextLinux_arm_h
1313

1414
#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
15+
#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"
1516
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
1617
#include "Plugins/Process/Utility/lldb-arm-register-enums.h"
1718

@@ -74,9 +75,6 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux {
7475

7576
bool WatchpointIsEnabled(uint32_t wp_index);
7677

77-
// Debug register type select
78-
enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK };
79-
8078
protected:
8179
Status DoReadRegisterValue(uint32_t offset, const char *reg_name,
8280
uint32_t size, RegisterValue &value) override;
@@ -102,17 +100,10 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux {
102100
uint32_t m_gpr_arm[k_num_gpr_registers_arm];
103101
RegisterInfoPOSIX_arm::FPU m_fpr;
104102

105-
// Debug register info for hardware breakpoints and watchpoints management.
106-
struct DREG {
107-
lldb::addr_t address; // Breakpoint/watchpoint address value.
108-
lldb::addr_t hit_addr; // Address at which last watchpoint trigger exception
109-
// occurred.
110-
lldb::addr_t real_addr; // Address value that should cause target to stop.
111-
uint32_t control; // Breakpoint/watchpoint control value.
112-
};
113-
114-
struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints
115-
struct DREG m_hwp_regs[16]; // Arm native linux hardware watchpoints
103+
std::array<NativeRegisterContextDBReg::DREG, 16>
104+
m_hbr_regs; // Arm native linux hardware breakpoints
105+
std::array<NativeRegisterContextDBReg::DREG, 16>
106+
m_hwp_regs; // Arm native linux hardware watchpoints
116107

117108
uint32_t m_max_hwp_supported;
118109
uint32_t m_max_hbp_supported;
@@ -124,7 +115,8 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux {
124115

125116
Status ReadHardwareDebugInfo();
126117

127-
Status WriteHardwareDebugRegs(int hwbType, int hwb_index);
118+
Status WriteHardwareDebugRegs(NativeRegisterContextDBReg::DREGType hwbType,
119+
int hwb_index);
128120

129121
uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;
130122

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp

Lines changed: 9 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
#if defined(__arm64__) || defined(__aarch64__)
1010

11-
#include "NativeRegisterContextLinux_arm.h"
1211
#include "NativeRegisterContextLinux_arm64.h"
12+
#include "NativeRegisterContextLinux_arm.h"
13+
#include "NativeRegisterContextLinux_arm64dbreg.h"
1314

1415
#include "lldb/Host/HostInfo.h"
1516
#include "lldb/Host/common/NativeProcessProtocol.h"
@@ -1146,68 +1147,23 @@ llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
11461147

11471148
::pid_t tid = m_thread.GetID();
11481149

1149-
int regset = NT_ARM_HW_WATCH;
1150-
struct iovec ioVec;
1151-
struct user_hwdebug_state dreg_state;
1152-
Status error;
1153-
1154-
ioVec.iov_base = &dreg_state;
1155-
ioVec.iov_len = sizeof(dreg_state);
1156-
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
1157-
&ioVec, ioVec.iov_len);
1158-
1150+
Status error = arm64::ReadHardwareDebugInfo(tid, m_max_hwp_supported,
1151+
m_max_hbp_supported);
11591152
if (error.Fail())
11601153
return error.ToError();
11611154

1162-
m_max_hwp_supported = dreg_state.dbg_info & 0xff;
1163-
1164-
regset = NT_ARM_HW_BREAK;
1165-
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
1166-
&ioVec, ioVec.iov_len);
1167-
1168-
if (error.Fail())
1169-
return error.ToError();
1170-
1171-
m_max_hbp_supported = dreg_state.dbg_info & 0xff;
11721155
m_refresh_hwdebug_info = false;
11731156

11741157
return llvm::Error::success();
11751158
}
11761159

11771160
llvm::Error
11781161
NativeRegisterContextLinux_arm64::WriteHardwareDebugRegs(DREGType hwbType) {
1179-
struct iovec ioVec;
1180-
struct user_hwdebug_state dreg_state;
1181-
int regset;
1182-
1183-
memset(&dreg_state, 0, sizeof(dreg_state));
1184-
ioVec.iov_base = &dreg_state;
1185-
1186-
switch (hwbType) {
1187-
case eDREGTypeWATCH:
1188-
regset = NT_ARM_HW_WATCH;
1189-
ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
1190-
(sizeof(dreg_state.dbg_regs[0]) * m_max_hwp_supported);
1191-
1192-
for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
1193-
dreg_state.dbg_regs[i].addr = m_hwp_regs[i].address;
1194-
dreg_state.dbg_regs[i].ctrl = m_hwp_regs[i].control;
1195-
}
1196-
break;
1197-
case eDREGTypeBREAK:
1198-
regset = NT_ARM_HW_BREAK;
1199-
ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
1200-
(sizeof(dreg_state.dbg_regs[0]) * m_max_hbp_supported);
1201-
1202-
for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
1203-
dreg_state.dbg_regs[i].addr = m_hbp_regs[i].address;
1204-
dreg_state.dbg_regs[i].ctrl = m_hbp_regs[i].control;
1205-
}
1206-
break;
1207-
}
1208-
1209-
return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, m_thread.GetID(),
1210-
&regset, &ioVec, ioVec.iov_len)
1162+
uint32_t max_supported =
1163+
(hwbType == eDREGTypeWATCH) ? m_max_hwp_supported : m_max_hbp_supported;
1164+
auto &regs = (hwbType == eDREGTypeWATCH) ? m_hwp_regs : m_hbp_regs;
1165+
return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported,
1166+
regs)
12111167
.ToError();
12121168
}
12131169

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//===-- NativeRegisterContextLinux_arm64dbreg.cpp -------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#if defined(__arm64__) || defined(__aarch64__)
10+
11+
#include "NativeRegisterContextLinux_arm64dbreg.h"
12+
#include "lldb/Host/linux/Ptrace.h"
13+
14+
#include <asm/ptrace.h>
15+
// System includes - They have to be included after framework includes because
16+
// they define some macros which collide with variable names in other modules
17+
#include <sys/uio.h>
18+
// NT_PRSTATUS and NT_FPREGSET definition
19+
#include <elf.h>
20+
21+
using namespace lldb;
22+
using namespace lldb_private;
23+
using namespace lldb_private::process_linux;
24+
25+
static Status ReadHardwareDebugInfoHelper(int regset, ::pid_t tid,
26+
uint32_t &max_supported) {
27+
struct iovec ioVec;
28+
struct user_hwdebug_state dreg_state;
29+
Status error;
30+
31+
ioVec.iov_base = &dreg_state;
32+
ioVec.iov_len = sizeof(dreg_state);
33+
error = NativeProcessLinux::PtraceWrapper(PTRACE_GETREGSET, tid, &regset,
34+
&ioVec, ioVec.iov_len);
35+
36+
if (error.Fail())
37+
return error;
38+
39+
max_supported = dreg_state.dbg_info & 0xff;
40+
return error;
41+
}
42+
43+
Status lldb_private::process_linux::arm64::ReadHardwareDebugInfo(
44+
::pid_t tid, uint32_t &max_hwp_supported, uint32_t &max_hbp_supported) {
45+
Status error =
46+
ReadHardwareDebugInfoHelper(NT_ARM_HW_WATCH, tid, max_hwp_supported);
47+
48+
if (error.Fail())
49+
return error;
50+
51+
return ReadHardwareDebugInfoHelper(NT_ARM_HW_BREAK, tid, max_hbp_supported);
52+
}
53+
54+
Status lldb_private::process_linux::arm64::WriteHardwareDebugRegs(
55+
int hwbType, ::pid_t tid, uint32_t max_supported,
56+
const std::array<NativeRegisterContextDBReg::DREG, 16> &regs) {
57+
int regset = hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH
58+
? NT_ARM_HW_WATCH
59+
: NT_ARM_HW_BREAK;
60+
61+
struct user_hwdebug_state dreg_state;
62+
memset(&dreg_state, 0, sizeof(dreg_state));
63+
for (uint32_t i = 0; i < max_supported; i++) {
64+
dreg_state.dbg_regs[i].addr = regs[i].address;
65+
dreg_state.dbg_regs[i].ctrl = regs[i].control;
66+
}
67+
68+
struct iovec ioVec;
69+
ioVec.iov_base = &dreg_state;
70+
ioVec.iov_len = sizeof(dreg_state.dbg_info) + sizeof(dreg_state.pad) +
71+
(sizeof(dreg_state.dbg_regs[0]) * max_supported);
72+
73+
return NativeProcessLinux::PtraceWrapper(PTRACE_SETREGSET, tid, &regset,
74+
&ioVec, ioVec.iov_len);
75+
}
76+
77+
#endif // defined (__arm64__) || defined (__aarch64__)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===-- NativeRegisterContextLinux_arm64dbreg.h -----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// When debugging 32-bit processes, Arm64 lldb-server should use 64-bit ptrace
10+
// interfaces. 32-bit ptrace interfaces should only be used by 32-bit server.
11+
// These functions are split out to be reused in both 32-bit and 64-bit register
12+
// context for 64-bit server.
13+
14+
#include "Plugins/Process/Linux/NativeProcessLinux.h"
15+
#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"
16+
#include "lldb/Utility/Status.h"
17+
18+
namespace lldb_private {
19+
namespace process_linux {
20+
namespace arm64 {
21+
22+
Status ReadHardwareDebugInfo(::pid_t tid, uint32_t &max_hwp_supported,
23+
uint32_t &max_hbp_supported);
24+
25+
Status WriteHardwareDebugRegs(
26+
int hwbType, ::pid_t tid, uint32_t max_supported,
27+
const std::array<NativeRegisterContextDBReg::DREG, 16> &regs);
28+
29+
} // namespace arm64
30+
} // namespace process_linux
31+
} // namespace lldb_private

0 commit comments

Comments
 (0)