Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
96380 views
//===-- NativeRegisterContextDBReg_arm64.cpp ------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "NativeRegisterContextDBReg_arm64.h"910#include "lldb/Utility/LLDBLog.h"11#include "lldb/Utility/Log.h"12#include "lldb/Utility/RegisterValue.h"1314using namespace lldb_private;1516// E (bit 0), used to enable breakpoint/watchpoint17constexpr uint32_t g_enable_bit = 1;18// PAC (bits 2:1): 0b1019constexpr uint32_t g_pac_bits = (2 << 1);2021// Returns appropriate control register bits for the specified size22static constexpr inline uint64_t GetSizeBits(int size) {23// BAS (bits 12:5) hold a bit-mask of addresses to watch24// e.g. 0b00000001 means 1 byte at address25// 0b00000011 means 2 bytes (addr..addr+1)26// ...27// 0b11111111 means 8 bytes (addr..addr+7)28return ((1 << size) - 1) << 5;29}3031uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareBreakpoints() {32Log *log = GetLog(LLDBLog::Breakpoints);33llvm::Error error = ReadHardwareDebugInfo();34if (error) {35LLDB_LOG_ERROR(log, std::move(error),36"failed to read debug registers: {0}");37return 0;38}3940return m_max_hbp_supported;41}4243uint32_t44NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr,45size_t size) {46Log *log = GetLog(LLDBLog::Breakpoints);47LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);4849// Read hardware breakpoint and watchpoint information.50llvm::Error error = ReadHardwareDebugInfo();51if (error) {52LLDB_LOG_ERROR(53log, std::move(error),54"unable to set breakpoint: failed to read debug registers: {0}");55return LLDB_INVALID_INDEX32;56}5758uint32_t control_value = 0, bp_index = 0;5960// Check if size has a valid hardware breakpoint length.61if (size != 4)62return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware63// breakpoint6465// Check 4-byte alignment for hardware breakpoint target address.66if (addr & 0x03)67return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned.6869// Setup control value70control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);7172// Iterate over stored breakpoints and find a free bp_index73bp_index = LLDB_INVALID_INDEX32;74for (uint32_t i = 0; i < m_max_hbp_supported; i++) {75if (!BreakpointIsEnabled(i))76bp_index = i; // Mark last free slot77else if (m_hbp_regs[i].address == addr)78return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.79}8081if (bp_index == LLDB_INVALID_INDEX32)82return LLDB_INVALID_INDEX32;8384// Update breakpoint in local cache85m_hbp_regs[bp_index].real_addr = addr;86m_hbp_regs[bp_index].address = addr;87m_hbp_regs[bp_index].control = control_value;8889// PTRACE call to set corresponding hardware breakpoint register.90error = WriteHardwareDebugRegs(eDREGTypeBREAK);9192if (error) {93m_hbp_regs[bp_index].address = 0;94m_hbp_regs[bp_index].control &= ~1;9596LLDB_LOG_ERROR(97log, std::move(error),98"unable to set breakpoint: failed to write debug registers: {0}");99return LLDB_INVALID_INDEX32;100}101102return bp_index;103}104105bool NativeRegisterContextDBReg_arm64::ClearHardwareBreakpoint(106uint32_t hw_idx) {107Log *log = GetLog(LLDBLog::Breakpoints);108LLDB_LOG(log, "hw_idx: {0}", hw_idx);109110// Read hardware breakpoint and watchpoint information.111llvm::Error error = ReadHardwareDebugInfo();112if (error) {113LLDB_LOG_ERROR(114log, std::move(error),115"unable to clear breakpoint: failed to read debug registers: {0}");116return false;117}118119if (hw_idx >= m_max_hbp_supported)120return false;121122// Create a backup we can revert to in case of failure.123lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address;124uint32_t tempControl = m_hbp_regs[hw_idx].control;125126m_hbp_regs[hw_idx].control &= ~g_enable_bit;127m_hbp_regs[hw_idx].address = 0;128129// PTRACE call to clear corresponding hardware breakpoint register.130error = WriteHardwareDebugRegs(eDREGTypeBREAK);131132if (error) {133m_hbp_regs[hw_idx].control = tempControl;134m_hbp_regs[hw_idx].address = tempAddr;135136LLDB_LOG_ERROR(137log, std::move(error),138"unable to clear breakpoint: failed to write debug registers: {0}");139return false;140}141142return true;143}144145Status NativeRegisterContextDBReg_arm64::GetHardwareBreakHitIndex(146uint32_t &bp_index, lldb::addr_t trap_addr) {147Log *log = GetLog(LLDBLog::Breakpoints);148149LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);150151lldb::addr_t break_addr;152153for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {154break_addr = m_hbp_regs[bp_index].address;155156if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) {157m_hbp_regs[bp_index].hit_addr = trap_addr;158return Status();159}160}161162bp_index = LLDB_INVALID_INDEX32;163return Status();164}165166Status NativeRegisterContextDBReg_arm64::ClearAllHardwareBreakpoints() {167Log *log = GetLog(LLDBLog::Breakpoints);168169LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__);170171// Read hardware breakpoint and watchpoint information.172llvm::Error error = ReadHardwareDebugInfo();173if (error)174return Status(std::move(error));175176for (uint32_t i = 0; i < m_max_hbp_supported; i++) {177if (BreakpointIsEnabled(i)) {178// Create a backup we can revert to in case of failure.179lldb::addr_t tempAddr = m_hbp_regs[i].address;180uint32_t tempControl = m_hbp_regs[i].control;181182// Clear watchpoints in local cache183m_hbp_regs[i].control &= ~g_enable_bit;184m_hbp_regs[i].address = 0;185186// Ptrace call to update hardware debug registers187error = WriteHardwareDebugRegs(eDREGTypeBREAK);188189if (error) {190m_hbp_regs[i].control = tempControl;191m_hbp_regs[i].address = tempAddr;192193return Status(std::move(error));194}195}196}197198return Status();199}200201bool NativeRegisterContextDBReg_arm64::BreakpointIsEnabled(uint32_t bp_index) {202if ((m_hbp_regs[bp_index].control & g_enable_bit) != 0)203return true;204else205return false;206}207208uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareWatchpoints() {209Log *log = GetLog(LLDBLog::Watchpoints);210llvm::Error error = ReadHardwareDebugInfo();211if (error) {212LLDB_LOG_ERROR(log, std::move(error),213"failed to read debug registers: {0}");214return 0;215}216217return m_max_hwp_supported;218}219220uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint(221lldb::addr_t addr, size_t size, uint32_t watch_flags) {222Log *log = GetLog(LLDBLog::Watchpoints);223LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,224watch_flags);225226// Read hardware breakpoint and watchpoint information.227llvm::Error error = ReadHardwareDebugInfo();228if (error) {229LLDB_LOG_ERROR(230log, std::move(error),231"unable to set watchpoint: failed to read debug registers: {0}");232return LLDB_INVALID_INDEX32;233}234235uint32_t control_value = 0, wp_index = 0;236lldb::addr_t real_addr = addr;237238// Check if we are setting watchpoint other than read/write/access Also239// update watchpoint flag to match AArch64 write-read bit configuration.240switch (watch_flags) {241case 1:242watch_flags = 2;243break;244case 2:245watch_flags = 1;246break;247case 3:248break;249default:250return LLDB_INVALID_INDEX32;251}252253// Check if size has a valid hardware watchpoint length.254if (size != 1 && size != 2 && size != 4 && size != 8)255return LLDB_INVALID_INDEX32;256257// Check 8-byte alignment for hardware watchpoint target address. Below is a258// hack to recalculate address and size in order to make sure we can watch259// non 8-byte aligned addresses as well.260if (addr & 0x07) {261uint8_t watch_mask = (addr & 0x07) + size;262263if (watch_mask > 0x08)264return LLDB_INVALID_INDEX32;265else if (watch_mask <= 0x02)266size = 2;267else if (watch_mask <= 0x04)268size = 4;269else270size = 8;271272addr = addr & (~0x07);273}274275// Setup control value276control_value = g_enable_bit | g_pac_bits | GetSizeBits(size);277control_value |= watch_flags << 3;278279// Iterate over stored watchpoints and find a free wp_index280wp_index = LLDB_INVALID_INDEX32;281for (uint32_t i = 0; i < m_max_hwp_supported; i++) {282if (!WatchpointIsEnabled(i))283wp_index = i; // Mark last free slot284else if (m_hwp_regs[i].address == addr) {285return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.286}287}288289if (wp_index == LLDB_INVALID_INDEX32)290return LLDB_INVALID_INDEX32;291292// Update watchpoint in local cache293m_hwp_regs[wp_index].real_addr = real_addr;294m_hwp_regs[wp_index].address = addr;295m_hwp_regs[wp_index].control = control_value;296297// PTRACE call to set corresponding watchpoint register.298error = WriteHardwareDebugRegs(eDREGTypeWATCH);299300if (error) {301m_hwp_regs[wp_index].address = 0;302m_hwp_regs[wp_index].control &= ~g_enable_bit;303304LLDB_LOG_ERROR(305log, std::move(error),306"unable to set watchpoint: failed to write debug registers: {0}");307return LLDB_INVALID_INDEX32;308}309310return wp_index;311}312313bool NativeRegisterContextDBReg_arm64::ClearHardwareWatchpoint(314uint32_t wp_index) {315Log *log = GetLog(LLDBLog::Watchpoints);316LLDB_LOG(log, "wp_index: {0}", wp_index);317318// Read hardware breakpoint and watchpoint information.319llvm::Error error = ReadHardwareDebugInfo();320if (error) {321LLDB_LOG_ERROR(322log, std::move(error),323"unable to clear watchpoint: failed to read debug registers: {0}");324return false;325}326327if (wp_index >= m_max_hwp_supported)328return false;329330// Create a backup we can revert to in case of failure.331lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;332uint32_t tempControl = m_hwp_regs[wp_index].control;333334// Update watchpoint in local cache335m_hwp_regs[wp_index].control &= ~g_enable_bit;336m_hwp_regs[wp_index].address = 0;337338// Ptrace call to update hardware debug registers339error = WriteHardwareDebugRegs(eDREGTypeWATCH);340341if (error) {342m_hwp_regs[wp_index].control = tempControl;343m_hwp_regs[wp_index].address = tempAddr;344345LLDB_LOG_ERROR(346log, std::move(error),347"unable to clear watchpoint: failed to write debug registers: {0}");348return false;349}350351return true;352}353354Status NativeRegisterContextDBReg_arm64::ClearAllHardwareWatchpoints() {355// Read hardware breakpoint and watchpoint information.356llvm::Error error = ReadHardwareDebugInfo();357if (error)358return Status(std::move(error));359360for (uint32_t i = 0; i < m_max_hwp_supported; i++) {361if (WatchpointIsEnabled(i)) {362// Create a backup we can revert to in case of failure.363lldb::addr_t tempAddr = m_hwp_regs[i].address;364uint32_t tempControl = m_hwp_regs[i].control;365366// Clear watchpoints in local cache367m_hwp_regs[i].control &= ~g_enable_bit;368m_hwp_regs[i].address = 0;369370// Ptrace call to update hardware debug registers371error = WriteHardwareDebugRegs(eDREGTypeWATCH);372373if (error) {374m_hwp_regs[i].control = tempControl;375m_hwp_regs[i].address = tempAddr;376377return Status(std::move(error));378}379}380}381382return Status();383}384385uint32_t386NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {387Log *log = GetLog(LLDBLog::Watchpoints);388LLDB_LOG(log, "wp_index: {0}", wp_index);389390switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) {391case 0x01:392return 1;393case 0x03:394return 2;395case 0x0f:396return 4;397case 0xff:398return 8;399default:400return 0;401}402}403404bool NativeRegisterContextDBReg_arm64::WatchpointIsEnabled(uint32_t wp_index) {405Log *log = GetLog(LLDBLog::Watchpoints);406LLDB_LOG(log, "wp_index: {0}", wp_index);407408if ((m_hwp_regs[wp_index].control & g_enable_bit) != 0)409return true;410else411return false;412}413414Status NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex(415uint32_t &wp_index, lldb::addr_t trap_addr) {416Log *log = GetLog(LLDBLog::Watchpoints);417LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);418419// Read hardware breakpoint and watchpoint information.420llvm::Error error = ReadHardwareDebugInfo();421if (error)422return Status(std::move(error));423424// Mask off ignored bits from watchpoint trap address.425trap_addr = FixWatchpointHitAddress(trap_addr);426427uint32_t watch_size;428lldb::addr_t watch_addr;429430for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) {431watch_size = GetWatchpointSize(wp_index);432watch_addr = m_hwp_regs[wp_index].address;433434if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&435trap_addr < watch_addr + watch_size) {436m_hwp_regs[wp_index].hit_addr = trap_addr;437return Status();438}439}440441wp_index = LLDB_INVALID_INDEX32;442return Status();443}444445lldb::addr_t446NativeRegisterContextDBReg_arm64::GetWatchpointAddress(uint32_t wp_index) {447Log *log = GetLog(LLDBLog::Watchpoints);448LLDB_LOG(log, "wp_index: {0}", wp_index);449450if (wp_index >= m_max_hwp_supported)451return LLDB_INVALID_ADDRESS;452453if (WatchpointIsEnabled(wp_index))454return m_hwp_regs[wp_index].real_addr;455return LLDB_INVALID_ADDRESS;456}457458lldb::addr_t459NativeRegisterContextDBReg_arm64::GetWatchpointHitAddress(uint32_t wp_index) {460Log *log = GetLog(LLDBLog::Watchpoints);461LLDB_LOG(log, "wp_index: {0}", wp_index);462463if (wp_index >= m_max_hwp_supported)464return LLDB_INVALID_ADDRESS;465466if (WatchpointIsEnabled(wp_index))467return m_hwp_regs[wp_index].hit_addr;468return LLDB_INVALID_ADDRESS;469}470471472