Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
557 changes: 57 additions & 500 deletions lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#define lldb_NativeRegisterContextLinux_arm_h

#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"
#include "Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h"
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
#include "Plugins/Process/Utility/lldb-arm-register-enums.h"

Expand All @@ -21,7 +21,8 @@ namespace process_linux {

class NativeProcessLinux;

class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux {
class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux,
public NativeRegisterContextDBReg_arm {
public:
NativeRegisterContextLinux_arm(const ArchSpec &target_arch,
NativeThreadProtocol &native_thread);
Expand All @@ -42,39 +43,6 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux {

Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;

// Hardware breakpoints/watchpoint 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;

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 DoReadRegisterValue(uint32_t offset, const char *reg_name,
uint32_t size, RegisterValue &value) override;
Expand All @@ -100,23 +68,18 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux {
uint32_t m_gpr_arm[k_num_gpr_registers_arm];
RegisterInfoPOSIX_arm::FPU m_fpr;

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;
bool m_refresh_hwdebug_info;

bool IsGPR(unsigned reg) const;

bool IsFPR(unsigned reg) const;

Status ReadHardwareDebugInfo();
llvm::Error ReadHardwareDebugInfo() override;

Status WriteHardwareDebugRegs(NativeRegisterContextDBReg::DREGType hwbType,
int hwb_index);
llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
#ifdef __arm__
llvm::Error WriteHardwareDebugReg(DREGType hwbType, int hwb_index);
#endif

uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;

Expand Down
1 change: 1 addition & 0 deletions lldb/source/Plugins/Process/Utility/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_lldb_library(lldbPluginProcessUtility
MemoryTagManagerAArch64MTE.cpp
NativeProcessSoftwareSingleStep.cpp
NativeRegisterContextDBReg.cpp
NativeRegisterContextDBReg_arm.cpp
NativeRegisterContextDBReg_arm64.cpp
NativeRegisterContextDBReg_loongarch.cpp
NativeRegisterContextDBReg_x86.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ uint32_t NativeRegisterContextDBReg::SetHardwareWatchpoint(
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
// update watchpoint flag to match ARM/AArch64/LoongArch write-read bit
// configuration.
switch (watch_flags) {
case lldb::eWatchpointKindWrite:
Expand All @@ -237,7 +237,7 @@ uint32_t NativeRegisterContextDBReg::SetHardwareWatchpoint(
return LLDB_INVALID_INDEX32;
}

control_value = MakeWatchControlValue(size, watch_flags);
control_value = MakeWatchControlValue(addr, size, watch_flags);

// Iterate over stored watchpoints and find a free wp_index
wp_index = LLDB_INVALID_INDEX32;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"

#include <array>
#include <optional>

// Common utilities for hardware breakpoints and hardware watchpoints on AArch64
// and LoongArch.
Expand Down Expand Up @@ -76,17 +77,26 @@ class NativeRegisterContextDBReg

// 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) {
virtual 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;

using BreakpointDetails = WatchpointDetails;
virtual std::optional<BreakpointDetails>
AdjustBreakpoint(const BreakpointDetails &details) {
return details;
}

virtual uint32_t MakeBreakControlValue(size_t size) = 0;
virtual uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) = 0;
virtual uint32_t MakeWatchControlValue(lldb::addr_t addr, size_t size,
uint32_t watch_flags) = 0;
virtual uint32_t GetWatchpointSize(uint32_t wp_index) = 0;

virtual llvm::Error ReadHardwareDebugInfo() = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//===-- NativeRegisterContextDBReg_arm.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_arm.h"

#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"

using namespace lldb_private;

uint32_t NativeRegisterContextDBReg_arm::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) & 0x0f) {
case 0x01:
return 1;
case 0x03:
return 2;
case 0x07:
return 3;
case 0x0f:
return 4;
default:
return 0;
}
}

std::optional<NativeRegisterContextDBReg::BreakpointDetails>
NativeRegisterContextDBReg_arm::AdjustBreakpoint(
const BreakpointDetails &details) {
BreakpointDetails bd = details;
// Use size to get a hint of arm vs thumb modes.
switch (bd.size) {
case 2:
bd.addr &= ~1;
break;
case 4:
bd.addr &= ~3;
break;
default:
return {};
}

return bd;
}

std::optional<NativeRegisterContextDBReg::WatchpointDetails>
NativeRegisterContextDBReg_arm::AdjustWatchpoint(
const WatchpointDetails &details) {
auto [size, addr] = details;

if (size == 0 || size > 4)
return {};

// Check 4-byte alignment for hardware watchpoint target address. Below is a
// hack to recalculate address and size in order to make sure we can watch
// non 4-byte aligned addresses as well.
if (addr & 0x03) {
uint8_t watch_mask = (addr & 0x03) + size;
if (watch_mask > 0x04)
return {};
else if (watch_mask <= 0x02)
size = 2;
else
size = 4;

addr = addr & (~0x03);
}

return WatchpointDetails{size, addr};
}

uint32_t NativeRegisterContextDBReg_arm::MakeBreakControlValue(size_t size) {
switch (size) {
case 2:
return (0x3 << 5) | 7;
case 4:
return (0xfu << 5) | 7;
default:
// We assume that AdjustBreakpoint would have caught this earlier.
llvm_unreachable("Invalid breakpoint size.");
}
}

uint32_t NativeRegisterContextDBReg_arm::MakeWatchControlValue(
lldb::addr_t addr, size_t size, uint32_t watch_flags) {
uint32_t addr_word_offset = 0, byte_mask = 0;

// We can only watch up to four bytes that follow a 4 byte aligned address
// per watchpoint register pair, so make sure we can properly encode this.
addr_word_offset = addr % 4;
byte_mask = ((1u << size) - 1u) << addr_word_offset;

// Check if we need multiple watchpoint register
if (byte_mask > 0xfu)
return LLDB_INVALID_INDEX32;

// Setup control value
// Make the byte_mask into a valid Byte Address Select mask
uint32_t control_value = byte_mask << 5;

// Turn on appropriate watchpoint flags read or write
control_value |= (watch_flags << 3);

// Enable this watchpoint and make it stop in privileged or user mode;
control_value |= 7;

return control_value;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===-- NativeRegisterContextDBReg_arm.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_arm_h
#define lldb_NativeRegisterContextDBReg_arm_h

#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"

namespace lldb_private {

class NativeRegisterContextDBReg_arm : public NativeRegisterContextDBReg {
public:
NativeRegisterContextDBReg_arm()
: NativeRegisterContextDBReg(/*enable_bit=*/0x1U) {}

private:
uint32_t GetWatchpointSize(uint32_t wp_index) override;

std::optional<WatchpointDetails>
AdjustWatchpoint(const WatchpointDetails &details) override;

std::optional<BreakpointDetails>
AdjustBreakpoint(const BreakpointDetails &details) override;

uint32_t MakeBreakControlValue(size_t size) override;

uint32_t MakeWatchControlValue(lldb::addr_t addr, size_t size,
uint32_t watch_flags) override;

bool ValidateBreakpoint(size_t size, lldb::addr_t addr) override {
// Break on 4 or 2 byte instructions.
return size == 4 || size == 2;
}
};

} // namespace lldb_private

#endif // #ifndef lldb_NativeRegisterContextDBReg_arm_h
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ uint32_t NativeRegisterContextDBReg_arm64::MakeBreakControlValue(size_t size) {
return m_hw_dbg_enable_bit | pac_bits | encoded_size;
}

uint32_t
NativeRegisterContextDBReg_arm64::MakeWatchControlValue(size_t size,
uint32_t watch_flags) {
uint32_t NativeRegisterContextDBReg_arm64::MakeWatchControlValue(
lldb::addr_t addr, size_t size, uint32_t watch_flags) {
(void)addr;

// PAC (bits 2:1): 0b10
const uint32_t pac_bits = 2 << 1;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class NativeRegisterContextDBReg_arm64 : public NativeRegisterContextDBReg {

uint32_t MakeBreakControlValue(size_t size) override;

uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override;
uint32_t MakeWatchControlValue(lldb::addr_t addr, size_t size,
uint32_t watch_flags) override;
};

} // namespace lldb_private
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ NativeRegisterContextDBReg_loongarch::MakeBreakControlValue(size_t size) {
}

uint32_t NativeRegisterContextDBReg_loongarch::MakeWatchControlValue(
size_t size, uint32_t watch_flags) {
lldb::addr_t addr, size_t size, uint32_t watch_flags) {
(void)addr;
// Encoding hardware watchpoint control value.
// Size encoded:
// case 1 : 0b11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class NativeRegisterContextDBReg_loongarch : public NativeRegisterContextDBReg {

uint32_t MakeBreakControlValue(size_t size) override;

uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override;
uint32_t MakeWatchControlValue(lldb::addr_t addr, size_t size,
uint32_t watch_flags) override;
};

} // namespace lldb_private
Expand Down
Loading