Index: include/lldb/Host/common/NativeRegisterContext.h =================================================================== --- include/lldb/Host/common/NativeRegisterContext.h +++ include/lldb/Host/common/NativeRegisterContext.h @@ -103,7 +103,7 @@ IsWatchpointHit(uint32_t wp_index, bool &is_hit); virtual Error - GetWatchpointHitIndex(uint32_t &wp_index); + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr); virtual Error IsWatchpointVacant (uint32_t wp_index, bool &is_vacant); Index: source/Host/common/NativeRegisterContext.cpp =================================================================== --- source/Host/common/NativeRegisterContext.cpp +++ source/Host/common/NativeRegisterContext.cpp @@ -309,7 +309,7 @@ } Error -NativeRegisterContext::GetWatchpointHitIndex(uint32_t &wp_index) +NativeRegisterContext::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) { wp_index = LLDB_INVALID_INDEX32; return Error ("not implemented"); Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -150,6 +150,17 @@ Error ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size); +#if defined (__arm__) || defined (__thumb__) + /// Reads hardware breakpoints and watchpoints capability information. + Error + ReadHardwareDebugInfo (lldb::tid_t tid, unsigned int &watch_count , + unsigned int &break_count); + + /// Write hardware breakpoint/watchpoint control and address registers. + Error + WriteHardwareDebugRegs (lldb::tid_t tid, lldb::addr_t *addr_buf, + uint32_t *cntrl_buf, int type, int count); +#endif /// Reads the specified register set into the specified buffer. /// For instance, the extended floating-point register set. Error Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -109,6 +109,19 @@ #ifndef PTRACE_ARCH_PRCTL #define PTRACE_ARCH_PRCTL 30 #endif +#if defined (__arm__) || defined (__thumb__) +#ifndef PTRACE_GETHBPREGS + #define PTRACE_GETHBPREGS 29 + #define PTRACE_SETHBPREGS 30 +#endif +#if !defined(PTRACE_TYPE_ARG3) + #define PTRACE_TYPE_ARG3 void * +#endif +#if !defined(PTRACE_TYPE_ARG4) + #define PTRACE_TYPE_ARG4 void * +#endif +#endif + #ifndef ARCH_GET_FS #define ARCH_SET_GS 0x1001 #define ARCH_SET_FS 0x1002 @@ -812,6 +825,45 @@ #endif } +#if defined (__arm__) || defined (__thumb__) + //------------------------------------------------------------------------------ + /// @class ReadDBGROperation + /// @brief Implements NativeProcessLinux::ReadDBGR. + class ReadDBGROperation : public Operation + { + public: + ReadDBGROperation(lldb::tid_t tid, unsigned int &count_wp, + unsigned int &count_bp) + : m_tid(tid), + m_count_wp(count_wp), + m_count_bp(count_bp) + { } + + void Execute(NativeProcessLinux *monitor) override; + + private: + lldb::tid_t m_tid; + unsigned int &m_count_wp; + unsigned int &m_count_bp; + unsigned int m_max_wp_len; + }; + + void + ReadDBGROperation::Execute(NativeProcessLinux *monitor) + { + unsigned int cap_val; + + PTRACE(PTRACE_GETHBPREGS, m_tid, nullptr, &cap_val, sizeof(unsigned int), m_error); + + if (!m_error.Success()) + return; + + m_count_wp = (cap_val >> 8) & 0xff; + m_count_bp = cap_val & 0xff; + m_max_wp_len = (cap_val >> 16) & 0xff; + } +#endif + //------------------------------------------------------------------------------ /// @class ReadRegisterSetOperation /// @brief Implements NativeProcessLinux::ReadRegisterSet. @@ -903,6 +955,66 @@ #endif } +#if defined (__arm__) || defined (__thumb__) + //------------------------------------------------------------------------------ + /// @class WriteDBGROperation + /// @brief Implements NativeProcessLinux::WriteFPR. + class WriteDBGROperation : public Operation + { + public: + WriteDBGROperation(lldb::tid_t tid, lldb::addr_t *addr_buf, + uint32_t *cntrl_buf, int type, int count) + : m_tid(tid), + m_address(addr_buf), + m_control(cntrl_buf), + m_type(type), + m_count(count) + { } + + void Execute(NativeProcessLinux *monitor) override; + + private: + lldb::tid_t m_tid; + lldb::addr_t * m_address; + uint32_t * m_control; + int m_type; + int m_count; + }; + + void + WriteDBGROperation::Execute(NativeProcessLinux *monitor) + { + if (m_type) + { + PTRACE(PTRACE_SETHBPREGS, m_tid, (PTRACE_TYPE_ARG3) ((m_count << 1) + 1), + m_address, sizeof(unsigned int), m_error); + + if (!m_error.Success()) + return; + + PTRACE(PTRACE_SETHBPREGS, m_tid, (PTRACE_TYPE_ARG3) ((m_count << 1) + 2), + m_control, sizeof(unsigned int), m_error); + + if (!m_error.Success()) + return; + } + else + { + PTRACE(PTRACE_SETHBPREGS, m_tid, (PTRACE_TYPE_ARG3) -((m_count << 1) + 1), + m_address, sizeof(unsigned int), m_error); + + if (!m_error.Success()) + return; + + PTRACE(PTRACE_SETHBPREGS, m_tid, (PTRACE_TYPE_ARG3) -((m_count << 1) + 2), + m_control, sizeof(unsigned int), m_error); + + if (!m_error.Success()) + return; + } + } +#endif + //------------------------------------------------------------------------------ /// @class WriteRegisterSetOperation /// @brief Implements NativeProcessLinux::WriteRegisterSet. @@ -2486,7 +2598,7 @@ { // If a watchpoint was hit, report it uint32_t wp_index; - Error error = thread_sp->GetRegisterContext()->GetWatchpointHitIndex(wp_index); + Error error = thread_sp->GetRegisterContext()->GetWatchpointHitIndex(wp_index, (lldb::addr_t)info->si_addr); if (error.Fail() && log) log->Printf("NativeProcessLinux::%s() " "received error while checking for watchpoint hits, " @@ -3904,6 +4016,26 @@ return op.GetError(); } +#if defined (__arm__) || defined (__thumb__) + +Error +NativeProcessLinux::ReadHardwareDebugInfo (lldb::tid_t tid, unsigned int &watch_count , unsigned int &break_count) +{ + ReadDBGROperation op(tid, watch_count, break_count); + m_monitor_up->DoOperation(&op); + return op.GetError(); +} + +Error +NativeProcessLinux::WriteHardwareDebugRegs (lldb::tid_t tid, lldb::addr_t *addr_buf, uint32_t *cntrl_buf, int type, int count) +{ + WriteDBGROperation op(tid, addr_buf, cntrl_buf, type, count); + m_monitor_up->DoOperation(&op); + return op.GetError(); +} + +#endif + Error NativeProcessLinux::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) { Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -42,6 +42,43 @@ Error WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) override; + //------------------------------------------------------------------ + // Hardware breakpoints/watchpoint mangement functions + //------------------------------------------------------------------ + + uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size) override; + + bool + ClearHardwareBreakpoint (uint32_t hw_idx) 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; + + Error + ClearAllHardwareWatchpoints () override; + + Error + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; + + lldb::addr_t + GetWatchpointAddress (uint32_t wp_index) override; + + bool + HardwareSingleStep (bool enable) override; + + uint32_t + GetWatchpointSize(uint32_t wp_index); + + bool + WatchpointIsEnabled(uint32_t wp_index); + private: struct RegInfo { @@ -77,6 +114,21 @@ uint32_t m_gpr_arm[k_num_gpr_registers_arm]; RegInfo m_reg_info; FPU m_fpr; + + // Debug register info for hardware breakpoints and watchpoints management. + struct DREG + { + lldb::addr_t address;// Breakpoint/watchpoint address value + uint32_t control; // Breakpoint/watchpoint control value + uint32_t refcount; // Used for enable/disable and refernce count + }; + + struct DREG m_hbr_regs[16]; // Arm native linux hardware breakpoints + struct DREG m_hwp_regs[16]; // 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; Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -12,10 +12,12 @@ #include "lldb/lldb-private-forward.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Error.h" +#include "lldb/Core/StreamString.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeThreadProtocol.h" #include "Plugins/Process/Linux/NativeProcessLinux.h" +#include "lldb/Core/Log.h" #define REG_CONTEXT_SIZE (GetGPRSize() + sizeof (m_fpr)) @@ -131,6 +133,13 @@ ::memset(&m_fpr, 0, sizeof (m_fpr)); ::memset(&m_gpr_arm, 0, sizeof (m_gpr_arm)); + ::memset(&m_hbr_regs, 0, sizeof (m_hbr_regs)); + ::memset(&m_hwp_regs, 0, sizeof (m_hwp_regs)); + + // 16 is just a maximum value, query hardware for actual watchpoint count + m_max_hwp_supported = 16; + m_max_hbp_supported = 16; + m_refresh_hwdebug_info = true; } uint32_t @@ -518,3 +527,382 @@ { return GetRegisterInfoInterface().GetGPRSize(); } + +uint32_t +NativeRegisterContextLinux_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + +#if defined (__arm__) || defined (__thumb__) + + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + if (!process_sp) + return false; + + NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); + // Check if our hardware breakpoint and watchpoint information is updated. + if (m_refresh_hwdebug_info) + { + process_p->ReadHardwareDebugInfo (m_thread.GetID (), m_max_hwp_supported, + m_max_hbp_supported); + m_refresh_hwdebug_info = false; + } +#endif + uint32_t control_value, bp_index; + + // Check if size has a valid hardware breakpoint length. + // Thumb instructions are 2-bytes but we have no way here to determine + // if target address is a thumb or arm instruction. + // TODO: Add support for setting thumb mode hardware breakpoints + if (size != 4 || size != 2) + return LLDB_INVALID_INDEX32; + + // Setup control value + // Make the byte_mask into a valid Byte Address Select mask + control_value = 0xfu << 5; + + // Enable this breakpoint and make it stop in privileged or user mode; + control_value |= 7; + + // Make sure bits 1:0 are clear in our address + // This should be different once we support thumb here. + addr &= ~((lldb::addr_t)3); + + // Iterate over stored hardware breakpoints + // Find a free bp_index or update reference count if duplicate. + 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 && m_hbr_regs[i].control == control_value) + { + bp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (bp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing watchpoint + if ((m_hbr_regs[bp_index].control & 1) == 0) + { + m_hbr_regs[bp_index].address = addr; + m_hbr_regs[bp_index].control = control_value; + m_hbr_regs[bp_index].refcount = 1; + + //TODO: PTRACE CALL HERE for an UPDATE + } + else + m_hbr_regs[bp_index].refcount++; + + return bp_index; +} + +bool +NativeRegisterContextLinux_arm::ClearHardwareBreakpoint (uint32_t hw_idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + if (hw_idx >= m_max_hbp_supported) + return false; + + // Update reference count if multiple references. + if (m_hbr_regs[hw_idx].refcount > 1) + { + m_hbr_regs[hw_idx].refcount--; + return true; + } + else if (m_hbr_regs[hw_idx].refcount == 1) + { + m_hbr_regs[hw_idx].control &= ~1; + m_hbr_regs[hw_idx].address = 0; + m_hbr_regs[hw_idx].refcount = 0; + + //TODO: PTRACE CALL HERE for an UPDATE + return true; + } + + return false; +} + +uint32_t +NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + return m_max_hwp_supported; +} + +uint32_t +NativeRegisterContextLinux_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + +#if defined (__arm__) || defined (__thumb__) + + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + if (!process_sp) + return false; + + NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); + // Check if our hardware breakpoint and watchpoint information is updated. + if (m_refresh_hwdebug_info) + { + process_p->ReadHardwareDebugInfo (m_thread.GetID (), m_max_hwp_supported, + m_max_hbp_supported); + m_refresh_hwdebug_info = false; + } +#endif + uint32_t control_value, wp_index, addr_word_offset, byte_mask; + // Can't watch zero bytes + // Can't watch more than 4 bytes per WVR/WCR pair + lldb_private::StreamString output_stream; + output_stream.Printf("Watchpoint created: "); + + if (size == 0 || size > 4) + return LLDB_INVALID_INDEX32; + + // 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 + // Find a free wp_index or update reference count if duplicate. + 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 && m_hwp_regs[i].control == control_value) + { + wp_index = i; // Mark duplicate index + break; // Stop searching here + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Add new or update existing watchpoint + if ((m_hwp_regs[wp_index].control & 1) == 0) + { + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + m_hwp_regs[wp_index].refcount = 1; +#if defined (__arm__) || defined (__thumb__) + + // PTRACE call to set corresponding watchpoint register. + process_p->WriteHardwareDebugRegs(m_thread.GetID (), &addr, + &control_value, 0, wp_index); +#endif + } + else + m_hwp_regs[wp_index].refcount++; + + return wp_index; +} + +bool +NativeRegisterContextLinux_arm::ClearHardwareWatchpoint (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + if (!process_sp) + return false; + + NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); + + if (wp_index >= m_max_hwp_supported) + return false; + + // Update reference count if multiple references. + if (m_hwp_regs[wp_index].refcount > 1) + { + m_hwp_regs[wp_index].refcount--; + return true; + } + else if (m_hwp_regs[wp_index].refcount == 1) + { + m_hwp_regs[wp_index].control &= ~1; + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].refcount = 0; +#if defined (__arm__) || defined (__thumb__) + + //TODO: PTRACE CALL HERE for an UPDATE + process_p->WriteHardwareDebugRegs(m_thread.GetID (), + &m_hwp_regs[wp_index].address, + &m_hwp_regs[wp_index].control, + 0, wp_index); +#endif + + return true; + } + + return false; +} + +Error +NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); + + Error ml_error; + ml_error.SetErrorToErrno(); + if (!process_sp) + return ml_error; + + NativeProcessLinux *const process_p = reinterpret_cast (process_sp.get ()); + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) + { + if (m_hwp_regs[i].control & 0x01) + { + m_hwp_regs[i].control &= ~1; + m_hwp_regs[i].address = 0; + m_hwp_regs[i].refcount = 0; + + process_p->WriteHardwareDebugRegs(m_thread.GetID (), + &m_hwp_regs[i].address, + &m_hwp_regs[i].control, + 0, i); + } + } + + return Error(); +} + +uint32_t +NativeRegisterContextLinux_arm::GetWatchpointSize(uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + 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(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + if ((m_hwp_regs[wp_index].control & 0x1) == 0x1) + return true; + else + return false; +} + +Error +NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + 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 (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index) + && trap_addr >= watch_addr && trap_addr < watch_addr + watch_size) + { + return Error(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Error(); +} + +lldb::addr_t +NativeRegisterContextLinux_arm::GetWatchpointAddress (uint32_t wp_index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].address; + else + return LLDB_INVALID_ADDRESS; +} + +bool +NativeRegisterContextLinux_arm::HardwareSingleStep (bool enable) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); + + if (log) + log->Printf ("NativeRegisterContextLinux_arm::%s()", __FUNCTION__); + + return false; +} Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.h @@ -50,7 +50,7 @@ IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; Error - GetWatchpointHitIndex(uint32_t &wp_index) override; + GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override; Error IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_x86_64.cpp @@ -1069,7 +1069,7 @@ } Error -NativeRegisterContextLinux_x86_64::GetWatchpointHitIndex(uint32_t &wp_index) { +NativeRegisterContextLinux_x86_64::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) { uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { Index: source/Plugins/Process/Linux/ProcessMonitor.h =================================================================== --- source/Plugins/Process/Linux/ProcessMonitor.h +++ source/Plugins/Process/Linux/ProcessMonitor.h @@ -138,6 +138,18 @@ bool ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size); +#if defined (__arm__) || defined (__thumb__) + /// Reads hardware breakpoints and watchpoints capability information. + bool + ReadHardwareDebugInfo (lldb::tid_t tid, unsigned int &watch_count , + unsigned int &break_count); + + /// Write hardware breakpoint/watchpoint control and address registers. + bool + WriteHardwareDebugRegs (lldb::tid_t tid, lldb::addr_t *addr_buf, + uint32_t *cntrl_buf, int type, int count); +#endif + /// Reads the specified register set into the specified buffer. /// For instance, the extended floating-point register set. bool Index: source/Plugins/Process/Linux/ProcessMonitor.cpp =================================================================== --- source/Plugins/Process/Linux/ProcessMonitor.cpp +++ source/Plugins/Process/Linux/ProcessMonitor.cpp @@ -70,6 +70,20 @@ #ifndef PTRACE_ARCH_PRCTL #define PTRACE_ARCH_PRCTL 30 #endif + +#if defined (__arm__) || defined (__thumb__) +#ifndef PTRACE_GETHBPREGS + #define PTRACE_GETHBPREGS 29 + #define PTRACE_SETHBPREGS 30 +#endif +#if !defined(PTRACE_TYPE_ARG3) + #define PTRACE_TYPE_ARG3 void * +#endif +#if !defined(PTRACE_TYPE_ARG4) + #define PTRACE_TYPE_ARG4 void * +#endif +#endif + #ifndef ARCH_GET_FS #define ARCH_SET_GS 0x1001 #define ARCH_SET_FS 0x1002 @@ -573,6 +587,94 @@ #endif } +#if defined (__arm__) || defined (__thumb__) +//------------------------------------------------------------------------------ +/// @class ReadDBGROperation +/// @brief Implements NativeProcessLinux::ReadDBGR. +class ReadDBGROperation : public Operation +{ +public: + ReadDBGROperation(lldb::tid_t tid, unsigned int &count_wp, + unsigned int &count_bp) + : m_tid(tid), + m_count_wp(count_wp), + m_count_bp(count_bp) + { } + + void Execute(ProcessMonitor *monitor) override; + +private: + lldb::tid_t m_tid; + unsigned int &m_count_wp; + unsigned int &m_count_bp; + unsigned int m_max_wp_len; +}; + +void +ReadDBGROperation::Execute(ProcessMonitor *monitor) +{ + unsigned int cap_val; + + PTRACE(PTRACE_GETHBPREGS, m_tid, nullptr, &cap_val, sizeof(unsigned int)); + + m_count_wp = (cap_val >> 8) & 0xff; + m_count_bp = cap_val & 0xff; + m_max_wp_len = (cap_val >> 16) & 0xff; +} +#endif + +#if defined (__arm__) || defined (__thumb__) +//------------------------------------------------------------------------------ +/// @class WriteDBGROperation +/// @brief Implements NativeProcessLinux::WriteFPR. +class WriteDBGROperation : public Operation +{ +public: + WriteDBGROperation(lldb::tid_t tid, lldb::addr_t *addr_buf, + uint32_t *cntrl_buf, int type, int count) + : m_tid(tid), + m_address(addr_buf), + m_control(cntrl_buf), + m_type(type), + m_count(count) + { } + + void Execute(ProcessMonitor *monitor) override; +private: + lldb::tid_t m_tid; + lldb::addr_t * m_address; + uint32_t * m_control; + int m_type; + int m_count; +}; + +void +WriteDBGROperation::Execute(ProcessMonitor *monitor) +{ + if (m_type) + { + PTRACE(PTRACE_SETHBPREGS, m_tid, + (PTRACE_TYPE_ARG3) ((m_count << 1) + 1), + m_address, sizeof(unsigned int)); + + PTRACE(PTRACE_SETHBPREGS, m_tid, + (PTRACE_TYPE_ARG3) ((m_count << 1) + 2), + m_control, sizeof(unsigned int)); + + } + else + { + PTRACE(PTRACE_SETHBPREGS, m_tid, + (PTRACE_TYPE_ARG3) -((m_count << 1) + 1), + m_address, sizeof(unsigned int)); + + PTRACE(PTRACE_SETHBPREGS, m_tid, + (PTRACE_TYPE_ARG3) -((m_count << 1) + 2), + m_control, sizeof(unsigned int)); + } +} +#endif + //------------------------------------------------------------------------------ /// @class WriteRegOperation /// @brief Implements ProcessMonitor::WriteRegisterValue. @@ -2184,6 +2286,28 @@ return result; } +#if defined (__arm__) || defined (__thumb__) + +bool +ProcessMonitor::ReadHardwareDebugInfo (lldb::tid_t tid, unsigned int &watch_count , unsigned int &break_count) +{ + bool result = true; + ReadDBGROperation op(tid, watch_count, break_count); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::WriteHardwareDebugRegs (lldb::tid_t tid, lldb::addr_t *addr_buf, uint32_t *cntrl_buf, int type, int count) +{ + bool result = true; + WriteDBGROperation op(tid, addr_buf, cntrl_buf, type, count); + DoOperation(&op); + return result; +} + +#endif + bool ProcessMonitor::WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char* reg_name, const RegisterValue &value)