https://github.com/DavidSpickett created https://github.com/llvm/llvm-project/pull/152284
Which is also used by AArch64 and LoongArch. To do this I had to make some adjustments to NativeRegisterContextDBReg: * New method AdjustBreakpoint. This is like AdjustWatchpoint, but only Arm needs to adjust breakpoints. For Arm this method removes the bottom address bits according to the mode. * MakeWatchControlValue now takes the address too. Arm uses this to generate the byte mask. To test this, I ran the test suite as usual on Arm and found no new failures, then ran it again with all test programs compiled with `-mthumb`. This means the binaries will be entirely Thumb code. Finally I tested it on AArch64, but this is mostly a build test, as I did not run the entire test suite compiled to AArch32. Prior to this change, these tests failed with `-mthumb`: ``` Failed Tests (14): lldb-api :: commands/process/reverse-continue/TestReverseContinue.py lldb-api :: functionalities/breakpoint/breakpoint_by_line_and_column/TestBreakpointByLineAndColumn.py lldb-api :: functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py lldb-api :: functionalities/reverse-execution/TestReverseContinueBreakpoints.py lldb-api :: functionalities/reverse-execution/TestReverseContinueWatchpoints.py lldb-api :: functionalities/tail_call_frames/disambiguate_call_site/TestDisambiguateCallSite.py lldb-api :: functionalities/tail_call_frames/disambiguate_paths_to_common_sink/TestDisambiguatePathsToCommonSink.py lldb-api :: functionalities/tail_call_frames/disambiguate_tail_call_seq/TestDisambiguateTailCallSeq.py lldb-api :: functionalities/tail_call_frames/inlining_and_tail_calls/TestInliningAndTailCalls.py lldb-api :: functionalities/tail_call_frames/sbapi_support/TestTailCallFrameSBAPI.py lldb-api :: functionalities/tail_call_frames/thread_step_out_message/TestArtificialFrameStepOutMessage.py lldb-api :: functionalities/thread/jump/TestThreadJump.py lldb-api :: lang/c/vla/TestVLA.py lldb-api :: linux/sepdebugsymlink/TestTargetSymbolsSepDebugSymlink.py ``` After this change, no new failures occurred. So I am confident it is correct. Looking at those failures, it's a few things: * Something in the reverse execution wrapper that I can't figure out, but is likely nothing to do with the real breakpoints. * The inability to tail call when building thumb code, because the call goes via. a veneer that might do a mode switch. * Assumptions about source locations being in specific places. None of which are massive issues I feel like need fixing before doing this port. >From 6753d9b62e599dac57d8ba710cedf0640e5b43cd Mon Sep 17 00:00:00 2001 From: David Spickett <david.spick...@linaro.org> Date: Mon, 16 Dec 2024 16:02:33 +0000 Subject: [PATCH 1/2] [lldb][ARM] Port Arm Linux to use NativeRegisterContextDBReg Which is also used by AArch64 and LoongArch. To do this I had to make some adjustments to NativeRegisterContextDBReg: * New method AdjustBreakpoint. This is like AdjustWatchpoint, but only Arm needs to adjust breakpoints. For Arm this method removes the bottom address bits according to the mode. * MakeWatchControlValue now takes the address too. Arm uses this to generate the byte mask. To test this, I ran the test suite as usual on Arm and found no new failures, then ran it again with all test programs compiled with `-mthumb`. This means the binaries will be entirely Thumb code. Finally I tested it on AArch64, but this is mostly a build test, as I did not run the entire test suite compiled to AArch32 mode. Prior to this change, these tests failed with `-mthumb`: ``` Failed Tests (14): lldb-api :: commands/process/reverse-continue/TestReverseContinue.py lldb-api :: functionalities/breakpoint/breakpoint_by_line_and_column/TestBreakpointByLineAndColumn.py lldb-api :: functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py lldb-api :: functionalities/reverse-execution/TestReverseContinueBreakpoints.py lldb-api :: functionalities/reverse-execution/TestReverseContinueWatchpoints.py lldb-api :: functionalities/tail_call_frames/disambiguate_call_site/TestDisambiguateCallSite.py lldb-api :: functionalities/tail_call_frames/disambiguate_paths_to_common_sink/TestDisambiguatePathsToCommonSink.py lldb-api :: functionalities/tail_call_frames/disambiguate_tail_call_seq/TestDisambiguateTailCallSeq.py lldb-api :: functionalities/tail_call_frames/inlining_and_tail_calls/TestInliningAndTailCalls.py lldb-api :: functionalities/tail_call_frames/sbapi_support/TestTailCallFrameSBAPI.py lldb-api :: functionalities/tail_call_frames/thread_step_out_message/TestArtificialFrameStepOutMessage.py lldb-api :: functionalities/thread/jump/TestThreadJump.py lldb-api :: lang/c/vla/TestVLA.py lldb-api :: linux/sepdebugsymlink/TestTargetSymbolsSepDebugSymlink.py ``` After this change, no new failures occured. So I am confident it is correct. Looking into those failures, it's a few things: * Something in the reverse execution wrapper that I can't figure out, but is likely nothing to do with the real breakpoints. * The inability to tail call when building thumb code, because the call goes via. a veneer that might do a mode switch. * Assumptions about source locations being in speciifc places. None of which are massive issues I feel like need fixing before doing this port. --- .../Linux/NativeRegisterContextLinux_arm.cpp | 529 ++---------------- .../Linux/NativeRegisterContextLinux_arm.h | 51 +- .../Plugins/Process/Utility/CMakeLists.txt | 1 + .../Utility/NativeRegisterContextDBReg.cpp | 4 +- .../Utility/NativeRegisterContextDBReg.h | 14 +- .../NativeRegisterContextDBReg_arm.cpp | 116 ++++ .../Utility/NativeRegisterContextDBReg_arm.h | 46 ++ .../NativeRegisterContextDBReg_arm64.cpp | 7 +- .../NativeRegisterContextDBReg_arm64.h | 3 +- .../NativeRegisterContextDBReg_loongarch.cpp | 3 +- .../NativeRegisterContextDBReg_loongarch.h | 3 +- 11 files changed, 243 insertions(+), 534 deletions(-) create mode 100644 lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp create mode 100644 lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp index fdafacf410d64..25e91c84ec584 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -76,7 +76,7 @@ NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm( ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr_arm, 0, sizeof(m_gpr_arm)); ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); - ::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs)); + ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); // 16 is just a maximum value, query hardware for actual watchpoint count m_max_hwp_supported = 16; @@ -283,484 +283,60 @@ bool NativeRegisterContextLinux_arm::IsFPR(unsigned reg) const { return false; } -uint32_t NativeRegisterContextLinux_arm::NumSupportedHardwareBreakpoints() { - Log *log = GetLog(POSIXLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__); - - Status error; - - // Read hardware breakpoint and watchpoint information. - error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return 0; - - LLDB_LOG(log, "{0}", m_max_hbp_supported); - return m_max_hbp_supported; -} - -uint32_t -NativeRegisterContextLinux_arm::SetHardwareBreakpoint(lldb::addr_t addr, - size_t size) { - Log *log = GetLog(POSIXLog::Breakpoints); - LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return LLDB_INVALID_INDEX32; - - uint32_t control_value = 0, bp_index = 0; - - // Setup address and control values. - // Use size to get a hint of arm vs thumb modes. - switch (size) { - case 2: - control_value = (0x3 << 5) | 7; - addr &= ~1; - break; - case 4: - control_value = (0xfu << 5) | 7; - addr &= ~3; - break; - default: - return LLDB_INVALID_INDEX32; - } - - // 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 ((m_hbr_regs[i].control & 1) == 0) { - bp_index = i; // Mark last free slot - } else if (m_hbr_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_hbr_regs[bp_index].real_addr = addr; - m_hbr_regs[bp_index].address = addr; - m_hbr_regs[bp_index].control = control_value; - - // PTRACE call to set corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, - bp_index); - - if (error.Fail()) { - m_hbr_regs[bp_index].address = 0; - m_hbr_regs[bp_index].control &= ~1; - - return LLDB_INVALID_INDEX32; - } - - return bp_index; -} - -bool NativeRegisterContextLinux_arm::ClearHardwareBreakpoint(uint32_t hw_idx) { - Log *log = GetLog(POSIXLog::Breakpoints); - LLDB_LOG(log, "hw_idx: {0}", hw_idx); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - 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_hbr_regs[hw_idx].address; - uint32_t tempControl = m_hbr_regs[hw_idx].control; - - m_hbr_regs[hw_idx].control &= ~1; - m_hbr_regs[hw_idx].address = 0; - - // PTRACE call to clear corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, - hw_idx); - - if (error.Fail()) { - m_hbr_regs[hw_idx].control = tempControl; - m_hbr_regs[hw_idx].address = tempAddr; - - return false; - } - - return true; -} - -Status NativeRegisterContextLinux_arm::GetHardwareBreakHitIndex( - uint32_t &bp_index, lldb::addr_t trap_addr) { - Log *log = GetLog(POSIXLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__); - - lldb::addr_t break_addr; - - for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { - break_addr = m_hbr_regs[bp_index].address; - - if ((m_hbr_regs[bp_index].control & 0x1) && (trap_addr == break_addr)) { - m_hbr_regs[bp_index].hit_addr = trap_addr; - return Status(); - } - } - - bp_index = LLDB_INVALID_INDEX32; - return Status(); -} - -Status NativeRegisterContextLinux_arm::ClearAllHardwareBreakpoints() { - Log *log = GetLog(POSIXLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__); - - Status error; - - // Read hardware breakpoint and watchpoint information. - error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return error; - - lldb::addr_t tempAddr = 0; - uint32_t tempControl = 0; - - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - if (m_hbr_regs[i].control & 0x01) { - // Create a backup we can revert to in case of failure. - tempAddr = m_hbr_regs[i].address; - tempControl = m_hbr_regs[i].control; - - // Clear breakpoints in local cache - m_hbr_regs[i].control &= ~1; - m_hbr_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = - WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, i); - - if (error.Fail()) { - m_hbr_regs[i].control = tempControl; - m_hbr_regs[i].address = tempAddr; - - return error; - } - } - } - - return Status(); -} - -uint32_t NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints() { - Log *log = GetLog(POSIXLog::Watchpoints); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return 0; - - LLDB_LOG(log, "{0}", m_max_hwp_supported); - return m_max_hwp_supported; -} - -uint32_t NativeRegisterContextLinux_arm::SetHardwareWatchpoint( - lldb::addr_t addr, size_t size, uint32_t watch_flags) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, - watch_flags); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return LLDB_INVALID_INDEX32; - - uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0; - lldb::addr_t real_addr = addr; - - // Check if we are setting watchpoint other than read/write/access Also - // update watchpoint flag to match Arm write-read bit configuration. - switch (watch_flags) { - case 1: - watch_flags = 2; - break; - case 2: - watch_flags = 1; - break; - case 3: - break; - default: - return LLDB_INVALID_INDEX32; - } - - // Can't watch zero bytes - // Can't watch more than 4 bytes per WVR/WCR pair - - if (size == 0 || size > 4) - return LLDB_INVALID_INDEX32; - - // 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 LLDB_INVALID_INDEX32; - else if (watch_mask <= 0x02) - size = 2; - else - size = 4; - - addr = addr & (~0x03); - } - - // 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 - 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; - - // Make sure bits 1:0 are clear in our address - addr &= ~((lldb::addr_t)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 ((m_hwp_regs[i].control & 1) == 0) { - 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(NativeRegisterContextDBReg::eDREGTypeWATCH, - wp_index); - - if (error.Fail()) { - m_hwp_regs[wp_index].address = 0; - m_hwp_regs[wp_index].control &= ~1; - - return LLDB_INVALID_INDEX32; - } - - return wp_index; -} - -bool NativeRegisterContextLinux_arm::ClearHardwareWatchpoint( - uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - 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 &= ~1; - m_hwp_regs[wp_index].address = 0; - - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, - wp_index); - - if (error.Fail()) { - m_hwp_regs[wp_index].control = tempControl; - m_hwp_regs[wp_index].address = tempAddr; - - return false; - } - - return true; -} - -Status NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints() { - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return error; - - lldb::addr_t tempAddr = 0; - uint32_t tempControl = 0; - - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - if (m_hwp_regs[i].control & 0x01) { - // Create a backup we can revert to in case of failure. - tempAddr = m_hwp_regs[i].address; - tempControl = m_hwp_regs[i].control; - - // Clear watchpoints in local cache - m_hwp_regs[i].control &= ~1; - m_hwp_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = - WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, i); - - if (error.Fail()) { - m_hwp_regs[i].control = tempControl; - m_hwp_regs[i].address = tempAddr; - - return error; - } - } - } - - return Status(); -} - -uint32_t NativeRegisterContextLinux_arm::GetWatchpointSize(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::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; - } -} -bool NativeRegisterContextLinux_arm::WatchpointIsEnabled(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if ((m_hwp_regs[wp_index].control & 0x1) == 0x1) - return true; - else - return false; -} - -Status -NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index, - lldb::addr_t trap_addr) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, 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 -NativeRegisterContextLinux_arm::GetWatchpointAddress(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::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; - else - return LLDB_INVALID_ADDRESS; -} - -lldb::addr_t -NativeRegisterContextLinux_arm::GetWatchpointHitAddress(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::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; - else - return LLDB_INVALID_ADDRESS; -} - -Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() { - Status error; - - if (!m_refresh_hwdebug_info) { - return Status(); - } +llvm::Error NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) + return llvm::Error::success(); #ifdef __arm__ unsigned int cap_val; - - error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(), - nullptr, &cap_val, - sizeof(unsigned int)); + Status error = NativeProcessLinux::PtraceWrapper( + PTRACE_GETHBPREGS, m_thread.GetID(), nullptr, &cap_val, + sizeof(unsigned int)); if (error.Fail()) - return error; + return error.ToError(); m_max_hwp_supported = (cap_val >> 8) & 0xff; m_max_hbp_supported = cap_val & 0xff; m_refresh_hwdebug_info = false; - return error; + return error.ToError(); #else // __aarch64__ return arm64::ReadHardwareDebugInfo(m_thread.GetID(), m_max_hwp_supported, - m_max_hbp_supported); + m_max_hbp_supported) + .ToError(); #endif // ifdef __arm__ } -Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs( - NativeRegisterContextDBReg::DREGType hwbType, int hwb_index) { - Status error; - +llvm::Error +NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(DREGType hwbType) { #ifdef __arm__ + uint32_t max_index = m_max_hbp_supported; + if (hwbType == eDREGTypeWATCH) + max_index = m_max_hwp_supported; + + for (uint32_t idx = 0; idx < max_index; ++idx) + if (auto error = WriteHardwareDebugReg(hwbType, idx)) + return error; + + return llvm::Error::success(); +#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_hbp_regs; + return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported, + regs).ToError(); +#endif // ifdef __arm__ +} + +llvm::Error +NativeRegisterContextLinux_arm::WriteHardwareDebugReg(DREGType hwbType, + int hwb_index) { + Status error; lldb::addr_t *addr_buf; uint32_t *ctrl_buf; @@ -774,15 +350,18 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs( sizeof(unsigned int)); if (error.Fail()) - return error; + return error.ToError(); error = NativeProcessLinux::PtraceWrapper( PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 2), ctrl_buf, sizeof(unsigned int)); + + if (error.Fail()) + return error.ToError(); } else { - addr_buf = &m_hbr_regs[hwb_index].address; - ctrl_buf = &m_hbr_regs[hwb_index].control; + addr_buf = &m_hbp_regs[hwb_index].address; + ctrl_buf = &m_hbp_regs[hwb_index].control; error = NativeProcessLinux::PtraceWrapper( PTRACE_SETHBPREGS, m_thread.GetID(), @@ -790,26 +369,18 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs( sizeof(unsigned int)); if (error.Fail()) - return error; + return error.ToError(); error = NativeProcessLinux::PtraceWrapper( PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 2), ctrl_buf, sizeof(unsigned int)); + + if (error.Fail()) + return error.ToError(); } - 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__ + return error.ToError(); } 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 3a31d68d7a3c4..6ac19e45f1069 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -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" @@ -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); @@ -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; @@ -100,23 +68,16 @@ 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; + llvm::Error WriteHardwareDebugReg(DREGType hwbType, int hwb_index) override; uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt index 5d99c22dafe15..b1e326ec064e4 100644 --- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -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 diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp index 19601b7f35d47..a79adb8858635 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp @@ -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: @@ -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; diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h index 9b6ecd382c3f3..97e40ab14eaaf 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h @@ -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. @@ -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; diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp new file mode 100644 index 0000000000000..e3b9a49253d0c --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp @@ -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; +} diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h new file mode 100644 index 0000000000000..0af590d1be002 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h @@ -0,0 +1,46 @@ +//===-- 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; + } + + virtual llvm::Error WriteHardwareDebugReg(DREGType hwbType, + int hwb_index) = 0; +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextDBReg_arm_h diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp index f2f772ad615c9..c134d2f5d5ce2 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp @@ -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; diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h index 42d6bacf64861..b88efd67ad45a 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h @@ -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 diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp index b3b5e6b4d4139..e5beca468b74e 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.cpp @@ -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 diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h index 19c5e4cdea263..6582371cf1a11 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_loongarch.h @@ -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 >From abc7017284cf07b80d739a5356b7ff5413838ddf Mon Sep 17 00:00:00 2001 From: David Spickett <david.spick...@linaro.org> Date: Tue, 5 Aug 2025 12:37:48 +0000 Subject: [PATCH 2/2] refactor write registers --- .../Linux/NativeRegisterContextLinux_arm.cpp | 45 +++++++------------ .../Linux/NativeRegisterContextLinux_arm.h | 4 +- .../Utility/NativeRegisterContextDBReg_arm.h | 3 -- 3 files changed, 18 insertions(+), 34 deletions(-) diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp index 25e91c84ec584..85395f1c15125 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -333,55 +333,40 @@ NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(DREGType hwbType) { #endif // ifdef __arm__ } +#ifdef __arm__ llvm::Error NativeRegisterContextLinux_arm::WriteHardwareDebugReg(DREGType hwbType, int hwb_index) { Status error; lldb::addr_t *addr_buf; uint32_t *ctrl_buf; + int addr_idx = (hwb_index << 1) + 1; + int ctrl_idx = addr_idx + 1; if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) { + addr_idx *= -1; addr_buf = &m_hwp_regs[hwb_index].address; + ctrl_idx *= -1; ctrl_buf = &m_hwp_regs[hwb_index].control; - - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 1), addr_buf, - sizeof(unsigned int)); - - if (error.Fail()) - return error.ToError(); - - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 2), ctrl_buf, - sizeof(unsigned int)); - - if (error.Fail()) - return error.ToError(); } else { addr_buf = &m_hbp_regs[hwb_index].address; ctrl_buf = &m_hbp_regs[hwb_index].control; + } - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 1), addr_buf, - sizeof(unsigned int)); - - if (error.Fail()) - return error.ToError(); + error = NativeProcessLinux::PtraceWrapper( + PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t)addr_idx, + addr_buf, sizeof(unsigned int)); - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 2), ctrl_buf, - sizeof(unsigned int)); + if (error.Fail()) + return error.ToError(); - if (error.Fail()) - return error.ToError(); - } + error = NativeProcessLinux::PtraceWrapper( + PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t)ctrl_idx, + ctrl_buf, sizeof(unsigned int)); return error.ToError(); } +#endif // ifdef __arm__ uint32_t NativeRegisterContextLinux_arm::CalculateFprOffset( const RegisterInfo *reg_info) const { diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h index 6ac19e45f1069..cf36859b16ad4 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -77,7 +77,9 @@ class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux, llvm::Error ReadHardwareDebugInfo() override; llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; - llvm::Error WriteHardwareDebugReg(DREGType hwbType, int hwb_index) override; +#ifdef __arm__ + llvm::Error WriteHardwareDebugReg(DREGType hwbType, int hwb_index); +#endif uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h index 0af590d1be002..92cc6bb654e7e 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h @@ -36,9 +36,6 @@ class NativeRegisterContextDBReg_arm : public NativeRegisterContextDBReg { // Break on 4 or 2 byte instructions. return size == 4 || size == 2; } - - virtual llvm::Error WriteHardwareDebugReg(DREGType hwbType, - int hwb_index) = 0; }; } // namespace lldb_private _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits