diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD')
16 files changed, 3822 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp new file mode 100644 index 000000000000..064bddd37052 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp @@ -0,0 +1,1088 @@ +//===-- NativeProcessFreeBSD.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 "NativeProcessFreeBSD.h" + +// clang-format off +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/sysctl.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <machine/elf.h> +// clang-format on + +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/posix/ProcessLauncherPosixFork.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/State.h" +#include "llvm/Support/Errno.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_freebsd; +using namespace llvm; + +// Simple helper function to ensure flags are enabled on the given file +// descriptor. +static Status EnsureFDFlags(int fd, int flags) { + Status error; + + int status = fcntl(fd, F_GETFL); + if (status == -1) { + error.SetErrorToErrno(); + return error; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +static Status CanTrace() { + int proc_debug, ret; + size_t len = sizeof(proc_debug); + ret = ::sysctlbyname("security.bsd.unprivileged_proc_debug", &proc_debug, + &len, nullptr, 0); + if (ret != 0) + return Status("sysctlbyname() security.bsd.unprivileged_proc_debug failed"); + + if (proc_debug < 1) + return Status( + "process debug disabled by security.bsd.unprivileged_proc_debug oid"); + + return {}; +} + +// Public Static Methods + +llvm::Expected<std::unique_ptr<NativeProcessProtocol>> +NativeProcessFreeBSD::Manager::Launch(ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate) { + Log *log = GetLog(POSIXLog::Process); + Status status; + + ::pid_t pid = ProcessLauncherPosixFork() + .LaunchProcess(launch_info, status) + .GetProcessId(); + LLDB_LOG(log, "pid = {0:x}", pid); + if (status.Fail()) { + LLDB_LOG(log, "failed to launch process: {0}", status); + auto error = CanTrace(); + if (error.Fail()) + return error.ToError(); + return status.ToError(); + } + + // Wait for the child process to trap on its call to execve. + int wstatus; + ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0); + assert(wpid == pid); + UNUSED_IF_ASSERT_DISABLED(wpid); + if (!WIFSTOPPED(wstatus)) { + LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}", + WaitStatus::Decode(wstatus)); + return llvm::make_error<StringError>("Could not sync with inferior process", + llvm::inconvertibleErrorCode()); + } + LLDB_LOG(log, "inferior started, now in stopped state"); + + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error<StringError>("Cannot get process architecture", + llvm::inconvertibleErrorCode()); + } + + // Set the architecture to the exe architecture. + LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid, + Info.GetArchitecture().GetArchitectureName()); + + std::unique_ptr<NativeProcessFreeBSD> process_up(new NativeProcessFreeBSD( + pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate, + Info.GetArchitecture(), m_mainloop)); + + status = process_up->SetupTrace(); + if (status.Fail()) + return status.ToError(); + + for (const auto &thread : process_up->m_threads) + static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP); + process_up->SetState(StateType::eStateStopped, false); + + return std::move(process_up); +} + +llvm::Expected<std::unique_ptr<NativeProcessProtocol>> +NativeProcessFreeBSD::Manager::Attach( + lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid = {0:x}", pid); + + // Retrieve the architecture for the running process. + ProcessInstanceInfo Info; + if (!Host::GetProcessInfo(pid, Info)) { + return llvm::make_error<StringError>("Cannot get process architecture", + llvm::inconvertibleErrorCode()); + } + + std::unique_ptr<NativeProcessFreeBSD> process_up(new NativeProcessFreeBSD( + pid, -1, native_delegate, Info.GetArchitecture(), m_mainloop)); + + Status status = process_up->Attach(); + if (!status.Success()) + return status.ToError(); + + return std::move(process_up); +} + +NativeProcessFreeBSD::Extension +NativeProcessFreeBSD::Manager::GetSupportedExtensions() const { + return +#if defined(PT_COREDUMP) + Extension::savecore | +#endif + Extension::multiprocess | Extension::fork | Extension::vfork | + Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 | + Extension::siginfo_read; +} + +// Public Instance Methods + +NativeProcessFreeBSD::NativeProcessFreeBSD(::pid_t pid, int terminal_fd, + NativeDelegate &delegate, + const ArchSpec &arch, + MainLoop &mainloop) + : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch), + m_main_loop(mainloop) { + if (m_terminal_fd != -1) { + Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); + assert(status.Success()); + } + + Status status; + m_sigchld_handle = mainloop.RegisterSignal( + SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, status); + assert(m_sigchld_handle && status.Success()); +} + +// Handles all waitpid events from the inferior process. +void NativeProcessFreeBSD::MonitorCallback(lldb::pid_t pid, int signal) { + switch (signal) { + case SIGTRAP: + return MonitorSIGTRAP(pid); + case SIGSTOP: + return MonitorSIGSTOP(pid); + default: + return MonitorSignal(pid, signal); + } +} + +void NativeProcessFreeBSD::MonitorExited(lldb::pid_t pid, WaitStatus status) { + Log *log = GetLog(POSIXLog::Process); + + LLDB_LOG(log, "got exit signal({0}) , pid = {1}", status, pid); + + /* Stop Tracking All Threads attached to Process */ + m_threads.clear(); + + SetExitStatus(status, true); + + // Notify delegate that our process has exited. + SetState(StateType::eStateExited, true); +} + +void NativeProcessFreeBSD::MonitorSIGSTOP(lldb::pid_t pid) { + /* Stop all Threads attached to Process */ + for (const auto &thread : m_threads) { + static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP, + nullptr); + } + SetState(StateType::eStateStopped, true); +} + +void NativeProcessFreeBSD::MonitorSIGTRAP(lldb::pid_t pid) { + Log *log = GetLog(POSIXLog::Process); + struct ptrace_lwpinfo info; + + const auto siginfo_err = PtraceWrapper(PT_LWPINFO, pid, &info, sizeof(info)); + if (siginfo_err.Fail()) { + LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); + return; + } + assert(info.pl_event == PL_EVENT_SIGNAL); + + LLDB_LOG(log, "got SIGTRAP, pid = {0}, lwpid = {1}, flags = {2:x}", pid, + info.pl_lwpid, info.pl_flags); + NativeThreadFreeBSD *thread = nullptr; + + if (info.pl_flags & (PL_FLAG_BORN | PL_FLAG_EXITED)) { + if (info.pl_flags & PL_FLAG_BORN) { + LLDB_LOG(log, "monitoring new thread, tid = {0}", info.pl_lwpid); + NativeThreadFreeBSD &t = AddThread(info.pl_lwpid); + + // Technically, the FreeBSD kernel copies the debug registers to new + // threads. However, there is a non-negligible delay between acquiring + // the DR values and reporting the new thread during which the user may + // establish a new watchpoint. In order to ensure that watchpoints + // established during this period are propagated to new threads, + // explicitly copy the DR value at the time the new thread is reported. + // + // See also: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=250954 + + llvm::Error error = t.CopyWatchpointsFrom( + static_cast<NativeThreadFreeBSD &>(*GetCurrentThread())); + if (error) { + LLDB_LOG_ERROR(log, std::move(error), + "failed to copy watchpoints to new thread {1}: {0}", + info.pl_lwpid); + SetState(StateType::eStateInvalid); + return; + } + } else /*if (info.pl_flags & PL_FLAG_EXITED)*/ { + LLDB_LOG(log, "thread exited, tid = {0}", info.pl_lwpid); + RemoveThread(info.pl_lwpid); + } + + Status error = + PtraceWrapper(PT_CONTINUE, pid, reinterpret_cast<void *>(1), 0); + if (error.Fail()) + SetState(StateType::eStateInvalid); + return; + } + + if (info.pl_flags & PL_FLAG_EXEC) { + Status error = ReinitializeThreads(); + if (error.Fail()) { + SetState(StateType::eStateInvalid); + return; + } + + // Let our delegate know we have just exec'd. + NotifyDidExec(); + + for (const auto &thread : m_threads) + static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedByExec(); + SetCurrentThreadID(m_threads.front()->GetID()); + SetState(StateType::eStateStopped, true); + return; + } + + if (info.pl_lwpid > 0) { + for (const auto &t : m_threads) { + if (t->GetID() == static_cast<lldb::tid_t>(info.pl_lwpid)) + thread = static_cast<NativeThreadFreeBSD *>(t.get()); + static_cast<NativeThreadFreeBSD *>(t.get())->SetStoppedWithNoReason(); + } + if (!thread) + LLDB_LOG(log, "thread not found in m_threads, pid = {0}, LWP = {1}", pid, + info.pl_lwpid); + } + + if (info.pl_flags & PL_FLAG_FORKED) { + assert(thread); + MonitorClone(info.pl_child_pid, info.pl_flags & PL_FLAG_VFORKED, *thread); + return; + } + + if (info.pl_flags & PL_FLAG_VFORK_DONE) { + assert(thread); + if ((m_enabled_extensions & Extension::vfork) == Extension::vfork) { + thread->SetStoppedByVForkDone(); + SetState(StateType::eStateStopped, true); + } else { + Status error = + PtraceWrapper(PT_CONTINUE, pid, reinterpret_cast<void *>(1), 0); + if (error.Fail()) + SetState(StateType::eStateInvalid); + } + return; + } + + if (info.pl_flags & PL_FLAG_SI) { + assert(info.pl_siginfo.si_signo == SIGTRAP); + LLDB_LOG(log, "SIGTRAP siginfo: si_code = {0}, pid = {1}", + info.pl_siginfo.si_code, info.pl_siginfo.si_pid); + + switch (info.pl_siginfo.si_code) { + case TRAP_BRKPT: + LLDB_LOG(log, "SIGTRAP/TRAP_BRKPT: si_addr: {0}", + info.pl_siginfo.si_addr); + + if (thread) { + auto thread_info = + m_threads_stepping_with_breakpoint.find(thread->GetID()); + if (thread_info != m_threads_stepping_with_breakpoint.end()) { + thread->SetStoppedByTrace(); + Status brkpt_error = RemoveBreakpoint(thread_info->second); + if (brkpt_error.Fail()) + LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", + thread_info->first, brkpt_error); + m_threads_stepping_with_breakpoint.erase(thread_info); + } else + thread->SetStoppedByBreakpoint(); + FixupBreakpointPCAsNeeded(*thread); + SetCurrentThreadID(thread->GetID()); + } + SetState(StateType::eStateStopped, true); + return; + case TRAP_TRACE: + LLDB_LOG(log, "SIGTRAP/TRAP_TRACE: si_addr: {0}", + info.pl_siginfo.si_addr); + + if (thread) { + auto ®ctx = static_cast<NativeRegisterContextFreeBSD &>( + thread->GetRegisterContext()); + uint32_t wp_index = LLDB_INVALID_INDEX32; + Status error = regctx.GetWatchpointHitIndex( + wp_index, reinterpret_cast<uintptr_t>(info.pl_siginfo.si_addr)); + if (error.Fail()) + LLDB_LOG(log, + "received error while checking for watchpoint hits, pid = " + "{0}, LWP = {1}, error = {2}", + pid, info.pl_lwpid, error); + if (wp_index != LLDB_INVALID_INDEX32) { + regctx.ClearWatchpointHit(wp_index); + thread->SetStoppedByWatchpoint(wp_index); + SetCurrentThreadID(thread->GetID()); + SetState(StateType::eStateStopped, true); + break; + } + + thread->SetStoppedByTrace(); + SetCurrentThreadID(thread->GetID()); + } + + SetState(StateType::eStateStopped, true); + return; + } + } + + // Either user-generated SIGTRAP or an unknown event that would + // otherwise leave the debugger hanging. + LLDB_LOG(log, "unknown SIGTRAP, passing to generic handler"); + MonitorSignal(pid, SIGTRAP); +} + +void NativeProcessFreeBSD::MonitorSignal(lldb::pid_t pid, int signal) { + Log *log = GetLog(POSIXLog::Process); + struct ptrace_lwpinfo info; + + const auto siginfo_err = PtraceWrapper(PT_LWPINFO, pid, &info, sizeof(info)); + if (siginfo_err.Fail()) { + LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); + return; + } + assert(info.pl_event == PL_EVENT_SIGNAL); + // TODO: do we need to handle !PL_FLAG_SI? + assert(info.pl_flags & PL_FLAG_SI); + assert(info.pl_siginfo.si_signo == signal); + + for (const auto &abs_thread : m_threads) { + NativeThreadFreeBSD &thread = + static_cast<NativeThreadFreeBSD &>(*abs_thread); + assert(info.pl_lwpid >= 0); + if (info.pl_lwpid == 0 || + static_cast<lldb::tid_t>(info.pl_lwpid) == thread.GetID()) { + thread.SetStoppedBySignal(info.pl_siginfo.si_signo, &info.pl_siginfo); + SetCurrentThreadID(thread.GetID()); + } else + thread.SetStoppedWithNoReason(); + } + SetState(StateType::eStateStopped, true); +} + +Status NativeProcessFreeBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr, + int data, int *result) { + Log *log = GetLog(POSIXLog::Ptrace); + Status error; + int ret; + + errno = 0; + ret = + ptrace(req, static_cast<::pid_t>(pid), static_cast<caddr_t>(addr), data); + + if (ret == -1) { + error = CanTrace(); + if (error.Success()) + error.SetErrorToErrno(); + } + + if (result) + *result = ret; + + LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3})={4:x}", req, pid, addr, data, ret); + + if (error.Fail()) + LLDB_LOG(log, "ptrace() failed: {0}", error); + + return error; +} + +llvm::Expected<llvm::ArrayRef<uint8_t>> +NativeProcessFreeBSD::GetSoftwareBreakpointTrapOpcode(size_t size_hint) { + static const uint8_t g_arm_opcode[] = {0xfe, 0xde, 0xff, 0xe7}; + static const uint8_t g_thumb_opcode[] = {0x01, 0xde}; + + switch (GetArchitecture().GetMachine()) { + case llvm::Triple::arm: + switch (size_hint) { + case 2: + return llvm::ArrayRef(g_thumb_opcode); + case 4: + return llvm::ArrayRef(g_arm_opcode); + default: + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unrecognised trap opcode size hint!"); + } + default: + return NativeProcessProtocol::GetSoftwareBreakpointTrapOpcode(size_hint); + } +} + +Status NativeProcessFreeBSD::Resume(const ResumeActionList &resume_actions) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid {0}", GetID()); + + Status ret; + + int signal = 0; + for (const auto &abs_thread : m_threads) { + assert(abs_thread && "thread list should not contain NULL threads"); + NativeThreadFreeBSD &thread = + static_cast<NativeThreadFreeBSD &>(*abs_thread); + + const ResumeAction *action = + resume_actions.GetActionForThread(thread.GetID(), true); + // we need to explicit issue suspend requests, so it is simpler to map it + // into proper action + ResumeAction suspend_action{thread.GetID(), eStateSuspended, + LLDB_INVALID_SIGNAL_NUMBER}; + + if (action == nullptr) { + LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), + thread.GetID()); + action = &suspend_action; + } + + LLDB_LOG( + log, + "processing resume action state {0} signal {1} for pid {2} tid {3}", + action->state, action->signal, GetID(), thread.GetID()); + + switch (action->state) { + case eStateRunning: + ret = thread.Resume(); + break; + case eStateStepping: + ret = thread.SingleStep(); + break; + case eStateSuspended: + case eStateStopped: + if (action->signal != LLDB_INVALID_SIGNAL_NUMBER) + return Status("Passing signal to suspended thread unsupported"); + + ret = thread.Suspend(); + break; + + default: + return Status( + "NativeProcessFreeBSD::%s (): unexpected state %s specified " + "for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString(action->state), GetID(), thread.GetID()); + } + + if (!ret.Success()) + return ret; + if (action->signal != LLDB_INVALID_SIGNAL_NUMBER) + signal = action->signal; + } + + ret = + PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), signal); + if (ret.Success()) + SetState(eStateRunning, true); + return ret; +} + +Status NativeProcessFreeBSD::Halt() { + Status error; + + // Do not try to stop a process that's already stopped, this may cause + // the SIGSTOP to get queued and stop the process again once resumed. + if (StateIsStoppedState(m_state, false)) + return error; + if (kill(GetID(), SIGSTOP) != 0) + error.SetErrorToErrno(); + return error; +} + +Status NativeProcessFreeBSD::Detach() { + Status error; + + // Stop monitoring the inferior. + m_sigchld_handle.reset(); + + // Tell ptrace to detach from the process. + if (GetID() == LLDB_INVALID_PROCESS_ID) + return error; + + return PtraceWrapper(PT_DETACH, GetID()); +} + +Status NativeProcessFreeBSD::Signal(int signo) { + Status error; + + if (kill(GetID(), signo)) + error.SetErrorToErrno(); + + return error; +} + +Status NativeProcessFreeBSD::Interrupt() { return Halt(); } + +Status NativeProcessFreeBSD::Kill() { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid {0}", GetID()); + + Status error; + + switch (m_state) { + case StateType::eStateInvalid: + case StateType::eStateExited: + case StateType::eStateCrashed: + case StateType::eStateDetached: + case StateType::eStateUnloaded: + // Nothing to do - the process is already dead. + LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), + StateAsCString(m_state)); + return error; + + case StateType::eStateConnected: + case StateType::eStateAttaching: + case StateType::eStateLaunching: + case StateType::eStateStopped: + case StateType::eStateRunning: + case StateType::eStateStepping: + case StateType::eStateSuspended: + // We can try to kill a process in these states. + break; + } + + return PtraceWrapper(PT_KILL, m_pid); +} + +Status NativeProcessFreeBSD::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + + if (m_supports_mem_region == LazyBool::eLazyBoolNo) { + // We're done. + return Status("unsupported"); + } + + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) { + return error; + } + + lldb::addr_t prev_base_address = 0; + // FIXME start by finding the last region that is <= target address using + // binary search. Data is sorted. + // There can be a ton of regions on pthreads apps with lots of threads. + for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); + ++it) { + MemoryRegionInfo &proc_entry_info = it->first; + // Sanity check assumption that memory map entries are ascending. + assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && + "descending memory map entries detected, unexpected"); + prev_base_address = proc_entry_info.GetRange().GetRangeBase(); + UNUSED_IF_ASSERT_DISABLED(prev_base_address); + // If the target address comes before this entry, indicate distance to next + // region. + if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetByteSize( + proc_entry_info.GetRange().GetRangeBase() - load_addr); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + return error; + } else if (proc_entry_info.GetRange().Contains(load_addr)) { + // The target address is within the memory region we're processing here. + range_info = proc_entry_info; + return error; + } + // The target memory address comes somewhere after the region we just + // parsed. + } + // If we made it here, we didn't find an entry that contained the given + // address. Return the load_addr as start and the amount of bytes betwwen + // load address and the end of the memory as size. + range_info.GetRange().SetRangeBase(load_addr); + range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); + return error; +} + +Status NativeProcessFreeBSD::PopulateMemoryRegionCache() { + Log *log = GetLog(POSIXLog::Process); + // If our cache is empty, pull the latest. There should always be at least + // one memory region if memory region handling is supported. + if (!m_mem_region_cache.empty()) { + LLDB_LOG(log, "reusing {0} cached memory region entries", + m_mem_region_cache.size()); + return Status(); + } + + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, static_cast<int>(m_pid)}; + int ret; + size_t len; + + ret = ::sysctl(mib, 4, nullptr, &len, nullptr, 0); + if (ret != 0) { + m_supports_mem_region = LazyBool::eLazyBoolNo; + return Status("sysctl() for KERN_PROC_VMMAP failed"); + } + + std::unique_ptr<WritableMemoryBuffer> buf = + llvm::WritableMemoryBuffer::getNewMemBuffer(len); + ret = ::sysctl(mib, 4, buf->getBufferStart(), &len, nullptr, 0); + if (ret != 0) { + m_supports_mem_region = LazyBool::eLazyBoolNo; + return Status("sysctl() for KERN_PROC_VMMAP failed"); + } + + char *bp = buf->getBufferStart(); + char *end = bp + len; + while (bp < end) { + auto *kv = reinterpret_cast<struct kinfo_vmentry *>(bp); + if (kv->kve_structsize == 0) + break; + bp += kv->kve_structsize; + + MemoryRegionInfo info; + info.Clear(); + info.GetRange().SetRangeBase(kv->kve_start); + info.GetRange().SetRangeEnd(kv->kve_end); + info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); + + if (kv->kve_protection & VM_PROT_READ) + info.SetReadable(MemoryRegionInfo::OptionalBool::eYes); + else + info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + + if (kv->kve_protection & VM_PROT_WRITE) + info.SetWritable(MemoryRegionInfo::OptionalBool::eYes); + else + info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + + if (kv->kve_protection & VM_PROT_EXECUTE) + info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); + else + info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + + if (kv->kve_path[0]) + info.SetName(kv->kve_path); + + m_mem_region_cache.emplace_back(info, + FileSpec(info.GetName().GetCString())); + } + + if (m_mem_region_cache.empty()) { + // No entries after attempting to read them. This shouldn't happen. Assume + // we don't support map entries. + LLDB_LOG(log, "failed to find any vmmap entries, assuming no support " + "for memory region metadata retrieval"); + m_supports_mem_region = LazyBool::eLazyBoolNo; + return Status("not supported"); + } + LLDB_LOG(log, "read {0} memory region entries from process {1}", + m_mem_region_cache.size(), GetID()); + // We support memory retrieval, remember that. + m_supports_mem_region = LazyBool::eLazyBoolYes; + + return Status(); +} + +size_t NativeProcessFreeBSD::UpdateThreads() { return m_threads.size(); } + +Status NativeProcessFreeBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) { + if (hardware) + return SetHardwareBreakpoint(addr, size); + return SetSoftwareBreakpoint(addr, size); +} + +Status NativeProcessFreeBSD::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) { + auto status = CanTrace(); + if (status.Fail()) + return status; + return error; + } + + FileSpec module_file_spec(module_path); + FileSystem::Instance().Resolve(module_file_spec); + + file_spec.Clear(); + for (const auto &it : m_mem_region_cache) { + if (it.second.GetFilename() == module_file_spec.GetFilename()) { + file_spec = it.second; + return Status(); + } + } + return Status("Module file (%s) not found in process' memory map!", + module_file_spec.GetFilename().AsCString()); +} + +Status +NativeProcessFreeBSD::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + load_addr = LLDB_INVALID_ADDRESS; + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) { + auto status = CanTrace(); + if (status.Fail()) + return status; + return error; + } + + FileSpec file(file_name); + for (const auto &it : m_mem_region_cache) { + if (it.second == file) { + load_addr = it.first.GetRange().GetRangeBase(); + return Status(); + } + } + return Status("No load address found for file %s.", file_name.str().c_str()); +} + +void NativeProcessFreeBSD::SigchldHandler() { + Log *log = GetLog(POSIXLog::Process); + int status; + ::pid_t wait_pid = + llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, WNOHANG); + + if (wait_pid == 0) + return; + + if (wait_pid == -1) { + Status error(errno, eErrorTypePOSIX); + LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error); + return; + } + + WaitStatus wait_status = WaitStatus::Decode(status); + bool exited = wait_status.type == WaitStatus::Exit || + (wait_status.type == WaitStatus::Signal && + wait_pid == static_cast<::pid_t>(GetID())); + + LLDB_LOG(log, + "waitpid ({0}, &status, _) => pid = {1}, status = {2}, exited = {3}", + GetID(), wait_pid, status, exited); + + if (exited) + MonitorExited(wait_pid, wait_status); + else { + assert(wait_status.type == WaitStatus::Stop); + MonitorCallback(wait_pid, wait_status.status); + } +} + +bool NativeProcessFreeBSD::HasThreadNoLock(lldb::tid_t thread_id) { + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + if (thread->GetID() == thread_id) { + // We have this thread. + return true; + } + } + + // We don't have this thread. + return false; +} + +NativeThreadFreeBSD &NativeProcessFreeBSD::AddThread(lldb::tid_t thread_id) { + Log *log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); + + assert(thread_id > 0); + assert(!HasThreadNoLock(thread_id) && + "attempted to add a thread by id that already exists"); + + // If this is the first thread, save it as the current thread + if (m_threads.empty()) + SetCurrentThreadID(thread_id); + + m_threads.push_back(std::make_unique<NativeThreadFreeBSD>(*this, thread_id)); + return static_cast<NativeThreadFreeBSD &>(*m_threads.back()); +} + +void NativeProcessFreeBSD::RemoveThread(lldb::tid_t thread_id) { + Log *log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "pid {0} removing thread with tid {1}", GetID(), thread_id); + + assert(thread_id > 0); + assert(HasThreadNoLock(thread_id) && + "attempted to remove a thread that does not exist"); + + for (auto it = m_threads.begin(); it != m_threads.end(); ++it) { + if ((*it)->GetID() == thread_id) { + m_threads.erase(it); + break; + } + } + + if (GetCurrentThreadID() == thread_id) + SetCurrentThreadID(m_threads.front()->GetID()); +} + +Status NativeProcessFreeBSD::Attach() { + // Attach to the requested process. + // An attach will cause the thread to stop with a SIGSTOP. + Status status = PtraceWrapper(PT_ATTACH, m_pid); + if (status.Fail()) + return status; + + int wstatus; + // Need to use WALLSIG otherwise we receive an error with errno=ECHLD At this + // point we should have a thread stopped if waitpid succeeds. + if ((wstatus = llvm::sys::RetryAfterSignal(-1, waitpid, m_pid, nullptr, 0)) < + 0) + return Status(errno, eErrorTypePOSIX); + + // Initialize threads and tracing status + // NB: this needs to be called before we set thread state + status = SetupTrace(); + if (status.Fail()) + return status; + + for (const auto &thread : m_threads) + static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP); + + // Let our process instance know the thread has stopped. + SetCurrentThreadID(m_threads.front()->GetID()); + SetState(StateType::eStateStopped, false); + return Status(); +} + +Status NativeProcessFreeBSD::ReadMemory(lldb::addr_t addr, void *buf, + size_t size, size_t &bytes_read) { + unsigned char *dst = static_cast<unsigned char *>(buf); + struct ptrace_io_desc io; + + Log *log = GetLog(POSIXLog::Memory); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + bytes_read = 0; + io.piod_op = PIOD_READ_D; + io.piod_len = size; + + do { + io.piod_offs = (void *)(addr + bytes_read); + io.piod_addr = dst + bytes_read; + + Status error = NativeProcessFreeBSD::PtraceWrapper(PT_IO, GetID(), &io); + if (error.Fail() || io.piod_len == 0) + return error; + + bytes_read += io.piod_len; + io.piod_len = size - bytes_read; + } while (bytes_read < size); + + return Status(); +} + +Status NativeProcessFreeBSD::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) { + const unsigned char *src = static_cast<const unsigned char *>(buf); + Status error; + struct ptrace_io_desc io; + + Log *log = GetLog(POSIXLog::Memory); + LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); + + bytes_written = 0; + io.piod_op = PIOD_WRITE_D; + io.piod_len = size; + + do { + io.piod_addr = + const_cast<void *>(static_cast<const void *>(src + bytes_written)); + io.piod_offs = (void *)(addr + bytes_written); + + Status error = NativeProcessFreeBSD::PtraceWrapper(PT_IO, GetID(), &io); + if (error.Fail() || io.piod_len == 0) + return error; + + bytes_written += io.piod_len; + io.piod_len = size - bytes_written; + } while (bytes_written < size); + + return error; +} + +llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> +NativeProcessFreeBSD::GetAuxvData() const { + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_AUXV, static_cast<int>(GetID())}; + size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo); + std::unique_ptr<WritableMemoryBuffer> buf = + llvm::WritableMemoryBuffer::getNewMemBuffer(auxv_size); + + if (::sysctl(mib, 4, buf->getBufferStart(), &auxv_size, nullptr, 0) != 0) + return std::error_code(errno, std::generic_category()); + + return buf; +} + +Status NativeProcessFreeBSD::SetupTrace() { + // Enable event reporting + int events; + Status status = + PtraceWrapper(PT_GET_EVENT_MASK, GetID(), &events, sizeof(events)); + if (status.Fail()) + return status; + events |= PTRACE_LWP | PTRACE_FORK | PTRACE_VFORK; + status = PtraceWrapper(PT_SET_EVENT_MASK, GetID(), &events, sizeof(events)); + if (status.Fail()) + return status; + + return ReinitializeThreads(); +} + +Status NativeProcessFreeBSD::ReinitializeThreads() { + // Clear old threads + m_threads.clear(); + + int num_lwps; + Status error = PtraceWrapper(PT_GETNUMLWPS, GetID(), nullptr, 0, &num_lwps); + if (error.Fail()) + return error; + + std::vector<lwpid_t> lwp_ids; + lwp_ids.resize(num_lwps); + error = PtraceWrapper(PT_GETLWPLIST, GetID(), lwp_ids.data(), + lwp_ids.size() * sizeof(lwpid_t), &num_lwps); + if (error.Fail()) + return error; + + // Reinitialize from scratch threads and register them in process + for (lwpid_t lwp : lwp_ids) + AddThread(lwp); + + return error; +} + +bool NativeProcessFreeBSD::SupportHardwareSingleStepping() const { + return !m_arch.IsMIPS(); +} + +void NativeProcessFreeBSD::MonitorClone(::pid_t child_pid, bool is_vfork, + NativeThreadFreeBSD &parent_thread) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "fork, child_pid={0}", child_pid); + + int status; + ::pid_t wait_pid = + llvm::sys::RetryAfterSignal(-1, ::waitpid, child_pid, &status, 0); + if (wait_pid != child_pid) { + LLDB_LOG(log, + "waiting for pid {0} failed. Assuming the pid has " + "disappeared in the meantime", + child_pid); + return; + } + if (WIFEXITED(status)) { + LLDB_LOG(log, + "waiting for pid {0} returned an 'exited' event. Not " + "tracking it.", + child_pid); + return; + } + + struct ptrace_lwpinfo info; + const auto siginfo_err = PtraceWrapper(PT_LWPINFO, child_pid, &info, sizeof(info)); + if (siginfo_err.Fail()) { + LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); + return; + } + assert(info.pl_event == PL_EVENT_SIGNAL); + lldb::tid_t child_tid = info.pl_lwpid; + + std::unique_ptr<NativeProcessFreeBSD> child_process{ + new NativeProcessFreeBSD(static_cast<::pid_t>(child_pid), m_terminal_fd, + m_delegate, m_arch, m_main_loop)}; + if (!is_vfork) + child_process->m_software_breakpoints = m_software_breakpoints; + + Extension expected_ext = is_vfork ? Extension::vfork : Extension::fork; + if ((m_enabled_extensions & expected_ext) == expected_ext) { + child_process->SetupTrace(); + for (const auto &thread : child_process->m_threads) + static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP); + child_process->SetState(StateType::eStateStopped, false); + + m_delegate.NewSubprocess(this, std::move(child_process)); + if (is_vfork) + parent_thread.SetStoppedByVFork(child_pid, child_tid); + else + parent_thread.SetStoppedByFork(child_pid, child_tid); + SetState(StateType::eStateStopped, true); + } else { + child_process->Detach(); + Status pt_error = + PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), 0); + if (pt_error.Fail()) { + LLDB_LOG_ERROR(log, pt_error.ToError(), + "unable to resume parent process {1}: {0}", GetID()); + SetState(StateType::eStateInvalid); + } + } +} + +llvm::Expected<std::string> +NativeProcessFreeBSD::SaveCore(llvm::StringRef path_hint) { +#if defined(PT_COREDUMP) + using namespace llvm::sys::fs; + + llvm::SmallString<128> path{path_hint}; + Status error; + struct ptrace_coredump pc = {}; + + // Try with the suggested path first. If there is no suggested path or it + // failed to open, use a temporary file. + if (path.empty() || + openFile(path, pc.pc_fd, CD_CreateNew, FA_Write, OF_None)) { + if (std::error_code errc = + createTemporaryFile("lldb", "core", pc.pc_fd, path)) + return llvm::createStringError(errc, "Unable to create a temporary file"); + } + error = PtraceWrapper(PT_COREDUMP, GetID(), &pc, sizeof(pc)); + + std::error_code close_err = closeFile(pc.pc_fd); + if (error.Fail()) + return error.ToError(); + if (close_err) + return llvm::createStringError( + close_err, "Unable to close the core dump after writing"); + return path.str().str(); +#else // !defined(PT_COREDUMP) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "PT_COREDUMP not supported in the FreeBSD version used to build LLDB"); +#endif +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h new file mode 100644 index 000000000000..b638367ebbaa --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h @@ -0,0 +1,136 @@ +//===-- NativeProcessFreeBSD.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 liblldb_NativeProcessFreeBSD_H_ +#define liblldb_NativeProcessFreeBSD_H_ + +#include "Plugins/Process/POSIX/NativeProcessELF.h" +#include "Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h" + +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" + +#include "NativeThreadFreeBSD.h" + +namespace lldb_private { +namespace process_freebsd { +/// \class NativeProcessFreeBSD +/// Manages communication with the inferior (debugee) process. +/// +/// Upon construction, this class prepares and launches an inferior process +/// for debugging. +/// +/// Changes in the inferior process state are broadcasted. +class NativeProcessFreeBSD : public NativeProcessELF, + private NativeProcessSoftwareSingleStep { +public: + class Manager : public NativeProcessProtocol::Manager { + public: + using NativeProcessProtocol::Manager::Manager; + + llvm::Expected<std::unique_ptr<NativeProcessProtocol>> + Launch(ProcessLaunchInfo &launch_info, + NativeDelegate &native_delegate) override; + + llvm::Expected<std::unique_ptr<NativeProcessProtocol>> + Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override; + + Extension GetSupportedExtensions() const override; + }; + + // NativeProcessProtocol Interface + Status Resume(const ResumeActionList &resume_actions) override; + + Status Halt() override; + + Status Detach() override; + + Status Signal(int signo) override; + + Status Interrupt() override; + + Status Kill() override; + + Status GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) override; + + Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, + size_t &bytes_written) override; + + size_t UpdateThreads() override; + + const ArchSpec &GetArchitecture() const override { return m_arch; } + + Status SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) override; + + // The two following methods are probably not necessary and probably + // will never be called. Nevertheless, we implement them right now + // to reduce the differences between different platforms and reduce + // the risk of the lack of implementation actually breaking something, + // at least for the time being. + Status GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) override; + Status GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) override; + + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> + GetAuxvData() const override; + + // Interface used by NativeRegisterContext-derived classes. + static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr, + int data = 0, int *result = nullptr); + + bool SupportHardwareSingleStepping() const; + + llvm::Expected<std::string> SaveCore(llvm::StringRef path_hint) override; + +protected: + llvm::Expected<llvm::ArrayRef<uint8_t>> + GetSoftwareBreakpointTrapOpcode(size_t size_hint) override; + +private: + MainLoop::SignalHandleUP m_sigchld_handle; + ArchSpec m_arch; + MainLoop& m_main_loop; + LazyBool m_supports_mem_region = eLazyBoolCalculate; + std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache; + + // Private Instance Methods + NativeProcessFreeBSD(::pid_t pid, int terminal_fd, NativeDelegate &delegate, + const ArchSpec &arch, MainLoop &mainloop); + + bool HasThreadNoLock(lldb::tid_t thread_id); + + NativeThreadFreeBSD &AddThread(lldb::tid_t thread_id); + void RemoveThread(lldb::tid_t thread_id); + + void MonitorCallback(lldb::pid_t pid, int signal); + void MonitorExited(lldb::pid_t pid, WaitStatus status); + void MonitorSIGSTOP(lldb::pid_t pid); + void MonitorSIGTRAP(lldb::pid_t pid); + void MonitorSignal(lldb::pid_t pid, int signal); + void MonitorClone(::pid_t child_pid, bool is_vfork, + NativeThreadFreeBSD &parent_thread); + + Status PopulateMemoryRegionCache(); + void SigchldHandler(); + + Status Attach(); + Status SetupTrace(); + Status ReinitializeThreads(); +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeProcessFreeBSD_H_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.cpp new file mode 100644 index 000000000000..3d744f773a26 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.cpp @@ -0,0 +1,29 @@ +//===-- NativeRegisterContextFreeBSD.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 "NativeRegisterContextFreeBSD.h" + +#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" + +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +// clang-format off +#include <sys/types.h> +#include <sys/ptrace.h> +// clang-format on + +NativeProcessFreeBSD &NativeRegisterContextFreeBSD::GetProcess() { + return static_cast<NativeProcessFreeBSD &>(m_thread.GetProcess()); +} + +::pid_t NativeRegisterContextFreeBSD::GetProcessPid() { + return GetProcess().GetID(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h new file mode 100644 index 000000000000..b7f659ef24de --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h @@ -0,0 +1,42 @@ +//===-- NativeRegisterContextFreeBSD.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_NativeRegisterContextFreeBSD_h +#define lldb_NativeRegisterContextFreeBSD_h + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; +class NativeThreadFreeBSD; + +class NativeRegisterContextFreeBSD + : public virtual NativeRegisterContextRegisterInfo { +public: + // This function is implemented in the NativeRegisterContextFreeBSD_* + // subclasses to create a new instance of the host specific + // NativeRegisterContextFreeBSD. The implementations can't collide as only one + // NativeRegisterContextFreeBSD_* variant should be compiled into the final + // executable. + static NativeRegisterContextFreeBSD * + CreateHostNativeRegisterContextFreeBSD(const ArchSpec &target_arch, + NativeThreadFreeBSD &native_thread); + virtual llvm::Error + CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) = 0; + +protected: + virtual NativeProcessFreeBSD &GetProcess(); + virtual ::pid_t GetProcessPid(); +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextFreeBSD_h diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm.cpp new file mode 100644 index 000000000000..f19085600d6c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm.cpp @@ -0,0 +1,202 @@ +//===-- NativeRegisterContextFreeBSD_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 +// +//===----------------------------------------------------------------------===// + +#if defined(__arm__) + +#include "NativeRegisterContextFreeBSD_arm.h" + +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h" + +// clang-format off +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/types.h> +// clang-format on + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +NativeRegisterContextFreeBSD * +NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) { + return new NativeRegisterContextFreeBSD_arm(target_arch, native_thread); +} + +NativeRegisterContextFreeBSD_arm::NativeRegisterContextFreeBSD_arm( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) + : NativeRegisterContextRegisterInfo( + native_thread, new RegisterInfoPOSIX_arm(target_arch)) {} + +RegisterInfoPOSIX_arm & +NativeRegisterContextFreeBSD_arm::GetRegisterInfo() const { + return static_cast<RegisterInfoPOSIX_arm &>(*m_register_info_interface_up); +} + +uint32_t NativeRegisterContextFreeBSD_arm::GetRegisterSetCount() const { + return GetRegisterInfo().GetRegisterSetCount(); +} + +const RegisterSet * +NativeRegisterContextFreeBSD_arm::GetRegisterSet(uint32_t set_index) const { + return GetRegisterInfo().GetRegisterSet(set_index); +} + +uint32_t NativeRegisterContextFreeBSD_arm::GetUserRegisterCount() const { + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) + count += GetRegisterSet(set_index)->num_registers; + return count; +} + +Status NativeRegisterContextFreeBSD_arm::ReadRegisterSet(uint32_t set) { + switch (set) { + case RegisterInfoPOSIX_arm::GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), + m_reg_data.data()); + case RegisterInfoPOSIX_arm::FPRegSet: + return NativeProcessFreeBSD::PtraceWrapper( + PT_GETVFPREGS, m_thread.GetID(), + m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm::GPR)); + } + llvm_unreachable("NativeRegisterContextFreeBSD_arm::ReadRegisterSet"); +} + +Status NativeRegisterContextFreeBSD_arm::WriteRegisterSet(uint32_t set) { + switch (set) { + case RegisterInfoPOSIX_arm::GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), + m_reg_data.data()); + case RegisterInfoPOSIX_arm::FPRegSet: + return NativeProcessFreeBSD::PtraceWrapper( + PT_SETVFPREGS, m_thread.GetID(), + m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm::GPR)); + } + llvm_unreachable("NativeRegisterContextFreeBSD_arm::WriteRegisterSet"); +} + +Status +NativeRegisterContextFreeBSD_arm::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : "<unknown register>"); + + uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg); + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset, + reg_info->byte_size, endian::InlHostByteOrder()); + return error; +} + +Status NativeRegisterContextFreeBSD_arm::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + + if (!reg_info) + return Status("reg_info NULL"); + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : "<unknown register>"); + + uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg); + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(), + reg_info->byte_size); + + return WriteRegisterSet(set); +} + +Status NativeRegisterContextFreeBSD_arm::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + Status error; + + error = ReadRegisterSet(RegisterInfoPOSIX_arm::GPRegSet); + if (error.Fail()) + return error; + + error = ReadRegisterSet(RegisterInfoPOSIX_arm::FPRegSet); + if (error.Fail()) + return error; + + data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0)); + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, m_reg_data.data(), m_reg_data.size()); + + return error; +} + +Status NativeRegisterContextFreeBSD_arm::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_arm::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != m_reg_data.size()) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_arm::%s data_sp contained mismatched " + "data size, expected %" PRIu64 ", actual %" PRIu64, + __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize()); + return error; + } + + const uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_arm::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + ::memcpy(m_reg_data.data(), src, m_reg_data.size()); + + error = WriteRegisterSet(RegisterInfoPOSIX_arm::GPRegSet); + if (error.Fail()) + return error; + + return WriteRegisterSet(RegisterInfoPOSIX_arm::FPRegSet); +} + +llvm::Error NativeRegisterContextFreeBSD_arm::CopyHardwareWatchpointsFrom( + NativeRegisterContextFreeBSD &source) { + return llvm::Error::success(); +} + +#endif // defined (__arm__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm.h new file mode 100644 index 000000000000..b9537e6952f6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm.h @@ -0,0 +1,68 @@ +//===-- NativeRegisterContextFreeBSD_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 +// +//===----------------------------------------------------------------------===// + +#if defined(__arm__) + +#ifndef lldb_NativeRegisterContextFreeBSD_arm_h +#define lldb_NativeRegisterContextFreeBSD_arm_h + +// clang-format off +#include <sys/types.h> +#include <machine/reg.h> +#include <machine/vfp.h> +// clang-format on + +#include "Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h" + +#include <array> + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeRegisterContextFreeBSD_arm : public NativeRegisterContextFreeBSD { +public: + NativeRegisterContextFreeBSD_arm(const ArchSpec &target_arch, + NativeThreadFreeBSD &native_thread); + + uint32_t GetRegisterSetCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + llvm::Error + CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override; + +private: + std::array<uint8_t, sizeof(reg) + sizeof(vfp_state)> m_reg_data; + + Status ReadRegisterSet(uint32_t set); + Status WriteRegisterSet(uint32_t set); + + RegisterInfoPOSIX_arm &GetRegisterInfo() const; +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextFreeBSD_arm_h + +#endif // defined (__arm__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.cpp new file mode 100644 index 000000000000..28ea8b7ac118 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.cpp @@ -0,0 +1,302 @@ +//===-- NativeRegisterContextFreeBSD_arm64.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__aarch64__) + +#include "NativeRegisterContextFreeBSD_arm64.h" + +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/RegisterFlagsDetector_arm64.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" + +// clang-format off +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/types.h> +// clang-format on + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +// A NativeRegisterContext is constructed per thread, but all threads' registers +// will contain the same fields. Therefore this mutex prevents each instance +// competing with the other, and subsequent instances from having to detect the +// fields all over again. +static std::mutex g_register_flags_detector_mutex; +static Arm64RegisterFlagsDetector g_register_flags_detector; + +NativeRegisterContextFreeBSD * +NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) { + std::lock_guard<std::mutex> lock(g_register_flags_detector_mutex); + if (!g_register_flags_detector.HasDetected()) { + NativeProcessFreeBSD &process = native_thread.GetProcess(); + g_register_flags_detector.DetectFields( + process.GetAuxValue(AuxVector::AUXV_FREEBSD_AT_HWCAP).value_or(0), + process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2).value_or(0)); + } + + return new NativeRegisterContextFreeBSD_arm64(target_arch, native_thread); +} + +NativeRegisterContextFreeBSD_arm64::NativeRegisterContextFreeBSD_arm64( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) + : NativeRegisterContextRegisterInfo( + native_thread, new RegisterInfoPOSIX_arm64(target_arch, 0)) +#ifdef LLDB_HAS_FREEBSD_WATCHPOINT + , + m_read_dbreg(false) +#endif +{ + g_register_flags_detector.UpdateRegisterInfo( + GetRegisterInfoInterface().GetRegisterInfo(), + GetRegisterInfoInterface().GetRegisterCount()); + + ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); + ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); +} + +RegisterInfoPOSIX_arm64 & +NativeRegisterContextFreeBSD_arm64::GetRegisterInfo() const { + return static_cast<RegisterInfoPOSIX_arm64 &>(*m_register_info_interface_up); +} + +uint32_t NativeRegisterContextFreeBSD_arm64::GetRegisterSetCount() const { + return GetRegisterInfo().GetRegisterSetCount(); +} + +const RegisterSet * +NativeRegisterContextFreeBSD_arm64::GetRegisterSet(uint32_t set_index) const { + return GetRegisterInfo().GetRegisterSet(set_index); +} + +uint32_t NativeRegisterContextFreeBSD_arm64::GetUserRegisterCount() const { + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) + count += GetRegisterSet(set_index)->num_registers; + return count; +} + +Status NativeRegisterContextFreeBSD_arm64::ReadRegisterSet(uint32_t set) { + switch (set) { + case RegisterInfoPOSIX_arm64::GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), + m_reg_data.data()); + case RegisterInfoPOSIX_arm64::FPRegSet: + return NativeProcessFreeBSD::PtraceWrapper( + PT_GETFPREGS, m_thread.GetID(), + m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR)); + } + llvm_unreachable("NativeRegisterContextFreeBSD_arm64::ReadRegisterSet"); +} + +Status NativeRegisterContextFreeBSD_arm64::WriteRegisterSet(uint32_t set) { + switch (set) { + case RegisterInfoPOSIX_arm64::GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), + m_reg_data.data()); + case RegisterInfoPOSIX_arm64::FPRegSet: + return NativeProcessFreeBSD::PtraceWrapper( + PT_SETFPREGS, m_thread.GetID(), + m_reg_data.data() + sizeof(RegisterInfoPOSIX_arm64::GPR)); + } + llvm_unreachable("NativeRegisterContextFreeBSD_arm64::WriteRegisterSet"); +} + +Status +NativeRegisterContextFreeBSD_arm64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : "<unknown register>"); + + uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg); + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset, + reg_info->byte_size, endian::InlHostByteOrder()); + return error; +} + +Status NativeRegisterContextFreeBSD_arm64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + + if (!reg_info) + return Status("reg_info NULL"); + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : "<unknown register>"); + + uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg); + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(), + reg_info->byte_size); + + return WriteRegisterSet(set); +} + +Status NativeRegisterContextFreeBSD_arm64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + Status error; + + error = ReadRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet); + if (error.Fail()) + return error; + + error = ReadRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet); + if (error.Fail()) + return error; + + data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0)); + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, m_reg_data.data(), m_reg_data.size()); + + return error; +} + +Status NativeRegisterContextFreeBSD_arm64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_arm64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != m_reg_data.size()) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_arm64::%s data_sp contained mismatched " + "data size, expected %" PRIu64 ", actual %" PRIu64, + __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize()); + return error; + } + + const uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_arm64::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + ::memcpy(m_reg_data.data(), src, m_reg_data.size()); + + error = WriteRegisterSet(RegisterInfoPOSIX_arm64::GPRegSet); + if (error.Fail()) + return error; + + return WriteRegisterSet(RegisterInfoPOSIX_arm64::FPRegSet); +} + +llvm::Error NativeRegisterContextFreeBSD_arm64::CopyHardwareWatchpointsFrom( + NativeRegisterContextFreeBSD &source) { +#ifdef LLDB_HAS_FREEBSD_WATCHPOINT + auto &r_source = static_cast<NativeRegisterContextFreeBSD_arm64 &>(source); + llvm::Error error = r_source.ReadHardwareDebugInfo(); + if (error) + return error; + + m_dbreg = r_source.m_dbreg; + m_hbp_regs = r_source.m_hbp_regs; + m_hwp_regs = r_source.m_hwp_regs; + m_max_hbp_supported = r_source.m_max_hbp_supported; + m_max_hwp_supported = r_source.m_max_hwp_supported; + m_read_dbreg = true; + + // on FreeBSD this writes both breakpoints and watchpoints + return WriteHardwareDebugRegs(eDREGTypeWATCH); +#else + return llvm::Error::success(); +#endif +} + +llvm::Error NativeRegisterContextFreeBSD_arm64::ReadHardwareDebugInfo() { +#ifdef LLDB_HAS_FREEBSD_WATCHPOINT + Log *log = GetLog(POSIXLog::Registers); + + // we're fully stateful, so no need to reread control registers ever + if (m_read_dbreg) + return llvm::Error::success(); + + Status res = NativeProcessFreeBSD::PtraceWrapper(PT_GETDBREGS, + m_thread.GetID(), &m_dbreg); + if (res.Fail()) + return res.ToError(); + + LLDB_LOG(log, "m_dbreg read: debug_ver={0}, nbkpts={1}, nwtpts={2}", + m_dbreg.db_debug_ver, m_dbreg.db_nbkpts, m_dbreg.db_nwtpts); + m_max_hbp_supported = m_dbreg.db_nbkpts; + m_max_hwp_supported = m_dbreg.db_nwtpts; + assert(m_max_hbp_supported <= m_hbp_regs.size()); + assert(m_max_hwp_supported <= m_hwp_regs.size()); + + m_read_dbreg = true; + return llvm::Error::success(); +#else + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Hardware breakpoints/watchpoints require FreeBSD 14.0"); +#endif +} + +llvm::Error +NativeRegisterContextFreeBSD_arm64::WriteHardwareDebugRegs(DREGType) { +#ifdef LLDB_HAS_FREEBSD_WATCHPOINT + assert(m_read_dbreg && "dbregs must be read before writing them back"); + + // copy data from m_*_regs to m_dbreg before writing it back + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + m_dbreg.db_breakregs[i].dbr_addr = m_hbp_regs[i].address; + m_dbreg.db_breakregs[i].dbr_ctrl = m_hbp_regs[i].control; + } + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + m_dbreg.db_watchregs[i].dbw_addr = m_hwp_regs[i].address; + m_dbreg.db_watchregs[i].dbw_ctrl = m_hwp_regs[i].control; + } + + return NativeProcessFreeBSD::PtraceWrapper(PT_SETDBREGS, m_thread.GetID(), + &m_dbreg) + .ToError(); +#else + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Hardware breakpoints/watchpoints require FreeBSD 14.0"); +#endif +} + +#endif // defined (__aarch64__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.h new file mode 100644 index 000000000000..ba876006c6c5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_arm64.h @@ -0,0 +1,86 @@ +//===-- NativeRegisterContextFreeBSD_arm64.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__aarch64__) + +#ifndef lldb_NativeRegisterContextFreeBSD_arm64_h +#define lldb_NativeRegisterContextFreeBSD_arm64_h + +// clang-format off +#include <sys/types.h> +#include <sys/param.h> +#include <machine/reg.h> +// clang-format on + +#include "Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h" +#include "Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" + +#include <array> + +#if __FreeBSD_version >= 1300139 +# define LLDB_HAS_FREEBSD_WATCHPOINT 1 +#endif + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeRegisterContextFreeBSD_arm64 + : public NativeRegisterContextFreeBSD, + public NativeRegisterContextDBReg_arm64 { +public: + NativeRegisterContextFreeBSD_arm64(const ArchSpec &target_arch, + NativeThreadFreeBSD &native_thread); + + uint32_t GetRegisterSetCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + llvm::Error + CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override; + +private: + // Due to alignment, FreeBSD reg/fpreg are a few bytes larger than + // LLDB's GPR/FPU structs. However, all fields have matching offsets + // and sizes, so we do not have to worry about these (and we have + // a unittest to assert that). + std::array<uint8_t, sizeof(reg) + sizeof(fpreg)> m_reg_data; +#ifdef LLDB_HAS_FREEBSD_WATCHPOINT + dbreg m_dbreg; + bool m_read_dbreg; +#endif + + Status ReadRegisterSet(uint32_t set); + Status WriteRegisterSet(uint32_t set); + + llvm::Error ReadHardwareDebugInfo() override; + llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; + + RegisterInfoPOSIX_arm64 &GetRegisterInfo() const; +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextFreeBSD_arm64_h + +#endif // defined (__aarch64__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_mips64.cpp new file mode 100644 index 000000000000..090d0f3802c3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_mips64.cpp @@ -0,0 +1,239 @@ +//===-- NativeRegisterContextFreeBSD_mips64.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__mips64__) + +#include "NativeRegisterContextFreeBSD_mips64.h" + +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h" +#include "Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h" + +// clang-format off +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/types.h> +// clang-format on +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +NativeRegisterContextFreeBSD * +NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) { + return new NativeRegisterContextFreeBSD_mips64(target_arch, native_thread); +} + +NativeRegisterContextFreeBSD_mips64::NativeRegisterContextFreeBSD_mips64( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) + : NativeRegisterContextRegisterInfo( + native_thread, new RegisterContextFreeBSD_mips64(target_arch)) {} + +RegisterContextFreeBSD_mips64 & +NativeRegisterContextFreeBSD_mips64::GetRegisterInfo() const { + return static_cast<RegisterContextFreeBSD_mips64 &>( + *m_register_info_interface_up); +} + +uint32_t NativeRegisterContextFreeBSD_mips64::GetRegisterSetCount() const { + return GetRegisterInfo().GetRegisterSetCount(); +} + +const RegisterSet * +NativeRegisterContextFreeBSD_mips64::GetRegisterSet(uint32_t set_index) const { + return GetRegisterInfo().GetRegisterSet(set_index); +} + +uint32_t NativeRegisterContextFreeBSD_mips64::GetUserRegisterCount() const { + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) + count += GetRegisterSet(set_index)->num_registers; + return count; +} + +std::optional<NativeRegisterContextFreeBSD_mips64::RegSetKind> +NativeRegisterContextFreeBSD_mips64::GetSetForNativeRegNum( + uint32_t reg_num) const { + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::mips64: + if (reg_num >= k_first_gpr_mips64 && reg_num <= k_last_gpr_mips64) + return GPRegSet; + if (reg_num >= k_first_fpr_mips64 && reg_num <= k_last_fpr_mips64) + return FPRegSet; + break; + default: + llvm_unreachable("Unhandled target architecture."); + } + + llvm_unreachable("Register does not belong to any register set"); +} + +Status NativeRegisterContextFreeBSD_mips64::ReadRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), + m_reg_data.data()); + case FPRegSet: + return NativeProcessFreeBSD::PtraceWrapper( + PT_GETFPREGS, m_thread.GetID(), + m_reg_data.data() + GetRegisterInfo().GetGPRSize()); + } + llvm_unreachable("NativeRegisterContextFreeBSD_mips64::ReadRegisterSet"); +} + +Status NativeRegisterContextFreeBSD_mips64::WriteRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), + m_reg_data.data()); + case FPRegSet: + return NativeProcessFreeBSD::PtraceWrapper( + PT_SETFPREGS, m_thread.GetID(), + m_reg_data.data() + GetRegisterInfo().GetGPRSize()); + } + llvm_unreachable("NativeRegisterContextFreeBSD_mips64::WriteRegisterSet"); +} + +Status +NativeRegisterContextFreeBSD_mips64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : "<unknown register>"); + + std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg); + if (!opt_set) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set", + reg_info->name); + return error; + } + + RegSetKind set = *opt_set; + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset, + reg_info->byte_size, endian::InlHostByteOrder()); + return error; +} + +Status NativeRegisterContextFreeBSD_mips64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + + if (!reg_info) + return Status("reg_info NULL"); + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : "<unknown register>"); + + std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg); + if (!opt_set) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set", + reg_info->name); + return error; + } + + RegSetKind set = *opt_set; + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(), + reg_info->byte_size); + + return WriteRegisterSet(set); +} + +Status NativeRegisterContextFreeBSD_mips64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + Status error; + + error = ReadRegisterSet(GPRegSet); + if (error.Fail()) + return error; + + error = ReadRegisterSet(FPRegSet); + if (error.Fail()) + return error; + + data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0)); + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, m_reg_data.data(), m_reg_data.size()); + + return error; +} + +Status NativeRegisterContextFreeBSD_mips64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_mips64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != m_reg_data.size()) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_mips64::%s data_sp contained mismatched " + "data size, expected %" PRIu64 ", actual %" PRIu64, + __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize()); + return error; + } + + const uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_mips64::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + ::memcpy(m_reg_data.data(), src, m_reg_data.size()); + + error = WriteRegisterSet(GPRegSet); + if (error.Fail()) + return error; + + return WriteRegisterSet(FPRegSet); +} + +llvm::Error NativeRegisterContextFreeBSD_mips64::CopyHardwareWatchpointsFrom( + NativeRegisterContextFreeBSD &source) { + return llvm::Error::success(); +} + +#endif // defined (__mips64__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_mips64.h new file mode 100644 index 000000000000..286b4fd8d8b9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_mips64.h @@ -0,0 +1,75 @@ +//===-- NativeRegisterContextFreeBSD_mips64.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__mips64__) + +#ifndef lldb_NativeRegisterContextFreeBSD_mips64_h +#define lldb_NativeRegisterContextFreeBSD_mips64_h + +// clang-format off +#include <sys/types.h> +#include <machine/reg.h> +// clang-format on + +#include "Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h" + +#include <array> +#include <optional> + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeRegisterContextFreeBSD_mips64 + : public NativeRegisterContextFreeBSD { +public: + NativeRegisterContextFreeBSD_mips64(const ArchSpec &target_arch, + NativeThreadFreeBSD &native_thread); + + uint32_t GetRegisterSetCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + llvm::Error + CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override; + +private: + enum RegSetKind { + GPRegSet, + FPRegSet, + }; + std::array<uint8_t, sizeof(reg) + sizeof(fpreg)> m_reg_data; + + std::optional<RegSetKind> GetSetForNativeRegNum(uint32_t reg_num) const; + + Status ReadRegisterSet(RegSetKind set); + Status WriteRegisterSet(RegSetKind set); + + RegisterContextFreeBSD_mips64 &GetRegisterInfo() const; +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextFreeBSD_mips64_h + +#endif // defined (__mips64__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_powerpc.cpp new file mode 100644 index 000000000000..fd5eb1ee2a1c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_powerpc.cpp @@ -0,0 +1,290 @@ +//===-- NativeRegisterContextFreeBSD_powerpc.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__powerpc__) + +#include "NativeRegisterContextFreeBSD_powerpc.h" + +#include "lldb/Host/HostInfo.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h" +// for register enum definitions +#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h" + +// clang-format off +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/types.h> +// clang-format on +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +static const uint32_t g_gpr_regnums[] = { + gpr_r0_powerpc, gpr_r1_powerpc, gpr_r2_powerpc, gpr_r3_powerpc, + gpr_r4_powerpc, gpr_r5_powerpc, gpr_r6_powerpc, gpr_r7_powerpc, + gpr_r8_powerpc, gpr_r9_powerpc, gpr_r10_powerpc, gpr_r11_powerpc, + gpr_r12_powerpc, gpr_r13_powerpc, gpr_r14_powerpc, gpr_r15_powerpc, + gpr_r16_powerpc, gpr_r17_powerpc, gpr_r18_powerpc, gpr_r19_powerpc, + gpr_r20_powerpc, gpr_r21_powerpc, gpr_r22_powerpc, gpr_r23_powerpc, + gpr_r24_powerpc, gpr_r25_powerpc, gpr_r26_powerpc, gpr_r27_powerpc, + gpr_r28_powerpc, gpr_r29_powerpc, gpr_r30_powerpc, gpr_r31_powerpc, + gpr_lr_powerpc, gpr_cr_powerpc, gpr_xer_powerpc, gpr_ctr_powerpc, + gpr_pc_powerpc, +}; + +static const uint32_t g_fpr_regnums[] = { + fpr_f0_powerpc, fpr_f1_powerpc, fpr_f2_powerpc, fpr_f3_powerpc, + fpr_f4_powerpc, fpr_f5_powerpc, fpr_f6_powerpc, fpr_f7_powerpc, + fpr_f8_powerpc, fpr_f9_powerpc, fpr_f10_powerpc, fpr_f11_powerpc, + fpr_f12_powerpc, fpr_f13_powerpc, fpr_f14_powerpc, fpr_f15_powerpc, + fpr_f16_powerpc, fpr_f17_powerpc, fpr_f18_powerpc, fpr_f19_powerpc, + fpr_f20_powerpc, fpr_f21_powerpc, fpr_f22_powerpc, fpr_f23_powerpc, + fpr_f24_powerpc, fpr_f25_powerpc, fpr_f26_powerpc, fpr_f27_powerpc, + fpr_f28_powerpc, fpr_f29_powerpc, fpr_f30_powerpc, fpr_f31_powerpc, + fpr_fpscr_powerpc, +}; + +// Number of register sets provided by this context. +enum { k_num_register_sets = 2 }; + +static const RegisterSet g_reg_sets_powerpc[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_powerpc, + g_gpr_regnums}, + {"Floating Point Registers", "fpr", k_num_fpr_registers_powerpc, + g_fpr_regnums}, +}; + +NativeRegisterContextFreeBSD * +NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) { + return new NativeRegisterContextFreeBSD_powerpc(target_arch, native_thread); +} + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) { + return new RegisterContextFreeBSD_powerpc32(target_arch); + } else { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + return new RegisterContextFreeBSD_powerpc64(target_arch); + } +} + +NativeRegisterContextFreeBSD_powerpc::NativeRegisterContextFreeBSD_powerpc( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) + : NativeRegisterContextRegisterInfo( + native_thread, CreateRegisterInfoInterface(target_arch)) {} + +RegisterContextFreeBSD_powerpc & +NativeRegisterContextFreeBSD_powerpc::GetRegisterInfo() const { + return static_cast<RegisterContextFreeBSD_powerpc &>( + *m_register_info_interface_up); +} + +uint32_t NativeRegisterContextFreeBSD_powerpc::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextFreeBSD_powerpc::GetRegisterSet(uint32_t set_index) const { + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::ppc: + return &g_reg_sets_powerpc[set_index]; + default: + llvm_unreachable("Unhandled target architecture."); + } +} + +std::optional<NativeRegisterContextFreeBSD_powerpc::RegSetKind> +NativeRegisterContextFreeBSD_powerpc::GetSetForNativeRegNum( + uint32_t reg_num) const { + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::ppc: + if (reg_num >= k_first_gpr_powerpc && reg_num <= k_last_gpr_powerpc) + return GPRegSet; + if (reg_num >= k_first_fpr && reg_num <= k_last_fpr) + return FPRegSet; + break; + default: + llvm_unreachable("Unhandled target architecture."); + } + + llvm_unreachable("Register does not belong to any register set"); +} + +uint32_t NativeRegisterContextFreeBSD_powerpc::GetUserRegisterCount() const { + uint32_t count = 0; + for (uint32_t set_index = 0; set_index < GetRegisterSetCount(); ++set_index) + count += GetRegisterSet(set_index)->num_registers; + return count; +} + +Status NativeRegisterContextFreeBSD_powerpc::ReadRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), + m_reg_data.data()); + case FPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_GETFPREGS, m_thread.GetID(), + m_reg_data.data() + sizeof(reg)); + } + llvm_unreachable("NativeRegisterContextFreeBSD_powerpc::ReadRegisterSet"); +} + +Status NativeRegisterContextFreeBSD_powerpc::WriteRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), + m_reg_data.data()); + case FPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_SETFPREGS, m_thread.GetID(), + m_reg_data.data() + sizeof(reg)); + } + llvm_unreachable("NativeRegisterContextFreeBSD_powerpc::WriteRegisterSet"); +} + +Status +NativeRegisterContextFreeBSD_powerpc::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : "<unknown register>"); + + std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg); + if (!opt_set) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set", + reg_info->name); + return error; + } + + RegSetKind set = *opt_set; + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + reg_value.SetBytes(m_reg_data.data() + reg_info->byte_offset, + reg_info->byte_size, endian::InlHostByteOrder()); + return error; +} + +Status NativeRegisterContextFreeBSD_powerpc::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + + if (!reg_info) + return Status("reg_info NULL"); + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + + if (reg == LLDB_INVALID_REGNUM) + return Status("no lldb regnum for %s", reg_info && reg_info->name + ? reg_info->name + : "<unknown register>"); + + std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg); + if (!opt_set) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set", + reg_info->name); + return error; + } + + RegSetKind set = *opt_set; + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + assert(reg_info->byte_offset + reg_info->byte_size <= m_reg_data.size()); + ::memcpy(m_reg_data.data() + reg_info->byte_offset, reg_value.GetBytes(), + reg_info->byte_size); + + return WriteRegisterSet(set); +} + +Status NativeRegisterContextFreeBSD_powerpc::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + Status error; + + error = ReadRegisterSet(GPRegSet); + if (error.Fail()) + return error; + + error = ReadRegisterSet(FPRegSet); + if (error.Fail()) + return error; + + data_sp.reset(new DataBufferHeap(m_reg_data.size(), 0)); + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, m_reg_data.data(), m_reg_data.size()); + + return error; +} + +Status NativeRegisterContextFreeBSD_powerpc::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_powerpc::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != m_reg_data.size()) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_powerpc::%s data_sp contained mismatched " + "data size, expected %zu, actual %" PRIu64, + __FUNCTION__, m_reg_data.size(), data_sp->GetByteSize()); + return error; + } + + const uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_powerpc::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + ::memcpy(m_reg_data.data(), src, m_reg_data.size()); + + error = WriteRegisterSet(GPRegSet); + if (error.Fail()) + return error; + + return WriteRegisterSet(FPRegSet); +} + +llvm::Error NativeRegisterContextFreeBSD_powerpc::CopyHardwareWatchpointsFrom( + NativeRegisterContextFreeBSD &source) { + return llvm::Error::success(); +} + +#endif // defined (__powerpc__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_powerpc.h new file mode 100644 index 000000000000..420db822acc0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_powerpc.h @@ -0,0 +1,75 @@ +//===-- NativeRegisterContextFreeBSD_powerpc.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__powerpc__) + +#ifndef lldb_NativeRegisterContextFreeBSD_powerpc_h +#define lldb_NativeRegisterContextFreeBSD_powerpc_h + +// clang-format off +#include <sys/types.h> +#include <machine/reg.h> +// clang-format on + +#include "Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h" + +#include <array> +#include <optional> + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeRegisterContextFreeBSD_powerpc + : public NativeRegisterContextFreeBSD { +public: + NativeRegisterContextFreeBSD_powerpc(const ArchSpec &target_arch, + NativeThreadFreeBSD &native_thread); + + uint32_t GetRegisterSetCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + llvm::Error + CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override; + +private: + enum RegSetKind { + GPRegSet, + FPRegSet, + }; + std::array<uint8_t, sizeof(reg) + sizeof(fpreg)> m_reg_data; + + std::optional<RegSetKind> GetSetForNativeRegNum(uint32_t reg_num) const; + + Status ReadRegisterSet(RegSetKind set); + Status WriteRegisterSet(RegSetKind set); + + RegisterContextFreeBSD_powerpc &GetRegisterInfo() const; +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextFreeBSD_powerpc_h + +#endif // defined (__powerpc__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_x86_64.cpp new file mode 100644 index 000000000000..5eed2d02b0a8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_x86_64.cpp @@ -0,0 +1,657 @@ +//===-- NativeRegisterContextFreeBSD_x86_64.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#include "NativeRegisterContextFreeBSD_x86_64.h" + +// clang-format off +#include <x86/fpu.h> +#include <x86/specialreg.h> +#include <cpuid.h> +// clang-format on + +#include "lldb/Host/HostInfo.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +#include "NativeProcessFreeBSD.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h" +#include <optional> + +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +// x86 64-bit general purpose registers. +static const uint32_t g_gpr_regnums_x86_64[] = { + lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64, + lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64, + lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64, + lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64, + lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64, + lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64, + lldb_eax_x86_64, lldb_ebx_x86_64, lldb_ecx_x86_64, lldb_edx_x86_64, + lldb_edi_x86_64, lldb_esi_x86_64, lldb_ebp_x86_64, lldb_esp_x86_64, + lldb_r8d_x86_64, // Low 32 bits or r8 + lldb_r9d_x86_64, // Low 32 bits or r9 + lldb_r10d_x86_64, // Low 32 bits or r10 + lldb_r11d_x86_64, // Low 32 bits or r11 + lldb_r12d_x86_64, // Low 32 bits or r12 + lldb_r13d_x86_64, // Low 32 bits or r13 + lldb_r14d_x86_64, // Low 32 bits or r14 + lldb_r15d_x86_64, // Low 32 bits or r15 + lldb_ax_x86_64, lldb_bx_x86_64, lldb_cx_x86_64, lldb_dx_x86_64, + lldb_di_x86_64, lldb_si_x86_64, lldb_bp_x86_64, lldb_sp_x86_64, + lldb_r8w_x86_64, // Low 16 bits or r8 + lldb_r9w_x86_64, // Low 16 bits or r9 + lldb_r10w_x86_64, // Low 16 bits or r10 + lldb_r11w_x86_64, // Low 16 bits or r11 + lldb_r12w_x86_64, // Low 16 bits or r12 + lldb_r13w_x86_64, // Low 16 bits or r13 + lldb_r14w_x86_64, // Low 16 bits or r14 + lldb_r15w_x86_64, // Low 16 bits or r15 + lldb_ah_x86_64, lldb_bh_x86_64, lldb_ch_x86_64, lldb_dh_x86_64, + lldb_al_x86_64, lldb_bl_x86_64, lldb_cl_x86_64, lldb_dl_x86_64, + lldb_dil_x86_64, lldb_sil_x86_64, lldb_bpl_x86_64, lldb_spl_x86_64, + lldb_r8l_x86_64, // Low 8 bits or r8 + lldb_r9l_x86_64, // Low 8 bits or r9 + lldb_r10l_x86_64, // Low 8 bits or r10 + lldb_r11l_x86_64, // Low 8 bits or r11 + lldb_r12l_x86_64, // Low 8 bits or r12 + lldb_r13l_x86_64, // Low 8 bits or r13 + lldb_r14l_x86_64, // Low 8 bits or r14 + lldb_r15l_x86_64, // Low 8 bits or r15 + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) - + 1 == + k_num_gpr_registers_x86_64, + "g_gpr_regnums_x86_64 has wrong number of register infos"); + +// x86 64-bit floating point registers. +static const uint32_t g_fpu_regnums_x86_64[] = { + lldb_fctrl_x86_64, lldb_fstat_x86_64, lldb_ftag_x86_64, + lldb_fop_x86_64, lldb_fiseg_x86_64, lldb_fioff_x86_64, + lldb_fip_x86_64, lldb_foseg_x86_64, lldb_fooff_x86_64, + lldb_fdp_x86_64, lldb_mxcsr_x86_64, lldb_mxcsrmask_x86_64, + lldb_st0_x86_64, lldb_st1_x86_64, lldb_st2_x86_64, + lldb_st3_x86_64, lldb_st4_x86_64, lldb_st5_x86_64, + lldb_st6_x86_64, lldb_st7_x86_64, lldb_mm0_x86_64, + lldb_mm1_x86_64, lldb_mm2_x86_64, lldb_mm3_x86_64, + lldb_mm4_x86_64, lldb_mm5_x86_64, lldb_mm6_x86_64, + lldb_mm7_x86_64, lldb_xmm0_x86_64, lldb_xmm1_x86_64, + lldb_xmm2_x86_64, lldb_xmm3_x86_64, lldb_xmm4_x86_64, + lldb_xmm5_x86_64, lldb_xmm6_x86_64, lldb_xmm7_x86_64, + lldb_xmm8_x86_64, lldb_xmm9_x86_64, lldb_xmm10_x86_64, + lldb_xmm11_x86_64, lldb_xmm12_x86_64, lldb_xmm13_x86_64, + lldb_xmm14_x86_64, lldb_xmm15_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_fpu_regnums_x86_64) / sizeof(g_fpu_regnums_x86_64[0])) - + 1 == + k_num_fpr_registers_x86_64, + "g_fpu_regnums_x86_64 has wrong number of register infos"); + +static const uint32_t g_avx_regnums_x86_64[] = { + lldb_ymm0_x86_64, lldb_ymm1_x86_64, lldb_ymm2_x86_64, lldb_ymm3_x86_64, + lldb_ymm4_x86_64, lldb_ymm5_x86_64, lldb_ymm6_x86_64, lldb_ymm7_x86_64, + lldb_ymm8_x86_64, lldb_ymm9_x86_64, lldb_ymm10_x86_64, lldb_ymm11_x86_64, + lldb_ymm12_x86_64, lldb_ymm13_x86_64, lldb_ymm14_x86_64, lldb_ymm15_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) - + 1 == + k_num_avx_registers_x86_64, + "g_avx_regnums_x86_64 has wrong number of register infos"); + +static const uint32_t g_mpx_regnums_x86_64[] = { + // Note: we currently do not provide them but this is needed to avoid + // unnamed groups in SBFrame::GetRegisterContext(). + lldb_bnd0_x86_64, lldb_bnd1_x86_64, lldb_bnd2_x86_64, + lldb_bnd3_x86_64, lldb_bndcfgu_x86_64, lldb_bndstatus_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_mpx_regnums_x86_64) / sizeof(g_mpx_regnums_x86_64[0])) - + 1 == + k_num_mpx_registers_x86_64, + "g_mpx_regnums_x86_64 has wrong number of register infos"); + +// x86 debug registers. +static const uint32_t g_dbr_regnums_x86_64[] = { + lldb_dr0_x86_64, lldb_dr1_x86_64, lldb_dr2_x86_64, lldb_dr3_x86_64, + lldb_dr4_x86_64, lldb_dr5_x86_64, lldb_dr6_x86_64, lldb_dr7_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_dbr_regnums_x86_64) / sizeof(g_dbr_regnums_x86_64[0])) - + 1 == + k_num_dbr_registers_x86_64, + "g_dbr_regnums_x86_64 has wrong number of register infos"); + +// x86 32-bit general purpose registers. +static const uint32_t g_gpr_regnums_i386[] = { + lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386, + lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386, + lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386, + lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386, + lldb_ax_i386, lldb_bx_i386, lldb_cx_i386, lldb_dx_i386, + lldb_di_i386, lldb_si_i386, lldb_bp_i386, lldb_sp_i386, + lldb_ah_i386, lldb_bh_i386, lldb_ch_i386, lldb_dh_i386, + lldb_al_i386, lldb_bl_i386, lldb_cl_i386, lldb_dl_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_gpr_regnums_i386) / sizeof(g_gpr_regnums_i386[0])) - + 1 == + k_num_gpr_registers_i386, + "g_gpr_regnums_i386 has wrong number of register infos"); + +// x86 32-bit floating point registers. +static const uint32_t g_fpu_regnums_i386[] = { + lldb_fctrl_i386, lldb_fstat_i386, lldb_ftag_i386, lldb_fop_i386, + lldb_fiseg_i386, lldb_fioff_i386, lldb_foseg_i386, lldb_fooff_i386, + lldb_mxcsr_i386, lldb_mxcsrmask_i386, lldb_st0_i386, lldb_st1_i386, + lldb_st2_i386, lldb_st3_i386, lldb_st4_i386, lldb_st5_i386, + lldb_st6_i386, lldb_st7_i386, lldb_mm0_i386, lldb_mm1_i386, + lldb_mm2_i386, lldb_mm3_i386, lldb_mm4_i386, lldb_mm5_i386, + lldb_mm6_i386, lldb_mm7_i386, lldb_xmm0_i386, lldb_xmm1_i386, + lldb_xmm2_i386, lldb_xmm3_i386, lldb_xmm4_i386, lldb_xmm5_i386, + lldb_xmm6_i386, lldb_xmm7_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_fpu_regnums_i386) / sizeof(g_fpu_regnums_i386[0])) - + 1 == + k_num_fpr_registers_i386, + "g_fpu_regnums_i386 has wrong number of register infos"); + +static const uint32_t g_avx_regnums_i386[] = { + lldb_ymm0_i386, lldb_ymm1_i386, lldb_ymm2_i386, lldb_ymm3_i386, + lldb_ymm4_i386, lldb_ymm5_i386, lldb_ymm6_i386, lldb_ymm7_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_avx_regnums_i386) / sizeof(g_avx_regnums_i386[0])) - + 1 == + k_num_avx_registers_i386, + "g_avx_regnums_i386 has wrong number of register infos"); + +static const uint32_t g_mpx_regnums_i386[] = { + // Note: we currently do not provide them but this is needed to avoid + // unnamed groups in SBFrame::GetRegisterContext(). + lldb_bnd0_i386, lldb_bnd1_i386, lldb_bnd2_i386, + lldb_bnd3_i386, lldb_bndcfgu_i386, lldb_bndstatus_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_mpx_regnums_i386) / sizeof(g_mpx_regnums_i386[0])) - + 1 == + k_num_mpx_registers_i386, + "g_mpx_regnums_i386 has wrong number of register infos"); + +// x86 debug registers. +static const uint32_t g_dbr_regnums_i386[] = { + lldb_dr0_i386, lldb_dr1_i386, lldb_dr2_i386, lldb_dr3_i386, + lldb_dr4_i386, lldb_dr5_i386, lldb_dr6_i386, lldb_dr7_i386, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_dbr_regnums_i386) / sizeof(g_dbr_regnums_i386[0])) - + 1 == + k_num_dbr_registers_i386, + "g_dbr_regnums_i386 has wrong number of register infos"); + +// Number of register sets provided by this context. +enum { k_num_register_sets = 5 }; + +// Register sets for x86 32-bit. +static const RegisterSet g_reg_sets_i386[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_i386, + g_gpr_regnums_i386}, + {"Floating Point Registers", "fpu", k_num_fpr_registers_i386, + g_fpu_regnums_i386}, + {"Debug Registers", "dbr", k_num_dbr_registers_i386, g_dbr_regnums_i386}, + {"Advanced Vector Extensions", "avx", k_num_avx_registers_i386, + g_avx_regnums_i386}, + {"Memory Protection Extensions", "mpx", k_num_mpx_registers_i386, + g_mpx_regnums_i386}, +}; + +// Register sets for x86 64-bit. +static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_x86_64, + g_gpr_regnums_x86_64}, + {"Floating Point Registers", "fpu", k_num_fpr_registers_x86_64, + g_fpu_regnums_x86_64}, + {"Debug Registers", "dbr", k_num_dbr_registers_x86_64, + g_dbr_regnums_x86_64}, + {"Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64, + g_avx_regnums_x86_64}, + {"Memory Protection Extensions", "mpx", k_num_mpx_registers_x86_64, + g_mpx_regnums_x86_64}, +}; + +#define REG_CONTEXT_SIZE (GetRegisterInfoInterface().GetGPRSize()) + +NativeRegisterContextFreeBSD * +NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) { + return new NativeRegisterContextFreeBSD_x86_64(target_arch, native_thread); +} + +// NativeRegisterContextFreeBSD_x86_64 members. + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) { + // 32-bit hosts run with a RegisterContextFreeBSD_i386 context. + return new RegisterContextFreeBSD_i386(target_arch); + } else { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + // X86_64 hosts know how to work with 64-bit and 32-bit EXEs using the + // x86_64 register context. + return new RegisterContextFreeBSD_x86_64(target_arch); + } +} + +NativeRegisterContextFreeBSD_x86_64::NativeRegisterContextFreeBSD_x86_64( + const ArchSpec &target_arch, NativeThreadFreeBSD &native_thread) + : NativeRegisterContextRegisterInfo( + native_thread, CreateRegisterInfoInterface(target_arch)), + NativeRegisterContextDBReg_x86(native_thread), m_regset_offsets({0}) { + assert(m_gpr.size() == GetRegisterInfoInterface().GetGPRSize()); + std::array<uint32_t, MaxRegSet + 1> first_regnos; + + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + first_regnos[FPRegSet] = lldb_fctrl_i386; + first_regnos[DBRegSet] = lldb_dr0_i386; + break; + case llvm::Triple::x86_64: + first_regnos[FPRegSet] = lldb_fctrl_x86_64; + first_regnos[DBRegSet] = lldb_dr0_x86_64; + break; + default: + llvm_unreachable("Unhandled target architecture."); + } + + for (int i : {FPRegSet, DBRegSet}) + m_regset_offsets[i] = GetRegisterInfoInterface() + .GetRegisterInfo()[first_regnos[i]] + .byte_offset; +} + +uint32_t NativeRegisterContextFreeBSD_x86_64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextFreeBSD_x86_64::GetRegisterSet(uint32_t set_index) const { + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return &g_reg_sets_i386[set_index]; + case llvm::Triple::x86_64: + return &g_reg_sets_x86_64[set_index]; + default: + llvm_unreachable("Unhandled target architecture."); + } +} + +std::optional<NativeRegisterContextFreeBSD_x86_64::RegSetKind> +NativeRegisterContextFreeBSD_x86_64::GetSetForNativeRegNum( + uint32_t reg_num) const { + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + if (reg_num >= k_first_gpr_i386 && reg_num <= k_last_gpr_i386) + return GPRegSet; + if (reg_num >= k_first_fpr_i386 && reg_num <= k_last_fpr_i386) + return FPRegSet; + if (reg_num >= k_first_avx_i386 && reg_num <= k_last_avx_i386) + return YMMRegSet; + if (reg_num >= k_first_mpxr_i386 && reg_num <= k_last_mpxr_i386) + return std::nullopt; // MPXR + if (reg_num >= k_first_mpxc_i386 && reg_num <= k_last_mpxc_i386) + return std::nullopt; // MPXC + if (reg_num >= k_first_dbr_i386 && reg_num <= k_last_dbr_i386) + return DBRegSet; // DBR + break; + case llvm::Triple::x86_64: + if (reg_num >= k_first_gpr_x86_64 && reg_num <= k_last_gpr_x86_64) + return GPRegSet; + if (reg_num >= k_first_fpr_x86_64 && reg_num <= k_last_fpr_x86_64) + return FPRegSet; + if (reg_num >= k_first_avx_x86_64 && reg_num <= k_last_avx_x86_64) + return YMMRegSet; + if (reg_num >= k_first_mpxr_x86_64 && reg_num <= k_last_mpxr_x86_64) + return std::nullopt; // MPXR + if (reg_num >= k_first_mpxc_x86_64 && reg_num <= k_last_mpxc_x86_64) + return std::nullopt; // MPXC + if (reg_num >= k_first_dbr_x86_64 && reg_num <= k_last_dbr_x86_64) + return DBRegSet; // DBR + break; + default: + llvm_unreachable("Unhandled target architecture."); + } + + llvm_unreachable("Register does not belong to any register set"); +} + +Status NativeRegisterContextFreeBSD_x86_64::ReadRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_GETREGS, m_thread.GetID(), + m_gpr.data()); + case FPRegSet: +#if defined(__x86_64__) + return NativeProcessFreeBSD::PtraceWrapper(PT_GETFPREGS, m_thread.GetID(), + m_fpr.data()); +#else + return NativeProcessFreeBSD::PtraceWrapper(PT_GETXMMREGS, m_thread.GetID(), + m_fpr.data()); +#endif + case DBRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_GETDBREGS, m_thread.GetID(), + m_dbr.data()); + case YMMRegSet: + case MPXRegSet: { + struct ptrace_xstate_info info; + Status ret = NativeProcessFreeBSD::PtraceWrapper( + PT_GETXSTATE_INFO, GetProcessPid(), &info, sizeof(info)); + if (!ret.Success()) + return ret; + + assert(info.xsave_mask & XFEATURE_ENABLED_X87); + assert(info.xsave_mask & XFEATURE_ENABLED_SSE); + + m_xsave_offsets[YMMRegSet] = LLDB_INVALID_XSAVE_OFFSET; + if (info.xsave_mask & XFEATURE_ENABLED_YMM_HI128) { + uint32_t eax, ecx, edx; + __get_cpuid_count(0x0D, 2, &eax, &m_xsave_offsets[YMMRegSet], &ecx, &edx); + } + + m_xsave.resize(info.xsave_len); + return NativeProcessFreeBSD::PtraceWrapper(PT_GETXSTATE, GetProcessPid(), + m_xsave.data(), m_xsave.size()); + } + } + llvm_unreachable("NativeRegisterContextFreeBSD_x86_64::ReadRegisterSet"); +} + +Status NativeRegisterContextFreeBSD_x86_64::WriteRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_SETREGS, m_thread.GetID(), + m_gpr.data()); + case FPRegSet: +#if defined(__x86_64__) + return NativeProcessFreeBSD::PtraceWrapper(PT_SETFPREGS, m_thread.GetID(), + m_fpr.data()); +#else + return NativeProcessFreeBSD::PtraceWrapper(PT_SETXMMREGS, m_thread.GetID(), + m_fpr.data()); +#endif + case DBRegSet: + return NativeProcessFreeBSD::PtraceWrapper(PT_SETDBREGS, m_thread.GetID(), + m_dbr.data()); + case YMMRegSet: + case MPXRegSet: + // ReadRegisterSet() must always be called before WriteRegisterSet(). + assert(m_xsave.size() > 0); + return NativeProcessFreeBSD::PtraceWrapper(PT_SETXSTATE, GetProcessPid(), + m_xsave.data(), m_xsave.size()); + } + llvm_unreachable("NativeRegisterContextFreeBSD_x86_64::WriteRegisterSet"); +} + +Status +NativeRegisterContextFreeBSD_x86_64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg); + if (!opt_set) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set", + reg_info->name); + return error; + } + + RegSetKind set = opt_set.value(); + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + switch (set) { + case GPRegSet: + case FPRegSet: + case DBRegSet: { + void *data = GetOffsetRegSetData(set, reg_info->byte_offset); + FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_fpr.data()); + if (data == &fpr->ftag) // ftag + reg_value.SetUInt16( + AbridgedToFullTagWord(fpr->ftag, fpr->fstat, fpr->stmm)); + else + reg_value.SetBytes(data, reg_info->byte_size, endian::InlHostByteOrder()); + break; + } + case YMMRegSet: { + std::optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg); + if (!ymm_reg) { + error.SetErrorStringWithFormat( + "register \"%s\" not supported by CPU/kernel", reg_info->name); + } else { + YMMReg ymm = XStateToYMM(ymm_reg->xmm, ymm_reg->ymm_hi); + reg_value.SetBytes(ymm.bytes, reg_info->byte_size, + endian::InlHostByteOrder()); + } + break; + } + case MPXRegSet: + llvm_unreachable("MPX regset should have returned error"); + } + + return error; +} + +Status NativeRegisterContextFreeBSD_x86_64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + std::optional<RegSetKind> opt_set = GetSetForNativeRegNum(reg); + if (!opt_set) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is in unrecognized set", + reg_info->name); + return error; + } + + RegSetKind set = opt_set.value(); + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + switch (set) { + case GPRegSet: + case FPRegSet: + case DBRegSet: { + void *data = GetOffsetRegSetData(set, reg_info->byte_offset); + FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_fpr.data()); + if (data == &fpr->ftag) // ftag + fpr->ftag = FullToAbridgedTagWord(reg_value.GetAsUInt16()); + else + ::memcpy(data, reg_value.GetBytes(), reg_value.GetByteSize()); + break; + } + case YMMRegSet: { + std::optional<YMMSplitPtr> ymm_reg = GetYMMSplitReg(reg); + if (!ymm_reg) { + error.SetErrorStringWithFormat( + "register \"%s\" not supported by CPU/kernel", reg_info->name); + } else { + YMMReg ymm; + ::memcpy(ymm.bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + YMMToXState(ymm, ymm_reg->xmm, ymm_reg->ymm_hi); + } + break; + } + case MPXRegSet: + llvm_unreachable("MPX regset should have returned error"); + } + + return WriteRegisterSet(set); +} + +Status NativeRegisterContextFreeBSD_x86_64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + Status error; + + data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0)); + error = ReadRegisterSet(GPRegSet); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, m_gpr.data(), GetRegisterInfoInterface().GetGPRSize()); + dst += GetRegisterInfoInterface().GetGPRSize(); + + return error; +} + +Status NativeRegisterContextFreeBSD_x86_64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_x86_64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { + error.SetErrorStringWithFormat( + "NativeRegisterContextFreeBSD_x86_64::%s data_sp contained mismatched " + "data size, expected %zu, actual %" PRIu64, + __FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize()); + return error; + } + + const uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + error.SetErrorStringWithFormat("NativeRegisterContextFreeBSD_x86_64::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + return error; + } + ::memcpy(m_gpr.data(), src, GetRegisterInfoInterface().GetGPRSize()); + + error = WriteRegisterSet(GPRegSet); + if (error.Fail()) + return error; + src += GetRegisterInfoInterface().GetGPRSize(); + + return error; +} + +llvm::Error NativeRegisterContextFreeBSD_x86_64::CopyHardwareWatchpointsFrom( + NativeRegisterContextFreeBSD &source) { + auto &r_source = static_cast<NativeRegisterContextFreeBSD_x86_64 &>(source); + // NB: This implicitly reads the whole dbreg set. + RegisterValue dr7; + Status res = r_source.ReadRegister(GetDR(7), dr7); + if (!res.Fail()) { + // copy dbregs only if any watchpoints were set + if ((dr7.GetAsUInt64() & 0xFF) == 0) + return llvm::Error::success(); + + m_dbr = r_source.m_dbr; + res = WriteRegisterSet(DBRegSet); + } + return res.ToError(); +} + +uint8_t * +NativeRegisterContextFreeBSD_x86_64::GetOffsetRegSetData(RegSetKind set, + size_t reg_offset) { + uint8_t *base; + switch (set) { + case GPRegSet: + base = m_gpr.data(); + break; + case FPRegSet: + base = m_fpr.data(); + break; + case DBRegSet: + base = m_dbr.data(); + break; + case YMMRegSet: + llvm_unreachable("GetRegSetData() is unsuitable for this regset."); + case MPXRegSet: + llvm_unreachable("MPX regset should have returned error"); + } + assert(reg_offset >= m_regset_offsets[set]); + return base + (reg_offset - m_regset_offsets[set]); +} + +std::optional<NativeRegisterContextFreeBSD_x86_64::YMMSplitPtr> +NativeRegisterContextFreeBSD_x86_64::GetYMMSplitReg(uint32_t reg) { + uint32_t offset = m_xsave_offsets[YMMRegSet]; + if (offset == LLDB_INVALID_XSAVE_OFFSET) + return std::nullopt; + + uint32_t reg_index; + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + reg_index = reg - lldb_ymm0_i386; + break; + case llvm::Triple::x86_64: + reg_index = reg - lldb_ymm0_x86_64; + break; + default: + llvm_unreachable("Unhandled target architecture."); + } + + auto *fpreg = reinterpret_cast<struct savexmm_ymm *>(m_xsave.data()); + auto *ymmreg = reinterpret_cast<struct ymmacc *>(m_xsave.data() + offset); + + return YMMSplitPtr{&fpreg->sv_xmm[reg_index], &ymmreg[reg_index]}; +} + +#endif // defined(__x86_64__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_x86_64.h new file mode 100644 index 000000000000..a522d850cb37 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD_x86_64.h @@ -0,0 +1,97 @@ +//===-- NativeRegisterContextFreeBSD_x86_64.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 +// +//===----------------------------------------------------------------------===// + +#if defined(__i386__) || defined(__x86_64__) + +#ifndef lldb_NativeRegisterContextFreeBSD_x86_64_h +#define lldb_NativeRegisterContextFreeBSD_x86_64_h + +// clang-format off +#include <sys/param.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <machine/reg.h> +// clang-format on + +#include <array> +#include <optional> + +#include "Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h" +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/NativeRegisterContextDBReg_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#define LLDB_INVALID_XSAVE_OFFSET UINT32_MAX + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeRegisterContextFreeBSD_x86_64 + : public NativeRegisterContextFreeBSD, + public NativeRegisterContextDBReg_x86 { +public: + NativeRegisterContextFreeBSD_x86_64(const ArchSpec &target_arch, + NativeThreadFreeBSD &native_thread); + uint32_t GetRegisterSetCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + llvm::Error + CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override; + +private: + // Private member types. + enum RegSetKind { + GPRegSet, + FPRegSet, + DBRegSet, + YMMRegSet, + MPXRegSet, + MaxRegSet = MPXRegSet, + }; + + // Private member variables. + std::array<uint8_t, sizeof(struct reg)> m_gpr; + std::array<uint8_t, 512> m_fpr; // FXSAVE + std::array<uint8_t, sizeof(struct dbreg)> m_dbr; + std::vector<uint8_t> m_xsave; + std::array<uint32_t, MaxRegSet + 1> m_xsave_offsets; + std::array<size_t, MaxRegSet + 1> m_regset_offsets; + + std::optional<RegSetKind> GetSetForNativeRegNum(uint32_t reg_num) const; + + Status ReadRegisterSet(RegSetKind set); + Status WriteRegisterSet(RegSetKind set); + + uint8_t *GetOffsetRegSetData(RegSetKind set, size_t reg_offset); + + struct YMMSplitPtr { + void *xmm; + void *ymm_hi; + }; + std::optional<YMMSplitPtr> GetYMMSplitReg(uint32_t reg); +}; + +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextFreeBSD_x86_64_h + +#endif // defined(__x86_64__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp new file mode 100644 index 000000000000..a0de7751c7e5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp @@ -0,0 +1,345 @@ +//===-- NativeThreadFreeBSD.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 "NativeThreadFreeBSD.h" +#include "NativeRegisterContextFreeBSD.h" + +#include "NativeProcessFreeBSD.h" + +#include "Plugins/Process/POSIX/CrashReason.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/State.h" +#include "llvm/Support/Errno.h" + +// clang-format off +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/sysctl.h> +#include <sys/user.h> +// clang-format on + +#include <sstream> +#include <vector> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_freebsd; + +NativeThreadFreeBSD::NativeThreadFreeBSD(NativeProcessFreeBSD &process, + lldb::tid_t tid) + : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), + m_stop_info(), + m_reg_context_up( + NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD( + process.GetArchitecture(), *this)), + m_stop_description() {} + +Status NativeThreadFreeBSD::Resume() { + Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, GetID()); + if (!ret.Success()) + return ret; + ret = NativeProcessFreeBSD::PtraceWrapper(PT_CLEARSTEP, GetID()); + // we can get EINVAL if the architecture in question does not support + // hardware single-stepping -- that's fine, we have nothing to clear + // then + if (ret.GetError() == EINVAL) + ret.Clear(); + if (ret.Success()) + SetRunning(); + return ret; +} + +Status NativeThreadFreeBSD::SingleStep() { + Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, GetID()); + if (!ret.Success()) + return ret; + ret = NativeProcessFreeBSD::PtraceWrapper(PT_SETSTEP, GetID()); + if (ret.Success()) + SetStepping(); + return ret; +} + +Status NativeThreadFreeBSD::Suspend() { + Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_SUSPEND, GetID()); + if (ret.Success()) + SetStopped(); + return ret; +} + +void NativeThreadFreeBSD::SetStoppedBySignal(uint32_t signo, + const siginfo_t *info) { + Log *log = GetLog(POSIXLog::Thread); + LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo); + + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonSignal; + m_stop_info.signo = signo; + + m_stop_description.clear(); + if (info) { + switch (signo) { + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + m_stop_description = GetCrashReasonString(*info); + break; + } + } +} + +void NativeThreadFreeBSD::SetStoppedByBreakpoint() { + SetStopped(); + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadFreeBSD::SetStoppedByTrace() { + SetStopped(); + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadFreeBSD::SetStoppedByExec() { + SetStopped(); + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadFreeBSD::SetStoppedByWatchpoint(uint32_t wp_index) { + lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); + + std::ostringstream ostr; + ostr << GetRegisterContext().GetWatchpointAddress(wp_index) << " "; + ostr << wp_index; + + ostr << " " << GetRegisterContext().GetWatchpointHitAddress(wp_index); + + SetStopped(); + m_stop_description = ostr.str(); + m_stop_info.reason = StopReason::eStopReasonWatchpoint; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadFreeBSD::SetStoppedByFork(lldb::pid_t child_pid, + lldb::tid_t child_tid) { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonFork; + m_stop_info.signo = SIGTRAP; + m_stop_info.details.fork.child_pid = child_pid; + m_stop_info.details.fork.child_tid = child_tid; +} + +void NativeThreadFreeBSD::SetStoppedByVFork(lldb::pid_t child_pid, + lldb::tid_t child_tid) { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonVFork; + m_stop_info.signo = SIGTRAP; + m_stop_info.details.fork.child_pid = child_pid; + m_stop_info.details.fork.child_tid = child_tid; +} + +void NativeThreadFreeBSD::SetStoppedByVForkDone() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonVForkDone; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadFreeBSD::SetStoppedWithNoReason() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_info.signo = 0; +} + +void NativeThreadFreeBSD::SetStopped() { + const StateType new_state = StateType::eStateStopped; + m_state = new_state; + m_stop_description.clear(); +} + +void NativeThreadFreeBSD::SetRunning() { + m_state = StateType::eStateRunning; + m_stop_info.reason = StopReason::eStopReasonNone; +} + +void NativeThreadFreeBSD::SetStepping() { + m_state = StateType::eStateStepping; + m_stop_info.reason = StopReason::eStopReasonNone; +} + +std::string NativeThreadFreeBSD::GetName() { + Log *log = GetLog(POSIXLog::Thread); + + std::vector<struct kinfo_proc> kp; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, + static_cast<int>(GetProcess().GetID())}; + + while (1) { + size_t len = kp.size() * sizeof(struct kinfo_proc); + void *ptr = len == 0 ? nullptr : kp.data(); + int error = ::sysctl(mib, 4, ptr, &len, nullptr, 0); + if (ptr == nullptr || (error != 0 && errno == ENOMEM)) { + kp.resize(len / sizeof(struct kinfo_proc)); + continue; + } + if (error != 0) { + len = 0; + LLDB_LOG(log, "tid = {0} in state {1} failed to get thread name: {2}", + GetID(), m_state, strerror(errno)); + } + kp.resize(len / sizeof(struct kinfo_proc)); + break; + } + + for (auto &procinfo : kp) { + if (procinfo.ki_tid == static_cast<lwpid_t>(GetID())) + return procinfo.ki_tdname; + } + + return ""; +} + +lldb::StateType NativeThreadFreeBSD::GetState() { return m_state; } + +bool NativeThreadFreeBSD::GetStopReason(ThreadStopInfo &stop_info, + std::string &description) { + Log *log = GetLog(POSIXLog::Thread); + description.clear(); + + switch (m_state) { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + stop_info = m_stop_info; + description = m_stop_description; + + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + LLDB_LOG(log, "tid = {0} in state {1} cannot answer stop reason", GetID(), + StateAsCString(m_state)); + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +NativeRegisterContextFreeBSD &NativeThreadFreeBSD::GetRegisterContext() { + assert(m_reg_context_up); + return *m_reg_context_up; +} + +Status NativeThreadFreeBSD::SetWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags, bool hardware) { + assert(m_state == eStateStopped); + if (!hardware) + return Status("not implemented"); + Status error = RemoveWatchpoint(addr); + if (error.Fail()) + return error; + uint32_t wp_index = + GetRegisterContext().SetHardwareWatchpoint(addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Status("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Status(); +} + +Status NativeThreadFreeBSD::RemoveWatchpoint(lldb::addr_t addr) { + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Status(); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (GetRegisterContext().ClearHardwareWatchpoint(wp_index)) + return Status(); + return Status("Clearing hardware watchpoint failed."); +} + +Status NativeThreadFreeBSD::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + assert(m_state == eStateStopped); + Status error = RemoveHardwareBreakpoint(addr); + if (error.Fail()) + return error; + + uint32_t bp_index = GetRegisterContext().SetHardwareBreakpoint(addr, size); + + if (bp_index == LLDB_INVALID_INDEX32) + return Status("Setting hardware breakpoint failed."); + + m_hw_break_index_map.insert({addr, bp_index}); + return Status(); +} + +Status NativeThreadFreeBSD::RemoveHardwareBreakpoint(lldb::addr_t addr) { + auto bp = m_hw_break_index_map.find(addr); + if (bp == m_hw_break_index_map.end()) + return Status(); + + uint32_t bp_index = bp->second; + if (GetRegisterContext().ClearHardwareBreakpoint(bp_index)) { + m_hw_break_index_map.erase(bp); + return Status(); + } + + return Status("Clearing hardware breakpoint failed."); +} + +llvm::Error +NativeThreadFreeBSD::CopyWatchpointsFrom(NativeThreadFreeBSD &source) { + llvm::Error s = GetRegisterContext().CopyHardwareWatchpointsFrom( + source.GetRegisterContext()); + if (!s) { + m_watchpoint_index_map = source.m_watchpoint_index_map; + m_hw_break_index_map = source.m_hw_break_index_map; + } + return s; +} + +NativeProcessFreeBSD &NativeThreadFreeBSD::GetProcess() { + return static_cast<NativeProcessFreeBSD &>(m_process); +} + +llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> +NativeThreadFreeBSD::GetSiginfo() const { + Log *log = GetLog(POSIXLog::Process); + + struct ptrace_lwpinfo info; + const auto siginfo_err = NativeProcessFreeBSD::PtraceWrapper( + PT_LWPINFO, GetID(), &info, sizeof(info)); + if (siginfo_err.Fail()) { + LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); + return siginfo_err.ToError(); + } + + if (info.pl_event != PL_EVENT_SIGNAL) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Thread not signaled"); + if (!(info.pl_flags & PL_FLAG_SI)) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "No siginfo for thread"); + + return llvm::MemoryBuffer::getMemBufferCopy( + llvm::StringRef(reinterpret_cast<const char *>(&info.pl_siginfo), + sizeof(info.pl_siginfo))); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h new file mode 100644 index 000000000000..edfb07658e19 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h @@ -0,0 +1,91 @@ +//===-- NativeThreadFreeBSD.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 liblldb_NativeThreadFreeBSD_H_ +#define liblldb_NativeThreadFreeBSD_H_ + +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include "Plugins/Process/FreeBSD/NativeRegisterContextFreeBSD.h" + +#include <csignal> +#include <map> +#include <string> + +namespace lldb_private { +namespace process_freebsd { + +class NativeProcessFreeBSD; + +class NativeThreadFreeBSD : public NativeThreadProtocol { + friend class NativeProcessFreeBSD; + +public: + NativeThreadFreeBSD(NativeProcessFreeBSD &process, lldb::tid_t tid); + + // NativeThreadProtocol Interface + std::string GetName() override; + + lldb::StateType GetState() override; + + bool GetStopReason(ThreadStopInfo &stop_info, + std::string &description) override; + + NativeRegisterContextFreeBSD &GetRegisterContext() override; + + Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, + bool hardware) override; + + Status RemoveWatchpoint(lldb::addr_t addr) override; + + Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; + + NativeProcessFreeBSD &GetProcess(); + + llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> + GetSiginfo() const override; + +private: + // Interface for friend classes + + Status Resume(); + Status SingleStep(); + Status Suspend(); + + void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); + void SetStoppedByBreakpoint(); + void SetStoppedByTrace(); + void SetStoppedByExec(); + void SetStoppedByWatchpoint(uint32_t wp_index); + void SetStoppedByFork(lldb::pid_t child_pid, lldb::tid_t child_tid); + void SetStoppedByVFork(lldb::pid_t child_pid, lldb::tid_t child_tid); + void SetStoppedByVForkDone(); + void SetStoppedWithNoReason(); + void SetStopped(); + void SetRunning(); + void SetStepping(); + + llvm::Error CopyWatchpointsFrom(NativeThreadFreeBSD &source); + + // Member Variables + lldb::StateType m_state; + ThreadStopInfo m_stop_info; + std::unique_ptr<NativeRegisterContextFreeBSD> m_reg_context_up; + std::string m_stop_description; + using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>; + WatchpointIndexMap m_watchpoint_index_map; + WatchpointIndexMap m_hw_break_index_map; +}; + +typedef std::shared_ptr<NativeThreadFreeBSD> NativeThreadFreeBSDSP; +} // namespace process_freebsd +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadFreeBSD_H_ |