| //===-- NativeProcessProtocol.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 "lldb/Host/common/NativeProcessProtocol.h" | |
| #include "lldb/Host/Host.h" | |
| #include "lldb/Host/common/NativeBreakpointList.h" | |
| #include "lldb/Host/common/NativeRegisterContext.h" | |
| #include "lldb/Host/common/NativeThreadProtocol.h" | |
| #include "lldb/Utility/LLDBAssert.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/State.h" | |
| #include "lldb/lldb-enumerations.h" | |
| #include "llvm/Support/Process.h" | |
| #include <optional> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| // NativeProcessProtocol Members | |
| NativeProcessProtocol::NativeProcessProtocol(lldb::pid_t pid, int terminal_fd, | |
| NativeDelegate &delegate) | |
| : m_pid(pid), m_delegate(delegate), m_terminal_fd(terminal_fd) { | |
| delegate.InitializeDelegate(this); | |
| } | |
| lldb_private::Status NativeProcessProtocol::Interrupt() { | |
| Status error; | |
| #if !defined(SIGSTOP) | |
| error = Status::FromErrorString("local host does not support signaling"); | |
| return error; | |
| #else | |
| return Signal(SIGSTOP); | |
| #endif | |
| } | |
| Status NativeProcessProtocol::IgnoreSignals(llvm::ArrayRef<int> signals) { | |
| m_signals_to_ignore.clear(); | |
| m_signals_to_ignore.insert_range(signals); | |
| return Status(); | |
| } | |
| lldb_private::Status | |
| NativeProcessProtocol::GetMemoryRegionInfo(lldb::addr_t load_addr, | |
| MemoryRegionInfo &range_info) { | |
| // Default: not implemented. | |
| return Status::FromErrorString("not implemented"); | |
| } | |
| lldb_private::Status | |
| NativeProcessProtocol::ReadMemoryTags(int32_t type, lldb::addr_t addr, | |
| size_t len, std::vector<uint8_t> &tags) { | |
| return Status::FromErrorString("not implemented"); | |
| } | |
| lldb_private::Status | |
| NativeProcessProtocol::WriteMemoryTags(int32_t type, lldb::addr_t addr, | |
| size_t len, | |
| const std::vector<uint8_t> &tags) { | |
| return Status::FromErrorString("not implemented"); | |
| } | |
| std::optional<WaitStatus> NativeProcessProtocol::GetExitStatus() { | |
| if (m_state == lldb::eStateExited) | |
| return m_exit_status; | |
| return std::nullopt; | |
| } | |
| bool NativeProcessProtocol::SetExitStatus(WaitStatus status, | |
| bool bNotifyStateChange) { | |
| Log *log = GetLog(LLDBLog::Process); | |
| LLDB_LOG(log, "status = {0}, notify = {1}", status, bNotifyStateChange); | |
| // Exit status already set | |
| if (m_state == lldb::eStateExited) { | |
| if (m_exit_status) | |
| LLDB_LOG(log, "exit status already set to {0}", *m_exit_status); | |
| else | |
| LLDB_LOG(log, "state is exited, but status not set"); | |
| return false; | |
| } | |
| m_state = lldb::eStateExited; | |
| m_exit_status = status; | |
| if (bNotifyStateChange) | |
| SynchronouslyNotifyProcessStateChanged(lldb::eStateExited); | |
| return true; | |
| } | |
| NativeThreadProtocol *NativeProcessProtocol::GetThreadAtIndex(uint32_t idx) { | |
| std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); | |
| if (idx < m_threads.size()) | |
| return m_threads[idx].get(); | |
| return nullptr; | |
| } | |
| NativeThreadProtocol * | |
| NativeProcessProtocol::GetThreadByIDUnlocked(lldb::tid_t tid) { | |
| for (const auto &thread : m_threads) { | |
| if (thread->GetID() == tid) | |
| return thread.get(); | |
| } | |
| return nullptr; | |
| } | |
| NativeThreadProtocol *NativeProcessProtocol::GetThreadByID(lldb::tid_t tid) { | |
| std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); | |
| return GetThreadByIDUnlocked(tid); | |
| } | |
| bool NativeProcessProtocol::IsAlive() const { | |
| return m_state != eStateDetached && m_state != eStateExited && | |
| m_state != eStateInvalid && m_state != eStateUnloaded; | |
| } | |
| const NativeWatchpointList::WatchpointMap & | |
| NativeProcessProtocol::GetWatchpointMap() const { | |
| return m_watchpoint_list.GetWatchpointMap(); | |
| } | |
| std::optional<std::pair<uint32_t, uint32_t>> | |
| NativeProcessProtocol::GetHardwareDebugSupportInfo() const { | |
| Log *log = GetLog(LLDBLog::Process); | |
| // get any thread | |
| NativeThreadProtocol *thread( | |
| const_cast<NativeProcessProtocol *>(this)->GetThreadAtIndex(0)); | |
| if (!thread) { | |
| LLDB_LOG(log, "failed to find a thread to grab a NativeRegisterContext!"); | |
| return std::nullopt; | |
| } | |
| NativeRegisterContext ®_ctx = thread->GetRegisterContext(); | |
| return std::make_pair(reg_ctx.NumSupportedHardwareBreakpoints(), | |
| reg_ctx.NumSupportedHardwareWatchpoints()); | |
| } | |
| Status NativeProcessProtocol::SetWatchpoint(lldb::addr_t addr, size_t size, | |
| uint32_t watch_flags, | |
| bool hardware) { | |
| // This default implementation assumes setting the watchpoint for the process | |
| // will require setting the watchpoint for each of the threads. Furthermore, | |
| // it will track watchpoints set for the process and will add them to each | |
| // thread that is attached to via the (FIXME implement) OnThreadAttached () | |
| // method. | |
| Log *log = GetLog(LLDBLog::Process); | |
| // Update the thread list | |
| UpdateThreads(); | |
| // Keep track of the threads we successfully set the watchpoint for. If one | |
| // of the thread watchpoint setting operations fails, back off and remove the | |
| // watchpoint for all the threads that were successfully set so we get back | |
| // to a consistent state. | |
| std::vector<NativeThreadProtocol *> watchpoint_established_threads; | |
| // Tell each thread to set a watchpoint. In the event that hardware | |
| // watchpoints are requested but the SetWatchpoint fails, try to set a | |
| // software watchpoint as a fallback. It's conceivable that if there are | |
| // more threads than hardware watchpoints available, some of the threads will | |
| // fail to set hardware watchpoints while software ones may be available. | |
| std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); | |
| for (const auto &thread : m_threads) { | |
| assert(thread && "thread list should not have a NULL thread!"); | |
| Status thread_error = | |
| thread->SetWatchpoint(addr, size, watch_flags, hardware); | |
| if (thread_error.Fail() && hardware) { | |
| // Try software watchpoints since we failed on hardware watchpoint | |
| // setting and we may have just run out of hardware watchpoints. | |
| thread_error = thread->SetWatchpoint(addr, size, watch_flags, false); | |
| if (thread_error.Success()) | |
| LLDB_LOG(log, | |
| "hardware watchpoint requested but software watchpoint set"); | |
| } | |
| if (thread_error.Success()) { | |
| // Remember that we set this watchpoint successfully in case we need to | |
| // clear it later. | |
| watchpoint_established_threads.push_back(thread.get()); | |
| } else { | |
| // Unset the watchpoint for each thread we successfully set so that we | |
| // get back to a consistent state of "not set" for the watchpoint. | |
| for (auto unwatch_thread_sp : watchpoint_established_threads) { | |
| Status remove_error = unwatch_thread_sp->RemoveWatchpoint(addr); | |
| if (remove_error.Fail()) | |
| LLDB_LOG(log, "RemoveWatchpoint failed for pid={0}, tid={1}: {2}", | |
| GetID(), unwatch_thread_sp->GetID(), remove_error); | |
| } | |
| return thread_error; | |
| } | |
| } | |
| return m_watchpoint_list.Add(addr, size, watch_flags, hardware); | |
| } | |
| Status NativeProcessProtocol::RemoveWatchpoint(lldb::addr_t addr) { | |
| // Update the thread list | |
| UpdateThreads(); | |
| Status overall_error; | |
| std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); | |
| for (const auto &thread : m_threads) { | |
| assert(thread && "thread list should not have a NULL thread!"); | |
| Status thread_error = thread->RemoveWatchpoint(addr); | |
| if (thread_error.Fail()) { | |
| // Keep track of the first thread error if any threads fail. We want to | |
| // try to remove the watchpoint from every thread, though, even if one or | |
| // more have errors. | |
| if (!overall_error.Fail()) | |
| overall_error = std::move(thread_error); | |
| } | |
| } | |
| Status error = m_watchpoint_list.Remove(addr); | |
| return overall_error.Fail() ? std::move(overall_error) : std::move(error); | |
| } | |
| const HardwareBreakpointMap & | |
| NativeProcessProtocol::GetHardwareBreakpointMap() const { | |
| return m_hw_breakpoints_map; | |
| } | |
| Status NativeProcessProtocol::SetHardwareBreakpoint(lldb::addr_t addr, | |
| size_t size) { | |
| // This default implementation assumes setting a hardware breakpoint for this | |
| // process will require setting same hardware breakpoint for each of its | |
| // existing threads. New thread will do the same once created. | |
| Log *log = GetLog(LLDBLog::Process); | |
| // Update the thread list | |
| UpdateThreads(); | |
| // Exit here if target does not have required hardware breakpoint capability. | |
| auto hw_debug_cap = GetHardwareDebugSupportInfo(); | |
| if (hw_debug_cap == std::nullopt || hw_debug_cap->first == 0 || | |
| hw_debug_cap->first <= m_hw_breakpoints_map.size()) | |
| return Status::FromErrorString( | |
| "Target does not have required no of hardware breakpoints"); | |
| // Vector below stores all thread pointer for which we have we successfully | |
| // set this hardware breakpoint. If any of the current process threads fails | |
| // to set this hardware breakpoint then roll back and remove this breakpoint | |
| // for all the threads that had already set it successfully. | |
| std::vector<NativeThreadProtocol *> breakpoint_established_threads; | |
| // Request to set a hardware breakpoint for each of current process threads. | |
| std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); | |
| for (const auto &thread : m_threads) { | |
| assert(thread && "thread list should not have a NULL thread!"); | |
| Status thread_error = thread->SetHardwareBreakpoint(addr, size); | |
| if (thread_error.Success()) { | |
| // Remember that we set this breakpoint successfully in case we need to | |
| // clear it later. | |
| breakpoint_established_threads.push_back(thread.get()); | |
| } else { | |
| // Unset the breakpoint for each thread we successfully set so that we | |
| // get back to a consistent state of "not set" for this hardware | |
| // breakpoint. | |
| for (auto rollback_thread_sp : breakpoint_established_threads) { | |
| Status remove_error = | |
| rollback_thread_sp->RemoveHardwareBreakpoint(addr); | |
| if (remove_error.Fail()) | |
| LLDB_LOG(log, | |
| "RemoveHardwareBreakpoint failed for pid={0}, tid={1}: {2}", | |
| GetID(), rollback_thread_sp->GetID(), remove_error); | |
| } | |
| return thread_error; | |
| } | |
| } | |
| // Register new hardware breakpoint into hardware breakpoints map of current | |
| // process. | |
| m_hw_breakpoints_map[addr] = {addr, size}; | |
| return Status(); | |
| } | |
| Status NativeProcessProtocol::RemoveHardwareBreakpoint(lldb::addr_t addr) { | |
| // Update the thread list | |
| UpdateThreads(); | |
| Status error; | |
| std::lock_guard<std::recursive_mutex> guard(m_threads_mutex); | |
| for (const auto &thread : m_threads) { | |
| assert(thread && "thread list should not have a NULL thread!"); | |
| error = thread->RemoveHardwareBreakpoint(addr); | |
| } | |
| // Also remove from hardware breakpoint map of current process. | |
| m_hw_breakpoints_map.erase(addr); | |
| return error; | |
| } | |
| void NativeProcessProtocol::SynchronouslyNotifyProcessStateChanged( | |
| lldb::StateType state) { | |
| Log *log = GetLog(LLDBLog::Process); | |
| m_delegate.ProcessStateChanged(this, state); | |
| switch (state) { | |
| case eStateStopped: | |
| case eStateExited: | |
| case eStateCrashed: | |
| NotifyTracersProcessDidStop(); | |
| break; | |
| default: | |
| break; | |
| } | |
| LLDB_LOG(log, "sent state notification [{0}] from process {1}", state, | |
| GetID()); | |
| } | |
| void NativeProcessProtocol::NotifyDidExec() { | |
| Log *log = GetLog(LLDBLog::Process); | |
| LLDB_LOG(log, "process {0} exec()ed", GetID()); | |
| m_software_breakpoints.clear(); | |
| m_delegate.DidExec(this); | |
| } | |
| Status NativeProcessProtocol::SetSoftwareBreakpoint(lldb::addr_t addr, | |
| uint32_t size_hint) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOG(log, "addr = {0:x}, size_hint = {1}", addr, size_hint); | |
| auto it = m_software_breakpoints.find(addr); | |
| if (it != m_software_breakpoints.end()) { | |
| ++it->second.ref_count; | |
| return Status(); | |
| } | |
| auto expected_bkpt = EnableSoftwareBreakpoint(addr, size_hint); | |
| if (!expected_bkpt) | |
| return Status::FromError(expected_bkpt.takeError()); | |
| m_software_breakpoints.emplace(addr, std::move(*expected_bkpt)); | |
| return Status(); | |
| } | |
| Status NativeProcessProtocol::RemoveSoftwareBreakpoint(lldb::addr_t addr) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| LLDB_LOG(log, "addr = {0:x}", addr); | |
| auto it = m_software_breakpoints.find(addr); | |
| if (it == m_software_breakpoints.end()) | |
| return Status::FromErrorString("Breakpoint not found."); | |
| assert(it->second.ref_count > 0); | |
| if (--it->second.ref_count > 0) | |
| return Status(); | |
| // Remove the entry from m_software_breakpoints rightaway, so that we don't | |
| // leave behind an entry with ref_count == 0 in case one of the following | |
| // conditions returns an error. The breakpoint is moved so that it can be | |
| // accessed below. | |
| SoftwareBreakpoint bkpt = std::move(it->second); | |
| m_software_breakpoints.erase(it); | |
| // This is the last reference. Let's remove the breakpoint. | |
| Status error; | |
| // Clear a software breakpoint instruction | |
| llvm::SmallVector<uint8_t, 4> curr_break_op(bkpt.breakpoint_opcodes.size(), | |
| 0); | |
| // Read the breakpoint opcode | |
| size_t bytes_read = 0; | |
| error = | |
| ReadMemory(addr, curr_break_op.data(), curr_break_op.size(), bytes_read); | |
| if (error.Fail() || bytes_read < curr_break_op.size()) { | |
| return Status::FromErrorStringWithFormat( | |
| "addr=0x%" PRIx64 ": tried to read %zu bytes but only read %zu", addr, | |
| curr_break_op.size(), bytes_read); | |
| } | |
| const auto &saved = bkpt.saved_opcodes; | |
| // Make sure the breakpoint opcode exists at this address | |
| if (llvm::ArrayRef(curr_break_op) != bkpt.breakpoint_opcodes) { | |
| if (curr_break_op != bkpt.saved_opcodes) | |
| return Status::FromErrorString( | |
| "Original breakpoint trap is no longer in memory."); | |
| LLDB_LOG(log, | |
| "Saved opcodes ({0:@[x]}) have already been restored at {1:x}.", | |
| llvm::make_range(saved.begin(), saved.end()), addr); | |
| } else { | |
| // We found a valid breakpoint opcode at this address, now restore the | |
| // saved opcode. | |
| size_t bytes_written = 0; | |
| error = WriteMemory(addr, saved.data(), saved.size(), bytes_written); | |
| if (error.Fail() || bytes_written < saved.size()) { | |
| return Status::FromErrorStringWithFormat( | |
| "addr=0x%" PRIx64 ": tried to write %zu bytes but only wrote %zu", | |
| addr, saved.size(), bytes_written); | |
| } | |
| // Verify that our original opcode made it back to the inferior | |
| llvm::SmallVector<uint8_t, 4> verify_opcode(saved.size(), 0); | |
| size_t verify_bytes_read = 0; | |
| error = ReadMemory(addr, verify_opcode.data(), verify_opcode.size(), | |
| verify_bytes_read); | |
| if (error.Fail() || verify_bytes_read < verify_opcode.size()) { | |
| return Status::FromErrorStringWithFormat( | |
| "addr=0x%" PRIx64 | |
| ": tried to read %zu verification bytes but only read %zu", | |
| addr, verify_opcode.size(), verify_bytes_read); | |
| } | |
| if (verify_opcode != saved) | |
| LLDB_LOG(log, "Restoring bytes at {0:x}: {1:@[x]}", addr, | |
| llvm::make_range(saved.begin(), saved.end())); | |
| } | |
| return Status(); | |
| } | |
| llvm::Expected<NativeProcessProtocol::SoftwareBreakpoint> | |
| NativeProcessProtocol::EnableSoftwareBreakpoint(lldb::addr_t addr, | |
| uint32_t size_hint) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| auto expected_trap = GetSoftwareBreakpointTrapOpcode(size_hint); | |
| if (!expected_trap) | |
| return expected_trap.takeError(); | |
| llvm::SmallVector<uint8_t, 4> saved_opcode_bytes(expected_trap->size(), 0); | |
| // Save the original opcodes by reading them so we can restore later. | |
| size_t bytes_read = 0; | |
| Status error = ReadMemory(addr, saved_opcode_bytes.data(), | |
| saved_opcode_bytes.size(), bytes_read); | |
| if (error.Fail()) | |
| return error.ToError(); | |
| // Ensure we read as many bytes as we expected. | |
| if (bytes_read != saved_opcode_bytes.size()) { | |
| return llvm::createStringError( | |
| llvm::inconvertibleErrorCode(), | |
| "Failed to read memory while attempting to set breakpoint: attempted " | |
| "to read {0} bytes but only read {1}.", | |
| saved_opcode_bytes.size(), bytes_read); | |
| } | |
| LLDB_LOG( | |
| log, "Overwriting bytes at {0:x}: {1:@[x]}", addr, | |
| llvm::make_range(saved_opcode_bytes.begin(), saved_opcode_bytes.end())); | |
| // Write a software breakpoint in place of the original opcode. | |
| size_t bytes_written = 0; | |
| error = WriteMemory(addr, expected_trap->data(), expected_trap->size(), | |
| bytes_written); | |
| if (error.Fail()) | |
| return error.ToError(); | |
| // Ensure we wrote as many bytes as we expected. | |
| if (bytes_written != expected_trap->size()) { | |
| return llvm::createStringError( | |
| llvm::inconvertibleErrorCode(), | |
| "Failed write memory while attempting to set " | |
| "breakpoint: attempted to write {0} bytes but only wrote {1}", | |
| expected_trap->size(), bytes_written); | |
| } | |
| llvm::SmallVector<uint8_t, 4> verify_bp_opcode_bytes(expected_trap->size(), | |
| 0); | |
| size_t verify_bytes_read = 0; | |
| error = ReadMemory(addr, verify_bp_opcode_bytes.data(), | |
| verify_bp_opcode_bytes.size(), verify_bytes_read); | |
| if (error.Fail()) | |
| return error.ToError(); | |
| // Ensure we read as many verification bytes as we expected. | |
| if (verify_bytes_read != verify_bp_opcode_bytes.size()) { | |
| return llvm::createStringError( | |
| llvm::inconvertibleErrorCode(), | |
| "Failed to read memory while " | |
| "attempting to verify breakpoint: attempted to read {0} bytes " | |
| "but only read {1}", | |
| verify_bp_opcode_bytes.size(), verify_bytes_read); | |
| } | |
| if (llvm::ArrayRef(verify_bp_opcode_bytes.data(), verify_bytes_read) != | |
| *expected_trap) { | |
| return llvm::createStringError( | |
| llvm::inconvertibleErrorCode(), | |
| "Verification of software breakpoint " | |
| "writing failed - trap opcodes not successfully read back " | |
| "after writing when setting breakpoint at {0:x}", | |
| addr); | |
| } | |
| LLDB_LOG(log, "addr = {0:x}: SUCCESS", addr); | |
| return SoftwareBreakpoint{1, saved_opcode_bytes, *expected_trap}; | |
| } | |
| llvm::Expected<llvm::ArrayRef<uint8_t>> | |
| NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_t size_hint) { | |
| static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4}; | |
| static const uint8_t g_i386_opcode[] = {0xCC}; | |
| static const uint8_t g_mips64_opcode[] = {0x00, 0x00, 0x00, 0x0d}; | |
| static const uint8_t g_mips64el_opcode[] = {0x0d, 0x00, 0x00, 0x00}; | |
| static const uint8_t g_msp430_opcode[] = {0x43, 0x43}; | |
| static const uint8_t g_s390x_opcode[] = {0x00, 0x01}; | |
| static const uint8_t g_ppc_opcode[] = {0x7f, 0xe0, 0x00, 0x08}; // trap | |
| static const uint8_t g_ppcle_opcode[] = {0x08, 0x00, 0xe0, 0x7f}; // trap | |
| static const uint8_t g_riscv_opcode[] = {0x73, 0x00, 0x10, 0x00}; // ebreak | |
| static const uint8_t g_riscv_opcode_c[] = {0x02, 0x90}; // c.ebreak | |
| static const uint8_t g_loongarch_opcode[] = {0x05, 0x00, 0x2a, | |
| 0x00}; // break 0x5 | |
| switch (GetArchitecture().GetMachine()) { | |
| case llvm::Triple::aarch64: | |
| case llvm::Triple::aarch64_32: | |
| return llvm::ArrayRef(g_aarch64_opcode); | |
| case llvm::Triple::x86: | |
| case llvm::Triple::x86_64: | |
| return llvm::ArrayRef(g_i386_opcode); | |
| case llvm::Triple::mips: | |
| case llvm::Triple::mips64: | |
| return llvm::ArrayRef(g_mips64_opcode); | |
| case llvm::Triple::mipsel: | |
| case llvm::Triple::mips64el: | |
| return llvm::ArrayRef(g_mips64el_opcode); | |
| case llvm::Triple::msp430: | |
| return llvm::ArrayRef(g_msp430_opcode); | |
| case llvm::Triple::systemz: | |
| return llvm::ArrayRef(g_s390x_opcode); | |
| case llvm::Triple::ppc: | |
| case llvm::Triple::ppc64: | |
| return llvm::ArrayRef(g_ppc_opcode); | |
| case llvm::Triple::ppc64le: | |
| return llvm::ArrayRef(g_ppcle_opcode); | |
| case llvm::Triple::riscv32: | |
| case llvm::Triple::riscv64: { | |
| return size_hint == 2 ? llvm::ArrayRef(g_riscv_opcode_c) | |
| : llvm::ArrayRef(g_riscv_opcode); | |
| } | |
| case llvm::Triple::loongarch32: | |
| case llvm::Triple::loongarch64: | |
| return llvm::ArrayRef(g_loongarch_opcode); | |
| default: | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "CPU type not supported!"); | |
| } | |
| } | |
| size_t NativeProcessProtocol::GetSoftwareBreakpointPCOffset() { | |
| switch (GetArchitecture().GetMachine()) { | |
| case llvm::Triple::x86: | |
| case llvm::Triple::x86_64: | |
| case llvm::Triple::systemz: | |
| // These architectures report increment the PC after breakpoint is hit. | |
| return cantFail(GetSoftwareBreakpointTrapOpcode(0)).size(); | |
| case llvm::Triple::arm: | |
| case llvm::Triple::aarch64: | |
| case llvm::Triple::aarch64_32: | |
| case llvm::Triple::mips64: | |
| case llvm::Triple::mips64el: | |
| case llvm::Triple::mips: | |
| case llvm::Triple::mipsel: | |
| case llvm::Triple::ppc: | |
| case llvm::Triple::ppc64: | |
| case llvm::Triple::ppc64le: | |
| case llvm::Triple::riscv32: | |
| case llvm::Triple::riscv64: | |
| case llvm::Triple::loongarch32: | |
| case llvm::Triple::loongarch64: | |
| // On these architectures the PC doesn't get updated for breakpoint hits. | |
| return 0; | |
| default: | |
| llvm_unreachable("CPU type not supported!"); | |
| } | |
| } | |
| void NativeProcessProtocol::FixupBreakpointPCAsNeeded( | |
| NativeThreadProtocol &thread) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| Status error; | |
| // Find out the size of a breakpoint (might depend on where we are in the | |
| // code). | |
| NativeRegisterContext &context = thread.GetRegisterContext(); | |
| uint32_t breakpoint_size = GetSoftwareBreakpointPCOffset(); | |
| LLDB_LOG(log, "breakpoint size: {0}", breakpoint_size); | |
| if (breakpoint_size == 0) | |
| return; | |
| // First try probing for a breakpoint at a software breakpoint location: PC - | |
| // breakpoint size. | |
| const lldb::addr_t initial_pc_addr = context.GetPCfromBreakpointLocation(); | |
| lldb::addr_t breakpoint_addr = initial_pc_addr; | |
| // Do not allow breakpoint probe to wrap around. | |
| if (breakpoint_addr >= breakpoint_size) | |
| breakpoint_addr -= breakpoint_size; | |
| if (m_software_breakpoints.count(breakpoint_addr) == 0) { | |
| // We didn't find one at a software probe location. Nothing to do. | |
| LLDB_LOG(log, | |
| "pid {0} no lldb software breakpoint found at current pc with " | |
| "adjustment: {1}", | |
| GetID(), breakpoint_addr); | |
| return; | |
| } | |
| // | |
| // We have a software breakpoint and need to adjust the PC. | |
| // | |
| // Change the program counter. | |
| LLDB_LOG(log, "pid {0} tid {1}: changing PC from {2:x} to {3:x}", GetID(), | |
| thread.GetID(), initial_pc_addr, breakpoint_addr); | |
| error = context.SetPC(breakpoint_addr); | |
| if (error.Fail()) { | |
| // This can happen in case the process was killed between the time we read | |
| // the PC and when we are updating it. There's nothing better to do than to | |
| // swallow the error. | |
| LLDB_LOG(log, "pid {0} tid {1}: failed to set PC: {2}", GetID(), | |
| thread.GetID(), error); | |
| } | |
| } | |
| Status NativeProcessProtocol::RemoveBreakpoint(lldb::addr_t addr, | |
| bool hardware) { | |
| if (hardware) | |
| return RemoveHardwareBreakpoint(addr); | |
| else | |
| return RemoveSoftwareBreakpoint(addr); | |
| } | |
| Status NativeProcessProtocol::ReadMemoryWithoutTrap(lldb::addr_t addr, | |
| void *buf, size_t size, | |
| size_t &bytes_read) { | |
| Status error = ReadMemory(addr, buf, size, bytes_read); | |
| if (error.Fail()) | |
| return error; | |
| llvm::MutableArrayRef data(static_cast<uint8_t *>(buf), bytes_read); | |
| for (const auto &pair : m_software_breakpoints) { | |
| lldb::addr_t bp_addr = pair.first; | |
| auto saved_opcodes = llvm::ArrayRef(pair.second.saved_opcodes); | |
| if (bp_addr + saved_opcodes.size() < addr || addr + bytes_read <= bp_addr) | |
| continue; // Breakpoint not in range, ignore | |
| if (bp_addr < addr) { | |
| saved_opcodes = saved_opcodes.drop_front(addr - bp_addr); | |
| bp_addr = addr; | |
| } | |
| auto bp_data = data.drop_front(bp_addr - addr); | |
| std::copy_n(saved_opcodes.begin(), | |
| std::min(saved_opcodes.size(), bp_data.size()), | |
| bp_data.begin()); | |
| } | |
| return Status(); | |
| } | |
| llvm::Expected<llvm::StringRef> | |
| NativeProcessProtocol::ReadCStringFromMemory(lldb::addr_t addr, char *buffer, | |
| size_t max_size, | |
| size_t &total_bytes_read) { | |
| static const size_t cache_line_size = | |
| llvm::sys::Process::getPageSizeEstimate(); | |
| size_t bytes_read = 0; | |
| size_t bytes_left = max_size; | |
| addr_t curr_addr = addr; | |
| size_t string_size; | |
| char *curr_buffer = buffer; | |
| total_bytes_read = 0; | |
| Status status; | |
| while (bytes_left > 0 && status.Success()) { | |
| addr_t cache_line_bytes_left = | |
| cache_line_size - (curr_addr % cache_line_size); | |
| addr_t bytes_to_read = std::min<addr_t>(bytes_left, cache_line_bytes_left); | |
| status = ReadMemory(curr_addr, static_cast<void *>(curr_buffer), | |
| bytes_to_read, bytes_read); | |
| if (bytes_read == 0) | |
| break; | |
| void *str_end = std::memchr(curr_buffer, '\0', bytes_read); | |
| if (str_end != nullptr) { | |
| total_bytes_read = | |
| static_cast<size_t>((static_cast<char *>(str_end) - buffer + 1)); | |
| status.Clear(); | |
| break; | |
| } | |
| total_bytes_read += bytes_read; | |
| curr_buffer += bytes_read; | |
| curr_addr += bytes_read; | |
| bytes_left -= bytes_read; | |
| } | |
| string_size = total_bytes_read - 1; | |
| // Make sure we return a null terminated string. | |
| if (bytes_left == 0 && max_size > 0 && buffer[max_size - 1] != '\0') { | |
| buffer[max_size - 1] = '\0'; | |
| total_bytes_read--; | |
| } | |
| if (!status.Success()) | |
| return status.ToError(); | |
| return llvm::StringRef(buffer, string_size); | |
| } | |
| lldb::StateType NativeProcessProtocol::GetState() const { | |
| std::lock_guard<std::recursive_mutex> guard(m_state_mutex); | |
| return m_state; | |
| } | |
| void NativeProcessProtocol::SetState(lldb::StateType state, | |
| bool notify_delegates) { | |
| std::lock_guard<std::recursive_mutex> guard(m_state_mutex); | |
| if (state == m_state) | |
| return; | |
| m_state = state; | |
| if (StateIsStoppedState(state, false)) { | |
| ++m_stop_id; | |
| // Give process a chance to do any stop id bump processing, such as | |
| // clearing cached data that is invalidated each time the process runs. | |
| // Note if/when we support some threads running, we'll end up needing to | |
| // manage this per thread and per process. | |
| DoStopIDBumped(m_stop_id); | |
| } | |
| // Optionally notify delegates of the state change. | |
| if (notify_delegates) | |
| SynchronouslyNotifyProcessStateChanged(state); | |
| } | |
| uint32_t NativeProcessProtocol::GetStopID() const { | |
| std::lock_guard<std::recursive_mutex> guard(m_state_mutex); | |
| return m_stop_id; | |
| } | |
| void NativeProcessProtocol::DoStopIDBumped(uint32_t /* newBumpId */) { | |
| // Default implementation does nothing. | |
| } | |
| NativeProcessProtocol::Manager::~Manager() = default; |