[Lldb-commits] [lldb] ae5836f - [LLDB][Process/Utility] Introduce NativeRegisterContextDBReg class
via lldb-commits
lldb-commits at lists.llvm.org
Wed Dec 11 18:04:27 PST 2024
Author: wanglei
Date: 2024-12-12T10:04:24+08:00
New Revision: ae5836f6b6a8544e6226f5c1ba6b1beacfe01aef
URL: https://github.com/llvm/llvm-project/commit/ae5836f6b6a8544e6226f5c1ba6b1beacfe01aef
DIFF: https://github.com/llvm/llvm-project/commit/ae5836f6b6a8544e6226f5c1ba6b1beacfe01aef.diff
LOG: [LLDB][Process/Utility] Introduce NativeRegisterContextDBReg class
Since the setup of debug registers for AArch64 and LoongArch is similar,
we extracted the shared logic from Class:
`NativeRegisterContextDBReg_arm64`
into a new Class:
`NativeRegisterContextDBReg`.
This will simplify the subsequent implementation of hardware breakpoints
and watchpoints on LoongArch.
Reviewed By: DavidSpickett
Pull Request: https://github.com/llvm/llvm-project/pull/118043
Added:
lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp
lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h
Modified:
lldb/source/Plugins/Process/Utility/CMakeLists.txt
lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
Removed:
################################################################################
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..1e0c1a5db09ca4
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp
@@ -0,0 +1,409 @@
+//===-- 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.
+ llvm::Error error = ReadHardwareDebugInfo();
+
+ if (error) {
+ LLDB_LOG_ERROR(log, std::move(error),
+ "failed to read debug registers: {0}");
+ return 0;
+ }
+
+ LLDB_LOG(log, "{0}", m_max_hbp_supported);
+ return m_max_hbp_supported;
+}
+
+uint32_t NativeRegisterContextDBReg::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.
+ llvm::Error error = ReadHardwareDebugInfo();
+ if (error) {
+ LLDB_LOG_ERROR(
+ log, std::move(error),
+ "unable to set breakpoint: failed to read debug registers: {0}");
+ return LLDB_INVALID_INDEX32;
+ }
+
+ uint32_t control_value = 0, bp_index = 0;
+
+ if (!ValidateBreakpoint(size, addr))
+ return LLDB_INVALID_INDEX32;
+
+ control_value = MakeBreakControlValue(size);
+
+ // 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].real_addr = addr;
+ m_hbp_regs[bp_index].address = addr;
+ m_hbp_regs[bp_index].control = control_value;
+
+ // PTRACE call to set corresponding hardware breakpoint register.
+ error = WriteHardwareDebugRegs(eDREGTypeBREAK);
+
+ if (error) {
+ m_hbp_regs[bp_index].address = 0;
+ 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}");
+ return LLDB_INVALID_INDEX32;
+ }
+
+ return bp_index;
+}
+
+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.
+ 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 &= ~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) {
+ 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::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.
+ 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))
+ 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) {
+ m_hbp_regs[i].control = tempControl;
+ m_hbp_regs[i].address = tempAddr;
+
+ return Status::FromError(std::move(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);
+ llvm::Error error = ReadHardwareDebugInfo();
+ if (error) {
+ LLDB_LOG_ERROR(log, std::move(error),
+ "failed to read debug registers: {0}");
+ return 0;
+ }
+
+ return m_max_hwp_supported;
+}
+
+uint32_t NativeRegisterContextDBReg::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.
+ llvm::Error error = ReadHardwareDebugInfo();
+ if (error) {
+ LLDB_LOG_ERROR(
+ log, std::move(error),
+ "unable to set watchpoint: failed to read debug registers: {0}");
+ return LLDB_INVALID_INDEX32;
+ }
+
+ uint32_t control_value = 0, wp_index = 0;
+ lldb::addr_t real_addr = addr;
+ WatchpointDetails details{size, addr};
+
+ auto adjusted = AdjustWatchpoint(details);
+ if (adjusted == std::nullopt)
+ return LLDB_INVALID_INDEX32;
+ size = adjusted->size;
+ addr = adjusted->addr;
+
+ // Check if we are setting watchpoint other than read/write/access Also
+ // update watchpoint flag to match AArch64/LoongArch write-read bit
+ // configuration.
+ switch (watch_flags) {
+ case lldb::eWatchpointKindWrite:
+ watch_flags = 2;
+ break;
+ case lldb::eWatchpointKindRead:
+ watch_flags = 1;
+ break;
+ case (lldb::eWatchpointKindRead | lldb::eWatchpointKindWrite):
+ break;
+ default:
+ return LLDB_INVALID_INDEX32;
+ }
+
+ control_value = MakeWatchControlValue(size, watch_flags);
+
+ // 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].real_addr = real_addr;
+ 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) {
+ m_hwp_regs[wp_index].address = 0;
+ 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}");
+ return LLDB_INVALID_INDEX32;
+ }
+
+ return wp_index;
+}
+
+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.
+ llvm::Error error = ReadHardwareDebugInfo();
+ if (error) {
+ LLDB_LOG_ERROR(
+ log, std::move(error),
+ "unable to set watchpoint: failed to read debug registers: {0}");
+ 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) {
+ 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 read debug registers: {0}");
+ return false;
+ }
+
+ return true;
+}
+
+Status NativeRegisterContextDBReg::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))
+ 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) {
+ m_hwp_regs[i].control = tempControl;
+ m_hwp_regs[i].address = tempAddr;
+
+ return Status::FromError(std::move(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.
+ llvm::Error error = ReadHardwareDebugInfo();
+ if (error)
+ return Status::FromError(std::move(error));
+
+ // AArch64 need 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();
+}
+
+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..e17a700f7dad7e
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h
@@ -0,0 +1,101 @@
+//===-- 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>
+
+// Common utilities for hardware breakpoints and hardware watchpoints on AArch64
+// and LoongArch.
+
+namespace lldb_private {
+
+class NativeRegisterContextDBReg
+ : public virtual NativeRegisterContextRegisterInfo {
+public:
+ explicit NativeRegisterContextDBReg(uint32_t enable_bit)
+ : m_hw_dbg_enable_bit(enable_bit) {}
+
+ 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;
+
+ 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;
+
+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;
+ const uint32_t m_hw_dbg_enable_bit;
+
+ bool WatchpointIsEnabled(uint32_t wp_index);
+ bool BreakpointIsEnabled(uint32_t bp_index);
+
+ // On AArch64 and Loongarch the hardware breakpoint length size is 4, and the
+ // target address must 4-byte alignment.
+ bool ValidateBreakpoint(size_t size, lldb::addr_t addr) {
+ return (size == 4) && !(addr & 0x3);
+ }
+ struct WatchpointDetails {
+ size_t size;
+ lldb::addr_t addr;
+ };
+ virtual std::optional<WatchpointDetails>
+ AdjustWatchpoint(const WatchpointDetails &details) = 0;
+ virtual uint32_t MakeBreakControlValue(size_t size) = 0;
+ virtual uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) = 0;
+ virtual uint32_t GetWatchpointSize(uint32_t wp_index) = 0;
+
+ 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;
+ }
+};
+
+} // 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..f2f772ad615c92 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
@@ -14,246 +14,33 @@
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);
-
-// Returns appropriate control register bits for the specified size
-static constexpr inline uint64_t GetSizeBits(int size) {
- // BAS (bits 12:5) hold a bit-mask of addresses to watch
- // e.g. 0b00000001 means 1 byte at address
- // 0b00000011 means 2 bytes (addr..addr+1)
- // ...
- // 0b11111111 means 8 bytes (addr..addr+7)
- 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) {
- Log *log = GetLog(LLDBLog::Breakpoints);
- 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}");
- return LLDB_INVALID_INDEX32;
- }
-
- uint32_t control_value = 0, bp_index = 0;
-
- // Check if size has a valid hardware breakpoint length.
- if (size != 4)
- return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 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.
-
- // Setup control value
- control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
-
- // 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].real_addr = addr;
- m_hbp_regs[bp_index].address = addr;
- m_hbp_regs[bp_index].control = control_value;
-
- // PTRACE call to set corresponding hardware breakpoint register.
- error = WriteHardwareDebugRegs(eDREGTypeBREAK);
-
- if (error) {
- m_hbp_regs[bp_index].address = 0;
- m_hbp_regs[bp_index].control &= ~1;
-
- LLDB_LOG_ERROR(
- log, std::move(error),
- "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() {
- Log *log = GetLog(LLDBLog::Watchpoints);
- llvm::Error error = ReadHardwareDebugInfo();
- if (error) {
- LLDB_LOG_ERROR(log, std::move(error),
- "failed to read debug registers: {0}");
- return 0;
- }
-
- return m_max_hwp_supported;
-}
-
-uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
- lldb::addr_t addr, size_t size, uint32_t watch_flags) {
+NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
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.
- llvm::Error error = ReadHardwareDebugInfo();
- if (error) {
- LLDB_LOG_ERROR(
- log, std::move(error),
- "unable to set watchpoint: failed to read debug registers: {0}");
- return LLDB_INVALID_INDEX32;
- }
-
- uint32_t control_value = 0, wp_index = 0;
- lldb::addr_t real_addr = addr;
+ LLDB_LOG(log, "wp_index: {0}", wp_index);
- // 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:
- watch_flags = 2;
- break;
- case 2:
- watch_flags = 1;
- break;
- case 3:
- break;
+ 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 LLDB_INVALID_INDEX32;
+ return 0;
}
+}
+std::optional<NativeRegisterContextDBReg::WatchpointDetails>
+NativeRegisterContextDBReg_arm64::AdjustWatchpoint(
+ const WatchpointDetails &details) {
+ size_t size = details.size;
+ lldb::addr_t addr = details.addr;
// Check if size has a valid hardware watchpoint length.
if (size != 1 && size != 2 && size != 4 && size != 8)
- return LLDB_INVALID_INDEX32;
+ return std::nullopt;
// Check 8-byte alignment for hardware watchpoint target address. Below is a
// hack to recalculate address and size in order to make sure we can watch
@@ -262,8 +49,9 @@ uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
uint8_t watch_mask = (addr & 0x07) + size;
if (watch_mask > 0x08)
- return LLDB_INVALID_INDEX32;
- else if (watch_mask <= 0x02)
+ return std::nullopt;
+
+ if (watch_mask <= 0x02)
size = 2;
else if (watch_mask <= 0x04)
size = 4;
@@ -272,199 +60,37 @@ uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(
addr = addr & (~0x07);
}
-
- // Setup control value
- control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);
- control_value |= watch_flags << 3;
-
- // 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].real_addr = real_addr;
- 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) {
- m_hwp_regs[wp_index].address = 0;
- m_hwp_regs[wp_index].control &= ~g_enable_bit;
-
- LLDB_LOG_ERROR(
- log, std::move(error),
- "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;
+ return WatchpointDetails{size, addr};
}
-Status NativeRegisterContextDBReg_arm64::ClearAllHardwareWatchpoints() {
- // Read hardware breakpoint and watchpoint information.
- llvm::Error error = ReadHardwareDebugInfo();
- if (error)
- return Status::FromError(std::move(error));
+uint32_t NativeRegisterContextDBReg_arm64::MakeBreakControlValue(size_t size) {
+ // PAC (bits 2:1): 0b10
+ const uint32_t pac_bits = 2 << 1;
- 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));
- }
- }
- }
+ // BAS (bits 12:5) hold a bit-mask of addresses to watch
+ // e.g. 0b00000001 means 1 byte at address
+ // 0b00000011 means 2 bytes (addr..addr+1)
+ // ...
+ // 0b11111111 means 8 bytes (addr..addr+7)
+ size_t encoded_size = ((1 << size) - 1) << 5;
- return Status();
+ // Return encoded hardware breakpoint control value.
+ return m_hw_dbg_enable_bit | pac_bits | encoded_size;
}
uint32_t
-NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
- Log *log = GetLog(LLDBLog::Watchpoints);
- LLDB_LOG(log, "wp_index: {0}", wp_index);
+NativeRegisterContextDBReg_arm64::MakeWatchControlValue(size_t size,
+ uint32_t watch_flags) {
+ // PAC (bits 2:1): 0b10
+ const uint32_t pac_bits = 2 << 1;
- 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;
+ // BAS (bits 12:5) hold a bit-mask of addresses to watch
+ // e.g. 0b00000001 means 1 byte at address
+ // 0b00000011 means 2 bytes (addr..addr+1)
+ // ...
+ // 0b11111111 means 8 bytes (addr..addr+7)
+ size_t encoded_size = ((1 << size) - 1) << 5;
- if (WatchpointIsEnabled(wp_index))
- return m_hwp_regs[wp_index].hit_addr;
- return LLDB_INVALID_ADDRESS;
+ // Return encoded hardware watchpoint control value.
+ return m_hw_dbg_enable_bit | pac_bits | encoded_size | (watch_flags << 3);
}
diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
index f8246ff4d71873..42d6bacf648615 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h
@@ -9,79 +9,24 @@
#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 };
+ NativeRegisterContextDBReg_arm64()
+ : NativeRegisterContextDBReg(/*enable_bit=*/0x1U) {}
-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.
- };
+private:
+ uint32_t GetWatchpointSize(uint32_t wp_index) override;
- std::array<struct DREG, 16> m_hbp_regs; // hardware breakpoints
- std::array<struct DREG, 16> m_hwp_regs; // hardware watchpoints
+ std::optional<WatchpointDetails>
+ AdjustWatchpoint(const WatchpointDetails &details) override;
- uint32_t m_max_hbp_supported;
- uint32_t m_max_hwp_supported;
+ uint32_t MakeBreakControlValue(size_t size) override;
- 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;
- }
+ uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override;
};
} // namespace lldb_private
More information about the lldb-commits
mailing list