diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Host/common/NativeProcessProtocol.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Host/common/NativeProcessProtocol.cpp')
-rw-r--r-- | gnu/llvm/lldb/source/Host/common/NativeProcessProtocol.cpp | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Host/common/NativeProcessProtocol.cpp b/gnu/llvm/lldb/source/Host/common/NativeProcessProtocol.cpp new file mode 100644 index 00000000000..712c448dc2c --- /dev/null +++ b/gnu/llvm/lldb/source/Host/common/NativeProcessProtocol.cpp @@ -0,0 +1,757 @@ +//===-- NativeProcessProtocol.cpp -------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#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/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/lldb-enumerations.h" + +#include "llvm/Support/Process.h" + +using namespace lldb; +using namespace lldb_private; + +// NativeProcessProtocol Members + +NativeProcessProtocol::NativeProcessProtocol(lldb::pid_t pid, int terminal_fd, + NativeDelegate &delegate) + : m_pid(pid), m_terminal_fd(terminal_fd) { + bool registered = RegisterNativeDelegate(delegate); + assert(registered); + (void)registered; +} + +lldb_private::Status NativeProcessProtocol::Interrupt() { + Status error; +#if !defined(SIGSTOP) + error.SetErrorString("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(signals.begin(), signals.end()); + return Status(); +} + +lldb_private::Status +NativeProcessProtocol::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + // Default: not implemented. + return Status("not implemented"); +} + +llvm::Optional<WaitStatus> NativeProcessProtocol::GetExitStatus() { + if (m_state == lldb::eStateExited) + return m_exit_status; + + return llvm::None; +} + +bool NativeProcessProtocol::SetExitStatus(WaitStatus status, + bool bNotifyStateChange) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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(); +} + +llvm::Optional<std::pair<uint32_t, uint32_t>> +NativeProcessProtocol::GetHardwareDebugSupportInfo() const { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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 llvm::None; + } + + 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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!"); + + const 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 = thread_error; + } + } + const Status error = m_watchpoint_list.Remove(addr); + return overall_error.Fail() ? overall_error : 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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 == llvm::None || hw_debug_cap->first == 0 || + hw_debug_cap->first <= m_hw_breakpoints_map.size()) + return Status("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; +} + +bool NativeProcessProtocol::RegisterNativeDelegate( + NativeDelegate &native_delegate) { + std::lock_guard<std::recursive_mutex> guard(m_delegates_mutex); + if (std::find(m_delegates.begin(), m_delegates.end(), &native_delegate) != + m_delegates.end()) + return false; + + m_delegates.push_back(&native_delegate); + native_delegate.InitializeDelegate(this); + return true; +} + +bool NativeProcessProtocol::UnregisterNativeDelegate( + NativeDelegate &native_delegate) { + std::lock_guard<std::recursive_mutex> guard(m_delegates_mutex); + + const auto initial_size = m_delegates.size(); + m_delegates.erase( + remove(m_delegates.begin(), m_delegates.end(), &native_delegate), + m_delegates.end()); + + // We removed the delegate if the count of delegates shrank after removing + // all copies of the given native_delegate from the vector. + return m_delegates.size() < initial_size; +} + +void NativeProcessProtocol::SynchronouslyNotifyProcessStateChanged( + lldb::StateType state) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + std::lock_guard<std::recursive_mutex> guard(m_delegates_mutex); + for (auto native_delegate : m_delegates) + native_delegate->ProcessStateChanged(this, state); + + if (log) { + if (!m_delegates.empty()) { + LLDB_LOGF(log, + "NativeProcessProtocol::%s: sent state notification [%s] " + "from process %" PRIu64, + __FUNCTION__, lldb_private::StateAsCString(state), GetID()); + } else { + LLDB_LOGF(log, + "NativeProcessProtocol::%s: would send state notification " + "[%s] from process %" PRIu64 ", but no delegates", + __FUNCTION__, lldb_private::StateAsCString(state), GetID()); + } + } +} + +void NativeProcessProtocol::NotifyDidExec() { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + LLDB_LOGF(log, "NativeProcessProtocol::%s - preparing to call delegates", + __FUNCTION__); + + { + std::lock_guard<std::recursive_mutex> guard(m_delegates_mutex); + for (auto native_delegate : m_delegates) + native_delegate->DidExec(this); + } +} + +Status NativeProcessProtocol::SetSoftwareBreakpoint(lldb::addr_t addr, + uint32_t size_hint) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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(expected_bkpt.takeError()); + + m_software_breakpoints.emplace(addr, std::move(*expected_bkpt)); + return Status(); +} + +Status NativeProcessProtocol::RemoveSoftwareBreakpoint(lldb::addr_t addr) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOG(log, "addr = {0:x}", addr); + auto it = m_software_breakpoints.find(addr); + if (it == m_software_breakpoints.end()) + return Status("Breakpoint not found."); + assert(it->second.ref_count > 0); + if (--it->second.ref_count > 0) + return Status(); + + // 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( + it->second.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("addr=0x%" PRIx64 + ": tried to read %zu bytes but only read %zu", + addr, curr_break_op.size(), bytes_read); + } + const auto &saved = it->second.saved_opcodes; + // Make sure the breakpoint opcode exists at this address + if (makeArrayRef(curr_break_op) != it->second.breakpoint_opcodes) { + if (curr_break_op != it->second.saved_opcodes) + return Status("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("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("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())); + } + + m_software_breakpoints.erase(it); + return Status(); +} + +llvm::Expected<NativeProcessProtocol::SoftwareBreakpoint> +NativeProcessProtocol::EnableSoftwareBreakpoint(lldb::addr_t addr, + uint32_t size_hint) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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::makeArrayRef(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_s390x_opcode[] = {0x00, 0x01}; + static const uint8_t g_ppc64le_opcode[] = {0x08, 0x00, 0xe0, 0x7f}; // trap + + switch (GetArchitecture().GetMachine()) { + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + return llvm::makeArrayRef(g_aarch64_opcode); + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + return llvm::makeArrayRef(g_i386_opcode); + + case llvm::Triple::mips: + case llvm::Triple::mips64: + return llvm::makeArrayRef(g_mips64_opcode); + + case llvm::Triple::mipsel: + case llvm::Triple::mips64el: + return llvm::makeArrayRef(g_mips64el_opcode); + + case llvm::Triple::systemz: + return llvm::makeArrayRef(g_s390x_opcode); + + case llvm::Triple::ppc64le: + return llvm::makeArrayRef(g_ppc64le_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::ppc64le: + // 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 = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_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; + + auto data = + llvm::makeMutableArrayRef(static_cast<uint8_t *>(buf), bytes_read); + for (const auto &pair : m_software_breakpoints) { + lldb::addr_t bp_addr = pair.first; + auto saved_opcodes = makeArrayRef(pair.second.saved_opcodes); + + if (bp_addr + saved_opcodes.size() < addr || addr + bytes_read <= bp_addr) + continue; // Breapoint 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::Factory::~Factory() = default; |