diff options
Diffstat (limited to 'lldb/source/Plugins/Process')
21 files changed, 671 insertions, 591 deletions
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp index fdafacf410d6..c1bc6a3f036b 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,522 +283,43 @@ 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__ - lldb::addr_t *addr_buf; - uint32_t *ctrl_buf; - - if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) { - addr_buf = &m_hwp_regs[hwb_index].address; - 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; - - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 2), ctrl_buf, - sizeof(unsigned int)); - } else { - addr_buf = &m_hbr_regs[hwb_index].address; - ctrl_buf = &m_hbr_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)); + uint32_t max_index = m_max_hbp_supported; + if (hwbType == eDREGTypeWATCH) + max_index = m_max_hwp_supported; - if (error.Fail()) + for (uint32_t idx = 0; idx < max_index; ++idx) + if (auto error = WriteHardwareDebugReg(hwbType, idx)) return error; - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 2), ctrl_buf, - sizeof(unsigned int)); - } - - return error; + return llvm::Error::success(); #else // __aarch64__ uint32_t max_supported = (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) @@ -806,12 +327,48 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs( : m_max_hbp_supported; auto ®s = (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) ? m_hwp_regs - : m_hbr_regs; + : m_hbp_regs; return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported, - regs); + regs) + .ToError(); #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; + } 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)addr_idx, + addr_buf, sizeof(unsigned int)); + + 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 { return reg_info->byte_offset - GetGPRSize(); diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h index 3a31d68d7a3c..cf36859b16ad 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 @@ public: 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,18 @@ private: 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; diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt index 5d99c22dafe1..b1e326ec064e 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 19601b7f35d4..f35027e93514 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp @@ -44,15 +44,16 @@ uint32_t NativeRegisterContextDBReg::SetHardwareBreakpoint(lldb::addr_t addr, return LLDB_INVALID_INDEX32; } - uint32_t control_value = 0, bp_index = 0; - if (!ValidateBreakpoint(size, addr)) return LLDB_INVALID_INDEX32; - control_value = MakeBreakControlValue(size); + uint32_t control_value = MakeBreakControlValue(size); + auto details = AdjustBreakpoint({size, addr}); + size = details.size; + addr = details.addr; // Iterate over stored breakpoints and find a free bp_index - bp_index = LLDB_INVALID_INDEX32; + uint32_t bp_index = LLDB_INVALID_INDEX32; for (uint32_t i = 0; i < m_max_hbp_supported; i++) { if (!BreakpointIsEnabled(i)) bp_index = i; // Mark last free slot @@ -222,7 +223,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: diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h index 9b6ecd382c3f..2dd11dcf74de 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,19 +77,25 @@ protected: // 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 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 GetWatchpointSize(uint32_t wp_index) = 0; - virtual llvm::Error ReadHardwareDebugInfo() = 0; virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0; virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) { 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 000000000000..803fed54d6b0 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp @@ -0,0 +1,117 @@ +//===-- 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::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}; +} + +NativeRegisterContextDBReg::BreakpointDetails +NativeRegisterContextDBReg_arm::AdjustBreakpoint( + const BreakpointDetails &details) { + BreakpointDetails bd = details; + // Use size to get a hint of arm vs thumb modes. + // LLDB usually aligns this client side, but other clients may not. + switch (bd.size) { + case 2: + bd.addr &= ~1; + break; + case 4: + bd.addr &= ~3; + break; + default: + // We assume that ValidateBreakpoint would have caught this earlier. + llvm_unreachable("Invalid breakpoint size!"); + } + + return bd; +} + +uint32_t NativeRegisterContextDBReg_arm::MakeBreakControlValue(size_t size) { + switch (size) { + case 2: + return (0x3 << 5) | 7; + case 4: + return (0xfu << 5) | 7; + default: + // ValidateBreakpoint would have rejected this earlier. + llvm_unreachable("Invalid breakpoint size."); + } +} + +uint32_t +NativeRegisterContextDBReg_arm::MakeWatchControlValue(size_t size, + uint32_t watch_flags) { + // 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. + // We assume that the address was 4 byte aligned by AdjustWatchpoint. + uint32_t byte_mask = (1u << size) - 1u; + + // 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 000000000000..253ae96c90c9 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h @@ -0,0 +1,42 @@ +//===-- 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; + + BreakpointDetails AdjustBreakpoint(const BreakpointDetails &details) override; + + uint32_t MakeBreakControlValue(size_t size) override; + + uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override; + + bool ValidateBreakpoint(size_t size, + [[maybe_unused]] 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 diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp index fbf128553fd5..3b8d6a84c964 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp @@ -79,7 +79,7 @@ static lldb_private::RegisterInfo g_register_infos_mte[] = { DEFINE_EXTENSION_REG(mte_ctrl)}; static lldb_private::RegisterInfo g_register_infos_tls[] = { - DEFINE_EXTENSION_REG(tpidr), + DEFINE_EXTENSION_REG_GENERIC(tpidr, LLDB_REGNUM_GENERIC_TP), // Only present when SME is present DEFINE_EXTENSION_REG(tpidr2)}; diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h index c9c4d7ceae55..829fa076d221 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h @@ -456,6 +456,7 @@ static uint32_t g_d29_invalidates[] = {fpu_v29, fpu_s29, LLDB_INVALID_REGNUM}; static uint32_t g_d30_invalidates[] = {fpu_v30, fpu_s30, LLDB_INVALID_REGNUM}; static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM}; +// clang-format off // Generates register kinds array with DWARF, EH frame and generic kind #define MISC_KIND(reg, type, generic_kind) \ { \ @@ -470,6 +471,11 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM}; LLDB_INVALID_REGNUM, lldb_kind \ } +#define GENERIC_KIND(genenric_kind) \ + { \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, genenric_kind, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM \ + } // Generates register kinds array for registers with only lldb kind #define KIND_ALL_INVALID \ { \ @@ -484,8 +490,6 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM}; #define MISC_FPU_KIND(lldb_kind) LLDB_KIND(lldb_kind) #define MISC_EXC_KIND(lldb_kind) LLDB_KIND(lldb_kind) -// clang-format off - // Defines a 64-bit general purpose register #define DEFINE_GPR64(reg, generic_kind) \ { \ @@ -540,6 +544,12 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM}; #reg, nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex, \ KIND_ALL_INVALID, nullptr, nullptr, nullptr, \ } + +#define DEFINE_EXTENSION_REG_GENERIC(reg, generic_kind) \ + { \ + #reg, nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex, \ + GENERIC_KIND(generic_kind), nullptr, nullptr, nullptr, \ + } static lldb_private::RegisterInfo g_register_infos_arm64_le[] = { // DEFINE_GPR64(name, GENERIC KIND) diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp index 88eeddf17878..8f5f1242116f 100644 --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -99,7 +99,7 @@ bool ProcessElfCore::CanDebug(lldb::TargetSP target_sp, ProcessElfCore::ProcessElfCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec &core_file) - : PostMortemProcess(target_sp, listener_sp, core_file) {} + : PostMortemProcess(target_sp, listener_sp, core_file), m_uuids() {} // Destructor ProcessElfCore::~ProcessElfCore() { @@ -257,12 +257,12 @@ Status ProcessElfCore::DoLoadCore() { // the main executable using data we found in the core file notes. lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); if (!exe_module_sp) { - // The first entry in the NT_FILE might be our executable if (!m_nt_file_entries.empty()) { + llvm::StringRef executable_path = GetMainExecutablePath(); ModuleSpec exe_module_spec; exe_module_spec.GetArchitecture() = arch; - exe_module_spec.GetUUID() = m_nt_file_entries[0].uuid; - exe_module_spec.GetFileSpec().SetFile(m_nt_file_entries[0].path, + exe_module_spec.GetUUID() = FindModuleUUID(executable_path); + exe_module_spec.GetFileSpec().SetFile(executable_path, FileSpec::Style::native); if (exe_module_spec.GetFileSpec()) { exe_module_sp = @@ -277,21 +277,38 @@ Status ProcessElfCore::DoLoadCore() { void ProcessElfCore::UpdateBuildIdForNTFileEntries() { Log *log = GetLog(LLDBLog::Process); + m_uuids.clear(); for (NT_FILE_Entry &entry : m_nt_file_entries) { - entry.uuid = FindBuidIdInCoreMemory(entry.start); - if (log && entry.uuid.IsValid()) - LLDB_LOGF(log, "%s found UUID @ %16.16" PRIx64 ": %s \"%s\"", - __FUNCTION__, entry.start, entry.uuid.GetAsString().c_str(), - entry.path.c_str()); + UUID uuid = FindBuidIdInCoreMemory(entry.start); + if (uuid.IsValid()) { + // Assert that either the path is not in the map or the UUID matches + assert(m_uuids.count(entry.path) == 0 || m_uuids[entry.path] == uuid); + m_uuids[entry.path] = uuid; + if (log) + LLDB_LOGF(log, "%s found UUID @ %16.16" PRIx64 ": %s \"%s\"", + __FUNCTION__, entry.start, uuid.GetAsString().c_str(), + entry.path.c_str()); + } } } +llvm::StringRef ProcessElfCore::GetMainExecutablePath() { + if (m_nt_file_entries.empty()) + return ""; + + // The first entry in the NT_FILE might be our executable + llvm::StringRef executable_path = m_nt_file_entries[0].path; + // Prefer the NT_FILE entry matching m_executable_name as main executable. + for (const NT_FILE_Entry &file_entry : m_nt_file_entries) + if (llvm::StringRef(file_entry.path).ends_with("/" + m_executable_name)) { + executable_path = file_entry.path; + break; + } + return executable_path; +} + UUID ProcessElfCore::FindModuleUUID(const llvm::StringRef path) { - // Returns the gnu uuid from matched NT_FILE entry - for (NT_FILE_Entry &entry : m_nt_file_entries) - if (path == entry.path && entry.uuid.IsValid()) - return entry.uuid; - return UUID(); + return m_uuids[std::string(path)]; } lldb_private::DynamicLoader *ProcessElfCore::GetDynamicLoader() { @@ -935,6 +952,7 @@ llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) { return status.ToError(); thread_data.name.assign (prpsinfo.pr_fname, strnlen (prpsinfo.pr_fname, sizeof (prpsinfo.pr_fname))); SetID(prpsinfo.pr_pid); + m_executable_name = prpsinfo.pr_fname; break; } case ELF::NT_SIGINFO: { diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h index a91c04a277f6..576c6858477a 100644 --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h @@ -17,6 +17,7 @@ #define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_PROCESSELFCORE_H #include <list> +#include <unordered_map> #include <vector> #include "lldb/Target/PostMortemProcess.h" @@ -115,10 +116,6 @@ private: lldb::addr_t end; lldb::addr_t file_ofs; std::string path; - // Add a UUID member for convenient access. The UUID value is not in the - // NT_FILE entries, we will find it in core memory and store it here for - // easy access. - lldb_private::UUID uuid; }; // For ProcessElfCore only @@ -152,6 +149,12 @@ private: // NT_FILE entries found from the NOTE segment std::vector<NT_FILE_Entry> m_nt_file_entries; + // Map from file path to UUID for quick lookup + std::unordered_map<std::string, lldb_private::UUID> m_uuids; + + // Executable name found from the ELF PRPSINFO + std::string m_executable_name; + // Parse thread(s) data structures(prstatus, prpsinfo) from given NOTE segment llvm::Error ParseThreadContextsFromNoteSegment( const elf::ELFProgramHeader &segment_header, @@ -165,6 +168,9 @@ private: lldb_private::UUID FindModuleUUID(const llvm::StringRef path) override; + // Returns the main executable path + llvm::StringRef GetMainExecutablePath(); + // Returns the value of certain type of note of a given start address lldb_private::UUID FindBuidIdInCoreMemory(lldb::addr_t address); diff --git a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp index 3a62081827c6..c9e90ebf8a4d 100644 --- a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp +++ b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp @@ -23,6 +23,10 @@ RegisterContextCorePOSIX_arm::RegisterContextCorePOSIX_arm( gpregset.GetByteSize()); m_gpr.SetData(m_gpr_buffer); m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + const llvm::Triple &target_triple = + m_register_info_up->GetTargetArchitecture().GetTriple(); + m_fpr = getRegset(notes, target_triple, ARM_VFP_Desc); } RegisterContextCorePOSIX_arm::~RegisterContextCorePOSIX_arm() = default; @@ -43,14 +47,25 @@ bool RegisterContextCorePOSIX_arm::WriteFPR() { bool RegisterContextCorePOSIX_arm::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) { - lldb::offset_t offset = reg_info->byte_offset; - if (offset + reg_info->byte_size <= GetGPRSize()) { - uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); - if (offset == reg_info->byte_offset + reg_info->byte_size) { - value = v; - return true; + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + return false; + + if (IsGPR(reg)) { + lldb::offset_t offset = reg_info->byte_offset; + if (m_gpr.ValidOffsetForDataOfSize(offset, reg_info->byte_size)) { + value = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + return offset == reg_info->byte_offset + reg_info->byte_size; } + } else if (IsFPR(reg)) { + assert(reg_info->byte_offset >= GetGPRSize()); + lldb::offset_t offset = reg_info->byte_offset - GetGPRSize(); + if (m_fpr.ValidOffsetForDataOfSize(offset, reg_info->byte_size)) + return value + .SetValueFromData(*reg_info, m_fpr, offset, /*partial_data_ok=*/false) + .Success(); } + return false; } diff --git a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h index 8d773a046bca..e466ee242181 100644 --- a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h +++ b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h @@ -48,6 +48,7 @@ protected: private: lldb::DataBufferSP m_gpr_buffer; lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; }; #endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_ARM_H diff --git a/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h b/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h index 59382a12cde0..a5b0d788a190 100644 --- a/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h +++ b/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h @@ -152,6 +152,11 @@ constexpr RegsetDesc AARCH64_GCS_Desc[] = { {llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_GCS}, }; +constexpr RegsetDesc ARM_VFP_Desc[] = { + {llvm::Triple::FreeBSD, llvm::Triple::arm, llvm::ELF::NT_ARM_VFP}, + {llvm::Triple::Linux, llvm::Triple::arm, llvm::ELF::NT_ARM_VFP}, +}; + constexpr RegsetDesc PPC_VMX_Desc[] = { {llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX}, {llvm::Triple::Linux, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX}, diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 85e141d41747..91f3a6ce383b 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -5773,7 +5773,7 @@ public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "process plugin packet monitor", "Send a qRcmd packet through the GDB remote protocol " - "and print the response." + "and print the response. " "The argument passed to this command will be hex " "encoded into a valid 'qRcmd' packet, sent and the " "response will be printed.") {} diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index 6037c8d2514b..a780b3f59ade 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -799,6 +799,23 @@ Status ProcessMachCore::DoGetMemoryRegionInfo(addr_t load_addr, region_info.SetMapped(MemoryRegionInfo::eNo); } return Status(); + } else { + // The corefile has no LC_SEGMENT at this virtual address, + // but see if there is a binary whose Section has been + // loaded at that address in the current Target. + Address addr; + if (GetTarget().ResolveLoadAddress(load_addr, addr)) { + SectionSP section_sp(addr.GetSection()); + if (section_sp) { + region_info.GetRange().SetRangeBase( + section_sp->GetLoadBaseAddress(&GetTarget())); + region_info.GetRange().SetByteSize(section_sp->GetByteSize()); + if (region_info.GetRange().Contains(load_addr)) { + region_info.SetLLDBPermissions(section_sp->GetPermissions()); + return Status(); + } + } + } } region_info.GetRange().SetRangeBase(load_addr); diff --git a/lldb/source/Plugins/Process/scripted/CMakeLists.txt b/lldb/source/Plugins/Process/scripted/CMakeLists.txt index 590166591a41..1516ad3132e3 100644 --- a/lldb/source/Plugins/Process/scripted/CMakeLists.txt +++ b/lldb/source/Plugins/Process/scripted/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_library(lldbPluginScriptedProcess PLUGIN ScriptedProcess.cpp ScriptedThread.cpp + ScriptedFrame.cpp LINK_COMPONENTS BinaryFormat diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp new file mode 100644 index 000000000000..6519df9185df --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp @@ -0,0 +1,191 @@ +//===----------------------------------------------------------------------===// +// +// 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 "ScriptedFrame.h" + +#include "lldb/Utility/DataBufferHeap.h" + +using namespace lldb; +using namespace lldb_private; + +void ScriptedFrame::CheckInterpreterAndScriptObject() const { + lldbassert(m_script_object_sp && "Invalid Script Object."); + lldbassert(GetInterface() && "Invalid Scripted Frame Interface."); +} + +llvm::Expected<std::shared_ptr<ScriptedFrame>> +ScriptedFrame::Create(ScriptedThread &thread, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_object) { + if (!thread.IsValid()) + return llvm::createStringError("Invalid scripted thread."); + + thread.CheckInterpreterAndScriptObject(); + + auto scripted_frame_interface = + thread.GetInterface()->CreateScriptedFrameInterface(); + if (!scripted_frame_interface) + return llvm::createStringError("failed to create scripted frame interface"); + + llvm::StringRef frame_class_name; + if (!script_object) { + std::optional<std::string> class_name = + thread.GetInterface()->GetScriptedFramePluginName(); + if (!class_name || class_name->empty()) + return llvm::createStringError( + "failed to get scripted thread class name"); + frame_class_name = *class_name; + } + + ExecutionContext exe_ctx(thread); + auto obj_or_err = scripted_frame_interface->CreatePluginObject( + frame_class_name, exe_ctx, args_sp, script_object); + + if (!obj_or_err) + return llvm::createStringError( + "failed to create script object: %s", + llvm::toString(obj_or_err.takeError()).c_str()); + + StructuredData::GenericSP owned_script_object_sp = *obj_or_err; + + if (!owned_script_object_sp->IsValid()) + return llvm::createStringError("created script object is invalid"); + + lldb::user_id_t frame_id = scripted_frame_interface->GetID(); + + lldb::addr_t pc = scripted_frame_interface->GetPC(); + SymbolContext sc; + Address symbol_addr; + if (pc != LLDB_INVALID_ADDRESS) { + symbol_addr.SetLoadAddress(pc, &thread.GetProcess()->GetTarget()); + symbol_addr.CalculateSymbolContext(&sc); + } + + std::optional<SymbolContext> maybe_sym_ctx = + scripted_frame_interface->GetSymbolContext(); + if (maybe_sym_ctx) { + sc = *maybe_sym_ctx; + } + + StructuredData::DictionarySP reg_info = + scripted_frame_interface->GetRegisterInfo(); + + if (!reg_info) + return llvm::createStringError( + "failed to get scripted thread registers info"); + + std::shared_ptr<DynamicRegisterInfo> register_info_sp = + DynamicRegisterInfo::Create( + *reg_info, thread.GetProcess()->GetTarget().GetArchitecture()); + + lldb::RegisterContextSP reg_ctx_sp; + + std::optional<std::string> reg_data = + scripted_frame_interface->GetRegisterContext(); + if (reg_data) { + DataBufferSP data_sp( + std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size())); + + if (!data_sp->GetByteSize()) + return llvm::createStringError("failed to copy raw registers data"); + + std::shared_ptr<RegisterContextMemory> reg_ctx_memory = + std::make_shared<RegisterContextMemory>( + thread, frame_id, *register_info_sp, LLDB_INVALID_ADDRESS); + if (!reg_ctx_memory) + return llvm::createStringError("failed to create a register context."); + + reg_ctx_memory->SetAllRegisterData(data_sp); + reg_ctx_sp = reg_ctx_memory; + } + + return std::make_shared<ScriptedFrame>( + thread, scripted_frame_interface, frame_id, pc, sc, reg_ctx_sp, + register_info_sp, owned_script_object_sp); +} + +ScriptedFrame::ScriptedFrame(ScriptedThread &thread, + ScriptedFrameInterfaceSP interface_sp, + lldb::user_id_t id, lldb::addr_t pc, + SymbolContext &sym_ctx, + lldb::RegisterContextSP reg_ctx_sp, + std::shared_ptr<DynamicRegisterInfo> reg_info_sp, + StructuredData::GenericSP script_object_sp) + : StackFrame(thread.shared_from_this(), /*frame_idx=*/id, + /*concrete_frame_idx=*/id, /*reg_context_sp=*/reg_ctx_sp, + /*cfa=*/0, /*pc=*/pc, + /*behaves_like_zeroth_frame=*/!id, /*symbol_ctx=*/&sym_ctx), + m_scripted_frame_interface_sp(interface_sp), + m_script_object_sp(script_object_sp), m_register_info_sp(reg_info_sp) {} + +ScriptedFrame::~ScriptedFrame() {} + +const char *ScriptedFrame::GetFunctionName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> function_name = GetInterface()->GetFunctionName(); + if (!function_name) + return nullptr; + return ConstString(function_name->c_str()).AsCString(); +} + +const char *ScriptedFrame::GetDisplayFunctionName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> function_name = + GetInterface()->GetDisplayFunctionName(); + if (!function_name) + return nullptr; + return ConstString(function_name->c_str()).AsCString(); +} + +bool ScriptedFrame::IsInlined() { return GetInterface()->IsInlined(); } + +bool ScriptedFrame::IsArtificial() const { + return GetInterface()->IsArtificial(); +} + +bool ScriptedFrame::IsHidden() { return GetInterface()->IsHidden(); } + +lldb::ScriptedFrameInterfaceSP ScriptedFrame::GetInterface() const { + return m_scripted_frame_interface_sp; +} + +std::shared_ptr<DynamicRegisterInfo> ScriptedFrame::GetDynamicRegisterInfo() { + CheckInterpreterAndScriptObject(); + + if (!m_register_info_sp) { + StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); + + Status error; + if (!reg_info) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, "Failed to get scripted frame registers info.", + error, LLDBLog::Thread); + + ThreadSP thread_sp = m_thread_wp.lock(); + if (!thread_sp || !thread_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, + "Failed to get scripted frame registers info: invalid thread.", error, + LLDBLog::Thread); + + ProcessSP process_sp = thread_sp->GetProcess(); + if (!process_sp || !process_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, + "Failed to get scripted frame registers info: invalid process.", + error, LLDBLog::Thread); + + m_register_info_sp = DynamicRegisterInfo::Create( + *reg_info, process_sp->GetTarget().GetArchitecture()); + } + + return m_register_info_sp; +} diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h new file mode 100644 index 000000000000..6e01e2fd7653 --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_PLUGINS_SCRIPTED_FRAME_H +#define LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H + +#include "Plugins/Process/Utility/RegisterContextMemory.h" +#include "ScriptedThread.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/DynamicRegisterInfo.h" +#include "lldb/Target/StackFrame.h" +#include <string> + +namespace lldb_private { +class ScriptedThread; +} + +namespace lldb_private { + +class ScriptedFrame : public lldb_private::StackFrame { + +public: + ScriptedFrame(ScriptedThread &thread, + lldb::ScriptedFrameInterfaceSP interface_sp, + lldb::user_id_t frame_idx, lldb::addr_t pc, + SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp, + std::shared_ptr<DynamicRegisterInfo> reg_info_sp, + StructuredData::GenericSP script_object_sp = nullptr); + + ~ScriptedFrame() override; + + static llvm::Expected<std::shared_ptr<ScriptedFrame>> + Create(ScriptedThread &thread, StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_object = nullptr); + + bool IsInlined() override; + bool IsArtificial() const override; + bool IsHidden() override; + const char *GetFunctionName() override; + const char *GetDisplayFunctionName() override; + +private: + void CheckInterpreterAndScriptObject() const; + lldb::ScriptedFrameInterfaceSP GetInterface() const; + + ScriptedFrame(const ScriptedFrame &) = delete; + const ScriptedFrame &operator=(const ScriptedFrame &) = delete; + + std::shared_ptr<DynamicRegisterInfo> GetDynamicRegisterInfo(); + + lldb::ScriptedFrameInterfaceSP m_scripted_frame_interface_sp; + lldb_private::StructuredData::GenericSP m_script_object_sp; + std::shared_ptr<DynamicRegisterInfo> m_register_info_sp; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp index d0d1508e8517..491efac5aade 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ScriptedThread.h" +#include "ScriptedFrame.h" #include "Plugins/Process/Utility/RegisterContextThreadMemory.h" #include "Plugins/Process/Utility/StopInfoMachException.h" @@ -173,40 +174,101 @@ bool ScriptedThread::LoadArtificialStackFrames() { .str(), error, LLDBLog::Thread); - StackFrameListSP frames = GetStackFrameList(); - - for (size_t idx = 0; idx < arr_size; idx++) { + auto create_frame_from_dict = + [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> { + Status error; std::optional<StructuredData::Dictionary *> maybe_dict = arr_sp->GetItemAtIndexAsDictionary(idx); - if (!maybe_dict) - return ScriptedInterface::ErrorWithMessage<bool>( + if (!maybe_dict) { + ScriptedInterface::ErrorWithMessage<bool>( LLVM_PRETTY_FUNCTION, llvm::Twine( "Couldn't get artificial stackframe dictionary at index (" + llvm::Twine(idx) + llvm::Twine(") from stackframe array.")) .str(), error, LLDBLog::Thread); + return error.ToError(); + } StructuredData::Dictionary *dict = *maybe_dict; lldb::addr_t pc; - if (!dict->GetValueForKeyAsInteger("pc", pc)) - return ScriptedInterface::ErrorWithMessage<bool>( + if (!dict->GetValueForKeyAsInteger("pc", pc)) { + ScriptedInterface::ErrorWithMessage<bool>( LLVM_PRETTY_FUNCTION, "Couldn't find value for key 'pc' in stackframe dictionary.", error, LLDBLog::Thread); + return error.ToError(); + } Address symbol_addr; symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget()); lldb::addr_t cfa = LLDB_INVALID_ADDRESS; bool cfa_is_valid = false; + const bool artificial = false; const bool behaves_like_zeroth_frame = false; SymbolContext sc; symbol_addr.CalculateSymbolContext(&sc); - StackFrameSP synth_frame_sp = std::make_shared<StackFrame>( - this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, - StackFrame::Kind::Artificial, behaves_like_zeroth_frame, &sc); + return std::make_shared<StackFrame>(this->shared_from_this(), idx, idx, cfa, + cfa_is_valid, pc, + StackFrame::Kind::Synthetic, artificial, + behaves_like_zeroth_frame, &sc); + }; + + auto create_frame_from_script_object = + [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> { + Status error; + StructuredData::ObjectSP object_sp = arr_sp->GetItemAtIndex(idx); + if (!object_sp || !object_sp->GetAsGeneric()) { + ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't get artificial stackframe object at index (" + + llvm::Twine(idx) + + llvm::Twine(") from stackframe array.")) + .str(), + error, LLDBLog::Thread); + return error.ToError(); + } + + auto frame_or_error = + ScriptedFrame::Create(*this, nullptr, object_sp->GetAsGeneric()); + + if (!frame_or_error) { + ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error); + return error.ToError(); + } + + StackFrameSP frame_sp = frame_or_error.get(); + lldbassert(frame_sp && "Couldn't initialize scripted frame."); + + return frame_sp; + }; + + StackFrameListSP frames = GetStackFrameList(); + + for (size_t idx = 0; idx < arr_size; idx++) { + StackFrameSP synth_frame_sp = nullptr; + + auto frame_from_dict_or_err = create_frame_from_dict(idx); + if (!frame_from_dict_or_err) { + auto frame_from_script_obj_or_err = create_frame_from_script_object(idx); + + if (!frame_from_script_obj_or_err) { + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't add artificial frame (" + llvm::Twine(idx) + + llvm::Twine(") to ScriptedThread StackFrameList.")) + .str(), + error, LLDBLog::Thread); + } else { + llvm::consumeError(frame_from_dict_or_err.takeError()); + synth_frame_sp = *frame_from_script_obj_or_err; + } + } else { + synth_frame_sp = *frame_from_dict_or_err; + } if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp)) return ScriptedInterface::ErrorWithMessage<bool>( diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h b/lldb/source/Plugins/Process/scripted/ScriptedThread.h index cd224d60ceef..ee5ace405967 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h @@ -15,11 +15,12 @@ #include "Plugins/Process/Utility/RegisterContextMemory.h" #include "lldb/Interpreter/ScriptInterpreter.h" -#include "lldb/Target//DynamicRegisterInfo.h" +#include "lldb/Target/DynamicRegisterInfo.h" #include "lldb/Target/Thread.h" namespace lldb_private { class ScriptedProcess; +class ScriptedFrame; } namespace lldb_private { @@ -61,6 +62,8 @@ public: StructuredData::ObjectSP FetchThreadExtendedInfo() override; private: + friend class ScriptedFrame; + void CheckInterpreterAndScriptObject() const; lldb::ScriptedThreadInterfaceSP GetInterface() const; |
