diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Process')
261 files changed, 67313 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_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp new file mode 100644 index 000000000000..635f99b4467d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp @@ -0,0 +1,335 @@ +//===-- ProcessFreeBSDKernel.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/DynamicLoader.h" + +#include "Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h" +#include "ProcessFreeBSDKernel.h" +#include "ThreadFreeBSDKernel.h" + +#if LLDB_ENABLE_FBSDVMCORE +#include <fvc.h> +#endif +#if defined(__FreeBSD__) +#include <kvm.h> +#endif + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel) + +namespace { + +#if LLDB_ENABLE_FBSDVMCORE +class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel { +public: + ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, lldb::ListenerSP listener, + fvc_t *fvc, const FileSpec &core_file); + + ~ProcessFreeBSDKernelFVC(); + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + +private: + fvc_t *m_fvc; + + const char *GetError(); +}; +#endif // LLDB_ENABLE_FBSDVMCORE + +#if defined(__FreeBSD__) +class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel { +public: + ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener, + kvm_t *fvc, const FileSpec &core_file); + + ~ProcessFreeBSDKernelKVM(); + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + +private: + kvm_t *m_kvm; + + const char *GetError(); +}; +#endif // defined(__FreeBSD__) + +} // namespace + +ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp, + ListenerSP listener_sp, + const FileSpec &core_file) + : PostMortemProcess(target_sp, listener_sp, core_file) {} + +lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp, + ListenerSP listener_sp, + const FileSpec *crash_file, + bool can_connect) { + ModuleSP executable = target_sp->GetExecutableModule(); + if (crash_file && !can_connect && executable) { +#if LLDB_ENABLE_FBSDVMCORE + fvc_t *fvc = + fvc_open(executable->GetFileSpec().GetPath().c_str(), + crash_file->GetPath().c_str(), nullptr, nullptr, nullptr); + if (fvc) + return std::make_shared<ProcessFreeBSDKernelFVC>(target_sp, listener_sp, + fvc, *crash_file); +#endif + +#if defined(__FreeBSD__) + kvm_t *kvm = + kvm_open2(executable->GetFileSpec().GetPath().c_str(), + crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr); + if (kvm) + return std::make_shared<ProcessFreeBSDKernelKVM>(target_sp, listener_sp, + kvm, *crash_file); +#endif + } + return nullptr; +} + +void ProcessFreeBSDKernel::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); + }); +} + +void ProcessFreeBSDKernel::Terminate() { + PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance); +} + +Status ProcessFreeBSDKernel::DoDestroy() { return Status(); } + +bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + return true; +} + +void ProcessFreeBSDKernel::RefreshStateAfterStop() {} + +bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) { + if (old_thread_list.GetSize(false) == 0) { + // Make up the thread the first time this is called so we can set our one + // and only core thread state up. + + // We cannot construct a thread without a register context as that crashes + // LLDB but we can construct a process without threads to provide minimal + // memory reading support. + switch (GetTarget().GetArchitecture().GetMachine()) { + case llvm::Triple::aarch64: + case llvm::Triple::x86: + case llvm::Triple::x86_64: + break; + default: + return false; + } + + Status error; + + // struct field offsets are written as symbols so that we don't have + // to figure them out ourselves + int32_t offset_p_list = ReadSignedIntegerFromMemory( + FindSymbol("proc_off_p_list"), 4, -1, error); + int32_t offset_p_pid = + ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error); + int32_t offset_p_threads = ReadSignedIntegerFromMemory( + FindSymbol("proc_off_p_threads"), 4, -1, error); + int32_t offset_p_comm = ReadSignedIntegerFromMemory( + FindSymbol("proc_off_p_comm"), 4, -1, error); + + int32_t offset_td_tid = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_tid"), 4, -1, error); + int32_t offset_td_plist = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_plist"), 4, -1, error); + int32_t offset_td_pcb = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_pcb"), 4, -1, error); + int32_t offset_td_oncpu = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_oncpu"), 4, -1, error); + int32_t offset_td_name = ReadSignedIntegerFromMemory( + FindSymbol("thread_off_td_name"), 4, -1, error); + + // fail if we were not able to read any of the offsets + if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 || + offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 || + offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1) + return false; + + // dumptid contains the thread-id of the crashing thread + // dumppcb contains its PCB + int32_t dumptid = + ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error); + lldb::addr_t dumppcb = FindSymbol("dumppcb"); + + // stoppcbs is an array of PCBs on all CPUs + // each element is of size pcb_size + int32_t pcbsize = + ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error); + lldb::addr_t stoppcbs = FindSymbol("stoppcbs"); + // In later FreeBSD versions stoppcbs is a pointer to the array. + int32_t osreldate = + ReadSignedIntegerFromMemory(FindSymbol("osreldate"), 4, -1, error); + if (stoppcbs != LLDB_INVALID_ADDRESS && osreldate >= 1400089) + stoppcbs = ReadPointerFromMemory(stoppcbs, error); + + // from FreeBSD sys/param.h + constexpr size_t fbsd_maxcomlen = 19; + + // iterate through a linked list of all processes + // allproc is a pointer to the first list element, p_list field + // (found at offset_p_list) specifies the next element + for (lldb::addr_t proc = + ReadPointerFromMemory(FindSymbol("allproc"), error); + proc != 0 && proc != LLDB_INVALID_ADDRESS; + proc = ReadPointerFromMemory(proc + offset_p_list, error)) { + int32_t pid = + ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error); + // process' command-line string + char comm[fbsd_maxcomlen + 1]; + ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error); + + // iterate through a linked list of all process' threads + // the initial thread is found in process' p_threads, subsequent + // elements are linked via td_plist field + for (lldb::addr_t td = + ReadPointerFromMemory(proc + offset_p_threads, error); + td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) { + int32_t tid = + ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error); + lldb::addr_t pcb_addr = + ReadPointerFromMemory(td + offset_td_pcb, error); + // whether process was on CPU (-1 if not, otherwise CPU number) + int32_t oncpu = + ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error); + // thread name + char thread_name[fbsd_maxcomlen + 1]; + ReadCStringFromMemory(td + offset_td_name, thread_name, + sizeof(thread_name), error); + + // if we failed to read TID, ignore this thread + if (tid == -1) + continue; + + std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm); + if (*thread_name && strcmp(thread_name, comm)) { + thread_desc += '/'; + thread_desc += thread_name; + } + + // roughly: + // 1. if the thread crashed, its PCB is going to be at "dumppcb" + // 2. if the thread was on CPU, its PCB is going to be on the CPU + // 3. otherwise, its PCB is in the thread struct + if (tid == dumptid) { + // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed + pcb_addr = dumppcb; + thread_desc += " (crashed)"; + } else if (oncpu != -1) { + // if we managed to read stoppcbs and pcb_size, use them to find + // the correct PCB + if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0) + pcb_addr = stoppcbs + oncpu * pcbsize; + else + pcb_addr = LLDB_INVALID_ADDRESS; + thread_desc += llvm::formatv(" (on CPU {0})", oncpu); + } + + ThreadSP thread_sp{ + new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)}; + new_thread_list.AddThread(thread_sp); + } + } + } else { + const uint32_t num_threads = old_thread_list.GetSize(false); + for (uint32_t i = 0; i < num_threads; ++i) + new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false)); + } + return new_thread_list.GetSize(false) > 0; +} + +Status ProcessFreeBSDKernel::DoLoadCore() { + // The core is already loaded by CreateInstance(). + return Status(); +} + +DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() { + if (m_dyld_up.get() == nullptr) + m_dyld_up.reset(DynamicLoader::FindPlugin( + this, DynamicLoaderFreeBSDKernel::GetPluginNameStatic())); + return m_dyld_up.get(); +} + +lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) { + ModuleSP mod_sp = GetTarget().GetExecutableModule(); + const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name)); + return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS; +} + +#if LLDB_ENABLE_FBSDVMCORE + +ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, + ListenerSP listener_sp, + fvc_t *fvc, + const FileSpec &core_file) + : ProcessFreeBSDKernel(target_sp, listener_sp, crash_file), m_fvc(fvc) {} + +ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() { + if (m_fvc) + fvc_close(m_fvc); +} + +size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf, + size_t size, Status &error) { + ssize_t rd = 0; + rd = fvc_read(m_fvc, addr, buf, size); + if (rd < 0 || static_cast<size_t>(rd) != size) { + error.SetErrorStringWithFormat("Reading memory failed: %s", GetError()); + return rd > 0 ? rd : 0; + } + return rd; +} + +const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); } + +#endif // LLDB_ENABLE_FBSDVMCORE + +#if defined(__FreeBSD__) + +ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, + ListenerSP listener_sp, + kvm_t *fvc, + const FileSpec &core_file) + : ProcessFreeBSDKernel(target_sp, listener_sp, core_file), m_kvm(fvc) {} + +ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() { + if (m_kvm) + kvm_close(m_kvm); +} + +size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf, + size_t size, Status &error) { + ssize_t rd = 0; + rd = kvm_read2(m_kvm, addr, buf, size); + if (rd < 0 || static_cast<size_t>(rd) != size) { + error.SetErrorStringWithFormat("Reading memory failed: %s", GetError()); + return rd > 0 ? rd : 0; + } + return rd; +} + +const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); } + +#endif // defined(__FreeBSD__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h new file mode 100644 index 000000000000..06c9d062441e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.h @@ -0,0 +1,54 @@ +//===-- ProcessFreeBSDKernel.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_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_PROCESSFREEBSDKERNEL_H +#define LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_PROCESSFREEBSDKERNEL_H + +#include "lldb/Target/PostMortemProcess.h" + +class ProcessFreeBSDKernel : public lldb_private::PostMortemProcess { +public: + ProcessFreeBSDKernel(lldb::TargetSP target_sp, lldb::ListenerSP listener, + const lldb_private::FileSpec &core_file); + + static lldb::ProcessSP + CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener, + const lldb_private::FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "freebsd-kernel"; } + + static llvm::StringRef GetPluginDescriptionStatic() { + return "FreeBSD kernel vmcore debugging plug-in."; + } + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + lldb_private::Status DoDestroy() override; + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + void RefreshStateAfterStop() override; + + lldb_private::Status DoLoadCore() override; + + lldb_private::DynamicLoader *GetDynamicLoader() override; + +protected: + bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + lldb::addr_t FindSymbol(const char* name); +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_PROCESSFREEBSDKERNEL_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_arm64.cpp new file mode 100644 index 000000000000..11843ddc82d9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_arm64.cpp @@ -0,0 +1,110 @@ +//===-- RegisterContextFreeBSDKernel_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextFreeBSDKernel_arm64.h" +#include "Plugins/Process/Utility/lldb-arm64-register-enums.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/Support/Endian.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextFreeBSDKernel_arm64::RegisterContextFreeBSDKernel_arm64( + Thread &thread, std::unique_ptr<RegisterInfoPOSIX_arm64> register_info_up, + lldb::addr_t pcb_addr) + : RegisterContextPOSIX_arm64(thread, std::move(register_info_up)), + m_pcb_addr(pcb_addr) {} + +bool RegisterContextFreeBSDKernel_arm64::ReadGPR() { return true; } + +bool RegisterContextFreeBSDKernel_arm64::ReadFPR() { return true; } + +bool RegisterContextFreeBSDKernel_arm64::WriteGPR() { + assert(0); + return false; +} + +bool RegisterContextFreeBSDKernel_arm64::WriteFPR() { + assert(0); + return false; +} + +bool RegisterContextFreeBSDKernel_arm64::ReadRegister( + const RegisterInfo *reg_info, RegisterValue &value) { + if (m_pcb_addr == LLDB_INVALID_ADDRESS) + return false; + + struct { + llvm::support::ulittle64_t x[30]; + llvm::support::ulittle64_t lr; + llvm::support::ulittle64_t _reserved; + llvm::support::ulittle64_t sp; + } pcb; + + Status error; + size_t rd = + m_thread.GetProcess()->ReadMemory(m_pcb_addr, &pcb, sizeof(pcb), error); + if (rd != sizeof(pcb)) + return false; + + uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + switch (reg) { + case gpr_x0_arm64: + case gpr_x1_arm64: + case gpr_x2_arm64: + case gpr_x3_arm64: + case gpr_x4_arm64: + case gpr_x5_arm64: + case gpr_x6_arm64: + case gpr_x7_arm64: + case gpr_x8_arm64: + case gpr_x9_arm64: + case gpr_x10_arm64: + case gpr_x11_arm64: + case gpr_x12_arm64: + case gpr_x13_arm64: + case gpr_x14_arm64: + case gpr_x15_arm64: + case gpr_x16_arm64: + case gpr_x17_arm64: + case gpr_x18_arm64: + case gpr_x19_arm64: + case gpr_x20_arm64: + case gpr_x21_arm64: + case gpr_x22_arm64: + case gpr_x23_arm64: + case gpr_x24_arm64: + case gpr_x25_arm64: + case gpr_x26_arm64: + case gpr_x27_arm64: + case gpr_x28_arm64: + case gpr_fp_arm64: + static_assert(gpr_fp_arm64 - gpr_x0_arm64 == 29, + "nonconsecutive arm64 register numbers"); + value = pcb.x[reg - gpr_x0_arm64]; + break; + case gpr_sp_arm64: + value = pcb.sp; + break; + case gpr_pc_arm64: + // The pc of crashing thread is stored in lr. + value = pcb.lr; + break; + default: + return false; + } + return true; +} + +bool RegisterContextFreeBSDKernel_arm64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_arm64.h new file mode 100644 index 000000000000..155dda6e748f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_arm64.h @@ -0,0 +1,41 @@ +//===-- RegisterContextFreeBSDKernel_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_REGISTERCONTEXTFREEBSDKERNEL_ARM64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_REGISTERCONTEXTFREEBSDKERNEL_ARM64_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +class RegisterContextFreeBSDKernel_arm64 : public RegisterContextPOSIX_arm64 { +public: + RegisterContextFreeBSDKernel_arm64( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_arm64> register_info_up, + lldb::addr_t pcb_addr); + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + +protected: + bool ReadGPR() override; + + bool ReadFPR() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + +private: + lldb::addr_t m_pcb_addr; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_REGISTERCONTEXTFREEBSDKERNEL_ARM64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_i386.cpp new file mode 100644 index 000000000000..fde85c9c9f0d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_i386.cpp @@ -0,0 +1,83 @@ +//===-- RegisterContextFreeBSDKernel_i386.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 "RegisterContextFreeBSDKernel_i386.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/Support/Endian.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextFreeBSDKernel_i386::RegisterContextFreeBSDKernel_i386( + Thread &thread, RegisterInfoInterface *register_info, lldb::addr_t pcb_addr) + : RegisterContextPOSIX_x86(thread, 0, register_info), m_pcb_addr(pcb_addr) { +} + +bool RegisterContextFreeBSDKernel_i386::ReadGPR() { return true; } + +bool RegisterContextFreeBSDKernel_i386::ReadFPR() { return true; } + +bool RegisterContextFreeBSDKernel_i386::WriteGPR() { + assert(0); + return false; +} + +bool RegisterContextFreeBSDKernel_i386::WriteFPR() { + assert(0); + return false; +} + +bool RegisterContextFreeBSDKernel_i386::ReadRegister( + const RegisterInfo *reg_info, RegisterValue &value) { + if (m_pcb_addr == LLDB_INVALID_ADDRESS) + return false; + + struct { + llvm::support::ulittle32_t edi; + llvm::support::ulittle32_t esi; + llvm::support::ulittle32_t ebp; + llvm::support::ulittle32_t esp; + llvm::support::ulittle32_t ebx; + llvm::support::ulittle32_t eip; + } pcb; + + Status error; + size_t rd = + m_thread.GetProcess()->ReadMemory(m_pcb_addr, &pcb, sizeof(pcb), error); + if (rd != sizeof(pcb)) + return false; + + uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + switch (reg) { +#define REG(x) \ + case lldb_##x##_i386: \ + value = pcb.x; \ + break; + + REG(edi); + REG(esi); + REG(ebp); + REG(esp); + REG(eip); + +#undef REG + + default: + return false; + } + + return true; +} + +bool RegisterContextFreeBSDKernel_i386::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_i386.h new file mode 100644 index 000000000000..218e3374f8df --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_i386.h @@ -0,0 +1,41 @@ +//===-- RegisterContextFreeBSDKernel_i386.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_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_REGISTERCONTEXTFREEBSDKERNEL_I386_H +#define LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_REGISTERCONTEXTFREEBSDKERNEL_I386_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_x86.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +class RegisterContextFreeBSDKernel_i386 : public RegisterContextPOSIX_x86 { +public: + RegisterContextFreeBSDKernel_i386( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + lldb::addr_t pcb_addr); + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + +protected: + bool ReadGPR() override; + + bool ReadFPR() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + +private: + lldb::addr_t m_pcb_addr; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_REGISTERCONTEXTFREEBSDKERNEL_I386_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_x86_64.cpp new file mode 100644 index 000000000000..ff57842e345c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_x86_64.cpp @@ -0,0 +1,88 @@ +//===-- RegisterContextFreeBSDKernel_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextFreeBSDKernel_x86_64.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/Support/Endian.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextFreeBSDKernel_x86_64::RegisterContextFreeBSDKernel_x86_64( + Thread &thread, RegisterInfoInterface *register_info, lldb::addr_t pcb_addr) + : RegisterContextPOSIX_x86(thread, 0, register_info), m_pcb_addr(pcb_addr) { +} + +bool RegisterContextFreeBSDKernel_x86_64::ReadGPR() { return true; } + +bool RegisterContextFreeBSDKernel_x86_64::ReadFPR() { return true; } + +bool RegisterContextFreeBSDKernel_x86_64::WriteGPR() { + assert(0); + return false; +} + +bool RegisterContextFreeBSDKernel_x86_64::WriteFPR() { + assert(0); + return false; +} + +bool RegisterContextFreeBSDKernel_x86_64::ReadRegister( + const RegisterInfo *reg_info, RegisterValue &value) { + if (m_pcb_addr == LLDB_INVALID_ADDRESS) + return false; + + struct { + llvm::support::ulittle64_t r15; + llvm::support::ulittle64_t r14; + llvm::support::ulittle64_t r13; + llvm::support::ulittle64_t r12; + llvm::support::ulittle64_t rbp; + llvm::support::ulittle64_t rsp; + llvm::support::ulittle64_t rbx; + llvm::support::ulittle64_t rip; + } pcb; + + Status error; + size_t rd = + m_thread.GetProcess()->ReadMemory(m_pcb_addr, &pcb, sizeof(pcb), error); + if (rd != sizeof(pcb)) + return false; + + uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + switch (reg) { +#define REG(x) \ + case lldb_##x##_x86_64: \ + value = pcb.x; \ + break; + + REG(r15); + REG(r14); + REG(r13); + REG(r12); + REG(rbp); + REG(rsp); + REG(rbx); + REG(rip); + +#undef REG + + default: + return false; + } + + return true; +} + +bool RegisterContextFreeBSDKernel_x86_64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_x86_64.h new file mode 100644 index 000000000000..9a2ac638dfea --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/RegisterContextFreeBSDKernel_x86_64.h @@ -0,0 +1,41 @@ +//===-- RegisterContextFreeBSDKernel_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_REGISTERCONTEXTFREEBSDKERNEL_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_REGISTERCONTEXTFREEBSDKERNEL_X86_64_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_x86.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +class RegisterContextFreeBSDKernel_x86_64 : public RegisterContextPOSIX_x86 { +public: + RegisterContextFreeBSDKernel_x86_64( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + lldb::addr_t pcb_addr); + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + +protected: + bool ReadGPR() override; + + bool ReadFPR() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + +private: + lldb::addr_t m_pcb_addr; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_REGISTERCONTEXTFREEBSDKERNEL_X86_64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.cpp new file mode 100644 index 000000000000..8d304086a163 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.cpp @@ -0,0 +1,86 @@ +//===-- ThreadFreeBSDKernel.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 "ThreadFreeBSDKernel.h" + +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/Log.h" + +#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" +#include "ProcessFreeBSDKernel.h" +#include "RegisterContextFreeBSDKernel_arm64.h" +#include "RegisterContextFreeBSDKernel_i386.h" +#include "RegisterContextFreeBSDKernel_x86_64.h" +#include "ThreadFreeBSDKernel.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadFreeBSDKernel::ThreadFreeBSDKernel(Process &process, lldb::tid_t tid, + lldb::addr_t pcb_addr, + std::string thread_name) + : Thread(process, tid), m_thread_name(std::move(thread_name)), + m_pcb_addr(pcb_addr) {} + +ThreadFreeBSDKernel::~ThreadFreeBSDKernel() {} + +void ThreadFreeBSDKernel::RefreshStateAfterStop() {} + +lldb::RegisterContextSP ThreadFreeBSDKernel::GetRegisterContext() { + if (!m_reg_context_sp) + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadFreeBSDKernel::CreateRegisterContextForFrame(StackFrame *frame) { + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) { + if (m_thread_reg_ctx_sp) + return m_thread_reg_ctx_sp; + + ProcessFreeBSDKernel *process = + static_cast<ProcessFreeBSDKernel *>(GetProcess().get()); + ArchSpec arch = process->GetTarget().GetArchitecture(); + + switch (arch.GetMachine()) { + case llvm::Triple::aarch64: + m_thread_reg_ctx_sp = + std::make_shared<RegisterContextFreeBSDKernel_arm64>( + *this, std::make_unique<RegisterInfoPOSIX_arm64>(arch, 0), + m_pcb_addr); + break; + case llvm::Triple::x86: + m_thread_reg_ctx_sp = std::make_shared<RegisterContextFreeBSDKernel_i386>( + *this, new RegisterContextFreeBSD_i386(arch), m_pcb_addr); + break; + case llvm::Triple::x86_64: + m_thread_reg_ctx_sp = + std::make_shared<RegisterContextFreeBSDKernel_x86_64>( + *this, new RegisterContextFreeBSD_x86_64(arch), m_pcb_addr); + break; + default: + assert(false && "Unsupported architecture passed to ThreadFreeBSDKernel"); + break; + } + + reg_ctx_sp = m_thread_reg_ctx_sp; + } else { + reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame); + } + return reg_ctx_sp; +} + +bool ThreadFreeBSDKernel::CalculateStopInfo() { return false; } diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.h b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.h new file mode 100644 index 000000000000..3bc019b63e68 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ThreadFreeBSDKernel.h @@ -0,0 +1,50 @@ +//===-- ThreadFreeBSDKernel.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_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_THREADFREEBSDKERNEL_H +#define LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_THREADFREEBSDKERNEL_H + +#include "lldb/Target/Thread.h" + +class ThreadFreeBSDKernel : public lldb_private::Thread { +public: + ThreadFreeBSDKernel(lldb_private::Process &process, lldb::tid_t tid, + lldb::addr_t pcb_addr, std::string thread_name); + + ~ThreadFreeBSDKernel() override; + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + const char *GetName() override { + if (m_thread_name.empty()) + return nullptr; + return m_thread_name.c_str(); + } + + void SetName(const char *name) override { + if (name && name[0]) + m_thread_name.assign(name); + else + m_thread_name.clear(); + } + +protected: + bool CalculateStopInfo() override; + +private: + std::string m_thread_name; + lldb::RegisterContextSP m_thread_reg_ctx_sp; + lldb::addr_t m_pcb_addr; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_THREADFREEBSDKERNEL_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp new file mode 100644 index 000000000000..451bb4866037 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp @@ -0,0 +1,1119 @@ +//===-- NativeProcessNetBSD.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 "NativeProcessNetBSD.h" + +#include "Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/posix/ProcessLauncherPosixFork.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/State.h" +#include "llvm/Support/Errno.h" + +// System includes - They have to be included after framework includes because +// they define some macros which collide with variable names in other modules +// clang-format off +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/sysctl.h> +#include <sys/wait.h> +#include <uvm/uvm_prot.h> +#include <elf.h> +#include <util.h> +// clang-format on + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_netbsd; +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; +} + +// Public Static Methods + +llvm::Expected<std::unique_ptr<NativeProcessProtocol>> +NativeProcessNetBSD::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); + 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); + (void)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<NativeProcessNetBSD> process_up(new NativeProcessNetBSD( + 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<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(SIGSTOP); + process_up->SetState(StateType::eStateStopped, false); + + return std::move(process_up); +} + +llvm::Expected<std::unique_ptr<NativeProcessProtocol>> +NativeProcessNetBSD::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<NativeProcessNetBSD> process_up(new NativeProcessNetBSD( + pid, -1, native_delegate, Info.GetArchitecture(), m_mainloop)); + + Status status = process_up->Attach(); + if (!status.Success()) + return status.ToError(); + + return std::move(process_up); +} + +NativeProcessNetBSD::Extension +NativeProcessNetBSD::Manager::GetSupportedExtensions() const { + return Extension::multiprocess | Extension::fork | Extension::vfork | + Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 | + Extension::savecore; +} + +// Public Instance Methods + +NativeProcessNetBSD::NativeProcessNetBSD(::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 NativeProcessNetBSD::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 NativeProcessNetBSD::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 NativeProcessNetBSD::MonitorSIGSTOP(lldb::pid_t pid) { + ptrace_siginfo_t info; + + const auto siginfo_err = + PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); + + // Get details on the signal raised. + if (siginfo_err.Success()) { + // Handle SIGSTOP from LLGS (LLDB GDB Server) + if (info.psi_siginfo.si_code == SI_USER && + info.psi_siginfo.si_pid == ::getpid()) { + /* Stop Tracking all Threads attached to Process */ + for (const auto &thread : m_threads) { + static_cast<NativeThreadNetBSD &>(*thread).SetStoppedBySignal( + SIGSTOP, &info.psi_siginfo); + } + } + SetState(StateType::eStateStopped, true); + } +} + +void NativeProcessNetBSD::MonitorSIGTRAP(lldb::pid_t pid) { + Log *log = GetLog(POSIXLog::Process); + ptrace_siginfo_t info; + + const auto siginfo_err = + PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); + + // Get details on the signal raised. + if (siginfo_err.Fail()) { + LLDB_LOG(log, "PT_GET_SIGINFO failed {0}", siginfo_err); + return; + } + + LLDB_LOG(log, "got SIGTRAP, pid = {0}, lwpid = {1}, si_code = {2}", pid, + info.psi_lwpid, info.psi_siginfo.si_code); + NativeThreadNetBSD *thread = nullptr; + + if (info.psi_lwpid > 0) { + for (const auto &t : m_threads) { + if (t->GetID() == static_cast<lldb::tid_t>(info.psi_lwpid)) { + thread = static_cast<NativeThreadNetBSD *>(t.get()); + break; + } + static_cast<NativeThreadNetBSD *>(t.get())->SetStoppedWithNoReason(); + } + if (!thread) + LLDB_LOG(log, "thread not found in m_threads, pid = {0}, LWP = {1}", pid, + info.psi_lwpid); + } + + switch (info.psi_siginfo.si_code) { + case TRAP_BRKPT: + if (thread) { + thread->SetStoppedByBreakpoint(); + FixupBreakpointPCAsNeeded(*thread); + } + SetState(StateType::eStateStopped, true); + return; + case TRAP_TRACE: + if (thread) + thread->SetStoppedByTrace(); + SetState(StateType::eStateStopped, true); + return; + case TRAP_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<NativeThreadNetBSD &>(*thread).SetStoppedByExec(); + SetState(StateType::eStateStopped, true); + return; + } + case TRAP_CHLD: { + ptrace_state_t pst; + Status error = PtraceWrapper(PT_GET_PROCESS_STATE, pid, &pst, sizeof(pst)); + if (error.Fail()) { + SetState(StateType::eStateInvalid); + return; + } + + assert(thread); + if (pst.pe_report_event == PTRACE_VFORK_DONE) { + 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); + } + } else { + assert(pst.pe_report_event == PTRACE_FORK || + pst.pe_report_event == PTRACE_VFORK); + MonitorClone(pst.pe_other_pid, pst.pe_report_event == PTRACE_VFORK, + *thread); + } + return; + } + case TRAP_LWP: { + ptrace_state_t pst; + Status error = PtraceWrapper(PT_GET_PROCESS_STATE, pid, &pst, sizeof(pst)); + if (error.Fail()) { + SetState(StateType::eStateInvalid); + return; + } + + switch (pst.pe_report_event) { + case PTRACE_LWP_CREATE: { + LLDB_LOG(log, "monitoring new thread, pid = {0}, LWP = {1}", pid, + pst.pe_lwp); + NativeThreadNetBSD &t = AddThread(pst.pe_lwp); + error = t.CopyWatchpointsFrom( + static_cast<NativeThreadNetBSD &>(*GetCurrentThread())); + if (error.Fail()) { + LLDB_LOG(log, "failed to copy watchpoints to new thread {0}: {1}", + pst.pe_lwp, error); + SetState(StateType::eStateInvalid); + return; + } + } break; + case PTRACE_LWP_EXIT: + LLDB_LOG(log, "removing exited thread, pid = {0}, LWP = {1}", pid, + pst.pe_lwp); + RemoveThread(pst.pe_lwp); + break; + } + + error = PtraceWrapper(PT_CONTINUE, pid, reinterpret_cast<void *>(1), 0); + if (error.Fail()) + SetState(StateType::eStateInvalid); + return; + } + case TRAP_DBREG: { + if (!thread) + break; + + auto ®ctx = static_cast<NativeRegisterContextNetBSD &>( + thread->GetRegisterContext()); + uint32_t wp_index = LLDB_INVALID_INDEX32; + Status error = regctx.GetWatchpointHitIndex( + wp_index, (uintptr_t)info.psi_siginfo.si_addr); + if (error.Fail()) + LLDB_LOG(log, + "received error while checking for watchpoint hits, pid = " + "{0}, LWP = {1}, error = {2}", + pid, info.psi_lwpid, error); + if (wp_index != LLDB_INVALID_INDEX32) { + thread->SetStoppedByWatchpoint(wp_index); + regctx.ClearWatchpointHit(wp_index); + SetState(StateType::eStateStopped, true); + return; + } + + thread->SetStoppedByTrace(); + 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 NativeProcessNetBSD::MonitorSignal(lldb::pid_t pid, int signal) { + Log *log = GetLog(POSIXLog::Process); + ptrace_siginfo_t info; + + const auto siginfo_err = + PtraceWrapper(PT_GET_SIGINFO, pid, &info, sizeof(info)); + if (siginfo_err.Fail()) { + LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err); + return; + } + + for (const auto &abs_thread : m_threads) { + NativeThreadNetBSD &thread = static_cast<NativeThreadNetBSD &>(*abs_thread); + assert(info.psi_lwpid >= 0); + if (info.psi_lwpid == 0 || + static_cast<lldb::tid_t>(info.psi_lwpid) == thread.GetID()) + thread.SetStoppedBySignal(info.psi_siginfo.si_signo, &info.psi_siginfo); + else + thread.SetStoppedWithNoReason(); + } + SetState(StateType::eStateStopped, true); +} + +Status NativeProcessNetBSD::StopProcess(lldb::pid_t pid) { +#ifdef PT_STOP + return PtraceWrapper(PT_STOP, pid); +#else + Log *log = GetLog(POSIXLog::Ptrace); + Status error; + int ret; + + errno = 0; + ret = kill(pid, SIGSTOP); + + if (ret == -1) + error.SetErrorToErrno(); + + LLDB_LOG(log, "kill({0}, SIGSTOP)", pid); + + if (error.Fail()) + LLDB_LOG(log, "kill() failed: {0}", error); + + return error; +#endif +} + +Status NativeProcessNetBSD::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), addr, data); + + if (ret == -1) + 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; +} + +static llvm::Expected<ptrace_siginfo_t> ComputeSignalInfo( + const std::vector<std::unique_ptr<NativeThreadProtocol>> &threads, + const ResumeActionList &resume_actions) { + // We need to account for three possible scenarios: + // 1. no signal being sent. + // 2. a signal being sent to one thread. + // 3. a signal being sent to the whole process. + + // Count signaled threads. While at it, determine which signal is being sent + // and ensure there's only one. + size_t signaled_threads = 0; + int signal = LLDB_INVALID_SIGNAL_NUMBER; + lldb::tid_t signaled_lwp; + for (const auto &thread : threads) { + assert(thread && "thread list should not contain NULL threads"); + const ResumeAction *action = + resume_actions.GetActionForThread(thread->GetID(), true); + if (action) { + if (action->signal != LLDB_INVALID_SIGNAL_NUMBER) { + signaled_threads++; + if (action->signal != signal) { + if (signal != LLDB_INVALID_SIGNAL_NUMBER) + return Status("NetBSD does not support passing multiple signals " + "simultaneously") + .ToError(); + signal = action->signal; + signaled_lwp = thread->GetID(); + } + } + } + } + + if (signaled_threads == 0) { + ptrace_siginfo_t siginfo; + siginfo.psi_siginfo.si_signo = LLDB_INVALID_SIGNAL_NUMBER; + return siginfo; + } + + if (signaled_threads > 1 && signaled_threads < threads.size()) + return Status("NetBSD does not support passing signal to 1<i<all threads") + .ToError(); + + ptrace_siginfo_t siginfo; + siginfo.psi_siginfo.si_signo = signal; + siginfo.psi_siginfo.si_code = SI_USER; + siginfo.psi_siginfo.si_pid = getpid(); + siginfo.psi_siginfo.si_uid = getuid(); + if (signaled_threads == 1) + siginfo.psi_lwpid = signaled_lwp; + else // signal for the whole process + siginfo.psi_lwpid = 0; + return siginfo; +} + +Status NativeProcessNetBSD::Resume(const ResumeActionList &resume_actions) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "pid {0}", GetID()); + + Status ret; + + Expected<ptrace_siginfo_t> siginfo = + ComputeSignalInfo(m_threads, resume_actions); + if (!siginfo) + return Status(siginfo.takeError()); + + for (const auto &abs_thread : m_threads) { + assert(abs_thread && "thread list should not contain NULL threads"); + NativeThreadNetBSD &thread = static_cast<NativeThreadNetBSD &>(*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("NativeProcessNetBSD::%s (): unexpected state %s specified " + "for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString(action->state), GetID(), + thread.GetID()); + } + + if (!ret.Success()) + return ret; + } + + int signal = 0; + if (siginfo->psi_siginfo.si_signo != LLDB_INVALID_SIGNAL_NUMBER) { + ret = PtraceWrapper(PT_SET_SIGINFO, GetID(), &siginfo.get(), + sizeof(*siginfo)); + if (!ret.Success()) + return ret; + signal = siginfo->psi_siginfo.si_signo; + } + + ret = + PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), signal); + if (ret.Success()) + SetState(eStateRunning, true); + return ret; +} + +Status NativeProcessNetBSD::Halt() { return StopProcess(GetID()); } + +Status NativeProcessNetBSD::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(), reinterpret_cast<void *>(1)); +} + +Status NativeProcessNetBSD::Signal(int signo) { + Status error; + + if (kill(GetID(), signo)) + error.SetErrorToErrno(); + + return error; +} + +Status NativeProcessNetBSD::Interrupt() { return StopProcess(GetID()); } + +Status NativeProcessNetBSD::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; + } + + if (kill(GetID(), SIGKILL) != 0) { + error.SetErrorToErrno(); + return error; + } + + return error; +} + +Status NativeProcessNetBSD::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 NativeProcessNetBSD::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(); + } + + struct kinfo_vmentry *vm; + size_t count, i; + vm = kinfo_getvmmap(GetID(), &count); + if (vm == NULL) { + m_supports_mem_region = LazyBool::eLazyBoolNo; + Status error; + error.SetErrorString("not supported"); + return error; + } + for (i = 0; i < count; i++) { + MemoryRegionInfo info; + info.Clear(); + info.GetRange().SetRangeBase(vm[i].kve_start); + info.GetRange().SetRangeEnd(vm[i].kve_end); + info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); + + if (vm[i].kve_protection & VM_PROT_READ) + info.SetReadable(MemoryRegionInfo::OptionalBool::eYes); + else + info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + + if (vm[i].kve_protection & VM_PROT_WRITE) + info.SetWritable(MemoryRegionInfo::OptionalBool::eYes); + else + info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + + if (vm[i].kve_protection & VM_PROT_EXECUTE) + info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); + else + info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + + if (vm[i].kve_path[0]) + info.SetName(vm[i].kve_path); + + m_mem_region_cache.emplace_back(info, + FileSpec(info.GetName().GetCString())); + } + free(vm); + + 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; + Status error; + error.SetErrorString("not supported"); + return error; + } + 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(); +} + +lldb::addr_t NativeProcessNetBSD::GetSharedLibraryInfoAddress() { + // punt on this for now + return LLDB_INVALID_ADDRESS; +} + +size_t NativeProcessNetBSD::UpdateThreads() { return m_threads.size(); } + +Status NativeProcessNetBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) { + if (hardware) + return Status("NativeProcessNetBSD does not support hardware breakpoints"); + else + return SetSoftwareBreakpoint(addr, size); +} + +Status NativeProcessNetBSD::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) + 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 NativeProcessNetBSD::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + load_addr = LLDB_INVALID_ADDRESS; + Status error = PopulateMemoryRegionCache(); + if (error.Fail()) + 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 NativeProcessNetBSD::SigchldHandler() { + Log *log = GetLog(POSIXLog::Process); + int status; + ::pid_t wait_pid = llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, + WALLSIG | 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 NativeProcessNetBSD::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; +} + +NativeThreadNetBSD &NativeProcessNetBSD::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<NativeThreadNetBSD>(*this, thread_id)); + return static_cast<NativeThreadNetBSD &>(*m_threads.back()); +} + +void NativeProcessNetBSD::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; + } + } +} + +Status NativeProcessNetBSD::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, + WALLSIG)) < 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<NativeThreadNetBSD &>(*thread).SetStoppedBySignal(SIGSTOP); + + // Let our process instance know the thread has stopped. + SetCurrentThreadID(m_threads.front()->GetID()); + SetState(StateType::eStateStopped, false); + return Status(); +} + +Status NativeProcessNetBSD::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 = NativeProcessNetBSD::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 NativeProcessNetBSD::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 = NativeProcessNetBSD::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>> +NativeProcessNetBSD::GetAuxvData() const { + /* + * ELF_AUX_ENTRIES is currently restricted to kernel + * (<sys/exec_elf.h> r. 1.155 specifies 15) + * + * ptrace(2) returns the whole AUXV including extra fiels after AT_NULL this + * information isn't needed. + */ + size_t auxv_size = 100 * sizeof(AuxInfo); + + ErrorOr<std::unique_ptr<WritableMemoryBuffer>> buf = + llvm::WritableMemoryBuffer::getNewMemBuffer(auxv_size); + + struct ptrace_io_desc io; + io.piod_op = PIOD_READ_AUXV; + io.piod_offs = 0; + io.piod_addr = static_cast<void *>(buf.get()->getBufferStart()); + io.piod_len = auxv_size; + + Status error = NativeProcessNetBSD::PtraceWrapper(PT_IO, GetID(), &io); + + if (error.Fail()) + return std::error_code(error.GetError(), std::generic_category()); + + if (io.piod_len < 1) + return std::error_code(ECANCELED, std::generic_category()); + + return std::move(buf); +} + +Status NativeProcessNetBSD::SetupTrace() { + // Enable event reporting + ptrace_event_t events; + Status status = + PtraceWrapper(PT_GET_EVENT_MASK, GetID(), &events, sizeof(events)); + if (status.Fail()) + return status; + // TODO: PTRACE_POSIX_SPAWN? + events.pe_set_event |= PTRACE_LWP_CREATE | PTRACE_LWP_EXIT | PTRACE_FORK | + PTRACE_VFORK | PTRACE_VFORK_DONE; + status = PtraceWrapper(PT_SET_EVENT_MASK, GetID(), &events, sizeof(events)); + if (status.Fail()) + return status; + + return ReinitializeThreads(); +} + +Status NativeProcessNetBSD::ReinitializeThreads() { + // Clear old threads + m_threads.clear(); + + // Initialize new thread +#ifdef PT_LWPSTATUS + struct ptrace_lwpstatus info = {}; + int op = PT_LWPNEXT; +#else + struct ptrace_lwpinfo info = {}; + int op = PT_LWPINFO; +#endif + + Status error = PtraceWrapper(op, GetID(), &info, sizeof(info)); + + if (error.Fail()) { + return error; + } + // Reinitialize from scratch threads and register them in process + while (info.pl_lwpid != 0) { + AddThread(info.pl_lwpid); + error = PtraceWrapper(op, GetID(), &info, sizeof(info)); + if (error.Fail()) { + return error; + } + } + + return error; +} + +void NativeProcessNetBSD::MonitorClone(::pid_t child_pid, bool is_vfork, + NativeThreadNetBSD &parent_thread) { + Log *log = GetLog(POSIXLog::Process); + LLDB_LOG(log, "clone, 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; + } + + ptrace_siginfo_t info; + const auto siginfo_err = + PtraceWrapper(PT_GET_SIGINFO, child_pid, &info, sizeof(info)); + if (siginfo_err.Fail()) { + LLDB_LOG(log, "PT_GET_SIGINFO failed {0}", siginfo_err); + return; + } + assert(info.psi_lwpid >= 0); + lldb::tid_t child_tid = info.psi_lwpid; + + std::unique_ptr<NativeProcessNetBSD> child_process{ + new NativeProcessNetBSD(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<NativeThreadNetBSD &>(*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, std::move(pt_error.ToError()), + "unable to resume parent process {1}: {0}", GetID()); + SetState(StateType::eStateInvalid); + } + } +} + +llvm::Expected<std::string> +NativeProcessNetBSD::SaveCore(llvm::StringRef path_hint) { + llvm::SmallString<128> path{path_hint}; + Status error; + + // Try with the suggested path first. + if (!path.empty()) { + error = PtraceWrapper(PT_DUMPCORE, GetID(), path.data(), path.size()); + if (!error.Fail()) + return path.str().str(); + + // If the request errored, fall back to a generic temporary file. + } + + if (std::error_code errc = + llvm::sys::fs::createTemporaryFile("lldb", "core", path)) + return llvm::createStringError(errc, "Unable to create a temporary file"); + + error = PtraceWrapper(PT_DUMPCORE, GetID(), path.data(), path.size()); + if (error.Fail()) + return error.ToError(); + return path.str().str(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h new file mode 100644 index 000000000000..f3d07651384f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h @@ -0,0 +1,130 @@ +//===-- NativeProcessNetBSD.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_NativeProcessNetBSD_H_ +#define liblldb_NativeProcessNetBSD_H_ + +#include "Plugins/Process/POSIX/NativeProcessELF.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/FileSpec.h" + +#include "NativeThreadNetBSD.h" + +namespace lldb_private { +namespace process_netbsd { +/// \class NativeProcessNetBSD +/// 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 NativeProcessNetBSD : public NativeProcessELF { +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; + + lldb::addr_t GetSharedLibraryInfoAddress() 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); + static Status StopProcess(lldb::pid_t pid); + + llvm::Expected<std::string> SaveCore(llvm::StringRef path_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 + NativeProcessNetBSD(::pid_t pid, int terminal_fd, NativeDelegate &delegate, + const ArchSpec &arch, MainLoop &mainloop); + + bool HasThreadNoLock(lldb::tid_t thread_id); + + NativeThreadNetBSD &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, + NativeThreadNetBSD &parent_thread); + + Status PopulateMemoryRegionCache(); + void SigchldHandler(); + + Status Attach(); + Status SetupTrace(); + Status ReinitializeThreads(); +}; + +} // namespace process_netbsd +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeProcessNetBSD_H_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp new file mode 100644 index 000000000000..4755dab0b073 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.cpp @@ -0,0 +1,34 @@ +//===-- NativeRegisterContextNetBSD.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 "NativeRegisterContextNetBSD.h" + +#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" + +#include "lldb/Host/common/NativeProcessProtocol.h" + +using namespace lldb_private; +using namespace lldb_private::process_netbsd; + +// clang-format off +#include <sys/types.h> +#include <sys/ptrace.h> +// clang-format on + +Status NativeRegisterContextNetBSD::DoRegisterSet(int ptrace_req, void *buf) { + return NativeProcessNetBSD::PtraceWrapper(ptrace_req, GetProcessPid(), buf, + m_thread.GetID()); +} + +NativeProcessNetBSD &NativeRegisterContextNetBSD::GetProcess() { + return static_cast<NativeProcessNetBSD &>(m_thread.GetProcess()); +} + +::pid_t NativeRegisterContextNetBSD::GetProcessPid() { + return GetProcess().GetID(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h new file mode 100644 index 000000000000..08490aad9e0b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h @@ -0,0 +1,44 @@ +//===-- NativeRegisterContextNetBSD.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_NativeRegisterContextNetBSD_h +#define lldb_NativeRegisterContextNetBSD_h + +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" + +namespace lldb_private { +namespace process_netbsd { + +class NativeProcessNetBSD; + +class NativeRegisterContextNetBSD + : public virtual NativeRegisterContextRegisterInfo { +public: + // This function is implemented in the NativeRegisterContextNetBSD_* + // subclasses to create a new instance of the host specific + // NativeRegisterContextNetBSD. The implementations can't collide as only one + // NativeRegisterContextNetBSD_* variant should be compiled into the final + // executable. + static NativeRegisterContextNetBSD * + CreateHostNativeRegisterContextNetBSD(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + virtual llvm::Error + CopyHardwareWatchpointsFrom(NativeRegisterContextNetBSD &source) = 0; + +protected: + Status DoRegisterSet(int req, void *buf); + virtual NativeProcessNetBSD &GetProcess(); + virtual ::pid_t GetProcessPid(); +}; + +} // namespace process_netbsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextNetBSD_h diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp new file mode 100644 index 000000000000..13607bd261d4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.cpp @@ -0,0 +1,645 @@ +//===-- NativeRegisterContextNetBSD_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 "NativeRegisterContextNetBSD_x86_64.h" + +#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 "Plugins/Process/Utility/RegisterContextNetBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h" + +// clang-format off +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/sysctl.h> +#include <sys/uio.h> +#include <x86/cpu.h> +#include <x86/cpu_extended_state.h> +#include <x86/specialreg.h> +#include <elf.h> +#include <err.h> +#include <cstdint> +#include <cstdlib> +#include <optional> +// clang-format on + +using namespace lldb_private; +using namespace lldb_private::process_netbsd; + +// 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()) + +NativeRegisterContextNetBSD * +NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { + return new NativeRegisterContextNetBSD_x86_64(target_arch, native_thread); +} + +// NativeRegisterContextNetBSD_x86_64 members. + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + if (HostInfo::GetArchitecture().GetAddressByteSize() == 4) { + // 32-bit hosts run with a RegisterContextNetBSD_i386 context. + return new RegisterContextNetBSD_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 RegisterContextNetBSD_x86_64(target_arch); + } +} + +NativeRegisterContextNetBSD_x86_64::NativeRegisterContextNetBSD_x86_64( + const ArchSpec &target_arch, NativeThreadProtocol &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, MaxRegularRegSet + 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 NativeRegisterContextNetBSD_x86_64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextNetBSD_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<NativeRegisterContextNetBSD_x86_64::RegSetKind> +NativeRegisterContextNetBSD_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 NativeRegisterContextNetBSD_x86_64::ReadRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return DoRegisterSet(PT_GETREGS, m_gpr.data()); + case DBRegSet: + return DoRegisterSet(PT_GETDBREGS, m_dbr.data()); + case FPRegSet: + case YMMRegSet: + case MPXRegSet: { + struct iovec iov = {m_xstate.data(), m_xstate.size()}; + Status ret = DoRegisterSet(PT_GETXSTATE, &iov); + assert(reinterpret_cast<xstate *>(m_xstate.data())->xs_rfbm & XCR0_X87); + return ret; + } + } + llvm_unreachable("NativeRegisterContextNetBSD_x86_64::ReadRegisterSet"); +} + +Status NativeRegisterContextNetBSD_x86_64::WriteRegisterSet(RegSetKind set) { + switch (set) { + case GPRegSet: + return DoRegisterSet(PT_SETREGS, m_gpr.data()); + case DBRegSet: + return DoRegisterSet(PT_SETDBREGS, m_dbr.data()); + case FPRegSet: + case YMMRegSet: + case MPXRegSet: { + struct iovec iov = {&m_xstate, sizeof(m_xstate)}; + return DoRegisterSet(PT_SETXSTATE, &iov); + } + } + llvm_unreachable("NativeRegisterContextNetBSD_x86_64::WriteRegisterSet"); +} + +Status +NativeRegisterContextNetBSD_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; + 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_xstate.data() + + offsetof(xstate, xs_fxsave)); + 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 NativeRegisterContextNetBSD_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; + uint64_t new_xstate_bv = 0; + + error = ReadRegisterSet(set); + if (error.Fail()) + return error; + + switch (set) { + case GPRegSet: + case DBRegSet: + ::memcpy(GetOffsetRegSetData(set, reg_info->byte_offset), + reg_value.GetBytes(), reg_value.GetByteSize()); + break; + case FPRegSet: { + void *data = GetOffsetRegSetData(set, reg_info->byte_offset); + FXSAVE *fpr = reinterpret_cast<FXSAVE *>(m_xstate.data() + + offsetof(xstate, xs_fxsave)); + if (data == &fpr->ftag) // ftag + fpr->ftag = FullToAbridgedTagWord(reg_value.GetAsUInt16()); + else + ::memcpy(data, reg_value.GetBytes(), reg_value.GetByteSize()); + if (data >= &fpr->xmm) + new_xstate_bv |= XCR0_SSE; + else if (data >= &fpr->mxcsr && data < &fpr->stmm) + new_xstate_bv |= XCR0_SSE; + else + new_xstate_bv |= XCR0_X87; + 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); + new_xstate_bv |= XCR0_SSE | XCR0_YMM_Hi128; + } + break; + } + case MPXRegSet: + llvm_unreachable("MPX regset should have returned error"); + } + + if (new_xstate_bv != 0) + reinterpret_cast<xstate *>(m_xstate.data())->xs_xstate_bv |= new_xstate_bv; + return WriteRegisterSet(set); +} + +Status NativeRegisterContextNetBSD_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 NativeRegisterContextNetBSD_x86_64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextNetBSD_x86_64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) { + error.SetErrorStringWithFormat( + "NativeRegisterContextNetBSD_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("NativeRegisterContextNetBSD_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 NativeRegisterContextNetBSD_x86_64::CopyHardwareWatchpointsFrom( + NativeRegisterContextNetBSD &source) { + auto &r_source = static_cast<NativeRegisterContextNetBSD_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 * +NativeRegisterContextNetBSD_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_xstate.data() + offsetof(xstate, xs_fxsave); + 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<NativeRegisterContextNetBSD_x86_64::YMMSplitPtr> +NativeRegisterContextNetBSD_x86_64::GetYMMSplitReg(uint32_t reg) { + auto xst = reinterpret_cast<xstate *>(m_xstate.data()); + if (!(xst->xs_rfbm & XCR0_SSE) || !(xst->xs_rfbm & XCR0_YMM_Hi128)) + 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."); + } + + return YMMSplitPtr{&xst->xs_fxsave.fx_xmm[reg_index], + &xst->xs_ymm_hi128.xs_ymm[reg_index]}; +} + +#endif // defined(__x86_64__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h new file mode 100644 index 000000000000..e4597b2f69c6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeRegisterContextNetBSD_x86_64.h @@ -0,0 +1,94 @@ +//===-- NativeRegisterContextNetBSD_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_NativeRegisterContextNetBSD_x86_64_h +#define lldb_NativeRegisterContextNetBSD_x86_64_h + +// clang-format off +#include <sys/param.h> +#include <sys/types.h> +#include <sys/ptrace.h> +#include <machine/reg.h> +// clang-format on + +#include <array> +#include <optional> + +#include "Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h" +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/NativeRegisterContextDBReg_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +namespace lldb_private { +namespace process_netbsd { + +class NativeProcessNetBSD; + +class NativeRegisterContextNetBSD_x86_64 + : public NativeRegisterContextNetBSD, + public NativeRegisterContextDBReg_x86 { +public: + NativeRegisterContextNetBSD_x86_64(const ArchSpec &target_arch, + NativeThreadProtocol &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(NativeRegisterContextNetBSD &source) override; + +private: + // Private member types. + enum RegSetKind { + GPRegSet, + FPRegSet, + DBRegSet, + MaxRegularRegSet = DBRegSet, + YMMRegSet, + MPXRegSet, + MaxRegSet = MPXRegSet, + }; + + // Private member variables. + std::array<uint8_t, sizeof(struct reg)> m_gpr; + std::array<uint8_t, sizeof(struct xstate)> m_xstate; + std::array<uint8_t, sizeof(struct dbreg)> m_dbr; + std::array<size_t, MaxRegularRegSet + 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_netbsd +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextNetBSD_x86_64_h + +#endif // defined(__x86_64__) diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp new file mode 100644 index 000000000000..77b4301ea22e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.cpp @@ -0,0 +1,329 @@ +//===-- NativeThreadNetBSD.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 "NativeThreadNetBSD.h" +#include "NativeRegisterContextNetBSD.h" + +#include "NativeProcessNetBSD.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> +// clang-format on + +#include <sstream> + +// clang-format off +#include <sys/types.h> +#include <sys/sysctl.h> +// clang-format on + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_netbsd; + +NativeThreadNetBSD::NativeThreadNetBSD(NativeProcessNetBSD &process, + lldb::tid_t tid) + : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid), + m_stop_info(), m_reg_context_up( +NativeRegisterContextNetBSD::CreateHostNativeRegisterContextNetBSD(process.GetArchitecture(), *this) +), m_stop_description() {} + +Status NativeThreadNetBSD::Resume() { + Status ret = NativeProcessNetBSD::PtraceWrapper(PT_RESUME, m_process.GetID(), + nullptr, GetID()); + if (!ret.Success()) + return ret; + ret = NativeProcessNetBSD::PtraceWrapper(PT_CLEARSTEP, m_process.GetID(), + nullptr, GetID()); + if (ret.Success()) + SetRunning(); + return ret; +} + +Status NativeThreadNetBSD::SingleStep() { + Status ret = NativeProcessNetBSD::PtraceWrapper(PT_RESUME, m_process.GetID(), + nullptr, GetID()); + if (!ret.Success()) + return ret; + ret = NativeProcessNetBSD::PtraceWrapper(PT_SETSTEP, m_process.GetID(), + nullptr, GetID()); + if (ret.Success()) + SetStepping(); + return ret; +} + +Status NativeThreadNetBSD::Suspend() { + Status ret = NativeProcessNetBSD::PtraceWrapper(PT_SUSPEND, m_process.GetID(), + nullptr, GetID()); + if (ret.Success()) + SetStopped(); + return ret; +} + +void NativeThreadNetBSD::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 NativeThreadNetBSD::SetStoppedByBreakpoint() { + SetStopped(); + m_stop_info.reason = StopReason::eStopReasonBreakpoint; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadNetBSD::SetStoppedByTrace() { + SetStopped(); + m_stop_info.reason = StopReason::eStopReasonTrace; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadNetBSD::SetStoppedByExec() { + SetStopped(); + m_stop_info.reason = StopReason::eStopReasonExec; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadNetBSD::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 NativeThreadNetBSD::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 NativeThreadNetBSD::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 NativeThreadNetBSD::SetStoppedByVForkDone() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonVForkDone; + m_stop_info.signo = SIGTRAP; +} + +void NativeThreadNetBSD::SetStoppedWithNoReason() { + SetStopped(); + + m_stop_info.reason = StopReason::eStopReasonNone; + m_stop_info.signo = 0; +} + +void NativeThreadNetBSD::SetStopped() { + const StateType new_state = StateType::eStateStopped; + m_state = new_state; + m_stop_description.clear(); +} + +void NativeThreadNetBSD::SetRunning() { + m_state = StateType::eStateRunning; + m_stop_info.reason = StopReason::eStopReasonNone; +} + +void NativeThreadNetBSD::SetStepping() { + m_state = StateType::eStateStepping; + m_stop_info.reason = StopReason::eStopReasonNone; +} + +std::string NativeThreadNetBSD::GetName() { +#ifdef PT_LWPSTATUS + struct ptrace_lwpstatus info = {}; + info.pl_lwpid = m_tid; + Status error = NativeProcessNetBSD::PtraceWrapper( + PT_LWPSTATUS, static_cast<int>(m_process.GetID()), &info, sizeof(info)); + if (error.Fail()) { + return ""; + } + return info.pl_name; +#else + std::vector<struct kinfo_lwp> infos; + Log *log = GetLog(POSIXLog::Thread); + + int mib[5] = {CTL_KERN, KERN_LWP, static_cast<int>(m_process.GetID()), + sizeof(struct kinfo_lwp), 0}; + size_t size; + + if (::sysctl(mib, 5, nullptr, &size, nullptr, 0) == -1 || size == 0) { + LLDB_LOG(log, "sysctl() for LWP info size failed: {0}", + llvm::sys::StrError()); + return ""; + } + + mib[4] = size / sizeof(size_t); + infos.resize(size / sizeof(struct kinfo_lwp)); + + if (sysctl(mib, 5, infos.data(), &size, NULL, 0) == -1 || size == 0) { + LLDB_LOG(log, "sysctl() for LWP info failed: {0}", llvm::sys::StrError()); + return ""; + } + + size_t nlwps = size / sizeof(struct kinfo_lwp); + for (size_t i = 0; i < nlwps; i++) { + if (static_cast<lldb::tid_t>(infos[i].l_lid) == m_tid) { + return infos[i].l_name; + } + } + + LLDB_LOG(log, "unable to find lwp {0} in LWP infos", m_tid); + return ""; +#endif +} + +lldb::StateType NativeThreadNetBSD::GetState() { return m_state; } + +bool NativeThreadNetBSD::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!"); +} + +NativeRegisterContextNetBSD &NativeThreadNetBSD::GetRegisterContext() { + assert(m_reg_context_up); + return *m_reg_context_up; +} + +Status NativeThreadNetBSD::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 NativeThreadNetBSD::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 NativeThreadNetBSD::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 NativeThreadNetBSD::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 +NativeThreadNetBSD::CopyWatchpointsFrom(NativeThreadNetBSD &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; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h new file mode 100644 index 000000000000..ee9305337fda --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/NetBSD/NativeThreadNetBSD.h @@ -0,0 +1,86 @@ +//===-- NativeThreadNetBSD.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_NativeThreadNetBSD_H_ +#define liblldb_NativeThreadNetBSD_H_ + +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include "Plugins/Process/NetBSD/NativeRegisterContextNetBSD.h" + +#include <csignal> +#include <map> +#include <string> + +namespace lldb_private { +namespace process_netbsd { + +class NativeProcessNetBSD; + +class NativeThreadNetBSD : public NativeThreadProtocol { + friend class NativeProcessNetBSD; + +public: + NativeThreadNetBSD(NativeProcessNetBSD &process, lldb::tid_t tid); + + // NativeThreadProtocol Interface + std::string GetName() override; + + lldb::StateType GetState() override; + + bool GetStopReason(ThreadStopInfo &stop_info, + std::string &description) override; + + NativeRegisterContextNetBSD &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; + +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(NativeThreadNetBSD& source); + + // Member Variables + lldb::StateType m_state; + ThreadStopInfo m_stop_info; + std::unique_ptr<NativeRegisterContextNetBSD> 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<NativeThreadNetBSD> NativeThreadNetBSDSP; +} // namespace process_netbsd +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadNetBSD_H_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp new file mode 100644 index 000000000000..d93b7cd0ce56 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.cpp @@ -0,0 +1,31 @@ +//===-- CrashReason.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 "CrashReason.h" + +#include "lldb/Target/UnixSignals.h" + +std::string GetCrashReasonString(const siginfo_t &info) { +#if defined(si_lower) && defined(si_upper) + std::optional<lldb::addr_t> lower = + reinterpret_cast<lldb::addr_t>(info.si_lower); + std::optional<lldb::addr_t> upper = + reinterpret_cast<lldb::addr_t>(info.si_upper); +#else + std::optional<lldb::addr_t> lower; + std::optional<lldb::addr_t> upper; +#endif + + std::string description = + lldb_private::UnixSignals::CreateForHost()->GetSignalDescription( + info.si_signo, info.si_code, + reinterpret_cast<uintptr_t>(info.si_addr), lower, upper); + assert(description.size() && "unexpected signal"); + + return "signal " + description; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h new file mode 100644 index 000000000000..2177726b76f0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/CrashReason.h @@ -0,0 +1,20 @@ +//===-- CrashReason.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_CrashReason_H_ +#define liblldb_CrashReason_H_ + +#include "lldb/lldb-types.h" + +#include <csignal> + +#include <string> + +std::string GetCrashReasonString(const siginfo_t &info); + +#endif // #ifndef liblldb_CrashReason_H_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp new file mode 100644 index 000000000000..23e94a5f55e2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.cpp @@ -0,0 +1,189 @@ +//===-- NativeProcessELF.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 "NativeProcessELF.h" + +#include "lldb/Utility/DataExtractor.h" +#include <optional> + +namespace lldb_private { + +std::optional<uint64_t> +NativeProcessELF::GetAuxValue(enum AuxVector::EntryType type) { + if (m_aux_vector == nullptr) { + auto buffer_or_error = GetAuxvData(); + if (!buffer_or_error) + return std::nullopt; + DataExtractor auxv_data(buffer_or_error.get()->getBufferStart(), + buffer_or_error.get()->getBufferSize(), + GetByteOrder(), GetAddressByteSize()); + m_aux_vector = std::make_unique<AuxVector>(auxv_data); + } + + return m_aux_vector->GetAuxValue(type); +} + +lldb::addr_t NativeProcessELF::GetSharedLibraryInfoAddress() { + if (!m_shared_library_info_addr) { + if (GetAddressByteSize() == 8) + m_shared_library_info_addr = + GetELFImageInfoAddress<llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Phdr, + llvm::ELF::Elf64_Dyn>(); + else + m_shared_library_info_addr = + GetELFImageInfoAddress<llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr, + llvm::ELF::Elf32_Dyn>(); + } + + return *m_shared_library_info_addr; +} + +template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN> +lldb::addr_t NativeProcessELF::GetELFImageInfoAddress() { + std::optional<uint64_t> maybe_phdr_addr = + GetAuxValue(AuxVector::AUXV_AT_PHDR); + std::optional<uint64_t> maybe_phdr_entry_size = + GetAuxValue(AuxVector::AUXV_AT_PHENT); + std::optional<uint64_t> maybe_phdr_num_entries = + GetAuxValue(AuxVector::AUXV_AT_PHNUM); + if (!maybe_phdr_addr || !maybe_phdr_entry_size || !maybe_phdr_num_entries) + return LLDB_INVALID_ADDRESS; + lldb::addr_t phdr_addr = *maybe_phdr_addr; + size_t phdr_entry_size = *maybe_phdr_entry_size; + size_t phdr_num_entries = *maybe_phdr_num_entries; + + // Find the PT_DYNAMIC segment (.dynamic section) in the program header and + // what the load bias by calculating the difference of the program header + // load address and its virtual address. + lldb::offset_t load_bias; + bool found_load_bias = false; + lldb::addr_t dynamic_section_addr = 0; + uint64_t dynamic_section_size = 0; + bool found_dynamic_section = false; + ELF_PHDR phdr_entry; + for (size_t i = 0; i < phdr_num_entries; i++) { + size_t bytes_read; + auto error = ReadMemory(phdr_addr + i * phdr_entry_size, &phdr_entry, + sizeof(phdr_entry), bytes_read); + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + if (phdr_entry.p_type == llvm::ELF::PT_PHDR) { + load_bias = phdr_addr - phdr_entry.p_vaddr; + found_load_bias = true; + } + + if (phdr_entry.p_type == llvm::ELF::PT_DYNAMIC) { + dynamic_section_addr = phdr_entry.p_vaddr; + dynamic_section_size = phdr_entry.p_memsz; + found_dynamic_section = true; + } + } + + if (!found_load_bias || !found_dynamic_section) + return LLDB_INVALID_ADDRESS; + + // Find the DT_DEBUG entry in the .dynamic section + dynamic_section_addr += load_bias; + ELF_DYN dynamic_entry; + size_t dynamic_num_entries = dynamic_section_size / sizeof(dynamic_entry); + for (size_t i = 0; i < dynamic_num_entries; i++) { + size_t bytes_read; + auto error = ReadMemory(dynamic_section_addr + i * sizeof(dynamic_entry), + &dynamic_entry, sizeof(dynamic_entry), bytes_read); + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + // Return the &DT_DEBUG->d_ptr which points to r_debug which contains the + // link_map. + if (dynamic_entry.d_tag == llvm::ELF::DT_DEBUG) { + return dynamic_section_addr + i * sizeof(dynamic_entry) + + sizeof(dynamic_entry.d_tag); + } + } + + return LLDB_INVALID_ADDRESS; +} + +template lldb::addr_t NativeProcessELF::GetELFImageInfoAddress< + llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr, llvm::ELF::Elf32_Dyn>(); +template lldb::addr_t NativeProcessELF::GetELFImageInfoAddress< + llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Phdr, llvm::ELF::Elf64_Dyn>(); + +template <typename T> +llvm::Expected<SVR4LibraryInfo> +NativeProcessELF::ReadSVR4LibraryInfo(lldb::addr_t link_map_addr) { + ELFLinkMap<T> link_map; + size_t bytes_read; + auto error = + ReadMemory(link_map_addr, &link_map, sizeof(link_map), bytes_read); + if (!error.Success()) + return error.ToError(); + + char name_buffer[PATH_MAX]; + llvm::Expected<llvm::StringRef> string_or_error = ReadCStringFromMemory( + link_map.l_name, &name_buffer[0], sizeof(name_buffer), bytes_read); + if (!string_or_error) + return string_or_error.takeError(); + + SVR4LibraryInfo info; + info.name = string_or_error->str(); + info.link_map = link_map_addr; + info.base_addr = link_map.l_addr; + info.ld_addr = link_map.l_ld; + info.next = link_map.l_next; + + return info; +} + +llvm::Expected<std::vector<SVR4LibraryInfo>> +NativeProcessELF::GetLoadedSVR4Libraries() { + // Address of DT_DEBUG.d_ptr which points to r_debug + lldb::addr_t info_address = GetSharedLibraryInfoAddress(); + if (info_address == LLDB_INVALID_ADDRESS) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Invalid shared library info address"); + // Address of r_debug + lldb::addr_t address = 0; + size_t bytes_read; + auto status = + ReadMemory(info_address, &address, GetAddressByteSize(), bytes_read); + if (!status.Success()) + return status.ToError(); + if (address == 0) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Invalid r_debug address"); + // Read r_debug.r_map + lldb::addr_t link_map = 0; + status = ReadMemory(address + GetAddressByteSize(), &link_map, + GetAddressByteSize(), bytes_read); + if (!status.Success()) + return status.ToError(); + if (link_map == 0) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Invalid link_map address"); + + std::vector<SVR4LibraryInfo> library_list; + while (link_map) { + llvm::Expected<SVR4LibraryInfo> info = + GetAddressByteSize() == 8 ? ReadSVR4LibraryInfo<uint64_t>(link_map) + : ReadSVR4LibraryInfo<uint32_t>(link_map); + if (!info) + return info.takeError(); + if (!info->name.empty() && info->base_addr != 0) + library_list.push_back(*info); + link_map = info->next; + } + + return library_list; +} + +void NativeProcessELF::NotifyDidExec() { + NativeProcessProtocol::NotifyDidExec(); + m_shared_library_info_addr.reset(); +} + +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h new file mode 100644 index 000000000000..937def94436b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h @@ -0,0 +1,65 @@ +//===-- NativeProcessELF.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_NativeProcessELF_H_ +#define liblldb_NativeProcessELF_H_ + +#include "Plugins/Process/Utility/AuxVector.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "llvm/BinaryFormat/ELF.h" +#include <optional> + +namespace lldb_private { + +/// \class NativeProcessELF +/// Abstract class that extends \a NativeProcessProtocol with ELF specific +/// logic. Meant to be subclassed by ELF based NativeProcess* implementations. +class NativeProcessELF : public NativeProcessProtocol { + using NativeProcessProtocol::NativeProcessProtocol; + +public: + std::optional<uint64_t> GetAuxValue(enum AuxVector::EntryType type); + +protected: + template <typename T> struct ELFLinkMap { + T l_addr; + T l_name; + T l_ld; + T l_next; + T l_prev; + }; + + lldb::addr_t GetSharedLibraryInfoAddress() override; + + template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN> + lldb::addr_t GetELFImageInfoAddress(); + + llvm::Expected<std::vector<SVR4LibraryInfo>> + GetLoadedSVR4Libraries() override; + + template <typename T> + llvm::Expected<SVR4LibraryInfo> + ReadSVR4LibraryInfo(lldb::addr_t link_map_addr); + + void NotifyDidExec() override; + + std::unique_ptr<AuxVector> m_aux_vector; + std::optional<lldb::addr_t> m_shared_library_info_addr; +}; + +// Explicitly declare the two 32/64 bit templates that NativeProcessELF.cpp will +// define. This allows us to keep the template definition here and usable +// elsewhere. +extern template lldb::addr_t NativeProcessELF::GetELFImageInfoAddress< + llvm::ELF::Elf32_Ehdr, llvm::ELF::Elf32_Phdr, llvm::ELF::Elf32_Dyn>(); +extern template lldb::addr_t NativeProcessELF::GetELFImageInfoAddress< + llvm::ELF::Elf64_Ehdr, llvm::ELF::Elf64_Phdr, llvm::ELF::Elf64_Dyn>(); + +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp new file mode 100644 index 000000000000..7ad88aabc2c0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp @@ -0,0 +1,34 @@ +//===-- ProcessPOSIXLog.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 "ProcessPOSIXLog.h" + +#include "llvm/Support/Threading.h" + +using namespace lldb_private; + +static constexpr Log::Category g_categories[] = { + {{"break"}, {"log breakpoints"}, POSIXLog::Breakpoints}, + {{"memory"}, {"log memory reads and writes"}, POSIXLog::Memory}, + {{"process"}, {"log process events and activities"}, POSIXLog::Process}, + {{"ptrace"}, {"log all calls to ptrace"}, POSIXLog::Ptrace}, + {{"registers"}, {"log register read/writes"}, POSIXLog::Registers}, + {{"thread"}, {"log thread events and activities"}, POSIXLog::Thread}, + {{"watch"}, {"log watchpoint related activities"}, POSIXLog::Watchpoints}, +}; + +static Log::Channel g_channel(g_categories, POSIXLog::Process); + +template <> Log::Channel &lldb_private::LogChannelFor<POSIXLog>() { + return g_channel; +} + +void ProcessPOSIXLog::Initialize() { + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, []() { Log::Register("posix", g_channel); }); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h new file mode 100644 index 000000000000..88cfdfd204c0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/POSIX/ProcessPOSIXLog.h @@ -0,0 +1,39 @@ +//===-- ProcessPOSIXLog.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_ProcessPOSIXLog_h_ +#define liblldb_ProcessPOSIXLog_h_ + +#include "lldb/Utility/Log.h" +#include "llvm/ADT/BitmaskEnum.h" + +namespace lldb_private { + +enum class POSIXLog : Log::MaskType { + Breakpoints = Log::ChannelFlag<0>, + Memory = Log::ChannelFlag<1>, + Process = Log::ChannelFlag<2>, + Ptrace = Log::ChannelFlag<3>, + Registers = Log::ChannelFlag<4>, + Thread = Log::ChannelFlag<5>, + Watchpoints = Log::ChannelFlag<6>, + Trace = Log::ChannelFlag<7>, + LLVM_MARK_AS_BITMASK_ENUM(Trace) +}; +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +class ProcessPOSIXLog { +public: + static void Initialize(); +}; + +template <> Log::Channel &LogChannelFor<POSIXLog>(); +} // namespace lldb_private + +#endif // liblldb_ProcessPOSIXLog_h_ diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h new file mode 100644 index 000000000000..fd3965fade19 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMDefines.h @@ -0,0 +1,191 @@ +//===-- ARMDefines.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_SOURCE_PLUGINS_PROCESS_UTILITY_ARMDEFINES_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_ARMDEFINES_H + +#include "llvm/Support/ErrorHandling.h" + +#include <cassert> +#include <cstdint> + +// Common definitions for the ARM/Thumb Instruction Set Architecture. + +namespace lldb_private { + +// ARM shifter types +enum ARM_ShifterType { + SRType_LSL, + SRType_LSR, + SRType_ASR, + SRType_ROR, + SRType_RRX, + SRType_Invalid +}; + +// ARM conditions // Meaning (integer) Meaning (floating-point) +// Condition flags +#define COND_EQ \ + 0x0 // Equal Equal Z == 1 +#define COND_NE \ + 0x1 // Not equal Not equal, or unordered Z == 0 +#define COND_CS \ + 0x2 // Carry set >, ==, or unordered C == 1 +#define COND_HS 0x2 +#define COND_CC \ + 0x3 // Carry clear Less than C == 0 +#define COND_LO 0x3 +#define COND_MI \ + 0x4 // Minus, negative Less than N == 1 +#define COND_PL \ + 0x5 // Plus, positive or zero >, ==, or unordered N == 0 +#define COND_VS \ + 0x6 // Overflow Unordered V == 1 +#define COND_VC \ + 0x7 // No overflow Not unordered V == 0 +#define COND_HI \ + 0x8 // Unsigned higher Greater than, or unordered C == 1 and Z == + // 0 +#define COND_LS \ + 0x9 // Unsigned lower or same Less than or equal C == 0 or Z == + // 1 +#define COND_GE \ + 0xA // Greater than or equal Greater than or equal N == V +#define COND_LT \ + 0xB // Less than Less than, or unordered N != V +#define COND_GT \ + 0xC // Greater than Greater than Z == 0 and N == + // V +#define COND_LE \ + 0xD // Less than or equal <, ==, or unordered Z == 1 or N != + // V +#define COND_AL \ + 0xE // Always (unconditional) Always (unconditional) Any +#define COND_UNCOND 0xF + +static inline const char *ARMCondCodeToString(uint32_t CC) { + switch (CC) { + case COND_EQ: + return "eq"; + case COND_NE: + return "ne"; + case COND_HS: + return "hs"; + case COND_LO: + return "lo"; + case COND_MI: + return "mi"; + case COND_PL: + return "pl"; + case COND_VS: + return "vs"; + case COND_VC: + return "vc"; + case COND_HI: + return "hi"; + case COND_LS: + return "ls"; + case COND_GE: + return "ge"; + case COND_LT: + return "lt"; + case COND_GT: + return "gt"; + case COND_LE: + return "le"; + case COND_AL: + return "al"; + } + llvm_unreachable("Unknown condition code"); +} + +static inline bool ARMConditionPassed(const uint32_t condition, + const uint32_t cpsr) { + const uint32_t cpsr_n = (cpsr >> 31) & 1u; // Negative condition code flag + const uint32_t cpsr_z = (cpsr >> 30) & 1u; // Zero condition code flag + const uint32_t cpsr_c = (cpsr >> 29) & 1u; // Carry condition code flag + const uint32_t cpsr_v = (cpsr >> 28) & 1u; // Overflow condition code flag + + switch (condition) { + case COND_EQ: + return (cpsr_z == 1); + case COND_NE: + return (cpsr_z == 0); + case COND_CS: + return (cpsr_c == 1); + case COND_CC: + return (cpsr_c == 0); + case COND_MI: + return (cpsr_n == 1); + case COND_PL: + return (cpsr_n == 0); + case COND_VS: + return (cpsr_v == 1); + case COND_VC: + return (cpsr_v == 0); + case COND_HI: + return ((cpsr_c == 1) && (cpsr_z == 0)); + case COND_LS: + return ((cpsr_c == 0) || (cpsr_z == 1)); + case COND_GE: + return (cpsr_n == cpsr_v); + case COND_LT: + return (cpsr_n != cpsr_v); + case COND_GT: + return ((cpsr_z == 0) && (cpsr_n == cpsr_v)); + case COND_LE: + return ((cpsr_z == 1) || (cpsr_n != cpsr_v)); + case COND_AL: + case COND_UNCOND: + default: + return true; + } + return false; +} + +// Bit positions for CPSR +#define CPSR_T_POS 5 +#define CPSR_F_POS 6 +#define CPSR_I_POS 7 +#define CPSR_A_POS 8 +#define CPSR_E_POS 9 +#define CPSR_J_POS 24 +#define CPSR_Q_POS 27 +#define CPSR_V_POS 28 +#define CPSR_C_POS 29 +#define CPSR_Z_POS 30 +#define CPSR_N_POS 31 + +// CPSR mode definitions +#define CPSR_MODE_USR 0x10u +#define CPSR_MODE_FIQ 0x11u +#define CPSR_MODE_IRQ 0x12u +#define CPSR_MODE_SVC 0x13u +#define CPSR_MODE_ABT 0x17u +#define CPSR_MODE_UND 0x1bu +#define CPSR_MODE_SYS 0x1fu + +// Masks for CPSR +#define MASK_CPSR_MODE_MASK (0x0000001fu) +#define MASK_CPSR_IT_MASK (0x0600fc00u) +#define MASK_CPSR_T (1u << CPSR_T_POS) +#define MASK_CPSR_F (1u << CPSR_F_POS) +#define MASK_CPSR_I (1u << CPSR_I_POS) +#define MASK_CPSR_A (1u << CPSR_A_POS) +#define MASK_CPSR_E (1u << CPSR_E_POS) +#define MASK_CPSR_GE_MASK (0x000f0000u) +#define MASK_CPSR_J (1u << CPSR_J_POS) +#define MASK_CPSR_Q (1u << CPSR_Q_POS) +#define MASK_CPSR_V (1u << CPSR_V_POS) +#define MASK_CPSR_C (1u << CPSR_C_POS) +#define MASK_CPSR_Z (1u << CPSR_Z_POS) +#define MASK_CPSR_N (1u << CPSR_N_POS) + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_ARMDEFINES_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h new file mode 100644 index 000000000000..9256f926275b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ARMUtils.h @@ -0,0 +1,377 @@ +//===-- ARMUtils.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_SOURCE_PLUGINS_PROCESS_UTILITY_ARMUTILS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_ARMUTILS_H + +#include "ARMDefines.h" +#include "InstructionUtils.h" +#include "llvm/ADT/bit.h" +#include "llvm/Support/MathExtras.h" + +// Common utilities for the ARM/Thumb Instruction Set Architecture. + +namespace lldb_private { + +static inline uint32_t Align(uint32_t val, uint32_t alignment) { + return alignment * (val / alignment); +} + +static inline uint32_t DecodeImmShift(const uint32_t type, const uint32_t imm5, + ARM_ShifterType &shift_t) { + switch (type) { + default: + assert(0 && "Invalid shift type"); + break; + case 0: + shift_t = SRType_LSL; + return imm5; + case 1: + shift_t = SRType_LSR; + return (imm5 == 0 ? 32 : imm5); + case 2: + shift_t = SRType_ASR; + return (imm5 == 0 ? 32 : imm5); + case 3: + if (imm5 == 0) { + shift_t = SRType_RRX; + return 1; + } else { + shift_t = SRType_ROR; + return imm5; + } + } + shift_t = SRType_Invalid; + return UINT32_MAX; +} + +// A8.6.35 CMP (register) -- Encoding T3 +// Convenience function. +static inline uint32_t DecodeImmShiftThumb(const uint32_t opcode, + ARM_ShifterType &shift_t) { + return DecodeImmShift(Bits32(opcode, 5, 4), + Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6), + shift_t); +} + +// A8.6.35 CMP (register) -- Encoding A1 +// Convenience function. +static inline uint32_t DecodeImmShiftARM(const uint32_t opcode, + ARM_ShifterType &shift_t) { + return DecodeImmShift(Bits32(opcode, 6, 5), Bits32(opcode, 11, 7), shift_t); +} + +static inline uint32_t DecodeImmShift(const ARM_ShifterType shift_t, + const uint32_t imm5) { + ARM_ShifterType dont_care; + return DecodeImmShift(shift_t, imm5, dont_care); +} + +static inline ARM_ShifterType DecodeRegShift(const uint32_t type) { + switch (type) { + default: + // assert(0 && "Invalid shift type"); + return SRType_Invalid; + case 0: + return SRType_LSL; + case 1: + return SRType_LSR; + case 2: + return SRType_ASR; + case 3: + return SRType_ROR; + } +} + +static inline uint32_t LSL_C(const uint32_t value, const uint32_t amount, + uint32_t &carry_out, bool *success) { + if (amount == 0) { + *success = false; + return 0; + } + *success = true; + carry_out = amount <= 32 ? Bit32(value, 32 - amount) : 0; + return value << amount; +} + +static inline uint32_t LSL(const uint32_t value, const uint32_t amount, + bool *success) { + *success = true; + if (amount == 0) + return value; + uint32_t dont_care; + uint32_t result = LSL_C(value, amount, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t LSR_C(const uint32_t value, const uint32_t amount, + uint32_t &carry_out, bool *success) { + if (amount == 0) { + *success = false; + return 0; + } + *success = true; + carry_out = amount <= 32 ? Bit32(value, amount - 1) : 0; + return value >> amount; +} + +static inline uint32_t LSR(const uint32_t value, const uint32_t amount, + bool *success) { + *success = true; + if (amount == 0) + return value; + uint32_t dont_care; + uint32_t result = LSR_C(value, amount, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t ASR_C(const uint32_t value, const uint32_t amount, + uint32_t &carry_out, bool *success) { + if (amount == 0 || amount > 32) { + *success = false; + return 0; + } + *success = true; + bool negative = BitIsSet(value, 31); + if (amount <= 32) { + carry_out = Bit32(value, amount - 1); + int64_t extended = llvm::SignExtend64<32>(value); + return UnsignedBits(extended, amount + 31, amount); + } else { + carry_out = (negative ? 1 : 0); + return (negative ? 0xffffffff : 0); + } +} + +static inline uint32_t ASR(const uint32_t value, const uint32_t amount, + bool *success) { + *success = true; + if (amount == 0) + return value; + uint32_t dont_care; + uint32_t result = ASR_C(value, amount, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t ROR_C(const uint32_t value, const uint32_t amount, + uint32_t &carry_out, bool *success) { + if (amount == 0) { + *success = false; + return 0; + } + *success = true; + uint32_t result = llvm::rotr<uint32_t>(value, amount); + carry_out = Bit32(value, 31); + return result; +} + +static inline uint32_t ROR(const uint32_t value, const uint32_t amount, + bool *success) { + *success = true; + if (amount == 0) + return value; + uint32_t dont_care; + uint32_t result = ROR_C(value, amount, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t RRX_C(const uint32_t value, const uint32_t carry_in, + uint32_t &carry_out, bool *success) { + *success = true; + carry_out = Bit32(value, 0); + return Bit32(carry_in, 0) << 31 | Bits32(value, 31, 1); +} + +static inline uint32_t RRX(const uint32_t value, const uint32_t carry_in, + bool *success) { + *success = true; + uint32_t dont_care; + uint32_t result = RRX_C(value, carry_in, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t Shift_C(const uint32_t value, ARM_ShifterType type, + const uint32_t amount, const uint32_t carry_in, + uint32_t &carry_out, bool *success) { + if (type == SRType_RRX && amount != 1) { + *success = false; + return 0; + } + *success = true; + + if (amount == 0) { + carry_out = carry_in; + return value; + } + uint32_t result; + switch (type) { + case SRType_LSL: + result = LSL_C(value, amount, carry_out, success); + break; + case SRType_LSR: + result = LSR_C(value, amount, carry_out, success); + break; + case SRType_ASR: + result = ASR_C(value, amount, carry_out, success); + break; + case SRType_ROR: + result = ROR_C(value, amount, carry_out, success); + break; + case SRType_RRX: + result = RRX_C(value, carry_in, carry_out, success); + break; + default: + *success = false; + break; + } + if (*success) + return result; + else + return 0; +} + +static inline uint32_t Shift(const uint32_t value, ARM_ShifterType type, + const uint32_t amount, const uint32_t carry_in, + bool *success) { + // Don't care about carry out in this case. + uint32_t dont_care; + uint32_t result = Shift_C(value, type, amount, carry_in, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t bits(const uint32_t val, const uint32_t msbit, + const uint32_t lsbit) { + return Bits32(val, msbit, lsbit); +} + +static inline uint32_t bit(const uint32_t val, const uint32_t msbit) { + return bits(val, msbit, msbit); +} + +static uint32_t ror(uint32_t val, uint32_t N, uint32_t shift) { + uint32_t m = shift % N; + return (val >> m) | (val << (N - m)); +} + +// (imm32, carry_out) = ARMExpandImm_C(imm12, carry_in) +static inline uint32_t ARMExpandImm_C(uint32_t opcode, uint32_t carry_in, + uint32_t &carry_out) { + uint32_t imm32; // the expanded result + uint32_t imm = bits(opcode, 7, 0); // immediate value + uint32_t amt = 2 * bits(opcode, 11, 8); // rotate amount + if (amt == 0) { + imm32 = imm; + carry_out = carry_in; + } else { + imm32 = ror(imm, 32, amt); + carry_out = Bit32(imm32, 31); + } + return imm32; +} + +static inline uint32_t ARMExpandImm(uint32_t opcode) { + // 'carry_in' argument to following function call does not affect the imm32 + // result. + uint32_t carry_in = 0; + uint32_t carry_out; + return ARMExpandImm_C(opcode, carry_in, carry_out); +} + +// (imm32, carry_out) = ThumbExpandImm_C(imm12, carry_in) +static inline uint32_t ThumbExpandImm_C(uint32_t opcode, uint32_t carry_in, + uint32_t &carry_out) { + uint32_t imm32 = 0; // the expanded result + const uint32_t i = bit(opcode, 26); + const uint32_t imm3 = bits(opcode, 14, 12); + const uint32_t abcdefgh = bits(opcode, 7, 0); + const uint32_t imm12 = i << 11 | imm3 << 8 | abcdefgh; + + if (bits(imm12, 11, 10) == 0) { + switch (bits(imm12, 9, 8)) { + default: // Keep static analyzer happy with a default case + break; + + case 0: + imm32 = abcdefgh; + break; + + case 1: + imm32 = abcdefgh << 16 | abcdefgh; + break; + + case 2: + imm32 = abcdefgh << 24 | abcdefgh << 8; + break; + + case 3: + imm32 = abcdefgh << 24 | abcdefgh << 16 | abcdefgh << 8 | abcdefgh; + break; + } + carry_out = carry_in; + } else { + const uint32_t unrotated_value = 0x80 | bits(imm12, 6, 0); + imm32 = ror(unrotated_value, 32, bits(imm12, 11, 7)); + carry_out = Bit32(imm32, 31); + } + return imm32; +} + +static inline uint32_t ThumbExpandImm(uint32_t opcode) { + // 'carry_in' argument to following function call does not affect the imm32 + // result. + uint32_t carry_in = 0; + uint32_t carry_out; + return ThumbExpandImm_C(opcode, carry_in, carry_out); +} + +// imm32 = ZeroExtend(i:imm3:imm8, 32) +static inline uint32_t ThumbImm12(uint32_t opcode) { + const uint32_t i = bit(opcode, 26); + const uint32_t imm3 = bits(opcode, 14, 12); + const uint32_t imm8 = bits(opcode, 7, 0); + const uint32_t imm12 = i << 11 | imm3 << 8 | imm8; + return imm12; +} + +// imm32 = ZeroExtend(imm7:'00', 32) +static inline uint32_t ThumbImm7Scaled(uint32_t opcode) { + const uint32_t imm7 = bits(opcode, 6, 0); + return imm7 * 4; +} + +// imm32 = ZeroExtend(imm8:'00', 32) +static inline uint32_t ThumbImm8Scaled(uint32_t opcode) { + const uint32_t imm8 = bits(opcode, 7, 0); + return imm8 * 4; +} + +// This function performs the check for the register numbers 13 and 15 that are +// not permitted for many Thumb register specifiers. +static inline bool BadReg(uint32_t n) { return n == 13 || n == 15; } + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_ARMUTILS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp new file mode 100644 index 000000000000..f495ffb1924e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.cpp @@ -0,0 +1,98 @@ +//===-- AuxVector.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 "AuxVector.h" +#include <optional> + +AuxVector::AuxVector(const lldb_private::DataExtractor &data) { + ParseAuxv(data); +} + +void AuxVector::ParseAuxv(const lldb_private::DataExtractor &data) { + lldb::offset_t offset = 0; + const size_t value_type_size = data.GetAddressByteSize() * 2; + while (data.ValidOffsetForDataOfSize(offset, value_type_size)) { + // We're not reading an address but an int that could be 32 or 64 bit + // depending on the address size, which is what GetAddress does. + const uint64_t type = data.GetAddress(&offset); + const uint64_t value = data.GetAddress(&offset); + if (type == AUXV_AT_NULL) + break; + if (type == AUXV_AT_IGNORE) + continue; + + m_auxv_entries[type] = value; + } +} + +std::optional<uint64_t> +AuxVector::GetAuxValue(enum EntryType entry_type) const { + auto it = m_auxv_entries.find(static_cast<uint64_t>(entry_type)); + if (it != m_auxv_entries.end()) + return it->second; + return std::nullopt; +} + +void AuxVector::DumpToLog(lldb_private::Log *log) const { + if (!log) + return; + + log->PutCString("AuxVector: "); + for (auto entry : m_auxv_entries) { + LLDB_LOGF(log, " %s [%" PRIu64 "]: %" PRIx64, + GetEntryName(static_cast<EntryType>(entry.first)), entry.first, + entry.second); + } +} + +const char *AuxVector::GetEntryName(EntryType type) const { + const char *name = "AT_???"; + +#define ENTRY_NAME(_type) \ + _type: \ + name = &#_type[5] + switch (type) { + case ENTRY_NAME(AUXV_AT_NULL); break; + case ENTRY_NAME(AUXV_AT_IGNORE); break; + case ENTRY_NAME(AUXV_AT_EXECFD); break; + case ENTRY_NAME(AUXV_AT_PHDR); break; + case ENTRY_NAME(AUXV_AT_PHENT); break; + case ENTRY_NAME(AUXV_AT_PHNUM); break; + case ENTRY_NAME(AUXV_AT_PAGESZ); break; + case ENTRY_NAME(AUXV_AT_BASE); break; + case ENTRY_NAME(AUXV_AT_FLAGS); break; + case ENTRY_NAME(AUXV_AT_ENTRY); break; + case ENTRY_NAME(AUXV_AT_NOTELF); break; + case ENTRY_NAME(AUXV_AT_UID); break; + case ENTRY_NAME(AUXV_AT_EUID); break; + case ENTRY_NAME(AUXV_AT_GID); break; + case ENTRY_NAME(AUXV_AT_EGID); break; + case ENTRY_NAME(AUXV_AT_CLKTCK); break; + case ENTRY_NAME(AUXV_AT_PLATFORM); break; + case ENTRY_NAME(AUXV_AT_HWCAP); break; + case ENTRY_NAME(AUXV_AT_FPUCW); break; + case ENTRY_NAME(AUXV_AT_DCACHEBSIZE); break; + case ENTRY_NAME(AUXV_AT_ICACHEBSIZE); break; + case ENTRY_NAME(AUXV_AT_UCACHEBSIZE); break; + case ENTRY_NAME(AUXV_AT_IGNOREPPC); break; + case ENTRY_NAME(AUXV_AT_SECURE); break; + case ENTRY_NAME(AUXV_AT_BASE_PLATFORM); break; + case ENTRY_NAME(AUXV_AT_RANDOM); break; + case ENTRY_NAME(AUXV_AT_HWCAP2); break; + case ENTRY_NAME(AUXV_AT_EXECFN); break; + case ENTRY_NAME(AUXV_AT_SYSINFO); break; + case ENTRY_NAME(AUXV_AT_SYSINFO_EHDR); break; + case ENTRY_NAME(AUXV_AT_L1I_CACHESHAPE); break; + case ENTRY_NAME(AUXV_AT_L1D_CACHESHAPE); break; + case ENTRY_NAME(AUXV_AT_L2_CACHESHAPE); break; + case ENTRY_NAME(AUXV_AT_L3_CACHESHAPE); break; + } +#undef ENTRY_NAME + + return name; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h new file mode 100644 index 000000000000..2670b34f6b0a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/AuxVector.h @@ -0,0 +1,84 @@ +//===-- AuxVector.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_SOURCE_PLUGINS_PROCESS_UTILITY_AUXVECTOR_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_AUXVECTOR_H + +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Log.h" +#include <optional> +#include <unordered_map> + +class AuxVector { + +public: + AuxVector(const lldb_private::DataExtractor &data); + + /// Constants describing the type of entry. + /// On Linux and FreeBSD, running "LD_SHOW_AUXV=1 ./executable" will spew AUX + /// information. Added AUXV prefix to avoid potential conflicts with system- + /// defined macros. For FreeBSD, the numbers can be found in sys/elf_common.h. + enum EntryType { + AUXV_AT_NULL = 0, ///< End of auxv. + AUXV_AT_IGNORE = 1, ///< Ignore entry. + AUXV_AT_EXECFD = 2, ///< File descriptor of program. + AUXV_AT_PHDR = 3, ///< Program headers. + AUXV_AT_PHENT = 4, ///< Size of program header. + AUXV_AT_PHNUM = 5, ///< Number of program headers. + AUXV_AT_PAGESZ = 6, ///< Page size. + AUXV_AT_BASE = 7, ///< Interpreter base address. + AUXV_AT_FLAGS = 8, ///< Flags. + AUXV_AT_ENTRY = 9, ///< Program entry point. + AUXV_AT_NOTELF = 10, ///< Set if program is not an ELF. + AUXV_AT_UID = 11, ///< UID. + AUXV_AT_EUID = 12, ///< Effective UID. + AUXV_AT_GID = 13, ///< GID. + AUXV_AT_EGID = 14, ///< Effective GID. + + // At this point Linux and FreeBSD diverge and many of the following values + // are Linux specific. If you use them make sure you are in Linux specific + // code or they have the same value on other platforms. + + AUXV_AT_CLKTCK = 17, ///< Clock frequency (e.g. times(2)). + AUXV_AT_PLATFORM = 15, ///< String identifying platform. + AUXV_AT_HWCAP = + 16, ///< Machine dependent hints about processor capabilities. + AUXV_AT_FPUCW = 18, ///< Used FPU control word. + AUXV_AT_DCACHEBSIZE = 19, ///< Data cache block size. + AUXV_AT_ICACHEBSIZE = 20, ///< Instruction cache block size. + AUXV_AT_UCACHEBSIZE = 21, ///< Unified cache block size. + AUXV_AT_IGNOREPPC = 22, ///< Entry should be ignored. + AUXV_AT_SECURE = 23, ///< Boolean, was exec setuid-like? + AUXV_AT_BASE_PLATFORM = 24, ///< String identifying real platforms. + AUXV_AT_RANDOM = 25, ///< Address of 16 random bytes. + AUXV_AT_HWCAP2 = 26, ///< Extension of AT_HWCAP. + AUXV_AT_EXECFN = 31, ///< Filename of executable. + AUXV_AT_SYSINFO = 32, ///< Pointer to the global system page used for system + /// calls and other nice things. + AUXV_AT_SYSINFO_EHDR = 33, + AUXV_AT_L1I_CACHESHAPE = 34, ///< Shapes of the caches. + AUXV_AT_L1D_CACHESHAPE = 35, + AUXV_AT_L2_CACHESHAPE = 36, + AUXV_AT_L3_CACHESHAPE = 37, + + // Platform specific values which may overlap the Linux values. + + AUXV_FREEBSD_AT_HWCAP = 25, ///< FreeBSD specific AT_HWCAP value. + }; + + std::optional<uint64_t> GetAuxValue(enum EntryType entry_type) const; + void DumpToLog(lldb_private::Log *log) const; + const char *GetEntryName(EntryType type) const; + +private: + void ParseAuxv(const lldb_private::DataExtractor &data); + + std::unordered_map<uint64_t, uint64_t> m_auxv_entries; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp new file mode 100644 index 000000000000..ebf197339446 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.cpp @@ -0,0 +1,139 @@ +//===-- FreeBSDSignals.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 "FreeBSDSignals.h" + +#ifdef __FreeBSD__ +#include <csignal> + +#ifndef FPE_FLTIDO +#define FPE_FLTIDO 9 +#endif + +#define ADD_SIGCODE(signal_name, signal_value, code_name, code_value, ...) \ + static_assert(signal_name == signal_value, \ + "Value mismatch for signal number " #signal_name); \ + static_assert(code_name == code_value, \ + "Value mismatch for signal code " #code_name); \ + AddSignalCode(signal_value, code_value, __VA_ARGS__) +#else +#define ADD_SIGCODE(signal_name, signal_value, code_name, code_value, ...) \ + AddSignalCode(signal_value, code_value, __VA_ARGS__) +#endif /* ifdef __FreeBSD */ + +using namespace lldb_private; + +FreeBSDSignals::FreeBSDSignals() : UnixSignals() { Reset(); } + +void FreeBSDSignals::Reset() { + UnixSignals::Reset(); + + // clang-format off + // SIGILL + ADD_SIGCODE(SIGILL, 4, ILL_ILLOPC, 1, "illegal opcode"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLOPN, 2, "illegal operand"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLADR, 3, "illegal addressing mode"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLTRP, 4, "illegal trap"); + ADD_SIGCODE(SIGILL, 4, ILL_PRVOPC, 5, "privileged opcode"); + ADD_SIGCODE(SIGILL, 4, ILL_PRVREG, 6, "privileged register"); + ADD_SIGCODE(SIGILL, 4, ILL_COPROC, 7, "coprocessor error"); + ADD_SIGCODE(SIGILL, 4, ILL_BADSTK, 8, "internal stack error"); + + // SIGFPE + ADD_SIGCODE(SIGFPE, 8, FPE_INTOVF, 1, "integer overflow"); + ADD_SIGCODE(SIGFPE, 8, FPE_INTDIV, 2, "integer divide by zero"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTDIV, 3, "floating point divide by zero"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTOVF, 4, "floating point overflow"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTUND, 5, "floating point underflow"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTRES, 6, "floating point inexact result"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTINV, 7, "invalid floating point operation"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTSUB, 8, "subscript out of range"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTIDO, 9, "input denormal operation"); + + // SIGBUS + ADD_SIGCODE(SIGBUS, 10, BUS_ADRALN, 1, "invalid address alignment"); + ADD_SIGCODE(SIGBUS, 10, BUS_ADRERR, 2, "nonexistent physical address"); + ADD_SIGCODE(SIGBUS, 10, BUS_OBJERR, 3, "object-specific hardware error"); + ADD_SIGCODE(SIGBUS, 10, BUS_OOMERR, 100, "no memory"); + + // SIGSEGV + ADD_SIGCODE(SIGSEGV, 11, SEGV_MAPERR, 1, "address not mapped to object", + SignalCodePrintOption::Address); + ADD_SIGCODE(SIGSEGV, 11, SEGV_ACCERR, 2, "invalid permissions for mapped object", + SignalCodePrintOption::Address); + ADD_SIGCODE(SIGSEGV, 11, SEGV_PKUERR, 100, "PKU violation", + SignalCodePrintOption::Address); + + // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION + // ===== ============== ======== ====== ====== ======================== + AddSignal(32, "SIGTHR", false, false, false, "thread interrupt"); + AddSignal(33, "SIGLIBRT", false, false, false, "reserved by real-time library"); + AddSignal(65, "SIGRTMIN", false, false, false, "real time signal 0"); + AddSignal(66, "SIGRTMIN+1", false, false, false, "real time signal 1"); + AddSignal(67, "SIGRTMIN+2", false, false, false, "real time signal 2"); + AddSignal(68, "SIGRTMIN+3", false, false, false, "real time signal 3"); + AddSignal(69, "SIGRTMIN+4", false, false, false, "real time signal 4"); + AddSignal(70, "SIGRTMIN+5", false, false, false, "real time signal 5"); + AddSignal(71, "SIGRTMIN+6", false, false, false, "real time signal 6"); + AddSignal(72, "SIGRTMIN+7", false, false, false, "real time signal 7"); + AddSignal(73, "SIGRTMIN+8", false, false, false, "real time signal 8"); + AddSignal(74, "SIGRTMIN+9", false, false, false, "real time signal 9"); + AddSignal(75, "SIGRTMIN+10", false, false, false, "real time signal 10"); + AddSignal(76, "SIGRTMIN+11", false, false, false, "real time signal 11"); + AddSignal(77, "SIGRTMIN+12", false, false, false, "real time signal 12"); + AddSignal(78, "SIGRTMIN+13", false, false, false, "real time signal 13"); + AddSignal(79, "SIGRTMIN+14", false, false, false, "real time signal 14"); + AddSignal(80, "SIGRTMIN+15", false, false, false, "real time signal 15"); + AddSignal(81, "SIGRTMIN+16", false, false, false, "real time signal 16"); + AddSignal(82, "SIGRTMIN+17", false, false, false, "real time signal 17"); + AddSignal(83, "SIGRTMIN+18", false, false, false, "real time signal 18"); + AddSignal(84, "SIGRTMIN+19", false, false, false, "real time signal 19"); + AddSignal(85, "SIGRTMIN+20", false, false, false, "real time signal 20"); + AddSignal(86, "SIGRTMIN+21", false, false, false, "real time signal 21"); + AddSignal(87, "SIGRTMIN+22", false, false, false, "real time signal 22"); + AddSignal(88, "SIGRTMIN+23", false, false, false, "real time signal 23"); + AddSignal(89, "SIGRTMIN+24", false, false, false, "real time signal 24"); + AddSignal(90, "SIGRTMIN+25", false, false, false, "real time signal 25"); + AddSignal(91, "SIGRTMIN+26", false, false, false, "real time signal 26"); + AddSignal(92, "SIGRTMIN+27", false, false, false, "real time signal 27"); + AddSignal(93, "SIGRTMIN+28", false, false, false, "real time signal 28"); + AddSignal(94, "SIGRTMIN+29", false, false, false, "real time signal 29"); + AddSignal(95, "SIGRTMIN+30", false, false, false, "real time signal 30"); + AddSignal(96, "SIGRTMAX-30", false, false, false, "real time signal 31"); + AddSignal(97, "SIGRTMAX-29", false, false, false, "real time signal 32"); + AddSignal(98, "SIGRTMAX-28", false, false, false, "real time signal 33"); + AddSignal(99, "SIGRTMAX-27", false, false, false, "real time signal 34"); + AddSignal(100, "SIGRTMAX-26", false, false, false, "real time signal 35"); + AddSignal(101, "SIGRTMAX-25", false, false, false, "real time signal 36"); + AddSignal(102, "SIGRTMAX-24", false, false, false, "real time signal 37"); + AddSignal(103, "SIGRTMAX-23", false, false, false, "real time signal 38"); + AddSignal(104, "SIGRTMAX-22", false, false, false, "real time signal 39"); + AddSignal(105, "SIGRTMAX-21", false, false, false, "real time signal 40"); + AddSignal(106, "SIGRTMAX-20", false, false, false, "real time signal 41"); + AddSignal(107, "SIGRTMAX-19", false, false, false, "real time signal 42"); + AddSignal(108, "SIGRTMAX-18", false, false, false, "real time signal 43"); + AddSignal(109, "SIGRTMAX-17", false, false, false, "real time signal 44"); + AddSignal(110, "SIGRTMAX-16", false, false, false, "real time signal 45"); + AddSignal(111, "SIGRTMAX-15", false, false, false, "real time signal 46"); + AddSignal(112, "SIGRTMAX-14", false, false, false, "real time signal 47"); + AddSignal(113, "SIGRTMAX-13", false, false, false, "real time signal 48"); + AddSignal(114, "SIGRTMAX-12", false, false, false, "real time signal 49"); + AddSignal(115, "SIGRTMAX-11", false, false, false, "real time signal 50"); + AddSignal(116, "SIGRTMAX-10", false, false, false, "real time signal 51"); + AddSignal(117, "SIGRTMAX-9", false, false, false, "real time signal 52"); + AddSignal(118, "SIGRTMAX-8", false, false, false, "real time signal 53"); + AddSignal(119, "SIGRTMAX-7", false, false, false, "real time signal 54"); + AddSignal(120, "SIGRTMAX-6", false, false, false, "real time signal 55"); + AddSignal(121, "SIGRTMAX-5", false, false, false, "real time signal 56"); + AddSignal(122, "SIGRTMAX-4", false, false, false, "real time signal 57"); + AddSignal(123, "SIGRTMAX-3", false, false, false, "real time signal 58"); + AddSignal(124, "SIGRTMAX-2", false, false, false, "real time signal 59"); + AddSignal(125, "SIGRTMAX-1", false, false, false, "real time signal 60"); + AddSignal(126, "SIGRTMAX", false, false, false, "real time signal 61"); + // clang-format on +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h new file mode 100644 index 000000000000..c4c810e54985 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/FreeBSDSignals.h @@ -0,0 +1,27 @@ +//===-- FreeBSDSignals.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_SOURCE_PLUGINS_PROCESS_UTILITY_FREEBSDSIGNALS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_FREEBSDSIGNALS_H + +#include "lldb/Target/UnixSignals.h" + +namespace lldb_private { + +/// FreeBSD specific set of Unix signals. +class FreeBSDSignals : public UnixSignals { +public: + FreeBSDSignals(); + +private: + void Reset() override; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_FREEBSDSIGNALS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp new file mode 100644 index 000000000000..15981a2c1cb8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp @@ -0,0 +1,181 @@ +//===-- GDBRemoteSignals.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 "GDBRemoteSignals.h" + +using namespace lldb_private; + +GDBRemoteSignals::GDBRemoteSignals() : UnixSignals() { Reset(); } + +GDBRemoteSignals::GDBRemoteSignals(const lldb::UnixSignalsSP &rhs) + : UnixSignals(*rhs) {} + +void GDBRemoteSignals::Reset() { + m_signals.clear(); + // clang-format off + // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION + // ====== ============== ======== ====== ====== =================================================== + AddSignal(1, "SIGHUP", false, true, true, "hangup"); + AddSignal(2, "SIGINT", true, true, true, "interrupt"); + AddSignal(3, "SIGQUIT", false, true, true, "quit"); + AddSignal(4, "SIGILL", false, true, true, "illegal instruction"); + AddSignal(5, "SIGTRAP", true, true, true, "trace trap (not reset when caught)"); + AddSignal(6, "SIGABRT", false, true, true, "abort()/IOT trap", "SIGIOT"); + AddSignal(7, "SIGEMT", false, true, true, "emulation trap"); + AddSignal(8, "SIGFPE", false, true, true, "floating point exception"); + AddSignal(9, "SIGKILL", false, true, true, "kill"); + AddSignal(10, "SIGBUS", false, true, true, "bus error"); + AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation"); + AddSignal(12, "SIGSYS", false, true, true, "invalid system call"); + AddSignal(13, "SIGPIPE", false, true, true, "write to pipe with reading end closed"); + AddSignal(14, "SIGALRM", false, false, false, "alarm"); + AddSignal(15, "SIGTERM", false, true, true, "termination requested"); + AddSignal(16, "SIGURG", false, true, true, "urgent data on socket"); + AddSignal(17, "SIGSTOP", true, true, true, "process stop"); + AddSignal(18, "SIGTSTP", false, true, true, "tty stop"); + AddSignal(19, "SIGCONT", false, false, true, "process continue"); + AddSignal(20, "SIGCHLD", false, false, true, "child status has changed", "SIGCLD"); + AddSignal(21, "SIGTTIN", false, true, true, "background tty read"); + AddSignal(22, "SIGTTOU", false, true, true, "background tty write"); + AddSignal(23, "SIGIO", false, true, true, "input/output ready/Pollable event"); + AddSignal(24, "SIGXCPU", false, true, true, "CPU resource exceeded"); + AddSignal(25, "SIGXFSZ", false, true, true, "file size limit exceeded"); + AddSignal(26, "SIGVTALRM", false, true, true, "virtual time alarm"); + AddSignal(27, "SIGPROF", false, false, false, "profiling time alarm"); + AddSignal(28, "SIGWINCH", false, true, true, "window size changes"); + AddSignal(29, "SIGLOST", false, true, true, "resource lost"); + AddSignal(30, "SIGUSR1", false, true, true, "user defined signal 1"); + AddSignal(31, "SIGUSR2", false, true, true, "user defined signal 2"); + AddSignal(32, "SIGPWR", false, true, true, "power failure"); + AddSignal(33, "SIGPOLL", false, true, true, "pollable event"); + AddSignal(34, "SIGWIND", false, true, true, "SIGWIND"); + AddSignal(35, "SIGPHONE", false, true, true, "SIGPHONE"); + AddSignal(36, "SIGWAITING", false, true, true, "process's LWPs are blocked"); + AddSignal(37, "SIGLWP", false, true, true, "signal LWP"); + AddSignal(38, "SIGDANGER", false, true, true, "swap space dangerously low"); + AddSignal(39, "SIGGRANT", false, true, true, "monitor mode granted"); + AddSignal(40, "SIGRETRACT", false, true, true, "need to relinquish monitor mode"); + AddSignal(41, "SIGMSG", false, true, true, "monitor mode data available"); + AddSignal(42, "SIGSOUND", false, true, true, "sound completed"); + AddSignal(43, "SIGSAK", false, true, true, "secure attention"); + AddSignal(44, "SIGPRIO", false, true, true, "SIGPRIO"); + + AddSignal(45, "SIG33", false, false, false, "real-time event 33"); + AddSignal(46, "SIG34", false, false, false, "real-time event 34"); + AddSignal(47, "SIG35", false, false, false, "real-time event 35"); + AddSignal(48, "SIG36", false, false, false, "real-time event 36"); + AddSignal(49, "SIG37", false, false, false, "real-time event 37"); + AddSignal(50, "SIG38", false, false, false, "real-time event 38"); + AddSignal(51, "SIG39", false, false, false, "real-time event 39"); + AddSignal(52, "SIG40", false, false, false, "real-time event 40"); + AddSignal(53, "SIG41", false, false, false, "real-time event 41"); + AddSignal(54, "SIG42", false, false, false, "real-time event 42"); + AddSignal(55, "SIG43", false, false, false, "real-time event 43"); + AddSignal(56, "SIG44", false, false, false, "real-time event 44"); + AddSignal(57, "SIG45", false, false, false, "real-time event 45"); + AddSignal(58, "SIG46", false, false, false, "real-time event 46"); + AddSignal(59, "SIG47", false, false, false, "real-time event 47"); + AddSignal(60, "SIG48", false, false, false, "real-time event 48"); + AddSignal(61, "SIG49", false, false, false, "real-time event 49"); + AddSignal(62, "SIG50", false, false, false, "real-time event 50"); + AddSignal(63, "SIG51", false, false, false, "real-time event 51"); + AddSignal(64, "SIG52", false, false, false, "real-time event 52"); + AddSignal(65, "SIG53", false, false, false, "real-time event 53"); + AddSignal(66, "SIG54", false, false, false, "real-time event 54"); + AddSignal(67, "SIG55", false, false, false, "real-time event 55"); + AddSignal(68, "SIG56", false, false, false, "real-time event 56"); + AddSignal(69, "SIG57", false, false, false, "real-time event 57"); + AddSignal(70, "SIG58", false, false, false, "real-time event 58"); + AddSignal(71, "SIG59", false, false, false, "real-time event 59"); + AddSignal(72, "SIG60", false, false, false, "real-time event 60"); + AddSignal(73, "SIG61", false, false, false, "real-time event 61"); + AddSignal(74, "SIG62", false, false, false, "real-time event 62"); + AddSignal(75, "SIG63", false, false, false, "real-time event 63"); + + AddSignal(76, "SIGCANCEL", false, true, true, "LWP internal signal"); + + AddSignal(77, "SIG32", false, false, false, "real-time event 32"); + AddSignal(78, "SIG64", false, false, false, "real-time event 64"); + AddSignal(79, "SIG65", false, false, false, "real-time event 65"); + AddSignal(80, "SIG66", false, false, false, "real-time event 66"); + AddSignal(81, "SIG67", false, false, false, "real-time event 67"); + AddSignal(82, "SIG68", false, false, false, "real-time event 68"); + AddSignal(83, "SIG69", false, false, false, "real-time event 69"); + AddSignal(84, "SIG70", false, false, false, "real-time event 70"); + AddSignal(85, "SIG71", false, false, false, "real-time event 71"); + AddSignal(86, "SIG72", false, false, false, "real-time event 72"); + AddSignal(87, "SIG73", false, false, false, "real-time event 73"); + AddSignal(88, "SIG74", false, false, false, "real-time event 74"); + AddSignal(89, "SIG75", false, false, false, "real-time event 75"); + AddSignal(90, "SIG76", false, false, false, "real-time event 76"); + AddSignal(91, "SIG77", false, false, false, "real-time event 77"); + AddSignal(92, "SIG78", false, false, false, "real-time event 78"); + AddSignal(93, "SIG79", false, false, false, "real-time event 79"); + AddSignal(94, "SIG80", false, false, false, "real-time event 80"); + AddSignal(95, "SIG81", false, false, false, "real-time event 81"); + AddSignal(96, "SIG82", false, false, false, "real-time event 82"); + AddSignal(97, "SIG83", false, false, false, "real-time event 83"); + AddSignal(98, "SIG84", false, false, false, "real-time event 84"); + AddSignal(99, "SIG85", false, false, false, "real-time event 85"); + AddSignal(100, "SIG86", false, false, false, "real-time event 86"); + AddSignal(101, "SIG87", false, false, false, "real-time event 87"); + AddSignal(102, "SIG88", false, false, false, "real-time event 88"); + AddSignal(103, "SIG89", false, false, false, "real-time event 89"); + AddSignal(104, "SIG90", false, false, false, "real-time event 90"); + AddSignal(105, "SIG91", false, false, false, "real-time event 91"); + AddSignal(106, "SIG92", false, false, false, "real-time event 92"); + AddSignal(107, "SIG93", false, false, false, "real-time event 93"); + AddSignal(108, "SIG94", false, false, false, "real-time event 94"); + AddSignal(109, "SIG95", false, false, false, "real-time event 95"); + AddSignal(110, "SIG96", false, false, false, "real-time event 96"); + AddSignal(111, "SIG97", false, false, false, "real-time event 97"); + AddSignal(112, "SIG98", false, false, false, "real-time event 98"); + AddSignal(113, "SIG99", false, false, false, "real-time event 99"); + AddSignal(114, "SIG100", false, false, false, "real-time event 100"); + AddSignal(115, "SIG101", false, false, false, "real-time event 101"); + AddSignal(116, "SIG102", false, false, false, "real-time event 102"); + AddSignal(117, "SIG103", false, false, false, "real-time event 103"); + AddSignal(118, "SIG104", false, false, false, "real-time event 104"); + AddSignal(119, "SIG105", false, false, false, "real-time event 105"); + AddSignal(120, "SIG106", false, false, false, "real-time event 106"); + AddSignal(121, "SIG107", false, false, false, "real-time event 107"); + AddSignal(122, "SIG108", false, false, false, "real-time event 108"); + AddSignal(123, "SIG109", false, false, false, "real-time event 109"); + AddSignal(124, "SIG110", false, false, false, "real-time event 110"); + AddSignal(125, "SIG111", false, false, false, "real-time event 111"); + AddSignal(126, "SIG112", false, false, false, "real-time event 112"); + AddSignal(127, "SIG113", false, false, false, "real-time event 113"); + AddSignal(128, "SIG114", false, false, false, "real-time event 114"); + AddSignal(129, "SIG115", false, false, false, "real-time event 115"); + AddSignal(130, "SIG116", false, false, false, "real-time event 116"); + AddSignal(131, "SIG117", false, false, false, "real-time event 117"); + AddSignal(132, "SIG118", false, false, false, "real-time event 118"); + AddSignal(133, "SIG119", false, false, false, "real-time event 119"); + AddSignal(134, "SIG120", false, false, false, "real-time event 120"); + AddSignal(135, "SIG121", false, false, false, "real-time event 121"); + AddSignal(136, "SIG122", false, false, false, "real-time event 122"); + AddSignal(137, "SIG123", false, false, false, "real-time event 123"); + AddSignal(138, "SIG124", false, false, false, "real-time event 124"); + AddSignal(139, "SIG125", false, false, false, "real-time event 125"); + AddSignal(140, "SIG126", false, false, false, "real-time event 126"); + AddSignal(141, "SIG127", false, false, false, "real-time event 127"); + + AddSignal(142, "SIGINFO", false, true, true, "information request"); + AddSignal(143, "unknown", false, true, true, "unknown signal"); + + AddSignal(145, "EXC_BAD_ACCESS", false, true, true, "could not access memory"); + AddSignal(146, "EXC_BAD_INSTRUCTION", false, true, true, "illegal instruction/operand"); + AddSignal(147, "EXC_ARITHMETIC", false, true, true, "arithmetic exception"); + AddSignal(148, "EXC_EMULATION", false, true, true, "emulation instruction"); + AddSignal(149, "EXC_SOFTWARE", false, true, true, "software generated exception"); + AddSignal(150, "EXC_BREAKPOINT", false, true, true, "breakpoint"); + + AddSignal(151, "SIGLIBRT", false, true, true, "librt internal signal"); + + // clang-format on +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h new file mode 100644 index 000000000000..4c260b94eba8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.h @@ -0,0 +1,30 @@ +//===-- GDBRemoteSignals.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_SOURCE_PLUGINS_PROCESS_UTILITY_GDBREMOTESIGNALS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_GDBREMOTESIGNALS_H + +#include "lldb/Target/UnixSignals.h" + +namespace lldb_private { + +/// Initially carries signals defined by the GDB Remote Serial Protocol. +/// Can be filled with platform's signals through PlatformRemoteGDBServer. +class GDBRemoteSignals : public UnixSignals { +public: + GDBRemoteSignals(); + + GDBRemoteSignals(const lldb::UnixSignalsSP &rhs); + +private: + void Reset() override; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_GDBREMOTESIGNALS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp new file mode 100644 index 000000000000..bc06757c806a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.cpp @@ -0,0 +1,85 @@ +//===-- HistoryThread.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" + +#include "Plugins/Process/Utility/HistoryThread.h" + +#include "Plugins/Process/Utility/HistoryUnwind.h" +#include "Plugins/Process/Utility/RegisterContextHistory.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrameList.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +// Constructor + +HistoryThread::HistoryThread(lldb_private::Process &process, lldb::tid_t tid, + std::vector<lldb::addr_t> pcs, + bool pcs_are_call_addresses) + : Thread(process, tid, true), m_framelist_mutex(), m_framelist(), + m_pcs(pcs), m_extended_unwind_token(LLDB_INVALID_ADDRESS), m_queue_name(), + m_thread_name(), m_originating_unique_thread_id(tid), + m_queue_id(LLDB_INVALID_QUEUE_ID) { + m_unwinder_up = + std::make_unique<HistoryUnwind>(*this, pcs, pcs_are_call_addresses); + Log *log = GetLog(LLDBLog::Object); + LLDB_LOGF(log, "%p HistoryThread::HistoryThread", static_cast<void *>(this)); +} + +// Destructor + +HistoryThread::~HistoryThread() { + Log *log = GetLog(LLDBLog::Object); + LLDB_LOGF(log, "%p HistoryThread::~HistoryThread (tid=0x%" PRIx64 ")", + static_cast<void *>(this), GetID()); + DestroyThread(); +} + +lldb::RegisterContextSP HistoryThread::GetRegisterContext() { + RegisterContextSP rctx; + if (m_pcs.size() > 0) { + rctx = std::make_shared<RegisterContextHistory>( + *this, 0, GetProcess()->GetAddressByteSize(), m_pcs[0]); + } + return rctx; +} + +lldb::RegisterContextSP +HistoryThread::CreateRegisterContextForFrame(StackFrame *frame) { + return m_unwinder_up->CreateRegisterContextForFrame(frame); +} + +lldb::StackFrameListSP HistoryThread::GetStackFrameList() { + // FIXME do not throw away the lock after we acquire it.. + std::unique_lock<std::mutex> lock(m_framelist_mutex); + lock.unlock(); + if (m_framelist.get() == nullptr) { + m_framelist = + std::make_shared<StackFrameList>(*this, StackFrameListSP(), true); + } + + return m_framelist; +} + +uint32_t HistoryThread::GetExtendedBacktraceOriginatingIndexID() { + if (m_originating_unique_thread_id != LLDB_INVALID_THREAD_ID) { + if (GetProcess()->HasAssignedIndexIDToThread( + m_originating_unique_thread_id)) { + return GetProcess()->AssignIndexIDToThread( + m_originating_unique_thread_id); + } + } + return LLDB_INVALID_THREAD_ID; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h new file mode 100644 index 000000000000..a66e0f2d4207 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryThread.h @@ -0,0 +1,92 @@ +//===-- HistoryThread.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_SOURCE_PLUGINS_PROCESS_UTILITY_HISTORYTHREAD_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_HISTORYTHREAD_H + +#include <mutex> + +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/StackFrameList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/UserID.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +/// \class HistoryThread HistoryThread.h "HistoryThread.h" +/// A thread object representing a backtrace from a previous point in the +/// process execution +/// +/// This subclass of Thread is used to provide a backtrace from earlier in +/// process execution. It is given a backtrace list of pc addresses and it +/// will create stack frames for them. + +class HistoryThread : public lldb_private::Thread { +public: + HistoryThread(lldb_private::Process &process, lldb::tid_t tid, + std::vector<lldb::addr_t> pcs, + bool pcs_are_call_addresses = false); + + ~HistoryThread() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + + void RefreshStateAfterStop() override {} + + bool CalculateStopInfo() override { return false; } + + void SetExtendedBacktraceToken(uint64_t token) override { + m_extended_unwind_token = token; + } + + uint64_t GetExtendedBacktraceToken() override { + return m_extended_unwind_token; + } + + const char *GetQueueName() override { return m_queue_name.c_str(); } + + void SetQueueName(const char *name) override { m_queue_name = name; } + + lldb::queue_id_t GetQueueID() override { return m_queue_id; } + + void SetQueueID(lldb::queue_id_t queue) override { m_queue_id = queue; } + + const char *GetThreadName() { return m_thread_name.c_str(); } + + uint32_t GetExtendedBacktraceOriginatingIndexID() override; + + void SetThreadName(const char *name) { m_thread_name = name; } + + const char *GetName() override { return m_thread_name.c_str(); } + + void SetName(const char *name) override { m_thread_name = name; } + +protected: + virtual lldb::StackFrameListSP GetStackFrameList(); + + mutable std::mutex m_framelist_mutex; + lldb::StackFrameListSP m_framelist; + std::vector<lldb::addr_t> m_pcs; + + uint64_t m_extended_unwind_token; + std::string m_queue_name; + std::string m_thread_name; + lldb::tid_t m_originating_unique_thread_id; + lldb::queue_id_t m_queue_id; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_HISTORYTHREAD_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp new file mode 100644 index 000000000000..7749dc6f5d51 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp @@ -0,0 +1,73 @@ +//===-- HistoryUnwind.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" + +#include "Plugins/Process/Utility/HistoryUnwind.h" +#include "Plugins/Process/Utility/RegisterContextHistory.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +// Constructor + +HistoryUnwind::HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs, + bool pcs_are_call_addresses) + : Unwind(thread), m_pcs(pcs), + m_pcs_are_call_addresses(pcs_are_call_addresses) {} + +// Destructor + +HistoryUnwind::~HistoryUnwind() = default; + +void HistoryUnwind::DoClear() { + std::lock_guard<std::recursive_mutex> guard(m_unwind_mutex); + m_pcs.clear(); +} + +lldb::RegisterContextSP +HistoryUnwind::DoCreateRegisterContextForFrame(StackFrame *frame) { + RegisterContextSP rctx; + if (frame) { + addr_t pc = frame->GetFrameCodeAddress().GetLoadAddress( + &frame->GetThread()->GetProcess()->GetTarget()); + if (pc != LLDB_INVALID_ADDRESS) { + rctx = std::make_shared<RegisterContextHistory>( + *frame->GetThread().get(), frame->GetConcreteFrameIndex(), + frame->GetThread()->GetProcess()->GetAddressByteSize(), pc); + } + } + return rctx; +} + +bool HistoryUnwind::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, + lldb::addr_t &pc, + bool &behaves_like_zeroth_frame) { + // FIXME do not throw away the lock after we acquire it.. + std::unique_lock<std::recursive_mutex> guard(m_unwind_mutex); + guard.unlock(); + if (frame_idx < m_pcs.size()) { + cfa = frame_idx; + pc = m_pcs[frame_idx]; + if (m_pcs_are_call_addresses) + behaves_like_zeroth_frame = true; + else + behaves_like_zeroth_frame = (frame_idx == 0); + return true; + } + return false; +} + +uint32_t HistoryUnwind::DoGetFrameCount() { return m_pcs.size(); } diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h new file mode 100644 index 000000000000..cb72b5d0a176 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/HistoryUnwind.h @@ -0,0 +1,46 @@ +//===-- HistoryUnwind.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_SOURCE_PLUGINS_PROCESS_UTILITY_HISTORYUNWIND_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_HISTORYUNWIND_H + +#include <vector> + +#include "lldb/Target/Unwind.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class HistoryUnwind : public lldb_private::Unwind { +public: + HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs, + bool pcs_are_call_addresses = false); + + ~HistoryUnwind() override; + +protected: + void DoClear() override; + + lldb::RegisterContextSP + DoCreateRegisterContextForFrame(StackFrame *frame) override; + + bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, + lldb::addr_t &pc, + bool &behaves_like_zeroth_frame) override; + uint32_t DoGetFrameCount() override; + +private: + std::vector<lldb::addr_t> m_pcs; + /// This boolean indicates that the PCs in the non-0 frames are call + /// addresses and not return addresses. + bool m_pcs_are_call_addresses; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_HISTORYUNWIND_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp new file mode 100644 index 000000000000..32c71d87c7f5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -0,0 +1,190 @@ +//===-- InferiorCallPOSIX.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 "InferiorCallPOSIX.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Host/Config.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlanCallFunction.h" + +#if LLDB_ENABLE_POSIX +#include <sys/mman.h> +#else +// define them +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 +#endif + +using namespace lldb; +using namespace lldb_private; + +bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, + addr_t addr, addr_t length, unsigned prot, + unsigned flags, addr_t fd, addr_t offset) { + Thread *thread = + process->GetThreadList().GetExpressionExecutionThread().get(); + if (thread == nullptr) + return false; + + ModuleFunctionSearchOptions function_options; + function_options.include_symbols = true; + function_options.include_inlines = false; + + SymbolContextList sc_list; + process->GetTarget().GetImages().FindFunctions( + ConstString("mmap"), eFunctionNameTypeFull, function_options, sc_list); + const uint32_t count = sc_list.GetSize(); + if (count > 0) { + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) { + const uint32_t range_scope = + eSymbolContextFunction | eSymbolContextSymbol; + const bool use_inline_block_range = false; + EvaluateExpressionOptions options; + options.SetStopOthers(true); + options.SetUnwindOnError(true); + options.SetIgnoreBreakpoints(true); + options.SetTryAllThreads(true); + options.SetDebug(false); + options.SetTimeout(process->GetUtilityExpressionTimeout()); + options.SetTrapExceptions(false); + + addr_t prot_arg; + if (prot == eMmapProtNone) + prot_arg = PROT_NONE; + else { + prot_arg = 0; + if (prot & eMmapProtExec) + prot_arg |= PROT_EXEC; + if (prot & eMmapProtRead) + prot_arg |= PROT_READ; + if (prot & eMmapProtWrite) + prot_arg |= PROT_WRITE; + } + + AddressRange mmap_range; + if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, + mmap_range)) { + auto type_system_or_err = + process->GetTarget().GetScratchTypeSystemForLanguage( + eLanguageTypeC); + if (!type_system_or_err) { + llvm::consumeError(type_system_or_err.takeError()); + return false; + } + auto ts = *type_system_or_err; + if (!ts) + return false; + CompilerType void_ptr_type = + ts->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType(); + const ArchSpec arch = process->GetTarget().GetArchitecture(); + MmapArgList args = + process->GetTarget().GetPlatform()->GetMmapArgumentList( + arch, addr, length, prot_arg, flags, fd, offset); + lldb::ThreadPlanSP call_plan_sp( + new ThreadPlanCallFunction(*thread, mmap_range.GetBaseAddress(), + void_ptr_type, args, options)); + if (call_plan_sp) { + DiagnosticManager diagnostics; + + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); + if (frame) { + ExecutionContext exe_ctx; + frame->CalculateExecutionContext(exe_ctx); + ExpressionResults result = process->RunThreadPlan( + exe_ctx, call_plan_sp, options, diagnostics); + if (result == eExpressionCompleted) { + + allocated_addr = + call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned( + LLDB_INVALID_ADDRESS); + if (process->GetAddressByteSize() == 4) { + if (allocated_addr == UINT32_MAX) + return false; + } else if (process->GetAddressByteSize() == 8) { + if (allocated_addr == UINT64_MAX) + return false; + } + return true; + } + } + } + } + } + } + + return false; +} + +bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr, + addr_t length) { + Thread *thread = + process->GetThreadList().GetExpressionExecutionThread().get(); + if (thread == nullptr) + return false; + + ModuleFunctionSearchOptions function_options; + function_options.include_symbols = true; + function_options.include_inlines = false; + + SymbolContextList sc_list; + process->GetTarget().GetImages().FindFunctions( + ConstString("munmap"), eFunctionNameTypeFull, function_options, sc_list); + const uint32_t count = sc_list.GetSize(); + if (count > 0) { + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) { + const uint32_t range_scope = + eSymbolContextFunction | eSymbolContextSymbol; + const bool use_inline_block_range = false; + EvaluateExpressionOptions options; + options.SetStopOthers(true); + options.SetUnwindOnError(true); + options.SetIgnoreBreakpoints(true); + options.SetTryAllThreads(true); + options.SetDebug(false); + options.SetTimeout(process->GetUtilityExpressionTimeout()); + options.SetTrapExceptions(false); + + AddressRange munmap_range; + if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, + munmap_range)) { + lldb::addr_t args[] = {addr, length}; + lldb::ThreadPlanSP call_plan_sp( + new ThreadPlanCallFunction(*thread, munmap_range.GetBaseAddress(), + CompilerType(), args, options)); + if (call_plan_sp) { + DiagnosticManager diagnostics; + + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); + if (frame) { + ExecutionContext exe_ctx; + frame->CalculateExecutionContext(exe_ctx); + ExpressionResults result = process->RunThreadPlan( + exe_ctx, call_plan_sp, options, diagnostics); + if (result == eExpressionCompleted) { + return true; + } + } + } + } + } + } + + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h new file mode 100644 index 000000000000..3623e10194f9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.h @@ -0,0 +1,35 @@ +//===-- InferiorCallPOSIX.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_SOURCE_PLUGINS_PROCESS_UTILITY_INFERIORCALLPOSIX_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_INFERIORCALLPOSIX_H + +// Inferior execution of POSIX functions. + +#include "lldb/lldb-types.h" + +namespace lldb_private { + +class Process; + +enum MmapProt { + eMmapProtNone = 0, + eMmapProtExec = 1, + eMmapProtRead = 2, + eMmapProtWrite = 4 +}; + +bool InferiorCallMmap(Process *proc, lldb::addr_t &allocated_addr, + lldb::addr_t addr, lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, lldb::addr_t offset); + +bool InferiorCallMunmap(Process *proc, lldb::addr_t addr, lldb::addr_t length); + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_INFERIORCALLPOSIX_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h new file mode 100644 index 000000000000..55b89440700b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/InstructionUtils.h @@ -0,0 +1,116 @@ +//===-- InstructionUtils.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_SOURCE_PLUGINS_PROCESS_UTILITY_INSTRUCTIONUTILS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_INSTRUCTIONUTILS_H + +#include <cassert> +#include <cstdint> + +// Common utilities for manipulating instruction bit fields. + +namespace lldb_private { + +// Return the bit field(s) from the most significant bit (msbit) to the +// least significant bit (lsbit) of a 64-bit unsigned value. +static inline uint64_t Bits64(const uint64_t bits, const uint32_t msbit, + const uint32_t lsbit) { + assert(msbit < 64 && lsbit <= msbit); + return (bits >> lsbit) & ((1ull << (msbit - lsbit + 1)) - 1); +} + +// Return the bit field(s) from the most significant bit (msbit) to the +// least significant bit (lsbit) of a 32-bit unsigned value. +static inline uint32_t Bits32(const uint32_t bits, const uint32_t msbit, + const uint32_t lsbit) { + assert(msbit < 32 && lsbit <= msbit); + return (bits >> lsbit) & ((1u << (msbit - lsbit + 1)) - 1); +} + +// Return the bit value from the 'bit' position of a 32-bit unsigned value. +static inline uint32_t Bit32(const uint32_t bits, const uint32_t bit) { + return (bits >> bit) & 1u; +} + +static inline uint64_t Bit64(const uint64_t bits, const uint32_t bit) { + return (bits >> bit) & 1ull; +} + +// Set the bit field(s) from the most significant bit (msbit) to the +// least significant bit (lsbit) of a 32-bit unsigned value to 'val'. +static inline void SetBits32(uint32_t &bits, const uint32_t msbit, + const uint32_t lsbit, const uint32_t val) { + assert(msbit < 32 && lsbit < 32 && msbit >= lsbit); + uint32_t mask = ((1u << (msbit - lsbit + 1)) - 1); + bits &= ~(mask << lsbit); + bits |= (val & mask) << lsbit; +} + +// Set the 'bit' position of a 32-bit unsigned value to 'val'. +static inline void SetBit32(uint32_t &bits, const uint32_t bit, + const uint32_t val) { + SetBits32(bits, bit, bit, val); +} + +// Rotate a 32-bit unsigned value right by the specified amount. +static inline uint32_t Rotr32(uint32_t bits, uint32_t amt) { + assert(amt < 32 && "Invalid rotate amount"); + return (bits >> amt) | (bits << ((32 - amt) & 31)); +} + +// Rotate a 32-bit unsigned value left by the specified amount. +static inline uint32_t Rotl32(uint32_t bits, uint32_t amt) { + assert(amt < 32 && "Invalid rotate amount"); + return (bits << amt) | (bits >> ((32 - amt) & 31)); +} + +// Create a mask that starts at bit zero and includes "bit" +static inline uint64_t MaskUpToBit(const uint64_t bit) { + if (bit >= 63) + return -1ll; + return (1ull << (bit + 1ull)) - 1ull; +} + +// Return an integer result equal to the number of bits of x that are ones. +static inline uint32_t BitCount(uint64_t x) { + // c accumulates the total bits set in x + uint32_t c; + for (c = 0; x; ++c) { + x &= x - 1; // clear the least significant bit set + } + return c; +} + +static inline bool BitIsSet(const uint64_t value, const uint64_t bit) { + return (value & (1ull << bit)) != 0; +} + +static inline bool BitIsClear(const uint64_t value, const uint64_t bit) { + return (value & (1ull << bit)) == 0; +} + +static inline uint64_t UnsignedBits(const uint64_t value, const uint64_t msbit, + const uint64_t lsbit) { + uint64_t result = value >> lsbit; + result &= MaskUpToBit(msbit - lsbit); + return result; +} + +static inline int64_t SignedBits(const uint64_t value, const uint64_t msbit, + const uint64_t lsbit) { + uint64_t result = UnsignedBits(value, msbit, lsbit); + if (BitIsSet(value, msbit)) { + // Sign extend + result |= ~MaskUpToBit(msbit - lsbit); + } + return result; +} + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_INSTRUCTIONUTILS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxPTraceDefines_arm64sve.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxPTraceDefines_arm64sve.h new file mode 100644 index 000000000000..8b5393ca1888 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxPTraceDefines_arm64sve.h @@ -0,0 +1,293 @@ +//===-- LinuxPTraceDefines_arm64sve.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_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXPTRACEDEFINES_ARM64SVE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXPTRACEDEFINES_ARM64SVE_H + +#include <cstdint> + +namespace lldb_private { +namespace sve { + +/* + * The SVE architecture leaves space for future expansion of the + * vector length beyond its initial architectural limit of 2048 bits + * (16 quadwords). + * + * See <Linux kernel source tree>/Documentation/arm64/sve.rst for a description + * of the vl/vq terminology. + */ + +const uint16_t vq_bytes = 16; /* number of bytes per quadword */ + +const uint16_t vq_min = 1; +const uint16_t vq_max = 512; + +const uint16_t vl_min = vq_min * vq_bytes; +const uint16_t vl_max = vq_max * vq_bytes; + +const uint16_t num_of_zregs = 32; +const uint16_t num_of_pregs = 16; + +inline uint16_t vl_valid(uint16_t vl) { + return (vl % vq_bytes == 0 && vl >= vl_min && vl <= vl_max); +} + +inline uint16_t vq_from_vl(uint16_t vl) { return vl / vq_bytes; } +inline uint16_t vl_from_vq(uint16_t vq) { return vq * vq_bytes; } + +/* A new signal frame record sve_context encodes the SVE Registers on signal + * delivery. sve_context struct definition may be included in asm/sigcontext.h. + * We define sve_context_size which will be used by LLDB sve helper functions. + * More information on sve_context can be found in Linux kernel source tree at + * Documentation/arm64/sve.rst. + */ + +const uint16_t sve_context_size = 16; + +/* + * If the SVE registers are currently live for the thread at signal delivery, + * sve_context.head.size >= + * SigContextSize(vq_from_vl(sve_context.vl)) + * and the register data may be accessed using the Sig*() functions. + * + * If sve_context.head.size < + * SigContextSize(vq_from_vl(sve_context.vl)), + * the SVE registers were not live for the thread and no register data + * is included: in this case, the Sig*() functions should not be + * used except for this check. + * + * The same convention applies when returning from a signal: a caller + * will need to remove or resize the sve_context block if it wants to + * make the SVE registers live when they were previously non-live or + * vice-versa. This may require the caller to allocate fresh + * memory and/or move other context blocks in the signal frame. + * + * Changing the vector length during signal return is not permitted: + * sve_context.vl must equal the thread's current vector length when + * doing a sigreturn. + * + * + * Note: for all these functions, the "vq" argument denotes the SVE + * vector length in quadwords (i.e., units of 128 bits). + * + * The correct way to obtain vq is to use vq_from_vl(vl). The + * result is valid if and only if vl_valid(vl) is true. This is + * guaranteed for a struct sve_context written by the kernel. + * + * + * Additional functions describe the contents and layout of the payload. + * For each, Sig*Offset(args) is the start offset relative to + * the start of struct sve_context, and Sig*Size(args) is the + * size in bytes: + * + * x type description + * - ---- ----------- + * REGS the entire SVE context + * + * ZREGS __uint128_t[num_of_zregs][vq] all Z-registers + * ZREG __uint128_t[vq] individual Z-register Zn + * + * PREGS uint16_t[num_of_pregs][vq] all P-registers + * PREG uint16_t[vq] individual P-register Pn + * + * FFR uint16_t[vq] first-fault status register + * + * Additional data might be appended in the future. + */ + +inline uint16_t SigZRegSize(uint16_t vq) { return vq * vq_bytes; } +inline uint16_t SigPRegSize(uint16_t vq) { return vq * vq_bytes / 8; } +inline uint16_t SigFFRSize(uint16_t vq) { return SigPRegSize(vq); } + +inline uint32_t SigRegsOffset() { + return (sve_context_size + vq_bytes - 1) / vq_bytes * vq_bytes; +} + +inline uint32_t SigZRegsOffset() { return SigRegsOffset(); } + +inline uint32_t SigZRegOffset(uint16_t vq, uint16_t n) { + return SigRegsOffset() + SigZRegSize(vq) * n; +} + +inline uint32_t SigZRegsSize(uint16_t vq) { + return SigZRegOffset(vq, num_of_zregs) - SigRegsOffset(); +} + +inline uint32_t SigPRegsOffset(uint16_t vq) { + return SigRegsOffset() + SigZRegsSize(vq); +} + +inline uint32_t SigPRegOffset(uint16_t vq, uint16_t n) { + return SigPRegsOffset(vq) + SigPRegSize(vq) * n; +} + +inline uint32_t SigpRegsSize(uint16_t vq) { + return SigPRegOffset(vq, num_of_pregs) - SigPRegsOffset(vq); +} + +inline uint32_t SigFFROffset(uint16_t vq) { + return SigPRegsOffset(vq) + SigpRegsSize(vq); +} + +inline uint32_t SigRegsSize(uint16_t vq) { + return SigFFROffset(vq) + SigFFRSize(vq) - SigRegsOffset(); +} + +inline uint32_t SVESigContextSize(uint16_t vq) { + return SigRegsOffset() + SigRegsSize(vq); +} + +struct user_sve_header { + uint32_t size; /* total meaningful regset content in bytes */ + uint32_t max_size; /* maxmium possible size for this thread */ + uint16_t vl; /* current vector length */ + uint16_t max_vl; /* maximum possible vector length */ + uint16_t flags; + uint16_t reserved; +}; + +using user_za_header = user_sve_header; + +/* Definitions for user_sve_header.flags: */ +const uint16_t ptrace_regs_mask = 1 << 0; +const uint16_t ptrace_regs_fpsimd = 0; +const uint16_t ptrace_regs_sve = ptrace_regs_mask; + +/* + * The remainder of the SVE state follows struct user_sve_header. The + * total size of the SVE state (including header) depends on the + * metadata in the header: PTraceSize(vq, flags) gives the total size + * of the state in bytes, including the header. + * + * Refer to <asm/sigcontext.h> for details of how to pass the correct + * "vq" argument to these macros. + */ + +/* Offset from the start of struct user_sve_header to the register data */ +inline uint16_t PTraceRegsOffset() { + return (sizeof(struct user_sve_header) + vq_bytes - 1) / vq_bytes * vq_bytes; +} + +/* + * The register data content and layout depends on the value of the + * flags field. + */ + +/* + * (flags & ptrace_regs_mask) == ptrace_regs_fpsimd case: + * + * The payload starts at offset PTraceFPSIMDOffset, and is of type + * struct user_fpsimd_state. Additional data might be appended in the + * future: use PTraceFPSIMDSize(vq, flags) to compute the total size. + * PTraceFPSIMDSize(vq, flags) will never be less than + * sizeof(struct user_fpsimd_state). + */ + +const uint32_t ptrace_fpsimd_offset = PTraceRegsOffset(); + +/* Return size of struct user_fpsimd_state from asm/ptrace.h */ +inline uint32_t PTraceFPSIMDSize(uint16_t vq, uint16_t flags) { return 528; } + +/* + * (flags & ptrace_regs_mask) == ptrace_regs_sve case: + * + * The payload starts at offset PTraceSVEOffset, and is of size + * PTraceSVESize(vq, flags). + * + * Additional functions describe the contents and layout of the payload. + * For each, PTrace*X*Offset(args) is the start offset relative to + * the start of struct user_sve_header, and PTrace*X*Size(args) is + * the size in bytes: + * + * x type description + * - ---- ----------- + * ZREGS \ + * ZREG | + * PREGS | refer to <asm/sigcontext.h> + * PREG | + * FFR / + * + * FPSR uint32_t FPSR + * FPCR uint32_t FPCR + * + * Additional data might be appended in the future. + */ + +inline uint32_t PTraceZRegSize(uint16_t vq) { return SigZRegSize(vq); } + +inline uint32_t PTracePRegSize(uint16_t vq) { return SigPRegSize(vq); } + +inline uint32_t PTraceFFRSize(uint16_t vq) { return SigFFRSize(vq); } + +const uint32_t fpsr_size = sizeof(uint32_t); +const uint32_t fpcr_size = sizeof(uint32_t); + +inline uint32_t SigToPTrace(uint32_t offset) { + return offset - SigRegsOffset() + PTraceRegsOffset(); +} + +const uint32_t ptrace_sve_offset = PTraceRegsOffset(); + +inline uint32_t PTraceZRegsOffset(uint16_t vq) { + return SigToPTrace(SigZRegsOffset()); +} + +inline uint32_t PTraceZRegOffset(uint16_t vq, uint16_t n) { + return SigToPTrace(SigZRegOffset(vq, n)); +} + +inline uint32_t PTraceZRegsSize(uint16_t vq) { + return PTraceZRegOffset(vq, num_of_zregs) - SigToPTrace(SigRegsOffset()); +} + +inline uint32_t PTracePRegsOffset(uint16_t vq) { + return SigToPTrace(SigPRegsOffset(vq)); +} + +inline uint32_t PTracePRegOffset(uint16_t vq, uint16_t n) { + return SigToPTrace(SigPRegOffset(vq, n)); +} + +inline uint32_t PTracePRegsSize(uint16_t vq) { + return PTracePRegOffset(vq, num_of_pregs) - PTracePRegsOffset(vq); +} + +inline uint32_t PTraceFFROffset(uint16_t vq) { + return SigToPTrace(SigFFROffset(vq)); +} + +inline uint32_t PTraceFPSROffset(uint16_t vq) { + return (PTraceFFROffset(vq) + PTraceFFRSize(vq) + (vq_bytes - 1)) / vq_bytes * + vq_bytes; +} + +inline uint32_t PTraceFPCROffset(uint16_t vq) { + return PTraceFPSROffset(vq) + fpsr_size; +} + +/* + * Any future extension appended after FPCR must be aligned to the next + * 128-bit boundary. + */ + +inline uint32_t PTraceSVESize(uint16_t vq, uint16_t flags) { + return (PTraceFPCROffset(vq) + fpcr_size - ptrace_sve_offset + vq_bytes - 1) / + vq_bytes * vq_bytes; +} + +inline uint32_t PTraceSize(uint16_t vq, uint16_t flags) { + return (flags & ptrace_regs_mask) == ptrace_regs_sve + ? ptrace_sve_offset + PTraceSVESize(vq, flags) + : ptrace_fpsimd_offset + PTraceFPSIMDSize(vq, flags); +} + +} // namespace SVE +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXPTRACEDEFINES_ARM64SVE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp new file mode 100644 index 000000000000..fd803c8cabaf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp @@ -0,0 +1,205 @@ +//===-- LinuxProcMaps.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 "LinuxProcMaps.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StringExtractor.h" +#include "llvm/ADT/StringRef.h" +#include <optional> + +using namespace lldb_private; + +enum class MapsKind { Maps, SMaps }; + +static llvm::Expected<MemoryRegionInfo> ProcMapError(const char *msg, + MapsKind kind) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), msg, + kind == MapsKind::Maps ? "maps" : "smaps"); +} + +static llvm::Expected<MemoryRegionInfo> +ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef maps_line, + MapsKind maps_kind) { + MemoryRegionInfo region; + StringExtractor line_extractor(maps_line); + + // Format: {address_start_hex}-{address_end_hex} perms offset dev inode + // pathname perms: rwxp (letter is present if set, '-' if not, final + // character is p=private, s=shared). + + // Parse out the starting address + lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0); + + // Parse out hyphen separating start and end address from range. + if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-')) + return ProcMapError( + "malformed /proc/{pid}/%s entry, missing dash between address range", + maps_kind); + + // Parse out the ending address + lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address); + + // Parse out the space after the address. + if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' ')) + return ProcMapError( + "malformed /proc/{pid}/%s entry, missing space after range", maps_kind); + + // Save the range. + region.GetRange().SetRangeBase(start_address); + region.GetRange().SetRangeEnd(end_address); + + // Any memory region in /proc/{pid}/(maps|smaps) is by definition mapped + // into the process. + region.SetMapped(MemoryRegionInfo::OptionalBool::eYes); + + // Parse out each permission entry. + if (line_extractor.GetBytesLeft() < 4) + return ProcMapError( + "malformed /proc/{pid}/%s entry, missing some portion of " + "permissions", + maps_kind); + + // Handle read permission. + const char read_perm_char = line_extractor.GetChar(); + if (read_perm_char == 'r') + region.SetReadable(MemoryRegionInfo::OptionalBool::eYes); + else if (read_perm_char == '-') + region.SetReadable(MemoryRegionInfo::OptionalBool::eNo); + else + return ProcMapError("unexpected /proc/{pid}/%s read permission char", + maps_kind); + + // Handle write permission. + const char write_perm_char = line_extractor.GetChar(); + if (write_perm_char == 'w') + region.SetWritable(MemoryRegionInfo::OptionalBool::eYes); + else if (write_perm_char == '-') + region.SetWritable(MemoryRegionInfo::OptionalBool::eNo); + else + return ProcMapError("unexpected /proc/{pid}/%s write permission char", + maps_kind); + + // Handle execute permission. + const char exec_perm_char = line_extractor.GetChar(); + if (exec_perm_char == 'x') + region.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); + else if (exec_perm_char == '-') + region.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); + else + return ProcMapError("unexpected /proc/{pid}/%s exec permission char", + maps_kind); + + // Handle sharing status (private/shared). + const char sharing_char = line_extractor.GetChar(); + if (sharing_char == 's') + region.SetShared(MemoryRegionInfo::OptionalBool::eYes); + else if (sharing_char == 'p') + region.SetShared(MemoryRegionInfo::OptionalBool::eNo); + else + region.SetShared(MemoryRegionInfo::OptionalBool::eDontKnow); + + line_extractor.SkipSpaces(); // Skip the separator + line_extractor.GetHexMaxU64(false, 0); // Read the offset + line_extractor.GetHexMaxU64(false, 0); // Read the major device number + line_extractor.GetChar(); // Read the device id separator + line_extractor.GetHexMaxU64(false, 0); // Read the major device number + line_extractor.SkipSpaces(); // Skip the separator + line_extractor.GetU64(0, 10); // Read the inode number + + line_extractor.SkipSpaces(); + const char *name = line_extractor.Peek(); + if (name) + region.SetName(name); + + return region; +} + +void lldb_private::ParseLinuxMapRegions(llvm::StringRef linux_map, + LinuxMapCallback const &callback) { + llvm::StringRef lines(linux_map); + llvm::StringRef line; + while (!lines.empty()) { + std::tie(line, lines) = lines.split('\n'); + if (!callback(ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::Maps))) + break; + } +} + +void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap, + LinuxMapCallback const &callback) { + // Entries in /smaps look like: + // 00400000-0048a000 r-xp 00000000 fd:03 960637 + // Size: 552 kB + // Rss: 460 kB + // <...> + // VmFlags: rd ex mr mw me dw + // 00500000-0058a000 rwxp 00000000 fd:03 960637 + // <...> + // + // Where the first line is identical to the /maps format + // and VmFlags is only printed for kernels >= 3.8. + + llvm::StringRef lines(linux_smap); + llvm::StringRef line; + std::optional<MemoryRegionInfo> region; + + while (lines.size()) { + std::tie(line, lines) = lines.split('\n'); + + // A property line looks like: + // <word>: <value> + // (no spaces on the left hand side) + // A header will have a ':' but the LHS will contain spaces + llvm::StringRef name; + llvm::StringRef value; + std::tie(name, value) = line.split(':'); + + // If this line is a property line + if (!name.contains(' ')) { + if (region) { + if (name == "VmFlags") { + if (value.contains("mt")) + region->SetMemoryTagged(MemoryRegionInfo::eYes); + else + region->SetMemoryTagged(MemoryRegionInfo::eNo); + } + // Ignore anything else + } else { + // Orphaned settings line + callback(ProcMapError( + "Found a property line without a corresponding mapping " + "in /proc/{pid}/%s", + MapsKind::SMaps)); + return; + } + } else { + // Must be a new region header + if (region) { + // Save current region + callback(*region); + region.reset(); + } + + // Try to start a new region + llvm::Expected<MemoryRegionInfo> new_region = + ParseMemoryRegionInfoFromProcMapsLine(line, MapsKind::SMaps); + if (new_region) { + region = *new_region; + } else { + // Stop at first invalid region header + callback(new_region.takeError()); + return; + } + } + } + + // Catch last region + if (region) + callback(*region); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h new file mode 100644 index 000000000000..02f78d55c290 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxProcMaps.h @@ -0,0 +1,27 @@ +//===-- LinuxProcMaps.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_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXPROCMAPS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXPROCMAPS_H + +#include "lldb/lldb-forward.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +namespace lldb_private { + +typedef std::function<bool(llvm::Expected<MemoryRegionInfo>)> LinuxMapCallback; + +void ParseLinuxMapRegions(llvm::StringRef linux_map, + LinuxMapCallback const &callback); +void ParseLinuxSMapRegions(llvm::StringRef linux_smap, + LinuxMapCallback const &callback); + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXPROCMAPS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp new file mode 100644 index 000000000000..3f25dbc6abbb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp @@ -0,0 +1,143 @@ +//===-- LinuxSignals.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 "LinuxSignals.h" + +#ifdef __linux__ +#include <csignal> + +#ifndef SEGV_BNDERR +#define SEGV_BNDERR 3 +#endif +#ifndef SEGV_MTEAERR +#define SEGV_MTEAERR 8 +#endif +#ifndef SEGV_MTESERR +#define SEGV_MTESERR 9 +#endif + +#define ADD_SIGCODE(signal_name, signal_value, code_name, code_value, ...) \ + static_assert(signal_name == signal_value, \ + "Value mismatch for signal number " #signal_name); \ + static_assert(code_name == code_value, \ + "Value mismatch for signal code " #code_name); \ + AddSignalCode(signal_value, code_value, __VA_ARGS__) +#else +#define ADD_SIGCODE(signal_name, signal_value, code_name, code_value, ...) \ + AddSignalCode(signal_value, code_value, __VA_ARGS__) +#endif /* ifdef __linux__ */ + +using namespace lldb_private; + +LinuxSignals::LinuxSignals() : UnixSignals() { Reset(); } + +void LinuxSignals::Reset() { + m_signals.clear(); + // clang-format off + // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION + // ====== ============== ======== ====== ====== =================================================== + AddSignal(1, "SIGHUP", false, true, true, "hangup"); + AddSignal(2, "SIGINT", true, true, true, "interrupt"); + AddSignal(3, "SIGQUIT", false, true, true, "quit"); + + AddSignal(4, "SIGILL", false, true, true, "illegal instruction"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLOPC, 1, "illegal opcode"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLOPN, 2, "illegal operand"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLADR, 3, "illegal addressing mode"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLTRP, 4, "illegal trap"); + ADD_SIGCODE(SIGILL, 4, ILL_PRVOPC, 5, "privileged opcode"); + ADD_SIGCODE(SIGILL, 4, ILL_PRVREG, 6, "privileged register"); + ADD_SIGCODE(SIGILL, 4, ILL_COPROC, 7, "coprocessor error"); + ADD_SIGCODE(SIGILL, 4, ILL_BADSTK, 8, "internal stack error"); + + AddSignal(5, "SIGTRAP", true, true, true, "trace trap (not reset when caught)"); + AddSignal(6, "SIGABRT", false, true, true, "abort()/IOT trap", "SIGIOT"); + + AddSignal(7, "SIGBUS", false, true, true, "bus error"); + ADD_SIGCODE(SIGBUS, 7, BUS_ADRALN, 1, "illegal alignment"); + ADD_SIGCODE(SIGBUS, 7, BUS_ADRERR, 2, "illegal address"); + ADD_SIGCODE(SIGBUS, 7, BUS_OBJERR, 3, "hardware error"); + + AddSignal(8, "SIGFPE", false, true, true, "floating point exception"); + ADD_SIGCODE(SIGFPE, 8, FPE_INTDIV, 1, "integer divide by zero"); + ADD_SIGCODE(SIGFPE, 8, FPE_INTOVF, 2, "integer overflow"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTDIV, 3, "floating point divide by zero"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTOVF, 4, "floating point overflow"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTUND, 5, "floating point underflow"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTRES, 6, "floating point inexact result"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTINV, 7, "floating point invalid operation"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTSUB, 8, "subscript out of range"); + + AddSignal(9, "SIGKILL", false, true, true, "kill"); + AddSignal(10, "SIGUSR1", false, true, true, "user defined signal 1"); + + AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation"); + ADD_SIGCODE(SIGSEGV, 11, SEGV_MAPERR, 1, "address not mapped to object", SignalCodePrintOption::Address); + ADD_SIGCODE(SIGSEGV, 11, SEGV_ACCERR, 2, "invalid permissions for mapped object", SignalCodePrintOption::Address); + ADD_SIGCODE(SIGSEGV, 11, SEGV_BNDERR, 3, "failed address bounds checks", SignalCodePrintOption::Bounds); + ADD_SIGCODE(SIGSEGV, 11, SEGV_MTEAERR, 8, "async tag check fault"); + ADD_SIGCODE(SIGSEGV, 11, SEGV_MTESERR, 9, "sync tag check fault", SignalCodePrintOption::Address); + // Some platforms will occasionally send nonstandard spurious SI_KERNEL + // codes. One way to get this is via unaligned SIMD loads. Treat it as invalid address. + ADD_SIGCODE(SIGSEGV, 11, SI_KERNEL, 0x80, "invalid address", SignalCodePrintOption::Address); + + AddSignal(12, "SIGUSR2", false, true, true, "user defined signal 2"); + AddSignal(13, "SIGPIPE", false, true, true, "write to pipe with reading end closed"); + AddSignal(14, "SIGALRM", false, false, false, "alarm"); + AddSignal(15, "SIGTERM", false, true, true, "termination requested"); + AddSignal(16, "SIGSTKFLT", false, true, true, "stack fault"); + AddSignal(17, "SIGCHLD", false, false, true, "child status has changed", "SIGCLD"); + AddSignal(18, "SIGCONT", false, false, true, "process continue"); + AddSignal(19, "SIGSTOP", true, true, true, "process stop"); + AddSignal(20, "SIGTSTP", false, true, true, "tty stop"); + AddSignal(21, "SIGTTIN", false, true, true, "background tty read"); + AddSignal(22, "SIGTTOU", false, true, true, "background tty write"); + AddSignal(23, "SIGURG", false, true, true, "urgent data on socket"); + AddSignal(24, "SIGXCPU", false, true, true, "CPU resource exceeded"); + AddSignal(25, "SIGXFSZ", false, true, true, "file size limit exceeded"); + AddSignal(26, "SIGVTALRM", false, true, true, "virtual time alarm"); + AddSignal(27, "SIGPROF", false, false, false, "profiling time alarm"); + AddSignal(28, "SIGWINCH", false, true, true, "window size changes"); + AddSignal(29, "SIGIO", false, true, true, "input/output ready/Pollable event", "SIGPOLL"); + AddSignal(30, "SIGPWR", false, true, true, "power failure"); + AddSignal(31, "SIGSYS", false, true, true, "invalid system call"); + AddSignal(32, "SIG32", false, false, false, "threading library internal signal 1"); + AddSignal(33, "SIG33", false, false, false, "threading library internal signal 2"); + AddSignal(34, "SIGRTMIN", false, false, false, "real time signal 0"); + AddSignal(35, "SIGRTMIN+1", false, false, false, "real time signal 1"); + AddSignal(36, "SIGRTMIN+2", false, false, false, "real time signal 2"); + AddSignal(37, "SIGRTMIN+3", false, false, false, "real time signal 3"); + AddSignal(38, "SIGRTMIN+4", false, false, false, "real time signal 4"); + AddSignal(39, "SIGRTMIN+5", false, false, false, "real time signal 5"); + AddSignal(40, "SIGRTMIN+6", false, false, false, "real time signal 6"); + AddSignal(41, "SIGRTMIN+7", false, false, false, "real time signal 7"); + AddSignal(42, "SIGRTMIN+8", false, false, false, "real time signal 8"); + AddSignal(43, "SIGRTMIN+9", false, false, false, "real time signal 9"); + AddSignal(44, "SIGRTMIN+10", false, false, false, "real time signal 10"); + AddSignal(45, "SIGRTMIN+11", false, false, false, "real time signal 11"); + AddSignal(46, "SIGRTMIN+12", false, false, false, "real time signal 12"); + AddSignal(47, "SIGRTMIN+13", false, false, false, "real time signal 13"); + AddSignal(48, "SIGRTMIN+14", false, false, false, "real time signal 14"); + AddSignal(49, "SIGRTMIN+15", false, false, false, "real time signal 15"); + AddSignal(50, "SIGRTMAX-14", false, false, false, "real time signal 16"); // switching to SIGRTMAX-xxx to match "kill -l" output + AddSignal(51, "SIGRTMAX-13", false, false, false, "real time signal 17"); + AddSignal(52, "SIGRTMAX-12", false, false, false, "real time signal 18"); + AddSignal(53, "SIGRTMAX-11", false, false, false, "real time signal 19"); + AddSignal(54, "SIGRTMAX-10", false, false, false, "real time signal 20"); + AddSignal(55, "SIGRTMAX-9", false, false, false, "real time signal 21"); + AddSignal(56, "SIGRTMAX-8", false, false, false, "real time signal 22"); + AddSignal(57, "SIGRTMAX-7", false, false, false, "real time signal 23"); + AddSignal(58, "SIGRTMAX-6", false, false, false, "real time signal 24"); + AddSignal(59, "SIGRTMAX-5", false, false, false, "real time signal 25"); + AddSignal(60, "SIGRTMAX-4", false, false, false, "real time signal 26"); + AddSignal(61, "SIGRTMAX-3", false, false, false, "real time signal 27"); + AddSignal(62, "SIGRTMAX-2", false, false, false, "real time signal 28"); + AddSignal(63, "SIGRTMAX-1", false, false, false, "real time signal 29"); + AddSignal(64, "SIGRTMAX", false, false, false, "real time signal 30"); + // clang-format on +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h new file mode 100644 index 000000000000..32c4744a96d0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/LinuxSignals.h @@ -0,0 +1,27 @@ +//===-- LinuxSignals.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_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXSIGNALS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXSIGNALS_H + +#include "lldb/Target/UnixSignals.h" + +namespace lldb_private { + +/// Linux specific set of Unix signals. +class LinuxSignals : public UnixSignals { +public: + LinuxSignals(); + +private: + void Reset() override; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LINUXSIGNALS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp new file mode 100644 index 000000000000..7e25bc4ea2a2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.cpp @@ -0,0 +1,356 @@ +//===-- MemoryTagManagerAArch64MTE.cpp --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MemoryTagManagerAArch64MTE.h" +#include "llvm/Support/Error.h" +#include <assert.h> + +using namespace lldb_private; + +static const unsigned MTE_START_BIT = 56; +static const unsigned MTE_TAG_MAX = 0xf; +static const unsigned MTE_GRANULE_SIZE = 16; + +lldb::addr_t +MemoryTagManagerAArch64MTE::GetLogicalTag(lldb::addr_t addr) const { + return (addr >> MTE_START_BIT) & MTE_TAG_MAX; +} + +lldb::addr_t +MemoryTagManagerAArch64MTE::RemoveTagBits(lldb::addr_t addr) const { + // Here we're ignoring the whole top byte. If you've got MTE + // you must also have TBI (top byte ignore). + // The other 4 bits could contain other extension bits or + // user metadata. + return addr & ~((lldb::addr_t)0xFF << MTE_START_BIT); +} + +ptrdiff_t MemoryTagManagerAArch64MTE::AddressDiff(lldb::addr_t addr1, + lldb::addr_t addr2) const { + return RemoveTagBits(addr1) - RemoveTagBits(addr2); +} + +lldb::addr_t MemoryTagManagerAArch64MTE::GetGranuleSize() const { + return MTE_GRANULE_SIZE; +} + +int32_t MemoryTagManagerAArch64MTE::GetAllocationTagType() const { + return eMTE_allocation; +} + +size_t MemoryTagManagerAArch64MTE::GetTagSizeInBytes() const { return 1; } + +MemoryTagManagerAArch64MTE::TagRange +MemoryTagManagerAArch64MTE::ExpandToGranule(TagRange range) const { + // Ignore reading a length of 0 + if (!range.IsValid()) + return range; + + const size_t granule = GetGranuleSize(); + + // Align start down to granule start + lldb::addr_t new_start = range.GetRangeBase(); + lldb::addr_t align_down_amount = new_start % granule; + new_start -= align_down_amount; + + // Account for the distance we moved the start above + size_t new_len = range.GetByteSize() + align_down_amount; + // Then align up to the end of the granule + size_t align_up_amount = granule - (new_len % granule); + if (align_up_amount != granule) + new_len += align_up_amount; + + return TagRange(new_start, new_len); +} + +static llvm::Error MakeInvalidRangeErr(lldb::addr_t addr, + lldb::addr_t end_addr) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "End address (0x%" PRIx64 + ") must be greater than the start address (0x%" PRIx64 ")", + end_addr, addr); +} + +llvm::Expected<MemoryTagManager::TagRange> +MemoryTagManagerAArch64MTE::MakeTaggedRange( + lldb::addr_t addr, lldb::addr_t end_addr, + const lldb_private::MemoryRegionInfos &memory_regions) const { + // First check that the range is not inverted. + // We must remove tags here otherwise an address with a higher + // tag value will always be > the other. + ptrdiff_t len = AddressDiff(end_addr, addr); + if (len <= 0) + return MakeInvalidRangeErr(addr, end_addr); + + // Region addresses will not have memory tags. So when searching + // we must use an untagged address. + MemoryRegionInfo::RangeType tag_range(RemoveTagBits(addr), len); + tag_range = ExpandToGranule(tag_range); + + // Make a copy so we can use the original for errors and the final return. + MemoryRegionInfo::RangeType remaining_range(tag_range); + + // While there are parts of the range that don't have a matching tagged memory + // region + while (remaining_range.IsValid()) { + // Search for a region that contains the start of the range + MemoryRegionInfos::const_iterator region = std::find_if( + memory_regions.cbegin(), memory_regions.cend(), + [&remaining_range](const MemoryRegionInfo ®ion) { + return region.GetRange().Contains(remaining_range.GetRangeBase()); + }); + + if (region == memory_regions.cend() || + region->GetMemoryTagged() != MemoryRegionInfo::eYes) { + // Some part of this range is untagged (or unmapped) so error + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Address range 0x%" PRIx64 ":0x%" PRIx64 + " is not in a memory tagged region", + tag_range.GetRangeBase(), + tag_range.GetRangeEnd()); + } + + // We've found some part of the range so remove that part and continue + // searching for the rest. Moving the base "slides" the range so we need to + // save/restore the original end. If old_end is less than the new base, the + // range will be set to have 0 size and we'll exit the while. + lldb::addr_t old_end = remaining_range.GetRangeEnd(); + remaining_range.SetRangeBase(region->GetRange().GetRangeEnd()); + remaining_range.SetRangeEnd(old_end); + } + + // Every part of the range is contained within a tagged memory region. + return tag_range; +} + +llvm::Expected<std::vector<MemoryTagManager::TagRange>> +MemoryTagManagerAArch64MTE::MakeTaggedRanges( + lldb::addr_t addr, lldb::addr_t end_addr, + const lldb_private::MemoryRegionInfos &memory_regions) const { + // First check that the range is not inverted. + // We must remove tags here otherwise an address with a higher + // tag value will always be > the other. + ptrdiff_t len = AddressDiff(end_addr, addr); + if (len <= 0) + return MakeInvalidRangeErr(addr, end_addr); + + std::vector<MemoryTagManager::TagRange> tagged_ranges; + // No memory regions means no tagged memory at all + if (memory_regions.empty()) + return tagged_ranges; + + // For the logic to work regions must be in ascending order + // which is what you'd have if you used GetMemoryRegions. + assert(std::is_sorted( + memory_regions.begin(), memory_regions.end(), + [](const MemoryRegionInfo &lhs, const MemoryRegionInfo &rhs) { + return lhs.GetRange().GetRangeBase() < rhs.GetRange().GetRangeBase(); + })); + + // If we're debugging userspace in an OS like Linux that uses an MMU, + // the only reason we'd get overlapping regions is incorrect data. + // It is possible that won't hold for embedded with memory protection + // units (MPUs) that allow overlaps. + // + // For now we're going to assume the former, as there is no good way + // to handle overlaps. For example: + // < requested range > + // [-- region 1 --] + // [-- region 2--] + // Where the first region will reduce the requested range to nothing + // and exit early before it sees the overlap. + MemoryRegionInfos::const_iterator overlap = std::adjacent_find( + memory_regions.begin(), memory_regions.end(), + [](const MemoryRegionInfo &lhs, const MemoryRegionInfo &rhs) { + return rhs.GetRange().DoesIntersect(lhs.GetRange()); + }); + UNUSED_IF_ASSERT_DISABLED(overlap); + assert(overlap == memory_regions.end()); + + // Region addresses will not have memory tags so when searching + // we must use an untagged address. + MemoryRegionInfo::RangeType range(RemoveTagBits(addr), len); + range = ExpandToGranule(range); + + // While there are regions to check and the range has non zero length + for (const MemoryRegionInfo ®ion : memory_regions) { + // If range we're checking has been reduced to zero length, exit early + if (!range.IsValid()) + break; + + // If the region doesn't overlap the range at all, ignore it. + if (!region.GetRange().DoesIntersect(range)) + continue; + + // If it's tagged record this sub-range. + // (assuming that it's already granule aligned) + if (region.GetMemoryTagged()) { + // The region found may extend outside the requested range. + // For example the first region might start before the range. + // We must only add what covers the requested range. + lldb::addr_t start = + std::max(range.GetRangeBase(), region.GetRange().GetRangeBase()); + lldb::addr_t end = + std::min(range.GetRangeEnd(), region.GetRange().GetRangeEnd()); + tagged_ranges.push_back(MemoryTagManager::TagRange(start, end - start)); + } + + // Move the range up to start at the end of the region. + lldb::addr_t old_end = range.GetRangeEnd(); + // This "slides" the range so it moves the end as well. + range.SetRangeBase(region.GetRange().GetRangeEnd()); + // So we set the end back to the original end address after sliding it up. + range.SetRangeEnd(old_end); + // (if the above were to try to set end < begin the range will just be set + // to 0 size) + } + + return tagged_ranges; +} + +llvm::Expected<std::vector<lldb::addr_t>> +MemoryTagManagerAArch64MTE::UnpackTagsData(const std::vector<uint8_t> &tags, + size_t granules /*=0*/) const { + // 0 means don't check the number of tags before unpacking + if (granules) { + size_t num_tags = tags.size() / GetTagSizeInBytes(); + if (num_tags != granules) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Packed tag data size does not match expected number of tags. " + "Expected %zu tag(s) for %zu granule(s), got %zu tag(s).", + granules, granules, num_tags); + } + } + + // (if bytes per tag was not 1, we would reconstruct them here) + + std::vector<lldb::addr_t> unpacked; + unpacked.reserve(tags.size()); + for (auto it = tags.begin(); it != tags.end(); ++it) { + // Check all tags are in range + if (*it > MTE_TAG_MAX) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Found tag 0x%x which is > max MTE tag value of 0x%x.", *it, + MTE_TAG_MAX); + } + unpacked.push_back(*it); + } + + return unpacked; +} + +std::vector<lldb::addr_t> +MemoryTagManagerAArch64MTE::UnpackTagsFromCoreFileSegment( + CoreReaderFn reader, lldb::addr_t tag_segment_virtual_address, + lldb::addr_t tag_segment_data_address, lldb::addr_t addr, + size_t len) const { + // We can assume by now that addr and len have been granule aligned by a tag + // manager. However because we have 2 tags per byte we need to round the range + // up again to align to 2 granule boundaries. + const size_t granule = GetGranuleSize(); + const size_t two_granules = granule * 2; + lldb::addr_t aligned_addr = addr; + size_t aligned_len = len; + + // First align the start address down. + if (aligned_addr % two_granules) { + assert(aligned_addr % two_granules == granule); + aligned_addr -= granule; + aligned_len += granule; + } + + // Then align the length up. + bool aligned_length_up = false; + if (aligned_len % two_granules) { + assert(aligned_len % two_granules == granule); + aligned_len += granule; + aligned_length_up = true; + } + + // ProcessElfCore should have validated this when it found the segment. + assert(aligned_addr >= tag_segment_virtual_address); + + // By now we know that aligned_addr is aligned to a 2 granule boundary. + const size_t offset_granules = + (aligned_addr - tag_segment_virtual_address) / granule; + // 2 tags per byte. + const size_t file_offset_in_bytes = offset_granules / 2; + + // By now we know that aligned_len is at least 2 granules. + const size_t tag_bytes_to_read = aligned_len / granule / 2; + std::vector<uint8_t> tag_data(tag_bytes_to_read); + const size_t bytes_copied = + reader(tag_segment_data_address + file_offset_in_bytes, tag_bytes_to_read, + tag_data.data()); + UNUSED_IF_ASSERT_DISABLED(bytes_copied); + assert(bytes_copied == tag_bytes_to_read); + + std::vector<lldb::addr_t> tags; + tags.reserve(2 * tag_data.size()); + // No need to check the range of the tag value here as each occupies only 4 + // bits. + for (auto tag_byte : tag_data) { + tags.push_back(tag_byte & 0xf); + tags.push_back(tag_byte >> 4); + } + + // If we aligned the address down, don't return the extra first tag. + if (addr != aligned_addr) + tags.erase(tags.begin()); + // If we aligned the length up, don't return the extra last tag. + if (aligned_length_up) + tags.pop_back(); + + return tags; +} + +llvm::Expected<std::vector<uint8_t>> MemoryTagManagerAArch64MTE::PackTags( + const std::vector<lldb::addr_t> &tags) const { + std::vector<uint8_t> packed; + packed.reserve(tags.size() * GetTagSizeInBytes()); + + for (auto tag : tags) { + if (tag > MTE_TAG_MAX) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Found tag 0x%" PRIx64 + " which is > max MTE tag value of 0x%x.", + tag, MTE_TAG_MAX); + } + packed.push_back(static_cast<uint8_t>(tag)); + } + + return packed; +} + +llvm::Expected<std::vector<lldb::addr_t>> +MemoryTagManagerAArch64MTE::RepeatTagsForRange( + const std::vector<lldb::addr_t> &tags, TagRange range) const { + std::vector<lldb::addr_t> new_tags; + + // If the range is not empty + if (range.IsValid()) { + if (tags.empty()) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Expected some tags to cover given range, got zero."); + } + + // We assume that this range has already been expanded/aligned to granules + size_t granules = range.GetByteSize() / GetGranuleSize(); + new_tags.reserve(granules); + for (size_t to_copy = 0; granules > 0; granules -= to_copy) { + to_copy = granules > tags.size() ? tags.size() : granules; + new_tags.insert(new_tags.end(), tags.begin(), tags.begin() + to_copy); + } + } + + return new_tags; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h new file mode 100644 index 000000000000..365e176e5b1d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h @@ -0,0 +1,63 @@ +//===-- MemoryTagManagerAArch64MTE.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_SOURCE_PLUGINS_PROCESS_UTILITY_MEMORYTAGMANAGERAARCH64MTE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_MEMORYTAGMANAGERAARCH64MTE_H + +#include "lldb/Target/MemoryTagManager.h" + +namespace lldb_private { + +class MemoryTagManagerAArch64MTE : public MemoryTagManager { +public: + // This enum is supposed to be shared for all of AArch64 but until + // there are more tag types than MTE, it will live here. + enum MTETagTypes { + eMTE_logical = 0, + eMTE_allocation = 1, + }; + + lldb::addr_t GetGranuleSize() const override; + int32_t GetAllocationTagType() const override; + size_t GetTagSizeInBytes() const override; + + lldb::addr_t GetLogicalTag(lldb::addr_t addr) const override; + lldb::addr_t RemoveTagBits(lldb::addr_t addr) const override; + ptrdiff_t AddressDiff(lldb::addr_t addr1, lldb::addr_t addr2) const override; + + TagRange ExpandToGranule(TagRange range) const override; + + llvm::Expected<TagRange> MakeTaggedRange( + lldb::addr_t addr, lldb::addr_t end_addr, + const lldb_private::MemoryRegionInfos &memory_regions) const override; + + llvm::Expected<std::vector<TagRange>> MakeTaggedRanges( + lldb::addr_t addr, lldb::addr_t end_addr, + const lldb_private::MemoryRegionInfos &memory_regions) const override; + + llvm::Expected<std::vector<lldb::addr_t>> + UnpackTagsData(const std::vector<uint8_t> &tags, + size_t granules = 0) const override; + + std::vector<lldb::addr_t> + UnpackTagsFromCoreFileSegment(CoreReaderFn reader, + lldb::addr_t tag_segment_virtual_address, + lldb::addr_t tag_segment_data_address, + lldb::addr_t addr, size_t len) const override; + + llvm::Expected<std::vector<uint8_t>> + PackTags(const std::vector<lldb::addr_t> &tags) const override; + + llvm::Expected<std::vector<lldb::addr_t>> + RepeatTagsForRange(const std::vector<lldb::addr_t> &tags, + TagRange range) const override; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_MEMORYTAGMANAGERAARCH64MTE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp new file mode 100644 index 000000000000..ef71a964eaf2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp @@ -0,0 +1,210 @@ +//===-- NativeProcessSoftwareSingleStep.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 "NativeProcessSoftwareSingleStep.h" + +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Utility/RegisterValue.h" + +#include <unordered_map> + +using namespace lldb; +using namespace lldb_private; + +namespace { + +struct EmulatorBaton { + NativeProcessProtocol &m_process; + NativeRegisterContext &m_reg_context; + + // eRegisterKindDWARF -> RegsiterValue + std::unordered_map<uint32_t, RegisterValue> m_register_values; + + EmulatorBaton(NativeProcessProtocol &process, + NativeRegisterContext ®_context) + : m_process(process), m_reg_context(reg_context) {} +}; + +} // anonymous namespace + +static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, void *dst, size_t length) { + EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton); + + size_t bytes_read; + emulator_baton->m_process.ReadMemory(addr, dst, length, bytes_read); + return bytes_read; +} + +static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value) { + EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton); + + auto it = emulator_baton->m_register_values.find( + reg_info->kinds[eRegisterKindDWARF]); + if (it != emulator_baton->m_register_values.end()) { + reg_value = it->second; + return true; + } + + // The emulator only fill in the dwarf regsiter numbers (and in some case the + // generic register numbers). Get the full register info from the register + // context based on the dwarf register numbers. + const RegisterInfo *full_reg_info = + emulator_baton->m_reg_context.GetRegisterInfo( + eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]); + + Status error = + emulator_baton->m_reg_context.ReadRegister(full_reg_info, reg_value); + if (error.Success()) + return true; + + return false; +} + +static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value) { + EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton); + emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = + reg_value; + return true; +} + +static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, const void *dst, + size_t length) { + return length; +} + +static lldb::addr_t ReadFlags(NativeRegisterContext ®siter_context) { + const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + return regsiter_context.ReadRegisterAsUnsigned(flags_info, + LLDB_INVALID_ADDRESS); +} + +static int GetSoftwareBreakpointSize(const ArchSpec &arch, + lldb::addr_t next_flags) { + if (arch.GetMachine() == llvm::Triple::arm) { + if (next_flags & 0x20) + // Thumb mode + return 2; + // Arm mode + return 4; + } + if (arch.IsMIPS() || arch.GetTriple().isPPC64() || + arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch()) + return 4; + return 0; +} + +static Status SetSoftwareBreakpointOnPC(const ArchSpec &arch, lldb::addr_t pc, + lldb::addr_t next_flags, + NativeProcessProtocol &process) { + int size_hint = GetSoftwareBreakpointSize(arch, next_flags); + Status error; + error = process.SetBreakpoint(pc, size_hint, /*hardware=*/false); + + // If setting the breakpoint fails because pc is out of the address + // space, ignore it and let the debugee segfault. + if (error.GetError() == EIO || error.GetError() == EFAULT) + return Status(); + if (error.Fail()) + return error; + + return Status(); +} + +Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping( + NativeThreadProtocol &thread) { + Status error; + NativeProcessProtocol &process = thread.GetProcess(); + NativeRegisterContext ®ister_context = thread.GetRegisterContext(); + const ArchSpec &arch = process.GetArchitecture(); + + std::unique_ptr<EmulateInstruction> emulator_up( + EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying, + nullptr)); + + if (emulator_up == nullptr) + return Status("Instruction emulator not found!"); + + EmulatorBaton baton(process, register_context); + emulator_up->SetBaton(&baton); + emulator_up->SetReadMemCallback(&ReadMemoryCallback); + emulator_up->SetReadRegCallback(&ReadRegisterCallback); + emulator_up->SetWriteMemCallback(&WriteMemoryCallback); + emulator_up->SetWriteRegCallback(&WriteRegisterCallback); + + if (!emulator_up->ReadInstruction()) { + // try to get at least the size of next instruction to set breakpoint. + auto instr_size = emulator_up->GetLastInstrSize(); + if (!instr_size) + return Status("Read instruction failed!"); + bool success = false; + auto pc = emulator_up->ReadRegisterUnsigned(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_PC, + LLDB_INVALID_ADDRESS, &success); + if (!success) + return Status("Reading pc failed!"); + lldb::addr_t next_pc = pc + *instr_size; + auto result = + SetSoftwareBreakpointOnPC(arch, next_pc, /* next_flags */ 0x0, process); + m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); + return result; + } + + bool emulation_result = + emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC); + + const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + + auto pc_it = + baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]); + auto flags_it = reg_info_flags == nullptr + ? baton.m_register_values.end() + : baton.m_register_values.find( + reg_info_flags->kinds[eRegisterKindDWARF]); + + lldb::addr_t next_pc; + lldb::addr_t next_flags; + if (emulation_result) { + assert(pc_it != baton.m_register_values.end() && + "Emulation was successfull but PC wasn't updated"); + next_pc = pc_it->second.GetAsUInt64(); + + if (flags_it != baton.m_register_values.end()) + next_flags = flags_it->second.GetAsUInt64(); + else + next_flags = ReadFlags(register_context); + } else if (pc_it == baton.m_register_values.end()) { + // Emulate instruction failed and it haven't changed PC. Advance PC with + // the size of the current opcode because the emulation of all + // PC modifying instruction should be successful. The failure most + // likely caused by a not supported instruction which don't modify PC. + next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize(); + next_flags = ReadFlags(register_context); + } else { + // The instruction emulation failed after it modified the PC. It is an + // unknown error where we can't continue because the next instruction is + // modifying the PC but we don't know how. + return Status("Instruction emulation failed unexpectedly."); + } + auto result = SetSoftwareBreakpointOnPC(arch, next_pc, next_flags, process); + m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); + return result; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h new file mode 100644 index 000000000000..f9435b7a84ba --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h @@ -0,0 +1,31 @@ +//===-- NativeProcessSoftwareSingleStep.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_NativeProcessSoftwareSingleStep_h +#define lldb_NativeProcessSoftwareSingleStep_h + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" + +#include <map> + +namespace lldb_private { + +class NativeProcessSoftwareSingleStep { +public: + Status SetupSoftwareSingleStepping(NativeThreadProtocol &thread); + +protected: + // List of thread ids stepping with a breakpoint with the address of + // the relevan breakpoint + std::map<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint; +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_NativeProcessSoftwareSingleStep_h diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp new file mode 100644 index 000000000000..4bec3de58668 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp @@ -0,0 +1,470 @@ +//===-- NativeRegisterContextDBReg_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 +// +//===----------------------------------------------------------------------===// + +#include "NativeRegisterContextDBReg_arm64.h" + +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" + +using namespace lldb_private; + +// E (bit 0), used to enable breakpoint/watchpoint +constexpr uint32_t g_enable_bit = 1; +// PAC (bits 2:1): 0b10 +constexpr uint32_t g_pac_bits = (2 << 1); + +// Returns appropriate control register bits for the specified size +static constexpr inline uint64_t GetSizeBits(int size) { + // BAS (bits 12:5) hold a bit-mask of addresses to watch + // e.g. 0b00000001 means 1 byte at address + // 0b00000011 means 2 bytes (addr..addr+1) + // ... + // 0b11111111 means 8 bytes (addr..addr+7) + return ((1 << size) - 1) << 5; +} + +uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareBreakpoints() { + Log *log = GetLog(LLDBLog::Breakpoints); + llvm::Error error = ReadHardwareDebugInfo(); + if (error) { + LLDB_LOG_ERROR(log, std::move(error), + "failed to read debug registers: {0}"); + return 0; + } + + return m_max_hbp_supported; +} + +uint32_t +NativeRegisterContextDBReg_arm64::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + Log *log = GetLog(LLDBLog::Breakpoints); + LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size); + + // Read hardware breakpoint and watchpoint information. + llvm::Error error = ReadHardwareDebugInfo(); + if (error) { + LLDB_LOG_ERROR( + log, std::move(error), + "unable to set breakpoint: failed to read debug registers: {0}"); + return LLDB_INVALID_INDEX32; + } + + uint32_t control_value = 0, bp_index = 0; + + // Check if size has a valid hardware breakpoint length. + if (size != 4) + return LLDB_INVALID_INDEX32; // Invalid size for a AArch64 hardware + // breakpoint + + // Check 4-byte alignment for hardware breakpoint target address. + if (addr & 0x03) + return LLDB_INVALID_INDEX32; // Invalid address, should be 4-byte aligned. + + // Setup control value + control_value = g_enable_bit | g_pac_bits | GetSizeBits(size); + + // Iterate over stored breakpoints and find a free bp_index + bp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + if (!BreakpointIsEnabled(i)) + bp_index = i; // Mark last free slot + else if (m_hbp_regs[i].address == addr) + return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints. + } + + if (bp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Update breakpoint in local cache + m_hbp_regs[bp_index].real_addr = addr; + m_hbp_regs[bp_index].address = addr; + m_hbp_regs[bp_index].control = control_value; + + // PTRACE call to set corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error) { + m_hbp_regs[bp_index].address = 0; + m_hbp_regs[bp_index].control &= ~1; + + LLDB_LOG_ERROR( + log, std::move(error), + "unable to set breakpoint: failed to write debug registers: {0}"); + return LLDB_INVALID_INDEX32; + } + + return bp_index; +} + +bool NativeRegisterContextDBReg_arm64::ClearHardwareBreakpoint( + uint32_t hw_idx) { + Log *log = GetLog(LLDBLog::Breakpoints); + LLDB_LOG(log, "hw_idx: {0}", hw_idx); + + // Read hardware breakpoint and watchpoint information. + llvm::Error error = ReadHardwareDebugInfo(); + if (error) { + LLDB_LOG_ERROR( + log, std::move(error), + "unable to clear breakpoint: failed to read debug registers: {0}"); + return false; + } + + if (hw_idx >= m_max_hbp_supported) + return false; + + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbp_regs[hw_idx].address; + uint32_t tempControl = m_hbp_regs[hw_idx].control; + + m_hbp_regs[hw_idx].control &= ~g_enable_bit; + m_hbp_regs[hw_idx].address = 0; + + // PTRACE call to clear corresponding hardware breakpoint register. + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error) { + m_hbp_regs[hw_idx].control = tempControl; + m_hbp_regs[hw_idx].address = tempAddr; + + LLDB_LOG_ERROR( + log, std::move(error), + "unable to clear breakpoint: failed to write debug registers: {0}"); + return false; + } + + return true; +} + +Status NativeRegisterContextDBReg_arm64::GetHardwareBreakHitIndex( + uint32_t &bp_index, lldb::addr_t trap_addr) { + Log *log = GetLog(LLDBLog::Breakpoints); + + LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__); + + lldb::addr_t break_addr; + + for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { + break_addr = m_hbp_regs[bp_index].address; + + if (BreakpointIsEnabled(bp_index) && trap_addr == break_addr) { + m_hbp_regs[bp_index].hit_addr = trap_addr; + return Status(); + } + } + + bp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +Status NativeRegisterContextDBReg_arm64::ClearAllHardwareBreakpoints() { + Log *log = GetLog(LLDBLog::Breakpoints); + + LLDB_LOGF(log, "NativeRegisterContextDBReg_arm64::%s()", __FUNCTION__); + + // Read hardware breakpoint and watchpoint information. + llvm::Error error = ReadHardwareDebugInfo(); + if (error) + return Status(std::move(error)); + + for (uint32_t i = 0; i < m_max_hbp_supported; i++) { + if (BreakpointIsEnabled(i)) { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hbp_regs[i].address; + uint32_t tempControl = m_hbp_regs[i].control; + + // Clear watchpoints in local cache + m_hbp_regs[i].control &= ~g_enable_bit; + m_hbp_regs[i].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeBREAK); + + if (error) { + m_hbp_regs[i].control = tempControl; + m_hbp_regs[i].address = tempAddr; + + return Status(std::move(error)); + } + } + } + + return Status(); +} + +bool NativeRegisterContextDBReg_arm64::BreakpointIsEnabled(uint32_t bp_index) { + if ((m_hbp_regs[bp_index].control & g_enable_bit) != 0) + return true; + else + return false; +} + +uint32_t NativeRegisterContextDBReg_arm64::NumSupportedHardwareWatchpoints() { + Log *log = GetLog(LLDBLog::Watchpoints); + llvm::Error error = ReadHardwareDebugInfo(); + if (error) { + LLDB_LOG_ERROR(log, std::move(error), + "failed to read debug registers: {0}"); + return 0; + } + + return m_max_hwp_supported; +} + +uint32_t NativeRegisterContextDBReg_arm64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, + watch_flags); + + // Read hardware breakpoint and watchpoint information. + llvm::Error error = ReadHardwareDebugInfo(); + if (error) { + LLDB_LOG_ERROR( + log, std::move(error), + "unable to set watchpoint: failed to read debug registers: {0}"); + return LLDB_INVALID_INDEX32; + } + + uint32_t control_value = 0, wp_index = 0; + lldb::addr_t real_addr = addr; + + // Check if we are setting watchpoint other than read/write/access Also + // update watchpoint flag to match AArch64 write-read bit configuration. + switch (watch_flags) { + case 1: + watch_flags = 2; + break; + case 2: + watch_flags = 1; + break; + case 3: + break; + default: + return LLDB_INVALID_INDEX32; + } + + // Check if size has a valid hardware watchpoint length. + if (size != 1 && size != 2 && size != 4 && size != 8) + return LLDB_INVALID_INDEX32; + + // Check 8-byte alignment for hardware watchpoint target address. Below is a + // hack to recalculate address and size in order to make sure we can watch + // non 8-byte aligned addresses as well. + if (addr & 0x07) { + uint8_t watch_mask = (addr & 0x07) + size; + + if (watch_mask > 0x08) + return LLDB_INVALID_INDEX32; + else if (watch_mask <= 0x02) + size = 2; + else if (watch_mask <= 0x04) + size = 4; + else + size = 8; + + addr = addr & (~0x07); + } + + // Setup control value + control_value = g_enable_bit | g_pac_bits | GetSizeBits(size); + control_value |= watch_flags << 3; + + // Iterate over stored watchpoints and find a free wp_index + wp_index = LLDB_INVALID_INDEX32; + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if (!WatchpointIsEnabled(i)) + wp_index = i; // Mark last free slot + else if (m_hwp_regs[i].address == addr) { + return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. + } + } + + if (wp_index == LLDB_INVALID_INDEX32) + return LLDB_INVALID_INDEX32; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].real_addr = real_addr; + m_hwp_regs[wp_index].address = addr; + m_hwp_regs[wp_index].control = control_value; + + // PTRACE call to set corresponding watchpoint register. + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error) { + m_hwp_regs[wp_index].address = 0; + m_hwp_regs[wp_index].control &= ~g_enable_bit; + + LLDB_LOG_ERROR( + log, std::move(error), + "unable to set watchpoint: failed to write debug registers: {0}"); + return LLDB_INVALID_INDEX32; + } + + return wp_index; +} + +bool NativeRegisterContextDBReg_arm64::ClearHardwareWatchpoint( + uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + // Read hardware breakpoint and watchpoint information. + llvm::Error error = ReadHardwareDebugInfo(); + if (error) { + LLDB_LOG_ERROR( + log, std::move(error), + "unable to clear watchpoint: failed to read debug registers: {0}"); + return false; + } + + if (wp_index >= m_max_hwp_supported) + return false; + + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; + uint32_t tempControl = m_hwp_regs[wp_index].control; + + // Update watchpoint in local cache + m_hwp_regs[wp_index].control &= ~g_enable_bit; + m_hwp_regs[wp_index].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error) { + m_hwp_regs[wp_index].control = tempControl; + m_hwp_regs[wp_index].address = tempAddr; + + LLDB_LOG_ERROR( + log, std::move(error), + "unable to clear watchpoint: failed to write debug registers: {0}"); + return false; + } + + return true; +} + +Status NativeRegisterContextDBReg_arm64::ClearAllHardwareWatchpoints() { + // Read hardware breakpoint and watchpoint information. + llvm::Error error = ReadHardwareDebugInfo(); + if (error) + return Status(std::move(error)); + + for (uint32_t i = 0; i < m_max_hwp_supported; i++) { + if (WatchpointIsEnabled(i)) { + // Create a backup we can revert to in case of failure. + lldb::addr_t tempAddr = m_hwp_regs[i].address; + uint32_t tempControl = m_hwp_regs[i].control; + + // Clear watchpoints in local cache + m_hwp_regs[i].control &= ~g_enable_bit; + m_hwp_regs[i].address = 0; + + // Ptrace call to update hardware debug registers + error = WriteHardwareDebugRegs(eDREGTypeWATCH); + + if (error) { + m_hwp_regs[i].control = tempControl; + m_hwp_regs[i].address = tempAddr; + + return Status(std::move(error)); + } + } + } + + return Status(); +} + +uint32_t +NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + switch ((m_hwp_regs[wp_index].control >> 5) & 0xff) { + case 0x01: + return 1; + case 0x03: + return 2; + case 0x0f: + return 4; + case 0xff: + return 8; + default: + return 0; + } +} + +bool NativeRegisterContextDBReg_arm64::WatchpointIsEnabled(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if ((m_hwp_regs[wp_index].control & g_enable_bit) != 0) + return true; + else + return false; +} + +Status NativeRegisterContextDBReg_arm64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); + + // Read hardware breakpoint and watchpoint information. + llvm::Error error = ReadHardwareDebugInfo(); + if (error) + return Status(std::move(error)); + + // Mask off ignored bits from watchpoint trap address. + trap_addr = FixWatchpointHitAddress(trap_addr); + + uint32_t watch_size; + lldb::addr_t watch_addr; + + for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { + watch_size = GetWatchpointSize(wp_index); + watch_addr = m_hwp_regs[wp_index].address; + + if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && + trap_addr < watch_addr + watch_size) { + m_hwp_regs[wp_index].hit_addr = trap_addr; + return Status(); + } + } + + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +lldb::addr_t +NativeRegisterContextDBReg_arm64::GetWatchpointAddress(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].real_addr; + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +NativeRegisterContextDBReg_arm64::GetWatchpointHitAddress(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + if (wp_index >= m_max_hwp_supported) + return LLDB_INVALID_ADDRESS; + + if (WatchpointIsEnabled(wp_index)) + return m_hwp_regs[wp_index].hit_addr; + return LLDB_INVALID_ADDRESS; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h new file mode 100644 index 000000000000..f8246ff4d718 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h @@ -0,0 +1,89 @@ +//===-- NativeRegisterContextDBReg_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 +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeRegisterContextDBReg_arm64_h +#define lldb_NativeRegisterContextDBReg_arm64_h + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" + +#include <array> + +namespace lldb_private { + +class NativeRegisterContextDBReg_arm64 + : public virtual NativeRegisterContextRegisterInfo { +public: + uint32_t NumSupportedHardwareBreakpoints() override; + + uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + + Status ClearAllHardwareBreakpoints() override; + + Status GetHardwareBreakHitIndex(uint32_t &bp_index, + lldb::addr_t trap_addr) override; + + bool BreakpointIsEnabled(uint32_t bp_index); + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t GetWatchpointSize(uint32_t wp_index); + + bool WatchpointIsEnabled(uint32_t wp_index); + + // Debug register type select + enum DREGType { eDREGTypeWATCH = 0, eDREGTypeBREAK }; + +protected: + /// Debug register info for hardware breakpoints and watchpoints management. + /// Watchpoints: For a user requested size 4 at addr 0x1004, where BAS + /// watchpoints are at doubleword (8-byte) alignment. + /// \a real_addr is 0x1004 + /// \a address is 0x1000 + /// size is 8 + /// If a one-byte write to 0x1006 is the most recent watchpoint trap, + /// \a hit_addr is 0x1006 + struct DREG { + lldb::addr_t address; // Breakpoint/watchpoint address value. + lldb::addr_t hit_addr; // Address at which last watchpoint trigger exception + // occurred. + lldb::addr_t real_addr; // Address value that should cause target to stop. + uint32_t control; // Breakpoint/watchpoint control value. + }; + + std::array<struct DREG, 16> m_hbp_regs; // hardware breakpoints + std::array<struct DREG, 16> m_hwp_regs; // hardware watchpoints + + uint32_t m_max_hbp_supported; + uint32_t m_max_hwp_supported; + + virtual llvm::Error ReadHardwareDebugInfo() = 0; + virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0; + virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) { + return hit_addr; + } +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextDBReg_arm64_h diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_x86.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_x86.cpp new file mode 100644 index 000000000000..f5525e3e3cb3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_x86.cpp @@ -0,0 +1,276 @@ +//===-- NativeRegisterContextDBReg_x86.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NativeRegisterContextDBReg_x86.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/RegisterValue.h" + +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +using namespace lldb_private; + +// Returns mask/value for status bit of wp_index in DR6 +static inline uint64_t GetStatusBit(uint32_t wp_index) { + // DR6: ...BBBB + // 3210 <- status bits for bp./wp. i; 1 if hit + return 1ULL << wp_index; +} + +// Returns mask/value for global enable bit of wp_index in DR7 +static inline uint64_t GetEnableBit(uint32_t wp_index) { + // DR7: ...GLGLGLGL + // 33221100 <- global/local enable for bp./wp.; 1 if enabled + // we use global bits because NetBSD kernel does not preserve local + // bits reliably; Linux seems fine with either + return 1ULL << (2 * wp_index + 1); +} + +// Returns mask for both enable bits of wp_index in DR7 +static inline uint64_t GetBothEnableBitMask(uint32_t wp_index) { + // DR7: ...GLGLGLGL + // 33221100 <- global/local enable for bp./wp.; 1 if enabled + return 3ULL << (2 * wp_index + 1); +} + +// Returns value for type bits of wp_index in DR7 +static inline uint64_t GetWatchTypeBits(uint32_t watch_flags, + uint32_t wp_index) { + // DR7: + // bit: 3322222222221111... + // 1098765432109876... + // val: SSTTSSTTSSTTSSTT... + // wp.: 3333222211110000... + // + // where T - type is 01 for write, 11 for r/w + return static_cast<uint64_t>(watch_flags) << (16 + 4 * wp_index); +} + +// Returns value for size bits of wp_index in DR7 +static inline uint64_t GetWatchSizeBits(uint32_t size, uint32_t wp_index) { + // DR7: + // bit: 3322222222221111... + // 1098765432109876... + // val: SSTTSSTTSSTTSSTT... + // wp.: 3333222211110000... + // + // where S - size is: + // 00 for 1 byte + // 01 for 2 bytes + // 10 for 8 bytes + // 11 for 4 bytes + return static_cast<uint64_t>(size == 8 ? 0x2 : size - 1) + << (18 + 4 * wp_index); +} + +// Returns bitmask for all bits controlling wp_index in DR7 +static inline uint64_t GetWatchControlBitmask(uint32_t wp_index) { + // DR7: + // bit: 33222222222211111111110000000000 + // 10987654321098765432109876543210 + // val: SSTTSSTTSSTTSSTTxxxxxxGLGLGLGLGL + // wp.: 3333222211110000xxxxxxEE33221100 + return GetBothEnableBitMask(wp_index) | (0xF << (16 + 4 * wp_index)); +} + +// Bit mask for control bits regarding all watchpoints. +static constexpr uint64_t watchpoint_all_control_bit_mask = 0xFFFF00FF; + +const RegisterInfo *NativeRegisterContextDBReg_x86::GetDR(int num) const { + assert(num >= 0 && num <= 7); + switch (GetRegisterInfoInterface().GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return GetRegisterInfoAtIndex(lldb_dr0_i386 + num); + case llvm::Triple::x86_64: + return GetRegisterInfoAtIndex(lldb_dr0_x86_64 + num); + default: + llvm_unreachable("Unhandled target architecture."); + } +} + +Status NativeRegisterContextDBReg_x86::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue dr6; + Status error = ReadRegister(GetDR(6), dr6); + if (error.Fail()) + is_hit = false; + else + is_hit = dr6.GetAsUInt64() & GetStatusBit(wp_index); + + return error; +} + +Status +NativeRegisterContextDBReg_x86::GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) { + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + for (wp_index = 0; wp_index < num_hw_wps; ++wp_index) { + bool is_hit; + Status error = IsWatchpointHit(wp_index, is_hit); + if (error.Fail()) { + wp_index = LLDB_INVALID_INDEX32; + return error; + } else if (is_hit) { + return error; + } + } + wp_index = LLDB_INVALID_INDEX32; + return Status(); +} + +Status NativeRegisterContextDBReg_x86::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue dr7; + Status error = ReadRegister(GetDR(7), dr7); + if (error.Fail()) + is_vacant = false; + else + is_vacant = !(dr7.GetAsUInt64() & GetEnableBit(wp_index)); + + return error; +} + +Status NativeRegisterContextDBReg_x86::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + // Read only watchpoints aren't supported on x86_64. Fall back to read/write + // waitchpoints instead. + // TODO: Add logic to detect when a write happens and ignore that watchpoint + // hit. + if (watch_flags == 2) + watch_flags = 3; + + if (watch_flags != 1 && watch_flags != 3) + return Status("Invalid read/write bits for watchpoint"); + if (size != 1 && size != 2 && size != 4 && size != 8) + return Status("Invalid size for watchpoint"); + + bool is_vacant; + Status error = IsWatchpointVacant(wp_index, is_vacant); + if (error.Fail()) + return error; + if (!is_vacant) + return Status("Watchpoint index not vacant"); + + RegisterValue dr7, drN; + error = ReadRegister(GetDR(7), dr7); + if (error.Fail()) + return error; + error = ReadRegister(GetDR(wp_index), drN); + if (error.Fail()) + return error; + + uint64_t control_bits = dr7.GetAsUInt64() & ~GetWatchControlBitmask(wp_index); + control_bits |= GetEnableBit(wp_index) | + GetWatchTypeBits(watch_flags, wp_index) | + GetWatchSizeBits(size, wp_index); + + // Clear dr6 if address or bits changed (i.e. we're not reenabling the same + // watchpoint). This can not be done when clearing watchpoints since + // the gdb-remote protocol repeatedly clears and readds watchpoints on all + // program threads, effectively clearing pending events on NetBSD. + // NB: enable bits in dr7 are always 0 here since we're (re)adding it + if (drN.GetAsUInt64() != addr || + (dr7.GetAsUInt64() & GetWatchControlBitmask(wp_index)) != + (GetWatchTypeBits(watch_flags, wp_index) | + GetWatchSizeBits(size, wp_index))) { + ClearWatchpointHit(wp_index); + + // We skip update to drN if neither address nor mode changed. + error = WriteRegister(GetDR(wp_index), RegisterValue(addr)); + if (error.Fail()) + return error; + } + + error = WriteRegister(GetDR(7), RegisterValue(control_bits)); + if (error.Fail()) + return error; + + return error; +} + +bool NativeRegisterContextDBReg_x86::ClearHardwareWatchpoint( + uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + RegisterValue dr7; + Status error = ReadRegister(GetDR(7), dr7); + if (error.Fail()) + return false; + + return WriteRegister(GetDR(7), RegisterValue(dr7.GetAsUInt64() & + ~GetBothEnableBitMask(wp_index))) + .Success(); +} + +Status NativeRegisterContextDBReg_x86::ClearWatchpointHit(uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue dr6; + Status error = ReadRegister(GetDR(6), dr6); + if (error.Fail()) + return error; + + return WriteRegister( + GetDR(6), RegisterValue(dr6.GetAsUInt64() & ~GetStatusBit(wp_index))); +} + +Status NativeRegisterContextDBReg_x86::ClearAllHardwareWatchpoints() { + RegisterValue dr7; + Status error = ReadRegister(GetDR(7), dr7); + if (error.Fail()) + return error; + return WriteRegister( + GetDR(7), + RegisterValue(dr7.GetAsUInt64() & ~watchpoint_all_control_bit_mask)); +} + +uint32_t NativeRegisterContextDBReg_x86::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + Log *log = GetLog(LLDBLog::Watchpoints); + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + for (uint32_t wp_index = 0; wp_index < num_hw_watchpoints; ++wp_index) { + bool is_vacant; + Status error = IsWatchpointVacant(wp_index, is_vacant); + if (is_vacant) { + error = SetHardwareWatchpointWithIndex(addr, size, watch_flags, wp_index); + if (error.Success()) + return wp_index; + } + if (error.Fail() && log) { + LLDB_LOGF(log, "NativeRegisterContextDBReg_x86::%s Error: %s", + __FUNCTION__, error.AsCString()); + } + } + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextDBReg_x86::GetWatchpointAddress(uint32_t wp_index) { + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + RegisterValue drN; + if (ReadRegister(GetDR(wp_index), drN).Fail()) + return LLDB_INVALID_ADDRESS; + return drN.GetAsUInt64(); +} + +uint32_t NativeRegisterContextDBReg_x86::NumSupportedHardwareWatchpoints() { + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_x86.h new file mode 100644 index 000000000000..4ca288d9dff7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_x86.h @@ -0,0 +1,54 @@ +//===-- NativeRegisterContextDBReg_x86.h ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_NativeRegisterContextDBReg_x86_h +#define lldb_NativeRegisterContextDBReg_x86_h + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" + +namespace lldb_private { + +class NativeRegisterContextDBReg_x86 + : public virtual NativeRegisterContextRegisterInfo { +public: + // NB: This constructor is here only because gcc<=6.5 requires a virtual base + // class initializer on abstract class (even though it is never used). It can + // be deleted once we move to gcc>=7.0. + NativeRegisterContextDBReg_x86(NativeThreadProtocol &thread) + : NativeRegisterContextRegisterInfo(thread, nullptr) {} + + Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool ClearHardwareWatchpoint(uint32_t wp_index) override; + + Status ClearWatchpointHit(uint32_t wp_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, + uint32_t wp_index); + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + virtual const RegisterInfo *GetDR(int num) const; +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextDBReg_x86_h diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp new file mode 100644 index 000000000000..3a875f7bb39b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.cpp @@ -0,0 +1,42 @@ +//===-- NativeRegisterContextRegisterInfo.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 "NativeRegisterContextRegisterInfo.h" +#include "lldb/lldb-private-forward.h" +#include "lldb/lldb-types.h" + +using namespace lldb_private; + +NativeRegisterContextRegisterInfo::NativeRegisterContextRegisterInfo( + NativeThreadProtocol &thread, + RegisterInfoInterface *register_info_interface) + : NativeRegisterContext(thread), + m_register_info_interface_up(register_info_interface) { + assert(register_info_interface && "null register_info_interface"); +} + +uint32_t NativeRegisterContextRegisterInfo::GetRegisterCount() const { + return m_register_info_interface_up->GetRegisterCount(); +} + +uint32_t NativeRegisterContextRegisterInfo::GetUserRegisterCount() const { + return m_register_info_interface_up->GetUserRegisterCount(); +} + +const RegisterInfo *NativeRegisterContextRegisterInfo::GetRegisterInfoAtIndex( + uint32_t reg_index) const { + if (reg_index <= GetRegisterCount()) + return m_register_info_interface_up->GetRegisterInfo() + reg_index; + else + return nullptr; +} + +const RegisterInfoInterface & +NativeRegisterContextRegisterInfo::GetRegisterInfoInterface() const { + return *m_register_info_interface_up; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h new file mode 100644 index 000000000000..0e96841fd909 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h @@ -0,0 +1,40 @@ +//===-- NativeRegisterContextRegisterInfo.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_SOURCE_PLUGINS_PROCESS_UTILITY_NATIVEREGISTERCONTEXTREGISTERINFO_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_NATIVEREGISTERCONTEXTREGISTERINFO_H + +#include <memory> + +#include "RegisterInfoInterface.h" +#include "lldb/Host/common/NativeRegisterContext.h" + +namespace lldb_private { +class NativeRegisterContextRegisterInfo : public NativeRegisterContext { +public: + /// + /// Construct a NativeRegisterContextRegisterInfo, taking ownership + /// of the register_info_interface pointer. + /// + NativeRegisterContextRegisterInfo( + NativeThreadProtocol &thread, + RegisterInfoInterface *register_info_interface); + + uint32_t GetRegisterCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterInfo *GetRegisterInfoAtIndex(uint32_t reg_index) const override; + + const RegisterInfoInterface &GetRegisterInfoInterface() const; + +protected: + std::unique_ptr<RegisterInfoInterface> m_register_info_interface_up; +}; +} +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp new file mode 100644 index 000000000000..6e4e5038566b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.cpp @@ -0,0 +1,99 @@ +//===-- NetBSDSignals.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 "NetBSDSignals.h" + +#ifdef __NetBSD__ +#include <csignal> + +#define ADD_SIGCODE(signal_name, signal_value, code_name, code_value, ...) \ + static_assert(signal_name == signal_value, \ + "Value mismatch for signal number " #signal_name); \ + static_assert(code_name == code_value, \ + "Value mismatch for signal code " #code_name); \ + AddSignalCode(signal_value, code_value, __VA_ARGS__) +#else +#define ADD_SIGCODE(signal_name, signal_value, code_name, code_value, ...) \ + AddSignalCode(signal_value, code_value, __VA_ARGS__) +#endif /* ifdef __NetBSD */ + +using namespace lldb_private; + +NetBSDSignals::NetBSDSignals() : UnixSignals() { Reset(); } + +void NetBSDSignals::Reset() { + UnixSignals::Reset(); + + // clang-format off + // SIGILL + ADD_SIGCODE(SIGILL, 4, ILL_ILLOPC, 1, "illegal opcode"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLOPN, 2, "illegal operand"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLADR, 3, "illegal addressing mode"); + ADD_SIGCODE(SIGILL, 4, ILL_ILLTRP, 4, "illegal trap"); + ADD_SIGCODE(SIGILL, 4, ILL_PRVOPC, 5, "privileged opcode"); + ADD_SIGCODE(SIGILL, 4, ILL_PRVREG, 6, "privileged register"); + ADD_SIGCODE(SIGILL, 4, ILL_COPROC, 7, "coprocessor error"); + ADD_SIGCODE(SIGILL, 4, ILL_BADSTK, 8, "internal stack error"); + + // SIGFPE + ADD_SIGCODE(SIGFPE, 8, FPE_INTDIV, 1, "integer divide by zero"); + ADD_SIGCODE(SIGFPE, 8, FPE_INTOVF, 2, "integer overflow"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTDIV, 3, "floating point divide by zero"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTOVF, 4, "floating point overflow"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTUND, 5, "floating point underflow"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTRES, 6, "floating point inexact result"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTINV, 7, "invalid floating point operation"); + ADD_SIGCODE(SIGFPE, 8, FPE_FLTSUB, 8, "subscript out of range"); + + // SIGBUS + ADD_SIGCODE(SIGBUS, 10, BUS_ADRALN, 1, "invalid address alignment"); + ADD_SIGCODE(SIGBUS, 10, BUS_ADRERR, 2, "non-existent physical address"); + ADD_SIGCODE(SIGBUS, 10, BUS_OBJERR, 3, "object specific hardware error"); + + // SIGSEGV + ADD_SIGCODE(SIGSEGV, 11, SEGV_MAPERR, 1, "address not mapped to object", + SignalCodePrintOption::Address); + ADD_SIGCODE(SIGSEGV, 11, SEGV_ACCERR, 2, "invalid permissions for mapped object", + SignalCodePrintOption::Address); + + // SIGNO NAME SUPPRESS STOP NOTIFY DESCRIPTION + // ===== ============== ======== ====== ====== ======================== + AddSignal(32, "SIGPWR", false, true, true, "power fail/restart (not reset when caught)"); + AddSignal(33, "SIGRTMIN", false, false, false, "real time signal 0"); + AddSignal(34, "SIGRTMIN+1", false, false, false, "real time signal 1"); + AddSignal(35, "SIGRTMIN+2", false, false, false, "real time signal 2"); + AddSignal(36, "SIGRTMIN+3", false, false, false, "real time signal 3"); + AddSignal(37, "SIGRTMIN+4", false, false, false, "real time signal 4"); + AddSignal(38, "SIGRTMIN+5", false, false, false, "real time signal 5"); + AddSignal(39, "SIGRTMIN+6", false, false, false, "real time signal 6"); + AddSignal(40, "SIGRTMIN+7", false, false, false, "real time signal 7"); + AddSignal(41, "SIGRTMIN+8", false, false, false, "real time signal 8"); + AddSignal(42, "SIGRTMIN+9", false, false, false, "real time signal 9"); + AddSignal(43, "SIGRTMIN+10", false, false, false, "real time signal 10"); + AddSignal(44, "SIGRTMIN+11", false, false, false, "real time signal 11"); + AddSignal(45, "SIGRTMIN+12", false, false, false, "real time signal 12"); + AddSignal(46, "SIGRTMIN+13", false, false, false, "real time signal 13"); + AddSignal(47, "SIGRTMIN+14", false, false, false, "real time signal 14"); + AddSignal(48, "SIGRTMIN+15", false, false, false, "real time signal 15"); + AddSignal(49, "SIGRTMIN-14", false, false, false, "real time signal 16"); + AddSignal(50, "SIGRTMAX-13", false, false, false, "real time signal 17"); + AddSignal(51, "SIGRTMAX-12", false, false, false, "real time signal 18"); + AddSignal(52, "SIGRTMAX-11", false, false, false, "real time signal 19"); + AddSignal(53, "SIGRTMAX-10", false, false, false, "real time signal 20"); + AddSignal(54, "SIGRTMAX-9", false, false, false, "real time signal 21"); + AddSignal(55, "SIGRTMAX-8", false, false, false, "real time signal 22"); + AddSignal(56, "SIGRTMAX-7", false, false, false, "real time signal 23"); + AddSignal(57, "SIGRTMAX-6", false, false, false, "real time signal 24"); + AddSignal(58, "SIGRTMAX-5", false, false, false, "real time signal 25"); + AddSignal(59, "SIGRTMAX-4", false, false, false, "real time signal 26"); + AddSignal(60, "SIGRTMAX-3", false, false, false, "real time signal 27"); + AddSignal(61, "SIGRTMAX-2", false, false, false, "real time signal 28"); + AddSignal(62, "SIGRTMAX-1", false, false, false, "real time signal 29"); + AddSignal(63, "SIGRTMAX", false, false, false, "real time signal 30"); + // clang-format on +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h new file mode 100644 index 000000000000..94bad7c19a49 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/NetBSDSignals.h @@ -0,0 +1,27 @@ +//===-- NetBSDSignals.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_SOURCE_PLUGINS_PROCESS_UTILITY_NETBSDSIGNALS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_NETBSDSIGNALS_H + +#include "lldb/Target/UnixSignals.h" + +namespace lldb_private { + +/// NetBSD specific set of Unix signals. +class NetBSDSignals : public UnixSignals { +public: + NetBSDSignals(); + +private: + void Reset() override; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_NETBSDSIGNALS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h new file mode 100644 index 000000000000..21582df91fb0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwinConstants.h @@ -0,0 +1,25 @@ +//===-- RegisterContextDarwinConstants.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWINCONSTANTS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWINCONSTANTS_H + +namespace lldb_private { + +/// Constants returned by various RegisterContextDarwin_*** functions. +#ifndef KERN_SUCCESS +#define KERN_SUCCESS 0 +#endif + +#ifndef KERN_INVALID_ARGUMENT +#define KERN_INVALID_ARGUMENT 4 +#endif + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWINCONSTANTS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp new file mode 100644 index 000000000000..c23e82a741a0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp @@ -0,0 +1,1745 @@ +//===-- RegisterContextDarwin_arm.cpp -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextDarwin_arm.h" +#include "RegisterContextDarwinConstants.h" + +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "Plugins/Process/Utility/InstructionUtils.h" + +#include <memory> + +#include "Utility/ARM_DWARF_Registers.h" +#include "Utility/ARM_ehframe_Registers.h" + +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +enum { + gpr_r0 = 0, + gpr_r1, + gpr_r2, + gpr_r3, + gpr_r4, + gpr_r5, + gpr_r6, + gpr_r7, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_sp = gpr_r13, + gpr_r14, + gpr_lr = gpr_r14, + gpr_r15, + gpr_pc = gpr_r15, + gpr_cpsr, + + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + fpu_fpscr, + + exc_exception, + exc_fsr, + exc_far, + + dbg_bvr0, + dbg_bvr1, + dbg_bvr2, + dbg_bvr3, + dbg_bvr4, + dbg_bvr5, + dbg_bvr6, + dbg_bvr7, + dbg_bvr8, + dbg_bvr9, + dbg_bvr10, + dbg_bvr11, + dbg_bvr12, + dbg_bvr13, + dbg_bvr14, + dbg_bvr15, + + dbg_bcr0, + dbg_bcr1, + dbg_bcr2, + dbg_bcr3, + dbg_bcr4, + dbg_bcr5, + dbg_bcr6, + dbg_bcr7, + dbg_bcr8, + dbg_bcr9, + dbg_bcr10, + dbg_bcr11, + dbg_bcr12, + dbg_bcr13, + dbg_bcr14, + dbg_bcr15, + + dbg_wvr0, + dbg_wvr1, + dbg_wvr2, + dbg_wvr3, + dbg_wvr4, + dbg_wvr5, + dbg_wvr6, + dbg_wvr7, + dbg_wvr8, + dbg_wvr9, + dbg_wvr10, + dbg_wvr11, + dbg_wvr12, + dbg_wvr13, + dbg_wvr14, + dbg_wvr15, + + dbg_wcr0, + dbg_wcr1, + dbg_wcr2, + dbg_wcr3, + dbg_wcr4, + dbg_wcr5, + dbg_wcr6, + dbg_wcr7, + dbg_wcr8, + dbg_wcr9, + dbg_wcr10, + dbg_wcr11, + dbg_wcr12, + dbg_wcr13, + dbg_wcr14, + dbg_wcr15, + + k_num_registers +}; + +#define GPR_OFFSET(idx) ((idx)*4) +#define FPU_OFFSET(idx) ((idx)*4 + sizeof(RegisterContextDarwin_arm::GPR)) +#define EXC_OFFSET(idx) \ + ((idx)*4 + sizeof(RegisterContextDarwin_arm::GPR) + \ + sizeof(RegisterContextDarwin_arm::FPU)) +#define DBG_OFFSET(reg) \ + ((LLVM_EXTENSION offsetof(RegisterContextDarwin_arm::DBG, reg) + \ + sizeof(RegisterContextDarwin_arm::GPR) + \ + sizeof(RegisterContextDarwin_arm::FPU) + \ + sizeof(RegisterContextDarwin_arm::EXC))) + +#define DEFINE_DBG(reg, i) \ + #reg, NULL, sizeof(((RegisterContextDarwin_arm::DBG *) NULL)->reg[i]), \ + DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM }, \ + nullptr, nullptr, nullptr, +#define REG_CONTEXT_SIZE \ + (sizeof(RegisterContextDarwin_arm::GPR) + \ + sizeof(RegisterContextDarwin_arm::FPU) + \ + sizeof(RegisterContextDarwin_arm::EXC)) + +static RegisterInfo g_register_infos[] = { + // General purpose registers + // NAME ALT SZ OFFSET ENCODING FORMAT + // EH_FRAME DWARF GENERIC + // PROCESS PLUGIN LLDB NATIVE + // ====== ======= == ============= ============= ============ + // =============== =============== ========================= + // ===================== ============= + {"r0", + nullptr, + 4, + GPR_OFFSET(0), + eEncodingUint, + eFormatHex, + {ehframe_r0, dwarf_r0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r0}, + nullptr, + nullptr, + nullptr, + }, + {"r1", + nullptr, + 4, + GPR_OFFSET(1), + eEncodingUint, + eFormatHex, + {ehframe_r1, dwarf_r1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r1}, + nullptr, + nullptr, + nullptr, + }, + {"r2", + nullptr, + 4, + GPR_OFFSET(2), + eEncodingUint, + eFormatHex, + {ehframe_r2, dwarf_r2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r2}, + nullptr, + nullptr, + nullptr, + }, + {"r3", + nullptr, + 4, + GPR_OFFSET(3), + eEncodingUint, + eFormatHex, + {ehframe_r3, dwarf_r3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r3}, + nullptr, + nullptr, + nullptr, + }, + {"r4", + nullptr, + 4, + GPR_OFFSET(4), + eEncodingUint, + eFormatHex, + {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r4}, + nullptr, + nullptr, + nullptr, + }, + {"r5", + nullptr, + 4, + GPR_OFFSET(5), + eEncodingUint, + eFormatHex, + {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r5}, + nullptr, + nullptr, + nullptr, + }, + {"r6", + nullptr, + 4, + GPR_OFFSET(6), + eEncodingUint, + eFormatHex, + {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r6}, + nullptr, + nullptr, + nullptr, + }, + {"r7", + nullptr, + 4, + GPR_OFFSET(7), + eEncodingUint, + eFormatHex, + {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, + gpr_r7}, + nullptr, + nullptr, + nullptr, + }, + {"r8", + nullptr, + 4, + GPR_OFFSET(8), + eEncodingUint, + eFormatHex, + {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r8}, + nullptr, + nullptr, + nullptr, + }, + {"r9", + nullptr, + 4, + GPR_OFFSET(9), + eEncodingUint, + eFormatHex, + {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_r9}, + nullptr, + nullptr, + nullptr, + }, + {"r10", + nullptr, + 4, + GPR_OFFSET(10), + eEncodingUint, + eFormatHex, + {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r10}, + nullptr, + nullptr, + nullptr, + }, + {"r11", + nullptr, + 4, + GPR_OFFSET(11), + eEncodingUint, + eFormatHex, + {ehframe_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r11}, + nullptr, + nullptr, + nullptr, + }, + {"r12", + nullptr, + 4, + GPR_OFFSET(12), + eEncodingUint, + eFormatHex, + {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r12}, + nullptr, + nullptr, + nullptr, + }, + {"sp", + "r13", + 4, + GPR_OFFSET(13), + eEncodingUint, + eFormatHex, + {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, + gpr_sp}, + nullptr, + nullptr, + nullptr, + }, + {"lr", + "r14", + 4, + GPR_OFFSET(14), + eEncodingUint, + eFormatHex, + {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM, + gpr_lr}, + nullptr, + nullptr, + nullptr, + }, + {"pc", + "r15", + 4, + GPR_OFFSET(15), + eEncodingUint, + eFormatHex, + {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, + gpr_pc}, + nullptr, + nullptr, + nullptr, + }, + {"cpsr", + "psr", + 4, + GPR_OFFSET(16), + eEncodingUint, + eFormatHex, + {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, + gpr_cpsr}, + nullptr, + nullptr, + nullptr, + }, + + {"s0", + nullptr, + 4, + FPU_OFFSET(0), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s0}, + nullptr, + nullptr, + nullptr, + }, + {"s1", + nullptr, + 4, + FPU_OFFSET(1), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s1}, + nullptr, + nullptr, + nullptr, + }, + {"s2", + nullptr, + 4, + FPU_OFFSET(2), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s2}, + nullptr, + nullptr, + nullptr, + }, + {"s3", + nullptr, + 4, + FPU_OFFSET(3), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s3}, + nullptr, + nullptr, + nullptr, + }, + {"s4", + nullptr, + 4, + FPU_OFFSET(4), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s4}, + nullptr, + nullptr, + nullptr, + }, + {"s5", + nullptr, + 4, + FPU_OFFSET(5), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s5}, + nullptr, + nullptr, + nullptr, + }, + {"s6", + nullptr, + 4, + FPU_OFFSET(6), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s6}, + nullptr, + nullptr, + nullptr, + }, + {"s7", + nullptr, + 4, + FPU_OFFSET(7), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s7}, + nullptr, + nullptr, + nullptr, + }, + {"s8", + nullptr, + 4, + FPU_OFFSET(8), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s8}, + nullptr, + nullptr, + nullptr, + }, + {"s9", + nullptr, + 4, + FPU_OFFSET(9), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s9}, + nullptr, + nullptr, + nullptr, + }, + {"s10", + nullptr, + 4, + FPU_OFFSET(10), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s10}, + nullptr, + nullptr, + nullptr, + }, + {"s11", + nullptr, + 4, + FPU_OFFSET(11), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s11}, + nullptr, + nullptr, + nullptr, + }, + {"s12", + nullptr, + 4, + FPU_OFFSET(12), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s12}, + nullptr, + nullptr, + nullptr, + }, + {"s13", + nullptr, + 4, + FPU_OFFSET(13), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s13}, + nullptr, + nullptr, + nullptr, + }, + {"s14", + nullptr, + 4, + FPU_OFFSET(14), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s14}, + nullptr, + nullptr, + nullptr, + }, + {"s15", + nullptr, + 4, + FPU_OFFSET(15), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s15}, + nullptr, + nullptr, + nullptr, + }, + {"s16", + nullptr, + 4, + FPU_OFFSET(16), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s16}, + nullptr, + nullptr, + nullptr, + }, + {"s17", + nullptr, + 4, + FPU_OFFSET(17), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s17}, + nullptr, + nullptr, + nullptr, + }, + {"s18", + nullptr, + 4, + FPU_OFFSET(18), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s18}, + nullptr, + nullptr, + nullptr, + }, + {"s19", + nullptr, + 4, + FPU_OFFSET(19), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s19}, + nullptr, + nullptr, + nullptr, + }, + {"s20", + nullptr, + 4, + FPU_OFFSET(20), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s20}, + nullptr, + nullptr, + nullptr, + }, + {"s21", + nullptr, + 4, + FPU_OFFSET(21), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s21}, + nullptr, + nullptr, + nullptr, + }, + {"s22", + nullptr, + 4, + FPU_OFFSET(22), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s22}, + nullptr, + nullptr, + nullptr, + }, + {"s23", + nullptr, + 4, + FPU_OFFSET(23), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s23}, + nullptr, + nullptr, + nullptr, + }, + {"s24", + nullptr, + 4, + FPU_OFFSET(24), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s24}, + nullptr, + nullptr, + nullptr, + }, + {"s25", + nullptr, + 4, + FPU_OFFSET(25), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s25}, + nullptr, + nullptr, + nullptr, + }, + {"s26", + nullptr, + 4, + FPU_OFFSET(26), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s26}, + nullptr, + nullptr, + nullptr, + }, + {"s27", + nullptr, + 4, + FPU_OFFSET(27), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s27}, + nullptr, + nullptr, + nullptr, + }, + {"s28", + nullptr, + 4, + FPU_OFFSET(28), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s28}, + nullptr, + nullptr, + nullptr, + }, + {"s29", + nullptr, + 4, + FPU_OFFSET(29), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s29}, + nullptr, + nullptr, + nullptr, + }, + {"s30", + nullptr, + 4, + FPU_OFFSET(30), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s30}, + nullptr, + nullptr, + nullptr, + }, + {"s31", + nullptr, + 4, + FPU_OFFSET(31), + eEncodingIEEE754, + eFormatFloat, + {LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + fpu_s31}, + nullptr, + nullptr, + nullptr, + }, + {"fpscr", + nullptr, + 4, + FPU_OFFSET(32), + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_fpscr}, + nullptr, + nullptr, + nullptr, + }, + + {"exception", + nullptr, + 4, + EXC_OFFSET(0), + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_exception}, + nullptr, + nullptr, + nullptr, + }, + {"fsr", + nullptr, + 4, + EXC_OFFSET(1), + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_fsr}, + nullptr, + nullptr, + nullptr, + }, + {"far", + nullptr, + 4, + EXC_OFFSET(2), + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_far}, + nullptr, + nullptr, + nullptr, + }, + + {DEFINE_DBG(bvr, 0)}, + {DEFINE_DBG(bvr, 1)}, + {DEFINE_DBG(bvr, 2)}, + {DEFINE_DBG(bvr, 3)}, + {DEFINE_DBG(bvr, 4)}, + {DEFINE_DBG(bvr, 5)}, + {DEFINE_DBG(bvr, 6)}, + {DEFINE_DBG(bvr, 7)}, + {DEFINE_DBG(bvr, 8)}, + {DEFINE_DBG(bvr, 9)}, + {DEFINE_DBG(bvr, 10)}, + {DEFINE_DBG(bvr, 11)}, + {DEFINE_DBG(bvr, 12)}, + {DEFINE_DBG(bvr, 13)}, + {DEFINE_DBG(bvr, 14)}, + {DEFINE_DBG(bvr, 15)}, + + {DEFINE_DBG(bcr, 0)}, + {DEFINE_DBG(bcr, 1)}, + {DEFINE_DBG(bcr, 2)}, + {DEFINE_DBG(bcr, 3)}, + {DEFINE_DBG(bcr, 4)}, + {DEFINE_DBG(bcr, 5)}, + {DEFINE_DBG(bcr, 6)}, + {DEFINE_DBG(bcr, 7)}, + {DEFINE_DBG(bcr, 8)}, + {DEFINE_DBG(bcr, 9)}, + {DEFINE_DBG(bcr, 10)}, + {DEFINE_DBG(bcr, 11)}, + {DEFINE_DBG(bcr, 12)}, + {DEFINE_DBG(bcr, 13)}, + {DEFINE_DBG(bcr, 14)}, + {DEFINE_DBG(bcr, 15)}, + + {DEFINE_DBG(wvr, 0)}, + {DEFINE_DBG(wvr, 1)}, + {DEFINE_DBG(wvr, 2)}, + {DEFINE_DBG(wvr, 3)}, + {DEFINE_DBG(wvr, 4)}, + {DEFINE_DBG(wvr, 5)}, + {DEFINE_DBG(wvr, 6)}, + {DEFINE_DBG(wvr, 7)}, + {DEFINE_DBG(wvr, 8)}, + {DEFINE_DBG(wvr, 9)}, + {DEFINE_DBG(wvr, 10)}, + {DEFINE_DBG(wvr, 11)}, + {DEFINE_DBG(wvr, 12)}, + {DEFINE_DBG(wvr, 13)}, + {DEFINE_DBG(wvr, 14)}, + {DEFINE_DBG(wvr, 15)}, + + {DEFINE_DBG(wcr, 0)}, + {DEFINE_DBG(wcr, 1)}, + {DEFINE_DBG(wcr, 2)}, + {DEFINE_DBG(wcr, 3)}, + {DEFINE_DBG(wcr, 4)}, + {DEFINE_DBG(wcr, 5)}, + {DEFINE_DBG(wcr, 6)}, + {DEFINE_DBG(wcr, 7)}, + {DEFINE_DBG(wcr, 8)}, + {DEFINE_DBG(wcr, 9)}, + {DEFINE_DBG(wcr, 10)}, + {DEFINE_DBG(wcr, 11)}, + {DEFINE_DBG(wcr, 12)}, + {DEFINE_DBG(wcr, 13)}, + {DEFINE_DBG(wcr, 14)}, + {DEFINE_DBG(wcr, 15)}}; + +// General purpose registers +static uint32_t g_gpr_regnums[] = { + gpr_r0, gpr_r1, gpr_r2, gpr_r3, gpr_r4, gpr_r5, gpr_r6, gpr_r7, gpr_r8, + gpr_r9, gpr_r10, gpr_r11, gpr_r12, gpr_sp, gpr_lr, gpr_pc, gpr_cpsr}; + +// Floating point registers +static uint32_t g_fpu_regnums[] = { + fpu_s0, fpu_s1, fpu_s2, fpu_s3, fpu_s4, fpu_s5, fpu_s6, + fpu_s7, fpu_s8, fpu_s9, fpu_s10, fpu_s11, fpu_s12, fpu_s13, + fpu_s14, fpu_s15, fpu_s16, fpu_s17, fpu_s18, fpu_s19, fpu_s20, + fpu_s21, fpu_s22, fpu_s23, fpu_s24, fpu_s25, fpu_s26, fpu_s27, + fpu_s28, fpu_s29, fpu_s30, fpu_s31, fpu_fpscr, +}; + +// Exception registers + +static uint32_t g_exc_regnums[] = { + exc_exception, exc_fsr, exc_far, +}; + +static size_t k_num_register_infos = std::size(g_register_infos); + +RegisterContextDarwin_arm::RegisterContextDarwin_arm( + Thread &thread, uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() { + uint32_t i; + for (i = 0; i < kNumErrors; i++) { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextDarwin_arm::~RegisterContextDarwin_arm() = default; + +void RegisterContextDarwin_arm::InvalidateAllRegisters() { + InvalidateAllRegisterStates(); +} + +size_t RegisterContextDarwin_arm::GetRegisterCount() { + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextDarwin_arm::GetRegisterInfoAtIndex(size_t reg) { + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return nullptr; +} + +size_t RegisterContextDarwin_arm::GetRegisterInfosCount() { + return k_num_register_infos; +} + +const RegisterInfo *RegisterContextDarwin_arm::GetRegisterInfos() { + return g_register_infos; +} + +// Number of registers in each register set +const size_t k_num_gpr_registers = std::size(g_gpr_regnums); +const size_t k_num_fpu_registers = std::size(g_fpu_regnums); +const size_t k_num_exc_registers = std::size(g_exc_regnums); + +// Register set definitions. The first definitions at register set index of +// zero is for all registers, followed by other registers sets. The register +// information for the all register set need not be filled in. +static const RegisterSet g_reg_sets[] = { + { + "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, + }, + {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums}, + {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}}; + +const size_t k_num_regsets = std::size(g_reg_sets); + +size_t RegisterContextDarwin_arm::GetRegisterSetCount() { + return k_num_regsets; +} + +const RegisterSet *RegisterContextDarwin_arm::GetRegisterSet(size_t reg_set) { + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return nullptr; +} + +// Register information definitions for 32 bit i386. +int RegisterContextDarwin_arm::GetSetForNativeRegNum(int reg) { + if (reg < fpu_s0) + return GPRRegSet; + else if (reg < exc_exception) + return FPURegSet; + else if (reg < k_num_registers) + return EXCRegSet; + return -1; +} + +int RegisterContextDarwin_arm::ReadGPR(bool force) { + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr)); + } + return GetError(GPRRegSet, Read); +} + +int RegisterContextDarwin_arm::ReadFPU(bool force) { + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu)); + } + return GetError(FPURegSet, Read); +} + +int RegisterContextDarwin_arm::ReadEXC(bool force) { + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadEXC(GetThreadID(), set, exc)); + } + return GetError(EXCRegSet, Read); +} + +int RegisterContextDarwin_arm::ReadDBG(bool force) { + int set = DBGRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadDBG(GetThreadID(), set, dbg)); + } + return GetError(DBGRegSet, Read); +} + +int RegisterContextDarwin_arm::WriteGPR() { + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr)); + SetError(set, Read, -1); + return GetError(GPRRegSet, Write); +} + +int RegisterContextDarwin_arm::WriteFPU() { + int set = FPURegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu)); + SetError(set, Read, -1); + return GetError(FPURegSet, Write); +} + +int RegisterContextDarwin_arm::WriteEXC() { + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc)); + SetError(set, Read, -1); + return GetError(EXCRegSet, Write); +} + +int RegisterContextDarwin_arm::WriteDBG() { + int set = DBGRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(set, Write, DoWriteDBG(GetThreadID(), set, dbg)); + SetError(set, Read, -1); + return GetError(DBGRegSet, Write); +} + +int RegisterContextDarwin_arm::ReadRegisterSet(uint32_t set, bool force) { + switch (set) { + case GPRRegSet: + return ReadGPR(force); + case GPRAltRegSet: + return ReadGPR(force); + case FPURegSet: + return ReadFPU(force); + case EXCRegSet: + return ReadEXC(force); + case DBGRegSet: + return ReadDBG(force); + default: + break; + } + return KERN_INVALID_ARGUMENT; +} + +int RegisterContextDarwin_arm::WriteRegisterSet(uint32_t set) { + // Make sure we have a valid context to set. + if (RegisterSetIsCached(set)) { + switch (set) { + case GPRRegSet: + return WriteGPR(); + case GPRAltRegSet: + return WriteGPR(); + case FPURegSet: + return WriteFPU(); + case EXCRegSet: + return WriteEXC(); + case DBGRegSet: + return WriteDBG(); + default: + break; + } + } + return KERN_INVALID_ARGUMENT; +} + +void RegisterContextDarwin_arm::LogDBGRegisters(Log *log, const DBG &dbg) { + if (log) { + for (uint32_t i = 0; i < 16; i++) + LLDB_LOGF(log, + "BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { " + "0x%8.8x, 0x%8.8x }", + i, i, dbg.bvr[i], dbg.bcr[i], i, i, dbg.wvr[i], dbg.wcr[i]); + } +} + +bool RegisterContextDarwin_arm::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = RegisterContextDarwin_arm::GetSetForNativeRegNum(reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + value.SetUInt32(gpr.r[reg - gpr_r0]); + break; + case gpr_cpsr: + value.SetUInt32(gpr.cpsr); + break; + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + value.SetUInt32(fpu.floats.s[reg], RegisterValue::eTypeFloat); + break; + + case fpu_fpscr: + value.SetUInt32(fpu.fpscr); + break; + + case exc_exception: + value.SetUInt32(exc.exception); + break; + case exc_fsr: + value.SetUInt32(exc.fsr); + break; + case exc_far: + value.SetUInt32(exc.far); + break; + + default: + value.SetValueToInvalid(); + return false; + } + return true; +} + +bool RegisterContextDarwin_arm::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = GetSetForNativeRegNum(reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + gpr.r[reg - gpr_r0] = value.GetAsUInt32(); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + fpu.floats.s[reg] = value.GetAsUInt32(); + break; + + case fpu_fpscr: + fpu.fpscr = value.GetAsUInt32(); + break; + + case exc_exception: + exc.exception = value.GetAsUInt32(); + break; + case exc_fsr: + exc.fsr = value.GetAsUInt32(); + break; + case exc_far: + exc.far = value.GetAsUInt32(); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool RegisterContextDarwin_arm::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0); + if (data_sp && ReadGPR(false) == KERN_SUCCESS && + ReadFPU(false) == KERN_SUCCESS && ReadEXC(false) == KERN_SUCCESS) { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy(dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy(dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool RegisterContextDarwin_arm::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy(&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy(&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy(&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == KERN_SUCCESS) + ++success_count; + if (WriteFPU() == KERN_SUCCESS) + ++success_count; + if (WriteEXC() == KERN_SUCCESS) + ++success_count; + return success_count == 3; + } + return false; +} + +uint32_t RegisterContextDarwin_arm::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t reg) { + if (kind == eRegisterKindGeneric) { + switch (reg) { + case LLDB_REGNUM_GENERIC_PC: + return gpr_pc; + case LLDB_REGNUM_GENERIC_SP: + return gpr_sp; + case LLDB_REGNUM_GENERIC_FP: + return gpr_r7; + case LLDB_REGNUM_GENERIC_RA: + return gpr_lr; + case LLDB_REGNUM_GENERIC_FLAGS: + return gpr_cpsr; + default: + break; + } + } else if (kind == eRegisterKindDWARF) { + switch (reg) { + case dwarf_r0: + return gpr_r0; + case dwarf_r1: + return gpr_r1; + case dwarf_r2: + return gpr_r2; + case dwarf_r3: + return gpr_r3; + case dwarf_r4: + return gpr_r4; + case dwarf_r5: + return gpr_r5; + case dwarf_r6: + return gpr_r6; + case dwarf_r7: + return gpr_r7; + case dwarf_r8: + return gpr_r8; + case dwarf_r9: + return gpr_r9; + case dwarf_r10: + return gpr_r10; + case dwarf_r11: + return gpr_r11; + case dwarf_r12: + return gpr_r12; + case dwarf_sp: + return gpr_sp; + case dwarf_lr: + return gpr_lr; + case dwarf_pc: + return gpr_pc; + case dwarf_spsr: + return gpr_cpsr; + + case dwarf_s0: + return fpu_s0; + case dwarf_s1: + return fpu_s1; + case dwarf_s2: + return fpu_s2; + case dwarf_s3: + return fpu_s3; + case dwarf_s4: + return fpu_s4; + case dwarf_s5: + return fpu_s5; + case dwarf_s6: + return fpu_s6; + case dwarf_s7: + return fpu_s7; + case dwarf_s8: + return fpu_s8; + case dwarf_s9: + return fpu_s9; + case dwarf_s10: + return fpu_s10; + case dwarf_s11: + return fpu_s11; + case dwarf_s12: + return fpu_s12; + case dwarf_s13: + return fpu_s13; + case dwarf_s14: + return fpu_s14; + case dwarf_s15: + return fpu_s15; + case dwarf_s16: + return fpu_s16; + case dwarf_s17: + return fpu_s17; + case dwarf_s18: + return fpu_s18; + case dwarf_s19: + return fpu_s19; + case dwarf_s20: + return fpu_s20; + case dwarf_s21: + return fpu_s21; + case dwarf_s22: + return fpu_s22; + case dwarf_s23: + return fpu_s23; + case dwarf_s24: + return fpu_s24; + case dwarf_s25: + return fpu_s25; + case dwarf_s26: + return fpu_s26; + case dwarf_s27: + return fpu_s27; + case dwarf_s28: + return fpu_s28; + case dwarf_s29: + return fpu_s29; + case dwarf_s30: + return fpu_s30; + case dwarf_s31: + return fpu_s31; + + default: + break; + } + } else if (kind == eRegisterKindEHFrame) { + switch (reg) { + case ehframe_r0: + return gpr_r0; + case ehframe_r1: + return gpr_r1; + case ehframe_r2: + return gpr_r2; + case ehframe_r3: + return gpr_r3; + case ehframe_r4: + return gpr_r4; + case ehframe_r5: + return gpr_r5; + case ehframe_r6: + return gpr_r6; + case ehframe_r7: + return gpr_r7; + case ehframe_r8: + return gpr_r8; + case ehframe_r9: + return gpr_r9; + case ehframe_r10: + return gpr_r10; + case ehframe_r11: + return gpr_r11; + case ehframe_r12: + return gpr_r12; + case ehframe_sp: + return gpr_sp; + case ehframe_lr: + return gpr_lr; + case ehframe_pc: + return gpr_pc; + case ehframe_cpsr: + return gpr_cpsr; + } + } else if (kind == eRegisterKindLLDB) { + return reg; + } + return LLDB_INVALID_REGNUM; +} + +uint32_t RegisterContextDarwin_arm::NumSupportedHardwareBreakpoints() { +#if defined(__APPLE__) && defined(__arm__) + // Set the init value to something that will let us know that we need to + // autodetect how many breakpoints are supported dynamically... + static uint32_t g_num_supported_hw_breakpoints = UINT32_MAX; + if (g_num_supported_hw_breakpoints == UINT32_MAX) { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_breakpoints = 0; + + uint32_t register_DBGDIDR; + + asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR)); + g_num_supported_hw_breakpoints = Bits32(register_DBGDIDR, 27, 24); + // Zero is reserved for the BRP count, so don't increment it if it is zero + if (g_num_supported_hw_breakpoints > 0) + g_num_supported_hw_breakpoints++; + } + return g_num_supported_hw_breakpoints; +#else + // TODO: figure out remote case here! + return 6; +#endif +} + +uint32_t RegisterContextDarwin_arm::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + // Make sure our address isn't bogus + if (addr & 1) + return LLDB_INVALID_INDEX32; + + int kret = ReadDBG(false); + + if (kret == KERN_SUCCESS) { + const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints(); + uint32_t i; + for (i = 0; i < num_hw_breakpoints; ++i) { + if ((dbg.bcr[i] & BCR_ENABLE) == 0) + break; // We found an available hw breakpoint slot (in i) + } + + // See if we found an available hw breakpoint slot above + if (i < num_hw_breakpoints) { + // Make sure bits 1:0 are clear in our address + dbg.bvr[i] = addr & ~((lldb::addr_t)3); + + if (size == 2 || addr & 2) { + uint32_t byte_addr_select = (addr & 2) ? BAS_IMVA_2_3 : BAS_IMVA_0_1; + + // We have a thumb breakpoint + // We have an ARM breakpoint + dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address match + byte_addr_select | // Set the correct byte address select + // so we only trigger on the correct + // opcode + S_USER | // Which modes should this breakpoint stop in? + BCR_ENABLE; // Enable this hardware breakpoint + // if (log) log->Printf + // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint( + // addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / + // 0x%8.8x (Thumb)", + // addr, + // size, + // i, + // i, + // dbg.bvr[i], + // dbg.bcr[i]); + } else if (size == 4) { + // We have an ARM breakpoint + dbg.bcr[i] = + BCR_M_IMVA_MATCH | // Stop on address match + BAS_IMVA_ALL | // Stop on any of the four bytes following the IMVA + S_USER | // Which modes should this breakpoint stop in? + BCR_ENABLE; // Enable this hardware breakpoint + // if (log) log->Printf + // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint( + // addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / + // 0x%8.8x (ARM)", + // addr, + // size, + // i, + // i, + // dbg.bvr[i], + // dbg.bcr[i]); + } + + kret = WriteDBG(); + // if (log) log->Printf + // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint() + // WriteDBG() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } + // else + // { + // if (log) log->Printf + // ("RegisterContextDarwin_arm::EnableHardwareBreakpoint(addr = + // %8.8p, size = %u) => all hardware breakpoint resources are + // being used.", addr, size); + // } + } + + return LLDB_INVALID_INDEX32; +} + +bool RegisterContextDarwin_arm::ClearHardwareBreakpoint(uint32_t hw_index) { + int kret = ReadDBG(false); + + const uint32_t num_hw_points = NumSupportedHardwareBreakpoints(); + if (kret == KERN_SUCCESS) { + if (hw_index < num_hw_points) { + dbg.bcr[hw_index] = 0; + // if (log) log->Printf + // ("RegisterContextDarwin_arm::SetHardwareBreakpoint( %u ) - + // BVR%u = 0x%8.8x BCR%u = 0x%8.8x", + // hw_index, + // hw_index, + // dbg.bvr[hw_index], + // hw_index, + // dbg.bcr[hw_index]); + + kret = WriteDBG(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + +uint32_t RegisterContextDarwin_arm::NumSupportedHardwareWatchpoints() { +#if defined(__APPLE__) && defined(__arm__) + // Set the init value to something that will let us know that we need to + // autodetect how many watchpoints are supported dynamically... + static uint32_t g_num_supported_hw_watchpoints = UINT32_MAX; + if (g_num_supported_hw_watchpoints == UINT32_MAX) { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_watchpoints = 0; + + uint32_t register_DBGDIDR; + asm("mrc p14, 0, %0, c0, c0, 0" : "=r"(register_DBGDIDR)); + g_num_supported_hw_watchpoints = Bits32(register_DBGDIDR, 31, 28) + 1; + } + return g_num_supported_hw_watchpoints; +#else + // TODO: figure out remote case here! + return 2; +#endif +} + +uint32_t RegisterContextDarwin_arm::SetHardwareWatchpoint(lldb::addr_t addr, + size_t size, + bool read, + bool write) { + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + // Can't watch zero bytes + if (size == 0) + return LLDB_INVALID_INDEX32; + + // We must watch for either read or write + if (!read && !write) + return LLDB_INVALID_INDEX32; + + // Can't watch more than 4 bytes per WVR/WCR pair + if (size > 4) + return LLDB_INVALID_INDEX32; + + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair. Since we have at most so we can only watch + // until the next 4 byte boundary and we need to make sure we can properly + // encode this. + uint32_t addr_word_offset = addr % 4; + // if (log) log->Printf + // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() - + // addr_word_offset = 0x%8.8x", addr_word_offset); + + uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset; + // if (log) log->Printf + // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() - byte_mask = + // 0x%8.8x", byte_mask); + if (byte_mask > 0xfu) + return LLDB_INVALID_INDEX32; + + // Read the debug state + int kret = ReadDBG(false); + + if (kret == KERN_SUCCESS) { + // Check to make sure we have the needed hardware support + uint32_t i = 0; + + for (i = 0; i < num_hw_watchpoints; ++i) { + if ((dbg.wcr[i] & WCR_ENABLE) == 0) + break; // We found an available hw breakpoint slot (in i) + } + + // See if we found an available hw breakpoint slot above + if (i < num_hw_watchpoints) { + // Make the byte_mask into a valid Byte Address Select mask + uint32_t byte_address_select = byte_mask << 5; + // Make sure bits 1:0 are clear in our address + dbg.wvr[i] = addr & ~((lldb::addr_t)3); + dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA + // that we will watch + S_USER | // Stop only in user mode + (read ? WCR_LOAD : 0) | // Stop on read access? + (write ? WCR_STORE : 0) | // Stop on write access? + WCR_ENABLE; // Enable this watchpoint; + + kret = WriteDBG(); + // if (log) log->Printf + // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() + // WriteDBG() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } else { + // if (log) log->Printf + // ("RegisterContextDarwin_arm::EnableHardwareWatchpoint(): All + // hardware resources (%u) are in use.", num_hw_watchpoints); + } + } + return LLDB_INVALID_INDEX32; +} + +bool RegisterContextDarwin_arm::ClearHardwareWatchpoint(uint32_t hw_index) { + int kret = ReadDBG(false); + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (kret == KERN_SUCCESS) { + if (hw_index < num_hw_points) { + dbg.wcr[hw_index] = 0; + // if (log) log->Printf + // ("RegisterContextDarwin_arm::ClearHardwareWatchpoint( %u ) - + // WVR%u = 0x%8.8x WCR%u = 0x%8.8x", + // hw_index, + // hw_index, + // dbg.wvr[hw_index], + // hw_index, + // dbg.wcr[hw_index]); + + kret = WriteDBG(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h new file mode 100644 index 000000000000..7ff1bded81f4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h @@ -0,0 +1,264 @@ +//===-- RegisterContextDarwin_arm.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_ARM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_ARM_H + +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" + +// BCR address match type +#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21)) +#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21)) +#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21)) +#define BCR_M_RESERVED ((uint32_t)(3u << 21)) + +// Link a BVR/BCR or WVR/WCR pair to another +#define E_ENABLE_LINKING ((uint32_t)(1u << 20)) + +// Byte Address Select +#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5)) +#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6)) +#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7)) +#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8)) +#define BAS_IMVA_0_1 ((uint32_t)(3u << 5)) +#define BAS_IMVA_2_3 ((uint32_t)(3u << 7)) +#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5)) + +// Break only in privileged or user mode +#define S_RSVD ((uint32_t)(0u << 1)) +#define S_PRIV ((uint32_t)(1u << 1)) +#define S_USER ((uint32_t)(2u << 1)) +#define S_PRIV_USER ((S_PRIV) | (S_USER)) + +#define BCR_ENABLE ((uint32_t)(1u)) +#define WCR_ENABLE ((uint32_t)(1u)) + +// Watchpoint load/store +#define WCR_LOAD ((uint32_t)(1u << 3)) +#define WCR_STORE ((uint32_t)(1u << 4)) + +class RegisterContextDarwin_arm : public lldb_private::RegisterContext { +public: + RegisterContextDarwin_arm(lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + ~RegisterContextDarwin_arm() override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue ®_value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue ®_value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + uint32_t NumSupportedHardwareBreakpoints() override; + + uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, + bool write) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + struct GPR { + uint32_t r[16]; // R0-R15 + uint32_t cpsr; // CPSR + }; + + struct QReg { + uint8_t bytes[16]; + }; + + struct FPU { + union { + uint32_t s[32]; + uint64_t d[32]; + QReg q[16]; // the 128-bit NEON registers + } floats; + uint32_t fpscr; + }; + + // struct NeonReg + // { + // uint8_t bytes[16]; + // }; + // + // struct VFPv3 + // { + // union { + // uint32_t s[32]; + // uint64_t d[32]; + // NeonReg q[16]; + // } v3; + // uint32_t fpscr; + // }; + + struct EXC { + uint32_t exception; + uint32_t fsr; /* Fault status */ + uint32_t far; /* Virtual Fault Address */ + }; + + struct DBG { + uint32_t bvr[16]; + uint32_t bcr[16]; + uint32_t wvr[16]; + uint32_t wcr[16]; + }; + + static void LogDBGRegisters(lldb_private::Log *log, const DBG &dbg); + +protected: + enum { + GPRRegSet = 1, // ARM_THREAD_STATE + GPRAltRegSet = 9, // ARM_THREAD_STATE32 + FPURegSet = 2, // ARM_VFP_STATE + EXCRegSet = 3, // ARM_EXCEPTION_STATE + DBGRegSet = 4 // ARM_DEBUG_STATE + }; + + enum { + GPRWordCount = sizeof(GPR) / sizeof(uint32_t), + FPUWordCount = sizeof(FPU) / sizeof(uint32_t), + EXCWordCount = sizeof(EXC) / sizeof(uint32_t), + DBGWordCount = sizeof(DBG) / sizeof(uint32_t) + }; + + enum { Read = 0, Write = 1, kNumErrors = 2 }; + + GPR gpr; + FPU fpu; + EXC exc; + DBG dbg; + int gpr_errs[2]; // Read/Write errors + int fpu_errs[2]; // Read/Write errors + int exc_errs[2]; // Read/Write errors + int dbg_errs[2]; // Read/Write errors + + void InvalidateAllRegisterStates() { + SetError(GPRRegSet, Read, -1); + SetError(FPURegSet, Read, -1); + SetError(EXCRegSet, Read, -1); + } + + int GetError(int flavor, uint32_t err_idx) const { + if (err_idx < kNumErrors) { + switch (flavor) { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: + return gpr_errs[err_idx]; + case FPURegSet: + return fpu_errs[err_idx]; + case EXCRegSet: + return exc_errs[err_idx]; + case DBGRegSet: + return dbg_errs[err_idx]; + default: + break; + } + } + return -1; + } + + bool SetError(int flavor, uint32_t err_idx, int err) { + if (err_idx < kNumErrors) { + switch (flavor) { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + case DBGRegSet: + exc_errs[err_idx] = err; + return true; + + default: + break; + } + } + return false; + } + + bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; } + + int ReadGPR(bool force); + + int ReadFPU(bool force); + + int ReadEXC(bool force); + + int ReadDBG(bool force); + + int WriteGPR(); + + int WriteFPU(); + + int WriteEXC(); + + int WriteDBG(); + + // Subclasses override these to do the actual reading. + virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) { return -1; } + + virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0; + + virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0; + + virtual int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) = 0; + + virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0; + + virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0; + + virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0; + + virtual int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) = 0; + + int ReadRegisterSet(uint32_t set, bool force); + + int WriteRegisterSet(uint32_t set); + + static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num); + + static int GetSetForNativeRegNum(int reg_num); + + static size_t GetRegisterInfosCount(); + + static const lldb_private::RegisterInfo *GetRegisterInfos(); +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_ARM_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp new file mode 100644 index 000000000000..3bcd9a28e3f1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.cpp @@ -0,0 +1,1041 @@ +//===-- RegisterContextDarwin_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextDarwin_arm64.h" +#include "RegisterContextDarwinConstants.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Compiler.h" + +#include "Plugins/Process/Utility/InstructionUtils.h" + +#include <memory> + +#if defined(__APPLE__) && (defined(__arm64__) || defined(__aarch64__)) +#include <sys/types.h> +#include <sys/sysctl.h> +#endif + +#include "Utility/ARM64_DWARF_Registers.h" + +using namespace lldb; +using namespace lldb_private; + +#define GPR_OFFSET(idx) ((idx)*8) +#define GPR_OFFSET_NAME(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::GPR, reg)) + +#define FPU_OFFSET(idx) ((idx)*16 + sizeof(RegisterContextDarwin_arm64::GPR)) +#define FPU_OFFSET_NAME(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::FPU, reg)) + +#define EXC_OFFSET_NAME(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::EXC, reg) + \ + sizeof(RegisterContextDarwin_arm64::GPR) + \ + sizeof(RegisterContextDarwin_arm64::FPU)) +#define DBG_OFFSET_NAME(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_arm64::DBG, reg) + \ + sizeof(RegisterContextDarwin_arm64::GPR) + \ + sizeof(RegisterContextDarwin_arm64::FPU) + \ + sizeof(RegisterContextDarwin_arm64::EXC)) + +#define DEFINE_DBG(reg, i) \ + #reg, NULL, \ + sizeof(((RegisterContextDarwin_arm64::DBG *) NULL)->reg[i]), \ + DBG_OFFSET_NAME(reg[i]), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM }, \ + NULL, NULL, NULL +#define REG_CONTEXT_SIZE \ + (sizeof(RegisterContextDarwin_arm64::GPR) + \ + sizeof(RegisterContextDarwin_arm64::FPU) + \ + sizeof(RegisterContextDarwin_arm64::EXC)) + +// Include RegisterInfos_arm64 to declare our g_register_infos_arm64 structure. +#define DECLARE_REGISTER_INFOS_ARM64_STRUCT +#include "RegisterInfos_arm64.h" +#undef DECLARE_REGISTER_INFOS_ARM64_STRUCT + +// General purpose registers +static uint32_t g_gpr_regnums[] = { + gpr_x0, gpr_x1, gpr_x2, gpr_x3, gpr_x4, gpr_x5, gpr_x6, + gpr_x7, gpr_x8, gpr_x9, gpr_x10, gpr_x11, gpr_x12, gpr_x13, + gpr_x14, gpr_x15, gpr_x16, gpr_x17, gpr_x18, gpr_x19, gpr_x20, + gpr_x21, gpr_x22, gpr_x23, gpr_x24, gpr_x25, gpr_x26, gpr_x27, + gpr_x28, gpr_fp, gpr_lr, gpr_sp, gpr_pc, gpr_cpsr}; + +// Floating point registers +static uint32_t g_fpu_regnums[] = { + fpu_v0, fpu_v1, fpu_v2, fpu_v3, fpu_v4, fpu_v5, fpu_v6, + fpu_v7, fpu_v8, fpu_v9, fpu_v10, fpu_v11, fpu_v12, fpu_v13, + fpu_v14, fpu_v15, fpu_v16, fpu_v17, fpu_v18, fpu_v19, fpu_v20, + fpu_v21, fpu_v22, fpu_v23, fpu_v24, fpu_v25, fpu_v26, fpu_v27, + fpu_v28, fpu_v29, fpu_v30, fpu_v31, fpu_fpsr, fpu_fpcr}; + +// Exception registers + +static uint32_t g_exc_regnums[] = {exc_far, exc_esr, exc_exception}; + +static size_t k_num_register_infos = std::size(g_register_infos_arm64_le); + +RegisterContextDarwin_arm64::RegisterContextDarwin_arm64( + Thread &thread, uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc(), dbg() { + uint32_t i; + for (i = 0; i < kNumErrors; i++) { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextDarwin_arm64::~RegisterContextDarwin_arm64() = default; + +void RegisterContextDarwin_arm64::InvalidateAllRegisters() { + InvalidateAllRegisterStates(); +} + +size_t RegisterContextDarwin_arm64::GetRegisterCount() { + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextDarwin_arm64::GetRegisterInfoAtIndex(size_t reg) { + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos_arm64_le[reg]; + return nullptr; +} + +size_t RegisterContextDarwin_arm64::GetRegisterInfosCount() { + return k_num_register_infos; +} + +const RegisterInfo *RegisterContextDarwin_arm64::GetRegisterInfos() { + return g_register_infos_arm64_le; +} + +// Number of registers in each register set +const size_t k_num_gpr_registers = std::size(g_gpr_regnums); +const size_t k_num_fpu_registers = std::size(g_fpu_regnums); +const size_t k_num_exc_registers = std::size(g_exc_regnums); + +// Register set definitions. The first definitions at register set index of +// zero is for all registers, followed by other registers sets. The register +// information for the all register set need not be filled in. +static const RegisterSet g_reg_sets[] = { + { + "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, + }, + {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums}, + {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}}; + +const size_t k_num_regsets = std::size(g_reg_sets); + +size_t RegisterContextDarwin_arm64::GetRegisterSetCount() { + return k_num_regsets; +} + +const RegisterSet *RegisterContextDarwin_arm64::GetRegisterSet(size_t reg_set) { + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return nullptr; +} + +// Register information definitions for arm64 +int RegisterContextDarwin_arm64::GetSetForNativeRegNum(int reg) { + if (reg < fpu_v0) + return GPRRegSet; + else if (reg < exc_far) + return FPURegSet; + else if (reg < k_num_registers) + return EXCRegSet; + return -1; +} + +int RegisterContextDarwin_arm64::ReadGPR(bool force) { + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr)); + } + return GetError(GPRRegSet, Read); +} + +int RegisterContextDarwin_arm64::ReadFPU(bool force) { + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu)); + } + return GetError(FPURegSet, Read); +} + +int RegisterContextDarwin_arm64::ReadEXC(bool force) { + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadEXC(GetThreadID(), set, exc)); + } + return GetError(EXCRegSet, Read); +} + +int RegisterContextDarwin_arm64::ReadDBG(bool force) { + int set = DBGRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadDBG(GetThreadID(), set, dbg)); + } + return GetError(DBGRegSet, Read); +} + +int RegisterContextDarwin_arm64::WriteGPR() { + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr)); + SetError(set, Read, -1); + return GetError(GPRRegSet, Write); +} + +int RegisterContextDarwin_arm64::WriteFPU() { + int set = FPURegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu)); + SetError(set, Read, -1); + return GetError(FPURegSet, Write); +} + +int RegisterContextDarwin_arm64::WriteEXC() { + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc)); + SetError(set, Read, -1); + return GetError(EXCRegSet, Write); +} + +int RegisterContextDarwin_arm64::WriteDBG() { + int set = DBGRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError(set, Write, DoWriteDBG(GetThreadID(), set, dbg)); + SetError(set, Read, -1); + return GetError(DBGRegSet, Write); +} + +int RegisterContextDarwin_arm64::ReadRegisterSet(uint32_t set, bool force) { + switch (set) { + case GPRRegSet: + return ReadGPR(force); + case FPURegSet: + return ReadFPU(force); + case EXCRegSet: + return ReadEXC(force); + case DBGRegSet: + return ReadDBG(force); + default: + break; + } + return KERN_INVALID_ARGUMENT; +} + +int RegisterContextDarwin_arm64::WriteRegisterSet(uint32_t set) { + // Make sure we have a valid context to set. + if (RegisterSetIsCached(set)) { + switch (set) { + case GPRRegSet: + return WriteGPR(); + case FPURegSet: + return WriteFPU(); + case EXCRegSet: + return WriteEXC(); + case DBGRegSet: + return WriteDBG(); + default: + break; + } + } + return KERN_INVALID_ARGUMENT; +} + +void RegisterContextDarwin_arm64::LogDBGRegisters(Log *log, const DBG &dbg) { + if (log) { + for (uint32_t i = 0; i < 16; i++) + LLDB_LOGF(log, + "BVR%-2u/BCR%-2u = { 0x%8.8" PRIu64 ", 0x%8.8" PRIu64 + " } WVR%-2u/WCR%-2u " + "= { 0x%8.8" PRIu64 ", 0x%8.8" PRIu64 " }", + i, i, dbg.bvr[i], dbg.bcr[i], i, i, dbg.wvr[i], dbg.wcr[i]); + } +} + +bool RegisterContextDarwin_arm64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = RegisterContextDarwin_arm64::GetSetForNativeRegNum(reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) { + case gpr_x0: + case gpr_x1: + case gpr_x2: + case gpr_x3: + case gpr_x4: + case gpr_x5: + case gpr_x6: + case gpr_x7: + case gpr_x8: + case gpr_x9: + case gpr_x10: + case gpr_x11: + case gpr_x12: + case gpr_x13: + case gpr_x14: + case gpr_x15: + case gpr_x16: + case gpr_x17: + case gpr_x18: + case gpr_x19: + case gpr_x20: + case gpr_x21: + case gpr_x22: + case gpr_x23: + case gpr_x24: + case gpr_x25: + case gpr_x26: + case gpr_x27: + case gpr_x28: + value.SetUInt64(gpr.x[reg - gpr_x0]); + break; + case gpr_fp: + value.SetUInt64(gpr.fp); + break; + case gpr_sp: + value.SetUInt64(gpr.sp); + break; + case gpr_lr: + value.SetUInt64(gpr.lr); + break; + case gpr_pc: + value.SetUInt64(gpr.pc); + break; + case gpr_cpsr: + value.SetUInt64(gpr.cpsr); + break; + + case gpr_w0: + case gpr_w1: + case gpr_w2: + case gpr_w3: + case gpr_w4: + case gpr_w5: + case gpr_w6: + case gpr_w7: + case gpr_w8: + case gpr_w9: + case gpr_w10: + case gpr_w11: + case gpr_w12: + case gpr_w13: + case gpr_w14: + case gpr_w15: + case gpr_w16: + case gpr_w17: + case gpr_w18: + case gpr_w19: + case gpr_w20: + case gpr_w21: + case gpr_w22: + case gpr_w23: + case gpr_w24: + case gpr_w25: + case gpr_w26: + case gpr_w27: + case gpr_w28: { + ProcessSP process_sp(m_thread.GetProcess()); + if (process_sp.get()) { + DataExtractor regdata(&gpr.x[reg - gpr_w0], 8, process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + offset_t offset = 0; + uint64_t retval = regdata.GetMaxU64(&offset, 8); + uint32_t retval_lower32 = static_cast<uint32_t>(retval & 0xffffffff); + value.SetUInt32(retval_lower32); + } + } break; + + case fpu_v0: + case fpu_v1: + case fpu_v2: + case fpu_v3: + case fpu_v4: + case fpu_v5: + case fpu_v6: + case fpu_v7: + case fpu_v8: + case fpu_v9: + case fpu_v10: + case fpu_v11: + case fpu_v12: + case fpu_v13: + case fpu_v14: + case fpu_v15: + case fpu_v16: + case fpu_v17: + case fpu_v18: + case fpu_v19: + case fpu_v20: + case fpu_v21: + case fpu_v22: + case fpu_v23: + case fpu_v24: + case fpu_v25: + case fpu_v26: + case fpu_v27: + case fpu_v28: + case fpu_v29: + case fpu_v30: + case fpu_v31: + value.SetBytes(fpu.v[reg - fpu_v0].bytes, reg_info->byte_size, + endian::InlHostByteOrder()); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: { + ProcessSP process_sp(m_thread.GetProcess()); + if (process_sp.get()) { + DataExtractor regdata(&fpu.v[reg - fpu_s0], 4, process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + offset_t offset = 0; + value.SetFloat(regdata.GetFloat(&offset)); + } + } break; + + case fpu_d0: + case fpu_d1: + case fpu_d2: + case fpu_d3: + case fpu_d4: + case fpu_d5: + case fpu_d6: + case fpu_d7: + case fpu_d8: + case fpu_d9: + case fpu_d10: + case fpu_d11: + case fpu_d12: + case fpu_d13: + case fpu_d14: + case fpu_d15: + case fpu_d16: + case fpu_d17: + case fpu_d18: + case fpu_d19: + case fpu_d20: + case fpu_d21: + case fpu_d22: + case fpu_d23: + case fpu_d24: + case fpu_d25: + case fpu_d26: + case fpu_d27: + case fpu_d28: + case fpu_d29: + case fpu_d30: + case fpu_d31: { + ProcessSP process_sp(m_thread.GetProcess()); + if (process_sp.get()) { + DataExtractor regdata(&fpu.v[reg - fpu_d0], 8, process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + offset_t offset = 0; + value.SetDouble(regdata.GetDouble(&offset)); + } + } break; + + case fpu_fpsr: + value.SetUInt32(fpu.fpsr); + break; + + case fpu_fpcr: + value.SetUInt32(fpu.fpcr); + break; + + case exc_exception: + value.SetUInt32(exc.exception); + break; + case exc_esr: + value.SetUInt32(exc.esr); + break; + case exc_far: + value.SetUInt64(exc.far); + break; + + default: + value.SetValueToInvalid(); + return false; + } + return true; +} + +bool RegisterContextDarwin_arm64::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = GetSetForNativeRegNum(reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) { + case gpr_x0: + case gpr_x1: + case gpr_x2: + case gpr_x3: + case gpr_x4: + case gpr_x5: + case gpr_x6: + case gpr_x7: + case gpr_x8: + case gpr_x9: + case gpr_x10: + case gpr_x11: + case gpr_x12: + case gpr_x13: + case gpr_x14: + case gpr_x15: + case gpr_x16: + case gpr_x17: + case gpr_x18: + case gpr_x19: + case gpr_x20: + case gpr_x21: + case gpr_x22: + case gpr_x23: + case gpr_x24: + case gpr_x25: + case gpr_x26: + case gpr_x27: + case gpr_x28: + case gpr_fp: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + gpr.x[reg - gpr_x0] = value.GetAsUInt64(); + break; + + case fpu_v0: + case fpu_v1: + case fpu_v2: + case fpu_v3: + case fpu_v4: + case fpu_v5: + case fpu_v6: + case fpu_v7: + case fpu_v8: + case fpu_v9: + case fpu_v10: + case fpu_v11: + case fpu_v12: + case fpu_v13: + case fpu_v14: + case fpu_v15: + case fpu_v16: + case fpu_v17: + case fpu_v18: + case fpu_v19: + case fpu_v20: + case fpu_v21: + case fpu_v22: + case fpu_v23: + case fpu_v24: + case fpu_v25: + case fpu_v26: + case fpu_v27: + case fpu_v28: + case fpu_v29: + case fpu_v30: + case fpu_v31: + ::memcpy(fpu.v[reg - fpu_v0].bytes, value.GetBytes(), + value.GetByteSize()); + break; + + case fpu_fpsr: + fpu.fpsr = value.GetAsUInt32(); + break; + + case fpu_fpcr: + fpu.fpcr = value.GetAsUInt32(); + break; + + case exc_exception: + exc.exception = value.GetAsUInt32(); + break; + case exc_esr: + exc.esr = value.GetAsUInt32(); + break; + case exc_far: + exc.far = value.GetAsUInt64(); + break; + + default: + return false; + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool RegisterContextDarwin_arm64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0); + if (ReadGPR(false) == KERN_SUCCESS && ReadFPU(false) == KERN_SUCCESS && + ReadEXC(false) == KERN_SUCCESS) { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy(dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy(dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool RegisterContextDarwin_arm64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy(&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy(&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy(&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == KERN_SUCCESS) + ++success_count; + if (WriteFPU() == KERN_SUCCESS) + ++success_count; + if (WriteEXC() == KERN_SUCCESS) + ++success_count; + return success_count == 3; + } + return false; +} + +uint32_t RegisterContextDarwin_arm64::ConvertRegisterKindToRegisterNumber( + RegisterKind kind, uint32_t reg) { + if (kind == eRegisterKindGeneric) { + switch (reg) { + case LLDB_REGNUM_GENERIC_PC: + return gpr_pc; + case LLDB_REGNUM_GENERIC_SP: + return gpr_sp; + case LLDB_REGNUM_GENERIC_FP: + return gpr_fp; + case LLDB_REGNUM_GENERIC_RA: + return gpr_lr; + case LLDB_REGNUM_GENERIC_FLAGS: + return gpr_cpsr; + default: + break; + } + } else if (kind == eRegisterKindDWARF) { + switch (reg) { + case arm64_dwarf::x0: + return gpr_x0; + case arm64_dwarf::x1: + return gpr_x1; + case arm64_dwarf::x2: + return gpr_x2; + case arm64_dwarf::x3: + return gpr_x3; + case arm64_dwarf::x4: + return gpr_x4; + case arm64_dwarf::x5: + return gpr_x5; + case arm64_dwarf::x6: + return gpr_x6; + case arm64_dwarf::x7: + return gpr_x7; + case arm64_dwarf::x8: + return gpr_x8; + case arm64_dwarf::x9: + return gpr_x9; + case arm64_dwarf::x10: + return gpr_x10; + case arm64_dwarf::x11: + return gpr_x11; + case arm64_dwarf::x12: + return gpr_x12; + case arm64_dwarf::x13: + return gpr_x13; + case arm64_dwarf::x14: + return gpr_x14; + case arm64_dwarf::x15: + return gpr_x15; + case arm64_dwarf::x16: + return gpr_x16; + case arm64_dwarf::x17: + return gpr_x17; + case arm64_dwarf::x18: + return gpr_x18; + case arm64_dwarf::x19: + return gpr_x19; + case arm64_dwarf::x20: + return gpr_x20; + case arm64_dwarf::x21: + return gpr_x21; + case arm64_dwarf::x22: + return gpr_x22; + case arm64_dwarf::x23: + return gpr_x23; + case arm64_dwarf::x24: + return gpr_x24; + case arm64_dwarf::x25: + return gpr_x25; + case arm64_dwarf::x26: + return gpr_x26; + case arm64_dwarf::x27: + return gpr_x27; + case arm64_dwarf::x28: + return gpr_x28; + + case arm64_dwarf::fp: + return gpr_fp; + case arm64_dwarf::sp: + return gpr_sp; + case arm64_dwarf::lr: + return gpr_lr; + case arm64_dwarf::pc: + return gpr_pc; + case arm64_dwarf::cpsr: + return gpr_cpsr; + + case arm64_dwarf::v0: + return fpu_v0; + case arm64_dwarf::v1: + return fpu_v1; + case arm64_dwarf::v2: + return fpu_v2; + case arm64_dwarf::v3: + return fpu_v3; + case arm64_dwarf::v4: + return fpu_v4; + case arm64_dwarf::v5: + return fpu_v5; + case arm64_dwarf::v6: + return fpu_v6; + case arm64_dwarf::v7: + return fpu_v7; + case arm64_dwarf::v8: + return fpu_v8; + case arm64_dwarf::v9: + return fpu_v9; + case arm64_dwarf::v10: + return fpu_v10; + case arm64_dwarf::v11: + return fpu_v11; + case arm64_dwarf::v12: + return fpu_v12; + case arm64_dwarf::v13: + return fpu_v13; + case arm64_dwarf::v14: + return fpu_v14; + case arm64_dwarf::v15: + return fpu_v15; + case arm64_dwarf::v16: + return fpu_v16; + case arm64_dwarf::v17: + return fpu_v17; + case arm64_dwarf::v18: + return fpu_v18; + case arm64_dwarf::v19: + return fpu_v19; + case arm64_dwarf::v20: + return fpu_v20; + case arm64_dwarf::v21: + return fpu_v21; + case arm64_dwarf::v22: + return fpu_v22; + case arm64_dwarf::v23: + return fpu_v23; + case arm64_dwarf::v24: + return fpu_v24; + case arm64_dwarf::v25: + return fpu_v25; + case arm64_dwarf::v26: + return fpu_v26; + case arm64_dwarf::v27: + return fpu_v27; + case arm64_dwarf::v28: + return fpu_v28; + case arm64_dwarf::v29: + return fpu_v29; + case arm64_dwarf::v30: + return fpu_v30; + case arm64_dwarf::v31: + return fpu_v31; + + default: + break; + } + } else if (kind == eRegisterKindEHFrame) { + switch (reg) { + case arm64_ehframe::x0: + return gpr_x0; + case arm64_ehframe::x1: + return gpr_x1; + case arm64_ehframe::x2: + return gpr_x2; + case arm64_ehframe::x3: + return gpr_x3; + case arm64_ehframe::x4: + return gpr_x4; + case arm64_ehframe::x5: + return gpr_x5; + case arm64_ehframe::x6: + return gpr_x6; + case arm64_ehframe::x7: + return gpr_x7; + case arm64_ehframe::x8: + return gpr_x8; + case arm64_ehframe::x9: + return gpr_x9; + case arm64_ehframe::x10: + return gpr_x10; + case arm64_ehframe::x11: + return gpr_x11; + case arm64_ehframe::x12: + return gpr_x12; + case arm64_ehframe::x13: + return gpr_x13; + case arm64_ehframe::x14: + return gpr_x14; + case arm64_ehframe::x15: + return gpr_x15; + case arm64_ehframe::x16: + return gpr_x16; + case arm64_ehframe::x17: + return gpr_x17; + case arm64_ehframe::x18: + return gpr_x18; + case arm64_ehframe::x19: + return gpr_x19; + case arm64_ehframe::x20: + return gpr_x20; + case arm64_ehframe::x21: + return gpr_x21; + case arm64_ehframe::x22: + return gpr_x22; + case arm64_ehframe::x23: + return gpr_x23; + case arm64_ehframe::x24: + return gpr_x24; + case arm64_ehframe::x25: + return gpr_x25; + case arm64_ehframe::x26: + return gpr_x26; + case arm64_ehframe::x27: + return gpr_x27; + case arm64_ehframe::x28: + return gpr_x28; + case arm64_ehframe::fp: + return gpr_fp; + case arm64_ehframe::sp: + return gpr_sp; + case arm64_ehframe::lr: + return gpr_lr; + case arm64_ehframe::pc: + return gpr_pc; + case arm64_ehframe::cpsr: + return gpr_cpsr; + } + } else if (kind == eRegisterKindLLDB) { + return reg; + } + return LLDB_INVALID_REGNUM; +} + +uint32_t RegisterContextDarwin_arm64::NumSupportedHardwareWatchpoints() { +#if defined(__APPLE__) && (defined(__arm64__) || defined(__aarch64__)) + // autodetect how many watchpoints are supported dynamically... + static uint32_t g_num_supported_hw_watchpoints = UINT32_MAX; + if (g_num_supported_hw_watchpoints == UINT32_MAX) { + size_t len; + uint32_t n = 0; + len = sizeof(n); + if (::sysctlbyname("hw.optional.watchpoint", &n, &len, NULL, 0) == 0) { + g_num_supported_hw_watchpoints = n; + } + } + return g_num_supported_hw_watchpoints; +#else + // TODO: figure out remote case here! + return 2; +#endif +} + +uint32_t RegisterContextDarwin_arm64::SetHardwareWatchpoint(lldb::addr_t addr, + size_t size, + bool read, + bool write) { + // if (log) log->Printf + // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint(addr = %8.8p, + // size = %u, read = %u, write = %u)", addr, size, read, write); + + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + // Can't watch zero bytes + if (size == 0) + return LLDB_INVALID_INDEX32; + + // We must watch for either read or write + if (!read && !write) + return LLDB_INVALID_INDEX32; + + // Can't watch more than 4 bytes per WVR/WCR pair + if (size > 4) + return LLDB_INVALID_INDEX32; + + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair. Since we have at most so we can only watch + // until the next 4 byte boundary and we need to make sure we can properly + // encode this. + uint32_t addr_word_offset = addr % 4; + // if (log) log->Printf + // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint() - + // addr_word_offset = 0x%8.8x", addr_word_offset); + + uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset; + // if (log) log->Printf + // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint() - byte_mask = + // 0x%8.8x", byte_mask); + if (byte_mask > 0xfu) + return LLDB_INVALID_INDEX32; + + // Read the debug state + int kret = ReadDBG(false); + + if (kret == KERN_SUCCESS) { + // Check to make sure we have the needed hardware support + uint32_t i = 0; + + for (i = 0; i < num_hw_watchpoints; ++i) { + if ((dbg.wcr[i] & WCR_ENABLE) == 0) + break; // We found an available hw breakpoint slot (in i) + } + + // See if we found an available hw breakpoint slot above + if (i < num_hw_watchpoints) { + // Make the byte_mask into a valid Byte Address Select mask + uint32_t byte_address_select = byte_mask << 5; + // Make sure bits 1:0 are clear in our address + dbg.wvr[i] = addr & ~((lldb::addr_t)3); + dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA + // that we will watch + S_USER | // Stop only in user mode + (read ? WCR_LOAD : 0) | // Stop on read access? + (write ? WCR_STORE : 0) | // Stop on write access? + WCR_ENABLE; // Enable this watchpoint; + + kret = WriteDBG(); + // if (log) log->Printf + // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint() + // WriteDBG() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } else { + // if (log) log->Printf + // ("RegisterContextDarwin_arm64::EnableHardwareWatchpoint(): + // All hardware resources (%u) are in use.", + // num_hw_watchpoints); + } + } + return LLDB_INVALID_INDEX32; +} + +bool RegisterContextDarwin_arm64::ClearHardwareWatchpoint(uint32_t hw_index) { + int kret = ReadDBG(false); + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (kret == KERN_SUCCESS) { + if (hw_index < num_hw_points) { + dbg.wcr[hw_index] = 0; + // if (log) log->Printf + // ("RegisterContextDarwin_arm64::ClearHardwareWatchpoint( %u ) + // - WVR%u = 0x%8.8x WCR%u = 0x%8.8x", + // hw_index, + // hw_index, + // dbg.wvr[hw_index], + // hw_index, + // dbg.wcr[hw_index]); + + kret = WriteDBG(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h new file mode 100644 index 000000000000..a0d7821ae9e8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_arm64.h @@ -0,0 +1,231 @@ +//===-- RegisterContextDarwin_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_ARM64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_ARM64_H + +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" + +// Break only in privileged or user mode +#define S_RSVD ((uint32_t)(0u << 1)) +#define S_PRIV ((uint32_t)(1u << 1)) +#define S_USER ((uint32_t)(2u << 1)) +#define S_PRIV_USER ((S_PRIV) | (S_USER)) + +#define WCR_ENABLE ((uint32_t)(1u)) + +// Watchpoint load/store +#define WCR_LOAD ((uint32_t)(1u << 3)) +#define WCR_STORE ((uint32_t)(1u << 4)) + +class RegisterContextDarwin_arm64 : public lldb_private::RegisterContext { +public: + RegisterContextDarwin_arm64(lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + ~RegisterContextDarwin_arm64() override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue ®_value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue ®_value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, + bool write) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + // mirrors <mach/arm/thread_status.h> arm_thread_state64_t + struct GPR { + uint64_t x[29]; // x0-x28 + uint64_t fp; // x29 + uint64_t lr; // x30 + uint64_t sp; // x31 + uint64_t pc; // pc + uint32_t cpsr; // cpsr + }; + + struct VReg { + alignas(16) char bytes[16]; + }; + + // mirrors <mach/arm/thread_status.h> arm_neon_state64_t + struct FPU { + VReg v[32]; + uint32_t fpsr; + uint32_t fpcr; + }; + + // mirrors <mach/arm/thread_status.h> arm_exception_state64_t + struct EXC { + uint64_t far; // Virtual Fault Address + uint32_t esr; // Exception syndrome + uint32_t exception; // number of arm exception token + }; + + // mirrors <mach/arm/thread_status.h> arm_debug_state64_t + struct DBG { + uint64_t bvr[16]; + uint64_t bcr[16]; + uint64_t wvr[16]; + uint64_t wcr[16]; + uint64_t mdscr_el1; + }; + + static void LogDBGRegisters(lldb_private::Log *log, const DBG &dbg); + +protected: + enum { + GPRRegSet = 6, // ARM_THREAD_STATE64 + FPURegSet = 17, // ARM_NEON_STATE64 + EXCRegSet = 7, // ARM_EXCEPTION_STATE64 + DBGRegSet = 15 // ARM_DEBUG_STATE64 + }; + + enum { + GPRWordCount = sizeof(GPR) / sizeof(uint32_t), // ARM_THREAD_STATE64_COUNT + FPUWordCount = sizeof(FPU) / sizeof(uint32_t), // ARM_NEON_STATE64_COUNT + EXCWordCount = + sizeof(EXC) / sizeof(uint32_t), // ARM_EXCEPTION_STATE64_COUNT + DBGWordCount = sizeof(DBG) / sizeof(uint32_t) // ARM_DEBUG_STATE64_COUNT + }; + + enum { Read = 0, Write = 1, kNumErrors = 2 }; + + GPR gpr; + FPU fpu; + EXC exc; + DBG dbg; + int gpr_errs[2]; // Read/Write errors + int fpu_errs[2]; // Read/Write errors + int exc_errs[2]; // Read/Write errors + int dbg_errs[2]; // Read/Write errors + + void InvalidateAllRegisterStates() { + SetError(GPRRegSet, Read, -1); + SetError(FPURegSet, Read, -1); + SetError(EXCRegSet, Read, -1); + } + + int GetError(int flavor, uint32_t err_idx) const { + if (err_idx < kNumErrors) { + switch (flavor) { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: + return gpr_errs[err_idx]; + case FPURegSet: + return fpu_errs[err_idx]; + case EXCRegSet: + return exc_errs[err_idx]; + case DBGRegSet: + return dbg_errs[err_idx]; + default: + break; + } + } + return -1; + } + + bool SetError(int flavor, uint32_t err_idx, int err) { + if (err_idx < kNumErrors) { + switch (flavor) { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + case DBGRegSet: + exc_errs[err_idx] = err; + return true; + + default: + break; + } + } + return false; + } + + bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; } + + int ReadGPR(bool force); + + int ReadFPU(bool force); + + int ReadEXC(bool force); + + int ReadDBG(bool force); + + int WriteGPR(); + + int WriteFPU(); + + int WriteEXC(); + + int WriteDBG(); + + // Subclasses override these to do the actual reading. + virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) { return -1; } + + virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0; + + virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0; + + virtual int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) = 0; + + virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0; + + virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0; + + virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0; + + virtual int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) = 0; + + int ReadRegisterSet(uint32_t set, bool force); + + int WriteRegisterSet(uint32_t set); + + static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num); + + static int GetSetForNativeRegNum(int reg_num); + + static size_t GetRegisterInfosCount(); + + static const lldb_private::RegisterInfo *GetRegisterInfos(); +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_ARM64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp new file mode 100644 index 000000000000..bae34af43a92 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp @@ -0,0 +1,963 @@ +//===-- RegisterContextDarwin_i386.cpp ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Compiler.h" + +#include <cstddef> + +#include <memory> + +#include "RegisterContextDarwin_i386.h" + +using namespace lldb; +using namespace lldb_private; + +enum { + gpr_eax = 0, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs, + + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + + exc_trapno, + exc_err, + exc_faultvaddr, + + k_num_registers, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp +}; + +enum { + ehframe_eax = 0, + ehframe_ecx, + ehframe_edx, + ehframe_ebx, + ehframe_ebp, + ehframe_esp, + ehframe_esi, + ehframe_edi, + ehframe_eip, + ehframe_eflags +}; + +enum { + dwarf_eax = 0, + dwarf_ecx, + dwarf_edx, + dwarf_ebx, + dwarf_esp, + dwarf_ebp, + dwarf_esi, + dwarf_edi, + dwarf_eip, + dwarf_eflags, + dwarf_stmm0 = 11, + dwarf_stmm1, + dwarf_stmm2, + dwarf_stmm3, + dwarf_stmm4, + dwarf_stmm5, + dwarf_stmm6, + dwarf_stmm7, + dwarf_xmm0 = 21, + dwarf_xmm1, + dwarf_xmm2, + dwarf_xmm3, + dwarf_xmm4, + dwarf_xmm5, + dwarf_xmm6, + dwarf_xmm7 +}; + +#define GPR_OFFSET(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_i386::GPR, reg)) +#define FPU_OFFSET(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_i386::FPU, reg) + \ + sizeof(RegisterContextDarwin_i386::GPR)) +#define EXC_OFFSET(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_i386::EXC, reg) + \ + sizeof(RegisterContextDarwin_i386::GPR) + \ + sizeof(RegisterContextDarwin_i386::FPU)) + +// These macros will auto define the register name, alt name, register size, +// register offset, encoding, format and native register. This ensures that the +// register state structures are defined correctly and have the correct sizes +// and offsets. +#define DEFINE_GPR(reg, alt) \ + #reg, alt, sizeof(((RegisterContextDarwin_i386::GPR *) NULL)->reg), \ + GPR_OFFSET(reg), eEncodingUint, eFormatHex +#define DEFINE_FPU_UINT(reg) \ + #reg, NULL, sizeof(((RegisterContextDarwin_i386::FPU *) NULL)->reg), \ + FPU_OFFSET(reg), eEncodingUint, eFormatHex +#define DEFINE_FPU_VECT(reg, i) \ + #reg #i, NULL, \ + sizeof(((RegisterContextDarwin_i386::FPU *) NULL)->reg[i].bytes), \ + FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, \ + {LLDB_INVALID_REGNUM, dwarf_##reg##i, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + fpu_##reg##i }, \ + nullptr, nullptr, nullptr, + +#define DEFINE_EXC(reg) \ + #reg, NULL, sizeof(((RegisterContextDarwin_i386::EXC *) NULL)->reg), \ + EXC_OFFSET(reg), eEncodingUint, eFormatHex +#define REG_CONTEXT_SIZE \ + (sizeof(RegisterContextDarwin_i386::GPR) + \ + sizeof(RegisterContextDarwin_i386::FPU) + \ + sizeof(RegisterContextDarwin_i386::EXC)) + +static RegisterInfo g_register_infos[] = { + // Macro auto defines most stuff eh_frame DWARF + // GENERIC PROCESS PLUGIN LLDB + // =============================== ======================= + // =================== ========================= ================== + // ================= + {DEFINE_GPR(eax, nullptr), + {ehframe_eax, dwarf_eax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_eax}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(ebx, nullptr), + {ehframe_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_ebx}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(ecx, nullptr), + {ehframe_ecx, dwarf_ecx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_ecx}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(edx, nullptr), + {ehframe_edx, dwarf_edx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_edx}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(edi, nullptr), + {ehframe_edi, dwarf_edi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_edi}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(esi, nullptr), + {ehframe_esi, dwarf_esi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_esi}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(ebp, "fp"), + {ehframe_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, + gpr_ebp}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(esp, "sp"), + {ehframe_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, + gpr_esp}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(ss, nullptr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_ss}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(eflags, "flags"), + {ehframe_eflags, dwarf_eflags, LLDB_REGNUM_GENERIC_FLAGS, + LLDB_INVALID_REGNUM, gpr_eflags}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(eip, "pc"), + {ehframe_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, + gpr_eip}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(cs, nullptr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_cs}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(ds, nullptr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_ds}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(es, nullptr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_es}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(fs, nullptr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_fs}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(gs, nullptr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_gs}, + nullptr, + nullptr, + nullptr, + }, + + {DEFINE_FPU_UINT(fcw), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_fcw}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(fsw), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_fsw}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(ftw), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_ftw}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(fop), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_fop}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(ip), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_ip}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(cs), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_cs}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(dp), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_dp}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(ds), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_ds}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(mxcsr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_mxcsr}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(mxcsrmask), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_mxcsrmask}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_VECT(stmm, 0)}, + {DEFINE_FPU_VECT(stmm, 1)}, + {DEFINE_FPU_VECT(stmm, 2)}, + {DEFINE_FPU_VECT(stmm, 3)}, + {DEFINE_FPU_VECT(stmm, 4)}, + {DEFINE_FPU_VECT(stmm, 5)}, + {DEFINE_FPU_VECT(stmm, 6)}, + {DEFINE_FPU_VECT(stmm, 7)}, + {DEFINE_FPU_VECT(xmm, 0)}, + {DEFINE_FPU_VECT(xmm, 1)}, + {DEFINE_FPU_VECT(xmm, 2)}, + {DEFINE_FPU_VECT(xmm, 3)}, + {DEFINE_FPU_VECT(xmm, 4)}, + {DEFINE_FPU_VECT(xmm, 5)}, + {DEFINE_FPU_VECT(xmm, 6)}, + {DEFINE_FPU_VECT(xmm, 7)}, + + {DEFINE_EXC(trapno), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_trapno}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_EXC(err), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_err}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_EXC(faultvaddr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_faultvaddr}, + nullptr, + nullptr, + nullptr, + }}; + +static size_t k_num_register_infos = std::size(g_register_infos); + +RegisterContextDarwin_i386::RegisterContextDarwin_i386( + Thread &thread, uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() { + uint32_t i; + for (i = 0; i < kNumErrors; i++) { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextDarwin_i386::~RegisterContextDarwin_i386() = default; + +void RegisterContextDarwin_i386::InvalidateAllRegisters() { + InvalidateAllRegisterStates(); +} + +size_t RegisterContextDarwin_i386::GetRegisterCount() { + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextDarwin_i386::GetRegisterInfoAtIndex(size_t reg) { + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return nullptr; +} + +size_t RegisterContextDarwin_i386::GetRegisterInfosCount() { + return k_num_register_infos; +} + +const RegisterInfo *RegisterContextDarwin_i386::GetRegisterInfos() { + return g_register_infos; +} + +// General purpose registers +static uint32_t g_gpr_regnums[] = { + gpr_eax, gpr_ebx, gpr_ecx, gpr_edx, gpr_edi, gpr_esi, gpr_ebp, gpr_esp, + gpr_ss, gpr_eflags, gpr_eip, gpr_cs, gpr_ds, gpr_es, gpr_fs, gpr_gs}; + +// Floating point registers +static uint32_t g_fpu_regnums[] = { + fpu_fcw, fpu_fsw, fpu_ftw, fpu_fop, fpu_ip, fpu_cs, + fpu_dp, fpu_ds, fpu_mxcsr, fpu_mxcsrmask, fpu_stmm0, fpu_stmm1, + fpu_stmm2, fpu_stmm3, fpu_stmm4, fpu_stmm5, fpu_stmm6, fpu_stmm7, + fpu_xmm0, fpu_xmm1, fpu_xmm2, fpu_xmm3, fpu_xmm4, fpu_xmm5, + fpu_xmm6, fpu_xmm7}; + +// Exception registers + +static uint32_t g_exc_regnums[] = {exc_trapno, exc_err, exc_faultvaddr}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = std::size(g_gpr_regnums); +const size_t k_num_fpu_registers = std::size(g_fpu_regnums); +const size_t k_num_exc_registers = std::size(g_exc_regnums); + +// Register set definitions. The first definitions at register set index of +// zero is for all registers, followed by other registers sets. The register +// information for the all register set need not be filled in. +static const RegisterSet g_reg_sets[] = { + { + "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, + }, + {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums}, + {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}}; + +const size_t k_num_regsets = std::size(g_reg_sets); + +size_t RegisterContextDarwin_i386::GetRegisterSetCount() { + return k_num_regsets; +} + +const RegisterSet *RegisterContextDarwin_i386::GetRegisterSet(size_t reg_set) { + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return nullptr; +} + +// Register information definitions for 32 bit i386. +int RegisterContextDarwin_i386::GetSetForNativeRegNum(int reg_num) { + if (reg_num < fpu_fcw) + return GPRRegSet; + else if (reg_num < exc_trapno) + return FPURegSet; + else if (reg_num < k_num_registers) + return EXCRegSet; + return -1; +} + +void RegisterContextDarwin_i386::LogGPR(Log *log, const char *title) { + if (log) { + if (title) + LLDB_LOGF(log, "%s", title); + for (uint32_t i = 0; i < k_num_gpr_registers; i++) { + uint32_t reg = gpr_eax + i; + LLDB_LOGF(log, "%12s = 0x%8.8x", g_register_infos[reg].name, + (&gpr.eax)[reg]); + } + } +} + +int RegisterContextDarwin_i386::ReadGPR(bool force) { + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr)); + } + return GetError(set, Read); +} + +int RegisterContextDarwin_i386::ReadFPU(bool force) { + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu)); + } + return GetError(set, Read); +} + +int RegisterContextDarwin_i386::ReadEXC(bool force) { + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadEXC(GetThreadID(), set, exc)); + } + return GetError(set, Read); +} + +int RegisterContextDarwin_i386::WriteGPR() { + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return -1; + } + SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr)); + SetError(set, Read, -1); + return GetError(set, Write); +} + +int RegisterContextDarwin_i386::WriteFPU() { + int set = FPURegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return -1; + } + SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu)); + SetError(set, Read, -1); + return GetError(set, Write); +} + +int RegisterContextDarwin_i386::WriteEXC() { + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return -1; + } + SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc)); + SetError(set, Read, -1); + return GetError(set, Write); +} + +int RegisterContextDarwin_i386::ReadRegisterSet(uint32_t set, bool force) { + switch (set) { + case GPRRegSet: + return ReadGPR(force); + case FPURegSet: + return ReadFPU(force); + case EXCRegSet: + return ReadEXC(force); + default: + break; + } + return -1; +} + +int RegisterContextDarwin_i386::WriteRegisterSet(uint32_t set) { + // Make sure we have a valid context to set. + if (RegisterSetIsCached(set)) { + switch (set) { + case GPRRegSet: + return WriteGPR(); + case FPURegSet: + return WriteFPU(); + case EXCRegSet: + return WriteEXC(); + default: + break; + } + } + return -1; +} + +bool RegisterContextDarwin_i386::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = RegisterContextDarwin_i386::GetSetForNativeRegNum(reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != 0) + return false; + + switch (reg) { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + value = (&gpr.eax)[reg - gpr_eax]; + break; + + case fpu_fcw: + value = fpu.fcw; + break; + + case fpu_fsw: + value = fpu.fsw; + break; + + case fpu_ftw: + value = fpu.ftw; + break; + + case fpu_fop: + value = fpu.fop; + break; + + case fpu_ip: + value = fpu.ip; + break; + + case fpu_cs: + value = fpu.cs; + break; + + case fpu_dp: + value = fpu.dp; + break; + + case fpu_ds: + value = fpu.ds; + break; + + case fpu_mxcsr: + value = fpu.mxcsr; + break; + + case fpu_mxcsrmask: + value = fpu.mxcsrmask; + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, + // RegisterContext::ReadRegisterBytes() must be used for these registers + //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes, + //10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, + // RegisterContext::ReadRegisterBytes() must be used for these registers + //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes, + //16); + return false; + + case exc_trapno: + value = exc.trapno; + break; + + case exc_err: + value = exc.err; + break; + + case exc_faultvaddr: + value = exc.faultvaddr; + break; + + default: + return false; + } + return true; +} + +bool RegisterContextDarwin_i386::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = GetSetForNativeRegNum(reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != 0) + return false; + + switch (reg) { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + (&gpr.eax)[reg - gpr_eax] = value.GetAsUInt32(); + break; + + case fpu_fcw: + fpu.fcw = value.GetAsUInt16(); + break; + + case fpu_fsw: + fpu.fsw = value.GetAsUInt16(); + break; + + case fpu_ftw: + fpu.ftw = value.GetAsUInt8(); + break; + + case fpu_fop: + fpu.fop = value.GetAsUInt16(); + break; + + case fpu_ip: + fpu.ip = value.GetAsUInt32(); + break; + + case fpu_cs: + fpu.cs = value.GetAsUInt16(); + break; + + case fpu_dp: + fpu.dp = value.GetAsUInt32(); + break; + + case fpu_ds: + fpu.ds = value.GetAsUInt16(); + break; + + case fpu_mxcsr: + fpu.mxcsr = value.GetAsUInt32(); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = value.GetAsUInt32(); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, + // RegisterContext::ReadRegisterBytes() must be used for these registers + ::memcpy(fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(), + value.GetByteSize()); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, + // RegisterContext::ReadRegisterBytes() must be used for these registers + ::memcpy(fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(), + value.GetByteSize()); + return false; + + case exc_trapno: + exc.trapno = value.GetAsUInt32(); + break; + + case exc_err: + exc.err = value.GetAsUInt32(); + break; + + case exc_faultvaddr: + exc.faultvaddr = value.GetAsUInt32(); + break; + + default: + return false; + } + return WriteRegisterSet(set) == 0; +} + +bool RegisterContextDarwin_i386::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0); + if (ReadGPR(false) == 0 && ReadFPU(false) == 0 && ReadEXC(false) == 0) { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy(dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy(dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool RegisterContextDarwin_i386::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy(&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy(&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy(&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == 0) + ++success_count; + if (WriteFPU() == 0) + ++success_count; + if (WriteEXC() == 0) + ++success_count; + return success_count == 3; + } + return false; +} + +uint32_t RegisterContextDarwin_i386::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t reg) { + if (kind == eRegisterKindGeneric) { + switch (reg) { + case LLDB_REGNUM_GENERIC_PC: + return gpr_eip; + case LLDB_REGNUM_GENERIC_SP: + return gpr_esp; + case LLDB_REGNUM_GENERIC_FP: + return gpr_ebp; + case LLDB_REGNUM_GENERIC_FLAGS: + return gpr_eflags; + case LLDB_REGNUM_GENERIC_RA: + default: + break; + } + } else if (kind == eRegisterKindEHFrame || kind == eRegisterKindDWARF) { + switch (reg) { + case dwarf_eax: + return gpr_eax; + case dwarf_ecx: + return gpr_ecx; + case dwarf_edx: + return gpr_edx; + case dwarf_ebx: + return gpr_ebx; + case dwarf_esp: + return gpr_esp; + case dwarf_ebp: + return gpr_ebp; + case dwarf_esi: + return gpr_esi; + case dwarf_edi: + return gpr_edi; + case dwarf_eip: + return gpr_eip; + case dwarf_eflags: + return gpr_eflags; + case dwarf_stmm0: + return fpu_stmm0; + case dwarf_stmm1: + return fpu_stmm1; + case dwarf_stmm2: + return fpu_stmm2; + case dwarf_stmm3: + return fpu_stmm3; + case dwarf_stmm4: + return fpu_stmm4; + case dwarf_stmm5: + return fpu_stmm5; + case dwarf_stmm6: + return fpu_stmm6; + case dwarf_stmm7: + return fpu_stmm7; + case dwarf_xmm0: + return fpu_xmm0; + case dwarf_xmm1: + return fpu_xmm1; + case dwarf_xmm2: + return fpu_xmm2; + case dwarf_xmm3: + return fpu_xmm3; + case dwarf_xmm4: + return fpu_xmm4; + case dwarf_xmm5: + return fpu_xmm5; + case dwarf_xmm6: + return fpu_xmm6; + case dwarf_xmm7: + return fpu_xmm7; + default: + break; + } + } else if (kind == eRegisterKindLLDB) { + return reg; + } + return LLDB_INVALID_REGNUM; +} + +bool RegisterContextDarwin_i386::HardwareSingleStep(bool enable) { + if (ReadGPR(false) != 0) + return false; + + const uint32_t trace_bit = 0x100u; + if (enable) { + // If the trace bit is already set, there is nothing to do + if (gpr.eflags & trace_bit) + return true; + else + gpr.eflags |= trace_bit; + } else { + // If the trace bit is already cleared, there is nothing to do + if (gpr.eflags & trace_bit) + gpr.eflags &= ~trace_bit; + else + return true; + } + + return WriteGPR() == 0; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h new file mode 100644 index 000000000000..be933f3be266 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h @@ -0,0 +1,208 @@ +//===-- RegisterContextDarwin_i386.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_I386_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_I386_H + +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" + +class RegisterContextDarwin_i386 : public lldb_private::RegisterContext { +public: + RegisterContextDarwin_i386(lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + ~RegisterContextDarwin_i386() override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + bool HardwareSingleStep(bool enable) override; + + struct GPR { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ss; + uint32_t eflags; + uint32_t eip; + uint32_t cs; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + }; + + struct MMSReg { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg { + uint8_t bytes[16]; + }; + + struct FPU { + uint32_t pad[2]; + uint16_t fcw; + uint16_t fsw; + uint8_t ftw; + uint8_t pad1; + uint16_t fop; + uint32_t ip; + uint16_t cs; + uint16_t pad2; + uint32_t dp; + uint16_t ds; + uint16_t pad3; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[8]; + uint8_t pad4[14 * 16]; + int pad5; + }; + + struct EXC { + uint32_t trapno; + uint32_t err; + uint32_t faultvaddr; + }; + +protected: + enum { GPRRegSet = 1, FPURegSet = 2, EXCRegSet = 3 }; + + enum { + GPRWordCount = sizeof(GPR) / sizeof(uint32_t), + FPUWordCount = sizeof(FPU) / sizeof(uint32_t), + EXCWordCount = sizeof(EXC) / sizeof(uint32_t) + }; + + enum { Read = 0, Write = 1, kNumErrors = 2 }; + + GPR gpr; + FPU fpu; + EXC exc; + int gpr_errs[2]; // Read/Write errors + int fpu_errs[2]; // Read/Write errors + int exc_errs[2]; // Read/Write errors + + void InvalidateAllRegisterStates() { + SetError(GPRRegSet, Read, -1); + SetError(FPURegSet, Read, -1); + SetError(EXCRegSet, Read, -1); + } + + int GetError(int flavor, uint32_t err_idx) const { + if (err_idx < kNumErrors) { + switch (flavor) { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: + return gpr_errs[err_idx]; + case FPURegSet: + return fpu_errs[err_idx]; + case EXCRegSet: + return exc_errs[err_idx]; + default: + break; + } + } + return -1; + } + + bool SetError(int flavor, uint32_t err_idx, int err) { + if (err_idx < kNumErrors) { + switch (flavor) { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + default: + break; + } + } + return false; + } + + bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; } + + void LogGPR(lldb_private::Log *log, const char *title); + + int ReadGPR(bool force); + + int ReadFPU(bool force); + + int ReadEXC(bool force); + + int WriteGPR(); + + int WriteFPU(); + + int WriteEXC(); + + // Subclasses override these to do the actual reading. + virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) = 0; + + virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0; + + virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0; + + virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0; + + virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0; + + virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0; + + int ReadRegisterSet(uint32_t set, bool force); + + int WriteRegisterSet(uint32_t set); + + static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num); + + static int GetSetForNativeRegNum(int reg_num); + + static size_t GetRegisterInfosCount(); + + static const lldb_private::RegisterInfo *GetRegisterInfos(); +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_I386_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp new file mode 100644 index 000000000000..08d84e827090 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp @@ -0,0 +1,1056 @@ +//===-- RegisterContextDarwin_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 +// +//===----------------------------------------------------------------------===// + +#include <cinttypes> +#include <cstdarg> +#include <cstddef> + +#include <memory> + +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextDarwin_x86_64.h" + +using namespace lldb; +using namespace lldb_private; + +enum { + gpr_rax = 0, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs, + + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15, + + exc_trapno, + exc_err, + exc_faultvaddr, + + k_num_registers, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp +}; + +enum ehframe_dwarf_regnums { + ehframe_dwarf_gpr_rax = 0, + ehframe_dwarf_gpr_rdx, + ehframe_dwarf_gpr_rcx, + ehframe_dwarf_gpr_rbx, + ehframe_dwarf_gpr_rsi, + ehframe_dwarf_gpr_rdi, + ehframe_dwarf_gpr_rbp, + ehframe_dwarf_gpr_rsp, + ehframe_dwarf_gpr_r8, + ehframe_dwarf_gpr_r9, + ehframe_dwarf_gpr_r10, + ehframe_dwarf_gpr_r11, + ehframe_dwarf_gpr_r12, + ehframe_dwarf_gpr_r13, + ehframe_dwarf_gpr_r14, + ehframe_dwarf_gpr_r15, + ehframe_dwarf_gpr_rip, + ehframe_dwarf_fpu_xmm0, + ehframe_dwarf_fpu_xmm1, + ehframe_dwarf_fpu_xmm2, + ehframe_dwarf_fpu_xmm3, + ehframe_dwarf_fpu_xmm4, + ehframe_dwarf_fpu_xmm5, + ehframe_dwarf_fpu_xmm6, + ehframe_dwarf_fpu_xmm7, + ehframe_dwarf_fpu_xmm8, + ehframe_dwarf_fpu_xmm9, + ehframe_dwarf_fpu_xmm10, + ehframe_dwarf_fpu_xmm11, + ehframe_dwarf_fpu_xmm12, + ehframe_dwarf_fpu_xmm13, + ehframe_dwarf_fpu_xmm14, + ehframe_dwarf_fpu_xmm15, + ehframe_dwarf_fpu_stmm0, + ehframe_dwarf_fpu_stmm1, + ehframe_dwarf_fpu_stmm2, + ehframe_dwarf_fpu_stmm3, + ehframe_dwarf_fpu_stmm4, + ehframe_dwarf_fpu_stmm5, + ehframe_dwarf_fpu_stmm6, + ehframe_dwarf_fpu_stmm7 + +}; + +#define GPR_OFFSET(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_x86_64::GPR, reg)) +#define FPU_OFFSET(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_x86_64::FPU, reg) + \ + sizeof(RegisterContextDarwin_x86_64::GPR)) +#define EXC_OFFSET(reg) \ + (LLVM_EXTENSION offsetof(RegisterContextDarwin_x86_64::EXC, reg) + \ + sizeof(RegisterContextDarwin_x86_64::GPR) + \ + sizeof(RegisterContextDarwin_x86_64::FPU)) + +// These macros will auto define the register name, alt name, register size, +// register offset, encoding, format and native register. This ensures that the +// register state structures are defined correctly and have the correct sizes +// and offsets. +#define DEFINE_GPR(reg, alt) \ + #reg, alt, sizeof(((RegisterContextDarwin_x86_64::GPR *) NULL)->reg), \ + GPR_OFFSET(reg), eEncodingUint, eFormatHex +#define DEFINE_FPU_UINT(reg) \ + #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::FPU *) NULL)->reg), \ + FPU_OFFSET(reg), eEncodingUint, eFormatHex +#define DEFINE_FPU_VECT(reg, i) \ + #reg #i, NULL, \ + sizeof(((RegisterContextDarwin_x86_64::FPU *) NULL)->reg[i].bytes), \ + FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, \ + {ehframe_dwarf_fpu_##reg##i, \ + ehframe_dwarf_fpu_##reg##i, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, fpu_##reg##i }, \ + nullptr, nullptr, nullptr, +#define DEFINE_EXC(reg) \ + #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::EXC *) NULL)->reg), \ + EXC_OFFSET(reg), eEncodingUint, eFormatHex + +#define REG_CONTEXT_SIZE \ + (sizeof(RegisterContextDarwin_x86_64::GPR) + \ + sizeof(RegisterContextDarwin_x86_64::FPU) + \ + sizeof(RegisterContextDarwin_x86_64::EXC)) + +// General purpose registers for 64 bit +static RegisterInfo g_register_infos[] = { + // Macro auto defines most stuff EH_FRAME DWARF + // GENERIC PROCESS PLUGIN LLDB + // =============================== ====================== + // =================== ========================== ==================== + // =================== + {DEFINE_GPR(rax, nullptr), + {ehframe_dwarf_gpr_rax, ehframe_dwarf_gpr_rax, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_rax}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(rbx, nullptr), + {ehframe_dwarf_gpr_rbx, ehframe_dwarf_gpr_rbx, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_rbx}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(rcx, nullptr), + {ehframe_dwarf_gpr_rcx, ehframe_dwarf_gpr_rcx, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_rcx}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(rdx, nullptr), + {ehframe_dwarf_gpr_rdx, ehframe_dwarf_gpr_rdx, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_rdx}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(rdi, nullptr), + {ehframe_dwarf_gpr_rdi, ehframe_dwarf_gpr_rdi, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_rdi}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(rsi, nullptr), + {ehframe_dwarf_gpr_rsi, ehframe_dwarf_gpr_rsi, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_rsi}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(rbp, "fp"), + {ehframe_dwarf_gpr_rbp, ehframe_dwarf_gpr_rbp, LLDB_REGNUM_GENERIC_FP, + LLDB_INVALID_REGNUM, gpr_rbp}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(rsp, "sp"), + {ehframe_dwarf_gpr_rsp, ehframe_dwarf_gpr_rsp, LLDB_REGNUM_GENERIC_SP, + LLDB_INVALID_REGNUM, gpr_rsp}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(r8, nullptr), + {ehframe_dwarf_gpr_r8, ehframe_dwarf_gpr_r8, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_r8}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(r9, nullptr), + {ehframe_dwarf_gpr_r9, ehframe_dwarf_gpr_r9, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_r9}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(r10, nullptr), + {ehframe_dwarf_gpr_r10, ehframe_dwarf_gpr_r10, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_r10}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(r11, nullptr), + {ehframe_dwarf_gpr_r11, ehframe_dwarf_gpr_r11, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_r11}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(r12, nullptr), + {ehframe_dwarf_gpr_r12, ehframe_dwarf_gpr_r12, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_r12}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(r13, nullptr), + {ehframe_dwarf_gpr_r13, ehframe_dwarf_gpr_r13, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_r13}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(r14, nullptr), + {ehframe_dwarf_gpr_r14, ehframe_dwarf_gpr_r14, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_r14}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(r15, nullptr), + {ehframe_dwarf_gpr_r15, ehframe_dwarf_gpr_r15, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_r15}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(rip, "pc"), + {ehframe_dwarf_gpr_rip, ehframe_dwarf_gpr_rip, LLDB_REGNUM_GENERIC_PC, + LLDB_INVALID_REGNUM, gpr_rip}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(rflags, "flags"), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, + LLDB_INVALID_REGNUM, gpr_rflags}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(cs, nullptr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_cs}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(fs, nullptr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_fs}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_GPR(gs, nullptr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, gpr_gs}, + nullptr, + nullptr, + nullptr, + }, + + {DEFINE_FPU_UINT(fcw), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_fcw}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(fsw), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_fsw}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(ftw), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_ftw}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(fop), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_fop}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(ip), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_ip}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(cs), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_cs}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(dp), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_dp}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(ds), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_ds}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(mxcsr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_mxcsr}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_UINT(mxcsrmask), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_mxcsrmask}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_FPU_VECT(stmm, 0)}, + {DEFINE_FPU_VECT(stmm, 1)}, + {DEFINE_FPU_VECT(stmm, 2)}, + {DEFINE_FPU_VECT(stmm, 3)}, + {DEFINE_FPU_VECT(stmm, 4)}, + {DEFINE_FPU_VECT(stmm, 5)}, + {DEFINE_FPU_VECT(stmm, 6)}, + {DEFINE_FPU_VECT(stmm, 7)}, + {DEFINE_FPU_VECT(xmm, 0)}, + {DEFINE_FPU_VECT(xmm, 1)}, + {DEFINE_FPU_VECT(xmm, 2)}, + {DEFINE_FPU_VECT(xmm, 3)}, + {DEFINE_FPU_VECT(xmm, 4)}, + {DEFINE_FPU_VECT(xmm, 5)}, + {DEFINE_FPU_VECT(xmm, 6)}, + {DEFINE_FPU_VECT(xmm, 7)}, + {DEFINE_FPU_VECT(xmm, 8)}, + {DEFINE_FPU_VECT(xmm, 9)}, + {DEFINE_FPU_VECT(xmm, 10)}, + {DEFINE_FPU_VECT(xmm, 11)}, + {DEFINE_FPU_VECT(xmm, 12)}, + {DEFINE_FPU_VECT(xmm, 13)}, + {DEFINE_FPU_VECT(xmm, 14)}, + {DEFINE_FPU_VECT(xmm, 15)}, + + {DEFINE_EXC(trapno), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_trapno}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_EXC(err), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_err}, + nullptr, + nullptr, + nullptr, + }, + {DEFINE_EXC(faultvaddr), + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_faultvaddr}, + nullptr, + nullptr, + nullptr, + }}; + +static size_t k_num_register_infos = std::size(g_register_infos); + +RegisterContextDarwin_x86_64::RegisterContextDarwin_x86_64( + Thread &thread, uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx), gpr(), fpu(), exc() { + uint32_t i; + for (i = 0; i < kNumErrors; i++) { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextDarwin_x86_64::~RegisterContextDarwin_x86_64() = default; + +void RegisterContextDarwin_x86_64::InvalidateAllRegisters() { + InvalidateAllRegisterStates(); +} + +size_t RegisterContextDarwin_x86_64::GetRegisterCount() { + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextDarwin_x86_64::GetRegisterInfoAtIndex(size_t reg) { + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return nullptr; +} + +size_t RegisterContextDarwin_x86_64::GetRegisterInfosCount() { + return k_num_register_infos; +} + +const lldb_private::RegisterInfo * +RegisterContextDarwin_x86_64::GetRegisterInfos() { + return g_register_infos; +} + +static uint32_t g_gpr_regnums[] = { + gpr_rax, gpr_rbx, gpr_rcx, gpr_rdx, gpr_rdi, gpr_rsi, gpr_rbp, + gpr_rsp, gpr_r8, gpr_r9, gpr_r10, gpr_r11, gpr_r12, gpr_r13, + gpr_r14, gpr_r15, gpr_rip, gpr_rflags, gpr_cs, gpr_fs, gpr_gs}; + +static uint32_t g_fpu_regnums[] = { + fpu_fcw, fpu_fsw, fpu_ftw, fpu_fop, fpu_ip, fpu_cs, + fpu_dp, fpu_ds, fpu_mxcsr, fpu_mxcsrmask, fpu_stmm0, fpu_stmm1, + fpu_stmm2, fpu_stmm3, fpu_stmm4, fpu_stmm5, fpu_stmm6, fpu_stmm7, + fpu_xmm0, fpu_xmm1, fpu_xmm2, fpu_xmm3, fpu_xmm4, fpu_xmm5, + fpu_xmm6, fpu_xmm7, fpu_xmm8, fpu_xmm9, fpu_xmm10, fpu_xmm11, + fpu_xmm12, fpu_xmm13, fpu_xmm14, fpu_xmm15}; + +static uint32_t g_exc_regnums[] = {exc_trapno, exc_err, exc_faultvaddr}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = std::size(g_gpr_regnums); +const size_t k_num_fpu_registers = std::size(g_fpu_regnums); +const size_t k_num_exc_registers = std::size(g_exc_regnums); + +// Register set definitions. The first definitions at register set index of +// zero is for all registers, followed by other registers sets. The register +// information for the all register set need not be filled in. +static const RegisterSet g_reg_sets[] = { + { + "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, + }, + {"Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums}, + {"Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums}}; + +const size_t k_num_regsets = std::size(g_reg_sets); + +size_t RegisterContextDarwin_x86_64::GetRegisterSetCount() { + return k_num_regsets; +} + +const RegisterSet * +RegisterContextDarwin_x86_64::GetRegisterSet(size_t reg_set) { + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return nullptr; +} + +int RegisterContextDarwin_x86_64::GetSetForNativeRegNum(int reg_num) { + if (reg_num < fpu_fcw) + return GPRRegSet; + else if (reg_num < exc_trapno) + return FPURegSet; + else if (reg_num < k_num_registers) + return EXCRegSet; + return -1; +} + +int RegisterContextDarwin_x86_64::ReadGPR(bool force) { + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr)); + } + return GetError(GPRRegSet, Read); +} + +int RegisterContextDarwin_x86_64::ReadFPU(bool force) { + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu)); + } + return GetError(FPURegSet, Read); +} + +int RegisterContextDarwin_x86_64::ReadEXC(bool force) { + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) { + SetError(set, Read, DoReadEXC(GetThreadID(), set, exc)); + } + return GetError(EXCRegSet, Read); +} + +int RegisterContextDarwin_x86_64::WriteGPR() { + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return -1; + } + SetError(set, Write, DoWriteGPR(GetThreadID(), set, gpr)); + SetError(set, Read, -1); + return GetError(set, Write); +} + +int RegisterContextDarwin_x86_64::WriteFPU() { + int set = FPURegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return -1; + } + SetError(set, Write, DoWriteFPU(GetThreadID(), set, fpu)); + SetError(set, Read, -1); + return GetError(set, Write); +} + +int RegisterContextDarwin_x86_64::WriteEXC() { + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) { + SetError(set, Write, -1); + return -1; + } + SetError(set, Write, DoWriteEXC(GetThreadID(), set, exc)); + SetError(set, Read, -1); + return GetError(set, Write); +} + +int RegisterContextDarwin_x86_64::ReadRegisterSet(uint32_t set, bool force) { + switch (set) { + case GPRRegSet: + return ReadGPR(force); + case FPURegSet: + return ReadFPU(force); + case EXCRegSet: + return ReadEXC(force); + default: + break; + } + return -1; +} + +int RegisterContextDarwin_x86_64::WriteRegisterSet(uint32_t set) { + // Make sure we have a valid context to set. + switch (set) { + case GPRRegSet: + return WriteGPR(); + case FPURegSet: + return WriteFPU(); + case EXCRegSet: + return WriteEXC(); + default: + break; + } + return -1; +} + +bool RegisterContextDarwin_x86_64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum(reg); + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != 0) + return false; + + switch (reg) { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + value = (&gpr.rax)[reg - gpr_rax]; + break; + + case fpu_fcw: + value = fpu.fcw; + break; + + case fpu_fsw: + value = fpu.fsw; + break; + + case fpu_ftw: + value = fpu.ftw; + break; + + case fpu_fop: + value = fpu.fop; + break; + + case fpu_ip: + value = fpu.ip; + break; + + case fpu_cs: + value = fpu.cs; + break; + + case fpu_dp: + value = fpu.dp; + break; + + case fpu_ds: + value = fpu.ds; + break; + + case fpu_mxcsr: + value = fpu.mxcsr; + break; + + case fpu_mxcsrmask: + value = fpu.mxcsrmask; + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + value.SetBytes(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, + endian::InlHostByteOrder()); + break; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + value.SetBytes(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, + endian::InlHostByteOrder()); + break; + + case exc_trapno: + value = exc.trapno; + break; + + case exc_err: + value = exc.err; + break; + + case exc_faultvaddr: + value = exc.faultvaddr; + break; + + default: + return false; + } + return true; +} + +bool RegisterContextDarwin_x86_64::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum(reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != 0) + return false; + + switch (reg) { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + (&gpr.rax)[reg - gpr_rax] = value.GetAsUInt64(); + break; + + case fpu_fcw: + fpu.fcw = value.GetAsUInt16(); + break; + + case fpu_fsw: + fpu.fsw = value.GetAsUInt16(); + break; + + case fpu_ftw: + fpu.ftw = value.GetAsUInt8(); + break; + + case fpu_fop: + fpu.fop = value.GetAsUInt16(); + break; + + case fpu_ip: + fpu.ip = value.GetAsUInt32(); + break; + + case fpu_cs: + fpu.cs = value.GetAsUInt16(); + break; + + case fpu_dp: + fpu.dp = value.GetAsUInt32(); + break; + + case fpu_ds: + fpu.ds = value.GetAsUInt16(); + break; + + case fpu_mxcsr: + fpu.mxcsr = value.GetAsUInt32(); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = value.GetAsUInt32(); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + ::memcpy(fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(), + value.GetByteSize()); + break; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + ::memcpy(fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(), + value.GetByteSize()); + return false; + + case exc_trapno: + exc.trapno = value.GetAsUInt32(); + break; + + case exc_err: + exc.err = value.GetAsUInt32(); + break; + + case exc_faultvaddr: + exc.faultvaddr = value.GetAsUInt64(); + break; + + default: + return false; + } + return WriteRegisterSet(set) == 0; +} + +bool RegisterContextDarwin_x86_64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + data_sp = std::make_shared<DataBufferHeap>(REG_CONTEXT_SIZE, 0); + if (ReadGPR(false) == 0 && ReadFPU(false) == 0 && ReadEXC(false) == 0) { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy(dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy(dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool RegisterContextDarwin_x86_64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy(&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy(&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy(&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == 0) + ++success_count; + if (WriteFPU() == 0) + ++success_count; + if (WriteEXC() == 0) + ++success_count; + return success_count == 3; + } + return false; +} + +uint32_t RegisterContextDarwin_x86_64::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t reg) { + if (kind == eRegisterKindGeneric) { + switch (reg) { + case LLDB_REGNUM_GENERIC_PC: + return gpr_rip; + case LLDB_REGNUM_GENERIC_SP: + return gpr_rsp; + case LLDB_REGNUM_GENERIC_FP: + return gpr_rbp; + case LLDB_REGNUM_GENERIC_FLAGS: + return gpr_rflags; + case LLDB_REGNUM_GENERIC_RA: + default: + break; + } + } else if (kind == eRegisterKindEHFrame || kind == eRegisterKindDWARF) { + switch (reg) { + case ehframe_dwarf_gpr_rax: + return gpr_rax; + case ehframe_dwarf_gpr_rdx: + return gpr_rdx; + case ehframe_dwarf_gpr_rcx: + return gpr_rcx; + case ehframe_dwarf_gpr_rbx: + return gpr_rbx; + case ehframe_dwarf_gpr_rsi: + return gpr_rsi; + case ehframe_dwarf_gpr_rdi: + return gpr_rdi; + case ehframe_dwarf_gpr_rbp: + return gpr_rbp; + case ehframe_dwarf_gpr_rsp: + return gpr_rsp; + case ehframe_dwarf_gpr_r8: + return gpr_r8; + case ehframe_dwarf_gpr_r9: + return gpr_r9; + case ehframe_dwarf_gpr_r10: + return gpr_r10; + case ehframe_dwarf_gpr_r11: + return gpr_r11; + case ehframe_dwarf_gpr_r12: + return gpr_r12; + case ehframe_dwarf_gpr_r13: + return gpr_r13; + case ehframe_dwarf_gpr_r14: + return gpr_r14; + case ehframe_dwarf_gpr_r15: + return gpr_r15; + case ehframe_dwarf_gpr_rip: + return gpr_rip; + case ehframe_dwarf_fpu_xmm0: + return fpu_xmm0; + case ehframe_dwarf_fpu_xmm1: + return fpu_xmm1; + case ehframe_dwarf_fpu_xmm2: + return fpu_xmm2; + case ehframe_dwarf_fpu_xmm3: + return fpu_xmm3; + case ehframe_dwarf_fpu_xmm4: + return fpu_xmm4; + case ehframe_dwarf_fpu_xmm5: + return fpu_xmm5; + case ehframe_dwarf_fpu_xmm6: + return fpu_xmm6; + case ehframe_dwarf_fpu_xmm7: + return fpu_xmm7; + case ehframe_dwarf_fpu_xmm8: + return fpu_xmm8; + case ehframe_dwarf_fpu_xmm9: + return fpu_xmm9; + case ehframe_dwarf_fpu_xmm10: + return fpu_xmm10; + case ehframe_dwarf_fpu_xmm11: + return fpu_xmm11; + case ehframe_dwarf_fpu_xmm12: + return fpu_xmm12; + case ehframe_dwarf_fpu_xmm13: + return fpu_xmm13; + case ehframe_dwarf_fpu_xmm14: + return fpu_xmm14; + case ehframe_dwarf_fpu_xmm15: + return fpu_xmm15; + case ehframe_dwarf_fpu_stmm0: + return fpu_stmm0; + case ehframe_dwarf_fpu_stmm1: + return fpu_stmm1; + case ehframe_dwarf_fpu_stmm2: + return fpu_stmm2; + case ehframe_dwarf_fpu_stmm3: + return fpu_stmm3; + case ehframe_dwarf_fpu_stmm4: + return fpu_stmm4; + case ehframe_dwarf_fpu_stmm5: + return fpu_stmm5; + case ehframe_dwarf_fpu_stmm6: + return fpu_stmm6; + case ehframe_dwarf_fpu_stmm7: + return fpu_stmm7; + default: + break; + } + } else if (kind == eRegisterKindLLDB) { + return reg; + } + return LLDB_INVALID_REGNUM; +} + +bool RegisterContextDarwin_x86_64::HardwareSingleStep(bool enable) { + if (ReadGPR(true) != 0) + return false; + + const uint64_t trace_bit = 0x100ull; + if (enable) { + + if (gpr.rflags & trace_bit) + return true; // trace bit is already set, there is nothing to do + else + gpr.rflags |= trace_bit; + } else { + if (gpr.rflags & trace_bit) + gpr.rflags &= ~trace_bit; + else + return true; // trace bit is clear, there is nothing to do + } + + return WriteGPR() == 0; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h new file mode 100644 index 000000000000..a132f92d4d49 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h @@ -0,0 +1,213 @@ +//===-- RegisterContextDarwin_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_X86_64_H + +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" + +class RegisterContextDarwin_x86_64 : public lldb_private::RegisterContext { +public: + RegisterContextDarwin_x86_64(lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + ~RegisterContextDarwin_x86_64() override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + bool HardwareSingleStep(bool enable) override; + + struct GPR { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; + }; + + struct MMSReg { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg { + uint8_t bytes[16]; + }; + + struct FPU { + uint32_t pad[2]; + uint16_t fcw; // "fctrl" + uint16_t fsw; // "fstat" + uint8_t ftw; // "ftag" + uint8_t pad1; + uint16_t fop; // "fop" + uint32_t ip; // "fioff" + uint16_t cs; // "fiseg" + uint16_t pad2; + uint32_t dp; // "fooff" + uint16_t ds; // "foseg" + uint16_t pad3; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[16]; + uint8_t pad4[6 * 16]; + int pad5; + }; + + struct EXC { + uint32_t trapno; + uint32_t err; + uint64_t faultvaddr; + }; + +protected: + enum { GPRRegSet = 4, FPURegSet = 5, EXCRegSet = 6 }; + + enum { + GPRWordCount = sizeof(GPR) / sizeof(uint32_t), + FPUWordCount = sizeof(FPU) / sizeof(uint32_t), + EXCWordCount = sizeof(EXC) / sizeof(uint32_t) + }; + + enum { Read = 0, Write = 1, kNumErrors = 2 }; + + GPR gpr; + FPU fpu; + EXC exc; + int gpr_errs[2]; // Read/Write errors + int fpu_errs[2]; // Read/Write errors + int exc_errs[2]; // Read/Write errors + + void InvalidateAllRegisterStates() { + SetError(GPRRegSet, Read, -1); + SetError(FPURegSet, Read, -1); + SetError(EXCRegSet, Read, -1); + } + + int GetError(int flavor, uint32_t err_idx) const { + if (err_idx < kNumErrors) { + switch (flavor) { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: + return gpr_errs[err_idx]; + case FPURegSet: + return fpu_errs[err_idx]; + case EXCRegSet: + return exc_errs[err_idx]; + default: + break; + } + } + return -1; + } + + bool SetError(int flavor, uint32_t err_idx, int err) { + if (err_idx < kNumErrors) { + switch (flavor) { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + default: + break; + } + } + return false; + } + + bool RegisterSetIsCached(int set) const { return GetError(set, Read) == 0; } + + void LogGPR(lldb_private::Log *log, const char *format, ...); + + int ReadGPR(bool force); + + int ReadFPU(bool force); + + int ReadEXC(bool force); + + int WriteGPR(); + + int WriteFPU(); + + int WriteEXC(); + + // Subclasses override these to do the actual reading. + virtual int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) = 0; + + virtual int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) = 0; + + virtual int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) = 0; + + virtual int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) = 0; + + virtual int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) = 0; + + virtual int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) = 0; + + int ReadRegisterSet(uint32_t set, bool force); + + int WriteRegisterSet(uint32_t set); + + static uint32_t GetRegisterNumber(uint32_t reg_kind, uint32_t reg_num); + + static int GetSetForNativeRegNum(int reg_num); + + static size_t GetRegisterInfosCount(); + + static const lldb_private::RegisterInfo *GetRegisterInfos(); +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDARWIN_X86_64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp new file mode 100644 index 000000000000..f41aa2ca599e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.cpp @@ -0,0 +1,120 @@ +//===-- RegisterContextDummy.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/lldb-private.h" + +#include "RegisterContextDummy.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextDummy::RegisterContextDummy(Thread &thread, + uint32_t concrete_frame_idx, + uint32_t address_byte_size) + : RegisterContext(thread, concrete_frame_idx) { + m_reg_set0.name = "General Purpose Registers"; + m_reg_set0.short_name = "GPR"; + m_reg_set0.num_registers = 1; + m_reg_set0.registers = new uint32_t(0); + + m_pc_reg_info.name = "pc"; + m_pc_reg_info.alt_name = "pc"; + m_pc_reg_info.byte_offset = 0; + m_pc_reg_info.byte_size = address_byte_size; + m_pc_reg_info.encoding = eEncodingUint; + m_pc_reg_info.format = eFormatPointer; + m_pc_reg_info.invalidate_regs = nullptr; + m_pc_reg_info.value_regs = nullptr; + m_pc_reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM; + m_pc_reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM; + m_pc_reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + m_pc_reg_info.kinds[eRegisterKindProcessPlugin] = LLDB_INVALID_REGNUM; + m_pc_reg_info.kinds[eRegisterKindLLDB] = LLDB_INVALID_REGNUM; +} + +RegisterContextDummy::~RegisterContextDummy() { + delete m_reg_set0.registers; + delete m_pc_reg_info.invalidate_regs; + delete m_pc_reg_info.value_regs; +} + +void RegisterContextDummy::InvalidateAllRegisters() {} + +size_t RegisterContextDummy::GetRegisterCount() { return 1; } + +const lldb_private::RegisterInfo * +RegisterContextDummy::GetRegisterInfoAtIndex(size_t reg) { + if (reg) + return nullptr; + return &m_pc_reg_info; +} + +size_t RegisterContextDummy::GetRegisterSetCount() { return 1; } + +const lldb_private::RegisterSet * +RegisterContextDummy::GetRegisterSet(size_t reg_set) { + if (reg_set) + return nullptr; + return &m_reg_set0; +} + +bool RegisterContextDummy::ReadRegister( + const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) { + if (!reg_info) + return false; + uint32_t reg_number = reg_info->kinds[eRegisterKindGeneric]; + if (reg_number == LLDB_REGNUM_GENERIC_PC) { + value.SetUInt(LLDB_INVALID_ADDRESS, reg_info->byte_size); + return true; + } + return false; +} + +bool RegisterContextDummy::WriteRegister( + const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) { + return false; +} + +bool RegisterContextDummy::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + return false; +} + +bool RegisterContextDummy::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return false; +} + +uint32_t RegisterContextDummy::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + if (kind == eRegisterKindGeneric && num == LLDB_REGNUM_GENERIC_PC) + return 0; + return LLDB_INVALID_REGNUM; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h new file mode 100644 index 000000000000..631ad30b16a8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextDummy.h @@ -0,0 +1,65 @@ +//===-- RegisterContextDummy.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDUMMY_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDUMMY_H + +#include <vector> + +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class RegisterContextDummy : public lldb_private::RegisterContext { +public: + typedef std::shared_ptr<RegisterContextDummy> SharedPtr; + + RegisterContextDummy(Thread &thread, uint32_t concrete_frame_idx, + uint32_t address_byte_size); + + ~RegisterContextDummy() override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + +private: + // For RegisterContextLLDB only + + lldb_private::RegisterSet m_reg_set0; // register set 0 (PC only) + lldb_private::RegisterInfo m_pc_reg_info; + + RegisterContextDummy(const RegisterContextDummy &) = delete; + const RegisterContextDummy &operator=(const RegisterContextDummy &) = delete; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTDUMMY_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp new file mode 100644 index 000000000000..df6a82c11255 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.cpp @@ -0,0 +1,83 @@ +//===-- RegisterContextFreeBSD_i386.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 "RegisterContextFreeBSD_i386.h" +#include "RegisterContextPOSIX_x86.h" + +using namespace lldb_private; +using namespace lldb; + +// http://svnweb.freebsd.org/base/head/sys/x86/include/reg.h +struct GPR { + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t isp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t trapno; + uint32_t err; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; + uint32_t gs; +}; + +struct DBG { + uint32_t dr[8]; /* debug registers */ + /* Index 0-3: debug address registers */ + /* Index 4-5: reserved */ + /* Index 6: debug status */ + /* Index 7: debug control */ +}; + +using FPR_i386 = FXSAVE; + +struct UserArea { + GPR gpr; + FPR_i386 i387; + DBG dbg; +}; + +#define DR_SIZE sizeof(uint32_t) +#define DR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, dbg) + \ + LLVM_EXTENSION offsetof(DBG, dr[reg_index])) + +// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure. +#define DECLARE_REGISTER_INFOS_I386_STRUCT +#include "RegisterInfos_i386.h" +#undef DECLARE_REGISTER_INFOS_I386_STRUCT + +RegisterContextFreeBSD_i386::RegisterContextFreeBSD_i386( + const ArchSpec &target_arch) + : RegisterInfoInterface(target_arch) {} + +size_t RegisterContextFreeBSD_i386::GetGPRSize() const { return sizeof(GPR); } + +const RegisterInfo *RegisterContextFreeBSD_i386::GetRegisterInfo() const { + switch (GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return g_register_infos_i386; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +uint32_t RegisterContextFreeBSD_i386::GetRegisterCount() const { + return static_cast<uint32_t>(sizeof(g_register_infos_i386) / + sizeof(g_register_infos_i386[0])); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h new file mode 100644 index 000000000000..5a3e5b0551d6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_i386.h @@ -0,0 +1,25 @@ +//===-- RegisterContextFreeBSD_i386.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTFREEBSD_I386_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTFREEBSD_I386_H + +#include "RegisterInfoInterface.h" + +class RegisterContextFreeBSD_i386 : public lldb_private::RegisterInfoInterface { +public: + RegisterContextFreeBSD_i386(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp new file mode 100644 index 000000000000..1f52c09df12e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp @@ -0,0 +1,179 @@ +//===-- RegisterContextFreeBSD_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 +// +//===---------------------------------------------------------------------===// + +#include "RegisterContextFreeBSD_mips64.h" +#include "RegisterContextPOSIX_mips64.h" +#include "lldb-mips-freebsd-register-enums.h" +#include <vector> + +using namespace lldb_private; +using namespace lldb; + +static const uint32_t g_gp_regnums_mips64[] = { + gpr_zero_mips64, gpr_r1_mips64, gpr_r2_mips64, gpr_r3_mips64, + gpr_r4_mips64, gpr_r5_mips64, gpr_r6_mips64, gpr_r7_mips64, + gpr_r8_mips64, gpr_r9_mips64, gpr_r10_mips64, gpr_r11_mips64, + gpr_r12_mips64, gpr_r13_mips64, gpr_r14_mips64, gpr_r15_mips64, + gpr_r16_mips64, gpr_r17_mips64, gpr_r18_mips64, gpr_r19_mips64, + gpr_r20_mips64, gpr_r21_mips64, gpr_r22_mips64, gpr_r23_mips64, + gpr_r24_mips64, gpr_r25_mips64, gpr_r26_mips64, gpr_r27_mips64, + gpr_gp_mips64, gpr_sp_mips64, gpr_r30_mips64, gpr_ra_mips64, + gpr_sr_mips64, gpr_mullo_mips64, gpr_mulhi_mips64, gpr_badvaddr_mips64, + gpr_cause_mips64, gpr_pc_mips64, gpr_ic_mips64, gpr_dummy_mips64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static_assert((sizeof(g_gp_regnums_mips64) / sizeof(g_gp_regnums_mips64[0])) - + 1 == + k_num_gpr_registers_mips64, + "g_gp_regnums_mips64 has wrong number of register infos"); + +const uint32_t g_fp_regnums_mips64[] = { + fpr_f0_mips64, fpr_f1_mips64, fpr_f2_mips64, fpr_f3_mips64, + fpr_f4_mips64, fpr_f5_mips64, fpr_f6_mips64, fpr_f7_mips64, + fpr_f8_mips64, fpr_f9_mips64, fpr_f10_mips64, fpr_f11_mips64, + fpr_f12_mips64, fpr_f13_mips64, fpr_f14_mips64, fpr_f15_mips64, + fpr_f16_mips64, fpr_f17_mips64, fpr_f18_mips64, fpr_f19_mips64, + fpr_f20_mips64, fpr_f21_mips64, fpr_f22_mips64, fpr_f23_mips64, + fpr_f24_mips64, fpr_f25_mips64, fpr_f26_mips64, fpr_f27_mips64, + fpr_f28_mips64, fpr_f29_mips64, fpr_f30_mips64, fpr_f31_mips64, + fpr_fcsr_mips64, fpr_fir_mips64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +static_assert((sizeof(g_fp_regnums_mips64) / sizeof(g_fp_regnums_mips64[0])) - + 1 == + k_num_fpr_registers_mips64, + "g_fp_regnums_mips64 has wrong number of register infos"); + +// Number of register sets provided by this context. +constexpr size_t k_num_register_sets = 2; + +static const RegisterSet g_reg_sets_mips64[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_mips64, + g_gp_regnums_mips64}, + {"Floating Point Registers", "fpu", k_num_fpr_registers_mips64, + g_fp_regnums_mips64}, +}; + +// http://svnweb.freebsd.org/base/head/sys/mips/include/regnum.h +typedef struct _GPR { + uint64_t zero; + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t gp; + uint64_t sp; + uint64_t r30; + uint64_t ra; + uint64_t sr; + uint64_t mullo; + uint64_t mulhi; + uint64_t badvaddr; + uint64_t cause; + uint64_t pc; + uint64_t ic; + uint64_t dummy; +} GPR_freebsd_mips; + +typedef struct _FPR { + uint64_t f0; + uint64_t f1; + uint64_t f2; + uint64_t f3; + uint64_t f4; + uint64_t f5; + uint64_t f6; + uint64_t f7; + uint64_t f8; + uint64_t f9; + uint64_t f10; + uint64_t f11; + uint64_t f12; + uint64_t f13; + uint64_t f14; + uint64_t f15; + uint64_t f16; + uint64_t f17; + uint64_t f18; + uint64_t f19; + uint64_t f20; + uint64_t f21; + uint64_t f22; + uint64_t f23; + uint64_t f24; + uint64_t f25; + uint64_t f26; + uint64_t f27; + uint64_t f28; + uint64_t f29; + uint64_t f30; + uint64_t f31; + uint64_t fcsr; + uint64_t fir; +} FPR_freebsd_mips; + +// Include RegisterInfos_mips64 to declare our g_register_infos_mips64 +// structure. +#define DECLARE_REGISTER_INFOS_MIPS64_STRUCT +#include "RegisterInfos_mips64.h" +#undef DECLARE_REGISTER_INFOS_MIPS64_STRUCT + +RegisterContextFreeBSD_mips64::RegisterContextFreeBSD_mips64( + const ArchSpec &target_arch) + : RegisterInfoInterface(target_arch) {} + +size_t RegisterContextFreeBSD_mips64::GetGPRSize() const { + return sizeof(GPR_freebsd_mips); +} + +const RegisterSet * +RegisterContextFreeBSD_mips64::GetRegisterSet(size_t set) const { + // Check if RegisterSet is available + if (set < k_num_register_sets) + return &g_reg_sets_mips64[set]; + return nullptr; +} + +size_t RegisterContextFreeBSD_mips64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterInfo *RegisterContextFreeBSD_mips64::GetRegisterInfo() const { + assert(GetTargetArchitecture().GetCore() == ArchSpec::eCore_mips64); + return g_register_infos_mips64; +} + +uint32_t RegisterContextFreeBSD_mips64::GetRegisterCount() const { + return static_cast<uint32_t>(sizeof(g_register_infos_mips64) / + sizeof(g_register_infos_mips64[0])); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h new file mode 100644 index 000000000000..39968eacf475 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h @@ -0,0 +1,30 @@ +//===-- RegisterContextFreeBSD_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTFREEBSD_MIPS64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTFREEBSD_MIPS64_H + +#include "RegisterInfoInterface.h" + +class RegisterContextFreeBSD_mips64 + : public lldb_private::RegisterInfoInterface { +public: + RegisterContextFreeBSD_mips64(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) const; + + size_t GetRegisterSetCount() const; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp new file mode 100644 index 000000000000..d8dfa434335b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp @@ -0,0 +1,233 @@ +//===-- RegisterContextFreeBSD_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 +// +//===---------------------------------------------------------------------===// + +#include "RegisterContextFreeBSD_powerpc.h" +#include "RegisterContextPOSIX_powerpc.h" +#include <vector> + +using namespace lldb_private; +using namespace lldb; + +// http://svnweb.freebsd.org/base/head/sys/powerpc/include/reg.h +typedef struct _GPR64 { + uint64_t r0; + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t r28; + uint64_t r29; + uint64_t r30; + uint64_t r31; + uint64_t lr; + uint64_t cr; + uint64_t xer; + uint64_t ctr; + uint64_t pc; +} GPR64; + +typedef struct _GPR32 { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r11; + uint32_t r12; + uint32_t r13; + uint32_t r14; + uint32_t r15; + uint32_t r16; + uint32_t r17; + uint32_t r18; + uint32_t r19; + uint32_t r20; + uint32_t r21; + uint32_t r22; + uint32_t r23; + uint32_t r24; + uint32_t r25; + uint32_t r26; + uint32_t r27; + uint32_t r28; + uint32_t r29; + uint32_t r30; + uint32_t r31; + uint32_t lr; + uint32_t cr; + uint32_t xer; + uint32_t ctr; + uint32_t pc; +} GPR32; + +typedef struct _FPR { + uint64_t f0; + uint64_t f1; + uint64_t f2; + uint64_t f3; + uint64_t f4; + uint64_t f5; + uint64_t f6; + uint64_t f7; + uint64_t f8; + uint64_t f9; + uint64_t f10; + uint64_t f11; + uint64_t f12; + uint64_t f13; + uint64_t f14; + uint64_t f15; + uint64_t f16; + uint64_t f17; + uint64_t f18; + uint64_t f19; + uint64_t f20; + uint64_t f21; + uint64_t f22; + uint64_t f23; + uint64_t f24; + uint64_t f25; + uint64_t f26; + uint64_t f27; + uint64_t f28; + uint64_t f29; + uint64_t f30; + uint64_t f31; + uint64_t fpscr; +} FPR; + +typedef struct _VMX { + uint32_t v0[4]; + uint32_t v1[4]; + uint32_t v2[4]; + uint32_t v3[4]; + uint32_t v4[4]; + uint32_t v5[4]; + uint32_t v6[4]; + uint32_t v7[4]; + uint32_t v8[4]; + uint32_t v9[4]; + uint32_t v10[4]; + uint32_t v11[4]; + uint32_t v12[4]; + uint32_t v13[4]; + uint32_t v14[4]; + uint32_t v15[4]; + uint32_t v16[4]; + uint32_t v17[4]; + uint32_t v18[4]; + uint32_t v19[4]; + uint32_t v20[4]; + uint32_t v21[4]; + uint32_t v22[4]; + uint32_t v23[4]; + uint32_t v24[4]; + uint32_t v25[4]; + uint32_t v26[4]; + uint32_t v27[4]; + uint32_t v28[4]; + uint32_t v29[4]; + uint32_t v30[4]; + uint32_t v31[4]; + uint32_t pad[2]; + uint32_t vrsave; + uint32_t vscr; +} VMX; + +// Include RegisterInfos_powerpc to declare our g_register_infos_powerpc +// structure. +#define DECLARE_REGISTER_INFOS_POWERPC_STRUCT +#include "RegisterInfos_powerpc.h" +#undef DECLARE_REGISTER_INFOS_POWERPC_STRUCT + +RegisterContextFreeBSD_powerpc::RegisterContextFreeBSD_powerpc( + const ArchSpec &target_arch) + : RegisterInfoInterface(target_arch) {} + +RegisterContextFreeBSD_powerpc::~RegisterContextFreeBSD_powerpc() = default; + +size_t RegisterContextFreeBSD_powerpc::GetGPRSize() const { + // This is an 'abstract' base, so no GPR struct. + return 0; +} + +const RegisterInfo *RegisterContextFreeBSD_powerpc::GetRegisterInfo() const { + llvm_unreachable("Abstract class!"); + return nullptr; +} + +uint32_t RegisterContextFreeBSD_powerpc::GetRegisterCount() const { return 0; } + +RegisterContextFreeBSD_powerpc32::RegisterContextFreeBSD_powerpc32( + const ArchSpec &target_arch) + : RegisterContextFreeBSD_powerpc(target_arch) {} + +RegisterContextFreeBSD_powerpc32::~RegisterContextFreeBSD_powerpc32() = default; + +size_t RegisterContextFreeBSD_powerpc32::GetGPRSize() const { + return sizeof(GPR32); +} + +const RegisterInfo *RegisterContextFreeBSD_powerpc32::GetRegisterInfo() const { + return g_register_infos_powerpc32; +} + +uint32_t RegisterContextFreeBSD_powerpc32::GetRegisterCount() const { + return static_cast<uint32_t>(sizeof(g_register_infos_powerpc32) / + sizeof(g_register_infos_powerpc32[0])); +} + +RegisterContextFreeBSD_powerpc64::RegisterContextFreeBSD_powerpc64( + const ArchSpec &target_arch) + : RegisterContextFreeBSD_powerpc(target_arch) {} + +RegisterContextFreeBSD_powerpc64::~RegisterContextFreeBSD_powerpc64() = default; + +size_t RegisterContextFreeBSD_powerpc64::GetGPRSize() const { + return sizeof(GPR64); +} + +const RegisterInfo *RegisterContextFreeBSD_powerpc64::GetRegisterInfo() const { + if (GetTargetArchitecture().GetMachine() == llvm::Triple::ppc) + return g_register_infos_powerpc64_32; + return g_register_infos_powerpc64; +} + +uint32_t RegisterContextFreeBSD_powerpc64::GetRegisterCount() const { + return static_cast<uint32_t>(sizeof(g_register_infos_powerpc64) / + sizeof(g_register_infos_powerpc64[0])); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h new file mode 100644 index 000000000000..7e4c43ba908a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h @@ -0,0 +1,52 @@ +//===-- RegisterContextFreeBSD_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTFREEBSD_POWERPC_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTFREEBSD_POWERPC_H + +#include "RegisterInfoInterface.h" + +class RegisterContextFreeBSD_powerpc + : public lldb_private::RegisterInfoInterface { +public: + RegisterContextFreeBSD_powerpc(const lldb_private::ArchSpec &target_arch); + ~RegisterContextFreeBSD_powerpc() override; + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; +}; + +class RegisterContextFreeBSD_powerpc32 : public RegisterContextFreeBSD_powerpc { +public: + RegisterContextFreeBSD_powerpc32(const lldb_private::ArchSpec &target_arch); + ~RegisterContextFreeBSD_powerpc32() override; + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; +}; + +class RegisterContextFreeBSD_powerpc64 : public RegisterContextFreeBSD_powerpc { +public: + RegisterContextFreeBSD_powerpc64(const lldb_private::ArchSpec &target_arch); + ~RegisterContextFreeBSD_powerpc64() override; + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTFREEBSD_POWERPC_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp new file mode 100644 index 000000000000..e0f3971c6e27 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp @@ -0,0 +1,145 @@ +//===-- RegisterContextFreeBSD_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 +// +//===---------------------------------------------------------------------===// + +#include "RegisterContextFreeBSD_x86_64.h" +#include "RegisterContextFreeBSD_i386.h" +#include "RegisterContextPOSIX_x86.h" +#include <vector> + +using namespace lldb_private; +using namespace lldb; + +// http://svnweb.freebsd.org/base/head/sys/x86/include/reg.h +typedef struct _GPR { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rcx; + uint64_t rax; + uint32_t trapno; + uint16_t fs; + uint16_t gs; + uint32_t err; + uint16_t es; + uint16_t ds; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; +} GPR; + +struct DBG { + uint64_t dr[16]; /* debug registers */ + /* Index 0-3: debug address registers */ + /* Index 4-5: reserved */ + /* Index 6: debug status */ + /* Index 7: debug control */ + /* Index 8-15: reserved */ +}; + +struct UserArea { + GPR gpr; + FPR fpr; + DBG dbg; +}; + +#define DR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, dbg) + \ + LLVM_EXTENSION offsetof(DBG, dr[reg_index])) + +// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64 +// structure. +#define DECLARE_REGISTER_INFOS_X86_64_STRUCT +#include "RegisterInfos_x86_64.h" +#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT + +static std::vector<lldb_private::RegisterInfo> &GetSharedRegisterInfoVector() { + static std::vector<lldb_private::RegisterInfo> register_infos; + return register_infos; +} + +static const RegisterInfo * +GetRegisterInfo_i386(const lldb_private::ArchSpec &arch) { + static std::vector<lldb_private::RegisterInfo> g_register_infos( + GetSharedRegisterInfoVector()); + + // Allocate RegisterInfo only once + if (g_register_infos.empty()) { + // Copy the register information from base class + std::unique_ptr<RegisterContextFreeBSD_i386> reg_interface( + new RegisterContextFreeBSD_i386(arch)); + const RegisterInfo *base_info = reg_interface->GetRegisterInfo(); + g_register_infos.insert(g_register_infos.end(), &base_info[0], + &base_info[k_num_registers_i386]); + +// Include RegisterInfos_x86_64 to update the g_register_infos structure +// with x86_64 offsets. +#define UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS +#include "RegisterInfos_x86_64.h" +#undef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS + } + + return &g_register_infos[0]; +} + +static const RegisterInfo * +PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return GetRegisterInfo_i386(target_arch); + case llvm::Triple::x86_64: + return g_register_infos_x86_64; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t +PrivateGetRegisterCount(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + // This vector should have already been filled. + assert(!GetSharedRegisterInfoVector().empty() && + "i386 register info vector not filled."); + return static_cast<uint32_t>(GetSharedRegisterInfoVector().size()); + case llvm::Triple::x86_64: + return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) / + sizeof(g_register_infos_x86_64[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextFreeBSD_x86_64::RegisterContextFreeBSD_x86_64( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(PrivateGetRegisterInfoPtr(target_arch)), + m_register_count(PrivateGetRegisterCount(target_arch)) {} + +size_t RegisterContextFreeBSD_x86_64::GetGPRSize() const { return sizeof(GPR); } + +const RegisterInfo *RegisterContextFreeBSD_x86_64::GetRegisterInfo() const { + return m_register_info_p; +} + +uint32_t RegisterContextFreeBSD_x86_64::GetRegisterCount() const { + return m_register_count; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h new file mode 100644 index 000000000000..d0f69fde1817 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h @@ -0,0 +1,30 @@ +//===-- RegisterContextFreeBSD_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTFREEBSD_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTFREEBSD_X86_64_H + +#include "RegisterInfoInterface.h" + +class RegisterContextFreeBSD_x86_64 + : public lldb_private::RegisterInfoInterface { +public: + RegisterContextFreeBSD_x86_64(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + const uint32_t m_register_count; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp new file mode 100644 index 000000000000..f06af93cfa83 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.cpp @@ -0,0 +1,121 @@ +//===-- RegisterContextHistory.cpp ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/lldb-private.h" + +#include "RegisterContextHistory.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextHistory::RegisterContextHistory(Thread &thread, + uint32_t concrete_frame_idx, + uint32_t address_byte_size, + addr_t pc_value) + : RegisterContext(thread, concrete_frame_idx), m_pc_value(pc_value) { + m_reg_set0.name = "General Purpose Registers"; + m_reg_set0.short_name = "GPR"; + m_reg_set0.num_registers = 1; + m_reg_set0.registers = new uint32_t(0); + + m_pc_reg_info.name = "pc"; + m_pc_reg_info.alt_name = "pc"; + m_pc_reg_info.byte_offset = 0; + m_pc_reg_info.byte_size = address_byte_size; + m_pc_reg_info.encoding = eEncodingUint; + m_pc_reg_info.format = eFormatPointer; + m_pc_reg_info.invalidate_regs = nullptr; + m_pc_reg_info.value_regs = nullptr; + m_pc_reg_info.kinds[eRegisterKindEHFrame] = LLDB_INVALID_REGNUM; + m_pc_reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM; + m_pc_reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + m_pc_reg_info.kinds[eRegisterKindProcessPlugin] = LLDB_INVALID_REGNUM; + m_pc_reg_info.kinds[eRegisterKindLLDB] = LLDB_INVALID_REGNUM; +} + +RegisterContextHistory::~RegisterContextHistory() { + delete m_reg_set0.registers; + delete m_pc_reg_info.invalidate_regs; + delete m_pc_reg_info.value_regs; +} + +void RegisterContextHistory::InvalidateAllRegisters() {} + +size_t RegisterContextHistory::GetRegisterCount() { return 1; } + +const lldb_private::RegisterInfo * +RegisterContextHistory::GetRegisterInfoAtIndex(size_t reg) { + if (reg) + return nullptr; + return &m_pc_reg_info; +} + +size_t RegisterContextHistory::GetRegisterSetCount() { return 1; } + +const lldb_private::RegisterSet * +RegisterContextHistory::GetRegisterSet(size_t reg_set) { + if (reg_set) + return nullptr; + return &m_reg_set0; +} + +bool RegisterContextHistory::ReadRegister( + const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) { + if (!reg_info) + return false; + uint32_t reg_number = reg_info->kinds[eRegisterKindGeneric]; + if (reg_number == LLDB_REGNUM_GENERIC_PC) { + value.SetUInt(m_pc_value, reg_info->byte_size); + return true; + } + return false; +} + +bool RegisterContextHistory::WriteRegister( + const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) { + return false; +} + +bool RegisterContextHistory::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + return false; +} + +bool RegisterContextHistory::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return false; +} + +uint32_t RegisterContextHistory::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + if (kind == eRegisterKindGeneric && num == LLDB_REGNUM_GENERIC_PC) + return 0; + return LLDB_INVALID_REGNUM; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h new file mode 100644 index 000000000000..a1eadac5d1b7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextHistory.h @@ -0,0 +1,67 @@ +//===-- RegisterContextHistory.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTHISTORY_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTHISTORY_H + +#include <vector> + +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class RegisterContextHistory : public lldb_private::RegisterContext { +public: + typedef std::shared_ptr<RegisterContextHistory> SharedPtr; + + RegisterContextHistory(Thread &thread, uint32_t concrete_frame_idx, + uint32_t address_byte_size, lldb::addr_t pc_value); + + ~RegisterContextHistory() override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + +private: + // For RegisterContextLLDB only + + lldb_private::RegisterSet m_reg_set0; // register set 0 (PC only) + lldb_private::RegisterInfo m_pc_reg_info; + + lldb::addr_t m_pc_value; + + RegisterContextHistory(const RegisterContextHistory &) = delete; + const RegisterContextHistory & + operator=(const RegisterContextHistory &) = delete; +}; +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTHISTORY_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp new file mode 100644 index 000000000000..9e022baa297b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.cpp @@ -0,0 +1,125 @@ +//===-- RegisterContextLinux_i386.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 "RegisterContextLinux_i386.h" +#include "RegisterContextPOSIX_x86.h" + +using namespace lldb_private; +using namespace lldb; + +struct GPR { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + uint32_t orig_eax; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; +}; + +struct FPR_i386 { + uint16_t fctrl; // FPU Control Word (fcw) + uint16_t fstat; // FPU Status Word (fsw) + uint16_t ftag; // FPU Tag Word (ftw) + uint16_t fop; // Last Instruction Opcode (fop) + union { + struct { + uint64_t fip; // Instruction Pointer + uint64_t fdp; // Data Pointer + } x86_64; + struct { + uint32_t fioff; // FPU IP Offset (fip) + uint32_t fiseg; // FPU IP Selector (fcs) + uint32_t fooff; // FPU Operand Pointer Offset (foo) + uint32_t foseg; // FPU Operand Pointer Selector (fos) + } i386_; // Added _ in the end to avoid error with gcc defining i386 in some + // cases + } ptr; + uint32_t mxcsr; // MXCSR Register State + uint32_t mxcsrmask; // MXCSR Mask + MMSReg stmm[8]; // 8*16 bytes for each FP-reg = 128 bytes + XMMReg xmm[8]; // 8*16 bytes for each XMM-reg = 128 bytes + uint32_t padding[56]; +}; + +struct UserArea { + GPR regs; // General purpose registers. + int32_t fpvalid; // True if FPU is being used. + FPR_i386 i387; // FPU registers. + uint32_t tsize; // Text segment size. + uint32_t dsize; // Data segment size. + uint32_t ssize; // Stack segment size. + uint32_t start_code; // VM address of text. + uint32_t start_stack; // VM address of stack bottom (top in rsp). + int32_t signal; // Signal causing core dump. + int32_t reserved; // Unused. + uint32_t ar0; // Location of GPR's. + uint32_t fpstate; // Location of FPR's. Should be a FXSTATE *, but this + // has to be 32-bits even on 64-bit systems. + uint32_t magic; // Identifier for core dumps. + char u_comm[32]; // Command causing core dump. + uint32_t u_debugreg[8]; // Debug registers (DR0 - DR7). +}; + +#define DR_SIZE sizeof(((UserArea *)NULL)->u_debugreg[0]) +#define DR_0_OFFSET 0xFC +#define DR_OFFSET(reg_index) (DR_0_OFFSET + (reg_index * 4)) +#define FPR_SIZE(reg) sizeof(((FPR_i386 *)NULL)->reg) + +// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure. +#define DECLARE_REGISTER_INFOS_I386_STRUCT +#include "RegisterInfos_i386.h" +#undef DECLARE_REGISTER_INFOS_I386_STRUCT + +RegisterContextLinux_i386::RegisterContextLinux_i386( + const ArchSpec &target_arch) + : RegisterContextLinux_x86( + target_arch, + {"orig_eax", + nullptr, + sizeof(((GPR *)nullptr)->orig_eax), + (LLVM_EXTENSION offsetof(GPR, orig_eax)), + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, + nullptr, + nullptr, + nullptr}) {} + +size_t RegisterContextLinux_i386::GetGPRSizeStatic() { return sizeof(GPR); } + +const RegisterInfo *RegisterContextLinux_i386::GetRegisterInfo() const { + switch (GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + return g_register_infos_i386; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +uint32_t RegisterContextLinux_i386::GetRegisterCount() const { + return static_cast<uint32_t>(sizeof(g_register_infos_i386) / + sizeof(g_register_infos_i386[0])); +} + +uint32_t RegisterContextLinux_i386::GetUserRegisterCount() const { + return static_cast<uint32_t>(k_num_user_registers_i386); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h new file mode 100644 index 000000000000..c10613993689 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_i386.h @@ -0,0 +1,29 @@ +//===-- RegisterContextLinux_i386.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTLINUX_I386_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTLINUX_I386_H + +#include "Plugins/Process/Utility/RegisterContextLinux_x86.h" + +class RegisterContextLinux_i386 + : public lldb_private::RegisterContextLinux_x86 { +public: + RegisterContextLinux_i386(const lldb_private::ArchSpec &target_arch); + + static size_t GetGPRSizeStatic(); + size_t GetGPRSize() const override { return GetGPRSizeStatic(); } + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + uint32_t GetUserRegisterCount() const override; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp new file mode 100644 index 000000000000..77627cfbdefe --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.cpp @@ -0,0 +1,69 @@ +//===-- RegisterContextLinux_s390x.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 "RegisterContextLinux_s390x.h" +#include "RegisterContextPOSIX_s390x.h" + +using namespace lldb_private; +using namespace lldb; + +// Include RegisterInfos_s390x to declare our g_register_infos_s390x structure. +#define DECLARE_REGISTER_INFOS_S390X_STRUCT +#include "RegisterInfos_s390x.h" +#undef DECLARE_REGISTER_INFOS_S390X_STRUCT + +static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::systemz: + return g_register_infos_s390x; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::systemz: + return k_num_registers_s390x; + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::systemz: + return k_num_user_registers_s390x + k_num_linux_registers_s390x; + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextLinux_s390x::RegisterContextLinux_s390x( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)), + m_user_register_count(GetUserRegisterInfoCount(target_arch)) {} + +const RegisterInfo *RegisterContextLinux_s390x::GetRegisterInfo() const { + return m_register_info_p; +} + +uint32_t RegisterContextLinux_s390x::GetRegisterCount() const { + return m_register_info_count; +} + +uint32_t RegisterContextLinux_s390x::GetUserRegisterCount() const { + return m_user_register_count; +} + +size_t RegisterContextLinux_s390x::GetGPRSize() const { return 0; } diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h new file mode 100644 index 000000000000..6bfe34de7acf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_s390x.h @@ -0,0 +1,32 @@ +//===-- RegisterContextLinux_s390x.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTLINUX_S390X_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTLINUX_S390X_H + +#include "RegisterInfoInterface.h" + +class RegisterContextLinux_s390x : public lldb_private::RegisterInfoInterface { +public: + RegisterContextLinux_s390x(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + uint32_t GetUserRegisterCount() const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; + uint32_t m_user_register_count; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86.h new file mode 100644 index 000000000000..0e1863864aa6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86.h @@ -0,0 +1,30 @@ +//===-- RegisterContextLinux_i386.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTLINUX_X86_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTLINUX_X86_H + +#include "RegisterInfoInterface.h" + +namespace lldb_private { + +class RegisterContextLinux_x86 : public RegisterInfoInterface { +public: + RegisterContextLinux_x86(const ArchSpec &target_arch, + RegisterInfo orig_ax_info) + : RegisterInfoInterface(target_arch), m_orig_ax_info(orig_ax_info) {} + + const RegisterInfo &GetOrigAxInfo() const { return m_orig_ax_info; } + +private: + lldb_private::RegisterInfo m_orig_ax_info; +}; + +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp new file mode 100644 index 000000000000..63c034a858d7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.cpp @@ -0,0 +1,184 @@ +//===-- RegisterContextLinux_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 +// +//===---------------------------------------------------------------------===// + +#include "RegisterContextLinux_x86_64.h" +#include "RegisterContextLinux_i386.h" +#include "RegisterContextPOSIX_x86.h" +#include <vector> + +using namespace lldb_private; +using namespace lldb; + +typedef struct _GPR { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t rbp; + uint64_t rbx; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rsi; + uint64_t rdi; + uint64_t orig_rax; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; + uint64_t fs_base; + uint64_t gs_base; + uint64_t ds; + uint64_t es; + uint64_t fs; + uint64_t gs; +} GPR; + +struct DBG { + uint64_t dr[8]; +}; + +struct UserArea { + GPR gpr; // General purpose registers. + int32_t fpvalid; // True if FPU is being used. + int32_t pad0; + FXSAVE fpr; // General purpose floating point registers (see FPR for extended + // register sets). + uint64_t tsize; // Text segment size. + uint64_t dsize; // Data segment size. + uint64_t ssize; // Stack segment size. + uint64_t start_code; // VM address of text. + uint64_t start_stack; // VM address of stack bottom (top in rsp). + int64_t signal; // Signal causing core dump. + int32_t reserved; // Unused. + int32_t pad1; + uint64_t ar0; // Location of GPR's. + FXSAVE *fpstate; // Location of FPR's. + uint64_t magic; // Identifier for core dumps. + char u_comm[32]; // Command causing core dump. + DBG dbg; // Debug registers. + uint64_t error_code; // CPU error code. + uint64_t fault_address; // Control register CR3. +}; + +#define DR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, dbg) + \ + LLVM_EXTENSION offsetof(DBG, dr[reg_index])) + +// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64_with_base +// structure. +#define DECLARE_REGISTER_INFOS_X86_64_STRUCT +#include "RegisterInfos_x86_64_with_base.h" +#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT + +static std::vector<lldb_private::RegisterInfo> &GetPrivateRegisterInfoVector() { + static std::vector<lldb_private::RegisterInfo> g_register_infos; + return g_register_infos; +} + +static const RegisterInfo * +GetRegisterInfo_i386(const lldb_private::ArchSpec &arch) { + std::vector<lldb_private::RegisterInfo> &g_register_infos = + GetPrivateRegisterInfoVector(); + + // Allocate RegisterInfo only once + if (g_register_infos.empty()) { + // Copy the register information from base class + std::unique_ptr<RegisterContextLinux_i386> reg_interface( + new RegisterContextLinux_i386(arch)); + const RegisterInfo *base_info = reg_interface->GetRegisterInfo(); + g_register_infos.insert(g_register_infos.end(), &base_info[0], + &base_info[k_num_registers_i386]); + +// Include RegisterInfos_x86_64 to update the g_register_infos structure +// with x86_64 offsets. +#define UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS +#include "RegisterInfos_x86_64_with_base.h" +#undef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS + } + + return &g_register_infos[0]; +} + +static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return GetRegisterInfo_i386(target_arch); + case llvm::Triple::x86_64: + return g_register_infos_x86_64_with_base; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: { + assert(!GetPrivateRegisterInfoVector().empty() && + "i386 register info not yet filled."); + return static_cast<uint32_t>(GetPrivateRegisterInfoVector().size()); + } + case llvm::Triple::x86_64: + return static_cast<uint32_t>(sizeof(g_register_infos_x86_64_with_base) / + sizeof(g_register_infos_x86_64_with_base[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return static_cast<uint32_t>(k_num_user_registers_i386); + case llvm::Triple::x86_64: + return static_cast<uint32_t>(x86_64_with_base::k_num_user_registers); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextLinux_x86_64::RegisterContextLinux_x86_64( + const ArchSpec &target_arch) + : lldb_private::RegisterContextLinux_x86( + target_arch, + {"orig_rax", + nullptr, + sizeof(((GPR *)nullptr)->orig_rax), + (LLVM_EXTENSION offsetof(GPR, orig_rax)), + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, + nullptr, + nullptr, + nullptr}), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)), + m_user_register_count(GetUserRegisterInfoCount(target_arch)) {} + +size_t RegisterContextLinux_x86_64::GetGPRSizeStatic() { return sizeof(GPR); } + +const RegisterInfo *RegisterContextLinux_x86_64::GetRegisterInfo() const { + return m_register_info_p; +} + +uint32_t RegisterContextLinux_x86_64::GetRegisterCount() const { + return m_register_info_count; +} + +uint32_t RegisterContextLinux_x86_64::GetUserRegisterCount() const { + return m_user_register_count; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h new file mode 100644 index 000000000000..d141ba66b4e2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextLinux_x86_64.h @@ -0,0 +1,34 @@ +//===-- RegisterContextLinux_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTLINUX_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTLINUX_X86_64_H + +#include "Plugins/Process/Utility/RegisterContextLinux_x86.h" + +class RegisterContextLinux_x86_64 + : public lldb_private::RegisterContextLinux_x86 { +public: + RegisterContextLinux_x86_64(const lldb_private::ArchSpec &target_arch); + + static size_t GetGPRSizeStatic(); + size_t GetGPRSize() const override { return GetGPRSizeStatic(); } + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + uint32_t GetUserRegisterCount() const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; + uint32_t m_user_register_count; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp new file mode 100644 index 000000000000..067d1c3705e4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp @@ -0,0 +1,74 @@ +//===-- RegisterContextMach_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(__APPLE__) + +#include "RegisterContextMach_arm.h" + +#include <mach/mach_types.h> +#include <mach/thread_act.h> + + +using namespace lldb; +using namespace lldb_private; + +RegisterContextMach_arm::RegisterContextMach_arm(Thread &thread, + uint32_t concrete_frame_idx) + : RegisterContextDarwin_arm(thread, concrete_frame_idx) {} + +RegisterContextMach_arm::~RegisterContextMach_arm() = default; + +int RegisterContextMach_arm::DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) { + mach_msg_type_number_t count = GPRWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count); +} + +int RegisterContextMach_arm::DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) { + mach_msg_type_number_t count = FPUWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count); +} + +int RegisterContextMach_arm::DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) { + mach_msg_type_number_t count = EXCWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count); +} + +int RegisterContextMach_arm::DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) { + mach_msg_type_number_t count = DBGWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&dbg, &count); +} + +int RegisterContextMach_arm::DoWriteGPR(lldb::tid_t tid, int flavor, + const GPR &gpr) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<GPR *>(&gpr)), + GPRWordCount); +} + +int RegisterContextMach_arm::DoWriteFPU(lldb::tid_t tid, int flavor, + const FPU &fpu) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<FPU *>(&fpu)), + FPUWordCount); +} + +int RegisterContextMach_arm::DoWriteEXC(lldb::tid_t tid, int flavor, + const EXC &exc) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<EXC *>(&exc)), + EXCWordCount); +} + +int RegisterContextMach_arm::DoWriteDBG(lldb::tid_t tid, int flavor, + const DBG &dbg) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<DBG *>(&dbg)), + DBGWordCount); +} + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h new file mode 100644 index 000000000000..fedd0062c99c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_arm.h @@ -0,0 +1,39 @@ +//===-- RegisterContextMach_arm.h -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMACH_ARM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMACH_ARM_H + +#include "RegisterContextDarwin_arm.h" + +class RegisterContextMach_arm : public RegisterContextDarwin_arm { +public: + RegisterContextMach_arm(lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + ~RegisterContextMach_arm() override; + +protected: + int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override; + + int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override; + + int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override; + + int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) override; + + int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override; + + int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override; + + int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override; + + int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) override; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMACH_ARM_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp new file mode 100644 index 000000000000..fe5cecef1b0c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp @@ -0,0 +1,60 @@ +//===-- RegisterContextMach_i386.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(__APPLE__) + +#include <mach/thread_act.h> + +#include "RegisterContextMach_i386.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextMach_i386::RegisterContextMach_i386(Thread &thread, + uint32_t concrete_frame_idx) + : RegisterContextDarwin_i386(thread, concrete_frame_idx) {} + +RegisterContextMach_i386::~RegisterContextMach_i386() = default; + +int RegisterContextMach_i386::DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) { + mach_msg_type_number_t count = GPRWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count); +} + +int RegisterContextMach_i386::DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) { + mach_msg_type_number_t count = FPUWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count); +} + +int RegisterContextMach_i386::DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) { + mach_msg_type_number_t count = EXCWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count); +} + +int RegisterContextMach_i386::DoWriteGPR(lldb::tid_t tid, int flavor, + const GPR &gpr) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<GPR *>(&gpr)), + GPRWordCount); +} + +int RegisterContextMach_i386::DoWriteFPU(lldb::tid_t tid, int flavor, + const FPU &fpu) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<FPU *>(&fpu)), + FPUWordCount); +} + +int RegisterContextMach_i386::DoWriteEXC(lldb::tid_t tid, int flavor, + const EXC &exc) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<EXC *>(&exc)), + EXCWordCount); +} + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h new file mode 100644 index 000000000000..8bdac083863d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_i386.h @@ -0,0 +1,35 @@ +//===-- RegisterContextMach_i386.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMACH_I386_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMACH_I386_H + +#include "RegisterContextDarwin_i386.h" + +class RegisterContextMach_i386 : public RegisterContextDarwin_i386 { +public: + RegisterContextMach_i386(lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + ~RegisterContextMach_i386() override; + +protected: + int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override; + + int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override; + + int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override; + + int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override; + + int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override; + + int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMACH_I386_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp new file mode 100644 index 000000000000..a3d8c4f649d2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp @@ -0,0 +1,63 @@ +//===-- RegisterContextMach_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(__APPLE__) + +#include <mach/thread_act.h> + +#include "RegisterContextMach_x86_64.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextMach_x86_64::RegisterContextMach_x86_64( + Thread &thread, uint32_t concrete_frame_idx) + : RegisterContextDarwin_x86_64(thread, concrete_frame_idx) {} + +RegisterContextMach_x86_64::~RegisterContextMach_x86_64() = default; + +int RegisterContextMach_x86_64::DoReadGPR(lldb::tid_t tid, int flavor, + GPR &gpr) { + mach_msg_type_number_t count = GPRWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count); +} + +int RegisterContextMach_x86_64::DoReadFPU(lldb::tid_t tid, int flavor, + FPU &fpu) { + mach_msg_type_number_t count = FPUWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count); +} + +int RegisterContextMach_x86_64::DoReadEXC(lldb::tid_t tid, int flavor, + EXC &exc) { + mach_msg_type_number_t count = EXCWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count); +} + +int RegisterContextMach_x86_64::DoWriteGPR(lldb::tid_t tid, int flavor, + const GPR &gpr) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<GPR *>(&gpr)), + GPRWordCount); +} + +int RegisterContextMach_x86_64::DoWriteFPU(lldb::tid_t tid, int flavor, + const FPU &fpu) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<FPU *>(&fpu)), + FPUWordCount); +} + +int RegisterContextMach_x86_64::DoWriteEXC(lldb::tid_t tid, int flavor, + const EXC &exc) { + return ::thread_set_state( + tid, flavor, reinterpret_cast<thread_state_t>(const_cast<EXC *>(&exc)), + EXCWordCount); +} + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h new file mode 100644 index 000000000000..99841a8e9a8d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h @@ -0,0 +1,36 @@ +//===-- RegisterContextMach_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMACH_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMACH_X86_64_H + +#include "RegisterContextDarwin_x86_64.h" + +class RegisterContextMach_x86_64 : public RegisterContextDarwin_x86_64 { +public: + RegisterContextMach_x86_64(lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + ~RegisterContextMach_x86_64() override; + +protected: + int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override; + + int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override; + + int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override; + + int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override; + + int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override; + + int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMACH_X86_64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp new file mode 100644 index 000000000000..84a19d5b1303 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.cpp @@ -0,0 +1,139 @@ +//===-- RegisterContextMemory.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 "RegisterContextMemory.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +using namespace lldb; +using namespace lldb_private; + +// RegisterContextMemory constructor +RegisterContextMemory::RegisterContextMemory(Thread &thread, + uint32_t concrete_frame_idx, + DynamicRegisterInfo ®_infos, + addr_t reg_data_addr) + : RegisterContext(thread, concrete_frame_idx), m_reg_infos(reg_infos), + m_reg_valid(), m_reg_data(), m_reg_data_addr(reg_data_addr) { + // Resize our vector of bools to contain one bool for every register. We will + // use these boolean values to know when a register value is valid in + // m_reg_data. + const size_t num_regs = reg_infos.GetNumRegisters(); + assert(num_regs > 0); + m_reg_valid.resize(num_regs); + + // Make a heap based buffer that is big enough to store all registers + m_data = + std::make_shared<DataBufferHeap>(reg_infos.GetRegisterDataByteSize(), 0); + m_reg_data.SetData(m_data); +} + +// Destructor +RegisterContextMemory::~RegisterContextMemory() = default; + +void RegisterContextMemory::InvalidateAllRegisters() { + if (m_reg_data_addr != LLDB_INVALID_ADDRESS) + SetAllRegisterValid(false); +} + +void RegisterContextMemory::SetAllRegisterValid(bool b) { + std::vector<bool>::iterator pos, end = m_reg_valid.end(); + for (pos = m_reg_valid.begin(); pos != end; ++pos) + *pos = b; +} + +size_t RegisterContextMemory::GetRegisterCount() { + return m_reg_infos.GetNumRegisters(); +} + +const RegisterInfo *RegisterContextMemory::GetRegisterInfoAtIndex(size_t reg) { + return m_reg_infos.GetRegisterInfoAtIndex(reg); +} + +size_t RegisterContextMemory::GetRegisterSetCount() { + return m_reg_infos.GetNumRegisterSets(); +} + +const RegisterSet *RegisterContextMemory::GetRegisterSet(size_t reg_set) { + return m_reg_infos.GetRegisterSet(reg_set); +} + +uint32_t RegisterContextMemory::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + return m_reg_infos.ConvertRegisterKindToRegisterNumber(kind, num); +} + +bool RegisterContextMemory::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB]; + if (!m_reg_valid[reg_num]) { + if (!ReadAllRegisterValues(m_data)) + return false; + } + const bool partial_data_ok = false; + return reg_value + .SetValueFromData(*reg_info, m_reg_data, reg_info->byte_offset, + partial_data_ok) + .Success(); +} + +bool RegisterContextMemory::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) { + if (m_reg_data_addr != LLDB_INVALID_ADDRESS) { + const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB]; + addr_t reg_addr = m_reg_data_addr + reg_info->byte_offset; + Status error(WriteRegisterValueToMemory(reg_info, reg_addr, + reg_info->byte_size, reg_value)); + m_reg_valid[reg_num] = false; + return error.Success(); + } + return false; +} + +bool RegisterContextMemory::ReadAllRegisterValues( + WritableDataBufferSP &data_sp) { + if (m_reg_data_addr != LLDB_INVALID_ADDRESS) { + ProcessSP process_sp(CalculateProcess()); + if (process_sp) { + Status error; + if (process_sp->ReadMemory(m_reg_data_addr, data_sp->GetBytes(), + data_sp->GetByteSize(), + error) == data_sp->GetByteSize()) { + SetAllRegisterValid(true); + return true; + } + } + } + return false; +} + +bool RegisterContextMemory::WriteAllRegisterValues( + const DataBufferSP &data_sp) { + if (m_reg_data_addr != LLDB_INVALID_ADDRESS) { + ProcessSP process_sp(CalculateProcess()); + if (process_sp) { + Status error; + SetAllRegisterValid(false); + if (process_sp->WriteMemory(m_reg_data_addr, data_sp->GetBytes(), + data_sp->GetByteSize(), + error) == data_sp->GetByteSize()) + return true; + } + } + return false; +} + +void RegisterContextMemory::SetAllRegisterData( + const lldb::DataBufferSP &data_sp) { + m_reg_data.SetData(data_sp); + SetAllRegisterValid(true); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h new file mode 100644 index 000000000000..2aad99ec9b21 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextMemory.h @@ -0,0 +1,75 @@ +//===-- RegisterContextMemory.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMEMORY_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMEMORY_H + +#include <vector> + +#include "lldb/Target/DynamicRegisterInfo.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/lldb-private.h" + +class RegisterContextMemory : public lldb_private::RegisterContext { +public: + RegisterContextMemory(lldb_private::Thread &thread, + uint32_t concrete_frame_idx, + lldb_private::DynamicRegisterInfo ®_info, + lldb::addr_t reg_data_addr); + + ~RegisterContextMemory() override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t reg_set) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + // If all of the thread register are in a contiguous buffer in + // memory, then the default ReadRegister/WriteRegister and + // ReadAllRegisterValues/WriteAllRegisterValues will work. If thread + // registers are not contiguous, clients will want to subclass this + // class and modify the read/write functions as needed. + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue ®_value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue ®_value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + void SetAllRegisterData(const lldb::DataBufferSP &data_sp); + +protected: + void SetAllRegisterValid(bool b); + + lldb_private::DynamicRegisterInfo &m_reg_infos; + std::vector<bool> m_reg_valid; + lldb::WritableDataBufferSP m_data; + lldb_private::DataExtractor m_reg_data; + lldb::addr_t m_reg_data_addr; // If this is valid, then we have a register + // context that is stored in memmory + +private: + RegisterContextMemory(const RegisterContextMemory &) = delete; + const RegisterContextMemory & + operator=(const RegisterContextMemory &) = delete; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTMEMORY_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_i386.cpp new file mode 100644 index 000000000000..a160c87db6cf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_i386.cpp @@ -0,0 +1,96 @@ +//===-- RegisterContextNetBSD_i386.cpp --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextNetBSD_i386.h" +#include "RegisterContextPOSIX_x86.h" + +using namespace lldb_private; +using namespace lldb; + +// this needs to match 'struct reg' +struct GPR { + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t eip; + uint32_t eflags; + uint32_t cs; + uint32_t ss; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; +}; + +struct FPR_i386 { + uint16_t fctrl; // FPU Control Word (fcw) + uint16_t fstat; // FPU Status Word (fsw) + uint16_t ftag; // FPU Tag Word (ftw) + uint16_t fop; // Last Instruction Opcode (fop) + union { + struct { + uint64_t fip; // Instruction Pointer + uint64_t fdp; // Data Pointer + } x86_64; + struct { + uint32_t fioff; // FPU IP Offset (fip) + uint32_t fiseg; // FPU IP Selector (fcs) + uint32_t fooff; // FPU Operand Pointer Offset (foo) + uint32_t foseg; // FPU Operand Pointer Selector (fos) + } i386_; // Added _ in the end to avoid error with gcc defining i386 in some + // cases + } ptr; + uint32_t mxcsr; // MXCSR Register State + uint32_t mxcsrmask; // MXCSR Mask + MMSReg stmm[8]; // 8*16 bytes for each FP-reg = 128 bytes + XMMReg xmm[8]; // 8*16 bytes for each XMM-reg = 128 bytes + uint32_t padding[56]; +}; + +struct UserArea { + GPR gpr; + FPR_i386 i387; + uint32_t u_debugreg[8]; // Debug registers (DR0 - DR7). + uint32_t tlsbase; +}; + +#define DR_SIZE sizeof(((UserArea *)NULL)->u_debugreg[0]) +#define DR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, u_debugreg[reg_index])) + +// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure. +#define DECLARE_REGISTER_INFOS_I386_STRUCT +#include "RegisterInfos_i386.h" +#undef DECLARE_REGISTER_INFOS_I386_STRUCT + +RegisterContextNetBSD_i386::RegisterContextNetBSD_i386( + const ArchSpec &target_arch) + : RegisterInfoInterface(target_arch) {} + +size_t RegisterContextNetBSD_i386::GetGPRSize() const { return sizeof(GPR); } + +const RegisterInfo *RegisterContextNetBSD_i386::GetRegisterInfo() const { + switch (GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + return g_register_infos_i386; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +uint32_t RegisterContextNetBSD_i386::GetRegisterCount() const { + return static_cast<uint32_t>(sizeof(g_register_infos_i386) / + sizeof(g_register_infos_i386[0])); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_i386.h new file mode 100644 index 000000000000..742bb18b8306 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_i386.h @@ -0,0 +1,25 @@ +//===-- RegisterContextNetBSD_i386.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTNETBSD_I386_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTNETBSD_I386_H + +#include "RegisterInfoInterface.h" + +class RegisterContextNetBSD_i386 : public lldb_private::RegisterInfoInterface { +public: + RegisterContextNetBSD_i386(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp new file mode 100644 index 000000000000..32bb7952820d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.cpp @@ -0,0 +1,178 @@ +//===-- RegisterContextNetBSD_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextNetBSD_x86_64.h" +#include "RegisterContextNetBSD_i386.h" +#include "RegisterContextPOSIX_x86.h" +#include "llvm/Support/Compiler.h" +#include "llvm/TargetParser/Triple.h" +#include <cassert> +#include <cstddef> + +using namespace lldb_private; +using namespace lldb; + +// src/sys/arch/amd64/include/frame_regs.h +typedef struct _GPR { + uint64_t rdi; /* 0 */ + uint64_t rsi; /* 1 */ + uint64_t rdx; /* 2 */ + uint64_t rcx; /* 3 */ + uint64_t r8; /* 4 */ + uint64_t r9; /* 5 */ + uint64_t r10; /* 6 */ + uint64_t r11; /* 7 */ + uint64_t r12; /* 8 */ + uint64_t r13; /* 9 */ + uint64_t r14; /* 10 */ + uint64_t r15; /* 11 */ + uint64_t rbp; /* 12 */ + uint64_t rbx; /* 13 */ + uint64_t rax; /* 14 */ + uint64_t gs; /* 15 */ + uint64_t fs; /* 16 */ + uint64_t es; /* 17 */ + uint64_t ds; /* 18 */ + uint64_t trapno; /* 19 */ + uint64_t err; /* 20 */ + uint64_t rip; /* 21 */ + uint64_t cs; /* 22 */ + uint64_t rflags; /* 23 */ + uint64_t rsp; /* 24 */ + uint64_t ss; /* 25 */ +} GPR; + +struct DBG { + uint64_t dr[16]; /* debug registers */ + /* Index 0-3: debug address registers */ + /* Index 4-5: reserved */ + /* Index 6: debug status */ + /* Index 7: debug control */ + /* Index 8-15: reserved */ +}; + +/* + * src/sys/arch/amd64/include/mcontext.h + * + * typedef struct { + * __gregset_t __gregs; + * __greg_t _mc_tlsbase; + * __fpregset_t __fpregs; + * } mcontext_t; + */ + +struct UserArea { + GPR gpr; + uint64_t mc_tlsbase; + FPR fpr; + DBG dbg; +}; + +#define DR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, dbg) + \ + LLVM_EXTENSION offsetof(DBG, dr[reg_index])) + + +// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64 +// structure. +#define DECLARE_REGISTER_INFOS_X86_64_STRUCT +#include "RegisterInfos_x86_64.h" +#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT + +static std::vector<lldb_private::RegisterInfo> &GetPrivateRegisterInfoVector() { + static std::vector<lldb_private::RegisterInfo> g_register_infos; + return g_register_infos; +} + +static const RegisterInfo * +GetRegisterInfo_i386(const lldb_private::ArchSpec &arch) { + std::vector<lldb_private::RegisterInfo> &g_register_infos = + GetPrivateRegisterInfoVector(); + + // Allocate RegisterInfo only once + if (g_register_infos.empty()) { + // Copy the register information from base class + std::unique_ptr<RegisterContextNetBSD_i386> reg_interface( + new RegisterContextNetBSD_i386(arch)); + const RegisterInfo *base_info = reg_interface->GetRegisterInfo(); + g_register_infos.insert(g_register_infos.end(), &base_info[0], + &base_info[k_num_registers_i386]); + +// Include RegisterInfos_x86_64 to update the g_register_infos structure +// with x86_64 offsets. +#define UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS +#include "RegisterInfos_x86_64.h" +#undef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS + } + + return &g_register_infos[0]; +} + +static const RegisterInfo * +PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return GetRegisterInfo_i386(target_arch); + case llvm::Triple::x86_64: + return g_register_infos_x86_64; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t +PrivateGetRegisterCount(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: { + assert(!GetPrivateRegisterInfoVector().empty() && + "i386 register info not yet filled."); + return static_cast<uint32_t>(GetPrivateRegisterInfoVector().size()); + } + case llvm::Triple::x86_64: + return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) / + sizeof(g_register_infos_x86_64[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +static uint32_t +PrivateGetUserRegisterCount(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return static_cast<uint32_t>(k_num_user_registers_i386); + case llvm::Triple::x86_64: + return static_cast<uint32_t>(k_num_user_registers_x86_64); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextNetBSD_x86_64::RegisterContextNetBSD_x86_64( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(PrivateGetRegisterInfoPtr(target_arch)), + m_register_count(PrivateGetRegisterCount(target_arch)), + m_user_register_count(PrivateGetUserRegisterCount(target_arch)) {} + +size_t RegisterContextNetBSD_x86_64::GetGPRSize() const { return sizeof(GPR); } + +const RegisterInfo *RegisterContextNetBSD_x86_64::GetRegisterInfo() const { + return m_register_info_p; +} + +uint32_t RegisterContextNetBSD_x86_64::GetRegisterCount() const { + return m_register_count; +} + +uint32_t RegisterContextNetBSD_x86_64::GetUserRegisterCount() const { + return m_user_register_count; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h new file mode 100644 index 000000000000..6f9787506013 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h @@ -0,0 +1,33 @@ +//===-- RegisterContextNetBSD_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTNETBSD_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTNETBSD_X86_64_H + +#include "RegisterInfoInterface.h" + +class RegisterContextNetBSD_x86_64 + : public lldb_private::RegisterInfoInterface { +public: + RegisterContextNetBSD_x86_64(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + uint32_t GetUserRegisterCount() const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + const uint32_t m_register_count; + const uint32_t m_user_register_count; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp new file mode 100644 index 000000000000..ddf6cc5c3c65 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.cpp @@ -0,0 +1,77 @@ +//===-- RegisterContextOpenBSD_i386.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 "RegisterContextOpenBSD_i386.h" +#include "RegisterContextPOSIX_x86.h" + +using namespace lldb_private; +using namespace lldb; + +// /usr/include/machine/reg.h +struct GPR { + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t eip; + uint32_t eflags; + uint32_t cs; + uint32_t ss; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; +}; + +struct dbreg { + uint32_t dr[8]; /* debug registers */ + /* Index 0-3: debug address registers */ + /* Index 4-5: reserved */ + /* Index 6: debug status */ + /* Index 7: debug control */ +}; + +using FPR_i386 = FXSAVE; + +struct UserArea { + GPR gpr; + FPR_i386 i387; +}; + +#define DR_SIZE sizeof(uint32_t) +#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(dbreg, dr[reg_index])) + +// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure. +#define DECLARE_REGISTER_INFOS_I386_STRUCT +#include "RegisterInfos_i386.h" +#undef DECLARE_REGISTER_INFOS_I386_STRUCT + +RegisterContextOpenBSD_i386::RegisterContextOpenBSD_i386( + const ArchSpec &target_arch) + : RegisterInfoInterface(target_arch) {} + +size_t RegisterContextOpenBSD_i386::GetGPRSize() const { return sizeof(GPR); } + +const RegisterInfo *RegisterContextOpenBSD_i386::GetRegisterInfo() const { + switch (GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return g_register_infos_i386; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +uint32_t RegisterContextOpenBSD_i386::GetRegisterCount() const { + return static_cast<uint32_t>(sizeof(g_register_infos_i386) / + sizeof(g_register_infos_i386[0])); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h new file mode 100644 index 000000000000..e6e24525b7fd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_i386.h @@ -0,0 +1,25 @@ +//===-- RegisterContextOpenBSD_i386.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTOPENBSD_I386_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTOPENBSD_I386_H + +#include "RegisterInfoInterface.h" + +class RegisterContextOpenBSD_i386 : public lldb_private::RegisterInfoInterface { +public: + RegisterContextOpenBSD_i386(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp new file mode 100644 index 000000000000..05c1f83efded --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.cpp @@ -0,0 +1,104 @@ +//===-- RegisterContextOpenBSD_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 +// +//===---------------------------------------------------------------------===// + +#include "RegisterContextOpenBSD_x86_64.h" +#include "RegisterContextPOSIX_x86.h" +#include <vector> + +using namespace lldb_private; +using namespace lldb; + +// /usr/include/machine/reg.h +typedef struct _GPR { + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + uint64_t rcx; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rbp; + uint64_t rbx; + uint64_t rax; + uint64_t rsp; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t ss; + uint64_t ds; + uint64_t es; + uint64_t fs; + uint64_t gs; +} GPR; + +struct DBG { + uint64_t dr[16]; /* debug registers */ + /* Index 0-3: debug address registers */ + /* Index 4-5: reserved */ + /* Index 6: debug status */ + /* Index 7: debug control */ + /* Index 8-15: reserved */ +}; + +struct UserArea { + GPR gpr; + FPR fpr; + DBG dbg; +}; + +#define DR_OFFSET(reg_index) (LLVM_EXTENSION offsetof(DBG, dr[reg_index])) + +// Include RegisterInfos_x86_64 to declare our g_register_infos_x86_64 +// structure. +#define DECLARE_REGISTER_INFOS_X86_64_STRUCT +#include "RegisterInfos_x86_64.h" +#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT + +static const RegisterInfo * +PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86_64: + return g_register_infos_x86_64; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t +PrivateGetRegisterCount(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86_64: + return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) / + sizeof(g_register_infos_x86_64[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterContextOpenBSD_x86_64::RegisterContextOpenBSD_x86_64( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(PrivateGetRegisterInfoPtr(target_arch)), + m_register_count(PrivateGetRegisterCount(target_arch)) {} + +size_t RegisterContextOpenBSD_x86_64::GetGPRSize() const { return sizeof(GPR); } + +const RegisterInfo *RegisterContextOpenBSD_x86_64::GetRegisterInfo() const { + return m_register_info_p; +} + +uint32_t RegisterContextOpenBSD_x86_64::GetRegisterCount() const { + return m_register_count; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h new file mode 100644 index 000000000000..b399c721546a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h @@ -0,0 +1,30 @@ +//===-- RegisterContextOpenBSD_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTOPENBSD_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTOPENBSD_X86_64_H + +#include "RegisterInfoInterface.h" + +class RegisterContextOpenBSD_x86_64 + : public lldb_private::RegisterInfoInterface { +public: + RegisterContextOpenBSD_x86_64(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + const uint32_t m_register_count; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp new file mode 100644 index 000000000000..684176bccdf0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp @@ -0,0 +1,98 @@ +//===-- RegisterContextPOSIX_arm.cpp --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <cerrno> +#include <cstdint> +#include <cstring> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_arm.h" + +using namespace lldb; +using namespace lldb_private; + +bool RegisterContextPOSIX_arm::IsGPR(unsigned reg) { + if (m_register_info_up->GetRegisterSetFromRegisterIndex(reg) == + RegisterInfoPOSIX_arm::GPRegSet) + return true; + return false; +} + +bool RegisterContextPOSIX_arm::IsFPR(unsigned reg) { + if (m_register_info_up->GetRegisterSetFromRegisterIndex(reg) == + RegisterInfoPOSIX_arm::FPRegSet) + return true; + return false; +} + +RegisterContextPOSIX_arm::RegisterContextPOSIX_arm( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_arm> register_info) + : lldb_private::RegisterContext(thread, 0), + m_register_info_up(std::move(register_info)) {} + +RegisterContextPOSIX_arm::~RegisterContextPOSIX_arm() = default; + +void RegisterContextPOSIX_arm::Invalidate() {} + +void RegisterContextPOSIX_arm::InvalidateAllRegisters() {} + +unsigned RegisterContextPOSIX_arm::GetRegisterOffset(unsigned reg) { + return m_register_info_up->GetRegisterInfo()[reg].byte_offset; +} + +unsigned RegisterContextPOSIX_arm::GetRegisterSize(unsigned reg) { + return m_register_info_up->GetRegisterInfo()[reg].byte_size; +} + +size_t RegisterContextPOSIX_arm::GetRegisterCount() { + return m_register_info_up->GetRegisterCount(); +} + +size_t RegisterContextPOSIX_arm::GetGPRSize() { + return m_register_info_up->GetGPRSize(); +} + +const lldb_private::RegisterInfo *RegisterContextPOSIX_arm::GetRegisterInfo() { + // Commonly, this method is overridden and g_register_infos is copied and + // specialized. So, use GetRegisterInfo() rather than g_register_infos in + // this scope. + return m_register_info_up->GetRegisterInfo(); +} + +const lldb_private::RegisterInfo * +RegisterContextPOSIX_arm::GetRegisterInfoAtIndex(size_t reg) { + if (reg < GetRegisterCount()) + return &GetRegisterInfo()[reg]; + + return nullptr; +} + +size_t RegisterContextPOSIX_arm::GetRegisterSetCount() { + return m_register_info_up->GetRegisterSetCount(); +} + +const lldb_private::RegisterSet * +RegisterContextPOSIX_arm::GetRegisterSet(size_t set) { + return m_register_info_up->GetRegisterSet(set); +} + +const char *RegisterContextPOSIX_arm::GetRegisterName(unsigned reg) { + if (reg < GetRegisterCount()) + return GetRegisterInfo()[reg].name; + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h new file mode 100644 index 000000000000..099c37d46f49 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h @@ -0,0 +1,62 @@ +//===-- RegisterContextPOSIX_arm.h ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_ARM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_ARM_H + +#include "RegisterInfoInterface.h" +#include "RegisterInfoPOSIX_arm.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" + +class RegisterContextPOSIX_arm : public lldb_private::RegisterContext { +public: + RegisterContextPOSIX_arm( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_arm> register_info); + + ~RegisterContextPOSIX_arm() override; + + void Invalidate(); + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + virtual size_t GetGPRSize(); + + virtual unsigned GetRegisterSize(unsigned reg); + + virtual unsigned GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + +protected: + std::unique_ptr<RegisterInfoPOSIX_arm> m_register_info_up; + + virtual const lldb_private::RegisterInfo *GetRegisterInfo(); + + bool IsGPR(unsigned reg); + + bool IsFPR(unsigned reg); + + size_t GetFPUSize() { return sizeof(RegisterInfoPOSIX_arm::FPU); } + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_ARM_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp new file mode 100644 index 000000000000..50e25568f2ae --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp @@ -0,0 +1,119 @@ +//===-- RegisterContextPOSIX_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 +// +//===----------------------------------------------------------------------===// + +#include <cerrno> +#include <cstdint> +#include <cstring> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_arm64.h" + +using namespace lldb; +using namespace lldb_private; + +bool RegisterContextPOSIX_arm64::IsGPR(unsigned reg) { + if (m_register_info_up->GetRegisterSetFromRegisterIndex(reg) == + RegisterInfoPOSIX_arm64::GPRegSet) + return true; + return false; +} + +bool RegisterContextPOSIX_arm64::IsFPR(unsigned reg) { + if (m_register_info_up->GetRegisterSetFromRegisterIndex(reg) == + RegisterInfoPOSIX_arm64::FPRegSet) + return true; + return false; +} + +bool RegisterContextPOSIX_arm64::IsSVE(unsigned reg) const { + return m_register_info_up->IsSVEReg(reg); +} + +bool RegisterContextPOSIX_arm64::IsSME(unsigned reg) const { + return m_register_info_up->IsSMEReg(reg); +} + +bool RegisterContextPOSIX_arm64::IsPAuth(unsigned reg) const { + return m_register_info_up->IsPAuthReg(reg); +} + +bool RegisterContextPOSIX_arm64::IsTLS(unsigned reg) const { + return m_register_info_up->IsTLSReg(reg); +} + +bool RegisterContextPOSIX_arm64::IsMTE(unsigned reg) const { + return m_register_info_up->IsMTEReg(reg); +} + +RegisterContextPOSIX_arm64::RegisterContextPOSIX_arm64( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_arm64> register_info) + : lldb_private::RegisterContext(thread, 0), + m_register_info_up(std::move(register_info)) {} + +RegisterContextPOSIX_arm64::~RegisterContextPOSIX_arm64() = default; + +void RegisterContextPOSIX_arm64::Invalidate() {} + +void RegisterContextPOSIX_arm64::InvalidateAllRegisters() {} + +unsigned RegisterContextPOSIX_arm64::GetRegisterOffset(unsigned reg) { + return m_register_info_up->GetRegisterInfo()[reg].byte_offset; +} + +unsigned RegisterContextPOSIX_arm64::GetRegisterSize(unsigned reg) { + return m_register_info_up->GetRegisterInfo()[reg].byte_size; +} + +size_t RegisterContextPOSIX_arm64::GetRegisterCount() { + return m_register_info_up->GetRegisterCount(); +} + +size_t RegisterContextPOSIX_arm64::GetGPRSize() { + return m_register_info_up->GetGPRSize(); +} + +const lldb_private::RegisterInfo * +RegisterContextPOSIX_arm64::GetRegisterInfo() { + // Commonly, this method is overridden and g_register_infos is copied and + // specialized. So, use GetRegisterInfo() rather than g_register_infos in + // this scope. + return m_register_info_up->GetRegisterInfo(); +} + +const lldb_private::RegisterInfo * +RegisterContextPOSIX_arm64::GetRegisterInfoAtIndex(size_t reg) { + if (reg < GetRegisterCount()) + return &GetRegisterInfo()[reg]; + + return nullptr; +} + +size_t RegisterContextPOSIX_arm64::GetRegisterSetCount() { + return m_register_info_up->GetRegisterSetCount(); +} + +const lldb_private::RegisterSet * +RegisterContextPOSIX_arm64::GetRegisterSet(size_t set) { + return m_register_info_up->GetRegisterSet(set); +} + +const char *RegisterContextPOSIX_arm64::GetRegisterName(unsigned reg) { + if (reg < GetRegisterCount()) + return GetRegisterInfo()[reg].name; + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h new file mode 100644 index 000000000000..b1226b25b4be --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h @@ -0,0 +1,86 @@ +//===-- RegisterContextPOSIX_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_ARM64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_ARM64_H + +#include "RegisterInfoInterface.h" +#include "RegisterInfoPOSIX_arm64.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" + +class RegisterContextPOSIX_arm64 : public lldb_private::RegisterContext { +public: + RegisterContextPOSIX_arm64( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_arm64> register_info); + + ~RegisterContextPOSIX_arm64() override; + + void Invalidate(); + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + virtual size_t GetGPRSize(); + + virtual unsigned GetRegisterSize(unsigned reg); + + virtual unsigned GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + +protected: + std::unique_ptr<RegisterInfoPOSIX_arm64> m_register_info_up; + + virtual const lldb_private::RegisterInfo *GetRegisterInfo(); + + bool IsGPR(unsigned reg); + + bool IsFPR(unsigned reg); + + size_t GetFPUSize() { return sizeof(RegisterInfoPOSIX_arm64::FPU); } + + bool IsSVE(unsigned reg) const; + bool IsPAuth(unsigned reg) const; + bool IsTLS(unsigned reg) const; + bool IsSME(unsigned reg) const; + bool IsMTE(unsigned reg) const; + + bool IsSVEZ(unsigned reg) const { return m_register_info_up->IsSVEZReg(reg); } + bool IsSVEP(unsigned reg) const { return m_register_info_up->IsSVEPReg(reg); } + bool IsSVEVG(unsigned reg) const { + return m_register_info_up->IsSVERegVG(reg); + } + bool IsSMEZA(unsigned reg) const { + return m_register_info_up->IsSMERegZA(reg); + } + + uint32_t GetRegNumSVEZ0() const { + return m_register_info_up->GetRegNumSVEZ0(); + } + uint32_t GetRegNumSVEFFR() const { + return m_register_info_up->GetRegNumSVEFFR(); + } + uint32_t GetRegNumFPCR() const { return m_register_info_up->GetRegNumFPCR(); } + uint32_t GetRegNumFPSR() const { return m_register_info_up->GetRegNumFPSR(); } + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_ARM64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_loongarch64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_loongarch64.cpp new file mode 100644 index 000000000000..a48a58f28f7a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_loongarch64.cpp @@ -0,0 +1,82 @@ +//===-- RegisterContextPOSIX_loongarch64.cpp --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_loongarch64.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextPOSIX_loongarch64::RegisterContextPOSIX_loongarch64( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_loongarch64> register_info) + : lldb_private::RegisterContext(thread, 0), + m_register_info_up(std::move(register_info)) {} + +RegisterContextPOSIX_loongarch64::~RegisterContextPOSIX_loongarch64() = default; + +void RegisterContextPOSIX_loongarch64::invalidate() {} + +void RegisterContextPOSIX_loongarch64::InvalidateAllRegisters() {} + +size_t RegisterContextPOSIX_loongarch64::GetRegisterCount() { + return m_register_info_up->GetRegisterCount(); +} + +size_t RegisterContextPOSIX_loongarch64::GetGPRSize() { + return m_register_info_up->GetGPRSize(); +} + +unsigned RegisterContextPOSIX_loongarch64::GetRegisterSize(unsigned int reg) { + return m_register_info_up->GetRegisterInfo()[reg].byte_size; +} + +unsigned RegisterContextPOSIX_loongarch64::GetRegisterOffset(unsigned int reg) { + return m_register_info_up->GetRegisterInfo()[reg].byte_offset; +} + +const lldb_private::RegisterInfo * +RegisterContextPOSIX_loongarch64::GetRegisterInfoAtIndex(size_t reg) { + if (reg < GetRegisterCount()) + return &GetRegisterInfo()[reg]; + + return nullptr; +} + +size_t RegisterContextPOSIX_loongarch64::GetRegisterSetCount() { + return m_register_info_up->GetRegisterCount(); +} + +const lldb_private::RegisterSet * +RegisterContextPOSIX_loongarch64::GetRegisterSet(size_t set) { + return m_register_info_up->GetRegisterSet(set); +} + +const lldb_private::RegisterInfo * +RegisterContextPOSIX_loongarch64::GetRegisterInfo() { + return m_register_info_up->GetRegisterInfo(); +} + +bool RegisterContextPOSIX_loongarch64::IsGPR(unsigned int reg) { + return m_register_info_up->GetRegisterSetFromRegisterIndex(reg) == + RegisterInfoPOSIX_loongarch64::GPRegSet; +} + +bool RegisterContextPOSIX_loongarch64::IsFPR(unsigned int reg) { + return m_register_info_up->GetRegisterSetFromRegisterIndex(reg) == + RegisterInfoPOSIX_loongarch64::FPRegSet; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_loongarch64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_loongarch64.h new file mode 100644 index 000000000000..95f93bb41f01 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_loongarch64.h @@ -0,0 +1,63 @@ +//===-- RegisterContextPOSIX_loongarch64.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_LOONGARCH64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_LOONGARCH64_H + +#include "RegisterInfoInterface.h" +#include "RegisterInfoPOSIX_loongarch64.h" +#include "lldb-loongarch-register-enums.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" + +class RegisterContextPOSIX_loongarch64 : public lldb_private::RegisterContext { +public: + RegisterContextPOSIX_loongarch64( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_loongarch64> register_info); + + ~RegisterContextPOSIX_loongarch64() override; + + void invalidate(); + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + virtual size_t GetGPRSize(); + + virtual unsigned GetRegisterSize(unsigned reg); + + virtual unsigned GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + +protected: + std::unique_ptr<RegisterInfoPOSIX_loongarch64> m_register_info_up; + + virtual const lldb_private::RegisterInfo *GetRegisterInfo(); + + bool IsGPR(unsigned reg); + + bool IsFPR(unsigned reg); + + size_t GetFPRSize() { return sizeof(RegisterInfoPOSIX_loongarch64::FPR); } + + uint32_t GetRegNumFCSR() const { return fpr_fcsr_loongarch; } + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_LOONGARCH64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp new file mode 100644 index 000000000000..3685d6ac72ad --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.cpp @@ -0,0 +1,138 @@ +//===-- RegisterContextPOSIX_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 +// +//===----------------------------------------------------------------------===// + +#include <cerrno> +#include <cstdint> +#include <cstring> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_mips64.h" +#include "RegisterContextFreeBSD_mips64.h" + +using namespace lldb_private; +using namespace lldb; + +bool RegisterContextPOSIX_mips64::IsGPR(unsigned reg) { + return reg < m_registers_count[gpr_registers_count]; // GPR's come first. +} + +bool RegisterContextPOSIX_mips64::IsFPR(unsigned reg) { + int set = GetRegisterSetCount(); + if (set > 1) + return reg < (m_registers_count[fpr_registers_count] + + m_registers_count[gpr_registers_count]); + return false; +} + +RegisterContextPOSIX_mips64::RegisterContextPOSIX_mips64( + Thread &thread, uint32_t concrete_frame_idx, + RegisterInfoInterface *register_info) + : RegisterContext(thread, concrete_frame_idx) { + m_register_info_up.reset(register_info); + m_num_registers = GetRegisterCount(); + int set = GetRegisterSetCount(); + + const RegisterSet *reg_set_ptr; + for(int i = 0; i < set; ++i) { + reg_set_ptr = GetRegisterSet(i); + m_registers_count[i] = reg_set_ptr->num_registers; + } + + assert(m_num_registers == + static_cast<uint32_t>(m_registers_count[gpr_registers_count] + + m_registers_count[fpr_registers_count] + + m_registers_count[msa_registers_count])); +} + +RegisterContextPOSIX_mips64::~RegisterContextPOSIX_mips64() = default; + +void RegisterContextPOSIX_mips64::Invalidate() {} + +void RegisterContextPOSIX_mips64::InvalidateAllRegisters() {} + +unsigned RegisterContextPOSIX_mips64::GetRegisterOffset(unsigned reg) { + assert(reg < m_num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_offset; +} + +unsigned RegisterContextPOSIX_mips64::GetRegisterSize(unsigned reg) { + assert(reg < m_num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_size; +} + +size_t RegisterContextPOSIX_mips64::GetRegisterCount() { + return m_register_info_up->GetRegisterCount(); +} + +size_t RegisterContextPOSIX_mips64::GetGPRSize() { + return m_register_info_up->GetGPRSize(); +} + +const RegisterInfo *RegisterContextPOSIX_mips64::GetRegisterInfo() { + // Commonly, this method is overridden and g_register_infos is copied and + // specialized. So, use GetRegisterInfo() rather than g_register_infos in + // this scope. + return m_register_info_up->GetRegisterInfo(); +} + +const RegisterInfo * +RegisterContextPOSIX_mips64::GetRegisterInfoAtIndex(size_t reg) { + if (reg < m_num_registers) + return &GetRegisterInfo()[reg]; + else + return nullptr; +} + +size_t RegisterContextPOSIX_mips64::GetRegisterSetCount() { + const auto *context = static_cast<const RegisterContextFreeBSD_mips64 *>( + m_register_info_up.get()); + return context->GetRegisterSetCount(); +} + +const RegisterSet *RegisterContextPOSIX_mips64::GetRegisterSet(size_t set) { + const auto *context = static_cast<const RegisterContextFreeBSD_mips64 *>( + m_register_info_up.get()); + return context->GetRegisterSet(set); +} + +const char *RegisterContextPOSIX_mips64::GetRegisterName(unsigned reg) { + assert(reg < m_num_registers && "Invalid register offset."); + return GetRegisterInfo()[reg].name; +} + +bool RegisterContextPOSIX_mips64::IsRegisterSetAvailable(size_t set_index) { + size_t num_sets = GetRegisterSetCount(); + + return (set_index < num_sets); +} + +// Used when parsing DWARF and EH frame information and any other object file +// sections that contain register numbers in them. +uint32_t RegisterContextPOSIX_mips64::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + const uint32_t num_regs = m_num_registers; + + assert(kind < kNumRegisterKinds); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg_idx); + + if (reg_info->kinds[kind] == num) + return reg_idx; + } + + return LLDB_INVALID_REGNUM; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h new file mode 100644 index 000000000000..b66dc3f44524 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h @@ -0,0 +1,78 @@ +//===-- RegisterContextPOSIX_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_MIPS64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_MIPS64_H + +#include "RegisterContext_mips.h" +#include "RegisterInfoInterface.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" + +class RegisterContextPOSIX_mips64 : public lldb_private::RegisterContext { +public: + + enum Register_count{ + gpr_registers_count = 0, + fpr_registers_count, + msa_registers_count, + register_set_count + }; + + RegisterContextPOSIX_mips64( + lldb_private::Thread &thread, uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + + ~RegisterContextPOSIX_mips64() override; + + void Invalidate(); + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + virtual size_t GetGPRSize(); + + virtual unsigned GetRegisterSize(unsigned reg); + + virtual unsigned GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + +protected: + uint32_t m_num_registers; + uint8_t m_registers_count[register_set_count]; + std::unique_ptr<lldb_private::RegisterInfoInterface> + m_register_info_up; // Register Info Interface (FreeBSD or Linux) + + // Determines if an extended register set is supported on the processor + // running the inferior process. + virtual bool IsRegisterSetAvailable(size_t set_index); + + virtual const lldb_private::RegisterInfo *GetRegisterInfo(); + + bool IsGPR(unsigned reg); + + bool IsFPR(unsigned reg); + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_MIPS64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp new file mode 100644 index 000000000000..cffd2865e385 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp @@ -0,0 +1,164 @@ +//===-- RegisterContextPOSIX_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 +// +//===----------------------------------------------------------------------===// + +#include <cerrno> +#include <cstdint> +#include <cstring> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_powerpc.h" + +using namespace lldb_private; +using namespace lldb; + +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, +}; + +static const uint32_t g_vmx_regnums[] = { + vmx_v0_powerpc, vmx_v1_powerpc, vmx_v2_powerpc, vmx_v3_powerpc, + vmx_v4_powerpc, vmx_v5_powerpc, vmx_v6_powerpc, vmx_v7_powerpc, + vmx_v8_powerpc, vmx_v9_powerpc, vmx_v10_powerpc, vmx_v11_powerpc, + vmx_v12_powerpc, vmx_v13_powerpc, vmx_v14_powerpc, vmx_v15_powerpc, + vmx_v16_powerpc, vmx_v17_powerpc, vmx_v18_powerpc, vmx_v19_powerpc, + vmx_v20_powerpc, vmx_v21_powerpc, vmx_v22_powerpc, vmx_v23_powerpc, + vmx_v24_powerpc, vmx_v25_powerpc, vmx_v26_powerpc, vmx_v27_powerpc, + vmx_v28_powerpc, vmx_v29_powerpc, vmx_v30_powerpc, vmx_v31_powerpc, + vmx_vrsave_powerpc, vmx_vscr_powerpc, +}; + +// Number of register sets provided by this context. +enum { k_num_register_sets = 3 }; + +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}, + {"Altivec/VMX Registers", "vmx", k_num_vmx_registers_powerpc, + g_vmx_regnums}, +}; + +static_assert(k_first_gpr_powerpc == 0, + "GPRs must index starting at 0, or fix IsGPR()"); +bool RegisterContextPOSIX_powerpc::IsGPR(unsigned reg) { + return (reg <= k_last_gpr_powerpc); // GPR's come first. +} + +bool RegisterContextPOSIX_powerpc::IsFPR(unsigned reg) { + return (reg >= k_first_fpr) && (reg <= k_last_fpr); +} + +bool RegisterContextPOSIX_powerpc::IsVMX(unsigned reg) { + return (reg >= k_first_vmx) && (reg <= k_last_vmx); +} + +RegisterContextPOSIX_powerpc::RegisterContextPOSIX_powerpc( + Thread &thread, uint32_t concrete_frame_idx, + RegisterInfoInterface *register_info) + : RegisterContext(thread, concrete_frame_idx) { + m_register_info_up.reset(register_info); +} + +RegisterContextPOSIX_powerpc::~RegisterContextPOSIX_powerpc() = default; + +void RegisterContextPOSIX_powerpc::Invalidate() {} + +void RegisterContextPOSIX_powerpc::InvalidateAllRegisters() {} + +unsigned RegisterContextPOSIX_powerpc::GetRegisterOffset(unsigned reg) { + assert(reg < k_num_registers_powerpc && "Invalid register number."); + return GetRegisterInfo()[reg].byte_offset; +} + +unsigned RegisterContextPOSIX_powerpc::GetRegisterSize(unsigned reg) { + assert(reg < k_num_registers_powerpc && "Invalid register number."); + return GetRegisterInfo()[reg].byte_size; +} + +size_t RegisterContextPOSIX_powerpc::GetRegisterCount() { + size_t num_registers = k_num_registers_powerpc; + return num_registers; +} + +size_t RegisterContextPOSIX_powerpc::GetGPRSize() { + return m_register_info_up->GetGPRSize(); +} + +const RegisterInfo *RegisterContextPOSIX_powerpc::GetRegisterInfo() { + // Commonly, this method is overridden and g_register_infos is copied and + // specialized. So, use GetRegisterInfo() rather than g_register_infos in + // this scope. + return m_register_info_up->GetRegisterInfo(); +} + +const RegisterInfo * +RegisterContextPOSIX_powerpc::GetRegisterInfoAtIndex(size_t reg) { + if (reg < k_num_registers_powerpc) + return &GetRegisterInfo()[reg]; + else + return nullptr; +} + +size_t RegisterContextPOSIX_powerpc::GetRegisterSetCount() { + size_t sets = 0; + for (size_t set = 0; set < k_num_register_sets; ++set) { + if (IsRegisterSetAvailable(set)) + ++sets; + } + + return sets; +} + +const RegisterSet *RegisterContextPOSIX_powerpc::GetRegisterSet(size_t set) { + if (IsRegisterSetAvailable(set)) + return &g_reg_sets_powerpc[set]; + else + return nullptr; +} + +const char *RegisterContextPOSIX_powerpc::GetRegisterName(unsigned reg) { + assert(reg < k_num_registers_powerpc && "Invalid register offset."); + return GetRegisterInfo()[reg].name; +} + +bool RegisterContextPOSIX_powerpc::IsRegisterSetAvailable(size_t set_index) { + size_t num_sets = k_num_register_sets; + + return (set_index < num_sets); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h new file mode 100644 index 000000000000..5dd8c890da6e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h @@ -0,0 +1,195 @@ +//===-- RegisterContextPOSIX_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_POWERPC_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_POWERPC_H + +#include "RegisterContext_powerpc.h" +#include "RegisterInfoInterface.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" + +// Internal codes for all powerpc registers. +enum { + k_first_gpr_powerpc, + gpr_r0_powerpc = k_first_gpr_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, + k_last_gpr_powerpc = gpr_pc_powerpc, + + k_first_fpr, + fpr_f0_powerpc = k_first_fpr, + 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, + k_last_fpr = fpr_fpscr_powerpc, + + k_first_vmx, + vmx_v0_powerpc = k_first_vmx, + vmx_v1_powerpc, + vmx_v2_powerpc, + vmx_v3_powerpc, + vmx_v4_powerpc, + vmx_v5_powerpc, + vmx_v6_powerpc, + vmx_v7_powerpc, + vmx_v8_powerpc, + vmx_v9_powerpc, + vmx_v10_powerpc, + vmx_v11_powerpc, + vmx_v12_powerpc, + vmx_v13_powerpc, + vmx_v14_powerpc, + vmx_v15_powerpc, + vmx_v16_powerpc, + vmx_v17_powerpc, + vmx_v18_powerpc, + vmx_v19_powerpc, + vmx_v20_powerpc, + vmx_v21_powerpc, + vmx_v22_powerpc, + vmx_v23_powerpc, + vmx_v24_powerpc, + vmx_v25_powerpc, + vmx_v26_powerpc, + vmx_v27_powerpc, + vmx_v28_powerpc, + vmx_v29_powerpc, + vmx_v30_powerpc, + vmx_v31_powerpc, + vmx_vrsave_powerpc, + vmx_vscr_powerpc, + k_last_vmx = vmx_vscr_powerpc, + + k_num_registers_powerpc, + k_num_gpr_registers_powerpc = k_last_gpr_powerpc - k_first_gpr_powerpc + 1, + k_num_fpr_registers_powerpc = k_last_fpr - k_first_fpr + 1, + k_num_vmx_registers_powerpc = k_last_vmx - k_first_vmx + 1, +}; + +class RegisterContextPOSIX_powerpc : public lldb_private::RegisterContext { +public: + RegisterContextPOSIX_powerpc( + lldb_private::Thread &thread, uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + + ~RegisterContextPOSIX_powerpc() override; + + void Invalidate(); + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + virtual size_t GetGPRSize(); + + virtual unsigned GetRegisterSize(unsigned reg); + + virtual unsigned GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + +protected: + uint64_t + m_gpr_powerpc[k_num_gpr_registers_powerpc]; // general purpose registers. + uint64_t + m_fpr_powerpc[k_num_fpr_registers_powerpc]; // floating point registers. + uint32_t m_vmx_powerpc[k_num_vmx_registers_powerpc][4]; + std::unique_ptr<lldb_private::RegisterInfoInterface> + m_register_info_up; // Register Info Interface (FreeBSD or Linux) + + // Determines if an extended register set is supported on the processor + // running the inferior process. + virtual bool IsRegisterSetAvailable(size_t set_index); + + virtual const lldb_private::RegisterInfo *GetRegisterInfo(); + + bool IsGPR(unsigned reg); + + bool IsFPR(unsigned reg); + + bool IsVMX(unsigned reg); + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool ReadVMX() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; + virtual bool WriteVMX() = 0; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_POWERPC_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp new file mode 100644 index 000000000000..f70ddeba209c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.cpp @@ -0,0 +1,183 @@ +//===-- RegisterContextPOSIX_ppc64le.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 <cerrno> +#include <cstdint> +#include <cstring> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_ppc64le.h" + +using namespace lldb_private; +using namespace lldb; + +static const uint32_t g_gpr_regnums[] = { + gpr_r0_ppc64le, gpr_r1_ppc64le, gpr_r2_ppc64le, gpr_r3_ppc64le, + gpr_r4_ppc64le, gpr_r5_ppc64le, gpr_r6_ppc64le, gpr_r7_ppc64le, + gpr_r8_ppc64le, gpr_r9_ppc64le, gpr_r10_ppc64le, gpr_r11_ppc64le, + gpr_r12_ppc64le, gpr_r13_ppc64le, gpr_r14_ppc64le, gpr_r15_ppc64le, + gpr_r16_ppc64le, gpr_r17_ppc64le, gpr_r18_ppc64le, gpr_r19_ppc64le, + gpr_r20_ppc64le, gpr_r21_ppc64le, gpr_r22_ppc64le, gpr_r23_ppc64le, + gpr_r24_ppc64le, gpr_r25_ppc64le, gpr_r26_ppc64le, gpr_r27_ppc64le, + gpr_r28_ppc64le, gpr_r29_ppc64le, gpr_r30_ppc64le, gpr_r31_ppc64le, + gpr_pc_ppc64le, gpr_msr_ppc64le, gpr_origr3_ppc64le, gpr_ctr_ppc64le, + gpr_lr_ppc64le, gpr_xer_ppc64le, gpr_cr_ppc64le, gpr_softe_ppc64le, + gpr_trap_ppc64le, +}; + +static const uint32_t g_fpr_regnums[] = { + fpr_f0_ppc64le, fpr_f1_ppc64le, fpr_f2_ppc64le, fpr_f3_ppc64le, + fpr_f4_ppc64le, fpr_f5_ppc64le, fpr_f6_ppc64le, fpr_f7_ppc64le, + fpr_f8_ppc64le, fpr_f9_ppc64le, fpr_f10_ppc64le, fpr_f11_ppc64le, + fpr_f12_ppc64le, fpr_f13_ppc64le, fpr_f14_ppc64le, fpr_f15_ppc64le, + fpr_f16_ppc64le, fpr_f17_ppc64le, fpr_f18_ppc64le, fpr_f19_ppc64le, + fpr_f20_ppc64le, fpr_f21_ppc64le, fpr_f22_ppc64le, fpr_f23_ppc64le, + fpr_f24_ppc64le, fpr_f25_ppc64le, fpr_f26_ppc64le, fpr_f27_ppc64le, + fpr_f28_ppc64le, fpr_f29_ppc64le, fpr_f30_ppc64le, fpr_f31_ppc64le, + fpr_fpscr_ppc64le, +}; + +static const uint32_t g_vmx_regnums[] = { + vmx_vr0_ppc64le, vmx_vr1_ppc64le, vmx_vr2_ppc64le, vmx_vr3_ppc64le, + vmx_vr4_ppc64le, vmx_vr5_ppc64le, vmx_vr6_ppc64le, vmx_vr7_ppc64le, + vmx_vr8_ppc64le, vmx_vr9_ppc64le, vmx_vr10_ppc64le, vmx_vr11_ppc64le, + vmx_vr12_ppc64le, vmx_vr13_ppc64le, vmx_vr14_ppc64le, vmx_vr15_ppc64le, + vmx_vr16_ppc64le, vmx_vr17_ppc64le, vmx_vr18_ppc64le, vmx_vr19_ppc64le, + vmx_vr20_ppc64le, vmx_vr21_ppc64le, vmx_vr22_ppc64le, vmx_vr23_ppc64le, + vmx_vr24_ppc64le, vmx_vr25_ppc64le, vmx_vr26_ppc64le, vmx_vr27_ppc64le, + vmx_vr28_ppc64le, vmx_vr29_ppc64le, vmx_vr30_ppc64le, vmx_vr31_ppc64le, + vmx_vscr_ppc64le, vmx_vrsave_ppc64le, +}; + +static const uint32_t g_vsx_regnums[] = { + vsx_vs0_ppc64le, vsx_vs1_ppc64le, vsx_vs2_ppc64le, vsx_vs3_ppc64le, + vsx_vs4_ppc64le, vsx_vs5_ppc64le, vsx_vs6_ppc64le, vsx_vs7_ppc64le, + vsx_vs8_ppc64le, vsx_vs9_ppc64le, vsx_vs10_ppc64le, vsx_vs11_ppc64le, + vsx_vs12_ppc64le, vsx_vs13_ppc64le, vsx_vs14_ppc64le, vsx_vs15_ppc64le, + vsx_vs16_ppc64le, vsx_vs17_ppc64le, vsx_vs18_ppc64le, vsx_vs19_ppc64le, + vsx_vs20_ppc64le, vsx_vs21_ppc64le, vsx_vs22_ppc64le, vsx_vs23_ppc64le, + vsx_vs24_ppc64le, vsx_vs25_ppc64le, vsx_vs26_ppc64le, vsx_vs27_ppc64le, + vsx_vs28_ppc64le, vsx_vs29_ppc64le, vsx_vs30_ppc64le, vsx_vs31_ppc64le, + vsx_vs32_ppc64le, vsx_vs33_ppc64le, vsx_vs34_ppc64le, vsx_vs35_ppc64le, + vsx_vs36_ppc64le, vsx_vs37_ppc64le, vsx_vs38_ppc64le, vsx_vs39_ppc64le, + vsx_vs40_ppc64le, vsx_vs41_ppc64le, vsx_vs42_ppc64le, vsx_vs43_ppc64le, + vsx_vs44_ppc64le, vsx_vs45_ppc64le, vsx_vs46_ppc64le, vsx_vs47_ppc64le, + vsx_vs48_ppc64le, vsx_vs49_ppc64le, vsx_vs50_ppc64le, vsx_vs51_ppc64le, + vsx_vs52_ppc64le, vsx_vs53_ppc64le, vsx_vs54_ppc64le, vsx_vs55_ppc64le, + vsx_vs56_ppc64le, vsx_vs57_ppc64le, vsx_vs58_ppc64le, vsx_vs59_ppc64le, + vsx_vs60_ppc64le, vsx_vs61_ppc64le, vsx_vs62_ppc64le, vsx_vs63_ppc64le, +}; + +// Number of register sets provided by this context. +enum { k_num_register_sets = 4 }; + +static const RegisterSet g_reg_sets_ppc64le[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_ppc64le, + g_gpr_regnums}, + {"Floating Point Registers", "fpr", k_num_fpr_registers_ppc64le, + g_fpr_regnums}, + {"Altivec/VMX Registers", "vmx", k_num_vmx_registers_ppc64le, + g_vmx_regnums}, + {"VSX Registers", "vsx", k_num_vsx_registers_ppc64le, g_vsx_regnums}, +}; + +bool RegisterContextPOSIX_ppc64le::IsGPR(unsigned reg) { + return (reg <= k_last_gpr_ppc64le); // GPR's come first. +} + +bool RegisterContextPOSIX_ppc64le::IsFPR(unsigned reg) { + return (reg >= k_first_fpr_ppc64le) && (reg <= k_last_fpr_ppc64le); +} + +bool RegisterContextPOSIX_ppc64le::IsVMX(unsigned reg) { + return (reg >= k_first_vmx_ppc64le) && (reg <= k_last_vmx_ppc64le); +} + +bool RegisterContextPOSIX_ppc64le::IsVSX(unsigned reg) { + return (reg >= k_first_vsx_ppc64le) && (reg <= k_last_vsx_ppc64le); +} + +RegisterContextPOSIX_ppc64le::RegisterContextPOSIX_ppc64le( + Thread &thread, uint32_t concrete_frame_idx, + RegisterInfoInterface *register_info) + : RegisterContext(thread, concrete_frame_idx) { + m_register_info_up.reset(register_info); +} + +void RegisterContextPOSIX_ppc64le::InvalidateAllRegisters() {} + +unsigned RegisterContextPOSIX_ppc64le::GetRegisterOffset(unsigned reg) { + assert(reg < k_num_registers_ppc64le && "Invalid register number."); + return GetRegisterInfo()[reg].byte_offset; +} + +unsigned RegisterContextPOSIX_ppc64le::GetRegisterSize(unsigned reg) { + assert(reg < k_num_registers_ppc64le && "Invalid register number."); + return GetRegisterInfo()[reg].byte_size; +} + +size_t RegisterContextPOSIX_ppc64le::GetRegisterCount() { + size_t num_registers = k_num_registers_ppc64le; + return num_registers; +} + +size_t RegisterContextPOSIX_ppc64le::GetGPRSize() { + return m_register_info_up->GetGPRSize(); +} + +const RegisterInfo *RegisterContextPOSIX_ppc64le::GetRegisterInfo() { + // Commonly, this method is overridden and g_register_infos is copied and + // specialized. So, use GetRegisterInfo() rather than g_register_infos in + // this scope. + return m_register_info_up->GetRegisterInfo(); +} + +const RegisterInfo * +RegisterContextPOSIX_ppc64le::GetRegisterInfoAtIndex(size_t reg) { + if (reg < k_num_registers_ppc64le) + return &GetRegisterInfo()[reg]; + else + return nullptr; +} + +size_t RegisterContextPOSIX_ppc64le::GetRegisterSetCount() { + size_t sets = 0; + for (size_t set = 0; set < k_num_register_sets; ++set) { + if (IsRegisterSetAvailable(set)) + ++sets; + } + + return sets; +} + +const RegisterSet *RegisterContextPOSIX_ppc64le::GetRegisterSet(size_t set) { + if (IsRegisterSetAvailable(set)) + return &g_reg_sets_ppc64le[set]; + else + return nullptr; +} + +const char *RegisterContextPOSIX_ppc64le::GetRegisterName(unsigned reg) { + assert(reg < k_num_registers_ppc64le && "Invalid register offset."); + return GetRegisterInfo()[reg].name; +} + +bool RegisterContextPOSIX_ppc64le::IsRegisterSetAvailable(size_t set_index) { + size_t num_sets = k_num_register_sets; + + return (set_index < num_sets); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h new file mode 100644 index 000000000000..66794ec9e9ca --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h @@ -0,0 +1,73 @@ +//===-- RegisterContextPOSIX_ppc64le.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_PPC64LE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_PPC64LE_H + +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" +#include "RegisterInfoInterface.h" +#include "Utility/PPC64LE_DWARF_Registers.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" + +class RegisterContextPOSIX_ppc64le : public lldb_private::RegisterContext { +public: + RegisterContextPOSIX_ppc64le( + lldb_private::Thread &thread, uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + virtual size_t GetGPRSize(); + + virtual unsigned GetRegisterSize(unsigned reg); + + virtual unsigned GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + +protected: + // 64-bit general purpose registers. + uint64_t m_gpr_ppc64le[k_num_gpr_registers_ppc64le]; + + // floating-point registers including extended register. + uint64_t m_fpr_ppc64le[k_num_fpr_registers_ppc64le]; + + // VMX registers. + uint64_t m_vmx_ppc64le[k_num_vmx_registers_ppc64le * 2]; + + // VSX registers. + uint64_t m_vsx_ppc64le[k_num_vsx_registers_ppc64le * 2]; + + std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_up; + + // Determines if an extended register set is supported on the processor + // running the inferior process. + virtual bool IsRegisterSetAvailable(size_t set_index); + + virtual const lldb_private::RegisterInfo *GetRegisterInfo(); + + bool IsGPR(unsigned reg); + + bool IsFPR(unsigned reg); + + bool IsVMX(unsigned reg); + + bool IsVSX(unsigned reg); + +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_PPC64LE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp new file mode 100644 index 000000000000..035ce00e1162 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.cpp @@ -0,0 +1,82 @@ +//===-- RegisterContextPOSIX_riscv64.cpp ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_riscv64.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextPOSIX_riscv64::RegisterContextPOSIX_riscv64( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info) + : lldb_private::RegisterContext(thread, 0), + m_register_info_up(std::move(register_info)) {} + +RegisterContextPOSIX_riscv64::~RegisterContextPOSIX_riscv64() = default; + +void RegisterContextPOSIX_riscv64::invalidate() {} + +void RegisterContextPOSIX_riscv64::InvalidateAllRegisters() {} + +size_t RegisterContextPOSIX_riscv64::GetRegisterCount() { + return m_register_info_up->GetRegisterCount(); +} + +size_t RegisterContextPOSIX_riscv64::GetGPRSize() { + return m_register_info_up->GetGPRSize(); +} + +unsigned RegisterContextPOSIX_riscv64::GetRegisterSize(unsigned int reg) { + return m_register_info_up->GetRegisterInfo()[reg].byte_size; +} + +unsigned RegisterContextPOSIX_riscv64::GetRegisterOffset(unsigned int reg) { + return m_register_info_up->GetRegisterInfo()[reg].byte_offset; +} + +const lldb_private::RegisterInfo * +RegisterContextPOSIX_riscv64::GetRegisterInfoAtIndex(size_t reg) { + if (reg < GetRegisterCount()) + return &GetRegisterInfo()[reg]; + + return nullptr; +} + +size_t RegisterContextPOSIX_riscv64::GetRegisterSetCount() { + return m_register_info_up->GetRegisterSetCount(); +} + +const lldb_private::RegisterSet * +RegisterContextPOSIX_riscv64::GetRegisterSet(size_t set) { + return m_register_info_up->GetRegisterSet(set); +} + +const lldb_private::RegisterInfo * +RegisterContextPOSIX_riscv64::GetRegisterInfo() { + return m_register_info_up->GetRegisterInfo(); +} + +bool RegisterContextPOSIX_riscv64::IsGPR(unsigned int reg) { + return m_register_info_up->GetRegisterSetFromRegisterIndex(reg) == + RegisterInfoPOSIX_riscv64::GPRegSet; +} + +bool RegisterContextPOSIX_riscv64::IsFPR(unsigned int reg) { + return m_register_info_up->GetRegisterSetFromRegisterIndex(reg) == + RegisterInfoPOSIX_riscv64::FPRegSet; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h new file mode 100644 index 000000000000..2431ed6ab8c6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h @@ -0,0 +1,63 @@ +//===-- RegisterContextPOSIX_riscv64.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_RISCV64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_RISCV64_H + +#include "RegisterInfoInterface.h" +#include "RegisterInfoPOSIX_riscv64.h" +#include "lldb-riscv-register-enums.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" + +class RegisterContextPOSIX_riscv64 : public lldb_private::RegisterContext { +public: + RegisterContextPOSIX_riscv64( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info); + + ~RegisterContextPOSIX_riscv64() override; + + void invalidate(); + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + virtual size_t GetGPRSize(); + + virtual unsigned GetRegisterSize(unsigned reg); + + virtual unsigned GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + +protected: + std::unique_ptr<RegisterInfoPOSIX_riscv64> m_register_info_up; + + virtual const lldb_private::RegisterInfo *GetRegisterInfo(); + + bool IsGPR(unsigned reg); + + bool IsFPR(unsigned reg); + + size_t GetFPRSize() { return sizeof(RegisterInfoPOSIX_riscv64::FPR); } + + uint32_t GetRegNumFCSR() const { return fpr_fcsr_riscv; } + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_RISCV64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp new file mode 100644 index 000000000000..b85da39b4c45 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.cpp @@ -0,0 +1,163 @@ +//===-- RegisterContextPOSIX_s390x.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 <cerrno> +#include <cstdint> +#include <cstring> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_s390x.h" +#include "RegisterContext_s390x.h" + +using namespace lldb_private; +using namespace lldb; + +// s390x 64-bit general purpose registers. +static const uint32_t g_gpr_regnums_s390x[] = { + lldb_r0_s390x, lldb_r1_s390x, lldb_r2_s390x, lldb_r3_s390x, + lldb_r4_s390x, lldb_r5_s390x, lldb_r6_s390x, lldb_r7_s390x, + lldb_r8_s390x, lldb_r9_s390x, lldb_r10_s390x, lldb_r11_s390x, + lldb_r12_s390x, lldb_r13_s390x, lldb_r14_s390x, lldb_r15_s390x, + lldb_acr0_s390x, lldb_acr1_s390x, lldb_acr2_s390x, lldb_acr3_s390x, + lldb_acr4_s390x, lldb_acr5_s390x, lldb_acr6_s390x, lldb_acr7_s390x, + lldb_acr8_s390x, lldb_acr9_s390x, lldb_acr10_s390x, lldb_acr11_s390x, + lldb_acr12_s390x, lldb_acr13_s390x, lldb_acr14_s390x, lldb_acr15_s390x, + lldb_pswm_s390x, lldb_pswa_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_gpr_regnums_s390x) / sizeof(g_gpr_regnums_s390x[0])) - + 1 == + k_num_gpr_registers_s390x, + "g_gpr_regnums_s390x has wrong number of register infos"); + +// s390x 64-bit floating point registers. +static const uint32_t g_fpu_regnums_s390x[] = { + lldb_f0_s390x, lldb_f1_s390x, lldb_f2_s390x, lldb_f3_s390x, + lldb_f4_s390x, lldb_f5_s390x, lldb_f6_s390x, lldb_f7_s390x, + lldb_f8_s390x, lldb_f9_s390x, lldb_f10_s390x, lldb_f11_s390x, + lldb_f12_s390x, lldb_f13_s390x, lldb_f14_s390x, lldb_f15_s390x, + lldb_fpc_s390x, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert((sizeof(g_fpu_regnums_s390x) / sizeof(g_fpu_regnums_s390x[0])) - + 1 == + k_num_fpr_registers_s390x, + "g_fpu_regnums_s390x has wrong number of register infos"); + +// Number of register sets provided by this context. +enum { k_num_register_sets = 2 }; + +// Register sets for s390x 64-bit. +static const RegisterSet g_reg_sets_s390x[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_s390x, + g_gpr_regnums_s390x}, + {"Floating Point Registers", "fpr", k_num_fpr_registers_s390x, + g_fpu_regnums_s390x}, +}; + +bool RegisterContextPOSIX_s390x::IsGPR(unsigned reg) { + return reg <= m_reg_info.last_gpr; // GPRs come first. +} + +bool RegisterContextPOSIX_s390x::IsFPR(unsigned reg) { + return (m_reg_info.first_fpr <= reg && reg <= m_reg_info.last_fpr); +} + +RegisterContextPOSIX_s390x::RegisterContextPOSIX_s390x( + Thread &thread, uint32_t concrete_frame_idx, + RegisterInfoInterface *register_info) + : RegisterContext(thread, concrete_frame_idx) { + m_register_info_up.reset(register_info); + + switch (register_info->GetTargetArchitecture().GetMachine()) { + case llvm::Triple::systemz: + m_reg_info.num_registers = k_num_registers_s390x; + m_reg_info.num_gpr_registers = k_num_gpr_registers_s390x; + m_reg_info.num_fpr_registers = k_num_fpr_registers_s390x; + m_reg_info.last_gpr = k_last_gpr_s390x; + m_reg_info.first_fpr = k_first_fpr_s390x; + m_reg_info.last_fpr = k_last_fpr_s390x; + break; + default: + assert(false && "Unhandled target architecture."); + break; + } +} + +RegisterContextPOSIX_s390x::~RegisterContextPOSIX_s390x() = default; + +void RegisterContextPOSIX_s390x::Invalidate() {} + +void RegisterContextPOSIX_s390x::InvalidateAllRegisters() {} + +const RegisterInfo *RegisterContextPOSIX_s390x::GetRegisterInfo() { + return m_register_info_up->GetRegisterInfo(); +} + +const RegisterInfo * +RegisterContextPOSIX_s390x::GetRegisterInfoAtIndex(size_t reg) { + if (reg < m_reg_info.num_registers) + return &GetRegisterInfo()[reg]; + else + return nullptr; +} + +size_t RegisterContextPOSIX_s390x::GetRegisterCount() { + return m_reg_info.num_registers; +} + +unsigned RegisterContextPOSIX_s390x::GetRegisterOffset(unsigned reg) { + assert(reg < m_reg_info.num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_offset; +} + +unsigned RegisterContextPOSIX_s390x::GetRegisterSize(unsigned reg) { + assert(reg < m_reg_info.num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_size; +} + +const char *RegisterContextPOSIX_s390x::GetRegisterName(unsigned reg) { + assert(reg < m_reg_info.num_registers && "Invalid register offset."); + return GetRegisterInfo()[reg].name; +} + +bool RegisterContextPOSIX_s390x::IsRegisterSetAvailable(size_t set_index) { + return set_index < k_num_register_sets; +} + +size_t RegisterContextPOSIX_s390x::GetRegisterSetCount() { + size_t sets = 0; + for (size_t set = 0; set < k_num_register_sets; ++set) { + if (IsRegisterSetAvailable(set)) + ++sets; + } + + return sets; +} + +const RegisterSet *RegisterContextPOSIX_s390x::GetRegisterSet(size_t set) { + if (IsRegisterSetAvailable(set)) { + switch (m_register_info_up->GetTargetArchitecture().GetMachine()) { + case llvm::Triple::systemz: + return &g_reg_sets_s390x[set]; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } + } + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h new file mode 100644 index 000000000000..7027af04f0bb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_s390x.h @@ -0,0 +1,72 @@ +//===-- RegisterContextPOSIX_s390x.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_S390X_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_S390X_H + +#include "RegisterContext_s390x.h" +#include "RegisterInfoInterface.h" +#include "lldb-s390x-register-enums.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" + +class RegisterContextPOSIX_s390x : public lldb_private::RegisterContext { +public: + RegisterContextPOSIX_s390x( + lldb_private::Thread &thread, uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + + ~RegisterContextPOSIX_s390x() override; + + void Invalidate(); + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + virtual unsigned GetRegisterSize(unsigned reg); + + virtual unsigned GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + +protected: + struct RegInfo { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + }; + + RegInfo m_reg_info; + std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_up; + + virtual bool IsRegisterSetAvailable(size_t set_index); + + virtual const lldb_private::RegisterInfo *GetRegisterInfo(); + + bool IsGPR(unsigned reg); + + bool IsFPR(unsigned reg); + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_S390X_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp new file mode 100644 index 000000000000..c14eb135c7b1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp @@ -0,0 +1,540 @@ +//===-- RegisterContextPOSIX_x86.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 <cerrno> +#include <cstdint> +#include <cstring> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_x86.h" +#include "RegisterContext_x86.h" + +using namespace lldb_private; +using namespace lldb; + +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 must be terminated with + // LLDB_INVALID_REGNUM. +}; +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"); + +const uint32_t g_lldb_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 must be terminated with + // LLDB_INVALID_REGNUM. +}; +static_assert((sizeof(g_lldb_regnums_i386) / sizeof(g_lldb_regnums_i386[0])) - + 1 == + k_num_fpr_registers_i386, + "g_lldb_regnums_i386 has wrong number of register infos"); + +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 must be terminated with + // LLDB_INVALID_REGNUM. +}; +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_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 must be terminated with + // LLDB_INVALID_REGNUM. +}; +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"); + +static const uint32_t g_lldb_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 must be terminated with + // LLDB_INVALID_REGNUM. +}; +static_assert((sizeof(g_lldb_regnums_x86_64) / + sizeof(g_lldb_regnums_x86_64[0])) - + 1 == + k_num_fpr_registers_x86_64, + "g_lldb_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 must be terminated with + // LLDB_INVALID_REGNUM. +}; +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"); + +uint32_t RegisterContextPOSIX_x86::g_contained_eax[] = {lldb_eax_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_ebx[] = {lldb_ebx_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_ecx[] = {lldb_ecx_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_edx[] = {lldb_edx_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_edi[] = {lldb_edi_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_esi[] = {lldb_esi_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_ebp[] = {lldb_ebp_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_esp[] = {lldb_esp_i386, + LLDB_INVALID_REGNUM}; + +uint32_t RegisterContextPOSIX_x86::g_invalidate_eax[] = { + lldb_eax_i386, lldb_ax_i386, lldb_ah_i386, lldb_al_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_ebx[] = { + lldb_ebx_i386, lldb_bx_i386, lldb_bh_i386, lldb_bl_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_ecx[] = { + lldb_ecx_i386, lldb_cx_i386, lldb_ch_i386, lldb_cl_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_edx[] = { + lldb_edx_i386, lldb_dx_i386, lldb_dh_i386, lldb_dl_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_edi[] = { + lldb_edi_i386, lldb_di_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_esi[] = { + lldb_esi_i386, lldb_si_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_ebp[] = { + lldb_ebp_i386, lldb_bp_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_esp[] = { + lldb_esp_i386, lldb_sp_i386, LLDB_INVALID_REGNUM}; + +uint32_t RegisterContextPOSIX_x86::g_contained_rax[] = {lldb_rax_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_rbx[] = {lldb_rbx_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_rcx[] = {lldb_rcx_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_rdx[] = {lldb_rdx_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_rdi[] = {lldb_rdi_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_rsi[] = {lldb_rsi_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_rbp[] = {lldb_rbp_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_rsp[] = {lldb_rsp_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_r8[] = {lldb_r8_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_r9[] = {lldb_r9_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_r10[] = {lldb_r10_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_r11[] = {lldb_r11_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_r12[] = {lldb_r12_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_r13[] = {lldb_r13_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_r14[] = {lldb_r14_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_r15[] = {lldb_r15_x86_64, + LLDB_INVALID_REGNUM}; + +uint32_t RegisterContextPOSIX_x86::g_invalidate_rax[] = { + lldb_rax_x86_64, lldb_eax_x86_64, lldb_ax_x86_64, + lldb_ah_x86_64, lldb_al_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rbx[] = { + lldb_rbx_x86_64, lldb_ebx_x86_64, lldb_bx_x86_64, + lldb_bh_x86_64, lldb_bl_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rcx[] = { + lldb_rcx_x86_64, lldb_ecx_x86_64, lldb_cx_x86_64, + lldb_ch_x86_64, lldb_cl_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rdx[] = { + lldb_rdx_x86_64, lldb_edx_x86_64, lldb_dx_x86_64, + lldb_dh_x86_64, lldb_dl_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rdi[] = { + lldb_rdi_x86_64, lldb_edi_x86_64, lldb_di_x86_64, lldb_dil_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rsi[] = { + lldb_rsi_x86_64, lldb_esi_x86_64, lldb_si_x86_64, lldb_sil_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rbp[] = { + lldb_rbp_x86_64, lldb_ebp_x86_64, lldb_bp_x86_64, lldb_bpl_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rsp[] = { + lldb_rsp_x86_64, lldb_esp_x86_64, lldb_sp_x86_64, lldb_spl_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r8[] = { + lldb_r8_x86_64, lldb_r8d_x86_64, lldb_r8w_x86_64, lldb_r8l_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r9[] = { + lldb_r9_x86_64, lldb_r9d_x86_64, lldb_r9w_x86_64, lldb_r9l_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r10[] = { + lldb_r10_x86_64, lldb_r10d_x86_64, lldb_r10w_x86_64, lldb_r10l_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r11[] = { + lldb_r11_x86_64, lldb_r11d_x86_64, lldb_r11w_x86_64, lldb_r11l_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r12[] = { + lldb_r12_x86_64, lldb_r12d_x86_64, lldb_r12w_x86_64, lldb_r12l_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r13[] = { + lldb_r13_x86_64, lldb_r13d_x86_64, lldb_r13w_x86_64, lldb_r13l_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r14[] = { + lldb_r14_x86_64, lldb_r14d_x86_64, lldb_r14w_x86_64, lldb_r14l_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r15[] = { + lldb_r15_x86_64, lldb_r15d_x86_64, lldb_r15w_x86_64, lldb_r15l_x86_64, + LLDB_INVALID_REGNUM}; + +uint32_t RegisterContextPOSIX_x86::g_contained_fip[] = {lldb_fip_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_fdp[] = {lldb_fdp_x86_64, + LLDB_INVALID_REGNUM}; + +uint32_t RegisterContextPOSIX_x86::g_invalidate_fip[] = { + lldb_fip_x86_64, lldb_fioff_x86_64, lldb_fiseg_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_fdp[] = { + lldb_fdp_x86_64, lldb_fooff_x86_64, lldb_foseg_x86_64, LLDB_INVALID_REGNUM}; + +uint32_t RegisterContextPOSIX_x86::g_contained_st0_32[] = {lldb_st0_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st1_32[] = {lldb_st1_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st2_32[] = {lldb_st2_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st3_32[] = {lldb_st3_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st4_32[] = {lldb_st4_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st5_32[] = {lldb_st5_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st6_32[] = {lldb_st6_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st7_32[] = {lldb_st7_i386, + LLDB_INVALID_REGNUM}; + +uint32_t RegisterContextPOSIX_x86::g_invalidate_st0_32[] = { + lldb_st0_i386, lldb_mm0_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st1_32[] = { + lldb_st1_i386, lldb_mm1_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st2_32[] = { + lldb_st2_i386, lldb_mm2_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st3_32[] = { + lldb_st3_i386, lldb_mm3_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st4_32[] = { + lldb_st4_i386, lldb_mm4_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st5_32[] = { + lldb_st5_i386, lldb_mm5_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st6_32[] = { + lldb_st6_i386, lldb_mm6_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st7_32[] = { + lldb_st7_i386, lldb_mm7_i386, LLDB_INVALID_REGNUM}; + +uint32_t RegisterContextPOSIX_x86::g_contained_st0_64[] = {lldb_st0_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st1_64[] = {lldb_st1_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st2_64[] = {lldb_st2_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st3_64[] = {lldb_st3_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st4_64[] = {lldb_st4_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st5_64[] = {lldb_st5_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st6_64[] = {lldb_st6_x86_64, + LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_contained_st7_64[] = {lldb_st7_x86_64, + LLDB_INVALID_REGNUM}; + +uint32_t RegisterContextPOSIX_x86::g_invalidate_st0_64[] = { + lldb_st0_x86_64, lldb_mm0_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st1_64[] = { + lldb_st1_x86_64, lldb_mm1_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st2_64[] = { + lldb_st2_x86_64, lldb_mm2_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st3_64[] = { + lldb_st3_x86_64, lldb_mm3_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st4_64[] = { + lldb_st4_x86_64, lldb_mm4_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st5_64[] = { + lldb_st5_x86_64, lldb_mm5_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st6_64[] = { + lldb_st6_x86_64, lldb_mm6_x86_64, LLDB_INVALID_REGNUM}; +uint32_t RegisterContextPOSIX_x86::g_invalidate_st7_64[] = { + lldb_st7_x86_64, lldb_mm7_x86_64, LLDB_INVALID_REGNUM}; + +// Number of register sets provided by this context. +enum { k_num_extended_register_sets = 1, k_num_register_sets = 3 }; + +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_lldb_regnums_i386}, + {"Advanced Vector Extensions", "avx", k_num_avx_registers_i386, + g_avx_regnums_i386}}; + +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_lldb_regnums_x86_64}, + {"Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64, + g_avx_regnums_x86_64}}; + +bool RegisterContextPOSIX_x86::IsGPR(unsigned reg) { + return reg <= GetRegInfo().last_gpr; // GPR's come first. +} + +bool RegisterContextPOSIX_x86::IsFPR(unsigned reg) { + return (GetRegInfo().first_fpr <= reg && reg <= GetRegInfo().last_fpr); +} + +bool RegisterContextPOSIX_x86::IsAVX(unsigned reg) { + return (GetRegInfo().first_ymm <= reg && reg <= GetRegInfo().last_ymm); +} + +bool RegisterContextPOSIX_x86::IsFPR(unsigned reg, FPRType fpr_type) { + bool generic_fpr = IsFPR(reg); + + if (fpr_type == eXSAVE) + return generic_fpr || IsAVX(reg); + return generic_fpr; +} + +RegisterContextPOSIX_x86::RegisterContextPOSIX_x86( + Thread &thread, uint32_t concrete_frame_idx, + RegisterInfoInterface *register_info) + : RegisterContext(thread, concrete_frame_idx) { + m_register_info_up.reset(register_info); + + ::memset(&m_fpr, 0, sizeof(FPR)); + ::memset(&m_ymm_set, 0, sizeof(YMM)); + + m_fpr_type = eNotValid; +} + +RegisterContextPOSIX_x86::~RegisterContextPOSIX_x86() = default; + +RegisterContextPOSIX_x86::FPRType RegisterContextPOSIX_x86::GetFPRType() { + if (m_fpr_type == eNotValid) { + // TODO: Use assembly to call cpuid on the inferior and query ebx or ecx + m_fpr_type = eXSAVE; // extended floating-point registers, if available + if (!ReadFPR()) + m_fpr_type = eFXSAVE; // assume generic floating-point registers + } + return m_fpr_type; +} + +void RegisterContextPOSIX_x86::Invalidate() {} + +void RegisterContextPOSIX_x86::InvalidateAllRegisters() {} + +unsigned RegisterContextPOSIX_x86::GetRegisterOffset(unsigned reg) { + assert(reg < GetRegInfo().num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_offset; +} + +RegInfo &RegisterContextPOSIX_x86::GetRegInfo() { + return GetRegInfoShared( + m_register_info_up->GetTargetArchitecture().GetMachine(), + /*with_base=*/false); +} + +unsigned RegisterContextPOSIX_x86::GetRegisterSize(unsigned reg) { + assert(reg < GetRegInfo().num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_size; +} + +size_t RegisterContextPOSIX_x86::GetRegisterCount() { + size_t num_registers = + GetRegInfo().num_gpr_registers + GetRegInfo().num_fpr_registers; + if (GetFPRType() == eXSAVE) + return num_registers + GetRegInfo().num_avx_registers; + return num_registers; +} + +size_t RegisterContextPOSIX_x86::GetGPRSize() { + return m_register_info_up->GetGPRSize(); +} + +size_t RegisterContextPOSIX_x86::GetFXSAVEOffset() { + return GetRegisterInfo()[GetRegInfo().first_fpr].byte_offset; +} + +const RegisterInfo *RegisterContextPOSIX_x86::GetRegisterInfo() { + // Commonly, this method is overridden and g_register_infos is copied and + // specialized. So, use GetRegisterInfo() rather than g_register_infos in + // this scope. + return m_register_info_up->GetRegisterInfo(); +} + +const RegisterInfo * +RegisterContextPOSIX_x86::GetRegisterInfoAtIndex(size_t reg) { + if (reg < GetRegInfo().num_registers) + return &GetRegisterInfo()[reg]; + else + return nullptr; +} + +size_t RegisterContextPOSIX_x86::GetRegisterSetCount() { + size_t sets = 0; + for (size_t set = 0; set < k_num_register_sets; ++set) { + if (IsRegisterSetAvailable(set)) + ++sets; + } + + return sets; +} + +const RegisterSet *RegisterContextPOSIX_x86::GetRegisterSet(size_t set) { + if (IsRegisterSetAvailable(set)) { + switch (m_register_info_up->GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return &g_reg_sets_i386[set]; + case llvm::Triple::x86_64: + return &g_reg_sets_x86_64[set]; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } + } + return nullptr; +} + +const char *RegisterContextPOSIX_x86::GetRegisterName(unsigned reg) { + assert(reg < GetRegInfo().num_registers && "Invalid register offset."); + return GetRegisterInfo()[reg].name; +} + +// Parse ymm registers and into xmm.bytes and ymmh.bytes. +bool RegisterContextPOSIX_x86::CopyYMMtoXSTATE(uint32_t reg, + lldb::ByteOrder byte_order) { + if (!IsAVX(reg)) + return false; + + if (byte_order == eByteOrderLittle) { + uint32_t reg_no = reg - GetRegInfo().first_ymm; + YMMToXState(m_ymm_set.ymm[reg_no], m_fpr.fxsave.xmm[reg_no].bytes, + m_fpr.xsave.ymmh[reg_no].bytes); + return true; + } + + return false; // unsupported or invalid byte order +} + +// Concatenate xmm.bytes with ymmh.bytes +bool RegisterContextPOSIX_x86::CopyXSTATEtoYMM(uint32_t reg, + lldb::ByteOrder byte_order) { + if (!IsAVX(reg)) + return false; + + if (byte_order == eByteOrderLittle) { + uint32_t reg_no = reg - GetRegInfo().first_ymm; + m_ymm_set.ymm[reg_no] = XStateToYMM(m_fpr.fxsave.xmm[reg_no].bytes, + m_fpr.xsave.ymmh[reg_no].bytes); + return true; + } + + return false; // unsupported or invalid byte order +} + +bool RegisterContextPOSIX_x86::IsRegisterSetAvailable(size_t set_index) { + // Note: Extended register sets are assumed to be at the end of g_reg_sets... + size_t num_sets = k_num_register_sets - k_num_extended_register_sets; + + if (GetFPRType() == eXSAVE) // ...and to start with AVX registers. + ++num_sets; + return (set_index < num_sets); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h new file mode 100644 index 000000000000..9284f5cc07d9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h @@ -0,0 +1,185 @@ +//===-- RegisterContextPOSIX_x86.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_X86_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_X86_H + +#include "RegisterContext_x86.h" +#include "RegisterInfoInterface.h" +#include "RegisterInfos_x86_64_with_base_shared.h" +#include "lldb-x86-register-enums.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Log.h" + +class RegisterContextPOSIX_x86 : public lldb_private::RegisterContext { +public: + RegisterContextPOSIX_x86(lldb_private::Thread &thread, + uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + + ~RegisterContextPOSIX_x86() override; + + void Invalidate(); + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + virtual size_t GetGPRSize(); + + virtual size_t GetFXSAVEOffset(); + + virtual unsigned GetRegisterSize(unsigned reg); + + virtual unsigned GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + + // Note: prefer kernel definitions over user-land + enum FPRType { + eNotValid = 0, + eFSAVE, // TODO + eFXSAVE, + eSOFT, // TODO + eXSAVE + }; + + static uint32_t g_contained_eax[]; + static uint32_t g_contained_ebx[]; + static uint32_t g_contained_ecx[]; + static uint32_t g_contained_edx[]; + static uint32_t g_contained_edi[]; + static uint32_t g_contained_esi[]; + static uint32_t g_contained_ebp[]; + static uint32_t g_contained_esp[]; + + static uint32_t g_invalidate_eax[]; + static uint32_t g_invalidate_ebx[]; + static uint32_t g_invalidate_ecx[]; + static uint32_t g_invalidate_edx[]; + static uint32_t g_invalidate_edi[]; + static uint32_t g_invalidate_esi[]; + static uint32_t g_invalidate_ebp[]; + static uint32_t g_invalidate_esp[]; + + static uint32_t g_contained_rax[]; + static uint32_t g_contained_rbx[]; + static uint32_t g_contained_rcx[]; + static uint32_t g_contained_rdx[]; + static uint32_t g_contained_rdi[]; + static uint32_t g_contained_rsi[]; + static uint32_t g_contained_rbp[]; + static uint32_t g_contained_rsp[]; + static uint32_t g_contained_r8[]; + static uint32_t g_contained_r9[]; + static uint32_t g_contained_r10[]; + static uint32_t g_contained_r11[]; + static uint32_t g_contained_r12[]; + static uint32_t g_contained_r13[]; + static uint32_t g_contained_r14[]; + static uint32_t g_contained_r15[]; + + static uint32_t g_invalidate_rax[]; + static uint32_t g_invalidate_rbx[]; + static uint32_t g_invalidate_rcx[]; + static uint32_t g_invalidate_rdx[]; + static uint32_t g_invalidate_rdi[]; + static uint32_t g_invalidate_rsi[]; + static uint32_t g_invalidate_rbp[]; + static uint32_t g_invalidate_rsp[]; + static uint32_t g_invalidate_r8[]; + static uint32_t g_invalidate_r9[]; + static uint32_t g_invalidate_r10[]; + static uint32_t g_invalidate_r11[]; + static uint32_t g_invalidate_r12[]; + static uint32_t g_invalidate_r13[]; + static uint32_t g_invalidate_r14[]; + static uint32_t g_invalidate_r15[]; + + static uint32_t g_contained_fip[]; + static uint32_t g_contained_fdp[]; + + static uint32_t g_invalidate_fip[]; + static uint32_t g_invalidate_fdp[]; + + static uint32_t g_contained_st0_32[]; + static uint32_t g_contained_st1_32[]; + static uint32_t g_contained_st2_32[]; + static uint32_t g_contained_st3_32[]; + static uint32_t g_contained_st4_32[]; + static uint32_t g_contained_st5_32[]; + static uint32_t g_contained_st6_32[]; + static uint32_t g_contained_st7_32[]; + + static uint32_t g_invalidate_st0_32[]; + static uint32_t g_invalidate_st1_32[]; + static uint32_t g_invalidate_st2_32[]; + static uint32_t g_invalidate_st3_32[]; + static uint32_t g_invalidate_st4_32[]; + static uint32_t g_invalidate_st5_32[]; + static uint32_t g_invalidate_st6_32[]; + static uint32_t g_invalidate_st7_32[]; + + static uint32_t g_contained_st0_64[]; + static uint32_t g_contained_st1_64[]; + static uint32_t g_contained_st2_64[]; + static uint32_t g_contained_st3_64[]; + static uint32_t g_contained_st4_64[]; + static uint32_t g_contained_st5_64[]; + static uint32_t g_contained_st6_64[]; + static uint32_t g_contained_st7_64[]; + + static uint32_t g_invalidate_st0_64[]; + static uint32_t g_invalidate_st1_64[]; + static uint32_t g_invalidate_st2_64[]; + static uint32_t g_invalidate_st3_64[]; + static uint32_t g_invalidate_st4_64[]; + static uint32_t g_invalidate_st5_64[]; + static uint32_t g_invalidate_st6_64[]; + static uint32_t g_invalidate_st7_64[]; + +protected: + FPRType + m_fpr_type; // determines the type of data stored by union FPR, if any. + lldb_private::FPR m_fpr; // floating-point registers including extended + // register sets. + lldb_private::YMM m_ymm_set; // copy of ymmh and xmm register halves. + std::unique_ptr<lldb_private::RegisterInfoInterface> + m_register_info_up; // Register Info Interface (FreeBSD or Linux) + + // Determines if an extended register set is supported on the processor + // running the inferior process. + virtual bool IsRegisterSetAvailable(size_t set_index); + + virtual const lldb_private::RegisterInfo *GetRegisterInfo(); + + bool IsGPR(unsigned reg); + + bool IsFPR(unsigned reg); + + bool IsAVX(unsigned reg); + + bool CopyXSTATEtoYMM(uint32_t reg, lldb::ByteOrder byte_order); + bool CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order); + bool IsFPR(unsigned reg, FPRType fpr_type); + FPRType GetFPRType(); + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; + virtual lldb_private::RegInfo &GetRegInfo(); +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTPOSIX_X86_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp new file mode 100644 index 000000000000..b5f2b0d2212d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp @@ -0,0 +1,216 @@ +//===-- RegisterContextThreadMemory.cpp -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/OperatingSystem.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Status.h" +#include "lldb/lldb-private.h" + +#include "RegisterContextThreadMemory.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextThreadMemory::RegisterContextThreadMemory( + Thread &thread, lldb::addr_t register_data_addr) + : RegisterContext(thread, 0), m_thread_wp(thread.shared_from_this()), + m_reg_ctx_sp(), m_register_data_addr(register_data_addr), m_stop_id(0) {} + +RegisterContextThreadMemory::~RegisterContextThreadMemory() = default; + +void RegisterContextThreadMemory::UpdateRegisterContext() { + ThreadSP thread_sp(m_thread_wp.lock()); + if (thread_sp) { + ProcessSP process_sp(thread_sp->GetProcess()); + + if (process_sp) { + const uint32_t stop_id = process_sp->GetModID().GetStopID(); + if (m_stop_id != stop_id) { + m_stop_id = stop_id; + m_reg_ctx_sp.reset(); + } + if (!m_reg_ctx_sp) { + ThreadSP backing_thread_sp(thread_sp->GetBackingThread()); + if (backing_thread_sp) { + m_reg_ctx_sp = backing_thread_sp->GetRegisterContext(); + } else { + OperatingSystem *os = process_sp->GetOperatingSystem(); + if (os->IsOperatingSystemPluginThread(thread_sp)) + m_reg_ctx_sp = os->CreateRegisterContextForThread( + thread_sp.get(), m_register_data_addr); + } + } + } else { + m_reg_ctx_sp.reset(); + } + } else { + m_reg_ctx_sp.reset(); + } +} + +// Subclasses must override these functions +void RegisterContextThreadMemory::InvalidateAllRegisters() { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + m_reg_ctx_sp->InvalidateAllRegisters(); +} + +size_t RegisterContextThreadMemory::GetRegisterCount() { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->GetRegisterCount(); + return 0; +} + +const RegisterInfo * +RegisterContextThreadMemory::GetRegisterInfoAtIndex(size_t reg) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->GetRegisterInfoAtIndex(reg); + return nullptr; +} + +size_t RegisterContextThreadMemory::GetRegisterSetCount() { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->GetRegisterSetCount(); + return 0; +} + +const RegisterSet *RegisterContextThreadMemory::GetRegisterSet(size_t reg_set) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->GetRegisterSet(reg_set); + return nullptr; +} + +bool RegisterContextThreadMemory::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ReadRegister(reg_info, reg_value); + return false; +} + +bool RegisterContextThreadMemory::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->WriteRegister(reg_info, reg_value); + return false; +} + +bool RegisterContextThreadMemory::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ReadAllRegisterValues(data_sp); + return false; +} + +bool RegisterContextThreadMemory::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->WriteAllRegisterValues(data_sp); + return false; +} + +bool RegisterContextThreadMemory::CopyFromRegisterContext( + lldb::RegisterContextSP reg_ctx_sp) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->CopyFromRegisterContext(reg_ctx_sp); + return false; +} + +uint32_t RegisterContextThreadMemory::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ConvertRegisterKindToRegisterNumber(kind, num); + return false; +} + +uint32_t RegisterContextThreadMemory::NumSupportedHardwareBreakpoints() { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->NumSupportedHardwareBreakpoints(); + return false; +} + +uint32_t RegisterContextThreadMemory::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->SetHardwareBreakpoint(addr, size); + return 0; +} + +bool RegisterContextThreadMemory::ClearHardwareBreakpoint(uint32_t hw_idx) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ClearHardwareBreakpoint(hw_idx); + return false; +} + +uint32_t RegisterContextThreadMemory::NumSupportedHardwareWatchpoints() { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->NumSupportedHardwareWatchpoints(); + return 0; +} + +uint32_t RegisterContextThreadMemory::SetHardwareWatchpoint(lldb::addr_t addr, + size_t size, + bool read, + bool write) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->SetHardwareWatchpoint(addr, size, read, write); + return 0; +} + +bool RegisterContextThreadMemory::ClearHardwareWatchpoint(uint32_t hw_index) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ClearHardwareWatchpoint(hw_index); + return false; +} + +bool RegisterContextThreadMemory::HardwareSingleStep(bool enable) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->HardwareSingleStep(enable); + return false; +} + +Status RegisterContextThreadMemory::ReadRegisterValueFromMemory( + const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr, + uint32_t src_len, RegisterValue ®_value) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ReadRegisterValueFromMemory(reg_info, src_addr, + src_len, reg_value); + Status error; + error.SetErrorString("invalid register context"); + return error; +} + +Status RegisterContextThreadMemory::WriteRegisterValueToMemory( + const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr, + uint32_t dst_len, const RegisterValue ®_value) { + UpdateRegisterContext(); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->WriteRegisterValueToMemory(reg_info, dst_addr, dst_len, + reg_value); + Status error; + error.SetErrorString("invalid register context"); + return error; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h new file mode 100644 index 000000000000..0a7314528f0a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextThreadMemory.h @@ -0,0 +1,102 @@ +//===-- RegisterContextThreadMemory.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTTHREADMEMORY_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTTHREADMEMORY_H + +#include <vector> + +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class RegisterContextThreadMemory : public lldb_private::RegisterContext { +public: + RegisterContextThreadMemory(Thread &thread, lldb::addr_t register_data_addr); + + ~RegisterContextThreadMemory() override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + // These two functions are used to implement "push" and "pop" of register + // states. They are used primarily + // for expression evaluation, where we need to push a new state (storing the + // old one in data_sp) and then + // restoring the original state by passing the data_sp we got from + // ReadAllRegisters to WriteAllRegisterValues. + // ReadAllRegisters will do what is necessary to return a coherent set of + // register values for this thread, which + // may mean e.g. interrupting a thread that is sitting in a kernel trap. That + // is a somewhat disruptive operation, + // so these API's should only be used when this behavior is needed. + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + bool CopyFromRegisterContext(lldb::RegisterContextSP context); + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + uint32_t NumSupportedHardwareBreakpoints() override; + + uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + bool ClearHardwareBreakpoint(uint32_t hw_idx) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, + bool write) override; + + bool ClearHardwareWatchpoint(uint32_t hw_index) override; + + bool HardwareSingleStep(bool enable) override; + + Status ReadRegisterValueFromMemory(const lldb_private::RegisterInfo *reg_info, + lldb::addr_t src_addr, uint32_t src_len, + RegisterValue ®_value) override; + + Status WriteRegisterValueToMemory(const lldb_private::RegisterInfo *reg_info, + lldb::addr_t dst_addr, uint32_t dst_len, + const RegisterValue ®_value) override; + +protected: + void UpdateRegisterContext(); + + lldb::ThreadWP m_thread_wp; + lldb::RegisterContextSP m_reg_ctx_sp; + lldb::addr_t m_register_data_addr; + uint32_t m_stop_id; + +private: + RegisterContextThreadMemory(const RegisterContextThreadMemory &) = delete; + const RegisterContextThreadMemory & + operator=(const RegisterContextThreadMemory &) = delete; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTTHREADMEMORY_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.cpp new file mode 100644 index 000000000000..faf4021aa499 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.cpp @@ -0,0 +1,89 @@ +//===-- RegisterContextWindows_i386.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 "RegisterContextWindows_i386.h" +#include "RegisterContext_x86.h" +#include "lldb-x86-register-enums.h" + +using namespace lldb_private; +using namespace lldb; + +namespace { +// Declare our g_register_infos structure. +typedef struct _GPR { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t eip; + uint32_t eflags; + uint32_t cs; + uint32_t fs; + uint32_t gs; + uint32_t ss; + uint32_t ds; + uint32_t es; +} GPR; + +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) + +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ +#reg, alt, sizeof(((GPR *)nullptr)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, \ + {kind1, kind2, kind3, kind4, lldb_##reg##_i386 }, nullptr, nullptr, \ + nullptr, \ + } + +// clang-format off +static RegisterInfo g_register_infos_i386[] = { +// General purpose registers EH_Frame DWARF Generic Process Plugin +// =========================== ================== ================ ========================= ==================== + DEFINE_GPR(eax, nullptr, ehframe_eax_i386, dwarf_eax_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebx, nullptr, ehframe_ebx_i386, dwarf_ebx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ecx, nullptr, ehframe_ecx_i386, dwarf_ecx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edx, nullptr, ehframe_edx_i386, dwarf_edx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edi, nullptr, ehframe_edi_i386, dwarf_edi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(esi, nullptr, ehframe_esi_i386, dwarf_esi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebp, "fp", ehframe_ebp_i386, dwarf_ebp_i386, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(esp, "sp", ehframe_esp_i386, dwarf_esp_i386, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(eip, "pc", ehframe_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(eflags, "flags", ehframe_eflags_i386, dwarf_eflags_i386, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, LLDB_INVALID_REGNUM, dwarf_cs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, LLDB_INVALID_REGNUM, dwarf_fs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, LLDB_INVALID_REGNUM, dwarf_gs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, LLDB_INVALID_REGNUM, dwarf_ss_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, LLDB_INVALID_REGNUM, dwarf_ds_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, LLDB_INVALID_REGNUM, dwarf_es_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), +}; +// clang-format on +} // namespace + +RegisterContextWindows_i386::RegisterContextWindows_i386( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch) { + assert(target_arch.GetMachine() == llvm::Triple::x86); +} + +const RegisterInfo *RegisterContextWindows_i386::GetRegisterInfo() const { + return g_register_infos_i386; +} + +uint32_t RegisterContextWindows_i386::GetRegisterCount() const { + return std::size(g_register_infos_i386); +} + +uint32_t RegisterContextWindows_i386::GetUserRegisterCount() const { + return std::size(g_register_infos_i386); +} + +size_t RegisterContextWindows_i386::GetGPRSize() const { return sizeof(GPR); } diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.h new file mode 100644 index 000000000000..6a5d3524300d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.h @@ -0,0 +1,27 @@ +//===-- RegisterContextWindows_i386.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTWINDOWS_I386_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTWINDOWS_I386_H + +#include "RegisterInfoInterface.h" + +class RegisterContextWindows_i386 : public lldb_private::RegisterInfoInterface { +public: + RegisterContextWindows_i386(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + uint32_t GetUserRegisterCount() const override; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.cpp new file mode 100644 index 000000000000..c3fc2e0026bc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.cpp @@ -0,0 +1,152 @@ +//===-- RegisterContextWindows_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextWindows_x86_64.h" +#include "RegisterContext_x86.h" +#include "lldb-x86-register-enums.h" + +#include <vector> + +using namespace lldb_private; +using namespace lldb; + +namespace { +typedef struct _GPR { + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t rsp; + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint16_t cs; + uint16_t fs; + uint16_t gs; + uint16_t ss; + uint16_t ds; + uint16_t es; +} GPR; + +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ +#reg, alt, sizeof(((GPR *)nullptr)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, \ + {kind1, kind2, kind3, kind4, lldb_##reg##_x86_64 }, nullptr, nullptr, \ + nullptr, \ + } + +typedef struct _FPReg { + XMMReg xmm0; + XMMReg xmm1; + XMMReg xmm2; + XMMReg xmm3; + XMMReg xmm4; + XMMReg xmm5; + XMMReg xmm6; + XMMReg xmm7; + XMMReg xmm8; + XMMReg xmm9; + XMMReg xmm10; + XMMReg xmm11; + XMMReg xmm12; + XMMReg xmm13; + XMMReg xmm14; + XMMReg xmm15; +} FPReg; + +#define FPR_OFFSET(regname) \ + (sizeof(GPR) + LLVM_EXTENSION offsetof(FPReg, regname)) + +#define DEFINE_XMM(reg) \ + { \ +#reg, NULL, sizeof(((FPReg *)nullptr)->reg), FPR_OFFSET(reg), \ + eEncodingUint, eFormatVectorOfUInt64, \ + {dwarf_##reg##_x86_64, dwarf_##reg##_x86_64, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_##reg##_x86_64 }, \ + nullptr, nullptr, nullptr, \ + } + +// clang-format off +static RegisterInfo g_register_infos_x86_64[] = { +// General purpose registers EH_Frame DWARF Generic Process Plugin +// =========================== ================== ================ ========================= ==================== + DEFINE_GPR(rax, nullptr, dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbx, nullptr, dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rcx, nullptr, dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdx, nullptr, dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdi, nullptr, dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsi, nullptr, dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbp, nullptr, dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsp, nullptr, dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(r8, nullptr, dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, nullptr, dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, nullptr, dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, nullptr, dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r12, nullptr, dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, nullptr, dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, nullptr, dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, nullptr, dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rip, nullptr, dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(rflags, nullptr, dwarf_rflags_x86_64, dwarf_rflags_x86_64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, dwarf_cs_x86_64, dwarf_cs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, dwarf_fs_x86_64, dwarf_fs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, dwarf_gs_x86_64, dwarf_gs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, dwarf_ss_x86_64, dwarf_ss_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, dwarf_ds_x86_64, dwarf_ds_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, dwarf_es_x86_64, dwarf_es_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_XMM(xmm0), + DEFINE_XMM(xmm1), + DEFINE_XMM(xmm2), + DEFINE_XMM(xmm3), + DEFINE_XMM(xmm4), + DEFINE_XMM(xmm5), + DEFINE_XMM(xmm6), + DEFINE_XMM(xmm7), + DEFINE_XMM(xmm8), + DEFINE_XMM(xmm9), + DEFINE_XMM(xmm10), + DEFINE_XMM(xmm11), + DEFINE_XMM(xmm12), + DEFINE_XMM(xmm13), + DEFINE_XMM(xmm14), + DEFINE_XMM(xmm15) +}; +// clang-format on +} // namespace + +RegisterContextWindows_x86_64::RegisterContextWindows_x86_64( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch) { + assert(target_arch.GetMachine() == llvm::Triple::x86_64); +} + +const RegisterInfo *RegisterContextWindows_x86_64::GetRegisterInfo() const { + return g_register_infos_x86_64; +} + +uint32_t RegisterContextWindows_x86_64::GetRegisterCount() const { + return std::size(g_register_infos_x86_64); +} + +uint32_t RegisterContextWindows_x86_64::GetUserRegisterCount() const { + return std::size(g_register_infos_x86_64); +} + +size_t RegisterContextWindows_x86_64::GetGPRSize() const { return sizeof(GPR); } diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.h new file mode 100644 index 000000000000..c29acf284841 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.h @@ -0,0 +1,28 @@ +//===-- RegisterContextWindows_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTWINDOWS_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXTWINDOWS_X86_64_H + +#include "RegisterInfoInterface.h" + +class RegisterContextWindows_x86_64 + : public lldb_private::RegisterInfoInterface { +public: + RegisterContextWindows_x86_64(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + uint32_t GetUserRegisterCount() const override; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h new file mode 100644 index 000000000000..15081f974c66 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_mips.h @@ -0,0 +1,374 @@ +//===-- RegisterContext_mips.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_MIPS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_MIPS_H + +#include <cstddef> +#include <cstdint> + +// eh_frame and DWARF Register numbers (eRegisterKindEHFrame & +// eRegisterKindDWARF) + +enum { + // GP Registers + dwarf_zero_mips = 0, + dwarf_r1_mips, + dwarf_r2_mips, + dwarf_r3_mips, + dwarf_r4_mips, + dwarf_r5_mips, + dwarf_r6_mips, + dwarf_r7_mips, + dwarf_r8_mips, + dwarf_r9_mips, + dwarf_r10_mips, + dwarf_r11_mips, + dwarf_r12_mips, + dwarf_r13_mips, + dwarf_r14_mips, + dwarf_r15_mips, + dwarf_r16_mips, + dwarf_r17_mips, + dwarf_r18_mips, + dwarf_r19_mips, + dwarf_r20_mips, + dwarf_r21_mips, + dwarf_r22_mips, + dwarf_r23_mips, + dwarf_r24_mips, + dwarf_r25_mips, + dwarf_r26_mips, + dwarf_r27_mips, + dwarf_gp_mips, + dwarf_sp_mips, + dwarf_r30_mips, + dwarf_ra_mips, + dwarf_sr_mips, + dwarf_lo_mips, + dwarf_hi_mips, + dwarf_bad_mips, + dwarf_cause_mips, + dwarf_pc_mips, + dwarf_f0_mips, + dwarf_f1_mips, + dwarf_f2_mips, + dwarf_f3_mips, + dwarf_f4_mips, + dwarf_f5_mips, + dwarf_f6_mips, + dwarf_f7_mips, + dwarf_f8_mips, + dwarf_f9_mips, + dwarf_f10_mips, + dwarf_f11_mips, + dwarf_f12_mips, + dwarf_f13_mips, + dwarf_f14_mips, + dwarf_f15_mips, + dwarf_f16_mips, + dwarf_f17_mips, + dwarf_f18_mips, + dwarf_f19_mips, + dwarf_f20_mips, + dwarf_f21_mips, + dwarf_f22_mips, + dwarf_f23_mips, + dwarf_f24_mips, + dwarf_f25_mips, + dwarf_f26_mips, + dwarf_f27_mips, + dwarf_f28_mips, + dwarf_f29_mips, + dwarf_f30_mips, + dwarf_f31_mips, + dwarf_fcsr_mips, + dwarf_fir_mips, + dwarf_w0_mips, + dwarf_w1_mips, + dwarf_w2_mips, + dwarf_w3_mips, + dwarf_w4_mips, + dwarf_w5_mips, + dwarf_w6_mips, + dwarf_w7_mips, + dwarf_w8_mips, + dwarf_w9_mips, + dwarf_w10_mips, + dwarf_w11_mips, + dwarf_w12_mips, + dwarf_w13_mips, + dwarf_w14_mips, + dwarf_w15_mips, + dwarf_w16_mips, + dwarf_w17_mips, + dwarf_w18_mips, + dwarf_w19_mips, + dwarf_w20_mips, + dwarf_w21_mips, + dwarf_w22_mips, + dwarf_w23_mips, + dwarf_w24_mips, + dwarf_w25_mips, + dwarf_w26_mips, + dwarf_w27_mips, + dwarf_w28_mips, + dwarf_w29_mips, + dwarf_w30_mips, + dwarf_w31_mips, + dwarf_mcsr_mips, + dwarf_mir_mips, + dwarf_config5_mips, + dwarf_ic_mips, + dwarf_dummy_mips +}; + +enum { + dwarf_zero_mips64 = 0, + dwarf_r1_mips64, + dwarf_r2_mips64, + dwarf_r3_mips64, + dwarf_r4_mips64, + dwarf_r5_mips64, + dwarf_r6_mips64, + dwarf_r7_mips64, + dwarf_r8_mips64, + dwarf_r9_mips64, + dwarf_r10_mips64, + dwarf_r11_mips64, + dwarf_r12_mips64, + dwarf_r13_mips64, + dwarf_r14_mips64, + dwarf_r15_mips64, + dwarf_r16_mips64, + dwarf_r17_mips64, + dwarf_r18_mips64, + dwarf_r19_mips64, + dwarf_r20_mips64, + dwarf_r21_mips64, + dwarf_r22_mips64, + dwarf_r23_mips64, + dwarf_r24_mips64, + dwarf_r25_mips64, + dwarf_r26_mips64, + dwarf_r27_mips64, + dwarf_gp_mips64, + dwarf_sp_mips64, + dwarf_r30_mips64, + dwarf_ra_mips64, + dwarf_sr_mips64, + dwarf_lo_mips64, + dwarf_hi_mips64, + dwarf_bad_mips64, + dwarf_cause_mips64, + dwarf_pc_mips64, + dwarf_f0_mips64, + dwarf_f1_mips64, + dwarf_f2_mips64, + dwarf_f3_mips64, + dwarf_f4_mips64, + dwarf_f5_mips64, + dwarf_f6_mips64, + dwarf_f7_mips64, + dwarf_f8_mips64, + dwarf_f9_mips64, + dwarf_f10_mips64, + dwarf_f11_mips64, + dwarf_f12_mips64, + dwarf_f13_mips64, + dwarf_f14_mips64, + dwarf_f15_mips64, + dwarf_f16_mips64, + dwarf_f17_mips64, + dwarf_f18_mips64, + dwarf_f19_mips64, + dwarf_f20_mips64, + dwarf_f21_mips64, + dwarf_f22_mips64, + dwarf_f23_mips64, + dwarf_f24_mips64, + dwarf_f25_mips64, + dwarf_f26_mips64, + dwarf_f27_mips64, + dwarf_f28_mips64, + dwarf_f29_mips64, + dwarf_f30_mips64, + dwarf_f31_mips64, + dwarf_fcsr_mips64, + dwarf_fir_mips64, + dwarf_ic_mips64, + dwarf_dummy_mips64, + dwarf_w0_mips64, + dwarf_w1_mips64, + dwarf_w2_mips64, + dwarf_w3_mips64, + dwarf_w4_mips64, + dwarf_w5_mips64, + dwarf_w6_mips64, + dwarf_w7_mips64, + dwarf_w8_mips64, + dwarf_w9_mips64, + dwarf_w10_mips64, + dwarf_w11_mips64, + dwarf_w12_mips64, + dwarf_w13_mips64, + dwarf_w14_mips64, + dwarf_w15_mips64, + dwarf_w16_mips64, + dwarf_w17_mips64, + dwarf_w18_mips64, + dwarf_w19_mips64, + dwarf_w20_mips64, + dwarf_w21_mips64, + dwarf_w22_mips64, + dwarf_w23_mips64, + dwarf_w24_mips64, + dwarf_w25_mips64, + dwarf_w26_mips64, + dwarf_w27_mips64, + dwarf_w28_mips64, + dwarf_w29_mips64, + dwarf_w30_mips64, + dwarf_w31_mips64, + dwarf_mcsr_mips64, + dwarf_mir_mips64, + dwarf_config5_mips64, +}; + +// GP registers +struct GPR_linux_mips { + uint64_t zero; + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t gp; + uint64_t sp; + uint64_t r30; + uint64_t ra; + uint64_t mullo; + uint64_t mulhi; + uint64_t pc; + uint64_t badvaddr; + uint64_t sr; + uint64_t cause; + uint64_t config5; +}; + +struct FPR_linux_mips { + uint64_t f0; + uint64_t f1; + uint64_t f2; + uint64_t f3; + uint64_t f4; + uint64_t f5; + uint64_t f6; + uint64_t f7; + uint64_t f8; + uint64_t f9; + uint64_t f10; + uint64_t f11; + uint64_t f12; + uint64_t f13; + uint64_t f14; + uint64_t f15; + uint64_t f16; + uint64_t f17; + uint64_t f18; + uint64_t f19; + uint64_t f20; + uint64_t f21; + uint64_t f22; + uint64_t f23; + uint64_t f24; + uint64_t f25; + uint64_t f26; + uint64_t f27; + uint64_t f28; + uint64_t f29; + uint64_t f30; + uint64_t f31; + uint32_t fcsr; + uint32_t fir; + uint32_t config5; +}; + +struct MSAReg { + uint8_t byte[16]; +}; + +struct MSA_linux_mips { + MSAReg w0; + MSAReg w1; + MSAReg w2; + MSAReg w3; + MSAReg w4; + MSAReg w5; + MSAReg w6; + MSAReg w7; + MSAReg w8; + MSAReg w9; + MSAReg w10; + MSAReg w11; + MSAReg w12; + MSAReg w13; + MSAReg w14; + MSAReg w15; + MSAReg w16; + MSAReg w17; + MSAReg w18; + MSAReg w19; + MSAReg w20; + MSAReg w21; + MSAReg w22; + MSAReg w23; + MSAReg w24; + MSAReg w25; + MSAReg w26; + MSAReg w27; + MSAReg w28; + MSAReg w29; + MSAReg w30; + MSAReg w31; + uint32_t fcsr; /* FPU control status register */ + uint32_t fir; /* FPU implementaion revision */ + uint32_t mcsr; /* MSA control status register */ + uint32_t mir; /* MSA implementation revision */ + uint32_t config5; /* Config5 register */ +}; + +struct UserArea { + GPR_linux_mips gpr; // General purpose registers. + FPR_linux_mips fpr; // Floating point registers. + MSA_linux_mips msa; // MSA registers. +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_MIPS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h new file mode 100644 index 000000000000..7407e2f402e0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_powerpc.h @@ -0,0 +1,123 @@ +//===-- RegisterContext_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_POWERPC_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_POWERPC_H + +// eh_frame and DWARF Register numbers (eRegisterKindEHFrame & +// eRegisterKindDWARF) +enum { + dwarf_r0_powerpc = 0, + dwarf_r1_powerpc, + dwarf_r2_powerpc, + dwarf_r3_powerpc, + dwarf_r4_powerpc, + dwarf_r5_powerpc, + dwarf_r6_powerpc, + dwarf_r7_powerpc, + dwarf_r8_powerpc, + dwarf_r9_powerpc, + dwarf_r10_powerpc, + dwarf_r11_powerpc, + dwarf_r12_powerpc, + dwarf_r13_powerpc, + dwarf_r14_powerpc, + dwarf_r15_powerpc, + dwarf_r16_powerpc, + dwarf_r17_powerpc, + dwarf_r18_powerpc, + dwarf_r19_powerpc, + dwarf_r20_powerpc, + dwarf_r21_powerpc, + dwarf_r22_powerpc, + dwarf_r23_powerpc, + dwarf_r24_powerpc, + dwarf_r25_powerpc, + dwarf_r26_powerpc, + dwarf_r27_powerpc, + dwarf_r28_powerpc, + dwarf_r29_powerpc, + dwarf_r30_powerpc, + dwarf_r31_powerpc, + dwarf_f0_powerpc, + dwarf_f1_powerpc, + dwarf_f2_powerpc, + dwarf_f3_powerpc, + dwarf_f4_powerpc, + dwarf_f5_powerpc, + dwarf_f6_powerpc, + dwarf_f7_powerpc, + dwarf_f8_powerpc, + dwarf_f9_powerpc, + dwarf_f10_powerpc, + dwarf_f11_powerpc, + dwarf_f12_powerpc, + dwarf_f13_powerpc, + dwarf_f14_powerpc, + dwarf_f15_powerpc, + dwarf_f16_powerpc, + dwarf_f17_powerpc, + dwarf_f18_powerpc, + dwarf_f19_powerpc, + dwarf_f20_powerpc, + dwarf_f21_powerpc, + dwarf_f22_powerpc, + dwarf_f23_powerpc, + dwarf_f24_powerpc, + dwarf_f25_powerpc, + dwarf_f26_powerpc, + dwarf_f27_powerpc, + dwarf_f28_powerpc, + dwarf_f29_powerpc, + dwarf_f30_powerpc, + dwarf_f31_powerpc, + dwarf_cr_powerpc, + dwarf_fpscr_powerpc, + dwarf_msr_powerpc, + dwarf_vscr_powerpc, + dwarf_xer_powerpc = 101, + dwarf_lr_powerpc = 108, + dwarf_ctr_powerpc, + dwarf_pc_powerpc, + dwarf_vrsave_powerpc = 356, + dwarf_v0_powerpc = 1124, + dwarf_v1_powerpc, + dwarf_v2_powerpc, + dwarf_v3_powerpc, + dwarf_v4_powerpc, + dwarf_v5_powerpc, + dwarf_v6_powerpc, + dwarf_v7_powerpc, + dwarf_v8_powerpc, + dwarf_v9_powerpc, + dwarf_v10_powerpc, + dwarf_v11_powerpc, + dwarf_v12_powerpc, + dwarf_v13_powerpc, + dwarf_v14_powerpc, + dwarf_v15_powerpc, + dwarf_v16_powerpc, + dwarf_v17_powerpc, + dwarf_v18_powerpc, + dwarf_v19_powerpc, + dwarf_v20_powerpc, + dwarf_v21_powerpc, + dwarf_v22_powerpc, + dwarf_v23_powerpc, + dwarf_v24_powerpc, + dwarf_v25_powerpc, + dwarf_v26_powerpc, + dwarf_v27_powerpc, + dwarf_v28_powerpc, + dwarf_v29_powerpc, + dwarf_v30_powerpc, + dwarf_v31_powerpc, +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_POWERPC_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h new file mode 100644 index 000000000000..248b3bd0beac --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_s390x.h @@ -0,0 +1,90 @@ +//===-- RegisterContext_s390x.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_S390X_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_S390X_H + +// SystemZ ehframe, dwarf regnums + +// EHFrame and DWARF Register numbers (eRegisterKindEHFrame & +// eRegisterKindDWARF) +enum { + // General Purpose Registers + dwarf_r0_s390x = 0, + dwarf_r1_s390x, + dwarf_r2_s390x, + dwarf_r3_s390x, + dwarf_r4_s390x, + dwarf_r5_s390x, + dwarf_r6_s390x, + dwarf_r7_s390x, + dwarf_r8_s390x, + dwarf_r9_s390x, + dwarf_r10_s390x, + dwarf_r11_s390x, + dwarf_r12_s390x, + dwarf_r13_s390x, + dwarf_r14_s390x, + dwarf_r15_s390x, + // Floating Point Registers / Vector Registers 0-15 + dwarf_f0_s390x = 16, + dwarf_f2_s390x, + dwarf_f4_s390x, + dwarf_f6_s390x, + dwarf_f1_s390x, + dwarf_f3_s390x, + dwarf_f5_s390x, + dwarf_f7_s390x, + dwarf_f8_s390x, + dwarf_f10_s390x, + dwarf_f12_s390x, + dwarf_f14_s390x, + dwarf_f9_s390x, + dwarf_f11_s390x, + dwarf_f13_s390x, + dwarf_f15_s390x, + // Access Registers + dwarf_acr0_s390x = 48, + dwarf_acr1_s390x, + dwarf_acr2_s390x, + dwarf_acr3_s390x, + dwarf_acr4_s390x, + dwarf_acr5_s390x, + dwarf_acr6_s390x, + dwarf_acr7_s390x, + dwarf_acr8_s390x, + dwarf_acr9_s390x, + dwarf_acr10_s390x, + dwarf_acr11_s390x, + dwarf_acr12_s390x, + dwarf_acr13_s390x, + dwarf_acr14_s390x, + dwarf_acr15_s390x, + // Program Status Word + dwarf_pswm_s390x = 64, + dwarf_pswa_s390x, + // Vector Registers 16-31 + dwarf_v16_s390x = 68, + dwarf_v18_s390x, + dwarf_v20_s390x, + dwarf_v22_s390x, + dwarf_v17_s390x, + dwarf_v19_s390x, + dwarf_v21_s390x, + dwarf_v23_s390x, + dwarf_v24_s390x, + dwarf_v26_s390x, + dwarf_v28_s390x, + dwarf_v30_s390x, + dwarf_v25_s390x, + dwarf_v27_s390x, + dwarf_v29_s390x, + dwarf_v31_s390x, +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.cpp new file mode 100644 index 000000000000..b21c72bd9621 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.cpp @@ -0,0 +1,58 @@ +//===-- RegisterContext_x86.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContext_x86.h" + +using namespace lldb_private; + +// Convert the 8-bit abridged FPU Tag Word (as found in FXSAVE) to the full +// 16-bit FPU Tag Word (as found in FSAVE, and used by gdb protocol). This +// requires knowing the values of the ST(i) registers and the FPU Status Word. +uint16_t lldb_private::AbridgedToFullTagWord(uint8_t abridged_tw, uint16_t sw, + llvm::ArrayRef<MMSReg> st_regs) { + // Tag word is using internal FPU register numbering rather than ST(i). + // Mapping to ST(i): i = FPU regno - TOP (Status Word, bits 11:13). + // Here we start with FPU reg 7 and go down. + int st = 7 - ((sw >> 11) & 7); + uint16_t tw = 0; + for (uint8_t mask = 0x80; mask != 0; mask >>= 1) { + tw <<= 2; + if (abridged_tw & mask) { + // The register is non-empty, so we need to check the value of ST(i). + uint16_t exp = + st_regs[st].comp.sign_exp & 0x7fff; // Discard the sign bit. + if (exp == 0) { + if (st_regs[st].comp.mantissa == 0) + tw |= 1; // Zero + else + tw |= 2; // Denormal + } else if (exp == 0x7fff) + tw |= 2; // Infinity or NaN + // 0 if normal number + } else + tw |= 3; // Empty register + + // Rotate ST down. + st = (st - 1) & 7; + } + + return tw; +} + +// Convert the 16-bit FPU Tag Word to the abridged 8-bit value, to be written +// into FXSAVE. +uint8_t lldb_private::FullToAbridgedTagWord(uint16_t tw) { + uint8_t abridged_tw = 0; + for (uint16_t mask = 0xc000; mask != 0; mask >>= 2) { + abridged_tw <<= 1; + // full TW uses 11 for empty registers, aTW uses 0 + if ((tw & mask) != mask) + abridged_tw |= 1; + } + return abridged_tw; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h new file mode 100644 index 000000000000..e24da37b7261 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterContext_x86.h @@ -0,0 +1,395 @@ +//===-- RegisterContext_x86.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_X86_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERCONTEXT_X86_H + +#include <cstddef> +#include <cstdint> + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/Support/Compiler.h" + +namespace lldb_private { +// i386 ehframe, dwarf regnums + +// Register numbers seen in eh_frame (eRegisterKindEHFrame) on i386 systems +// (non-Darwin) +// +enum { + ehframe_eax_i386 = 0, + ehframe_ecx_i386, + ehframe_edx_i386, + ehframe_ebx_i386, + + // on Darwin esp & ebp are reversed in the eh_frame section for i386 (versus + // dwarf's reg numbering). + // To be specific: + // i386+darwin eh_frame: 4 is ebp, 5 is esp + // i386+everyone else eh_frame: 4 is esp, 5 is ebp + // i386 dwarf: 4 is esp, 5 is ebp + // lldb will get the darwin-specific eh_frame reg numberings from debugserver, + // or the ABI, so we + // only encode the generally correct 4 == esp, 5 == ebp numbers in this + // generic header. + + ehframe_esp_i386, + ehframe_ebp_i386, + ehframe_esi_i386, + ehframe_edi_i386, + ehframe_eip_i386, + ehframe_eflags_i386, + ehframe_st0_i386 = 12, + ehframe_st1_i386, + ehframe_st2_i386, + ehframe_st3_i386, + ehframe_st4_i386, + ehframe_st5_i386, + ehframe_st6_i386, + ehframe_st7_i386, + ehframe_xmm0_i386 = 21, + ehframe_xmm1_i386, + ehframe_xmm2_i386, + ehframe_xmm3_i386, + ehframe_xmm4_i386, + ehframe_xmm5_i386, + ehframe_xmm6_i386, + ehframe_xmm7_i386, + ehframe_mm0_i386 = 29, + ehframe_mm1_i386, + ehframe_mm2_i386, + ehframe_mm3_i386, + ehframe_mm4_i386, + ehframe_mm5_i386, + ehframe_mm6_i386, + ehframe_mm7_i386, +}; + +// DWARF register numbers (eRegisterKindDWARF) +// Intel's x86 or IA-32 +enum { + // General Purpose Registers. + dwarf_eax_i386 = 0, + dwarf_ecx_i386, + dwarf_edx_i386, + dwarf_ebx_i386, + dwarf_esp_i386, + dwarf_ebp_i386, + dwarf_esi_i386, + dwarf_edi_i386, + dwarf_eip_i386, + dwarf_eflags_i386, + // Floating Point Registers + dwarf_st0_i386 = 11, + dwarf_st1_i386, + dwarf_st2_i386, + dwarf_st3_i386, + dwarf_st4_i386, + dwarf_st5_i386, + dwarf_st6_i386, + dwarf_st7_i386, + // SSE Registers + dwarf_xmm0_i386 = 21, + dwarf_xmm1_i386, + dwarf_xmm2_i386, + dwarf_xmm3_i386, + dwarf_xmm4_i386, + dwarf_xmm5_i386, + dwarf_xmm6_i386, + dwarf_xmm7_i386, + // MMX Registers + dwarf_mm0_i386 = 29, + dwarf_mm1_i386, + dwarf_mm2_i386, + dwarf_mm3_i386, + dwarf_mm4_i386, + dwarf_mm5_i386, + dwarf_mm6_i386, + dwarf_mm7_i386, + dwarf_fctrl_i386 = 37, // x87 control word + dwarf_fstat_i386 = 38, // x87 status word + dwarf_mxcsr_i386 = 39, + dwarf_es_i386 = 40, + dwarf_cs_i386 = 41, + dwarf_ss_i386 = 42, + dwarf_ds_i386 = 43, + dwarf_fs_i386 = 44, + dwarf_gs_i386 = 45, + + // I believe the ymm registers use the dwarf_xmm%_i386 register numbers and + // then differentiate based on size of the register. + dwarf_bnd0_i386 = 101, + dwarf_bnd1_i386, + dwarf_bnd2_i386, + dwarf_bnd3_i386, +}; + +// AMD x86_64, AMD64, Intel EM64T, or Intel 64 ehframe, dwarf regnums + +// EHFrame and DWARF Register numbers (eRegisterKindEHFrame & +// eRegisterKindDWARF) +// This is the spec I used (as opposed to x86-64-abi-0.99.pdf): +// http://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf +enum { + // GP Registers + dwarf_rax_x86_64 = 0, + dwarf_rdx_x86_64, + dwarf_rcx_x86_64, + dwarf_rbx_x86_64, + dwarf_rsi_x86_64, + dwarf_rdi_x86_64, + dwarf_rbp_x86_64, + dwarf_rsp_x86_64, + // Extended GP Registers + dwarf_r8_x86_64 = 8, + dwarf_r9_x86_64, + dwarf_r10_x86_64, + dwarf_r11_x86_64, + dwarf_r12_x86_64, + dwarf_r13_x86_64, + dwarf_r14_x86_64, + dwarf_r15_x86_64, + // Return Address (RA) mapped to RIP + dwarf_rip_x86_64 = 16, + // SSE Vector Registers + dwarf_xmm0_x86_64 = 17, + dwarf_xmm1_x86_64, + dwarf_xmm2_x86_64, + dwarf_xmm3_x86_64, + dwarf_xmm4_x86_64, + dwarf_xmm5_x86_64, + dwarf_xmm6_x86_64, + dwarf_xmm7_x86_64, + dwarf_xmm8_x86_64, + dwarf_xmm9_x86_64, + dwarf_xmm10_x86_64, + dwarf_xmm11_x86_64, + dwarf_xmm12_x86_64, + dwarf_xmm13_x86_64, + dwarf_xmm14_x86_64, + dwarf_xmm15_x86_64, + // Floating Point Registers + dwarf_st0_x86_64 = 33, + dwarf_st1_x86_64, + dwarf_st2_x86_64, + dwarf_st3_x86_64, + dwarf_st4_x86_64, + dwarf_st5_x86_64, + dwarf_st6_x86_64, + dwarf_st7_x86_64, + // MMX Registers + dwarf_mm0_x86_64 = 41, + dwarf_mm1_x86_64, + dwarf_mm2_x86_64, + dwarf_mm3_x86_64, + dwarf_mm4_x86_64, + dwarf_mm5_x86_64, + dwarf_mm6_x86_64, + dwarf_mm7_x86_64, + // Control and Status Flags Register + dwarf_rflags_x86_64 = 49, + // selector registers + dwarf_es_x86_64 = 50, + dwarf_cs_x86_64, + dwarf_ss_x86_64, + dwarf_ds_x86_64, + dwarf_fs_x86_64, + dwarf_gs_x86_64, + // Base registers + dwarf_fs_base_x86_64 = 58, + dwarf_gs_base_x86_64 = 59, + // Floating point control registers + dwarf_mxcsr_x86_64 = 64, // Media Control and Status + dwarf_fctrl_x86_64, // x87 control word + dwarf_fstat_x86_64, // x87 status word + // Upper Vector Registers + dwarf_ymm0h_x86_64 = 67, + dwarf_ymm1h_x86_64, + dwarf_ymm2h_x86_64, + dwarf_ymm3h_x86_64, + dwarf_ymm4h_x86_64, + dwarf_ymm5h_x86_64, + dwarf_ymm6h_x86_64, + dwarf_ymm7h_x86_64, + dwarf_ymm8h_x86_64, + dwarf_ymm9h_x86_64, + dwarf_ymm10h_x86_64, + dwarf_ymm11h_x86_64, + dwarf_ymm12h_x86_64, + dwarf_ymm13h_x86_64, + dwarf_ymm14h_x86_64, + dwarf_ymm15h_x86_64, + // MPX registers + dwarf_bnd0_x86_64 = 126, + dwarf_bnd1_x86_64, + dwarf_bnd2_x86_64, + dwarf_bnd3_x86_64, + // AVX2 Vector Mask Registers + // dwarf_k0_x86_64 = 118, + // dwarf_k1_x86_64, + // dwarf_k2_x86_64, + // dwarf_k3_x86_64, + // dwarf_k4_x86_64, + // dwarf_k5_x86_64, + // dwarf_k6_x86_64, + // dwarf_k7_x86_64, +}; + +// Generic floating-point registers + +LLVM_PACKED_START +struct MMSRegComp { + uint64_t mantissa; + uint16_t sign_exp; +}; + +struct MMSReg { + union { + uint8_t bytes[10]; + MMSRegComp comp; + }; + uint8_t pad[6]; +}; +LLVM_PACKED_END + +static_assert(sizeof(MMSRegComp) == 10, "MMSRegComp is not 10 bytes of size"); +static_assert(sizeof(MMSReg) == 16, "MMSReg is not 16 bytes of size"); + +struct XMMReg { + uint8_t bytes[16]; // 128-bits for each XMM register +}; + +// i387_fxsave_struct +struct FXSAVE { + uint16_t fctrl; // FPU Control Word (fcw) + uint16_t fstat; // FPU Status Word (fsw) + uint16_t ftag; // FPU Tag Word (ftw) + uint16_t fop; // Last Instruction Opcode (fop) + union { + struct { + uint64_t fip; // Instruction Pointer + uint64_t fdp; // Data Pointer + } x86_64; + struct { + uint32_t fioff; // FPU IP Offset (fip) + uint32_t fiseg; // FPU IP Selector (fcs) + uint32_t fooff; // FPU Operand Pointer Offset (foo) + uint32_t foseg; // FPU Operand Pointer Selector (fos) + } i386_; // Added _ in the end to avoid error with gcc defining i386 in some + // cases + } ptr; + uint32_t mxcsr; // MXCSR Register State + uint32_t mxcsrmask; // MXCSR Mask + MMSReg stmm[8]; // 8*16 bytes for each FP-reg = 128 bytes + XMMReg xmm[16]; // 16*16 bytes for each XMM-reg = 256 bytes + uint8_t padding1[48]; + uint64_t xcr0; + uint8_t padding2[40]; +}; + +// Extended floating-point registers + +struct YMMHReg { + uint8_t bytes[16]; // 16 * 8 bits for the high bytes of each YMM register +}; + +struct YMMReg { + uint8_t bytes[32]; // 16 * 16 bits for each YMM register +}; + +struct YMM { + YMMReg ymm[16]; // assembled from ymmh and xmm registers +}; + +struct MPXReg { + uint8_t bytes[16]; // MPX 128 bit bound registers +}; + +struct MPXCsr { + uint8_t bytes[8]; // MPX 64 bit bndcfgu and bndstatus registers (collectively + // BNDCSR state) +}; + +struct MPX { + MPXReg mpxr[4]; + MPXCsr mpxc[2]; +}; + +LLVM_PACKED_START +struct XSAVE_HDR { + enum class XFeature : uint64_t { + FP = 1, + SSE = FP << 1, + YMM = SSE << 1, + BNDREGS = YMM << 1, + BNDCSR = BNDREGS << 1, + OPMASK = BNDCSR << 1, + ZMM_Hi256 = OPMASK << 1, + Hi16_ZMM = ZMM_Hi256 << 1, + PT = Hi16_ZMM << 1, + PKRU = PT << 1, + LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue*/ PKRU) + }; + + XFeature xstate_bv; // OS enabled xstate mask to determine the extended states + // supported by the processor + XFeature xcomp_bv; // Mask to indicate the format of the XSAVE area and of + // the XRSTOR instruction + uint64_t reserved1[1]; + uint64_t reserved2[5]; +}; +static_assert(sizeof(XSAVE_HDR) == 64, "XSAVE_HDR layout incorrect"); +LLVM_PACKED_END + +// x86 extensions to FXSAVE (i.e. for AVX and MPX processors) +LLVM_PACKED_START +struct XSAVE { + FXSAVE i387; // floating point registers typical in i387_fxsave_struct + XSAVE_HDR header; // The xsave_hdr_struct can be used to determine if the + // following extensions are usable + YMMHReg ymmh[16]; // High 16 bytes of each of 16 YMM registers (the low bytes + // are in FXSAVE.xmm for compatibility with SSE) + uint64_t reserved3[16]; + MPXReg mpxr[4]; // MPX BNDREG state, containing 128-bit bound registers + MPXCsr mpxc[2]; // MPX BNDCSR state, containing 64-bit BNDCFGU and + // BNDSTATUS registers +}; +LLVM_PACKED_END + +// Floating-point registers +union FPR { + FXSAVE fxsave; // Generic floating-point registers. + XSAVE xsave; // x86 extended processor state. +}; + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +// Convenience function to combine YMM register data from XSAVE-style input. +inline YMMReg XStateToYMM(const void* xmm_bytes, const void* ymmh_bytes) { + YMMReg ret; + + ::memcpy(ret.bytes, xmm_bytes, sizeof(XMMReg)); + ::memcpy(ret.bytes + sizeof(XMMReg), ymmh_bytes, sizeof(YMMHReg)); + + return ret; +} + +// Convenience function to copy YMM register data into XSAVE-style output. +inline void YMMToXState(const YMMReg& input, void* xmm_bytes, void* ymmh_bytes) { + ::memcpy(xmm_bytes, input.bytes, sizeof(XMMReg)); + ::memcpy(ymmh_bytes, input.bytes + sizeof(XMMReg), sizeof(YMMHReg)); +} + +uint16_t AbridgedToFullTagWord(uint8_t abridged_tw, uint16_t sw, + llvm::ArrayRef<MMSReg> st_regs); +uint8_t FullToAbridgedTagWord(uint16_t tw); + +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterFlagsDetector_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterFlagsDetector_arm64.cpp new file mode 100644 index 000000000000..7c8dba368093 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterFlagsDetector_arm64.cpp @@ -0,0 +1,217 @@ +//===-- RegisterFlagsDetector_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterFlagsDetector_arm64.h" +#include "lldb/lldb-private-types.h" + +// This file is built on all systems because it is used by native processes and +// core files, so we manually define the needed HWCAP values here. +// These values are the same for Linux and FreeBSD. + +#define HWCAP_FPHP (1ULL << 9) +#define HWCAP_ASIMDHP (1ULL << 10) +#define HWCAP_DIT (1ULL << 24) +#define HWCAP_SSBS (1ULL << 28) + +#define HWCAP2_BTI (1ULL << 17) +#define HWCAP2_MTE (1ULL << 18) +#define HWCAP2_AFP (1ULL << 20) +#define HWCAP2_SME (1ULL << 23) +#define HWCAP2_EBF16 (1ULL << 32) + +using namespace lldb_private; + +Arm64RegisterFlagsDetector::Fields +Arm64RegisterFlagsDetector::DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2) { + (void)hwcap; + + if (!(hwcap2 & HWCAP2_SME)) + return {}; + + // Represents the pseudo register that lldb-server builds, which itself + // matches the architectural register SCVR. The fields match SVCR in the Arm + // manual. + return { + {"ZA", 1}, + {"SM", 0}, + }; +} + +Arm64RegisterFlagsDetector::Fields +Arm64RegisterFlagsDetector::DetectMTECtrlFields(uint64_t hwcap, + uint64_t hwcap2) { + (void)hwcap; + + if (!(hwcap2 & HWCAP2_MTE)) + return {}; + + // Represents the contents of NT_ARM_TAGGED_ADDR_CTRL and the value passed + // to prctl(PR_TAGGED_ADDR_CTRL...). Fields are derived from the defines + // used to build the value. + + static const FieldEnum tcf_enum( + "tcf_enum", + {{0, "TCF_NONE"}, {1, "TCF_SYNC"}, {2, "TCF_ASYNC"}, {3, "TCF_ASYMM"}}); + return {{"TAGS", 3, 18}, // 16 bit bitfield shifted up by PR_MTE_TAG_SHIFT. + {"TCF", 1, 2, &tcf_enum}, + {"TAGGED_ADDR_ENABLE", 0}}; +} + +Arm64RegisterFlagsDetector::Fields +Arm64RegisterFlagsDetector::DetectFPCRFields(uint64_t hwcap, uint64_t hwcap2) { + static const FieldEnum rmode_enum( + "rmode_enum", {{0, "RN"}, {1, "RP"}, {2, "RM"}, {3, "RZ"}}); + + std::vector<RegisterFlags::Field> fpcr_fields{ + {"AHP", 26}, {"DN", 25}, {"FZ", 24}, {"RMode", 22, 23, &rmode_enum}, + // Bits 21-20 are "Stride" which is unused in AArch64 state. + }; + + // FEAT_FP16 is indicated by the presence of FPHP (floating point half + // precision) and ASIMDHP (Advanced SIMD half precision) features. + if ((hwcap & HWCAP_FPHP) && (hwcap & HWCAP_ASIMDHP)) + fpcr_fields.push_back({"FZ16", 19}); + + // Bits 18-16 are "Len" which is unused in AArch64 state. + + fpcr_fields.push_back({"IDE", 15}); + + // Bit 14 is unused. + if (hwcap2 & HWCAP2_EBF16) + fpcr_fields.push_back({"EBF", 13}); + + fpcr_fields.push_back({"IXE", 12}); + fpcr_fields.push_back({"UFE", 11}); + fpcr_fields.push_back({"OFE", 10}); + fpcr_fields.push_back({"DZE", 9}); + fpcr_fields.push_back({"IOE", 8}); + // Bits 7-3 reserved. + + if (hwcap2 & HWCAP2_AFP) { + fpcr_fields.push_back({"NEP", 2}); + fpcr_fields.push_back({"AH", 1}); + fpcr_fields.push_back({"FIZ", 0}); + } + + return fpcr_fields; +} + +Arm64RegisterFlagsDetector::Fields +Arm64RegisterFlagsDetector::DetectFPSRFields(uint64_t hwcap, uint64_t hwcap2) { + // fpsr's contents are constant. + (void)hwcap; + (void)hwcap2; + + return { + // Bits 31-28 are N/Z/C/V, only used by AArch32. + {"QC", 27}, + // Bits 26-8 reserved. + {"IDC", 7}, + // Bits 6-5 reserved. + {"IXC", 4}, + {"UFC", 3}, + {"OFC", 2}, + {"DZC", 1}, + {"IOC", 0}, + }; +} + +Arm64RegisterFlagsDetector::Fields +Arm64RegisterFlagsDetector::DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2) { + // The fields here are a combination of the Arm manual's SPSR_EL1, + // plus a few changes where Linux has decided not to make use of them at all, + // or at least not from userspace. + + // Status bits that are always present. + std::vector<RegisterFlags::Field> cpsr_fields{ + {"N", 31}, {"Z", 30}, {"C", 29}, {"V", 28}, + // Bits 27-26 reserved. + }; + + if (hwcap2 & HWCAP2_MTE) + cpsr_fields.push_back({"TCO", 25}); + if (hwcap & HWCAP_DIT) + cpsr_fields.push_back({"DIT", 24}); + + // UAO and PAN are bits 23 and 22 and have no meaning for userspace so + // are treated as reserved by the kernels. + + cpsr_fields.push_back({"SS", 21}); + cpsr_fields.push_back({"IL", 20}); + // Bits 19-14 reserved. + + // Bit 13, ALLINT, requires FEAT_NMI that isn't relevant to userspace, and we + // can't detect either, don't show this field. + if (hwcap & HWCAP_SSBS) + cpsr_fields.push_back({"SSBS", 12}); + if (hwcap2 & HWCAP2_BTI) + cpsr_fields.push_back({"BTYPE", 10, 11}); + + cpsr_fields.push_back({"D", 9}); + cpsr_fields.push_back({"A", 8}); + cpsr_fields.push_back({"I", 7}); + cpsr_fields.push_back({"F", 6}); + // Bit 5 reserved + // Called "M" in the ARMARM. + cpsr_fields.push_back({"nRW", 4}); + // This is a 4 bit field M[3:0] in the ARMARM, we split it into parts. + cpsr_fields.push_back({"EL", 2, 3}); + // Bit 1 is unused and expected to be 0. + cpsr_fields.push_back({"SP", 0}); + + return cpsr_fields; +} + +void Arm64RegisterFlagsDetector::DetectFields(uint64_t hwcap, uint64_t hwcap2) { + for (auto ® : m_registers) + reg.m_flags.SetFields(reg.m_detector(hwcap, hwcap2)); + m_has_detected = true; +} + +void Arm64RegisterFlagsDetector::UpdateRegisterInfo( + const RegisterInfo *reg_info, uint32_t num_regs) { + assert(m_has_detected && + "Must call DetectFields before updating register info."); + + // Register names will not be duplicated, so we do not want to compare against + // one if it has already been found. Each time we find one, we erase it from + // this list. + std::vector<std::pair<llvm::StringRef, const RegisterFlags *>> + search_registers; + for (const auto ® : m_registers) { + // It is possible that a register is all extension dependent fields, and + // none of them are present. + if (reg.m_flags.GetFields().size()) + search_registers.push_back({reg.m_name, ®.m_flags}); + } + + // Walk register information while there are registers we know need + // to be updated. Example: + // Register information: [a, b, c, d] + // To be patched: [b, c] + // * a != b, a != c, do nothing and move on. + // * b == b, patch b, new patch list is [c], move on. + // * c == c, patch c, patch list is empty, exit early without looking at d. + for (uint32_t idx = 0; idx < num_regs && search_registers.size(); + ++idx, ++reg_info) { + auto reg_it = std::find_if( + search_registers.cbegin(), search_registers.cend(), + [reg_info](auto reg) { return reg.first == reg_info->name; }); + + if (reg_it != search_registers.end()) { + // Attach the field information. + reg_info->flags_type = reg_it->second; + // We do not expect to see this name again so don't look for it again. + search_registers.erase(reg_it); + } + } + + // We do not assert that search_registers is empty here, because it may + // contain registers from optional extensions that are not present on the + // current target. +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterFlagsDetector_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterFlagsDetector_arm64.h new file mode 100644 index 000000000000..a5bb38670b9c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterFlagsDetector_arm64.h @@ -0,0 +1,86 @@ +//===-- RegisterFlagsDetector_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERFLAGSDETECTOR_ARM64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERFLAGSDETECTOR_ARM64_H + +#include "lldb/Target/RegisterFlags.h" +#include "llvm/ADT/StringRef.h" +#include <functional> + +namespace lldb_private { + +struct RegisterInfo; + +/// This class manages the storage and detection of register field information. +/// The same register may have different fields on different CPUs. This class +/// abstracts out the field detection process so we can use it on live processes +/// and core files. +/// +/// The way to use this class is: +/// * Make an instance somewhere that will last as long as the debug session +/// (because your final register info will point to this instance). +/// * Read hardware capabilities from a core note, binary, prctl, etc. +/// * Pass those to DetectFields. +/// * Call UpdateRegisterInfo with your RegisterInfo to add pointers +/// to the detected fields for all registers listed in this class. +/// +/// This must be done in that order, and you should ensure that if multiple +/// threads will reference the information, a mutex is used to make sure only +/// one calls DetectFields. +class Arm64RegisterFlagsDetector { +public: + /// For the registers listed in this class, detect which fields are + /// present. Must be called before UpdateRegisterInfos. + /// If called more than once, fields will be redetected each time from + /// scratch. If the target would not have this register at all, the list of + /// fields will be left empty. + void DetectFields(uint64_t hwcap, uint64_t hwcap2); + + /// Add the field information of any registers named in this class, + /// to the relevant RegisterInfo instances. Note that this will be done + /// with a pointer to the instance of this class that you call this on, so + /// the lifetime of that instance must be at least that of the register info. + void UpdateRegisterInfo(const RegisterInfo *reg_info, uint32_t num_regs); + + /// Returns true if field detection has been run at least once. + bool HasDetected() const { return m_has_detected; } + +private: + using Fields = std::vector<RegisterFlags::Field>; + using DetectorFn = std::function<Fields(uint64_t, uint64_t)>; + + static Fields DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2); + static Fields DetectFPSRFields(uint64_t hwcap, uint64_t hwcap2); + static Fields DetectFPCRFields(uint64_t hwcap, uint64_t hwcap2); + static Fields DetectMTECtrlFields(uint64_t hwcap, uint64_t hwcap2); + static Fields DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2); + + struct RegisterEntry { + RegisterEntry(llvm::StringRef name, unsigned size, DetectorFn detector) + : m_name(name), m_flags(std::string(name) + "_flags", size, {}), + m_detector(detector) {} + + llvm::StringRef m_name; + RegisterFlags m_flags; + DetectorFn m_detector; + } m_registers[5] = { + RegisterEntry("cpsr", 4, DetectCPSRFields), + RegisterEntry("fpsr", 4, DetectFPSRFields), + RegisterEntry("fpcr", 4, DetectFPCRFields), + RegisterEntry("mte_ctrl", 8, DetectMTECtrlFields), + RegisterEntry("svcr", 8, DetectSVCRFields), + }; + + // Becomes true once field detection has been run for all registers. + bool m_has_detected = false; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERFLAGSDETECTOR_ARM64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoAndSetInterface.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoAndSetInterface.h new file mode 100644 index 000000000000..7e569dc9ba78 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoAndSetInterface.h @@ -0,0 +1,36 @@ +//===-- RegisterInfoAndSetInterface.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOANDSETINTERFACE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOANDSETINTERFACE_H + +#include "RegisterInfoInterface.h" + +#include "lldb/Utility/ArchSpec.h" +#include "lldb/lldb-private-types.h" +#include <vector> + +namespace lldb_private { + +class RegisterInfoAndSetInterface : public RegisterInfoInterface { +public: + RegisterInfoAndSetInterface(const lldb_private::ArchSpec &target_arch) + : RegisterInfoInterface(target_arch) {} + + virtual size_t GetFPRSize() const = 0; + + virtual const lldb_private::RegisterSet * + GetRegisterSet(size_t reg_set) const = 0; + + virtual size_t GetRegisterSetCount() const = 0; + + virtual size_t GetRegisterSetFromRegisterIndex(uint32_t reg_index) const = 0; +}; +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h new file mode 100644 index 000000000000..a79c5cc22b24 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoInterface.h @@ -0,0 +1,49 @@ +//===-- RegisterInfoInterface.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOINTERFACE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOINTERFACE_H + +#include "lldb/Utility/ArchSpec.h" +#include "lldb/lldb-private-types.h" +#include <vector> + +namespace lldb_private { + +/// \class RegisterInfoInterface +/// +/// RegisterInfo interface to patch RegisterInfo structure for archs. +class RegisterInfoInterface { +public: + RegisterInfoInterface(const lldb_private::ArchSpec &target_arch) + : m_target_arch(target_arch) {} + virtual ~RegisterInfoInterface() = default; + + virtual size_t GetGPRSize() const = 0; + + virtual const lldb_private::RegisterInfo *GetRegisterInfo() const = 0; + + // Returns the number of registers including the user registers and the + // lldb internal registers also + virtual uint32_t GetRegisterCount() const = 0; + + // Returns the number of the user registers (excluding the registers + // kept for lldb internal use only). Subclasses should override it if + // they belongs to an architecture with lldb internal registers. + virtual uint32_t GetUserRegisterCount() const { return GetRegisterCount(); } + + const lldb_private::ArchSpec &GetTargetArchitecture() const { + return m_target_arch; + } + +private: + lldb_private::ArchSpec m_target_arch; +}; +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp new file mode 100644 index 000000000000..d47647422ae2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp @@ -0,0 +1,193 @@ +//===-- RegisterInfoPOSIX_arm.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------------===// + +#include <cassert> +#include <cstddef> +#include <vector> + +#include "lldb/lldb-defines.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterInfoPOSIX_arm.h" + +using namespace lldb; +using namespace lldb_private; + +// Based on RegisterContextDarwin_arm.cpp +#define GPR_OFFSET(idx) ((idx)*4) +#define FPU_OFFSET(idx) ((idx)*4 + sizeof(RegisterInfoPOSIX_arm::GPR)) +#define FPSCR_OFFSET \ + (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm::FPU, fpscr) + \ + sizeof(RegisterInfoPOSIX_arm::GPR)) +#define EXC_OFFSET(idx) \ + ((idx)*4 + sizeof(RegisterInfoPOSIX_arm::GPR) + \ + sizeof(RegisterInfoPOSIX_arm::FPU)) +#define DBG_OFFSET(reg) \ + ((LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm::DBG, reg) + \ + sizeof(RegisterInfoPOSIX_arm::GPR) + sizeof(RegisterInfoPOSIX_arm::FPU) + \ + sizeof(RegisterInfoPOSIX_arm::EXC))) + +#define DEFINE_DBG(reg, i) \ + #reg, NULL, sizeof(((RegisterInfoPOSIX_arm::DBG *) NULL)->reg[i]), \ + DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + dbg_##reg##i }, \ + NULL, NULL, NULL, +#define REG_CONTEXT_SIZE \ + (sizeof(RegisterInfoPOSIX_arm::GPR) + sizeof(RegisterInfoPOSIX_arm::FPU) + \ + sizeof(RegisterInfoPOSIX_arm::EXC)) + +// Include RegisterInfos_arm to declare our g_register_infos_arm structure. +#define DECLARE_REGISTER_INFOS_ARM_STRUCT +#include "RegisterInfos_arm.h" +#undef DECLARE_REGISTER_INFOS_ARM_STRUCT + +static const lldb_private::RegisterInfo * +GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::arm: + return g_register_infos_arm; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t +GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::arm: + return static_cast<uint32_t>(sizeof(g_register_infos_arm) / + sizeof(g_register_infos_arm[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +// Number of register sets provided by this context. +enum { + k_num_gpr_registers = gpr_cpsr - gpr_r0 + 1, + k_num_fpr_registers = fpu_q15 - fpu_s0 + 1, + k_num_register_sets = 2 +}; + +// arm general purpose registers. +static const uint32_t g_gpr_regnums_arm[] = { + gpr_r0, gpr_r1, + gpr_r2, gpr_r3, + gpr_r4, gpr_r5, + gpr_r6, gpr_r7, + gpr_r8, gpr_r9, + gpr_r10, gpr_r11, + gpr_r12, gpr_sp, + gpr_lr, gpr_pc, + gpr_cpsr, LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_gpr_regnums_arm / sizeof g_gpr_regnums_arm[0]) - 1) == + k_num_gpr_registers, + "g_gpr_regnums_arm has wrong number of register infos"); + +// arm floating point registers. +static const uint32_t g_fpu_regnums_arm[] = { + fpu_s0, fpu_s1, + fpu_s2, fpu_s3, + fpu_s4, fpu_s5, + fpu_s6, fpu_s7, + fpu_s8, fpu_s9, + fpu_s10, fpu_s11, + fpu_s12, fpu_s13, + fpu_s14, fpu_s15, + fpu_s16, fpu_s17, + fpu_s18, fpu_s19, + fpu_s20, fpu_s21, + fpu_s22, fpu_s23, + fpu_s24, fpu_s25, + fpu_s26, fpu_s27, + fpu_s28, fpu_s29, + fpu_s30, fpu_s31, + fpu_fpscr, fpu_d0, + fpu_d1, fpu_d2, + fpu_d3, fpu_d4, + fpu_d5, fpu_d6, + fpu_d7, fpu_d8, + fpu_d9, fpu_d10, + fpu_d11, fpu_d12, + fpu_d13, fpu_d14, + fpu_d15, fpu_d16, + fpu_d17, fpu_d18, + fpu_d19, fpu_d20, + fpu_d21, fpu_d22, + fpu_d23, fpu_d24, + fpu_d25, fpu_d26, + fpu_d27, fpu_d28, + fpu_d29, fpu_d30, + fpu_d31, fpu_q0, + fpu_q1, fpu_q2, + fpu_q3, fpu_q4, + fpu_q5, fpu_q6, + fpu_q7, fpu_q8, + fpu_q9, fpu_q10, + fpu_q11, fpu_q12, + fpu_q13, fpu_q14, + fpu_q15, LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert(((sizeof g_fpu_regnums_arm / sizeof g_fpu_regnums_arm[0]) - 1) == + k_num_fpr_registers, + "g_fpu_regnums_arm has wrong number of register infos"); + +// Register sets for arm. +static const RegisterSet g_reg_sets_arm[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers, + g_gpr_regnums_arm}, + {"Floating Point Registers", "fpu", k_num_fpr_registers, + g_fpu_regnums_arm}}; + +RegisterInfoPOSIX_arm::RegisterInfoPOSIX_arm( + const lldb_private::ArchSpec &target_arch) + : lldb_private::RegisterInfoAndSetInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)) {} + +size_t RegisterInfoPOSIX_arm::GetGPRSize() const { + return sizeof(struct RegisterInfoPOSIX_arm::GPR); +} + +size_t RegisterInfoPOSIX_arm::GetFPRSize() const { + return sizeof(struct RegisterInfoPOSIX_arm::FPU); +} + +const lldb_private::RegisterInfo * +RegisterInfoPOSIX_arm::GetRegisterInfo() const { + return m_register_info_p; +} + +size_t RegisterInfoPOSIX_arm::GetRegisterSetCount() const { + return k_num_register_sets; +} + +size_t RegisterInfoPOSIX_arm::GetRegisterSetFromRegisterIndex( + uint32_t reg_index) const { + if (reg_index <= gpr_cpsr) + return GPRegSet; + if (reg_index <= fpu_q15) + return FPRegSet; + return LLDB_INVALID_REGNUM; +} + +const lldb_private::RegisterSet * +RegisterInfoPOSIX_arm::GetRegisterSet(size_t set_index) const { + if (set_index < GetRegisterSetCount()) + return &g_reg_sets_arm[set_index]; + return nullptr; +} + +uint32_t RegisterInfoPOSIX_arm::GetRegisterCount() const { + return m_register_info_count; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h new file mode 100644 index 000000000000..db155d757ca8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h @@ -0,0 +1,72 @@ +//===-- RegisterInfoPOSIX_arm.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_ARM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_ARM_H + +#include "RegisterInfoAndSetInterface.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" + +class RegisterInfoPOSIX_arm : public lldb_private::RegisterInfoAndSetInterface { +public: + enum { GPRegSet = 0, FPRegSet}; + + struct GPR { + uint32_t r[16]; // R0-R15 + uint32_t cpsr; // CPSR + }; + + struct QReg { + uint8_t bytes[16]; + }; + + struct FPU { + union { + uint32_t s[32]; + uint64_t d[32]; + QReg q[16]; // the 128-bit NEON registers + } floats; + uint32_t fpscr; + }; + struct EXC { + uint32_t exception; + uint32_t fsr; /* Fault status */ + uint32_t far; /* Virtual Fault Address */ + }; + + struct DBG { + uint32_t bvr[16]; + uint32_t bcr[16]; + uint32_t wvr[16]; + uint32_t wcr[16]; + }; + + RegisterInfoPOSIX_arm(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + size_t GetFPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + const lldb_private::RegisterSet * + GetRegisterSet(size_t reg_set) const override; + + size_t GetRegisterSetCount() const override; + + size_t GetRegisterSetFromRegisterIndex(uint32_t reg_index) const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_ARM_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp new file mode 100644 index 000000000000..054b7d9b2ec5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp @@ -0,0 +1,561 @@ +//===-- RegisterInfoPOSIX_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 +// +//===---------------------------------------------------------------------===// + +#include <cassert> +#include <cstddef> +#include <vector> + +#include "lldb/lldb-defines.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterInfoPOSIX_arm64.h" + +// Based on RegisterContextDarwin_arm64.cpp +#define GPR_OFFSET(idx) ((idx)*8) +#define GPR_OFFSET_NAME(reg) \ + (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::GPR, reg)) + +#define FPU_OFFSET(idx) ((idx)*16 + sizeof(RegisterInfoPOSIX_arm64::GPR)) +#define FPU_OFFSET_NAME(reg) \ + (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::FPU, reg) + \ + sizeof(RegisterInfoPOSIX_arm64::GPR)) + +// This information is based on AArch64 with SVE architecture reference manual. +// AArch64 with SVE has 32 Z and 16 P vector registers. There is also an FFR +// (First Fault) register and a VG (Vector Granule) pseudo register. + +// SVE 16-byte quad word is the basic unit of expansion in vector length. +#define SVE_QUAD_WORD_BYTES 16 + +// Vector length is the multiplier which decides the no of quad words, +// (multiples of 128-bits or 16-bytes) present in a Z register. Vector length +// is decided during execution and can change at runtime. SVE AArch64 register +// infos have modes one for each valid value of vector length. A change in +// vector length requires register context to update sizes of SVE Z, P and FFR. +// Also register context needs to update byte offsets of all registers affected +// by the change in vector length. +#define SVE_REGS_DEFAULT_OFFSET_LINUX sizeof(RegisterInfoPOSIX_arm64::GPR) + +#define SVE_OFFSET_VG SVE_REGS_DEFAULT_OFFSET_LINUX + +#define EXC_OFFSET_NAME(reg) \ + (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::EXC, reg) + \ + sizeof(RegisterInfoPOSIX_arm64::GPR) + \ + sizeof(RegisterInfoPOSIX_arm64::FPU)) +#define DBG_OFFSET_NAME(reg) \ + (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm64::DBG, reg) + \ + sizeof(RegisterInfoPOSIX_arm64::GPR) + \ + sizeof(RegisterInfoPOSIX_arm64::FPU) + \ + sizeof(RegisterInfoPOSIX_arm64::EXC)) + +#define DEFINE_DBG(reg, i) \ + #reg, NULL, \ + sizeof(((RegisterInfoPOSIX_arm64::DBG *) NULL)->reg[i]), \ + DBG_OFFSET_NAME(reg[i]), lldb::eEncodingUint, lldb::eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + dbg_##reg##i }, \ + NULL, NULL, NULL, +#define REG_CONTEXT_SIZE \ + (sizeof(RegisterInfoPOSIX_arm64::GPR) + \ + sizeof(RegisterInfoPOSIX_arm64::FPU) + \ + sizeof(RegisterInfoPOSIX_arm64::EXC)) + +// Include RegisterInfos_arm64 to declare our g_register_infos_arm64 structure. +#define DECLARE_REGISTER_INFOS_ARM64_STRUCT +#include "RegisterInfos_arm64.h" +#include "RegisterInfos_arm64_sve.h" +#undef DECLARE_REGISTER_INFOS_ARM64_STRUCT + +static lldb_private::RegisterInfo g_register_infos_pauth[] = { + DEFINE_EXTENSION_REG(data_mask), DEFINE_EXTENSION_REG(code_mask)}; + +static lldb_private::RegisterInfo g_register_infos_mte[] = { + DEFINE_EXTENSION_REG(mte_ctrl)}; + +static lldb_private::RegisterInfo g_register_infos_tls[] = { + DEFINE_EXTENSION_REG(tpidr), + // Only present when SME is present + DEFINE_EXTENSION_REG(tpidr2)}; + +static lldb_private::RegisterInfo g_register_infos_sme[] = { + DEFINE_EXTENSION_REG(svcr), + DEFINE_EXTENSION_REG(svg), + // 16 is a default size we will change later. + {"za", nullptr, 16, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, + KIND_ALL_INVALID, nullptr, nullptr, nullptr}}; + +static lldb_private::RegisterInfo g_register_infos_sme2[] = { + {"zt0", nullptr, 64, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, + KIND_ALL_INVALID, nullptr, nullptr, nullptr}}; + +// Number of register sets provided by this context. +enum { + k_num_gpr_registers = gpr_w28 - gpr_x0 + 1, + k_num_fpr_registers = fpu_fpcr - fpu_v0 + 1, + k_num_sve_registers = sve_ffr - sve_vg + 1, + k_num_mte_register = 1, + // Number of TLS registers is dynamic so it is not listed here. + k_num_pauth_register = 2, + // SME2's ZT0 will also be added to this set if present. So this number is + // only for SME1 registers. + k_num_sme_register = 3, + k_num_register_sets_default = 2, + k_num_register_sets = 3 +}; + +// ARM64 general purpose registers. +static const uint32_t g_gpr_regnums_arm64[] = { + gpr_x0, gpr_x1, gpr_x2, gpr_x3, + gpr_x4, gpr_x5, gpr_x6, gpr_x7, + gpr_x8, gpr_x9, gpr_x10, gpr_x11, + gpr_x12, gpr_x13, gpr_x14, gpr_x15, + gpr_x16, gpr_x17, gpr_x18, gpr_x19, + gpr_x20, gpr_x21, gpr_x22, gpr_x23, + gpr_x24, gpr_x25, gpr_x26, gpr_x27, + gpr_x28, gpr_fp, gpr_lr, gpr_sp, + gpr_pc, gpr_cpsr, gpr_w0, gpr_w1, + gpr_w2, gpr_w3, gpr_w4, gpr_w5, + gpr_w6, gpr_w7, gpr_w8, gpr_w9, + gpr_w10, gpr_w11, gpr_w12, gpr_w13, + gpr_w14, gpr_w15, gpr_w16, gpr_w17, + gpr_w18, gpr_w19, gpr_w20, gpr_w21, + gpr_w22, gpr_w23, gpr_w24, gpr_w25, + gpr_w26, gpr_w27, gpr_w28, LLDB_INVALID_REGNUM}; + +static_assert(((sizeof g_gpr_regnums_arm64 / sizeof g_gpr_regnums_arm64[0]) - + 1) == k_num_gpr_registers, + "g_gpr_regnums_arm64 has wrong number of register infos"); + +// ARM64 floating point registers. +static const uint32_t g_fpu_regnums_arm64[] = { + fpu_v0, fpu_v1, fpu_v2, + fpu_v3, fpu_v4, fpu_v5, + fpu_v6, fpu_v7, fpu_v8, + fpu_v9, fpu_v10, fpu_v11, + fpu_v12, fpu_v13, fpu_v14, + fpu_v15, fpu_v16, fpu_v17, + fpu_v18, fpu_v19, fpu_v20, + fpu_v21, fpu_v22, fpu_v23, + fpu_v24, fpu_v25, fpu_v26, + fpu_v27, fpu_v28, fpu_v29, + fpu_v30, fpu_v31, fpu_s0, + fpu_s1, fpu_s2, fpu_s3, + fpu_s4, fpu_s5, fpu_s6, + fpu_s7, fpu_s8, fpu_s9, + fpu_s10, fpu_s11, fpu_s12, + fpu_s13, fpu_s14, fpu_s15, + fpu_s16, fpu_s17, fpu_s18, + fpu_s19, fpu_s20, fpu_s21, + fpu_s22, fpu_s23, fpu_s24, + fpu_s25, fpu_s26, fpu_s27, + fpu_s28, fpu_s29, fpu_s30, + fpu_s31, fpu_d0, fpu_d1, + fpu_d2, fpu_d3, fpu_d4, + fpu_d5, fpu_d6, fpu_d7, + fpu_d8, fpu_d9, fpu_d10, + fpu_d11, fpu_d12, fpu_d13, + fpu_d14, fpu_d15, fpu_d16, + fpu_d17, fpu_d18, fpu_d19, + fpu_d20, fpu_d21, fpu_d22, + fpu_d23, fpu_d24, fpu_d25, + fpu_d26, fpu_d27, fpu_d28, + fpu_d29, fpu_d30, fpu_d31, + fpu_fpsr, fpu_fpcr, LLDB_INVALID_REGNUM}; +static_assert(((sizeof g_fpu_regnums_arm64 / sizeof g_fpu_regnums_arm64[0]) - + 1) == k_num_fpr_registers, + "g_fpu_regnums_arm64 has wrong number of register infos"); + +// ARM64 SVE registers. +static const uint32_t g_sve_regnums_arm64[] = { + sve_vg, sve_z0, sve_z1, + sve_z2, sve_z3, sve_z4, + sve_z5, sve_z6, sve_z7, + sve_z8, sve_z9, sve_z10, + sve_z11, sve_z12, sve_z13, + sve_z14, sve_z15, sve_z16, + sve_z17, sve_z18, sve_z19, + sve_z20, sve_z21, sve_z22, + sve_z23, sve_z24, sve_z25, + sve_z26, sve_z27, sve_z28, + sve_z29, sve_z30, sve_z31, + sve_p0, sve_p1, sve_p2, + sve_p3, sve_p4, sve_p5, + sve_p6, sve_p7, sve_p8, + sve_p9, sve_p10, sve_p11, + sve_p12, sve_p13, sve_p14, + sve_p15, sve_ffr, LLDB_INVALID_REGNUM}; +static_assert(((sizeof g_sve_regnums_arm64 / sizeof g_sve_regnums_arm64[0]) - + 1) == k_num_sve_registers, + "g_sve_regnums_arm64 has wrong number of register infos"); + +// Register sets for ARM64. +static const lldb_private::RegisterSet g_reg_sets_arm64[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers, + g_gpr_regnums_arm64}, + {"Floating Point Registers", "fpu", k_num_fpr_registers, + g_fpu_regnums_arm64}, + {"Scalable Vector Extension Registers", "sve", k_num_sve_registers, + g_sve_regnums_arm64}}; + +static const lldb_private::RegisterSet g_reg_set_pauth_arm64 = { + "Pointer Authentication Registers", "pauth", k_num_pauth_register, nullptr}; + +static const lldb_private::RegisterSet g_reg_set_mte_arm64 = { + "MTE Control Register", "mte", k_num_mte_register, nullptr}; + +// The size of the TLS set is dynamic, so not listed here. + +static const lldb_private::RegisterSet g_reg_set_sme_arm64 = { + "Scalable Matrix Extension Registers", "sme", k_num_sme_register, nullptr}; + +RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64( + const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets) + : lldb_private::RegisterInfoAndSetInterface(target_arch), + m_opt_regsets(opt_regsets) { + switch (target_arch.GetMachine()) { + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: { + m_register_set_p = g_reg_sets_arm64; + m_register_set_count = k_num_register_sets_default; + m_per_regset_regnum_range[GPRegSet] = std::make_pair(gpr_x0, gpr_w28 + 1); + m_per_regset_regnum_range[FPRegSet] = std::make_pair(fpu_v0, fpu_fpcr + 1); + + // Now configure register sets supported by current target. If we have a + // dynamic register set like MTE, Pointer Authentication regset then we need + // to create dynamic register infos and regset array. Push back all optional + // register infos and regset and calculate register offsets accordingly. + if (m_opt_regsets.AnySet(eRegsetMaskSVE | eRegsetMaskSSVE)) { + m_register_info_p = g_register_infos_arm64_sve_le; + m_register_info_count = sve_ffr + 1; + m_per_regset_regnum_range[m_register_set_count++] = + std::make_pair(sve_vg, sve_ffr + 1); + } else { + m_register_info_p = g_register_infos_arm64_le; + m_register_info_count = fpu_fpcr + 1; + } + + if (m_opt_regsets.AnySet(eRegsetMaskDynamic)) { + llvm::ArrayRef<lldb_private::RegisterInfo> reg_infos_ref = + llvm::ArrayRef(m_register_info_p, m_register_info_count); + llvm::ArrayRef<lldb_private::RegisterSet> reg_sets_ref = + llvm::ArrayRef(m_register_set_p, m_register_set_count); + llvm::copy(reg_infos_ref, std::back_inserter(m_dynamic_reg_infos)); + llvm::copy(reg_sets_ref, std::back_inserter(m_dynamic_reg_sets)); + + if (m_opt_regsets.AllSet(eRegsetMaskPAuth)) + AddRegSetPAuth(); + + if (m_opt_regsets.AllSet(eRegsetMaskMTE)) + AddRegSetMTE(); + + // The TLS set always contains tpidr but only has tpidr2 when SME is + // present. + AddRegSetTLS(m_opt_regsets.AllSet(eRegsetMaskSSVE)); + + if (m_opt_regsets.AnySet(eRegsetMaskSSVE)) + AddRegSetSME(m_opt_regsets.AnySet(eRegsetMaskZT)); + + m_register_info_count = m_dynamic_reg_infos.size(); + m_register_info_p = m_dynamic_reg_infos.data(); + m_register_set_p = m_dynamic_reg_sets.data(); + m_register_set_count = m_dynamic_reg_sets.size(); + } + break; + } + default: + assert(false && "Unhandled target architecture."); + } +} + +uint32_t RegisterInfoPOSIX_arm64::GetRegisterCount() const { + return m_register_info_count; +} + +size_t RegisterInfoPOSIX_arm64::GetGPRSizeStatic() { + return sizeof(struct RegisterInfoPOSIX_arm64::GPR); +} + +size_t RegisterInfoPOSIX_arm64::GetFPRSize() const { + return sizeof(struct RegisterInfoPOSIX_arm64::FPU); +} + +const lldb_private::RegisterInfo * +RegisterInfoPOSIX_arm64::GetRegisterInfo() const { + return m_register_info_p; +} + +size_t RegisterInfoPOSIX_arm64::GetRegisterSetCount() const { + return m_register_set_count; +} + +size_t RegisterInfoPOSIX_arm64::GetRegisterSetFromRegisterIndex( + uint32_t reg_index) const { + for (const auto ®set_range : m_per_regset_regnum_range) { + if (reg_index >= regset_range.second.first && + reg_index < regset_range.second.second) + return regset_range.first; + } + return LLDB_INVALID_REGNUM; +} + +const lldb_private::RegisterSet * +RegisterInfoPOSIX_arm64::GetRegisterSet(size_t set_index) const { + if (set_index < GetRegisterSetCount()) + return &m_register_set_p[set_index]; + return nullptr; +} + +void RegisterInfoPOSIX_arm64::AddRegSetPAuth() { + uint32_t pa_regnum = m_dynamic_reg_infos.size(); + for (uint32_t i = 0; i < k_num_pauth_register; i++) { + pauth_regnum_collection.push_back(pa_regnum + i); + m_dynamic_reg_infos.push_back(g_register_infos_pauth[i]); + m_dynamic_reg_infos[pa_regnum + i].byte_offset = + m_dynamic_reg_infos[pa_regnum + i - 1].byte_offset + + m_dynamic_reg_infos[pa_regnum + i - 1].byte_size; + m_dynamic_reg_infos[pa_regnum + i].kinds[lldb::eRegisterKindLLDB] = + pa_regnum + i; + } + + m_per_regset_regnum_range[m_register_set_count] = + std::make_pair(pa_regnum, m_dynamic_reg_infos.size()); + m_dynamic_reg_sets.push_back(g_reg_set_pauth_arm64); + m_dynamic_reg_sets.back().registers = pauth_regnum_collection.data(); +} + +void RegisterInfoPOSIX_arm64::AddRegSetMTE() { + uint32_t mte_regnum = m_dynamic_reg_infos.size(); + m_mte_regnum_collection.push_back(mte_regnum); + m_dynamic_reg_infos.push_back(g_register_infos_mte[0]); + m_dynamic_reg_infos[mte_regnum].byte_offset = + m_dynamic_reg_infos[mte_regnum - 1].byte_offset + + m_dynamic_reg_infos[mte_regnum - 1].byte_size; + m_dynamic_reg_infos[mte_regnum].kinds[lldb::eRegisterKindLLDB] = mte_regnum; + + m_per_regset_regnum_range[m_register_set_count] = + std::make_pair(mte_regnum, mte_regnum + 1); + m_dynamic_reg_sets.push_back(g_reg_set_mte_arm64); + m_dynamic_reg_sets.back().registers = m_mte_regnum_collection.data(); +} + +void RegisterInfoPOSIX_arm64::AddRegSetTLS(bool has_tpidr2) { + uint32_t tls_regnum = m_dynamic_reg_infos.size(); + uint32_t num_regs = has_tpidr2 ? 2 : 1; + for (uint32_t i = 0; i < num_regs; i++) { + m_tls_regnum_collection.push_back(tls_regnum + i); + m_dynamic_reg_infos.push_back(g_register_infos_tls[i]); + m_dynamic_reg_infos[tls_regnum + i].byte_offset = + m_dynamic_reg_infos[tls_regnum + i - 1].byte_offset + + m_dynamic_reg_infos[tls_regnum + i - 1].byte_size; + m_dynamic_reg_infos[tls_regnum + i].kinds[lldb::eRegisterKindLLDB] = + tls_regnum + i; + } + + m_per_regset_regnum_range[m_register_set_count] = + std::make_pair(tls_regnum, m_dynamic_reg_infos.size()); + m_dynamic_reg_sets.push_back( + {"Thread Local Storage Registers", "tls", num_regs, nullptr}); + m_dynamic_reg_sets.back().registers = m_tls_regnum_collection.data(); +} + +void RegisterInfoPOSIX_arm64::AddRegSetSME(bool has_zt) { + const uint32_t first_sme_regnum = m_dynamic_reg_infos.size(); + uint32_t sme_regnum = first_sme_regnum; + + for (uint32_t i = 0; i < k_num_sme_register; ++i, ++sme_regnum) { + m_sme_regnum_collection.push_back(sme_regnum); + m_dynamic_reg_infos.push_back(g_register_infos_sme[i]); + m_dynamic_reg_infos[sme_regnum].byte_offset = + m_dynamic_reg_infos[sme_regnum - 1].byte_offset + + m_dynamic_reg_infos[sme_regnum - 1].byte_size; + m_dynamic_reg_infos[sme_regnum].kinds[lldb::eRegisterKindLLDB] = sme_regnum; + } + + lldb_private::RegisterSet sme_regset = g_reg_set_sme_arm64; + + if (has_zt) { + m_sme_regnum_collection.push_back(sme_regnum); + m_dynamic_reg_infos.push_back(g_register_infos_sme2[0]); + m_dynamic_reg_infos[sme_regnum].byte_offset = + m_dynamic_reg_infos[sme_regnum - 1].byte_offset + + m_dynamic_reg_infos[sme_regnum - 1].byte_size; + m_dynamic_reg_infos[sme_regnum].kinds[lldb::eRegisterKindLLDB] = sme_regnum; + + sme_regset.num_registers += 1; + } + + m_per_regset_regnum_range[m_register_set_count] = + std::make_pair(first_sme_regnum, m_dynamic_reg_infos.size()); + m_dynamic_reg_sets.push_back(sme_regset); + m_dynamic_reg_sets.back().registers = m_sme_regnum_collection.data(); + + // When vg is written during streaming mode, svg will also change, as vg and + // svg in this state are both showing the streaming vector length. + // We model this as vg invalidating svg. In non-streaming mode this doesn't + // happen but to keep things simple we will invalidate svg anyway. + // + // This must be added now, rather than when vg is defined because SME is a + // dynamic set that may or may not be present. + static uint32_t vg_invalidates[] = {sme_regnum + 1 /*svg*/, + LLDB_INVALID_REGNUM}; + m_dynamic_reg_infos[GetRegNumSVEVG()].invalidate_regs = vg_invalidates; +} + +uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLengthSVE(uint32_t sve_vq) { + // sve_vq contains SVE Quad vector length in context of AArch64 SVE. + // SVE register infos if enabled cannot be disabled by selecting sve_vq = 0. + // Also if an invalid or previously set vector length is passed to this + // function then it will exit immediately with previously set vector length. + if (!VectorSizeIsValid(sve_vq) || m_vector_reg_vq == sve_vq) + return m_vector_reg_vq; + + // We cannot enable AArch64 only mode if SVE was enabled. + if (sve_vq == eVectorQuadwordAArch64 && + m_vector_reg_vq > eVectorQuadwordAArch64) + sve_vq = eVectorQuadwordAArch64SVE; + + m_vector_reg_vq = sve_vq; + + if (sve_vq == eVectorQuadwordAArch64) + return m_vector_reg_vq; + std::vector<lldb_private::RegisterInfo> ®_info_ref = + m_per_vq_reg_infos[sve_vq]; + + if (reg_info_ref.empty()) { + reg_info_ref = llvm::ArrayRef(m_register_info_p, m_register_info_count); + + uint32_t offset = SVE_REGS_DEFAULT_OFFSET_LINUX; + reg_info_ref[fpu_fpsr].byte_offset = offset; + reg_info_ref[fpu_fpcr].byte_offset = offset + 4; + reg_info_ref[sve_vg].byte_offset = offset + 8; + offset += 16; + + // Update Z registers size and offset + uint32_t s_reg_base = fpu_s0; + uint32_t d_reg_base = fpu_d0; + uint32_t v_reg_base = fpu_v0; + uint32_t z_reg_base = sve_z0; + + for (uint32_t index = 0; index < 32; index++) { + reg_info_ref[s_reg_base + index].byte_offset = offset; + reg_info_ref[d_reg_base + index].byte_offset = offset; + reg_info_ref[v_reg_base + index].byte_offset = offset; + reg_info_ref[z_reg_base + index].byte_offset = offset; + + reg_info_ref[z_reg_base + index].byte_size = sve_vq * SVE_QUAD_WORD_BYTES; + offset += reg_info_ref[z_reg_base + index].byte_size; + } + + // Update P registers and FFR size and offset + for (uint32_t it = sve_p0; it <= sve_ffr; it++) { + reg_info_ref[it].byte_offset = offset; + reg_info_ref[it].byte_size = sve_vq * SVE_QUAD_WORD_BYTES / 8; + offset += reg_info_ref[it].byte_size; + } + + for (uint32_t it = sve_ffr + 1; it < m_register_info_count; it++) { + reg_info_ref[it].byte_offset = offset; + offset += reg_info_ref[it].byte_size; + } + + m_per_vq_reg_infos[sve_vq] = reg_info_ref; + } + + m_register_info_p = m_per_vq_reg_infos[sve_vq].data(); + return m_vector_reg_vq; +} + +void RegisterInfoPOSIX_arm64::ConfigureVectorLengthZA(uint32_t za_vq) { + if (!VectorSizeIsValid(za_vq) || m_za_reg_vq == za_vq) + return; + + m_za_reg_vq = za_vq; + + // For SVE changes, we replace m_register_info_p completely. ZA is in a + // dynamic set and is just 1 register so we make an exception to const here. + lldb_private::RegisterInfo *non_const_reginfo = + const_cast<lldb_private::RegisterInfo *>(m_register_info_p); + non_const_reginfo[m_sme_regnum_collection[2]].byte_size = + (za_vq * 16) * (za_vq * 16); +} + +bool RegisterInfoPOSIX_arm64::IsSVEReg(unsigned reg) const { + if (m_vector_reg_vq > eVectorQuadwordAArch64) + return (sve_vg <= reg && reg <= sve_ffr); + else + return false; +} + +bool RegisterInfoPOSIX_arm64::IsSVEZReg(unsigned reg) const { + return (sve_z0 <= reg && reg <= sve_z31); +} + +bool RegisterInfoPOSIX_arm64::IsSVEPReg(unsigned reg) const { + return (sve_p0 <= reg && reg <= sve_p15); +} + +bool RegisterInfoPOSIX_arm64::IsSVERegVG(unsigned reg) const { + return sve_vg == reg; +} + +bool RegisterInfoPOSIX_arm64::IsSMERegZA(unsigned reg) const { + return reg == m_sme_regnum_collection[2]; +} + +bool RegisterInfoPOSIX_arm64::IsSMERegZT(unsigned reg) const { + // ZT0 is part of the SME register set only if SME2 is present. + return m_sme_regnum_collection.size() >= 4 && + reg == m_sme_regnum_collection[3]; +} + +bool RegisterInfoPOSIX_arm64::IsPAuthReg(unsigned reg) const { + return llvm::is_contained(pauth_regnum_collection, reg); +} + +bool RegisterInfoPOSIX_arm64::IsMTEReg(unsigned reg) const { + return llvm::is_contained(m_mte_regnum_collection, reg); +} + +bool RegisterInfoPOSIX_arm64::IsTLSReg(unsigned reg) const { + return llvm::is_contained(m_tls_regnum_collection, reg); +} + +bool RegisterInfoPOSIX_arm64::IsSMEReg(unsigned reg) const { + return llvm::is_contained(m_sme_regnum_collection, reg); +} + +uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; } + +uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; } + +uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPCR() const { return fpu_fpcr; } + +uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPSR() const { return fpu_fpsr; } + +uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEVG() const { return sve_vg; } + +uint32_t RegisterInfoPOSIX_arm64::GetRegNumSMESVG() const { + return m_sme_regnum_collection[1]; +} + +uint32_t RegisterInfoPOSIX_arm64::GetPAuthOffset() const { + return m_register_info_p[pauth_regnum_collection[0]].byte_offset; +} + +uint32_t RegisterInfoPOSIX_arm64::GetMTEOffset() const { + return m_register_info_p[m_mte_regnum_collection[0]].byte_offset; +} + +uint32_t RegisterInfoPOSIX_arm64::GetTLSOffset() const { + return m_register_info_p[m_tls_regnum_collection[0]].byte_offset; +} + +uint32_t RegisterInfoPOSIX_arm64::GetSMEOffset() const { + return m_register_info_p[m_sme_regnum_collection[0]].byte_offset; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h new file mode 100644 index 000000000000..3b8171042c73 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h @@ -0,0 +1,186 @@ +//===-- RegisterInfoPOSIX_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_ARM64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_ARM64_H + +#include "RegisterInfoAndSetInterface.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/Flags.h" +#include "lldb/lldb-private.h" +#include <map> + +enum class SVEState : uint8_t { Unknown, Disabled, FPSIMD, Full, Streaming }; + +class RegisterInfoPOSIX_arm64 + : public lldb_private::RegisterInfoAndSetInterface { +public: + enum { GPRegSet = 0, FPRegSet }; + + // AArch64 register set mask value + enum { + eRegsetMaskDefault = 0, + eRegsetMaskSVE = 1, + eRegsetMaskSSVE = 2, + eRegsetMaskPAuth = 4, + eRegsetMaskMTE = 8, + eRegsetMaskTLS = 16, + eRegsetMaskZA = 32, + eRegsetMaskZT = 64, + eRegsetMaskDynamic = ~1, + }; + + // AArch64 Register set FP/SIMD feature configuration + enum { + eVectorQuadwordAArch64, + eVectorQuadwordAArch64SVE, + eVectorQuadwordAArch64SVEMax = 256 + }; + + // based on RegisterContextDarwin_arm64.h + LLVM_PACKED_START + struct GPR { + uint64_t x[29]; // x0-x28 + uint64_t fp; // x29 + uint64_t lr; // x30 + uint64_t sp; // x31 + uint64_t pc; // pc + uint32_t cpsr; // cpsr + uint32_t pad; + }; + LLVM_PACKED_END + + // based on RegisterContextDarwin_arm64.h + struct VReg { + uint8_t bytes[16]; + }; + + // based on RegisterContextDarwin_arm64.h + struct FPU { + VReg v[32]; + uint32_t fpsr; + uint32_t fpcr; + }; + + // based on RegisterContextDarwin_arm64.h + struct EXC { + uint64_t far; // Virtual Fault Address + uint32_t esr; // Exception syndrome + uint32_t exception; // number of arm exception token + }; + + // based on RegisterContextDarwin_arm64.h + struct DBG { + uint64_t bvr[16]; + uint64_t bcr[16]; + uint64_t wvr[16]; + uint64_t wcr[16]; + uint64_t mdscr_el1; + }; + + RegisterInfoPOSIX_arm64(const lldb_private::ArchSpec &target_arch, + lldb_private::Flags opt_regsets); + + static size_t GetGPRSizeStatic(); + size_t GetGPRSize() const override { return GetGPRSizeStatic(); } + + size_t GetFPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + const lldb_private::RegisterSet * + GetRegisterSet(size_t reg_set) const override; + + size_t GetRegisterSetCount() const override; + + size_t GetRegisterSetFromRegisterIndex(uint32_t reg_index) const override; + + void AddRegSetPAuth(); + + void AddRegSetMTE(); + + void AddRegSetTLS(bool has_tpidr2); + + void AddRegSetSME(bool has_zt); + + uint32_t ConfigureVectorLengthSVE(uint32_t sve_vq); + + void ConfigureVectorLengthZA(uint32_t za_vq); + + bool VectorSizeIsValid(uint32_t vq) { + // coverity[unsigned_compare] + if (vq >= eVectorQuadwordAArch64 && vq <= eVectorQuadwordAArch64SVEMax) + return true; + return false; + } + + bool IsSVEPresent() const { return m_opt_regsets.AnySet(eRegsetMaskSVE); } + bool IsSSVEPresent() const { return m_opt_regsets.AnySet(eRegsetMaskSSVE); } + bool IsZAPresent() const { return m_opt_regsets.AnySet(eRegsetMaskZA); } + bool IsZTPresent() const { return m_opt_regsets.AnySet(eRegsetMaskZT); } + bool IsPAuthPresent() const { return m_opt_regsets.AnySet(eRegsetMaskPAuth); } + bool IsMTEPresent() const { return m_opt_regsets.AnySet(eRegsetMaskMTE); } + bool IsTLSPresent() const { return m_opt_regsets.AnySet(eRegsetMaskTLS); } + + bool IsSVEReg(unsigned reg) const; + bool IsSVEZReg(unsigned reg) const; + bool IsSVEPReg(unsigned reg) const; + bool IsSVERegVG(unsigned reg) const; + bool IsPAuthReg(unsigned reg) const; + bool IsMTEReg(unsigned reg) const; + bool IsTLSReg(unsigned reg) const; + bool IsSMEReg(unsigned reg) const; + bool IsSMERegZA(unsigned reg) const; + bool IsSMERegZT(unsigned reg) const; + + uint32_t GetRegNumSVEZ0() const; + uint32_t GetRegNumSVEFFR() const; + uint32_t GetRegNumFPCR() const; + uint32_t GetRegNumFPSR() const; + uint32_t GetRegNumSVEVG() const; + uint32_t GetRegNumSMESVG() const; + uint32_t GetPAuthOffset() const; + uint32_t GetMTEOffset() const; + uint32_t GetTLSOffset() const; + uint32_t GetSMEOffset() const; + +private: + typedef std::map<uint32_t, std::vector<lldb_private::RegisterInfo>> + per_vq_register_infos; + + per_vq_register_infos m_per_vq_reg_infos; + + uint32_t m_vector_reg_vq = eVectorQuadwordAArch64; + uint32_t m_za_reg_vq = eVectorQuadwordAArch64; + + // In normal operation this is const. Only when SVE or SME registers change + // size is it either replaced or the content modified. + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; + + const lldb_private::RegisterSet *m_register_set_p; + uint32_t m_register_set_count; + + // Contains pair of [start, end] register numbers of a register set with start + // and end included. + std::map<uint32_t, std::pair<uint32_t, uint32_t>> m_per_regset_regnum_range; + + lldb_private::Flags m_opt_regsets; + + std::vector<lldb_private::RegisterInfo> m_dynamic_reg_infos; + std::vector<lldb_private::RegisterSet> m_dynamic_reg_sets; + + std::vector<uint32_t> pauth_regnum_collection; + std::vector<uint32_t> m_mte_regnum_collection; + std::vector<uint32_t> m_tls_regnum_collection; + std::vector<uint32_t> m_sme_regnum_collection; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.cpp new file mode 100644 index 000000000000..6c723afe4b69 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.cpp @@ -0,0 +1,158 @@ +//===-- RegisterInfoPOSIX_loongarch64.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 <cassert> +#include <lldb/Utility/Flags.h> +#include <stddef.h> + +#include "lldb/lldb-defines.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterInfoPOSIX_loongarch64.h" + +#define GPR_OFFSET(idx) ((idx)*8 + 0) +#define FPR_OFFSET(idx) ((idx)*8 + sizeof(RegisterInfoPOSIX_loongarch64::GPR)) +#define FCC_OFFSET(idx) ((idx)*1 + 32 * 8 + sizeof(RegisterInfoPOSIX_loongarch64::GPR)) +#define FCSR_OFFSET (8 * 1 + 32 * 8 + sizeof(RegisterInfoPOSIX_loongarch64::GPR)) + +#define REG_CONTEXT_SIZE \ + (sizeof(RegisterInfoPOSIX_loongarch64::GPR) + \ + sizeof(RegisterInfoPOSIX_loongarch64::FPR)) + +#define DECLARE_REGISTER_INFOS_LOONGARCH64_STRUCT +#include "RegisterInfos_loongarch64.h" +#undef DECLARE_REGISTER_INFOS_LOONGARCH64_STRUCT + +const lldb_private::RegisterInfo * +RegisterInfoPOSIX_loongarch64::GetRegisterInfoPtr( + const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::loongarch64: + return g_register_infos_loongarch64; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +uint32_t RegisterInfoPOSIX_loongarch64::GetRegisterInfoCount( + const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::loongarch64: + return static_cast<uint32_t>(sizeof(g_register_infos_loongarch64) / + sizeof(g_register_infos_loongarch64[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +// Number of register sets provided by this context. +enum { + k_num_gpr_registers = gpr_last_loongarch - gpr_first_loongarch + 1, + k_num_fpr_registers = fpr_last_loongarch - fpr_first_loongarch + 1, + k_num_register_sets = 2 +}; + +// LoongArch64 general purpose registers. +static const uint32_t g_gpr_regnums_loongarch64[] = { + gpr_r0_loongarch, gpr_r1_loongarch, gpr_r2_loongarch, + gpr_r3_loongarch, gpr_r4_loongarch, gpr_r5_loongarch, + gpr_r6_loongarch, gpr_r7_loongarch, gpr_r8_loongarch, + gpr_r9_loongarch, gpr_r10_loongarch, gpr_r11_loongarch, + gpr_r12_loongarch, gpr_r13_loongarch, gpr_r14_loongarch, + gpr_r15_loongarch, gpr_r16_loongarch, gpr_r17_loongarch, + gpr_r18_loongarch, gpr_r19_loongarch, gpr_r20_loongarch, + gpr_r21_loongarch, gpr_r22_loongarch, gpr_r23_loongarch, + gpr_r24_loongarch, gpr_r25_loongarch, gpr_r26_loongarch, + gpr_r27_loongarch, gpr_r28_loongarch, gpr_r29_loongarch, + gpr_r30_loongarch, gpr_r31_loongarch, gpr_orig_a0_loongarch, + gpr_pc_loongarch, gpr_badv_loongarch, gpr_reserved0_loongarch, + gpr_reserved1_loongarch, gpr_reserved2_loongarch, gpr_reserved3_loongarch, + gpr_reserved4_loongarch, gpr_reserved5_loongarch, gpr_reserved6_loongarch, + gpr_reserved7_loongarch, gpr_reserved8_loongarch, gpr_reserved9_loongarch, + LLDB_INVALID_REGNUM}; + +static_assert(((sizeof g_gpr_regnums_loongarch64 / + sizeof g_gpr_regnums_loongarch64[0]) - + 1) == k_num_gpr_registers, + "g_gpr_regnums_loongarch64 has wrong number of register infos"); + +// LoongArch64 floating point registers. +static const uint32_t g_fpr_regnums_loongarch64[] = { + fpr_f0_loongarch, fpr_f1_loongarch, fpr_f2_loongarch, + fpr_f3_loongarch, fpr_f4_loongarch, fpr_f5_loongarch, + fpr_f6_loongarch, fpr_f7_loongarch, fpr_f8_loongarch, + fpr_f9_loongarch, fpr_f10_loongarch, fpr_f11_loongarch, + fpr_f12_loongarch, fpr_f13_loongarch, fpr_f14_loongarch, + fpr_f15_loongarch, fpr_f16_loongarch, fpr_f17_loongarch, + fpr_f18_loongarch, fpr_f19_loongarch, fpr_f20_loongarch, + fpr_f21_loongarch, fpr_f22_loongarch, fpr_f23_loongarch, + fpr_f24_loongarch, fpr_f25_loongarch, fpr_f26_loongarch, + fpr_f27_loongarch, fpr_f28_loongarch, fpr_f29_loongarch, + fpr_f30_loongarch, fpr_f31_loongarch, fpr_fcc0_loongarch, + fpr_fcc1_loongarch, fpr_fcc2_loongarch, fpr_fcc3_loongarch, + fpr_fcc4_loongarch, fpr_fcc5_loongarch, fpr_fcc6_loongarch, + fpr_fcc7_loongarch, fpr_fcsr_loongarch, LLDB_INVALID_REGNUM}; + +static_assert(((sizeof g_fpr_regnums_loongarch64 / + sizeof g_fpr_regnums_loongarch64[0]) - + 1) == k_num_fpr_registers, + "g_fpr_regnums_loongarch64 has wrong number of register infos"); + +// Register sets for LoongArch64. +static const lldb_private::RegisterSet + g_reg_sets_loongarch64[k_num_register_sets] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers, + g_gpr_regnums_loongarch64}, + {"Floating Point Registers", "fpr", k_num_fpr_registers, + g_fpr_regnums_loongarch64}}; + +RegisterInfoPOSIX_loongarch64::RegisterInfoPOSIX_loongarch64( + const lldb_private::ArchSpec &target_arch, lldb_private::Flags flags) + : lldb_private::RegisterInfoAndSetInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)) {} + +uint32_t RegisterInfoPOSIX_loongarch64::GetRegisterCount() const { + return m_register_info_count; +} + +size_t RegisterInfoPOSIX_loongarch64::GetGPRSize() const { + return sizeof(struct RegisterInfoPOSIX_loongarch64::GPR); +} + +size_t RegisterInfoPOSIX_loongarch64::GetFPRSize() const { + return sizeof(struct RegisterInfoPOSIX_loongarch64::FPR); +} + +const lldb_private::RegisterInfo * +RegisterInfoPOSIX_loongarch64::GetRegisterInfo() const { + return m_register_info_p; +} + +size_t RegisterInfoPOSIX_loongarch64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +size_t RegisterInfoPOSIX_loongarch64::GetRegisterSetFromRegisterIndex( + uint32_t reg_index) const { + // coverity[unsigned_compare] + if (reg_index >= gpr_first_loongarch && reg_index <= gpr_last_loongarch) + return GPRegSet; + if (reg_index >= fpr_first_loongarch && reg_index <= fpr_last_loongarch) + return FPRegSet; + return LLDB_INVALID_REGNUM; +} + +const lldb_private::RegisterSet * +RegisterInfoPOSIX_loongarch64::GetRegisterSet(size_t set_index) const { + if (set_index < GetRegisterSetCount()) + return &g_reg_sets_loongarch64[set_index]; + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h new file mode 100644 index 000000000000..a3338acbbc97 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h @@ -0,0 +1,69 @@ +//===-- RegisterInfoPOSIX_loongarch64.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_LOONGARCH64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_LOONGARCH64_H + +#include "RegisterInfoAndSetInterface.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" +#include <map> + +class RegisterInfoPOSIX_loongarch64 + : public lldb_private::RegisterInfoAndSetInterface { +public: + static const lldb_private::RegisterInfo * + GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch); + static uint32_t + GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch); + +public: + enum RegSetKind { + GPRegSet, + FPRegSet, + }; + + struct GPR { + uint64_t gpr[32]; + + uint64_t orig_a0; + uint64_t csr_era; + uint64_t csr_badv; + uint64_t reserved[10]; + }; + + struct FPR { + uint64_t fpr[32]; + uint64_t fcc; + uint32_t fcsr; + }; + + RegisterInfoPOSIX_loongarch64(const lldb_private::ArchSpec &target_arch, + lldb_private::Flags flags); + + size_t GetGPRSize() const override; + + size_t GetFPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + const lldb_private::RegisterSet * + GetRegisterSet(size_t reg_set) const override; + + size_t GetRegisterSetCount() const override; + + size_t GetRegisterSetFromRegisterIndex(uint32_t reg_index) const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp new file mode 100644 index 000000000000..159fd2856443 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.cpp @@ -0,0 +1,63 @@ +//===-- RegisterInfoPOSIX_ppc64le.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 <cassert> +#include <cstddef> +#include <vector> + +#include "lldb/lldb-defines.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterInfoPOSIX_ppc64le.h" + +// Include RegisterInfoPOSIX_ppc64le to declare our g_register_infos_ppc64le +#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT +#include "RegisterInfos_ppc64le.h" +#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT + +static const lldb_private::RegisterInfo * +GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::ppc64le: + return g_register_infos_ppc64le; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +static uint32_t +GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::ppc64le: + return static_cast<uint32_t>(sizeof(g_register_infos_ppc64le) / + sizeof(g_register_infos_ppc64le[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +RegisterInfoPOSIX_ppc64le::RegisterInfoPOSIX_ppc64le( + const lldb_private::ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)) {} + +size_t RegisterInfoPOSIX_ppc64le::GetGPRSize() const { + return sizeof(GPR); +} + +const lldb_private::RegisterInfo * +RegisterInfoPOSIX_ppc64le::GetRegisterInfo() const { + return m_register_info_p; +} + +uint32_t RegisterInfoPOSIX_ppc64le::GetRegisterCount() const { + return m_register_info_count; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h new file mode 100644 index 000000000000..98549ac0dda4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h @@ -0,0 +1,31 @@ +//===-- RegisterInfoPOSIX_ppc64le.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_PPC64LE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_PPC64LE_H + +#include "RegisterInfoInterface.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" + +class RegisterInfoPOSIX_ppc64le : public lldb_private::RegisterInfoInterface { +public: + RegisterInfoPOSIX_ppc64le(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp new file mode 100644 index 000000000000..3819401c543b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.cpp @@ -0,0 +1,142 @@ +//===-- RegisterInfoPOSIX_riscv64.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 <cassert> +#include <lldb/Utility/Flags.h> +#include <stddef.h> + +#include "lldb/lldb-defines.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterInfoPOSIX_riscv64.h" + +#define GPR_OFFSET(idx) ((idx)*8 + 0) +#define FPR_OFFSET(idx) ((idx)*8 + sizeof(RegisterInfoPOSIX_riscv64::GPR)) + +#define REG_CONTEXT_SIZE \ + (sizeof(RegisterInfoPOSIX_riscv64::GPR) + \ + sizeof(RegisterInfoPOSIX_riscv64::FPR)) + +#define DECLARE_REGISTER_INFOS_RISCV64_STRUCT +#include "RegisterInfos_riscv64.h" +#undef DECLARE_REGISTER_INFOS_RISCV64_STRUCT + +const lldb_private::RegisterInfo *RegisterInfoPOSIX_riscv64::GetRegisterInfoPtr( + const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::riscv64: + return g_register_infos_riscv64_le; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } +} + +uint32_t RegisterInfoPOSIX_riscv64::GetRegisterInfoCount( + const lldb_private::ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::riscv64: + return static_cast<uint32_t>(sizeof(g_register_infos_riscv64_le) / + sizeof(g_register_infos_riscv64_le[0])); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + +// Number of register sets provided by this context. +enum { + k_num_gpr_registers = gpr_last_riscv - gpr_first_riscv + 1, + k_num_fpr_registers = fpr_last_riscv - fpr_first_riscv + 1, + k_num_register_sets = 2 +}; + +// RISC-V64 general purpose registers. +static const uint32_t g_gpr_regnums_riscv64[] = { + gpr_pc_riscv, gpr_ra_riscv, gpr_sp_riscv, gpr_x3_riscv, + gpr_x4_riscv, gpr_x5_riscv, gpr_x6_riscv, gpr_x7_riscv, + gpr_fp_riscv, gpr_x9_riscv, gpr_x10_riscv, gpr_x11_riscv, + gpr_x12_riscv, gpr_x13_riscv, gpr_x14_riscv, gpr_x15_riscv, + gpr_x16_riscv, gpr_x17_riscv, gpr_x18_riscv, gpr_x19_riscv, + gpr_x20_riscv, gpr_x21_riscv, gpr_x22_riscv, gpr_x23_riscv, + gpr_x24_riscv, gpr_x25_riscv, gpr_x26_riscv, gpr_x27_riscv, + gpr_x28_riscv, gpr_x29_riscv, gpr_x30_riscv, gpr_x31_riscv, + gpr_x0_riscv, LLDB_INVALID_REGNUM}; + +static_assert(((sizeof g_gpr_regnums_riscv64 / + sizeof g_gpr_regnums_riscv64[0]) - + 1) == k_num_gpr_registers, + "g_gpr_regnums_riscv64 has wrong number of register infos"); + +// RISC-V64 floating point registers. +static const uint32_t g_fpr_regnums_riscv64[] = { + fpr_f0_riscv, fpr_f1_riscv, fpr_f2_riscv, fpr_f3_riscv, + fpr_f4_riscv, fpr_f5_riscv, fpr_f6_riscv, fpr_f7_riscv, + fpr_f8_riscv, fpr_f9_riscv, fpr_f10_riscv, fpr_f11_riscv, + fpr_f12_riscv, fpr_f13_riscv, fpr_f14_riscv, fpr_f15_riscv, + fpr_f16_riscv, fpr_f17_riscv, fpr_f18_riscv, fpr_f19_riscv, + fpr_f20_riscv, fpr_f21_riscv, fpr_f22_riscv, fpr_f23_riscv, + fpr_f24_riscv, fpr_f25_riscv, fpr_f26_riscv, fpr_f27_riscv, + fpr_f28_riscv, fpr_f29_riscv, fpr_f30_riscv, fpr_f31_riscv, + fpr_fcsr_riscv, LLDB_INVALID_REGNUM}; + +static_assert(((sizeof g_fpr_regnums_riscv64 / + sizeof g_fpr_regnums_riscv64[0]) - + 1) == k_num_fpr_registers, + "g_fpr_regnums_riscv64 has wrong number of register infos"); + +// Register sets for RISC-V64. +static const lldb_private::RegisterSet g_reg_sets_riscv64[k_num_register_sets] = + {{"General Purpose Registers", "gpr", k_num_gpr_registers, + g_gpr_regnums_riscv64}, + {"Floating Point Registers", "fpr", k_num_fpr_registers, + g_fpr_regnums_riscv64}}; + +RegisterInfoPOSIX_riscv64::RegisterInfoPOSIX_riscv64( + const lldb_private::ArchSpec &target_arch, lldb_private::Flags flags) + : lldb_private::RegisterInfoAndSetInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)) {} + +uint32_t RegisterInfoPOSIX_riscv64::GetRegisterCount() const { + return m_register_info_count; +} + +size_t RegisterInfoPOSIX_riscv64::GetGPRSize() const { + return sizeof(struct RegisterInfoPOSIX_riscv64::GPR); +} + +size_t RegisterInfoPOSIX_riscv64::GetFPRSize() const { + return sizeof(struct RegisterInfoPOSIX_riscv64::FPR); +} + +const lldb_private::RegisterInfo * +RegisterInfoPOSIX_riscv64::GetRegisterInfo() const { + return m_register_info_p; +} + +size_t RegisterInfoPOSIX_riscv64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +size_t RegisterInfoPOSIX_riscv64::GetRegisterSetFromRegisterIndex( + uint32_t reg_index) const { + // coverity[unsigned_compare] + if (reg_index >= gpr_first_riscv && reg_index <= gpr_last_riscv) + return GPRegSet; + if (reg_index >= fpr_first_riscv && reg_index <= fpr_last_riscv) + return FPRegSet; + return LLDB_INVALID_REGNUM; +} + +const lldb_private::RegisterSet * +RegisterInfoPOSIX_riscv64::GetRegisterSet(size_t set_index) const { + if (set_index < GetRegisterSetCount()) + return &g_reg_sets_riscv64[set_index]; + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h new file mode 100644 index 000000000000..4bf4bede0132 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h @@ -0,0 +1,66 @@ +//===-- RegisterInfoPOSIX_riscv64.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_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_RISCV64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_RISCV64_H + +#include "RegisterInfoAndSetInterface.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/lldb-private.h" +#include <map> + +class RegisterInfoPOSIX_riscv64 + : public lldb_private::RegisterInfoAndSetInterface { +public: + static const lldb_private::RegisterInfo * + GetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch); + static uint32_t + GetRegisterInfoCount(const lldb_private::ArchSpec &target_arch); + +public: + enum { GPRegSet = 0, FPRegSet }; + + struct GPR { + // note: gpr[0] is pc, not x0 + uint64_t gpr[32]; + }; + + struct FPR { + uint64_t fpr[32]; + uint32_t fcsr; + }; + + struct VPR { + // The size should be VLEN*32 in bits, but we don't have VLEN here. + void *vpr; + }; + + RegisterInfoPOSIX_riscv64(const lldb_private::ArchSpec &target_arch, + lldb_private::Flags flags); + + size_t GetGPRSize() const override; + + size_t GetFPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + const lldb_private::RegisterSet * + GetRegisterSet(size_t reg_set) const override; + + size_t GetRegisterSetCount() const override; + + size_t GetRegisterSetFromRegisterIndex(uint32_t reg_index) const override; + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h new file mode 100644 index 000000000000..ae6a442d7a1d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h @@ -0,0 +1,800 @@ +//===-- RegisterInfos_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 +// +//===----------------------------------------------------------------------===// + +#ifdef DECLARE_REGISTER_INFOS_ARM_STRUCT + +#include <cstddef> + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private.h" + +#include "Utility/ARM_DWARF_Registers.h" +#include "Utility/ARM_ehframe_Registers.h" + +using namespace lldb; +using namespace lldb_private; + +#ifndef GPR_OFFSET +#error GPR_OFFSET must be defined before including this header file +#endif + +#ifndef FPU_OFFSET +#error FPU_OFFSET must be defined before including this header file +#endif + +#ifndef FPSCR_OFFSET +#error FPSCR_OFFSET must be defined before including this header file +#endif + +#ifndef EXC_OFFSET +#error EXC_OFFSET_NAME must be defined before including this header file +#endif + +#ifndef DEFINE_DBG +#error DEFINE_DBG must be defined before including this header file +#endif + +enum { + gpr_r0 = 0, + gpr_r1, + gpr_r2, + gpr_r3, + gpr_r4, + gpr_r5, + gpr_r6, + gpr_r7, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_sp = gpr_r13, + gpr_r14, + gpr_lr = gpr_r14, + gpr_r15, + gpr_pc = gpr_r15, + gpr_cpsr, + + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + fpu_fpscr, + + fpu_d0, + fpu_d1, + fpu_d2, + fpu_d3, + fpu_d4, + fpu_d5, + fpu_d6, + fpu_d7, + fpu_d8, + fpu_d9, + fpu_d10, + fpu_d11, + fpu_d12, + fpu_d13, + fpu_d14, + fpu_d15, + fpu_d16, + fpu_d17, + fpu_d18, + fpu_d19, + fpu_d20, + fpu_d21, + fpu_d22, + fpu_d23, + fpu_d24, + fpu_d25, + fpu_d26, + fpu_d27, + fpu_d28, + fpu_d29, + fpu_d30, + fpu_d31, + + fpu_q0, + fpu_q1, + fpu_q2, + fpu_q3, + fpu_q4, + fpu_q5, + fpu_q6, + fpu_q7, + fpu_q8, + fpu_q9, + fpu_q10, + fpu_q11, + fpu_q12, + fpu_q13, + fpu_q14, + fpu_q15, + + exc_exception, + exc_fsr, + exc_far, + + dbg_bvr0, + dbg_bvr1, + dbg_bvr2, + dbg_bvr3, + dbg_bvr4, + dbg_bvr5, + dbg_bvr6, + dbg_bvr7, + dbg_bvr8, + dbg_bvr9, + dbg_bvr10, + dbg_bvr11, + dbg_bvr12, + dbg_bvr13, + dbg_bvr14, + dbg_bvr15, + + dbg_bcr0, + dbg_bcr1, + dbg_bcr2, + dbg_bcr3, + dbg_bcr4, + dbg_bcr5, + dbg_bcr6, + dbg_bcr7, + dbg_bcr8, + dbg_bcr9, + dbg_bcr10, + dbg_bcr11, + dbg_bcr12, + dbg_bcr13, + dbg_bcr14, + dbg_bcr15, + + dbg_wvr0, + dbg_wvr1, + dbg_wvr2, + dbg_wvr3, + dbg_wvr4, + dbg_wvr5, + dbg_wvr6, + dbg_wvr7, + dbg_wvr8, + dbg_wvr9, + dbg_wvr10, + dbg_wvr11, + dbg_wvr12, + dbg_wvr13, + dbg_wvr14, + dbg_wvr15, + + dbg_wcr0, + dbg_wcr1, + dbg_wcr2, + dbg_wcr3, + dbg_wcr4, + dbg_wcr5, + dbg_wcr6, + dbg_wcr7, + dbg_wcr8, + dbg_wcr9, + dbg_wcr10, + dbg_wcr11, + dbg_wcr12, + dbg_wcr13, + dbg_wcr14, + dbg_wcr15, + + k_num_registers +}; + +static uint32_t g_s0_invalidates[] = {fpu_d0, fpu_q0, LLDB_INVALID_REGNUM}; +static uint32_t g_s1_invalidates[] = {fpu_d0, fpu_q0, LLDB_INVALID_REGNUM}; +static uint32_t g_s2_invalidates[] = {fpu_d1, fpu_q0, LLDB_INVALID_REGNUM}; +static uint32_t g_s3_invalidates[] = {fpu_d1, fpu_q0, LLDB_INVALID_REGNUM}; +static uint32_t g_s4_invalidates[] = {fpu_d2, fpu_q1, LLDB_INVALID_REGNUM}; +static uint32_t g_s5_invalidates[] = {fpu_d2, fpu_q1, LLDB_INVALID_REGNUM}; +static uint32_t g_s6_invalidates[] = {fpu_d3, fpu_q1, LLDB_INVALID_REGNUM}; +static uint32_t g_s7_invalidates[] = {fpu_d3, fpu_q1, LLDB_INVALID_REGNUM}; +static uint32_t g_s8_invalidates[] = {fpu_d4, fpu_q2, LLDB_INVALID_REGNUM}; +static uint32_t g_s9_invalidates[] = {fpu_d4, fpu_q2, LLDB_INVALID_REGNUM}; +static uint32_t g_s10_invalidates[] = {fpu_d5, fpu_q2, LLDB_INVALID_REGNUM}; +static uint32_t g_s11_invalidates[] = {fpu_d5, fpu_q2, LLDB_INVALID_REGNUM}; +static uint32_t g_s12_invalidates[] = {fpu_d6, fpu_q3, LLDB_INVALID_REGNUM}; +static uint32_t g_s13_invalidates[] = {fpu_d6, fpu_q3, LLDB_INVALID_REGNUM}; +static uint32_t g_s14_invalidates[] = {fpu_d7, fpu_q3, LLDB_INVALID_REGNUM}; +static uint32_t g_s15_invalidates[] = {fpu_d7, fpu_q3, LLDB_INVALID_REGNUM}; +static uint32_t g_s16_invalidates[] = {fpu_d8, fpu_q4, LLDB_INVALID_REGNUM}; +static uint32_t g_s17_invalidates[] = {fpu_d8, fpu_q4, LLDB_INVALID_REGNUM}; +static uint32_t g_s18_invalidates[] = {fpu_d9, fpu_q4, LLDB_INVALID_REGNUM}; +static uint32_t g_s19_invalidates[] = {fpu_d9, fpu_q4, LLDB_INVALID_REGNUM}; +static uint32_t g_s20_invalidates[] = {fpu_d10, fpu_q5, LLDB_INVALID_REGNUM}; +static uint32_t g_s21_invalidates[] = {fpu_d10, fpu_q5, LLDB_INVALID_REGNUM}; +static uint32_t g_s22_invalidates[] = {fpu_d11, fpu_q5, LLDB_INVALID_REGNUM}; +static uint32_t g_s23_invalidates[] = {fpu_d11, fpu_q5, LLDB_INVALID_REGNUM}; +static uint32_t g_s24_invalidates[] = {fpu_d12, fpu_q6, LLDB_INVALID_REGNUM}; +static uint32_t g_s25_invalidates[] = {fpu_d12, fpu_q6, LLDB_INVALID_REGNUM}; +static uint32_t g_s26_invalidates[] = {fpu_d13, fpu_q6, LLDB_INVALID_REGNUM}; +static uint32_t g_s27_invalidates[] = {fpu_d13, fpu_q6, LLDB_INVALID_REGNUM}; +static uint32_t g_s28_invalidates[] = {fpu_d14, fpu_q7, LLDB_INVALID_REGNUM}; +static uint32_t g_s29_invalidates[] = {fpu_d14, fpu_q7, LLDB_INVALID_REGNUM}; +static uint32_t g_s30_invalidates[] = {fpu_d15, fpu_q7, LLDB_INVALID_REGNUM}; +static uint32_t g_s31_invalidates[] = {fpu_d15, fpu_q7, LLDB_INVALID_REGNUM}; + +static uint32_t g_d0_invalidates[] = {fpu_q0, fpu_s0, fpu_s1, + LLDB_INVALID_REGNUM}; +static uint32_t g_d1_invalidates[] = {fpu_q0, fpu_s2, fpu_s3, + LLDB_INVALID_REGNUM}; +static uint32_t g_d2_invalidates[] = {fpu_q1, fpu_s4, fpu_s5, + LLDB_INVALID_REGNUM}; +static uint32_t g_d3_invalidates[] = {fpu_q1, fpu_s6, fpu_s7, + LLDB_INVALID_REGNUM}; +static uint32_t g_d4_invalidates[] = {fpu_q2, fpu_s8, fpu_s9, + LLDB_INVALID_REGNUM}; +static uint32_t g_d5_invalidates[] = {fpu_q2, fpu_s10, fpu_s11, + LLDB_INVALID_REGNUM}; +static uint32_t g_d6_invalidates[] = {fpu_q3, fpu_s12, fpu_s13, + LLDB_INVALID_REGNUM}; +static uint32_t g_d7_invalidates[] = {fpu_q3, fpu_s14, fpu_s15, + LLDB_INVALID_REGNUM}; +static uint32_t g_d8_invalidates[] = {fpu_q4, fpu_s16, fpu_s17, + LLDB_INVALID_REGNUM}; +static uint32_t g_d9_invalidates[] = {fpu_q4, fpu_s18, fpu_s19, + LLDB_INVALID_REGNUM}; +static uint32_t g_d10_invalidates[] = {fpu_q5, fpu_s20, fpu_s21, + LLDB_INVALID_REGNUM}; +static uint32_t g_d11_invalidates[] = {fpu_q5, fpu_s22, fpu_s23, + LLDB_INVALID_REGNUM}; +static uint32_t g_d12_invalidates[] = {fpu_q6, fpu_s24, fpu_s25, + LLDB_INVALID_REGNUM}; +static uint32_t g_d13_invalidates[] = {fpu_q6, fpu_s26, fpu_s27, + LLDB_INVALID_REGNUM}; +static uint32_t g_d14_invalidates[] = {fpu_q7, fpu_s28, fpu_s29, + LLDB_INVALID_REGNUM}; +static uint32_t g_d15_invalidates[] = {fpu_q7, fpu_s30, fpu_s31, + LLDB_INVALID_REGNUM}; +static uint32_t g_d16_invalidates[] = {fpu_q8, LLDB_INVALID_REGNUM}; +static uint32_t g_d17_invalidates[] = {fpu_q8, LLDB_INVALID_REGNUM}; +static uint32_t g_d18_invalidates[] = {fpu_q9, LLDB_INVALID_REGNUM}; +static uint32_t g_d19_invalidates[] = {fpu_q9, LLDB_INVALID_REGNUM}; +static uint32_t g_d20_invalidates[] = {fpu_q10, LLDB_INVALID_REGNUM}; +static uint32_t g_d21_invalidates[] = {fpu_q10, LLDB_INVALID_REGNUM}; +static uint32_t g_d22_invalidates[] = {fpu_q11, LLDB_INVALID_REGNUM}; +static uint32_t g_d23_invalidates[] = {fpu_q11, LLDB_INVALID_REGNUM}; +static uint32_t g_d24_invalidates[] = {fpu_q12, LLDB_INVALID_REGNUM}; +static uint32_t g_d25_invalidates[] = {fpu_q12, LLDB_INVALID_REGNUM}; +static uint32_t g_d26_invalidates[] = {fpu_q13, LLDB_INVALID_REGNUM}; +static uint32_t g_d27_invalidates[] = {fpu_q13, LLDB_INVALID_REGNUM}; +static uint32_t g_d28_invalidates[] = {fpu_q14, LLDB_INVALID_REGNUM}; +static uint32_t g_d29_invalidates[] = {fpu_q14, LLDB_INVALID_REGNUM}; +static uint32_t g_d30_invalidates[] = {fpu_q15, LLDB_INVALID_REGNUM}; +static uint32_t g_d31_invalidates[] = {fpu_q15, LLDB_INVALID_REGNUM}; + +static uint32_t g_q0_invalidates[] = { + fpu_d0, fpu_d1, fpu_s0, fpu_s1, fpu_s2, fpu_s3, LLDB_INVALID_REGNUM}; +static uint32_t g_q1_invalidates[] = { + fpu_d2, fpu_d3, fpu_s4, fpu_s5, fpu_s6, fpu_s7, LLDB_INVALID_REGNUM}; +static uint32_t g_q2_invalidates[] = { + fpu_d4, fpu_d5, fpu_s8, fpu_s9, fpu_s10, fpu_s11, LLDB_INVALID_REGNUM}; +static uint32_t g_q3_invalidates[] = { + fpu_d6, fpu_d7, fpu_s12, fpu_s13, fpu_s14, fpu_s15, LLDB_INVALID_REGNUM}; +static uint32_t g_q4_invalidates[] = { + fpu_d8, fpu_d9, fpu_s16, fpu_s17, fpu_s18, fpu_s19, LLDB_INVALID_REGNUM}; +static uint32_t g_q5_invalidates[] = { + fpu_d10, fpu_d11, fpu_s20, fpu_s21, fpu_s22, fpu_s23, LLDB_INVALID_REGNUM}; +static uint32_t g_q6_invalidates[] = { + fpu_d12, fpu_d13, fpu_s24, fpu_s25, fpu_s26, fpu_s27, LLDB_INVALID_REGNUM}; +static uint32_t g_q7_invalidates[] = { + fpu_d14, fpu_d15, fpu_s28, fpu_s29, fpu_s30, fpu_s31, LLDB_INVALID_REGNUM}; +static uint32_t g_q8_invalidates[] = {fpu_d16, fpu_d17, LLDB_INVALID_REGNUM}; +static uint32_t g_q9_invalidates[] = {fpu_d18, fpu_d19, LLDB_INVALID_REGNUM}; +static uint32_t g_q10_invalidates[] = {fpu_d20, fpu_d21, LLDB_INVALID_REGNUM}; +static uint32_t g_q11_invalidates[] = {fpu_d22, fpu_d23, LLDB_INVALID_REGNUM}; +static uint32_t g_q12_invalidates[] = {fpu_d24, fpu_d25, LLDB_INVALID_REGNUM}; +static uint32_t g_q13_invalidates[] = {fpu_d26, fpu_d27, LLDB_INVALID_REGNUM}; +static uint32_t g_q14_invalidates[] = {fpu_d28, fpu_d29, LLDB_INVALID_REGNUM}; +static uint32_t g_q15_invalidates[] = {fpu_d30, fpu_d31, LLDB_INVALID_REGNUM}; + +static uint32_t g_q0_contained[] = {fpu_q0, LLDB_INVALID_REGNUM}; +static uint32_t g_q1_contained[] = {fpu_q1, LLDB_INVALID_REGNUM}; +static uint32_t g_q2_contained[] = {fpu_q2, LLDB_INVALID_REGNUM}; +static uint32_t g_q3_contained[] = {fpu_q3, LLDB_INVALID_REGNUM}; +static uint32_t g_q4_contained[] = {fpu_q4, LLDB_INVALID_REGNUM}; +static uint32_t g_q5_contained[] = {fpu_q5, LLDB_INVALID_REGNUM}; +static uint32_t g_q6_contained[] = {fpu_q6, LLDB_INVALID_REGNUM}; +static uint32_t g_q7_contained[] = {fpu_q7, LLDB_INVALID_REGNUM}; +static uint32_t g_q8_contained[] = {fpu_q8, LLDB_INVALID_REGNUM}; +static uint32_t g_q9_contained[] = {fpu_q9, LLDB_INVALID_REGNUM}; +static uint32_t g_q10_contained[] = {fpu_q10, LLDB_INVALID_REGNUM}; +static uint32_t g_q11_contained[] = {fpu_q11, LLDB_INVALID_REGNUM}; +static uint32_t g_q12_contained[] = {fpu_q12, LLDB_INVALID_REGNUM}; +static uint32_t g_q13_contained[] = {fpu_q13, LLDB_INVALID_REGNUM}; +static uint32_t g_q14_contained[] = {fpu_q14, LLDB_INVALID_REGNUM}; +static uint32_t g_q15_contained[] = {fpu_q15, LLDB_INVALID_REGNUM}; + +#define FPU_REG(name, size, offset, qreg) \ + { \ + #name, nullptr, size, FPU_OFFSET(offset), eEncodingIEEE754, eFormatFloat, \ + {LLDB_INVALID_REGNUM, dwarf_##name, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, fpu_##name }, \ + g_##qreg##_contained, g_##name##_invalidates, nullptr, \ + } + +#define FPU_QREG(name, offset) \ + { \ + #name, nullptr, 16, FPU_OFFSET(offset), eEncodingVector, \ + eFormatVectorOfUInt8, \ + {LLDB_INVALID_REGNUM, dwarf_##name, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, fpu_##name }, \ + nullptr, g_##name##_invalidates, nullptr, \ + } + +static RegisterInfo g_register_infos_arm[] = { + // NAME ALT SZ OFFSET ENCODING FORMAT + // EH_FRAME DWARF GENERIC + // PROCESS PLUGIN LLDB NATIVE VALUE REGS INVALIDATE REGS + // =========== ======= == ============== ================ + // ==================== =================== =================== + // ========================== =================== ============= + // ============== ================= + { + "r0", + nullptr, + 4, + GPR_OFFSET(0), + eEncodingUint, + eFormatHex, + {ehframe_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, + gpr_r0}, + nullptr, + nullptr, + nullptr, + }, + { + "r1", + nullptr, + 4, + GPR_OFFSET(1), + eEncodingUint, + eFormatHex, + {ehframe_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, + gpr_r1}, + nullptr, + nullptr, + nullptr, + }, + { + "r2", + nullptr, + 4, + GPR_OFFSET(2), + eEncodingUint, + eFormatHex, + {ehframe_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, + gpr_r2}, + nullptr, + nullptr, + nullptr, + }, + { + "r3", + nullptr, + 4, + GPR_OFFSET(3), + eEncodingUint, + eFormatHex, + {ehframe_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, + gpr_r3}, + nullptr, + nullptr, + nullptr, + }, + { + "r4", + nullptr, + 4, + GPR_OFFSET(4), + eEncodingUint, + eFormatHex, + {ehframe_r4, dwarf_r4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r4}, + nullptr, + nullptr, + nullptr, + }, + { + "r5", + nullptr, + 4, + GPR_OFFSET(5), + eEncodingUint, + eFormatHex, + {ehframe_r5, dwarf_r5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r5}, + nullptr, + nullptr, + nullptr, + }, + { + "r6", + nullptr, + 4, + GPR_OFFSET(6), + eEncodingUint, + eFormatHex, + {ehframe_r6, dwarf_r6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r6}, + nullptr, + nullptr, + nullptr, + }, + { + "r7", + nullptr, + 4, + GPR_OFFSET(7), + eEncodingUint, + eFormatHex, + {ehframe_r7, dwarf_r7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r7}, + nullptr, + nullptr, + nullptr, + }, + { + "r8", + nullptr, + 4, + GPR_OFFSET(8), + eEncodingUint, + eFormatHex, + {ehframe_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r8}, + nullptr, + nullptr, + nullptr, + }, + { + "r9", + nullptr, + 4, + GPR_OFFSET(9), + eEncodingUint, + eFormatHex, + {ehframe_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r9}, + nullptr, + nullptr, + nullptr, + }, + { + "r10", + nullptr, + 4, + GPR_OFFSET(10), + eEncodingUint, + eFormatHex, + {ehframe_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r10}, + nullptr, + nullptr, + nullptr, + }, + { + "r11", + nullptr, + 4, + GPR_OFFSET(11), + eEncodingUint, + eFormatHex, + {ehframe_r11, dwarf_r11, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, + gpr_r11}, + nullptr, + nullptr, + nullptr, + }, + { + "r12", + nullptr, + 4, + GPR_OFFSET(12), + eEncodingUint, + eFormatHex, + {ehframe_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + gpr_r12}, + nullptr, + nullptr, + nullptr, + }, + { + "sp", + "r13", + 4, + GPR_OFFSET(13), + eEncodingUint, + eFormatHex, + {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, + gpr_sp}, + nullptr, + nullptr, + nullptr, + }, + { + "lr", + "r14", + 4, + GPR_OFFSET(14), + eEncodingUint, + eFormatHex, + {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM, + gpr_lr}, + nullptr, + nullptr, + nullptr, + }, + { + "pc", + "r15", + 4, + GPR_OFFSET(15), + eEncodingUint, + eFormatHex, + {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, + gpr_pc}, + nullptr, + nullptr, + nullptr, + }, + { + "cpsr", + "psr", + 4, + GPR_OFFSET(16), + eEncodingUint, + eFormatHex, + {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, + LLDB_INVALID_REGNUM, gpr_cpsr}, + nullptr, + nullptr, + nullptr, + }, + + FPU_REG(s0, 4, 0, q0), + FPU_REG(s1, 4, 1, q0), + FPU_REG(s2, 4, 2, q0), + FPU_REG(s3, 4, 3, q0), + FPU_REG(s4, 4, 4, q1), + FPU_REG(s5, 4, 5, q1), + FPU_REG(s6, 4, 6, q1), + FPU_REG(s7, 4, 7, q1), + FPU_REG(s8, 4, 8, q2), + FPU_REG(s9, 4, 9, q2), + FPU_REG(s10, 4, 10, q2), + FPU_REG(s11, 4, 11, q2), + FPU_REG(s12, 4, 12, q3), + FPU_REG(s13, 4, 13, q3), + FPU_REG(s14, 4, 14, q3), + FPU_REG(s15, 4, 15, q3), + FPU_REG(s16, 4, 16, q4), + FPU_REG(s17, 4, 17, q4), + FPU_REG(s18, 4, 18, q4), + FPU_REG(s19, 4, 19, q4), + FPU_REG(s20, 4, 20, q5), + FPU_REG(s21, 4, 21, q5), + FPU_REG(s22, 4, 22, q5), + FPU_REG(s23, 4, 23, q5), + FPU_REG(s24, 4, 24, q6), + FPU_REG(s25, 4, 25, q6), + FPU_REG(s26, 4, 26, q6), + FPU_REG(s27, 4, 27, q6), + FPU_REG(s28, 4, 28, q7), + FPU_REG(s29, 4, 29, q7), + FPU_REG(s30, 4, 30, q7), + FPU_REG(s31, 4, 31, q7), + + { + "fpscr", + nullptr, + 4, + FPSCR_OFFSET, + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, fpu_fpscr}, + nullptr, + nullptr, + nullptr, + }, + + FPU_REG(d0, 8, 0, q0), + FPU_REG(d1, 8, 2, q0), + FPU_REG(d2, 8, 4, q1), + FPU_REG(d3, 8, 6, q1), + FPU_REG(d4, 8, 8, q2), + FPU_REG(d5, 8, 10, q2), + FPU_REG(d6, 8, 12, q3), + FPU_REG(d7, 8, 14, q3), + FPU_REG(d8, 8, 16, q4), + FPU_REG(d9, 8, 18, q4), + FPU_REG(d10, 8, 20, q5), + FPU_REG(d11, 8, 22, q5), + FPU_REG(d12, 8, 24, q6), + FPU_REG(d13, 8, 26, q6), + FPU_REG(d14, 8, 28, q7), + FPU_REG(d15, 8, 30, q7), + FPU_REG(d16, 8, 32, q8), + FPU_REG(d17, 8, 34, q8), + FPU_REG(d18, 8, 36, q9), + FPU_REG(d19, 8, 38, q9), + FPU_REG(d20, 8, 40, q10), + FPU_REG(d21, 8, 42, q10), + FPU_REG(d22, 8, 44, q11), + FPU_REG(d23, 8, 46, q11), + FPU_REG(d24, 8, 48, q12), + FPU_REG(d25, 8, 50, q12), + FPU_REG(d26, 8, 52, q13), + FPU_REG(d27, 8, 54, q13), + FPU_REG(d28, 8, 56, q14), + FPU_REG(d29, 8, 58, q14), + FPU_REG(d30, 8, 60, q15), + FPU_REG(d31, 8, 62, q15), + + FPU_QREG(q0, 0), + FPU_QREG(q1, 4), + FPU_QREG(q2, 8), + FPU_QREG(q3, 12), + FPU_QREG(q4, 16), + FPU_QREG(q5, 20), + FPU_QREG(q6, 24), + FPU_QREG(q7, 28), + FPU_QREG(q8, 32), + FPU_QREG(q9, 36), + FPU_QREG(q10, 40), + FPU_QREG(q11, 44), + FPU_QREG(q12, 48), + FPU_QREG(q13, 52), + FPU_QREG(q14, 56), + FPU_QREG(q15, 60), + + { + "exception", + nullptr, + 4, + EXC_OFFSET(0), + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_exception}, + nullptr, + nullptr, + nullptr, + }, + { + "fsr", + nullptr, + 4, + EXC_OFFSET(1), + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_fsr}, + nullptr, + nullptr, + nullptr, + }, + { + "far", + nullptr, + 4, + EXC_OFFSET(2), + eEncodingUint, + eFormatHex, + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, exc_far}, + nullptr, + nullptr, + nullptr, + }, + + {DEFINE_DBG(bvr, 0)}, + {DEFINE_DBG(bvr, 1)}, + {DEFINE_DBG(bvr, 2)}, + {DEFINE_DBG(bvr, 3)}, + {DEFINE_DBG(bvr, 4)}, + {DEFINE_DBG(bvr, 5)}, + {DEFINE_DBG(bvr, 6)}, + {DEFINE_DBG(bvr, 7)}, + {DEFINE_DBG(bvr, 8)}, + {DEFINE_DBG(bvr, 9)}, + {DEFINE_DBG(bvr, 10)}, + {DEFINE_DBG(bvr, 11)}, + {DEFINE_DBG(bvr, 12)}, + {DEFINE_DBG(bvr, 13)}, + {DEFINE_DBG(bvr, 14)}, + {DEFINE_DBG(bvr, 15)}, + + {DEFINE_DBG(bcr, 0)}, + {DEFINE_DBG(bcr, 1)}, + {DEFINE_DBG(bcr, 2)}, + {DEFINE_DBG(bcr, 3)}, + {DEFINE_DBG(bcr, 4)}, + {DEFINE_DBG(bcr, 5)}, + {DEFINE_DBG(bcr, 6)}, + {DEFINE_DBG(bcr, 7)}, + {DEFINE_DBG(bcr, 8)}, + {DEFINE_DBG(bcr, 9)}, + {DEFINE_DBG(bcr, 10)}, + {DEFINE_DBG(bcr, 11)}, + {DEFINE_DBG(bcr, 12)}, + {DEFINE_DBG(bcr, 13)}, + {DEFINE_DBG(bcr, 14)}, + {DEFINE_DBG(bcr, 15)}, + + {DEFINE_DBG(wvr, 0)}, + {DEFINE_DBG(wvr, 1)}, + {DEFINE_DBG(wvr, 2)}, + {DEFINE_DBG(wvr, 3)}, + {DEFINE_DBG(wvr, 4)}, + {DEFINE_DBG(wvr, 5)}, + {DEFINE_DBG(wvr, 6)}, + {DEFINE_DBG(wvr, 7)}, + {DEFINE_DBG(wvr, 8)}, + {DEFINE_DBG(wvr, 9)}, + {DEFINE_DBG(wvr, 10)}, + {DEFINE_DBG(wvr, 11)}, + {DEFINE_DBG(wvr, 12)}, + {DEFINE_DBG(wvr, 13)}, + {DEFINE_DBG(wvr, 14)}, + {DEFINE_DBG(wvr, 15)}, + + {DEFINE_DBG(wcr, 0)}, + {DEFINE_DBG(wcr, 1)}, + {DEFINE_DBG(wcr, 2)}, + {DEFINE_DBG(wcr, 3)}, + {DEFINE_DBG(wcr, 4)}, + {DEFINE_DBG(wcr, 5)}, + {DEFINE_DBG(wcr, 6)}, + {DEFINE_DBG(wcr, 7)}, + {DEFINE_DBG(wcr, 8)}, + {DEFINE_DBG(wcr, 9)}, + {DEFINE_DBG(wcr, 10)}, + {DEFINE_DBG(wcr, 11)}, + {DEFINE_DBG(wcr, 12)}, + {DEFINE_DBG(wcr, 13)}, + {DEFINE_DBG(wcr, 14)}, + {DEFINE_DBG(wcr, 15)}}; + +#endif // DECLARE_REGISTER_INFOS_ARM_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h new file mode 100644 index 000000000000..c9c4d7ceae55 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h @@ -0,0 +1,793 @@ +//===-- RegisterInfos_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 +// +//===----------------------------------------------------------------------===// + +#ifdef DECLARE_REGISTER_INFOS_ARM64_STRUCT + +#include <cstddef> + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private.h" + +#include "Utility/ARM64_DWARF_Registers.h" +#include "Utility/ARM64_ehframe_Registers.h" + +#ifndef GPR_OFFSET +#error GPR_OFFSET must be defined before including this header file +#endif + +#ifndef GPR_OFFSET_NAME +#error GPR_OFFSET_NAME must be defined before including this header file +#endif + +#ifndef FPU_OFFSET +#error FPU_OFFSET must be defined before including this header file +#endif + +#ifndef FPU_OFFSET_NAME +#error FPU_OFFSET_NAME must be defined before including this header file +#endif + +#ifndef EXC_OFFSET_NAME +#error EXC_OFFSET_NAME must be defined before including this header file +#endif + +#ifndef DBG_OFFSET_NAME +#error DBG_OFFSET_NAME must be defined before including this header file +#endif + +#ifndef DEFINE_DBG +#error DEFINE_DBG must be defined before including this header file +#endif + +// Offsets for a little-endian layout of the register context +#define GPR_W_PSEUDO_REG_ENDIAN_OFFSET 0 +#define FPU_S_PSEUDO_REG_ENDIAN_OFFSET 0 +#define FPU_D_PSEUDO_REG_ENDIAN_OFFSET 0 + +enum { + gpr_x0 = 0, + gpr_x1, + gpr_x2, + gpr_x3, + gpr_x4, + gpr_x5, + gpr_x6, + gpr_x7, + gpr_x8, + gpr_x9, + gpr_x10, + gpr_x11, + gpr_x12, + gpr_x13, + gpr_x14, + gpr_x15, + gpr_x16, + gpr_x17, + gpr_x18, + gpr_x19, + gpr_x20, + gpr_x21, + gpr_x22, + gpr_x23, + gpr_x24, + gpr_x25, + gpr_x26, + gpr_x27, + gpr_x28, + gpr_x29 = 29, + gpr_fp = gpr_x29, + gpr_x30 = 30, + gpr_lr = gpr_x30, + gpr_ra = gpr_x30, + gpr_x31 = 31, + gpr_sp = gpr_x31, + gpr_pc = 32, + gpr_cpsr, + + gpr_w0, + gpr_w1, + gpr_w2, + gpr_w3, + gpr_w4, + gpr_w5, + gpr_w6, + gpr_w7, + gpr_w8, + gpr_w9, + gpr_w10, + gpr_w11, + gpr_w12, + gpr_w13, + gpr_w14, + gpr_w15, + gpr_w16, + gpr_w17, + gpr_w18, + gpr_w19, + gpr_w20, + gpr_w21, + gpr_w22, + gpr_w23, + gpr_w24, + gpr_w25, + gpr_w26, + gpr_w27, + gpr_w28, + + fpu_v0, + fpu_v1, + fpu_v2, + fpu_v3, + fpu_v4, + fpu_v5, + fpu_v6, + fpu_v7, + fpu_v8, + fpu_v9, + fpu_v10, + fpu_v11, + fpu_v12, + fpu_v13, + fpu_v14, + fpu_v15, + fpu_v16, + fpu_v17, + fpu_v18, + fpu_v19, + fpu_v20, + fpu_v21, + fpu_v22, + fpu_v23, + fpu_v24, + fpu_v25, + fpu_v26, + fpu_v27, + fpu_v28, + fpu_v29, + fpu_v30, + fpu_v31, + + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + + fpu_d0, + fpu_d1, + fpu_d2, + fpu_d3, + fpu_d4, + fpu_d5, + fpu_d6, + fpu_d7, + fpu_d8, + fpu_d9, + fpu_d10, + fpu_d11, + fpu_d12, + fpu_d13, + fpu_d14, + fpu_d15, + fpu_d16, + fpu_d17, + fpu_d18, + fpu_d19, + fpu_d20, + fpu_d21, + fpu_d22, + fpu_d23, + fpu_d24, + fpu_d25, + fpu_d26, + fpu_d27, + fpu_d28, + fpu_d29, + fpu_d30, + fpu_d31, + + fpu_fpsr, + fpu_fpcr, + + exc_far, + exc_esr, + exc_exception, + + dbg_bvr0, + dbg_bvr1, + dbg_bvr2, + dbg_bvr3, + dbg_bvr4, + dbg_bvr5, + dbg_bvr6, + dbg_bvr7, + dbg_bvr8, + dbg_bvr9, + dbg_bvr10, + dbg_bvr11, + dbg_bvr12, + dbg_bvr13, + dbg_bvr14, + dbg_bvr15, + + dbg_bcr0, + dbg_bcr1, + dbg_bcr2, + dbg_bcr3, + dbg_bcr4, + dbg_bcr5, + dbg_bcr6, + dbg_bcr7, + dbg_bcr8, + dbg_bcr9, + dbg_bcr10, + dbg_bcr11, + dbg_bcr12, + dbg_bcr13, + dbg_bcr14, + dbg_bcr15, + + dbg_wvr0, + dbg_wvr1, + dbg_wvr2, + dbg_wvr3, + dbg_wvr4, + dbg_wvr5, + dbg_wvr6, + dbg_wvr7, + dbg_wvr8, + dbg_wvr9, + dbg_wvr10, + dbg_wvr11, + dbg_wvr12, + dbg_wvr13, + dbg_wvr14, + dbg_wvr15, + + dbg_wcr0, + dbg_wcr1, + dbg_wcr2, + dbg_wcr3, + dbg_wcr4, + dbg_wcr5, + dbg_wcr6, + dbg_wcr7, + dbg_wcr8, + dbg_wcr9, + dbg_wcr10, + dbg_wcr11, + dbg_wcr12, + dbg_wcr13, + dbg_wcr14, + dbg_wcr15, + + k_num_registers +}; + +static uint32_t g_contained_x0[] = {gpr_x0, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x1[] = {gpr_x1, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x2[] = {gpr_x2, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x3[] = {gpr_x3, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x4[] = {gpr_x4, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x5[] = {gpr_x5, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x6[] = {gpr_x6, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x7[] = {gpr_x7, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x8[] = {gpr_x8, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x9[] = {gpr_x9, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x10[] = {gpr_x10, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x11[] = {gpr_x11, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x12[] = {gpr_x12, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x13[] = {gpr_x13, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x14[] = {gpr_x14, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x15[] = {gpr_x15, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x16[] = {gpr_x16, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x17[] = {gpr_x17, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x18[] = {gpr_x18, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x19[] = {gpr_x19, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x20[] = {gpr_x20, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x21[] = {gpr_x21, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x22[] = {gpr_x22, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x23[] = {gpr_x23, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x24[] = {gpr_x24, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x25[] = {gpr_x25, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x26[] = {gpr_x26, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x27[] = {gpr_x27, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_x28[] = {gpr_x28, LLDB_INVALID_REGNUM}; + +static uint32_t g_w0_invalidates[] = {gpr_x0, LLDB_INVALID_REGNUM}; +static uint32_t g_w1_invalidates[] = {gpr_x1, LLDB_INVALID_REGNUM}; +static uint32_t g_w2_invalidates[] = {gpr_x2, LLDB_INVALID_REGNUM}; +static uint32_t g_w3_invalidates[] = {gpr_x3, LLDB_INVALID_REGNUM}; +static uint32_t g_w4_invalidates[] = {gpr_x4, LLDB_INVALID_REGNUM}; +static uint32_t g_w5_invalidates[] = {gpr_x5, LLDB_INVALID_REGNUM}; +static uint32_t g_w6_invalidates[] = {gpr_x6, LLDB_INVALID_REGNUM}; +static uint32_t g_w7_invalidates[] = {gpr_x7, LLDB_INVALID_REGNUM}; +static uint32_t g_w8_invalidates[] = {gpr_x8, LLDB_INVALID_REGNUM}; +static uint32_t g_w9_invalidates[] = {gpr_x9, LLDB_INVALID_REGNUM}; +static uint32_t g_w10_invalidates[] = {gpr_x10, LLDB_INVALID_REGNUM}; +static uint32_t g_w11_invalidates[] = {gpr_x11, LLDB_INVALID_REGNUM}; +static uint32_t g_w12_invalidates[] = {gpr_x12, LLDB_INVALID_REGNUM}; +static uint32_t g_w13_invalidates[] = {gpr_x13, LLDB_INVALID_REGNUM}; +static uint32_t g_w14_invalidates[] = {gpr_x14, LLDB_INVALID_REGNUM}; +static uint32_t g_w15_invalidates[] = {gpr_x15, LLDB_INVALID_REGNUM}; +static uint32_t g_w16_invalidates[] = {gpr_x16, LLDB_INVALID_REGNUM}; +static uint32_t g_w17_invalidates[] = {gpr_x17, LLDB_INVALID_REGNUM}; +static uint32_t g_w18_invalidates[] = {gpr_x18, LLDB_INVALID_REGNUM}; +static uint32_t g_w19_invalidates[] = {gpr_x19, LLDB_INVALID_REGNUM}; +static uint32_t g_w20_invalidates[] = {gpr_x20, LLDB_INVALID_REGNUM}; +static uint32_t g_w21_invalidates[] = {gpr_x21, LLDB_INVALID_REGNUM}; +static uint32_t g_w22_invalidates[] = {gpr_x22, LLDB_INVALID_REGNUM}; +static uint32_t g_w23_invalidates[] = {gpr_x23, LLDB_INVALID_REGNUM}; +static uint32_t g_w24_invalidates[] = {gpr_x24, LLDB_INVALID_REGNUM}; +static uint32_t g_w25_invalidates[] = {gpr_x25, LLDB_INVALID_REGNUM}; +static uint32_t g_w26_invalidates[] = {gpr_x26, LLDB_INVALID_REGNUM}; +static uint32_t g_w27_invalidates[] = {gpr_x27, LLDB_INVALID_REGNUM}; +static uint32_t g_w28_invalidates[] = {gpr_x28, LLDB_INVALID_REGNUM}; + +static uint32_t g_contained_v0[] = {fpu_v0, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v1[] = {fpu_v1, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v2[] = {fpu_v2, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v3[] = {fpu_v3, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v4[] = {fpu_v4, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v5[] = {fpu_v5, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v6[] = {fpu_v6, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v7[] = {fpu_v7, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v8[] = {fpu_v8, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v9[] = {fpu_v9, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v10[] = {fpu_v10, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v11[] = {fpu_v11, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v12[] = {fpu_v12, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v13[] = {fpu_v13, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v14[] = {fpu_v14, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v15[] = {fpu_v15, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v16[] = {fpu_v16, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v17[] = {fpu_v17, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v18[] = {fpu_v18, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v19[] = {fpu_v19, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v20[] = {fpu_v20, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v21[] = {fpu_v21, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v22[] = {fpu_v22, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v23[] = {fpu_v23, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v24[] = {fpu_v24, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v25[] = {fpu_v25, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v26[] = {fpu_v26, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v27[] = {fpu_v27, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v28[] = {fpu_v28, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v29[] = {fpu_v29, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v30[] = {fpu_v30, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_v31[] = {fpu_v31, LLDB_INVALID_REGNUM}; + +static uint32_t g_s0_invalidates[] = {fpu_v0, fpu_d0, LLDB_INVALID_REGNUM}; +static uint32_t g_s1_invalidates[] = {fpu_v1, fpu_d1, LLDB_INVALID_REGNUM}; +static uint32_t g_s2_invalidates[] = {fpu_v2, fpu_d2, LLDB_INVALID_REGNUM}; +static uint32_t g_s3_invalidates[] = {fpu_v3, fpu_d3, LLDB_INVALID_REGNUM}; +static uint32_t g_s4_invalidates[] = {fpu_v4, fpu_d4, LLDB_INVALID_REGNUM}; +static uint32_t g_s5_invalidates[] = {fpu_v5, fpu_d5, LLDB_INVALID_REGNUM}; +static uint32_t g_s6_invalidates[] = {fpu_v6, fpu_d6, LLDB_INVALID_REGNUM}; +static uint32_t g_s7_invalidates[] = {fpu_v7, fpu_d7, LLDB_INVALID_REGNUM}; +static uint32_t g_s8_invalidates[] = {fpu_v8, fpu_d8, LLDB_INVALID_REGNUM}; +static uint32_t g_s9_invalidates[] = {fpu_v9, fpu_d9, LLDB_INVALID_REGNUM}; +static uint32_t g_s10_invalidates[] = {fpu_v10, fpu_d10, LLDB_INVALID_REGNUM}; +static uint32_t g_s11_invalidates[] = {fpu_v11, fpu_d11, LLDB_INVALID_REGNUM}; +static uint32_t g_s12_invalidates[] = {fpu_v12, fpu_d12, LLDB_INVALID_REGNUM}; +static uint32_t g_s13_invalidates[] = {fpu_v13, fpu_d13, LLDB_INVALID_REGNUM}; +static uint32_t g_s14_invalidates[] = {fpu_v14, fpu_d14, LLDB_INVALID_REGNUM}; +static uint32_t g_s15_invalidates[] = {fpu_v15, fpu_d15, LLDB_INVALID_REGNUM}; +static uint32_t g_s16_invalidates[] = {fpu_v16, fpu_d16, LLDB_INVALID_REGNUM}; +static uint32_t g_s17_invalidates[] = {fpu_v17, fpu_d17, LLDB_INVALID_REGNUM}; +static uint32_t g_s18_invalidates[] = {fpu_v18, fpu_d18, LLDB_INVALID_REGNUM}; +static uint32_t g_s19_invalidates[] = {fpu_v19, fpu_d19, LLDB_INVALID_REGNUM}; +static uint32_t g_s20_invalidates[] = {fpu_v20, fpu_d20, LLDB_INVALID_REGNUM}; +static uint32_t g_s21_invalidates[] = {fpu_v21, fpu_d21, LLDB_INVALID_REGNUM}; +static uint32_t g_s22_invalidates[] = {fpu_v22, fpu_d22, LLDB_INVALID_REGNUM}; +static uint32_t g_s23_invalidates[] = {fpu_v23, fpu_d23, LLDB_INVALID_REGNUM}; +static uint32_t g_s24_invalidates[] = {fpu_v24, fpu_d24, LLDB_INVALID_REGNUM}; +static uint32_t g_s25_invalidates[] = {fpu_v25, fpu_d25, LLDB_INVALID_REGNUM}; +static uint32_t g_s26_invalidates[] = {fpu_v26, fpu_d26, LLDB_INVALID_REGNUM}; +static uint32_t g_s27_invalidates[] = {fpu_v27, fpu_d27, LLDB_INVALID_REGNUM}; +static uint32_t g_s28_invalidates[] = {fpu_v28, fpu_d28, LLDB_INVALID_REGNUM}; +static uint32_t g_s29_invalidates[] = {fpu_v29, fpu_d29, LLDB_INVALID_REGNUM}; +static uint32_t g_s30_invalidates[] = {fpu_v30, fpu_d30, LLDB_INVALID_REGNUM}; +static uint32_t g_s31_invalidates[] = {fpu_v31, fpu_d31, LLDB_INVALID_REGNUM}; + +static uint32_t g_d0_invalidates[] = {fpu_v0, fpu_s0, LLDB_INVALID_REGNUM}; +static uint32_t g_d1_invalidates[] = {fpu_v1, fpu_s1, LLDB_INVALID_REGNUM}; +static uint32_t g_d2_invalidates[] = {fpu_v2, fpu_s2, LLDB_INVALID_REGNUM}; +static uint32_t g_d3_invalidates[] = {fpu_v3, fpu_s3, LLDB_INVALID_REGNUM}; +static uint32_t g_d4_invalidates[] = {fpu_v4, fpu_s4, LLDB_INVALID_REGNUM}; +static uint32_t g_d5_invalidates[] = {fpu_v5, fpu_s5, LLDB_INVALID_REGNUM}; +static uint32_t g_d6_invalidates[] = {fpu_v6, fpu_s6, LLDB_INVALID_REGNUM}; +static uint32_t g_d7_invalidates[] = {fpu_v7, fpu_s7, LLDB_INVALID_REGNUM}; +static uint32_t g_d8_invalidates[] = {fpu_v8, fpu_s8, LLDB_INVALID_REGNUM}; +static uint32_t g_d9_invalidates[] = {fpu_v9, fpu_s9, LLDB_INVALID_REGNUM}; +static uint32_t g_d10_invalidates[] = {fpu_v10, fpu_s10, LLDB_INVALID_REGNUM}; +static uint32_t g_d11_invalidates[] = {fpu_v11, fpu_s11, LLDB_INVALID_REGNUM}; +static uint32_t g_d12_invalidates[] = {fpu_v12, fpu_s12, LLDB_INVALID_REGNUM}; +static uint32_t g_d13_invalidates[] = {fpu_v13, fpu_s13, LLDB_INVALID_REGNUM}; +static uint32_t g_d14_invalidates[] = {fpu_v14, fpu_s14, LLDB_INVALID_REGNUM}; +static uint32_t g_d15_invalidates[] = {fpu_v15, fpu_s15, LLDB_INVALID_REGNUM}; +static uint32_t g_d16_invalidates[] = {fpu_v16, fpu_s16, LLDB_INVALID_REGNUM}; +static uint32_t g_d17_invalidates[] = {fpu_v17, fpu_s17, LLDB_INVALID_REGNUM}; +static uint32_t g_d18_invalidates[] = {fpu_v18, fpu_s18, LLDB_INVALID_REGNUM}; +static uint32_t g_d19_invalidates[] = {fpu_v19, fpu_s19, LLDB_INVALID_REGNUM}; +static uint32_t g_d20_invalidates[] = {fpu_v20, fpu_s20, LLDB_INVALID_REGNUM}; +static uint32_t g_d21_invalidates[] = {fpu_v21, fpu_s21, LLDB_INVALID_REGNUM}; +static uint32_t g_d22_invalidates[] = {fpu_v22, fpu_s22, LLDB_INVALID_REGNUM}; +static uint32_t g_d23_invalidates[] = {fpu_v23, fpu_s23, LLDB_INVALID_REGNUM}; +static uint32_t g_d24_invalidates[] = {fpu_v24, fpu_s24, LLDB_INVALID_REGNUM}; +static uint32_t g_d25_invalidates[] = {fpu_v25, fpu_s25, LLDB_INVALID_REGNUM}; +static uint32_t g_d26_invalidates[] = {fpu_v26, fpu_s26, LLDB_INVALID_REGNUM}; +static uint32_t g_d27_invalidates[] = {fpu_v27, fpu_s27, LLDB_INVALID_REGNUM}; +static uint32_t g_d28_invalidates[] = {fpu_v28, fpu_s28, LLDB_INVALID_REGNUM}; +static uint32_t g_d29_invalidates[] = {fpu_v29, fpu_s29, LLDB_INVALID_REGNUM}; +static uint32_t g_d30_invalidates[] = {fpu_v30, fpu_s30, LLDB_INVALID_REGNUM}; +static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM}; + +// Generates register kinds array with DWARF, EH frame and generic kind +#define MISC_KIND(reg, type, generic_kind) \ + { \ + arm64_ehframe::reg, arm64_dwarf::reg, generic_kind, LLDB_INVALID_REGNUM, \ + type##_##reg \ + } + +// Generates register kinds array for registers with only lldb kind +#define LLDB_KIND(lldb_kind) \ + { \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_kind \ + } + +// Generates register kinds array for registers with only lldb kind +#define KIND_ALL_INVALID \ + { \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM \ + } + +// Generates register kinds array for vector registers +#define GPR64_KIND(reg, generic_kind) MISC_KIND(reg, gpr, generic_kind) +#define VREG_KIND(reg) MISC_KIND(reg, fpu, LLDB_INVALID_REGNUM) +#define MISC_GPR_KIND(lldb_kind) MISC_KIND(cpsr, gpr, LLDB_REGNUM_GENERIC_FLAGS) +#define MISC_FPU_KIND(lldb_kind) LLDB_KIND(lldb_kind) +#define MISC_EXC_KIND(lldb_kind) LLDB_KIND(lldb_kind) + +// clang-format off + +// Defines a 64-bit general purpose register +#define DEFINE_GPR64(reg, generic_kind) \ + { \ + #reg, nullptr, 8, GPR_OFFSET(gpr_##reg), lldb::eEncodingUint, \ + lldb::eFormatHex, GPR64_KIND(reg, generic_kind), nullptr, nullptr, \ + nullptr, \ + } + +// Defines a 64-bit general purpose register +#define DEFINE_GPR64_ALT(reg, alt, generic_kind) \ + { \ + #reg, #alt, 8, GPR_OFFSET(gpr_##reg), lldb::eEncodingUint, \ + lldb::eFormatHex, GPR64_KIND(reg, generic_kind), nullptr, nullptr, \ + nullptr, \ + } + +// Defines a 32-bit general purpose pseudo register +#define DEFINE_GPR32(wreg, xreg) \ + { \ + #wreg, nullptr, 4, \ + GPR_OFFSET(gpr_##xreg) + GPR_W_PSEUDO_REG_ENDIAN_OFFSET, \ + lldb::eEncodingUint, lldb::eFormatHex, LLDB_KIND(gpr_##wreg), \ + g_contained_##xreg, g_##wreg##_invalidates, nullptr, \ + } + +// Defines a vector register with 16-byte size +#define DEFINE_VREG(reg) \ + { \ + #reg, nullptr, 16, FPU_OFFSET(fpu_##reg - fpu_v0), lldb::eEncodingVector, \ + lldb::eFormatVectorOfUInt8, VREG_KIND(reg), nullptr, nullptr, nullptr, \ + } + +// Defines S and D pseudo registers mapping over corresponding vector register +#define DEFINE_FPU_PSEUDO(reg, size, offset, vreg) \ + { \ + #reg, nullptr, size, FPU_OFFSET(fpu_##vreg - fpu_v0) + offset, \ + lldb::eEncodingIEEE754, lldb::eFormatFloat, LLDB_KIND(fpu_##reg), \ + g_contained_##vreg, g_##reg##_invalidates, nullptr, \ + } + +// Defines miscellaneous status and control registers like cpsr, fpsr etc +#define DEFINE_MISC_REGS(reg, size, TYPE, lldb_kind) \ + { \ + #reg, nullptr, size, TYPE##_OFFSET_NAME(reg), lldb::eEncodingUint, \ + lldb::eFormatHex, MISC_##TYPE##_KIND(lldb_kind), nullptr, nullptr, \ + nullptr, \ + } + +// Defines pointer authentication mask registers +#define DEFINE_EXTENSION_REG(reg) \ + { \ + #reg, nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex, \ + KIND_ALL_INVALID, nullptr, nullptr, nullptr, \ + } + +static lldb_private::RegisterInfo g_register_infos_arm64_le[] = { + // DEFINE_GPR64(name, GENERIC KIND) + DEFINE_GPR64(x0, LLDB_REGNUM_GENERIC_ARG1), + DEFINE_GPR64(x1, LLDB_REGNUM_GENERIC_ARG2), + DEFINE_GPR64(x2, LLDB_REGNUM_GENERIC_ARG3), + DEFINE_GPR64(x3, LLDB_REGNUM_GENERIC_ARG4), + DEFINE_GPR64(x4, LLDB_REGNUM_GENERIC_ARG5), + DEFINE_GPR64(x5, LLDB_REGNUM_GENERIC_ARG6), + DEFINE_GPR64(x6, LLDB_REGNUM_GENERIC_ARG7), + DEFINE_GPR64(x7, LLDB_REGNUM_GENERIC_ARG8), + DEFINE_GPR64(x8, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x9, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x10, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x11, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x12, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x13, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x14, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x15, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x16, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x17, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x18, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x19, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x20, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x21, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x22, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x23, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x24, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x25, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x26, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x27, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x28, LLDB_INVALID_REGNUM), + // DEFINE_GPR64(name, GENERIC KIND) + DEFINE_GPR64_ALT(fp, x29, LLDB_REGNUM_GENERIC_FP), + DEFINE_GPR64_ALT(lr, x30, LLDB_REGNUM_GENERIC_RA), + DEFINE_GPR64_ALT(sp, x31, LLDB_REGNUM_GENERIC_SP), + DEFINE_GPR64(pc, LLDB_REGNUM_GENERIC_PC), + + // DEFINE_MISC_REGS(name, size, TYPE, lldb kind) + DEFINE_MISC_REGS(cpsr, 4, GPR, gpr_cpsr), + + // DEFINE_GPR32(name, parent name) + DEFINE_GPR32(w0, x0), + DEFINE_GPR32(w1, x1), + DEFINE_GPR32(w2, x2), + DEFINE_GPR32(w3, x3), + DEFINE_GPR32(w4, x4), + DEFINE_GPR32(w5, x5), + DEFINE_GPR32(w6, x6), + DEFINE_GPR32(w7, x7), + DEFINE_GPR32(w8, x8), + DEFINE_GPR32(w9, x9), + DEFINE_GPR32(w10, x10), + DEFINE_GPR32(w11, x11), + DEFINE_GPR32(w12, x12), + DEFINE_GPR32(w13, x13), + DEFINE_GPR32(w14, x14), + DEFINE_GPR32(w15, x15), + DEFINE_GPR32(w16, x16), + DEFINE_GPR32(w17, x17), + DEFINE_GPR32(w18, x18), + DEFINE_GPR32(w19, x19), + DEFINE_GPR32(w20, x20), + DEFINE_GPR32(w21, x21), + DEFINE_GPR32(w22, x22), + DEFINE_GPR32(w23, x23), + DEFINE_GPR32(w24, x24), + DEFINE_GPR32(w25, x25), + DEFINE_GPR32(w26, x26), + DEFINE_GPR32(w27, x27), + DEFINE_GPR32(w28, x28), + + // DEFINE_VREG(name) + DEFINE_VREG(v0), + DEFINE_VREG(v1), + DEFINE_VREG(v2), + DEFINE_VREG(v3), + DEFINE_VREG(v4), + DEFINE_VREG(v5), + DEFINE_VREG(v6), + DEFINE_VREG(v7), + DEFINE_VREG(v8), + DEFINE_VREG(v9), + DEFINE_VREG(v10), + DEFINE_VREG(v11), + DEFINE_VREG(v12), + DEFINE_VREG(v13), + DEFINE_VREG(v14), + DEFINE_VREG(v15), + DEFINE_VREG(v16), + DEFINE_VREG(v17), + DEFINE_VREG(v18), + DEFINE_VREG(v19), + DEFINE_VREG(v20), + DEFINE_VREG(v21), + DEFINE_VREG(v22), + DEFINE_VREG(v23), + DEFINE_VREG(v24), + DEFINE_VREG(v25), + DEFINE_VREG(v26), + DEFINE_VREG(v27), + DEFINE_VREG(v28), + DEFINE_VREG(v29), + DEFINE_VREG(v30), + DEFINE_VREG(v31), + + // DEFINE_FPU_PSEUDO(name, size, ENDIAN OFFSET, parent register) + DEFINE_FPU_PSEUDO(s0, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v0), + DEFINE_FPU_PSEUDO(s1, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v1), + DEFINE_FPU_PSEUDO(s2, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v2), + DEFINE_FPU_PSEUDO(s3, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v3), + DEFINE_FPU_PSEUDO(s4, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v4), + DEFINE_FPU_PSEUDO(s5, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v5), + DEFINE_FPU_PSEUDO(s6, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v6), + DEFINE_FPU_PSEUDO(s7, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v7), + DEFINE_FPU_PSEUDO(s8, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v8), + DEFINE_FPU_PSEUDO(s9, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v9), + DEFINE_FPU_PSEUDO(s10, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v10), + DEFINE_FPU_PSEUDO(s11, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v11), + DEFINE_FPU_PSEUDO(s12, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v12), + DEFINE_FPU_PSEUDO(s13, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v13), + DEFINE_FPU_PSEUDO(s14, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v14), + DEFINE_FPU_PSEUDO(s15, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v15), + DEFINE_FPU_PSEUDO(s16, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v16), + DEFINE_FPU_PSEUDO(s17, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v17), + DEFINE_FPU_PSEUDO(s18, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v18), + DEFINE_FPU_PSEUDO(s19, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v19), + DEFINE_FPU_PSEUDO(s20, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v20), + DEFINE_FPU_PSEUDO(s21, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v21), + DEFINE_FPU_PSEUDO(s22, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v22), + DEFINE_FPU_PSEUDO(s23, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v23), + DEFINE_FPU_PSEUDO(s24, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v24), + DEFINE_FPU_PSEUDO(s25, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v25), + DEFINE_FPU_PSEUDO(s26, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v26), + DEFINE_FPU_PSEUDO(s27, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v27), + DEFINE_FPU_PSEUDO(s28, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v28), + DEFINE_FPU_PSEUDO(s29, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v29), + DEFINE_FPU_PSEUDO(s30, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v30), + DEFINE_FPU_PSEUDO(s31, 4, FPU_S_PSEUDO_REG_ENDIAN_OFFSET, v31), + + DEFINE_FPU_PSEUDO(d0, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v0), + DEFINE_FPU_PSEUDO(d1, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v1), + DEFINE_FPU_PSEUDO(d2, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v2), + DEFINE_FPU_PSEUDO(d3, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v3), + DEFINE_FPU_PSEUDO(d4, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v4), + DEFINE_FPU_PSEUDO(d5, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v5), + DEFINE_FPU_PSEUDO(d6, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v6), + DEFINE_FPU_PSEUDO(d7, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v7), + DEFINE_FPU_PSEUDO(d8, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v8), + DEFINE_FPU_PSEUDO(d9, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v9), + DEFINE_FPU_PSEUDO(d10, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v10), + DEFINE_FPU_PSEUDO(d11, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v11), + DEFINE_FPU_PSEUDO(d12, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v12), + DEFINE_FPU_PSEUDO(d13, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v13), + DEFINE_FPU_PSEUDO(d14, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v14), + DEFINE_FPU_PSEUDO(d15, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v15), + DEFINE_FPU_PSEUDO(d16, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v16), + DEFINE_FPU_PSEUDO(d17, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v17), + DEFINE_FPU_PSEUDO(d18, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v18), + DEFINE_FPU_PSEUDO(d19, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v19), + DEFINE_FPU_PSEUDO(d20, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v20), + DEFINE_FPU_PSEUDO(d21, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v21), + DEFINE_FPU_PSEUDO(d22, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v22), + DEFINE_FPU_PSEUDO(d23, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v23), + DEFINE_FPU_PSEUDO(d24, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v24), + DEFINE_FPU_PSEUDO(d25, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v25), + DEFINE_FPU_PSEUDO(d26, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v26), + DEFINE_FPU_PSEUDO(d27, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v27), + DEFINE_FPU_PSEUDO(d28, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v28), + DEFINE_FPU_PSEUDO(d29, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v29), + DEFINE_FPU_PSEUDO(d30, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v30), + DEFINE_FPU_PSEUDO(d31, 8, FPU_D_PSEUDO_REG_ENDIAN_OFFSET, v31), + + // DEFINE_MISC_REGS(name, size, TYPE, lldb kind) + DEFINE_MISC_REGS(fpsr, 4, FPU, fpu_fpsr), + DEFINE_MISC_REGS(fpcr, 4, FPU, fpu_fpcr), + DEFINE_MISC_REGS(far, 8, EXC, exc_far), + DEFINE_MISC_REGS(esr, 4, EXC, exc_esr), + DEFINE_MISC_REGS(exception, 4, EXC, exc_exception), + + {DEFINE_DBG(bvr, 0)}, + {DEFINE_DBG(bvr, 1)}, + {DEFINE_DBG(bvr, 2)}, + {DEFINE_DBG(bvr, 3)}, + {DEFINE_DBG(bvr, 4)}, + {DEFINE_DBG(bvr, 5)}, + {DEFINE_DBG(bvr, 6)}, + {DEFINE_DBG(bvr, 7)}, + {DEFINE_DBG(bvr, 8)}, + {DEFINE_DBG(bvr, 9)}, + {DEFINE_DBG(bvr, 10)}, + {DEFINE_DBG(bvr, 11)}, + {DEFINE_DBG(bvr, 12)}, + {DEFINE_DBG(bvr, 13)}, + {DEFINE_DBG(bvr, 14)}, + {DEFINE_DBG(bvr, 15)}, + + {DEFINE_DBG(bcr, 0)}, + {DEFINE_DBG(bcr, 1)}, + {DEFINE_DBG(bcr, 2)}, + {DEFINE_DBG(bcr, 3)}, + {DEFINE_DBG(bcr, 4)}, + {DEFINE_DBG(bcr, 5)}, + {DEFINE_DBG(bcr, 6)}, + {DEFINE_DBG(bcr, 7)}, + {DEFINE_DBG(bcr, 8)}, + {DEFINE_DBG(bcr, 9)}, + {DEFINE_DBG(bcr, 10)}, + {DEFINE_DBG(bcr, 11)}, + {DEFINE_DBG(bcr, 12)}, + {DEFINE_DBG(bcr, 13)}, + {DEFINE_DBG(bcr, 14)}, + {DEFINE_DBG(bcr, 15)}, + + {DEFINE_DBG(wvr, 0)}, + {DEFINE_DBG(wvr, 1)}, + {DEFINE_DBG(wvr, 2)}, + {DEFINE_DBG(wvr, 3)}, + {DEFINE_DBG(wvr, 4)}, + {DEFINE_DBG(wvr, 5)}, + {DEFINE_DBG(wvr, 6)}, + {DEFINE_DBG(wvr, 7)}, + {DEFINE_DBG(wvr, 8)}, + {DEFINE_DBG(wvr, 9)}, + {DEFINE_DBG(wvr, 10)}, + {DEFINE_DBG(wvr, 11)}, + {DEFINE_DBG(wvr, 12)}, + {DEFINE_DBG(wvr, 13)}, + {DEFINE_DBG(wvr, 14)}, + {DEFINE_DBG(wvr, 15)}, + + {DEFINE_DBG(wcr, 0)}, + {DEFINE_DBG(wcr, 1)}, + {DEFINE_DBG(wcr, 2)}, + {DEFINE_DBG(wcr, 3)}, + {DEFINE_DBG(wcr, 4)}, + {DEFINE_DBG(wcr, 5)}, + {DEFINE_DBG(wcr, 6)}, + {DEFINE_DBG(wcr, 7)}, + {DEFINE_DBG(wcr, 8)}, + {DEFINE_DBG(wcr, 9)}, + {DEFINE_DBG(wcr, 10)}, + {DEFINE_DBG(wcr, 11)}, + {DEFINE_DBG(wcr, 12)}, + {DEFINE_DBG(wcr, 13)}, + {DEFINE_DBG(wcr, 14)}, + {DEFINE_DBG(wcr, 15)} +}; +// clang-format on + +#endif // DECLARE_REGISTER_INFOS_ARM64_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64_sve.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64_sve.h new file mode 100644 index 000000000000..283c4c17e760 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64_sve.h @@ -0,0 +1,573 @@ +//===-- RegisterInfos_arm64_sve.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 +// +//===----------------------------------------------------------------------===// + +#ifdef DECLARE_REGISTER_INFOS_ARM64_STRUCT + +enum { + sve_vg = exc_far, + + sve_z0, + sve_z1, + sve_z2, + sve_z3, + sve_z4, + sve_z5, + sve_z6, + sve_z7, + sve_z8, + sve_z9, + sve_z10, + sve_z11, + sve_z12, + sve_z13, + sve_z14, + sve_z15, + sve_z16, + sve_z17, + sve_z18, + sve_z19, + sve_z20, + sve_z21, + sve_z22, + sve_z23, + sve_z24, + sve_z25, + sve_z26, + sve_z27, + sve_z28, + sve_z29, + sve_z30, + sve_z31, + + sve_p0, + sve_p1, + sve_p2, + sve_p3, + sve_p4, + sve_p5, + sve_p6, + sve_p7, + sve_p8, + sve_p9, + sve_p10, + sve_p11, + sve_p12, + sve_p13, + sve_p14, + sve_p15, + + sve_ffr, +}; + +#ifndef SVE_OFFSET_VG +#error SVE_OFFSET_VG must be defined before including this header file +#endif + +static uint32_t g_sve_s0_invalidates[] = {sve_z0, fpu_v0, fpu_d0, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s1_invalidates[] = {sve_z1, fpu_v1, fpu_d1, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s2_invalidates[] = {sve_z2, fpu_v2, fpu_d2, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s3_invalidates[] = {sve_z3, fpu_v3, fpu_d3, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s4_invalidates[] = {sve_z4, fpu_v4, fpu_d4, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s5_invalidates[] = {sve_z5, fpu_v5, fpu_d5, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s6_invalidates[] = {sve_z6, fpu_v6, fpu_d6, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s7_invalidates[] = {sve_z7, fpu_v7, fpu_d7, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s8_invalidates[] = {sve_z8, fpu_v8, fpu_d8, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s9_invalidates[] = {sve_z9, fpu_v9, fpu_d9, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s10_invalidates[] = {sve_z10, fpu_v10, fpu_d10, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s11_invalidates[] = {sve_z11, fpu_v11, fpu_d11, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s12_invalidates[] = {sve_z12, fpu_v12, fpu_d12, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s13_invalidates[] = {sve_z13, fpu_v13, fpu_d13, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s14_invalidates[] = {sve_z14, fpu_v14, fpu_d14, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s15_invalidates[] = {sve_z15, fpu_v15, fpu_d15, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s16_invalidates[] = {sve_z16, fpu_v16, fpu_d16, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s17_invalidates[] = {sve_z17, fpu_v17, fpu_d17, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s18_invalidates[] = {sve_z18, fpu_v18, fpu_d18, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s19_invalidates[] = {sve_z19, fpu_v19, fpu_d19, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s20_invalidates[] = {sve_z20, fpu_v20, fpu_d20, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s21_invalidates[] = {sve_z21, fpu_v21, fpu_d21, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s22_invalidates[] = {sve_z22, fpu_v22, fpu_d22, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s23_invalidates[] = {sve_z23, fpu_v23, fpu_d23, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s24_invalidates[] = {sve_z24, fpu_v24, fpu_d24, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s25_invalidates[] = {sve_z25, fpu_v25, fpu_d25, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s26_invalidates[] = {sve_z26, fpu_v26, fpu_d26, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s27_invalidates[] = {sve_z27, fpu_v27, fpu_d27, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s28_invalidates[] = {sve_z28, fpu_v28, fpu_d28, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s29_invalidates[] = {sve_z29, fpu_v29, fpu_d29, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s30_invalidates[] = {sve_z30, fpu_v30, fpu_d30, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_s31_invalidates[] = {sve_z31, fpu_v31, fpu_d31, + LLDB_INVALID_REGNUM}; + +static uint32_t g_sve_d0_invalidates[] = {sve_z0, fpu_v0, fpu_s0, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d1_invalidates[] = {sve_z1, fpu_v1, fpu_s1, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d2_invalidates[] = {sve_z2, fpu_v2, fpu_s2, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d3_invalidates[] = {sve_z3, fpu_v3, fpu_s3, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d4_invalidates[] = {sve_z4, fpu_v4, fpu_s4, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d5_invalidates[] = {sve_z5, fpu_v5, fpu_s5, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d6_invalidates[] = {sve_z6, fpu_v6, fpu_s6, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d7_invalidates[] = {sve_z7, fpu_v7, fpu_s7, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d8_invalidates[] = {sve_z8, fpu_v8, fpu_s8, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d9_invalidates[] = {sve_z9, fpu_v9, fpu_s9, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d10_invalidates[] = {sve_z10, fpu_v10, fpu_s10, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d11_invalidates[] = {sve_z11, fpu_v11, fpu_s11, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d12_invalidates[] = {sve_z12, fpu_v12, fpu_s12, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d13_invalidates[] = {sve_z13, fpu_v13, fpu_s13, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d14_invalidates[] = {sve_z14, fpu_v14, fpu_s14, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d15_invalidates[] = {sve_z15, fpu_v15, fpu_s15, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d16_invalidates[] = {sve_z16, fpu_v16, fpu_s16, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d17_invalidates[] = {sve_z17, fpu_v17, fpu_s17, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d18_invalidates[] = {sve_z18, fpu_v18, fpu_s18, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d19_invalidates[] = {sve_z19, fpu_v19, fpu_s19, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d20_invalidates[] = {sve_z20, fpu_v20, fpu_s20, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d21_invalidates[] = {sve_z21, fpu_v21, fpu_s21, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d22_invalidates[] = {sve_z22, fpu_v22, fpu_s22, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d23_invalidates[] = {sve_z23, fpu_v23, fpu_s23, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d24_invalidates[] = {sve_z24, fpu_v24, fpu_s24, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d25_invalidates[] = {sve_z25, fpu_v25, fpu_s25, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d26_invalidates[] = {sve_z26, fpu_v26, fpu_s26, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d27_invalidates[] = {sve_z27, fpu_v27, fpu_s27, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d28_invalidates[] = {sve_z28, fpu_v28, fpu_s28, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d29_invalidates[] = {sve_z29, fpu_v29, fpu_s29, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d30_invalidates[] = {sve_z30, fpu_v30, fpu_s30, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_d31_invalidates[] = {sve_z31, fpu_v31, fpu_s31, + LLDB_INVALID_REGNUM}; + +static uint32_t g_sve_v0_invalidates[] = {sve_z0, fpu_d0, fpu_s0, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v1_invalidates[] = {sve_z1, fpu_d1, fpu_s1, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v2_invalidates[] = {sve_z2, fpu_d2, fpu_s2, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v3_invalidates[] = {sve_z3, fpu_d3, fpu_s3, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v4_invalidates[] = {sve_z4, fpu_d4, fpu_s4, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v5_invalidates[] = {sve_z5, fpu_d5, fpu_s5, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v6_invalidates[] = {sve_z6, fpu_d6, fpu_s6, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v7_invalidates[] = {sve_z7, fpu_d7, fpu_s7, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v8_invalidates[] = {sve_z8, fpu_d8, fpu_s8, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v9_invalidates[] = {sve_z9, fpu_d9, fpu_s9, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v10_invalidates[] = {sve_z10, fpu_d10, fpu_s10, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v11_invalidates[] = {sve_z11, fpu_d11, fpu_s11, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v12_invalidates[] = {sve_z12, fpu_d12, fpu_s12, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v13_invalidates[] = {sve_z13, fpu_d13, fpu_s13, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v14_invalidates[] = {sve_z14, fpu_d14, fpu_s14, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v15_invalidates[] = {sve_z15, fpu_d15, fpu_s15, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v16_invalidates[] = {sve_z16, fpu_d16, fpu_s16, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v17_invalidates[] = {sve_z17, fpu_d17, fpu_s17, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v18_invalidates[] = {sve_z18, fpu_d18, fpu_s18, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v19_invalidates[] = {sve_z19, fpu_d19, fpu_s19, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v20_invalidates[] = {sve_z20, fpu_d20, fpu_s20, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v21_invalidates[] = {sve_z21, fpu_d21, fpu_s21, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v22_invalidates[] = {sve_z22, fpu_d22, fpu_s22, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v23_invalidates[] = {sve_z23, fpu_d23, fpu_s23, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v24_invalidates[] = {sve_z24, fpu_d24, fpu_s24, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v25_invalidates[] = {sve_z25, fpu_d25, fpu_s25, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v26_invalidates[] = {sve_z26, fpu_d26, fpu_s26, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v27_invalidates[] = {sve_z27, fpu_d27, fpu_s27, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v28_invalidates[] = {sve_z28, fpu_d28, fpu_s28, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v29_invalidates[] = {sve_z29, fpu_d29, fpu_s29, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v30_invalidates[] = {sve_z30, fpu_d30, fpu_s30, + LLDB_INVALID_REGNUM}; +static uint32_t g_sve_v31_invalidates[] = {sve_z31, fpu_d31, fpu_s31, + LLDB_INVALID_REGNUM}; + +static uint32_t g_contained_z0[] = {sve_z0, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z1[] = {sve_z1, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z2[] = {sve_z2, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z3[] = {sve_z3, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z4[] = {sve_z4, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z5[] = {sve_z5, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z6[] = {sve_z6, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z7[] = {sve_z7, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z8[] = {sve_z8, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z9[] = {sve_z9, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z10[] = {sve_z10, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z11[] = {sve_z11, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z12[] = {sve_z12, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z13[] = {sve_z13, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z14[] = {sve_z14, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z15[] = {sve_z15, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z16[] = {sve_z16, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z17[] = {sve_z17, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z18[] = {sve_z18, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z19[] = {sve_z19, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z20[] = {sve_z20, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z21[] = {sve_z21, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z22[] = {sve_z22, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z23[] = {sve_z23, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z24[] = {sve_z24, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z25[] = {sve_z25, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z26[] = {sve_z26, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z27[] = {sve_z27, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z28[] = {sve_z28, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z29[] = {sve_z29, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z30[] = {sve_z30, LLDB_INVALID_REGNUM}; +static uint32_t g_contained_z31[] = {sve_z31, LLDB_INVALID_REGNUM}; + +#define VG_OFFSET_NAME(reg) SVE_OFFSET_VG + +#define SVE_REG_KIND(reg) MISC_KIND(reg, sve, LLDB_INVALID_REGNUM) +#define MISC_VG_KIND(lldb_kind) MISC_KIND(vg, sve, LLDB_INVALID_REGNUM) + +// Default offset SVE Z registers and all corresponding pseudo registers +// ( S, D and V registers) is zero and will be configured during execution. + +// clang-format off + +// Defines sve pseudo vector (V) register with 16-byte size +#define DEFINE_VREG_SVE(vreg, zreg) \ + { \ + #vreg, nullptr, 16, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, \ + VREG_KIND(vreg), g_contained_##zreg, g_sve_##vreg##_invalidates, \ + nullptr, \ + } + +// Defines S and D pseudo registers mapping over corresponding vector register +#define DEFINE_FPU_PSEUDO_SVE(reg, size, zreg) \ + { \ + #reg, nullptr, size, 0, lldb::eEncodingIEEE754, lldb::eFormatFloat, \ + LLDB_KIND(fpu_##reg), g_contained_##zreg, g_sve_##reg##_invalidates, \ + nullptr, \ + } + +// Defines a Z vector register with 16-byte default size +#define DEFINE_ZREG(reg) \ + { \ + #reg, nullptr, 16, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, \ + SVE_REG_KIND(reg), nullptr, nullptr, nullptr, \ + } + +// Defines a P vector register with 2-byte default size +#define DEFINE_PREG(reg) \ + { \ + #reg, nullptr, 2, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, \ + SVE_REG_KIND(reg), nullptr, nullptr, nullptr, \ + } + +static lldb_private::RegisterInfo g_register_infos_arm64_sve_le[] = { + // DEFINE_GPR64(name, GENERIC KIND) + DEFINE_GPR64(x0, LLDB_REGNUM_GENERIC_ARG1), + DEFINE_GPR64(x1, LLDB_REGNUM_GENERIC_ARG2), + DEFINE_GPR64(x2, LLDB_REGNUM_GENERIC_ARG3), + DEFINE_GPR64(x3, LLDB_REGNUM_GENERIC_ARG4), + DEFINE_GPR64(x4, LLDB_REGNUM_GENERIC_ARG5), + DEFINE_GPR64(x5, LLDB_REGNUM_GENERIC_ARG6), + DEFINE_GPR64(x6, LLDB_REGNUM_GENERIC_ARG7), + DEFINE_GPR64(x7, LLDB_REGNUM_GENERIC_ARG8), + DEFINE_GPR64(x8, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x9, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x10, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x11, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x12, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x13, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x14, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x15, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x16, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x17, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x18, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x19, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x20, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x21, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x22, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x23, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x24, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x25, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x26, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x27, LLDB_INVALID_REGNUM), + DEFINE_GPR64(x28, LLDB_INVALID_REGNUM), + // DEFINE_GPR64(name, GENERIC KIND) + DEFINE_GPR64_ALT(fp, x29, LLDB_REGNUM_GENERIC_FP), + DEFINE_GPR64_ALT(lr, x30, LLDB_REGNUM_GENERIC_RA), + DEFINE_GPR64_ALT(sp, x31, LLDB_REGNUM_GENERIC_SP), + DEFINE_GPR64(pc, LLDB_REGNUM_GENERIC_PC), + + // DEFINE_MISC_REGS(name, size, TYPE, lldb kind) + DEFINE_MISC_REGS(cpsr, 4, GPR, gpr_cpsr), + + // DEFINE_GPR32(name, parent name) + DEFINE_GPR32(w0, x0), + DEFINE_GPR32(w1, x1), + DEFINE_GPR32(w2, x2), + DEFINE_GPR32(w3, x3), + DEFINE_GPR32(w4, x4), + DEFINE_GPR32(w5, x5), + DEFINE_GPR32(w6, x6), + DEFINE_GPR32(w7, x7), + DEFINE_GPR32(w8, x8), + DEFINE_GPR32(w9, x9), + DEFINE_GPR32(w10, x10), + DEFINE_GPR32(w11, x11), + DEFINE_GPR32(w12, x12), + DEFINE_GPR32(w13, x13), + DEFINE_GPR32(w14, x14), + DEFINE_GPR32(w15, x15), + DEFINE_GPR32(w16, x16), + DEFINE_GPR32(w17, x17), + DEFINE_GPR32(w18, x18), + DEFINE_GPR32(w19, x19), + DEFINE_GPR32(w20, x20), + DEFINE_GPR32(w21, x21), + DEFINE_GPR32(w22, x22), + DEFINE_GPR32(w23, x23), + DEFINE_GPR32(w24, x24), + DEFINE_GPR32(w25, x25), + DEFINE_GPR32(w26, x26), + DEFINE_GPR32(w27, x27), + DEFINE_GPR32(w28, x28), + + // DEFINE_VREG_SVE(v register, z register) + DEFINE_VREG_SVE(v0, z0), + DEFINE_VREG_SVE(v1, z1), + DEFINE_VREG_SVE(v2, z2), + DEFINE_VREG_SVE(v3, z3), + DEFINE_VREG_SVE(v4, z4), + DEFINE_VREG_SVE(v5, z5), + DEFINE_VREG_SVE(v6, z6), + DEFINE_VREG_SVE(v7, z7), + DEFINE_VREG_SVE(v8, z8), + DEFINE_VREG_SVE(v9, z9), + DEFINE_VREG_SVE(v10, z10), + DEFINE_VREG_SVE(v11, z11), + DEFINE_VREG_SVE(v12, z12), + DEFINE_VREG_SVE(v13, z13), + DEFINE_VREG_SVE(v14, z14), + DEFINE_VREG_SVE(v15, z15), + DEFINE_VREG_SVE(v16, z16), + DEFINE_VREG_SVE(v17, z17), + DEFINE_VREG_SVE(v18, z18), + DEFINE_VREG_SVE(v19, z19), + DEFINE_VREG_SVE(v20, z20), + DEFINE_VREG_SVE(v21, z21), + DEFINE_VREG_SVE(v22, z22), + DEFINE_VREG_SVE(v23, z23), + DEFINE_VREG_SVE(v24, z24), + DEFINE_VREG_SVE(v25, z25), + DEFINE_VREG_SVE(v26, z26), + DEFINE_VREG_SVE(v27, z27), + DEFINE_VREG_SVE(v28, z28), + DEFINE_VREG_SVE(v29, z29), + DEFINE_VREG_SVE(v30, z30), + DEFINE_VREG_SVE(v31, z31), + + // DEFINE_FPU_PSEUDO(name, size, ENDIAN OFFSET, parent register) + DEFINE_FPU_PSEUDO_SVE(s0, 4, z0), + DEFINE_FPU_PSEUDO_SVE(s1, 4, z1), + DEFINE_FPU_PSEUDO_SVE(s2, 4, z2), + DEFINE_FPU_PSEUDO_SVE(s3, 4, z3), + DEFINE_FPU_PSEUDO_SVE(s4, 4, z4), + DEFINE_FPU_PSEUDO_SVE(s5, 4, z5), + DEFINE_FPU_PSEUDO_SVE(s6, 4, z6), + DEFINE_FPU_PSEUDO_SVE(s7, 4, z7), + DEFINE_FPU_PSEUDO_SVE(s8, 4, z8), + DEFINE_FPU_PSEUDO_SVE(s9, 4, z9), + DEFINE_FPU_PSEUDO_SVE(s10, 4, z10), + DEFINE_FPU_PSEUDO_SVE(s11, 4, z11), + DEFINE_FPU_PSEUDO_SVE(s12, 4, z12), + DEFINE_FPU_PSEUDO_SVE(s13, 4, z13), + DEFINE_FPU_PSEUDO_SVE(s14, 4, z14), + DEFINE_FPU_PSEUDO_SVE(s15, 4, z15), + DEFINE_FPU_PSEUDO_SVE(s16, 4, z16), + DEFINE_FPU_PSEUDO_SVE(s17, 4, z17), + DEFINE_FPU_PSEUDO_SVE(s18, 4, z18), + DEFINE_FPU_PSEUDO_SVE(s19, 4, z19), + DEFINE_FPU_PSEUDO_SVE(s20, 4, z20), + DEFINE_FPU_PSEUDO_SVE(s21, 4, z21), + DEFINE_FPU_PSEUDO_SVE(s22, 4, z22), + DEFINE_FPU_PSEUDO_SVE(s23, 4, z23), + DEFINE_FPU_PSEUDO_SVE(s24, 4, z24), + DEFINE_FPU_PSEUDO_SVE(s25, 4, z25), + DEFINE_FPU_PSEUDO_SVE(s26, 4, z26), + DEFINE_FPU_PSEUDO_SVE(s27, 4, z27), + DEFINE_FPU_PSEUDO_SVE(s28, 4, z28), + DEFINE_FPU_PSEUDO_SVE(s29, 4, z29), + DEFINE_FPU_PSEUDO_SVE(s30, 4, z30), + DEFINE_FPU_PSEUDO_SVE(s31, 4, z31), + + DEFINE_FPU_PSEUDO_SVE(d0, 8, z0), + DEFINE_FPU_PSEUDO_SVE(d1, 8, z1), + DEFINE_FPU_PSEUDO_SVE(d2, 8, z2), + DEFINE_FPU_PSEUDO_SVE(d3, 8, z3), + DEFINE_FPU_PSEUDO_SVE(d4, 8, z4), + DEFINE_FPU_PSEUDO_SVE(d5, 8, z5), + DEFINE_FPU_PSEUDO_SVE(d6, 8, z6), + DEFINE_FPU_PSEUDO_SVE(d7, 8, z7), + DEFINE_FPU_PSEUDO_SVE(d8, 8, z8), + DEFINE_FPU_PSEUDO_SVE(d9, 8, z9), + DEFINE_FPU_PSEUDO_SVE(d10, 8, z10), + DEFINE_FPU_PSEUDO_SVE(d11, 8, z11), + DEFINE_FPU_PSEUDO_SVE(d12, 8, z12), + DEFINE_FPU_PSEUDO_SVE(d13, 8, z13), + DEFINE_FPU_PSEUDO_SVE(d14, 8, z14), + DEFINE_FPU_PSEUDO_SVE(d15, 8, z15), + DEFINE_FPU_PSEUDO_SVE(d16, 8, z16), + DEFINE_FPU_PSEUDO_SVE(d17, 8, z17), + DEFINE_FPU_PSEUDO_SVE(d18, 8, z18), + DEFINE_FPU_PSEUDO_SVE(d19, 8, z19), + DEFINE_FPU_PSEUDO_SVE(d20, 8, z20), + DEFINE_FPU_PSEUDO_SVE(d21, 8, z21), + DEFINE_FPU_PSEUDO_SVE(d22, 8, z22), + DEFINE_FPU_PSEUDO_SVE(d23, 8, z23), + DEFINE_FPU_PSEUDO_SVE(d24, 8, z24), + DEFINE_FPU_PSEUDO_SVE(d25, 8, z25), + DEFINE_FPU_PSEUDO_SVE(d26, 8, z26), + DEFINE_FPU_PSEUDO_SVE(d27, 8, z27), + DEFINE_FPU_PSEUDO_SVE(d28, 8, z28), + DEFINE_FPU_PSEUDO_SVE(d29, 8, z29), + DEFINE_FPU_PSEUDO_SVE(d30, 8, z30), + DEFINE_FPU_PSEUDO_SVE(d31, 8, z31), + + // DEFINE_MISC_REGS(name, size, TYPE, lldb kind) + DEFINE_MISC_REGS(fpsr, 4, FPU, fpu_fpsr), + DEFINE_MISC_REGS(fpcr, 4, FPU, fpu_fpcr), + + DEFINE_MISC_REGS(vg, 8, VG, sve_vg), + // DEFINE_ZREG(name) + DEFINE_ZREG(z0), + DEFINE_ZREG(z1), + DEFINE_ZREG(z2), + DEFINE_ZREG(z3), + DEFINE_ZREG(z4), + DEFINE_ZREG(z5), + DEFINE_ZREG(z6), + DEFINE_ZREG(z7), + DEFINE_ZREG(z8), + DEFINE_ZREG(z9), + DEFINE_ZREG(z10), + DEFINE_ZREG(z11), + DEFINE_ZREG(z12), + DEFINE_ZREG(z13), + DEFINE_ZREG(z14), + DEFINE_ZREG(z15), + DEFINE_ZREG(z16), + DEFINE_ZREG(z17), + DEFINE_ZREG(z18), + DEFINE_ZREG(z19), + DEFINE_ZREG(z20), + DEFINE_ZREG(z21), + DEFINE_ZREG(z22), + DEFINE_ZREG(z23), + DEFINE_ZREG(z24), + DEFINE_ZREG(z25), + DEFINE_ZREG(z26), + DEFINE_ZREG(z27), + DEFINE_ZREG(z28), + DEFINE_ZREG(z29), + DEFINE_ZREG(z30), + DEFINE_ZREG(z31), + + // DEFINE_PREG(name) + DEFINE_PREG(p0), + DEFINE_PREG(p1), + DEFINE_PREG(p2), + DEFINE_PREG(p3), + DEFINE_PREG(p4), + DEFINE_PREG(p5), + DEFINE_PREG(p6), + DEFINE_PREG(p7), + DEFINE_PREG(p8), + DEFINE_PREG(p9), + DEFINE_PREG(p10), + DEFINE_PREG(p11), + DEFINE_PREG(p12), + DEFINE_PREG(p13), + DEFINE_PREG(p14), + DEFINE_PREG(p15), + + // DEFINE FFR + DEFINE_PREG(ffr) + // clang-format on +}; + +#endif // DECLARE_REGISTER_INFOS_ARM64_SVE_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h new file mode 100644 index 000000000000..e9f8065bffd8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_i386.h @@ -0,0 +1,310 @@ +//===-- RegisterInfos_i386.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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/Compiler.h" +#include <cstddef> +#include <cstdint> + + +#ifdef DECLARE_REGISTER_INFOS_I386_STRUCT + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) + +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, i387) + \ + LLVM_EXTENSION offsetof(FPR_i386, regname)) + +// Computes the offset of the YMM register assembled from register halves. +// Based on DNBArchImplI386.cpp from debugserver +#define YMM_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, i387) + \ + LLVM_EXTENSION offsetof(FPR, fxsave) + \ + LLVM_EXTENSION offsetof(FXSAVE, xmm[7]) + sizeof(XMMReg) + \ + (32 * reg_index)) + +#define BNDR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, i387) + \ + LLVM_EXTENSION offsetof(FPR, xsave) + \ + LLVM_EXTENSION offsetof(XSAVE, mpxr[reg_index])) + +#define BNDC_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, i387) + \ + LLVM_EXTENSION offsetof(FPR, xsave) + \ + LLVM_EXTENSION offsetof(XSAVE, mpxc[reg_index])) + +// Number of bytes needed to represent a FPR. +#if !defined(FPR_SIZE) +#define FPR_SIZE(reg) sizeof(((FXSAVE *)nullptr)->reg) +#endif + +// Number of bytes needed to represent the i'th FP register. +#define FP_SIZE sizeof(((MMSReg *)nullptr)->bytes) + +// Number of bytes needed to represent an XMM register. +#define XMM_SIZE sizeof(XMMReg) + +// Number of bytes needed to represent a YMM register. +#define YMM_SIZE sizeof(YMMReg) + +// Number of bytes needed to represent MPX registers. +#define BNDR_SIZE sizeof(MPXReg) +#define BNDC_SIZE sizeof(MPXCsr) + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ + #reg, alt, sizeof(((GPR *)nullptr)->reg), \ + GPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, kind4, \ + lldb_##reg##_i386 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \ + { \ + #name, nullptr, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, kind4, \ + lldb_##name##_i386 }, \ + nullptr, nullptr, nullptr, \ + } + +// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB + +#define DEFINE_FP_ST(reg, i) \ + { \ + #reg #i, nullptr, FP_SIZE, \ + LLVM_EXTENSION FPR_OFFSET( \ + stmm[i]), eEncodingVector, eFormatVectorOfUInt8, \ + {ehframe_st##i##_i386, dwarf_st##i##_i386, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_st##i##_i386 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_FP_MM(reg, i, streg) \ + { \ + #reg #i, nullptr, sizeof(uint64_t), LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ + eEncodingUint, eFormatHex, \ + {dwarf_mm##i##_i386, dwarf_mm##i##_i386, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_mm##i##_i386 }, \ + RegisterContextPOSIX_x86::g_contained_##streg##_32, \ + RegisterContextPOSIX_x86::g_invalidate_##streg##_32, \ + nullptr, \ + } + +#define DEFINE_XMM(reg, i) \ + { \ + #reg #i, nullptr, XMM_SIZE, \ + LLVM_EXTENSION FPR_OFFSET( \ + reg[i]), eEncodingVector, eFormatVectorOfUInt8, \ + {ehframe_##reg##i##_i386, dwarf_##reg##i##_i386, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg##i##_i386 }, \ + nullptr, nullptr, nullptr, \ + } + +// I believe the YMM registers use dwarf_xmm_%_i386 register numbers and then +// differentiate based on register size. +#define DEFINE_YMM(reg, i) \ + { \ + #reg #i, nullptr, YMM_SIZE, \ + LLVM_EXTENSION YMM_OFFSET(i), eEncodingVector, eFormatVectorOfUInt8, \ + {LLDB_INVALID_REGNUM, dwarf_xmm##i##_i386, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg##i##_i386 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_BNDR(reg, i) \ + { \ + #reg #i, nullptr, BNDR_SIZE, \ + LLVM_EXTENSION BNDR_OFFSET(i), eEncodingVector, eFormatVectorOfUInt64, \ + {dwarf_##reg##i##_i386, dwarf_##reg##i##_i386, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_##reg##i##_i386 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_BNDC(name, i) \ + { \ + #name, nullptr, BNDC_SIZE, \ + LLVM_EXTENSION BNDC_OFFSET(i), eEncodingVector, \ + eFormatVectorOfUInt8, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_##name##_i386 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_DR(reg, i) \ + { \ + #reg #i, nullptr, DR_SIZE, \ + DR_OFFSET(i), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg##i##_i386 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_16(reg16, reg32) \ + { \ + #reg16, nullptr, 2, \ + GPR_OFFSET(reg32), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg16##_i386 }, \ + RegisterContextPOSIX_x86::g_contained_##reg32, \ + RegisterContextPOSIX_x86::g_invalidate_##reg32, \ + nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_8H(reg8, reg32) \ + { \ + #reg8, nullptr, 1, \ + GPR_OFFSET(reg32) + 1, eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg8##_i386 }, \ + RegisterContextPOSIX_x86::g_contained_##reg32, \ + RegisterContextPOSIX_x86::g_invalidate_##reg32, \ + nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_8L(reg8, reg32) \ + { \ + #reg8, nullptr, 1, \ + GPR_OFFSET(reg32), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg8##_i386 }, \ + RegisterContextPOSIX_x86::g_contained_##reg32, \ + RegisterContextPOSIX_x86::g_invalidate_##reg32, \ + nullptr, \ + } + +static RegisterInfo g_register_infos_i386[] = { + // General purpose registers. + DEFINE_GPR(eax, nullptr, ehframe_eax_i386, dwarf_eax_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebx, nullptr, ehframe_ebx_i386, dwarf_ebx_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ecx, nullptr, ehframe_ecx_i386, dwarf_ecx_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edx, nullptr, ehframe_edx_i386, dwarf_edx_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edi, nullptr, ehframe_edi_i386, dwarf_edi_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(esi, nullptr, ehframe_esi_i386, dwarf_esi_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebp, "fp", ehframe_ebp_i386, dwarf_ebp_i386, + LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(esp, "sp", ehframe_esp_i386, dwarf_esp_i386, + LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(eip, "pc", ehframe_eip_i386, dwarf_eip_i386, + LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(eflags, "flags", ehframe_eflags_i386, dwarf_eflags_i386, + LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, LLDB_INVALID_REGNUM, dwarf_cs_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, LLDB_INVALID_REGNUM, dwarf_fs_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, LLDB_INVALID_REGNUM, dwarf_gs_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, LLDB_INVALID_REGNUM, dwarf_ss_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, LLDB_INVALID_REGNUM, dwarf_ds_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, LLDB_INVALID_REGNUM, dwarf_es_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + DEFINE_GPR_PSEUDO_16(ax, eax), DEFINE_GPR_PSEUDO_16(bx, ebx), + DEFINE_GPR_PSEUDO_16(cx, ecx), DEFINE_GPR_PSEUDO_16(dx, edx), + DEFINE_GPR_PSEUDO_16(di, edi), DEFINE_GPR_PSEUDO_16(si, esi), + DEFINE_GPR_PSEUDO_16(bp, ebp), DEFINE_GPR_PSEUDO_16(sp, esp), + DEFINE_GPR_PSEUDO_8H(ah, eax), DEFINE_GPR_PSEUDO_8H(bh, ebx), + DEFINE_GPR_PSEUDO_8H(ch, ecx), DEFINE_GPR_PSEUDO_8H(dh, edx), + DEFINE_GPR_PSEUDO_8L(al, eax), DEFINE_GPR_PSEUDO_8L(bl, ebx), + DEFINE_GPR_PSEUDO_8L(cl, ecx), DEFINE_GPR_PSEUDO_8L(dl, edx), + + // i387 Floating point registers. + DEFINE_FPR(fctrl, fctrl, LLDB_INVALID_REGNUM, dwarf_fctrl_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fstat, fstat, LLDB_INVALID_REGNUM, dwarf_fstat_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(ftag, ftag, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fop, fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fiseg, ptr.i386_.fiseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fioff, ptr.i386_.fioff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(foseg, ptr.i386_.foseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fooff, ptr.i386_.fooff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(mxcsr, mxcsr, LLDB_INVALID_REGNUM, dwarf_mxcsr_i386, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(mxcsrmask, mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + // FP registers. + DEFINE_FP_ST(st, 0), DEFINE_FP_ST(st, 1), DEFINE_FP_ST(st, 2), + DEFINE_FP_ST(st, 3), DEFINE_FP_ST(st, 4), DEFINE_FP_ST(st, 5), + DEFINE_FP_ST(st, 6), DEFINE_FP_ST(st, 7), + + DEFINE_FP_MM(mm, 0, st0), DEFINE_FP_MM(mm, 1, st1), + DEFINE_FP_MM(mm, 2, st2), DEFINE_FP_MM(mm, 3, st3), + DEFINE_FP_MM(mm, 4, st4), DEFINE_FP_MM(mm, 5, st5), + DEFINE_FP_MM(mm, 6, st6), DEFINE_FP_MM(mm, 7, st7), + + // XMM registers + DEFINE_XMM(xmm, 0), DEFINE_XMM(xmm, 1), DEFINE_XMM(xmm, 2), + DEFINE_XMM(xmm, 3), DEFINE_XMM(xmm, 4), DEFINE_XMM(xmm, 5), + DEFINE_XMM(xmm, 6), DEFINE_XMM(xmm, 7), + + // Copy of YMM registers assembled from xmm and ymmh + DEFINE_YMM(ymm, 0), DEFINE_YMM(ymm, 1), DEFINE_YMM(ymm, 2), + DEFINE_YMM(ymm, 3), DEFINE_YMM(ymm, 4), DEFINE_YMM(ymm, 5), + DEFINE_YMM(ymm, 6), DEFINE_YMM(ymm, 7), + + // MPX registers + DEFINE_BNDR(bnd, 0), + DEFINE_BNDR(bnd, 1), + DEFINE_BNDR(bnd, 2), + DEFINE_BNDR(bnd, 3), + + DEFINE_BNDC(bndcfgu, 0), + DEFINE_BNDC(bndstatus, 1), + + // Debug registers for lldb internal use + DEFINE_DR(dr, 0), DEFINE_DR(dr, 1), DEFINE_DR(dr, 2), DEFINE_DR(dr, 3), + DEFINE_DR(dr, 4), DEFINE_DR(dr, 5), DEFINE_DR(dr, 6), DEFINE_DR(dr, 7)}; + +static_assert((sizeof(g_register_infos_i386) / + sizeof(g_register_infos_i386[0])) == k_num_registers_i386, + "g_register_infos_x86_64 has wrong number of register infos"); + +#undef GPR_OFFSET +#undef FPR_OFFSET +#undef YMM_OFFSET +#undef FPR_SIZE +#undef FP_SIZE +#undef XMM_SIZE +#undef YMM_SIZE +#undef DEFINE_GPR +#undef DEFINE_FPR +#undef DEFINE_FP +#undef DEFINE_XMM +#undef DEFINE_YMM +#undef DEFINE_BNDR +#undef DEFINE_BNDC +#undef DEFINE_DR +#undef DEFINE_GPR_PSEUDO_16 +#undef DEFINE_GPR_PSEUDO_8H +#undef DEFINE_GPR_PSEUDO_8L + +#endif // DECLARE_REGISTER_INFOS_I386_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_loongarch64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_loongarch64.h new file mode 100644 index 000000000000..3fb1e6a5fbef --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_loongarch64.h @@ -0,0 +1,171 @@ +//===-- RegisterInfos_loongarch64.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 +// +//===----------------------------------------------------------------------===// + +#ifdef DECLARE_REGISTER_INFOS_LOONGARCH64_STRUCT + +#include <stddef.h> + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private.h" + +#include "Utility/LoongArch_DWARF_Registers.h" +#include "lldb-loongarch-register-enums.h" + +#ifndef GPR_OFFSET +#error GPR_OFFSET must be defined before including this header file +#endif + +#ifndef FPR_OFFSET +#error FPR_OFFSET must be defined before including this header file +#endif + +using namespace loongarch_dwarf; + +// clang-format off + +// I suppose EHFrame and DWARF are the same. +#define KIND_HELPER(reg, generic_kind) \ + { \ + loongarch_dwarf::dwarf_##reg, loongarch_dwarf::dwarf_##reg, generic_kind, \ + LLDB_INVALID_REGNUM, reg##_loongarch \ + } + +// Generates register kinds array for generic purpose registers +#define GPR64_KIND(reg, generic_kind) KIND_HELPER(reg, generic_kind) + +// Generates register kinds array for floating point registers +#define FPR64_KIND(reg, generic_kind) KIND_HELPER(reg, generic_kind) + +// Defines a 64-bit general purpose register +#define DEFINE_GPR64(reg, generic_kind) DEFINE_GPR64_ALT(reg, reg, generic_kind) +#define DEFINE_GPR64_ALT(reg, alt, generic_kind) \ + { \ + #reg, #alt, 8, GPR_OFFSET(gpr_##reg##_loongarch - gpr_first_loongarch), \ + lldb::eEncodingUint, lldb::eFormatHex, \ + GPR64_KIND(gpr_##reg, generic_kind), nullptr, nullptr, nullptr, \ + } + +// Defines a 64-bit floating point register +#define DEFINE_FPR64(reg, generic_kind) DEFINE_FPR64_ALT(reg, reg, generic_kind) +#define DEFINE_FPR64_ALT(reg, alt, generic_kind) \ + { \ + #reg, #alt, 8, FPR_OFFSET(fpr_##reg##_loongarch - fpr_first_loongarch), \ + lldb::eEncodingUint, lldb::eFormatHex, \ + FPR64_KIND(fpr_##reg, generic_kind), nullptr, nullptr, nullptr, \ + } + +#define DEFINE_FCC(reg, generic_kind) \ + { \ + #reg, nullptr, 1, FCC_OFFSET(fpr_##reg##_loongarch - fpr_fcc0_loongarch), \ + lldb::eEncodingUint, lldb::eFormatHex, \ + FPR64_KIND(fpr_##reg, generic_kind), nullptr, nullptr, nullptr, \ + } + +#define DEFINE_FCSR(reg, generic_kind) \ + { \ + #reg, nullptr, 4, FCSR_OFFSET, \ + lldb::eEncodingUint, lldb::eFormatHex, \ + FPR64_KIND(fpr_##reg, generic_kind), nullptr, nullptr, nullptr, \ + } + +// clang-format on + +static lldb_private::RegisterInfo g_register_infos_loongarch64[] = { + DEFINE_GPR64_ALT(r0, zero, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r1, ra, LLDB_REGNUM_GENERIC_RA), + DEFINE_GPR64_ALT(r2, tp, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r3, sp, LLDB_REGNUM_GENERIC_SP), + DEFINE_GPR64_ALT(r4, a0, LLDB_REGNUM_GENERIC_ARG1), + DEFINE_GPR64_ALT(r5, a1, LLDB_REGNUM_GENERIC_ARG2), + DEFINE_GPR64_ALT(r6, a2, LLDB_REGNUM_GENERIC_ARG3), + DEFINE_GPR64_ALT(r7, a3, LLDB_REGNUM_GENERIC_ARG4), + DEFINE_GPR64_ALT(r8, a4, LLDB_REGNUM_GENERIC_ARG5), + DEFINE_GPR64_ALT(r9, a5, LLDB_REGNUM_GENERIC_ARG6), + DEFINE_GPR64_ALT(r10, a6, LLDB_REGNUM_GENERIC_ARG7), + DEFINE_GPR64_ALT(r11, a7, LLDB_REGNUM_GENERIC_ARG8), + DEFINE_GPR64_ALT(r12, t0, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r13, t1, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r14, t2, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r15, t3, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r16, t4, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r17, t5, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r18, t6, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r19, t7, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r20, t8, LLDB_INVALID_REGNUM), + DEFINE_GPR64(r21, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r22, fp, LLDB_REGNUM_GENERIC_FP), + DEFINE_GPR64_ALT(r23, s0, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r24, s1, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r25, s2, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r26, s3, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r27, s4, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r28, s5, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r29, s6, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r30, s7, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(r31, s8, LLDB_INVALID_REGNUM), + + DEFINE_GPR64(orig_a0, LLDB_INVALID_REGNUM), + DEFINE_GPR64(pc, LLDB_REGNUM_GENERIC_PC), + DEFINE_GPR64(badv, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved0, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved1, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved2, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved3, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved4, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved5, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved6, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved7, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved8, LLDB_INVALID_REGNUM), + DEFINE_GPR64(reserved9, LLDB_INVALID_REGNUM), + + DEFINE_FPR64_ALT(f0, fa0, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f1, fa1, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f2, fa2, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f3, fa3, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f4, fa4, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f5, fa5, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f6, fa6, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f7, fa7, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f8, ft0, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f9, ft1, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f10, ft2, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f11, ft3, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f12, ft4, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f13, ft5, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f14, ft6, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f15, ft7, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f16, ft8, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f17, ft9, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f18, ft10, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f19, ft11, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f20, ft12, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f21, ft13, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f22, ft14, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f23, ft15, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f24, fs0, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f25, fs1, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f26, fs2, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f27, fs3, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f28, fs4, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f29, fs5, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f30, fs6, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(f31, fs7, LLDB_INVALID_REGNUM), + + DEFINE_FCC(fcc0, LLDB_INVALID_REGNUM), + DEFINE_FCC(fcc1, LLDB_INVALID_REGNUM), + DEFINE_FCC(fcc2, LLDB_INVALID_REGNUM), + DEFINE_FCC(fcc3, LLDB_INVALID_REGNUM), + DEFINE_FCC(fcc4, LLDB_INVALID_REGNUM), + DEFINE_FCC(fcc5, LLDB_INVALID_REGNUM), + DEFINE_FCC(fcc6, LLDB_INVALID_REGNUM), + DEFINE_FCC(fcc7, LLDB_INVALID_REGNUM), + DEFINE_FCSR(fcsr, LLDB_INVALID_REGNUM), +}; + +#endif // DECLARE_REGISTER_INFOS_LOONGARCH64_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h new file mode 100644 index 000000000000..93f93d56fda2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips.h @@ -0,0 +1,305 @@ +//===-- RegisterInfos_mips.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 +// +//===---------------------------------------------------------------------===// + +#include <cstddef> + +#include "lldb/Core/dwarf.h" +#include "llvm/Support/Compiler.h" + + +#ifdef DECLARE_REGISTER_INFOS_MIPS_STRUCT + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, gpr) + \ + LLVM_EXTENSION offsetof(GPR_linux_mips, regname)) + +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR_linux_mips, regname)) + +// Computes the offset of the given MSA in the extended data area. +#define MSA_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, msa) + \ + LLVM_EXTENSION offsetof(MSA_linux_mips, regname)) + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3) \ + { \ + #reg, alt, sizeof(((GPR_linux_mips *) NULL)->reg) / 2, \ + GPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, ptrace_##reg##_mips, \ + gpr_##reg##_mips }, \ + NULL, NULL, NULL, 0 \ + } + +const uint8_t dwarf_opcode_mips[] = { + llvm::dwarf::DW_OP_regx, dwarf_sr_mips, llvm::dwarf::DW_OP_lit1, + llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shl, llvm::dwarf::DW_OP_and, + llvm::dwarf::DW_OP_lit26, llvm::dwarf::DW_OP_shr}; + +#define DEFINE_FPR(reg, alt, kind1, kind2, kind3) \ + { \ + #reg, alt, sizeof(((FPR_linux_mips *) NULL)->reg), \ + FPR_OFFSET(reg), eEncodingIEEE754, eFormatFloat, \ + {kind1, kind2, kind3, ptrace_##reg##_mips, \ + fpr_##reg##_mips }, \ + NULL, NULL, dwarf_opcode_mips, \ + sizeof(dwarf_opcode_mips) \ + } + +#define DEFINE_FPR_INFO(reg, alt, kind1, kind2, kind3) \ + { \ + #reg, alt, sizeof(((FPR_linux_mips *) NULL)->reg), \ + FPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, ptrace_##reg##_mips, \ + fpr_##reg##_mips }, \ + NULL, NULL, NULL, 0 \ + } + +#define DEFINE_MSA(reg, alt, kind1, kind2, kind3, kind4) \ + { \ + #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \ + MSA_OFFSET(reg), eEncodingVector, eFormatVectorOfUInt8, \ + {kind1, kind2, kind3, kind4, \ + msa_##reg##_mips }, \ + NULL, NULL, NULL, 0 \ + } + +#define DEFINE_MSA_INFO(reg, alt, kind1, kind2, kind3, kind4) \ + { \ + #reg, alt, sizeof(((MSA_linux_mips *) 0)->reg), \ + MSA_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, kind4, \ + msa_##reg##_mips }, \ + NULL, NULL, NULL, 0 \ + } + +// RegisterKind: EH_Frame, DWARF, Generic, Procss Plugin, LLDB + +static RegisterInfo g_register_infos_mips[] = { + DEFINE_GPR(zero, "zero", dwarf_zero_mips, dwarf_zero_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r1, "at", dwarf_r1_mips, dwarf_r1_mips, LLDB_INVALID_REGNUM), + DEFINE_GPR(r2, nullptr, dwarf_r2_mips, dwarf_r2_mips, LLDB_INVALID_REGNUM), + DEFINE_GPR(r3, nullptr, dwarf_r3_mips, dwarf_r3_mips, LLDB_INVALID_REGNUM), + DEFINE_GPR(r4, nullptr, dwarf_r4_mips, dwarf_r4_mips, + LLDB_REGNUM_GENERIC_ARG1), + DEFINE_GPR(r5, nullptr, dwarf_r5_mips, dwarf_r5_mips, + LLDB_REGNUM_GENERIC_ARG2), + DEFINE_GPR(r6, nullptr, dwarf_r6_mips, dwarf_r6_mips, + LLDB_REGNUM_GENERIC_ARG3), + DEFINE_GPR(r7, nullptr, dwarf_r7_mips, dwarf_r7_mips, + LLDB_REGNUM_GENERIC_ARG4), + DEFINE_GPR(r8, nullptr, dwarf_r8_mips, dwarf_r8_mips, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, nullptr, dwarf_r9_mips, dwarf_r9_mips, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, nullptr, dwarf_r10_mips, dwarf_r10_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, nullptr, dwarf_r11_mips, dwarf_r11_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r12, nullptr, dwarf_r12_mips, dwarf_r12_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, nullptr, dwarf_r13_mips, dwarf_r13_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, nullptr, dwarf_r14_mips, dwarf_r14_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, nullptr, dwarf_r15_mips, dwarf_r15_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r16, nullptr, dwarf_r16_mips, dwarf_r16_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r17, nullptr, dwarf_r17_mips, dwarf_r17_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r18, nullptr, dwarf_r18_mips, dwarf_r18_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r19, nullptr, dwarf_r19_mips, dwarf_r19_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r20, nullptr, dwarf_r20_mips, dwarf_r20_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r21, nullptr, dwarf_r21_mips, dwarf_r21_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r22, nullptr, dwarf_r22_mips, dwarf_r22_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r23, nullptr, dwarf_r23_mips, dwarf_r23_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r24, nullptr, dwarf_r24_mips, dwarf_r24_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r25, nullptr, dwarf_r25_mips, dwarf_r25_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r26, nullptr, dwarf_r26_mips, dwarf_r26_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(r27, nullptr, dwarf_r27_mips, dwarf_r27_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(gp, "gp", dwarf_gp_mips, dwarf_gp_mips, LLDB_INVALID_REGNUM), + DEFINE_GPR(sp, "sp", dwarf_sp_mips, dwarf_sp_mips, LLDB_REGNUM_GENERIC_SP), + DEFINE_GPR(r30, "fp", dwarf_r30_mips, dwarf_r30_mips, + LLDB_REGNUM_GENERIC_FP), + DEFINE_GPR(ra, "ra", dwarf_ra_mips, dwarf_ra_mips, LLDB_REGNUM_GENERIC_RA), + DEFINE_GPR(sr, "status", dwarf_sr_mips, dwarf_sr_mips, + LLDB_REGNUM_GENERIC_FLAGS), + DEFINE_GPR(mullo, nullptr, dwarf_lo_mips, dwarf_lo_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(mulhi, nullptr, dwarf_hi_mips, dwarf_hi_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(badvaddr, nullptr, dwarf_bad_mips, dwarf_bad_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(cause, nullptr, dwarf_cause_mips, dwarf_cause_mips, + LLDB_INVALID_REGNUM), + DEFINE_GPR(pc, nullptr, dwarf_pc_mips, dwarf_pc_mips, + LLDB_REGNUM_GENERIC_PC), + DEFINE_GPR(config5, nullptr, dwarf_config5_mips, dwarf_config5_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f0, nullptr, dwarf_f0_mips, dwarf_f0_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f1, nullptr, dwarf_f1_mips, dwarf_f1_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f2, nullptr, dwarf_f2_mips, dwarf_f2_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f3, nullptr, dwarf_f3_mips, dwarf_f3_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f4, nullptr, dwarf_f4_mips, dwarf_f4_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f5, nullptr, dwarf_f5_mips, dwarf_f5_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f6, nullptr, dwarf_f6_mips, dwarf_f6_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f7, nullptr, dwarf_f7_mips, dwarf_f7_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f8, nullptr, dwarf_f8_mips, dwarf_f8_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f9, nullptr, dwarf_f9_mips, dwarf_f9_mips, LLDB_INVALID_REGNUM), + DEFINE_FPR(f10, nullptr, dwarf_f10_mips, dwarf_f10_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f11, nullptr, dwarf_f11_mips, dwarf_f11_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f12, nullptr, dwarf_f12_mips, dwarf_f12_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f13, nullptr, dwarf_f13_mips, dwarf_f13_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f14, nullptr, dwarf_f14_mips, dwarf_f14_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f15, nullptr, dwarf_f15_mips, dwarf_f15_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f16, nullptr, dwarf_f16_mips, dwarf_f16_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f17, nullptr, dwarf_f17_mips, dwarf_f17_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f18, nullptr, dwarf_f18_mips, dwarf_f18_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f19, nullptr, dwarf_f19_mips, dwarf_f19_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f20, nullptr, dwarf_f20_mips, dwarf_f20_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f21, nullptr, dwarf_f21_mips, dwarf_f21_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f22, nullptr, dwarf_f22_mips, dwarf_f22_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f23, nullptr, dwarf_f23_mips, dwarf_f23_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f24, nullptr, dwarf_f24_mips, dwarf_f24_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f25, nullptr, dwarf_f25_mips, dwarf_f25_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f26, nullptr, dwarf_f26_mips, dwarf_f26_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f27, nullptr, dwarf_f27_mips, dwarf_f27_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f28, nullptr, dwarf_f28_mips, dwarf_f28_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f29, nullptr, dwarf_f29_mips, dwarf_f29_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f30, nullptr, dwarf_f30_mips, dwarf_f30_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f31, nullptr, dwarf_f31_mips, dwarf_f31_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO(fcsr, nullptr, dwarf_fcsr_mips, dwarf_fcsr_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO(fir, nullptr, dwarf_fir_mips, dwarf_fir_mips, + LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO(config5, nullptr, dwarf_config5_mips, dwarf_config5_mips, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w0, nullptr, dwarf_w0_mips, dwarf_w0_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w1, nullptr, dwarf_w1_mips, dwarf_w1_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w2, nullptr, dwarf_w2_mips, dwarf_w2_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w3, nullptr, dwarf_w3_mips, dwarf_w3_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w4, nullptr, dwarf_w4_mips, dwarf_w4_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w5, nullptr, dwarf_w5_mips, dwarf_w5_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w6, nullptr, dwarf_w6_mips, dwarf_w6_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w7, nullptr, dwarf_w7_mips, dwarf_w7_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w8, nullptr, dwarf_w8_mips, dwarf_w8_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w9, nullptr, dwarf_w9_mips, dwarf_w9_mips, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_MSA(w10, nullptr, dwarf_w10_mips, dwarf_w10_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w11, nullptr, dwarf_w11_mips, dwarf_w11_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w12, nullptr, dwarf_w12_mips, dwarf_w12_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w13, nullptr, dwarf_w13_mips, dwarf_w13_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w14, nullptr, dwarf_w14_mips, dwarf_w14_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w15, nullptr, dwarf_w15_mips, dwarf_w15_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w16, nullptr, dwarf_w16_mips, dwarf_w16_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w17, nullptr, dwarf_w17_mips, dwarf_w17_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w18, nullptr, dwarf_w18_mips, dwarf_w18_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w19, nullptr, dwarf_w19_mips, dwarf_w19_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w20, nullptr, dwarf_w10_mips, dwarf_w20_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w21, nullptr, dwarf_w21_mips, dwarf_w21_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w22, nullptr, dwarf_w22_mips, dwarf_w22_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w23, nullptr, dwarf_w23_mips, dwarf_w23_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w24, nullptr, dwarf_w24_mips, dwarf_w24_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w25, nullptr, dwarf_w25_mips, dwarf_w25_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w26, nullptr, dwarf_w26_mips, dwarf_w26_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w27, nullptr, dwarf_w27_mips, dwarf_w27_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w28, nullptr, dwarf_w28_mips, dwarf_w28_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w29, nullptr, dwarf_w29_mips, dwarf_w29_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w30, nullptr, dwarf_w30_mips, dwarf_w30_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA(w31, nullptr, dwarf_w31_mips, dwarf_w31_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA_INFO(mcsr, nullptr, dwarf_mcsr_mips, dwarf_mcsr_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA_INFO(mir, nullptr, dwarf_mir_mips, dwarf_mir_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA_INFO(fcsr, nullptr, dwarf_fcsr_mips, dwarf_fcsr_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA_INFO(fir, nullptr, dwarf_fir_mips, dwarf_fir_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_MSA_INFO(config5, nullptr, dwarf_config5_mips, dwarf_config5_mips, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM)}; + +static_assert((sizeof(g_register_infos_mips) / + sizeof(g_register_infos_mips[0])) == k_num_registers_mips, + "g_register_infos_mips has wrong number of register infos"); + +#undef GPR_OFFSET +#undef FPR_OFFSET +#undef MSA_OFFSET +#undef DEFINE_GPR +#undef DEFINE_FPR +#undef DEFINE_FPR_INFO +#undef DEFINE_MSA +#undef DEFINE_MSA_INFO + +#endif // DECLARE_REGISTER_INFOS_MIPS_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h new file mode 100644 index 000000000000..0a382032ac8b --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_mips64.h @@ -0,0 +1,223 @@ +//===-- RegisterInfos_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 +// +//===----------------------------------------------------------------------===// + +#include <cstddef> + +#include "lldb/Core/dwarf.h" +#include "llvm/Support/Compiler.h" + +#ifdef DECLARE_REGISTER_INFOS_MIPS64_STRUCT + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR_freebsd_mips, regname)) + +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(regname) \ + (sizeof(GPR_freebsd_mips) + \ + LLVM_EXTENSION offsetof(FPR_freebsd_mips, regname)) + +// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ + #reg, alt, sizeof(((GPR_freebsd_mips *) 0)->reg), \ + GPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, kind4, \ + gpr_##reg##_mips64 }, \ + NULL, NULL, NULL, \ + } + +#define DEFINE_FPR(reg, alt, kind1, kind2, kind3) \ + { \ + #reg, alt, sizeof(((FPR_freebsd_mips *) 0)->reg), \ + FPR_OFFSET(reg), eEncodingIEEE754, eFormatFloat, \ + {kind1, kind2, kind3, LLDB_INVALID_REGNUM, \ + fpr_##reg##_mips64 }, \ + NULL, NULL, NULL, \ + } + +#define DEFINE_FPR_INFO(reg, alt, kind1, kind2, kind3) \ + { \ + #reg, alt, sizeof(((FPR_freebsd_mips *) 0)->reg), \ + FPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, LLDB_INVALID_REGNUM, \ + fpr_##reg##_mips64 }, \ + NULL, NULL, NULL, \ + } + + +static RegisterInfo g_register_infos_mips64[] = { +// General purpose registers. EH_Frame, DWARF, +// Generic, Process Plugin + DEFINE_GPR(zero, "r0", dwarf_zero_mips64, dwarf_zero_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r1, nullptr, dwarf_r1_mips64, dwarf_r1_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r2, nullptr, dwarf_r2_mips64, dwarf_r2_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r3, nullptr, dwarf_r3_mips64, dwarf_r3_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r4, nullptr, dwarf_r4_mips64, dwarf_r4_mips64, + LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM), + DEFINE_GPR(r5, nullptr, dwarf_r5_mips64, dwarf_r5_mips64, + LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM), + DEFINE_GPR(r6, nullptr, dwarf_r6_mips64, dwarf_r6_mips64, + LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM), + DEFINE_GPR(r7, nullptr, dwarf_r7_mips64, dwarf_r7_mips64, + LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM), + DEFINE_GPR(r8, nullptr, dwarf_r8_mips64, dwarf_r8_mips64, + LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, nullptr, dwarf_r9_mips64, dwarf_r9_mips64, + LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, nullptr, dwarf_r10_mips64, dwarf_r10_mips64, + LLDB_REGNUM_GENERIC_ARG7, LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, nullptr, dwarf_r11_mips64, dwarf_r11_mips64, + LLDB_REGNUM_GENERIC_ARG8, LLDB_INVALID_REGNUM), + DEFINE_GPR(r12, nullptr, dwarf_r12_mips64, dwarf_r12_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, nullptr, dwarf_r13_mips64, dwarf_r13_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, nullptr, dwarf_r14_mips64, dwarf_r14_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, nullptr, dwarf_r15_mips64, dwarf_r15_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r16, nullptr, dwarf_r16_mips64, dwarf_r16_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r17, nullptr, dwarf_r17_mips64, dwarf_r17_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r18, nullptr, dwarf_r18_mips64, dwarf_r18_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r19, nullptr, dwarf_r19_mips64, dwarf_r19_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r20, nullptr, dwarf_r20_mips64, dwarf_r20_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r21, nullptr, dwarf_r21_mips64, dwarf_r21_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r22, nullptr, dwarf_r22_mips64, dwarf_r22_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r23, nullptr, dwarf_r23_mips64, dwarf_r23_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r24, nullptr, dwarf_r24_mips64, dwarf_r24_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r25, nullptr, dwarf_r25_mips64, dwarf_r25_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r26, nullptr, dwarf_r26_mips64, dwarf_r26_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r27, nullptr, dwarf_r27_mips64, dwarf_r27_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gp, "r28", dwarf_gp_mips64, dwarf_gp_mips64, LLDB_INVALID_REGNUM, + LLDB_INVALID_REGNUM), + DEFINE_GPR(sp, "r29", dwarf_sp_mips64, dwarf_sp_mips64, + LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(r30, nullptr, dwarf_r30_mips64, dwarf_r30_mips64, + LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(ra, "r31", dwarf_ra_mips64, dwarf_ra_mips64, + LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM), + DEFINE_GPR(sr, nullptr, dwarf_sr_mips64, dwarf_sr_mips64, + LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(mullo, nullptr, dwarf_lo_mips64, dwarf_lo_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(mulhi, nullptr, dwarf_hi_mips64, dwarf_hi_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(badvaddr, nullptr, dwarf_bad_mips64, dwarf_bad_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(cause, nullptr, dwarf_cause_mips64, dwarf_cause_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(pc, "pc", dwarf_pc_mips64, dwarf_pc_mips64, + LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(ic, nullptr, dwarf_ic_mips64, dwarf_ic_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(dummy, nullptr, dwarf_dummy_mips64, dwarf_dummy_mips64, + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + DEFINE_FPR(f0, nullptr, dwarf_f0_mips64, dwarf_f0_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f1, nullptr, dwarf_f1_mips64, dwarf_f1_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f2, nullptr, dwarf_f2_mips64, dwarf_f2_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f3, nullptr, dwarf_f3_mips64, dwarf_f3_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f4, nullptr, dwarf_f4_mips64, dwarf_f4_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f5, nullptr, dwarf_f5_mips64, dwarf_f5_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f6, nullptr, dwarf_f6_mips64, dwarf_f6_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f7, nullptr, dwarf_f7_mips64, dwarf_f7_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f8, nullptr, dwarf_f8_mips64, dwarf_f8_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f9, nullptr, dwarf_f9_mips64, dwarf_f9_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f10, nullptr, dwarf_f10_mips64, dwarf_f10_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f11, nullptr, dwarf_f11_mips64, dwarf_f11_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f12, nullptr, dwarf_f12_mips64, dwarf_f12_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f13, nullptr, dwarf_f13_mips64, dwarf_f13_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f14, nullptr, dwarf_f14_mips64, dwarf_f14_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f15, nullptr, dwarf_f15_mips64, dwarf_f15_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f16, nullptr, dwarf_f16_mips64, dwarf_f16_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f17, nullptr, dwarf_f17_mips64, dwarf_f17_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f18, nullptr, dwarf_f18_mips64, dwarf_f18_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f19, nullptr, dwarf_f19_mips64, dwarf_f19_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f20, nullptr, dwarf_f20_mips64, dwarf_f20_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f21, nullptr, dwarf_f21_mips64, dwarf_f21_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f22, nullptr, dwarf_f22_mips64, dwarf_f22_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f23, nullptr, dwarf_f23_mips64, dwarf_f23_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f24, nullptr, dwarf_f24_mips64, dwarf_f24_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f25, nullptr, dwarf_f25_mips64, dwarf_f25_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f26, nullptr, dwarf_f26_mips64, dwarf_f26_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f27, nullptr, dwarf_f27_mips64, dwarf_f27_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f28, nullptr, dwarf_f28_mips64, dwarf_f28_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f29, nullptr, dwarf_f29_mips64, dwarf_f29_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f30, nullptr, dwarf_f30_mips64, dwarf_f30_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR(f31, nullptr, dwarf_f31_mips64, dwarf_f31_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO(fcsr, nullptr, dwarf_fcsr_mips64, dwarf_fcsr_mips64, + LLDB_INVALID_REGNUM), + DEFINE_FPR_INFO(fir, nullptr, dwarf_fir_mips64, dwarf_fir_mips64, + LLDB_INVALID_REGNUM), +}; + +static_assert((sizeof(g_register_infos_mips64) / + sizeof(g_register_infos_mips64[0])) == k_num_registers_mips64, + "g_register_infos_mips64 has wrong number of register infos"); + +#undef DEFINE_GPR +#undef DEFINE_GPR_INFO +#undef DEFINE_FPR +#undef DEFINE_FPR_INFO +#undef DEFINE_MSA +#undef DEFINE_MSA_INFO +#undef GPR_OFFSET +#undef FPR_OFFSET +#undef MSA_OFFSET + +#endif // DECLARE_REGISTER_INFOS_MIPS64_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h new file mode 100644 index 000000000000..31f79f537911 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_powerpc.h @@ -0,0 +1,228 @@ +//===-- RegisterInfos_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 +// +//===---------------------------------------------------------------------===// + +#include <cstddef> + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) (offsetof(GPR, regname)) +#define FPR_OFFSET(regname) (sizeof(GPR) + offsetof(FPR, regname)) +#define VMX_OFFSET(regname) (sizeof(GPR) + sizeof(FPR) + offsetof(VMX, regname)) +#define GPR_SIZE(regname) (sizeof(((GPR *)NULL)->regname)) + +#ifdef DECLARE_REGISTER_INFOS_POWERPC_STRUCT + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, lldb_kind) \ + { \ + #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {dwarf_##reg##_powerpc, \ + dwarf_##reg##_powerpc, lldb_kind, \ + LLDB_INVALID_REGNUM, \ + gpr_##reg##_powerpc }, \ + NULL, NULL, NULL, \ + } +#define DEFINE_FPR(reg, lldb_kind) \ + { \ + #reg, NULL, 8, FPR_OFFSET(reg), eEncodingIEEE754, eFormatFloat, \ + {dwarf_##reg##_powerpc, dwarf_##reg##_powerpc, \ + lldb_kind, LLDB_INVALID_REGNUM, \ + fpr_##reg##_powerpc }, \ + NULL, NULL, NULL, \ + } +#define DEFINE_VMX(reg, lldb_kind) \ + { \ + #reg, NULL, 16, VMX_OFFSET(reg), eEncodingVector, eFormatVectorOfUInt32, \ + {dwarf_##reg##_powerpc, dwarf_##reg##_powerpc, \ + lldb_kind, LLDB_INVALID_REGNUM, \ + vmx_##reg##_powerpc }, \ + NULL, NULL, NULL, \ + } + +// General purpose registers. EH_Frame, DWARF, +// Generic, Process Plugin +#define POWERPC_REGS \ + DEFINE_GPR(r0, NULL, LLDB_INVALID_REGNUM) \ + , DEFINE_GPR(r1, NULL, LLDB_REGNUM_GENERIC_SP), \ + DEFINE_GPR(r2, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r3, NULL, LLDB_REGNUM_GENERIC_ARG1), \ + DEFINE_GPR(r4, NULL, LLDB_REGNUM_GENERIC_ARG2), \ + DEFINE_GPR(r5, NULL, LLDB_REGNUM_GENERIC_ARG3), \ + DEFINE_GPR(r6, NULL, LLDB_REGNUM_GENERIC_ARG4), \ + DEFINE_GPR(r7, NULL, LLDB_REGNUM_GENERIC_ARG5), \ + DEFINE_GPR(r8, NULL, LLDB_REGNUM_GENERIC_ARG6), \ + DEFINE_GPR(r9, NULL, LLDB_REGNUM_GENERIC_ARG7), \ + DEFINE_GPR(r10, NULL, LLDB_REGNUM_GENERIC_ARG8), \ + DEFINE_GPR(r11, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r12, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r13, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r14, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r15, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r16, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r17, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r18, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r19, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r20, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r21, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r22, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r23, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r24, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r25, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r26, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r27, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r28, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r29, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r30, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r31, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(lr, NULL, LLDB_REGNUM_GENERIC_RA), \ + DEFINE_GPR(cr, NULL, LLDB_REGNUM_GENERIC_FLAGS), \ + DEFINE_GPR(xer, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(ctr, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(pc, NULL, LLDB_REGNUM_GENERIC_PC), \ + DEFINE_FPR(f0, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f1, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f2, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f3, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f4, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f5, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f6, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f7, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f8, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f9, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f10, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f11, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f12, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f13, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f14, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f15, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f16, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f17, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f18, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f19, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f20, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f21, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f22, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f23, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f24, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f25, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f26, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f27, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f28, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f29, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f30, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f31, LLDB_INVALID_REGNUM), \ + {"fpscr", \ + NULL, \ + 8, \ + FPR_OFFSET(fpscr), \ + eEncodingUint, \ + eFormatHex, \ + {dwarf_fpscr_powerpc, dwarf_fpscr_powerpc, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, fpr_fpscr_powerpc}, \ + NULL, \ + NULL, \ + NULL, \ + }, \ + DEFINE_VMX(v0, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v1, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v2, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v3, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v4, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v5, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v6, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v7, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v8, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v9, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v10, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v11, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v12, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v13, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v14, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v15, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v16, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v17, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v18, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v19, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v20, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v21, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v22, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v23, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v24, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v25, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v26, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v27, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v28, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v29, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v30, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(v31, LLDB_INVALID_REGNUM), \ + {"vrsave", \ + NULL, \ + 4, \ + VMX_OFFSET(vrsave), \ + eEncodingUint, \ + eFormatHex, \ + {dwarf_vrsave_powerpc, dwarf_vrsave_powerpc, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, vmx_vrsave_powerpc}, \ + NULL, \ + NULL, \ + NULL, \ + }, \ + {"vscr", \ + NULL, \ + 4, \ + VMX_OFFSET(vscr), \ + eEncodingUint, \ + eFormatHex, \ + {dwarf_vscr_powerpc, dwarf_vscr_powerpc, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, vmx_vscr_powerpc}, \ + NULL, \ + NULL, \ + NULL, \ + }, + +static RegisterInfo g_register_infos_powerpc64[] = { +#define GPR GPR64 + POWERPC_REGS +#undef GPR +}; + +static RegisterInfo g_register_infos_powerpc32[] = { +#define GPR GPR32 + POWERPC_REGS +#undef GPR +}; + +static RegisterInfo g_register_infos_powerpc64_32[] = { +#define GPR GPR64 +#undef GPR_SIZE +#define GPR_SIZE(reg) (sizeof(uint32_t)) +#undef GPR_OFFSET +#define GPR_OFFSET(regname) \ + (offsetof(GPR, regname) + (sizeof(((GPR *)NULL)->regname) - GPR_SIZE(reg))) + POWERPC_REGS +#undef GPR +}; + +static_assert((sizeof(g_register_infos_powerpc32) / + sizeof(g_register_infos_powerpc32[0])) == + k_num_registers_powerpc, + "g_register_infos_powerpc32 has wrong number of register infos"); +static_assert((sizeof(g_register_infos_powerpc64) / + sizeof(g_register_infos_powerpc64[0])) == + k_num_registers_powerpc, + "g_register_infos_powerpc64 has wrong number of register infos"); +static_assert(sizeof(g_register_infos_powerpc64_32) == + sizeof(g_register_infos_powerpc64), + "g_register_infos_powerpc64_32 doesn't match size of " + "g_register_infos_powerpc64"); + +#undef DEFINE_FPR +#undef DEFINE_GPR + +#endif // DECLARE_REGISTER_INFOS_POWERPC_STRUCT + +#undef GPR_OFFSET diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h new file mode 100644 index 000000000000..e15e1d5fc4a2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64.h @@ -0,0 +1,329 @@ +//===-- RegisterInfos_ppc64.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 +// +//===----------------------------------------------------------------------===// + +#ifdef DECLARE_REGISTER_INFOS_PPC64_STRUCT + +#include <cstddef> + +// Computes the offset of the given GPR_PPC64 in the user data area. +#define GPR_PPC64_OFFSET(regname) (offsetof(GPR_PPC64, regname)) +#define FPR_PPC64_OFFSET(regname) (offsetof(FPR_PPC64, regname) \ + + sizeof(GPR_PPC64)) +#define VMX_PPC64_OFFSET(regname) (offsetof(VMX_PPC64, regname) \ + + sizeof(GPR_PPC64) + sizeof(FPR_PPC64)) +#define GPR_PPC64_SIZE(regname) (sizeof(((GPR_PPC64 *)NULL)->regname)) + +#include "Utility/PPC64_DWARF_Registers.h" +#include "lldb-ppc64-register-enums.h" + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR_PPC64(reg, alt, lldb_kind) \ + { \ + #reg, alt, GPR_PPC64_SIZE(reg), GPR_PPC64_OFFSET(reg), lldb::eEncodingUint,\ + lldb::eFormatHex, \ + {ppc64_dwarf::dwarf_##reg##_ppc64, \ + ppc64_dwarf::dwarf_##reg##_ppc64, \ + lldb_kind, \ + LLDB_INVALID_REGNUM, \ + gpr_##reg##_ppc64 }, \ + NULL, NULL, NULL, \ + } +#define DEFINE_FPR_PPC64(reg, alt, lldb_kind) \ + { \ +#reg, alt, 8, FPR_PPC64_OFFSET(reg), lldb::eEncodingIEEE754, \ + lldb::eFormatFloat, \ + {ppc64_dwarf::dwarf_##reg##_ppc64, \ + ppc64_dwarf::dwarf_##reg##_ppc64, lldb_kind, LLDB_INVALID_REGNUM, \ + fpr_##reg##_ppc64 }, \ + NULL, NULL, NULL, \ + } +#define DEFINE_VMX_PPC64(reg, lldb_kind) \ + { \ +#reg, NULL, 16, VMX_PPC64_OFFSET(reg), lldb::eEncodingVector, \ + lldb::eFormatVectorOfUInt32, \ + {ppc64_dwarf::dwarf_##reg##_ppc64, \ + ppc64_dwarf::dwarf_##reg##_ppc64, lldb_kind, LLDB_INVALID_REGNUM, \ + vmx_##reg##_ppc64 }, \ + NULL, NULL, NULL, \ + } + +// General purpose registers. +// EH_Frame, Generic, Process Plugin +#define PPC64_REGS \ + DEFINE_GPR_PPC64(r0, NULL, LLDB_INVALID_REGNUM) \ + , DEFINE_GPR_PPC64(r1, NULL, LLDB_REGNUM_GENERIC_SP), \ + DEFINE_GPR_PPC64(r2, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r3, NULL, LLDB_REGNUM_GENERIC_ARG1), \ + DEFINE_GPR_PPC64(r4, NULL, LLDB_REGNUM_GENERIC_ARG2), \ + DEFINE_GPR_PPC64(r5, NULL, LLDB_REGNUM_GENERIC_ARG3), \ + DEFINE_GPR_PPC64(r6, NULL, LLDB_REGNUM_GENERIC_ARG4), \ + DEFINE_GPR_PPC64(r7, NULL, LLDB_REGNUM_GENERIC_ARG5), \ + DEFINE_GPR_PPC64(r8, NULL, LLDB_REGNUM_GENERIC_ARG6), \ + DEFINE_GPR_PPC64(r9, NULL, LLDB_REGNUM_GENERIC_ARG7), \ + DEFINE_GPR_PPC64(r10, NULL, LLDB_REGNUM_GENERIC_ARG8), \ + DEFINE_GPR_PPC64(r11, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r12, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r13, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r14, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r15, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r16, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r17, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r18, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r19, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r20, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r21, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r22, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r23, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r24, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r25, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r26, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r27, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r28, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r29, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r30, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(r31, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(cr, NULL, LLDB_REGNUM_GENERIC_FLAGS), \ + DEFINE_GPR_PPC64(msr, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(xer, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(lr, NULL, LLDB_REGNUM_GENERIC_RA), \ + DEFINE_GPR_PPC64(ctr, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR_PPC64(pc, NULL, LLDB_REGNUM_GENERIC_PC), \ + DEFINE_FPR_PPC64(f0, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f1, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f2, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f3, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f4, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f5, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f6, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f7, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f8, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f9, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f10, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f11, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f12, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f13, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f14, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f15, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f16, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f17, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f18, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f19, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f20, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f21, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f22, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f23, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f24, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f25, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f26, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f27, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f28, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f29, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f30, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR_PPC64(f31, NULL, LLDB_INVALID_REGNUM), \ + {"fpscr", \ + NULL, \ + 8, \ + FPR_PPC64_OFFSET(fpscr), \ + lldb::eEncodingUint, \ + lldb::eFormatHex, \ + {ppc64_dwarf::dwarf_fpscr_ppc64, \ + ppc64_dwarf::dwarf_fpscr_ppc64, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, fpr_fpscr_ppc64}, \ + NULL, \ + NULL, \ + NULL, \ + }, \ + DEFINE_VMX_PPC64(vr0, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr1, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr2, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr3, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr4, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr5, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr6, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr7, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr8, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr9, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr10, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr11, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr12, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr13, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr14, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr15, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr16, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr17, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr18, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr19, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr20, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr21, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr22, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr23, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr24, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr25, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr26, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr27, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr28, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr29, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr30, LLDB_INVALID_REGNUM), \ + DEFINE_VMX_PPC64(vr31, LLDB_INVALID_REGNUM), \ + {"vscr", \ + NULL, \ + 4, \ + VMX_PPC64_OFFSET(vscr), \ + lldb::eEncodingUint, \ + lldb::eFormatHex, \ + {ppc64_dwarf::dwarf_vscr_ppc64, ppc64_dwarf::dwarf_vscr_ppc64, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, vmx_vscr_ppc64}, \ + NULL, \ + NULL, \ + NULL, \ + }, \ + {"vrsave", \ + NULL, \ + 4, \ + VMX_PPC64_OFFSET(vrsave), \ + lldb::eEncodingUint, \ + lldb::eFormatHex, \ + {ppc64_dwarf::dwarf_vrsave_ppc64, \ + ppc64_dwarf::dwarf_vrsave_ppc64, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, vmx_vrsave_ppc64}, \ + NULL, \ + NULL, \ + NULL, \ + }, /* */ + +typedef struct _GPR_PPC64 { + uint64_t r0; + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t r28; + uint64_t r29; + uint64_t r30; + uint64_t r31; + uint64_t cr; + uint64_t msr; + uint64_t xer; + uint64_t lr; + uint64_t ctr; + uint64_t pc; + uint64_t pad[3]; +} GPR_PPC64; + +typedef struct _FPR_PPC64 { + uint64_t f0; + uint64_t f1; + uint64_t f2; + uint64_t f3; + uint64_t f4; + uint64_t f5; + uint64_t f6; + uint64_t f7; + uint64_t f8; + uint64_t f9; + uint64_t f10; + uint64_t f11; + uint64_t f12; + uint64_t f13; + uint64_t f14; + uint64_t f15; + uint64_t f16; + uint64_t f17; + uint64_t f18; + uint64_t f19; + uint64_t f20; + uint64_t f21; + uint64_t f22; + uint64_t f23; + uint64_t f24; + uint64_t f25; + uint64_t f26; + uint64_t f27; + uint64_t f28; + uint64_t f29; + uint64_t f30; + uint64_t f31; + uint64_t fpscr; +} FPR_PPC64; + +typedef struct _VMX_PPC64 { + uint32_t vr0[4]; + uint32_t vr1[4]; + uint32_t vr2[4]; + uint32_t vr3[4]; + uint32_t vr4[4]; + uint32_t vr5[4]; + uint32_t vr6[4]; + uint32_t vr7[4]; + uint32_t vr8[4]; + uint32_t vr9[4]; + uint32_t vr10[4]; + uint32_t vr11[4]; + uint32_t vr12[4]; + uint32_t vr13[4]; + uint32_t vr14[4]; + uint32_t vr15[4]; + uint32_t vr16[4]; + uint32_t vr17[4]; + uint32_t vr18[4]; + uint32_t vr19[4]; + uint32_t vr20[4]; + uint32_t vr21[4]; + uint32_t vr22[4]; + uint32_t vr23[4]; + uint32_t vr24[4]; + uint32_t vr25[4]; + uint32_t vr26[4]; + uint32_t vr27[4]; + uint32_t vr28[4]; + uint32_t vr29[4]; + uint32_t vr30[4]; + uint32_t vr31[4]; + uint32_t pad[2]; + uint32_t vscr[2]; + uint32_t vrsave; +} VMX_PPC64; + + +static lldb_private::RegisterInfo g_register_infos_ppc64[] = { + PPC64_REGS +}; + +static_assert((sizeof(g_register_infos_ppc64) / + sizeof(g_register_infos_ppc64[0])) == + k_num_registers_ppc64, + "g_register_infos_powerpc64 has wrong number of register infos"); + +#undef DEFINE_FPR_PPC64 +#undef DEFINE_GPR_PPC64 +#undef DEFINE_VMX_PPC64 + +#endif // DECLARE_REGISTER_INFOS_PPC64_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h new file mode 100644 index 000000000000..18489fb74f86 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_ppc64le.h @@ -0,0 +1,474 @@ +//===-- RegisterInfos_ppc64le.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 +// +//===----------------------------------------------------------------------===// + +#ifdef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT + +#include <cstddef> + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) (offsetof(GPR, regname)) +#define FPR_OFFSET(regname) (offsetof(FPR, regname) + sizeof(GPR)) +#define VMX_OFFSET(regname) (offsetof(VMX, regname) + sizeof(GPR) + sizeof(FPR)) +#define VSX_OFFSET(regname) \ + (offsetof(VSX, regname) + sizeof(GPR) + sizeof(FPR) + sizeof(VMX)) +#define GPR_SIZE(regname) (sizeof(((GPR *)NULL)->regname)) + +#include "Utility/PPC64LE_DWARF_Registers.h" +#include "lldb-ppc64le-register-enums.h" + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, lldb_kind) \ + { \ + #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), lldb::eEncodingUint, \ + lldb::eFormatHex, \ + {ppc64le_dwarf::dwarf_##reg##_ppc64le,\ + ppc64le_dwarf::dwarf_##reg##_ppc64le,\ + lldb_kind, \ + LLDB_INVALID_REGNUM, \ + gpr_##reg##_ppc64le }, \ + NULL, NULL, NULL, \ + } +#define DEFINE_FPR(reg, alt, lldb_kind) \ + { \ +#reg, alt, 8, FPR_OFFSET(reg), lldb::eEncodingIEEE754, lldb::eFormatFloat, \ + {ppc64le_dwarf::dwarf_##reg##_ppc64le, \ + ppc64le_dwarf::dwarf_##reg##_ppc64le, lldb_kind, LLDB_INVALID_REGNUM, \ + fpr_##reg##_ppc64le }, \ + NULL, NULL, NULL, \ + } +#define DEFINE_VMX(reg, lldb_kind) \ + { \ +#reg, NULL, 16, VMX_OFFSET(reg), lldb::eEncodingVector, \ + lldb::eFormatVectorOfUInt32, \ + {ppc64le_dwarf::dwarf_##reg##_ppc64le, \ + ppc64le_dwarf::dwarf_##reg##_ppc64le, lldb_kind, LLDB_INVALID_REGNUM, \ + vmx_##reg##_ppc64le }, \ + NULL, NULL, NULL, \ + } +#define DEFINE_VSX(reg, lldb_kind) \ + { \ +#reg, NULL, 16, VSX_OFFSET(reg), lldb::eEncodingVector, \ + lldb::eFormatVectorOfUInt32, \ + {ppc64le_dwarf::dwarf_##reg##_ppc64le, \ + ppc64le_dwarf::dwarf_##reg##_ppc64le, lldb_kind, LLDB_INVALID_REGNUM, \ + vsx_##reg##_ppc64le }, \ + NULL, NULL, NULL, \ + } + +// General purpose registers. +// EH_Frame, Generic, Process Plugin +#define POWERPC_REGS \ + DEFINE_GPR(r0, NULL, LLDB_INVALID_REGNUM) \ + , DEFINE_GPR(r1, NULL, LLDB_REGNUM_GENERIC_SP), \ + DEFINE_GPR(r2, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r3, NULL, LLDB_REGNUM_GENERIC_ARG1), \ + DEFINE_GPR(r4, NULL, LLDB_REGNUM_GENERIC_ARG2), \ + DEFINE_GPR(r5, NULL, LLDB_REGNUM_GENERIC_ARG3), \ + DEFINE_GPR(r6, NULL, LLDB_REGNUM_GENERIC_ARG4), \ + DEFINE_GPR(r7, NULL, LLDB_REGNUM_GENERIC_ARG5), \ + DEFINE_GPR(r8, NULL, LLDB_REGNUM_GENERIC_ARG6), \ + DEFINE_GPR(r9, NULL, LLDB_REGNUM_GENERIC_ARG7), \ + DEFINE_GPR(r10, NULL, LLDB_REGNUM_GENERIC_ARG8), \ + DEFINE_GPR(r11, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r12, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r13, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r14, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r15, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r16, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r17, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r18, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r19, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r20, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r21, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r22, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r23, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r24, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r25, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r26, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r27, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r28, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r29, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r30, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r31, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(pc, NULL, LLDB_REGNUM_GENERIC_PC), \ + DEFINE_GPR(msr, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(origr3, "orig_r3", LLDB_INVALID_REGNUM), \ + DEFINE_GPR(ctr, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(lr, NULL, LLDB_REGNUM_GENERIC_RA), \ + DEFINE_GPR(xer, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(cr, NULL, LLDB_REGNUM_GENERIC_FLAGS), \ + DEFINE_GPR(softe, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(trap, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f0, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f1, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f2, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f3, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f4, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f5, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f6, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f7, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f8, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f9, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f10, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f11, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f12, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f13, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f14, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f15, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f16, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f17, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f18, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f19, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f20, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f21, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f22, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f23, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f24, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f25, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f26, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f27, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f28, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f29, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f30, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f31, NULL, LLDB_INVALID_REGNUM), \ + {"fpscr", \ + NULL, \ + 8, \ + FPR_OFFSET(fpscr), \ + lldb::eEncodingUint, \ + lldb::eFormatHex, \ + {ppc64le_dwarf::dwarf_fpscr_ppc64le, \ + ppc64le_dwarf::dwarf_fpscr_ppc64le, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, fpr_fpscr_ppc64le}, \ + NULL, \ + NULL, \ + NULL, \ + }, \ + DEFINE_VMX(vr0, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr1, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr2, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr3, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr4, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr5, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr6, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr7, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr8, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr9, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr10, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr11, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr12, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr13, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr14, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr15, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr16, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr17, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr18, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr19, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr20, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr21, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr22, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr23, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr24, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr25, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr26, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr27, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr28, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr29, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr30, LLDB_INVALID_REGNUM), \ + DEFINE_VMX(vr31, LLDB_INVALID_REGNUM), \ + {"vscr", \ + NULL, \ + 4, \ + VMX_OFFSET(vscr), \ + lldb::eEncodingUint, \ + lldb::eFormatHex, \ + {ppc64le_dwarf::dwarf_vscr_ppc64le, ppc64le_dwarf::dwarf_vscr_ppc64le, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, vmx_vscr_ppc64le}, \ + NULL, \ + NULL, \ + NULL, \ + }, \ + {"vrsave", \ + NULL, \ + 4, \ + VMX_OFFSET(vrsave), \ + lldb::eEncodingUint, \ + lldb::eFormatHex, \ + {ppc64le_dwarf::dwarf_vrsave_ppc64le, \ + ppc64le_dwarf::dwarf_vrsave_ppc64le, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, vmx_vrsave_ppc64le}, \ + NULL, \ + NULL, \ + NULL, \ + }, \ + DEFINE_VSX(vs0, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs1, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs2, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs3, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs4, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs5, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs6, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs7, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs8, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs9, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs10, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs11, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs12, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs13, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs14, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs15, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs16, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs17, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs18, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs19, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs20, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs21, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs22, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs23, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs24, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs25, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs26, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs27, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs28, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs29, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs30, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs31, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs32, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs33, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs34, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs35, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs36, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs37, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs38, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs39, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs40, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs41, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs42, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs43, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs44, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs45, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs46, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs47, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs48, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs49, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs50, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs51, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs52, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs53, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs54, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs55, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs56, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs57, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs58, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs59, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs50, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs61, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs62, LLDB_INVALID_REGNUM), \ + DEFINE_VSX(vs63, LLDB_INVALID_REGNUM), /* */ + +typedef struct _GPR { + uint64_t r0; + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t r28; + uint64_t r29; + uint64_t r30; + uint64_t r31; + uint64_t pc; + uint64_t msr; + uint64_t origr3; + uint64_t ctr; + uint64_t lr; + uint64_t xer; + uint64_t cr; + uint64_t softe; + uint64_t trap; + uint64_t pad[3]; +} GPR; + +typedef struct _FPR { + uint64_t f0; + uint64_t f1; + uint64_t f2; + uint64_t f3; + uint64_t f4; + uint64_t f5; + uint64_t f6; + uint64_t f7; + uint64_t f8; + uint64_t f9; + uint64_t f10; + uint64_t f11; + uint64_t f12; + uint64_t f13; + uint64_t f14; + uint64_t f15; + uint64_t f16; + uint64_t f17; + uint64_t f18; + uint64_t f19; + uint64_t f20; + uint64_t f21; + uint64_t f22; + uint64_t f23; + uint64_t f24; + uint64_t f25; + uint64_t f26; + uint64_t f27; + uint64_t f28; + uint64_t f29; + uint64_t f30; + uint64_t f31; + uint64_t fpscr; +} FPR; + +typedef struct _VMX { + uint32_t vr0[4]; + uint32_t vr1[4]; + uint32_t vr2[4]; + uint32_t vr3[4]; + uint32_t vr4[4]; + uint32_t vr5[4]; + uint32_t vr6[4]; + uint32_t vr7[4]; + uint32_t vr8[4]; + uint32_t vr9[4]; + uint32_t vr10[4]; + uint32_t vr11[4]; + uint32_t vr12[4]; + uint32_t vr13[4]; + uint32_t vr14[4]; + uint32_t vr15[4]; + uint32_t vr16[4]; + uint32_t vr17[4]; + uint32_t vr18[4]; + uint32_t vr19[4]; + uint32_t vr20[4]; + uint32_t vr21[4]; + uint32_t vr22[4]; + uint32_t vr23[4]; + uint32_t vr24[4]; + uint32_t vr25[4]; + uint32_t vr26[4]; + uint32_t vr27[4]; + uint32_t vr28[4]; + uint32_t vr29[4]; + uint32_t vr30[4]; + uint32_t vr31[4]; + uint32_t pad[2]; + uint32_t vscr[2]; + uint32_t vrsave; +} VMX; + +typedef struct _VSX { + uint32_t vs0[4]; + uint32_t vs1[4]; + uint32_t vs2[4]; + uint32_t vs3[4]; + uint32_t vs4[4]; + uint32_t vs5[4]; + uint32_t vs6[4]; + uint32_t vs7[4]; + uint32_t vs8[4]; + uint32_t vs9[4]; + uint32_t vs10[4]; + uint32_t vs11[4]; + uint32_t vs12[4]; + uint32_t vs13[4]; + uint32_t vs14[4]; + uint32_t vs15[4]; + uint32_t vs16[4]; + uint32_t vs17[4]; + uint32_t vs18[4]; + uint32_t vs19[4]; + uint32_t vs20[4]; + uint32_t vs21[4]; + uint32_t vs22[4]; + uint32_t vs23[4]; + uint32_t vs24[4]; + uint32_t vs25[4]; + uint32_t vs26[4]; + uint32_t vs27[4]; + uint32_t vs28[4]; + uint32_t vs29[4]; + uint32_t vs30[4]; + uint32_t vs31[4]; + uint32_t vs32[4]; + uint32_t vs33[4]; + uint32_t vs34[4]; + uint32_t vs35[4]; + uint32_t vs36[4]; + uint32_t vs37[4]; + uint32_t vs38[4]; + uint32_t vs39[4]; + uint32_t vs40[4]; + uint32_t vs41[4]; + uint32_t vs42[4]; + uint32_t vs43[4]; + uint32_t vs44[4]; + uint32_t vs45[4]; + uint32_t vs46[4]; + uint32_t vs47[4]; + uint32_t vs48[4]; + uint32_t vs49[4]; + uint32_t vs50[4]; + uint32_t vs51[4]; + uint32_t vs52[4]; + uint32_t vs53[4]; + uint32_t vs54[4]; + uint32_t vs55[4]; + uint32_t vs56[4]; + uint32_t vs57[4]; + uint32_t vs58[4]; + uint32_t vs59[4]; + uint32_t vs60[4]; + uint32_t vs61[4]; + uint32_t vs62[4]; + uint32_t vs63[4]; +} VSX; + +static lldb_private::RegisterInfo g_register_infos_ppc64le[] = { + POWERPC_REGS +}; + +static_assert((sizeof(g_register_infos_ppc64le) / + sizeof(g_register_infos_ppc64le[0])) == + k_num_registers_ppc64le, + "g_register_infos_powerpc64 has wrong number of register infos"); + +#undef DEFINE_FPR +#undef DEFINE_GPR +#undef DEFINE_VMX +#undef DEFINE_VSX + +#endif // DECLARE_REGISTER_INFOS_PPC64LE_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_riscv64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_riscv64.h new file mode 100644 index 000000000000..720d900c7b97 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_riscv64.h @@ -0,0 +1,186 @@ +//===-- RegisterInfos_riscv64.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 +// +//===----------------------------------------------------------------------===// + +#ifdef DECLARE_REGISTER_INFOS_RISCV64_STRUCT + +#include <stddef.h> + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private.h" + +#include "Utility/RISCV_DWARF_Registers.h" +#include "lldb-riscv-register-enums.h" + +#ifndef GPR_OFFSET +#error GPR_OFFSET must be defined before including this header file +#endif + +#ifndef FPR_OFFSET +#error FPR_OFFSET must be defined before including this header file +#endif + +using namespace riscv_dwarf; + +// clang-format off + +// I suppose EHFrame and DWARF are the same. +#define KIND_HELPER(reg, generic_kind) \ + { \ + riscv_dwarf::dwarf_##reg, riscv_dwarf::dwarf_##reg, generic_kind, \ + LLDB_INVALID_REGNUM, reg##_riscv \ + } + +// Generates register kinds array for vector registers +#define GPR64_KIND(reg, generic_kind) KIND_HELPER(reg, generic_kind) + +// FPR register kinds array for vector registers +#define FPR64_KIND(reg, generic_kind) KIND_HELPER(reg, generic_kind) + +// VPR register kinds array for vector registers +#define VPR_KIND(reg, generic_kind) KIND_HELPER(reg, generic_kind) + +// Defines a 64-bit general purpose register +#define DEFINE_GPR64(reg, generic_kind) DEFINE_GPR64_ALT(reg, reg, generic_kind) + +// Defines a 64-bit general purpose register +#define DEFINE_GPR64_ALT(reg, alt, generic_kind) \ + { \ + #reg, #alt, 8, GPR_OFFSET(gpr_##reg##_riscv - gpr_first_riscv), \ + lldb::eEncodingUint, lldb::eFormatHex, \ + GPR64_KIND(gpr_##reg, generic_kind), nullptr, nullptr, nullptr, \ + } + +#define DEFINE_FPR64(reg, generic_kind) DEFINE_FPR64_ALT(reg, reg, generic_kind) + +#define DEFINE_FPR64_ALT(reg, alt, generic_kind) DEFINE_FPR_ALT(reg, alt, 8, generic_kind) + +#define DEFINE_FPR_ALT(reg, alt, size, generic_kind) \ + { \ + #reg, #alt, size, FPR_OFFSET(fpr_##reg##_riscv - fpr_first_riscv), \ + lldb::eEncodingUint, lldb::eFormatHex, \ + FPR64_KIND(fpr_##reg, generic_kind), nullptr, nullptr, nullptr, \ + } + +#define DEFINE_VPR(reg, generic_kind) DEFINE_VPR_ALT(reg, reg, generic_kind) + +// Defines a scalable vector register, with default size 128 bits +// The byte offset 0 is a placeholder, which should be corrected at runtime. +#define DEFINE_VPR_ALT(reg, alt, generic_kind) \ + { \ + #reg, #alt, 16, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8, \ + VPR_KIND(vpr_##reg, generic_kind), nullptr, nullptr, nullptr \ + } + +// clang-format on + +static lldb_private::RegisterInfo g_register_infos_riscv64_le[] = { + // DEFINE_GPR64(name, GENERIC KIND) + DEFINE_GPR64(pc, LLDB_REGNUM_GENERIC_PC), + DEFINE_GPR64_ALT(ra, x1, LLDB_REGNUM_GENERIC_RA), + DEFINE_GPR64_ALT(sp, x2, LLDB_REGNUM_GENERIC_SP), + DEFINE_GPR64_ALT(gp, x3, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(tp, x4, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(t0, x5, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(t1, x6, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(t2, x7, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(fp, x8, LLDB_REGNUM_GENERIC_FP), + DEFINE_GPR64_ALT(s1, x9, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(a0, x10, LLDB_REGNUM_GENERIC_ARG1), + DEFINE_GPR64_ALT(a1, x11, LLDB_REGNUM_GENERIC_ARG2), + DEFINE_GPR64_ALT(a2, x12, LLDB_REGNUM_GENERIC_ARG3), + DEFINE_GPR64_ALT(a3, x13, LLDB_REGNUM_GENERIC_ARG4), + DEFINE_GPR64_ALT(a4, x14, LLDB_REGNUM_GENERIC_ARG5), + DEFINE_GPR64_ALT(a5, x15, LLDB_REGNUM_GENERIC_ARG6), + DEFINE_GPR64_ALT(a6, x16, LLDB_REGNUM_GENERIC_ARG7), + DEFINE_GPR64_ALT(a7, x17, LLDB_REGNUM_GENERIC_ARG8), + DEFINE_GPR64_ALT(s2, x18, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(s3, x19, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(s4, x20, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(s5, x21, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(s6, x22, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(s7, x23, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(s8, x24, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(s9, x25, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(s10, x26, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(s11, x27, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(t3, x28, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(t4, x29, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(t5, x30, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(t6, x31, LLDB_INVALID_REGNUM), + DEFINE_GPR64_ALT(zero, x0, LLDB_INVALID_REGNUM), + + DEFINE_FPR64_ALT(ft0, f0, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft1, f1, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft2, f2, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft3, f3, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft4, f4, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft5, f5, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft6, f6, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft7, f7, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs0, f8, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs1, f9, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fa0, f10, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fa1, f11, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fa2, f12, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fa3, f13, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fa4, f14, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fa5, f15, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fa6, f16, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fa7, f17, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs2, f18, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs3, f19, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs4, f20, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs5, f21, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs6, f22, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs7, f23, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs8, f24, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs9, f25, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs10, f26, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(fs11, f27, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft8, f28, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft9, f29, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft10, f30, LLDB_INVALID_REGNUM), + DEFINE_FPR64_ALT(ft11, f31, LLDB_INVALID_REGNUM), + DEFINE_FPR_ALT(fcsr, nullptr, 4, LLDB_INVALID_REGNUM), + + DEFINE_VPR(v0, LLDB_INVALID_REGNUM), + DEFINE_VPR(v1, LLDB_INVALID_REGNUM), + DEFINE_VPR(v2, LLDB_INVALID_REGNUM), + DEFINE_VPR(v3, LLDB_INVALID_REGNUM), + DEFINE_VPR(v4, LLDB_INVALID_REGNUM), + DEFINE_VPR(v5, LLDB_INVALID_REGNUM), + DEFINE_VPR(v6, LLDB_INVALID_REGNUM), + DEFINE_VPR(v7, LLDB_INVALID_REGNUM), + DEFINE_VPR(v8, LLDB_INVALID_REGNUM), + DEFINE_VPR(v9, LLDB_INVALID_REGNUM), + DEFINE_VPR(v10, LLDB_INVALID_REGNUM), + DEFINE_VPR(v11, LLDB_INVALID_REGNUM), + DEFINE_VPR(v12, LLDB_INVALID_REGNUM), + DEFINE_VPR(v13, LLDB_INVALID_REGNUM), + DEFINE_VPR(v14, LLDB_INVALID_REGNUM), + DEFINE_VPR(v15, LLDB_INVALID_REGNUM), + DEFINE_VPR(v16, LLDB_INVALID_REGNUM), + DEFINE_VPR(v17, LLDB_INVALID_REGNUM), + DEFINE_VPR(v18, LLDB_INVALID_REGNUM), + DEFINE_VPR(v19, LLDB_INVALID_REGNUM), + DEFINE_VPR(v20, LLDB_INVALID_REGNUM), + DEFINE_VPR(v21, LLDB_INVALID_REGNUM), + DEFINE_VPR(v22, LLDB_INVALID_REGNUM), + DEFINE_VPR(v23, LLDB_INVALID_REGNUM), + DEFINE_VPR(v24, LLDB_INVALID_REGNUM), + DEFINE_VPR(v25, LLDB_INVALID_REGNUM), + DEFINE_VPR(v26, LLDB_INVALID_REGNUM), + DEFINE_VPR(v27, LLDB_INVALID_REGNUM), + DEFINE_VPR(v28, LLDB_INVALID_REGNUM), + DEFINE_VPR(v29, LLDB_INVALID_REGNUM), + DEFINE_VPR(v30, LLDB_INVALID_REGNUM), + DEFINE_VPR(v31, LLDB_INVALID_REGNUM), +}; + +#endif // DECLARE_REGISTER_INFOS_RISCV64_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h new file mode 100644 index 000000000000..7b5f204ebbad --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_s390x.h @@ -0,0 +1,124 @@ +//===-- RegisterInfos_s390x.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 +// +//===----------------------------------------------------------------------===// + +#include <cstddef> + +#include "llvm/Support/Compiler.h" + + +#ifdef DECLARE_REGISTER_INFOS_S390X_STRUCT + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(num) (16 + 8 * num) +// Computes the offset of the given ACR in the user data area. +#define ACR_OFFSET(num) (16 + 8 * 16 + 4 * num) +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(num) (8 + 8 * num) + +// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB + +#define DEFINE_GPR(name, size, offset, alt, generic) \ + { \ + #name, alt, size, offset, eEncodingUint, eFormatHex, \ + {dwarf_##name##_s390x, dwarf_##name##_s390x, generic, \ + LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \ + NULL, NULL, NULL, \ + } + +#define DEFINE_GPR_NODWARF(name, size, offset, alt, generic) \ + { \ + #name, alt, size, offset, eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, generic, \ + LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \ + NULL, NULL, NULL, \ + } + +#define DEFINE_FPR(name, size, offset) \ + { \ + #name, NULL, size, offset, eEncodingUint, eFormatHex, \ + {dwarf_##name##_s390x, dwarf_##name##_s390x, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \ + NULL, NULL, NULL, \ + } + +#define DEFINE_FPR_NODWARF(name, size, offset) \ + { \ + #name, NULL, size, offset, eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_##name##_s390x }, \ + NULL, NULL, NULL, \ + } + +static RegisterInfo g_register_infos_s390x[] = { + // General purpose registers. + DEFINE_GPR(r0, 8, GPR_OFFSET(0), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r1, 8, GPR_OFFSET(1), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r2, 8, GPR_OFFSET(2), nullptr, LLDB_REGNUM_GENERIC_ARG1), + DEFINE_GPR(r3, 8, GPR_OFFSET(3), nullptr, LLDB_REGNUM_GENERIC_ARG2), + DEFINE_GPR(r4, 8, GPR_OFFSET(4), nullptr, LLDB_REGNUM_GENERIC_ARG3), + DEFINE_GPR(r5, 8, GPR_OFFSET(5), nullptr, LLDB_REGNUM_GENERIC_ARG4), + DEFINE_GPR(r6, 8, GPR_OFFSET(6), nullptr, LLDB_REGNUM_GENERIC_ARG5), + DEFINE_GPR(r7, 8, GPR_OFFSET(7), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r8, 8, GPR_OFFSET(8), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, 8, GPR_OFFSET(9), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, 8, GPR_OFFSET(10), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, 8, GPR_OFFSET(11), nullptr, LLDB_REGNUM_GENERIC_FP), + DEFINE_GPR(r12, 8, GPR_OFFSET(12), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, 8, GPR_OFFSET(13), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, 8, GPR_OFFSET(14), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, 8, GPR_OFFSET(15), nullptr, LLDB_REGNUM_GENERIC_SP), + DEFINE_GPR(acr0, 4, ACR_OFFSET(0), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr1, 4, ACR_OFFSET(1), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr2, 4, ACR_OFFSET(2), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr3, 4, ACR_OFFSET(3), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr4, 4, ACR_OFFSET(4), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr5, 4, ACR_OFFSET(5), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr6, 4, ACR_OFFSET(6), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr7, 4, ACR_OFFSET(7), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr8, 4, ACR_OFFSET(8), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr9, 4, ACR_OFFSET(9), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr10, 4, ACR_OFFSET(10), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr11, 4, ACR_OFFSET(11), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr12, 4, ACR_OFFSET(12), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr13, 4, ACR_OFFSET(13), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr14, 4, ACR_OFFSET(14), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(acr15, 4, ACR_OFFSET(15), nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR(pswm, 8, 0, nullptr, LLDB_REGNUM_GENERIC_FLAGS), + DEFINE_GPR(pswa, 8, 8, nullptr, LLDB_REGNUM_GENERIC_PC), + + // Floating point registers. + DEFINE_FPR(f0, 8, FPR_OFFSET(0)), DEFINE_FPR(f1, 8, FPR_OFFSET(1)), + DEFINE_FPR(f2, 8, FPR_OFFSET(2)), DEFINE_FPR(f3, 8, FPR_OFFSET(3)), + DEFINE_FPR(f4, 8, FPR_OFFSET(4)), DEFINE_FPR(f5, 8, FPR_OFFSET(5)), + DEFINE_FPR(f6, 8, FPR_OFFSET(6)), DEFINE_FPR(f7, 8, FPR_OFFSET(7)), + DEFINE_FPR(f8, 8, FPR_OFFSET(8)), DEFINE_FPR(f9, 8, FPR_OFFSET(9)), + DEFINE_FPR(f10, 8, FPR_OFFSET(10)), DEFINE_FPR(f11, 8, FPR_OFFSET(11)), + DEFINE_FPR(f12, 8, FPR_OFFSET(12)), DEFINE_FPR(f13, 8, FPR_OFFSET(13)), + DEFINE_FPR(f14, 8, FPR_OFFSET(14)), DEFINE_FPR(f15, 8, FPR_OFFSET(15)), + DEFINE_FPR_NODWARF(fpc, 4, 0), + + // Linux operating-specific info. + DEFINE_GPR_NODWARF(orig_r2, 8, 16 + 16 * 8 + 16 * 4, nullptr, + LLDB_INVALID_REGNUM), + DEFINE_GPR_NODWARF(last_break, 8, 0, nullptr, LLDB_INVALID_REGNUM), + DEFINE_GPR_NODWARF(system_call, 4, 0, nullptr, LLDB_INVALID_REGNUM), +}; + +static_assert((sizeof(g_register_infos_s390x) / + sizeof(g_register_infos_s390x[0])) == k_num_registers_s390x, + "g_register_infos_s390x has wrong number of register infos"); + +#undef GPR_OFFSET +#undef ACR_OFFSET +#undef FPR_OFFSET +#undef DEFINE_GPR +#undef DEFINE_GPR_NODWARF +#undef DEFINE_FPR +#undef DEFINE_FPR_NODWARF + +#endif // DECLARE_REGISTER_INFOS_S390X_STRUCT diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h new file mode 100644 index 000000000000..163438158155 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64.h @@ -0,0 +1,486 @@ +//===-- RegisterInfos_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 +// +//===----------------------------------------------------------------------===// + +// This file is meant to be textually included. Do not #include modular +// headers here. + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) + +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, fxsave) + \ + LLVM_EXTENSION offsetof(FXSAVE, regname)) + +// Computes the offset of the YMM register assembled from register halves. +// Based on DNBArchImplX86_64.cpp from debugserver +#define YMM_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xsave) + \ + LLVM_EXTENSION offsetof(XSAVE, ymmh[0]) + (32 * reg_index)) + +// Guarantees BNDR/BNDC offsets do not overlap with YMM offsets. +#define GDB_REMOTE_OFFSET 128 + +#define BNDR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xsave) + \ + LLVM_EXTENSION offsetof(XSAVE, mpxr[reg_index]) + GDB_REMOTE_OFFSET) + +#define BNDC_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xsave) + \ + LLVM_EXTENSION offsetof(XSAVE, mpxc[reg_index]) + GDB_REMOTE_OFFSET) + +#ifdef DECLARE_REGISTER_INFOS_X86_64_STRUCT + +// Number of bytes needed to represent a FPR. +#define FPR_SIZE(reg) sizeof(((FXSAVE *)nullptr)->reg) + +// Number of bytes needed to represent the i'th FP register. +#define FP_SIZE sizeof(((MMSReg *)nullptr)->bytes) + +// Number of bytes needed to represent an XMM register. +#define XMM_SIZE sizeof(XMMReg) + +// Number of bytes needed to represent a YMM register. +#define YMM_SIZE sizeof(YMMReg) + +// Number of bytes needed to represent MPX registers. +#define BNDR_SIZE sizeof(MPXReg) +#define BNDC_SIZE sizeof(MPXCsr) + +#define DR_SIZE sizeof(((DBG *)nullptr)->dr[0]) + +// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ + #reg, alt, sizeof(((GPR *)nullptr)->reg), \ + GPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, kind4, \ + lldb_##reg##_x86_64 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \ + { \ + #name, nullptr, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, kind4, \ + lldb_##name##_x86_64 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_FP_ST(reg, i) \ + { \ + #reg #i, nullptr, FP_SIZE, \ + LLVM_EXTENSION FPR_OFFSET( \ + stmm[i]), eEncodingVector, eFormatVectorOfUInt8, \ + {dwarf_st##i##_x86_64, dwarf_st##i##_x86_64, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_st##i##_x86_64 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_FP_MM(reg, i, streg) \ + { \ + #reg #i, nullptr, sizeof(uint64_t), LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ + eEncodingUint, eFormatHex, \ + {dwarf_mm##i##_x86_64, dwarf_mm##i##_x86_64, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_mm##i##_x86_64 }, \ + RegisterContextPOSIX_x86::g_contained_##streg##_64, \ + RegisterContextPOSIX_x86::g_invalidate_##streg##_64, \ + nullptr, \ + } + +#define DEFINE_XMM(reg, i) \ + { \ + #reg #i, nullptr, XMM_SIZE, \ + LLVM_EXTENSION FPR_OFFSET( \ + reg[i]), eEncodingVector, eFormatVectorOfUInt8, \ + {dwarf_##reg##i##_x86_64, dwarf_##reg##i##_x86_64, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg##i##_x86_64 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_YMM(reg, i) \ + { \ + #reg #i, nullptr, YMM_SIZE, \ + LLVM_EXTENSION YMM_OFFSET(i), eEncodingVector, eFormatVectorOfUInt8, \ + {dwarf_##reg##i##h_x86_64, \ + dwarf_##reg##i##h_x86_64, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg##i##_x86_64 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_BNDR(reg, i) \ + { \ + #reg #i, nullptr, BNDR_SIZE, \ + LLVM_EXTENSION BNDR_OFFSET(i), eEncodingVector, eFormatVectorOfUInt64, \ + {dwarf_##reg##i##_x86_64, \ + dwarf_##reg##i##_x86_64, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg##i##_x86_64 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_BNDC(name, i) \ + { \ + #name, nullptr, BNDC_SIZE, \ + LLVM_EXTENSION BNDC_OFFSET(i), eEncodingVector, eFormatVectorOfUInt8, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_##name##_x86_64 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_DR(reg, i) \ + { \ + #reg #i, nullptr, DR_SIZE, \ + DR_OFFSET(i), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg##i##_x86_64 }, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_32(reg32, reg64) \ + { \ + #reg32, nullptr, 4, \ + GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg32##_x86_64 }, \ + RegisterContextPOSIX_x86::g_contained_##reg64, \ + RegisterContextPOSIX_x86::g_invalidate_##reg64, \ + nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_16(reg16, reg64) \ + { \ + #reg16, nullptr, 2, \ + GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg16##_x86_64 }, \ + RegisterContextPOSIX_x86::g_contained_##reg64, \ + RegisterContextPOSIX_x86::g_invalidate_##reg64, \ + nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_8H(reg8, reg64) \ + { \ + #reg8, nullptr, 1, \ + GPR_OFFSET(reg64) + 1, eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg8##_x86_64 }, \ + RegisterContextPOSIX_x86::g_contained_##reg64, \ + RegisterContextPOSIX_x86::g_invalidate_##reg64, \ + nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_8L(reg8, reg64) \ + { \ + #reg8, nullptr, 1, \ + GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + lldb_##reg8##_x86_64 }, \ + RegisterContextPOSIX_x86::g_contained_##reg64, \ + RegisterContextPOSIX_x86::g_invalidate_##reg64, \ + nullptr \ + } + +#define DEFINE_FPR_32(name, reg, kind1, kind2, kind3, kind4, reg64) \ + { \ + #name, nullptr, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, kind4, lldb_##name##_x86_64 }, \ + RegisterContextPOSIX_x86::g_contained_##reg64, \ + RegisterContextPOSIX_x86::g_invalidate_##reg64, \ + nullptr, \ + } + +// clang-format off +static RegisterInfo g_register_infos_x86_64[] = { +// General purpose registers EH_Frame DWARF Generic Process Plugin +// =========================== ================== ================ ========================= ==================== + DEFINE_GPR(rax, nullptr, dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbx, nullptr, dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rcx, nullptr, dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdx, nullptr, dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdi, nullptr, dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsi, nullptr, dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbp, nullptr, dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsp, nullptr, dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(r8, nullptr, dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, nullptr, dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, nullptr, dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, nullptr, dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r12, nullptr, dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, nullptr, dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, nullptr, dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, nullptr, dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rip, nullptr, dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(rflags, nullptr, dwarf_rflags_x86_64, dwarf_rflags_x86_64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, dwarf_cs_x86_64, dwarf_cs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, dwarf_fs_x86_64, dwarf_fs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, dwarf_gs_x86_64, dwarf_gs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, dwarf_ss_x86_64, dwarf_ss_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, dwarf_ds_x86_64, dwarf_ds_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, dwarf_es_x86_64, dwarf_es_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + DEFINE_GPR_PSEUDO_32(eax, rax), DEFINE_GPR_PSEUDO_32(ebx, rbx), + DEFINE_GPR_PSEUDO_32(ecx, rcx), DEFINE_GPR_PSEUDO_32(edx, rdx), + DEFINE_GPR_PSEUDO_32(edi, rdi), DEFINE_GPR_PSEUDO_32(esi, rsi), + DEFINE_GPR_PSEUDO_32(ebp, rbp), DEFINE_GPR_PSEUDO_32(esp, rsp), + DEFINE_GPR_PSEUDO_32(r8d, r8), DEFINE_GPR_PSEUDO_32(r9d, r9), + DEFINE_GPR_PSEUDO_32(r10d, r10), DEFINE_GPR_PSEUDO_32(r11d, r11), + DEFINE_GPR_PSEUDO_32(r12d, r12), DEFINE_GPR_PSEUDO_32(r13d, r13), + DEFINE_GPR_PSEUDO_32(r14d, r14), DEFINE_GPR_PSEUDO_32(r15d, r15), + DEFINE_GPR_PSEUDO_16(ax, rax), DEFINE_GPR_PSEUDO_16(bx, rbx), + DEFINE_GPR_PSEUDO_16(cx, rcx), DEFINE_GPR_PSEUDO_16(dx, rdx), + DEFINE_GPR_PSEUDO_16(di, rdi), DEFINE_GPR_PSEUDO_16(si, rsi), + DEFINE_GPR_PSEUDO_16(bp, rbp), DEFINE_GPR_PSEUDO_16(sp, rsp), + DEFINE_GPR_PSEUDO_16(r8w, r8), DEFINE_GPR_PSEUDO_16(r9w, r9), + DEFINE_GPR_PSEUDO_16(r10w, r10), DEFINE_GPR_PSEUDO_16(r11w, r11), + DEFINE_GPR_PSEUDO_16(r12w, r12), DEFINE_GPR_PSEUDO_16(r13w, r13), + DEFINE_GPR_PSEUDO_16(r14w, r14), DEFINE_GPR_PSEUDO_16(r15w, r15), + DEFINE_GPR_PSEUDO_8H(ah, rax), DEFINE_GPR_PSEUDO_8H(bh, rbx), + DEFINE_GPR_PSEUDO_8H(ch, rcx), DEFINE_GPR_PSEUDO_8H(dh, rdx), + DEFINE_GPR_PSEUDO_8L(al, rax), DEFINE_GPR_PSEUDO_8L(bl, rbx), + DEFINE_GPR_PSEUDO_8L(cl, rcx), DEFINE_GPR_PSEUDO_8L(dl, rdx), + DEFINE_GPR_PSEUDO_8L(dil, rdi), DEFINE_GPR_PSEUDO_8L(sil, rsi), + DEFINE_GPR_PSEUDO_8L(bpl, rbp), DEFINE_GPR_PSEUDO_8L(spl, rsp), + DEFINE_GPR_PSEUDO_8L(r8l, r8), DEFINE_GPR_PSEUDO_8L(r9l, r9), + DEFINE_GPR_PSEUDO_8L(r10l, r10), DEFINE_GPR_PSEUDO_8L(r11l, r11), + DEFINE_GPR_PSEUDO_8L(r12l, r12), DEFINE_GPR_PSEUDO_8L(r13l, r13), + DEFINE_GPR_PSEUDO_8L(r14l, r14), DEFINE_GPR_PSEUDO_8L(r15l, r15), + +// i387 Floating point registers. EH_frame DWARF Generic Process Plugin reg64 +// ====================================== =============== ================== =================== ==================== ===== + DEFINE_FPR(fctrl, fctrl, dwarf_fctrl_x86_64, dwarf_fctrl_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fstat, fstat, dwarf_fstat_x86_64, dwarf_fstat_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(ftag, ftag, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fop, fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_32(fiseg, ptr.i386_.fiseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fip), + DEFINE_FPR_32(fioff, ptr.i386_.fioff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fip), + DEFINE_FPR(fip, ptr.x86_64.fip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_32(foseg, ptr.i386_.foseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fdp), + DEFINE_FPR_32(fooff, ptr.i386_.fooff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fdp), + DEFINE_FPR(fdp, ptr.x86_64.fdp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(mxcsr, mxcsr, dwarf_mxcsr_x86_64, dwarf_mxcsr_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(mxcsrmask, mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + // FP registers. + DEFINE_FP_ST(st, 0), DEFINE_FP_ST(st, 1), DEFINE_FP_ST(st, 2), + DEFINE_FP_ST(st, 3), DEFINE_FP_ST(st, 4), DEFINE_FP_ST(st, 5), + DEFINE_FP_ST(st, 6), DEFINE_FP_ST(st, 7), + + DEFINE_FP_MM(mm, 0, st0), DEFINE_FP_MM(mm, 1, st1), + DEFINE_FP_MM(mm, 2, st2), DEFINE_FP_MM(mm, 3, st3), + DEFINE_FP_MM(mm, 4, st4), DEFINE_FP_MM(mm, 5, st5), + DEFINE_FP_MM(mm, 6, st6), DEFINE_FP_MM(mm, 7, st7), + + // XMM registers + DEFINE_XMM(xmm, 0), DEFINE_XMM(xmm, 1), DEFINE_XMM(xmm, 2), + DEFINE_XMM(xmm, 3), DEFINE_XMM(xmm, 4), DEFINE_XMM(xmm, 5), + DEFINE_XMM(xmm, 6), DEFINE_XMM(xmm, 7), DEFINE_XMM(xmm, 8), + DEFINE_XMM(xmm, 9), DEFINE_XMM(xmm, 10), DEFINE_XMM(xmm, 11), + DEFINE_XMM(xmm, 12), DEFINE_XMM(xmm, 13), DEFINE_XMM(xmm, 14), + DEFINE_XMM(xmm, 15), + + // Copy of YMM registers assembled from xmm and ymmh + DEFINE_YMM(ymm, 0), DEFINE_YMM(ymm, 1), DEFINE_YMM(ymm, 2), + DEFINE_YMM(ymm, 3), DEFINE_YMM(ymm, 4), DEFINE_YMM(ymm, 5), + DEFINE_YMM(ymm, 6), DEFINE_YMM(ymm, 7), DEFINE_YMM(ymm, 8), + DEFINE_YMM(ymm, 9), DEFINE_YMM(ymm, 10), DEFINE_YMM(ymm, 11), + DEFINE_YMM(ymm, 12), DEFINE_YMM(ymm, 13), DEFINE_YMM(ymm, 14), + DEFINE_YMM(ymm, 15), + + // MPX registers + DEFINE_BNDR(bnd, 0), + DEFINE_BNDR(bnd, 1), + DEFINE_BNDR(bnd, 2), + DEFINE_BNDR(bnd, 3), + + DEFINE_BNDC(bndcfgu, 0), + DEFINE_BNDC(bndstatus, 1), + + // Debug registers for lldb internal use + DEFINE_DR(dr, 0), DEFINE_DR(dr, 1), DEFINE_DR(dr, 2), DEFINE_DR(dr, 3), + DEFINE_DR(dr, 4), DEFINE_DR(dr, 5), DEFINE_DR(dr, 6), DEFINE_DR(dr, 7)}; + +// clang-format on + +static_assert((sizeof(g_register_infos_x86_64) / + sizeof(g_register_infos_x86_64[0])) == k_num_registers_x86_64, + "g_register_infos_x86_64 has wrong number of register infos"); + +#undef FPR_SIZE +#undef FP_SIZE +#undef XMM_SIZE +#undef YMM_SIZE +#undef DEFINE_GPR +#undef DEFINE_FPR +#undef DEFINE_FP +#undef DEFINE_XMM +#undef DEFINE_YMM +#undef DEFINE_BNDR +#undef DEFINE_BNDC +#undef DEFINE_DR +#undef DEFINE_GPR_PSEUDO_32 +#undef DEFINE_GPR_PSEUDO_16 +#undef DEFINE_GPR_PSEUDO_8H +#undef DEFINE_GPR_PSEUDO_8L + +#endif // DECLARE_REGISTER_INFOS_X86_64_STRUCT + +#ifdef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS + +#define UPDATE_GPR_INFO(reg, reg64) \ + do { \ + g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64); \ + } while (false); + +#define UPDATE_GPR_INFO_8H(reg, reg64) \ + do { \ + g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64) + 1; \ + } while (false); + +#define UPDATE_FPR_INFO(reg, reg64) \ + do { \ + g_register_infos[lldb_##reg##_i386].byte_offset = FPR_OFFSET(reg64); \ + } while (false); + +#define UPDATE_FP_INFO(reg, i) \ + do { \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(stmm[i]); \ + } while (false); + +#define UPDATE_XMM_INFO(reg, i) \ + do { \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(reg[i]); \ + } while (false); + +#define UPDATE_YMM_INFO(reg, i) \ + do { \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = YMM_OFFSET(i); \ + } while (false); + +#define UPDATE_DR_INFO(reg_index) \ + do { \ + g_register_infos[lldb_dr##reg_index##_i386].byte_offset = \ + DR_OFFSET(reg_index); \ + } while (false); + +// Update the register offsets +UPDATE_GPR_INFO(eax, rax); +UPDATE_GPR_INFO(ebx, rbx); +UPDATE_GPR_INFO(ecx, rcx); +UPDATE_GPR_INFO(edx, rdx); +UPDATE_GPR_INFO(edi, rdi); +UPDATE_GPR_INFO(esi, rsi); +UPDATE_GPR_INFO(ebp, rbp); +UPDATE_GPR_INFO(esp, rsp); +UPDATE_GPR_INFO(eip, rip); +UPDATE_GPR_INFO(eflags, rflags); +UPDATE_GPR_INFO(cs, cs); +UPDATE_GPR_INFO(fs, fs); +UPDATE_GPR_INFO(gs, gs); +UPDATE_GPR_INFO(ss, ss); +UPDATE_GPR_INFO(ds, ds); +UPDATE_GPR_INFO(es, es); + +UPDATE_GPR_INFO(ax, rax); +UPDATE_GPR_INFO(bx, rbx); +UPDATE_GPR_INFO(cx, rcx); +UPDATE_GPR_INFO(dx, rdx); +UPDATE_GPR_INFO(di, rdi); +UPDATE_GPR_INFO(si, rsi); +UPDATE_GPR_INFO(bp, rbp); +UPDATE_GPR_INFO(sp, rsp); +UPDATE_GPR_INFO_8H(ah, rax); +UPDATE_GPR_INFO_8H(bh, rbx); +UPDATE_GPR_INFO_8H(ch, rcx); +UPDATE_GPR_INFO_8H(dh, rdx); +UPDATE_GPR_INFO(al, rax); +UPDATE_GPR_INFO(bl, rbx); +UPDATE_GPR_INFO(cl, rcx); +UPDATE_GPR_INFO(dl, rdx); + +UPDATE_FPR_INFO(fctrl, fctrl); +UPDATE_FPR_INFO(fstat, fstat); +UPDATE_FPR_INFO(ftag, ftag); +UPDATE_FPR_INFO(fop, fop); +UPDATE_FPR_INFO(fiseg, ptr.i386_.fiseg); +UPDATE_FPR_INFO(fioff, ptr.i386_.fioff); +UPDATE_FPR_INFO(fooff, ptr.i386_.fooff); +UPDATE_FPR_INFO(foseg, ptr.i386_.foseg); +UPDATE_FPR_INFO(mxcsr, mxcsr); +UPDATE_FPR_INFO(mxcsrmask, mxcsrmask); + +UPDATE_FP_INFO(st, 0); +UPDATE_FP_INFO(st, 1); +UPDATE_FP_INFO(st, 2); +UPDATE_FP_INFO(st, 3); +UPDATE_FP_INFO(st, 4); +UPDATE_FP_INFO(st, 5); +UPDATE_FP_INFO(st, 6); +UPDATE_FP_INFO(st, 7); +UPDATE_FP_INFO(mm, 0); +UPDATE_FP_INFO(mm, 1); +UPDATE_FP_INFO(mm, 2); +UPDATE_FP_INFO(mm, 3); +UPDATE_FP_INFO(mm, 4); +UPDATE_FP_INFO(mm, 5); +UPDATE_FP_INFO(mm, 6); +UPDATE_FP_INFO(mm, 7); + +UPDATE_XMM_INFO(xmm, 0); +UPDATE_XMM_INFO(xmm, 1); +UPDATE_XMM_INFO(xmm, 2); +UPDATE_XMM_INFO(xmm, 3); +UPDATE_XMM_INFO(xmm, 4); +UPDATE_XMM_INFO(xmm, 5); +UPDATE_XMM_INFO(xmm, 6); +UPDATE_XMM_INFO(xmm, 7); + +UPDATE_YMM_INFO(ymm, 0); +UPDATE_YMM_INFO(ymm, 1); +UPDATE_YMM_INFO(ymm, 2); +UPDATE_YMM_INFO(ymm, 3); +UPDATE_YMM_INFO(ymm, 4); +UPDATE_YMM_INFO(ymm, 5); +UPDATE_YMM_INFO(ymm, 6); +UPDATE_YMM_INFO(ymm, 7); + +UPDATE_DR_INFO(0); +UPDATE_DR_INFO(1); +UPDATE_DR_INFO(2); +UPDATE_DR_INFO(3); +UPDATE_DR_INFO(4); +UPDATE_DR_INFO(5); +UPDATE_DR_INFO(6); +UPDATE_DR_INFO(7); + +#undef UPDATE_GPR_INFO +#undef UPDATE_GPR_INFO_8H +#undef UPDATE_FPR_INFO +#undef UPDATE_FP_INFO +#undef UPDATE_XMM_INFO +#undef UPDATE_YMM_INFO +#undef UPDATE_DR_INFO + +#endif // UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS + +#undef GPR_OFFSET +#undef FPR_OFFSET +#undef YMM_OFFSET diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64_with_base.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64_with_base.h new file mode 100644 index 000000000000..b111d8f62d1f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64_with_base.h @@ -0,0 +1,471 @@ +//===-- RegisterInfos_x86_64_with_base.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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterInfos_x86_64_with_base_shared.h" + +// This file is meant to be textually included. Do not #include modular +// headers here. + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) + +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, fxsave) + \ + LLVM_EXTENSION offsetof(FXSAVE, regname)) + +// Computes the offset of the YMM register assembled from register halves. +// Based on DNBArchImplX86_64.cpp from debugserver +#define YMM_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xsave) + \ + LLVM_EXTENSION offsetof(XSAVE, ymmh[0]) + (32 * reg_index)) + +// Guarantees BNDR/BNDC offsets do not overlap with YMM offsets. +#define GDB_REMOTE_OFFSET 128 + +#define BNDR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xsave) + \ + LLVM_EXTENSION offsetof(XSAVE, mpxr[reg_index]) + GDB_REMOTE_OFFSET) + +#define BNDC_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR, xsave) + \ + LLVM_EXTENSION offsetof(XSAVE, mpxc[reg_index]) + GDB_REMOTE_OFFSET) + +#ifdef DECLARE_REGISTER_INFOS_X86_64_STRUCT + +// Number of bytes needed to represent a FPR. +#define FPR_SIZE(reg) sizeof(((FXSAVE *)nullptr)->reg) + +// Number of bytes needed to represent the i'th FP register. +#define FP_SIZE sizeof(((MMSReg *)nullptr)->bytes) + +// Number of bytes needed to represent an XMM register. +#define XMM_SIZE sizeof(XMMReg) + +// Number of bytes needed to represent a YMM register. +#define YMM_SIZE sizeof(YMMReg) + +// Number of bytes needed to represent MPX registers. +#define BNDR_SIZE sizeof(MPXReg) +#define BNDC_SIZE sizeof(MPXCsr) + +#define DR_SIZE sizeof(((DBG *)nullptr)->dr[0]) + +// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ + #reg, alt, sizeof(((GPR *)nullptr)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, \ + {kind1, kind2, kind3, kind4, x86_64_with_base::lldb_##reg}, nullptr, \ + nullptr, nullptr, \ + } + +#define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \ + { \ + #name, nullptr, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, kind4, x86_64_with_base::lldb_##name}, nullptr, \ + nullptr, nullptr, \ + } + +#define DEFINE_FP_ST(reg, i) \ + { \ + #reg #i, nullptr, FP_SIZE, LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ + eEncodingVector, eFormatVectorOfUInt8, \ + {dwarf_st##i##_x86_64, dwarf_st##i##_x86_64, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, x86_64_with_base::lldb_st##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_FP_MM(reg, i, streg) \ + { \ + #reg #i, nullptr, sizeof(uint64_t), LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ + eEncodingUint, eFormatHex, \ + {dwarf_mm##i##_x86_64, dwarf_mm##i##_x86_64, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, x86_64_with_base::lldb_mm##i}, \ + RegisterInfos_x86_64_with_base_shared::g_contained_##streg##_64, \ + RegisterInfos_x86_64_with_base_shared::g_invalidate_##streg##_64, \ + nullptr, \ + } + +#define DEFINE_XMM(reg, i) \ + { \ + #reg #i, nullptr, XMM_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \ + eEncodingVector, eFormatVectorOfUInt8, \ + {dwarf_##reg##i##_x86_64, dwarf_##reg##i##_x86_64, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + x86_64_with_base::lldb_##reg##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_YMM(reg, i) \ + { \ + #reg #i, nullptr, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(i), eEncodingVector, \ + eFormatVectorOfUInt8, \ + {dwarf_##reg##i##h_x86_64, dwarf_##reg##i##h_x86_64, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + x86_64_with_base::lldb_##reg##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_BNDR(reg, i) \ + { \ + #reg #i, nullptr, BNDR_SIZE, LLVM_EXTENSION BNDR_OFFSET(i), \ + eEncodingVector, eFormatVectorOfUInt64, \ + {dwarf_##reg##i##_x86_64, dwarf_##reg##i##_x86_64, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + x86_64_with_base::lldb_##reg##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_BNDC(name, i) \ + { \ + #name, nullptr, BNDC_SIZE, LLVM_EXTENSION BNDC_OFFSET(i), eEncodingVector, \ + eFormatVectorOfUInt8, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, x86_64_with_base::lldb_##name}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_DR(reg, i) \ + { \ + #reg #i, nullptr, DR_SIZE, DR_OFFSET(i), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, x86_64_with_base::lldb_##reg##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_32(reg32, reg64) \ + { \ + #reg32, nullptr, 4, GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, x86_64_with_base::lldb_##reg32}, \ + RegisterInfos_x86_64_with_base_shared::g_contained_##reg64, \ + RegisterInfos_x86_64_with_base_shared::g_invalidate_##reg64, nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_16(reg16, reg64) \ + { \ + #reg16, nullptr, 2, GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, x86_64_with_base::lldb_##reg16}, \ + RegisterInfos_x86_64_with_base_shared::g_contained_##reg64, \ + RegisterInfos_x86_64_with_base_shared::g_invalidate_##reg64, nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_8H(reg8, reg64) \ + { \ + #reg8, nullptr, 1, GPR_OFFSET(reg64) + 1, eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, x86_64_with_base::lldb_##reg8}, \ + RegisterInfos_x86_64_with_base_shared::g_contained_##reg64, \ + RegisterInfos_x86_64_with_base_shared::g_invalidate_##reg64, nullptr, \ + } + +#define DEFINE_GPR_PSEUDO_8L(reg8, reg64) \ + { \ + #reg8, nullptr, 1, GPR_OFFSET(reg64), eEncodingUint, eFormatHex, \ + {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, x86_64_with_base::lldb_##reg8}, \ + RegisterInfos_x86_64_with_base_shared::g_contained_##reg64, \ + RegisterInfos_x86_64_with_base_shared::g_invalidate_##reg64, nullptr \ + } + +#define DEFINE_FPR_32(name, reg, kind1, kind2, kind3, kind4, reg64) \ + { \ + #name, nullptr, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, eFormatHex, \ + {kind1, kind2, kind3, kind4, x86_64_with_base::lldb_##name}, \ + RegisterInfos_x86_64_with_base_shared::g_contained_##reg64, \ + RegisterInfos_x86_64_with_base_shared::g_invalidate_##reg64, nullptr, \ + } + +// clang-format off +static RegisterInfo g_register_infos_x86_64_with_base[] = { +// General purpose registers EH_Frame DWARF Generic Process Plugin +// =========================== ================== ================ ========================= ==================== + DEFINE_GPR(rax, nullptr, dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbx, nullptr, dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rcx, nullptr, dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdx, nullptr, dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdi, nullptr, dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsi, nullptr, dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbp, nullptr, dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsp, nullptr, dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(r8, nullptr, dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, nullptr, dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, nullptr, dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, nullptr, dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r12, nullptr, dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, nullptr, dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, nullptr, dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, nullptr, dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rip, nullptr, dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(rflags, nullptr, dwarf_rflags_x86_64, dwarf_rflags_x86_64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, dwarf_cs_x86_64, dwarf_cs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, dwarf_fs_x86_64, dwarf_fs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, dwarf_gs_x86_64, dwarf_gs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, dwarf_ss_x86_64, dwarf_ss_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs_base,nullptr, dwarf_fs_base_x86_64, dwarf_fs_base_x86_64, LLDB_REGNUM_GENERIC_TP, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs_base,nullptr, dwarf_gs_base_x86_64, dwarf_gs_base_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, dwarf_ds_x86_64, dwarf_ds_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, dwarf_es_x86_64, dwarf_es_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + DEFINE_GPR_PSEUDO_32(eax, rax), DEFINE_GPR_PSEUDO_32(ebx, rbx), + DEFINE_GPR_PSEUDO_32(ecx, rcx), DEFINE_GPR_PSEUDO_32(edx, rdx), + DEFINE_GPR_PSEUDO_32(edi, rdi), DEFINE_GPR_PSEUDO_32(esi, rsi), + DEFINE_GPR_PSEUDO_32(ebp, rbp), DEFINE_GPR_PSEUDO_32(esp, rsp), + DEFINE_GPR_PSEUDO_32(r8d, r8), DEFINE_GPR_PSEUDO_32(r9d, r9), + DEFINE_GPR_PSEUDO_32(r10d, r10), DEFINE_GPR_PSEUDO_32(r11d, r11), + DEFINE_GPR_PSEUDO_32(r12d, r12), DEFINE_GPR_PSEUDO_32(r13d, r13), + DEFINE_GPR_PSEUDO_32(r14d, r14), DEFINE_GPR_PSEUDO_32(r15d, r15), + DEFINE_GPR_PSEUDO_16(ax, rax), DEFINE_GPR_PSEUDO_16(bx, rbx), + DEFINE_GPR_PSEUDO_16(cx, rcx), DEFINE_GPR_PSEUDO_16(dx, rdx), + DEFINE_GPR_PSEUDO_16(di, rdi), DEFINE_GPR_PSEUDO_16(si, rsi), + DEFINE_GPR_PSEUDO_16(bp, rbp), DEFINE_GPR_PSEUDO_16(sp, rsp), + DEFINE_GPR_PSEUDO_16(r8w, r8), DEFINE_GPR_PSEUDO_16(r9w, r9), + DEFINE_GPR_PSEUDO_16(r10w, r10), DEFINE_GPR_PSEUDO_16(r11w, r11), + DEFINE_GPR_PSEUDO_16(r12w, r12), DEFINE_GPR_PSEUDO_16(r13w, r13), + DEFINE_GPR_PSEUDO_16(r14w, r14), DEFINE_GPR_PSEUDO_16(r15w, r15), + DEFINE_GPR_PSEUDO_8H(ah, rax), DEFINE_GPR_PSEUDO_8H(bh, rbx), + DEFINE_GPR_PSEUDO_8H(ch, rcx), DEFINE_GPR_PSEUDO_8H(dh, rdx), + DEFINE_GPR_PSEUDO_8L(al, rax), DEFINE_GPR_PSEUDO_8L(bl, rbx), + DEFINE_GPR_PSEUDO_8L(cl, rcx), DEFINE_GPR_PSEUDO_8L(dl, rdx), + DEFINE_GPR_PSEUDO_8L(dil, rdi), DEFINE_GPR_PSEUDO_8L(sil, rsi), + DEFINE_GPR_PSEUDO_8L(bpl, rbp), DEFINE_GPR_PSEUDO_8L(spl, rsp), + DEFINE_GPR_PSEUDO_8L(r8l, r8), DEFINE_GPR_PSEUDO_8L(r9l, r9), + DEFINE_GPR_PSEUDO_8L(r10l, r10), DEFINE_GPR_PSEUDO_8L(r11l, r11), + DEFINE_GPR_PSEUDO_8L(r12l, r12), DEFINE_GPR_PSEUDO_8L(r13l, r13), + DEFINE_GPR_PSEUDO_8L(r14l, r14), DEFINE_GPR_PSEUDO_8L(r15l, r15), + +// i387 Floating point registers. EH_frame DWARF Generic Process Plugin reg64 +// ====================================== =============== ================== =================== ==================== ===== + DEFINE_FPR(fctrl, fctrl, dwarf_fctrl_x86_64, dwarf_fctrl_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fstat, fstat, dwarf_fstat_x86_64, dwarf_fstat_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(ftag, ftag, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(fop, fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_32(fiseg, ptr.i386_.fiseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fip), + DEFINE_FPR_32(fioff, ptr.i386_.fioff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fip), + DEFINE_FPR(fip, ptr.x86_64.fip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR_32(foseg, ptr.i386_.foseg, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fdp), + DEFINE_FPR_32(fooff, ptr.i386_.fooff, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, fdp), + DEFINE_FPR(fdp, ptr.x86_64.fdp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(mxcsr, mxcsr, dwarf_mxcsr_x86_64, dwarf_mxcsr_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR(mxcsrmask, mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + // FP registers. + DEFINE_FP_ST(st, 0), DEFINE_FP_ST(st, 1), DEFINE_FP_ST(st, 2), + DEFINE_FP_ST(st, 3), DEFINE_FP_ST(st, 4), DEFINE_FP_ST(st, 5), + DEFINE_FP_ST(st, 6), DEFINE_FP_ST(st, 7), + + DEFINE_FP_MM(mm, 0, st0), DEFINE_FP_MM(mm, 1, st1), + DEFINE_FP_MM(mm, 2, st2), DEFINE_FP_MM(mm, 3, st3), + DEFINE_FP_MM(mm, 4, st4), DEFINE_FP_MM(mm, 5, st5), + DEFINE_FP_MM(mm, 6, st6), DEFINE_FP_MM(mm, 7, st7), + + // XMM registers + DEFINE_XMM(xmm, 0), DEFINE_XMM(xmm, 1), DEFINE_XMM(xmm, 2), + DEFINE_XMM(xmm, 3), DEFINE_XMM(xmm, 4), DEFINE_XMM(xmm, 5), + DEFINE_XMM(xmm, 6), DEFINE_XMM(xmm, 7), DEFINE_XMM(xmm, 8), + DEFINE_XMM(xmm, 9), DEFINE_XMM(xmm, 10), DEFINE_XMM(xmm, 11), + DEFINE_XMM(xmm, 12), DEFINE_XMM(xmm, 13), DEFINE_XMM(xmm, 14), + DEFINE_XMM(xmm, 15), + + // Copy of YMM registers assembled from xmm and ymmh + DEFINE_YMM(ymm, 0), DEFINE_YMM(ymm, 1), DEFINE_YMM(ymm, 2), + DEFINE_YMM(ymm, 3), DEFINE_YMM(ymm, 4), DEFINE_YMM(ymm, 5), + DEFINE_YMM(ymm, 6), DEFINE_YMM(ymm, 7), DEFINE_YMM(ymm, 8), + DEFINE_YMM(ymm, 9), DEFINE_YMM(ymm, 10), DEFINE_YMM(ymm, 11), + DEFINE_YMM(ymm, 12), DEFINE_YMM(ymm, 13), DEFINE_YMM(ymm, 14), + DEFINE_YMM(ymm, 15), + + // MPX registers + DEFINE_BNDR(bnd, 0), + DEFINE_BNDR(bnd, 1), + DEFINE_BNDR(bnd, 2), + DEFINE_BNDR(bnd, 3), + + DEFINE_BNDC(bndcfgu, 0), + DEFINE_BNDC(bndstatus, 1), + + // Debug registers for lldb internal use + DEFINE_DR(dr, 0), DEFINE_DR(dr, 1), DEFINE_DR(dr, 2), DEFINE_DR(dr, 3), + DEFINE_DR(dr, 4), DEFINE_DR(dr, 5), DEFINE_DR(dr, 6), DEFINE_DR(dr, 7)}; + +// clang-format on + +static_assert( + (sizeof(g_register_infos_x86_64_with_base) / + sizeof(g_register_infos_x86_64_with_base[0])) == + x86_64_with_base::k_num_registers, + "g_register_infos_x86_64_with_base has wrong number of register infos"); + +#undef FPR_SIZE +#undef FP_SIZE +#undef XMM_SIZE +#undef YMM_SIZE +#undef DEFINE_GPR +#undef DEFINE_FPR +#undef DEFINE_FP +#undef DEFINE_XMM +#undef DEFINE_YMM +#undef DEFINE_BNDR +#undef DEFINE_BNDC +#undef DEFINE_DR +#undef DEFINE_GPR_PSEUDO_32 +#undef DEFINE_GPR_PSEUDO_16 +#undef DEFINE_GPR_PSEUDO_8H +#undef DEFINE_GPR_PSEUDO_8L + +#endif // DECLARE_REGISTER_INFOS_X86_64_STRUCT + +#ifdef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS + +#define UPDATE_GPR_INFO(reg, reg64) \ + do { \ + g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64); \ + } while (false); + +#define UPDATE_GPR_INFO_8H(reg, reg64) \ + do { \ + g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64) + 1; \ + } while (false); + +#define UPDATE_FPR_INFO(reg, reg64) \ + do { \ + g_register_infos[lldb_##reg##_i386].byte_offset = FPR_OFFSET(reg64); \ + } while (false); + +#define UPDATE_FP_INFO(reg, i) \ + do { \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(stmm[i]); \ + } while (false); + +#define UPDATE_XMM_INFO(reg, i) \ + do { \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(reg[i]); \ + } while (false); + +#define UPDATE_YMM_INFO(reg, i) \ + do { \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = YMM_OFFSET(i); \ + } while (false); + +#define UPDATE_DR_INFO(reg_index) \ + do { \ + g_register_infos[lldb_dr##reg_index##_i386].byte_offset = \ + DR_OFFSET(reg_index); \ + } while (false); + +// Update the register offsets +UPDATE_GPR_INFO(eax, rax); +UPDATE_GPR_INFO(ebx, rbx); +UPDATE_GPR_INFO(ecx, rcx); +UPDATE_GPR_INFO(edx, rdx); +UPDATE_GPR_INFO(edi, rdi); +UPDATE_GPR_INFO(esi, rsi); +UPDATE_GPR_INFO(ebp, rbp); +UPDATE_GPR_INFO(esp, rsp); +UPDATE_GPR_INFO(eip, rip); +UPDATE_GPR_INFO(eflags, rflags); +UPDATE_GPR_INFO(cs, cs); +UPDATE_GPR_INFO(fs, fs); +UPDATE_GPR_INFO(gs, gs); +UPDATE_GPR_INFO(ss, ss); +UPDATE_GPR_INFO(ds, ds); +UPDATE_GPR_INFO(es, es); + +UPDATE_GPR_INFO(ax, rax); +UPDATE_GPR_INFO(bx, rbx); +UPDATE_GPR_INFO(cx, rcx); +UPDATE_GPR_INFO(dx, rdx); +UPDATE_GPR_INFO(di, rdi); +UPDATE_GPR_INFO(si, rsi); +UPDATE_GPR_INFO(bp, rbp); +UPDATE_GPR_INFO(sp, rsp); +UPDATE_GPR_INFO_8H(ah, rax); +UPDATE_GPR_INFO_8H(bh, rbx); +UPDATE_GPR_INFO_8H(ch, rcx); +UPDATE_GPR_INFO_8H(dh, rdx); +UPDATE_GPR_INFO(al, rax); +UPDATE_GPR_INFO(bl, rbx); +UPDATE_GPR_INFO(cl, rcx); +UPDATE_GPR_INFO(dl, rdx); + +UPDATE_FPR_INFO(fctrl, fctrl); +UPDATE_FPR_INFO(fstat, fstat); +UPDATE_FPR_INFO(ftag, ftag); +UPDATE_FPR_INFO(fop, fop); +UPDATE_FPR_INFO(fiseg, ptr.i386_.fiseg); +UPDATE_FPR_INFO(fioff, ptr.i386_.fioff); +UPDATE_FPR_INFO(fooff, ptr.i386_.fooff); +UPDATE_FPR_INFO(foseg, ptr.i386_.foseg); +UPDATE_FPR_INFO(mxcsr, mxcsr); +UPDATE_FPR_INFO(mxcsrmask, mxcsrmask); + +UPDATE_FP_INFO(st, 0); +UPDATE_FP_INFO(st, 1); +UPDATE_FP_INFO(st, 2); +UPDATE_FP_INFO(st, 3); +UPDATE_FP_INFO(st, 4); +UPDATE_FP_INFO(st, 5); +UPDATE_FP_INFO(st, 6); +UPDATE_FP_INFO(st, 7); +UPDATE_FP_INFO(mm, 0); +UPDATE_FP_INFO(mm, 1); +UPDATE_FP_INFO(mm, 2); +UPDATE_FP_INFO(mm, 3); +UPDATE_FP_INFO(mm, 4); +UPDATE_FP_INFO(mm, 5); +UPDATE_FP_INFO(mm, 6); +UPDATE_FP_INFO(mm, 7); + +UPDATE_XMM_INFO(xmm, 0); +UPDATE_XMM_INFO(xmm, 1); +UPDATE_XMM_INFO(xmm, 2); +UPDATE_XMM_INFO(xmm, 3); +UPDATE_XMM_INFO(xmm, 4); +UPDATE_XMM_INFO(xmm, 5); +UPDATE_XMM_INFO(xmm, 6); +UPDATE_XMM_INFO(xmm, 7); + +UPDATE_YMM_INFO(ymm, 0); +UPDATE_YMM_INFO(ymm, 1); +UPDATE_YMM_INFO(ymm, 2); +UPDATE_YMM_INFO(ymm, 3); +UPDATE_YMM_INFO(ymm, 4); +UPDATE_YMM_INFO(ymm, 5); +UPDATE_YMM_INFO(ymm, 6); +UPDATE_YMM_INFO(ymm, 7); + +UPDATE_DR_INFO(0); +UPDATE_DR_INFO(1); +UPDATE_DR_INFO(2); +UPDATE_DR_INFO(3); +UPDATE_DR_INFO(4); +UPDATE_DR_INFO(5); +UPDATE_DR_INFO(6); +UPDATE_DR_INFO(7); + +#undef UPDATE_GPR_INFO +#undef UPDATE_GPR_INFO_8H +#undef UPDATE_FPR_INFO +#undef UPDATE_FP_INFO +#undef UPDATE_XMM_INFO +#undef UPDATE_YMM_INFO +#undef UPDATE_DR_INFO + +#endif // UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS + +#undef GPR_OFFSET +#undef FPR_OFFSET +#undef YMM_OFFSET diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64_with_base_shared.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64_with_base_shared.cpp new file mode 100644 index 000000000000..7b2d64de230f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64_with_base_shared.cpp @@ -0,0 +1,321 @@ +//===-- RegisterInfos_x86_64_with_base_shared.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 "RegisterInfos_x86_64_with_base_shared.h" + +#include "lldb/lldb-defines.h" +#include <mutex> + +using namespace lldb; + +namespace lldb_private { + +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_eax[] = { + lldb_eax_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_ebx[] = { + lldb_ebx_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_ecx[] = { + lldb_ecx_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_edx[] = { + lldb_edx_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_edi[] = { + lldb_edi_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_esi[] = { + lldb_esi_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_ebp[] = { + lldb_ebp_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_esp[] = { + lldb_esp_i386, LLDB_INVALID_REGNUM}; + +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_eax[] = { + lldb_eax_i386, lldb_ax_i386, lldb_ah_i386, lldb_al_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_ebx[] = { + lldb_ebx_i386, lldb_bx_i386, lldb_bh_i386, lldb_bl_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_ecx[] = { + lldb_ecx_i386, lldb_cx_i386, lldb_ch_i386, lldb_cl_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_edx[] = { + lldb_edx_i386, lldb_dx_i386, lldb_dh_i386, lldb_dl_i386, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_edi[] = { + lldb_edi_i386, lldb_di_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_esi[] = { + lldb_esi_i386, lldb_si_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_ebp[] = { + lldb_ebp_i386, lldb_bp_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_esp[] = { + lldb_esp_i386, lldb_sp_i386, LLDB_INVALID_REGNUM}; + +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_rax[] = { + x86_64_with_base::lldb_rax, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_rbx[] = { + x86_64_with_base::lldb_rbx, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_rcx[] = { + x86_64_with_base::lldb_rcx, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_rdx[] = { + x86_64_with_base::lldb_rdx, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_rdi[] = { + x86_64_with_base::lldb_rdi, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_rsi[] = { + x86_64_with_base::lldb_rsi, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_rbp[] = { + x86_64_with_base::lldb_rbp, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_rsp[] = { + x86_64_with_base::lldb_rsp, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_r8[] = { + x86_64_with_base::lldb_r8, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_r9[] = { + x86_64_with_base::lldb_r9, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_r10[] = { + x86_64_with_base::lldb_r10, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_r11[] = { + x86_64_with_base::lldb_r11, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_r12[] = { + x86_64_with_base::lldb_r12, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_r13[] = { + x86_64_with_base::lldb_r13, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_r14[] = { + x86_64_with_base::lldb_r14, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_r15[] = { + x86_64_with_base::lldb_r15, LLDB_INVALID_REGNUM}; + +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_rax[] = { + x86_64_with_base::lldb_rax, x86_64_with_base::lldb_eax, + x86_64_with_base::lldb_ax, x86_64_with_base::lldb_ah, + x86_64_with_base::lldb_al, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_rbx[] = { + x86_64_with_base::lldb_rbx, x86_64_with_base::lldb_ebx, + x86_64_with_base::lldb_bx, x86_64_with_base::lldb_bh, + x86_64_with_base::lldb_bl, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_rcx[] = { + x86_64_with_base::lldb_rcx, x86_64_with_base::lldb_ecx, + x86_64_with_base::lldb_cx, x86_64_with_base::lldb_ch, + x86_64_with_base::lldb_cl, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_rdx[] = { + x86_64_with_base::lldb_rdx, x86_64_with_base::lldb_edx, + x86_64_with_base::lldb_dx, x86_64_with_base::lldb_dh, + x86_64_with_base::lldb_dl, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_rdi[] = { + x86_64_with_base::lldb_rdi, x86_64_with_base::lldb_edi, + x86_64_with_base::lldb_di, x86_64_with_base::lldb_dil, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_rsi[] = { + x86_64_with_base::lldb_rsi, x86_64_with_base::lldb_esi, + x86_64_with_base::lldb_si, x86_64_with_base::lldb_sil, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_rbp[] = { + x86_64_with_base::lldb_rbp, x86_64_with_base::lldb_ebp, + x86_64_with_base::lldb_bp, x86_64_with_base::lldb_bpl, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_rsp[] = { + x86_64_with_base::lldb_rsp, x86_64_with_base::lldb_esp, + x86_64_with_base::lldb_sp, x86_64_with_base::lldb_spl, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_r8[] = { + x86_64_with_base::lldb_r8, x86_64_with_base::lldb_r8d, + x86_64_with_base::lldb_r8w, x86_64_with_base::lldb_r8l, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_r9[] = { + x86_64_with_base::lldb_r9, x86_64_with_base::lldb_r9d, + x86_64_with_base::lldb_r9w, x86_64_with_base::lldb_r9l, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_r10[] = { + x86_64_with_base::lldb_r10, x86_64_with_base::lldb_r10d, + x86_64_with_base::lldb_r10w, x86_64_with_base::lldb_r10l, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_r11[] = { + x86_64_with_base::lldb_r11, x86_64_with_base::lldb_r11d, + x86_64_with_base::lldb_r11w, x86_64_with_base::lldb_r11l, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_r12[] = { + x86_64_with_base::lldb_r12, x86_64_with_base::lldb_r12d, + x86_64_with_base::lldb_r12w, x86_64_with_base::lldb_r12l, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_r13[] = { + x86_64_with_base::lldb_r13, x86_64_with_base::lldb_r13d, + x86_64_with_base::lldb_r13w, x86_64_with_base::lldb_r13l, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_r14[] = { + x86_64_with_base::lldb_r14, x86_64_with_base::lldb_r14d, + x86_64_with_base::lldb_r14w, x86_64_with_base::lldb_r14l, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_r15[] = { + x86_64_with_base::lldb_r15, x86_64_with_base::lldb_r15d, + x86_64_with_base::lldb_r15w, x86_64_with_base::lldb_r15l, + LLDB_INVALID_REGNUM}; + +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_fip[] = { + x86_64_with_base::lldb_fip, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_fdp[] = { + x86_64_with_base::lldb_fdp, LLDB_INVALID_REGNUM}; + +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_fip[] = { + x86_64_with_base::lldb_fip, x86_64_with_base::lldb_fioff, + x86_64_with_base::lldb_fiseg, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_fdp[] = { + x86_64_with_base::lldb_fdp, x86_64_with_base::lldb_fooff, + x86_64_with_base::lldb_foseg, LLDB_INVALID_REGNUM}; + +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st0_32[] = { + lldb_st0_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st1_32[] = { + lldb_st1_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st2_32[] = { + lldb_st2_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st3_32[] = { + lldb_st3_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st4_32[] = { + lldb_st4_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st5_32[] = { + lldb_st5_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st6_32[] = { + lldb_st6_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st7_32[] = { + lldb_st7_i386, LLDB_INVALID_REGNUM}; + +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st0_32[] = { + lldb_st0_i386, lldb_mm0_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st1_32[] = { + lldb_st1_i386, lldb_mm1_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st2_32[] = { + lldb_st2_i386, lldb_mm2_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st3_32[] = { + lldb_st3_i386, lldb_mm3_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st4_32[] = { + lldb_st4_i386, lldb_mm4_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st5_32[] = { + lldb_st5_i386, lldb_mm5_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st6_32[] = { + lldb_st6_i386, lldb_mm6_i386, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st7_32[] = { + lldb_st7_i386, lldb_mm7_i386, LLDB_INVALID_REGNUM}; + +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st0_64[] = { + x86_64_with_base::lldb_st0, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st1_64[] = { + x86_64_with_base::lldb_st1, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st2_64[] = { + x86_64_with_base::lldb_st2, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st3_64[] = { + x86_64_with_base::lldb_st3, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st4_64[] = { + x86_64_with_base::lldb_st4, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st5_64[] = { + x86_64_with_base::lldb_st5, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st6_64[] = { + x86_64_with_base::lldb_st6, LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_contained_st7_64[] = { + x86_64_with_base::lldb_st7, LLDB_INVALID_REGNUM}; + +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st0_64[] = { + x86_64_with_base::lldb_st0, x86_64_with_base::lldb_mm0, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st1_64[] = { + x86_64_with_base::lldb_st1, x86_64_with_base::lldb_mm1, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st2_64[] = { + x86_64_with_base::lldb_st2, x86_64_with_base::lldb_mm2, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st3_64[] = { + x86_64_with_base::lldb_st3, x86_64_with_base::lldb_mm3, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st4_64[] = { + x86_64_with_base::lldb_st4, x86_64_with_base::lldb_mm4, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st5_64[] = { + x86_64_with_base::lldb_st5, x86_64_with_base::lldb_mm5, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st6_64[] = { + x86_64_with_base::lldb_st6, x86_64_with_base::lldb_mm6, + LLDB_INVALID_REGNUM}; +uint32_t RegisterInfos_x86_64_with_base_shared::g_invalidate_st7_64[] = { + x86_64_with_base::lldb_st7, x86_64_with_base::lldb_mm7, + LLDB_INVALID_REGNUM}; + +RegInfo &GetRegInfoShared(llvm::Triple::ArchType arch_type, bool with_base) { + static std::once_flag once_flag_x86, once_flag_x86_64, + once_flag_x86_64_with_base; + static RegInfo reg_info_x86, reg_info_x86_64, reg_info_x86_64_with_base, reg_info_invalid; + + switch (arch_type) { + case llvm::Triple::x86: + std::call_once(once_flag_x86, []() { + reg_info_x86.num_registers = k_num_registers_i386; + reg_info_x86.num_gpr_registers = k_num_gpr_registers_i386; + reg_info_x86.num_fpr_registers = k_num_fpr_registers_i386; + reg_info_x86.num_avx_registers = k_num_avx_registers_i386; + reg_info_x86.last_gpr = k_last_gpr_i386; + reg_info_x86.first_fpr = k_first_fpr_i386; + reg_info_x86.last_fpr = k_last_fpr_i386; + reg_info_x86.first_st = lldb_st0_i386; + reg_info_x86.last_st = lldb_st7_i386; + reg_info_x86.first_mm = lldb_mm0_i386; + reg_info_x86.last_mm = lldb_mm7_i386; + reg_info_x86.first_xmm = lldb_xmm0_i386; + reg_info_x86.last_xmm = lldb_xmm7_i386; + reg_info_x86.first_ymm = lldb_ymm0_i386; + reg_info_x86.last_ymm = lldb_ymm7_i386; + reg_info_x86.first_dr = lldb_dr0_i386; + reg_info_x86.gpr_flags = lldb_eflags_i386; + }); + + return reg_info_x86; + case llvm::Triple::x86_64: + if (with_base) { + std::call_once(once_flag_x86_64_with_base, []() { + reg_info_x86_64_with_base.num_registers = + x86_64_with_base::k_num_registers; + reg_info_x86_64_with_base.num_gpr_registers = + x86_64_with_base::k_num_gpr_registers; + reg_info_x86_64_with_base.num_fpr_registers = + x86_64_with_base::k_num_fpr_registers; + reg_info_x86_64_with_base.num_avx_registers = + x86_64_with_base::k_num_avx_registers; + reg_info_x86_64_with_base.last_gpr = x86_64_with_base::k_last_gpr; + reg_info_x86_64_with_base.first_fpr = x86_64_with_base::k_first_fpr; + reg_info_x86_64_with_base.last_fpr = x86_64_with_base::k_last_fpr; + reg_info_x86_64_with_base.first_st = x86_64_with_base::lldb_st0; + reg_info_x86_64_with_base.last_st = x86_64_with_base::lldb_st7; + reg_info_x86_64_with_base.first_mm = x86_64_with_base::lldb_mm0; + reg_info_x86_64_with_base.last_mm = x86_64_with_base::lldb_mm7; + reg_info_x86_64_with_base.first_xmm = x86_64_with_base::lldb_xmm0; + reg_info_x86_64_with_base.last_xmm = x86_64_with_base::lldb_xmm15; + reg_info_x86_64_with_base.first_ymm = x86_64_with_base::lldb_ymm0; + reg_info_x86_64_with_base.last_ymm = x86_64_with_base::lldb_ymm15; + reg_info_x86_64_with_base.first_dr = x86_64_with_base::lldb_dr0; + reg_info_x86_64_with_base.gpr_flags = x86_64_with_base::lldb_rflags; + }); + + return reg_info_x86_64_with_base; + } else { + std::call_once(once_flag_x86_64, []() { + reg_info_x86_64.num_registers = k_num_registers_x86_64; + reg_info_x86_64.num_gpr_registers = k_num_gpr_registers_x86_64; + reg_info_x86_64.num_fpr_registers = k_num_fpr_registers_x86_64; + reg_info_x86_64.num_avx_registers = k_num_avx_registers_x86_64; + reg_info_x86_64.last_gpr = k_last_gpr_x86_64; + reg_info_x86_64.first_fpr = k_first_fpr_x86_64; + reg_info_x86_64.last_fpr = k_last_fpr_x86_64; + reg_info_x86_64.first_st = lldb_st0_x86_64; + reg_info_x86_64.last_st = lldb_st7_x86_64; + reg_info_x86_64.first_mm = lldb_mm0_x86_64; + reg_info_x86_64.last_mm = lldb_mm7_x86_64; + reg_info_x86_64.first_xmm = lldb_xmm0_x86_64; + reg_info_x86_64.last_xmm = lldb_xmm15_x86_64; + reg_info_x86_64.first_ymm = lldb_ymm0_x86_64; + reg_info_x86_64.last_ymm = lldb_ymm15_x86_64; + reg_info_x86_64.first_dr = lldb_dr0_x86_64; + reg_info_x86_64.gpr_flags = lldb_rflags_x86_64; + }); + return reg_info_x86_64; + } + default: + assert(false && "Unhandled target architecture."); + return reg_info_invalid; + } +} + +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64_with_base_shared.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64_with_base_shared.h new file mode 100644 index 000000000000..5e4406c1fa27 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/RegisterInfos_x86_64_with_base_shared.h @@ -0,0 +1,142 @@ +//===-- RegisterInfos_x86_64_with_base_shared.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 +// +//===----------------------------------------------------------------------===// + +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" +#include <stdint.h> + +#ifndef lldb_RegisterInfos_x86_64_with_base_shared_h +#define lldb_RegisterInfos_x86_64_with_base_shared_h + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" + +namespace lldb_private { + +struct RegisterInfos_x86_64_with_base_shared { + static uint32_t g_contained_eax[]; + static uint32_t g_contained_ebx[]; + static uint32_t g_contained_ecx[]; + static uint32_t g_contained_edx[]; + static uint32_t g_contained_edi[]; + static uint32_t g_contained_esi[]; + static uint32_t g_contained_ebp[]; + static uint32_t g_contained_esp[]; + + static uint32_t g_invalidate_eax[]; + static uint32_t g_invalidate_ebx[]; + static uint32_t g_invalidate_ecx[]; + static uint32_t g_invalidate_edx[]; + static uint32_t g_invalidate_edi[]; + static uint32_t g_invalidate_esi[]; + static uint32_t g_invalidate_ebp[]; + static uint32_t g_invalidate_esp[]; + + static uint32_t g_contained_rax[]; + static uint32_t g_contained_rbx[]; + static uint32_t g_contained_rcx[]; + static uint32_t g_contained_rdx[]; + static uint32_t g_contained_rdi[]; + static uint32_t g_contained_rsi[]; + static uint32_t g_contained_rbp[]; + static uint32_t g_contained_rsp[]; + static uint32_t g_contained_r8[]; + static uint32_t g_contained_r9[]; + static uint32_t g_contained_r10[]; + static uint32_t g_contained_r11[]; + static uint32_t g_contained_r12[]; + static uint32_t g_contained_r13[]; + static uint32_t g_contained_r14[]; + static uint32_t g_contained_r15[]; + + static uint32_t g_invalidate_rax[]; + static uint32_t g_invalidate_rbx[]; + static uint32_t g_invalidate_rcx[]; + static uint32_t g_invalidate_rdx[]; + static uint32_t g_invalidate_rdi[]; + static uint32_t g_invalidate_rsi[]; + static uint32_t g_invalidate_rbp[]; + static uint32_t g_invalidate_rsp[]; + static uint32_t g_invalidate_r8[]; + static uint32_t g_invalidate_r9[]; + static uint32_t g_invalidate_r10[]; + static uint32_t g_invalidate_r11[]; + static uint32_t g_invalidate_r12[]; + static uint32_t g_invalidate_r13[]; + static uint32_t g_invalidate_r14[]; + static uint32_t g_invalidate_r15[]; + + static uint32_t g_contained_fip[]; + static uint32_t g_contained_fdp[]; + + static uint32_t g_invalidate_fip[]; + static uint32_t g_invalidate_fdp[]; + + static uint32_t g_contained_st0_32[]; + static uint32_t g_contained_st1_32[]; + static uint32_t g_contained_st2_32[]; + static uint32_t g_contained_st3_32[]; + static uint32_t g_contained_st4_32[]; + static uint32_t g_contained_st5_32[]; + static uint32_t g_contained_st6_32[]; + static uint32_t g_contained_st7_32[]; + + static uint32_t g_invalidate_st0_32[]; + static uint32_t g_invalidate_st1_32[]; + static uint32_t g_invalidate_st2_32[]; + static uint32_t g_invalidate_st3_32[]; + static uint32_t g_invalidate_st4_32[]; + static uint32_t g_invalidate_st5_32[]; + static uint32_t g_invalidate_st6_32[]; + static uint32_t g_invalidate_st7_32[]; + + static uint32_t g_contained_st0_64[]; + static uint32_t g_contained_st1_64[]; + static uint32_t g_contained_st2_64[]; + static uint32_t g_contained_st3_64[]; + static uint32_t g_contained_st4_64[]; + static uint32_t g_contained_st5_64[]; + static uint32_t g_contained_st6_64[]; + static uint32_t g_contained_st7_64[]; + + static uint32_t g_invalidate_st0_64[]; + static uint32_t g_invalidate_st1_64[]; + static uint32_t g_invalidate_st2_64[]; + static uint32_t g_invalidate_st3_64[]; + static uint32_t g_invalidate_st4_64[]; + static uint32_t g_invalidate_st5_64[]; + static uint32_t g_invalidate_st6_64[]; + static uint32_t g_invalidate_st7_64[]; +}; + +struct RegInfo { + uint32_t num_registers; + uint32_t num_gpr_registers; + uint32_t num_fpr_registers; + uint32_t num_avx_registers; + + uint32_t last_gpr; + uint32_t first_fpr; + uint32_t last_fpr; + + uint32_t first_st; + uint32_t last_st; + uint32_t first_mm; + uint32_t last_mm; + uint32_t first_xmm; + uint32_t last_xmm; + uint32_t first_ymm; + uint32_t last_ymm; + + uint32_t first_dr; + uint32_t gpr_flags; +}; + +RegInfo &GetRegInfoShared(llvm::Triple::ArchType arch_type, bool with_base); + +} // namespace lldb_private + +#endif // ifndef lldb_RegisterInfos_x86_64_with_base_shared_h diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp new file mode 100644 index 000000000000..25cee369d7ee --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp @@ -0,0 +1,862 @@ +//===-- StopInfoMachException.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 "StopInfoMachException.h" + +#include "lldb/lldb-forward.h" + +#if defined(__APPLE__) +// Needed for the EXC_RESOURCE interpretation macros +#include <kern/exc_resource.h> +#endif + +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +/// Information about a pointer-authentication related instruction. +struct PtrauthInstructionInfo { + bool IsAuthenticated; + bool IsLoad; + bool DoesBranch; +}; + +/// Get any pointer-authentication related information about the instruction +/// at address \p at_addr. +static std::optional<PtrauthInstructionInfo> +GetPtrauthInstructionInfo(Target &target, const ArchSpec &arch, + const Address &at_addr) { + const char *plugin_name = nullptr; + const char *flavor = nullptr; + AddressRange range_bounds(at_addr, 4); + const bool prefer_file_cache = true; + DisassemblerSP disassembler_sp = Disassembler::DisassembleRange( + arch, plugin_name, flavor, target, range_bounds, prefer_file_cache); + if (!disassembler_sp) + return std::nullopt; + + InstructionList &insn_list = disassembler_sp->GetInstructionList(); + InstructionSP insn = insn_list.GetInstructionAtIndex(0); + if (!insn) + return std::nullopt; + + return PtrauthInstructionInfo{insn->IsAuthenticated(), insn->IsLoad(), + insn->DoesBranch()}; +} + +/// Describe the load address of \p addr using the format filename:line:col. +static void DescribeAddressBriefly(Stream &strm, const Address &addr, + Target &target) { + strm.Printf("at address=0x%" PRIx64, addr.GetLoadAddress(&target)); + StreamString s; + if (addr.GetDescription(s, target, eDescriptionLevelBrief)) + strm.Printf(" %s", s.GetString().data()); + strm.Printf(".\n"); +} + +bool StopInfoMachException::DeterminePtrauthFailure(ExecutionContext &exe_ctx) { + bool IsBreakpoint = m_value == 6; // EXC_BREAKPOINT + bool IsBadAccess = m_value == 1; // EXC_BAD_ACCESS + if (!IsBreakpoint && !IsBadAccess) + return false; + + // Check that we have a live process. + if (!exe_ctx.HasProcessScope() || !exe_ctx.HasThreadScope() || + !exe_ctx.HasTargetScope()) + return false; + + Thread &thread = *exe_ctx.GetThreadPtr(); + StackFrameSP current_frame = thread.GetStackFrameAtIndex(0); + if (!current_frame) + return false; + + Target &target = *exe_ctx.GetTargetPtr(); + Process &process = *exe_ctx.GetProcessPtr(); + const ArchSpec &arch = target.GetArchitecture(); + + // Check for a ptrauth-enabled target. + const bool ptrauth_enabled_target = + arch.GetCore() == ArchSpec::eCore_arm_arm64e; + if (!ptrauth_enabled_target) + return false; + + // Set up a stream we can write a diagnostic into. + StreamString strm; + auto emit_ptrauth_prologue = [&](uint64_t at_address) { + strm.Printf("EXC_BAD_ACCESS (code=%" PRIu64 ", address=0x%" PRIx64 ")\n", + m_exc_code, at_address); + strm.Printf("Note: Possible pointer authentication failure detected.\n"); + }; + + ABISP abi_sp = process.GetABI(); + assert(abi_sp && "Missing ABI info"); + + // Check if we have a "brk 0xc47x" trap, where the value that failed to + // authenticate is in x16. + Address current_address = current_frame->GetFrameCodeAddress(); + if (IsBreakpoint) { + RegisterContext *reg_ctx = exe_ctx.GetRegisterContext(); + if (!reg_ctx) + return false; + + const RegisterInfo *X16Info = reg_ctx->GetRegisterInfoByName("x16"); + RegisterValue X16Val; + if (!reg_ctx->ReadRegister(X16Info, X16Val)) + return false; + uint64_t bad_address = X16Val.GetAsUInt64(); + + uint64_t fixed_bad_address = abi_sp->FixCodeAddress(bad_address); + Address brk_address; + if (!target.ResolveLoadAddress(fixed_bad_address, brk_address)) + return false; + + auto brk_ptrauth_info = + GetPtrauthInstructionInfo(target, arch, current_address); + if (brk_ptrauth_info && brk_ptrauth_info->IsAuthenticated) { + emit_ptrauth_prologue(bad_address); + strm.Printf("Found value that failed to authenticate "); + DescribeAddressBriefly(strm, brk_address, target); + m_description = std::string(strm.GetString()); + return true; + } + return false; + } + + assert(IsBadAccess && "Handle EXC_BAD_ACCESS only after this point"); + + // Check that we have the "bad address" from an EXC_BAD_ACCESS. + if (m_exc_data_count < 2) + return false; + + // Ok, we know the Target is valid and that it describes a ptrauth-enabled + // device. Now, we need to determine whether this exception was caused by a + // ptrauth failure. + + uint64_t bad_address = m_exc_subcode; + uint64_t fixed_bad_address = abi_sp->FixCodeAddress(bad_address); + uint64_t current_pc = current_address.GetLoadAddress(&target); + + // Detect: LDRAA, LDRAB (Load Register, with pointer authentication). + // + // If an authenticated load results in an exception, the instruction at the + // current PC should be one of LDRAx. + if (bad_address != current_pc && fixed_bad_address != current_pc) { + auto ptrauth_info = + GetPtrauthInstructionInfo(target, arch, current_address); + if (ptrauth_info && ptrauth_info->IsAuthenticated && ptrauth_info->IsLoad) { + emit_ptrauth_prologue(bad_address); + strm.Printf("Found authenticated load instruction "); + DescribeAddressBriefly(strm, current_address, target); + m_description = std::string(strm.GetString()); + return true; + } + } + + // Detect: BLRAA, BLRAAZ, BLRAB, BLRABZ (Branch with Link to Register, with + // pointer authentication). + // + // TODO: Detect: BRAA, BRAAZ, BRAB, BRABZ (Branch to Register, with pointer + // authentication). At a minimum, this requires call site info support for + // indirect calls. + // + // If an authenticated call or tail call results in an exception, stripping + // the bad address should give the current PC, which points to the address + // we tried to branch to. + if (bad_address != current_pc && fixed_bad_address == current_pc) { + if (StackFrameSP parent_frame = thread.GetStackFrameAtIndex(1)) { + addr_t return_pc = + parent_frame->GetFrameCodeAddress().GetLoadAddress(&target); + Address blr_address; + if (!target.ResolveLoadAddress(return_pc - 4, blr_address)) + return false; + + auto blr_ptrauth_info = + GetPtrauthInstructionInfo(target, arch, blr_address); + if (blr_ptrauth_info && blr_ptrauth_info->IsAuthenticated && + blr_ptrauth_info->DoesBranch) { + emit_ptrauth_prologue(bad_address); + strm.Printf("Found authenticated indirect branch "); + DescribeAddressBriefly(strm, blr_address, target); + m_description = std::string(strm.GetString()); + return true; + } + } + } + + // TODO: Detect: RETAA, RETAB (Return from subroutine, with pointer + // authentication). + // + // Is there a motivating, non-malicious code snippet that corrupts LR? + + return false; +} + +const char *StopInfoMachException::GetDescription() { + if (!m_description.empty()) + return m_description.c_str(); + if (GetValue() == eStopReasonInvalid) + return "invalid stop reason!"; + + ExecutionContext exe_ctx(m_thread_wp.lock()); + Target *target = exe_ctx.GetTargetPtr(); + const llvm::Triple::ArchType cpu = + target ? target->GetArchitecture().GetMachine() + : llvm::Triple::UnknownArch; + + const char *exc_desc = nullptr; + const char *code_label = "code"; + const char *code_desc = nullptr; + const char *subcode_label = "subcode"; + const char *subcode_desc = nullptr; + +#if defined(__APPLE__) + char code_desc_buf[32]; + char subcode_desc_buf[32]; +#endif + + switch (m_value) { + case 1: // EXC_BAD_ACCESS + exc_desc = "EXC_BAD_ACCESS"; + subcode_label = "address"; + switch (cpu) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + switch (m_exc_code) { + case 0xd: + code_desc = "EXC_I386_GPFLT"; + m_exc_data_count = 1; + break; + } + break; + case llvm::Triple::arm: + case llvm::Triple::thumb: + switch (m_exc_code) { + case 0x101: + code_desc = "EXC_ARM_DA_ALIGN"; + break; + case 0x102: + code_desc = "EXC_ARM_DA_DEBUG"; + break; + } + break; + + case llvm::Triple::aarch64: + if (DeterminePtrauthFailure(exe_ctx)) + return m_description.c_str(); + break; + + default: + break; + } + break; + + case 2: // EXC_BAD_INSTRUCTION + exc_desc = "EXC_BAD_INSTRUCTION"; + switch (cpu) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + if (m_exc_code == 1) + code_desc = "EXC_I386_INVOP"; + break; + + case llvm::Triple::arm: + case llvm::Triple::thumb: + if (m_exc_code == 1) + code_desc = "EXC_ARM_UNDEFINED"; + break; + + default: + break; + } + break; + + case 3: // EXC_ARITHMETIC + exc_desc = "EXC_ARITHMETIC"; + switch (cpu) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + switch (m_exc_code) { + case 1: + code_desc = "EXC_I386_DIV"; + break; + case 2: + code_desc = "EXC_I386_INTO"; + break; + case 3: + code_desc = "EXC_I386_NOEXT"; + break; + case 4: + code_desc = "EXC_I386_EXTOVR"; + break; + case 5: + code_desc = "EXC_I386_EXTERR"; + break; + case 6: + code_desc = "EXC_I386_EMERR"; + break; + case 7: + code_desc = "EXC_I386_BOUND"; + break; + case 8: + code_desc = "EXC_I386_SSEEXTERR"; + break; + } + break; + + default: + break; + } + break; + + case 4: // EXC_EMULATION + exc_desc = "EXC_EMULATION"; + break; + + case 5: // EXC_SOFTWARE + exc_desc = "EXC_SOFTWARE"; + if (m_exc_code == 0x10003) { + subcode_desc = "EXC_SOFT_SIGNAL"; + subcode_label = "signo"; + } + break; + + case 6: // EXC_BREAKPOINT + { + exc_desc = "EXC_BREAKPOINT"; + switch (cpu) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + switch (m_exc_code) { + case 1: + code_desc = "EXC_I386_SGL"; + break; + case 2: + code_desc = "EXC_I386_BPT"; + break; + } + break; + + case llvm::Triple::arm: + case llvm::Triple::thumb: + switch (m_exc_code) { + case 0x101: + code_desc = "EXC_ARM_DA_ALIGN"; + break; + case 0x102: + code_desc = "EXC_ARM_DA_DEBUG"; + break; + case 1: + code_desc = "EXC_ARM_BREAKPOINT"; + break; + // FIXME temporary workaround, exc_code 0 does not really mean + // EXC_ARM_BREAKPOINT + case 0: + code_desc = "EXC_ARM_BREAKPOINT"; + break; + } + break; + + case llvm::Triple::aarch64: + if (DeterminePtrauthFailure(exe_ctx)) + return m_description.c_str(); + break; + + default: + break; + } + } break; + + case 7: + exc_desc = "EXC_SYSCALL"; + break; + + case 8: + exc_desc = "EXC_MACH_SYSCALL"; + break; + + case 9: + exc_desc = "EXC_RPC_ALERT"; + break; + + case 10: + exc_desc = "EXC_CRASH"; + break; + case 11: + exc_desc = "EXC_RESOURCE"; +#if defined(__APPLE__) + { + int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(m_exc_code); + + code_label = "limit"; + code_desc = code_desc_buf; + subcode_label = "observed"; + subcode_desc = subcode_desc_buf; + + switch (resource_type) { + case RESOURCE_TYPE_CPU: + exc_desc = + "EXC_RESOURCE (RESOURCE_TYPE_CPU: CPU usage monitor tripped)"; + snprintf(code_desc_buf, sizeof(code_desc_buf), "%d%%", + (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE(m_exc_code)); + snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d%%", + (int)EXC_RESOURCE_CPUMONITOR_DECODE_PERCENTAGE_OBSERVED( + m_exc_subcode)); + break; + case RESOURCE_TYPE_WAKEUPS: + exc_desc = "EXC_RESOURCE (RESOURCE_TYPE_WAKEUPS: idle wakeups monitor " + "tripped)"; + snprintf( + code_desc_buf, sizeof(code_desc_buf), "%d w/s", + (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_PERMITTED(m_exc_code)); + snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d w/s", + (int)EXC_RESOURCE_CPUMONITOR_DECODE_WAKEUPS_OBSERVED( + m_exc_subcode)); + break; + case RESOURCE_TYPE_MEMORY: + exc_desc = "EXC_RESOURCE (RESOURCE_TYPE_MEMORY: high watermark memory " + "limit exceeded)"; + snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB", + (int)EXC_RESOURCE_HWM_DECODE_LIMIT(m_exc_code)); + subcode_desc = nullptr; + subcode_label = nullptr; + break; +#if defined(RESOURCE_TYPE_IO) + // RESOURCE_TYPE_IO is introduced in macOS SDK 10.12. + case RESOURCE_TYPE_IO: + exc_desc = "EXC_RESOURCE RESOURCE_TYPE_IO"; + snprintf(code_desc_buf, sizeof(code_desc_buf), "%d MB", + (int)EXC_RESOURCE_IO_DECODE_LIMIT(m_exc_code)); + snprintf(subcode_desc_buf, sizeof(subcode_desc_buf), "%d MB", + (int)EXC_RESOURCE_IO_OBSERVED(m_exc_subcode)); + ; + break; +#endif + } + } +#endif + break; + case 12: + exc_desc = "EXC_GUARD"; + break; + } + + StreamString strm; + + if (exc_desc) + strm.PutCString(exc_desc); + else + strm.Printf("EXC_??? (%" PRIu64 ")", m_value); + + if (m_exc_data_count >= 1) { + if (code_desc) + strm.Printf(" (%s=%s", code_label, code_desc); + else + strm.Printf(" (%s=%" PRIu64, code_label, m_exc_code); + } + + if (m_exc_data_count >= 2) { + if (subcode_label && subcode_desc) + strm.Printf(", %s=%s", subcode_label, subcode_desc); + else if (subcode_label) + strm.Printf(", %s=0x%" PRIx64, subcode_label, m_exc_subcode); + } + + if (m_exc_data_count > 0) + strm.PutChar(')'); + + m_description = std::string(strm.GetString()); + return m_description.c_str(); +} + +static StopInfoSP GetStopInfoForHardwareBP(Thread &thread, Target *target, + uint32_t exc_data_count, + uint64_t exc_sub_code, + uint64_t exc_sub_sub_code) { + // Try hardware watchpoint. + if (target) { + // The exc_sub_code indicates the data break address. + WatchpointResourceSP wp_rsrc_sp = + target->GetProcessSP()->GetWatchpointResourceList().FindByAddress( + (addr_t)exc_sub_code); + if (wp_rsrc_sp && wp_rsrc_sp->GetNumberOfConstituents() > 0) { + return StopInfo::CreateStopReasonWithWatchpointID( + thread, wp_rsrc_sp->GetConstituentAtIndex(0)->GetID()); + } + } + + // Try hardware breakpoint. + ProcessSP process_sp(thread.GetProcess()); + if (process_sp) { + // The exc_sub_code indicates the data break address. + lldb::BreakpointSiteSP bp_sp = + process_sp->GetBreakpointSiteList().FindByAddress( + (lldb::addr_t)exc_sub_code); + if (bp_sp && bp_sp->IsEnabled()) { + return StopInfo::CreateStopReasonWithBreakpointSiteID(thread, + bp_sp->GetID()); + } + } + + return nullptr; +} + +#if defined(__APPLE__) +const char * +StopInfoMachException::MachException::Name(exception_type_t exc_type) { + switch (exc_type) { + case EXC_BAD_ACCESS: + return "EXC_BAD_ACCESS"; + case EXC_BAD_INSTRUCTION: + return "EXC_BAD_INSTRUCTION"; + case EXC_ARITHMETIC: + return "EXC_ARITHMETIC"; + case EXC_EMULATION: + return "EXC_EMULATION"; + case EXC_SOFTWARE: + return "EXC_SOFTWARE"; + case EXC_BREAKPOINT: + return "EXC_BREAKPOINT"; + case EXC_SYSCALL: + return "EXC_SYSCALL"; + case EXC_MACH_SYSCALL: + return "EXC_MACH_SYSCALL"; + case EXC_RPC_ALERT: + return "EXC_RPC_ALERT"; +#ifdef EXC_CRASH + case EXC_CRASH: + return "EXC_CRASH"; +#endif + case EXC_RESOURCE: + return "EXC_RESOURCE"; +#ifdef EXC_GUARD + case EXC_GUARD: + return "EXC_GUARD"; +#endif +#ifdef EXC_CORPSE_NOTIFY + case EXC_CORPSE_NOTIFY: + return "EXC_CORPSE_NOTIFY"; +#endif +#ifdef EXC_CORPSE_VARIANT_BIT + case EXC_CORPSE_VARIANT_BIT: + return "EXC_CORPSE_VARIANT_BIT"; +#endif + default: + break; + } + return NULL; +} + +std::optional<exception_type_t> +StopInfoMachException::MachException::ExceptionCode(const char *name) { + return llvm::StringSwitch<std::optional<exception_type_t>>(name) + .Case("EXC_BAD_ACCESS", EXC_BAD_ACCESS) + .Case("EXC_BAD_INSTRUCTION", EXC_BAD_INSTRUCTION) + .Case("EXC_ARITHMETIC", EXC_ARITHMETIC) + .Case("EXC_EMULATION", EXC_EMULATION) + .Case("EXC_SOFTWARE", EXC_SOFTWARE) + .Case("EXC_BREAKPOINT", EXC_BREAKPOINT) + .Case("EXC_SYSCALL", EXC_SYSCALL) + .Case("EXC_MACH_SYSCALL", EXC_MACH_SYSCALL) + .Case("EXC_RPC_ALERT", EXC_RPC_ALERT) +#ifdef EXC_CRASH + .Case("EXC_CRASH", EXC_CRASH) +#endif + .Case("EXC_RESOURCE", EXC_RESOURCE) +#ifdef EXC_GUARD + .Case("EXC_GUARD", EXC_GUARD) +#endif +#ifdef EXC_CORPSE_NOTIFY + .Case("EXC_CORPSE_NOTIFY", EXC_CORPSE_NOTIFY) +#endif + .Default(std::nullopt); +} +#endif + +StopInfoSP StopInfoMachException::CreateStopReasonWithMachException( + Thread &thread, uint32_t exc_type, uint32_t exc_data_count, + uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code, + bool pc_already_adjusted, bool adjust_pc_if_needed) { + if (exc_type == 0) + return StopInfoSP(); + + bool not_stepping_but_got_singlestep_exception = false; + uint32_t pc_decrement = 0; + ExecutionContext exe_ctx(thread.shared_from_this()); + Target *target = exe_ctx.GetTargetPtr(); + const llvm::Triple::ArchType cpu = + target ? target->GetArchitecture().GetMachine() + : llvm::Triple::UnknownArch; + + switch (exc_type) { + case 1: // EXC_BAD_ACCESS + case 2: // EXC_BAD_INSTRUCTION + case 3: // EXC_ARITHMETIC + case 4: // EXC_EMULATION + break; + + case 5: // EXC_SOFTWARE + if (exc_code == 0x10003) // EXC_SOFT_SIGNAL + { + if (exc_sub_code == 5) { + // On MacOSX, a SIGTRAP can signify that a process has called exec, + // so we should check with our dynamic loader to verify. + ProcessSP process_sp(thread.GetProcess()); + if (process_sp) { + DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader(); + if (dynamic_loader && dynamic_loader->ProcessDidExec()) { + // The program was re-exec'ed + return StopInfo::CreateStopReasonWithExec(thread); + } + } + } + return StopInfo::CreateStopReasonWithSignal(thread, exc_sub_code); + } + break; + + case 6: // EXC_BREAKPOINT + { + bool is_actual_breakpoint = false; + bool is_trace_if_actual_breakpoint_missing = false; + switch (cpu) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + if (exc_code == 1) // EXC_I386_SGL + { + if (!exc_sub_code) { + // This looks like a plain trap. + // Have to check if there is a breakpoint here as well. When you + // single-step onto a trap, the single step stops you not to trap. + // Since we also do that check below, let's just use that logic. + is_actual_breakpoint = true; + is_trace_if_actual_breakpoint_missing = true; + } else { + if (StopInfoSP stop_info = + GetStopInfoForHardwareBP(thread, target, exc_data_count, + exc_sub_code, exc_sub_sub_code)) + return stop_info; + } + } else if (exc_code == 2 || // EXC_I386_BPT + exc_code == 3) // EXC_I386_BPTFLT + { + // KDP returns EXC_I386_BPTFLT for trace breakpoints + if (exc_code == 3) + is_trace_if_actual_breakpoint_missing = true; + + is_actual_breakpoint = true; + if (!pc_already_adjusted) + pc_decrement = 1; + } + break; + + case llvm::Triple::arm: + case llvm::Triple::thumb: + if (exc_code == 0x102) // EXC_ARM_DA_DEBUG + { + // LWP_TODO: We need to find the WatchpointResource that matches + // the address, and evaluate its Watchpoints. + + // It's a watchpoint, then, if the exc_sub_code indicates a + // known/enabled data break address from our watchpoint list. + lldb::WatchpointSP wp_sp; + if (target) + wp_sp = target->GetWatchpointList().FindByAddress( + (lldb::addr_t)exc_sub_code); + if (wp_sp && wp_sp->IsEnabled()) { + return StopInfo::CreateStopReasonWithWatchpointID(thread, + wp_sp->GetID()); + } else { + is_actual_breakpoint = true; + is_trace_if_actual_breakpoint_missing = true; + } + } else if (exc_code == 1) // EXC_ARM_BREAKPOINT + { + is_actual_breakpoint = true; + is_trace_if_actual_breakpoint_missing = true; + } else if (exc_code == 0) // FIXME not EXC_ARM_BREAKPOINT but a kernel + // is currently returning this so accept it + // as indicating a breakpoint until the + // kernel is fixed + { + is_actual_breakpoint = true; + is_trace_if_actual_breakpoint_missing = true; + } + break; + + case llvm::Triple::aarch64_32: + case llvm::Triple::aarch64: { + // xnu describes three things with type EXC_BREAKPOINT: + // + // exc_code 0x102 [EXC_ARM_DA_DEBUG], exc_sub_code addr-of-insn + // Watchpoint access. exc_sub_code is the address of the + // instruction which trigged the watchpoint trap. + // debugserver may add the watchpoint number that was triggered + // in exc_sub_sub_code. + // + // exc_code 1 [EXC_ARM_BREAKPOINT], exc_sub_code 0 + // Instruction step has completed. + // + // exc_code 1 [EXC_ARM_BREAKPOINT], exc_sub_code address-of-instruction + // Software breakpoint instruction executed. + + if (exc_code == 1 && exc_sub_code == 0) // EXC_ARM_BREAKPOINT + { + // This is hit when we single instruction step aka MDSCR_EL1 SS bit 0 + // is set + is_actual_breakpoint = true; + is_trace_if_actual_breakpoint_missing = true; + if (thread.GetTemporaryResumeState() != eStateStepping) + not_stepping_but_got_singlestep_exception = true; + } + if (exc_code == 0x102) // EXC_ARM_DA_DEBUG + { + // LWP_TODO: We need to find the WatchpointResource that matches + // the address, and evaluate its Watchpoints. + + // It's a watchpoint, then, if the exc_sub_code indicates a + // known/enabled data break address from our watchpoint list. + lldb::WatchpointSP wp_sp; + if (target) + wp_sp = target->GetWatchpointList().FindByAddress( + (lldb::addr_t)exc_sub_code); + if (wp_sp && wp_sp->IsEnabled()) { + return StopInfo::CreateStopReasonWithWatchpointID(thread, + wp_sp->GetID()); + } + // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as + // EXC_BAD_ACCESS + if (thread.GetTemporaryResumeState() == eStateStepping) + return StopInfo::CreateStopReasonToTrace(thread); + } + // It looks like exc_sub_code has the 4 bytes of the instruction that + // triggered the exception, i.e. our breakpoint opcode + is_actual_breakpoint = exc_code == 1; + break; + } + + default: + break; + } + + if (is_actual_breakpoint) { + RegisterContextSP reg_ctx_sp(thread.GetRegisterContext()); + addr_t pc = reg_ctx_sp->GetPC() - pc_decrement; + + ProcessSP process_sp(thread.CalculateProcess()); + + lldb::BreakpointSiteSP bp_site_sp; + if (process_sp) + bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp && bp_site_sp->IsEnabled()) { + // Update the PC if we were asked to do so, but only do so if we find + // a breakpoint that we know about cause this could be a trap + // instruction in the code + if (pc_decrement > 0 && adjust_pc_if_needed) + reg_ctx_sp->SetPC(pc); + + // If the breakpoint is for this thread, then we'll report the hit, + // but if it is for another thread, we can just report no reason. We + // don't need to worry about stepping over the breakpoint here, that + // will be taken care of when the thread resumes and notices that + // there's a breakpoint under the pc. If we have an operating system + // plug-in, we might have set a thread specific breakpoint using the + // operating system thread ID, so we can't make any assumptions about + // the thread ID so we must always report the breakpoint regardless + // of the thread. + if (bp_site_sp->ValidForThisThread(thread) || + thread.GetProcess()->GetOperatingSystem() != nullptr) + return StopInfo::CreateStopReasonWithBreakpointSiteID( + thread, bp_site_sp->GetID()); + else if (is_trace_if_actual_breakpoint_missing) + return StopInfo::CreateStopReasonToTrace(thread); + else + return StopInfoSP(); + } + + // Don't call this a trace if we weren't single stepping this thread. + if (is_trace_if_actual_breakpoint_missing && + thread.GetTemporaryResumeState() == eStateStepping) { + return StopInfo::CreateStopReasonToTrace(thread); + } + } + } break; + + case 7: // EXC_SYSCALL + case 8: // EXC_MACH_SYSCALL + case 9: // EXC_RPC_ALERT + case 10: // EXC_CRASH + break; + } + + return std::make_shared<StopInfoMachException>( + thread, exc_type, exc_data_count, exc_code, exc_sub_code, + not_stepping_but_got_singlestep_exception); +} + +// Detect an unusual situation on Darwin where: +// +// 0. We did an instruction-step before this. +// 1. We have a hardware breakpoint or watchpoint set. +// 2. We resumed the process, but not with an instruction-step. +// 3. The thread gets an "instruction-step completed" mach exception. +// 4. The pc has not advanced - it is the same as before. +// +// This method returns true for that combination of events. +bool StopInfoMachException::WasContinueInterrupted(Thread &thread) { + Log *log = GetLog(LLDBLog::Step); + + // We got an instruction-step completed mach exception but we were not + // doing an instruction step on this thread. + if (!m_not_stepping_but_got_singlestep_exception) + return false; + + RegisterContextSP reg_ctx_sp(thread.GetRegisterContext()); + std::optional<addr_t> prev_pc = thread.GetPreviousFrameZeroPC(); + if (!reg_ctx_sp || !prev_pc) + return false; + + // The previous pc value and current pc value are the same. + if (*prev_pc != reg_ctx_sp->GetPC()) + return false; + + // We have a watchpoint -- this is the kernel bug. + ProcessSP process_sp = thread.GetProcess(); + if (process_sp->GetWatchpointResourceList().GetSize()) { + LLDB_LOGF(log, + "Thread stopped with insn-step completed mach exception but " + "thread was not stepping; there is a hardware watchpoint set."); + return true; + } + + // We have a hardware breakpoint -- this is the kernel bug. + auto &bp_site_list = process_sp->GetBreakpointSiteList(); + for (auto &site : bp_site_list.Sites()) { + if (site->IsHardware() && site->IsEnabled()) { + LLDB_LOGF(log, + "Thread stopped with insn-step completed mach exception but " + "thread was not stepping; there is a hardware breakpoint set."); + return true; + } + } + + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h new file mode 100644 index 000000000000..c612ac400b4c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/StopInfoMachException.h @@ -0,0 +1,76 @@ +//===-- StopInfoMachException.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_SOURCE_PLUGINS_PROCESS_UTILITY_STOPINFOMACHEXCEPTION_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_STOPINFOMACHEXCEPTION_H + +#include <optional> +#include <string> + +#include "lldb/Target/StopInfo.h" + +#if defined(__APPLE__) +// Needed for the EXC_* defines +#include <mach/exception.h> +#endif + +namespace lldb_private { + +class StopInfoMachException : public StopInfo { + /// Determine the pointer-authentication related failure that caused this + /// exception. Returns true and fills out the failure description if there + /// is auth-related failure, and returns false otherwise. + bool DeterminePtrauthFailure(ExecutionContext &exe_ctx); + +public: + // Constructors and Destructors + StopInfoMachException(Thread &thread, uint32_t exc_type, + uint32_t exc_data_count, uint64_t exc_code, + uint64_t exc_subcode, + bool not_stepping_but_got_singlestep_exception) + : StopInfo(thread, exc_type), m_exc_data_count(exc_data_count), + m_exc_code(exc_code), m_exc_subcode(exc_subcode), + m_not_stepping_but_got_singlestep_exception( + not_stepping_but_got_singlestep_exception) {} + + ~StopInfoMachException() override = default; + + lldb::StopReason GetStopReason() const override { + return lldb::eStopReasonException; + } + + const char *GetDescription() override; + +#if defined(__APPLE__) + struct MachException { + static const char *Name(exception_type_t exc_type); + static std::optional<exception_type_t> ExceptionCode(const char *name); + }; +#endif + + // Since some mach exceptions will be reported as breakpoints, signals, + // or trace, we use this static accessor which will translate the mach + // exception into the correct StopInfo. + static lldb::StopInfoSP CreateStopReasonWithMachException( + Thread &thread, uint32_t exc_type, uint32_t exc_data_count, + uint64_t exc_code, uint64_t exc_sub_code, uint64_t exc_sub_sub_code, + bool pc_already_adjusted = true, bool adjust_pc_if_needed = false); + + bool WasContinueInterrupted(Thread &thread) override; + +protected: + uint32_t m_exc_data_count; + uint64_t m_exc_code; + uint64_t m_exc_subcode; + + bool m_not_stepping_but_got_singlestep_exception; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_STOPINFOMACHEXCEPTION_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp new file mode 100644 index 000000000000..89ecc757a68f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.cpp @@ -0,0 +1,98 @@ +//===-- ThreadMemory.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 "Plugins/Process/Utility/ThreadMemory.h" + +#include "Plugins/Process/Utility/RegisterContextThreadMemory.h" +#include "lldb/Target/OperatingSystem.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Unwind.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +ThreadMemory::ThreadMemory(Process &process, tid_t tid, + const ValueObjectSP &thread_info_valobj_sp) + : Thread(process, tid), m_backing_thread_sp(), + m_thread_info_valobj_sp(thread_info_valobj_sp), m_name(), m_queue(), + m_register_data_addr(LLDB_INVALID_ADDRESS) {} + +ThreadMemory::ThreadMemory(Process &process, lldb::tid_t tid, + llvm::StringRef name, llvm::StringRef queue, + lldb::addr_t register_data_addr) + : Thread(process, tid), m_backing_thread_sp(), m_thread_info_valobj_sp(), + m_name(std::string(name)), m_queue(std::string(queue)), + m_register_data_addr(register_data_addr) {} + +ThreadMemory::~ThreadMemory() { DestroyThread(); } + +void ThreadMemory::WillResume(StateType resume_state) { + if (m_backing_thread_sp) + m_backing_thread_sp->WillResume(resume_state); +} + +void ThreadMemory::ClearStackFrames() { + if (m_backing_thread_sp) + m_backing_thread_sp->ClearStackFrames(); + Thread::ClearStackFrames(); +} + +RegisterContextSP ThreadMemory::GetRegisterContext() { + if (!m_reg_context_sp) + m_reg_context_sp = std::make_shared<RegisterContextThreadMemory>( + *this, m_register_data_addr); + return m_reg_context_sp; +} + +RegisterContextSP +ThreadMemory::CreateRegisterContextForFrame(StackFrame *frame) { + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) + return GetRegisterContext(); + return GetUnwinder().CreateRegisterContextForFrame(frame); +} + +bool ThreadMemory::CalculateStopInfo() { + if (m_backing_thread_sp) { + lldb::StopInfoSP backing_stop_info_sp( + m_backing_thread_sp->GetPrivateStopInfo()); + if (backing_stop_info_sp && + backing_stop_info_sp->IsValidForOperatingSystemThread(*this)) { + backing_stop_info_sp->SetThread(shared_from_this()); + SetStopInfo(backing_stop_info_sp); + return true; + } + } else { + ProcessSP process_sp(GetProcess()); + + if (process_sp) { + OperatingSystem *os = process_sp->GetOperatingSystem(); + if (os) { + SetStopInfo(os->CreateThreadStopReason(this)); + return true; + } + } + } + return false; +} + +void ThreadMemory::RefreshStateAfterStop() { + if (m_backing_thread_sp) + return m_backing_thread_sp->RefreshStateAfterStop(); + + if (m_reg_context_sp) + m_reg_context_sp->InvalidateAllRegisters(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h new file mode 100644 index 000000000000..d124f5780ea9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/ThreadMemory.h @@ -0,0 +1,107 @@ +//===-- ThreadMemory.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_SOURCE_PLUGINS_PROCESS_UTILITY_THREADMEMORY_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_THREADMEMORY_H + +#include <string> + +#include "lldb/Target/Thread.h" + +class ThreadMemory : public lldb_private::Thread { +public: + ThreadMemory(lldb_private::Process &process, lldb::tid_t tid, + const lldb::ValueObjectSP &thread_info_valobj_sp); + + ThreadMemory(lldb_private::Process &process, lldb::tid_t tid, + llvm::StringRef name, llvm::StringRef queue, + lldb::addr_t register_data_addr); + + ~ThreadMemory() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + bool CalculateStopInfo() override; + + const char *GetInfo() override { + if (m_backing_thread_sp) + m_backing_thread_sp->GetInfo(); + return nullptr; + } + + const char *GetName() override { + if (!m_name.empty()) + return m_name.c_str(); + if (m_backing_thread_sp) + m_backing_thread_sp->GetName(); + return nullptr; + } + + const char *GetQueueName() override { + if (!m_queue.empty()) + return m_queue.c_str(); + if (m_backing_thread_sp) + m_backing_thread_sp->GetQueueName(); + return nullptr; + } + + void WillResume(lldb::StateType resume_state) override; + + void DidResume() override { + if (m_backing_thread_sp) + m_backing_thread_sp->DidResume(); + } + + lldb::user_id_t GetProtocolID() const override { + if (m_backing_thread_sp) + return m_backing_thread_sp->GetProtocolID(); + return Thread::GetProtocolID(); + } + + void RefreshStateAfterStop() override; + + lldb::ValueObjectSP &GetValueObject() { return m_thread_info_valobj_sp; } + + void ClearStackFrames() override; + + void ClearBackingThread() override { m_backing_thread_sp.reset(); } + + bool SetBackingThread(const lldb::ThreadSP &thread_sp) override { + // printf ("Thread 0x%llx is being backed by thread 0x%llx\n", GetID(), + // thread_sp->GetID()); + m_backing_thread_sp = thread_sp; + return (bool)thread_sp; + } + + lldb::ThreadSP GetBackingThread() const override { + return m_backing_thread_sp; + } + +protected: + bool IsOperatingSystemPluginThread() const override { return true; } + + // If this memory thread is actually represented by a thread from the + // lldb_private::Process subclass, then fill in the thread here and + // all APIs will be routed through this thread object. If m_backing_thread_sp + // is empty, then this thread is simply in memory with no representation + // through the process plug-in. + lldb::ThreadSP m_backing_thread_sp; + lldb::ValueObjectSP m_thread_info_valobj_sp; + std::string m_name; + std::string m_queue; + lldb::addr_t m_register_data_addr; + +private: + ThreadMemory(const ThreadMemory &) = delete; + const ThreadMemory &operator=(const ThreadMemory &) = delete; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_THREADMEMORY_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h new file mode 100644 index 000000000000..8f0eed4f02c9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm-register-enums.h @@ -0,0 +1,199 @@ +//===-- lldb-arm-register-enums.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_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_ARM_REGISTER_ENUMS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_ARM_REGISTER_ENUMS_H + +namespace lldb_private { +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +// Internal codes for all ARM registers. +enum { + k_first_gpr_arm = 0, + gpr_r0_arm = k_first_gpr_arm, + gpr_r1_arm, + gpr_r2_arm, + gpr_r3_arm, + gpr_r4_arm, + gpr_r5_arm, + gpr_r6_arm, + gpr_r7_arm, + gpr_r8_arm, + gpr_r9_arm, + gpr_r10_arm, + gpr_r11_arm, + gpr_r12_arm, + gpr_r13_arm, + gpr_sp_arm = gpr_r13_arm, + gpr_r14_arm, + gpr_lr_arm = gpr_r14_arm, + gpr_r15_arm, + gpr_pc_arm = gpr_r15_arm, + gpr_cpsr_arm, + + k_last_gpr_arm = gpr_cpsr_arm, + + k_first_fpr_arm, + fpu_s0_arm = k_first_fpr_arm, + fpu_s1_arm, + fpu_s2_arm, + fpu_s3_arm, + fpu_s4_arm, + fpu_s5_arm, + fpu_s6_arm, + fpu_s7_arm, + fpu_s8_arm, + fpu_s9_arm, + fpu_s10_arm, + fpu_s11_arm, + fpu_s12_arm, + fpu_s13_arm, + fpu_s14_arm, + fpu_s15_arm, + fpu_s16_arm, + fpu_s17_arm, + fpu_s18_arm, + fpu_s19_arm, + fpu_s20_arm, + fpu_s21_arm, + fpu_s22_arm, + fpu_s23_arm, + fpu_s24_arm, + fpu_s25_arm, + fpu_s26_arm, + fpu_s27_arm, + fpu_s28_arm, + fpu_s29_arm, + fpu_s30_arm, + fpu_s31_arm, + fpu_fpscr_arm, + fpu_d0_arm, + fpu_d1_arm, + fpu_d2_arm, + fpu_d3_arm, + fpu_d4_arm, + fpu_d5_arm, + fpu_d6_arm, + fpu_d7_arm, + fpu_d8_arm, + fpu_d9_arm, + fpu_d10_arm, + fpu_d11_arm, + fpu_d12_arm, + fpu_d13_arm, + fpu_d14_arm, + fpu_d15_arm, + fpu_d16_arm, + fpu_d17_arm, + fpu_d18_arm, + fpu_d19_arm, + fpu_d20_arm, + fpu_d21_arm, + fpu_d22_arm, + fpu_d23_arm, + fpu_d24_arm, + fpu_d25_arm, + fpu_d26_arm, + fpu_d27_arm, + fpu_d28_arm, + fpu_d29_arm, + fpu_d30_arm, + fpu_d31_arm, + fpu_q0_arm, + fpu_q1_arm, + fpu_q2_arm, + fpu_q3_arm, + fpu_q4_arm, + fpu_q5_arm, + fpu_q6_arm, + fpu_q7_arm, + fpu_q8_arm, + fpu_q9_arm, + fpu_q10_arm, + fpu_q11_arm, + fpu_q12_arm, + fpu_q13_arm, + fpu_q14_arm, + fpu_q15_arm, + k_last_fpr_arm = fpu_q15_arm, + exc_exception_arm, + exc_fsr_arm, + exc_far_arm, + + dbg_bvr0_arm, + dbg_bvr1_arm, + dbg_bvr2_arm, + dbg_bvr3_arm, + dbg_bvr4_arm, + dbg_bvr5_arm, + dbg_bvr6_arm, + dbg_bvr7_arm, + dbg_bvr8_arm, + dbg_bvr9_arm, + dbg_bvr10_arm, + dbg_bvr11_arm, + dbg_bvr12_arm, + dbg_bvr13_arm, + dbg_bvr14_arm, + dbg_bvr15_arm, + dbg_bcr0_arm, + dbg_bcr1_arm, + dbg_bcr2_arm, + dbg_bcr3_arm, + dbg_bcr4_arm, + dbg_bcr5_arm, + dbg_bcr6_arm, + dbg_bcr7_arm, + dbg_bcr8_arm, + dbg_bcr9_arm, + dbg_bcr10_arm, + dbg_bcr11_arm, + dbg_bcr12_arm, + dbg_bcr13_arm, + dbg_bcr14_arm, + dbg_bcr15_arm, + dbg_wvr0_arm, + dbg_wvr1_arm, + dbg_wvr2_arm, + dbg_wvr3_arm, + dbg_wvr4_arm, + dbg_wvr5_arm, + dbg_wvr6_arm, + dbg_wvr7_arm, + dbg_wvr8_arm, + dbg_wvr9_arm, + dbg_wvr10_arm, + dbg_wvr11_arm, + dbg_wvr12_arm, + dbg_wvr13_arm, + dbg_wvr14_arm, + dbg_wvr15_arm, + dbg_wcr0_arm, + dbg_wcr1_arm, + dbg_wcr2_arm, + dbg_wcr3_arm, + dbg_wcr4_arm, + dbg_wcr5_arm, + dbg_wcr6_arm, + dbg_wcr7_arm, + dbg_wcr8_arm, + dbg_wcr9_arm, + dbg_wcr10_arm, + dbg_wcr11_arm, + dbg_wcr12_arm, + dbg_wcr13_arm, + dbg_wcr14_arm, + dbg_wcr15_arm, + + k_num_registers_arm, + k_num_gpr_registers_arm = k_last_gpr_arm - k_first_gpr_arm + 1, + k_num_fpr_registers_arm = k_last_fpr_arm - k_first_fpr_arm + 1 +}; +} + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_ARM_REGISTER_ENUMS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h new file mode 100644 index 000000000000..39d47b8801cc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-arm64-register-enums.h @@ -0,0 +1,264 @@ +//===-- lldb-arm64-register-enums.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_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_ARM64_REGISTER_ENUMS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_ARM64_REGISTER_ENUMS_H + +namespace lldb_private { +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +// Internal codes for all ARM64 registers. +enum { + k_first_gpr_arm64, + gpr_x0_arm64 = k_first_gpr_arm64, + gpr_x1_arm64, + gpr_x2_arm64, + gpr_x3_arm64, + gpr_x4_arm64, + gpr_x5_arm64, + gpr_x6_arm64, + gpr_x7_arm64, + gpr_x8_arm64, + gpr_x9_arm64, + gpr_x10_arm64, + gpr_x11_arm64, + gpr_x12_arm64, + gpr_x13_arm64, + gpr_x14_arm64, + gpr_x15_arm64, + gpr_x16_arm64, + gpr_x17_arm64, + gpr_x18_arm64, + gpr_x19_arm64, + gpr_x20_arm64, + gpr_x21_arm64, + gpr_x22_arm64, + gpr_x23_arm64, + gpr_x24_arm64, + gpr_x25_arm64, + gpr_x26_arm64, + gpr_x27_arm64, + gpr_x28_arm64, + gpr_fp_arm64, + gpr_lr_arm64, + gpr_sp_arm64, + gpr_pc_arm64, + gpr_cpsr_arm64, + + gpr_w0_arm64, + gpr_w1_arm64, + gpr_w2_arm64, + gpr_w3_arm64, + gpr_w4_arm64, + gpr_w5_arm64, + gpr_w6_arm64, + gpr_w7_arm64, + gpr_w8_arm64, + gpr_w9_arm64, + gpr_w10_arm64, + gpr_w11_arm64, + gpr_w12_arm64, + gpr_w13_arm64, + gpr_w14_arm64, + gpr_w15_arm64, + gpr_w16_arm64, + gpr_w17_arm64, + gpr_w18_arm64, + gpr_w19_arm64, + gpr_w20_arm64, + gpr_w21_arm64, + gpr_w22_arm64, + gpr_w23_arm64, + gpr_w24_arm64, + gpr_w25_arm64, + gpr_w26_arm64, + gpr_w27_arm64, + gpr_w28_arm64, + + k_last_gpr_arm64 = gpr_w28_arm64, + + k_first_fpr_arm64, + fpu_v0_arm64 = k_first_fpr_arm64, + fpu_v1_arm64, + fpu_v2_arm64, + fpu_v3_arm64, + fpu_v4_arm64, + fpu_v5_arm64, + fpu_v6_arm64, + fpu_v7_arm64, + fpu_v8_arm64, + fpu_v9_arm64, + fpu_v10_arm64, + fpu_v11_arm64, + fpu_v12_arm64, + fpu_v13_arm64, + fpu_v14_arm64, + fpu_v15_arm64, + fpu_v16_arm64, + fpu_v17_arm64, + fpu_v18_arm64, + fpu_v19_arm64, + fpu_v20_arm64, + fpu_v21_arm64, + fpu_v22_arm64, + fpu_v23_arm64, + fpu_v24_arm64, + fpu_v25_arm64, + fpu_v26_arm64, + fpu_v27_arm64, + fpu_v28_arm64, + fpu_v29_arm64, + fpu_v30_arm64, + fpu_v31_arm64, + + fpu_s0_arm64, + fpu_s1_arm64, + fpu_s2_arm64, + fpu_s3_arm64, + fpu_s4_arm64, + fpu_s5_arm64, + fpu_s6_arm64, + fpu_s7_arm64, + fpu_s8_arm64, + fpu_s9_arm64, + fpu_s10_arm64, + fpu_s11_arm64, + fpu_s12_arm64, + fpu_s13_arm64, + fpu_s14_arm64, + fpu_s15_arm64, + fpu_s16_arm64, + fpu_s17_arm64, + fpu_s18_arm64, + fpu_s19_arm64, + fpu_s20_arm64, + fpu_s21_arm64, + fpu_s22_arm64, + fpu_s23_arm64, + fpu_s24_arm64, + fpu_s25_arm64, + fpu_s26_arm64, + fpu_s27_arm64, + fpu_s28_arm64, + fpu_s29_arm64, + fpu_s30_arm64, + fpu_s31_arm64, + + fpu_d0_arm64, + fpu_d1_arm64, + fpu_d2_arm64, + fpu_d3_arm64, + fpu_d4_arm64, + fpu_d5_arm64, + fpu_d6_arm64, + fpu_d7_arm64, + fpu_d8_arm64, + fpu_d9_arm64, + fpu_d10_arm64, + fpu_d11_arm64, + fpu_d12_arm64, + fpu_d13_arm64, + fpu_d14_arm64, + fpu_d15_arm64, + fpu_d16_arm64, + fpu_d17_arm64, + fpu_d18_arm64, + fpu_d19_arm64, + fpu_d20_arm64, + fpu_d21_arm64, + fpu_d22_arm64, + fpu_d23_arm64, + fpu_d24_arm64, + fpu_d25_arm64, + fpu_d26_arm64, + fpu_d27_arm64, + fpu_d28_arm64, + fpu_d29_arm64, + fpu_d30_arm64, + fpu_d31_arm64, + + fpu_fpsr_arm64, + fpu_fpcr_arm64, + k_last_fpr_arm64 = fpu_fpcr_arm64, + + exc_far_arm64, + exc_esr_arm64, + exc_exception_arm64, + + dbg_bvr0_arm64, + dbg_bvr1_arm64, + dbg_bvr2_arm64, + dbg_bvr3_arm64, + dbg_bvr4_arm64, + dbg_bvr5_arm64, + dbg_bvr6_arm64, + dbg_bvr7_arm64, + dbg_bvr8_arm64, + dbg_bvr9_arm64, + dbg_bvr10_arm64, + dbg_bvr11_arm64, + dbg_bvr12_arm64, + dbg_bvr13_arm64, + dbg_bvr14_arm64, + dbg_bvr15_arm64, + dbg_bcr0_arm64, + dbg_bcr1_arm64, + dbg_bcr2_arm64, + dbg_bcr3_arm64, + dbg_bcr4_arm64, + dbg_bcr5_arm64, + dbg_bcr6_arm64, + dbg_bcr7_arm64, + dbg_bcr8_arm64, + dbg_bcr9_arm64, + dbg_bcr10_arm64, + dbg_bcr11_arm64, + dbg_bcr12_arm64, + dbg_bcr13_arm64, + dbg_bcr14_arm64, + dbg_bcr15_arm64, + dbg_wvr0_arm64, + dbg_wvr1_arm64, + dbg_wvr2_arm64, + dbg_wvr3_arm64, + dbg_wvr4_arm64, + dbg_wvr5_arm64, + dbg_wvr6_arm64, + dbg_wvr7_arm64, + dbg_wvr8_arm64, + dbg_wvr9_arm64, + dbg_wvr10_arm64, + dbg_wvr11_arm64, + dbg_wvr12_arm64, + dbg_wvr13_arm64, + dbg_wvr14_arm64, + dbg_wvr15_arm64, + dbg_wcr0_arm64, + dbg_wcr1_arm64, + dbg_wcr2_arm64, + dbg_wcr3_arm64, + dbg_wcr4_arm64, + dbg_wcr5_arm64, + dbg_wcr6_arm64, + dbg_wcr7_arm64, + dbg_wcr8_arm64, + dbg_wcr9_arm64, + dbg_wcr10_arm64, + dbg_wcr11_arm64, + dbg_wcr12_arm64, + dbg_wcr13_arm64, + dbg_wcr14_arm64, + dbg_wcr15_arm64, + + k_num_registers_arm64, + k_num_gpr_registers_arm64 = k_last_gpr_arm64 - k_first_gpr_arm64 + 1, + k_num_fpr_registers_arm64 = k_last_fpr_arm64 - k_first_fpr_arm64 + 1 +}; +} + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_ARM64_REGISTER_ENUMS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-loongarch-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-loongarch-register-enums.h new file mode 100644 index 000000000000..f55c807f86c0 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-loongarch-register-enums.h @@ -0,0 +1,178 @@ +//===-- lldb-loongarch-register-enums.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_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_LOONGARCH_REGISTER_ENUMS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_LOONGARCH_REGISTER_ENUMS_H + +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +// Internal codes for all loongarch registers. +enum { + // The same order as user_regs_struct in <asm/ptrace.h> + // note: these enum values are used as byte_offset + gpr_first_loongarch = 0, + gpr_r0_loongarch = gpr_first_loongarch, + gpr_r1_loongarch, + gpr_r2_loongarch, + gpr_r3_loongarch, + gpr_r4_loongarch, + gpr_r5_loongarch, + gpr_r6_loongarch, + gpr_r7_loongarch, + gpr_r8_loongarch, + gpr_r9_loongarch, + gpr_r10_loongarch, + gpr_r11_loongarch, + gpr_r12_loongarch, + gpr_r13_loongarch, + gpr_r14_loongarch, + gpr_r15_loongarch, + gpr_r16_loongarch, + gpr_r17_loongarch, + gpr_r18_loongarch, + gpr_r19_loongarch, + gpr_r20_loongarch, + gpr_r21_loongarch, + gpr_r22_loongarch, + gpr_r23_loongarch, + gpr_r24_loongarch, + gpr_r25_loongarch, + gpr_r26_loongarch, + gpr_r27_loongarch, + gpr_r28_loongarch, + gpr_r29_loongarch, + gpr_r30_loongarch, + gpr_r31_loongarch, + gpr_orig_a0_loongarch, + gpr_pc_loongarch, + gpr_badv_loongarch, + gpr_reserved0_loongarch, + gpr_reserved1_loongarch, + gpr_reserved2_loongarch, + gpr_reserved3_loongarch, + gpr_reserved4_loongarch, + gpr_reserved5_loongarch, + gpr_reserved6_loongarch, + gpr_reserved7_loongarch, + gpr_reserved8_loongarch, + gpr_reserved9_loongarch, + gpr_last_loongarch = 44, + + gpr_zero_loongarch = gpr_r0_loongarch, + gpr_ra_loongarch = gpr_r1_loongarch, + gpr_tp_loongarch = gpr_r2_loongarch, + gpr_sp_loongarch = gpr_r3_loongarch, + gpr_a0_loongarch = gpr_r4_loongarch, + gpr_a1_loongarch = gpr_r5_loongarch, + gpr_a2_loongarch = gpr_r6_loongarch, + gpr_a3_loongarch = gpr_r7_loongarch, + gpr_a4_loongarch = gpr_r8_loongarch, + gpr_a5_loongarch = gpr_r9_loongarch, + gpr_a6_loongarch = gpr_r10_loongarch, + gpr_a7_loongarch = gpr_r11_loongarch, + gpr_t0_loongarch = gpr_r12_loongarch, + gpr_t1_loongarch = gpr_r13_loongarch, + gpr_t2_loongarch = gpr_r14_loongarch, + gpr_t3_loongarch = gpr_r15_loongarch, + gpr_t4_loongarch = gpr_r16_loongarch, + gpr_t5_loongarch = gpr_r17_loongarch, + gpr_t6_loongarch = gpr_r18_loongarch, + gpr_t7_loongarch = gpr_r19_loongarch, + gpr_t8_loongarch = gpr_r20_loongarch, + gpr_fp_loongarch = gpr_r22_loongarch, + gpr_s0_loongarch = gpr_r23_loongarch, + gpr_s1_loongarch = gpr_r24_loongarch, + gpr_s2_loongarch = gpr_r25_loongarch, + gpr_s3_loongarch = gpr_r26_loongarch, + gpr_s4_loongarch = gpr_r27_loongarch, + gpr_s5_loongarch = gpr_r28_loongarch, + gpr_s6_loongarch = gpr_r29_loongarch, + gpr_s7_loongarch = gpr_r30_loongarch, + gpr_s8_loongarch = gpr_r31_loongarch, + + fpr_first_loongarch = 45, + fpr_f0_loongarch = fpr_first_loongarch, + fpr_f1_loongarch, + fpr_f2_loongarch, + fpr_f3_loongarch, + fpr_f4_loongarch, + fpr_f5_loongarch, + fpr_f6_loongarch, + fpr_f7_loongarch, + fpr_f8_loongarch, + fpr_f9_loongarch, + fpr_f10_loongarch, + fpr_f11_loongarch, + fpr_f12_loongarch, + fpr_f13_loongarch, + fpr_f14_loongarch, + fpr_f15_loongarch, + fpr_f16_loongarch, + fpr_f17_loongarch, + fpr_f18_loongarch, + fpr_f19_loongarch, + fpr_f20_loongarch, + fpr_f21_loongarch, + fpr_f22_loongarch, + fpr_f23_loongarch, + fpr_f24_loongarch, + fpr_f25_loongarch, + fpr_f26_loongarch, + fpr_f27_loongarch, + fpr_f28_loongarch, + fpr_f29_loongarch, + fpr_f30_loongarch, + fpr_f31_loongarch, + fpr_fcc0_loongarch, + fpr_fcc1_loongarch, + fpr_fcc2_loongarch, + fpr_fcc3_loongarch, + fpr_fcc4_loongarch, + fpr_fcc5_loongarch, + fpr_fcc6_loongarch, + fpr_fcc7_loongarch, + fpr_fcsr_loongarch, + fpr_last_loongarch = fpr_fcsr_loongarch, + + fpr_fa0_loongarch = fpr_f0_loongarch, + fpr_fa1_loongarch = fpr_f1_loongarch, + fpr_fa2_loongarch = fpr_f2_loongarch, + fpr_fa3_loongarch = fpr_f3_loongarch, + fpr_fa4_loongarch = fpr_f4_loongarch, + fpr_fa5_loongarch = fpr_f5_loongarch, + fpr_fa6_loongarch = fpr_f6_loongarch, + fpr_fa7_loongarch = fpr_f7_loongarch, + fpr_ft0_loongarch = fpr_f8_loongarch, + fpr_ft1_loongarch = fpr_f9_loongarch, + fpr_ft2_loongarch = fpr_f10_loongarch, + fpr_ft3_loongarch = fpr_f11_loongarch, + fpr_ft4_loongarch = fpr_f12_loongarch, + fpr_ft5_loongarch = fpr_f13_loongarch, + fpr_ft6_loongarch = fpr_f14_loongarch, + fpr_ft7_loongarch = fpr_f15_loongarch, + fpr_ft8_loongarch = fpr_f16_loongarch, + fpr_ft9_loongarch = fpr_f17_loongarch, + fpr_ft10_loongarch = fpr_f18_loongarch, + fpr_ft11_loongarch = fpr_f19_loongarch, + fpr_ft12_loongarch = fpr_f20_loongarch, + fpr_ft13_loongarch = fpr_f21_loongarch, + fpr_ft14_loongarch = fpr_f22_loongarch, + fpr_ft15_loongarch = fpr_f23_loongarch, + fpr_fs0_loongarch = fpr_f24_loongarch, + fpr_fs1_loongarch = fpr_f25_loongarch, + fpr_fs2_loongarch = fpr_f26_loongarch, + fpr_fs3_loongarch = fpr_f27_loongarch, + fpr_fs4_loongarch = fpr_f28_loongarch, + fpr_fs5_loongarch = fpr_f29_loongarch, + fpr_fs6_loongarch = fpr_f30_loongarch, + fpr_fs7_loongarch = fpr_f31_loongarch, + + k_num_registers_loongarch +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_LOONGARCH_REGISTER_ENUMS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h new file mode 100644 index 000000000000..000f6e3847e7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h @@ -0,0 +1,103 @@ +//===-- lldb-mips-freebsd-register-enums.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_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_MIPS_FREEBSD_REGISTER_ENUMS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_MIPS_FREEBSD_REGISTER_ENUMS_H + +namespace lldb_private { +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +// Internal codes for all mips registers. +enum { + k_first_gpr_mips64, + gpr_zero_mips64 = k_first_gpr_mips64, + gpr_r1_mips64, + gpr_r2_mips64, + gpr_r3_mips64, + gpr_r4_mips64, + gpr_r5_mips64, + gpr_r6_mips64, + gpr_r7_mips64, + gpr_r8_mips64, + gpr_r9_mips64, + gpr_r10_mips64, + gpr_r11_mips64, + gpr_r12_mips64, + gpr_r13_mips64, + gpr_r14_mips64, + gpr_r15_mips64, + gpr_r16_mips64, + gpr_r17_mips64, + gpr_r18_mips64, + gpr_r19_mips64, + gpr_r20_mips64, + gpr_r21_mips64, + gpr_r22_mips64, + gpr_r23_mips64, + gpr_r24_mips64, + gpr_r25_mips64, + gpr_r26_mips64, + gpr_r27_mips64, + gpr_gp_mips64, + gpr_sp_mips64, + gpr_r30_mips64, + gpr_ra_mips64, + gpr_sr_mips64, + gpr_mullo_mips64, + gpr_mulhi_mips64, + gpr_badvaddr_mips64, + gpr_cause_mips64, + gpr_pc_mips64, + gpr_ic_mips64, + gpr_dummy_mips64, + k_last_gpr_mips64 = gpr_dummy_mips64, + + k_first_fpr_mips64, + fpr_f0_mips64 = k_first_fpr_mips64, + fpr_f1_mips64, + fpr_f2_mips64, + fpr_f3_mips64, + fpr_f4_mips64, + fpr_f5_mips64, + fpr_f6_mips64, + fpr_f7_mips64, + fpr_f8_mips64, + fpr_f9_mips64, + fpr_f10_mips64, + fpr_f11_mips64, + fpr_f12_mips64, + fpr_f13_mips64, + fpr_f14_mips64, + fpr_f15_mips64, + fpr_f16_mips64, + fpr_f17_mips64, + fpr_f18_mips64, + fpr_f19_mips64, + fpr_f20_mips64, + fpr_f21_mips64, + fpr_f22_mips64, + fpr_f23_mips64, + fpr_f24_mips64, + fpr_f25_mips64, + fpr_f26_mips64, + fpr_f27_mips64, + fpr_f28_mips64, + fpr_f29_mips64, + fpr_f30_mips64, + fpr_f31_mips64, + fpr_fcsr_mips64, + fpr_fir_mips64, + k_last_fpr_mips64 = fpr_fir_mips64, + + k_num_registers_mips64, + + k_num_gpr_registers_mips64 = k_last_gpr_mips64 - k_first_gpr_mips64 + 1, + k_num_fpr_registers_mips64 = k_last_fpr_mips64 - k_first_fpr_mips64 + 1, +}; +} // namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_MIPS_FREEBSD_REGISTER_ENUMS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h new file mode 100644 index 000000000000..40a75c006d84 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64-register-enums.h @@ -0,0 +1,136 @@ +//===-- lldb-ppc64-register-enums.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_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_PPC64_REGISTER_ENUMS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_PPC64_REGISTER_ENUMS_H + +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +// Internal codes for all ppc64 registers. +enum { + k_first_gpr_ppc64, + gpr_r0_ppc64 = k_first_gpr_ppc64, + gpr_r1_ppc64, + gpr_r2_ppc64, + gpr_r3_ppc64, + gpr_r4_ppc64, + gpr_r5_ppc64, + gpr_r6_ppc64, + gpr_r7_ppc64, + gpr_r8_ppc64, + gpr_r9_ppc64, + gpr_r10_ppc64, + gpr_r11_ppc64, + gpr_r12_ppc64, + gpr_r13_ppc64, + gpr_r14_ppc64, + gpr_r15_ppc64, + gpr_r16_ppc64, + gpr_r17_ppc64, + gpr_r18_ppc64, + gpr_r19_ppc64, + gpr_r20_ppc64, + gpr_r21_ppc64, + gpr_r22_ppc64, + gpr_r23_ppc64, + gpr_r24_ppc64, + gpr_r25_ppc64, + gpr_r26_ppc64, + gpr_r27_ppc64, + gpr_r28_ppc64, + gpr_r29_ppc64, + gpr_r30_ppc64, + gpr_r31_ppc64, + gpr_cr_ppc64, + gpr_msr_ppc64, + gpr_xer_ppc64, + gpr_lr_ppc64, + gpr_ctr_ppc64, + gpr_pc_ppc64, + k_last_gpr_ppc64 = gpr_pc_ppc64, + + k_first_fpr_ppc64, + fpr_f0_ppc64 = k_first_fpr_ppc64, + fpr_f1_ppc64, + fpr_f2_ppc64, + fpr_f3_ppc64, + fpr_f4_ppc64, + fpr_f5_ppc64, + fpr_f6_ppc64, + fpr_f7_ppc64, + fpr_f8_ppc64, + fpr_f9_ppc64, + fpr_f10_ppc64, + fpr_f11_ppc64, + fpr_f12_ppc64, + fpr_f13_ppc64, + fpr_f14_ppc64, + fpr_f15_ppc64, + fpr_f16_ppc64, + fpr_f17_ppc64, + fpr_f18_ppc64, + fpr_f19_ppc64, + fpr_f20_ppc64, + fpr_f21_ppc64, + fpr_f22_ppc64, + fpr_f23_ppc64, + fpr_f24_ppc64, + fpr_f25_ppc64, + fpr_f26_ppc64, + fpr_f27_ppc64, + fpr_f28_ppc64, + fpr_f29_ppc64, + fpr_f30_ppc64, + fpr_f31_ppc64, + fpr_fpscr_ppc64, + k_last_fpr_ppc64 = fpr_fpscr_ppc64, + + k_first_vmx_ppc64, + vmx_vr0_ppc64 = k_first_vmx_ppc64, + vmx_vr1_ppc64, + vmx_vr2_ppc64, + vmx_vr3_ppc64, + vmx_vr4_ppc64, + vmx_vr5_ppc64, + vmx_vr6_ppc64, + vmx_vr7_ppc64, + vmx_vr8_ppc64, + vmx_vr9_ppc64, + vmx_vr10_ppc64, + vmx_vr11_ppc64, + vmx_vr12_ppc64, + vmx_vr13_ppc64, + vmx_vr14_ppc64, + vmx_vr15_ppc64, + vmx_vr16_ppc64, + vmx_vr17_ppc64, + vmx_vr18_ppc64, + vmx_vr19_ppc64, + vmx_vr20_ppc64, + vmx_vr21_ppc64, + vmx_vr22_ppc64, + vmx_vr23_ppc64, + vmx_vr24_ppc64, + vmx_vr25_ppc64, + vmx_vr26_ppc64, + vmx_vr27_ppc64, + vmx_vr28_ppc64, + vmx_vr29_ppc64, + vmx_vr30_ppc64, + vmx_vr31_ppc64, + vmx_vscr_ppc64, + vmx_vrsave_ppc64, + k_last_vmx_ppc64 = vmx_vrsave_ppc64, + + k_num_registers_ppc64, + k_num_gpr_registers_ppc64 = k_last_gpr_ppc64 - k_first_gpr_ppc64 + 1, + k_num_fpr_registers_ppc64 = k_last_fpr_ppc64 - k_first_fpr_ppc64 + 1, + k_num_vmx_registers_ppc64 = k_last_vmx_ppc64 - k_first_vmx_ppc64 + 1, +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_PPC64_REGISTER_ENUMS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h new file mode 100644 index 000000000000..a7b5bc5ad9e3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-ppc64le-register-enums.h @@ -0,0 +1,207 @@ +//===-- lldb-ppc64le-register-enums.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_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_PPC64LE_REGISTER_ENUMS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_PPC64LE_REGISTER_ENUMS_H + +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +// Internal codes for all ppc64le registers. +enum { + k_first_gpr_ppc64le, + gpr_r0_ppc64le = k_first_gpr_ppc64le, + gpr_r1_ppc64le, + gpr_r2_ppc64le, + gpr_r3_ppc64le, + gpr_r4_ppc64le, + gpr_r5_ppc64le, + gpr_r6_ppc64le, + gpr_r7_ppc64le, + gpr_r8_ppc64le, + gpr_r9_ppc64le, + gpr_r10_ppc64le, + gpr_r11_ppc64le, + gpr_r12_ppc64le, + gpr_r13_ppc64le, + gpr_r14_ppc64le, + gpr_r15_ppc64le, + gpr_r16_ppc64le, + gpr_r17_ppc64le, + gpr_r18_ppc64le, + gpr_r19_ppc64le, + gpr_r20_ppc64le, + gpr_r21_ppc64le, + gpr_r22_ppc64le, + gpr_r23_ppc64le, + gpr_r24_ppc64le, + gpr_r25_ppc64le, + gpr_r26_ppc64le, + gpr_r27_ppc64le, + gpr_r28_ppc64le, + gpr_r29_ppc64le, + gpr_r30_ppc64le, + gpr_r31_ppc64le, + gpr_pc_ppc64le, + gpr_msr_ppc64le, + gpr_origr3_ppc64le, + gpr_ctr_ppc64le, + gpr_lr_ppc64le, + gpr_xer_ppc64le, + gpr_cr_ppc64le, + gpr_softe_ppc64le, + gpr_trap_ppc64le, + k_last_gpr_ppc64le = gpr_trap_ppc64le, + + k_first_fpr_ppc64le, + fpr_f0_ppc64le = k_first_fpr_ppc64le, + fpr_f1_ppc64le, + fpr_f2_ppc64le, + fpr_f3_ppc64le, + fpr_f4_ppc64le, + fpr_f5_ppc64le, + fpr_f6_ppc64le, + fpr_f7_ppc64le, + fpr_f8_ppc64le, + fpr_f9_ppc64le, + fpr_f10_ppc64le, + fpr_f11_ppc64le, + fpr_f12_ppc64le, + fpr_f13_ppc64le, + fpr_f14_ppc64le, + fpr_f15_ppc64le, + fpr_f16_ppc64le, + fpr_f17_ppc64le, + fpr_f18_ppc64le, + fpr_f19_ppc64le, + fpr_f20_ppc64le, + fpr_f21_ppc64le, + fpr_f22_ppc64le, + fpr_f23_ppc64le, + fpr_f24_ppc64le, + fpr_f25_ppc64le, + fpr_f26_ppc64le, + fpr_f27_ppc64le, + fpr_f28_ppc64le, + fpr_f29_ppc64le, + fpr_f30_ppc64le, + fpr_f31_ppc64le, + fpr_fpscr_ppc64le, + k_last_fpr_ppc64le = fpr_fpscr_ppc64le, + + k_first_vmx_ppc64le, + vmx_vr0_ppc64le = k_first_vmx_ppc64le, + vmx_vr1_ppc64le, + vmx_vr2_ppc64le, + vmx_vr3_ppc64le, + vmx_vr4_ppc64le, + vmx_vr5_ppc64le, + vmx_vr6_ppc64le, + vmx_vr7_ppc64le, + vmx_vr8_ppc64le, + vmx_vr9_ppc64le, + vmx_vr10_ppc64le, + vmx_vr11_ppc64le, + vmx_vr12_ppc64le, + vmx_vr13_ppc64le, + vmx_vr14_ppc64le, + vmx_vr15_ppc64le, + vmx_vr16_ppc64le, + vmx_vr17_ppc64le, + vmx_vr18_ppc64le, + vmx_vr19_ppc64le, + vmx_vr20_ppc64le, + vmx_vr21_ppc64le, + vmx_vr22_ppc64le, + vmx_vr23_ppc64le, + vmx_vr24_ppc64le, + vmx_vr25_ppc64le, + vmx_vr26_ppc64le, + vmx_vr27_ppc64le, + vmx_vr28_ppc64le, + vmx_vr29_ppc64le, + vmx_vr30_ppc64le, + vmx_vr31_ppc64le, + vmx_vscr_ppc64le, + vmx_vrsave_ppc64le, + k_last_vmx_ppc64le = vmx_vrsave_ppc64le, + + k_first_vsx_ppc64le, + vsx_vs0_ppc64le = k_first_vsx_ppc64le, + vsx_vs1_ppc64le, + vsx_vs2_ppc64le, + vsx_vs3_ppc64le, + vsx_vs4_ppc64le, + vsx_vs5_ppc64le, + vsx_vs6_ppc64le, + vsx_vs7_ppc64le, + vsx_vs8_ppc64le, + vsx_vs9_ppc64le, + vsx_vs10_ppc64le, + vsx_vs11_ppc64le, + vsx_vs12_ppc64le, + vsx_vs13_ppc64le, + vsx_vs14_ppc64le, + vsx_vs15_ppc64le, + vsx_vs16_ppc64le, + vsx_vs17_ppc64le, + vsx_vs18_ppc64le, + vsx_vs19_ppc64le, + vsx_vs20_ppc64le, + vsx_vs21_ppc64le, + vsx_vs22_ppc64le, + vsx_vs23_ppc64le, + vsx_vs24_ppc64le, + vsx_vs25_ppc64le, + vsx_vs26_ppc64le, + vsx_vs27_ppc64le, + vsx_vs28_ppc64le, + vsx_vs29_ppc64le, + vsx_vs30_ppc64le, + vsx_vs31_ppc64le, + vsx_vs32_ppc64le, + vsx_vs33_ppc64le, + vsx_vs34_ppc64le, + vsx_vs35_ppc64le, + vsx_vs36_ppc64le, + vsx_vs37_ppc64le, + vsx_vs38_ppc64le, + vsx_vs39_ppc64le, + vsx_vs40_ppc64le, + vsx_vs41_ppc64le, + vsx_vs42_ppc64le, + vsx_vs43_ppc64le, + vsx_vs44_ppc64le, + vsx_vs45_ppc64le, + vsx_vs46_ppc64le, + vsx_vs47_ppc64le, + vsx_vs48_ppc64le, + vsx_vs49_ppc64le, + vsx_vs50_ppc64le, + vsx_vs51_ppc64le, + vsx_vs52_ppc64le, + vsx_vs53_ppc64le, + vsx_vs54_ppc64le, + vsx_vs55_ppc64le, + vsx_vs56_ppc64le, + vsx_vs57_ppc64le, + vsx_vs58_ppc64le, + vsx_vs59_ppc64le, + vsx_vs60_ppc64le, + vsx_vs61_ppc64le, + vsx_vs62_ppc64le, + vsx_vs63_ppc64le, + k_last_vsx_ppc64le = vsx_vs63_ppc64le, + + k_num_registers_ppc64le, + k_num_gpr_registers_ppc64le = k_last_gpr_ppc64le - k_first_gpr_ppc64le + 1, + k_num_fpr_registers_ppc64le = k_last_fpr_ppc64le - k_first_fpr_ppc64le + 1, + k_num_vmx_registers_ppc64le = k_last_vmx_ppc64le - k_first_vmx_ppc64le + 1, + k_num_vsx_registers_ppc64le = k_last_vsx_ppc64le - k_first_vsx_ppc64le + 1, +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_PPC64LE_REGISTER_ENUMS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-riscv-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-riscv-register-enums.h new file mode 100644 index 000000000000..caec313750ab --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-riscv-register-enums.h @@ -0,0 +1,193 @@ +//===-- lldb-riscv-register-enums.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_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_RISCV_REGISTER_ENUMS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_RISCV_REGISTER_ENUMS_H + +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +// Internal codes for all riscv registers. +enum { + // The same order as user_regs_struct in <asm/ptrace.h> + // note: these enum values are used as byte_offset + gpr_first_riscv = 0, + gpr_pc_riscv = gpr_first_riscv, + gpr_x1_riscv, + gpr_x2_riscv, + gpr_x3_riscv, + gpr_x4_riscv, + gpr_x5_riscv, + gpr_x6_riscv, + gpr_x7_riscv, + gpr_x8_riscv, + gpr_x9_riscv, + gpr_x10_riscv, + gpr_x11_riscv, + gpr_x12_riscv, + gpr_x13_riscv, + gpr_x14_riscv, + gpr_x15_riscv, + gpr_x16_riscv, + gpr_x17_riscv, + gpr_x18_riscv, + gpr_x19_riscv, + gpr_x20_riscv, + gpr_x21_riscv, + gpr_x22_riscv, + gpr_x23_riscv, + gpr_x24_riscv, + gpr_x25_riscv, + gpr_x26_riscv, + gpr_x27_riscv, + gpr_x28_riscv, + gpr_x29_riscv, + gpr_x30_riscv, + gpr_x31_riscv, + gpr_x0_riscv, + gpr_zero_riscv = gpr_x0_riscv, + gpr_ra_riscv = gpr_x1_riscv, + gpr_sp_riscv = gpr_x2_riscv, + gpr_gp_riscv = gpr_x3_riscv, + gpr_tp_riscv = gpr_x4_riscv, + gpr_t0_riscv = gpr_x5_riscv, + gpr_t1_riscv = gpr_x6_riscv, + gpr_t2_riscv = gpr_x7_riscv, + gpr_fp_riscv = gpr_x8_riscv, + gpr_s1_riscv = gpr_x9_riscv, + gpr_a0_riscv = gpr_x10_riscv, + gpr_a1_riscv = gpr_x11_riscv, + gpr_a2_riscv = gpr_x12_riscv, + gpr_a3_riscv = gpr_x13_riscv, + gpr_a4_riscv = gpr_x14_riscv, + gpr_a5_riscv = gpr_x15_riscv, + gpr_a6_riscv = gpr_x16_riscv, + gpr_a7_riscv = gpr_x17_riscv, + gpr_s2_riscv = gpr_x18_riscv, + gpr_s3_riscv = gpr_x19_riscv, + gpr_s4_riscv = gpr_x20_riscv, + gpr_s5_riscv = gpr_x21_riscv, + gpr_s6_riscv = gpr_x22_riscv, + gpr_s7_riscv = gpr_x23_riscv, + gpr_s8_riscv = gpr_x24_riscv, + gpr_s9_riscv = gpr_x25_riscv, + gpr_s10_riscv = gpr_x26_riscv, + gpr_s11_riscv = gpr_x27_riscv, + gpr_t3_riscv = gpr_x28_riscv, + gpr_t4_riscv = gpr_x29_riscv, + gpr_t5_riscv = gpr_x30_riscv, + gpr_t6_riscv = gpr_x31_riscv, + gpr_last_riscv = gpr_x0_riscv, + + fpr_first_riscv = 33, + fpr_f0_riscv = fpr_first_riscv, + fpr_f1_riscv, + fpr_f2_riscv, + fpr_f3_riscv, + fpr_f4_riscv, + fpr_f5_riscv, + fpr_f6_riscv, + fpr_f7_riscv, + fpr_f8_riscv, + fpr_f9_riscv, + fpr_f10_riscv, + fpr_f11_riscv, + fpr_f12_riscv, + fpr_f13_riscv, + fpr_f14_riscv, + fpr_f15_riscv, + fpr_f16_riscv, + fpr_f17_riscv, + fpr_f18_riscv, + fpr_f19_riscv, + fpr_f20_riscv, + fpr_f21_riscv, + fpr_f22_riscv, + fpr_f23_riscv, + fpr_f24_riscv, + fpr_f25_riscv, + fpr_f26_riscv, + fpr_f27_riscv, + fpr_f28_riscv, + fpr_f29_riscv, + fpr_f30_riscv, + fpr_f31_riscv, + + fpr_fcsr_riscv, + fpr_ft0_riscv = fpr_f0_riscv, + fpr_ft1_riscv = fpr_f1_riscv, + fpr_ft2_riscv = fpr_f2_riscv, + fpr_ft3_riscv = fpr_f3_riscv, + fpr_ft4_riscv = fpr_f4_riscv, + fpr_ft5_riscv = fpr_f5_riscv, + fpr_ft6_riscv = fpr_f6_riscv, + fpr_ft7_riscv = fpr_f7_riscv, + fpr_fs0_riscv = fpr_f8_riscv, + fpr_fs1_riscv = fpr_f9_riscv, + fpr_fa0_riscv = fpr_f10_riscv, + fpr_fa1_riscv = fpr_f11_riscv, + fpr_fa2_riscv = fpr_f12_riscv, + fpr_fa3_riscv = fpr_f13_riscv, + fpr_fa4_riscv = fpr_f14_riscv, + fpr_fa5_riscv = fpr_f15_riscv, + fpr_fa6_riscv = fpr_f16_riscv, + fpr_fa7_riscv = fpr_f17_riscv, + fpr_fs2_riscv = fpr_f18_riscv, + fpr_fs3_riscv = fpr_f19_riscv, + fpr_fs4_riscv = fpr_f20_riscv, + fpr_fs5_riscv = fpr_f21_riscv, + fpr_fs6_riscv = fpr_f22_riscv, + fpr_fs7_riscv = fpr_f23_riscv, + fpr_fs8_riscv = fpr_f24_riscv, + fpr_fs9_riscv = fpr_f25_riscv, + fpr_fs10_riscv = fpr_f26_riscv, + fpr_fs11_riscv = fpr_f27_riscv, + fpr_ft8_riscv = fpr_f28_riscv, + fpr_ft9_riscv = fpr_f29_riscv, + fpr_ft10_riscv = fpr_f30_riscv, + fpr_ft11_riscv = fpr_f31_riscv, + fpr_last_riscv = fpr_fcsr_riscv, + + vpr_first_riscv = 66, + vpr_v0_riscv = vpr_first_riscv, + vpr_v1_riscv, + vpr_v2_riscv, + vpr_v3_riscv, + vpr_v4_riscv, + vpr_v5_riscv, + vpr_v6_riscv, + vpr_v7_riscv, + vpr_v8_riscv, + vpr_v9_riscv, + vpr_v10_riscv, + vpr_v11_riscv, + vpr_v12_riscv, + vpr_v13_riscv, + vpr_v14_riscv, + vpr_v15_riscv, + vpr_v16_riscv, + vpr_v17_riscv, + vpr_v18_riscv, + vpr_v19_riscv, + vpr_v20_riscv, + vpr_v21_riscv, + vpr_v22_riscv, + vpr_v23_riscv, + vpr_v24_riscv, + vpr_v25_riscv, + vpr_v26_riscv, + vpr_v27_riscv, + vpr_v28_riscv, + vpr_v29_riscv, + vpr_v30_riscv, + vpr_v31_riscv, + vpr_last_riscv = vpr_v31_riscv, + + k_num_registers_riscv +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_RISCV_REGISTER_ENUMS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h new file mode 100644 index 000000000000..23c441e1c803 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-s390x-register-enums.h @@ -0,0 +1,90 @@ +//===-- lldb-s390x-register-enums.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_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_S390X_REGISTER_ENUMS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_S390X_REGISTER_ENUMS_H + +namespace lldb_private { +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +// Internal codes for all s390x registers. +enum { + k_first_gpr_s390x, + lldb_r0_s390x = k_first_gpr_s390x, + lldb_r1_s390x, + lldb_r2_s390x, + lldb_r3_s390x, + lldb_r4_s390x, + lldb_r5_s390x, + lldb_r6_s390x, + lldb_r7_s390x, + lldb_r8_s390x, + lldb_r9_s390x, + lldb_r10_s390x, + lldb_r11_s390x, + lldb_r12_s390x, + lldb_r13_s390x, + lldb_r14_s390x, + lldb_r15_s390x, + lldb_acr0_s390x, + lldb_acr1_s390x, + lldb_acr2_s390x, + lldb_acr3_s390x, + lldb_acr4_s390x, + lldb_acr5_s390x, + lldb_acr6_s390x, + lldb_acr7_s390x, + lldb_acr8_s390x, + lldb_acr9_s390x, + lldb_acr10_s390x, + lldb_acr11_s390x, + lldb_acr12_s390x, + lldb_acr13_s390x, + lldb_acr14_s390x, + lldb_acr15_s390x, + lldb_pswm_s390x, + lldb_pswa_s390x, + k_last_gpr_s390x = lldb_pswa_s390x, + + k_first_fpr_s390x, + lldb_f0_s390x = k_first_fpr_s390x, + lldb_f1_s390x, + lldb_f2_s390x, + lldb_f3_s390x, + lldb_f4_s390x, + lldb_f5_s390x, + lldb_f6_s390x, + lldb_f7_s390x, + lldb_f8_s390x, + lldb_f9_s390x, + lldb_f10_s390x, + lldb_f11_s390x, + lldb_f12_s390x, + lldb_f13_s390x, + lldb_f14_s390x, + lldb_f15_s390x, + lldb_fpc_s390x, + k_last_fpr_s390x = lldb_fpc_s390x, + + // These are only available on Linux. + k_first_linux_s390x, + lldb_orig_r2_s390x = k_first_linux_s390x, + lldb_last_break_s390x, + lldb_system_call_s390x, + k_last_linux_s390x = lldb_system_call_s390x, + + k_num_registers_s390x, + k_num_gpr_registers_s390x = k_last_gpr_s390x - k_first_gpr_s390x + 1, + k_num_fpr_registers_s390x = k_last_fpr_s390x - k_first_fpr_s390x + 1, + k_num_linux_registers_s390x = k_last_linux_s390x - k_first_linux_s390x + 1, + k_num_user_registers_s390x = + k_num_gpr_registers_s390x + k_num_fpr_registers_s390x, +}; +} + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_S390X_REGISTER_ENUMS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h new file mode 100644 index 000000000000..85aa254d6621 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/Utility/lldb-x86-register-enums.h @@ -0,0 +1,517 @@ +//===-- lldb-x86-register-enums.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_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_X86_REGISTER_ENUMS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_X86_REGISTER_ENUMS_H + +namespace lldb_private { +// LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + +// Internal codes for all i386 registers. +enum { + k_first_gpr_i386, + lldb_eax_i386 = k_first_gpr_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, + + k_first_alias_i386, + lldb_ax_i386 = k_first_alias_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, + k_last_alias_i386 = lldb_dl_i386, + + k_last_gpr_i386 = k_last_alias_i386, + + k_first_fpr_i386, + lldb_fctrl_i386 = k_first_fpr_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, + k_last_fpr_i386 = lldb_xmm7_i386, + + k_first_avx_i386, + lldb_ymm0_i386 = k_first_avx_i386, + lldb_ymm1_i386, + lldb_ymm2_i386, + lldb_ymm3_i386, + lldb_ymm4_i386, + lldb_ymm5_i386, + lldb_ymm6_i386, + lldb_ymm7_i386, + k_last_avx_i386 = lldb_ymm7_i386, + + k_first_mpxr_i386, + lldb_bnd0_i386 = k_first_mpxr_i386, + lldb_bnd1_i386, + lldb_bnd2_i386, + lldb_bnd3_i386, + k_last_mpxr_i386 = lldb_bnd3_i386, + + k_first_mpxc_i386, + lldb_bndcfgu_i386 = k_first_mpxc_i386, + lldb_bndstatus_i386, + k_last_mpxc_i386 = lldb_bndstatus_i386, + + k_first_dbr_i386, + lldb_dr0_i386 = k_first_dbr_i386, + lldb_dr1_i386, + lldb_dr2_i386, + lldb_dr3_i386, + lldb_dr4_i386, + lldb_dr5_i386, + lldb_dr6_i386, + lldb_dr7_i386, + k_last_dbr_i386 = lldb_dr7_i386, + + k_num_registers_i386, + k_num_gpr_registers_i386 = k_last_gpr_i386 - k_first_gpr_i386 + 1, + k_num_fpr_registers_i386 = k_last_fpr_i386 - k_first_fpr_i386 + 1, + k_num_avx_registers_i386 = k_last_avx_i386 - k_first_avx_i386 + 1, + k_num_mpx_registers_i386 = k_last_mpxc_i386 - k_first_mpxr_i386 + 1, + k_num_user_registers_i386 = k_num_gpr_registers_i386 + + k_num_fpr_registers_i386 + + k_num_avx_registers_i386 + + k_num_mpx_registers_i386, + k_num_dbr_registers_i386 = k_last_dbr_i386 - k_first_dbr_i386 + 1, +}; + +// Internal codes for all x86_64 registers. +enum { + k_first_gpr_x86_64, + lldb_rax_x86_64 = k_first_gpr_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, + + k_first_alias_x86_64, + lldb_eax_x86_64 = k_first_alias_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 of r8 + lldb_r9d_x86_64, // Low 32 bits of r9 + lldb_r10d_x86_64, // Low 32 bits of r10 + lldb_r11d_x86_64, // Low 32 bits of r11 + lldb_r12d_x86_64, // Low 32 bits of r12 + lldb_r13d_x86_64, // Low 32 bits of r13 + lldb_r14d_x86_64, // Low 32 bits of r14 + lldb_r15d_x86_64, // Low 32 bits of 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 of r8 + lldb_r9w_x86_64, // Low 16 bits of r9 + lldb_r10w_x86_64, // Low 16 bits of r10 + lldb_r11w_x86_64, // Low 16 bits of r11 + lldb_r12w_x86_64, // Low 16 bits of r12 + lldb_r13w_x86_64, // Low 16 bits of r13 + lldb_r14w_x86_64, // Low 16 bits of r14 + lldb_r15w_x86_64, // Low 16 bits of 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 of r8 + lldb_r9l_x86_64, // Low 8 bits of r9 + lldb_r10l_x86_64, // Low 8 bits of r10 + lldb_r11l_x86_64, // Low 8 bits of r11 + lldb_r12l_x86_64, // Low 8 bits of r12 + lldb_r13l_x86_64, // Low 8 bits of r13 + lldb_r14l_x86_64, // Low 8 bits of r14 + lldb_r15l_x86_64, // Low 8 bits of r15 + k_last_alias_x86_64 = lldb_r15l_x86_64, + + k_last_gpr_x86_64 = k_last_alias_x86_64, + + k_first_fpr_x86_64, + lldb_fctrl_x86_64 = k_first_fpr_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, + k_last_fpr_x86_64 = lldb_xmm15_x86_64, + + k_first_avx_x86_64, + lldb_ymm0_x86_64 = k_first_avx_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, + k_last_avx_x86_64 = lldb_ymm15_x86_64, + + k_first_mpxr_x86_64, + lldb_bnd0_x86_64 = k_first_mpxr_x86_64, + lldb_bnd1_x86_64, + lldb_bnd2_x86_64, + lldb_bnd3_x86_64, + k_last_mpxr_x86_64 = lldb_bnd3_x86_64, + + k_first_mpxc_x86_64, + lldb_bndcfgu_x86_64 = k_first_mpxc_x86_64, + lldb_bndstatus_x86_64, + k_last_mpxc_x86_64 = lldb_bndstatus_x86_64, + + k_first_dbr_x86_64, + lldb_dr0_x86_64 = k_first_dbr_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, + k_last_dbr_x86_64 = lldb_dr7_x86_64, + + k_num_registers_x86_64, + k_num_gpr_registers_x86_64 = k_last_gpr_x86_64 - k_first_gpr_x86_64 + 1, + k_num_fpr_registers_x86_64 = k_last_fpr_x86_64 - k_first_fpr_x86_64 + 1, + k_num_avx_registers_x86_64 = k_last_avx_x86_64 - k_first_avx_x86_64 + 1, + k_num_mpx_registers_x86_64 = k_last_mpxc_x86_64 - k_first_mpxr_x86_64 + 1, + k_num_user_registers_x86_64 = k_num_gpr_registers_x86_64 + + k_num_fpr_registers_x86_64 + + k_num_avx_registers_x86_64 + + k_num_mpx_registers_x86_64, + k_num_dbr_registers_x86_64 = k_last_dbr_x86_64 - k_first_dbr_x86_64 + 1, +}; + +// For platform that supports fs_base/gs_base registers. +namespace x86_64_with_base { +enum { + k_first_gpr, + lldb_rax = k_first_gpr, + lldb_rbx, + lldb_rcx, + lldb_rdx, + lldb_rdi, + lldb_rsi, + lldb_rbp, + lldb_rsp, + lldb_r8, + lldb_r9, + lldb_r10, + lldb_r11, + lldb_r12, + lldb_r13, + lldb_r14, + lldb_r15, + lldb_rip, + lldb_rflags, + lldb_cs, + lldb_fs, + lldb_gs, + lldb_ss, + lldb_fs_base, + lldb_gs_base, + lldb_ds, + lldb_es, + + k_first_alias, + lldb_eax = k_first_alias, + lldb_ebx, + lldb_ecx, + lldb_edx, + lldb_edi, + lldb_esi, + lldb_ebp, + lldb_esp, + lldb_r8d, // Low 32 bits of r8 + lldb_r9d, // Low 32 bits of r9 + lldb_r10d, // Low 32 bits of r10 + lldb_r11d, // Low 32 bits of r11 + lldb_r12d, // Low 32 bits of r12 + lldb_r13d, // Low 32 bits of r13 + lldb_r14d, // Low 32 bits of r14 + lldb_r15d, // Low 32 bits of r15 + lldb_ax, + lldb_bx, + lldb_cx, + lldb_dx, + lldb_di, + lldb_si, + lldb_bp, + lldb_sp, + lldb_r8w, // Low 16 bits of r8 + lldb_r9w, // Low 16 bits of r9 + lldb_r10w, // Low 16 bits of r10 + lldb_r11w, // Low 16 bits of r11 + lldb_r12w, // Low 16 bits of r12 + lldb_r13w, // Low 16 bits of r13 + lldb_r14w, // Low 16 bits of r14 + lldb_r15w, // Low 16 bits of r15 + lldb_ah, + lldb_bh, + lldb_ch, + lldb_dh, + lldb_al, + lldb_bl, + lldb_cl, + lldb_dl, + lldb_dil, + lldb_sil, + lldb_bpl, + lldb_spl, + lldb_r8l, // Low 8 bits of r8 + lldb_r9l, // Low 8 bits of r9 + lldb_r10l, // Low 8 bits of r10 + lldb_r11l, // Low 8 bits of r11 + lldb_r12l, // Low 8 bits of r12 + lldb_r13l, // Low 8 bits of r13 + lldb_r14l, // Low 8 bits of r14 + lldb_r15l, // Low 8 bits of r15 + k_last_alias = lldb_r15l, + + k_last_gpr = k_last_alias, + + k_first_fpr, + lldb_fctrl = k_first_fpr, + lldb_fstat, + lldb_ftag, + lldb_fop, + lldb_fiseg, + lldb_fioff, + lldb_fip, + lldb_foseg, + lldb_fooff, + lldb_fdp, + lldb_mxcsr, + lldb_mxcsrmask, + lldb_st0, + lldb_st1, + lldb_st2, + lldb_st3, + lldb_st4, + lldb_st5, + lldb_st6, + lldb_st7, + lldb_mm0, + lldb_mm1, + lldb_mm2, + lldb_mm3, + lldb_mm4, + lldb_mm5, + lldb_mm6, + lldb_mm7, + lldb_xmm0, + lldb_xmm1, + lldb_xmm2, + lldb_xmm3, + lldb_xmm4, + lldb_xmm5, + lldb_xmm6, + lldb_xmm7, + lldb_xmm8, + lldb_xmm9, + lldb_xmm10, + lldb_xmm11, + lldb_xmm12, + lldb_xmm13, + lldb_xmm14, + lldb_xmm15, + k_last_fpr = lldb_xmm15, + + k_first_avx, + lldb_ymm0 = k_first_avx, + lldb_ymm1, + lldb_ymm2, + lldb_ymm3, + lldb_ymm4, + lldb_ymm5, + lldb_ymm6, + lldb_ymm7, + lldb_ymm8, + lldb_ymm9, + lldb_ymm10, + lldb_ymm11, + lldb_ymm12, + lldb_ymm13, + lldb_ymm14, + lldb_ymm15, + k_last_avx = lldb_ymm15, + + k_first_mpxr, + lldb_bnd0 = k_first_mpxr, + lldb_bnd1, + lldb_bnd2, + lldb_bnd3, + k_last_mpxr = lldb_bnd3, + + k_first_mpxc, + lldb_bndcfgu = k_first_mpxc, + lldb_bndstatus, + k_last_mpxc = lldb_bndstatus, + + k_first_dbr, + lldb_dr0 = k_first_dbr, + lldb_dr1, + lldb_dr2, + lldb_dr3, + lldb_dr4, + lldb_dr5, + lldb_dr6, + lldb_dr7, + k_last_dbr = lldb_dr7, + + k_num_registers, + k_num_gpr_registers = k_last_gpr - k_first_gpr + 1, + k_num_fpr_registers = k_last_fpr - k_first_fpr + 1, + k_num_avx_registers = k_last_avx - k_first_avx + 1, + k_num_mpx_registers = k_last_mpxc - k_first_mpxr + 1, + k_num_user_registers = k_num_gpr_registers + + k_num_fpr_registers + + k_num_avx_registers + + k_num_mpx_registers, + k_num_dbr_registers = k_last_dbr - k_first_dbr + 1, +}; +} // namespace x86_64_with_base + +} + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_LLDB_X86_REGISTER_ENUMS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp new file mode 100644 index 000000000000..30af9345999c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -0,0 +1,1097 @@ +//===-- ProcessElfCore.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 <cstdlib> + +#include <memory> +#include <mutex> + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/Threading.h" + +#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h" +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "ProcessElfCore.h" +#include "ThreadElfCore.h" + +using namespace lldb_private; +namespace ELF = llvm::ELF; + +LLDB_PLUGIN_DEFINE(ProcessElfCore) + +llvm::StringRef ProcessElfCore::GetPluginDescriptionStatic() { + return "ELF core dump plug-in."; +} + +void ProcessElfCore::Terminate() { + PluginManager::UnregisterPlugin(ProcessElfCore::CreateInstance); +} + +lldb::ProcessSP ProcessElfCore::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file, + bool can_connect) { + lldb::ProcessSP process_sp; + if (crash_file && !can_connect) { + // Read enough data for an ELF32 header or ELF64 header Note: Here we care + // about e_type field only, so it is safe to ignore possible presence of + // the header extension. + const size_t header_size = sizeof(llvm::ELF::Elf64_Ehdr); + + auto data_sp = FileSystem::Instance().CreateDataBuffer( + crash_file->GetPath(), header_size, 0); + if (data_sp && data_sp->GetByteSize() == header_size && + elf::ELFHeader::MagicBytesMatch(data_sp->GetBytes())) { + elf::ELFHeader elf_header; + DataExtractor data(data_sp, lldb::eByteOrderLittle, 4); + lldb::offset_t data_offset = 0; + if (elf_header.Parse(data, &data_offset)) { + // Check whether we're dealing with a raw FreeBSD "full memory dump" + // ELF vmcore that needs to be handled via FreeBSDKernel plugin instead. + if (elf_header.e_ident[7] == 0xFF && elf_header.e_version == 0) + return process_sp; + if (elf_header.e_type == llvm::ELF::ET_CORE) + process_sp = std::make_shared<ProcessElfCore>(target_sp, listener_sp, + *crash_file); + } + } + } + return process_sp; +} + +bool ProcessElfCore::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + // For now we are just making sure the file exists for a given module + if (!m_core_module_sp && FileSystem::Instance().Exists(m_core_file)) { + ModuleSpec core_module_spec(m_core_file, target_sp->GetArchitecture()); + Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp, + nullptr, nullptr, nullptr)); + if (m_core_module_sp) { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile) + return true; + } + } + return false; +} + +// ProcessElfCore constructor +ProcessElfCore::ProcessElfCore(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec &core_file) + : PostMortemProcess(target_sp, listener_sp, core_file) {} + +// Destructor +ProcessElfCore::~ProcessElfCore() { + Clear(); + // We need to call finalize on the process before destroying ourselves to + // make sure all of the broadcaster cleanup goes as planned. If we destruct + // this class, then Process::~Process() might have problems trying to fully + // destroy the broadcaster. + Finalize(true /* destructing */); +} + +lldb::addr_t ProcessElfCore::AddAddressRangeFromLoadSegment( + const elf::ELFProgramHeader &header) { + const lldb::addr_t addr = header.p_vaddr; + FileRange file_range(header.p_offset, header.p_filesz); + VMRangeToFileOffset::Entry range_entry(addr, header.p_memsz, file_range); + + // Only add to m_core_aranges if the file size is non zero. Some core files + // have PT_LOAD segments for all address ranges, but set f_filesz to zero for + // the .text sections since they can be retrieved from the object files. + if (header.p_filesz > 0) { + VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); + if (last_entry && last_entry->GetRangeEnd() == range_entry.GetRangeBase() && + last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase() && + last_entry->GetByteSize() == last_entry->data.GetByteSize()) { + last_entry->SetRangeEnd(range_entry.GetRangeEnd()); + last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd()); + } else { + m_core_aranges.Append(range_entry); + } + } + // Keep a separate map of permissions that isn't coalesced so all ranges + // are maintained. + const uint32_t permissions = + ((header.p_flags & llvm::ELF::PF_R) ? lldb::ePermissionsReadable : 0u) | + ((header.p_flags & llvm::ELF::PF_W) ? lldb::ePermissionsWritable : 0u) | + ((header.p_flags & llvm::ELF::PF_X) ? lldb::ePermissionsExecutable : 0u); + + m_core_range_infos.Append( + VMRangeToPermissions::Entry(addr, header.p_memsz, permissions)); + + return addr; +} + +lldb::addr_t ProcessElfCore::AddAddressRangeFromMemoryTagSegment( + const elf::ELFProgramHeader &header) { + // If lldb understood multiple kinds of tag segments we would record the type + // of the segment here also. As long as there is only 1 type lldb looks for, + // there is no need. + FileRange file_range(header.p_offset, header.p_filesz); + m_core_tag_ranges.Append( + VMRangeToFileOffset::Entry(header.p_vaddr, header.p_memsz, file_range)); + + return header.p_vaddr; +} + +// Process Control +Status ProcessElfCore::DoLoadCore() { + Status error; + if (!m_core_module_sp) { + error.SetErrorString("invalid core module"); + return error; + } + + ObjectFileELF *core = (ObjectFileELF *)(m_core_module_sp->GetObjectFile()); + if (core == nullptr) { + error.SetErrorString("invalid core object file"); + return error; + } + + llvm::ArrayRef<elf::ELFProgramHeader> segments = core->ProgramHeaders(); + if (segments.size() == 0) { + error.SetErrorString("core file has no segments"); + return error; + } + + SetCanJIT(false); + + m_thread_data_valid = true; + + bool ranges_are_sorted = true; + lldb::addr_t vm_addr = 0; + lldb::addr_t tag_addr = 0; + /// Walk through segments and Thread and Address Map information. + /// PT_NOTE - Contains Thread and Register information + /// PT_LOAD - Contains a contiguous range of Process Address Space + /// PT_AARCH64_MEMTAG_MTE - Contains AArch64 MTE memory tags for a range of + /// Process Address Space. + for (const elf::ELFProgramHeader &H : segments) { + DataExtractor data = core->GetSegmentData(H); + + // Parse thread contexts and auxv structure + if (H.p_type == llvm::ELF::PT_NOTE) { + if (llvm::Error error = ParseThreadContextsFromNoteSegment(H, data)) + return Status(std::move(error)); + } + // PT_LOAD segments contains address map + if (H.p_type == llvm::ELF::PT_LOAD) { + lldb::addr_t last_addr = AddAddressRangeFromLoadSegment(H); + if (vm_addr > last_addr) + ranges_are_sorted = false; + vm_addr = last_addr; + } else if (H.p_type == llvm::ELF::PT_AARCH64_MEMTAG_MTE) { + lldb::addr_t last_addr = AddAddressRangeFromMemoryTagSegment(H); + if (tag_addr > last_addr) + ranges_are_sorted = false; + tag_addr = last_addr; + } + } + + if (!ranges_are_sorted) { + m_core_aranges.Sort(); + m_core_range_infos.Sort(); + m_core_tag_ranges.Sort(); + } + + // Even if the architecture is set in the target, we need to override it to + // match the core file which is always single arch. + ArchSpec arch(m_core_module_sp->GetArchitecture()); + + ArchSpec target_arch = GetTarget().GetArchitecture(); + ArchSpec core_arch(m_core_module_sp->GetArchitecture()); + target_arch.MergeFrom(core_arch); + GetTarget().SetArchitecture(target_arch); + + SetUnixSignals(UnixSignals::Create(GetArchitecture())); + + // Ensure we found at least one thread that was stopped on a signal. + bool siginfo_signal_found = false; + bool prstatus_signal_found = false; + // Check we found a signal in a SIGINFO note. + for (const auto &thread_data : m_thread_data) { + if (thread_data.signo != 0) + siginfo_signal_found = true; + if (thread_data.prstatus_sig != 0) + prstatus_signal_found = true; + } + if (!siginfo_signal_found) { + // If we don't have signal from SIGINFO use the signal from each threads + // PRSTATUS note. + if (prstatus_signal_found) { + for (auto &thread_data : m_thread_data) + thread_data.signo = thread_data.prstatus_sig; + } else if (m_thread_data.size() > 0) { + // If all else fails force the first thread to be SIGSTOP + m_thread_data.begin()->signo = + GetUnixSignals()->GetSignalNumberFromName("SIGSTOP"); + } + } + + // Try to find gnu build id before we load the executable. + UpdateBuildIdForNTFileEntries(); + + // Core files are useless without the main executable. See if we can locate + // the main executable using data we found in the core file notes. + lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); + if (!exe_module_sp) { + // The first entry in the NT_FILE might be our executable + if (!m_nt_file_entries.empty()) { + ModuleSpec exe_module_spec; + exe_module_spec.GetArchitecture() = arch; + exe_module_spec.GetUUID() = m_nt_file_entries[0].uuid; + exe_module_spec.GetFileSpec().SetFile(m_nt_file_entries[0].path, + FileSpec::Style::native); + if (exe_module_spec.GetFileSpec()) { + exe_module_sp = + GetTarget().GetOrCreateModule(exe_module_spec, true /* notify */); + if (exe_module_sp) + GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo); + } + } + } + return error; +} + +void ProcessElfCore::UpdateBuildIdForNTFileEntries() { + for (NT_FILE_Entry &entry : m_nt_file_entries) { + entry.uuid = FindBuidIdInCoreMemory(entry.start); + } +} + +lldb_private::DynamicLoader *ProcessElfCore::GetDynamicLoader() { + if (m_dyld_up.get() == nullptr) + m_dyld_up.reset(DynamicLoader::FindPlugin( + this, DynamicLoaderPOSIXDYLD::GetPluginNameStatic())); + return m_dyld_up.get(); +} + +bool ProcessElfCore::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) { + const uint32_t num_threads = GetNumThreadContexts(); + if (!m_thread_data_valid) + return false; + + for (lldb::tid_t tid = 0; tid < num_threads; ++tid) { + const ThreadData &td = m_thread_data[tid]; + lldb::ThreadSP thread_sp(new ThreadElfCore(*this, td)); + new_thread_list.AddThread(thread_sp); + } + return new_thread_list.GetSize(false) > 0; +} + +void ProcessElfCore::RefreshStateAfterStop() {} + +Status ProcessElfCore::DoDestroy() { return Status(); } + +// Process Queries + +bool ProcessElfCore::IsAlive() { return true; } + +// Process Memory +size_t ProcessElfCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + if (lldb::ABISP abi_sp = GetABI()) + addr = abi_sp->FixAnyAddress(addr); + + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // in core files we have it all cached our our core file anyway. + return DoReadMemory(addr, buf, size, error); +} + +Status ProcessElfCore::DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo ®ion_info) { + region_info.Clear(); + const VMRangeToPermissions::Entry *permission_entry = + m_core_range_infos.FindEntryThatContainsOrFollows(load_addr); + if (permission_entry) { + if (permission_entry->Contains(load_addr)) { + region_info.GetRange().SetRangeBase(permission_entry->GetRangeBase()); + region_info.GetRange().SetRangeEnd(permission_entry->GetRangeEnd()); + const Flags permissions(permission_entry->data); + region_info.SetReadable(permissions.Test(lldb::ePermissionsReadable) + ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + region_info.SetWritable(permissions.Test(lldb::ePermissionsWritable) + ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + region_info.SetExecutable(permissions.Test(lldb::ePermissionsExecutable) + ? MemoryRegionInfo::eYes + : MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eYes); + + // A region is memory tagged if there is a memory tag segment that covers + // the exact same range. + region_info.SetMemoryTagged(MemoryRegionInfo::eNo); + const VMRangeToFileOffset::Entry *tag_entry = + m_core_tag_ranges.FindEntryStartsAt(permission_entry->GetRangeBase()); + if (tag_entry && + tag_entry->GetRangeEnd() == permission_entry->GetRangeEnd()) + region_info.SetMemoryTagged(MemoryRegionInfo::eYes); + } else if (load_addr < permission_entry->GetRangeBase()) { + region_info.GetRange().SetRangeBase(load_addr); + region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase()); + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + region_info.SetMemoryTagged(MemoryRegionInfo::eNo); + } + return Status(); + } + + region_info.GetRange().SetRangeBase(load_addr); + region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + region_info.SetMemoryTagged(MemoryRegionInfo::eNo); + return Status(); +} + +size_t ProcessElfCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + + if (core_objfile == nullptr) + return 0; + + // Get the address range + const VMRangeToFileOffset::Entry *address_range = + m_core_aranges.FindEntryThatContains(addr); + if (address_range == nullptr || address_range->GetRangeEnd() < addr) { + error.SetErrorStringWithFormat("core file does not contain 0x%" PRIx64, + addr); + return 0; + } + + // Convert the address into core file offset + const lldb::addr_t offset = addr - address_range->GetRangeBase(); + const lldb::addr_t file_start = address_range->data.GetRangeBase(); + const lldb::addr_t file_end = address_range->data.GetRangeEnd(); + size_t bytes_to_read = size; // Number of bytes to read from the core file + size_t bytes_copied = 0; // Number of bytes actually read from the core file + lldb::addr_t bytes_left = + 0; // Number of bytes available in the core file from the given address + + // Don't proceed if core file doesn't contain the actual data for this + // address range. + if (file_start == file_end) + return 0; + + // Figure out how many on-disk bytes remain in this segment starting at the + // given offset + if (file_end > file_start + offset) + bytes_left = file_end - (file_start + offset); + + if (bytes_to_read > bytes_left) + bytes_to_read = bytes_left; + + // If there is data available on the core file read it + if (bytes_to_read) + bytes_copied = + core_objfile->CopyData(offset + file_start, bytes_to_read, buf); + + return bytes_copied; +} + +llvm::Expected<std::vector<lldb::addr_t>> +ProcessElfCore::ReadMemoryTags(lldb::addr_t addr, size_t len) { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile == nullptr) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "No core object file."); + + llvm::Expected<const MemoryTagManager *> tag_manager_or_err = + GetMemoryTagManager(); + if (!tag_manager_or_err) + return tag_manager_or_err.takeError(); + + // LLDB only supports AArch64 MTE tag segments so we do not need to worry + // about the segment type here. If you got here then you must have a tag + // manager (meaning you are debugging AArch64) and all the segments in this + // list will have had type PT_AARCH64_MEMTAG_MTE. + const VMRangeToFileOffset::Entry *tag_entry = + m_core_tag_ranges.FindEntryThatContains(addr); + // If we don't have a tag segment or the range asked for extends outside the + // segment. + if (!tag_entry || (addr + len) >= tag_entry->GetRangeEnd()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "No tag segment that covers this range."); + + const MemoryTagManager *tag_manager = *tag_manager_or_err; + return tag_manager->UnpackTagsFromCoreFileSegment( + [core_objfile](lldb::offset_t offset, size_t length, void *dst) { + return core_objfile->CopyData(offset, length, dst); + }, + tag_entry->GetRangeBase(), tag_entry->data.GetRangeBase(), addr, len); +} + +void ProcessElfCore::Clear() { + m_thread_list.Clear(); + + SetUnixSignals(std::make_shared<UnixSignals>()); +} + +void ProcessElfCore::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); + }); +} + +lldb::addr_t ProcessElfCore::GetImageInfoAddress() { + ObjectFile *obj_file = GetTarget().GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(&GetTarget()); + + if (addr.IsValid()) + return addr.GetLoadAddress(&GetTarget()); + return LLDB_INVALID_ADDRESS; +} + +// Parse a FreeBSD NT_PRSTATUS note - see FreeBSD sys/procfs.h for details. +static void ParseFreeBSDPrStatus(ThreadData &thread_data, + const DataExtractor &data, + bool lp64) { + lldb::offset_t offset = 0; + int pr_version = data.GetU32(&offset); + + Log *log = GetLog(LLDBLog::Process); + if (log) { + if (pr_version > 1) + LLDB_LOGF(log, "FreeBSD PRSTATUS unexpected version %d", pr_version); + } + + // Skip padding, pr_statussz, pr_gregsetsz, pr_fpregsetsz, pr_osreldate + if (lp64) + offset += 32; + else + offset += 16; + + thread_data.signo = data.GetU32(&offset); // pr_cursig + thread_data.tid = data.GetU32(&offset); // pr_pid + if (lp64) + offset += 4; + + size_t len = data.GetByteSize() - offset; + thread_data.gpregset = DataExtractor(data, offset, len); +} + +// Parse a FreeBSD NT_PRPSINFO note - see FreeBSD sys/procfs.h for details. +static void ParseFreeBSDPrPsInfo(ProcessElfCore &process, + const DataExtractor &data, + bool lp64) { + lldb::offset_t offset = 0; + int pr_version = data.GetU32(&offset); + + Log *log = GetLog(LLDBLog::Process); + if (log) { + if (pr_version > 1) + LLDB_LOGF(log, "FreeBSD PRPSINFO unexpected version %d", pr_version); + } + + // Skip pr_psinfosz, pr_fname, pr_psargs + offset += 108; + if (lp64) + offset += 4; + + process.SetID(data.GetU32(&offset)); // pr_pid +} + +static llvm::Error ParseNetBSDProcInfo(const DataExtractor &data, + uint32_t &cpi_nlwps, + uint32_t &cpi_signo, + uint32_t &cpi_siglwp, + uint32_t &cpi_pid) { + lldb::offset_t offset = 0; + + uint32_t version = data.GetU32(&offset); + if (version != 1) + return llvm::make_error<llvm::StringError>( + "Error parsing NetBSD core(5) notes: Unsupported procinfo version", + llvm::inconvertibleErrorCode()); + + uint32_t cpisize = data.GetU32(&offset); + if (cpisize != NETBSD::NT_PROCINFO_SIZE) + return llvm::make_error<llvm::StringError>( + "Error parsing NetBSD core(5) notes: Unsupported procinfo size", + llvm::inconvertibleErrorCode()); + + cpi_signo = data.GetU32(&offset); /* killing signal */ + + offset += NETBSD::NT_PROCINFO_CPI_SIGCODE_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_SIGPEND_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_SIGMASK_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_SIGIGNORE_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_SIGCATCH_SIZE; + cpi_pid = data.GetU32(&offset); + offset += NETBSD::NT_PROCINFO_CPI_PPID_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_PGRP_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_SID_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_RUID_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_EUID_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_SVUID_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_RGID_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_EGID_SIZE; + offset += NETBSD::NT_PROCINFO_CPI_SVGID_SIZE; + cpi_nlwps = data.GetU32(&offset); /* number of LWPs */ + + offset += NETBSD::NT_PROCINFO_CPI_NAME_SIZE; + cpi_siglwp = data.GetU32(&offset); /* LWP target of killing signal */ + + return llvm::Error::success(); +} + +static void ParseOpenBSDProcInfo(ThreadData &thread_data, + const DataExtractor &data) { + lldb::offset_t offset = 0; + + int version = data.GetU32(&offset); + if (version != 1) + return; + + offset += 4; + thread_data.signo = data.GetU32(&offset); +} + +llvm::Expected<std::vector<CoreNote>> +ProcessElfCore::parseSegment(const DataExtractor &segment) { + lldb::offset_t offset = 0; + std::vector<CoreNote> result; + + while (offset < segment.GetByteSize()) { + ELFNote note = ELFNote(); + if (!note.Parse(segment, &offset)) + return llvm::make_error<llvm::StringError>( + "Unable to parse note segment", llvm::inconvertibleErrorCode()); + + size_t note_start = offset; + size_t note_size = llvm::alignTo(note.n_descsz, 4); + + result.push_back({note, DataExtractor(segment, note_start, note_size)}); + offset += note_size; + } + + return std::move(result); +} + +llvm::Error ProcessElfCore::parseFreeBSDNotes(llvm::ArrayRef<CoreNote> notes) { + ArchSpec arch = GetArchitecture(); + bool lp64 = (arch.GetMachine() == llvm::Triple::aarch64 || + arch.GetMachine() == llvm::Triple::mips64 || + arch.GetMachine() == llvm::Triple::ppc64 || + arch.GetMachine() == llvm::Triple::x86_64); + bool have_prstatus = false; + bool have_prpsinfo = false; + ThreadData thread_data; + for (const auto ¬e : notes) { + if (note.info.n_name != "FreeBSD") + continue; + + if ((note.info.n_type == ELF::NT_PRSTATUS && have_prstatus) || + (note.info.n_type == ELF::NT_PRPSINFO && have_prpsinfo)) { + assert(thread_data.gpregset.GetByteSize() > 0); + // Add the new thread to thread list + m_thread_data.push_back(thread_data); + thread_data = ThreadData(); + have_prstatus = false; + have_prpsinfo = false; + } + + switch (note.info.n_type) { + case ELF::NT_PRSTATUS: + have_prstatus = true; + ParseFreeBSDPrStatus(thread_data, note.data, lp64); + break; + case ELF::NT_PRPSINFO: + have_prpsinfo = true; + ParseFreeBSDPrPsInfo(*this, note.data, lp64); + break; + case ELF::NT_FREEBSD_THRMISC: { + lldb::offset_t offset = 0; + thread_data.name = note.data.GetCStr(&offset, 20); + break; + } + case ELF::NT_FREEBSD_PROCSTAT_AUXV: + // FIXME: FreeBSD sticks an int at the beginning of the note + m_auxv = DataExtractor(note.data, 4, note.data.GetByteSize() - 4); + break; + default: + thread_data.notes.push_back(note); + break; + } + } + if (!have_prstatus) { + return llvm::make_error<llvm::StringError>( + "Could not find NT_PRSTATUS note in core file.", + llvm::inconvertibleErrorCode()); + } + m_thread_data.push_back(thread_data); + return llvm::Error::success(); +} + +/// NetBSD specific Thread context from PT_NOTE segment +/// +/// NetBSD ELF core files use notes to provide information about +/// the process's state. The note name is "NetBSD-CORE" for +/// information that is global to the process, and "NetBSD-CORE@nn", +/// where "nn" is the lwpid of the LWP that the information belongs +/// to (such as register state). +/// +/// NetBSD uses the following note identifiers: +/// +/// ELF_NOTE_NETBSD_CORE_PROCINFO (value 1) +/// Note is a "netbsd_elfcore_procinfo" structure. +/// ELF_NOTE_NETBSD_CORE_AUXV (value 2; since NetBSD 8.0) +/// Note is an array of AuxInfo structures. +/// +/// NetBSD also uses ptrace(2) request numbers (the ones that exist in +/// machine-dependent space) to identify register info notes. The +/// info in such notes is in the same format that ptrace(2) would +/// export that information. +/// +/// For more information see /usr/include/sys/exec_elf.h +/// +llvm::Error ProcessElfCore::parseNetBSDNotes(llvm::ArrayRef<CoreNote> notes) { + ThreadData thread_data; + bool had_nt_regs = false; + + // To be extracted from struct netbsd_elfcore_procinfo + // Used to sanity check of the LWPs of the process + uint32_t nlwps = 0; + uint32_t signo = 0; // killing signal + uint32_t siglwp = 0; // LWP target of killing signal + uint32_t pr_pid = 0; + + for (const auto ¬e : notes) { + llvm::StringRef name = note.info.n_name; + + if (name == "NetBSD-CORE") { + if (note.info.n_type == NETBSD::NT_PROCINFO) { + llvm::Error error = ParseNetBSDProcInfo(note.data, nlwps, signo, + siglwp, pr_pid); + if (error) + return error; + SetID(pr_pid); + } else if (note.info.n_type == NETBSD::NT_AUXV) { + m_auxv = note.data; + } + } else if (name.consume_front("NetBSD-CORE@")) { + lldb::tid_t tid; + if (name.getAsInteger(10, tid)) + return llvm::make_error<llvm::StringError>( + "Error parsing NetBSD core(5) notes: Cannot convert LWP ID " + "to integer", + llvm::inconvertibleErrorCode()); + + switch (GetArchitecture().GetMachine()) { + case llvm::Triple::aarch64: { + // Assume order PT_GETREGS, PT_GETFPREGS + if (note.info.n_type == NETBSD::AARCH64::NT_REGS) { + // If this is the next thread, push the previous one first. + if (had_nt_regs) { + m_thread_data.push_back(thread_data); + thread_data = ThreadData(); + had_nt_regs = false; + } + + thread_data.gpregset = note.data; + thread_data.tid = tid; + if (thread_data.gpregset.GetByteSize() == 0) + return llvm::make_error<llvm::StringError>( + "Could not find general purpose registers note in core file.", + llvm::inconvertibleErrorCode()); + had_nt_regs = true; + } else if (note.info.n_type == NETBSD::AARCH64::NT_FPREGS) { + if (!had_nt_regs || tid != thread_data.tid) + return llvm::make_error<llvm::StringError>( + "Error parsing NetBSD core(5) notes: Unexpected order " + "of NOTEs PT_GETFPREG before PT_GETREG", + llvm::inconvertibleErrorCode()); + thread_data.notes.push_back(note); + } + } break; + case llvm::Triple::x86: { + // Assume order PT_GETREGS, PT_GETFPREGS + if (note.info.n_type == NETBSD::I386::NT_REGS) { + // If this is the next thread, push the previous one first. + if (had_nt_regs) { + m_thread_data.push_back(thread_data); + thread_data = ThreadData(); + had_nt_regs = false; + } + + thread_data.gpregset = note.data; + thread_data.tid = tid; + if (thread_data.gpregset.GetByteSize() == 0) + return llvm::make_error<llvm::StringError>( + "Could not find general purpose registers note in core file.", + llvm::inconvertibleErrorCode()); + had_nt_regs = true; + } else if (note.info.n_type == NETBSD::I386::NT_FPREGS) { + if (!had_nt_regs || tid != thread_data.tid) + return llvm::make_error<llvm::StringError>( + "Error parsing NetBSD core(5) notes: Unexpected order " + "of NOTEs PT_GETFPREG before PT_GETREG", + llvm::inconvertibleErrorCode()); + thread_data.notes.push_back(note); + } + } break; + case llvm::Triple::x86_64: { + // Assume order PT_GETREGS, PT_GETFPREGS + if (note.info.n_type == NETBSD::AMD64::NT_REGS) { + // If this is the next thread, push the previous one first. + if (had_nt_regs) { + m_thread_data.push_back(thread_data); + thread_data = ThreadData(); + had_nt_regs = false; + } + + thread_data.gpregset = note.data; + thread_data.tid = tid; + if (thread_data.gpregset.GetByteSize() == 0) + return llvm::make_error<llvm::StringError>( + "Could not find general purpose registers note in core file.", + llvm::inconvertibleErrorCode()); + had_nt_regs = true; + } else if (note.info.n_type == NETBSD::AMD64::NT_FPREGS) { + if (!had_nt_regs || tid != thread_data.tid) + return llvm::make_error<llvm::StringError>( + "Error parsing NetBSD core(5) notes: Unexpected order " + "of NOTEs PT_GETFPREG before PT_GETREG", + llvm::inconvertibleErrorCode()); + thread_data.notes.push_back(note); + } + } break; + default: + break; + } + } + } + + // Push the last thread. + if (had_nt_regs) + m_thread_data.push_back(thread_data); + + if (m_thread_data.empty()) + return llvm::make_error<llvm::StringError>( + "Error parsing NetBSD core(5) notes: No threads information " + "specified in notes", + llvm::inconvertibleErrorCode()); + + if (m_thread_data.size() != nlwps) + return llvm::make_error<llvm::StringError>( + "Error parsing NetBSD core(5) notes: Mismatch between the number " + "of LWPs in netbsd_elfcore_procinfo and the number of LWPs specified " + "by MD notes", + llvm::inconvertibleErrorCode()); + + // Signal targeted at the whole process. + if (siglwp == 0) { + for (auto &data : m_thread_data) + data.signo = signo; + } + // Signal destined for a particular LWP. + else { + bool passed = false; + + for (auto &data : m_thread_data) { + if (data.tid == siglwp) { + data.signo = signo; + passed = true; + break; + } + } + + if (!passed) + return llvm::make_error<llvm::StringError>( + "Error parsing NetBSD core(5) notes: Signal passed to unknown LWP", + llvm::inconvertibleErrorCode()); + } + + return llvm::Error::success(); +} + +llvm::Error ProcessElfCore::parseOpenBSDNotes(llvm::ArrayRef<CoreNote> notes) { + ThreadData thread_data = {}; + for (const auto ¬e : notes) { + // OpenBSD per-thread information is stored in notes named "OpenBSD@nnn" so + // match on the initial part of the string. + if (!llvm::StringRef(note.info.n_name).starts_with("OpenBSD")) + continue; + + switch (note.info.n_type) { + case OPENBSD::NT_PROCINFO: + ParseOpenBSDProcInfo(thread_data, note.data); + break; + case OPENBSD::NT_AUXV: + m_auxv = note.data; + break; + case OPENBSD::NT_REGS: + thread_data.gpregset = note.data; + break; + default: + thread_data.notes.push_back(note); + break; + } + } + if (thread_data.gpregset.GetByteSize() == 0) { + return llvm::make_error<llvm::StringError>( + "Could not find general purpose registers note in core file.", + llvm::inconvertibleErrorCode()); + } + m_thread_data.push_back(thread_data); + return llvm::Error::success(); +} + +/// A description of a linux process usually contains the following NOTE +/// entries: +/// - NT_PRPSINFO - General process information like pid, uid, name, ... +/// - NT_SIGINFO - Information about the signal that terminated the process +/// - NT_AUXV - Process auxiliary vector +/// - NT_FILE - Files mapped into memory +/// +/// Additionally, for each thread in the process the core file will contain at +/// least the NT_PRSTATUS note, containing the thread id and general purpose +/// registers. It may include additional notes for other register sets (floating +/// point and vector registers, ...). The tricky part here is that some of these +/// notes have "CORE" in their owner fields, while other set it to "LINUX". +llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) { + const ArchSpec &arch = GetArchitecture(); + bool have_prstatus = false; + bool have_prpsinfo = false; + ThreadData thread_data; + for (const auto ¬e : notes) { + if (note.info.n_name != "CORE" && note.info.n_name != "LINUX") + continue; + + if ((note.info.n_type == ELF::NT_PRSTATUS && have_prstatus) || + (note.info.n_type == ELF::NT_PRPSINFO && have_prpsinfo)) { + assert(thread_data.gpregset.GetByteSize() > 0); + // Add the new thread to thread list + m_thread_data.push_back(thread_data); + thread_data = ThreadData(); + have_prstatus = false; + have_prpsinfo = false; + } + + switch (note.info.n_type) { + case ELF::NT_PRSTATUS: { + have_prstatus = true; + ELFLinuxPrStatus prstatus; + Status status = prstatus.Parse(note.data, arch); + if (status.Fail()) + return status.ToError(); + thread_data.prstatus_sig = prstatus.pr_cursig; + thread_data.tid = prstatus.pr_pid; + uint32_t header_size = ELFLinuxPrStatus::GetSize(arch); + size_t len = note.data.GetByteSize() - header_size; + thread_data.gpregset = DataExtractor(note.data, header_size, len); + break; + } + case ELF::NT_PRPSINFO: { + have_prpsinfo = true; + ELFLinuxPrPsInfo prpsinfo; + Status status = prpsinfo.Parse(note.data, arch); + if (status.Fail()) + return status.ToError(); + thread_data.name.assign (prpsinfo.pr_fname, strnlen (prpsinfo.pr_fname, sizeof (prpsinfo.pr_fname))); + SetID(prpsinfo.pr_pid); + break; + } + case ELF::NT_SIGINFO: { + ELFLinuxSigInfo siginfo; + Status status = siginfo.Parse(note.data, arch); + if (status.Fail()) + return status.ToError(); + thread_data.signo = siginfo.si_signo; + thread_data.code = siginfo.si_code; + break; + } + case ELF::NT_FILE: { + m_nt_file_entries.clear(); + lldb::offset_t offset = 0; + const uint64_t count = note.data.GetAddress(&offset); + note.data.GetAddress(&offset); // Skip page size + for (uint64_t i = 0; i < count; ++i) { + NT_FILE_Entry entry; + entry.start = note.data.GetAddress(&offset); + entry.end = note.data.GetAddress(&offset); + entry.file_ofs = note.data.GetAddress(&offset); + m_nt_file_entries.push_back(entry); + } + for (uint64_t i = 0; i < count; ++i) { + const char *path = note.data.GetCStr(&offset); + if (path && path[0]) + m_nt_file_entries[i].path.assign(path); + } + break; + } + case ELF::NT_AUXV: + m_auxv = note.data; + break; + default: + thread_data.notes.push_back(note); + break; + } + } + // Add last entry in the note section + if (have_prstatus) + m_thread_data.push_back(thread_data); + return llvm::Error::success(); +} + +/// Parse Thread context from PT_NOTE segment and store it in the thread list +/// A note segment consists of one or more NOTE entries, but their types and +/// meaning differ depending on the OS. +llvm::Error ProcessElfCore::ParseThreadContextsFromNoteSegment( + const elf::ELFProgramHeader &segment_header, + const DataExtractor &segment_data) { + assert(segment_header.p_type == llvm::ELF::PT_NOTE); + + auto notes_or_error = parseSegment(segment_data); + if(!notes_or_error) + return notes_or_error.takeError(); + switch (GetArchitecture().GetTriple().getOS()) { + case llvm::Triple::FreeBSD: + return parseFreeBSDNotes(*notes_or_error); + case llvm::Triple::Linux: + return parseLinuxNotes(*notes_or_error); + case llvm::Triple::NetBSD: + return parseNetBSDNotes(*notes_or_error); + case llvm::Triple::OpenBSD: + return parseOpenBSDNotes(*notes_or_error); + default: + return llvm::make_error<llvm::StringError>( + "Don't know how to parse core file. Unsupported OS.", + llvm::inconvertibleErrorCode()); + } +} + +UUID ProcessElfCore::FindBuidIdInCoreMemory(lldb::addr_t address) { + UUID invalid_uuid; + const uint32_t addr_size = GetAddressByteSize(); + const size_t elf_header_size = addr_size == 4 ? sizeof(llvm::ELF::Elf32_Ehdr) + : sizeof(llvm::ELF::Elf64_Ehdr); + + std::vector<uint8_t> elf_header_bytes; + elf_header_bytes.resize(elf_header_size); + Status error; + size_t byte_read = + ReadMemory(address, elf_header_bytes.data(), elf_header_size, error); + if (byte_read != elf_header_size || + !elf::ELFHeader::MagicBytesMatch(elf_header_bytes.data())) + return invalid_uuid; + DataExtractor elf_header_data(elf_header_bytes.data(), elf_header_size, + GetByteOrder(), addr_size); + lldb::offset_t offset = 0; + + elf::ELFHeader elf_header; + elf_header.Parse(elf_header_data, &offset); + + const lldb::addr_t ph_addr = address + elf_header.e_phoff; + + std::vector<uint8_t> ph_bytes; + ph_bytes.resize(elf_header.e_phentsize); + for (unsigned int i = 0; i < elf_header.e_phnum; ++i) { + byte_read = ReadMemory(ph_addr + i * elf_header.e_phentsize, + ph_bytes.data(), elf_header.e_phentsize, error); + if (byte_read != elf_header.e_phentsize) + break; + DataExtractor program_header_data(ph_bytes.data(), elf_header.e_phentsize, + GetByteOrder(), addr_size); + offset = 0; + elf::ELFProgramHeader program_header; + program_header.Parse(program_header_data, &offset); + if (program_header.p_type != llvm::ELF::PT_NOTE) + continue; + + std::vector<uint8_t> note_bytes; + note_bytes.resize(program_header.p_memsz); + + byte_read = ReadMemory(program_header.p_vaddr, note_bytes.data(), + program_header.p_memsz, error); + if (byte_read != program_header.p_memsz) + continue; + DataExtractor segment_data(note_bytes.data(), note_bytes.size(), + GetByteOrder(), addr_size); + auto notes_or_error = parseSegment(segment_data); + if (!notes_or_error) + return invalid_uuid; + for (const CoreNote ¬e : *notes_or_error) { + if (note.info.n_namesz == 4 && + note.info.n_type == llvm::ELF::NT_GNU_BUILD_ID && + "GNU" == note.info.n_name && + note.data.ValidOffsetForDataOfSize(0, note.info.n_descsz)) + return UUID(note.data.GetData().take_front(note.info.n_descsz)); + } + } + return invalid_uuid; +} + +uint32_t ProcessElfCore::GetNumThreadContexts() { + if (!m_thread_data_valid) + DoLoadCore(); + return m_thread_data.size(); +} + +ArchSpec ProcessElfCore::GetArchitecture() { + ArchSpec arch = m_core_module_sp->GetObjectFile()->GetArchitecture(); + + ArchSpec target_arch = GetTarget().GetArchitecture(); + arch.MergeFrom(target_arch); + + // On MIPS there is no way to differentiate betwenn 32bit and 64bit core + // files and this information can't be merged in from the target arch so we + // fail back to unconditionally returning the target arch in this config. + if (target_arch.IsMIPS()) { + return target_arch; + } + + return arch; +} + +DataExtractor ProcessElfCore::GetAuxvData() { + const uint8_t *start = m_auxv.GetDataStart(); + size_t len = m_auxv.GetByteSize(); + lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(start, len)); + return DataExtractor(buffer, GetByteOrder(), GetAddressByteSize()); +} + +bool ProcessElfCore::GetProcessInfo(ProcessInstanceInfo &info) { + info.Clear(); + info.SetProcessID(GetID()); + info.SetArchitecture(GetArchitecture()); + lldb::ModuleSP module_sp = GetTarget().GetExecutableModule(); + if (module_sp) { + const bool add_exe_file_as_first_arg = false; + info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(), + add_exe_file_as_first_arg); + } + return true; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h new file mode 100644 index 000000000000..668a7c484674 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h @@ -0,0 +1,187 @@ +//===-- ProcessElfCore.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 +// +// Notes about Linux Process core dumps: +// 1) Linux core dump is stored as ELF file. +// 2) The ELF file's PT_NOTE and PT_LOAD segments describes the program's +// address space and thread contexts. +// 3) PT_NOTE segment contains note entries which describes a thread context. +// 4) PT_LOAD segment describes a valid contiguous range of process address +// space. +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_PROCESSELFCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_PROCESSELFCORE_H + +#include <list> +#include <vector> + +#include "lldb/Target/PostMortemProcess.h" +#include "lldb/Utility/Status.h" + +#include "Plugins/ObjectFile/ELF/ELFHeader.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +struct ThreadData; + +class ProcessElfCore : public lldb_private::PostMortemProcess { +public: + // Constructors and Destructors + static lldb::ProcessSP + CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const lldb_private::FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "elf-core"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + // Constructors and Destructors + ProcessElfCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const lldb_private::FileSpec &core_file); + + ~ProcessElfCore() override; + + // Check if a given Process + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + // Creating a new process, or attaching to an existing one + lldb_private::Status DoLoadCore() override; + + lldb_private::DynamicLoader *GetDynamicLoader() override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // Process Control + lldb_private::Status DoDestroy() override; + + void RefreshStateAfterStop() override; + + lldb_private::Status WillResume() override { + lldb_private::Status error; + error.SetErrorStringWithFormatv( + "error: {0} does not support resuming processes", GetPluginName()); + return error; + } + + // Process Queries + bool IsAlive() override; + + bool WarnBeforeDetach() const override { return false; } + + // Process Memory + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; + + // We do not implement DoReadMemoryTags. Instead all the work is done in + // ReadMemoryTags which avoids having to unpack and repack tags. + llvm::Expected<std::vector<lldb::addr_t>> ReadMemoryTags(lldb::addr_t addr, + size_t len) override; + + lldb::addr_t GetImageInfoAddress() override; + + lldb_private::ArchSpec GetArchitecture(); + + // Returns AUXV structure found in the core file + lldb_private::DataExtractor GetAuxvData() override; + + bool GetProcessInfo(lldb_private::ProcessInstanceInfo &info) override; + +protected: + void Clear(); + + bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) override; + + lldb_private::Status + DoGetMemoryRegionInfo(lldb::addr_t load_addr, + lldb_private::MemoryRegionInfo ®ion_info) override; + + bool SupportsMemoryTagging() override { return !m_core_tag_ranges.IsEmpty(); } + +private: + struct NT_FILE_Entry { + lldb::addr_t start; + lldb::addr_t end; + lldb::addr_t file_ofs; + std::string path; + // Add a UUID member for convenient access. The UUID value is not in the + // NT_FILE entries, we will find it in core memory and store it here for + // easy access. + lldb_private::UUID uuid; + }; + + // For ProcessElfCore only + typedef lldb_private::Range<lldb::addr_t, lldb::addr_t> FileRange; + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, FileRange> + VMRangeToFileOffset; + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, uint32_t> + VMRangeToPermissions; + + lldb::ModuleSP m_core_module_sp; + std::string m_dyld_plugin_name; + + // True if m_thread_contexts contains valid entries + bool m_thread_data_valid = false; + + // Contain thread data read from NOTE segments + std::vector<ThreadData> m_thread_data; + + // AUXV structure found from the NOTE segment + lldb_private::DataExtractor m_auxv; + + // Address ranges found in the core + VMRangeToFileOffset m_core_aranges; + + // Permissions for all ranges + VMRangeToPermissions m_core_range_infos; + + // Memory tag ranges found in the core + VMRangeToFileOffset m_core_tag_ranges; + + // NT_FILE entries found from the NOTE segment + std::vector<NT_FILE_Entry> m_nt_file_entries; + + // Parse thread(s) data structures(prstatus, prpsinfo) from given NOTE segment + llvm::Error ParseThreadContextsFromNoteSegment( + const elf::ELFProgramHeader &segment_header, + const lldb_private::DataExtractor &segment_data); + + // Returns number of thread contexts stored in the core file + uint32_t GetNumThreadContexts(); + + // Populate gnu uuid for each NT_FILE entry + void UpdateBuildIdForNTFileEntries(); + + // Returns the value of certain type of note of a given start address + lldb_private::UUID FindBuidIdInCoreMemory(lldb::addr_t address); + + // Parse a contiguous address range of the process from LOAD segment + lldb::addr_t + AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader &header); + + // Parse a contiguous address range from a memory tag segment + lldb::addr_t + AddAddressRangeFromMemoryTagSegment(const elf::ELFProgramHeader &header); + + llvm::Expected<std::vector<lldb_private::CoreNote>> + parseSegment(const lldb_private::DataExtractor &segment); + llvm::Error parseFreeBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes); + llvm::Error parseNetBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes); + llvm::Error parseOpenBSDNotes(llvm::ArrayRef<lldb_private::CoreNote> notes); + llvm::Error parseLinuxNotes(llvm::ArrayRef<lldb_private::CoreNote> notes); +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_PROCESSELFCORE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextLinuxCore_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextLinuxCore_x86_64.cpp new file mode 100644 index 000000000000..b806292c7a1a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextLinuxCore_x86_64.cpp @@ -0,0 +1,237 @@ +//===-- RegisterContextLinuxCore_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextLinuxCore_x86_64.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/RegisterValue.h" + +using namespace lldb_private; + +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 must be terminated with + // LLDB_INVALID_REGNUM. +}; +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"); + +const uint32_t g_lldb_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 must be terminated with + // LLDB_INVALID_REGNUM. +}; +static_assert((sizeof(g_lldb_regnums_i386) / sizeof(g_lldb_regnums_i386[0])) - + 1 == + k_num_fpr_registers_i386, + "g_lldb_regnums_i386 has wrong number of register infos"); + +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 must be terminated with + // LLDB_INVALID_REGNUM. +}; +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_gpr_regnums_x86_64[] = { + x86_64_with_base::lldb_rax, + x86_64_with_base::lldb_rbx, + x86_64_with_base::lldb_rcx, + x86_64_with_base::lldb_rdx, + x86_64_with_base::lldb_rdi, + x86_64_with_base::lldb_rsi, + x86_64_with_base::lldb_rbp, + x86_64_with_base::lldb_rsp, + x86_64_with_base::lldb_r8, + x86_64_with_base::lldb_r9, + x86_64_with_base::lldb_r10, + x86_64_with_base::lldb_r11, + x86_64_with_base::lldb_r12, + x86_64_with_base::lldb_r13, + x86_64_with_base::lldb_r14, + x86_64_with_base::lldb_r15, + x86_64_with_base::lldb_rip, + x86_64_with_base::lldb_rflags, + x86_64_with_base::lldb_cs, + x86_64_with_base::lldb_fs, + x86_64_with_base::lldb_gs, + x86_64_with_base::lldb_ss, + x86_64_with_base::lldb_fs_base, + x86_64_with_base::lldb_gs_base, + x86_64_with_base::lldb_ds, + x86_64_with_base::lldb_es, + x86_64_with_base::lldb_eax, + x86_64_with_base::lldb_ebx, + x86_64_with_base::lldb_ecx, + x86_64_with_base::lldb_edx, + x86_64_with_base::lldb_edi, + x86_64_with_base::lldb_esi, + x86_64_with_base::lldb_ebp, + x86_64_with_base::lldb_esp, + x86_64_with_base::lldb_r8d, // Low 32 bits or r8 + x86_64_with_base::lldb_r9d, // Low 32 bits or r9 + x86_64_with_base::lldb_r10d, // Low 32 bits or r10 + x86_64_with_base::lldb_r11d, // Low 32 bits or r11 + x86_64_with_base::lldb_r12d, // Low 32 bits or r12 + x86_64_with_base::lldb_r13d, // Low 32 bits or r13 + x86_64_with_base::lldb_r14d, // Low 32 bits or r14 + x86_64_with_base::lldb_r15d, // Low 32 bits or r15 + x86_64_with_base::lldb_ax, + x86_64_with_base::lldb_bx, + x86_64_with_base::lldb_cx, + x86_64_with_base::lldb_dx, + x86_64_with_base::lldb_di, + x86_64_with_base::lldb_si, + x86_64_with_base::lldb_bp, + x86_64_with_base::lldb_sp, + x86_64_with_base::lldb_r8w, // Low 16 bits or r8 + x86_64_with_base::lldb_r9w, // Low 16 bits or r9 + x86_64_with_base::lldb_r10w, // Low 16 bits or r10 + x86_64_with_base::lldb_r11w, // Low 16 bits or r11 + x86_64_with_base::lldb_r12w, // Low 16 bits or r12 + x86_64_with_base::lldb_r13w, // Low 16 bits or r13 + x86_64_with_base::lldb_r14w, // Low 16 bits or r14 + x86_64_with_base::lldb_r15w, // Low 16 bits or r15 + x86_64_with_base::lldb_ah, + x86_64_with_base::lldb_bh, + x86_64_with_base::lldb_ch, + x86_64_with_base::lldb_dh, + x86_64_with_base::lldb_al, + x86_64_with_base::lldb_bl, + x86_64_with_base::lldb_cl, + x86_64_with_base::lldb_dl, + x86_64_with_base::lldb_dil, + x86_64_with_base::lldb_sil, + x86_64_with_base::lldb_bpl, + x86_64_with_base::lldb_spl, + x86_64_with_base::lldb_r8l, // Low 8 bits or r8 + x86_64_with_base::lldb_r9l, // Low 8 bits or r9 + x86_64_with_base::lldb_r10l, // Low 8 bits or r10 + x86_64_with_base::lldb_r11l, // Low 8 bits or r11 + x86_64_with_base::lldb_r12l, // Low 8 bits or r12 + x86_64_with_base::lldb_r13l, // Low 8 bits or r13 + x86_64_with_base::lldb_r14l, // Low 8 bits or r14 + x86_64_with_base::lldb_r15l, // Low 8 bits or r15 + LLDB_INVALID_REGNUM // Register sets must be terminated with + // LLDB_INVALID_REGNUM. +}; +static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) - + 1 == + x86_64_with_base::k_num_gpr_registers, + "g_gpr_regnums_x86_64 has wrong number of register infos"); + +static const uint32_t g_lldb_regnums_x86_64[] = { + x86_64_with_base::lldb_fctrl, x86_64_with_base::lldb_fstat, + x86_64_with_base::lldb_ftag, x86_64_with_base::lldb_fop, + x86_64_with_base::lldb_fiseg, x86_64_with_base::lldb_fioff, + x86_64_with_base::lldb_fip, x86_64_with_base::lldb_foseg, + x86_64_with_base::lldb_fooff, x86_64_with_base::lldb_fdp, + x86_64_with_base::lldb_mxcsr, x86_64_with_base::lldb_mxcsrmask, + x86_64_with_base::lldb_st0, x86_64_with_base::lldb_st1, + x86_64_with_base::lldb_st2, x86_64_with_base::lldb_st3, + x86_64_with_base::lldb_st4, x86_64_with_base::lldb_st5, + x86_64_with_base::lldb_st6, x86_64_with_base::lldb_st7, + x86_64_with_base::lldb_mm0, x86_64_with_base::lldb_mm1, + x86_64_with_base::lldb_mm2, x86_64_with_base::lldb_mm3, + x86_64_with_base::lldb_mm4, x86_64_with_base::lldb_mm5, + x86_64_with_base::lldb_mm6, x86_64_with_base::lldb_mm7, + x86_64_with_base::lldb_xmm0, x86_64_with_base::lldb_xmm1, + x86_64_with_base::lldb_xmm2, x86_64_with_base::lldb_xmm3, + x86_64_with_base::lldb_xmm4, x86_64_with_base::lldb_xmm5, + x86_64_with_base::lldb_xmm6, x86_64_with_base::lldb_xmm7, + x86_64_with_base::lldb_xmm8, x86_64_with_base::lldb_xmm9, + x86_64_with_base::lldb_xmm10, x86_64_with_base::lldb_xmm11, + x86_64_with_base::lldb_xmm12, x86_64_with_base::lldb_xmm13, + x86_64_with_base::lldb_xmm14, x86_64_with_base::lldb_xmm15, + LLDB_INVALID_REGNUM // Register sets must be terminated with + // LLDB_INVALID_REGNUM. +}; +static_assert( + (sizeof(g_lldb_regnums_x86_64) / sizeof(g_lldb_regnums_x86_64[0])) - 1 == + x86_64_with_base::k_num_fpr_registers, + "g_lldb_regnums_x86_64 has wrong number of register infos"); + +static const uint32_t g_avx_regnums_x86_64[] = { + x86_64_with_base::lldb_ymm0, x86_64_with_base::lldb_ymm1, + x86_64_with_base::lldb_ymm2, x86_64_with_base::lldb_ymm3, + x86_64_with_base::lldb_ymm4, x86_64_with_base::lldb_ymm5, + x86_64_with_base::lldb_ymm6, x86_64_with_base::lldb_ymm7, + x86_64_with_base::lldb_ymm8, x86_64_with_base::lldb_ymm9, + x86_64_with_base::lldb_ymm10, x86_64_with_base::lldb_ymm11, + x86_64_with_base::lldb_ymm12, x86_64_with_base::lldb_ymm13, + x86_64_with_base::lldb_ymm14, x86_64_with_base::lldb_ymm15, + LLDB_INVALID_REGNUM // Register sets must be terminated with + // LLDB_INVALID_REGNUM. +}; +static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) - + 1 == + x86_64_with_base::k_num_avx_registers, + "g_avx_regnums_x86_64 has wrong number of register infos"); + +static const RegisterSet g_reg_sets_i386[] = { + {"General Purpose Registers", "gpr", k_num_gpr_registers_i386, + g_gpr_regnums_i386}, + {"Floating Point Registers", "fpu", k_num_fpr_registers_i386, + g_lldb_regnums_i386}, + {"Advanced Vector Extensions", "avx", k_num_avx_registers_i386, + g_avx_regnums_i386}}; + +static const RegisterSet g_reg_sets_x86_64[] = { + {"General Purpose Registers", "gpr", x86_64_with_base::k_num_gpr_registers, + g_gpr_regnums_x86_64}, + {"Floating Point Registers", "fpu", x86_64_with_base::k_num_fpr_registers, + g_lldb_regnums_x86_64}, + {"Advanced Vector Extensions", "avx", x86_64_with_base::k_num_avx_registers, + g_avx_regnums_x86_64}}; + +RegisterContextLinuxCore_x86_64::RegisterContextLinuxCore_x86_64( + Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) + : RegisterContextCorePOSIX_x86_64(thread, register_info, gpregset, notes) {} + +const RegisterSet *RegisterContextLinuxCore_x86_64::GetRegisterSet(size_t set) { + if (IsRegisterSetAvailable(set)) { + switch (m_register_info_up->GetTargetArchitecture().GetMachine()) { + case llvm::Triple::x86: + return &g_reg_sets_i386[set]; + case llvm::Triple::x86_64: + return &g_reg_sets_x86_64[set]; + default: + assert(false && "Unhandled target architecture."); + return nullptr; + } + } + return nullptr; +} + +RegInfo &RegisterContextLinuxCore_x86_64::GetRegInfo() { + return GetRegInfoShared( + m_register_info_up->GetTargetArchitecture().GetMachine(), + /*with_base=*/true); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextLinuxCore_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextLinuxCore_x86_64.h new file mode 100644 index 000000000000..a68ed82d718c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextLinuxCore_x86_64.h @@ -0,0 +1,28 @@ +//===-- RegisterContextLinuxCore_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTLINUXCORE_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTLINUXCORE_X86_64_H + +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "RegisterContextPOSIXCore_x86_64.h" + +class RegisterContextLinuxCore_x86_64 : public RegisterContextCorePOSIX_x86_64 { +public: + RegisterContextLinuxCore_x86_64( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + lldb_private::RegInfo &GetRegInfo() override; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTLINUXCORE_X86_64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp new file mode 100644 index 000000000000..3a62081827c6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp @@ -0,0 +1,74 @@ +//===-- RegisterContextPOSIXCore_arm.cpp ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextPOSIXCore_arm.h" + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" + +#include <memory> + +using namespace lldb_private; + +RegisterContextCorePOSIX_arm::RegisterContextCorePOSIX_arm( + Thread &thread, std::unique_ptr<RegisterInfoPOSIX_arm> register_info, + const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) + : RegisterContextPOSIX_arm(thread, std::move(register_info)) { + m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(), + gpregset.GetByteSize()); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); +} + +RegisterContextCorePOSIX_arm::~RegisterContextCorePOSIX_arm() = default; + +bool RegisterContextCorePOSIX_arm::ReadGPR() { return true; } + +bool RegisterContextCorePOSIX_arm::ReadFPR() { return false; } + +bool RegisterContextCorePOSIX_arm::WriteGPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_arm::WriteFPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_arm::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + lldb::offset_t offset = reg_info->byte_offset; + if (offset + reg_info->byte_size <= GetGPRSize()) { + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) { + value = v; + return true; + } + } + return false; +} + +bool RegisterContextCorePOSIX_arm::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_arm::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + return false; +} + +bool RegisterContextCorePOSIX_arm::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_arm::HardwareSingleStep(bool enable) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h new file mode 100644 index 000000000000..8d773a046bca --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h @@ -0,0 +1,53 @@ +//===-- RegisterContextPOSIXCore_arm.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_ARM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_ARM_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_arm.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" + +class RegisterContextCorePOSIX_arm : public RegisterContextPOSIX_arm { +public: + RegisterContextCorePOSIX_arm( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_arm> register_info, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + ~RegisterContextCorePOSIX_arm() override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + bool HardwareSingleStep(bool enable) override; + +protected: + bool ReadGPR() override; + + bool ReadFPR() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb_private::DataExtractor m_gpr; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_ARM_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp new file mode 100644 index 000000000000..413bf1bbdb2a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp @@ -0,0 +1,396 @@ +//===-- RegisterContextPOSIXCore_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextPOSIXCore_arm64.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" + +#include "Plugins/Process/Utility/AuxVector.h" +#include "Plugins/Process/Utility/RegisterFlagsDetector_arm64.h" +#include "Plugins/Process/elf-core/ProcessElfCore.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" + +#include <memory> + +using namespace lldb_private; + +std::unique_ptr<RegisterContextCorePOSIX_arm64> +RegisterContextCorePOSIX_arm64::Create(Thread &thread, const ArchSpec &arch, + const DataExtractor &gpregset, + llvm::ArrayRef<CoreNote> notes) { + Flags opt_regsets = RegisterInfoPOSIX_arm64::eRegsetMaskDefault; + + DataExtractor ssve_data = + getRegset(notes, arch.GetTriple(), AARCH64_SSVE_Desc); + if (ssve_data.GetByteSize() >= sizeof(sve::user_sve_header)) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSSVE); + + DataExtractor sve_data = getRegset(notes, arch.GetTriple(), AARCH64_SVE_Desc); + if (sve_data.GetByteSize() >= sizeof(sve::user_sve_header)) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSVE); + + // Pointer Authentication register set data is based on struct + // user_pac_mask declared in ptrace.h. See reference implementation + // in Linux kernel source at arch/arm64/include/uapi/asm/ptrace.h. + DataExtractor pac_data = getRegset(notes, arch.GetTriple(), AARCH64_PAC_Desc); + if (pac_data.GetByteSize() >= sizeof(uint64_t) * 2) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskPAuth); + + DataExtractor tls_data = getRegset(notes, arch.GetTriple(), AARCH64_TLS_Desc); + // A valid note will always contain at least one register, "tpidr". It may + // expand in future. + if (tls_data.GetByteSize() >= sizeof(uint64_t)) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS); + + DataExtractor za_data = getRegset(notes, arch.GetTriple(), AARCH64_ZA_Desc); + // Nothing if ZA is not present, just the header if it is disabled. + if (za_data.GetByteSize() >= sizeof(sve::user_za_header)) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskZA); + + DataExtractor mte_data = getRegset(notes, arch.GetTriple(), AARCH64_MTE_Desc); + if (mte_data.GetByteSize() >= sizeof(uint64_t)) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE); + + DataExtractor zt_data = getRegset(notes, arch.GetTriple(), AARCH64_ZT_Desc); + // Although ZT0 can be in a disabled state like ZA can, the kernel reports + // its content as 0s in that state. Therefore even a disabled ZT0 will have + // a note containing those 0s. ZT0 is a 512 bit / 64 byte register. + if (zt_data.GetByteSize() >= 64) + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskZT); + + auto register_info_up = + std::make_unique<RegisterInfoPOSIX_arm64>(arch, opt_regsets); + return std::unique_ptr<RegisterContextCorePOSIX_arm64>( + new RegisterContextCorePOSIX_arm64(thread, std::move(register_info_up), + gpregset, notes)); +} + +RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64( + Thread &thread, std::unique_ptr<RegisterInfoPOSIX_arm64> register_info, + const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) + : RegisterContextPOSIX_arm64(thread, std::move(register_info)) { + ::memset(&m_sme_pseudo_regs, 0, sizeof(m_sme_pseudo_regs)); + + ProcessElfCore *process = + static_cast<ProcessElfCore *>(thread.GetProcess().get()); + llvm::Triple::OSType os = process->GetArchitecture().GetTriple().getOS(); + if ((os == llvm::Triple::Linux) || (os == llvm::Triple::FreeBSD)) { + AuxVector aux_vec(process->GetAuxvData()); + std::optional<uint64_t> auxv_at_hwcap = aux_vec.GetAuxValue( + os == llvm::Triple::FreeBSD ? AuxVector::AUXV_FREEBSD_AT_HWCAP + : AuxVector::AUXV_AT_HWCAP); + std::optional<uint64_t> auxv_at_hwcap2 = + aux_vec.GetAuxValue(AuxVector::AUXV_AT_HWCAP2); + + m_register_flags_detector.DetectFields(auxv_at_hwcap.value_or(0), + auxv_at_hwcap2.value_or(0)); + m_register_flags_detector.UpdateRegisterInfo(GetRegisterInfo(), + GetRegisterCount()); + } + + m_gpr_data.SetData(std::make_shared<DataBufferHeap>(gpregset.GetDataStart(), + gpregset.GetByteSize())); + m_gpr_data.SetByteOrder(gpregset.GetByteOrder()); + + const llvm::Triple &target_triple = + m_register_info_up->GetTargetArchitecture().GetTriple(); + m_fpr_data = getRegset(notes, target_triple, FPR_Desc); + + if (m_register_info_up->IsSSVEPresent()) { + m_sve_data = getRegset(notes, target_triple, AARCH64_SSVE_Desc); + lldb::offset_t flags_offset = 12; + uint16_t flags = m_sve_data.GetU32(&flags_offset); + if ((flags & sve::ptrace_regs_mask) == sve::ptrace_regs_sve) + m_sve_state = SVEState::Streaming; + } + + if (m_sve_state != SVEState::Streaming && m_register_info_up->IsSVEPresent()) + m_sve_data = getRegset(notes, target_triple, AARCH64_SVE_Desc); + + if (m_register_info_up->IsPAuthPresent()) + m_pac_data = getRegset(notes, target_triple, AARCH64_PAC_Desc); + + if (m_register_info_up->IsTLSPresent()) + m_tls_data = getRegset(notes, target_triple, AARCH64_TLS_Desc); + + if (m_register_info_up->IsZAPresent()) + m_za_data = getRegset(notes, target_triple, AARCH64_ZA_Desc); + + if (m_register_info_up->IsMTEPresent()) + m_mte_data = getRegset(notes, target_triple, AARCH64_MTE_Desc); + + if (m_register_info_up->IsZTPresent()) + m_zt_data = getRegset(notes, target_triple, AARCH64_ZT_Desc); + + ConfigureRegisterContext(); +} + +RegisterContextCorePOSIX_arm64::~RegisterContextCorePOSIX_arm64() = default; + +bool RegisterContextCorePOSIX_arm64::ReadGPR() { return true; } + +bool RegisterContextCorePOSIX_arm64::ReadFPR() { return false; } + +bool RegisterContextCorePOSIX_arm64::WriteGPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_arm64::WriteFPR() { + assert(0); + return false; +} + +const uint8_t *RegisterContextCorePOSIX_arm64::GetSVEBuffer(uint64_t offset) { + return m_sve_data.GetDataStart() + offset; +} + +void RegisterContextCorePOSIX_arm64::ConfigureRegisterContext() { + if (m_sve_data.GetByteSize() > sizeof(sve::user_sve_header)) { + uint64_t sve_header_field_offset = 8; + m_sve_vector_length = m_sve_data.GetU16(&sve_header_field_offset); + + if (m_sve_state != SVEState::Streaming) { + sve_header_field_offset = 12; + uint16_t sve_header_flags_field = + m_sve_data.GetU16(&sve_header_field_offset); + if ((sve_header_flags_field & sve::ptrace_regs_mask) == + sve::ptrace_regs_fpsimd) + m_sve_state = SVEState::FPSIMD; + else if ((sve_header_flags_field & sve::ptrace_regs_mask) == + sve::ptrace_regs_sve) + m_sve_state = SVEState::Full; + } + + if (!sve::vl_valid(m_sve_vector_length)) { + m_sve_state = SVEState::Disabled; + m_sve_vector_length = 0; + } + } else + m_sve_state = SVEState::Disabled; + + if (m_sve_state != SVEState::Disabled) + m_register_info_up->ConfigureVectorLengthSVE( + sve::vq_from_vl(m_sve_vector_length)); + + if (m_sve_state == SVEState::Streaming) + m_sme_pseudo_regs.ctrl_reg |= 1; + + if (m_za_data.GetByteSize() >= sizeof(sve::user_za_header)) { + lldb::offset_t vlen_offset = 8; + uint16_t svl = m_za_data.GetU16(&vlen_offset); + m_sme_pseudo_regs.svg_reg = svl / 8; + m_register_info_up->ConfigureVectorLengthZA(svl / 16); + + // If there is register data then ZA is active. The size of the note may be + // misleading here so we use the size field of the embedded header. + lldb::offset_t size_offset = 0; + uint32_t size = m_za_data.GetU32(&size_offset); + if (size > sizeof(sve::user_za_header)) + m_sme_pseudo_regs.ctrl_reg |= 1 << 1; + } +} + +uint32_t RegisterContextCorePOSIX_arm64::CalculateSVEOffset( + const RegisterInfo *reg_info) { + // Start of Z0 data is after GPRs plus 8 bytes of vg register + uint32_t sve_reg_offset = LLDB_INVALID_INDEX32; + if (m_sve_state == SVEState::FPSIMD) { + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + sve_reg_offset = sve::ptrace_fpsimd_offset + (reg - GetRegNumSVEZ0()) * 16; + } else if (m_sve_state == SVEState::Full || + m_sve_state == SVEState::Streaming) { + uint32_t sve_z0_offset = GetGPRSize() + 16; + sve_reg_offset = + sve::SigRegsOffset() + reg_info->byte_offset - sve_z0_offset; + } + + return sve_reg_offset; +} + +bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + Status error; + lldb::offset_t offset; + + offset = reg_info->byte_offset; + if (offset + reg_info->byte_size <= GetGPRSize()) { + value.SetFromMemoryData(*reg_info, m_gpr_data.GetDataStart() + offset, + reg_info->byte_size, lldb::eByteOrderLittle, error); + return error.Success(); + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + return false; + + if (IsFPR(reg)) { + if (m_sve_state == SVEState::Disabled) { + // SVE is disabled take legacy route for FPU register access + offset -= GetGPRSize(); + if (offset < m_fpr_data.GetByteSize()) { + value.SetFromMemoryData(*reg_info, m_fpr_data.GetDataStart() + offset, + reg_info->byte_size, lldb::eByteOrderLittle, + error); + return error.Success(); + } + } else { + // FPSR and FPCR will be located right after Z registers in + // SVEState::FPSIMD while in SVEState::Full/SVEState::Streaming they will + // be located at the end of register data after an alignment correction + // based on currently selected vector length. + uint32_t sve_reg_num = LLDB_INVALID_REGNUM; + if (reg == GetRegNumFPSR()) { + sve_reg_num = reg; + if (m_sve_state == SVEState::Full || m_sve_state == SVEState::Streaming) + offset = sve::PTraceFPSROffset(sve::vq_from_vl(m_sve_vector_length)); + else if (m_sve_state == SVEState::FPSIMD) + offset = sve::ptrace_fpsimd_offset + (32 * 16); + } else if (reg == GetRegNumFPCR()) { + sve_reg_num = reg; + if (m_sve_state == SVEState::Full || m_sve_state == SVEState::Streaming) + offset = sve::PTraceFPCROffset(sve::vq_from_vl(m_sve_vector_length)); + else if (m_sve_state == SVEState::FPSIMD) + offset = sve::ptrace_fpsimd_offset + (32 * 16) + 4; + } else { + // Extract SVE Z register value register number for this reg_info + if (reg_info->value_regs && + reg_info->value_regs[0] != LLDB_INVALID_REGNUM) + sve_reg_num = reg_info->value_regs[0]; + offset = CalculateSVEOffset(GetRegisterInfoAtIndex(sve_reg_num)); + } + + assert(sve_reg_num != LLDB_INVALID_REGNUM); + assert(offset < m_sve_data.GetByteSize()); + value.SetFromMemoryData(*reg_info, GetSVEBuffer(offset), + reg_info->byte_size, lldb::eByteOrderLittle, + error); + } + } else if (IsSVE(reg)) { + if (IsSVEVG(reg)) { + value = GetSVERegVG(); + return true; + } + + switch (m_sve_state) { + case SVEState::FPSIMD: { + // In FPSIMD state SVE payload mirrors legacy fpsimd struct and so just + // copy 16 bytes of v register to the start of z register. All other + // SVE register will be set to zero. + uint64_t byte_size = 1; + uint8_t zeros = 0; + const uint8_t *src = &zeros; + if (IsSVEZ(reg)) { + byte_size = 16; + offset = CalculateSVEOffset(reg_info); + assert(offset < m_sve_data.GetByteSize()); + src = GetSVEBuffer(offset); + } + value.SetFromMemoryData(*reg_info, src, byte_size, lldb::eByteOrderLittle, + error); + } break; + case SVEState::Full: + case SVEState::Streaming: + offset = CalculateSVEOffset(reg_info); + assert(offset < m_sve_data.GetByteSize()); + value.SetFromMemoryData(*reg_info, GetSVEBuffer(offset), + reg_info->byte_size, lldb::eByteOrderLittle, + error); + break; + case SVEState::Disabled: + default: + return false; + } + } else if (IsPAuth(reg)) { + offset = reg_info->byte_offset - m_register_info_up->GetPAuthOffset(); + assert(offset < m_pac_data.GetByteSize()); + value.SetFromMemoryData(*reg_info, m_pac_data.GetDataStart() + offset, + reg_info->byte_size, lldb::eByteOrderLittle, error); + } else if (IsTLS(reg)) { + offset = reg_info->byte_offset - m_register_info_up->GetTLSOffset(); + assert(offset < m_tls_data.GetByteSize()); + value.SetFromMemoryData(*reg_info, m_tls_data.GetDataStart() + offset, + reg_info->byte_size, lldb::eByteOrderLittle, error); + } else if (IsMTE(reg)) { + offset = reg_info->byte_offset - m_register_info_up->GetMTEOffset(); + assert(offset < m_mte_data.GetByteSize()); + value.SetFromMemoryData(*reg_info, m_mte_data.GetDataStart() + offset, + reg_info->byte_size, lldb::eByteOrderLittle, error); + } else if (IsSME(reg)) { + // If you had SME in the process, active or otherwise, there will at least + // be a ZA header. No header, no SME at all. + if (m_za_data.GetByteSize() < sizeof(sve::user_za_header)) + return false; + + if (m_register_info_up->IsSMERegZA(reg)) { + // Don't use the size of the note to tell whether ZA is enabled. There may + // be non-register padding data after the header. Use the embedded + // header's size field instead. + lldb::offset_t size_offset = 0; + uint32_t size = m_za_data.GetU32(&size_offset); + bool za_enabled = size > sizeof(sve::user_za_header); + + size_t za_note_size = m_za_data.GetByteSize(); + // For a disabled ZA we fake a value of all 0s. + if (!za_enabled) { + uint64_t svl = m_sme_pseudo_regs.svg_reg * 8; + za_note_size = sizeof(sve::user_za_header) + (svl * svl); + } + + const uint8_t *src = nullptr; + std::vector<uint8_t> disabled_za_data; + + if (za_enabled) + src = m_za_data.GetDataStart(); + else { + disabled_za_data.resize(za_note_size); + std::fill(disabled_za_data.begin(), disabled_za_data.end(), 0); + src = disabled_za_data.data(); + } + + value.SetFromMemoryData(*reg_info, src + sizeof(sve::user_za_header), + reg_info->byte_size, lldb::eByteOrderLittle, + error); + } else if (m_register_info_up->IsSMERegZT(reg)) { + value.SetFromMemoryData(*reg_info, m_zt_data.GetDataStart(), + reg_info->byte_size, lldb::eByteOrderLittle, + error); + } else { + offset = reg_info->byte_offset - m_register_info_up->GetSMEOffset(); + assert(offset < sizeof(m_sme_pseudo_regs)); + // Host endian since these values are derived instead of being read from a + // core file note. + value.SetFromMemoryData( + *reg_info, reinterpret_cast<uint8_t *>(&m_sme_pseudo_regs) + offset, + reg_info->byte_size, lldb_private::endian::InlHostByteOrder(), error); + } + } else + return false; + + return error.Success(); +} + +bool RegisterContextCorePOSIX_arm64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_arm64::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + return false; +} + +bool RegisterContextCorePOSIX_arm64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_arm64::HardwareSingleStep(bool enable) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h new file mode 100644 index 000000000000..ff94845e58d6 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h @@ -0,0 +1,89 @@ +//===-- RegisterContextPOSIXCore_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_ARM64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_ARM64_H + +#include "Plugins/Process/Utility/LinuxPTraceDefines_arm64sve.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h" +#include "Plugins/Process/Utility/RegisterFlagsDetector_arm64.h" + +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" + +class RegisterContextCorePOSIX_arm64 : public RegisterContextPOSIX_arm64 { +public: + static std::unique_ptr<RegisterContextCorePOSIX_arm64> + Create(lldb_private::Thread &thread, const lldb_private::ArchSpec &arch, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + ~RegisterContextCorePOSIX_arm64() override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + bool HardwareSingleStep(bool enable) override; + +protected: + RegisterContextCorePOSIX_arm64( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_arm64> register_info, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + bool ReadGPR() override; + + bool ReadFPR() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + +private: + lldb_private::DataExtractor m_gpr_data; + lldb_private::DataExtractor m_fpr_data; + lldb_private::DataExtractor m_sve_data; + lldb_private::DataExtractor m_pac_data; + lldb_private::DataExtractor m_tls_data; + lldb_private::DataExtractor m_za_data; + lldb_private::DataExtractor m_mte_data; + lldb_private::DataExtractor m_zt_data; + + SVEState m_sve_state = SVEState::Unknown; + uint16_t m_sve_vector_length = 0; + + // These are pseudo registers derived from the values in SSVE and ZA data. + struct __attribute__((packed)) sme_pseudo_regs { + uint64_t ctrl_reg; + uint64_t svg_reg; + }; + static_assert(sizeof(sme_pseudo_regs) == 16); + + struct sme_pseudo_regs m_sme_pseudo_regs; + + lldb_private::Arm64RegisterFlagsDetector m_register_flags_detector; + + const uint8_t *GetSVEBuffer(uint64_t offset = 0); + + void ConfigureRegisterContext(); + + uint32_t CalculateSVEOffset(const lldb_private::RegisterInfo *reg_info); + + uint64_t GetSVERegVG() { return m_sve_vector_length / 8; } +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_ARM64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp new file mode 100644 index 000000000000..56e68742ead7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp @@ -0,0 +1,91 @@ +//===-- RegisterContextPOSIXCore_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextPOSIXCore_mips64.h" + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" + +#include <memory> + +using namespace lldb_private; + +RegisterContextCorePOSIX_mips64::RegisterContextCorePOSIX_mips64( + Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) + : RegisterContextPOSIX_mips64(thread, 0, register_info) { + m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(), + gpregset.GetByteSize()); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + DataExtractor fpregset = getRegset( + notes, register_info->GetTargetArchitecture().GetTriple(), FPR_Desc); + m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(), + fpregset.GetByteSize()); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); +} + +RegisterContextCorePOSIX_mips64::~RegisterContextCorePOSIX_mips64() = default; + +bool RegisterContextCorePOSIX_mips64::ReadGPR() { return true; } + +bool RegisterContextCorePOSIX_mips64::ReadFPR() { return false; } + +bool RegisterContextCorePOSIX_mips64::WriteGPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_mips64::WriteFPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_mips64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + + lldb::offset_t offset = reg_info->byte_offset; + lldb_private::ArchSpec arch = m_register_info_up->GetTargetArchitecture(); + uint64_t v; + if (IsGPR(reg_info->kinds[lldb::eRegisterKindLLDB])) { + if (reg_info->byte_size == 4 && !(arch.GetMachine() == llvm::Triple::mips64el)) + // In case of 32bit core file, the register data are placed at 4 byte + // offset. + offset = offset / 2; + v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + value = v; + return true; + } else if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) { + offset = offset - sizeof(GPR_linux_mips); + v =m_fpr.GetMaxU64(&offset, reg_info->byte_size); + value = v; + return true; + } + return false; +} + +bool RegisterContextCorePOSIX_mips64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_mips64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} + +bool RegisterContextCorePOSIX_mips64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_mips64::HardwareSingleStep(bool enable) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h new file mode 100644 index 000000000000..529b00215e35 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.h @@ -0,0 +1,55 @@ +//===-- RegisterContextPOSIXCore_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_MIPS64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_MIPS64_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_mips64.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" + +class RegisterContextCorePOSIX_mips64 : public RegisterContextPOSIX_mips64 { +public: + RegisterContextCorePOSIX_mips64( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + ~RegisterContextCorePOSIX_mips64() override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + bool HardwareSingleStep(bool enable) override; + +protected: + bool ReadGPR() override; + + bool ReadFPR() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb::DataBufferSP m_fpr_buffer; + lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_MIPS64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp new file mode 100644 index 000000000000..4e7be91c3895 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp @@ -0,0 +1,111 @@ +//===-- RegisterContextPOSIXCore_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextPOSIXCore_powerpc.h" + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" + +#include <memory> + +using namespace lldb_private; + +RegisterContextCorePOSIX_powerpc::RegisterContextCorePOSIX_powerpc( + Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) + : RegisterContextPOSIX_powerpc(thread, 0, register_info) { + m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(), + gpregset.GetByteSize()); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + ArchSpec arch = register_info->GetTargetArchitecture(); + DataExtractor fpregset = getRegset(notes, arch.GetTriple(), FPR_Desc); + m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(), + fpregset.GetByteSize()); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); + + DataExtractor vregset = getRegset(notes, arch.GetTriple(), PPC_VMX_Desc); + m_vec_buffer = std::make_shared<DataBufferHeap>(vregset.GetDataStart(), + vregset.GetByteSize()); + m_vec.SetData(m_vec_buffer); + m_vec.SetByteOrder(vregset.GetByteOrder()); +} + +RegisterContextCorePOSIX_powerpc::~RegisterContextCorePOSIX_powerpc() = default; + +bool RegisterContextCorePOSIX_powerpc::ReadGPR() { return true; } + +bool RegisterContextCorePOSIX_powerpc::ReadFPR() { return true; } + +bool RegisterContextCorePOSIX_powerpc::ReadVMX() { return true; } + +bool RegisterContextCorePOSIX_powerpc::WriteGPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_powerpc::WriteFPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_powerpc::WriteVMX() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_powerpc::ReadRegister( + const RegisterInfo *reg_info, RegisterValue &value) { + lldb::offset_t offset = reg_info->byte_offset; + if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint64_t v = m_fpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) { + value = v; + return true; + } + } else if (IsVMX(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint32_t v[4]; + offset = m_vec.CopyData(offset, reg_info->byte_size, &v); + if (offset == reg_info->byte_size) { + value.SetBytes(v, reg_info->byte_size, m_vec.GetByteOrder()); + return true; + } + } else { + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) { + if (reg_info->byte_size < sizeof(v)) + value = (uint32_t)v; + else + value = v; + return true; + } + } + return false; +} + +bool RegisterContextCorePOSIX_powerpc::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_powerpc::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} + +bool RegisterContextCorePOSIX_powerpc::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_powerpc::HardwareSingleStep(bool enable) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h new file mode 100644 index 000000000000..5364c5589238 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h @@ -0,0 +1,60 @@ +//===-- RegisterContextPOSIXCore_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_POWERPC_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_POWERPC_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "lldb/Utility/DataExtractor.h" + +class RegisterContextCorePOSIX_powerpc : public RegisterContextPOSIX_powerpc { +public: + RegisterContextCorePOSIX_powerpc( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + ~RegisterContextCorePOSIX_powerpc() override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + bool HardwareSingleStep(bool enable) override; + +protected: + bool ReadGPR() override; + + bool ReadFPR() override; + + bool ReadVMX() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + + bool WriteVMX() override; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb::DataBufferSP m_fpr_buffer; + lldb::DataBufferSP m_vec_buffer; + lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; + lldb_private::DataExtractor m_vec; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_POWERPC_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp new file mode 100644 index 000000000000..d44fb399e18a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.cpp @@ -0,0 +1,133 @@ +//===-- RegisterContextPOSIXCore_ppc64le.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 "RegisterContextPOSIXCore_ppc64le.h" + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" + +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +#include <memory> + +using namespace lldb_private; + +RegisterContextCorePOSIX_ppc64le::RegisterContextCorePOSIX_ppc64le( + Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) + : RegisterContextPOSIX_ppc64le(thread, 0, register_info) { + m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(), + gpregset.GetByteSize()); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + ArchSpec arch = register_info->GetTargetArchitecture(); + DataExtractor fpregset = getRegset(notes, arch.GetTriple(), FPR_Desc); + m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(), + fpregset.GetByteSize()); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); + + DataExtractor vmxregset = getRegset(notes, arch.GetTriple(), PPC_VMX_Desc); + m_vmx_buffer = std::make_shared<DataBufferHeap>(vmxregset.GetDataStart(), + vmxregset.GetByteSize()); + m_vmx.SetData(m_vmx_buffer); + m_vmx.SetByteOrder(vmxregset.GetByteOrder()); + + DataExtractor vsxregset = getRegset(notes, arch.GetTriple(), PPC_VSX_Desc); + m_vsx_buffer = std::make_shared<DataBufferHeap>(vsxregset.GetDataStart(), + vsxregset.GetByteSize()); + m_vsx.SetData(m_vsx_buffer); + m_vsx.SetByteOrder(vsxregset.GetByteOrder()); +} + +size_t RegisterContextCorePOSIX_ppc64le::GetFPRSize() const { + return k_num_fpr_registers_ppc64le * sizeof(uint64_t); +} + +size_t RegisterContextCorePOSIX_ppc64le::GetVMXSize() const { + return (k_num_vmx_registers_ppc64le - 1) * sizeof(uint64_t) * 2 + + sizeof(uint32_t); +} + +size_t RegisterContextCorePOSIX_ppc64le::GetVSXSize() const { + return k_num_vsx_registers_ppc64le * sizeof(uint64_t) * 2; +} + +bool RegisterContextCorePOSIX_ppc64le::ReadRegister( + const RegisterInfo *reg_info, RegisterValue &value) { + lldb::offset_t offset = reg_info->byte_offset; + + if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint64_t v; + offset -= GetGPRSize(); + offset = m_fpr.CopyData(offset, reg_info->byte_size, &v); + + if (offset == reg_info->byte_size) { + value.SetBytes(&v, reg_info->byte_size, m_fpr.GetByteOrder()); + return true; + } + } else if (IsVMX(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint32_t v[4]; + offset -= GetGPRSize() + GetFPRSize(); + offset = m_vmx.CopyData(offset, reg_info->byte_size, &v); + + if (offset == reg_info->byte_size) { + value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder()); + return true; + } + } else if (IsVSX(reg_info->kinds[lldb::eRegisterKindLLDB])) { + uint32_t v[4]; + lldb::offset_t tmp_offset; + offset -= GetGPRSize() + GetFPRSize() + GetVMXSize(); + + if (offset < GetVSXSize() / 2) { + tmp_offset = m_vsx.CopyData(offset / 2, reg_info->byte_size / 2, &v); + + if (tmp_offset != reg_info->byte_size / 2) { + return false; + } + + uint8_t *dst = (uint8_t *)&v + sizeof(uint64_t); + tmp_offset = m_fpr.CopyData(offset / 2, reg_info->byte_size / 2, dst); + + if (tmp_offset != reg_info->byte_size / 2) { + return false; + } + + value.SetBytes(&v, reg_info->byte_size, m_vsx.GetByteOrder()); + return true; + } else { + offset = + m_vmx.CopyData(offset - GetVSXSize() / 2, reg_info->byte_size, &v); + if (offset == reg_info->byte_size) { + value.SetBytes(v, reg_info->byte_size, m_vmx.GetByteOrder()); + return true; + } + } + } else { + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + + if (offset == reg_info->byte_offset + reg_info->byte_size) { + if (reg_info->byte_size < sizeof(v)) + value = (uint32_t)v; + else + value = v; + return true; + } + } + + return false; +} + +bool RegisterContextCorePOSIX_ppc64le::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h new file mode 100644 index 000000000000..8de77a7e25bf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_ppc64le.h @@ -0,0 +1,48 @@ +//===-- RegisterContextPOSIXCore_ppc64le.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_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_PPC64LE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_PPC64LE_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_ppc64le.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "lldb/Utility/DataExtractor.h" + +class RegisterContextCorePOSIX_ppc64le : public RegisterContextPOSIX_ppc64le { +public: + RegisterContextCorePOSIX_ppc64le( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + +protected: + size_t GetFPRSize() const; + + size_t GetVMXSize() const; + + size_t GetVSXSize() const; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb::DataBufferSP m_fpr_buffer; + lldb::DataBufferSP m_vmx_buffer; + lldb::DataBufferSP m_vsx_buffer; + lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; + lldb_private::DataExtractor m_vmx; + lldb_private::DataExtractor m_vsx; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_PPC64LE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_riscv64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_riscv64.cpp new file mode 100644 index 000000000000..5ba18cdb9889 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_riscv64.cpp @@ -0,0 +1,82 @@ +//===-- RegisterContextPOSIXCore_riscv64.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 "RegisterContextPOSIXCore_riscv64.h" + +#include "lldb/Utility/DataBufferHeap.h" + +using namespace lldb_private; + +std::unique_ptr<RegisterContextCorePOSIX_riscv64> +RegisterContextCorePOSIX_riscv64::Create(Thread &thread, const ArchSpec &arch, + const DataExtractor &gpregset, + llvm::ArrayRef<CoreNote> notes) { + return std::unique_ptr<RegisterContextCorePOSIX_riscv64>( + new RegisterContextCorePOSIX_riscv64( + thread, std::make_unique<RegisterInfoPOSIX_riscv64>(arch, Flags()), + gpregset, notes)); +} + +RegisterContextCorePOSIX_riscv64::RegisterContextCorePOSIX_riscv64( + Thread &thread, std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info, + const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) + : RegisterContextPOSIX_riscv64(thread, std::move(register_info)) { + + m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(), + gpregset.GetByteSize()); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + ArchSpec arch = m_register_info_up->GetTargetArchitecture(); + DataExtractor fpregset = getRegset(notes, arch.GetTriple(), FPR_Desc); + m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(), + fpregset.GetByteSize()); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); +} + +RegisterContextCorePOSIX_riscv64::~RegisterContextCorePOSIX_riscv64() = default; + +bool RegisterContextCorePOSIX_riscv64::ReadGPR() { return true; } + +bool RegisterContextCorePOSIX_riscv64::ReadFPR() { return true; } + +bool RegisterContextCorePOSIX_riscv64::WriteGPR() { + assert(false && "Writing registers is not allowed for core dumps"); + return false; +} + +bool RegisterContextCorePOSIX_riscv64::WriteFPR() { + assert(false && "Writing registers is not allowed for core dumps"); + return false; +} + +bool RegisterContextCorePOSIX_riscv64::ReadRegister( + const RegisterInfo *reg_info, RegisterValue &value) { + const uint8_t *src = nullptr; + lldb::offset_t offset = reg_info->byte_offset; + + if (IsGPR(reg_info->kinds[lldb::eRegisterKindLLDB])) { + src = m_gpr.GetDataStart(); + } else if (IsFPR(reg_info->kinds[lldb::eRegisterKindLLDB])) { + src = m_fpr.GetDataStart(); + offset -= GetGPRSize(); + } else { + return false; + } + + Status error; + value.SetFromMemoryData(*reg_info, src + offset, reg_info->byte_size, + lldb::eByteOrderLittle, error); + return error.Success(); +} + +bool RegisterContextCorePOSIX_riscv64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_riscv64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_riscv64.h new file mode 100644 index 000000000000..3cf9531df2c1 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_riscv64.h @@ -0,0 +1,60 @@ +//===-- RegisterContextPOSIXCore_riscv64.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_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_RISCV64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_RISCV64_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_riscv64.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h" + +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/RegisterValue.h" + +#include <memory> + +class RegisterContextCorePOSIX_riscv64 : public RegisterContextPOSIX_riscv64 { +public: + static std::unique_ptr<RegisterContextCorePOSIX_riscv64> + Create(lldb_private::Thread &thread, const lldb_private::ArchSpec &arch, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + ~RegisterContextCorePOSIX_riscv64() override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + +protected: + RegisterContextCorePOSIX_riscv64( + lldb_private::Thread &thread, + std::unique_ptr<RegisterInfoPOSIX_riscv64> register_info, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + bool ReadGPR() override; + + bool ReadFPR() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb::DataBufferSP m_fpr_buffer; + + lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_RISCV64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp new file mode 100644 index 000000000000..69707eeb3f1e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.cpp @@ -0,0 +1,96 @@ +//===-- RegisterContextPOSIXCore_s390x.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 "RegisterContextPOSIXCore_s390x.h" + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" + +#include <memory> + +using namespace lldb_private; + +RegisterContextCorePOSIX_s390x::RegisterContextCorePOSIX_s390x( + Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) + : RegisterContextPOSIX_s390x(thread, 0, register_info) { + m_gpr_buffer = std::make_shared<DataBufferHeap>(gpregset.GetDataStart(), + gpregset.GetByteSize()); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + + DataExtractor fpregset = getRegset( + notes, register_info->GetTargetArchitecture().GetTriple(), FPR_Desc); + m_fpr_buffer = std::make_shared<DataBufferHeap>(fpregset.GetDataStart(), + fpregset.GetByteSize()); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); +} + +RegisterContextCorePOSIX_s390x::~RegisterContextCorePOSIX_s390x() = default; + +bool RegisterContextCorePOSIX_s390x::ReadGPR() { return true; } + +bool RegisterContextCorePOSIX_s390x::ReadFPR() { return true; } + +bool RegisterContextCorePOSIX_s390x::WriteGPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_s390x::WriteFPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_s390x::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) + return false; + + if (IsGPR(reg)) { + lldb::offset_t offset = reg_info->byte_offset; + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) { + value.SetUInt(v, reg_info->byte_size); + return true; + } + } + + if (IsFPR(reg)) { + lldb::offset_t offset = reg_info->byte_offset; + uint64_t v = m_fpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) { + value.SetUInt(v, reg_info->byte_size); + return true; + } + } + + return false; +} + +bool RegisterContextCorePOSIX_s390x::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_s390x::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + return false; +} + +bool RegisterContextCorePOSIX_s390x::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_s390x::HardwareSingleStep(bool enable) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h new file mode 100644 index 000000000000..edb7cbc9462f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_s390x.h @@ -0,0 +1,55 @@ +//===-- RegisterContextPOSIXCore_s390x.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_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_S390X_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_S390X_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_s390x.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "lldb/Utility/DataExtractor.h" + +class RegisterContextCorePOSIX_s390x : public RegisterContextPOSIX_s390x { +public: + RegisterContextCorePOSIX_s390x( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + ~RegisterContextCorePOSIX_s390x() override; + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + bool HardwareSingleStep(bool enable) override; + +protected: + bool ReadGPR() override; + + bool ReadFPR() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb_private::DataExtractor m_gpr; + + lldb::DataBufferSP m_fpr_buffer; + lldb_private::DataExtractor m_fpr; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_S390X_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp new file mode 100644 index 000000000000..845312f4c1ed --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp @@ -0,0 +1,99 @@ +//===-- RegisterContextPOSIXCore_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextPOSIXCore_x86_64.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/RegisterValue.h" + +using namespace lldb_private; + +RegisterContextCorePOSIX_x86_64::RegisterContextCorePOSIX_x86_64( + Thread &thread, RegisterInfoInterface *register_info, + const DataExtractor &gpregset, llvm::ArrayRef<CoreNote> notes) + : RegisterContextPOSIX_x86(thread, 0, register_info) { + size_t size, len; + + size = GetGPRSize(); + m_gpregset.reset(new uint8_t[size]); + len = + gpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_gpregset.get()); + if (len != size) + m_gpregset.reset(); + + DataExtractor fpregset = getRegset( + notes, register_info->GetTargetArchitecture().GetTriple(), FPR_Desc); + size = sizeof(FXSAVE); + m_fpregset.reset(new uint8_t[size]); + len = + fpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_fpregset.get()); + if (len != size) + m_fpregset.reset(); +} + +bool RegisterContextCorePOSIX_x86_64::ReadGPR() { + return m_gpregset != nullptr; +} + +bool RegisterContextCorePOSIX_x86_64::ReadFPR() { + return m_fpregset != nullptr; +} + +bool RegisterContextCorePOSIX_x86_64::WriteGPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_x86_64::WriteFPR() { + assert(0); + return false; +} + +bool RegisterContextCorePOSIX_x86_64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + const uint8_t *src; + size_t offset; + const size_t fxsave_offset = reg_info->byte_offset - GetFXSAVEOffset(); + // make the offset relative to the beginning of the FXSAVE structure because + // this is the data that we have (not the entire UserArea) + + if (m_gpregset && reg_info->byte_offset < GetGPRSize()) { + src = m_gpregset.get(); + offset = reg_info->byte_offset; + } else if (m_fpregset && fxsave_offset < sizeof(FXSAVE)) { + src = m_fpregset.get(); + offset = fxsave_offset; + } else { + return false; + } + + Status error; + value.SetFromMemoryData(*reg_info, src + offset, reg_info->byte_size, + lldb::eByteOrderLittle, error); + + return error.Success(); +} + +bool RegisterContextCorePOSIX_x86_64::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_x86_64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue &value) { + return false; +} + +bool RegisterContextCorePOSIX_x86_64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + return false; +} + +bool RegisterContextCorePOSIX_x86_64::HardwareSingleStep(bool enable) { + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h new file mode 100644 index 000000000000..46416a2381db --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h @@ -0,0 +1,49 @@ +//===-- RegisterContextPOSIXCore_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_X86_64_H + +#include "Plugins/Process/Utility/RegisterContextPOSIX_x86.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +class RegisterContextCorePOSIX_x86_64 : public RegisterContextPOSIX_x86 { +public: + RegisterContextCorePOSIX_x86_64( + lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + llvm::ArrayRef<lldb_private::CoreNote> notes); + + bool ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value) override; + + bool WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + bool HardwareSingleStep(bool enable) override; + +protected: + bool ReadGPR() override; + + bool ReadFPR() override; + + bool WriteGPR() override; + + bool WriteFPR() override; + +private: + std::unique_ptr<uint8_t[]> m_gpregset; + std::unique_ptr<uint8_t[]> m_fpregset; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_X86_64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp new file mode 100644 index 000000000000..7455d78774ee --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.cpp @@ -0,0 +1,39 @@ +//===-- RegisterUtilities.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 "Plugins/Process/elf-core/RegisterUtilities.h" +#include "llvm/ADT/STLExtras.h" +#include <optional> + +using namespace lldb_private; + +static std::optional<uint32_t> +getNoteType(const llvm::Triple &Triple, + llvm::ArrayRef<RegsetDesc> RegsetDescs) { + for (const auto &Entry : RegsetDescs) { + if (Entry.OS != Triple.getOS()) + continue; + if (Entry.Arch != llvm::Triple::UnknownArch && + Entry.Arch != Triple.getArch()) + continue; + return Entry.Note; + } + return std::nullopt; +} + +DataExtractor lldb_private::getRegset(llvm::ArrayRef<CoreNote> Notes, + const llvm::Triple &Triple, + llvm::ArrayRef<RegsetDesc> RegsetDescs) { + auto TypeOr = getNoteType(Triple, RegsetDescs); + if (!TypeOr) + return DataExtractor(); + uint32_t Type = *TypeOr; + auto Iter = llvm::find_if( + Notes, [Type](const CoreNote &Note) { return Note.info.n_type == Type; }); + return Iter == Notes.end() ? DataExtractor() : DataExtractor(Iter->data); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h new file mode 100644 index 000000000000..12aa5f72371c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h @@ -0,0 +1,158 @@ +//===-- RegisterUtilities.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_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERUTILITIES_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERUTILITIES_H + +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/BinaryFormat/ELF.h" + +namespace lldb_private { +/// Core files PT_NOTE segment descriptor types + +namespace NETBSD { +enum { NT_PROCINFO = 1, NT_AUXV = 2 }; + +/* Size in bytes */ +enum { NT_PROCINFO_SIZE = 160 }; + +/* Size in bytes */ +enum { + NT_PROCINFO_CPI_VERSION_SIZE = 4, + NT_PROCINFO_CPI_CPISIZE_SIZE = 4, + NT_PROCINFO_CPI_SIGNO_SIZE = 4, + NT_PROCINFO_CPI_SIGCODE_SIZE = 4, + NT_PROCINFO_CPI_SIGPEND_SIZE = 16, + NT_PROCINFO_CPI_SIGMASK_SIZE = 16, + NT_PROCINFO_CPI_SIGIGNORE_SIZE = 16, + NT_PROCINFO_CPI_SIGCATCH_SIZE = 16, + NT_PROCINFO_CPI_PID_SIZE = 4, + NT_PROCINFO_CPI_PPID_SIZE = 4, + NT_PROCINFO_CPI_PGRP_SIZE = 4, + NT_PROCINFO_CPI_SID_SIZE = 4, + NT_PROCINFO_CPI_RUID_SIZE = 4, + NT_PROCINFO_CPI_EUID_SIZE = 4, + NT_PROCINFO_CPI_SVUID_SIZE = 4, + NT_PROCINFO_CPI_RGID_SIZE = 4, + NT_PROCINFO_CPI_EGID_SIZE = 4, + NT_PROCINFO_CPI_SVGID_SIZE = 4, + NT_PROCINFO_CPI_NLWPS_SIZE = 4, + NT_PROCINFO_CPI_NAME_SIZE = 32, + NT_PROCINFO_CPI_SIGLWP_SIZE = 4, +}; + +namespace AARCH64 { +enum { NT_REGS = 32, NT_FPREGS = 34 }; +} + +namespace AMD64 { +enum { NT_REGS = 33, NT_FPREGS = 35 }; +} + +namespace I386 { +enum { NT_REGS = 33, NT_FPREGS = 35 }; +} + +} // namespace NETBSD + +namespace OPENBSD { +enum { + NT_PROCINFO = 10, + NT_AUXV = 11, + NT_REGS = 20, + NT_FPREGS = 21, +}; +} + +struct CoreNote { + ELFNote info; + DataExtractor data; +}; + +// A structure describing how to find a register set in a core file from a given +// OS. +struct RegsetDesc { + // OS to which this entry applies to. Must not be UnknownOS. + llvm::Triple::OSType OS; + + // Architecture to which this entry applies to. Can be UnknownArch, in which + // case it applies to all architectures of a given OS. + llvm::Triple::ArchType Arch; + + // The note type under which the register set can be found. + uint32_t Note; +}; + +// Returns the register set in Notes which corresponds to the specified Triple +// according to the list of register set descriptions in RegsetDescs. The list +// is scanned linearly, so you can use a more specific entry (e.g. linux-i386) +// to override a more general entry (e.g. general linux), as long as you place +// it earlier in the list. If a register set is not found, it returns an empty +// DataExtractor. +DataExtractor getRegset(llvm::ArrayRef<CoreNote> Notes, + const llvm::Triple &Triple, + llvm::ArrayRef<RegsetDesc> RegsetDescs); + +constexpr RegsetDesc FPR_Desc[] = { + // FreeBSD/i386 core NT_FPREGSET is x87 FSAVE result but the XSAVE dump + // starts with FXSAVE struct, so use that instead if available. + {llvm::Triple::FreeBSD, llvm::Triple::x86, llvm::ELF::NT_X86_XSTATE}, + {llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, llvm::ELF::NT_FPREGSET}, + // In a i386 core file NT_FPREGSET is present, but it's not the result + // of the FXSAVE instruction like in 64 bit files. + // The result from FXSAVE is in NT_PRXFPREG for i386 core files + {llvm::Triple::Linux, llvm::Triple::x86, llvm::ELF::NT_PRXFPREG}, + {llvm::Triple::Linux, llvm::Triple::UnknownArch, llvm::ELF::NT_FPREGSET}, + {llvm::Triple::NetBSD, llvm::Triple::aarch64, NETBSD::AARCH64::NT_FPREGS}, + {llvm::Triple::NetBSD, llvm::Triple::x86, NETBSD::I386::NT_FPREGS}, + {llvm::Triple::NetBSD, llvm::Triple::x86_64, NETBSD::AMD64::NT_FPREGS}, + {llvm::Triple::OpenBSD, llvm::Triple::UnknownArch, OPENBSD::NT_FPREGS}, +}; + +constexpr RegsetDesc AARCH64_SVE_Desc[] = { + {llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_SVE}, +}; + +constexpr RegsetDesc AARCH64_SSVE_Desc[] = { + {llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_SSVE}, +}; + +constexpr RegsetDesc AARCH64_ZA_Desc[] = { + {llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_ZA}, +}; + +constexpr RegsetDesc AARCH64_ZT_Desc[] = { + {llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_ZT}, +}; + +constexpr RegsetDesc AARCH64_PAC_Desc[] = { + {llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_PAC_MASK}, +}; + +constexpr RegsetDesc AARCH64_TLS_Desc[] = { + {llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_TLS}, +}; + +constexpr RegsetDesc AARCH64_MTE_Desc[] = { + {llvm::Triple::Linux, llvm::Triple::aarch64, + llvm::ELF::NT_ARM_TAGGED_ADDR_CTRL}, +}; + +constexpr RegsetDesc PPC_VMX_Desc[] = { + {llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX}, + {llvm::Triple::Linux, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX}, +}; + +constexpr RegsetDesc PPC_VSX_Desc[] = { + {llvm::Triple::Linux, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VSX}, +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERUTILITIES_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp new file mode 100644 index 000000000000..c931583cf21c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -0,0 +1,440 @@ +//===-- ThreadElfCore.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h" +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#ifdef LLDB_ENABLE_ALL +#include "Plugins/Process/Utility/RegisterContextLinux_s390x.h" +#endif // LLDB_ENABLE_ALL +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/Utility/RegisterContextNetBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextNetBSD_x86_64.h" +#include "Plugins/Process/Utility/RegisterContextOpenBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h" +#include "Plugins/Process/Utility/RegisterInfoPOSIX_ppc64le.h" +#include "ProcessElfCore.h" +#include "RegisterContextLinuxCore_x86_64.h" +#include "RegisterContextPOSIXCore_arm.h" +#include "RegisterContextPOSIXCore_arm64.h" +#include "RegisterContextPOSIXCore_mips64.h" +#include "RegisterContextPOSIXCore_powerpc.h" +#include "RegisterContextPOSIXCore_ppc64le.h" +#include "RegisterContextPOSIXCore_riscv64.h" +#ifdef LLDB_ENABLE_ALL +#include "RegisterContextPOSIXCore_s390x.h" +#endif // LLDB_ENABLE_ALL +#include "RegisterContextPOSIXCore_x86_64.h" +#include "ThreadElfCore.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +// Construct a Thread object with given data +ThreadElfCore::ThreadElfCore(Process &process, const ThreadData &td) + : Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(), + m_signo(td.signo), m_code(td.code), m_gpregset_data(td.gpregset), + m_notes(td.notes) {} + +ThreadElfCore::~ThreadElfCore() { DestroyThread(); } + +void ThreadElfCore::RefreshStateAfterStop() { + GetRegisterContext()->InvalidateIfNeeded(false); +} + +RegisterContextSP ThreadElfCore::GetRegisterContext() { + if (!m_reg_context_sp) { + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + } + return m_reg_context_sp; +} + +RegisterContextSP +ThreadElfCore::CreateRegisterContextForFrame(StackFrame *frame) { + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + Log *log = GetLog(LLDBLog::Thread); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + bool is_linux = false; + if (concrete_frame_idx == 0) { + if (m_thread_reg_ctx_sp) + return m_thread_reg_ctx_sp; + + ProcessElfCore *process = static_cast<ProcessElfCore *>(GetProcess().get()); + ArchSpec arch = process->GetArchitecture(); + RegisterInfoInterface *reg_interface = nullptr; + + switch (arch.GetTriple().getOS()) { + case llvm::Triple::FreeBSD: { + switch (arch.GetMachine()) { + case llvm::Triple::aarch64: + case llvm::Triple::arm: + break; + case llvm::Triple::ppc: + reg_interface = new RegisterContextFreeBSD_powerpc32(arch); + break; + case llvm::Triple::ppc64: + reg_interface = new RegisterContextFreeBSD_powerpc64(arch); + break; + case llvm::Triple::mips64: + reg_interface = new RegisterContextFreeBSD_mips64(arch); + break; + case llvm::Triple::x86: + reg_interface = new RegisterContextFreeBSD_i386(arch); + break; + case llvm::Triple::x86_64: + reg_interface = new RegisterContextFreeBSD_x86_64(arch); + break; + default: + break; + } + break; + } + + case llvm::Triple::NetBSD: { + switch (arch.GetMachine()) { + case llvm::Triple::aarch64: + break; + case llvm::Triple::x86: + reg_interface = new RegisterContextNetBSD_i386(arch); + break; + case llvm::Triple::x86_64: + reg_interface = new RegisterContextNetBSD_x86_64(arch); + break; + default: + break; + } + break; + } + + case llvm::Triple::Linux: { + is_linux = true; + switch (arch.GetMachine()) { + case llvm::Triple::aarch64: + break; + case llvm::Triple::ppc64le: + reg_interface = new RegisterInfoPOSIX_ppc64le(arch); + break; +#ifdef LLDB_ENABLE_ALL + case llvm::Triple::systemz: + reg_interface = new RegisterContextLinux_s390x(arch); + break; +#endif // LLDB_ENABLE_ALL + case llvm::Triple::x86: + reg_interface = new RegisterContextLinux_i386(arch); + break; + case llvm::Triple::x86_64: + reg_interface = new RegisterContextLinux_x86_64(arch); + break; + default: + break; + } + break; + } + + case llvm::Triple::OpenBSD: { + switch (arch.GetMachine()) { + case llvm::Triple::aarch64: + break; + case llvm::Triple::x86: + reg_interface = new RegisterContextOpenBSD_i386(arch); + break; + case llvm::Triple::x86_64: + reg_interface = new RegisterContextOpenBSD_x86_64(arch); + break; + default: + break; + } + break; + } + + default: + break; + } + + if (!reg_interface && arch.GetMachine() != llvm::Triple::aarch64 && + arch.GetMachine() != llvm::Triple::arm && + arch.GetMachine() != llvm::Triple::riscv64) { + LLDB_LOGF(log, "elf-core::%s:: Architecture(%d) or OS(%d) not supported", + __FUNCTION__, arch.GetMachine(), arch.GetTriple().getOS()); + assert(false && "Architecture or OS not supported"); + } + + switch (arch.GetMachine()) { + case llvm::Triple::aarch64: + m_thread_reg_ctx_sp = RegisterContextCorePOSIX_arm64::Create( + *this, arch, m_gpregset_data, m_notes); + break; + case llvm::Triple::arm: + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_arm>( + *this, std::make_unique<RegisterInfoPOSIX_arm>(arch), m_gpregset_data, + m_notes); + break; + case llvm::Triple::riscv64: + m_thread_reg_ctx_sp = RegisterContextCorePOSIX_riscv64::Create( + *this, arch, m_gpregset_data, m_notes); + break; + case llvm::Triple::mipsel: + case llvm::Triple::mips: + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_mips64>( + *this, reg_interface, m_gpregset_data, m_notes); + break; + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_mips64>( + *this, reg_interface, m_gpregset_data, m_notes); + break; + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_powerpc>( + *this, reg_interface, m_gpregset_data, m_notes); + break; + case llvm::Triple::ppc64le: + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_ppc64le>( + *this, reg_interface, m_gpregset_data, m_notes); + break; +#ifdef LLDB_ENABLE_ALL + case llvm::Triple::systemz: + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_s390x>( + *this, reg_interface, m_gpregset_data, m_notes); + break; +#endif // LLDB_ENABLE_ALL + case llvm::Triple::x86: + case llvm::Triple::x86_64: + if (is_linux) { + m_thread_reg_ctx_sp = std::make_shared<RegisterContextLinuxCore_x86_64>( + *this, reg_interface, m_gpregset_data, m_notes); + } else { + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>( + *this, reg_interface, m_gpregset_data, m_notes); + } + break; + default: + break; + } + + reg_ctx_sp = m_thread_reg_ctx_sp; + } else { + reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame); + } + return reg_ctx_sp; +} + +bool ThreadElfCore::CalculateStopInfo() { + ProcessSP process_sp(GetProcess()); + if (!process_sp) + return false; + + SetStopInfo(StopInfo::CreateStopReasonWithSignal( + *this, m_signo, /*description=*/nullptr, m_code)); + return true; +} + +// Parse PRSTATUS from NOTE entry +ELFLinuxPrStatus::ELFLinuxPrStatus() { + memset(this, 0, sizeof(ELFLinuxPrStatus)); +} + +size_t ELFLinuxPrStatus::GetSize(const lldb_private::ArchSpec &arch) { + constexpr size_t mips_linux_pr_status_size_o32 = 96; + constexpr size_t mips_linux_pr_status_size_n32 = 72; + constexpr size_t num_ptr_size_members = 10; + if (arch.IsMIPS()) { + std::string abi = arch.GetTargetABI(); + assert(!abi.empty() && "ABI is not set"); + if (!abi.compare("n64")) + return sizeof(ELFLinuxPrStatus); + else if (!abi.compare("o32")) + return mips_linux_pr_status_size_o32; + // N32 ABI + return mips_linux_pr_status_size_n32; + } + switch (arch.GetCore()) { + case lldb_private::ArchSpec::eCore_x86_32_i386: + case lldb_private::ArchSpec::eCore_x86_32_i486: + return 72; + default: + if (arch.GetAddressByteSize() == 8) + return sizeof(ELFLinuxPrStatus); + else + return sizeof(ELFLinuxPrStatus) - num_ptr_size_members * 4; + } +} + +Status ELFLinuxPrStatus::Parse(const DataExtractor &data, + const ArchSpec &arch) { + Status error; + if (GetSize(arch) > data.GetByteSize()) { + error.SetErrorStringWithFormat( + "NT_PRSTATUS size should be %zu, but the remaining bytes are: %" PRIu64, + GetSize(arch), data.GetByteSize()); + return error; + } + + // Read field by field to correctly account for endianess of both the core + // dump and the platform running lldb. + offset_t offset = 0; + si_signo = data.GetU32(&offset); + si_code = data.GetU32(&offset); + si_errno = data.GetU32(&offset); + + pr_cursig = data.GetU16(&offset); + offset += 2; // pad + + pr_sigpend = data.GetAddress(&offset); + pr_sighold = data.GetAddress(&offset); + + pr_pid = data.GetU32(&offset); + pr_ppid = data.GetU32(&offset); + pr_pgrp = data.GetU32(&offset); + pr_sid = data.GetU32(&offset); + + pr_utime.tv_sec = data.GetAddress(&offset); + pr_utime.tv_usec = data.GetAddress(&offset); + + pr_stime.tv_sec = data.GetAddress(&offset); + pr_stime.tv_usec = data.GetAddress(&offset); + + pr_cutime.tv_sec = data.GetAddress(&offset); + pr_cutime.tv_usec = data.GetAddress(&offset); + + pr_cstime.tv_sec = data.GetAddress(&offset); + pr_cstime.tv_usec = data.GetAddress(&offset); + + return error; +} + +// Parse PRPSINFO from NOTE entry +ELFLinuxPrPsInfo::ELFLinuxPrPsInfo() { + memset(this, 0, sizeof(ELFLinuxPrPsInfo)); +} + +size_t ELFLinuxPrPsInfo::GetSize(const lldb_private::ArchSpec &arch) { + constexpr size_t mips_linux_pr_psinfo_size_o32_n32 = 128; + if (arch.IsMIPS()) { + uint8_t address_byte_size = arch.GetAddressByteSize(); + if (address_byte_size == 8) + return sizeof(ELFLinuxPrPsInfo); + return mips_linux_pr_psinfo_size_o32_n32; + } + + switch (arch.GetCore()) { + case lldb_private::ArchSpec::eCore_s390x_generic: + case lldb_private::ArchSpec::eCore_x86_64_x86_64: + return sizeof(ELFLinuxPrPsInfo); + case lldb_private::ArchSpec::eCore_x86_32_i386: + case lldb_private::ArchSpec::eCore_x86_32_i486: + return 124; + default: + return 0; + } +} + +Status ELFLinuxPrPsInfo::Parse(const DataExtractor &data, + const ArchSpec &arch) { + Status error; + ByteOrder byteorder = data.GetByteOrder(); + if (GetSize(arch) > data.GetByteSize()) { + error.SetErrorStringWithFormat( + "NT_PRPSINFO size should be %zu, but the remaining bytes are: %" PRIu64, + GetSize(arch), data.GetByteSize()); + return error; + } + size_t size = 0; + offset_t offset = 0; + + pr_state = data.GetU8(&offset); + pr_sname = data.GetU8(&offset); + pr_zomb = data.GetU8(&offset); + pr_nice = data.GetU8(&offset); + if (data.GetAddressByteSize() == 8) { + // Word align the next field on 64 bit. + offset += 4; + } + + pr_flag = data.GetAddress(&offset); + + if (arch.IsMIPS()) { + // The pr_uid and pr_gid is always 32 bit irrespective of platforms + pr_uid = data.GetU32(&offset); + pr_gid = data.GetU32(&offset); + } else { + // 16 bit on 32 bit platforms, 32 bit on 64 bit platforms + pr_uid = data.GetMaxU64(&offset, data.GetAddressByteSize() >> 1); + pr_gid = data.GetMaxU64(&offset, data.GetAddressByteSize() >> 1); + } + + pr_pid = data.GetU32(&offset); + pr_ppid = data.GetU32(&offset); + pr_pgrp = data.GetU32(&offset); + pr_sid = data.GetU32(&offset); + + size = 16; + data.ExtractBytes(offset, size, byteorder, pr_fname); + offset += size; + + size = 80; + data.ExtractBytes(offset, size, byteorder, pr_psargs); + offset += size; + + return error; +} + +// Parse SIGINFO from NOTE entry +ELFLinuxSigInfo::ELFLinuxSigInfo() { memset(this, 0, sizeof(ELFLinuxSigInfo)); } + +size_t ELFLinuxSigInfo::GetSize(const lldb_private::ArchSpec &arch) { + if (arch.IsMIPS()) + return sizeof(ELFLinuxSigInfo); + switch (arch.GetCore()) { + case lldb_private::ArchSpec::eCore_x86_64_x86_64: + return sizeof(ELFLinuxSigInfo); + case lldb_private::ArchSpec::eCore_s390x_generic: + case lldb_private::ArchSpec::eCore_x86_32_i386: + case lldb_private::ArchSpec::eCore_x86_32_i486: + return 12; + default: + return 0; + } +} + +Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch) { + Status error; + if (GetSize(arch) > data.GetByteSize()) { + error.SetErrorStringWithFormat( + "NT_SIGINFO size should be %zu, but the remaining bytes are: %" PRIu64, + GetSize(arch), data.GetByteSize()); + return error; + } + + // Parsing from a 32 bit ELF core file, and populating/reusing the structure + // properly, because the struct is for the 64 bit version + offset_t offset = 0; + si_signo = data.GetU32(&offset); + si_errno = data.GetU32(&offset); + si_code = data.GetU32(&offset); + + return error; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h new file mode 100644 index 000000000000..2f3ed2a01779 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h @@ -0,0 +1,178 @@ +//===-- ThreadElfCore.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_SOURCE_PLUGINS_PROCESS_ELF_CORE_THREADELFCORE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_THREADELFCORE_H + +#include "Plugins/Process/elf-core/RegisterUtilities.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/ADT/DenseMap.h" +#include <string> + +struct compat_timeval { + alignas(8) uint64_t tv_sec; + alignas(8) uint64_t tv_usec; +}; + +// PRSTATUS structure's size differs based on architecture. +// This is the layout in the x86-64 arch. +// In the i386 case we parse it manually and fill it again +// in the same structure +// The gp registers are also a part of this struct, but they are handled +// separately + +#undef si_signo +#undef si_code +#undef si_errno + +struct ELFLinuxPrStatus { + int32_t si_signo; + int32_t si_code; + int32_t si_errno; + + int16_t pr_cursig; + + alignas(8) uint64_t pr_sigpend; + alignas(8) uint64_t pr_sighold; + + uint32_t pr_pid; + uint32_t pr_ppid; + uint32_t pr_pgrp; + uint32_t pr_sid; + + compat_timeval pr_utime; + compat_timeval pr_stime; + compat_timeval pr_cutime; + compat_timeval pr_cstime; + + ELFLinuxPrStatus(); + + lldb_private::Status Parse(const lldb_private::DataExtractor &data, + const lldb_private::ArchSpec &arch); + + // Return the bytesize of the structure + // 64 bit - just sizeof + // 32 bit - hardcoded because we are reusing the struct, but some of the + // members are smaller - + // so the layout is not the same + static size_t GetSize(const lldb_private::ArchSpec &arch); +}; + +static_assert(sizeof(ELFLinuxPrStatus) == 112, + "sizeof ELFLinuxPrStatus is not correct!"); + +struct ELFLinuxSigInfo { + int32_t si_signo; + int32_t si_code; + int32_t si_errno; + + ELFLinuxSigInfo(); + + lldb_private::Status Parse(const lldb_private::DataExtractor &data, + const lldb_private::ArchSpec &arch); + + // Return the bytesize of the structure + // 64 bit - just sizeof + // 32 bit - hardcoded because we are reusing the struct, but some of the + // members are smaller - + // so the layout is not the same + static size_t GetSize(const lldb_private::ArchSpec &arch); +}; + +static_assert(sizeof(ELFLinuxSigInfo) == 12, + "sizeof ELFLinuxSigInfo is not correct!"); + +// PRPSINFO structure's size differs based on architecture. +// This is the layout in the x86-64 arch case. +// In the i386 case we parse it manually and fill it again +// in the same structure +struct ELFLinuxPrPsInfo { + char pr_state; + char pr_sname; + char pr_zomb; + char pr_nice; + alignas(8) uint64_t pr_flag; + uint32_t pr_uid; + uint32_t pr_gid; + int32_t pr_pid; + int32_t pr_ppid; + int32_t pr_pgrp; + int32_t pr_sid; + char pr_fname[16]; + char pr_psargs[80]; + + ELFLinuxPrPsInfo(); + + lldb_private::Status Parse(const lldb_private::DataExtractor &data, + const lldb_private::ArchSpec &arch); + + // Return the bytesize of the structure + // 64 bit - just sizeof + // 32 bit - hardcoded because we are reusing the struct, but some of the + // members are smaller - + // so the layout is not the same + static size_t GetSize(const lldb_private::ArchSpec &arch); +}; + +static_assert(sizeof(ELFLinuxPrPsInfo) == 136, + "sizeof ELFLinuxPrPsInfo is not correct!"); + +struct ThreadData { + lldb_private::DataExtractor gpregset; + std::vector<lldb_private::CoreNote> notes; + lldb::tid_t tid; + int signo = 0; + int code = 0; + int prstatus_sig = 0; + std::string name; +}; + +class ThreadElfCore : public lldb_private::Thread { +public: + ThreadElfCore(lldb_private::Process &process, const ThreadData &td); + + ~ThreadElfCore() override; + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + static bool ThreadIDIsValid(lldb::tid_t thread) { return thread != 0; } + + const char *GetName() override { + if (m_thread_name.empty()) + return nullptr; + return m_thread_name.c_str(); + } + + void SetName(const char *name) override { + if (name && name[0]) + m_thread_name.assign(name); + else + m_thread_name.clear(); + } + +protected: + // Member variables. + std::string m_thread_name; + lldb::RegisterContextSP m_thread_reg_ctx_sp; + + int m_signo; + int m_code; + + lldb_private::DataExtractor m_gpregset_data; + std::vector<lldb_private::CoreNote> m_notes; + + bool CalculateStopInfo() override; +}; + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_THREADELFCORE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp new file mode 100644 index 000000000000..394b62559da7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp @@ -0,0 +1,404 @@ +//===-- GDBRemoteClientBase.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 "GDBRemoteClientBase.h" + +#include "llvm/ADT/StringExtras.h" + +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/LLDBAssert.h" + +#include "ProcessGDBRemoteLog.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace std::chrono; + +// When we've sent a continue packet and are waiting for the target to stop, +// we wake up the wait with this interval to make sure the stub hasn't gone +// away while we were waiting. +static const seconds kWakeupInterval(5); + +///////////////////////// +// GDBRemoteClientBase // +///////////////////////// + +GDBRemoteClientBase::ContinueDelegate::~ContinueDelegate() = default; + +GDBRemoteClientBase::GDBRemoteClientBase(const char *comm_name) + : GDBRemoteCommunication(), Broadcaster(nullptr, comm_name), + m_async_count(0), m_is_running(false), m_should_stop(false) {} + +StateType GDBRemoteClientBase::SendContinuePacketAndWaitForResponse( + ContinueDelegate &delegate, const UnixSignals &signals, + llvm::StringRef payload, std::chrono::seconds interrupt_timeout, + StringExtractorGDBRemote &response) { + Log *log = GetLog(GDBRLog::Process); + response.Clear(); + + { + std::lock_guard<std::mutex> lock(m_mutex); + m_continue_packet = std::string(payload); + m_should_stop = false; + } + ContinueLock cont_lock(*this); + if (!cont_lock) + return eStateInvalid; + OnRunPacketSent(true); + // The main ReadPacket loop wakes up at computed_timeout intervals, just to + // check that the connection hasn't dropped. When we wake up we also check + // whether there is an interrupt request that has reached its endpoint. + // If we want a shorter interrupt timeout that kWakeupInterval, we need to + // choose the shorter interval for the wake up as well. + std::chrono::seconds computed_timeout = std::min(interrupt_timeout, + kWakeupInterval); + for (;;) { + PacketResult read_result = ReadPacket(response, computed_timeout, false); + // Reset the computed_timeout to the default value in case we are going + // round again. + computed_timeout = std::min(interrupt_timeout, kWakeupInterval); + switch (read_result) { + case PacketResult::ErrorReplyTimeout: { + std::lock_guard<std::mutex> lock(m_mutex); + if (m_async_count == 0) { + continue; + } + auto cur_time = steady_clock::now(); + if (cur_time >= m_interrupt_endpoint) + return eStateInvalid; + else { + // We woke up and found an interrupt is in flight, but we haven't + // exceeded the interrupt wait time. So reset the wait time to the + // time left till the interrupt timeout. But don't wait longer + // than our wakeup timeout. + auto new_wait = m_interrupt_endpoint - cur_time; + computed_timeout = std::min(kWakeupInterval, + std::chrono::duration_cast<std::chrono::seconds>(new_wait)); + continue; + } + break; + } + case PacketResult::Success: + break; + default: + LLDB_LOGF(log, "GDBRemoteClientBase::%s () ReadPacket(...) => false", + __FUNCTION__); + return eStateInvalid; + } + if (response.Empty()) + return eStateInvalid; + + const char stop_type = response.GetChar(); + LLDB_LOGF(log, "GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__, + response.GetStringRef().data()); + + switch (stop_type) { + case 'W': + case 'X': + return eStateExited; + case 'E': + // ERROR + return eStateInvalid; + default: + LLDB_LOGF(log, "GDBRemoteClientBase::%s () unrecognized async packet", + __FUNCTION__); + return eStateInvalid; + case 'O': { + std::string inferior_stdout; + response.GetHexByteString(inferior_stdout); + delegate.HandleAsyncStdout(inferior_stdout); + break; + } + case 'A': + delegate.HandleAsyncMisc( + llvm::StringRef(response.GetStringRef()).substr(1)); + break; + case 'J': + delegate.HandleAsyncStructuredDataPacket(response.GetStringRef()); + break; + case 'T': + case 'S': + // Do this with the continue lock held. + const bool should_stop = ShouldStop(signals, response); + response.SetFilePos(0); + + // The packet we should resume with. In the future we should check our + // thread list and "do the right thing" for new threads that show up + // while we stop and run async packets. Setting the packet to 'c' to + // continue all threads is the right thing to do 99.99% of the time + // because if a thread was single stepping, and we sent an interrupt, we + // will notice above that we didn't stop due to an interrupt but stopped + // due to stepping and we would _not_ continue. This packet may get + // modified by the async actions (e.g. to send a signal). + m_continue_packet = 'c'; + cont_lock.unlock(); + + delegate.HandleStopReply(); + if (should_stop) + return eStateStopped; + + switch (cont_lock.lock()) { + case ContinueLock::LockResult::Success: + break; + case ContinueLock::LockResult::Failed: + return eStateInvalid; + case ContinueLock::LockResult::Cancelled: + return eStateStopped; + } + OnRunPacketSent(false); + break; + } + } +} + +bool GDBRemoteClientBase::SendAsyncSignal( + int signo, std::chrono::seconds interrupt_timeout) { + Lock lock(*this, interrupt_timeout); + if (!lock || !lock.DidInterrupt()) + return false; + + m_continue_packet = 'C'; + m_continue_packet += llvm::hexdigit((signo / 16) % 16); + m_continue_packet += llvm::hexdigit(signo % 16); + return true; +} + +bool GDBRemoteClientBase::Interrupt(std::chrono::seconds interrupt_timeout) { + Lock lock(*this, interrupt_timeout); + if (!lock.DidInterrupt()) + return false; + m_should_stop = true; + return true; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteClientBase::SendPacketAndWaitForResponse( + llvm::StringRef payload, StringExtractorGDBRemote &response, + std::chrono::seconds interrupt_timeout) { + Lock lock(*this, interrupt_timeout); + if (!lock) { + if (Log *log = GetLog(GDBRLog::Process)) + LLDB_LOGF(log, + "GDBRemoteClientBase::%s failed to get mutex, not sending " + "packet '%.*s'", + __FUNCTION__, int(payload.size()), payload.data()); + return PacketResult::ErrorSendFailed; + } + + return SendPacketAndWaitForResponseNoLock(payload, response); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteClientBase::ReadPacketWithOutputSupport( + StringExtractorGDBRemote &response, Timeout<std::micro> timeout, + bool sync_on_timeout, + llvm::function_ref<void(llvm::StringRef)> output_callback) { + auto result = ReadPacket(response, timeout, sync_on_timeout); + while (result == PacketResult::Success && response.IsNormalResponse() && + response.PeekChar() == 'O') { + response.GetChar(); + std::string output; + if (response.GetHexByteString(output)) + output_callback(output); + result = ReadPacket(response, timeout, sync_on_timeout); + } + return result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteClientBase::SendPacketAndReceiveResponseWithOutputSupport( + llvm::StringRef payload, StringExtractorGDBRemote &response, + std::chrono::seconds interrupt_timeout, + llvm::function_ref<void(llvm::StringRef)> output_callback) { + Lock lock(*this, interrupt_timeout); + if (!lock) { + if (Log *log = GetLog(GDBRLog::Process)) + LLDB_LOGF(log, + "GDBRemoteClientBase::%s failed to get mutex, not sending " + "packet '%.*s'", + __FUNCTION__, int(payload.size()), payload.data()); + return PacketResult::ErrorSendFailed; + } + + PacketResult packet_result = SendPacketNoLock(payload); + if (packet_result != PacketResult::Success) + return packet_result; + + return ReadPacketWithOutputSupport(response, GetPacketTimeout(), true, + output_callback); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteClientBase::SendPacketAndWaitForResponseNoLock( + llvm::StringRef payload, StringExtractorGDBRemote &response) { + PacketResult packet_result = SendPacketNoLock(payload); + if (packet_result != PacketResult::Success) + return packet_result; + + const size_t max_response_retries = 3; + for (size_t i = 0; i < max_response_retries; ++i) { + packet_result = ReadPacket(response, GetPacketTimeout(), true); + // Make sure we received a response + if (packet_result != PacketResult::Success) + return packet_result; + // Make sure our response is valid for the payload that was sent + if (response.ValidateResponse()) + return packet_result; + // Response says it wasn't valid + Log *log = GetLog(GDBRLog::Packets); + LLDB_LOGF( + log, + "error: packet with payload \"%.*s\" got invalid response \"%s\": %s", + int(payload.size()), payload.data(), response.GetStringRef().data(), + (i == (max_response_retries - 1)) + ? "using invalid response and giving up" + : "ignoring response and waiting for another"); + } + return packet_result; +} + +bool GDBRemoteClientBase::ShouldStop(const UnixSignals &signals, + StringExtractorGDBRemote &response) { + std::lock_guard<std::mutex> lock(m_mutex); + + if (m_async_count == 0) + return true; // We were not interrupted. The process stopped on its own. + + // Older debugserver stubs (before April 2016) can return two stop-reply + // packets in response to a ^C packet. Additionally, all debugservers still + // return two stop replies if the inferior stops due to some other reason + // before the remote stub manages to interrupt it. We need to wait for this + // additional packet to make sure the packet sequence does not get skewed. + StringExtractorGDBRemote extra_stop_reply_packet; + ReadPacket(extra_stop_reply_packet, milliseconds(100), false); + + // Interrupting is typically done using SIGSTOP or SIGINT, so if the process + // stops with some other signal, we definitely want to stop. + const uint8_t signo = response.GetHexU8(UINT8_MAX); + if (signo != signals.GetSignalNumberFromName("SIGSTOP") && + signo != signals.GetSignalNumberFromName("SIGINT")) + return true; + + // We probably only stopped to perform some async processing, so continue + // after that is done. + // TODO: This is not 100% correct, as the process may have been stopped with + // SIGINT or SIGSTOP that was not caused by us (e.g. raise(SIGINT)). This will + // normally cause a stop, but if it's done concurrently with a async + // interrupt, that stop will get eaten (llvm.org/pr20231). + return false; +} + +void GDBRemoteClientBase::OnRunPacketSent(bool first) { + if (first) + BroadcastEvent(eBroadcastBitRunPacketSent, nullptr); +} + +/////////////////////////////////////// +// GDBRemoteClientBase::ContinueLock // +/////////////////////////////////////// + +GDBRemoteClientBase::ContinueLock::ContinueLock(GDBRemoteClientBase &comm) + : m_comm(comm), m_acquired(false) { + lock(); +} + +GDBRemoteClientBase::ContinueLock::~ContinueLock() { + if (m_acquired) + unlock(); +} + +void GDBRemoteClientBase::ContinueLock::unlock() { + lldbassert(m_acquired); + { + std::unique_lock<std::mutex> lock(m_comm.m_mutex); + m_comm.m_is_running = false; + } + m_comm.m_cv.notify_all(); + m_acquired = false; +} + +GDBRemoteClientBase::ContinueLock::LockResult +GDBRemoteClientBase::ContinueLock::lock() { + Log *log = GetLog(GDBRLog::Process); + LLDB_LOGF(log, "GDBRemoteClientBase::ContinueLock::%s() resuming with %s", + __FUNCTION__, m_comm.m_continue_packet.c_str()); + + lldbassert(!m_acquired); + std::unique_lock<std::mutex> lock(m_comm.m_mutex); + m_comm.m_cv.wait(lock, [this] { return m_comm.m_async_count == 0; }); + if (m_comm.m_should_stop) { + m_comm.m_should_stop = false; + LLDB_LOGF(log, "GDBRemoteClientBase::ContinueLock::%s() cancelled", + __FUNCTION__); + return LockResult::Cancelled; + } + if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) != + PacketResult::Success) + return LockResult::Failed; + + lldbassert(!m_comm.m_is_running); + m_comm.m_is_running = true; + m_acquired = true; + return LockResult::Success; +} + +/////////////////////////////// +// GDBRemoteClientBase::Lock // +/////////////////////////////// + +GDBRemoteClientBase::Lock::Lock(GDBRemoteClientBase &comm, + std::chrono::seconds interrupt_timeout) + : m_async_lock(comm.m_async_mutex, std::defer_lock), m_comm(comm), + m_interrupt_timeout(interrupt_timeout), m_acquired(false), + m_did_interrupt(false) { + SyncWithContinueThread(); + if (m_acquired) + m_async_lock.lock(); +} + +void GDBRemoteClientBase::Lock::SyncWithContinueThread() { + Log *log = GetLog(GDBRLog::Process|GDBRLog::Packets); + std::unique_lock<std::mutex> lock(m_comm.m_mutex); + if (m_comm.m_is_running && m_interrupt_timeout == std::chrono::seconds(0)) + return; // We were asked to avoid interrupting the sender. Lock is not + // acquired. + + ++m_comm.m_async_count; + if (m_comm.m_is_running) { + if (m_comm.m_async_count == 1) { + // The sender has sent the continue packet and we are the first async + // packet. Let's interrupt it. + const char ctrl_c = '\x03'; + ConnectionStatus status = eConnectionStatusSuccess; + size_t bytes_written = m_comm.Write(&ctrl_c, 1, status, nullptr); + if (bytes_written == 0) { + --m_comm.m_async_count; + LLDB_LOGF(log, "GDBRemoteClientBase::Lock::Lock failed to send " + "interrupt packet"); + return; + } + m_comm.m_interrupt_endpoint = steady_clock::now() + m_interrupt_timeout; + if (log) + log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03"); + } + m_comm.m_cv.wait(lock, [this] { return !m_comm.m_is_running; }); + m_did_interrupt = true; + } + m_acquired = true; +} + +GDBRemoteClientBase::Lock::~Lock() { + if (!m_acquired) + return; + { + std::unique_lock<std::mutex> lock(m_comm.m_mutex); + --m_comm.m_async_count; + } + m_comm.m_cv.notify_one(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h new file mode 100644 index 000000000000..b47fee76a2ab --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h @@ -0,0 +1,175 @@ +//===-- GDBRemoteClientBase.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECLIENTBASE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECLIENTBASE_H + +#include "GDBRemoteCommunication.h" + +#include <condition_variable> + +namespace lldb_private { +namespace process_gdb_remote { + +class GDBRemoteClientBase : public GDBRemoteCommunication, public Broadcaster { +public: + enum { + eBroadcastBitRunPacketSent = (1u << 0), + }; + + struct ContinueDelegate { + virtual ~ContinueDelegate(); + virtual void HandleAsyncStdout(llvm::StringRef out) = 0; + virtual void HandleAsyncMisc(llvm::StringRef data) = 0; + virtual void HandleStopReply() = 0; + + /// Process asynchronously-received structured data. + /// + /// \param[in] data + /// The complete data packet, expected to start with JSON-async. + virtual void HandleAsyncStructuredDataPacket(llvm::StringRef data) = 0; + }; + + GDBRemoteClientBase(const char *comm_name); + + bool SendAsyncSignal(int signo, std::chrono::seconds interrupt_timeout); + + bool Interrupt(std::chrono::seconds interrupt_timeout); + + lldb::StateType SendContinuePacketAndWaitForResponse( + ContinueDelegate &delegate, const UnixSignals &signals, + llvm::StringRef payload, std::chrono::seconds interrupt_timeout, + StringExtractorGDBRemote &response); + + // If interrupt_timeout == 0 seconds, don't interrupt the target. + // Only send the packet if the target is stopped. + // If you want to use this mode, use the fact that the timeout is defaulted + // so it's clear from the call-site that you are using no-interrupt. + // If it is non-zero, interrupt the target if it is running, and + // send the packet. + // It the target doesn't respond within the given timeout, it returns + // ErrorReplyTimeout. + PacketResult SendPacketAndWaitForResponse( + llvm::StringRef payload, StringExtractorGDBRemote &response, + std::chrono::seconds interrupt_timeout = std::chrono::seconds(0)); + + PacketResult ReadPacketWithOutputSupport( + StringExtractorGDBRemote &response, Timeout<std::micro> timeout, + bool sync_on_timeout, + llvm::function_ref<void(llvm::StringRef)> output_callback); + + PacketResult SendPacketAndReceiveResponseWithOutputSupport( + llvm::StringRef payload, StringExtractorGDBRemote &response, + std::chrono::seconds interrupt_timeout, + llvm::function_ref<void(llvm::StringRef)> output_callback); + + class Lock { + public: + // If interrupt_timeout == 0 seconds, only take the lock if the target is + // not running. If using this option, use the fact that the + // interrupt_timeout is defaulted so it will be obvious at the call site + // that you are choosing this mode. If it is non-zero, interrupt the target + // if it is running, waiting for the given timeout for the interrupt to + // succeed. + Lock(GDBRemoteClientBase &comm, + std::chrono::seconds interrupt_timeout = std::chrono::seconds(0)); + ~Lock(); + + explicit operator bool() { return m_acquired; } + + // Whether we had to interrupt the continue thread to acquire the + // connection. + bool DidInterrupt() const { return m_did_interrupt; } + + private: + std::unique_lock<std::recursive_mutex> m_async_lock; + GDBRemoteClientBase &m_comm; + std::chrono::seconds m_interrupt_timeout; + bool m_acquired; + bool m_did_interrupt; + + void SyncWithContinueThread(); + }; + +protected: + PacketResult + SendPacketAndWaitForResponseNoLock(llvm::StringRef payload, + StringExtractorGDBRemote &response); + + virtual void OnRunPacketSent(bool first); + +private: + /// Variables handling synchronization between the Continue thread and any + /// other threads wishing to send packets over the connection. Either the + /// continue thread has control over the connection (m_is_running == true) or + /// the connection is free for an arbitrary number of other senders to take + /// which indicate their interest by incrementing m_async_count. + /// + /// Semantics of individual states: + /// + /// - m_continue_packet == false, m_async_count == 0: + /// connection is free + /// - m_continue_packet == true, m_async_count == 0: + /// only continue thread is present + /// - m_continue_packet == true, m_async_count > 0: + /// continue thread has control, async threads should interrupt it and wait + /// for it to set m_continue_packet to false + /// - m_continue_packet == false, m_async_count > 0: + /// async threads have control, continue thread needs to wait for them to + /// finish (m_async_count goes down to 0). + /// @{ + std::mutex m_mutex; + std::condition_variable m_cv; + + /// Packet with which to resume after an async interrupt. Can be changed by + /// an async thread e.g. to inject a signal. + std::string m_continue_packet; + + /// When was the interrupt packet sent. Used to make sure we time out if the + /// stub does not respond to interrupt requests. + std::chrono::time_point<std::chrono::steady_clock> m_interrupt_endpoint; + + /// Number of threads interested in sending. + uint32_t m_async_count; + + /// Whether the continue thread has control. + bool m_is_running; + + /// Whether we should resume after a stop. + bool m_should_stop; + /// @} + + /// This handles the synchronization between individual async threads. For + /// now they just use a simple mutex. + std::recursive_mutex m_async_mutex; + + bool ShouldStop(const UnixSignals &signals, + StringExtractorGDBRemote &response); + + class ContinueLock { + public: + enum class LockResult { Success, Cancelled, Failed }; + + explicit ContinueLock(GDBRemoteClientBase &comm); + ~ContinueLock(); + explicit operator bool() { return m_acquired; } + + LockResult lock(); + + void unlock(); + + private: + GDBRemoteClientBase &m_comm; + bool m_acquired; + }; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECLIENTBASE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp new file mode 100644 index 000000000000..187370eb36ca --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -0,0 +1,1320 @@ +//===-- GDBRemoteCommunication.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 "GDBRemoteCommunication.h" + +#include <climits> +#include <cstring> +#include <future> +#include <sys/stat.h> + +#include "lldb/Host/Config.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" +#include "lldb/Target/Platform.h" +#include "lldb/Utility/Event.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "ProcessGDBRemoteLog.h" + +#if defined(__APPLE__) +#define DEBUGSERVER_BASENAME "debugserver" +#elif defined(_WIN32) +#define DEBUGSERVER_BASENAME "lldb-server.exe" +#else +#define DEBUGSERVER_BASENAME "lldb-server" +#endif + +#if defined(HAVE_LIBCOMPRESSION) +#include <compression.h> +#endif + +#if LLVM_ENABLE_ZLIB +#include <zlib.h> +#endif + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +// GDBRemoteCommunication constructor +GDBRemoteCommunication::GDBRemoteCommunication() + : Communication(), +#ifdef LLDB_CONFIGURATION_DEBUG + m_packet_timeout(1000), +#else + m_packet_timeout(1), +#endif + m_echo_number(0), m_supports_qEcho(eLazyBoolCalculate), m_history(512), + m_send_acks(true), m_is_platform(false), + m_compression_type(CompressionType::None), m_listen_url() { +} + +// Destructor +GDBRemoteCommunication::~GDBRemoteCommunication() { + if (IsConnected()) { + Disconnect(); + } + +#if defined(HAVE_LIBCOMPRESSION) + if (m_decompression_scratch) + free (m_decompression_scratch); +#endif +} + +char GDBRemoteCommunication::CalculcateChecksum(llvm::StringRef payload) { + int checksum = 0; + + for (char c : payload) + checksum += c; + + return checksum & 255; +} + +size_t GDBRemoteCommunication::SendAck() { + Log *log = GetLog(GDBRLog::Packets); + ConnectionStatus status = eConnectionStatusSuccess; + char ch = '+'; + const size_t bytes_written = WriteAll(&ch, 1, status, nullptr); + LLDB_LOGF(log, "<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch); + m_history.AddPacket(ch, GDBRemotePacket::ePacketTypeSend, bytes_written); + return bytes_written; +} + +size_t GDBRemoteCommunication::SendNack() { + Log *log = GetLog(GDBRLog::Packets); + ConnectionStatus status = eConnectionStatusSuccess; + char ch = '-'; + const size_t bytes_written = WriteAll(&ch, 1, status, nullptr); + LLDB_LOGF(log, "<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch); + m_history.AddPacket(ch, GDBRemotePacket::ePacketTypeSend, bytes_written); + return bytes_written; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::SendPacketNoLock(llvm::StringRef payload) { + StreamString packet(0, 4, eByteOrderBig); + packet.PutChar('$'); + packet.Write(payload.data(), payload.size()); + packet.PutChar('#'); + packet.PutHex8(CalculcateChecksum(payload)); + std::string packet_str = std::string(packet.GetString()); + + return SendRawPacketNoLock(packet_str); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::SendNotificationPacketNoLock( + llvm::StringRef notify_type, std::deque<std::string> &queue, + llvm::StringRef payload) { + PacketResult ret = PacketResult::Success; + + // If there are no notification in the queue, send the notification + // packet. + if (queue.empty()) { + StreamString packet(0, 4, eByteOrderBig); + packet.PutChar('%'); + packet.Write(notify_type.data(), notify_type.size()); + packet.PutChar(':'); + packet.Write(payload.data(), payload.size()); + packet.PutChar('#'); + packet.PutHex8(CalculcateChecksum(payload)); + ret = SendRawPacketNoLock(packet.GetString(), true); + } + + queue.push_back(payload.str()); + return ret; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet, + bool skip_ack) { + if (IsConnected()) { + Log *log = GetLog(GDBRLog::Packets); + ConnectionStatus status = eConnectionStatusSuccess; + const char *packet_data = packet.data(); + const size_t packet_length = packet.size(); + size_t bytes_written = WriteAll(packet_data, packet_length, status, nullptr); + if (log) { + size_t binary_start_offset = 0; + if (strncmp(packet_data, "$vFile:pwrite:", strlen("$vFile:pwrite:")) == + 0) { + const char *first_comma = strchr(packet_data, ','); + if (first_comma) { + const char *second_comma = strchr(first_comma + 1, ','); + if (second_comma) + binary_start_offset = second_comma - packet_data + 1; + } + } + + // If logging was just enabled and we have history, then dump out what we + // have to the log so we get the historical context. The Dump() call that + // logs all of the packet will set a boolean so that we don't dump this + // more than once + if (!m_history.DidDumpToLog()) + m_history.Dump(log); + + if (binary_start_offset) { + StreamString strm; + // Print non binary data header + strm.Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written, + (int)binary_start_offset, packet_data); + const uint8_t *p; + // Print binary data exactly as sent + for (p = (const uint8_t *)packet_data + binary_start_offset; *p != '#'; + ++p) + strm.Printf("\\x%2.2x", *p); + // Print the checksum + strm.Printf("%*s", (int)3, p); + log->PutString(strm.GetString()); + } else + LLDB_LOGF(log, "<%4" PRIu64 "> send packet: %.*s", + (uint64_t)bytes_written, (int)packet_length, packet_data); + } + + m_history.AddPacket(packet.str(), packet_length, + GDBRemotePacket::ePacketTypeSend, bytes_written); + + if (bytes_written == packet_length) { + if (!skip_ack && GetSendAcks()) + return GetAck(); + else + return PacketResult::Success; + } else { + LLDB_LOGF(log, "error: failed to send packet: %.*s", (int)packet_length, + packet_data); + } + } + return PacketResult::ErrorSendFailed; +} + +GDBRemoteCommunication::PacketResult GDBRemoteCommunication::GetAck() { + StringExtractorGDBRemote packet; + PacketResult result = WaitForPacketNoLock(packet, GetPacketTimeout(), false); + if (result == PacketResult::Success) { + if (packet.GetResponseType() == + StringExtractorGDBRemote::ResponseType::eAck) + return PacketResult::Success; + else + return PacketResult::ErrorSendAck; + } + return result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::ReadPacket(StringExtractorGDBRemote &response, + Timeout<std::micro> timeout, + bool sync_on_timeout) { + using ResponseType = StringExtractorGDBRemote::ResponseType; + + Log *log = GetLog(GDBRLog::Packets); + for (;;) { + PacketResult result = + WaitForPacketNoLock(response, timeout, sync_on_timeout); + if (result != PacketResult::Success || + (response.GetResponseType() != ResponseType::eAck && + response.GetResponseType() != ResponseType::eNack)) + return result; + LLDB_LOG(log, "discarding spurious `{0}` packet", response.GetStringRef()); + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote &packet, + Timeout<std::micro> timeout, + bool sync_on_timeout) { + uint8_t buffer[8192]; + Status error; + + Log *log = GetLog(GDBRLog::Packets); + + // Check for a packet from our cache first without trying any reading... + if (CheckForPacket(nullptr, 0, packet) != PacketType::Invalid) + return PacketResult::Success; + + bool timed_out = false; + bool disconnected = false; + while (IsConnected() && !timed_out) { + lldb::ConnectionStatus status = eConnectionStatusNoConnection; + size_t bytes_read = Read(buffer, sizeof(buffer), timeout, status, &error); + + LLDB_LOGV(log, + "Read(buffer, sizeof(buffer), timeout = {0}, " + "status = {1}, error = {2}) => bytes_read = {3}", + timeout, Communication::ConnectionStatusAsString(status), error, + bytes_read); + + if (bytes_read > 0) { + if (CheckForPacket(buffer, bytes_read, packet) != PacketType::Invalid) + return PacketResult::Success; + } else { + switch (status) { + case eConnectionStatusTimedOut: + case eConnectionStatusInterrupted: + if (sync_on_timeout) { + /// Sync the remote GDB server and make sure we get a response that + /// corresponds to what we send. + /// + /// Sends a "qEcho" packet and makes sure it gets the exact packet + /// echoed back. If the qEcho packet isn't supported, we send a qC + /// packet and make sure we get a valid thread ID back. We use the + /// "qC" packet since its response if very unique: is responds with + /// "QC%x" where %x is the thread ID of the current thread. This + /// makes the response unique enough from other packet responses to + /// ensure we are back on track. + /// + /// This packet is needed after we time out sending a packet so we + /// can ensure that we are getting the response for the packet we + /// are sending. There are no sequence IDs in the GDB remote + /// protocol (there used to be, but they are not supported anymore) + /// so if you timeout sending packet "abc", you might then send + /// packet "cde" and get the response for the previous "abc" packet. + /// Many responses are "OK" or "" (unsupported) or "EXX" (error) so + /// many responses for packets can look like responses for other + /// packets. So if we timeout, we need to ensure that we can get + /// back on track. If we can't get back on track, we must + /// disconnect. + bool sync_success = false; + bool got_actual_response = false; + // We timed out, we need to sync back up with the + char echo_packet[32]; + int echo_packet_len = 0; + RegularExpression response_regex; + + if (m_supports_qEcho == eLazyBoolYes) { + echo_packet_len = ::snprintf(echo_packet, sizeof(echo_packet), + "qEcho:%u", ++m_echo_number); + std::string regex_str = "^"; + regex_str += echo_packet; + regex_str += "$"; + response_regex = RegularExpression(regex_str); + } else { + echo_packet_len = + ::snprintf(echo_packet, sizeof(echo_packet), "qC"); + response_regex = + RegularExpression(llvm::StringRef("^QC[0-9A-Fa-f]+$")); + } + + PacketResult echo_packet_result = + SendPacketNoLock(llvm::StringRef(echo_packet, echo_packet_len)); + if (echo_packet_result == PacketResult::Success) { + const uint32_t max_retries = 3; + uint32_t successful_responses = 0; + for (uint32_t i = 0; i < max_retries; ++i) { + StringExtractorGDBRemote echo_response; + echo_packet_result = + WaitForPacketNoLock(echo_response, timeout, false); + if (echo_packet_result == PacketResult::Success) { + ++successful_responses; + if (response_regex.Execute(echo_response.GetStringRef())) { + sync_success = true; + break; + } else if (successful_responses == 1) { + // We got something else back as the first successful + // response, it probably is the response to the packet we + // actually wanted, so copy it over if this is the first + // success and continue to try to get the qEcho response + packet = echo_response; + got_actual_response = true; + } + } else if (echo_packet_result == PacketResult::ErrorReplyTimeout) + continue; // Packet timed out, continue waiting for a response + else + break; // Something else went wrong getting the packet back, we + // failed and are done trying + } + } + + // We weren't able to sync back up with the server, we must abort + // otherwise all responses might not be from the right packets... + if (sync_success) { + // We timed out, but were able to recover + if (got_actual_response) { + // We initially timed out, but we did get a response that came in + // before the successful reply to our qEcho packet, so lets say + // everything is fine... + return PacketResult::Success; + } + } else { + disconnected = true; + Disconnect(); + } + } + timed_out = true; + break; + case eConnectionStatusSuccess: + // printf ("status = success but error = %s\n", + // error.AsCString("<invalid>")); + break; + + case eConnectionStatusEndOfFile: + case eConnectionStatusNoConnection: + case eConnectionStatusLostConnection: + case eConnectionStatusError: + disconnected = true; + Disconnect(); + break; + } + } + } + packet.Clear(); + if (disconnected) + return PacketResult::ErrorDisconnected; + if (timed_out) + return PacketResult::ErrorReplyTimeout; + else + return PacketResult::ErrorReplyFailed; +} + +bool GDBRemoteCommunication::DecompressPacket() { + Log *log = GetLog(GDBRLog::Packets); + + if (!CompressionIsEnabled()) + return true; + + size_t pkt_size = m_bytes.size(); + + // Smallest possible compressed packet is $N#00 - an uncompressed empty + // reply, most commonly indicating an unsupported packet. Anything less than + // 5 characters, it's definitely not a compressed packet. + if (pkt_size < 5) + return true; + + if (m_bytes[0] != '$' && m_bytes[0] != '%') + return true; + if (m_bytes[1] != 'C' && m_bytes[1] != 'N') + return true; + + size_t hash_mark_idx = m_bytes.find('#'); + if (hash_mark_idx == std::string::npos) + return true; + if (hash_mark_idx + 2 >= m_bytes.size()) + return true; + + if (!::isxdigit(m_bytes[hash_mark_idx + 1]) || + !::isxdigit(m_bytes[hash_mark_idx + 2])) + return true; + + size_t content_length = + pkt_size - + 5; // not counting '$', 'C' | 'N', '#', & the two hex checksum chars + size_t content_start = 2; // The first character of the + // compressed/not-compressed text of the packet + size_t checksum_idx = + hash_mark_idx + + 1; // The first character of the two hex checksum characters + + // Normally size_of_first_packet == m_bytes.size() but m_bytes may contain + // multiple packets. size_of_first_packet is the size of the initial packet + // which we'll replace with the decompressed version of, leaving the rest of + // m_bytes unmodified. + size_t size_of_first_packet = hash_mark_idx + 3; + + // Compressed packets ("$C") start with a base10 number which is the size of + // the uncompressed payload, then a : and then the compressed data. e.g. + // $C1024:<binary>#00 Update content_start and content_length to only include + // the <binary> part of the packet. + + uint64_t decompressed_bufsize = ULONG_MAX; + if (m_bytes[1] == 'C') { + size_t i = content_start; + while (i < hash_mark_idx && isdigit(m_bytes[i])) + i++; + if (i < hash_mark_idx && m_bytes[i] == ':') { + i++; + content_start = i; + content_length = hash_mark_idx - content_start; + std::string bufsize_str(m_bytes.data() + 2, i - 2 - 1); + errno = 0; + decompressed_bufsize = ::strtoul(bufsize_str.c_str(), nullptr, 10); + if (errno != 0 || decompressed_bufsize == ULONG_MAX) { + m_bytes.erase(0, size_of_first_packet); + return false; + } + } + } + + if (GetSendAcks()) { + char packet_checksum_cstr[3]; + packet_checksum_cstr[0] = m_bytes[checksum_idx]; + packet_checksum_cstr[1] = m_bytes[checksum_idx + 1]; + packet_checksum_cstr[2] = '\0'; + long packet_checksum = strtol(packet_checksum_cstr, nullptr, 16); + + long actual_checksum = CalculcateChecksum( + llvm::StringRef(m_bytes).substr(1, hash_mark_idx - 1)); + bool success = packet_checksum == actual_checksum; + if (!success) { + LLDB_LOGF(log, + "error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x", + (int)(pkt_size), m_bytes.c_str(), (uint8_t)packet_checksum, + (uint8_t)actual_checksum); + } + // Send the ack or nack if needed + if (!success) { + SendNack(); + m_bytes.erase(0, size_of_first_packet); + return false; + } else { + SendAck(); + } + } + + if (m_bytes[1] == 'N') { + // This packet was not compressed -- delete the 'N' character at the start + // and the packet may be processed as-is. + m_bytes.erase(1, 1); + return true; + } + + // Reverse the gdb-remote binary escaping that was done to the compressed + // text to guard characters like '$', '#', '}', etc. + std::vector<uint8_t> unescaped_content; + unescaped_content.reserve(content_length); + size_t i = content_start; + while (i < hash_mark_idx) { + if (m_bytes[i] == '}') { + i++; + unescaped_content.push_back(m_bytes[i] ^ 0x20); + } else { + unescaped_content.push_back(m_bytes[i]); + } + i++; + } + + uint8_t *decompressed_buffer = nullptr; + size_t decompressed_bytes = 0; + + if (decompressed_bufsize != ULONG_MAX) { + decompressed_buffer = (uint8_t *)malloc(decompressed_bufsize); + if (decompressed_buffer == nullptr) { + m_bytes.erase(0, size_of_first_packet); + return false; + } + } + +#if defined(HAVE_LIBCOMPRESSION) + if (m_compression_type == CompressionType::ZlibDeflate || + m_compression_type == CompressionType::LZFSE || + m_compression_type == CompressionType::LZ4 || + m_compression_type == CompressionType::LZMA) { + compression_algorithm compression_type; + if (m_compression_type == CompressionType::LZFSE) + compression_type = COMPRESSION_LZFSE; + else if (m_compression_type == CompressionType::ZlibDeflate) + compression_type = COMPRESSION_ZLIB; + else if (m_compression_type == CompressionType::LZ4) + compression_type = COMPRESSION_LZ4_RAW; + else if (m_compression_type == CompressionType::LZMA) + compression_type = COMPRESSION_LZMA; + + if (m_decompression_scratch_type != m_compression_type) { + if (m_decompression_scratch) { + free (m_decompression_scratch); + m_decompression_scratch = nullptr; + } + size_t scratchbuf_size = 0; + if (m_compression_type == CompressionType::LZFSE) + scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZFSE); + else if (m_compression_type == CompressionType::LZ4) + scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZ4_RAW); + else if (m_compression_type == CompressionType::ZlibDeflate) + scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_ZLIB); + else if (m_compression_type == CompressionType::LZMA) + scratchbuf_size = + compression_decode_scratch_buffer_size(COMPRESSION_LZMA); + if (scratchbuf_size > 0) { + m_decompression_scratch = (void*) malloc (scratchbuf_size); + m_decompression_scratch_type = m_compression_type; + } + } + + if (decompressed_bufsize != ULONG_MAX && decompressed_buffer != nullptr) { + decompressed_bytes = compression_decode_buffer( + decompressed_buffer, decompressed_bufsize, + (uint8_t *)unescaped_content.data(), unescaped_content.size(), + m_decompression_scratch, compression_type); + } + } +#endif + +#if LLVM_ENABLE_ZLIB + if (decompressed_bytes == 0 && decompressed_bufsize != ULONG_MAX && + decompressed_buffer != nullptr && + m_compression_type == CompressionType::ZlibDeflate) { + z_stream stream; + memset(&stream, 0, sizeof(z_stream)); + stream.next_in = (Bytef *)unescaped_content.data(); + stream.avail_in = (uInt)unescaped_content.size(); + stream.total_in = 0; + stream.next_out = (Bytef *)decompressed_buffer; + stream.avail_out = decompressed_bufsize; + stream.total_out = 0; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + + if (inflateInit2(&stream, -15) == Z_OK) { + int status = inflate(&stream, Z_NO_FLUSH); + inflateEnd(&stream); + if (status == Z_STREAM_END) { + decompressed_bytes = stream.total_out; + } + } + } +#endif + + if (decompressed_bytes == 0 || decompressed_buffer == nullptr) { + if (decompressed_buffer) + free(decompressed_buffer); + m_bytes.erase(0, size_of_first_packet); + return false; + } + + std::string new_packet; + new_packet.reserve(decompressed_bytes + 6); + new_packet.push_back(m_bytes[0]); + new_packet.append((const char *)decompressed_buffer, decompressed_bytes); + new_packet.push_back('#'); + if (GetSendAcks()) { + uint8_t decompressed_checksum = CalculcateChecksum( + llvm::StringRef((const char *)decompressed_buffer, decompressed_bytes)); + char decompressed_checksum_str[3]; + snprintf(decompressed_checksum_str, 3, "%02x", decompressed_checksum); + new_packet.append(decompressed_checksum_str); + } else { + new_packet.push_back('0'); + new_packet.push_back('0'); + } + + m_bytes.replace(0, size_of_first_packet, new_packet.data(), + new_packet.size()); + + free(decompressed_buffer); + return true; +} + +GDBRemoteCommunication::PacketType +GDBRemoteCommunication::CheckForPacket(const uint8_t *src, size_t src_len, + StringExtractorGDBRemote &packet) { + // Put the packet data into the buffer in a thread safe fashion + std::lock_guard<std::recursive_mutex> guard(m_bytes_mutex); + + Log *log = GetLog(GDBRLog::Packets); + + if (src && src_len > 0) { + if (log && log->GetVerbose()) { + StreamString s; + LLDB_LOGF(log, "GDBRemoteCommunication::%s adding %u bytes: %.*s", + __FUNCTION__, (uint32_t)src_len, (uint32_t)src_len, src); + } + m_bytes.append((const char *)src, src_len); + } + + bool isNotifyPacket = false; + + // Parse up the packets into gdb remote packets + if (!m_bytes.empty()) { + // end_idx must be one past the last valid packet byte. Start it off with + // an invalid value that is the same as the current index. + size_t content_start = 0; + size_t content_length = 0; + size_t total_length = 0; + size_t checksum_idx = std::string::npos; + + // Size of packet before it is decompressed, for logging purposes + size_t original_packet_size = m_bytes.size(); + if (CompressionIsEnabled()) { + if (!DecompressPacket()) { + packet.Clear(); + return GDBRemoteCommunication::PacketType::Standard; + } + } + + switch (m_bytes[0]) { + case '+': // Look for ack + case '-': // Look for cancel + case '\x03': // ^C to halt target + content_length = total_length = 1; // The command is one byte long... + break; + + case '%': // Async notify packet + isNotifyPacket = true; + [[fallthrough]]; + + case '$': + // Look for a standard gdb packet? + { + size_t hash_pos = m_bytes.find('#'); + if (hash_pos != std::string::npos) { + if (hash_pos + 2 < m_bytes.size()) { + checksum_idx = hash_pos + 1; + // Skip the dollar sign + content_start = 1; + // Don't include the # in the content or the $ in the content + // length + content_length = hash_pos - 1; + + total_length = + hash_pos + 3; // Skip the # and the two hex checksum bytes + } else { + // Checksum bytes aren't all here yet + content_length = std::string::npos; + } + } + } + break; + + default: { + // We have an unexpected byte and we need to flush all bad data that is + // in m_bytes, so we need to find the first byte that is a '+' (ACK), '-' + // (NACK), \x03 (CTRL+C interrupt), or '$' character (start of packet + // header) or of course, the end of the data in m_bytes... + const size_t bytes_len = m_bytes.size(); + bool done = false; + uint32_t idx; + for (idx = 1; !done && idx < bytes_len; ++idx) { + switch (m_bytes[idx]) { + case '+': + case '-': + case '\x03': + case '%': + case '$': + done = true; + break; + + default: + break; + } + } + LLDB_LOGF(log, "GDBRemoteCommunication::%s tossing %u junk bytes: '%.*s'", + __FUNCTION__, idx - 1, idx - 1, m_bytes.c_str()); + m_bytes.erase(0, idx - 1); + } break; + } + + if (content_length == std::string::npos) { + packet.Clear(); + return GDBRemoteCommunication::PacketType::Invalid; + } else if (total_length > 0) { + + // We have a valid packet... + assert(content_length <= m_bytes.size()); + assert(total_length <= m_bytes.size()); + assert(content_length <= total_length); + size_t content_end = content_start + content_length; + + bool success = true; + if (log) { + // If logging was just enabled and we have history, then dump out what + // we have to the log so we get the historical context. The Dump() call + // that logs all of the packet will set a boolean so that we don't dump + // this more than once + if (!m_history.DidDumpToLog()) + m_history.Dump(log); + + bool binary = false; + // Only detect binary for packets that start with a '$' and have a + // '#CC' checksum + if (m_bytes[0] == '$' && total_length > 4) { + for (size_t i = 0; !binary && i < total_length; ++i) { + unsigned char c = m_bytes[i]; + if (!llvm::isPrint(c) && !llvm::isSpace(c)) { + binary = true; + } + } + } + if (binary) { + StreamString strm; + // Packet header... + if (CompressionIsEnabled()) + strm.Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %c", + (uint64_t)original_packet_size, (uint64_t)total_length, + m_bytes[0]); + else + strm.Printf("<%4" PRIu64 "> read packet: %c", + (uint64_t)total_length, m_bytes[0]); + for (size_t i = content_start; i < content_end; ++i) { + // Remove binary escaped bytes when displaying the packet... + const char ch = m_bytes[i]; + if (ch == 0x7d) { + // 0x7d is the escape character. The next character is to be + // XOR'd with 0x20. + const char escapee = m_bytes[++i] ^ 0x20; + strm.Printf("%2.2x", escapee); + } else { + strm.Printf("%2.2x", (uint8_t)ch); + } + } + // Packet footer... + strm.Printf("%c%c%c", m_bytes[total_length - 3], + m_bytes[total_length - 2], m_bytes[total_length - 1]); + log->PutString(strm.GetString()); + } else { + if (CompressionIsEnabled()) + LLDB_LOGF(log, "<%4" PRIu64 ":%" PRIu64 "> read packet: %.*s", + (uint64_t)original_packet_size, (uint64_t)total_length, + (int)(total_length), m_bytes.c_str()); + else + LLDB_LOGF(log, "<%4" PRIu64 "> read packet: %.*s", + (uint64_t)total_length, (int)(total_length), + m_bytes.c_str()); + } + } + + m_history.AddPacket(m_bytes, total_length, + GDBRemotePacket::ePacketTypeRecv, total_length); + + // Copy the packet from m_bytes to packet_str expanding the run-length + // encoding in the process. + std ::string packet_str = + ExpandRLE(m_bytes.substr(content_start, content_end - content_start)); + packet = StringExtractorGDBRemote(packet_str); + + if (m_bytes[0] == '$' || m_bytes[0] == '%') { + assert(checksum_idx < m_bytes.size()); + if (::isxdigit(m_bytes[checksum_idx + 0]) || + ::isxdigit(m_bytes[checksum_idx + 1])) { + if (GetSendAcks()) { + const char *packet_checksum_cstr = &m_bytes[checksum_idx]; + char packet_checksum = strtol(packet_checksum_cstr, nullptr, 16); + char actual_checksum = CalculcateChecksum( + llvm::StringRef(m_bytes).slice(content_start, content_end)); + success = packet_checksum == actual_checksum; + if (!success) { + LLDB_LOGF(log, + "error: checksum mismatch: %.*s expected 0x%2.2x, " + "got 0x%2.2x", + (int)(total_length), m_bytes.c_str(), + (uint8_t)packet_checksum, (uint8_t)actual_checksum); + } + // Send the ack or nack if needed + if (!success) + SendNack(); + else + SendAck(); + } + } else { + success = false; + LLDB_LOGF(log, "error: invalid checksum in packet: '%s'\n", + m_bytes.c_str()); + } + } + + m_bytes.erase(0, total_length); + packet.SetFilePos(0); + + if (isNotifyPacket) + return GDBRemoteCommunication::PacketType::Notify; + else + return GDBRemoteCommunication::PacketType::Standard; + } + } + packet.Clear(); + return GDBRemoteCommunication::PacketType::Invalid; +} + +Status GDBRemoteCommunication::StartListenThread(const char *hostname, + uint16_t port) { + if (m_listen_thread.IsJoinable()) + return Status("listen thread already running"); + + char listen_url[512]; + if (hostname && hostname[0]) + snprintf(listen_url, sizeof(listen_url), "listen://%s:%i", hostname, port); + else + snprintf(listen_url, sizeof(listen_url), "listen://%i", port); + m_listen_url = listen_url; + SetConnection(std::make_unique<ConnectionFileDescriptor>()); + llvm::Expected<HostThread> listen_thread = ThreadLauncher::LaunchThread( + listen_url, [this] { return GDBRemoteCommunication::ListenThread(); }); + if (!listen_thread) + return Status(listen_thread.takeError()); + m_listen_thread = *listen_thread; + + return Status(); +} + +bool GDBRemoteCommunication::JoinListenThread() { + if (m_listen_thread.IsJoinable()) + m_listen_thread.Join(nullptr); + return true; +} + +lldb::thread_result_t GDBRemoteCommunication::ListenThread() { + Status error; + ConnectionFileDescriptor *connection = + (ConnectionFileDescriptor *)GetConnection(); + + if (connection) { + // Do the listen on another thread so we can continue on... + if (connection->Connect( + m_listen_url.c_str(), + [this](llvm::StringRef port_str) { + uint16_t port = 0; + llvm::to_integer(port_str, port, 10); + m_port_promise.set_value(port); + }, + &error) != eConnectionStatusSuccess) + SetConnection(nullptr); + } + return {}; +} + +Status GDBRemoteCommunication::StartDebugserverProcess( + const char *url, Platform *platform, ProcessLaunchInfo &launch_info, + uint16_t *port, const Args *inferior_args, int pass_comm_fd) { + Log *log = GetLog(GDBRLog::Process); + LLDB_LOGF(log, "GDBRemoteCommunication::%s(url=%s, port=%" PRIu16 ")", + __FUNCTION__, url ? url : "<empty>", port ? *port : uint16_t(0)); + + Status error; + // If we locate debugserver, keep that located version around + static FileSpec g_debugserver_file_spec; + + char debugserver_path[PATH_MAX]; + FileSpec &debugserver_file_spec = launch_info.GetExecutableFile(); + + Environment host_env = Host::GetEnvironment(); + + // Always check to see if we have an environment override for the path to the + // debugserver to use and use it if we do. + std::string env_debugserver_path = host_env.lookup("LLDB_DEBUGSERVER_PATH"); + if (!env_debugserver_path.empty()) { + debugserver_file_spec.SetFile(env_debugserver_path, + FileSpec::Style::native); + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() gdb-remote stub exe path set " + "from environment variable: %s", + __FUNCTION__, env_debugserver_path.c_str()); + } else + debugserver_file_spec = g_debugserver_file_spec; + bool debugserver_exists = + FileSystem::Instance().Exists(debugserver_file_spec); + if (!debugserver_exists) { + // The debugserver binary is in the LLDB.framework/Resources directory. + debugserver_file_spec = HostInfo::GetSupportExeDir(); + if (debugserver_file_spec) { + debugserver_file_spec.AppendPathComponent(DEBUGSERVER_BASENAME); + debugserver_exists = FileSystem::Instance().Exists(debugserver_file_spec); + if (debugserver_exists) { + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() found gdb-remote stub exe '%s'", + __FUNCTION__, debugserver_file_spec.GetPath().c_str()); + + g_debugserver_file_spec = debugserver_file_spec; + } else { + if (platform) + debugserver_file_spec = + platform->LocateExecutable(DEBUGSERVER_BASENAME); + else + debugserver_file_spec.Clear(); + if (debugserver_file_spec) { + // Platform::LocateExecutable() wouldn't return a path if it doesn't + // exist + debugserver_exists = true; + } else { + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() could not find " + "gdb-remote stub exe '%s'", + __FUNCTION__, debugserver_file_spec.GetPath().c_str()); + } + // Don't cache the platform specific GDB server binary as it could + // change from platform to platform + g_debugserver_file_spec.Clear(); + } + } + } + + if (debugserver_exists) { + debugserver_file_spec.GetPath(debugserver_path, sizeof(debugserver_path)); + + Args &debugserver_args = launch_info.GetArguments(); + debugserver_args.Clear(); + + // Start args with "debugserver /file/path -r --" + debugserver_args.AppendArgument(llvm::StringRef(debugserver_path)); + +#if !defined(__APPLE__) + // First argument to lldb-server must be mode in which to run. + debugserver_args.AppendArgument(llvm::StringRef("gdbserver")); +#endif + + // If a url is supplied then use it + if (url) + debugserver_args.AppendArgument(llvm::StringRef(url)); + + if (pass_comm_fd >= 0) { + StreamString fd_arg; + fd_arg.Printf("--fd=%i", pass_comm_fd); + debugserver_args.AppendArgument(fd_arg.GetString()); + // Send "pass_comm_fd" down to the inferior so it can use it to + // communicate back with this process + launch_info.AppendDuplicateFileAction(pass_comm_fd, pass_comm_fd); + } + + // use native registers, not the GDB registers + debugserver_args.AppendArgument(llvm::StringRef("--native-regs")); + + if (launch_info.GetLaunchInSeparateProcessGroup()) { + debugserver_args.AppendArgument(llvm::StringRef("--setsid")); + } + + llvm::SmallString<128> named_pipe_path; + // socket_pipe is used by debug server to communicate back either + // TCP port or domain socket name which it listens on. + // The second purpose of the pipe to serve as a synchronization point - + // once data is written to the pipe, debug server is up and running. + Pipe socket_pipe; + + // port is null when debug server should listen on domain socket - we're + // not interested in port value but rather waiting for debug server to + // become available. + if (pass_comm_fd == -1) { + if (url) { +// Create a temporary file to get the stdout/stderr and redirect the output of +// the command into this file. We will later read this file if all goes well +// and fill the data into "command_output_ptr" +#if defined(__APPLE__) + // Binding to port zero, we need to figure out what port it ends up + // using using a named pipe... + error = socket_pipe.CreateWithUniqueName("debugserver-named-pipe", + false, named_pipe_path); + if (error.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() " + "named pipe creation failed: %s", + __FUNCTION__, error.AsCString()); + return error; + } + debugserver_args.AppendArgument(llvm::StringRef("--named-pipe")); + debugserver_args.AppendArgument(named_pipe_path); +#else + // Binding to port zero, we need to figure out what port it ends up + // using using an unnamed pipe... + error = socket_pipe.CreateNew(true); + if (error.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() " + "unnamed pipe creation failed: %s", + __FUNCTION__, error.AsCString()); + return error; + } + pipe_t write = socket_pipe.GetWritePipe(); + debugserver_args.AppendArgument(llvm::StringRef("--pipe")); + debugserver_args.AppendArgument(llvm::to_string(write)); + launch_info.AppendCloseFileAction(socket_pipe.GetReadFileDescriptor()); +#endif + } else { + // No host and port given, so lets listen on our end and make the + // debugserver connect to us.. + error = StartListenThread("127.0.0.1", 0); + if (error.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() unable to start listen " + "thread: %s", + __FUNCTION__, error.AsCString()); + return error; + } + + // Wait for 10 seconds to resolve the bound port + std::future<uint16_t> port_future = m_port_promise.get_future(); + uint16_t port_ = port_future.wait_for(std::chrono::seconds(10)) == + std::future_status::ready + ? port_future.get() + : 0; + if (port_ > 0) { + char port_cstr[32]; + snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", port_); + // Send the host and port down that debugserver and specify an option + // so that it connects back to the port we are listening to in this + // process + debugserver_args.AppendArgument(llvm::StringRef("--reverse-connect")); + debugserver_args.AppendArgument(llvm::StringRef(port_cstr)); + if (port) + *port = port_; + } else { + error.SetErrorString("failed to bind to port 0 on 127.0.0.1"); + LLDB_LOGF(log, "GDBRemoteCommunication::%s() failed: %s", + __FUNCTION__, error.AsCString()); + return error; + } + } + } + std::string env_debugserver_log_file = + host_env.lookup("LLDB_DEBUGSERVER_LOG_FILE"); + if (!env_debugserver_log_file.empty()) { + debugserver_args.AppendArgument( + llvm::formatv("--log-file={0}", env_debugserver_log_file).str()); + } + +#if defined(__APPLE__) + const char *env_debugserver_log_flags = + getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); + if (env_debugserver_log_flags) { + debugserver_args.AppendArgument( + llvm::formatv("--log-flags={0}", env_debugserver_log_flags).str()); + } +#else + std::string env_debugserver_log_channels = + host_env.lookup("LLDB_SERVER_LOG_CHANNELS"); + if (!env_debugserver_log_channels.empty()) { + debugserver_args.AppendArgument( + llvm::formatv("--log-channels={0}", env_debugserver_log_channels) + .str()); + } +#endif + + // Add additional args, starting with LLDB_DEBUGSERVER_EXTRA_ARG_1 until an + // env var doesn't come back. + uint32_t env_var_index = 1; + bool has_env_var; + do { + char env_var_name[64]; + snprintf(env_var_name, sizeof(env_var_name), + "LLDB_DEBUGSERVER_EXTRA_ARG_%" PRIu32, env_var_index++); + std::string extra_arg = host_env.lookup(env_var_name); + has_env_var = !extra_arg.empty(); + + if (has_env_var) { + debugserver_args.AppendArgument(llvm::StringRef(extra_arg)); + LLDB_LOGF(log, + "GDBRemoteCommunication::%s adding env var %s contents " + "to stub command line (%s)", + __FUNCTION__, env_var_name, extra_arg.c_str()); + } + } while (has_env_var); + + if (inferior_args && inferior_args->GetArgumentCount() > 0) { + debugserver_args.AppendArgument(llvm::StringRef("--")); + debugserver_args.AppendArguments(*inferior_args); + } + + // Copy the current environment to the gdbserver/debugserver instance + launch_info.GetEnvironment() = host_env; + + // Close STDIN, STDOUT and STDERR. + launch_info.AppendCloseFileAction(STDIN_FILENO); + launch_info.AppendCloseFileAction(STDOUT_FILENO); + launch_info.AppendCloseFileAction(STDERR_FILENO); + + // Redirect STDIN, STDOUT and STDERR to "/dev/null". + launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false); + launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true); + launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true); + + if (log) { + StreamString string_stream; + Platform *const platform = nullptr; + launch_info.Dump(string_stream, platform); + LLDB_LOGF(log, "launch info for gdb-remote stub:\n%s", + string_stream.GetData()); + } + error = Host::LaunchProcess(launch_info); + + if (error.Success() && + (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) && + pass_comm_fd == -1) { + if (named_pipe_path.size() > 0) { + error = socket_pipe.OpenAsReader(named_pipe_path, false); + if (error.Fail()) + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() " + "failed to open named pipe %s for reading: %s", + __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); + } + + if (socket_pipe.CanWrite()) + socket_pipe.CloseWriteFileDescriptor(); + if (socket_pipe.CanRead()) { + char port_cstr[PATH_MAX] = {0}; + port_cstr[0] = '\0'; + size_t num_bytes = sizeof(port_cstr); + // Read port from pipe with 10 second timeout. + error = socket_pipe.ReadWithTimeout( + port_cstr, num_bytes, std::chrono::seconds{10}, num_bytes); + if (error.Success() && (port != nullptr)) { + assert(num_bytes > 0 && port_cstr[num_bytes - 1] == '\0'); + uint16_t child_port = 0; + // FIXME: improve error handling + llvm::to_integer(port_cstr, child_port); + if (*port == 0 || *port == child_port) { + *port = child_port; + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() " + "debugserver listens %u port", + __FUNCTION__, *port); + } else { + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() " + "debugserver listening on port " + "%d but requested port was %d", + __FUNCTION__, (uint32_t)child_port, (uint32_t)(*port)); + } + } else { + LLDB_LOGF(log, + "GDBRemoteCommunication::%s() " + "failed to read a port value from pipe %s: %s", + __FUNCTION__, named_pipe_path.c_str(), error.AsCString()); + } + socket_pipe.Close(); + } + + if (named_pipe_path.size() > 0) { + const auto err = socket_pipe.Delete(named_pipe_path); + if (err.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunication::%s failed to delete pipe %s: %s", + __FUNCTION__, named_pipe_path.c_str(), err.AsCString()); + } + } + + // Make sure we actually connect with the debugserver... + JoinListenThread(); + } + } else { + error.SetErrorStringWithFormat("unable to locate " DEBUGSERVER_BASENAME); + } + + if (error.Fail()) { + LLDB_LOGF(log, "GDBRemoteCommunication::%s() failed: %s", __FUNCTION__, + error.AsCString()); + } + + return error; +} + +void GDBRemoteCommunication::DumpHistory(Stream &strm) { m_history.Dump(strm); } + +llvm::Error +GDBRemoteCommunication::ConnectLocally(GDBRemoteCommunication &client, + GDBRemoteCommunication &server) { + const bool child_processes_inherit = false; + const int backlog = 5; + TCPSocket listen_socket(true, child_processes_inherit); + if (llvm::Error error = + listen_socket.Listen("localhost:0", backlog).ToError()) + return error; + + Socket *accept_socket = nullptr; + std::future<Status> accept_status = std::async( + std::launch::async, [&] { return listen_socket.Accept(accept_socket); }); + + llvm::SmallString<32> remote_addr; + llvm::raw_svector_ostream(remote_addr) + << "connect://localhost:" << listen_socket.GetLocalPortNumber(); + + std::unique_ptr<ConnectionFileDescriptor> conn_up( + new ConnectionFileDescriptor()); + Status status; + if (conn_up->Connect(remote_addr, &status) != lldb::eConnectionStatusSuccess) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unable to connect: %s", status.AsCString()); + + client.SetConnection(std::move(conn_up)); + if (llvm::Error error = accept_status.get().ToError()) + return error; + + server.SetConnection( + std::make_unique<ConnectionFileDescriptor>(accept_socket)); + return llvm::Error::success(); +} + +GDBRemoteCommunication::ScopedTimeout::ScopedTimeout( + GDBRemoteCommunication &gdb_comm, std::chrono::seconds timeout) + : m_gdb_comm(gdb_comm), m_saved_timeout(0), m_timeout_modified(false) { + auto curr_timeout = gdb_comm.GetPacketTimeout(); + // Only update the timeout if the timeout is greater than the current + // timeout. If the current timeout is larger, then just use that. + if (curr_timeout < timeout) { + m_timeout_modified = true; + m_saved_timeout = m_gdb_comm.SetPacketTimeout(timeout); + } +} + +GDBRemoteCommunication::ScopedTimeout::~ScopedTimeout() { + // Only restore the timeout if we set it in the constructor. + if (m_timeout_modified) + m_gdb_comm.SetPacketTimeout(m_saved_timeout); +} + +void llvm::format_provider<GDBRemoteCommunication::PacketResult>::format( + const GDBRemoteCommunication::PacketResult &result, raw_ostream &Stream, + StringRef Style) { + using PacketResult = GDBRemoteCommunication::PacketResult; + + switch (result) { + case PacketResult::Success: + Stream << "Success"; + break; + case PacketResult::ErrorSendFailed: + Stream << "ErrorSendFailed"; + break; + case PacketResult::ErrorSendAck: + Stream << "ErrorSendAck"; + break; + case PacketResult::ErrorReplyFailed: + Stream << "ErrorReplyFailed"; + break; + case PacketResult::ErrorReplyTimeout: + Stream << "ErrorReplyTimeout"; + break; + case PacketResult::ErrorReplyInvalid: + Stream << "ErrorReplyInvalid"; + break; + case PacketResult::ErrorReplyAck: + Stream << "ErrorReplyAck"; + break; + case PacketResult::ErrorDisconnected: + Stream << "ErrorDisconnected"; + break; + case PacketResult::ErrorNoSequenceLock: + Stream << "ErrorNoSequenceLock"; + break; + } +} + +std::string GDBRemoteCommunication::ExpandRLE(std::string packet) { + // Reserve enough byte for the most common case (no RLE used). + std::string decoded; + decoded.reserve(packet.size()); + for (std::string::const_iterator c = packet.begin(); c != packet.end(); ++c) { + if (*c == '*') { + // '*' indicates RLE. Next character will give us the repeat count and + // previous character is what is to be repeated. + char char_to_repeat = decoded.back(); + // Number of time the previous character is repeated. + int repeat_count = *++c + 3 - ' '; + // We have the char_to_repeat and repeat_count. Now push it in the + // packet. + for (int i = 0; i < repeat_count; ++i) + decoded.push_back(char_to_repeat); + } else if (*c == 0x7d) { + // 0x7d is the escape character. The next character is to be XOR'd with + // 0x20. + char escapee = *++c ^ 0x20; + decoded.push_back(escapee); + } else { + decoded.push_back(*c); + } + } + return decoded; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h new file mode 100644 index 000000000000..4e59bd5ec8be --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -0,0 +1,249 @@ +//===-- GDBRemoteCommunication.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATION_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATION_H + +#include "GDBRemoteCommunicationHistory.h" + +#include <condition_variable> +#include <future> +#include <mutex> +#include <queue> +#include <string> +#include <vector> + +#include "lldb/Core/Communication.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Listener.h" +#include "lldb/Utility/Predicate.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" +#include "lldb/lldb-public.h" + +namespace lldb_private { +namespace repro { +class PacketRecorder; +} +namespace process_gdb_remote { + +enum GDBStoppointType { + eStoppointInvalid = -1, + eBreakpointSoftware = 0, + eBreakpointHardware, + eWatchpointWrite, + eWatchpointRead, + eWatchpointReadWrite +}; + +enum class CompressionType { + None = 0, // no compression + ZlibDeflate, // zlib's deflate compression scheme, requires zlib or Apple's + // libcompression + LZFSE, // an Apple compression scheme, requires Apple's libcompression + LZ4, // lz compression - called "lz4 raw" in libcompression terms, compat with + // https://code.google.com/p/lz4/ + LZMA, // Lempel–Ziv–Markov chain algorithm +}; + +// Data included in the vFile:fstat packet. +// https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat +struct GDBRemoteFStatData { + llvm::support::ubig32_t gdb_st_dev; + llvm::support::ubig32_t gdb_st_ino; + llvm::support::ubig32_t gdb_st_mode; + llvm::support::ubig32_t gdb_st_nlink; + llvm::support::ubig32_t gdb_st_uid; + llvm::support::ubig32_t gdb_st_gid; + llvm::support::ubig32_t gdb_st_rdev; + llvm::support::ubig64_t gdb_st_size; + llvm::support::ubig64_t gdb_st_blksize; + llvm::support::ubig64_t gdb_st_blocks; + llvm::support::ubig32_t gdb_st_atime; + llvm::support::ubig32_t gdb_st_mtime; + llvm::support::ubig32_t gdb_st_ctime; +}; +static_assert(sizeof(GDBRemoteFStatData) == 64, + "size of GDBRemoteFStatData is not 64"); + +enum GDBErrno { +#define HANDLE_ERRNO(name, value) GDB_##name = value, +#include "Plugins/Process/gdb-remote/GDBRemoteErrno.def" + GDB_EUNKNOWN = 9999 +}; + +class ProcessGDBRemote; + +class GDBRemoteCommunication : public Communication { +public: + enum class PacketType { Invalid = 0, Standard, Notify }; + + enum class PacketResult { + Success = 0, // Success + ErrorSendFailed, // Status sending the packet + ErrorSendAck, // Didn't get an ack back after sending a packet + ErrorReplyFailed, // Status getting the reply + ErrorReplyTimeout, // Timed out waiting for reply + ErrorReplyInvalid, // Got a reply but it wasn't valid for the packet that + // was sent + ErrorReplyAck, // Sending reply ack failed + ErrorDisconnected, // We were disconnected + ErrorNoSequenceLock // We couldn't get the sequence lock for a multi-packet + // request + }; + + // Class to change the timeout for a given scope and restore it to the + // original value when the + // created ScopedTimeout object got out of scope + class ScopedTimeout { + public: + ScopedTimeout(GDBRemoteCommunication &gdb_comm, + std::chrono::seconds timeout); + ~ScopedTimeout(); + + private: + GDBRemoteCommunication &m_gdb_comm; + std::chrono::seconds m_saved_timeout; + // Don't ever reduce the timeout for a packet, only increase it. If the + // requested timeout if less than the current timeout, we don't set it + // and won't need to restore it. + bool m_timeout_modified; + }; + + GDBRemoteCommunication(); + + ~GDBRemoteCommunication() override; + + PacketResult GetAck(); + + size_t SendAck(); + + size_t SendNack(); + + char CalculcateChecksum(llvm::StringRef payload); + + PacketType CheckForPacket(const uint8_t *src, size_t src_len, + StringExtractorGDBRemote &packet); + + bool GetSendAcks() { return m_send_acks; } + + // Set the global packet timeout. + // + // For clients, this is the timeout that gets used when sending + // packets and waiting for responses. For servers, this is used when waiting + // for ACKs. + std::chrono::seconds SetPacketTimeout(std::chrono::seconds packet_timeout) { + const auto old_packet_timeout = m_packet_timeout; + m_packet_timeout = packet_timeout; + return old_packet_timeout; + } + + std::chrono::seconds GetPacketTimeout() const { return m_packet_timeout; } + + // Start a debugserver instance on the current host using the + // supplied connection URL. + Status StartDebugserverProcess( + const char *url, + Platform *platform, // If non nullptr, then check with the platform for + // the GDB server binary if it can't be located + ProcessLaunchInfo &launch_info, uint16_t *port, const Args *inferior_args, + int pass_comm_fd); // Communication file descriptor to pass during + // fork/exec to avoid having to connect/accept + + void DumpHistory(Stream &strm); + + void SetPacketRecorder(repro::PacketRecorder *recorder); + + static llvm::Error ConnectLocally(GDBRemoteCommunication &client, + GDBRemoteCommunication &server); + + /// Expand GDB run-length encoding. + static std::string ExpandRLE(std::string); + +protected: + std::chrono::seconds m_packet_timeout; + uint32_t m_echo_number; + LazyBool m_supports_qEcho; + GDBRemoteCommunicationHistory m_history; + bool m_send_acks; + bool m_is_platform; // Set to true if this class represents a platform, + // false if this class represents a debug session for + // a single process + + std::string m_bytes; + std::recursive_mutex m_bytes_mutex; + CompressionType m_compression_type; + + PacketResult SendPacketNoLock(llvm::StringRef payload); + PacketResult SendNotificationPacketNoLock(llvm::StringRef notify_type, + std::deque<std::string>& queue, + llvm::StringRef payload); + PacketResult SendRawPacketNoLock(llvm::StringRef payload, + bool skip_ack = false); + + PacketResult ReadPacket(StringExtractorGDBRemote &response, + Timeout<std::micro> timeout, bool sync_on_timeout); + + PacketResult WaitForPacketNoLock(StringExtractorGDBRemote &response, + Timeout<std::micro> timeout, + bool sync_on_timeout); + + bool CompressionIsEnabled() { + return m_compression_type != CompressionType::None; + } + + // If compression is enabled, decompress the packet in m_bytes and update + // m_bytes with the uncompressed version. + // Returns 'true' packet was decompressed and m_bytes is the now-decompressed + // text. + // Returns 'false' if unable to decompress or if the checksum was invalid. + // + // NB: Once the packet has been decompressed, checksum cannot be computed + // based + // on m_bytes. The checksum was for the compressed packet. + bool DecompressPacket(); + + Status StartListenThread(const char *hostname = "127.0.0.1", + uint16_t port = 0); + + bool JoinListenThread(); + + lldb::thread_result_t ListenThread(); + +private: + // Promise used to grab the port number from listening thread + std::promise<uint16_t> m_port_promise; + + HostThread m_listen_thread; + std::string m_listen_url; + +#if defined(HAVE_LIBCOMPRESSION) + CompressionType m_decompression_scratch_type = CompressionType::None; + void *m_decompression_scratch = nullptr; +#endif + + GDBRemoteCommunication(const GDBRemoteCommunication &) = delete; + const GDBRemoteCommunication & + operator=(const GDBRemoteCommunication &) = delete; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +namespace llvm { +template <> +struct format_provider< + lldb_private::process_gdb_remote::GDBRemoteCommunication::PacketResult> { + static void format(const lldb_private::process_gdb_remote:: + GDBRemoteCommunication::PacketResult &state, + raw_ostream &Stream, StringRef Style); +}; +} // namespace llvm + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATION_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp new file mode 100644 index 000000000000..74e392249a94 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -0,0 +1,4353 @@ +//===-- GDBRemoteCommunicationClient.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 "GDBRemoteCommunicationClient.h" + +#include <cmath> +#include <sys/stat.h> + +#include <numeric> +#include <optional> +#include <sstream> + +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/SafeMachO.h" +#include "lldb/Host/XML.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" + +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "lldb/Host/Config.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/JSON.h" + +#if defined(HAVE_LIBCOMPRESSION) +#include <compression.h> +#endif + +using namespace lldb; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private; +using namespace std::chrono; + +llvm::raw_ostream &process_gdb_remote::operator<<(llvm::raw_ostream &os, + const QOffsets &offsets) { + return os << llvm::formatv( + "QOffsets({0}, [{1:@[x]}])", offsets.segments, + llvm::make_range(offsets.offsets.begin(), offsets.offsets.end())); +} + +// GDBRemoteCommunicationClient constructor +GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() + : GDBRemoteClientBase("gdb-remote.client"), + + m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true), + m_supports_qUserName(true), m_supports_qGroupName(true), + m_supports_qThreadStopInfo(true), m_supports_z0(true), + m_supports_z1(true), m_supports_z2(true), m_supports_z3(true), + m_supports_z4(true), m_supports_QEnvironment(true), + m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true), + m_qSymbol_requests_done(false), m_supports_qModuleInfo(true), + m_supports_jThreadsInfo(true), m_supports_jModulesInfo(true), + m_supports_vFileSize(true), m_supports_vFileMode(true), + m_supports_vFileExists(true), m_supports_vRun(true), + + m_host_arch(), m_host_distribution_id(), m_process_arch(), m_os_build(), + m_os_kernel(), m_hostname(), m_gdb_server_name(), + m_default_packet_timeout(0), m_qSupported_response(), + m_supported_async_json_packets_sp(), m_qXfer_memory_map() {} + +// Destructor +GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() { + if (IsConnected()) + Disconnect(); +} + +bool GDBRemoteCommunicationClient::HandshakeWithServer(Status *error_ptr) { + ResetDiscoverableSettings(false); + + // Start the read thread after we send the handshake ack since if we fail to + // send the handshake ack, there is no reason to continue... + std::chrono::steady_clock::time_point start_of_handshake = + std::chrono::steady_clock::now(); + if (SendAck()) { + // The return value from QueryNoAckModeSupported() is true if the packet + // was sent and _any_ response (including UNIMPLEMENTED) was received), or + // false if no response was received. This quickly tells us if we have a + // live connection to a remote GDB server... + if (QueryNoAckModeSupported()) { + return true; + } else { + std::chrono::steady_clock::time_point end_of_handshake = + std::chrono::steady_clock::now(); + auto handshake_timeout = + std::chrono::duration<double>(end_of_handshake - start_of_handshake) + .count(); + if (error_ptr) { + if (!IsConnected()) + error_ptr->SetErrorString("Connection shut down by remote side " + "while waiting for reply to initial " + "handshake packet"); + else + error_ptr->SetErrorStringWithFormat( + "failed to get reply to handshake packet within timeout of " + "%.1f seconds", + handshake_timeout); + } + } + } else { + if (error_ptr) + error_ptr->SetErrorString("failed to send the handshake ack"); + } + return false; +} + +bool GDBRemoteCommunicationClient::GetEchoSupported() { + if (m_supports_qEcho == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qEcho == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQPassSignalsSupported() { + if (m_supports_QPassSignals == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_QPassSignals == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported() { + if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_augmented_libraries_svr4_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported() { + if (m_supports_qXfer_libraries_svr4_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported() { + if (m_supports_qXfer_libraries_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_libraries_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferAuxvReadSupported() { + if (m_supports_qXfer_auxv_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_auxv_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() { + if (m_supports_qXfer_features_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_features_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferMemoryMapReadSupported() { + if (m_supports_qXfer_memory_map_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_memory_map_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetQXferSigInfoReadSupported() { + if (m_supports_qXfer_siginfo_read == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_qXfer_siginfo_read == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetMultiprocessSupported() { + if (m_supports_memory_tagging == eLazyBoolCalculate) + GetRemoteQSupported(); + return m_supports_multiprocess == eLazyBoolYes; +} + +uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { + if (m_max_packet_size == 0) { + GetRemoteQSupported(); + } + return m_max_packet_size; +} + +bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() { + if (m_supports_not_sending_acks == eLazyBoolCalculate) { + m_send_acks = true; + m_supports_not_sending_acks = eLazyBoolNo; + + // This is the first real packet that we'll send in a debug session and it + // may take a little longer than normal to receive a reply. Wait at least + // 6 seconds for a reply to this packet. + + ScopedTimeout timeout(*this, std::max(GetPacketTimeout(), seconds(6))); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("QStartNoAckMode", response) == + PacketResult::Success) { + if (response.IsOKResponse()) { + m_send_acks = false; + m_supports_not_sending_acks = eLazyBoolYes; + } + return true; + } + } + return false; +} + +void GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported() { + if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) { + m_supports_threads_in_stop_reply = eLazyBoolNo; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response) == + PacketResult::Success) { + if (response.IsOKResponse()) + m_supports_threads_in_stop_reply = eLazyBoolYes; + } + } +} + +bool GDBRemoteCommunicationClient::GetVAttachOrWaitSupported() { + if (m_attach_or_wait_reply == eLazyBoolCalculate) { + m_attach_or_wait_reply = eLazyBoolNo; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response) == + PacketResult::Success) { + if (response.IsOKResponse()) + m_attach_or_wait_reply = eLazyBoolYes; + } + } + return m_attach_or_wait_reply == eLazyBoolYes; +} + +bool GDBRemoteCommunicationClient::GetSyncThreadStateSupported() { + if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) { + m_prepare_for_reg_writing_reply = eLazyBoolNo; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response) == + PacketResult::Success) { + if (response.IsOKResponse()) + m_prepare_for_reg_writing_reply = eLazyBoolYes; + } + } + return m_prepare_for_reg_writing_reply == eLazyBoolYes; +} + +void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { + if (!did_exec) { + // Hard reset everything, this is when we first connect to a GDB server + m_supports_not_sending_acks = eLazyBoolCalculate; + m_supports_thread_suffix = eLazyBoolCalculate; + m_supports_threads_in_stop_reply = eLazyBoolCalculate; + m_supports_vCont_c = eLazyBoolCalculate; + m_supports_vCont_C = eLazyBoolCalculate; + m_supports_vCont_s = eLazyBoolCalculate; + m_supports_vCont_S = eLazyBoolCalculate; + m_supports_p = eLazyBoolCalculate; + m_supports_x = eLazyBoolCalculate; + m_supports_QSaveRegisterState = eLazyBoolCalculate; + m_qHostInfo_is_valid = eLazyBoolCalculate; + m_curr_pid_is_valid = eLazyBoolCalculate; + m_qGDBServerVersion_is_valid = eLazyBoolCalculate; + m_supports_alloc_dealloc_memory = eLazyBoolCalculate; + m_supports_memory_region_info = eLazyBoolCalculate; + m_prepare_for_reg_writing_reply = eLazyBoolCalculate; + m_attach_or_wait_reply = eLazyBoolCalculate; + m_avoid_g_packets = eLazyBoolCalculate; + m_supports_multiprocess = eLazyBoolCalculate; + m_supports_qSaveCore = eLazyBoolCalculate; + m_supports_qXfer_auxv_read = eLazyBoolCalculate; + m_supports_qXfer_libraries_read = eLazyBoolCalculate; + m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; + m_supports_qXfer_features_read = eLazyBoolCalculate; + m_supports_qXfer_memory_map_read = eLazyBoolCalculate; + m_supports_qXfer_siginfo_read = eLazyBoolCalculate; + m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; + m_uses_native_signals = eLazyBoolCalculate; + m_supports_qProcessInfoPID = true; + m_supports_qfProcessInfo = true; + m_supports_qUserName = true; + m_supports_qGroupName = true; + m_supports_qThreadStopInfo = true; + m_supports_z0 = true; + m_supports_z1 = true; + m_supports_z2 = true; + m_supports_z3 = true; + m_supports_z4 = true; + m_supports_QEnvironment = true; + m_supports_QEnvironmentHexEncoded = true; + m_supports_qSymbol = true; + m_qSymbol_requests_done = false; + m_supports_qModuleInfo = true; + m_host_arch.Clear(); + m_host_distribution_id.clear(); + m_os_version = llvm::VersionTuple(); + m_os_build.clear(); + m_os_kernel.clear(); + m_hostname.clear(); + m_gdb_server_name.clear(); + m_gdb_server_version = UINT32_MAX; + m_default_packet_timeout = seconds(0); + m_target_vm_page_size = 0; + m_max_packet_size = 0; + m_qSupported_response.clear(); + m_supported_async_json_packets_is_valid = false; + m_supported_async_json_packets_sp.reset(); + m_supports_jModulesInfo = true; + } + + // These flags should be reset when we first connect to a GDB server and when + // our inferior process execs + m_qProcessInfo_is_valid = eLazyBoolCalculate; + m_process_arch.Clear(); +} + +void GDBRemoteCommunicationClient::GetRemoteQSupported() { + // Clear out any capabilities we expect to see in the qSupported response + m_supports_qXfer_auxv_read = eLazyBoolNo; + m_supports_qXfer_libraries_read = eLazyBoolNo; + m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; + m_supports_augmented_libraries_svr4_read = eLazyBoolNo; + m_supports_qXfer_features_read = eLazyBoolNo; + m_supports_qXfer_memory_map_read = eLazyBoolNo; + m_supports_qXfer_siginfo_read = eLazyBoolNo; + m_supports_multiprocess = eLazyBoolNo; + m_supports_qEcho = eLazyBoolNo; + m_supports_QPassSignals = eLazyBoolNo; + m_supports_memory_tagging = eLazyBoolNo; + m_supports_qSaveCore = eLazyBoolNo; + m_uses_native_signals = eLazyBoolNo; + + m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if + // not, we assume no limit + + // build the qSupported packet + std::vector<std::string> features = {"xmlRegisters=i386,arm,mips,arc", + "multiprocess+", "fork-events+", + "vfork-events+"}; + StreamString packet; + packet.PutCString("qSupported"); + for (uint32_t i = 0; i < features.size(); ++i) { + packet.PutCString(i == 0 ? ":" : ";"); + packet.PutCString(features[i]); + } + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + // Hang on to the qSupported packet, so that platforms can do custom + // configuration of the transport before attaching/launching the process. + m_qSupported_response = response.GetStringRef().str(); + + for (llvm::StringRef x : llvm::split(response.GetStringRef(), ';')) { + if (x == "qXfer:auxv:read+") + m_supports_qXfer_auxv_read = eLazyBoolYes; + else if (x == "qXfer:libraries-svr4:read+") + m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; + else if (x == "augmented-libraries-svr4-read") { + m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; // implied + m_supports_augmented_libraries_svr4_read = eLazyBoolYes; + } else if (x == "qXfer:libraries:read+") + m_supports_qXfer_libraries_read = eLazyBoolYes; + else if (x == "qXfer:features:read+") + m_supports_qXfer_features_read = eLazyBoolYes; + else if (x == "qXfer:memory-map:read+") + m_supports_qXfer_memory_map_read = eLazyBoolYes; + else if (x == "qXfer:siginfo:read+") + m_supports_qXfer_siginfo_read = eLazyBoolYes; + else if (x == "qEcho") + m_supports_qEcho = eLazyBoolYes; + else if (x == "QPassSignals+") + m_supports_QPassSignals = eLazyBoolYes; + else if (x == "multiprocess+") + m_supports_multiprocess = eLazyBoolYes; + else if (x == "memory-tagging+") + m_supports_memory_tagging = eLazyBoolYes; + else if (x == "qSaveCore+") + m_supports_qSaveCore = eLazyBoolYes; + else if (x == "native-signals+") + m_uses_native_signals = eLazyBoolYes; + // Look for a list of compressions in the features list e.g. + // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib- + // deflate,lzma + else if (x.consume_front("SupportedCompressions=")) { + llvm::SmallVector<llvm::StringRef, 4> compressions; + x.split(compressions, ','); + if (!compressions.empty()) + MaybeEnableCompression(compressions); + } else if (x.consume_front("SupportedWatchpointTypes=")) { + llvm::SmallVector<llvm::StringRef, 4> watchpoint_types; + x.split(watchpoint_types, ','); + m_watchpoint_types = eWatchpointHardwareFeatureUnknown; + for (auto wp_type : watchpoint_types) { + if (wp_type == "x86_64") + m_watchpoint_types |= eWatchpointHardwareX86; + if (wp_type == "aarch64-mask") + m_watchpoint_types |= eWatchpointHardwareArmMASK; + if (wp_type == "aarch64-bas") + m_watchpoint_types |= eWatchpointHardwareArmBAS; + } + } else if (x.consume_front("PacketSize=")) { + StringExtractorGDBRemote packet_response(x); + m_max_packet_size = + packet_response.GetHexMaxU64(/*little_endian=*/false, UINT64_MAX); + if (m_max_packet_size == 0) { + m_max_packet_size = UINT64_MAX; // Must have been a garbled response + Log *log(GetLog(GDBRLog::Process)); + LLDB_LOGF(log, "Garbled PacketSize spec in qSupported response"); + } + } + } + } +} + +bool GDBRemoteCommunicationClient::GetThreadSuffixSupported() { + if (m_supports_thread_suffix == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_thread_suffix = eLazyBoolNo; + if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response) == + PacketResult::Success) { + if (response.IsOKResponse()) + m_supports_thread_suffix = eLazyBoolYes; + } + } + return m_supports_thread_suffix; +} +bool GDBRemoteCommunicationClient::GetVContSupported(char flavor) { + if (m_supports_vCont_c == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_vCont_any = eLazyBoolNo; + m_supports_vCont_all = eLazyBoolNo; + m_supports_vCont_c = eLazyBoolNo; + m_supports_vCont_C = eLazyBoolNo; + m_supports_vCont_s = eLazyBoolNo; + m_supports_vCont_S = eLazyBoolNo; + if (SendPacketAndWaitForResponse("vCont?", response) == + PacketResult::Success) { + const char *response_cstr = response.GetStringRef().data(); + if (::strstr(response_cstr, ";c")) + m_supports_vCont_c = eLazyBoolYes; + + if (::strstr(response_cstr, ";C")) + m_supports_vCont_C = eLazyBoolYes; + + if (::strstr(response_cstr, ";s")) + m_supports_vCont_s = eLazyBoolYes; + + if (::strstr(response_cstr, ";S")) + m_supports_vCont_S = eLazyBoolYes; + + if (m_supports_vCont_c == eLazyBoolYes && + m_supports_vCont_C == eLazyBoolYes && + m_supports_vCont_s == eLazyBoolYes && + m_supports_vCont_S == eLazyBoolYes) { + m_supports_vCont_all = eLazyBoolYes; + } + + if (m_supports_vCont_c == eLazyBoolYes || + m_supports_vCont_C == eLazyBoolYes || + m_supports_vCont_s == eLazyBoolYes || + m_supports_vCont_S == eLazyBoolYes) { + m_supports_vCont_any = eLazyBoolYes; + } + } + } + + switch (flavor) { + case 'a': + return m_supports_vCont_any; + case 'A': + return m_supports_vCont_all; + case 'c': + return m_supports_vCont_c; + case 'C': + return m_supports_vCont_C; + case 's': + return m_supports_vCont_s; + case 'S': + return m_supports_vCont_S; + default: + break; + } + return false; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationClient::SendThreadSpecificPacketAndWaitForResponse( + lldb::tid_t tid, StreamString &&payload, + StringExtractorGDBRemote &response) { + Lock lock(*this); + if (!lock) { + if (Log *log = GetLog(GDBRLog::Process | GDBRLog::Packets)) + LLDB_LOGF(log, + "GDBRemoteCommunicationClient::%s: Didn't get sequence mutex " + "for %s packet.", + __FUNCTION__, payload.GetData()); + return PacketResult::ErrorNoSequenceLock; + } + + if (GetThreadSuffixSupported()) + payload.Printf(";thread:%4.4" PRIx64 ";", tid); + else { + if (!SetCurrentThread(tid)) + return PacketResult::ErrorSendFailed; + } + + return SendPacketAndWaitForResponseNoLock(payload.GetString(), response); +} + +// Check if the target supports 'p' packet. It sends out a 'p' packet and +// checks the response. A normal packet will tell us that support is available. +// +// Takes a valid thread ID because p needs to apply to a thread. +bool GDBRemoteCommunicationClient::GetpPacketSupported(lldb::tid_t tid) { + if (m_supports_p == eLazyBoolCalculate) + m_supports_p = GetThreadPacketSupported(tid, "p0"); + return m_supports_p; +} + +LazyBool GDBRemoteCommunicationClient::GetThreadPacketSupported( + lldb::tid_t tid, llvm::StringRef packetStr) { + StreamString payload; + payload.PutCString(packetStr); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response) == PacketResult::Success && + response.IsNormalResponse()) { + return eLazyBoolYes; + } + return eLazyBoolNo; +} + +bool GDBRemoteCommunicationClient::GetSaveCoreSupported() const { + return m_supports_qSaveCore == eLazyBoolYes; +} + +StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() { + // Get information on all threads at one using the "jThreadsInfo" packet + StructuredData::ObjectSP object_sp; + + if (m_supports_jThreadsInfo) { + StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); + if (SendPacketAndWaitForResponse("jThreadsInfo", response) == + PacketResult::Success) { + if (response.IsUnsupportedResponse()) { + m_supports_jThreadsInfo = false; + } else if (!response.Empty()) { + object_sp = StructuredData::ParseJSON(response.GetStringRef()); + } + } + } + return object_sp; +} + +bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() { + if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_jThreadExtendedInfo = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response) == + PacketResult::Success) { + if (response.IsOKResponse()) { + m_supports_jThreadExtendedInfo = eLazyBoolYes; + } + } + } + return m_supports_jThreadExtendedInfo; +} + +void GDBRemoteCommunicationClient::EnableErrorStringInPacket() { + if (m_supports_error_string_reply == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + // We try to enable error strings in remote packets but if we fail, we just + // work in the older way. + m_supports_error_string_reply = eLazyBoolNo; + if (SendPacketAndWaitForResponse("QEnableErrorStrings", response) == + PacketResult::Success) { + if (response.IsOKResponse()) { + m_supports_error_string_reply = eLazyBoolYes; + } + } + } +} + +bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported() { + if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:", + response) == PacketResult::Success) { + if (response.IsOKResponse()) { + m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes; + } + } + } + return m_supports_jLoadedDynamicLibrariesInfos; +} + +bool GDBRemoteCommunicationClient::GetSharedCacheInfoSupported() { + if (m_supports_jGetSharedCacheInfo == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_jGetSharedCacheInfo = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jGetSharedCacheInfo:", response) == + PacketResult::Success) { + if (response.IsOKResponse()) { + m_supports_jGetSharedCacheInfo = eLazyBoolYes; + } + } + } + return m_supports_jGetSharedCacheInfo; +} + +bool GDBRemoteCommunicationClient::GetDynamicLoaderProcessStateSupported() { + if (m_supports_jGetDyldProcessState == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_jGetDyldProcessState = eLazyBoolNo; + if (SendPacketAndWaitForResponse("jGetDyldProcessState", response) == + PacketResult::Success) { + if (!response.IsUnsupportedResponse()) + m_supports_jGetDyldProcessState = eLazyBoolYes; + } + } + return m_supports_jGetDyldProcessState; +} + +bool GDBRemoteCommunicationClient::GetMemoryTaggingSupported() { + if (m_supports_memory_tagging == eLazyBoolCalculate) { + GetRemoteQSupported(); + } + return m_supports_memory_tagging == eLazyBoolYes; +} + +DataBufferSP GDBRemoteCommunicationClient::ReadMemoryTags(lldb::addr_t addr, + size_t len, + int32_t type) { + StreamString packet; + packet.Printf("qMemTags:%" PRIx64 ",%zx:%" PRIx32, addr, len, type); + StringExtractorGDBRemote response; + + Log *log = GetLog(GDBRLog::Memory); + + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success || + !response.IsNormalResponse()) { + LLDB_LOGF(log, "GDBRemoteCommunicationClient::%s: qMemTags packet failed", + __FUNCTION__); + return nullptr; + } + + // We are expecting + // m<hex encoded bytes> + + if (response.GetChar() != 'm') { + LLDB_LOGF(log, + "GDBRemoteCommunicationClient::%s: qMemTags response did not " + "begin with \"m\"", + __FUNCTION__); + return nullptr; + } + + size_t expected_bytes = response.GetBytesLeft() / 2; + WritableDataBufferSP buffer_sp(new DataBufferHeap(expected_bytes, 0)); + size_t got_bytes = response.GetHexBytesAvail(buffer_sp->GetData()); + // Check both because in some situations chars are consumed even + // if the decoding fails. + if (response.GetBytesLeft() || (expected_bytes != got_bytes)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationClient::%s: Invalid data in qMemTags response", + __FUNCTION__); + return nullptr; + } + + return buffer_sp; +} + +Status GDBRemoteCommunicationClient::WriteMemoryTags( + lldb::addr_t addr, size_t len, int32_t type, + const std::vector<uint8_t> &tags) { + // Format QMemTags:address,length:type:tags + StreamString packet; + packet.Printf("QMemTags:%" PRIx64 ",%zx:%" PRIx32 ":", addr, len, type); + packet.PutBytesAsRawHex8(tags.data(), tags.size()); + + Status status; + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success || + !response.IsOKResponse()) { + status.SetErrorString("QMemTags packet failed"); + } + return status; +} + +bool GDBRemoteCommunicationClient::GetxPacketSupported() { + if (m_supports_x == eLazyBoolCalculate) { + StringExtractorGDBRemote response; + m_supports_x = eLazyBoolNo; + char packet[256]; + snprintf(packet, sizeof(packet), "x0,0"); + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success) { + if (response.IsOKResponse()) + m_supports_x = eLazyBoolYes; + } + } + return m_supports_x; +} + +lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) { + if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes) + return m_curr_pid; + + // First try to retrieve the pid via the qProcessInfo request. + GetCurrentProcessInfo(allow_lazy); + if (m_curr_pid_is_valid == eLazyBoolYes) { + // We really got it. + return m_curr_pid; + } else { + // If we don't get a response for qProcessInfo, check if $qC gives us a + // result. $qC only returns a real process id on older debugserver and + // lldb-platform stubs. The gdb remote protocol documents $qC as returning + // the thread id, which newer debugserver and lldb-gdbserver stubs return + // correctly. + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qC", response) == PacketResult::Success) { + if (response.GetChar() == 'Q') { + if (response.GetChar() == 'C') { + m_curr_pid_run = m_curr_pid = + response.GetHexMaxU64(false, LLDB_INVALID_PROCESS_ID); + if (m_curr_pid != LLDB_INVALID_PROCESS_ID) { + m_curr_pid_is_valid = eLazyBoolYes; + return m_curr_pid; + } + } + } + } + + // If we don't get a response for $qC, check if $qfThreadID gives us a + // result. + if (m_curr_pid == LLDB_INVALID_PROCESS_ID) { + bool sequence_mutex_unavailable; + auto ids = GetCurrentProcessAndThreadIDs(sequence_mutex_unavailable); + if (!ids.empty() && !sequence_mutex_unavailable) { + // If server returned an explicit PID, use that. + m_curr_pid_run = m_curr_pid = ids.front().first; + // Otherwise, use the TID of the first thread (Linux hack). + if (m_curr_pid == LLDB_INVALID_PROCESS_ID) + m_curr_pid_run = m_curr_pid = ids.front().second; + m_curr_pid_is_valid = eLazyBoolYes; + return m_curr_pid; + } + } + } + + return LLDB_INVALID_PROCESS_ID; +} + +llvm::Error GDBRemoteCommunicationClient::LaunchProcess(const Args &args) { + if (!args.GetArgumentAtIndex(0)) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Nothing to launch"); + // try vRun first + if (m_supports_vRun) { + StreamString packet; + packet.PutCString("vRun"); + for (const Args::ArgEntry &arg : args) { + packet.PutChar(';'); + packet.PutStringAsRawHex8(arg.ref()); + } + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Sending vRun packet failed"); + + if (response.IsErrorResponse()) + return response.GetStatus().ToError(); + + // vRun replies with a stop reason packet + // FIXME: right now we just discard the packet and LLDB queries + // for stop reason again + if (!response.IsUnsupportedResponse()) + return llvm::Error::success(); + + m_supports_vRun = false; + } + + // fallback to A + StreamString packet; + packet.PutChar('A'); + llvm::ListSeparator LS(","); + for (const auto &arg : llvm::enumerate(args)) { + packet << LS; + packet.Format("{0},{1},", arg.value().ref().size() * 2, arg.index()); + packet.PutStringAsRawHex8(arg.value().ref()); + } + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Sending A packet failed"); + } + if (!response.IsOKResponse()) + return response.GetStatus().ToError(); + + if (SendPacketAndWaitForResponse("qLaunchSuccess", response) != + PacketResult::Success) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Sending qLaunchSuccess packet failed"); + } + if (response.IsOKResponse()) + return llvm::Error::success(); + if (response.GetChar() == 'E') { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + response.GetStringRef().substr(1)); + } + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "unknown error occurred launching process"); +} + +int GDBRemoteCommunicationClient::SendEnvironment(const Environment &env) { + llvm::SmallVector<std::pair<llvm::StringRef, llvm::StringRef>, 0> vec; + for (const auto &kv : env) + vec.emplace_back(kv.first(), kv.second); + llvm::sort(vec, llvm::less_first()); + for (const auto &[k, v] : vec) { + int r = SendEnvironmentPacket((k + "=" + v).str().c_str()); + if (r != 0) + return r; + } + return 0; +} + +int GDBRemoteCommunicationClient::SendEnvironmentPacket( + char const *name_equal_value) { + if (name_equal_value && name_equal_value[0]) { + bool send_hex_encoding = false; + for (const char *p = name_equal_value; *p != '\0' && !send_hex_encoding; + ++p) { + if (llvm::isPrint(*p)) { + switch (*p) { + case '$': + case '#': + case '*': + case '}': + send_hex_encoding = true; + break; + default: + break; + } + } else { + // We have non printable characters, lets hex encode this... + send_hex_encoding = true; + } + } + + StringExtractorGDBRemote response; + // Prefer sending unencoded, if possible and the server supports it. + if (!send_hex_encoding && m_supports_QEnvironment) { + StreamString packet; + packet.Printf("QEnvironment:%s", name_equal_value); + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success) + return -1; + + if (response.IsOKResponse()) + return 0; + if (response.IsUnsupportedResponse()) + m_supports_QEnvironment = false; + else { + uint8_t error = response.GetError(); + if (error) + return error; + return -1; + } + } + + if (m_supports_QEnvironmentHexEncoded) { + StreamString packet; + packet.PutCString("QEnvironmentHexEncoded:"); + packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value)); + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success) + return -1; + + if (response.IsOKResponse()) + return 0; + if (response.IsUnsupportedResponse()) + m_supports_QEnvironmentHexEncoded = false; + else { + uint8_t error = response.GetError(); + if (error) + return error; + return -1; + } + } + } + return -1; +} + +int GDBRemoteCommunicationClient::SendLaunchArchPacket(char const *arch) { + if (arch && arch[0]) { + StreamString packet; + packet.Printf("QLaunchArch:%s", arch); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int GDBRemoteCommunicationClient::SendLaunchEventDataPacket( + char const *data, bool *was_supported) { + if (data && *data != '\0') { + StreamString packet; + packet.Printf("QSetProcessEvent:%s", data); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) { + if (was_supported) + *was_supported = true; + return 0; + } else if (response.IsUnsupportedResponse()) { + if (was_supported) + *was_supported = false; + return -1; + } else { + uint8_t error = response.GetError(); + if (was_supported) + *was_supported = true; + if (error) + return error; + } + } + } + return -1; +} + +llvm::VersionTuple GDBRemoteCommunicationClient::GetOSVersion() { + GetHostInfo(); + return m_os_version; +} + +llvm::VersionTuple GDBRemoteCommunicationClient::GetMacCatalystVersion() { + GetHostInfo(); + return m_maccatalyst_version; +} + +std::optional<std::string> GDBRemoteCommunicationClient::GetOSBuildString() { + if (GetHostInfo()) { + if (!m_os_build.empty()) + return m_os_build; + } + return std::nullopt; +} + +std::optional<std::string> +GDBRemoteCommunicationClient::GetOSKernelDescription() { + if (GetHostInfo()) { + if (!m_os_kernel.empty()) + return m_os_kernel; + } + return std::nullopt; +} + +bool GDBRemoteCommunicationClient::GetHostname(std::string &s) { + if (GetHostInfo()) { + if (!m_hostname.empty()) { + s = m_hostname; + return true; + } + } + s.clear(); + return false; +} + +ArchSpec GDBRemoteCommunicationClient::GetSystemArchitecture() { + if (GetHostInfo()) + return m_host_arch; + return ArchSpec(); +} + +const lldb_private::ArchSpec & +GDBRemoteCommunicationClient::GetProcessArchitecture() { + if (m_qProcessInfo_is_valid == eLazyBoolCalculate) + GetCurrentProcessInfo(); + return m_process_arch; +} + +bool GDBRemoteCommunicationClient::GetProcessStandaloneBinary( + UUID &uuid, addr_t &value, bool &value_is_offset) { + if (m_qProcessInfo_is_valid == eLazyBoolCalculate) + GetCurrentProcessInfo(); + + // Return true if we have a UUID or an address/offset of the + // main standalone / firmware binary being used. + if (!m_process_standalone_uuid.IsValid() && + m_process_standalone_value == LLDB_INVALID_ADDRESS) + return false; + + uuid = m_process_standalone_uuid; + value = m_process_standalone_value; + value_is_offset = m_process_standalone_value_is_offset; + return true; +} + +std::vector<addr_t> +GDBRemoteCommunicationClient::GetProcessStandaloneBinaries() { + if (m_qProcessInfo_is_valid == eLazyBoolCalculate) + GetCurrentProcessInfo(); + return m_binary_addresses; +} + +bool GDBRemoteCommunicationClient::GetGDBServerVersion() { + if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) { + m_gdb_server_name.clear(); + m_gdb_server_version = 0; + m_qGDBServerVersion_is_valid = eLazyBoolNo; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qGDBServerVersion", response) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + llvm::StringRef name, value; + bool success = false; + while (response.GetNameColonValue(name, value)) { + if (name == "name") { + success = true; + m_gdb_server_name = std::string(value); + } else if (name == "version") { + llvm::StringRef major, minor; + std::tie(major, minor) = value.split('.'); + if (!major.getAsInteger(0, m_gdb_server_version)) + success = true; + } + } + if (success) + m_qGDBServerVersion_is_valid = eLazyBoolYes; + } + } + } + return m_qGDBServerVersion_is_valid == eLazyBoolYes; +} + +void GDBRemoteCommunicationClient::MaybeEnableCompression( + llvm::ArrayRef<llvm::StringRef> supported_compressions) { + CompressionType avail_type = CompressionType::None; + llvm::StringRef avail_name; + +#if defined(HAVE_LIBCOMPRESSION) + if (avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "lzfse") { + avail_type = CompressionType::LZFSE; + avail_name = compression; + break; + } + } + } +#endif + +#if defined(HAVE_LIBCOMPRESSION) + if (avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "zlib-deflate") { + avail_type = CompressionType::ZlibDeflate; + avail_name = compression; + break; + } + } + } +#endif + +#if LLVM_ENABLE_ZLIB + if (avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "zlib-deflate") { + avail_type = CompressionType::ZlibDeflate; + avail_name = compression; + break; + } + } + } +#endif + +#if defined(HAVE_LIBCOMPRESSION) + if (avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "lz4") { + avail_type = CompressionType::LZ4; + avail_name = compression; + break; + } + } + } +#endif + +#if defined(HAVE_LIBCOMPRESSION) + if (avail_type == CompressionType::None) { + for (auto compression : supported_compressions) { + if (compression == "lzma") { + avail_type = CompressionType::LZMA; + avail_name = compression; + break; + } + } + } +#endif + + if (avail_type != CompressionType::None) { + StringExtractorGDBRemote response; + std::string packet = "QEnableCompression:type:" + avail_name.str() + ";"; + if (SendPacketAndWaitForResponse(packet, response) != PacketResult::Success) + return; + + if (response.IsOKResponse()) { + m_compression_type = avail_type; + } + } +} + +const char *GDBRemoteCommunicationClient::GetGDBServerProgramName() { + if (GetGDBServerVersion()) { + if (!m_gdb_server_name.empty()) + return m_gdb_server_name.c_str(); + } + return nullptr; +} + +uint32_t GDBRemoteCommunicationClient::GetGDBServerProgramVersion() { + if (GetGDBServerVersion()) + return m_gdb_server_version; + return 0; +} + +bool GDBRemoteCommunicationClient::GetDefaultThreadId(lldb::tid_t &tid) { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qC", response) != PacketResult::Success) + return false; + + if (!response.IsNormalResponse()) + return false; + + if (response.GetChar() == 'Q' && response.GetChar() == 'C') { + auto pid_tid = response.GetPidTid(0); + if (!pid_tid) + return false; + + lldb::pid_t pid = pid_tid->first; + // invalid + if (pid == StringExtractorGDBRemote::AllProcesses) + return false; + + // if we get pid as well, update m_curr_pid + if (pid != 0) { + m_curr_pid_run = m_curr_pid = pid; + m_curr_pid_is_valid = eLazyBoolYes; + } + tid = pid_tid->second; + } + + return true; +} + +static void ParseOSType(llvm::StringRef value, std::string &os_name, + std::string &environment) { + if (value == "iossimulator" || value == "tvossimulator" || + value == "watchossimulator" || value == "xrossimulator" || + value == "visionossimulator") { + environment = "simulator"; + os_name = value.drop_back(environment.size()).str(); + } else if (value == "maccatalyst") { + os_name = "ios"; + environment = "macabi"; + } else { + os_name = value.str(); + } +} + +bool GDBRemoteCommunicationClient::GetHostInfo(bool force) { + Log *log = GetLog(GDBRLog::Process); + + if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) { + // host info computation can require DNS traffic and shelling out to external processes. + // Increase the timeout to account for that. + ScopedTimeout timeout(*this, seconds(10)); + m_qHostInfo_is_valid = eLazyBoolNo; + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qHostInfo", response) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + llvm::StringRef name; + llvm::StringRef value; + uint32_t cpu = LLDB_INVALID_CPUTYPE; + uint32_t sub = 0; + std::string arch_name; + std::string os_name; + std::string environment; + std::string vendor_name; + std::string triple; + uint32_t pointer_byte_size = 0; + ByteOrder byte_order = eByteOrderInvalid; + uint32_t num_keys_decoded = 0; + while (response.GetNameColonValue(name, value)) { + if (name == "cputype") { + // exception type in big endian hex + if (!value.getAsInteger(0, cpu)) + ++num_keys_decoded; + } else if (name == "cpusubtype") { + // exception count in big endian hex + if (!value.getAsInteger(0, sub)) + ++num_keys_decoded; + } else if (name == "arch") { + arch_name = std::string(value); + ++num_keys_decoded; + } else if (name == "triple") { + StringExtractor extractor(value); + extractor.GetHexByteString(triple); + ++num_keys_decoded; + } else if (name == "distribution_id") { + StringExtractor extractor(value); + extractor.GetHexByteString(m_host_distribution_id); + ++num_keys_decoded; + } else if (name == "os_build") { + StringExtractor extractor(value); + extractor.GetHexByteString(m_os_build); + ++num_keys_decoded; + } else if (name == "hostname") { + StringExtractor extractor(value); + extractor.GetHexByteString(m_hostname); + ++num_keys_decoded; + } else if (name == "os_kernel") { + StringExtractor extractor(value); + extractor.GetHexByteString(m_os_kernel); + ++num_keys_decoded; + } else if (name == "ostype") { + ParseOSType(value, os_name, environment); + ++num_keys_decoded; + } else if (name == "vendor") { + vendor_name = std::string(value); + ++num_keys_decoded; + } else if (name == "endian") { + byte_order = llvm::StringSwitch<lldb::ByteOrder>(value) + .Case("little", eByteOrderLittle) + .Case("big", eByteOrderBig) + .Case("pdp", eByteOrderPDP) + .Default(eByteOrderInvalid); + if (byte_order != eByteOrderInvalid) + ++num_keys_decoded; + } else if (name == "ptrsize") { + if (!value.getAsInteger(0, pointer_byte_size)) + ++num_keys_decoded; + } else if (name == "addressing_bits") { + if (!value.getAsInteger(0, m_low_mem_addressing_bits)) { + ++num_keys_decoded; + } + } else if (name == "high_mem_addressing_bits") { + if (!value.getAsInteger(0, m_high_mem_addressing_bits)) + ++num_keys_decoded; + } else if (name == "low_mem_addressing_bits") { + if (!value.getAsInteger(0, m_low_mem_addressing_bits)) + ++num_keys_decoded; + } else if (name == "os_version" || + name == "version") // Older debugserver binaries used + // the "version" key instead of + // "os_version"... + { + if (!m_os_version.tryParse(value)) + ++num_keys_decoded; + } else if (name == "maccatalyst_version") { + if (!m_maccatalyst_version.tryParse(value)) + ++num_keys_decoded; + } else if (name == "watchpoint_exceptions_received") { + m_watchpoints_trigger_after_instruction = + llvm::StringSwitch<LazyBool>(value) + .Case("before", eLazyBoolNo) + .Case("after", eLazyBoolYes) + .Default(eLazyBoolCalculate); + if (m_watchpoints_trigger_after_instruction != eLazyBoolCalculate) + ++num_keys_decoded; + } else if (name == "default_packet_timeout") { + uint32_t timeout_seconds; + if (!value.getAsInteger(0, timeout_seconds)) { + m_default_packet_timeout = seconds(timeout_seconds); + SetPacketTimeout(m_default_packet_timeout); + ++num_keys_decoded; + } + } else if (name == "vm-page-size") { + int page_size; + if (!value.getAsInteger(0, page_size)) { + m_target_vm_page_size = page_size; + ++num_keys_decoded; + } + } + } + + if (num_keys_decoded > 0) + m_qHostInfo_is_valid = eLazyBoolYes; + + if (triple.empty()) { + if (arch_name.empty()) { + if (cpu != LLDB_INVALID_CPUTYPE) { + m_host_arch.SetArchitecture(eArchTypeMachO, cpu, sub); + if (pointer_byte_size) { + assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) { + assert(byte_order == m_host_arch.GetByteOrder()); + } + + if (!vendor_name.empty()) + m_host_arch.GetTriple().setVendorName( + llvm::StringRef(vendor_name)); + if (!os_name.empty()) + m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name)); + if (!environment.empty()) + m_host_arch.GetTriple().setEnvironmentName(environment); + } + } else { + std::string triple; + triple += arch_name; + if (!vendor_name.empty() || !os_name.empty()) { + triple += '-'; + if (vendor_name.empty()) + triple += "unknown"; + else + triple += vendor_name; + triple += '-'; + if (os_name.empty()) + triple += "unknown"; + else + triple += os_name; + } + m_host_arch.SetTriple(triple.c_str()); + + llvm::Triple &host_triple = m_host_arch.GetTriple(); + if (host_triple.getVendor() == llvm::Triple::Apple && + host_triple.getOS() == llvm::Triple::Darwin) { + switch (m_host_arch.GetMachine()) { + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + case llvm::Triple::arm: + case llvm::Triple::thumb: + host_triple.setOS(llvm::Triple::IOS); + break; + default: + host_triple.setOS(llvm::Triple::MacOSX); + break; + } + } + if (pointer_byte_size) { + assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) { + assert(byte_order == m_host_arch.GetByteOrder()); + } + } + } else { + m_host_arch.SetTriple(triple.c_str()); + if (pointer_byte_size) { + assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) { + assert(byte_order == m_host_arch.GetByteOrder()); + } + + LLDB_LOGF(log, + "GDBRemoteCommunicationClient::%s parsed host " + "architecture as %s, triple as %s from triple text %s", + __FUNCTION__, + m_host_arch.GetArchitectureName() + ? m_host_arch.GetArchitectureName() + : "<null-arch-name>", + m_host_arch.GetTriple().getTriple().c_str(), + triple.c_str()); + } + } + } + } + return m_qHostInfo_is_valid == eLazyBoolYes; +} + +int GDBRemoteCommunicationClient::SendStdinNotification(const char *data, + size_t data_len) { + StreamString packet; + packet.PutCString("I"); + packet.PutBytesAsRawHex8(data, data_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + return 0; + } + return response.GetError(); +} + +const lldb_private::ArchSpec & +GDBRemoteCommunicationClient::GetHostArchitecture() { + if (m_qHostInfo_is_valid == eLazyBoolCalculate) + GetHostInfo(); + return m_host_arch; +} + +AddressableBits GDBRemoteCommunicationClient::GetAddressableBits() { + AddressableBits addressable_bits; + if (m_qHostInfo_is_valid == eLazyBoolCalculate) + GetHostInfo(); + + if (m_low_mem_addressing_bits == m_high_mem_addressing_bits) + addressable_bits.SetAddressableBits(m_low_mem_addressing_bits); + else + addressable_bits.SetAddressableBits(m_low_mem_addressing_bits, + m_high_mem_addressing_bits); + return addressable_bits; +} + +seconds GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout() { + if (m_qHostInfo_is_valid == eLazyBoolCalculate) + GetHostInfo(); + return m_default_packet_timeout; +} + +addr_t GDBRemoteCommunicationClient::AllocateMemory(size_t size, + uint32_t permissions) { + if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { + m_supports_alloc_dealloc_memory = eLazyBoolYes; + char packet[64]; + const int packet_len = ::snprintf( + packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", (uint64_t)size, + permissions & lldb::ePermissionsReadable ? "r" : "", + permissions & lldb::ePermissionsWritable ? "w" : "", + permissions & lldb::ePermissionsExecutable ? "x" : ""); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success) { + if (response.IsUnsupportedResponse()) + m_supports_alloc_dealloc_memory = eLazyBoolNo; + else if (!response.IsErrorResponse()) + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + } else { + m_supports_alloc_dealloc_memory = eLazyBoolNo; + } + } + return LLDB_INVALID_ADDRESS; +} + +bool GDBRemoteCommunicationClient::DeallocateMemory(addr_t addr) { + if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { + m_supports_alloc_dealloc_memory = eLazyBoolYes; + char packet[64]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success) { + if (response.IsUnsupportedResponse()) + m_supports_alloc_dealloc_memory = eLazyBoolNo; + else if (response.IsOKResponse()) + return true; + } else { + m_supports_alloc_dealloc_memory = eLazyBoolNo; + } + } + return false; +} + +Status GDBRemoteCommunicationClient::Detach(bool keep_stopped, + lldb::pid_t pid) { + Status error; + lldb_private::StreamString packet; + + packet.PutChar('D'); + if (keep_stopped) { + if (m_supports_detach_stay_stopped == eLazyBoolCalculate) { + char packet[64]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:"); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success && + response.IsOKResponse()) { + m_supports_detach_stay_stopped = eLazyBoolYes; + } else { + m_supports_detach_stay_stopped = eLazyBoolNo; + } + } + + if (m_supports_detach_stay_stopped == eLazyBoolNo) { + error.SetErrorString("Stays stopped not supported by this target."); + return error; + } else { + packet.PutChar('1'); + } + } + + if (GetMultiprocessSupported()) { + // Some servers (e.g. qemu) require specifying the PID even if only a single + // process is running. + if (pid == LLDB_INVALID_PROCESS_ID) + pid = GetCurrentProcessID(); + packet.PutChar(';'); + packet.PutHex64(pid); + } else if (pid != LLDB_INVALID_PROCESS_ID) { + error.SetErrorString("Multiprocess extension not supported by the server."); + return error; + } + + StringExtractorGDBRemote response; + PacketResult packet_result = + SendPacketAndWaitForResponse(packet.GetString(), response); + if (packet_result != PacketResult::Success) + error.SetErrorString("Sending isconnect packet failed."); + return error; +} + +Status GDBRemoteCommunicationClient::GetMemoryRegionInfo( + lldb::addr_t addr, lldb_private::MemoryRegionInfo ®ion_info) { + Status error; + region_info.Clear(); + + if (m_supports_memory_region_info != eLazyBoolNo) { + m_supports_memory_region_info = eLazyBoolYes; + char packet[64]; + const int packet_len = ::snprintf( + packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success && + response.GetResponseType() == StringExtractorGDBRemote::eResponse) { + llvm::StringRef name; + llvm::StringRef value; + addr_t addr_value = LLDB_INVALID_ADDRESS; + bool success = true; + bool saw_permissions = false; + while (success && response.GetNameColonValue(name, value)) { + if (name == "start") { + if (!value.getAsInteger(16, addr_value)) + region_info.GetRange().SetRangeBase(addr_value); + } else if (name == "size") { + if (!value.getAsInteger(16, addr_value)) { + region_info.GetRange().SetByteSize(addr_value); + if (region_info.GetRange().GetRangeEnd() < + region_info.GetRange().GetRangeBase()) { + // Range size overflowed, truncate it. + region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + } + } + } else if (name == "permissions" && region_info.GetRange().IsValid()) { + saw_permissions = true; + if (region_info.GetRange().Contains(addr)) { + if (value.contains('r')) + region_info.SetReadable(MemoryRegionInfo::eYes); + else + region_info.SetReadable(MemoryRegionInfo::eNo); + + if (value.contains('w')) + region_info.SetWritable(MemoryRegionInfo::eYes); + else + region_info.SetWritable(MemoryRegionInfo::eNo); + + if (value.contains('x')) + region_info.SetExecutable(MemoryRegionInfo::eYes); + else + region_info.SetExecutable(MemoryRegionInfo::eNo); + + region_info.SetMapped(MemoryRegionInfo::eYes); + } else { + // The reported region does not contain this address -- we're + // looking at an unmapped page + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + } + } else if (name == "name") { + StringExtractorGDBRemote name_extractor(value); + std::string name; + name_extractor.GetHexByteString(name); + region_info.SetName(name.c_str()); + } else if (name == "flags") { + region_info.SetMemoryTagged(MemoryRegionInfo::eNo); + + llvm::StringRef flags = value; + llvm::StringRef flag; + while (flags.size()) { + flags = flags.ltrim(); + std::tie(flag, flags) = flags.split(' '); + // To account for trailing whitespace + if (flag.size()) { + if (flag == "mt") { + region_info.SetMemoryTagged(MemoryRegionInfo::eYes); + break; + } + } + } + } else if (name == "type") { + std::string comma_sep_str = value.str(); + size_t comma_pos; + while ((comma_pos = comma_sep_str.find(',')) != std::string::npos) { + comma_sep_str[comma_pos] = '\0'; + if (comma_sep_str == "stack") { + region_info.SetIsStackMemory(MemoryRegionInfo::eYes); + } + } + // handle final (or only) type of "stack" + if (comma_sep_str == "stack") { + region_info.SetIsStackMemory(MemoryRegionInfo::eYes); + } + } else if (name == "error") { + StringExtractorGDBRemote error_extractor(value); + std::string error_string; + // Now convert the HEX bytes into a string value + error_extractor.GetHexByteString(error_string); + error.SetErrorString(error_string.c_str()); + } else if (name == "dirty-pages") { + std::vector<addr_t> dirty_page_list; + for (llvm::StringRef x : llvm::split(value, ',')) { + addr_t page; + x.consume_front("0x"); + if (llvm::to_integer(x, page, 16)) + dirty_page_list.push_back(page); + } + region_info.SetDirtyPageList(dirty_page_list); + } + } + + if (m_target_vm_page_size != 0) + region_info.SetPageSize(m_target_vm_page_size); + + if (region_info.GetRange().IsValid()) { + // We got a valid address range back but no permissions -- which means + // this is an unmapped page + if (!saw_permissions) { + region_info.SetReadable(MemoryRegionInfo::eNo); + region_info.SetWritable(MemoryRegionInfo::eNo); + region_info.SetExecutable(MemoryRegionInfo::eNo); + region_info.SetMapped(MemoryRegionInfo::eNo); + } + } else { + // We got an invalid address range back + error.SetErrorString("Server returned invalid range"); + } + } else { + m_supports_memory_region_info = eLazyBoolNo; + } + } + + if (m_supports_memory_region_info == eLazyBoolNo) { + error.SetErrorString("qMemoryRegionInfo is not supported"); + } + + // Try qXfer:memory-map:read to get region information not included in + // qMemoryRegionInfo + MemoryRegionInfo qXfer_region_info; + Status qXfer_error = GetQXferMemoryMapRegionInfo(addr, qXfer_region_info); + + if (error.Fail()) { + // If qMemoryRegionInfo failed, but qXfer:memory-map:read succeeded, use + // the qXfer result as a fallback + if (qXfer_error.Success()) { + region_info = qXfer_region_info; + error.Clear(); + } else { + region_info.Clear(); + } + } else if (qXfer_error.Success()) { + // If both qMemoryRegionInfo and qXfer:memory-map:read succeeded, and if + // both regions are the same range, update the result to include the flash- + // memory information that is specific to the qXfer result. + if (region_info.GetRange() == qXfer_region_info.GetRange()) { + region_info.SetFlash(qXfer_region_info.GetFlash()); + region_info.SetBlocksize(qXfer_region_info.GetBlocksize()); + } + } + return error; +} + +Status GDBRemoteCommunicationClient::GetQXferMemoryMapRegionInfo( + lldb::addr_t addr, MemoryRegionInfo ®ion) { + Status error = LoadQXferMemoryMap(); + if (!error.Success()) + return error; + for (const auto &map_region : m_qXfer_memory_map) { + if (map_region.GetRange().Contains(addr)) { + region = map_region; + return error; + } + } + error.SetErrorString("Region not found"); + return error; +} + +Status GDBRemoteCommunicationClient::LoadQXferMemoryMap() { + + Status error; + + if (m_qXfer_memory_map_loaded) + // Already loaded, return success + return error; + + if (!XMLDocument::XMLEnabled()) { + error.SetErrorString("XML is not supported"); + return error; + } + + if (!GetQXferMemoryMapReadSupported()) { + error.SetErrorString("Memory map is not supported"); + return error; + } + + llvm::Expected<std::string> xml = ReadExtFeature("memory-map", ""); + if (!xml) + return Status(xml.takeError()); + + XMLDocument xml_document; + + if (!xml_document.ParseMemory(xml->c_str(), xml->size())) { + error.SetErrorString("Failed to parse memory map xml"); + return error; + } + + XMLNode map_node = xml_document.GetRootElement("memory-map"); + if (!map_node) { + error.SetErrorString("Invalid root node in memory map xml"); + return error; + } + + m_qXfer_memory_map.clear(); + + map_node.ForEachChildElement([this](const XMLNode &memory_node) -> bool { + if (!memory_node.IsElement()) + return true; + if (memory_node.GetName() != "memory") + return true; + auto type = memory_node.GetAttributeValue("type", ""); + uint64_t start; + uint64_t length; + if (!memory_node.GetAttributeValueAsUnsigned("start", start)) + return true; + if (!memory_node.GetAttributeValueAsUnsigned("length", length)) + return true; + MemoryRegionInfo region; + region.GetRange().SetRangeBase(start); + region.GetRange().SetByteSize(length); + if (type == "rom") { + region.SetReadable(MemoryRegionInfo::eYes); + this->m_qXfer_memory_map.push_back(region); + } else if (type == "ram") { + region.SetReadable(MemoryRegionInfo::eYes); + region.SetWritable(MemoryRegionInfo::eYes); + this->m_qXfer_memory_map.push_back(region); + } else if (type == "flash") { + region.SetFlash(MemoryRegionInfo::eYes); + memory_node.ForEachChildElement( + [®ion](const XMLNode &prop_node) -> bool { + if (!prop_node.IsElement()) + return true; + if (prop_node.GetName() != "property") + return true; + auto propname = prop_node.GetAttributeValue("name", ""); + if (propname == "blocksize") { + uint64_t blocksize; + if (prop_node.GetElementTextAsUnsigned(blocksize)) + region.SetBlocksize(blocksize); + } + return true; + }); + this->m_qXfer_memory_map.push_back(region); + } + return true; + }); + + m_qXfer_memory_map_loaded = true; + + return error; +} + +std::optional<uint32_t> GDBRemoteCommunicationClient::GetWatchpointSlotCount() { + if (m_supports_watchpoint_support_info == eLazyBoolYes) { + return m_num_supported_hardware_watchpoints; + } + + std::optional<uint32_t> num; + if (m_supports_watchpoint_support_info != eLazyBoolNo) { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qWatchpointSupportInfo:", response) == + PacketResult::Success) { + m_supports_watchpoint_support_info = eLazyBoolYes; + llvm::StringRef name; + llvm::StringRef value; + while (response.GetNameColonValue(name, value)) { + if (name == "num") { + value.getAsInteger(0, m_num_supported_hardware_watchpoints); + num = m_num_supported_hardware_watchpoints; + } + } + if (!num) { + m_supports_watchpoint_support_info = eLazyBoolNo; + } + } else { + m_supports_watchpoint_support_info = eLazyBoolNo; + } + } + + return num; +} + +WatchpointHardwareFeature +GDBRemoteCommunicationClient::GetSupportedWatchpointTypes() { + return m_watchpoint_types; +} + +std::optional<bool> GDBRemoteCommunicationClient::GetWatchpointReportedAfter() { + if (m_qHostInfo_is_valid == eLazyBoolCalculate) + GetHostInfo(); + + // Process determines this by target CPU, but allow for the + // remote stub to override it via the qHostInfo + // watchpoint_exceptions_received key, if it is present. + if (m_qHostInfo_is_valid == eLazyBoolYes) { + if (m_watchpoints_trigger_after_instruction == eLazyBoolNo) + return false; + if (m_watchpoints_trigger_after_instruction == eLazyBoolYes) + return true; + } + + return std::nullopt; +} + +int GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) { + if (file_spec) { + std::string path{file_spec.GetPath(false)}; + StreamString packet; + packet.PutCString("QSetSTDIN:"); + packet.PutStringAsRawHex8(path); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) { + if (file_spec) { + std::string path{file_spec.GetPath(false)}; + StreamString packet; + packet.PutCString("QSetSTDOUT:"); + packet.PutStringAsRawHex8(path); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) { + if (file_spec) { + std::string path{file_spec.GetPath(false)}; + StreamString packet; + packet.PutCString("QSetSTDERR:"); + packet.PutStringAsRawHex8(path); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +bool GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qGetWorkingDir", response) == + PacketResult::Success) { + if (response.IsUnsupportedResponse()) + return false; + if (response.IsErrorResponse()) + return false; + std::string cwd; + response.GetHexByteString(cwd); + working_dir.SetFile(cwd, GetHostArchitecture().GetTriple()); + return !cwd.empty(); + } + return false; +} + +int GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) { + if (working_dir) { + std::string path{working_dir.GetPath(false)}; + StreamString packet; + packet.PutCString("QSetWorkingDir:"); + packet.PutStringAsRawHex8(path); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int GDBRemoteCommunicationClient::SetDisableASLR(bool enable) { + char packet[32]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "QSetDisableASLR:%i", enable ? 1 : 0); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + return -1; +} + +int GDBRemoteCommunicationClient::SetDetachOnError(bool enable) { + char packet[32]; + const int packet_len = ::snprintf(packet, sizeof(packet), + "QSetDetachOnError:%i", enable ? 1 : 0); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == PacketResult::Success) { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + return -1; +} + +bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse( + StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) { + if (response.IsNormalResponse()) { + llvm::StringRef name; + llvm::StringRef value; + StringExtractor extractor; + + uint32_t cpu = LLDB_INVALID_CPUTYPE; + uint32_t sub = 0; + std::string vendor; + std::string os_type; + + while (response.GetNameColonValue(name, value)) { + if (name == "pid") { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + value.getAsInteger(0, pid); + process_info.SetProcessID(pid); + } else if (name == "ppid") { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + value.getAsInteger(0, pid); + process_info.SetParentProcessID(pid); + } else if (name == "uid") { + uint32_t uid = UINT32_MAX; + value.getAsInteger(0, uid); + process_info.SetUserID(uid); + } else if (name == "euid") { + uint32_t uid = UINT32_MAX; + value.getAsInteger(0, uid); + process_info.SetEffectiveUserID(uid); + } else if (name == "gid") { + uint32_t gid = UINT32_MAX; + value.getAsInteger(0, gid); + process_info.SetGroupID(gid); + } else if (name == "egid") { + uint32_t gid = UINT32_MAX; + value.getAsInteger(0, gid); + process_info.SetEffectiveGroupID(gid); + } else if (name == "triple") { + StringExtractor extractor(value); + std::string triple; + extractor.GetHexByteString(triple); + process_info.GetArchitecture().SetTriple(triple.c_str()); + } else if (name == "name") { + StringExtractor extractor(value); + // The process name from ASCII hex bytes since we can't control the + // characters in a process name + std::string name; + extractor.GetHexByteString(name); + process_info.GetExecutableFile().SetFile(name, FileSpec::Style::native); + } else if (name == "args") { + llvm::StringRef encoded_args(value), hex_arg; + + bool is_arg0 = true; + while (!encoded_args.empty()) { + std::tie(hex_arg, encoded_args) = encoded_args.split('-'); + std::string arg; + StringExtractor extractor(hex_arg); + if (extractor.GetHexByteString(arg) * 2 != hex_arg.size()) { + // In case of wrong encoding, we discard all the arguments + process_info.GetArguments().Clear(); + process_info.SetArg0(""); + break; + } + if (is_arg0) + process_info.SetArg0(arg); + else + process_info.GetArguments().AppendArgument(arg); + is_arg0 = false; + } + } else if (name == "cputype") { + value.getAsInteger(0, cpu); + } else if (name == "cpusubtype") { + value.getAsInteger(0, sub); + } else if (name == "vendor") { + vendor = std::string(value); + } else if (name == "ostype") { + os_type = std::string(value); + } + } + + if (cpu != LLDB_INVALID_CPUTYPE && !vendor.empty() && !os_type.empty()) { + if (vendor == "apple") { + process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, + sub); + process_info.GetArchitecture().GetTriple().setVendorName( + llvm::StringRef(vendor)); + process_info.GetArchitecture().GetTriple().setOSName( + llvm::StringRef(os_type)); + } + } + + if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + return true; + } + return false; +} + +bool GDBRemoteCommunicationClient::GetProcessInfo( + lldb::pid_t pid, ProcessInstanceInfo &process_info) { + process_info.Clear(); + + if (m_supports_qProcessInfoPID) { + char packet[32]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "qProcessInfoPID:%" PRIu64, pid); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success) { + return DecodeProcessInfoResponse(response, process_info); + } else { + m_supports_qProcessInfoPID = false; + return false; + } + } + return false; +} + +bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) { + Log *log(GetLog(GDBRLog::Process | GDBRLog::Packets)); + + if (allow_lazy) { + if (m_qProcessInfo_is_valid == eLazyBoolYes) + return true; + if (m_qProcessInfo_is_valid == eLazyBoolNo) + return false; + } + + GetHostInfo(); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qProcessInfo", response) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + llvm::StringRef name; + llvm::StringRef value; + uint32_t cpu = LLDB_INVALID_CPUTYPE; + uint32_t sub = 0; + std::string arch_name; + std::string os_name; + std::string environment; + std::string vendor_name; + std::string triple; + std::string elf_abi; + uint32_t pointer_byte_size = 0; + StringExtractor extractor; + ByteOrder byte_order = eByteOrderInvalid; + uint32_t num_keys_decoded = 0; + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + while (response.GetNameColonValue(name, value)) { + if (name == "cputype") { + if (!value.getAsInteger(16, cpu)) + ++num_keys_decoded; + } else if (name == "cpusubtype") { + if (!value.getAsInteger(16, sub)) { + ++num_keys_decoded; + // Workaround for pre-2024 Apple debugserver, which always + // returns arm64e on arm64e-capable hardware regardless of + // what the process is. This can be deleted at some point + // in the future. + if (cpu == llvm::MachO::CPU_TYPE_ARM64 && + sub == llvm::MachO::CPU_SUBTYPE_ARM64E) { + if (GetGDBServerVersion()) + if (m_gdb_server_version >= 1000 && + m_gdb_server_version <= 1504) + sub = 0; + } + } + } else if (name == "triple") { + StringExtractor extractor(value); + extractor.GetHexByteString(triple); + ++num_keys_decoded; + } else if (name == "ostype") { + ParseOSType(value, os_name, environment); + ++num_keys_decoded; + } else if (name == "vendor") { + vendor_name = std::string(value); + ++num_keys_decoded; + } else if (name == "endian") { + byte_order = llvm::StringSwitch<lldb::ByteOrder>(value) + .Case("little", eByteOrderLittle) + .Case("big", eByteOrderBig) + .Case("pdp", eByteOrderPDP) + .Default(eByteOrderInvalid); + if (byte_order != eByteOrderInvalid) + ++num_keys_decoded; + } else if (name == "ptrsize") { + if (!value.getAsInteger(16, pointer_byte_size)) + ++num_keys_decoded; + } else if (name == "pid") { + if (!value.getAsInteger(16, pid)) + ++num_keys_decoded; + } else if (name == "elf_abi") { + elf_abi = std::string(value); + ++num_keys_decoded; + } else if (name == "main-binary-uuid") { + m_process_standalone_uuid.SetFromStringRef(value); + ++num_keys_decoded; + } else if (name == "main-binary-slide") { + StringExtractor extractor(value); + m_process_standalone_value = + extractor.GetU64(LLDB_INVALID_ADDRESS, 16); + if (m_process_standalone_value != LLDB_INVALID_ADDRESS) { + m_process_standalone_value_is_offset = true; + ++num_keys_decoded; + } + } else if (name == "main-binary-address") { + StringExtractor extractor(value); + m_process_standalone_value = + extractor.GetU64(LLDB_INVALID_ADDRESS, 16); + if (m_process_standalone_value != LLDB_INVALID_ADDRESS) { + m_process_standalone_value_is_offset = false; + ++num_keys_decoded; + } + } else if (name == "binary-addresses") { + m_binary_addresses.clear(); + ++num_keys_decoded; + for (llvm::StringRef x : llvm::split(value, ',')) { + addr_t vmaddr; + x.consume_front("0x"); + if (llvm::to_integer(x, vmaddr, 16)) + m_binary_addresses.push_back(vmaddr); + } + } + } + if (num_keys_decoded > 0) + m_qProcessInfo_is_valid = eLazyBoolYes; + if (pid != LLDB_INVALID_PROCESS_ID) { + m_curr_pid_is_valid = eLazyBoolYes; + m_curr_pid_run = m_curr_pid = pid; + } + + // Set the ArchSpec from the triple if we have it. + if (!triple.empty()) { + m_process_arch.SetTriple(triple.c_str()); + m_process_arch.SetFlags(elf_abi); + if (pointer_byte_size) { + assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); + } + } else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && + !vendor_name.empty()) { + llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name); + if (!environment.empty()) + triple.setEnvironmentName(environment); + + assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat); + assert(triple.getObjectFormat() != llvm::Triple::Wasm); + assert(triple.getObjectFormat() != llvm::Triple::XCOFF); + switch (triple.getObjectFormat()) { + case llvm::Triple::MachO: + m_process_arch.SetArchitecture(eArchTypeMachO, cpu, sub); + break; + case llvm::Triple::ELF: + m_process_arch.SetArchitecture(eArchTypeELF, cpu, sub); + break; + case llvm::Triple::COFF: + m_process_arch.SetArchitecture(eArchTypeCOFF, cpu, sub); + break; + case llvm::Triple::GOFF: + case llvm::Triple::SPIRV: + case llvm::Triple::Wasm: + case llvm::Triple::XCOFF: + case llvm::Triple::DXContainer: + LLDB_LOGF(log, "error: not supported target architecture"); + return false; + case llvm::Triple::UnknownObjectFormat: + LLDB_LOGF(log, "error: failed to determine target architecture"); + return false; + } + + if (pointer_byte_size) { + assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) { + assert(byte_order == m_process_arch.GetByteOrder()); + } + m_process_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name)); + m_process_arch.GetTriple().setOSName(llvm::StringRef(os_name)); + m_process_arch.GetTriple().setEnvironmentName(llvm::StringRef(environment)); + } + return true; + } + } else { + m_qProcessInfo_is_valid = eLazyBoolNo; + } + + return false; +} + +uint32_t GDBRemoteCommunicationClient::FindProcesses( + const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) { + process_infos.clear(); + + if (m_supports_qfProcessInfo) { + StreamString packet; + packet.PutCString("qfProcessInfo"); + if (!match_info.MatchAllProcesses()) { + packet.PutChar(':'); + const char *name = match_info.GetProcessInfo().GetName(); + bool has_name_match = false; + if (name && name[0]) { + has_name_match = true; + NameMatch name_match_type = match_info.GetNameMatchType(); + switch (name_match_type) { + case NameMatch::Ignore: + has_name_match = false; + break; + + case NameMatch::Equals: + packet.PutCString("name_match:equals;"); + break; + + case NameMatch::Contains: + packet.PutCString("name_match:contains;"); + break; + + case NameMatch::StartsWith: + packet.PutCString("name_match:starts_with;"); + break; + + case NameMatch::EndsWith: + packet.PutCString("name_match:ends_with;"); + break; + + case NameMatch::RegularExpression: + packet.PutCString("name_match:regex;"); + break; + } + if (has_name_match) { + packet.PutCString("name:"); + packet.PutBytesAsRawHex8(name, ::strlen(name)); + packet.PutChar(';'); + } + } + + if (match_info.GetProcessInfo().ProcessIDIsValid()) + packet.Printf("pid:%" PRIu64 ";", + match_info.GetProcessInfo().GetProcessID()); + if (match_info.GetProcessInfo().ParentProcessIDIsValid()) + packet.Printf("parent_pid:%" PRIu64 ";", + match_info.GetProcessInfo().GetParentProcessID()); + if (match_info.GetProcessInfo().UserIDIsValid()) + packet.Printf("uid:%u;", match_info.GetProcessInfo().GetUserID()); + if (match_info.GetProcessInfo().GroupIDIsValid()) + packet.Printf("gid:%u;", match_info.GetProcessInfo().GetGroupID()); + if (match_info.GetProcessInfo().EffectiveUserIDIsValid()) + packet.Printf("euid:%u;", + match_info.GetProcessInfo().GetEffectiveUserID()); + if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) + packet.Printf("egid:%u;", + match_info.GetProcessInfo().GetEffectiveGroupID()); + packet.Printf("all_users:%u;", match_info.GetMatchAllUsers() ? 1 : 0); + if (match_info.GetProcessInfo().GetArchitecture().IsValid()) { + const ArchSpec &match_arch = + match_info.GetProcessInfo().GetArchitecture(); + const llvm::Triple &triple = match_arch.GetTriple(); + packet.PutCString("triple:"); + packet.PutCString(triple.getTriple()); + packet.PutChar(';'); + } + } + StringExtractorGDBRemote response; + // Increase timeout as the first qfProcessInfo packet takes a long time on + // Android. The value of 1min was arrived at empirically. + ScopedTimeout timeout(*this, minutes(1)); + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + do { + ProcessInstanceInfo process_info; + if (!DecodeProcessInfoResponse(response, process_info)) + break; + process_infos.push_back(process_info); + response = StringExtractorGDBRemote(); + } while (SendPacketAndWaitForResponse("qsProcessInfo", response) == + PacketResult::Success); + } else { + m_supports_qfProcessInfo = false; + return 0; + } + } + return process_infos.size(); +} + +bool GDBRemoteCommunicationClient::GetUserName(uint32_t uid, + std::string &name) { + if (m_supports_qUserName) { + char packet[32]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "qUserName:%i", uid); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + // Make sure we parsed the right number of characters. The response is + // the hex encoded user name and should make up the entire packet. If + // there are any non-hex ASCII bytes, the length won't match below.. + if (response.GetHexByteString(name) * 2 == + response.GetStringRef().size()) + return true; + } + } else { + m_supports_qUserName = false; + return false; + } + } + return false; +} + +bool GDBRemoteCommunicationClient::GetGroupName(uint32_t gid, + std::string &name) { + if (m_supports_qGroupName) { + char packet[32]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "qGroupName:%i", gid); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success) { + if (response.IsNormalResponse()) { + // Make sure we parsed the right number of characters. The response is + // the hex encoded group name and should make up the entire packet. If + // there are any non-hex ASCII bytes, the length won't match below.. + if (response.GetHexByteString(name) * 2 == + response.GetStringRef().size()) + return true; + } + } else { + m_supports_qGroupName = false; + return false; + } + } + return false; +} + +static void MakeSpeedTestPacket(StreamString &packet, uint32_t send_size, + uint32_t recv_size) { + packet.Clear(); + packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); + uint32_t bytes_left = send_size; + while (bytes_left > 0) { + if (bytes_left >= 26) { + packet.PutCString("abcdefghijklmnopqrstuvwxyz"); + bytes_left -= 26; + } else { + packet.Printf("%*.*s;", bytes_left, bytes_left, + "abcdefghijklmnopqrstuvwxyz"); + bytes_left = 0; + } + } +} + +duration<float> +calculate_standard_deviation(const std::vector<duration<float>> &v) { + if (v.size() == 0) + return duration<float>::zero(); + using Dur = duration<float>; + Dur sum = std::accumulate(std::begin(v), std::end(v), Dur()); + Dur mean = sum / v.size(); + float accum = 0; + for (auto d : v) { + float delta = (d - mean).count(); + accum += delta * delta; + }; + + return Dur(sqrtf(accum / (v.size() - 1))); +} + +void GDBRemoteCommunicationClient::TestPacketSpeed(const uint32_t num_packets, + uint32_t max_send, + uint32_t max_recv, + uint64_t recv_amount, + bool json, Stream &strm) { + + if (SendSpeedTestPacket(0, 0)) { + StreamString packet; + if (json) + strm.Printf("{ \"packet_speeds\" : {\n \"num_packets\" : %u,\n " + "\"results\" : [", + num_packets); + else + strm.Printf("Testing sending %u packets of various sizes:\n", + num_packets); + strm.Flush(); + + uint32_t result_idx = 0; + uint32_t send_size; + std::vector<duration<float>> packet_times; + + for (send_size = 0; send_size <= max_send; + send_size ? send_size *= 2 : send_size = 4) { + for (uint32_t recv_size = 0; recv_size <= max_recv; + recv_size ? recv_size *= 2 : recv_size = 4) { + MakeSpeedTestPacket(packet, send_size, recv_size); + + packet_times.clear(); + // Test how long it takes to send 'num_packets' packets + const auto start_time = steady_clock::now(); + for (uint32_t i = 0; i < num_packets; ++i) { + const auto packet_start_time = steady_clock::now(); + StringExtractorGDBRemote response; + SendPacketAndWaitForResponse(packet.GetString(), response); + const auto packet_end_time = steady_clock::now(); + packet_times.push_back(packet_end_time - packet_start_time); + } + const auto end_time = steady_clock::now(); + const auto total_time = end_time - start_time; + + float packets_per_second = + ((float)num_packets) / duration<float>(total_time).count(); + auto average_per_packet = num_packets > 0 ? total_time / num_packets + : duration<float>::zero(); + const duration<float> standard_deviation = + calculate_standard_deviation(packet_times); + if (json) { + strm.Format("{0}\n {{\"send_size\" : {1,6}, \"recv_size\" : " + "{2,6}, \"total_time_nsec\" : {3,12:ns-}, " + "\"standard_deviation_nsec\" : {4,9:ns-f0}}", + result_idx > 0 ? "," : "", send_size, recv_size, + total_time, standard_deviation); + ++result_idx; + } else { + strm.Format("qSpeedTest(send={0,7}, recv={1,7}) in {2:s+f9} for " + "{3,9:f2} packets/s ({4,10:ms+f6} per packet) with " + "standard deviation of {5,10:ms+f6}\n", + send_size, recv_size, duration<float>(total_time), + packets_per_second, duration<float>(average_per_packet), + standard_deviation); + } + strm.Flush(); + } + } + + const float k_recv_amount_mb = (float)recv_amount / (1024.0f * 1024.0f); + if (json) + strm.Printf("\n ]\n },\n \"download_speed\" : {\n \"byte_size\" " + ": %" PRIu64 ",\n \"results\" : [", + recv_amount); + else + strm.Printf("Testing receiving %2.1fMB of data using varying receive " + "packet sizes:\n", + k_recv_amount_mb); + strm.Flush(); + send_size = 0; + result_idx = 0; + for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) { + MakeSpeedTestPacket(packet, send_size, recv_size); + + // If we have a receive size, test how long it takes to receive 4MB of + // data + if (recv_size > 0) { + const auto start_time = steady_clock::now(); + uint32_t bytes_read = 0; + uint32_t packet_count = 0; + while (bytes_read < recv_amount) { + StringExtractorGDBRemote response; + SendPacketAndWaitForResponse(packet.GetString(), response); + bytes_read += recv_size; + ++packet_count; + } + const auto end_time = steady_clock::now(); + const auto total_time = end_time - start_time; + float mb_second = ((float)recv_amount) / + duration<float>(total_time).count() / + (1024.0 * 1024.0); + float packets_per_second = + ((float)packet_count) / duration<float>(total_time).count(); + const auto average_per_packet = packet_count > 0 + ? total_time / packet_count + : duration<float>::zero(); + + if (json) { + strm.Format("{0}\n {{\"send_size\" : {1,6}, \"recv_size\" : " + "{2,6}, \"total_time_nsec\" : {3,12:ns-}}", + result_idx > 0 ? "," : "", send_size, recv_size, + total_time); + ++result_idx; + } else { + strm.Format("qSpeedTest(send={0,7}, recv={1,7}) {2,6} packets needed " + "to receive {3:f1}MB in {4:s+f9} for {5} MB/sec for " + "{6,9:f2} packets/sec ({7,10:ms+f6} per packet)\n", + send_size, recv_size, packet_count, k_recv_amount_mb, + duration<float>(total_time), mb_second, + packets_per_second, duration<float>(average_per_packet)); + } + strm.Flush(); + } + } + if (json) + strm.Printf("\n ]\n }\n}\n"); + else + strm.EOL(); + } +} + +bool GDBRemoteCommunicationClient::SendSpeedTestPacket(uint32_t send_size, + uint32_t recv_size) { + StreamString packet; + packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); + uint32_t bytes_left = send_size; + while (bytes_left > 0) { + if (bytes_left >= 26) { + packet.PutCString("abcdefghijklmnopqrstuvwxyz"); + bytes_left -= 26; + } else { + packet.Printf("%*.*s;", bytes_left, bytes_left, + "abcdefghijklmnopqrstuvwxyz"); + bytes_left = 0; + } + } + + StringExtractorGDBRemote response; + return SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success; +} + +bool GDBRemoteCommunicationClient::LaunchGDBServer( + const char *remote_accept_hostname, lldb::pid_t &pid, uint16_t &port, + std::string &socket_name) { + pid = LLDB_INVALID_PROCESS_ID; + port = 0; + socket_name.clear(); + + StringExtractorGDBRemote response; + StreamString stream; + stream.PutCString("qLaunchGDBServer;"); + std::string hostname; + if (remote_accept_hostname && remote_accept_hostname[0]) + hostname = remote_accept_hostname; + else { + if (HostInfo::GetHostname(hostname)) { + // Make the GDB server we launch only accept connections from this host + stream.Printf("host:%s;", hostname.c_str()); + } else { + // Make the GDB server we launch accept connections from any host since + // we can't figure out the hostname + stream.Printf("host:*;"); + } + } + // give the process a few seconds to startup + ScopedTimeout timeout(*this, seconds(10)); + + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + if (response.IsErrorResponse()) + return false; + + llvm::StringRef name; + llvm::StringRef value; + while (response.GetNameColonValue(name, value)) { + if (name == "port") + value.getAsInteger(0, port); + else if (name == "pid") + value.getAsInteger(0, pid); + else if (name.compare("socket_name") == 0) { + StringExtractor extractor(value); + extractor.GetHexByteString(socket_name); + } + } + return true; + } + return false; +} + +size_t GDBRemoteCommunicationClient::QueryGDBServer( + std::vector<std::pair<uint16_t, std::string>> &connection_urls) { + connection_urls.clear(); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qQueryGDBServer", response) != + PacketResult::Success) + return 0; + + StructuredData::ObjectSP data = + StructuredData::ParseJSON(response.GetStringRef()); + if (!data) + return 0; + + StructuredData::Array *array = data->GetAsArray(); + if (!array) + return 0; + + for (size_t i = 0, count = array->GetSize(); i < count; ++i) { + std::optional<StructuredData::Dictionary *> maybe_element = + array->GetItemAtIndexAsDictionary(i); + if (!maybe_element) + continue; + + StructuredData::Dictionary *element = *maybe_element; + uint16_t port = 0; + if (StructuredData::ObjectSP port_osp = + element->GetValueForKey(llvm::StringRef("port"))) + port = port_osp->GetUnsignedIntegerValue(0); + + std::string socket_name; + if (StructuredData::ObjectSP socket_name_osp = + element->GetValueForKey(llvm::StringRef("socket_name"))) + socket_name = std::string(socket_name_osp->GetStringValue()); + + if (port != 0 || !socket_name.empty()) + connection_urls.emplace_back(port, socket_name); + } + return connection_urls.size(); +} + +bool GDBRemoteCommunicationClient::KillSpawnedProcess(lldb::pid_t pid) { + StreamString stream; + stream.Printf("qKillSpawnedProcess:%" PRId64, pid); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) + return true; + } + return false; +} + +std::optional<PidTid> GDBRemoteCommunicationClient::SendSetCurrentThreadPacket( + uint64_t tid, uint64_t pid, char op) { + lldb_private::StreamString packet; + packet.PutChar('H'); + packet.PutChar(op); + + if (pid != LLDB_INVALID_PROCESS_ID) + packet.Printf("p%" PRIx64 ".", pid); + + if (tid == UINT64_MAX) + packet.PutCString("-1"); + else + packet.Printf("%" PRIx64, tid); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) + return {{pid, tid}}; + + /* + * Connected bare-iron target (like YAMON gdb-stub) may not have support for + * Hg packet. + * The reply from '?' packet could be as simple as 'S05'. There is no packet + * which can + * give us pid and/or tid. Assume pid=tid=1 in such cases. + */ + if (response.IsUnsupportedResponse() && IsConnected()) + return {{1, 1}}; + } + return std::nullopt; +} + +bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid, + uint64_t pid) { + if (m_curr_tid == tid && + (m_curr_pid == pid || LLDB_INVALID_PROCESS_ID == pid)) + return true; + + std::optional<PidTid> ret = SendSetCurrentThreadPacket(tid, pid, 'g'); + if (ret) { + if (ret->pid != LLDB_INVALID_PROCESS_ID) + m_curr_pid = ret->pid; + m_curr_tid = ret->tid; + } + return ret.has_value(); +} + +bool GDBRemoteCommunicationClient::SetCurrentThreadForRun(uint64_t tid, + uint64_t pid) { + if (m_curr_tid_run == tid && + (m_curr_pid_run == pid || LLDB_INVALID_PROCESS_ID == pid)) + return true; + + std::optional<PidTid> ret = SendSetCurrentThreadPacket(tid, pid, 'c'); + if (ret) { + if (ret->pid != LLDB_INVALID_PROCESS_ID) + m_curr_pid_run = ret->pid; + m_curr_tid_run = ret->tid; + } + return ret.has_value(); +} + +bool GDBRemoteCommunicationClient::GetStopReply( + StringExtractorGDBRemote &response) { + if (SendPacketAndWaitForResponse("?", response) == PacketResult::Success) + return response.IsNormalResponse(); + return false; +} + +bool GDBRemoteCommunicationClient::GetThreadStopInfo( + lldb::tid_t tid, StringExtractorGDBRemote &response) { + if (m_supports_qThreadStopInfo) { + char packet[256]; + int packet_len = + ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + if (SendPacketAndWaitForResponse(packet, response) == + PacketResult::Success) { + if (response.IsUnsupportedResponse()) + m_supports_qThreadStopInfo = false; + else if (response.IsNormalResponse()) + return true; + else + return false; + } else { + m_supports_qThreadStopInfo = false; + } + } + return false; +} + +uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket( + GDBStoppointType type, bool insert, addr_t addr, uint32_t length, + std::chrono::seconds timeout) { + Log *log = GetLog(LLDBLog::Breakpoints); + LLDB_LOGF(log, "GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64, + __FUNCTION__, insert ? "add" : "remove", addr); + + // Check if the stub is known not to support this breakpoint type + if (!SupportsGDBStoppointPacket(type)) + return UINT8_MAX; + // Construct the breakpoint packet + char packet[64]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "%c%i,%" PRIx64 ",%x", + insert ? 'Z' : 'z', type, addr, length); + // Check we haven't overwritten the end of the packet buffer + assert(packet_len + 1 < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + // Make sure the response is either "OK", "EXX" where XX are two hex digits, + // or "" (unsupported) + response.SetResponseValidatorToOKErrorNotSupported(); + // Try to send the breakpoint packet, and check that it was correctly sent + if (SendPacketAndWaitForResponse(packet, response, timeout) == + PacketResult::Success) { + // Receive and OK packet when the breakpoint successfully placed + if (response.IsOKResponse()) + return 0; + + // Status while setting breakpoint, send back specific error + if (response.IsErrorResponse()) + return response.GetError(); + + // Empty packet informs us that breakpoint is not supported + if (response.IsUnsupportedResponse()) { + // Disable this breakpoint type since it is unsupported + switch (type) { + case eBreakpointSoftware: + m_supports_z0 = false; + break; + case eBreakpointHardware: + m_supports_z1 = false; + break; + case eWatchpointWrite: + m_supports_z2 = false; + break; + case eWatchpointRead: + m_supports_z3 = false; + break; + case eWatchpointReadWrite: + m_supports_z4 = false; + break; + case eStoppointInvalid: + return UINT8_MAX; + } + } + } + // Signal generic failure + return UINT8_MAX; +} + +std::vector<std::pair<lldb::pid_t, lldb::tid_t>> +GDBRemoteCommunicationClient::GetCurrentProcessAndThreadIDs( + bool &sequence_mutex_unavailable) { + std::vector<std::pair<lldb::pid_t, lldb::tid_t>> ids; + + Lock lock(*this); + if (lock) { + sequence_mutex_unavailable = false; + StringExtractorGDBRemote response; + + PacketResult packet_result; + for (packet_result = + SendPacketAndWaitForResponseNoLock("qfThreadInfo", response); + packet_result == PacketResult::Success && response.IsNormalResponse(); + packet_result = + SendPacketAndWaitForResponseNoLock("qsThreadInfo", response)) { + char ch = response.GetChar(); + if (ch == 'l') + break; + if (ch == 'm') { + do { + auto pid_tid = response.GetPidTid(LLDB_INVALID_PROCESS_ID); + // If we get an invalid response, break out of the loop. + // If there are valid tids, they have been added to ids. + // If there are no valid tids, we'll fall through to the + // bare-iron target handling below. + if (!pid_tid) + break; + + ids.push_back(*pid_tid); + ch = response.GetChar(); // Skip the command separator + } while (ch == ','); // Make sure we got a comma separator + } + } + + /* + * Connected bare-iron target (like YAMON gdb-stub) may not have support for + * qProcessInfo, qC and qfThreadInfo packets. The reply from '?' packet + * could + * be as simple as 'S05'. There is no packet which can give us pid and/or + * tid. + * Assume pid=tid=1 in such cases. + */ + if ((response.IsUnsupportedResponse() || response.IsNormalResponse()) && + ids.size() == 0 && IsConnected()) { + ids.emplace_back(1, 1); + } + } else { + Log *log(GetLog(GDBRLog::Process | GDBRLog::Packets)); + LLDB_LOG(log, "error: failed to get packet sequence mutex, not sending " + "packet 'qfThreadInfo'"); + sequence_mutex_unavailable = true; + } + + return ids; +} + +size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs( + std::vector<lldb::tid_t> &thread_ids, bool &sequence_mutex_unavailable) { + lldb::pid_t pid = GetCurrentProcessID(); + thread_ids.clear(); + + auto ids = GetCurrentProcessAndThreadIDs(sequence_mutex_unavailable); + if (ids.empty() || sequence_mutex_unavailable) + return 0; + + for (auto id : ids) { + // skip threads that do not belong to the current process + if (id.first != LLDB_INVALID_PROCESS_ID && id.first != pid) + continue; + if (id.second != LLDB_INVALID_THREAD_ID && + id.second != StringExtractorGDBRemote::AllThreads) + thread_ids.push_back(id.second); + } + + return thread_ids.size(); +} + +lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qShlibInfoAddr", response) != + PacketResult::Success || + !response.IsNormalResponse()) + return LLDB_INVALID_ADDRESS; + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); +} + +lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand( + llvm::StringRef command, + const FileSpec & + working_dir, // Pass empty FileSpec to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the + // process to exit + std::string + *command_output, // Pass NULL if you don't want the command output + const Timeout<std::micro> &timeout) { + lldb_private::StreamString stream; + stream.PutCString("qPlatform_shell:"); + stream.PutBytesAsRawHex8(command.data(), command.size()); + stream.PutChar(','); + uint32_t timeout_sec = UINT32_MAX; + if (timeout) { + // TODO: Use chrono version of std::ceil once c++17 is available. + timeout_sec = std::ceil(std::chrono::duration<double>(*timeout).count()); + } + stream.PutHex32(timeout_sec); + if (working_dir) { + std::string path{working_dir.GetPath(false)}; + stream.PutChar(','); + stream.PutStringAsRawHex8(path); + } + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + if (response.GetChar() != 'F') + return Status("malformed reply"); + if (response.GetChar() != ',') + return Status("malformed reply"); + uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX); + if (exitcode == UINT32_MAX) + return Status("unable to run remote process"); + else if (status_ptr) + *status_ptr = exitcode; + if (response.GetChar() != ',') + return Status("malformed reply"); + uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX); + if (signo_ptr) + *signo_ptr = signo; + if (response.GetChar() != ',') + return Status("malformed reply"); + std::string output; + response.GetEscapedBinaryData(output); + if (command_output) + command_output->assign(output); + return Status(); + } + return Status("unable to send packet"); +} + +Status GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec, + uint32_t file_permissions) { + std::string path{file_spec.GetPath(false)}; + lldb_private::StreamString stream; + stream.PutCString("qPlatform_mkdir:"); + stream.PutHex32(file_permissions); + stream.PutChar(','); + stream.PutStringAsRawHex8(path); + llvm::StringRef packet = stream.GetString(); + StringExtractorGDBRemote response; + + if (SendPacketAndWaitForResponse(packet, response) != PacketResult::Success) + return Status("failed to send '%s' packet", packet.str().c_str()); + + if (response.GetChar() != 'F') + return Status("invalid response to '%s' packet", packet.str().c_str()); + + return Status(response.GetHexMaxU32(false, UINT32_MAX), eErrorTypePOSIX); +} + +Status +GDBRemoteCommunicationClient::SetFilePermissions(const FileSpec &file_spec, + uint32_t file_permissions) { + std::string path{file_spec.GetPath(false)}; + lldb_private::StreamString stream; + stream.PutCString("qPlatform_chmod:"); + stream.PutHex32(file_permissions); + stream.PutChar(','); + stream.PutStringAsRawHex8(path); + llvm::StringRef packet = stream.GetString(); + StringExtractorGDBRemote response; + + if (SendPacketAndWaitForResponse(packet, response) != PacketResult::Success) + return Status("failed to send '%s' packet", stream.GetData()); + + if (response.GetChar() != 'F') + return Status("invalid response to '%s' packet", stream.GetData()); + + return Status(response.GetHexMaxU32(false, UINT32_MAX), eErrorTypePOSIX); +} + +static int gdb_errno_to_system(int err) { + switch (err) { +#define HANDLE_ERRNO(name, value) \ + case GDB_##name: \ + return name; +#include "Plugins/Process/gdb-remote/GDBRemoteErrno.def" + default: + return -1; + } +} + +static uint64_t ParseHostIOPacketResponse(StringExtractorGDBRemote &response, + uint64_t fail_result, Status &error) { + response.SetFilePos(0); + if (response.GetChar() != 'F') + return fail_result; + int32_t result = response.GetS32(-2, 16); + if (result == -2) + return fail_result; + if (response.GetChar() == ',') { + int result_errno = gdb_errno_to_system(response.GetS32(-1, 16)); + if (result_errno != -1) + error.SetError(result_errno, eErrorTypePOSIX); + else + error.SetError(-1, eErrorTypeGeneric); + } else + error.Clear(); + return result; +} +lldb::user_id_t +GDBRemoteCommunicationClient::OpenFile(const lldb_private::FileSpec &file_spec, + File::OpenOptions flags, mode_t mode, + Status &error) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:open:"); + if (path.empty()) + return UINT64_MAX; + stream.PutStringAsRawHex8(path); + stream.PutChar(','); + stream.PutHex32(flags); + stream.PutChar(','); + stream.PutHex32(mode); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + return ParseHostIOPacketResponse(response, UINT64_MAX, error); + } + return UINT64_MAX; +} + +bool GDBRemoteCommunicationClient::CloseFile(lldb::user_id_t fd, + Status &error) { + lldb_private::StreamString stream; + stream.Printf("vFile:close:%x", (int)fd); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + return ParseHostIOPacketResponse(response, -1, error) == 0; + } + return false; +} + +std::optional<GDBRemoteFStatData> +GDBRemoteCommunicationClient::FStat(lldb::user_id_t fd) { + lldb_private::StreamString stream; + stream.Printf("vFile:fstat:%" PRIx64, fd); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + if (response.GetChar() != 'F') + return std::nullopt; + int64_t size = response.GetS64(-1, 16); + if (size > 0 && response.GetChar() == ';') { + std::string buffer; + if (response.GetEscapedBinaryData(buffer)) { + GDBRemoteFStatData out; + if (buffer.size() != sizeof(out)) + return std::nullopt; + memcpy(&out, buffer.data(), sizeof(out)); + return out; + } + } + } + return std::nullopt; +} + +std::optional<GDBRemoteFStatData> +GDBRemoteCommunicationClient::Stat(const lldb_private::FileSpec &file_spec) { + Status error; + lldb::user_id_t fd = OpenFile(file_spec, File::eOpenOptionReadOnly, 0, error); + if (fd == UINT64_MAX) + return std::nullopt; + std::optional<GDBRemoteFStatData> st = FStat(fd); + CloseFile(fd, error); + return st; +} + +// Extension of host I/O packets to get the file size. +lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize( + const lldb_private::FileSpec &file_spec) { + if (m_supports_vFileSize) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:size:"); + stream.PutStringAsRawHex8(path); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) != + PacketResult::Success) + return UINT64_MAX; + + if (!response.IsUnsupportedResponse()) { + if (response.GetChar() != 'F') + return UINT64_MAX; + uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); + return retcode; + } + m_supports_vFileSize = false; + } + + // Fallback to fstat. + std::optional<GDBRemoteFStatData> st = Stat(file_spec); + return st ? st->gdb_st_size : UINT64_MAX; +} + +void GDBRemoteCommunicationClient::AutoCompleteDiskFileOrDirectory( + CompletionRequest &request, bool only_dir) { + lldb_private::StreamString stream; + stream.PutCString("qPathComplete:"); + stream.PutHex32(only_dir ? 1 : 0); + stream.PutChar(','); + stream.PutStringAsRawHex8(request.GetCursorArgumentPrefix()); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + StreamString strm; + char ch = response.GetChar(); + if (ch != 'M') + return; + while (response.Peek()) { + strm.Clear(); + while ((ch = response.GetHexU8(0, false)) != '\0') + strm.PutChar(ch); + request.AddCompletion(strm.GetString()); + if (response.GetChar() != ',') + break; + } + } +} + +Status +GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec, + uint32_t &file_permissions) { + if (m_supports_vFileMode) { + std::string path{file_spec.GetPath(false)}; + Status error; + lldb_private::StreamString stream; + stream.PutCString("vFile:mode:"); + stream.PutStringAsRawHex8(path); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) != + PacketResult::Success) { + error.SetErrorStringWithFormat("failed to send '%s' packet", + stream.GetData()); + return error; + } + if (!response.IsUnsupportedResponse()) { + if (response.GetChar() != 'F') { + error.SetErrorStringWithFormat("invalid response to '%s' packet", + stream.GetData()); + } else { + const uint32_t mode = response.GetS32(-1, 16); + if (static_cast<int32_t>(mode) == -1) { + if (response.GetChar() == ',') { + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + else + error.SetErrorToGenericError(); + } else + error.SetErrorToGenericError(); + } else { + file_permissions = mode & (S_IRWXU | S_IRWXG | S_IRWXO); + } + } + return error; + } else { // response.IsUnsupportedResponse() + m_supports_vFileMode = false; + } + } + + // Fallback to fstat. + if (std::optional<GDBRemoteFStatData> st = Stat(file_spec)) { + file_permissions = st->gdb_st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + return Status(); + } + return Status("fstat failed"); +} + +uint64_t GDBRemoteCommunicationClient::ReadFile(lldb::user_id_t fd, + uint64_t offset, void *dst, + uint64_t dst_len, + Status &error) { + lldb_private::StreamString stream; + stream.Printf("vFile:pread:%x,%" PRIx64 ",%" PRIx64, (int)fd, dst_len, + offset); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + if (response.GetChar() != 'F') + return 0; + int64_t retcode = response.GetS64(-1, 16); + if (retcode == -1) { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') { + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + return -1; + } + const char next = (response.Peek() ? *response.Peek() : 0); + if (next == ',') + return 0; + if (next == ';') { + response.GetChar(); // skip the semicolon + std::string buffer; + if (response.GetEscapedBinaryData(buffer)) { + const uint64_t data_to_write = + std::min<uint64_t>(dst_len, buffer.size()); + if (data_to_write > 0) + memcpy(dst, &buffer[0], data_to_write); + return data_to_write; + } + } + } + return 0; +} + +uint64_t GDBRemoteCommunicationClient::WriteFile(lldb::user_id_t fd, + uint64_t offset, + const void *src, + uint64_t src_len, + Status &error) { + lldb_private::StreamGDBRemote stream; + stream.Printf("vFile:pwrite:%x,%" PRIx64 ",", (int)fd, offset); + stream.PutEscapedBytes(src, src_len); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + if (response.GetChar() != 'F') { + error.SetErrorStringWithFormat("write file failed"); + return 0; + } + int64_t bytes_written = response.GetS64(-1, 16); + if (bytes_written == -1) { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') { + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + return -1; + } + return bytes_written; + } else { + error.SetErrorString("failed to send vFile:pwrite packet"); + } + return 0; +} + +Status GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src, + const FileSpec &dst) { + std::string src_path{src.GetPath(false)}, dst_path{dst.GetPath(false)}; + Status error; + lldb_private::StreamGDBRemote stream; + stream.PutCString("vFile:symlink:"); + // the unix symlink() command reverses its parameters where the dst if first, + // so we follow suit here + stream.PutStringAsRawHex8(dst_path); + stream.PutChar(','); + stream.PutStringAsRawHex8(src_path); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + if (response.GetChar() == 'F') { + uint32_t result = response.GetHexMaxU32(false, UINT32_MAX); + if (result != 0) { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') { + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + } + } else { + // Should have returned with 'F<result>[,<errno>]' + error.SetErrorStringWithFormat("symlink failed"); + } + } else { + error.SetErrorString("failed to send vFile:symlink packet"); + } + return error; +} + +Status GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) { + std::string path{file_spec.GetPath(false)}; + Status error; + lldb_private::StreamGDBRemote stream; + stream.PutCString("vFile:unlink:"); + // the unix symlink() command reverses its parameters where the dst if first, + // so we follow suit here + stream.PutStringAsRawHex8(path); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + if (response.GetChar() == 'F') { + uint32_t result = response.GetHexMaxU32(false, UINT32_MAX); + if (result != 0) { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') { + int response_errno = gdb_errno_to_system(response.GetS32(-1, 16)); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + } + } else { + // Should have returned with 'F<result>[,<errno>]' + error.SetErrorStringWithFormat("unlink failed"); + } + } else { + error.SetErrorString("failed to send vFile:unlink packet"); + } + return error; +} + +// Extension of host I/O packets to get whether a file exists. +bool GDBRemoteCommunicationClient::GetFileExists( + const lldb_private::FileSpec &file_spec) { + if (m_supports_vFileExists) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:exists:"); + stream.PutStringAsRawHex8(path); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) != + PacketResult::Success) + return false; + if (!response.IsUnsupportedResponse()) { + if (response.GetChar() != 'F') + return false; + if (response.GetChar() != ',') + return false; + bool retcode = (response.GetChar() != '0'); + return retcode; + } else + m_supports_vFileExists = false; + } + + // Fallback to open. + Status error; + lldb::user_id_t fd = OpenFile(file_spec, File::eOpenOptionReadOnly, 0, error); + if (fd == UINT64_MAX) + return false; + CloseFile(fd, error); + return true; +} + +llvm::ErrorOr<llvm::MD5::MD5Result> GDBRemoteCommunicationClient::CalculateMD5( + const lldb_private::FileSpec &file_spec) { + std::string path(file_spec.GetPath(false)); + lldb_private::StreamString stream; + stream.PutCString("vFile:MD5:"); + stream.PutStringAsRawHex8(path); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response) == + PacketResult::Success) { + if (response.GetChar() != 'F') + return std::make_error_code(std::errc::illegal_byte_sequence); + if (response.GetChar() != ',') + return std::make_error_code(std::errc::illegal_byte_sequence); + if (response.Peek() && *response.Peek() == 'x') + return std::make_error_code(std::errc::no_such_file_or_directory); + + // GDBRemoteCommunicationServerCommon::Handle_vFile_MD5 concatenates low and + // high hex strings. We can't use response.GetHexMaxU64 because that can't + // handle the concatenated hex string. What would happen is parsing the low + // would consume the whole response packet which would give incorrect + // results. Instead, we get the byte string for each low and high hex + // separately, and parse them. + // + // An alternate way to handle this is to change the server to put a + // delimiter between the low/high parts, and change the client to parse the + // delimiter. However, we choose not to do this so existing lldb-servers + // don't have to be patched + + // The checksum is 128 bits encoded as hex + // This means low/high are halves of 64 bits each, in otherwords, 8 bytes. + // Each byte takes 2 hex characters in the response. + const size_t MD5_HALF_LENGTH = sizeof(uint64_t) * 2; + + // Get low part + auto part = + response.GetStringRef().substr(response.GetFilePos(), MD5_HALF_LENGTH); + if (part.size() != MD5_HALF_LENGTH) + return std::make_error_code(std::errc::illegal_byte_sequence); + response.SetFilePos(response.GetFilePos() + part.size()); + + uint64_t low; + if (part.getAsInteger(/*radix=*/16, low)) + return std::make_error_code(std::errc::illegal_byte_sequence); + + // Get high part + part = + response.GetStringRef().substr(response.GetFilePos(), MD5_HALF_LENGTH); + if (part.size() != MD5_HALF_LENGTH) + return std::make_error_code(std::errc::illegal_byte_sequence); + response.SetFilePos(response.GetFilePos() + part.size()); + + uint64_t high; + if (part.getAsInteger(/*radix=*/16, high)) + return std::make_error_code(std::errc::illegal_byte_sequence); + + llvm::MD5::MD5Result result; + llvm::support::endian::write<uint64_t, llvm::endianness::little>( + result.data(), low); + llvm::support::endian::write<uint64_t, llvm::endianness::little>( + result.data() + 8, high); + + return result; + } + return std::make_error_code(std::errc::operation_canceled); +} + +bool GDBRemoteCommunicationClient::AvoidGPackets(ProcessGDBRemote *process) { + // Some targets have issues with g/G packets and we need to avoid using them + if (m_avoid_g_packets == eLazyBoolCalculate) { + if (process) { + m_avoid_g_packets = eLazyBoolNo; + const ArchSpec &arch = process->GetTarget().GetArchitecture(); + if (arch.IsValid() && + arch.GetTriple().getVendor() == llvm::Triple::Apple && + arch.GetTriple().getOS() == llvm::Triple::IOS && + (arch.GetTriple().getArch() == llvm::Triple::aarch64 || + arch.GetTriple().getArch() == llvm::Triple::aarch64_32)) { + m_avoid_g_packets = eLazyBoolYes; + uint32_t gdb_server_version = GetGDBServerProgramVersion(); + if (gdb_server_version != 0) { + const char *gdb_server_name = GetGDBServerProgramName(); + if (gdb_server_name && strcmp(gdb_server_name, "debugserver") == 0) { + if (gdb_server_version >= 310) + m_avoid_g_packets = eLazyBoolNo; + } + } + } + } + } + return m_avoid_g_packets == eLazyBoolYes; +} + +DataBufferSP GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, + uint32_t reg) { + StreamString payload; + payload.Printf("p%x", reg); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response) != PacketResult::Success || + !response.IsNormalResponse()) + return nullptr; + + WritableDataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + return buffer_sp; +} + +DataBufferSP GDBRemoteCommunicationClient::ReadAllRegisters(lldb::tid_t tid) { + StreamString payload; + payload.PutChar('g'); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response) != PacketResult::Success || + !response.IsNormalResponse()) + return nullptr; + + WritableDataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + return buffer_sp; +} + +bool GDBRemoteCommunicationClient::WriteRegister(lldb::tid_t tid, + uint32_t reg_num, + llvm::ArrayRef<uint8_t> data) { + StreamString payload; + payload.Printf("P%x=", reg_num); + payload.PutBytesAsRawHex8(data.data(), data.size(), + endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + StringExtractorGDBRemote response; + return SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response) == PacketResult::Success && + response.IsOKResponse(); +} + +bool GDBRemoteCommunicationClient::WriteAllRegisters( + lldb::tid_t tid, llvm::ArrayRef<uint8_t> data) { + StreamString payload; + payload.PutChar('G'); + payload.PutBytesAsRawHex8(data.data(), data.size(), + endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + StringExtractorGDBRemote response; + return SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response) == PacketResult::Success && + response.IsOKResponse(); +} + +bool GDBRemoteCommunicationClient::SaveRegisterState(lldb::tid_t tid, + uint32_t &save_id) { + save_id = 0; // Set to invalid save ID + if (m_supports_QSaveRegisterState == eLazyBoolNo) + return false; + + m_supports_QSaveRegisterState = eLazyBoolYes; + StreamString payload; + payload.PutCString("QSaveRegisterState"); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response) != PacketResult::Success) + return false; + + if (response.IsUnsupportedResponse()) + m_supports_QSaveRegisterState = eLazyBoolNo; + + const uint32_t response_save_id = response.GetU32(0); + if (response_save_id == 0) + return false; + + save_id = response_save_id; + return true; +} + +bool GDBRemoteCommunicationClient::RestoreRegisterState(lldb::tid_t tid, + uint32_t save_id) { + // We use the "m_supports_QSaveRegisterState" variable here because the + // QSaveRegisterState and QRestoreRegisterState packets must both be + // supported in order to be useful + if (m_supports_QSaveRegisterState == eLazyBoolNo) + return false; + + StreamString payload; + payload.Printf("QRestoreRegisterState:%u", save_id); + StringExtractorGDBRemote response; + if (SendThreadSpecificPacketAndWaitForResponse( + tid, std::move(payload), response) != PacketResult::Success) + return false; + + if (response.IsOKResponse()) + return true; + + if (response.IsUnsupportedResponse()) + m_supports_QSaveRegisterState = eLazyBoolNo; + return false; +} + +bool GDBRemoteCommunicationClient::SyncThreadState(lldb::tid_t tid) { + if (!GetSyncThreadStateSupported()) + return false; + + StreamString packet; + StringExtractorGDBRemote response; + packet.Printf("QSyncThreadState:%4.4" PRIx64 ";", tid); + return SendPacketAndWaitForResponse(packet.GetString(), response) == + GDBRemoteCommunication::PacketResult::Success && + response.IsOKResponse(); +} + +llvm::Expected<TraceSupportedResponse> +GDBRemoteCommunicationClient::SendTraceSupported(std::chrono::seconds timeout) { + Log *log = GetLog(GDBRLog::Process); + + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jLLDBTraceSupported"); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + timeout) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsErrorResponse()) + return response.GetStatus().ToError(); + if (response.IsUnsupportedResponse()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "jLLDBTraceSupported is unsupported"); + + return llvm::json::parse<TraceSupportedResponse>(response.Peek(), + "TraceSupportedResponse"); + } + LLDB_LOG(log, "failed to send packet: jLLDBTraceSupported"); + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to send packet: jLLDBTraceSupported"); +} + +llvm::Error +GDBRemoteCommunicationClient::SendTraceStop(const TraceStopRequest &request, + std::chrono::seconds timeout) { + Log *log = GetLog(GDBRLog::Process); + + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jLLDBTraceStop:"); + + std::string json_string; + llvm::raw_string_ostream os(json_string); + os << toJSON(request); + os.flush(); + + escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + timeout) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsErrorResponse()) + return response.GetStatus().ToError(); + if (response.IsUnsupportedResponse()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "jLLDBTraceStop is unsupported"); + if (response.IsOKResponse()) + return llvm::Error::success(); + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Invalid jLLDBTraceStart response"); + } + LLDB_LOG(log, "failed to send packet: jLLDBTraceStop"); + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to send packet: jLLDBTraceStop '%s'", + escaped_packet.GetData()); +} + +llvm::Error +GDBRemoteCommunicationClient::SendTraceStart(const llvm::json::Value ¶ms, + std::chrono::seconds timeout) { + Log *log = GetLog(GDBRLog::Process); + + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jLLDBTraceStart:"); + + std::string json_string; + llvm::raw_string_ostream os(json_string); + os << params; + os.flush(); + + escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + timeout) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsErrorResponse()) + return response.GetStatus().ToError(); + if (response.IsUnsupportedResponse()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "jLLDBTraceStart is unsupported"); + if (response.IsOKResponse()) + return llvm::Error::success(); + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Invalid jLLDBTraceStart response"); + } + LLDB_LOG(log, "failed to send packet: jLLDBTraceStart"); + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to send packet: jLLDBTraceStart '%s'", + escaped_packet.GetData()); +} + +llvm::Expected<std::string> +GDBRemoteCommunicationClient::SendTraceGetState(llvm::StringRef type, + std::chrono::seconds timeout) { + Log *log = GetLog(GDBRLog::Process); + + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jLLDBTraceGetState:"); + + std::string json_string; + llvm::raw_string_ostream os(json_string); + os << toJSON(TraceGetStateRequest{type.str()}); + os.flush(); + + escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + timeout) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsErrorResponse()) + return response.GetStatus().ToError(); + if (response.IsUnsupportedResponse()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "jLLDBTraceGetState is unsupported"); + return std::string(response.Peek()); + } + + LLDB_LOG(log, "failed to send packet: jLLDBTraceGetState"); + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "failed to send packet: jLLDBTraceGetState '%s'", + escaped_packet.GetData()); +} + +llvm::Expected<std::vector<uint8_t>> +GDBRemoteCommunicationClient::SendTraceGetBinaryData( + const TraceGetBinaryDataRequest &request, std::chrono::seconds timeout) { + Log *log = GetLog(GDBRLog::Process); + + StreamGDBRemote escaped_packet; + escaped_packet.PutCString("jLLDBTraceGetBinaryData:"); + + std::string json_string; + llvm::raw_string_ostream os(json_string); + os << toJSON(request); + os.flush(); + + escaped_packet.PutEscapedBytes(json_string.c_str(), json_string.size()); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(escaped_packet.GetString(), response, + timeout) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsErrorResponse()) + return response.GetStatus().ToError(); + std::string data; + response.GetEscapedBinaryData(data); + return std::vector<uint8_t>(data.begin(), data.end()); + } + LLDB_LOG(log, "failed to send packet: jLLDBTraceGetBinaryData"); + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "failed to send packet: jLLDBTraceGetBinaryData '%s'", + escaped_packet.GetData()); +} + +std::optional<QOffsets> GDBRemoteCommunicationClient::GetQOffsets() { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qOffsets", response) != + PacketResult::Success) + return std::nullopt; + if (!response.IsNormalResponse()) + return std::nullopt; + + QOffsets result; + llvm::StringRef ref = response.GetStringRef(); + const auto &GetOffset = [&] { + addr_t offset; + if (ref.consumeInteger(16, offset)) + return false; + result.offsets.push_back(offset); + return true; + }; + + if (ref.consume_front("Text=")) { + result.segments = false; + if (!GetOffset()) + return std::nullopt; + if (!ref.consume_front(";Data=") || !GetOffset()) + return std::nullopt; + if (ref.empty()) + return result; + if (ref.consume_front(";Bss=") && GetOffset() && ref.empty()) + return result; + } else if (ref.consume_front("TextSeg=")) { + result.segments = true; + if (!GetOffset()) + return std::nullopt; + if (ref.empty()) + return result; + if (ref.consume_front(";DataSeg=") && GetOffset() && ref.empty()) + return result; + } + return std::nullopt; +} + +bool GDBRemoteCommunicationClient::GetModuleInfo( + const FileSpec &module_file_spec, const lldb_private::ArchSpec &arch_spec, + ModuleSpec &module_spec) { + if (!m_supports_qModuleInfo) + return false; + + std::string module_path = module_file_spec.GetPath(false); + if (module_path.empty()) + return false; + + StreamString packet; + packet.PutCString("qModuleInfo:"); + packet.PutStringAsRawHex8(module_path); + packet.PutCString(";"); + const auto &triple = arch_spec.GetTriple().getTriple(); + packet.PutStringAsRawHex8(triple); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet.GetString(), response) != + PacketResult::Success) + return false; + + if (response.IsErrorResponse()) + return false; + + if (response.IsUnsupportedResponse()) { + m_supports_qModuleInfo = false; + return false; + } + + llvm::StringRef name; + llvm::StringRef value; + + module_spec.Clear(); + module_spec.GetFileSpec() = module_file_spec; + + while (response.GetNameColonValue(name, value)) { + if (name == "uuid" || name == "md5") { + StringExtractor extractor(value); + std::string uuid; + extractor.GetHexByteString(uuid); + module_spec.GetUUID().SetFromStringRef(uuid); + } else if (name == "triple") { + StringExtractor extractor(value); + std::string triple; + extractor.GetHexByteString(triple); + module_spec.GetArchitecture().SetTriple(triple.c_str()); + } else if (name == "file_offset") { + uint64_t ival = 0; + if (!value.getAsInteger(16, ival)) + module_spec.SetObjectOffset(ival); + } else if (name == "file_size") { + uint64_t ival = 0; + if (!value.getAsInteger(16, ival)) + module_spec.SetObjectSize(ival); + } else if (name == "file_path") { + StringExtractor extractor(value); + std::string path; + extractor.GetHexByteString(path); + module_spec.GetFileSpec() = FileSpec(path, arch_spec.GetTriple()); + } + } + + return true; +} + +static std::optional<ModuleSpec> +ParseModuleSpec(StructuredData::Dictionary *dict) { + ModuleSpec result; + if (!dict) + return std::nullopt; + + llvm::StringRef string; + uint64_t integer; + + if (!dict->GetValueForKeyAsString("uuid", string)) + return std::nullopt; + if (!result.GetUUID().SetFromStringRef(string)) + return std::nullopt; + + if (!dict->GetValueForKeyAsInteger("file_offset", integer)) + return std::nullopt; + result.SetObjectOffset(integer); + + if (!dict->GetValueForKeyAsInteger("file_size", integer)) + return std::nullopt; + result.SetObjectSize(integer); + + if (!dict->GetValueForKeyAsString("triple", string)) + return std::nullopt; + result.GetArchitecture().SetTriple(string); + + if (!dict->GetValueForKeyAsString("file_path", string)) + return std::nullopt; + result.GetFileSpec() = FileSpec(string, result.GetArchitecture().GetTriple()); + + return result; +} + +std::optional<std::vector<ModuleSpec>> +GDBRemoteCommunicationClient::GetModulesInfo( + llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) { + namespace json = llvm::json; + + if (!m_supports_jModulesInfo) + return std::nullopt; + + json::Array module_array; + for (const FileSpec &module_file_spec : module_file_specs) { + module_array.push_back( + json::Object{{"file", module_file_spec.GetPath(false)}, + {"triple", triple.getTriple()}}); + } + StreamString unescaped_payload; + unescaped_payload.PutCString("jModulesInfo:"); + unescaped_payload.AsRawOstream() << std::move(module_array); + + StreamGDBRemote payload; + payload.PutEscapedBytes(unescaped_payload.GetString().data(), + unescaped_payload.GetSize()); + + // Increase the timeout for jModulesInfo since this packet can take longer. + ScopedTimeout timeout(*this, std::chrono::seconds(10)); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(payload.GetString(), response) != + PacketResult::Success || + response.IsErrorResponse()) + return std::nullopt; + + if (response.IsUnsupportedResponse()) { + m_supports_jModulesInfo = false; + return std::nullopt; + } + + StructuredData::ObjectSP response_object_sp = + StructuredData::ParseJSON(response.GetStringRef()); + if (!response_object_sp) + return std::nullopt; + + StructuredData::Array *response_array = response_object_sp->GetAsArray(); + if (!response_array) + return std::nullopt; + + std::vector<ModuleSpec> result; + for (size_t i = 0; i < response_array->GetSize(); ++i) { + if (std::optional<ModuleSpec> module_spec = ParseModuleSpec( + response_array->GetItemAtIndex(i)->GetAsDictionary())) + result.push_back(*module_spec); + } + + return result; +} + +// query the target remote for extended information using the qXfer packet +// +// example: object='features', annex='target.xml' +// return: <xml output> or error +llvm::Expected<std::string> +GDBRemoteCommunicationClient::ReadExtFeature(llvm::StringRef object, + llvm::StringRef annex) { + + std::string output; + llvm::raw_string_ostream output_stream(output); + StringExtractorGDBRemote chunk; + + uint64_t size = GetRemoteMaxPacketSize(); + if (size == 0) + size = 0x1000; + size = size - 1; // Leave space for the 'm' or 'l' character in the response + int offset = 0; + bool active = true; + + // loop until all data has been read + while (active) { + + // send query extended feature packet + std::string packet = + ("qXfer:" + object + ":read:" + annex + ":" + + llvm::Twine::utohexstr(offset) + "," + llvm::Twine::utohexstr(size)) + .str(); + + GDBRemoteCommunication::PacketResult res = + SendPacketAndWaitForResponse(packet, chunk); + + if (res != GDBRemoteCommunication::PacketResult::Success || + chunk.GetStringRef().empty()) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Error sending $qXfer packet"); + } + + // check packet code + switch (chunk.GetStringRef()[0]) { + // last chunk + case ('l'): + active = false; + [[fallthrough]]; + + // more chunks + case ('m'): + output_stream << chunk.GetStringRef().drop_front(); + offset += chunk.GetStringRef().size() - 1; + break; + + // unknown chunk + default: + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Invalid continuation code from $qXfer packet"); + } + } + + return output_stream.str(); +} + +// Notify the target that gdb is prepared to serve symbol lookup requests. +// packet: "qSymbol::" +// reply: +// OK The target does not need to look up any (more) symbols. +// qSymbol:<sym_name> The target requests the value of symbol sym_name (hex +// encoded). +// LLDB may provide the value by sending another qSymbol +// packet +// in the form of"qSymbol:<sym_value>:<sym_name>". +// +// Three examples: +// +// lldb sends: qSymbol:: +// lldb receives: OK +// Remote gdb stub does not need to know the addresses of any symbols, lldb +// does not +// need to ask again in this session. +// +// lldb sends: qSymbol:: +// lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 +// lldb sends: qSymbol::64697370617463685f71756575655f6f666673657473 +// lldb receives: OK +// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb does +// not know +// the address at this time. lldb needs to send qSymbol:: again when it has +// more +// solibs loaded. +// +// lldb sends: qSymbol:: +// lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 +// lldb sends: qSymbol:2bc97554:64697370617463685f71756575655f6f666673657473 +// lldb receives: OK +// Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb says +// that it +// is at address 0x2bc97554. Remote gdb stub sends 'OK' indicating that it +// does not +// need any more symbols. lldb does not need to ask again in this session. + +void GDBRemoteCommunicationClient::ServeSymbolLookups( + lldb_private::Process *process) { + // Set to true once we've resolved a symbol to an address for the remote + // stub. If we get an 'OK' response after this, the remote stub doesn't need + // any more symbols and we can stop asking. + bool symbol_response_provided = false; + + // Is this the initial qSymbol:: packet? + bool first_qsymbol_query = true; + + if (m_supports_qSymbol && !m_qSymbol_requests_done) { + Lock lock(*this); + if (lock) { + StreamString packet; + packet.PutCString("qSymbol::"); + StringExtractorGDBRemote response; + while (SendPacketAndWaitForResponseNoLock(packet.GetString(), response) == + PacketResult::Success) { + if (response.IsOKResponse()) { + if (symbol_response_provided || first_qsymbol_query) { + m_qSymbol_requests_done = true; + } + + // We are done serving symbols requests + return; + } + first_qsymbol_query = false; + + if (response.IsUnsupportedResponse()) { + // qSymbol is not supported by the current GDB server we are + // connected to + m_supports_qSymbol = false; + return; + } else { + llvm::StringRef response_str(response.GetStringRef()); + if (response_str.starts_with("qSymbol:")) { + response.SetFilePos(strlen("qSymbol:")); + std::string symbol_name; + if (response.GetHexByteString(symbol_name)) { + if (symbol_name.empty()) + return; + + addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; + lldb_private::SymbolContextList sc_list; + process->GetTarget().GetImages().FindSymbolsWithNameAndType( + ConstString(symbol_name), eSymbolTypeAny, sc_list); + for (const SymbolContext &sc : sc_list) { + if (symbol_load_addr != LLDB_INVALID_ADDRESS) + break; + if (sc.symbol) { + switch (sc.symbol->GetType()) { + case eSymbolTypeInvalid: + case eSymbolTypeAbsolute: + case eSymbolTypeUndefined: + case eSymbolTypeSourceFile: + case eSymbolTypeHeaderFile: + case eSymbolTypeObjectFile: + case eSymbolTypeCommonBlock: + case eSymbolTypeBlock: + case eSymbolTypeLocal: + case eSymbolTypeParam: + case eSymbolTypeVariable: + case eSymbolTypeVariableType: + case eSymbolTypeLineEntry: + case eSymbolTypeLineHeader: + case eSymbolTypeScopeBegin: + case eSymbolTypeScopeEnd: + case eSymbolTypeAdditional: + case eSymbolTypeCompiler: + case eSymbolTypeInstrumentation: + case eSymbolTypeTrampoline: + break; + + case eSymbolTypeCode: + case eSymbolTypeResolver: + case eSymbolTypeData: + case eSymbolTypeRuntime: + case eSymbolTypeException: + case eSymbolTypeObjCClass: + case eSymbolTypeObjCMetaClass: + case eSymbolTypeObjCIVar: + case eSymbolTypeReExported: + symbol_load_addr = + sc.symbol->GetLoadAddress(&process->GetTarget()); + break; + } + } + } + // This is the normal path where our symbol lookup was successful + // and we want to send a packet with the new symbol value and see + // if another lookup needs to be done. + + // Change "packet" to contain the requested symbol value and name + packet.Clear(); + packet.PutCString("qSymbol:"); + if (symbol_load_addr != LLDB_INVALID_ADDRESS) { + packet.Printf("%" PRIx64, symbol_load_addr); + symbol_response_provided = true; + } else { + symbol_response_provided = false; + } + packet.PutCString(":"); + packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size()); + continue; // go back to the while loop and send "packet" and wait + // for another response + } + } + } + } + // If we make it here, the symbol request packet response wasn't valid or + // our symbol lookup failed so we must abort + return; + + } else if (Log *log = GetLog(GDBRLog::Process | GDBRLog::Packets)) { + LLDB_LOGF(log, + "GDBRemoteCommunicationClient::%s: Didn't get sequence mutex.", + __FUNCTION__); + } + } +} + +StructuredData::Array * +GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins() { + if (!m_supported_async_json_packets_is_valid) { + // Query the server for the array of supported asynchronous JSON packets. + m_supported_async_json_packets_is_valid = true; + + Log *log = GetLog(GDBRLog::Process); + + // Poll it now. + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qStructuredDataPlugins", response) == + PacketResult::Success) { + m_supported_async_json_packets_sp = + StructuredData::ParseJSON(response.GetStringRef()); + if (m_supported_async_json_packets_sp && + !m_supported_async_json_packets_sp->GetAsArray()) { + // We were returned something other than a JSON array. This is + // invalid. Clear it out. + LLDB_LOGF(log, + "GDBRemoteCommunicationClient::%s(): " + "QSupportedAsyncJSONPackets returned invalid " + "result: %s", + __FUNCTION__, response.GetStringRef().data()); + m_supported_async_json_packets_sp.reset(); + } + } else { + LLDB_LOGF(log, + "GDBRemoteCommunicationClient::%s(): " + "QSupportedAsyncJSONPackets unsupported", + __FUNCTION__); + } + + if (log && m_supported_async_json_packets_sp) { + StreamString stream; + m_supported_async_json_packets_sp->Dump(stream); + LLDB_LOGF(log, + "GDBRemoteCommunicationClient::%s(): supported async " + "JSON packets: %s", + __FUNCTION__, stream.GetData()); + } + } + + return m_supported_async_json_packets_sp + ? m_supported_async_json_packets_sp->GetAsArray() + : nullptr; +} + +Status GDBRemoteCommunicationClient::SendSignalsToIgnore( + llvm::ArrayRef<int32_t> signals) { + // Format packet: + // QPassSignals:<hex_sig1>;<hex_sig2>...;<hex_sigN> + auto range = llvm::make_range(signals.begin(), signals.end()); + std::string packet = formatv("QPassSignals:{0:$[;]@(x-2)}", range).str(); + + StringExtractorGDBRemote response; + auto send_status = SendPacketAndWaitForResponse(packet, response); + + if (send_status != GDBRemoteCommunication::PacketResult::Success) + return Status("Sending QPassSignals packet failed"); + + if (response.IsOKResponse()) { + return Status(); + } else { + return Status("Unknown error happened during sending QPassSignals packet."); + } +} + +Status GDBRemoteCommunicationClient::ConfigureRemoteStructuredData( + llvm::StringRef type_name, const StructuredData::ObjectSP &config_sp) { + Status error; + + if (type_name.empty()) { + error.SetErrorString("invalid type_name argument"); + return error; + } + + // Build command: Configure{type_name}: serialized config data. + StreamGDBRemote stream; + stream.PutCString("QConfigure"); + stream.PutCString(type_name); + stream.PutChar(':'); + if (config_sp) { + // Gather the plain-text version of the configuration data. + StreamString unescaped_stream; + config_sp->Dump(unescaped_stream); + unescaped_stream.Flush(); + + // Add it to the stream in escaped fashion. + stream.PutEscapedBytes(unescaped_stream.GetString().data(), + unescaped_stream.GetSize()); + } + stream.Flush(); + + // Send the packet. + StringExtractorGDBRemote response; + auto result = SendPacketAndWaitForResponse(stream.GetString(), response); + if (result == PacketResult::Success) { + // We failed if the config result comes back other than OK. + if (response.GetStringRef() == "OK") { + // Okay! + error.Clear(); + } else { + error.SetErrorStringWithFormatv( + "configuring StructuredData feature {0} failed with error {1}", + type_name, response.GetStringRef()); + } + } else { + // Can we get more data here on the failure? + error.SetErrorStringWithFormatv( + "configuring StructuredData feature {0} failed when sending packet: " + "PacketResult={1}", + type_name, (int)result); + } + return error; +} + +void GDBRemoteCommunicationClient::OnRunPacketSent(bool first) { + GDBRemoteClientBase::OnRunPacketSent(first); + m_curr_tid = LLDB_INVALID_THREAD_ID; +} + +bool GDBRemoteCommunicationClient::UsesNativeSignals() { + if (m_uses_native_signals == eLazyBoolCalculate) + GetRemoteQSupported(); + if (m_uses_native_signals == eLazyBoolYes) + return true; + + // If the remote didn't indicate native-signal support explicitly, + // check whether it is an old version of lldb-server. + return GetThreadSuffixSupported(); +} + +llvm::Expected<int> GDBRemoteCommunicationClient::KillProcess(lldb::pid_t pid) { + StringExtractorGDBRemote response; + GDBRemoteCommunication::ScopedTimeout(*this, seconds(3)); + + if (SendPacketAndWaitForResponse("k", response, GetPacketTimeout()) != + PacketResult::Success) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to send k packet"); + + char packet_cmd = response.GetChar(0); + if (packet_cmd == 'W' || packet_cmd == 'X') + return response.GetHexU8(); + + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "unexpected response to k packet: %s", + response.GetStringRef().str().c_str()); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h new file mode 100644 index 000000000000..898d176abc34 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -0,0 +1,658 @@ +//===-- GDBRemoteCommunicationClient.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONCLIENT_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONCLIENT_H + +#include "GDBRemoteClientBase.h" + +#include <chrono> +#include <map> +#include <mutex> +#include <optional> +#include <string> +#include <vector> + +#include "lldb/Host/File.h" +#include "lldb/Utility/AddressableBits.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/GDBRemote.h" +#include "lldb/Utility/ProcessInfo.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/TraceGDBRemotePackets.h" +#include "lldb/Utility/UUID.h" +#if defined(_WIN32) +#include "lldb/Host/windows/PosixApi.h" +#endif + +#include "llvm/Support/VersionTuple.h" + +namespace lldb_private { +namespace process_gdb_remote { + +/// The offsets used by the target when relocating the executable. Decoded from +/// qOffsets packet response. +struct QOffsets { + /// If true, the offsets field describes segments. Otherwise, it describes + /// sections. + bool segments; + + /// The individual offsets. Section offsets have two or three members. + /// Segment offsets have either one of two. + std::vector<uint64_t> offsets; +}; +inline bool operator==(const QOffsets &a, const QOffsets &b) { + return a.segments == b.segments && a.offsets == b.offsets; +} +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const QOffsets &offsets); + +// A trivial struct used to return a pair of PID and TID. +struct PidTid { + uint64_t pid; + uint64_t tid; +}; + +class GDBRemoteCommunicationClient : public GDBRemoteClientBase { +public: + GDBRemoteCommunicationClient(); + + ~GDBRemoteCommunicationClient() override; + + // After connecting, send the handshake to the server to make sure + // we are communicating with it. + bool HandshakeWithServer(Status *error_ptr); + + bool GetThreadSuffixSupported(); + + // This packet is usually sent first and the boolean return value + // indicates if the packet was send and any response was received + // even in the response is UNIMPLEMENTED. If the packet failed to + // get a response, then false is returned. This quickly tells us + // if we were able to connect and communicate with the remote GDB + // server + bool QueryNoAckModeSupported(); + + void GetListThreadsInStopReplySupported(); + + lldb::pid_t GetCurrentProcessID(bool allow_lazy = true); + + bool LaunchGDBServer(const char *remote_accept_hostname, lldb::pid_t &pid, + uint16_t &port, std::string &socket_name); + + size_t QueryGDBServer( + std::vector<std::pair<uint16_t, std::string>> &connection_urls); + + bool KillSpawnedProcess(lldb::pid_t pid); + + /// Launch the process using the provided arguments. + /// + /// \param[in] args + /// A list of program arguments. The first entry is the program being run. + llvm::Error LaunchProcess(const Args &args); + + /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the + /// environment that will get used when launching an application + /// in conjunction with the 'A' packet. This function can be called + /// multiple times in a row in order to pass on the desired + /// environment that the inferior should be launched with. + /// + /// \param[in] name_equal_value + /// A NULL terminated C string that contains a single environment + /// in the format "NAME=VALUE". + /// + /// \return + /// Zero if the response was "OK", a positive value if the + /// the response was "Exx" where xx are two hex digits, or + /// -1 if the call is unsupported or any other unexpected + /// response was received. + int SendEnvironmentPacket(char const *name_equal_value); + int SendEnvironment(const Environment &env); + + int SendLaunchArchPacket(const char *arch); + + int SendLaunchEventDataPacket(const char *data, + bool *was_supported = nullptr); + + /// Sends a GDB remote protocol 'I' packet that delivers stdin + /// data to the remote process. + /// + /// \param[in] data + /// A pointer to stdin data. + /// + /// \param[in] data_len + /// The number of bytes available at \a data. + /// + /// \return + /// Zero if the attach was successful, or an error indicating + /// an error code. + int SendStdinNotification(const char *data, size_t data_len); + + /// Sets the path to use for stdin/out/err for a process + /// that will be launched with the 'A' packet. + /// + /// \param[in] file_spec + /// The path to use for stdin/out/err + /// + /// \return + /// Zero if the for success, or an error code for failure. + int SetSTDIN(const FileSpec &file_spec); + int SetSTDOUT(const FileSpec &file_spec); + int SetSTDERR(const FileSpec &file_spec); + + /// Sets the disable ASLR flag to \a enable for a process that will + /// be launched with the 'A' packet. + /// + /// \param[in] enable + /// A boolean value indicating whether to disable ASLR or not. + /// + /// \return + /// Zero if the for success, or an error code for failure. + int SetDisableASLR(bool enable); + + /// Sets the DetachOnError flag to \a enable for the process controlled by the + /// stub. + /// + /// \param[in] enable + /// A boolean value indicating whether to detach on error or not. + /// + /// \return + /// Zero if the for success, or an error code for failure. + int SetDetachOnError(bool enable); + + /// Sets the working directory to \a path for a process that will + /// be launched with the 'A' packet for non platform based + /// connections. If this packet is sent to a GDB server that + /// implements the platform, it will change the current working + /// directory for the platform process. + /// + /// \param[in] working_dir + /// The path to a directory to use when launching our process + /// + /// \return + /// Zero if the for success, or an error code for failure. + int SetWorkingDir(const FileSpec &working_dir); + + /// Gets the current working directory of a remote platform GDB + /// server. + /// + /// \param[out] working_dir + /// The current working directory on the remote platform. + /// + /// \return + /// Boolean for success + bool GetWorkingDir(FileSpec &working_dir); + + lldb::addr_t AllocateMemory(size_t size, uint32_t permissions); + + bool DeallocateMemory(lldb::addr_t addr); + + Status Detach(bool keep_stopped, lldb::pid_t pid = LLDB_INVALID_PROCESS_ID); + + Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info); + + std::optional<uint32_t> GetWatchpointSlotCount(); + + std::optional<bool> GetWatchpointReportedAfter(); + + WatchpointHardwareFeature GetSupportedWatchpointTypes(); + + const ArchSpec &GetHostArchitecture(); + + std::chrono::seconds GetHostDefaultPacketTimeout(); + + const ArchSpec &GetProcessArchitecture(); + + bool GetProcessStandaloneBinary(UUID &uuid, lldb::addr_t &value, + bool &value_is_offset); + + std::vector<lldb::addr_t> GetProcessStandaloneBinaries(); + + void GetRemoteQSupported(); + + bool GetVContSupported(char flavor); + + bool GetpPacketSupported(lldb::tid_t tid); + + bool GetxPacketSupported(); + + bool GetVAttachOrWaitSupported(); + + bool GetSyncThreadStateSupported(); + + void ResetDiscoverableSettings(bool did_exec); + + bool GetHostInfo(bool force = false); + + bool GetDefaultThreadId(lldb::tid_t &tid); + + llvm::VersionTuple GetOSVersion(); + + llvm::VersionTuple GetMacCatalystVersion(); + + std::optional<std::string> GetOSBuildString(); + + std::optional<std::string> GetOSKernelDescription(); + + ArchSpec GetSystemArchitecture(); + + lldb_private::AddressableBits GetAddressableBits(); + + bool GetHostname(std::string &s); + + lldb::addr_t GetShlibInfoAddr(); + + bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info); + + uint32_t FindProcesses(const ProcessInstanceInfoMatch &process_match_info, + ProcessInstanceInfoList &process_infos); + + bool GetUserName(uint32_t uid, std::string &name); + + bool GetGroupName(uint32_t gid, std::string &name); + + bool HasFullVContSupport() { return GetVContSupported('A'); } + + bool HasAnyVContSupport() { return GetVContSupported('a'); } + + bool GetStopReply(StringExtractorGDBRemote &response); + + bool GetThreadStopInfo(lldb::tid_t tid, StringExtractorGDBRemote &response); + + bool SupportsGDBStoppointPacket(GDBStoppointType type) { + switch (type) { + case eBreakpointSoftware: + return m_supports_z0; + case eBreakpointHardware: + return m_supports_z1; + case eWatchpointWrite: + return m_supports_z2; + case eWatchpointRead: + return m_supports_z3; + case eWatchpointReadWrite: + return m_supports_z4; + default: + return false; + } + } + + uint8_t SendGDBStoppointTypePacket( + GDBStoppointType type, // Type of breakpoint or watchpoint + bool insert, // Insert or remove? + lldb::addr_t addr, // Address of breakpoint or watchpoint + uint32_t length, // Byte Size of breakpoint or watchpoint + std::chrono::seconds interrupt_timeout); // Time to wait for an interrupt + + void TestPacketSpeed(const uint32_t num_packets, uint32_t max_send, + uint32_t max_recv, uint64_t recv_amount, bool json, + Stream &strm); + + // This packet is for testing the speed of the interface only. Both + // the client and server need to support it, but this allows us to + // measure the packet speed without any other work being done on the + // other end and avoids any of that work affecting the packet send + // and response times. + bool SendSpeedTestPacket(uint32_t send_size, uint32_t recv_size); + + std::optional<PidTid> SendSetCurrentThreadPacket(uint64_t tid, uint64_t pid, + char op); + + bool SetCurrentThread(uint64_t tid, + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID); + + bool SetCurrentThreadForRun(uint64_t tid, + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID); + + bool GetQXferAuxvReadSupported(); + + void EnableErrorStringInPacket(); + + bool GetQXferLibrariesReadSupported(); + + bool GetQXferLibrariesSVR4ReadSupported(); + + uint64_t GetRemoteMaxPacketSize(); + + bool GetEchoSupported(); + + bool GetQPassSignalsSupported(); + + bool GetAugmentedLibrariesSVR4ReadSupported(); + + bool GetQXferFeaturesReadSupported(); + + bool GetQXferMemoryMapReadSupported(); + + bool GetQXferSigInfoReadSupported(); + + bool GetMultiprocessSupported(); + + LazyBool SupportsAllocDeallocMemory() // const + { + // Uncomment this to have lldb pretend the debug server doesn't respond to + // alloc/dealloc memory packets. + // m_supports_alloc_dealloc_memory = lldb_private::eLazyBoolNo; + return m_supports_alloc_dealloc_memory; + } + + std::vector<std::pair<lldb::pid_t, lldb::tid_t>> + GetCurrentProcessAndThreadIDs(bool &sequence_mutex_unavailable); + + size_t GetCurrentThreadIDs(std::vector<lldb::tid_t> &thread_ids, + bool &sequence_mutex_unavailable); + + lldb::user_id_t OpenFile(const FileSpec &file_spec, File::OpenOptions flags, + mode_t mode, Status &error); + + bool CloseFile(lldb::user_id_t fd, Status &error); + + std::optional<GDBRemoteFStatData> FStat(lldb::user_id_t fd); + + // NB: this is just a convenience wrapper over open() + fstat(). It does not + // work if the file cannot be opened. + std::optional<GDBRemoteFStatData> Stat(const FileSpec &file_spec); + + lldb::user_id_t GetFileSize(const FileSpec &file_spec); + + void AutoCompleteDiskFileOrDirectory(CompletionRequest &request, + bool only_dir); + + Status GetFilePermissions(const FileSpec &file_spec, + uint32_t &file_permissions); + + Status SetFilePermissions(const FileSpec &file_spec, + uint32_t file_permissions); + + uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, + uint64_t dst_len, Status &error); + + uint64_t WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src, + uint64_t src_len, Status &error); + + Status CreateSymlink(const FileSpec &src, const FileSpec &dst); + + Status Unlink(const FileSpec &file_spec); + + Status MakeDirectory(const FileSpec &file_spec, uint32_t mode); + + bool GetFileExists(const FileSpec &file_spec); + + Status RunShellCommand( + llvm::StringRef command, + const FileSpec &working_dir, // Pass empty FileSpec to use the current + // working directory + int *status_ptr, // Pass nullptr if you don't want the process exit status + int *signo_ptr, // Pass nullptr if you don't want the signal that caused + // the process to exit + std::string + *command_output, // Pass nullptr if you don't want the command output + const Timeout<std::micro> &timeout); + + llvm::ErrorOr<llvm::MD5::MD5Result> CalculateMD5(const FileSpec &file_spec); + + lldb::DataBufferSP ReadRegister( + lldb::tid_t tid, + uint32_t + reg_num); // Must be the eRegisterKindProcessPlugin register number + + lldb::DataBufferSP ReadAllRegisters(lldb::tid_t tid); + + bool + WriteRegister(lldb::tid_t tid, + uint32_t reg_num, // eRegisterKindProcessPlugin register number + llvm::ArrayRef<uint8_t> data); + + bool WriteAllRegisters(lldb::tid_t tid, llvm::ArrayRef<uint8_t> data); + + bool SaveRegisterState(lldb::tid_t tid, uint32_t &save_id); + + bool RestoreRegisterState(lldb::tid_t tid, uint32_t save_id); + + bool SyncThreadState(lldb::tid_t tid); + + const char *GetGDBServerProgramName(); + + uint32_t GetGDBServerProgramVersion(); + + bool AvoidGPackets(ProcessGDBRemote *process); + + StructuredData::ObjectSP GetThreadsInfo(); + + bool GetThreadExtendedInfoSupported(); + + bool GetLoadedDynamicLibrariesInfosSupported(); + + bool GetSharedCacheInfoSupported(); + + bool GetDynamicLoaderProcessStateSupported(); + + bool GetMemoryTaggingSupported(); + + bool UsesNativeSignals(); + + lldb::DataBufferSP ReadMemoryTags(lldb::addr_t addr, size_t len, + int32_t type); + + Status WriteMemoryTags(lldb::addr_t addr, size_t len, int32_t type, + const std::vector<uint8_t> &tags); + + /// Use qOffsets to query the offset used when relocating the target + /// executable. If successful, the returned structure will contain at least + /// one value in the offsets field. + std::optional<QOffsets> GetQOffsets(); + + bool GetModuleInfo(const FileSpec &module_file_spec, + const ArchSpec &arch_spec, ModuleSpec &module_spec); + + std::optional<std::vector<ModuleSpec>> + GetModulesInfo(llvm::ArrayRef<FileSpec> module_file_specs, + const llvm::Triple &triple); + + llvm::Expected<std::string> ReadExtFeature(llvm::StringRef object, + llvm::StringRef annex); + + void ServeSymbolLookups(lldb_private::Process *process); + + // Sends QPassSignals packet to the server with given signals to ignore. + Status SendSignalsToIgnore(llvm::ArrayRef<int32_t> signals); + + /// Return the feature set supported by the gdb-remote server. + /// + /// This method returns the remote side's response to the qSupported + /// packet. The response is the complete string payload returned + /// to the client. + /// + /// \return + /// The string returned by the server to the qSupported query. + const std::string &GetServerSupportedFeatures() const { + return m_qSupported_response; + } + + /// Return the array of async JSON packet types supported by the remote. + /// + /// This method returns the remote side's array of supported JSON + /// packet types as a list of type names. Each of the results are + /// expected to have an Enable{type_name} command to enable and configure + /// the related feature. Each type_name for an enabled feature will + /// possibly send async-style packets that contain a payload of a + /// binhex-encoded JSON dictionary. The dictionary will have a + /// string field named 'type', that contains the type_name of the + /// supported packet type. + /// + /// There is a Plugin category called structured-data plugins. + /// A plugin indicates whether it knows how to handle a type_name. + /// If so, it can be used to process the async JSON packet. + /// + /// \return + /// The string returned by the server to the qSupported query. + lldb_private::StructuredData::Array *GetSupportedStructuredDataPlugins(); + + /// Configure a StructuredData feature on the remote end. + /// + /// \see \b Process::ConfigureStructuredData(...) for details. + Status + ConfigureRemoteStructuredData(llvm::StringRef type_name, + const StructuredData::ObjectSP &config_sp); + + llvm::Expected<TraceSupportedResponse> + SendTraceSupported(std::chrono::seconds interrupt_timeout); + + llvm::Error SendTraceStart(const llvm::json::Value &request, + std::chrono::seconds interrupt_timeout); + + llvm::Error SendTraceStop(const TraceStopRequest &request, + std::chrono::seconds interrupt_timeout); + + llvm::Expected<std::string> + SendTraceGetState(llvm::StringRef type, + std::chrono::seconds interrupt_timeout); + + llvm::Expected<std::vector<uint8_t>> + SendTraceGetBinaryData(const TraceGetBinaryDataRequest &request, + std::chrono::seconds interrupt_timeout); + + bool GetSaveCoreSupported() const; + + llvm::Expected<int> KillProcess(lldb::pid_t pid); + +protected: + LazyBool m_supports_not_sending_acks = eLazyBoolCalculate; + LazyBool m_supports_thread_suffix = eLazyBoolCalculate; + LazyBool m_supports_threads_in_stop_reply = eLazyBoolCalculate; + LazyBool m_supports_vCont_all = eLazyBoolCalculate; + LazyBool m_supports_vCont_any = eLazyBoolCalculate; + LazyBool m_supports_vCont_c = eLazyBoolCalculate; + LazyBool m_supports_vCont_C = eLazyBoolCalculate; + LazyBool m_supports_vCont_s = eLazyBoolCalculate; + LazyBool m_supports_vCont_S = eLazyBoolCalculate; + LazyBool m_qHostInfo_is_valid = eLazyBoolCalculate; + LazyBool m_curr_pid_is_valid = eLazyBoolCalculate; + LazyBool m_qProcessInfo_is_valid = eLazyBoolCalculate; + LazyBool m_qGDBServerVersion_is_valid = eLazyBoolCalculate; + LazyBool m_supports_alloc_dealloc_memory = eLazyBoolCalculate; + LazyBool m_supports_memory_region_info = eLazyBoolCalculate; + LazyBool m_supports_watchpoint_support_info = eLazyBoolCalculate; + LazyBool m_supports_detach_stay_stopped = eLazyBoolCalculate; + LazyBool m_watchpoints_trigger_after_instruction = eLazyBoolCalculate; + LazyBool m_attach_or_wait_reply = eLazyBoolCalculate; + LazyBool m_prepare_for_reg_writing_reply = eLazyBoolCalculate; + LazyBool m_supports_p = eLazyBoolCalculate; + LazyBool m_supports_x = eLazyBoolCalculate; + LazyBool m_avoid_g_packets = eLazyBoolCalculate; + LazyBool m_supports_QSaveRegisterState = eLazyBoolCalculate; + LazyBool m_supports_qXfer_auxv_read = eLazyBoolCalculate; + LazyBool m_supports_qXfer_libraries_read = eLazyBoolCalculate; + LazyBool m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; + LazyBool m_supports_qXfer_features_read = eLazyBoolCalculate; + LazyBool m_supports_qXfer_memory_map_read = eLazyBoolCalculate; + LazyBool m_supports_qXfer_siginfo_read = eLazyBoolCalculate; + LazyBool m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; + LazyBool m_supports_jThreadExtendedInfo = eLazyBoolCalculate; + LazyBool m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolCalculate; + LazyBool m_supports_jGetSharedCacheInfo = eLazyBoolCalculate; + LazyBool m_supports_jGetDyldProcessState = eLazyBoolCalculate; + LazyBool m_supports_QPassSignals = eLazyBoolCalculate; + LazyBool m_supports_error_string_reply = eLazyBoolCalculate; + LazyBool m_supports_multiprocess = eLazyBoolCalculate; + LazyBool m_supports_memory_tagging = eLazyBoolCalculate; + LazyBool m_supports_qSaveCore = eLazyBoolCalculate; + LazyBool m_uses_native_signals = eLazyBoolCalculate; + + bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1, + m_supports_qUserName : 1, m_supports_qGroupName : 1, + m_supports_qThreadStopInfo : 1, m_supports_z0 : 1, m_supports_z1 : 1, + m_supports_z2 : 1, m_supports_z3 : 1, m_supports_z4 : 1, + m_supports_QEnvironment : 1, m_supports_QEnvironmentHexEncoded : 1, + m_supports_qSymbol : 1, m_qSymbol_requests_done : 1, + m_supports_qModuleInfo : 1, m_supports_jThreadsInfo : 1, + m_supports_jModulesInfo : 1, m_supports_vFileSize : 1, + m_supports_vFileMode : 1, m_supports_vFileExists : 1, + m_supports_vRun : 1; + + /// Current gdb remote protocol process identifier for all other operations + lldb::pid_t m_curr_pid = LLDB_INVALID_PROCESS_ID; + /// Current gdb remote protocol process identifier for continue, step, etc + lldb::pid_t m_curr_pid_run = LLDB_INVALID_PROCESS_ID; + /// Current gdb remote protocol thread identifier for all other operations + lldb::tid_t m_curr_tid = LLDB_INVALID_THREAD_ID; + /// Current gdb remote protocol thread identifier for continue, step, etc + lldb::tid_t m_curr_tid_run = LLDB_INVALID_THREAD_ID; + + uint32_t m_num_supported_hardware_watchpoints = 0; + WatchpointHardwareFeature m_watchpoint_types = + eWatchpointHardwareFeatureUnknown; + uint32_t m_low_mem_addressing_bits = 0; + uint32_t m_high_mem_addressing_bits = 0; + + ArchSpec m_host_arch; + std::string m_host_distribution_id; + ArchSpec m_process_arch; + UUID m_process_standalone_uuid; + lldb::addr_t m_process_standalone_value = LLDB_INVALID_ADDRESS; + bool m_process_standalone_value_is_offset = false; + std::vector<lldb::addr_t> m_binary_addresses; + llvm::VersionTuple m_os_version; + llvm::VersionTuple m_maccatalyst_version; + std::string m_os_build; + std::string m_os_kernel; + std::string m_hostname; + std::string m_gdb_server_name; // from reply to qGDBServerVersion, empty if + // qGDBServerVersion is not supported + uint32_t m_gdb_server_version = + UINT32_MAX; // from reply to qGDBServerVersion, zero if + // qGDBServerVersion is not supported + std::chrono::seconds m_default_packet_timeout; + int m_target_vm_page_size = 0; // target system VM page size; 0 unspecified + uint64_t m_max_packet_size = 0; // as returned by qSupported + std::string m_qSupported_response; // the complete response to qSupported + + bool m_supported_async_json_packets_is_valid = false; + lldb_private::StructuredData::ObjectSP m_supported_async_json_packets_sp; + + std::vector<MemoryRegionInfo> m_qXfer_memory_map; + bool m_qXfer_memory_map_loaded = false; + + bool GetCurrentProcessInfo(bool allow_lazy_pid = true); + + bool GetGDBServerVersion(); + + // Given the list of compression types that the remote debug stub can support, + // possibly enable compression if we find an encoding we can handle. + void MaybeEnableCompression( + llvm::ArrayRef<llvm::StringRef> supported_compressions); + + bool DecodeProcessInfoResponse(StringExtractorGDBRemote &response, + ProcessInstanceInfo &process_info); + + void OnRunPacketSent(bool first) override; + + PacketResult SendThreadSpecificPacketAndWaitForResponse( + lldb::tid_t tid, StreamString &&payload, + StringExtractorGDBRemote &response); + + Status SendGetTraceDataPacket(StreamGDBRemote &packet, lldb::user_id_t uid, + lldb::tid_t thread_id, + llvm::MutableArrayRef<uint8_t> &buffer, + size_t offset); + + Status LoadQXferMemoryMap(); + + Status GetQXferMemoryMapRegionInfo(lldb::addr_t addr, + MemoryRegionInfo ®ion); + + LazyBool GetThreadPacketSupported(lldb::tid_t tid, llvm::StringRef packetStr); + +private: + GDBRemoteCommunicationClient(const GDBRemoteCommunicationClient &) = delete; + const GDBRemoteCommunicationClient & + operator=(const GDBRemoteCommunicationClient &) = delete; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONCLIENT_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp new file mode 100644 index 000000000000..5d4a537befeb --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp @@ -0,0 +1,95 @@ +//===-- GDBRemoteCommunicationHistory.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 "GDBRemoteCommunicationHistory.h" + +// Other libraries and framework includes +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Log.h" + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +GDBRemoteCommunicationHistory::GDBRemoteCommunicationHistory(uint32_t size) + : m_packets() { + if (size) + m_packets.resize(size); +} + +GDBRemoteCommunicationHistory::~GDBRemoteCommunicationHistory() = default; + +void GDBRemoteCommunicationHistory::AddPacket(char packet_char, + GDBRemotePacket::Type type, + uint32_t bytes_transmitted) { + const size_t size = m_packets.size(); + if (size == 0) + return; + + const uint32_t idx = GetNextIndex(); + m_packets[idx].packet.data.assign(1, packet_char); + m_packets[idx].type = type; + m_packets[idx].bytes_transmitted = bytes_transmitted; + m_packets[idx].packet_idx = m_total_packet_count; + m_packets[idx].tid = llvm::get_threadid(); +} + +void GDBRemoteCommunicationHistory::AddPacket(const std::string &src, + uint32_t src_len, + GDBRemotePacket::Type type, + uint32_t bytes_transmitted) { + const size_t size = m_packets.size(); + if (size == 0) + return; + + const uint32_t idx = GetNextIndex(); + m_packets[idx].packet.data.assign(src, 0, src_len); + m_packets[idx].type = type; + m_packets[idx].bytes_transmitted = bytes_transmitted; + m_packets[idx].packet_idx = m_total_packet_count; + m_packets[idx].tid = llvm::get_threadid(); +} + +void GDBRemoteCommunicationHistory::Dump(Stream &strm) const { + const uint32_t size = GetNumPacketsInHistory(); + const uint32_t first_idx = GetFirstSavedPacketIndex(); + const uint32_t stop_idx = m_curr_idx + size; + for (uint32_t i = first_idx; i < stop_idx; ++i) { + const uint32_t idx = NormalizeIndex(i); + const GDBRemotePacket &entry = m_packets[idx]; + if (entry.type == GDBRemotePacket::ePacketTypeInvalid || + entry.packet.data.empty()) + break; + strm.Printf("history[%u] ", entry.packet_idx); + entry.Dump(strm); + } +} + +void GDBRemoteCommunicationHistory::Dump(Log *log) const { + if (!log || m_dumped_to_log) + return; + + m_dumped_to_log = true; + const uint32_t size = GetNumPacketsInHistory(); + const uint32_t first_idx = GetFirstSavedPacketIndex(); + const uint32_t stop_idx = m_curr_idx + size; + for (uint32_t i = first_idx; i < stop_idx; ++i) { + const uint32_t idx = NormalizeIndex(i); + const GDBRemotePacket &entry = m_packets[idx]; + if (entry.type == GDBRemotePacket::ePacketTypeInvalid || + entry.packet.data.empty()) + break; + LLDB_LOGF(log, "history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s", + entry.packet_idx, entry.tid, entry.bytes_transmitted, + (entry.type == GDBRemotePacket::ePacketTypeSend) ? "send" + : "read", + entry.packet.data.c_str()); + } +} + diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h new file mode 100644 index 000000000000..2fbc621a90e7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h @@ -0,0 +1,76 @@ +//===-- GDBRemoteCommunicationHistory.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONHISTORY_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONHISTORY_H + +#include <string> +#include <vector> + +#include "lldb/Utility/GDBRemote.h" +#include "lldb/lldb-public.h" +#include "llvm/Support/raw_ostream.h" + +namespace lldb_private { +namespace process_gdb_remote { + +/// The history keeps a circular buffer of GDB remote packets. The history is +/// used for logging and replaying GDB remote packets. +class GDBRemoteCommunicationHistory { +public: + GDBRemoteCommunicationHistory(uint32_t size = 0); + + ~GDBRemoteCommunicationHistory(); + + // For single char packets for ack, nack and /x03 + void AddPacket(char packet_char, GDBRemotePacket::Type type, + uint32_t bytes_transmitted); + + void AddPacket(const std::string &src, uint32_t src_len, + GDBRemotePacket::Type type, uint32_t bytes_transmitted); + + void Dump(Stream &strm) const; + void Dump(Log *log) const; + bool DidDumpToLog() const { return m_dumped_to_log; } + +private: + uint32_t GetFirstSavedPacketIndex() const { + if (m_total_packet_count < m_packets.size()) + return 0; + else + return m_curr_idx + 1; + } + + uint32_t GetNumPacketsInHistory() const { + if (m_total_packet_count < m_packets.size()) + return m_total_packet_count; + else + return (uint32_t)m_packets.size(); + } + + uint32_t GetNextIndex() { + ++m_total_packet_count; + const uint32_t idx = m_curr_idx; + m_curr_idx = NormalizeIndex(idx + 1); + return idx; + } + + uint32_t NormalizeIndex(uint32_t i) const { + return m_packets.empty() ? 0 : i % m_packets.size(); + } + + std::vector<GDBRemotePacket> m_packets; + uint32_t m_curr_idx = 0; + uint32_t m_total_packet_count = 0; + mutable bool m_dumped_to_log = false; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONHISTORY_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp new file mode 100644 index 000000000000..e7a292f858a8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -0,0 +1,168 @@ +//===-- GDBRemoteCommunicationServer.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 <cerrno> + +#include "lldb/Host/Config.h" + +#include "GDBRemoteCommunicationServer.h" + +#include "ProcessGDBRemoteLog.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" +#include "lldb/Utility/UnimplementedError.h" +#include "llvm/Support/JSON.h" +#include <cstring> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace llvm; + +GDBRemoteCommunicationServer::GDBRemoteCommunicationServer() + : GDBRemoteCommunication(), m_exit_now(false) { + RegisterPacketHandler( + StringExtractorGDBRemote::eServerPacketType_QEnableErrorStrings, + [this](StringExtractorGDBRemote packet, Status &error, bool &interrupt, + bool &quit) { return this->Handle_QErrorStringEnable(packet); }); +} + +GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() = default; + +void GDBRemoteCommunicationServer::RegisterPacketHandler( + StringExtractorGDBRemote::ServerPacketType packet_type, + PacketHandler handler) { + m_packet_handlers[packet_type] = std::move(handler); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::GetPacketAndSendResponse( + Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) { + StringExtractorGDBRemote packet; + + PacketResult packet_result = ReadPacket(packet, timeout, false); + if (packet_result == PacketResult::Success) { + const StringExtractorGDBRemote::ServerPacketType packet_type = + packet.GetServerPacketType(); + switch (packet_type) { + case StringExtractorGDBRemote::eServerPacketType_nack: + case StringExtractorGDBRemote::eServerPacketType_ack: + break; + + case StringExtractorGDBRemote::eServerPacketType_invalid: + error.SetErrorString("invalid packet"); + quit = true; + break; + + case StringExtractorGDBRemote::eServerPacketType_unimplemented: + packet_result = SendUnimplementedResponse(packet.GetStringRef().data()); + break; + + default: + auto handler_it = m_packet_handlers.find(packet_type); + if (handler_it == m_packet_handlers.end()) + packet_result = SendUnimplementedResponse(packet.GetStringRef().data()); + else + packet_result = handler_it->second(packet, error, interrupt, quit); + break; + } + } else { + if (!IsConnected()) { + error.SetErrorString("lost connection"); + quit = true; + } else { + error.SetErrorString("timeout"); + } + } + + // Check if anything occurred that would force us to want to exit. + if (m_exit_now) + quit = true; + + return packet_result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendUnimplementedResponse(const char *) { + // TODO: Log the packet we aren't handling... + return SendPacketNoLock(""); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendErrorResponse(uint8_t err) { + char packet[16]; + int packet_len = ::snprintf(packet, sizeof(packet), "E%2.2x", err); + assert(packet_len < (int)sizeof(packet)); + return SendPacketNoLock(llvm::StringRef(packet, packet_len)); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendErrorResponse(const Status &error) { + if (m_send_error_strings) { + lldb_private::StreamString packet; + packet.Printf("E%2.2x;", static_cast<uint8_t>(error.GetError())); + packet.PutStringAsRawHex8(error.AsCString()); + return SendPacketNoLock(packet.GetString()); + } else + return SendErrorResponse(error.GetError()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendErrorResponse(llvm::Error error) { + assert(error); + std::unique_ptr<llvm::ErrorInfoBase> EIB; + std::unique_ptr<UnimplementedError> UE; + llvm::handleAllErrors( + std::move(error), + [&](std::unique_ptr<UnimplementedError> E) { UE = std::move(E); }, + [&](std::unique_ptr<llvm::ErrorInfoBase> E) { EIB = std::move(E); }); + + if (EIB) + return SendErrorResponse(Status(llvm::Error(std::move(EIB)))); + return SendUnimplementedResponse(""); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::Handle_QErrorStringEnable( + StringExtractorGDBRemote &packet) { + m_send_error_strings = true; + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendIllFormedResponse( + const StringExtractorGDBRemote &failed_packet, const char *message) { + Log *log = GetLog(GDBRLog::Packets); + LLDB_LOGF(log, "GDBRemoteCommunicationServer::%s: ILLFORMED: '%s' (%s)", + __FUNCTION__, failed_packet.GetStringRef().data(), + message ? message : ""); + return SendErrorResponse(0x03); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendOKResponse() { + return SendPacketNoLock("OK"); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendJSONResponse(const json::Value &value) { + std::string json_string; + raw_string_ostream os(json_string); + os << value; + os.flush(); + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(json_string.c_str(), json_string.size()); + return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServer::SendJSONResponse(Expected<json::Value> value) { + if (!value) + return SendErrorResponse(value.takeError()); + return SendJSONResponse(*value); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h new file mode 100644 index 000000000000..ccbcd0ac5bf5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -0,0 +1,86 @@ +//===-- GDBRemoteCommunicationServer.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVER_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVER_H + +#include <functional> +#include <map> + +#include "GDBRemoteCommunication.h" +#include "lldb/lldb-private-forward.h" + +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +class StringExtractorGDBRemote; + +namespace lldb_private { +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class GDBRemoteCommunicationServer : public GDBRemoteCommunication { +public: + using PacketHandler = + std::function<PacketResult(StringExtractorGDBRemote &packet, + Status &error, bool &interrupt, bool &quit)>; + + GDBRemoteCommunicationServer(); + + ~GDBRemoteCommunicationServer() override; + + void + RegisterPacketHandler(StringExtractorGDBRemote::ServerPacketType packet_type, + PacketHandler handler); + + PacketResult GetPacketAndSendResponse(Timeout<std::micro> timeout, + Status &error, bool &interrupt, + bool &quit); + +protected: + std::map<StringExtractorGDBRemote::ServerPacketType, PacketHandler> + m_packet_handlers; + bool m_exit_now; // use in asynchronous handling to indicate process should + // exit. + + bool m_send_error_strings = false; // If the client enables this then + // we will send error strings as well. + + PacketResult Handle_QErrorStringEnable(StringExtractorGDBRemote &packet); + + PacketResult SendErrorResponse(const Status &error); + + PacketResult SendErrorResponse(llvm::Error error); + + PacketResult SendUnimplementedResponse(const char *packet); + + PacketResult SendErrorResponse(uint8_t error); + + PacketResult SendIllFormedResponse(const StringExtractorGDBRemote &packet, + const char *error_message); + + PacketResult SendOKResponse(); + + /// Serialize and send a JSON object response. + PacketResult SendJSONResponse(const llvm::json::Value &value); + + /// Serialize and send a JSON object response, or respond with an error if the + /// input object is an \a llvm::Error. + PacketResult SendJSONResponse(llvm::Expected<llvm::json::Value> value); + +private: + GDBRemoteCommunicationServer(const GDBRemoteCommunicationServer &) = delete; + const GDBRemoteCommunicationServer & + operator=(const GDBRemoteCommunicationServer &) = delete; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp new file mode 100644 index 000000000000..f9d37490e16a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -0,0 +1,1388 @@ +//===-- GDBRemoteCommunicationServerCommon.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 "GDBRemoteCommunicationServerCommon.h" + +#include <cerrno> + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#include <chrono> +#include <cstring> +#include <optional> + +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileAction.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/SafeMachO.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Platform.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/GDBRemote.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StructuredData.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/JSON.h" +#include "llvm/TargetParser/Triple.h" + +#include "ProcessGDBRemoteLog.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#ifdef __ANDROID__ +#include "lldb/Host/android/HostInfoAndroid.h" +#include "lldb/Host/common/ZipFileResolver.h" +#endif + +using namespace lldb; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private; + +#ifdef __ANDROID__ +const static uint32_t g_default_packet_timeout_sec = 20; // seconds +#else +const static uint32_t g_default_packet_timeout_sec = 0; // not specified +#endif + +// GDBRemoteCommunicationServerCommon constructor +GDBRemoteCommunicationServerCommon::GDBRemoteCommunicationServerCommon() + : GDBRemoteCommunicationServer(), m_process_launch_info(), + m_process_launch_error(), m_proc_infos(), m_proc_infos_index(0) { + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_A, + &GDBRemoteCommunicationServerCommon::Handle_A); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QEnvironment, + &GDBRemoteCommunicationServerCommon::Handle_QEnvironment); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QEnvironmentHexEncoded, + &GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qfProcessInfo, + &GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qGroupName, + &GDBRemoteCommunicationServerCommon::Handle_qGroupName); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qHostInfo, + &GDBRemoteCommunicationServerCommon::Handle_qHostInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QLaunchArch, + &GDBRemoteCommunicationServerCommon::Handle_QLaunchArch); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess, + &GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qEcho, + &GDBRemoteCommunicationServerCommon::Handle_qEcho); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qModuleInfo, + &GDBRemoteCommunicationServerCommon::Handle_qModuleInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jModulesInfo, + &GDBRemoteCommunicationServerCommon::Handle_jModulesInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qPlatform_chmod, + &GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qPlatform_mkdir, + &GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qPlatform_shell, + &GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qProcessInfoPID, + &GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QSetDetachOnError, + &GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QSetSTDERR, + &GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QSetSTDIN, + &GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT, + &GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qSpeedTest, + &GDBRemoteCommunicationServerCommon::Handle_qSpeedTest); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qsProcessInfo, + &GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode, + &GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qSupported, + &GDBRemoteCommunicationServerCommon::Handle_qSupported); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qUserName, + &GDBRemoteCommunicationServerCommon::Handle_qUserName); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_close, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Close); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_exists, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Exists); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_md5, + &GDBRemoteCommunicationServerCommon::Handle_vFile_MD5); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_mode, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Mode); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_open, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Open); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_pread, + &GDBRemoteCommunicationServerCommon::Handle_vFile_pRead); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_pwrite, + &GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_size, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Size); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_fstat, + &GDBRemoteCommunicationServerCommon::Handle_vFile_FStat); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_stat, + &GDBRemoteCommunicationServerCommon::Handle_vFile_Stat); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_symlink, + &GDBRemoteCommunicationServerCommon::Handle_vFile_symlink); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_unlink, + &GDBRemoteCommunicationServerCommon::Handle_vFile_unlink); +} + +// Destructor +GDBRemoteCommunicationServerCommon::~GDBRemoteCommunicationServerCommon() = + default; + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qHostInfo( + StringExtractorGDBRemote &packet) { + StreamString response; + + // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00 + + ArchSpec host_arch(HostInfo::GetArchitecture()); + const llvm::Triple &host_triple = host_arch.GetTriple(); + response.PutCString("triple:"); + response.PutStringAsRawHex8(host_triple.getTriple()); + response.Printf(";ptrsize:%u;", host_arch.GetAddressByteSize()); + + llvm::StringRef distribution_id = HostInfo::GetDistributionId(); + if (!distribution_id.empty()) { + response.PutCString("distribution_id:"); + response.PutStringAsRawHex8(distribution_id); + response.PutCString(";"); + } + +#if defined(__APPLE__) + // For parity with debugserver, we'll include the vendor key. + response.PutCString("vendor:apple;"); + + // Send out MachO info. + uint32_t cpu = host_arch.GetMachOCPUType(); + uint32_t sub = host_arch.GetMachOCPUSubType(); + if (cpu != LLDB_INVALID_CPUTYPE) + response.Printf("cputype:%u;", cpu); + if (sub != LLDB_INVALID_CPUTYPE) + response.Printf("cpusubtype:%u;", sub); + + if (cpu == llvm::MachO::CPU_TYPE_ARM || cpu == llvm::MachO::CPU_TYPE_ARM64) { +// Indicate the OS type. +#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 + response.PutCString("ostype:tvos;"); +#elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + response.PutCString("ostype:watchos;"); +#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 + response.PutCString("ostype:bridgeos;"); +#else + response.PutCString("ostype:ios;"); +#endif + + // On arm, we use "synchronous" watchpoints which means the exception is + // delivered before the instruction executes. + response.PutCString("watchpoint_exceptions_received:before;"); + } else { + response.PutCString("ostype:macosx;"); + response.Printf("watchpoint_exceptions_received:after;"); + } + +#else + if (host_arch.GetMachine() == llvm::Triple::aarch64 || + host_arch.GetMachine() == llvm::Triple::aarch64_32 || + host_arch.GetMachine() == llvm::Triple::aarch64_be || + host_arch.GetMachine() == llvm::Triple::arm || + host_arch.GetMachine() == llvm::Triple::armeb || host_arch.IsMIPS()) + response.Printf("watchpoint_exceptions_received:before;"); + else + response.Printf("watchpoint_exceptions_received:after;"); +#endif + + switch (endian::InlHostByteOrder()) { + case eByteOrderBig: + response.PutCString("endian:big;"); + break; + case eByteOrderLittle: + response.PutCString("endian:little;"); + break; + case eByteOrderPDP: + response.PutCString("endian:pdp;"); + break; + default: + response.PutCString("endian:unknown;"); + break; + } + + llvm::VersionTuple version = HostInfo::GetOSVersion(); + if (!version.empty()) { + response.Format("os_version:{0}", version.getAsString()); + response.PutChar(';'); + } + +#if defined(__APPLE__) + llvm::VersionTuple maccatalyst_version = HostInfo::GetMacCatalystVersion(); + if (!maccatalyst_version.empty()) { + response.Format("maccatalyst_version:{0}", + maccatalyst_version.getAsString()); + response.PutChar(';'); + } +#endif + + if (std::optional<std::string> s = HostInfo::GetOSBuildString()) { + response.PutCString("os_build:"); + response.PutStringAsRawHex8(*s); + response.PutChar(';'); + } + if (std::optional<std::string> s = HostInfo::GetOSKernelDescription()) { + response.PutCString("os_kernel:"); + response.PutStringAsRawHex8(*s); + response.PutChar(';'); + } + + std::string s; +#if defined(__APPLE__) + +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + // For iOS devices, we are connected through a USB Mux so we never pretend to + // actually have a hostname as far as the remote lldb that is connecting to + // this lldb-platform is concerned + response.PutCString("hostname:"); + response.PutStringAsRawHex8("127.0.0.1"); + response.PutChar(';'); +#else // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + if (HostInfo::GetHostname(s)) { + response.PutCString("hostname:"); + response.PutStringAsRawHex8(s); + response.PutChar(';'); + } +#endif // #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) + +#else // #if defined(__APPLE__) + if (HostInfo::GetHostname(s)) { + response.PutCString("hostname:"); + response.PutStringAsRawHex8(s); + response.PutChar(';'); + } +#endif // #if defined(__APPLE__) + // coverity[unsigned_compare] + if (g_default_packet_timeout_sec > 0) + response.Printf("default_packet_timeout:%u;", g_default_packet_timeout_sec); + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qProcessInfoPID( + StringExtractorGDBRemote &packet) { + // Packet format: "qProcessInfoPID:%i" where %i is the pid + packet.SetFilePos(::strlen("qProcessInfoPID:")); + lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID); + if (pid != LLDB_INVALID_PROCESS_ID) { + ProcessInstanceInfo proc_info; + if (Host::GetProcessInfo(pid, proc_info)) { + StreamString response; + CreateProcessInfoResponse(proc_info, response); + return SendPacketNoLock(response.GetString()); + } + } + return SendErrorResponse(1); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qfProcessInfo( + StringExtractorGDBRemote &packet) { + m_proc_infos_index = 0; + m_proc_infos.clear(); + + ProcessInstanceInfoMatch match_info; + packet.SetFilePos(::strlen("qfProcessInfo")); + if (packet.GetChar() == ':') { + llvm::StringRef key; + llvm::StringRef value; + while (packet.GetNameColonValue(key, value)) { + bool success = true; + if (key == "name") { + StringExtractor extractor(value); + std::string file; + extractor.GetHexByteString(file); + match_info.GetProcessInfo().GetExecutableFile().SetFile( + file, FileSpec::Style::native); + } else if (key == "name_match") { + NameMatch name_match = llvm::StringSwitch<NameMatch>(value) + .Case("equals", NameMatch::Equals) + .Case("starts_with", NameMatch::StartsWith) + .Case("ends_with", NameMatch::EndsWith) + .Case("contains", NameMatch::Contains) + .Case("regex", NameMatch::RegularExpression) + .Default(NameMatch::Ignore); + match_info.SetNameMatchType(name_match); + if (name_match == NameMatch::Ignore) + return SendErrorResponse(2); + } else if (key == "pid") { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + if (value.getAsInteger(0, pid)) + return SendErrorResponse(2); + match_info.GetProcessInfo().SetProcessID(pid); + } else if (key == "parent_pid") { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + if (value.getAsInteger(0, pid)) + return SendErrorResponse(2); + match_info.GetProcessInfo().SetParentProcessID(pid); + } else if (key == "uid") { + uint32_t uid = UINT32_MAX; + if (value.getAsInteger(0, uid)) + return SendErrorResponse(2); + match_info.GetProcessInfo().SetUserID(uid); + } else if (key == "gid") { + uint32_t gid = UINT32_MAX; + if (value.getAsInteger(0, gid)) + return SendErrorResponse(2); + match_info.GetProcessInfo().SetGroupID(gid); + } else if (key == "euid") { + uint32_t uid = UINT32_MAX; + if (value.getAsInteger(0, uid)) + return SendErrorResponse(2); + match_info.GetProcessInfo().SetEffectiveUserID(uid); + } else if (key == "egid") { + uint32_t gid = UINT32_MAX; + if (value.getAsInteger(0, gid)) + return SendErrorResponse(2); + match_info.GetProcessInfo().SetEffectiveGroupID(gid); + } else if (key == "all_users") { + match_info.SetMatchAllUsers( + OptionArgParser::ToBoolean(value, false, &success)); + } else if (key == "triple") { + match_info.GetProcessInfo().GetArchitecture() = + HostInfo::GetAugmentedArchSpec(value); + } else { + success = false; + } + + if (!success) + return SendErrorResponse(2); + } + } + + if (Host::FindProcesses(match_info, m_proc_infos)) { + // We found something, return the first item by calling the get subsequent + // process info packet handler... + return Handle_qsProcessInfo(packet); + } + return SendErrorResponse(3); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qsProcessInfo( + StringExtractorGDBRemote &packet) { + if (m_proc_infos_index < m_proc_infos.size()) { + StreamString response; + CreateProcessInfoResponse(m_proc_infos[m_proc_infos_index], response); + ++m_proc_infos_index; + return SendPacketNoLock(response.GetString()); + } + return SendErrorResponse(4); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qUserName( + StringExtractorGDBRemote &packet) { +#if LLDB_ENABLE_POSIX + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "GDBRemoteCommunicationServerCommon::%s begin", __FUNCTION__); + + // Packet format: "qUserName:%i" where %i is the uid + packet.SetFilePos(::strlen("qUserName:")); + uint32_t uid = packet.GetU32(UINT32_MAX); + if (uid != UINT32_MAX) { + if (std::optional<llvm::StringRef> name = + HostInfo::GetUserIDResolver().GetUserName(uid)) { + StreamString response; + response.PutStringAsRawHex8(*name); + return SendPacketNoLock(response.GetString()); + } + } + LLDB_LOGF(log, "GDBRemoteCommunicationServerCommon::%s end", __FUNCTION__); +#endif + return SendErrorResponse(5); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qGroupName( + StringExtractorGDBRemote &packet) { +#if LLDB_ENABLE_POSIX + // Packet format: "qGroupName:%i" where %i is the gid + packet.SetFilePos(::strlen("qGroupName:")); + uint32_t gid = packet.GetU32(UINT32_MAX); + if (gid != UINT32_MAX) { + if (std::optional<llvm::StringRef> name = + HostInfo::GetUserIDResolver().GetGroupName(gid)) { + StreamString response; + response.PutStringAsRawHex8(*name); + return SendPacketNoLock(response.GetString()); + } + } +#endif + return SendErrorResponse(6); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qSpeedTest( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("qSpeedTest:")); + + llvm::StringRef key; + llvm::StringRef value; + bool success = packet.GetNameColonValue(key, value); + if (success && key == "response_size") { + uint32_t response_size = 0; + if (!value.getAsInteger(0, response_size)) { + if (response_size == 0) + return SendOKResponse(); + StreamString response; + uint32_t bytes_left = response_size; + response.PutCString("data:"); + while (bytes_left > 0) { + if (bytes_left >= 26) { + response.PutCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + bytes_left -= 26; + } else { + response.Printf("%*.*s;", bytes_left, bytes_left, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + bytes_left = 0; + } + } + return SendPacketNoLock(response.GetString()); + } + } + return SendErrorResponse(7); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Open( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:open:")); + std::string path; + packet.GetHexByteStringTerminatedBy(path, ','); + if (!path.empty()) { + if (packet.GetChar() == ',') { + auto flags = File::OpenOptions(packet.GetHexMaxU32(false, 0)); + if (packet.GetChar() == ',') { + mode_t mode = packet.GetHexMaxU32(false, 0600); + FileSpec path_spec(path); + FileSystem::Instance().Resolve(path_spec); + // Do not close fd. + auto file = FileSystem::Instance().Open(path_spec, flags, mode, false); + + StreamString response; + response.PutChar('F'); + + int descriptor = File::kInvalidDescriptor; + if (file) { + descriptor = file.get()->GetDescriptor(); + response.Printf("%x", descriptor); + } else { + response.PutCString("-1"); + std::error_code code = errorToErrorCode(file.takeError()); + if (code.category() == std::system_category()) { + response.Printf(",%x", code.value()); + } + } + + return SendPacketNoLock(response.GetString()); + } + } + } + return SendErrorResponse(18); +} + +static GDBErrno system_errno_to_gdb(int err) { + switch (err) { +#define HANDLE_ERRNO(name, value) \ + case name: \ + return GDB_##name; +#include "Plugins/Process/gdb-remote/GDBRemoteErrno.def" + default: + return GDB_EUNKNOWN; + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Close( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:close:")); + int fd = packet.GetS32(-1, 16); + int err = -1; + int save_errno = 0; + if (fd >= 0) { + NativeFile file(fd, File::OpenOptions(0), true); + Status error = file.Close(); + err = 0; + save_errno = error.GetError(); + } else { + save_errno = EINVAL; + } + StreamString response; + response.PutChar('F'); + response.Printf("%x", err); + if (save_errno) + response.Printf(",%x", system_errno_to_gdb(save_errno)); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_pRead( + StringExtractorGDBRemote &packet) { + StreamGDBRemote response; + packet.SetFilePos(::strlen("vFile:pread:")); + int fd = packet.GetS32(-1, 16); + if (packet.GetChar() == ',') { + size_t count = packet.GetHexMaxU64(false, SIZE_MAX); + if (packet.GetChar() == ',') { + off_t offset = packet.GetHexMaxU32(false, UINT32_MAX); + if (count == SIZE_MAX) { + response.Printf("F-1:%x", EINVAL); + return SendPacketNoLock(response.GetString()); + } + + std::string buffer(count, 0); + NativeFile file(fd, File::eOpenOptionReadOnly, false); + Status error = file.Read(static_cast<void *>(&buffer[0]), count, offset); + const int save_errno = error.GetError(); + response.PutChar('F'); + if (error.Success()) { + response.Printf("%zx", count); + response.PutChar(';'); + response.PutEscapedBytes(&buffer[0], count); + } else { + response.PutCString("-1"); + if (save_errno) + response.Printf(",%x", system_errno_to_gdb(save_errno)); + } + return SendPacketNoLock(response.GetString()); + } + } + return SendErrorResponse(21); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_pWrite( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:pwrite:")); + + StreamGDBRemote response; + response.PutChar('F'); + + int fd = packet.GetS32(-1, 16); + if (packet.GetChar() == ',') { + off_t offset = packet.GetHexMaxU32(false, UINT32_MAX); + if (packet.GetChar() == ',') { + std::string buffer; + if (packet.GetEscapedBinaryData(buffer)) { + NativeFile file(fd, File::eOpenOptionWriteOnly, false); + size_t count = buffer.size(); + Status error = + file.Write(static_cast<const void *>(&buffer[0]), count, offset); + const int save_errno = error.GetError(); + if (error.Success()) + response.Printf("%zx", count); + else { + response.PutCString("-1"); + if (save_errno) + response.Printf(",%x", system_errno_to_gdb(save_errno)); + } + } else { + response.Printf("-1,%x", EINVAL); + } + return SendPacketNoLock(response.GetString()); + } + } + return SendErrorResponse(27); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Size( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:size:")); + std::string path; + packet.GetHexByteString(path); + if (!path.empty()) { + uint64_t Size; + if (llvm::sys::fs::file_size(path, Size)) + return SendErrorResponse(5); + StreamString response; + response.PutChar('F'); + response.PutHex64(Size); + if (Size == UINT64_MAX) { + response.PutChar(','); + response.PutHex64(Size); // TODO: replace with Host::GetSyswideErrorCode() + } + return SendPacketNoLock(response.GetString()); + } + return SendErrorResponse(22); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Mode( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:mode:")); + std::string path; + packet.GetHexByteString(path); + if (!path.empty()) { + FileSpec file_spec(path); + FileSystem::Instance().Resolve(file_spec); + std::error_code ec; + const uint32_t mode = FileSystem::Instance().GetPermissions(file_spec, ec); + StreamString response; + if (mode != llvm::sys::fs::perms_not_known) + response.Printf("F%x", mode); + else + response.Printf("F-1,%x", (int)Status(ec).GetError()); + return SendPacketNoLock(response.GetString()); + } + return SendErrorResponse(23); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Exists( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:exists:")); + std::string path; + packet.GetHexByteString(path); + if (!path.empty()) { + bool retcode = llvm::sys::fs::exists(path); + StreamString response; + response.PutChar('F'); + response.PutChar(','); + if (retcode) + response.PutChar('1'); + else + response.PutChar('0'); + return SendPacketNoLock(response.GetString()); + } + return SendErrorResponse(24); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_symlink( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:symlink:")); + std::string dst, src; + packet.GetHexByteStringTerminatedBy(dst, ','); + packet.GetChar(); // Skip ',' char + packet.GetHexByteString(src); + + FileSpec src_spec(src); + FileSystem::Instance().Resolve(src_spec); + Status error = FileSystem::Instance().Symlink(src_spec, FileSpec(dst)); + + StreamString response; + response.Printf("F%x,%x", error.GetError(), error.GetError()); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_unlink( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:unlink:")); + std::string path; + packet.GetHexByteString(path); + Status error(llvm::sys::fs::remove(path)); + StreamString response; + response.Printf("F%x,%x", error.GetError(), error.GetError()); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("qPlatform_shell:")); + std::string path; + std::string working_dir; + packet.GetHexByteStringTerminatedBy(path, ','); + if (!path.empty()) { + if (packet.GetChar() == ',') { + // FIXME: add timeout to qPlatform_shell packet + // uint32_t timeout = packet.GetHexMaxU32(false, 32); + if (packet.GetChar() == ',') + packet.GetHexByteString(working_dir); + int status, signo; + std::string output; + FileSpec working_spec(working_dir); + FileSystem::Instance().Resolve(working_spec); + Status err = + Host::RunShellCommand(path.c_str(), working_spec, &status, &signo, + &output, std::chrono::seconds(10)); + StreamGDBRemote response; + if (err.Fail()) { + response.PutCString("F,"); + response.PutHex32(UINT32_MAX); + } else { + response.PutCString("F,"); + response.PutHex32(status); + response.PutChar(','); + response.PutHex32(signo); + response.PutChar(','); + response.PutEscapedBytes(output.c_str(), output.size()); + } + return SendPacketNoLock(response.GetString()); + } + } + return SendErrorResponse(24); +} + +template <typename T, typename U> +static void fill_clamp(T &dest, U src, typename T::value_type fallback) { + static_assert(std::is_unsigned<typename T::value_type>::value, + "Destination type must be unsigned."); + using UU = std::make_unsigned_t<U>; + constexpr auto T_max = std::numeric_limits<typename T::value_type>::max(); + dest = src >= 0 && static_cast<UU>(src) <= T_max ? src : fallback; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_FStat( + StringExtractorGDBRemote &packet) { + StreamGDBRemote response; + packet.SetFilePos(::strlen("vFile:fstat:")); + int fd = packet.GetS32(-1, 16); + + struct stat file_stats; + if (::fstat(fd, &file_stats) == -1) { + const int save_errno = errno; + response.Printf("F-1,%x", system_errno_to_gdb(save_errno)); + return SendPacketNoLock(response.GetString()); + } + + GDBRemoteFStatData data; + fill_clamp(data.gdb_st_dev, file_stats.st_dev, 0); + fill_clamp(data.gdb_st_ino, file_stats.st_ino, 0); + data.gdb_st_mode = file_stats.st_mode; + fill_clamp(data.gdb_st_nlink, file_stats.st_nlink, UINT32_MAX); + fill_clamp(data.gdb_st_uid, file_stats.st_uid, 0); + fill_clamp(data.gdb_st_gid, file_stats.st_gid, 0); + fill_clamp(data.gdb_st_rdev, file_stats.st_rdev, 0); + data.gdb_st_size = file_stats.st_size; +#if !defined(_WIN32) + data.gdb_st_blksize = file_stats.st_blksize; + data.gdb_st_blocks = file_stats.st_blocks; +#else + data.gdb_st_blksize = 0; + data.gdb_st_blocks = 0; +#endif + fill_clamp(data.gdb_st_atime, file_stats.st_atime, 0); + fill_clamp(data.gdb_st_mtime, file_stats.st_mtime, 0); + fill_clamp(data.gdb_st_ctime, file_stats.st_ctime, 0); + + response.Printf("F%zx;", sizeof(data)); + response.PutEscapedBytes(&data, sizeof(data)); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_Stat( + StringExtractorGDBRemote &packet) { + return SendUnimplementedResponse( + "GDBRemoteCommunicationServerCommon::Handle_vFile_Stat() unimplemented"); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_MD5( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:MD5:")); + std::string path; + packet.GetHexByteString(path); + if (!path.empty()) { + StreamGDBRemote response; + auto Result = llvm::sys::fs::md5_contents(path); + if (!Result) { + response.PutCString("F,"); + response.PutCString("x"); + } else { + response.PutCString("F,"); + response.PutHex64(Result->low()); + response.PutHex64(Result->high()); + } + return SendPacketNoLock(response.GetString()); + } + return SendErrorResponse(25); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qPlatform_mkdir( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("qPlatform_mkdir:")); + mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); + if (packet.GetChar() == ',') { + std::string path; + packet.GetHexByteString(path); + Status error(llvm::sys::fs::create_directory(path, mode)); + + StreamGDBRemote response; + response.Printf("F%x", error.GetError()); + + return SendPacketNoLock(response.GetString()); + } + return SendErrorResponse(20); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qPlatform_chmod( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("qPlatform_chmod:")); + + auto perms = + static_cast<llvm::sys::fs::perms>(packet.GetHexMaxU32(false, UINT32_MAX)); + if (packet.GetChar() == ',') { + std::string path; + packet.GetHexByteString(path); + Status error(llvm::sys::fs::setPermissions(path, perms)); + + StreamGDBRemote response; + response.Printf("F%x", error.GetError()); + + return SendPacketNoLock(response.GetString()); + } + return SendErrorResponse(19); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qSupported( + StringExtractorGDBRemote &packet) { + // Parse client-indicated features. + llvm::SmallVector<llvm::StringRef, 4> client_features; + packet.GetStringRef().split(client_features, ';'); + return SendPacketNoLock(llvm::join(HandleFeatures(client_features), ";")); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetDetachOnError( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QSetDetachOnError:")); + if (packet.GetU32(0)) + m_process_launch_info.GetFlags().Set(eLaunchFlagDetachOnError); + else + m_process_launch_info.GetFlags().Clear(eLaunchFlagDetachOnError); + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QStartNoAckMode( + StringExtractorGDBRemote &packet) { + // Send response first before changing m_send_acks to we ack this packet + PacketResult packet_result = SendOKResponse(); + m_send_acks = false; + return packet_result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetSTDIN( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QSetSTDIN:")); + FileAction file_action; + std::string path; + packet.GetHexByteString(path); + const bool read = true; + const bool write = false; + if (file_action.Open(STDIN_FILENO, FileSpec(path), read, write)) { + m_process_launch_info.AppendFileAction(file_action); + return SendOKResponse(); + } + return SendErrorResponse(15); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetSTDOUT( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QSetSTDOUT:")); + FileAction file_action; + std::string path; + packet.GetHexByteString(path); + const bool read = false; + const bool write = true; + if (file_action.Open(STDOUT_FILENO, FileSpec(path), read, write)) { + m_process_launch_info.AppendFileAction(file_action); + return SendOKResponse(); + } + return SendErrorResponse(16); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QSetSTDERR( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QSetSTDERR:")); + FileAction file_action; + std::string path; + packet.GetHexByteString(path); + const bool read = false; + const bool write = true; + if (file_action.Open(STDERR_FILENO, FileSpec(path), read, write)) { + m_process_launch_info.AppendFileAction(file_action); + return SendOKResponse(); + } + return SendErrorResponse(17); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qLaunchSuccess( + StringExtractorGDBRemote &packet) { + if (m_process_launch_error.Success()) + return SendOKResponse(); + StreamString response; + response.PutChar('E'); + response.PutCString(m_process_launch_error.AsCString("<unknown error>")); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QEnvironment( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QEnvironment:")); + const uint32_t bytes_left = packet.GetBytesLeft(); + if (bytes_left > 0) { + m_process_launch_info.GetEnvironment().insert(packet.Peek()); + return SendOKResponse(); + } + return SendErrorResponse(12); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QEnvironmentHexEncoded( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QEnvironmentHexEncoded:")); + const uint32_t bytes_left = packet.GetBytesLeft(); + if (bytes_left > 0) { + std::string str; + packet.GetHexByteString(str); + m_process_launch_info.GetEnvironment().insert(str); + return SendOKResponse(); + } + return SendErrorResponse(12); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_QLaunchArch( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QLaunchArch:")); + const uint32_t bytes_left = packet.GetBytesLeft(); + if (bytes_left > 0) { + const char *arch_triple = packet.Peek(); + m_process_launch_info.SetArchitecture( + HostInfo::GetAugmentedArchSpec(arch_triple)); + return SendOKResponse(); + } + return SendErrorResponse(13); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_A(StringExtractorGDBRemote &packet) { + // The 'A' packet is the most over designed packet ever here with redundant + // argument indexes, redundant argument lengths and needed hex encoded + // argument string values. Really all that is needed is a comma separated hex + // encoded argument value list, but we will stay true to the documented + // version of the 'A' packet here... + + Log *log = GetLog(LLDBLog::Process); + int actual_arg_index = 0; + + packet.SetFilePos(1); // Skip the 'A' + bool success = true; + while (success && packet.GetBytesLeft() > 0) { + // Decode the decimal argument string length. This length is the number of + // hex nibbles in the argument string value. + const uint32_t arg_len = packet.GetU32(UINT32_MAX); + if (arg_len == UINT32_MAX) + success = false; + else { + // Make sure the argument hex string length is followed by a comma + if (packet.GetChar() != ',') + success = false; + else { + // Decode the argument index. We ignore this really because who would + // really send down the arguments in a random order??? + const uint32_t arg_idx = packet.GetU32(UINT32_MAX); + if (arg_idx == UINT32_MAX) + success = false; + else { + // Make sure the argument index is followed by a comma + if (packet.GetChar() != ',') + success = false; + else { + // Decode the argument string value from hex bytes back into a UTF8 + // string and make sure the length matches the one supplied in the + // packet + std::string arg; + if (packet.GetHexByteStringFixedLength(arg, arg_len) != + (arg_len / 2)) + success = false; + else { + // If there are any bytes left + if (packet.GetBytesLeft()) { + if (packet.GetChar() != ',') + success = false; + } + + if (success) { + if (arg_idx == 0) + m_process_launch_info.GetExecutableFile().SetFile( + arg, FileSpec::Style::native); + m_process_launch_info.GetArguments().AppendArgument(arg); + LLDB_LOGF(log, "LLGSPacketHandler::%s added arg %d: \"%s\"", + __FUNCTION__, actual_arg_index, arg.c_str()); + ++actual_arg_index; + } + } + } + } + } + } + } + + if (success) { + m_process_launch_error = LaunchProcess(); + if (m_process_launch_error.Success()) + return SendOKResponse(); + LLDB_LOG(log, "failed to launch exe: {0}", m_process_launch_error); + } + return SendErrorResponse(8); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qEcho( + StringExtractorGDBRemote &packet) { + // Just echo back the exact same packet for qEcho... + return SendPacketNoLock(packet.GetStringRef()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_qModuleInfo( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("qModuleInfo:")); + + std::string module_path; + packet.GetHexByteStringTerminatedBy(module_path, ';'); + if (module_path.empty()) + return SendErrorResponse(1); + + if (packet.GetChar() != ';') + return SendErrorResponse(2); + + std::string triple; + packet.GetHexByteString(triple); + + ModuleSpec matched_module_spec = GetModuleInfo(module_path, triple); + if (!matched_module_spec.GetFileSpec()) + return SendErrorResponse(3); + + const auto file_offset = matched_module_spec.GetObjectOffset(); + const auto file_size = matched_module_spec.GetObjectSize(); + const auto uuid_str = matched_module_spec.GetUUID().GetAsString(""); + + StreamGDBRemote response; + + if (uuid_str.empty()) { + auto Result = llvm::sys::fs::md5_contents( + matched_module_spec.GetFileSpec().GetPath()); + if (!Result) + return SendErrorResponse(5); + response.PutCString("md5:"); + response.PutStringAsRawHex8(Result->digest()); + } else { + response.PutCString("uuid:"); + response.PutStringAsRawHex8(uuid_str); + } + response.PutChar(';'); + + const auto &module_arch = matched_module_spec.GetArchitecture(); + response.PutCString("triple:"); + response.PutStringAsRawHex8(module_arch.GetTriple().getTriple()); + response.PutChar(';'); + + response.PutCString("file_path:"); + response.PutStringAsRawHex8( + matched_module_spec.GetFileSpec().GetPath().c_str()); + response.PutChar(';'); + response.PutCString("file_offset:"); + response.PutHex64(file_offset); + response.PutChar(';'); + response.PutCString("file_size:"); + response.PutHex64(file_size); + response.PutChar(';'); + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_jModulesInfo( + StringExtractorGDBRemote &packet) { + namespace json = llvm::json; + + packet.SetFilePos(::strlen("jModulesInfo:")); + + StructuredData::ObjectSP object_sp = StructuredData::ParseJSON(packet.Peek()); + if (!object_sp) + return SendErrorResponse(1); + + StructuredData::Array *packet_array = object_sp->GetAsArray(); + if (!packet_array) + return SendErrorResponse(2); + + json::Array response_array; + for (size_t i = 0; i < packet_array->GetSize(); ++i) { + StructuredData::Dictionary *query = + packet_array->GetItemAtIndex(i)->GetAsDictionary(); + if (!query) + continue; + llvm::StringRef file, triple; + if (!query->GetValueForKeyAsString("file", file) || + !query->GetValueForKeyAsString("triple", triple)) + continue; + + ModuleSpec matched_module_spec = GetModuleInfo(file, triple); + if (!matched_module_spec.GetFileSpec()) + continue; + + const auto file_offset = matched_module_spec.GetObjectOffset(); + const auto file_size = matched_module_spec.GetObjectSize(); + const auto uuid_str = matched_module_spec.GetUUID().GetAsString(""); + if (uuid_str.empty()) + continue; + const auto triple_str = + matched_module_spec.GetArchitecture().GetTriple().getTriple(); + const auto file_path = matched_module_spec.GetFileSpec().GetPath(); + + json::Object response{{"uuid", uuid_str}, + {"triple", triple_str}, + {"file_path", file_path}, + {"file_offset", static_cast<int64_t>(file_offset)}, + {"file_size", static_cast<int64_t>(file_size)}}; + response_array.push_back(std::move(response)); + } + + StreamString response; + response.AsRawOstream() << std::move(response_array); + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetString().data(), + response.GetSize()); + return SendPacketNoLock(escaped_response.GetString()); +} + +void GDBRemoteCommunicationServerCommon::CreateProcessInfoResponse( + const ProcessInstanceInfo &proc_info, StreamString &response) { + response.Printf( + "pid:%" PRIu64 ";ppid:%" PRIu64 ";uid:%i;gid:%i;euid:%i;egid:%i;", + proc_info.GetProcessID(), proc_info.GetParentProcessID(), + proc_info.GetUserID(), proc_info.GetGroupID(), + proc_info.GetEffectiveUserID(), proc_info.GetEffectiveGroupID()); + response.PutCString("name:"); + response.PutStringAsRawHex8(proc_info.GetExecutableFile().GetPath().c_str()); + + response.PutChar(';'); + response.PutCString("args:"); + response.PutStringAsRawHex8(proc_info.GetArg0()); + for (auto &arg : proc_info.GetArguments()) { + response.PutChar('-'); + response.PutStringAsRawHex8(arg.ref()); + } + + response.PutChar(';'); + const ArchSpec &proc_arch = proc_info.GetArchitecture(); + if (proc_arch.IsValid()) { + const llvm::Triple &proc_triple = proc_arch.GetTriple(); + response.PutCString("triple:"); + response.PutStringAsRawHex8(proc_triple.getTriple()); + response.PutChar(';'); + } +} + +void GDBRemoteCommunicationServerCommon:: + CreateProcessInfoResponse_DebugServerStyle( + const ProcessInstanceInfo &proc_info, StreamString &response) { + response.Printf("pid:%" PRIx64 ";parent-pid:%" PRIx64 + ";real-uid:%x;real-gid:%x;effective-uid:%x;effective-gid:%x;", + proc_info.GetProcessID(), proc_info.GetParentProcessID(), + proc_info.GetUserID(), proc_info.GetGroupID(), + proc_info.GetEffectiveUserID(), + proc_info.GetEffectiveGroupID()); + + const ArchSpec &proc_arch = proc_info.GetArchitecture(); + if (proc_arch.IsValid()) { + const llvm::Triple &proc_triple = proc_arch.GetTriple(); +#if defined(__APPLE__) + // We'll send cputype/cpusubtype. + const uint32_t cpu_type = proc_arch.GetMachOCPUType(); + if (cpu_type != 0) + response.Printf("cputype:%" PRIx32 ";", cpu_type); + + const uint32_t cpu_subtype = proc_arch.GetMachOCPUSubType(); + if (cpu_subtype != 0) + response.Printf("cpusubtype:%" PRIx32 ";", cpu_subtype); + + const std::string vendor = proc_triple.getVendorName().str(); + if (!vendor.empty()) + response.Printf("vendor:%s;", vendor.c_str()); +#else + // We'll send the triple. + response.PutCString("triple:"); + response.PutStringAsRawHex8(proc_triple.getTriple()); + response.PutChar(';'); +#endif + std::string ostype = std::string(proc_triple.getOSName()); + // Adjust so ostype reports ios for Apple/ARM and Apple/ARM64. + if (proc_triple.getVendor() == llvm::Triple::Apple) { + switch (proc_triple.getArch()) { + case llvm::Triple::arm: + case llvm::Triple::thumb: + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + ostype = "ios"; + break; + default: + // No change. + break; + } + } + response.Printf("ostype:%s;", ostype.c_str()); + + switch (proc_arch.GetByteOrder()) { + case lldb::eByteOrderLittle: + response.PutCString("endian:little;"); + break; + case lldb::eByteOrderBig: + response.PutCString("endian:big;"); + break; + case lldb::eByteOrderPDP: + response.PutCString("endian:pdp;"); + break; + default: + // Nothing. + break; + } + // In case of MIPS64, pointer size is depend on ELF ABI For N32 the pointer + // size is 4 and for N64 it is 8 + std::string abi = proc_arch.GetTargetABI(); + if (!abi.empty()) + response.Printf("elf_abi:%s;", abi.c_str()); + response.Printf("ptrsize:%d;", proc_arch.GetAddressByteSize()); + } +} + +FileSpec GDBRemoteCommunicationServerCommon::FindModuleFile( + const std::string &module_path, const ArchSpec &arch) { +#ifdef __ANDROID__ + return HostInfoAndroid::ResolveLibraryPath(module_path, arch); +#else + FileSpec file_spec(module_path); + FileSystem::Instance().Resolve(file_spec); + return file_spec; +#endif +} + +ModuleSpec +GDBRemoteCommunicationServerCommon::GetModuleInfo(llvm::StringRef module_path, + llvm::StringRef triple) { + ArchSpec arch(triple); + + FileSpec req_module_path_spec(module_path); + FileSystem::Instance().Resolve(req_module_path_spec); + + const FileSpec module_path_spec = + FindModuleFile(req_module_path_spec.GetPath(), arch); + + lldb::offset_t file_offset = 0; + lldb::offset_t file_size = 0; +#ifdef __ANDROID__ + // In Android API level 23 and above, dynamic loader is able to load .so file + // directly from zip file. In that case, module_path will be + // "zip_path!/so_path". Resolve the zip file path, .so file offset and size. + ZipFileResolver::FileKind file_kind = ZipFileResolver::eFileKindInvalid; + std::string file_path; + if (!ZipFileResolver::ResolveSharedLibraryPath( + module_path_spec, file_kind, file_path, file_offset, file_size)) { + return ModuleSpec(); + } + lldbassert(file_kind != ZipFileResolver::eFileKindInvalid); + // For zip .so file, this file_path will contain only the actual zip file + // path for the object file processing. Otherwise it is the same as + // module_path. + const FileSpec actual_module_path_spec(file_path); +#else + // It is just module_path_spec reference for other platforms. + const FileSpec &actual_module_path_spec = module_path_spec; +#endif + + const ModuleSpec module_spec(actual_module_path_spec, arch); + + ModuleSpecList module_specs; + if (!ObjectFile::GetModuleSpecifications(actual_module_path_spec, file_offset, + file_size, module_specs)) + return ModuleSpec(); + + ModuleSpec matched_module_spec; + if (!module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + return ModuleSpec(); + +#ifdef __ANDROID__ + if (file_kind == ZipFileResolver::eFileKindZip) { + // For zip .so file, matched_module_spec contains only the actual zip file + // path for the object file processing. Overwrite the matched_module_spec + // file spec with the original module_path_spec to pass "zip_path!/so_path" + // through to PlatformAndroid::DownloadModuleSlice. + *matched_module_spec.GetFileSpecPtr() = module_path_spec; + } +#endif + + return matched_module_spec; +} + +std::vector<std::string> GDBRemoteCommunicationServerCommon::HandleFeatures( + const llvm::ArrayRef<llvm::StringRef> client_features) { + // 128KBytes is a reasonable max packet size--debugger can always use less. + constexpr uint32_t max_packet_size = 128 * 1024; + + // Features common to platform server and llgs. + return { + llvm::formatv("PacketSize={0}", max_packet_size), + "QStartNoAckMode+", + "qEcho+", + "native-signals+", + }; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h new file mode 100644 index 000000000000..b4f1eb3e61c4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h @@ -0,0 +1,154 @@ +//===-- GDBRemoteCommunicationServerCommon.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVERCOMMON_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVERCOMMON_H + +#include <string> + +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/lldb-private-forward.h" + +#include "GDBRemoteCommunicationServer.h" + +class StringExtractorGDBRemote; + +namespace lldb_private { +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class GDBRemoteCommunicationServerCommon : public GDBRemoteCommunicationServer { +public: + GDBRemoteCommunicationServerCommon(); + + ~GDBRemoteCommunicationServerCommon() override; + +protected: + ProcessLaunchInfo m_process_launch_info; + Status m_process_launch_error; + ProcessInstanceInfoList m_proc_infos; + uint32_t m_proc_infos_index; + + PacketResult Handle_A(StringExtractorGDBRemote &packet); + + PacketResult Handle_qHostInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_qProcessInfoPID(StringExtractorGDBRemote &packet); + + PacketResult Handle_qfProcessInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_qsProcessInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_qUserName(StringExtractorGDBRemote &packet); + + PacketResult Handle_qGroupName(StringExtractorGDBRemote &packet); + + PacketResult Handle_qSpeedTest(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_Open(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_Close(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_pRead(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_pWrite(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_Size(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_Mode(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_Exists(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_symlink(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_unlink(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_FStat(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_Stat(StringExtractorGDBRemote &packet); + + PacketResult Handle_vFile_MD5(StringExtractorGDBRemote &packet); + + PacketResult Handle_qEcho(StringExtractorGDBRemote &packet); + + PacketResult Handle_qModuleInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_jModulesInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_qPlatform_shell(StringExtractorGDBRemote &packet); + + PacketResult Handle_qPlatform_mkdir(StringExtractorGDBRemote &packet); + + PacketResult Handle_qPlatform_chmod(StringExtractorGDBRemote &packet); + + PacketResult Handle_qSupported(StringExtractorGDBRemote &packet); + + PacketResult Handle_QSetDetachOnError(StringExtractorGDBRemote &packet); + + PacketResult Handle_QStartNoAckMode(StringExtractorGDBRemote &packet); + + PacketResult Handle_QSetSTDIN(StringExtractorGDBRemote &packet); + + PacketResult Handle_QSetSTDOUT(StringExtractorGDBRemote &packet); + + PacketResult Handle_QSetSTDERR(StringExtractorGDBRemote &packet); + + PacketResult Handle_qLaunchSuccess(StringExtractorGDBRemote &packet); + + PacketResult Handle_QEnvironment(StringExtractorGDBRemote &packet); + + PacketResult Handle_QEnvironmentHexEncoded(StringExtractorGDBRemote &packet); + + PacketResult Handle_QLaunchArch(StringExtractorGDBRemote &packet); + + static void CreateProcessInfoResponse(const ProcessInstanceInfo &proc_info, + StreamString &response); + + static void CreateProcessInfoResponse_DebugServerStyle( + const ProcessInstanceInfo &proc_info, StreamString &response); + + template <typename T> + void RegisterMemberFunctionHandler( + StringExtractorGDBRemote::ServerPacketType packet_type, + PacketResult (T::*handler)(StringExtractorGDBRemote &packet)) { + RegisterPacketHandler(packet_type, + [this, handler](StringExtractorGDBRemote packet, + Status &error, bool &interrupt, + bool &quit) { + return (static_cast<T *>(this)->*handler)(packet); + }); + } + + /// Launch a process with the current launch settings. + /// + /// This method supports running an lldb-gdbserver or similar + /// server in a situation where the startup code has been provided + /// with all the information for a child process to be launched. + /// + /// \return + /// An Status object indicating the success or failure of the + /// launch. + virtual Status LaunchProcess() = 0; + + virtual FileSpec FindModuleFile(const std::string &module_path, + const ArchSpec &arch); + + // Process client_features (qSupported) and return an array of server features + // to be returned in response. + virtual std::vector<std::string> + HandleFeatures(llvm::ArrayRef<llvm::StringRef> client_features); + +private: + ModuleSpec GetModuleInfo(llvm::StringRef module_path, llvm::StringRef triple); +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVERCOMMON_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp new file mode 100644 index 000000000000..08d5f5039d51 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -0,0 +1,4321 @@ +//===-- GDBRemoteCommunicationServerLLGS.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 <cerrno> + +#include "lldb/Host/Config.h" + +#include <chrono> +#include <cstring> +#include <limits> +#include <optional> +#include <thread> + +#include "GDBRemoteCommunicationServerLLGS.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Debug.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileAction.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/PosixApi.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/Endian.h" +#include "lldb/Utility/GDBRemote.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/UnimplementedError.h" +#include "lldb/Utility/UriParser.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/TargetParser/Triple.h" + +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace llvm; + +// GDBRemote Errors + +namespace { +enum GDBRemoteServerError { + // Set to the first unused error number in literal form below + eErrorFirst = 29, + eErrorNoProcess = eErrorFirst, + eErrorResume, + eErrorExitStatus +}; +} + +// GDBRemoteCommunicationServerLLGS constructor +GDBRemoteCommunicationServerLLGS::GDBRemoteCommunicationServerLLGS( + MainLoop &mainloop, NativeProcessProtocol::Manager &process_manager) + : GDBRemoteCommunicationServerCommon(), m_mainloop(mainloop), + m_process_manager(process_manager), m_current_process(nullptr), + m_continue_process(nullptr), m_stdio_communication() { + RegisterPacketHandlers(); +} + +void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() { + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_C, + &GDBRemoteCommunicationServerLLGS::Handle_C); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_c, + &GDBRemoteCommunicationServerLLGS::Handle_c); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_D, + &GDBRemoteCommunicationServerLLGS::Handle_D); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_H, + &GDBRemoteCommunicationServerLLGS::Handle_H); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_I, + &GDBRemoteCommunicationServerLLGS::Handle_I); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_interrupt, + &GDBRemoteCommunicationServerLLGS::Handle_interrupt); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_m, + &GDBRemoteCommunicationServerLLGS::Handle_memory_read); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_M, + &GDBRemoteCommunicationServerLLGS::Handle_M); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType__M, + &GDBRemoteCommunicationServerLLGS::Handle__M); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType__m, + &GDBRemoteCommunicationServerLLGS::Handle__m); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_p, + &GDBRemoteCommunicationServerLLGS::Handle_p); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_P, + &GDBRemoteCommunicationServerLLGS::Handle_P); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC, + &GDBRemoteCommunicationServerLLGS::Handle_qC); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_T, + &GDBRemoteCommunicationServerLLGS::Handle_T); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qfThreadInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qFileLoadAddress, + &GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, + &GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QThreadSuffixSupported, + &GDBRemoteCommunicationServerLLGS::Handle_QThreadSuffixSupported); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QListThreadsInStopReply, + &GDBRemoteCommunicationServerLLGS::Handle_QListThreadsInStopReply); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qMemoryRegionInfoSupported, + &GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qProcessInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qRegisterInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QRestoreRegisterState, + &GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QSaveRegisterState, + &GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR, + &GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, + &GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qsThreadInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jThreadsInfo, + &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo, + &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qXfer, + &GDBRemoteCommunicationServerLLGS::Handle_qXfer); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_s, + &GDBRemoteCommunicationServerLLGS::Handle_s); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_stop_reason, + &GDBRemoteCommunicationServerLLGS::Handle_stop_reason); // ? + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vAttach, + &GDBRemoteCommunicationServerLLGS::Handle_vAttach); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vAttachWait, + &GDBRemoteCommunicationServerLLGS::Handle_vAttachWait); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qVAttachOrWaitSupported, + &GDBRemoteCommunicationServerLLGS::Handle_qVAttachOrWaitSupported); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vAttachOrWait, + &GDBRemoteCommunicationServerLLGS::Handle_vAttachOrWait); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vCont, + &GDBRemoteCommunicationServerLLGS::Handle_vCont); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vCont_actions, + &GDBRemoteCommunicationServerLLGS::Handle_vCont_actions); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vRun, + &GDBRemoteCommunicationServerLLGS::Handle_vRun); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_x, + &GDBRemoteCommunicationServerLLGS::Handle_memory_read); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_Z, + &GDBRemoteCommunicationServerLLGS::Handle_Z); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z, + &GDBRemoteCommunicationServerLLGS::Handle_z); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QPassSignals, + &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jLLDBTraceSupported, + &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupported); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStart, + &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jLLDBTraceStop, + &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStop); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jLLDBTraceGetState, + &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceGetState); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jLLDBTraceGetBinaryData, + &GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceGetBinaryData); + + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g, + &GDBRemoteCommunicationServerLLGS::Handle_g); + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qMemTags, + &GDBRemoteCommunicationServerLLGS::Handle_qMemTags); + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QMemTags, + &GDBRemoteCommunicationServerLLGS::Handle_QMemTags); + + RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k, + [this](StringExtractorGDBRemote packet, Status &error, + bool &interrupt, bool &quit) { + quit = true; + return this->Handle_k(packet); + }); + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vKill, + &GDBRemoteCommunicationServerLLGS::Handle_vKill); + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qLLDBSaveCore, + &GDBRemoteCommunicationServerLLGS::Handle_qSaveCore); + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QNonStop, + &GDBRemoteCommunicationServerLLGS::Handle_QNonStop); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vStdio, + &GDBRemoteCommunicationServerLLGS::Handle_vStdio); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vStopped, + &GDBRemoteCommunicationServerLLGS::Handle_vStopped); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vCtrlC, + &GDBRemoteCommunicationServerLLGS::Handle_vCtrlC); +} + +void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) { + m_process_launch_info = info; +} + +Status GDBRemoteCommunicationServerLLGS::LaunchProcess() { + Log *log = GetLog(LLDBLog::Process); + + if (!m_process_launch_info.GetArguments().GetArgumentCount()) + return Status("%s: no process command line specified to launch", + __FUNCTION__); + + const bool should_forward_stdio = + m_process_launch_info.GetFileActionForFD(STDIN_FILENO) == nullptr || + m_process_launch_info.GetFileActionForFD(STDOUT_FILENO) == nullptr || + m_process_launch_info.GetFileActionForFD(STDERR_FILENO) == nullptr; + m_process_launch_info.SetLaunchInSeparateProcessGroup(true); + m_process_launch_info.GetFlags().Set(eLaunchFlagDebug); + + if (should_forward_stdio) { + // Temporarily relax the following for Windows until we can take advantage + // of the recently added pty support. This doesn't really affect the use of + // lldb-server on Windows. +#if !defined(_WIN32) + if (llvm::Error Err = m_process_launch_info.SetUpPtyRedirection()) + return Status(std::move(Err)); +#endif + } + + { + std::lock_guard<std::recursive_mutex> guard(m_debugged_process_mutex); + assert(m_debugged_processes.empty() && "lldb-server creating debugged " + "process but one already exists"); + auto process_or = m_process_manager.Launch(m_process_launch_info, *this); + if (!process_or) + return Status(process_or.takeError()); + m_continue_process = m_current_process = process_or->get(); + m_debugged_processes.emplace( + m_current_process->GetID(), + DebuggedProcess{std::move(*process_or), DebuggedProcess::Flag{}}); + } + + SetEnabledExtensions(*m_current_process); + + // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol as + // needed. llgs local-process debugging may specify PTY paths, which will + // make these file actions non-null process launch -i/e/o will also make + // these file actions non-null nullptr means that the traffic is expected to + // flow over gdb-remote protocol + if (should_forward_stdio) { + // nullptr means it's not redirected to file or pty (in case of LLGS local) + // at least one of stdio will be transferred pty<->gdb-remote we need to + // give the pty primary handle to this object to read and/or write + LLDB_LOG(log, + "pid = {0}: setting up stdout/stderr redirection via $O " + "gdb-remote commands", + m_current_process->GetID()); + + // Setup stdout/stderr mapping from inferior to $O + auto terminal_fd = m_current_process->GetTerminalFileDescriptor(); + if (terminal_fd >= 0) { + LLDB_LOGF(log, + "ProcessGDBRemoteCommunicationServerLLGS::%s setting " + "inferior STDIO fd to %d", + __FUNCTION__, terminal_fd); + Status status = SetSTDIOFileDescriptor(terminal_fd); + if (status.Fail()) + return status; + } else { + LLDB_LOGF(log, + "ProcessGDBRemoteCommunicationServerLLGS::%s ignoring " + "inferior STDIO since terminal fd reported as %d", + __FUNCTION__, terminal_fd); + } + } else { + LLDB_LOG(log, + "pid = {0} skipping stdout/stderr redirection via $O: inferior " + "will communicate over client-provided file descriptors", + m_current_process->GetID()); + } + + printf("Launched '%s' as process %" PRIu64 "...\n", + m_process_launch_info.GetArguments().GetArgumentAtIndex(0), + m_current_process->GetID()); + + return Status(); +} + +Status GDBRemoteCommunicationServerLLGS::AttachToProcess(lldb::pid_t pid) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64, + __FUNCTION__, pid); + + // Before we try to attach, make sure we aren't already monitoring something + // else. + if (!m_debugged_processes.empty()) + return Status("cannot attach to process %" PRIu64 + " when another process with pid %" PRIu64 + " is being debugged.", + pid, m_current_process->GetID()); + + // Try to attach. + auto process_or = m_process_manager.Attach(pid, *this); + if (!process_or) { + Status status(process_or.takeError()); + llvm::errs() << llvm::formatv("failed to attach to process {0}: {1}\n", pid, + status); + return status; + } + m_continue_process = m_current_process = process_or->get(); + m_debugged_processes.emplace( + m_current_process->GetID(), + DebuggedProcess{std::move(*process_or), DebuggedProcess::Flag{}}); + SetEnabledExtensions(*m_current_process); + + // Setup stdout/stderr mapping from inferior. + auto terminal_fd = m_current_process->GetTerminalFileDescriptor(); + if (terminal_fd >= 0) { + LLDB_LOGF(log, + "ProcessGDBRemoteCommunicationServerLLGS::%s setting " + "inferior STDIO fd to %d", + __FUNCTION__, terminal_fd); + Status status = SetSTDIOFileDescriptor(terminal_fd); + if (status.Fail()) + return status; + } else { + LLDB_LOGF(log, + "ProcessGDBRemoteCommunicationServerLLGS::%s ignoring " + "inferior STDIO since terminal fd reported as %d", + __FUNCTION__, terminal_fd); + } + + printf("Attached to process %" PRIu64 "...\n", pid); + return Status(); +} + +Status GDBRemoteCommunicationServerLLGS::AttachWaitProcess( + llvm::StringRef process_name, bool include_existing) { + Log *log = GetLog(LLDBLog::Process); + + std::chrono::milliseconds polling_interval = std::chrono::milliseconds(1); + + // Create the matcher used to search the process list. + ProcessInstanceInfoList exclusion_list; + ProcessInstanceInfoMatch match_info; + match_info.GetProcessInfo().GetExecutableFile().SetFile( + process_name, llvm::sys::path::Style::native); + match_info.SetNameMatchType(NameMatch::Equals); + + if (include_existing) { + LLDB_LOG(log, "including existing processes in search"); + } else { + // Create the excluded process list before polling begins. + Host::FindProcesses(match_info, exclusion_list); + LLDB_LOG(log, "placed '{0}' processes in the exclusion list.", + exclusion_list.size()); + } + + LLDB_LOG(log, "waiting for '{0}' to appear", process_name); + + auto is_in_exclusion_list = + [&exclusion_list](const ProcessInstanceInfo &info) { + for (auto &excluded : exclusion_list) { + if (excluded.GetProcessID() == info.GetProcessID()) + return true; + } + return false; + }; + + ProcessInstanceInfoList loop_process_list; + while (true) { + loop_process_list.clear(); + if (Host::FindProcesses(match_info, loop_process_list)) { + // Remove all the elements that are in the exclusion list. + llvm::erase_if(loop_process_list, is_in_exclusion_list); + + // One match! We found the desired process. + if (loop_process_list.size() == 1) { + auto matching_process_pid = loop_process_list[0].GetProcessID(); + LLDB_LOG(log, "found pid {0}", matching_process_pid); + return AttachToProcess(matching_process_pid); + } + + // Multiple matches! Return an error reporting the PIDs we found. + if (loop_process_list.size() > 1) { + StreamString error_stream; + error_stream.Format( + "Multiple executables with name: '{0}' found. Pids: ", + process_name); + for (size_t i = 0; i < loop_process_list.size() - 1; ++i) { + error_stream.Format("{0}, ", loop_process_list[i].GetProcessID()); + } + error_stream.Format("{0}.", loop_process_list.back().GetProcessID()); + + Status error; + error.SetErrorString(error_stream.GetString()); + return error; + } + } + // No matches, we have not found the process. Sleep until next poll. + LLDB_LOG(log, "sleep {0} seconds", polling_interval); + std::this_thread::sleep_for(polling_interval); + } +} + +void GDBRemoteCommunicationServerLLGS::InitializeDelegate( + NativeProcessProtocol *process) { + assert(process && "process cannot be NULL"); + Log *log = GetLog(LLDBLog::Process); + if (log) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s called with " + "NativeProcessProtocol pid %" PRIu64 ", current state: %s", + __FUNCTION__, process->GetID(), + StateAsCString(process->GetState())); + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendWResponse( + NativeProcessProtocol *process) { + assert(process && "process cannot be NULL"); + Log *log = GetLog(LLDBLog::Process); + + // send W notification + auto wait_status = process->GetExitStatus(); + if (!wait_status) { + LLDB_LOG(log, "pid = {0}, failed to retrieve process exit status", + process->GetID()); + + StreamGDBRemote response; + response.PutChar('E'); + response.PutHex8(GDBRemoteServerError::eErrorExitStatus); + return SendPacketNoLock(response.GetString()); + } + + LLDB_LOG(log, "pid = {0}, returning exit type {1}", process->GetID(), + *wait_status); + + // If the process was killed through vKill, return "OK". + if (bool(m_debugged_processes.at(process->GetID()).flags & + DebuggedProcess::Flag::vkilled)) + return SendOKResponse(); + + StreamGDBRemote response; + response.Format("{0:g}", *wait_status); + if (bool(m_extensions_supported & + NativeProcessProtocol::Extension::multiprocess)) + response.Format(";process:{0:x-}", process->GetID()); + if (m_non_stop) + return SendNotificationPacketNoLock("Stop", m_stop_notification_queue, + response.GetString()); + return SendPacketNoLock(response.GetString()); +} + +static void AppendHexValue(StreamString &response, const uint8_t *buf, + uint32_t buf_size, bool swap) { + int64_t i; + if (swap) { + for (i = buf_size - 1; i >= 0; i--) + response.PutHex8(buf[i]); + } else { + for (i = 0; i < buf_size; i++) + response.PutHex8(buf[i]); + } +} + +static llvm::StringRef GetEncodingNameOrEmpty(const RegisterInfo ®_info) { + switch (reg_info.encoding) { + case eEncodingUint: + return "uint"; + case eEncodingSint: + return "sint"; + case eEncodingIEEE754: + return "ieee754"; + case eEncodingVector: + return "vector"; + default: + return ""; + } +} + +static llvm::StringRef GetFormatNameOrEmpty(const RegisterInfo ®_info) { + switch (reg_info.format) { + case eFormatBinary: + return "binary"; + case eFormatDecimal: + return "decimal"; + case eFormatHex: + return "hex"; + case eFormatFloat: + return "float"; + case eFormatVectorOfSInt8: + return "vector-sint8"; + case eFormatVectorOfUInt8: + return "vector-uint8"; + case eFormatVectorOfSInt16: + return "vector-sint16"; + case eFormatVectorOfUInt16: + return "vector-uint16"; + case eFormatVectorOfSInt32: + return "vector-sint32"; + case eFormatVectorOfUInt32: + return "vector-uint32"; + case eFormatVectorOfFloat32: + return "vector-float32"; + case eFormatVectorOfUInt64: + return "vector-uint64"; + case eFormatVectorOfUInt128: + return "vector-uint128"; + default: + return ""; + }; +} + +static llvm::StringRef GetKindGenericOrEmpty(const RegisterInfo ®_info) { + switch (reg_info.kinds[RegisterKind::eRegisterKindGeneric]) { + case LLDB_REGNUM_GENERIC_PC: + return "pc"; + case LLDB_REGNUM_GENERIC_SP: + return "sp"; + case LLDB_REGNUM_GENERIC_FP: + return "fp"; + case LLDB_REGNUM_GENERIC_RA: + return "ra"; + case LLDB_REGNUM_GENERIC_FLAGS: + return "flags"; + case LLDB_REGNUM_GENERIC_ARG1: + return "arg1"; + case LLDB_REGNUM_GENERIC_ARG2: + return "arg2"; + case LLDB_REGNUM_GENERIC_ARG3: + return "arg3"; + case LLDB_REGNUM_GENERIC_ARG4: + return "arg4"; + case LLDB_REGNUM_GENERIC_ARG5: + return "arg5"; + case LLDB_REGNUM_GENERIC_ARG6: + return "arg6"; + case LLDB_REGNUM_GENERIC_ARG7: + return "arg7"; + case LLDB_REGNUM_GENERIC_ARG8: + return "arg8"; + case LLDB_REGNUM_GENERIC_TP: + return "tp"; + default: + return ""; + } +} + +static void CollectRegNums(const uint32_t *reg_num, StreamString &response, + bool usehex) { + for (int i = 0; *reg_num != LLDB_INVALID_REGNUM; ++reg_num, ++i) { + if (i > 0) + response.PutChar(','); + if (usehex) + response.Printf("%" PRIx32, *reg_num); + else + response.Printf("%" PRIu32, *reg_num); + } +} + +static void WriteRegisterValueInHexFixedWidth( + StreamString &response, NativeRegisterContext ®_ctx, + const RegisterInfo ®_info, const RegisterValue *reg_value_p, + lldb::ByteOrder byte_order) { + RegisterValue reg_value; + if (!reg_value_p) { + Status error = reg_ctx.ReadRegister(®_info, reg_value); + if (error.Success()) + reg_value_p = ®_value; + // else log. + } + + if (reg_value_p) { + AppendHexValue(response, (const uint8_t *)reg_value_p->GetBytes(), + reg_value_p->GetByteSize(), + byte_order == lldb::eByteOrderLittle); + } else { + // Zero-out any unreadable values. + if (reg_info.byte_size > 0) { + std::vector<uint8_t> zeros(reg_info.byte_size, '\0'); + AppendHexValue(response, zeros.data(), zeros.size(), false); + } + } +} + +static std::optional<json::Object> +GetRegistersAsJSON(NativeThreadProtocol &thread) { + Log *log = GetLog(LLDBLog::Thread); + + NativeRegisterContext& reg_ctx = thread.GetRegisterContext(); + + json::Object register_object; + +#ifdef LLDB_JTHREADSINFO_FULL_REGISTER_SET + const auto expedited_regs = + reg_ctx.GetExpeditedRegisters(ExpeditedRegs::Full); +#else + const auto expedited_regs = + reg_ctx.GetExpeditedRegisters(ExpeditedRegs::Minimal); +#endif + if (expedited_regs.empty()) + return std::nullopt; + + for (auto ®_num : expedited_regs) { + const RegisterInfo *const reg_info_p = + reg_ctx.GetRegisterInfoAtIndex(reg_num); + if (reg_info_p == nullptr) { + LLDB_LOGF(log, + "%s failed to get register info for register index %" PRIu32, + __FUNCTION__, reg_num); + continue; + } + + if (reg_info_p->value_regs != nullptr) + continue; // Only expedite registers that are not contained in other + // registers. + + RegisterValue reg_value; + Status error = reg_ctx.ReadRegister(reg_info_p, reg_value); + if (error.Fail()) { + LLDB_LOGF(log, "%s failed to read register '%s' index %" PRIu32 ": %s", + __FUNCTION__, + reg_info_p->name ? reg_info_p->name : "<unnamed-register>", + reg_num, error.AsCString()); + continue; + } + + StreamString stream; + WriteRegisterValueInHexFixedWidth(stream, reg_ctx, *reg_info_p, + ®_value, lldb::eByteOrderBig); + + register_object.try_emplace(llvm::to_string(reg_num), + stream.GetString().str()); + } + + return register_object; +} + +static const char *GetStopReasonString(StopReason stop_reason) { + switch (stop_reason) { + case eStopReasonTrace: + return "trace"; + case eStopReasonBreakpoint: + return "breakpoint"; + case eStopReasonWatchpoint: + return "watchpoint"; + case eStopReasonSignal: + return "signal"; + case eStopReasonException: + return "exception"; + case eStopReasonExec: + return "exec"; + case eStopReasonProcessorTrace: + return "processor trace"; + case eStopReasonFork: + return "fork"; + case eStopReasonVFork: + return "vfork"; + case eStopReasonVForkDone: + return "vforkdone"; + case eStopReasonInstrumentation: + case eStopReasonInvalid: + case eStopReasonPlanComplete: + case eStopReasonThreadExiting: + case eStopReasonNone: + break; // ignored + } + return nullptr; +} + +static llvm::Expected<json::Array> +GetJSONThreadsInfo(NativeProcessProtocol &process, bool abridged) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); + + json::Array threads_array; + + // Ensure we can get info on the given thread. + for (NativeThreadProtocol &thread : process.Threads()) { + lldb::tid_t tid = thread.GetID(); + // Grab the reason this thread stopped. + struct ThreadStopInfo tid_stop_info; + std::string description; + if (!thread.GetStopReason(tid_stop_info, description)) + return llvm::make_error<llvm::StringError>( + "failed to get stop reason", llvm::inconvertibleErrorCode()); + + const int signum = tid_stop_info.signo; + if (log) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 + " tid %" PRIu64 + " got signal signo = %d, reason = %d, exc_type = %" PRIu64, + __FUNCTION__, process.GetID(), tid, signum, + tid_stop_info.reason, tid_stop_info.details.exception.type); + } + + json::Object thread_obj; + + if (!abridged) { + if (std::optional<json::Object> registers = GetRegistersAsJSON(thread)) + thread_obj.try_emplace("registers", std::move(*registers)); + } + + thread_obj.try_emplace("tid", static_cast<int64_t>(tid)); + + if (signum != 0) + thread_obj.try_emplace("signal", signum); + + const std::string thread_name = thread.GetName(); + if (!thread_name.empty()) + thread_obj.try_emplace("name", thread_name); + + const char *stop_reason = GetStopReasonString(tid_stop_info.reason); + if (stop_reason) + thread_obj.try_emplace("reason", stop_reason); + + if (!description.empty()) + thread_obj.try_emplace("description", description); + + if ((tid_stop_info.reason == eStopReasonException) && + tid_stop_info.details.exception.type) { + thread_obj.try_emplace( + "metype", static_cast<int64_t>(tid_stop_info.details.exception.type)); + + json::Array medata_array; + for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; + ++i) { + medata_array.push_back( + static_cast<int64_t>(tid_stop_info.details.exception.data[i])); + } + thread_obj.try_emplace("medata", std::move(medata_array)); + } + threads_array.push_back(std::move(thread_obj)); + } + return threads_array; +} + +StreamString +GDBRemoteCommunicationServerLLGS::PrepareStopReplyPacketForThread( + NativeThreadProtocol &thread) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); + + NativeProcessProtocol &process = thread.GetProcess(); + + LLDB_LOG(log, "preparing packet for pid {0} tid {1}", process.GetID(), + thread.GetID()); + + // Grab the reason this thread stopped. + StreamString response; + struct ThreadStopInfo tid_stop_info; + std::string description; + if (!thread.GetStopReason(tid_stop_info, description)) + return response; + + // FIXME implement register handling for exec'd inferiors. + // if (tid_stop_info.reason == eStopReasonExec) { + // const bool force = true; + // InitializeRegisters(force); + // } + + // Output the T packet with the thread + response.PutChar('T'); + int signum = tid_stop_info.signo; + LLDB_LOG( + log, + "pid {0}, tid {1}, got signal signo = {2}, reason = {3}, exc_type = {4}", + process.GetID(), thread.GetID(), signum, int(tid_stop_info.reason), + tid_stop_info.details.exception.type); + + // Print the signal number. + response.PutHex8(signum & 0xff); + + // Include the (pid and) tid. + response.PutCString("thread:"); + AppendThreadIDToResponse(response, process.GetID(), thread.GetID()); + response.PutChar(';'); + + // Include the thread name if there is one. + const std::string thread_name = thread.GetName(); + if (!thread_name.empty()) { + size_t thread_name_len = thread_name.length(); + + if (::strcspn(thread_name.c_str(), "$#+-;:") == thread_name_len) { + response.PutCString("name:"); + response.PutCString(thread_name); + } else { + // The thread name contains special chars, send as hex bytes. + response.PutCString("hexname:"); + response.PutStringAsRawHex8(thread_name); + } + response.PutChar(';'); + } + + // If a 'QListThreadsInStopReply' was sent to enable this feature, we will + // send all thread IDs back in the "threads" key whose value is a list of hex + // thread IDs separated by commas: + // "threads:10a,10b,10c;" + // This will save the debugger from having to send a pair of qfThreadInfo and + // qsThreadInfo packets, but it also might take a lot of room in the stop + // reply packet, so it must be enabled only on systems where there are no + // limits on packet lengths. + if (m_list_threads_in_stop_reply) { + response.PutCString("threads:"); + + uint32_t thread_num = 0; + for (NativeThreadProtocol &listed_thread : process.Threads()) { + if (thread_num > 0) + response.PutChar(','); + response.Printf("%" PRIx64, listed_thread.GetID()); + ++thread_num; + } + response.PutChar(';'); + + // Include JSON info that describes the stop reason for any threads that + // actually have stop reasons. We use the new "jstopinfo" key whose values + // is hex ascii JSON that contains the thread IDs thread stop info only for + // threads that have stop reasons. Only send this if we have more than one + // thread otherwise this packet has all the info it needs. + if (thread_num > 1) { + const bool threads_with_valid_stop_info_only = true; + llvm::Expected<json::Array> threads_info = GetJSONThreadsInfo( + *m_current_process, threads_with_valid_stop_info_only); + if (threads_info) { + response.PutCString("jstopinfo:"); + StreamString unescaped_response; + unescaped_response.AsRawOstream() << std::move(*threads_info); + response.PutStringAsRawHex8(unescaped_response.GetData()); + response.PutChar(';'); + } else { + LLDB_LOG_ERROR(log, threads_info.takeError(), + "failed to prepare a jstopinfo field for pid {1}: {0}", + process.GetID()); + } + } + + response.PutCString("thread-pcs"); + char delimiter = ':'; + for (NativeThreadProtocol &thread : process.Threads()) { + NativeRegisterContext ®_ctx = thread.GetRegisterContext(); + + uint32_t reg_to_read = reg_ctx.ConvertRegisterKindToRegisterNumber( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const RegisterInfo *const reg_info_p = + reg_ctx.GetRegisterInfoAtIndex(reg_to_read); + + RegisterValue reg_value; + Status error = reg_ctx.ReadRegister(reg_info_p, reg_value); + if (error.Fail()) { + LLDB_LOGF(log, "%s failed to read register '%s' index %" PRIu32 ": %s", + __FUNCTION__, + reg_info_p->name ? reg_info_p->name : "<unnamed-register>", + reg_to_read, error.AsCString()); + continue; + } + + response.PutChar(delimiter); + delimiter = ','; + WriteRegisterValueInHexFixedWidth(response, reg_ctx, *reg_info_p, + ®_value, endian::InlHostByteOrder()); + } + + response.PutChar(';'); + } + + // + // Expedite registers. + // + + // Grab the register context. + NativeRegisterContext ®_ctx = thread.GetRegisterContext(); + const auto expedited_regs = + reg_ctx.GetExpeditedRegisters(ExpeditedRegs::Full); + + for (auto ®_num : expedited_regs) { + const RegisterInfo *const reg_info_p = + reg_ctx.GetRegisterInfoAtIndex(reg_num); + // Only expediate registers that are not contained in other registers. + if (reg_info_p != nullptr && reg_info_p->value_regs == nullptr) { + RegisterValue reg_value; + Status error = reg_ctx.ReadRegister(reg_info_p, reg_value); + if (error.Success()) { + response.Printf("%.02x:", reg_num); + WriteRegisterValueInHexFixedWidth(response, reg_ctx, *reg_info_p, + ®_value, lldb::eByteOrderBig); + response.PutChar(';'); + } else { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed to read " + "register '%s' index %" PRIu32 ": %s", + __FUNCTION__, + reg_info_p->name ? reg_info_p->name : "<unnamed-register>", + reg_num, error.AsCString()); + } + } + } + + const char *reason_str = GetStopReasonString(tid_stop_info.reason); + if (reason_str != nullptr) { + response.Printf("reason:%s;", reason_str); + } + + if (!description.empty()) { + // Description may contains special chars, send as hex bytes. + response.PutCString("description:"); + response.PutStringAsRawHex8(description); + response.PutChar(';'); + } else if ((tid_stop_info.reason == eStopReasonException) && + tid_stop_info.details.exception.type) { + response.PutCString("metype:"); + response.PutHex64(tid_stop_info.details.exception.type); + response.PutCString(";mecount:"); + response.PutHex32(tid_stop_info.details.exception.data_count); + response.PutChar(';'); + + for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) { + response.PutCString("medata:"); + response.PutHex64(tid_stop_info.details.exception.data[i]); + response.PutChar(';'); + } + } + + // Include child process PID/TID for forks. + if (tid_stop_info.reason == eStopReasonFork || + tid_stop_info.reason == eStopReasonVFork) { + assert(bool(m_extensions_supported & + NativeProcessProtocol::Extension::multiprocess)); + if (tid_stop_info.reason == eStopReasonFork) + assert(bool(m_extensions_supported & + NativeProcessProtocol::Extension::fork)); + if (tid_stop_info.reason == eStopReasonVFork) + assert(bool(m_extensions_supported & + NativeProcessProtocol::Extension::vfork)); + response.Printf("%s:p%" PRIx64 ".%" PRIx64 ";", reason_str, + tid_stop_info.details.fork.child_pid, + tid_stop_info.details.fork.child_tid); + } + + return response; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread( + NativeProcessProtocol &process, lldb::tid_t tid, bool force_synchronous) { + // Ensure we can get info on the given thread. + NativeThreadProtocol *thread = process.GetThreadByID(tid); + if (!thread) + return SendErrorResponse(51); + + StreamString response = PrepareStopReplyPacketForThread(*thread); + if (response.Empty()) + return SendErrorResponse(42); + + if (m_non_stop && !force_synchronous) { + PacketResult ret = SendNotificationPacketNoLock( + "Stop", m_stop_notification_queue, response.GetString()); + // Queue notification events for the remaining threads. + EnqueueStopReplyPackets(tid); + return ret; + } + + return SendPacketNoLock(response.GetString()); +} + +void GDBRemoteCommunicationServerLLGS::EnqueueStopReplyPackets( + lldb::tid_t thread_to_skip) { + if (!m_non_stop) + return; + + for (NativeThreadProtocol &listed_thread : m_current_process->Threads()) { + if (listed_thread.GetID() != thread_to_skip) { + StreamString stop_reply = PrepareStopReplyPacketForThread(listed_thread); + if (!stop_reply.Empty()) + m_stop_notification_queue.push_back(stop_reply.GetString().str()); + } + } +} + +void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited( + NativeProcessProtocol *process) { + assert(process && "process cannot be NULL"); + + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + PacketResult result = SendStopReasonForState( + *process, StateType::eStateExited, /*force_synchronous=*/false); + if (result != PacketResult::Success) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed to send stop " + "notification for PID %" PRIu64 ", state: eStateExited", + __FUNCTION__, process->GetID()); + } + + if (m_current_process == process) + m_current_process = nullptr; + if (m_continue_process == process) + m_continue_process = nullptr; + + lldb::pid_t pid = process->GetID(); + m_mainloop.AddPendingCallback([this, pid](MainLoopBase &loop) { + auto find_it = m_debugged_processes.find(pid); + assert(find_it != m_debugged_processes.end()); + bool vkilled = bool(find_it->second.flags & DebuggedProcess::Flag::vkilled); + m_debugged_processes.erase(find_it); + // Terminate the main loop only if vKill has not been used. + // When running in non-stop mode, wait for the vStopped to clear + // the notification queue. + if (m_debugged_processes.empty() && !m_non_stop && !vkilled) { + // Close the pipe to the inferior terminal i/o if we launched it and set + // one up. + MaybeCloseInferiorTerminalConnection(); + + // We are ready to exit the debug monitor. + m_exit_now = true; + loop.RequestTermination(); + } + }); +} + +void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped( + NativeProcessProtocol *process) { + assert(process && "process cannot be NULL"); + + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + PacketResult result = SendStopReasonForState( + *process, StateType::eStateStopped, /*force_synchronous=*/false); + if (result != PacketResult::Success) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed to send stop " + "notification for PID %" PRIu64 ", state: eStateExited", + __FUNCTION__, process->GetID()); + } +} + +void GDBRemoteCommunicationServerLLGS::ProcessStateChanged( + NativeProcessProtocol *process, lldb::StateType state) { + assert(process && "process cannot be NULL"); + Log *log = GetLog(LLDBLog::Process); + if (log) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s called with " + "NativeProcessProtocol pid %" PRIu64 ", state: %s", + __FUNCTION__, process->GetID(), StateAsCString(state)); + } + + switch (state) { + case StateType::eStateRunning: + break; + + case StateType::eStateStopped: + // Make sure we get all of the pending stdout/stderr from the inferior and + // send it to the lldb host before we send the state change notification + SendProcessOutput(); + // Then stop the forwarding, so that any late output (see llvm.org/pr25652) + // does not interfere with our protocol. + if (!m_non_stop) + StopSTDIOForwarding(); + HandleInferiorState_Stopped(process); + break; + + case StateType::eStateExited: + // Same as above + SendProcessOutput(); + if (!m_non_stop) + StopSTDIOForwarding(); + HandleInferiorState_Exited(process); + break; + + default: + if (log) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s didn't handle state " + "change for pid %" PRIu64 ", new state: %s", + __FUNCTION__, process->GetID(), StateAsCString(state)); + } + break; + } +} + +void GDBRemoteCommunicationServerLLGS::DidExec(NativeProcessProtocol *process) { + ClearProcessSpecificData(); +} + +void GDBRemoteCommunicationServerLLGS::NewSubprocess( + NativeProcessProtocol *parent_process, + std::unique_ptr<NativeProcessProtocol> child_process) { + lldb::pid_t child_pid = child_process->GetID(); + assert(child_pid != LLDB_INVALID_PROCESS_ID); + assert(m_debugged_processes.find(child_pid) == m_debugged_processes.end()); + m_debugged_processes.emplace( + child_pid, + DebuggedProcess{std::move(child_process), DebuggedProcess::Flag{}}); +} + +void GDBRemoteCommunicationServerLLGS::DataAvailableCallback() { + Log *log = GetLog(GDBRLog::Comm); + + bool interrupt = false; + bool done = false; + Status error; + while (true) { + const PacketResult result = GetPacketAndSendResponse( + std::chrono::microseconds(0), error, interrupt, done); + if (result == PacketResult::ErrorReplyTimeout) + break; // No more packets in the queue + + if ((result != PacketResult::Success)) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s processing a packet " + "failed: %s", + __FUNCTION__, error.AsCString()); + m_mainloop.RequestTermination(); + break; + } + } +} + +Status GDBRemoteCommunicationServerLLGS::InitializeConnection( + std::unique_ptr<Connection> connection) { + IOObjectSP read_object_sp = connection->GetReadObject(); + GDBRemoteCommunicationServer::SetConnection(std::move(connection)); + + Status error; + m_network_handle_up = m_mainloop.RegisterReadObject( + read_object_sp, [this](MainLoopBase &) { DataAvailableCallback(); }, + error); + return error; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendONotification(const char *buffer, + uint32_t len) { + if ((buffer == nullptr) || (len == 0)) { + // Nothing to send. + return PacketResult::Success; + } + + StreamString response; + response.PutChar('O'); + response.PutBytesAsRawHex8(buffer, len); + + if (m_non_stop) + return SendNotificationPacketNoLock("Stdio", m_stdio_notification_queue, + response.GetString()); + return SendPacketNoLock(response.GetString()); +} + +Status GDBRemoteCommunicationServerLLGS::SetSTDIOFileDescriptor(int fd) { + Status error; + + // Set up the reading/handling of process I/O + std::unique_ptr<ConnectionFileDescriptor> conn_up( + new ConnectionFileDescriptor(fd, true)); + if (!conn_up) { + error.SetErrorString("failed to create ConnectionFileDescriptor"); + return error; + } + + m_stdio_communication.SetCloseOnEOF(false); + m_stdio_communication.SetConnection(std::move(conn_up)); + if (!m_stdio_communication.IsConnected()) { + error.SetErrorString( + "failed to set connection for inferior I/O communication"); + return error; + } + + return Status(); +} + +void GDBRemoteCommunicationServerLLGS::StartSTDIOForwarding() { + // Don't forward if not connected (e.g. when attaching). + if (!m_stdio_communication.IsConnected()) + return; + + Status error; + assert(!m_stdio_handle_up); + m_stdio_handle_up = m_mainloop.RegisterReadObject( + m_stdio_communication.GetConnection()->GetReadObject(), + [this](MainLoopBase &) { SendProcessOutput(); }, error); + + if (!m_stdio_handle_up) { + // Not much we can do about the failure. Log it and continue without + // forwarding. + if (Log *log = GetLog(LLDBLog::Process)) + LLDB_LOG(log, "Failed to set up stdio forwarding: {0}", error); + } +} + +void GDBRemoteCommunicationServerLLGS::StopSTDIOForwarding() { + m_stdio_handle_up.reset(); +} + +void GDBRemoteCommunicationServerLLGS::SendProcessOutput() { + char buffer[1024]; + ConnectionStatus status; + Status error; + while (true) { + size_t bytes_read = m_stdio_communication.Read( + buffer, sizeof buffer, std::chrono::microseconds(0), status, &error); + switch (status) { + case eConnectionStatusSuccess: + SendONotification(buffer, bytes_read); + break; + case eConnectionStatusLostConnection: + case eConnectionStatusEndOfFile: + case eConnectionStatusError: + case eConnectionStatusNoConnection: + if (Log *log = GetLog(LLDBLog::Process)) + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s Stopping stdio " + "forwarding as communication returned status %d (error: " + "%s)", + __FUNCTION__, status, error.AsCString()); + m_stdio_handle_up.reset(); + return; + + case eConnectionStatusInterrupted: + case eConnectionStatusTimedOut: + return; + } + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceSupported( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(Status("Process not running.")); + + return SendJSONResponse(m_current_process->TraceSupported()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStop( + StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(Status("Process not running.")); + + packet.ConsumeFront("jLLDBTraceStop:"); + Expected<TraceStopRequest> stop_request = + json::parse<TraceStopRequest>(packet.Peek(), "TraceStopRequest"); + if (!stop_request) + return SendErrorResponse(stop_request.takeError()); + + if (Error err = m_current_process->TraceStop(*stop_request)) + return SendErrorResponse(std::move(err)); + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceStart( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(Status("Process not running.")); + + packet.ConsumeFront("jLLDBTraceStart:"); + Expected<TraceStartRequest> request = + json::parse<TraceStartRequest>(packet.Peek(), "TraceStartRequest"); + if (!request) + return SendErrorResponse(request.takeError()); + + if (Error err = m_current_process->TraceStart(packet.Peek(), request->type)) + return SendErrorResponse(std::move(err)); + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceGetState( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(Status("Process not running.")); + + packet.ConsumeFront("jLLDBTraceGetState:"); + Expected<TraceGetStateRequest> request = + json::parse<TraceGetStateRequest>(packet.Peek(), "TraceGetStateRequest"); + if (!request) + return SendErrorResponse(request.takeError()); + + return SendJSONResponse(m_current_process->TraceGetState(request->type)); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jLLDBTraceGetBinaryData( + StringExtractorGDBRemote &packet) { + + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(Status("Process not running.")); + + packet.ConsumeFront("jLLDBTraceGetBinaryData:"); + llvm::Expected<TraceGetBinaryDataRequest> request = + llvm::json::parse<TraceGetBinaryDataRequest>(packet.Peek(), + "TraceGetBinaryDataRequest"); + if (!request) + return SendErrorResponse(Status(request.takeError())); + + if (Expected<std::vector<uint8_t>> bytes = + m_current_process->TraceGetBinaryData(*request)) { + StreamGDBRemote response; + response.PutEscapedBytes(bytes->data(), bytes->size()); + return SendPacketNoLock(response.GetString()); + } else + return SendErrorResponse(bytes.takeError()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qProcessInfo( + StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + lldb::pid_t pid = m_current_process->GetID(); + + if (pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(1); + + ProcessInstanceInfo proc_info; + if (!Host::GetProcessInfo(pid, proc_info)) + return SendErrorResponse(1); + + StreamString response; + CreateProcessInfoResponse_DebugServerStyle(proc_info, response); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qC(StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + // Make sure we set the current thread so g and p packets return the data the + // gdb will expect. + lldb::tid_t tid = m_current_process->GetCurrentThreadID(); + SetCurrentThreadID(tid); + + NativeThreadProtocol *thread = m_current_process->GetCurrentThread(); + if (!thread) + return SendErrorResponse(69); + + StreamString response; + response.PutCString("QC"); + AppendThreadIDToResponse(response, m_current_process->GetID(), + thread->GetID()); + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_k(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + if (!m_non_stop) + StopSTDIOForwarding(); + + if (m_debugged_processes.empty()) { + LLDB_LOG(log, "No debugged process found."); + return PacketResult::Success; + } + + for (auto it = m_debugged_processes.begin(); it != m_debugged_processes.end(); + ++it) { + LLDB_LOG(log, "Killing process {0}", it->first); + Status error = it->second.process_up->Kill(); + if (error.Fail()) + LLDB_LOG(log, "Failed to kill debugged process {0}: {1}", it->first, + error); + } + + // The response to kill packet is undefined per the spec. LLDB + // follows the same rules as for continue packets, i.e. no response + // in all-stop mode, and "OK" in non-stop mode; in both cases this + // is followed by the actual stop reason. + return SendContinueSuccessResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vKill( + StringExtractorGDBRemote &packet) { + if (!m_non_stop) + StopSTDIOForwarding(); + + packet.SetFilePos(6); // vKill; + uint32_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); + if (pid == LLDB_INVALID_PROCESS_ID) + return SendIllFormedResponse(packet, + "vKill failed to parse the process id"); + + auto it = m_debugged_processes.find(pid); + if (it == m_debugged_processes.end()) + return SendErrorResponse(42); + + Status error = it->second.process_up->Kill(); + if (error.Fail()) + return SendErrorResponse(error.ToError()); + + // OK response is sent when the process dies. + it->second.flags |= DebuggedProcess::Flag::vkilled; + return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QSetDisableASLR:")); + if (packet.GetU32(0)) + m_process_launch_info.GetFlags().Set(eLaunchFlagDisableASLR); + else + m_process_launch_info.GetFlags().Clear(eLaunchFlagDisableASLR); + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QSetWorkingDir( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QSetWorkingDir:")); + std::string path; + packet.GetHexByteString(path); + m_process_launch_info.SetWorkingDirectory(FileSpec(path)); + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qGetWorkingDir( + StringExtractorGDBRemote &packet) { + FileSpec working_dir{m_process_launch_info.GetWorkingDirectory()}; + if (working_dir) { + StreamString response; + response.PutStringAsRawHex8(working_dir.GetPath().c_str()); + return SendPacketNoLock(response.GetString()); + } + + return SendErrorResponse(14); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QThreadSuffixSupported( + StringExtractorGDBRemote &packet) { + m_thread_suffix_supported = true; + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QListThreadsInStopReply( + StringExtractorGDBRemote &packet) { + m_list_threads_in_stop_reply = true; + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::ResumeProcess( + NativeProcessProtocol &process, const ResumeActionList &actions) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); + + // In non-stop protocol mode, the process could be running already. + // We do not support resuming threads independently, so just error out. + if (!process.CanResume()) { + LLDB_LOG(log, "process {0} cannot be resumed (state={1})", process.GetID(), + process.GetState()); + return SendErrorResponse(0x37); + } + + Status error = process.Resume(actions); + if (error.Fail()) { + LLDB_LOG(log, "process {0} failed to resume: {1}", process.GetID(), error); + return SendErrorResponse(GDBRemoteServerError::eErrorResume); + } + + LLDB_LOG(log, "process {0} resumed", process.GetID()); + + return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_C(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); + LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + // Ensure we have a native process. + if (!m_continue_process) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s no debugged process " + "shared pointer", + __FUNCTION__); + return SendErrorResponse(0x36); + } + + // Pull out the signal number. + packet.SetFilePos(::strlen("C")); + if (packet.GetBytesLeft() < 1) { + // Shouldn't be using a C without a signal. + return SendIllFormedResponse(packet, "C packet specified without signal."); + } + const uint32_t signo = + packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); + if (signo == std::numeric_limits<uint32_t>::max()) + return SendIllFormedResponse(packet, "failed to parse signal number"); + + // Handle optional continue address. + if (packet.GetBytesLeft() > 0) { + // FIXME add continue at address support for $C{signo}[;{continue-address}]. + if (*packet.Peek() == ';') + return SendUnimplementedResponse(packet.GetStringRef().data()); + else + return SendIllFormedResponse( + packet, "unexpected content after $C{signal-number}"); + } + + // In non-stop protocol mode, the process could be running already. + // We do not support resuming threads independently, so just error out. + if (!m_continue_process->CanResume()) { + LLDB_LOG(log, "process cannot be resumed (state={0})", + m_continue_process->GetState()); + return SendErrorResponse(0x37); + } + + ResumeActionList resume_actions(StateType::eStateRunning, + LLDB_INVALID_SIGNAL_NUMBER); + Status error; + + // We have two branches: what to do if a continue thread is specified (in + // which case we target sending the signal to that thread), or when we don't + // have a continue thread set (in which case we send a signal to the + // process). + + // TODO discuss with Greg Clayton, make sure this makes sense. + + lldb::tid_t signal_tid = GetContinueThreadID(); + if (signal_tid != LLDB_INVALID_THREAD_ID) { + // The resume action for the continue thread (or all threads if a continue + // thread is not set). + ResumeAction action = {GetContinueThreadID(), StateType::eStateRunning, + static_cast<int>(signo)}; + + // Add the action for the continue thread (or all threads when the continue + // thread isn't present). + resume_actions.Append(action); + } else { + // Send the signal to the process since we weren't targeting a specific + // continue thread with the signal. + error = m_continue_process->Signal(signo); + if (error.Fail()) { + LLDB_LOG(log, "failed to send signal for process {0}: {1}", + m_continue_process->GetID(), error); + + return SendErrorResponse(0x52); + } + } + + // NB: this checks CanResume() twice but using a single code path for + // resuming still seems worth it. + PacketResult resume_res = ResumeProcess(*m_continue_process, resume_actions); + if (resume_res != PacketResult::Success) + return resume_res; + + // Don't send an "OK" packet, except in non-stop mode; + // otherwise, the response is the stopped/exited message. + return SendContinueSuccessResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_c(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); + LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); + + packet.SetFilePos(packet.GetFilePos() + ::strlen("c")); + + // For now just support all continue. + const bool has_continue_address = (packet.GetBytesLeft() > 0); + if (has_continue_address) { + LLDB_LOG(log, "not implemented for c[address] variant [{0} remains]", + packet.Peek()); + return SendUnimplementedResponse(packet.GetStringRef().data()); + } + + // Ensure we have a native process. + if (!m_continue_process) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s no debugged process " + "shared pointer", + __FUNCTION__); + return SendErrorResponse(0x36); + } + + // Build the ResumeActionList + ResumeActionList actions(StateType::eStateRunning, + LLDB_INVALID_SIGNAL_NUMBER); + + PacketResult resume_res = ResumeProcess(*m_continue_process, actions); + if (resume_res != PacketResult::Success) + return resume_res; + + return SendContinueSuccessResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vCont_actions( + StringExtractorGDBRemote &packet) { + StreamString response; + response.Printf("vCont;c;C;s;S;t"); + + return SendPacketNoLock(response.GetString()); +} + +static bool ResumeActionListStopsAllThreads(ResumeActionList &actions) { + // We're doing a stop-all if and only if our only action is a "t" for all + // threads. + if (const ResumeAction *default_action = + actions.GetActionForThread(LLDB_INVALID_THREAD_ID, false)) { + if (default_action->state == eStateSuspended && actions.GetSize() == 1) + return true; + } + + return false; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vCont( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s handling vCont packet", + __FUNCTION__); + + packet.SetFilePos(::strlen("vCont")); + + if (packet.GetBytesLeft() == 0) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s missing action from " + "vCont package", + __FUNCTION__); + return SendIllFormedResponse(packet, "Missing action from vCont package"); + } + + if (::strcmp(packet.Peek(), ";s") == 0) { + // Move past the ';', then do a simple 's'. + packet.SetFilePos(packet.GetFilePos() + 1); + return Handle_s(packet); + } + + std::unordered_map<lldb::pid_t, ResumeActionList> thread_actions; + + while (packet.GetBytesLeft() && *packet.Peek() == ';') { + // Skip the semi-colon. + packet.GetChar(); + + // Build up the thread action. + ResumeAction thread_action; + thread_action.tid = LLDB_INVALID_THREAD_ID; + thread_action.state = eStateInvalid; + thread_action.signal = LLDB_INVALID_SIGNAL_NUMBER; + + const char action = packet.GetChar(); + switch (action) { + case 'C': + thread_action.signal = packet.GetHexMaxU32(false, 0); + if (thread_action.signal == 0) + return SendIllFormedResponse( + packet, "Could not parse signal in vCont packet C action"); + [[fallthrough]]; + + case 'c': + // Continue + thread_action.state = eStateRunning; + break; + + case 'S': + thread_action.signal = packet.GetHexMaxU32(false, 0); + if (thread_action.signal == 0) + return SendIllFormedResponse( + packet, "Could not parse signal in vCont packet S action"); + [[fallthrough]]; + + case 's': + // Step + thread_action.state = eStateStepping; + break; + + case 't': + // Stop + thread_action.state = eStateSuspended; + break; + + default: + return SendIllFormedResponse(packet, "Unsupported vCont action"); + break; + } + + // If there's no thread-id (e.g. "vCont;c"), it's "p-1.-1". + lldb::pid_t pid = StringExtractorGDBRemote::AllProcesses; + lldb::tid_t tid = StringExtractorGDBRemote::AllThreads; + + // Parse out optional :{thread-id} value. + if (packet.GetBytesLeft() && (*packet.Peek() == ':')) { + // Consume the separator. + packet.GetChar(); + + auto pid_tid = packet.GetPidTid(LLDB_INVALID_PROCESS_ID); + if (!pid_tid) + return SendIllFormedResponse(packet, "Malformed thread-id"); + + pid = pid_tid->first; + tid = pid_tid->second; + } + + if (thread_action.state == eStateSuspended && + tid != StringExtractorGDBRemote::AllThreads) { + return SendIllFormedResponse( + packet, "'t' action not supported for individual threads"); + } + + // If we get TID without PID, it's the current process. + if (pid == LLDB_INVALID_PROCESS_ID) { + if (!m_continue_process) { + LLDB_LOG(log, "no process selected via Hc"); + return SendErrorResponse(0x36); + } + pid = m_continue_process->GetID(); + } + + assert(pid != LLDB_INVALID_PROCESS_ID); + if (tid == StringExtractorGDBRemote::AllThreads) + tid = LLDB_INVALID_THREAD_ID; + thread_action.tid = tid; + + if (pid == StringExtractorGDBRemote::AllProcesses) { + if (tid != LLDB_INVALID_THREAD_ID) + return SendIllFormedResponse( + packet, "vCont: p-1 is not valid with a specific tid"); + for (auto &process_it : m_debugged_processes) + thread_actions[process_it.first].Append(thread_action); + } else + thread_actions[pid].Append(thread_action); + } + + assert(thread_actions.size() >= 1); + if (thread_actions.size() > 1 && !m_non_stop) + return SendIllFormedResponse( + packet, + "Resuming multiple processes is supported in non-stop mode only"); + + for (std::pair<lldb::pid_t, ResumeActionList> x : thread_actions) { + auto process_it = m_debugged_processes.find(x.first); + if (process_it == m_debugged_processes.end()) { + LLDB_LOG(log, "vCont failed for process {0}: process not debugged", + x.first); + return SendErrorResponse(GDBRemoteServerError::eErrorResume); + } + + // There are four possible scenarios here. These are: + // 1. vCont on a stopped process that resumes at least one thread. + // In this case, we call Resume(). + // 2. vCont on a stopped process that leaves all threads suspended. + // A no-op. + // 3. vCont on a running process that requests suspending all + // running threads. In this case, we call Interrupt(). + // 4. vCont on a running process that requests suspending a subset + // of running threads or resuming a subset of suspended threads. + // Since we do not support full nonstop mode, this is unsupported + // and we return an error. + + assert(process_it->second.process_up); + if (ResumeActionListStopsAllThreads(x.second)) { + if (process_it->second.process_up->IsRunning()) { + assert(m_non_stop); + + Status error = process_it->second.process_up->Interrupt(); + if (error.Fail()) { + LLDB_LOG(log, "vCont failed to halt process {0}: {1}", x.first, + error); + return SendErrorResponse(GDBRemoteServerError::eErrorResume); + } + + LLDB_LOG(log, "halted process {0}", x.first); + + // hack to avoid enabling stdio forwarding after stop + // TODO: remove this when we improve stdio forwarding for nonstop + assert(thread_actions.size() == 1); + return SendOKResponse(); + } + } else { + PacketResult resume_res = + ResumeProcess(*process_it->second.process_up, x.second); + if (resume_res != PacketResult::Success) + return resume_res; + } + } + + return SendContinueSuccessResponse(); +} + +void GDBRemoteCommunicationServerLLGS::SetCurrentThreadID(lldb::tid_t tid) { + Log *log = GetLog(LLDBLog::Thread); + LLDB_LOG(log, "setting current thread id to {0}", tid); + + m_current_tid = tid; + if (m_current_process) + m_current_process->SetCurrentThreadID(m_current_tid); +} + +void GDBRemoteCommunicationServerLLGS::SetContinueThreadID(lldb::tid_t tid) { + Log *log = GetLog(LLDBLog::Thread); + LLDB_LOG(log, "setting continue thread id to {0}", tid); + + m_continue_tid = tid; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_stop_reason( + StringExtractorGDBRemote &packet) { + // Handle the $? gdbremote command. + + if (m_non_stop) { + // Clear the notification queue first, except for pending exit + // notifications. + llvm::erase_if(m_stop_notification_queue, [](const std::string &x) { + return x.front() != 'W' && x.front() != 'X'; + }); + + if (m_current_process) { + // Queue stop reply packets for all active threads. Start with + // the current thread (for clients that don't actually support multiple + // stop reasons). + NativeThreadProtocol *thread = m_current_process->GetCurrentThread(); + if (thread) { + StreamString stop_reply = PrepareStopReplyPacketForThread(*thread); + if (!stop_reply.Empty()) + m_stop_notification_queue.push_back(stop_reply.GetString().str()); + } + EnqueueStopReplyPackets(thread ? thread->GetID() + : LLDB_INVALID_THREAD_ID); + } + + // If the notification queue is empty (i.e. everything is running), send OK. + if (m_stop_notification_queue.empty()) + return SendOKResponse(); + + // Send the first item from the new notification queue synchronously. + return SendPacketNoLock(m_stop_notification_queue.front()); + } + + // If no process, indicate error + if (!m_current_process) + return SendErrorResponse(02); + + return SendStopReasonForState(*m_current_process, + m_current_process->GetState(), + /*force_synchronous=*/true); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendStopReasonForState( + NativeProcessProtocol &process, lldb::StateType process_state, + bool force_synchronous) { + Log *log = GetLog(LLDBLog::Process); + + if (m_disabling_non_stop) { + // Check if we are waiting for any more processes to stop. If we are, + // do not send the OK response yet. + for (const auto &it : m_debugged_processes) { + if (it.second.process_up->IsRunning()) + return PacketResult::Success; + } + + // If all expected processes were stopped after a QNonStop:0 request, + // send the OK response. + m_disabling_non_stop = false; + return SendOKResponse(); + } + + switch (process_state) { + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + // NOTE: gdb protocol doc looks like it should return $OK + // when everything is running (i.e. no stopped result). + return PacketResult::Success; // Ignore + + case eStateSuspended: + case eStateStopped: + case eStateCrashed: { + lldb::tid_t tid = process.GetCurrentThreadID(); + // Make sure we set the current thread so g and p packets return the data + // the gdb will expect. + SetCurrentThreadID(tid); + return SendStopReplyPacketForThread(process, tid, force_synchronous); + } + + case eStateInvalid: + case eStateUnloaded: + case eStateExited: + return SendWResponse(&process); + + default: + LLDB_LOG(log, "pid {0}, current state reporting not handled: {1}", + process.GetID(), process_state); + break; + } + + return SendErrorResponse(0); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qRegisterInfo( + StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(68); + + // Ensure we have a thread. + NativeThreadProtocol *thread = m_current_process->GetThreadAtIndex(0); + if (!thread) + return SendErrorResponse(69); + + // Get the register context for the first thread. + NativeRegisterContext ®_context = thread->GetRegisterContext(); + + // Parse out the register number from the request. + packet.SetFilePos(strlen("qRegisterInfo")); + const uint32_t reg_index = + packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); + if (reg_index == std::numeric_limits<uint32_t>::max()) + return SendErrorResponse(69); + + // Return the end of registers response if we've iterated one past the end of + // the register set. + if (reg_index >= reg_context.GetUserRegisterCount()) + return SendErrorResponse(69); + + const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index); + if (!reg_info) + return SendErrorResponse(69); + + // Build the reginfos response. + StreamGDBRemote response; + + response.PutCString("name:"); + response.PutCString(reg_info->name); + response.PutChar(';'); + + if (reg_info->alt_name && reg_info->alt_name[0]) { + response.PutCString("alt-name:"); + response.PutCString(reg_info->alt_name); + response.PutChar(';'); + } + + response.Printf("bitsize:%" PRIu32 ";", reg_info->byte_size * 8); + + if (!reg_context.RegisterOffsetIsDynamic()) + response.Printf("offset:%" PRIu32 ";", reg_info->byte_offset); + + llvm::StringRef encoding = GetEncodingNameOrEmpty(*reg_info); + if (!encoding.empty()) + response << "encoding:" << encoding << ';'; + + llvm::StringRef format = GetFormatNameOrEmpty(*reg_info); + if (!format.empty()) + response << "format:" << format << ';'; + + const char *const register_set_name = + reg_context.GetRegisterSetNameForRegisterAtIndex(reg_index); + if (register_set_name) + response << "set:" << register_set_name << ';'; + + if (reg_info->kinds[RegisterKind::eRegisterKindEHFrame] != + LLDB_INVALID_REGNUM) + response.Printf("ehframe:%" PRIu32 ";", + reg_info->kinds[RegisterKind::eRegisterKindEHFrame]); + + if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != LLDB_INVALID_REGNUM) + response.Printf("dwarf:%" PRIu32 ";", + reg_info->kinds[RegisterKind::eRegisterKindDWARF]); + + llvm::StringRef kind_generic = GetKindGenericOrEmpty(*reg_info); + if (!kind_generic.empty()) + response << "generic:" << kind_generic << ';'; + + if (reg_info->value_regs && reg_info->value_regs[0] != LLDB_INVALID_REGNUM) { + response.PutCString("container-regs:"); + CollectRegNums(reg_info->value_regs, response, true); + response.PutChar(';'); + } + + if (reg_info->invalidate_regs && reg_info->invalidate_regs[0]) { + response.PutCString("invalidate-regs:"); + CollectRegNums(reg_info->invalidate_regs, response, true); + response.PutChar(';'); + } + + return SendPacketNoLock(response.GetString()); +} + +void GDBRemoteCommunicationServerLLGS::AddProcessThreads( + StreamGDBRemote &response, NativeProcessProtocol &process, bool &had_any) { + Log *log = GetLog(LLDBLog::Thread); + + lldb::pid_t pid = process.GetID(); + if (pid == LLDB_INVALID_PROCESS_ID) + return; + + LLDB_LOG(log, "iterating over threads of process {0}", process.GetID()); + for (NativeThreadProtocol &thread : process.Threads()) { + LLDB_LOG(log, "iterated thread tid={0}", thread.GetID()); + response.PutChar(had_any ? ',' : 'm'); + AppendThreadIDToResponse(response, pid, thread.GetID()); + had_any = true; + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo( + StringExtractorGDBRemote &packet) { + assert(m_debugged_processes.size() <= 1 || + bool(m_extensions_supported & + NativeProcessProtocol::Extension::multiprocess)); + + bool had_any = false; + StreamGDBRemote response; + + for (auto &pid_ptr : m_debugged_processes) + AddProcessThreads(response, *pid_ptr.second.process_up, had_any); + + if (!had_any) + return SendOKResponse(); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo( + StringExtractorGDBRemote &packet) { + // FIXME for now we return the full thread list in the initial packet and + // always do nothing here. + return SendPacketNoLock("l"); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_g(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Thread); + + // Move past packet name. + packet.SetFilePos(strlen("g")); + + // Get the thread to use. + NativeThreadProtocol *thread = GetThreadFromSuffix(packet); + if (!thread) { + LLDB_LOG(log, "failed, no thread available"); + return SendErrorResponse(0x15); + } + + // Get the thread's register context. + NativeRegisterContext ®_ctx = thread->GetRegisterContext(); + + std::vector<uint8_t> regs_buffer; + for (uint32_t reg_num = 0; reg_num < reg_ctx.GetUserRegisterCount(); + ++reg_num) { + const RegisterInfo *reg_info = reg_ctx.GetRegisterInfoAtIndex(reg_num); + + if (reg_info == nullptr) { + LLDB_LOG(log, "failed to get register info for register index {0}", + reg_num); + return SendErrorResponse(0x15); + } + + if (reg_info->value_regs != nullptr) + continue; // skip registers that are contained in other registers + + RegisterValue reg_value; + Status error = reg_ctx.ReadRegister(reg_info, reg_value); + if (error.Fail()) { + LLDB_LOG(log, "failed to read register at index {0}", reg_num); + return SendErrorResponse(0x15); + } + + if (reg_info->byte_offset + reg_info->byte_size >= regs_buffer.size()) + // Resize the buffer to guarantee it can store the register offsetted + // data. + regs_buffer.resize(reg_info->byte_offset + reg_info->byte_size); + + // Copy the register offsetted data to the buffer. + memcpy(regs_buffer.data() + reg_info->byte_offset, reg_value.GetBytes(), + reg_info->byte_size); + } + + // Write the response. + StreamGDBRemote response; + response.PutBytesAsRawHex8(regs_buffer.data(), regs_buffer.size()); + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_p(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Thread); + + // Parse out the register number from the request. + packet.SetFilePos(strlen("p")); + const uint32_t reg_index = + packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); + if (reg_index == std::numeric_limits<uint32_t>::max()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, could not " + "parse register number from request \"%s\"", + __FUNCTION__, packet.GetStringRef().data()); + return SendErrorResponse(0x15); + } + + // Get the thread to use. + NativeThreadProtocol *thread = GetThreadFromSuffix(packet); + if (!thread) { + LLDB_LOG(log, "failed, no thread available"); + return SendErrorResponse(0x15); + } + + // Get the thread's register context. + NativeRegisterContext ®_context = thread->GetRegisterContext(); + + // Return the end of registers response if we've iterated one past the end of + // the register set. + if (reg_index >= reg_context.GetUserRegisterCount()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, requested " + "register %" PRIu32 " beyond register count %" PRIu32, + __FUNCTION__, reg_index, reg_context.GetUserRegisterCount()); + return SendErrorResponse(0x15); + } + + const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index); + if (!reg_info) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, requested " + "register %" PRIu32 " returned NULL", + __FUNCTION__, reg_index); + return SendErrorResponse(0x15); + } + + // Build the reginfos response. + StreamGDBRemote response; + + // Retrieve the value + RegisterValue reg_value; + Status error = reg_context.ReadRegister(reg_info, reg_value); + if (error.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, read of " + "requested register %" PRIu32 " (%s) failed: %s", + __FUNCTION__, reg_index, reg_info->name, error.AsCString()); + return SendErrorResponse(0x15); + } + + const uint8_t *const data = + static_cast<const uint8_t *>(reg_value.GetBytes()); + if (!data) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed to get data " + "bytes from requested register %" PRIu32, + __FUNCTION__, reg_index); + return SendErrorResponse(0x15); + } + + // FIXME flip as needed to get data in big/little endian format for this host. + for (uint32_t i = 0; i < reg_value.GetByteSize(); ++i) + response.PutHex8(data[i]); + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_P(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Thread); + + // Ensure there is more content. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Empty P packet"); + + // Parse out the register number from the request. + packet.SetFilePos(strlen("P")); + const uint32_t reg_index = + packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); + if (reg_index == std::numeric_limits<uint32_t>::max()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, could not " + "parse register number from request \"%s\"", + __FUNCTION__, packet.GetStringRef().data()); + return SendErrorResponse(0x29); + } + + // Note debugserver would send an E30 here. + if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != '=')) + return SendIllFormedResponse( + packet, "P packet missing '=' char after register number"); + + // Parse out the value. + size_t reg_size = packet.GetHexBytesAvail(m_reg_bytes); + + // Get the thread to use. + NativeThreadProtocol *thread = GetThreadFromSuffix(packet); + if (!thread) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, no thread " + "available (thread index 0)", + __FUNCTION__); + return SendErrorResponse(0x28); + } + + // Get the thread's register context. + NativeRegisterContext ®_context = thread->GetRegisterContext(); + const RegisterInfo *reg_info = reg_context.GetRegisterInfoAtIndex(reg_index); + if (!reg_info) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, requested " + "register %" PRIu32 " returned NULL", + __FUNCTION__, reg_index); + return SendErrorResponse(0x48); + } + + // Return the end of registers response if we've iterated one past the end of + // the register set. + if (reg_index >= reg_context.GetUserRegisterCount()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, requested " + "register %" PRIu32 " beyond register count %" PRIu32, + __FUNCTION__, reg_index, reg_context.GetUserRegisterCount()); + return SendErrorResponse(0x47); + } + + if (reg_size != reg_info->byte_size) + return SendIllFormedResponse(packet, "P packet register size is incorrect"); + + // Build the reginfos response. + StreamGDBRemote response; + + RegisterValue reg_value(ArrayRef<uint8_t>(m_reg_bytes, reg_size), + m_current_process->GetArchitecture().GetByteOrder()); + Status error = reg_context.WriteRegister(reg_info, reg_value); + if (error.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, write of " + "requested register %" PRIu32 " (%s) failed: %s", + __FUNCTION__, reg_index, reg_info->name, error.AsCString()); + return SendErrorResponse(0x32); + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Thread); + + // Parse out which variant of $H is requested. + packet.SetFilePos(strlen("H")); + if (packet.GetBytesLeft() < 1) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, H command " + "missing {g,c} variant", + __FUNCTION__); + return SendIllFormedResponse(packet, "H command missing {g,c} variant"); + } + + const char h_variant = packet.GetChar(); + NativeProcessProtocol *default_process; + switch (h_variant) { + case 'g': + default_process = m_current_process; + break; + + case 'c': + default_process = m_continue_process; + break; + + default: + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, invalid $H variant %c", + __FUNCTION__, h_variant); + return SendIllFormedResponse(packet, + "H variant unsupported, should be c or g"); + } + + // Parse out the thread number. + auto pid_tid = packet.GetPidTid(default_process ? default_process->GetID() + : LLDB_INVALID_PROCESS_ID); + if (!pid_tid) + return SendErrorResponse(llvm::make_error<StringError>( + inconvertibleErrorCode(), "Malformed thread-id")); + + lldb::pid_t pid = pid_tid->first; + lldb::tid_t tid = pid_tid->second; + + if (pid == StringExtractorGDBRemote::AllProcesses) + return SendUnimplementedResponse("Selecting all processes not supported"); + if (pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(llvm::make_error<StringError>( + inconvertibleErrorCode(), "No current process and no PID provided")); + + // Check the process ID and find respective process instance. + auto new_process_it = m_debugged_processes.find(pid); + if (new_process_it == m_debugged_processes.end()) + return SendErrorResponse(llvm::make_error<StringError>( + inconvertibleErrorCode(), + llvm::formatv("No process with PID {0} debugged", pid))); + + // Ensure we have the given thread when not specifying -1 (all threads) or 0 + // (any thread). + if (tid != LLDB_INVALID_THREAD_ID && tid != 0) { + NativeThreadProtocol *thread = + new_process_it->second.process_up->GetThreadByID(tid); + if (!thread) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, tid %" PRIu64 + " not found", + __FUNCTION__, tid); + return SendErrorResponse(0x15); + } + } + + // Now switch the given process and thread type. + switch (h_variant) { + case 'g': + m_current_process = new_process_it->second.process_up.get(); + SetCurrentThreadID(tid); + break; + + case 'c': + m_continue_process = new_process_it->second.process_up.get(); + SetContinueThreadID(tid); + break; + + default: + assert(false && "unsupported $H variant - shouldn't get here"); + return SendIllFormedResponse(packet, + "H variant unsupported, should be c or g"); + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_I(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Thread); + + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + packet.SetFilePos(::strlen("I")); + uint8_t tmp[4096]; + for (;;) { + size_t read = packet.GetHexBytesAvail(tmp); + if (read == 0) { + break; + } + // write directly to stdin *this might block if stdin buffer is full* + // TODO: enqueue this block in circular buffer and send window size to + // remote host + ConnectionStatus status; + Status error; + m_stdio_communication.WriteAll(tmp, read, status, &error); + if (error.Fail()) { + return SendErrorResponse(0x15); + } + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_interrupt( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); + + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOG(log, "failed, no process available"); + return SendErrorResponse(0x15); + } + + // Interrupt the process. + Status error = m_current_process->Interrupt(); + if (error.Fail()) { + LLDB_LOG(log, "failed for process {0}: {1}", m_current_process->GetID(), + error); + return SendErrorResponse(GDBRemoteServerError::eErrorResume); + } + + LLDB_LOG(log, "stopped process {0}", m_current_process->GetID()); + + // No response required from stop all. + return PacketResult::Success; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_memory_read( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + // Parse out the memory address. + packet.SetFilePos(strlen("m")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short m packet"); + + // Read the address. Punting on validation. + // FIXME replace with Hex U64 read with no default value that fails on failed + // read. + const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); + + // Validate comma. + if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) + return SendIllFormedResponse(packet, "Comma sep missing in m packet"); + + // Get # bytes to read. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Length missing in m packet"); + + const uint64_t byte_count = packet.GetHexMaxU64(false, 0); + if (byte_count == 0) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s nothing to read: " + "zero-length packet", + __FUNCTION__); + return SendOKResponse(); + } + + // Allocate the response buffer. + std::string buf(byte_count, '\0'); + if (buf.empty()) + return SendErrorResponse(0x78); + + // Retrieve the process memory. + size_t bytes_read = 0; + Status error = m_current_process->ReadMemoryWithoutTrap( + read_addr, &buf[0], byte_count, bytes_read); + if (error.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 + " mem 0x%" PRIx64 ": failed to read. Error: %s", + __FUNCTION__, m_current_process->GetID(), read_addr, + error.AsCString()); + return SendErrorResponse(0x08); + } + + if (bytes_read == 0) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 + " mem 0x%" PRIx64 ": read 0 of %" PRIu64 " requested bytes", + __FUNCTION__, m_current_process->GetID(), read_addr, byte_count); + return SendErrorResponse(0x08); + } + + StreamGDBRemote response; + packet.SetFilePos(0); + char kind = packet.GetChar('?'); + if (kind == 'x') + response.PutEscapedBytes(buf.data(), byte_count); + else { + assert(kind == 'm'); + for (size_t i = 0; i < bytes_read; ++i) + response.PutHex8(buf[i]); + } + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle__M(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + // Parse out the memory address. + packet.SetFilePos(strlen("_M")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short _M packet"); + + const lldb::addr_t size = packet.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + if (size == LLDB_INVALID_ADDRESS) + return SendIllFormedResponse(packet, "Address not valid"); + if (packet.GetChar() != ',') + return SendIllFormedResponse(packet, "Bad packet"); + Permissions perms = {}; + while (packet.GetBytesLeft() > 0) { + switch (packet.GetChar()) { + case 'r': + perms |= ePermissionsReadable; + break; + case 'w': + perms |= ePermissionsWritable; + break; + case 'x': + perms |= ePermissionsExecutable; + break; + default: + return SendIllFormedResponse(packet, "Bad permissions"); + } + } + + llvm::Expected<addr_t> addr = m_current_process->AllocateMemory(size, perms); + if (!addr) + return SendErrorResponse(addr.takeError()); + + StreamGDBRemote response; + response.PutHex64(*addr); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle__m(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + // Parse out the memory address. + packet.SetFilePos(strlen("_m")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short m packet"); + + const lldb::addr_t addr = packet.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + if (addr == LLDB_INVALID_ADDRESS) + return SendIllFormedResponse(packet, "Address not valid"); + + if (llvm::Error Err = m_current_process->DeallocateMemory(addr)) + return SendErrorResponse(std::move(Err)); + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_M(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + // Parse out the memory address. + packet.SetFilePos(strlen("M")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short M packet"); + + // Read the address. Punting on validation. + // FIXME replace with Hex U64 read with no default value that fails on failed + // read. + const lldb::addr_t write_addr = packet.GetHexMaxU64(false, 0); + + // Validate comma. + if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ',')) + return SendIllFormedResponse(packet, "Comma sep missing in M packet"); + + // Get # bytes to read. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Length missing in M packet"); + + const uint64_t byte_count = packet.GetHexMaxU64(false, 0); + if (byte_count == 0) { + LLDB_LOG(log, "nothing to write: zero-length packet"); + return PacketResult::Success; + } + + // Validate colon. + if ((packet.GetBytesLeft() < 1) || (packet.GetChar() != ':')) + return SendIllFormedResponse( + packet, "Comma sep missing in M packet after byte length"); + + // Allocate the conversion buffer. + std::vector<uint8_t> buf(byte_count, 0); + if (buf.empty()) + return SendErrorResponse(0x78); + + // Convert the hex memory write contents to bytes. + StreamGDBRemote response; + const uint64_t convert_count = packet.GetHexBytes(buf, 0); + if (convert_count != byte_count) { + LLDB_LOG(log, + "pid {0} mem {1:x}: asked to write {2} bytes, but only found {3} " + "to convert.", + m_current_process->GetID(), write_addr, byte_count, convert_count); + return SendIllFormedResponse(packet, "M content byte length specified did " + "not match hex-encoded content " + "length"); + } + + // Write the process memory. + size_t bytes_written = 0; + Status error = m_current_process->WriteMemory(write_addr, &buf[0], byte_count, + bytes_written); + if (error.Fail()) { + LLDB_LOG(log, "pid {0} mem {1:x}: failed to write. Error: {2}", + m_current_process->GetID(), write_addr, error); + return SendErrorResponse(0x09); + } + + if (bytes_written == 0) { + LLDB_LOG(log, "pid {0} mem {1:x}: wrote 0 of {2} requested bytes", + m_current_process->GetID(), write_addr, byte_count); + return SendErrorResponse(0x09); + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfoSupported( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + // Currently only the NativeProcessProtocol knows if it can handle a + // qMemoryRegionInfoSupported request, but we're not guaranteed to be + // attached to a process. For now we'll assume the client only asks this + // when a process is being debugged. + + // Ensure we have a process running; otherwise, we can't figure this out + // since we won't have a NativeProcessProtocol. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + // Test if we can get any region back when asking for the region around NULL. + MemoryRegionInfo region_info; + const Status error = m_current_process->GetMemoryRegionInfo(0, region_info); + if (error.Fail()) { + // We don't support memory region info collection for this + // NativeProcessProtocol. + return SendUnimplementedResponse(""); + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + // Ensure we have a process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x15); + } + + // Parse out the memory address. + packet.SetFilePos(strlen("qMemoryRegionInfo:")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short qMemoryRegionInfo: packet"); + + // Read the address. Punting on validation. + const lldb::addr_t read_addr = packet.GetHexMaxU64(false, 0); + + StreamGDBRemote response; + + // Get the memory region info for the target address. + MemoryRegionInfo region_info; + const Status error = + m_current_process->GetMemoryRegionInfo(read_addr, region_info); + if (error.Fail()) { + // Return the error message. + + response.PutCString("error:"); + response.PutStringAsRawHex8(error.AsCString()); + response.PutChar(';'); + } else { + // Range start and size. + response.Printf("start:%" PRIx64 ";size:%" PRIx64 ";", + region_info.GetRange().GetRangeBase(), + region_info.GetRange().GetByteSize()); + + // Permissions. + if (region_info.GetReadable() || region_info.GetWritable() || + region_info.GetExecutable()) { + // Write permissions info. + response.PutCString("permissions:"); + + if (region_info.GetReadable()) + response.PutChar('r'); + if (region_info.GetWritable()) + response.PutChar('w'); + if (region_info.GetExecutable()) + response.PutChar('x'); + + response.PutChar(';'); + } + + // Flags + MemoryRegionInfo::OptionalBool memory_tagged = + region_info.GetMemoryTagged(); + if (memory_tagged != MemoryRegionInfo::eDontKnow) { + response.PutCString("flags:"); + if (memory_tagged == MemoryRegionInfo::eYes) { + response.PutCString("mt"); + } + response.PutChar(';'); + } + + // Name + ConstString name = region_info.GetName(); + if (name) { + response.PutCString("name:"); + response.PutStringAsRawHex8(name.GetStringRef()); + response.PutChar(';'); + } + } + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_Z(StringExtractorGDBRemote &packet) { + // Ensure we have a process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOG(log, "failed, no process available"); + return SendErrorResponse(0x15); + } + + // Parse out software or hardware breakpoint or watchpoint requested. + packet.SetFilePos(strlen("Z")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse( + packet, "Too short Z packet, missing software/hardware specifier"); + + bool want_breakpoint = true; + bool want_hardware = false; + uint32_t watch_flags = 0; + + const GDBStoppointType stoppoint_type = + GDBStoppointType(packet.GetS32(eStoppointInvalid)); + switch (stoppoint_type) { + case eBreakpointSoftware: + want_hardware = false; + want_breakpoint = true; + break; + case eBreakpointHardware: + want_hardware = true; + want_breakpoint = true; + break; + case eWatchpointWrite: + watch_flags = 1; + want_hardware = true; + want_breakpoint = false; + break; + case eWatchpointRead: + watch_flags = 2; + want_hardware = true; + want_breakpoint = false; + break; + case eWatchpointReadWrite: + watch_flags = 3; + want_hardware = true; + want_breakpoint = false; + break; + case eStoppointInvalid: + return SendIllFormedResponse( + packet, "Z packet had invalid software/hardware specifier"); + } + + if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') + return SendIllFormedResponse( + packet, "Malformed Z packet, expecting comma after stoppoint type"); + + // Parse out the stoppoint address. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short Z packet, missing address"); + const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); + + if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') + return SendIllFormedResponse( + packet, "Malformed Z packet, expecting comma after address"); + + // Parse out the stoppoint size (i.e. size hint for opcode size). + const uint32_t size = + packet.GetHexMaxU32(false, std::numeric_limits<uint32_t>::max()); + if (size == std::numeric_limits<uint32_t>::max()) + return SendIllFormedResponse( + packet, "Malformed Z packet, failed to parse size argument"); + + if (want_breakpoint) { + // Try to set the breakpoint. + const Status error = + m_current_process->SetBreakpoint(addr, size, want_hardware); + if (error.Success()) + return SendOKResponse(); + Log *log = GetLog(LLDBLog::Breakpoints); + LLDB_LOG(log, "pid {0} failed to set breakpoint: {1}", + m_current_process->GetID(), error); + return SendErrorResponse(0x09); + } else { + // Try to set the watchpoint. + const Status error = m_current_process->SetWatchpoint( + addr, size, watch_flags, want_hardware); + if (error.Success()) + return SendOKResponse(); + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "pid {0} failed to set watchpoint: {1}", + m_current_process->GetID(), error); + return SendErrorResponse(0x09); + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) { + // Ensure we have a process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + Log *log = GetLog(LLDBLog::Process); + LLDB_LOG(log, "failed, no process available"); + return SendErrorResponse(0x15); + } + + // Parse out software or hardware breakpoint or watchpoint requested. + packet.SetFilePos(strlen("z")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse( + packet, "Too short z packet, missing software/hardware specifier"); + + bool want_breakpoint = true; + bool want_hardware = false; + + const GDBStoppointType stoppoint_type = + GDBStoppointType(packet.GetS32(eStoppointInvalid)); + switch (stoppoint_type) { + case eBreakpointHardware: + want_breakpoint = true; + want_hardware = true; + break; + case eBreakpointSoftware: + want_breakpoint = true; + break; + case eWatchpointWrite: + want_breakpoint = false; + break; + case eWatchpointRead: + want_breakpoint = false; + break; + case eWatchpointReadWrite: + want_breakpoint = false; + break; + default: + return SendIllFormedResponse( + packet, "z packet had invalid software/hardware specifier"); + } + + if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') + return SendIllFormedResponse( + packet, "Malformed z packet, expecting comma after stoppoint type"); + + // Parse out the stoppoint address. + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse(packet, "Too short z packet, missing address"); + const lldb::addr_t addr = packet.GetHexMaxU64(false, 0); + + if ((packet.GetBytesLeft() < 1) || packet.GetChar() != ',') + return SendIllFormedResponse( + packet, "Malformed z packet, expecting comma after address"); + + /* + // Parse out the stoppoint size (i.e. size hint for opcode size). + const uint32_t size = packet.GetHexMaxU32 (false, + std::numeric_limits<uint32_t>::max ()); + if (size == std::numeric_limits<uint32_t>::max ()) + return SendIllFormedResponse(packet, "Malformed z packet, failed to parse + size argument"); + */ + + if (want_breakpoint) { + // Try to clear the breakpoint. + const Status error = + m_current_process->RemoveBreakpoint(addr, want_hardware); + if (error.Success()) + return SendOKResponse(); + Log *log = GetLog(LLDBLog::Breakpoints); + LLDB_LOG(log, "pid {0} failed to remove breakpoint: {1}", + m_current_process->GetID(), error); + return SendErrorResponse(0x09); + } else { + // Try to clear the watchpoint. + const Status error = m_current_process->RemoveWatchpoint(addr); + if (error.Success()) + return SendOKResponse(); + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "pid {0} failed to remove watchpoint: {1}", + m_current_process->GetID(), error); + return SendErrorResponse(0x09); + } +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_s(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); + + // Ensure we have a process. + if (!m_continue_process || + (m_continue_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(0x32); + } + + // We first try to use a continue thread id. If any one or any all set, use + // the current thread. Bail out if we don't have a thread id. + lldb::tid_t tid = GetContinueThreadID(); + if (tid == 0 || tid == LLDB_INVALID_THREAD_ID) + tid = GetCurrentThreadID(); + if (tid == LLDB_INVALID_THREAD_ID) + return SendErrorResponse(0x33); + + // Double check that we have such a thread. + // TODO investigate: on MacOSX we might need to do an UpdateThreads () here. + NativeThreadProtocol *thread = m_continue_process->GetThreadByID(tid); + if (!thread) + return SendErrorResponse(0x33); + + // Create the step action for the given thread. + ResumeAction action = {tid, eStateStepping, LLDB_INVALID_SIGNAL_NUMBER}; + + // Setup the actions list. + ResumeActionList actions; + actions.Append(action); + + // All other threads stop while we're single stepping a thread. + actions.SetDefaultThreadActionIfNeeded(eStateStopped, 0); + + PacketResult resume_res = ResumeProcess(*m_continue_process, actions); + if (resume_res != PacketResult::Success) + return resume_res; + + // No response here, unless in non-stop mode. + // Otherwise, the stop or exit will come from the resulting action. + return SendContinueSuccessResponse(); +} + +llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> +GDBRemoteCommunicationServerLLGS::BuildTargetXml() { + // Ensure we have a thread. + NativeThreadProtocol *thread = m_current_process->GetThreadAtIndex(0); + if (!thread) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "No thread available"); + + Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); + // Get the register context for the first thread. + NativeRegisterContext ®_context = thread->GetRegisterContext(); + + StreamString response; + + response.Printf("<?xml version=\"1.0\"?>\n"); + response.Printf("<target version=\"1.0\">\n"); + response.IndentMore(); + + response.Indent(); + response.Printf("<architecture>%s</architecture>\n", + m_current_process->GetArchitecture() + .GetTriple() + .getArchName() + .str() + .c_str()); + + response.Indent("<feature>\n"); + + const int registers_count = reg_context.GetUserRegisterCount(); + if (registers_count) + response.IndentMore(); + + llvm::StringSet<> field_enums_seen; + for (int reg_index = 0; reg_index < registers_count; reg_index++) { + const RegisterInfo *reg_info = + reg_context.GetRegisterInfoAtIndex(reg_index); + + if (!reg_info) { + LLDB_LOGF(log, + "%s failed to get register info for register index %" PRIu32, + "target.xml", reg_index); + continue; + } + + if (reg_info->flags_type) { + response.IndentMore(); + reg_info->flags_type->EnumsToXML(response, field_enums_seen); + reg_info->flags_type->ToXML(response); + response.IndentLess(); + } + + response.Indent(); + response.Printf("<reg name=\"%s\" bitsize=\"%" PRIu32 + "\" regnum=\"%d\" ", + reg_info->name, reg_info->byte_size * 8, reg_index); + + if (!reg_context.RegisterOffsetIsDynamic()) + response.Printf("offset=\"%" PRIu32 "\" ", reg_info->byte_offset); + + if (reg_info->alt_name && reg_info->alt_name[0]) + response.Printf("altname=\"%s\" ", reg_info->alt_name); + + llvm::StringRef encoding = GetEncodingNameOrEmpty(*reg_info); + if (!encoding.empty()) + response << "encoding=\"" << encoding << "\" "; + + llvm::StringRef format = GetFormatNameOrEmpty(*reg_info); + if (!format.empty()) + response << "format=\"" << format << "\" "; + + if (reg_info->flags_type) + response << "type=\"" << reg_info->flags_type->GetID() << "\" "; + + const char *const register_set_name = + reg_context.GetRegisterSetNameForRegisterAtIndex(reg_index); + if (register_set_name) + response << "group=\"" << register_set_name << "\" "; + + if (reg_info->kinds[RegisterKind::eRegisterKindEHFrame] != + LLDB_INVALID_REGNUM) + response.Printf("ehframe_regnum=\"%" PRIu32 "\" ", + reg_info->kinds[RegisterKind::eRegisterKindEHFrame]); + + if (reg_info->kinds[RegisterKind::eRegisterKindDWARF] != + LLDB_INVALID_REGNUM) + response.Printf("dwarf_regnum=\"%" PRIu32 "\" ", + reg_info->kinds[RegisterKind::eRegisterKindDWARF]); + + llvm::StringRef kind_generic = GetKindGenericOrEmpty(*reg_info); + if (!kind_generic.empty()) + response << "generic=\"" << kind_generic << "\" "; + + if (reg_info->value_regs && + reg_info->value_regs[0] != LLDB_INVALID_REGNUM) { + response.PutCString("value_regnums=\""); + CollectRegNums(reg_info->value_regs, response, false); + response.Printf("\" "); + } + + if (reg_info->invalidate_regs && reg_info->invalidate_regs[0]) { + response.PutCString("invalidate_regnums=\""); + CollectRegNums(reg_info->invalidate_regs, response, false); + response.Printf("\" "); + } + + response.Printf("/>\n"); + } + + if (registers_count) + response.IndentLess(); + + response.Indent("</feature>\n"); + response.IndentLess(); + response.Indent("</target>\n"); + return MemoryBuffer::getMemBufferCopy(response.GetString(), "target.xml"); +} + +llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> +GDBRemoteCommunicationServerLLGS::ReadXferObject(llvm::StringRef object, + llvm::StringRef annex) { + // Make sure we have a valid process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "No process available"); + } + + if (object == "auxv") { + // Grab the auxv data. + auto buffer_or_error = m_current_process->GetAuxvData(); + if (!buffer_or_error) + return llvm::errorCodeToError(buffer_or_error.getError()); + return std::move(*buffer_or_error); + } + + if (object == "siginfo") { + NativeThreadProtocol *thread = m_current_process->GetCurrentThread(); + if (!thread) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "no current thread"); + + auto buffer_or_error = thread->GetSiginfo(); + if (!buffer_or_error) + return buffer_or_error.takeError(); + return std::move(*buffer_or_error); + } + + if (object == "libraries-svr4") { + auto library_list = m_current_process->GetLoadedSVR4Libraries(); + if (!library_list) + return library_list.takeError(); + + StreamString response; + response.Printf("<library-list-svr4 version=\"1.0\">"); + for (auto const &library : *library_list) { + response.Printf("<library name=\"%s\" ", + XMLEncodeAttributeValue(library.name.c_str()).c_str()); + response.Printf("lm=\"0x%" PRIx64 "\" ", library.link_map); + response.Printf("l_addr=\"0x%" PRIx64 "\" ", library.base_addr); + response.Printf("l_ld=\"0x%" PRIx64 "\" />", library.ld_addr); + } + response.Printf("</library-list-svr4>"); + return MemoryBuffer::getMemBufferCopy(response.GetString(), __FUNCTION__); + } + + if (object == "features" && annex == "target.xml") + return BuildTargetXml(); + + return llvm::make_error<UnimplementedError>(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qXfer( + StringExtractorGDBRemote &packet) { + SmallVector<StringRef, 5> fields; + // The packet format is "qXfer:<object>:<action>:<annex>:offset,length" + StringRef(packet.GetStringRef()).split(fields, ':', 4); + if (fields.size() != 5) + return SendIllFormedResponse(packet, "malformed qXfer packet"); + StringRef &xfer_object = fields[1]; + StringRef &xfer_action = fields[2]; + StringRef &xfer_annex = fields[3]; + StringExtractor offset_data(fields[4]); + if (xfer_action != "read") + return SendUnimplementedResponse("qXfer action not supported"); + // Parse offset. + const uint64_t xfer_offset = + offset_data.GetHexMaxU64(false, std::numeric_limits<uint64_t>::max()); + if (xfer_offset == std::numeric_limits<uint64_t>::max()) + return SendIllFormedResponse(packet, "qXfer packet missing offset"); + // Parse out comma. + if (offset_data.GetChar() != ',') + return SendIllFormedResponse(packet, + "qXfer packet missing comma after offset"); + // Parse out the length. + const uint64_t xfer_length = + offset_data.GetHexMaxU64(false, std::numeric_limits<uint64_t>::max()); + if (xfer_length == std::numeric_limits<uint64_t>::max()) + return SendIllFormedResponse(packet, "qXfer packet missing length"); + + // Get a previously constructed buffer if it exists or create it now. + std::string buffer_key = (xfer_object + xfer_action + xfer_annex).str(); + auto buffer_it = m_xfer_buffer_map.find(buffer_key); + if (buffer_it == m_xfer_buffer_map.end()) { + auto buffer_up = ReadXferObject(xfer_object, xfer_annex); + if (!buffer_up) + return SendErrorResponse(buffer_up.takeError()); + buffer_it = m_xfer_buffer_map + .insert(std::make_pair(buffer_key, std::move(*buffer_up))) + .first; + } + + // Send back the response + StreamGDBRemote response; + bool done_with_buffer = false; + llvm::StringRef buffer = buffer_it->second->getBuffer(); + if (xfer_offset >= buffer.size()) { + // We have nothing left to send. Mark the buffer as complete. + response.PutChar('l'); + done_with_buffer = true; + } else { + // Figure out how many bytes are available starting at the given offset. + buffer = buffer.drop_front(xfer_offset); + // Mark the response type according to whether we're reading the remainder + // of the data. + if (xfer_length >= buffer.size()) { + // There will be nothing left to read after this + response.PutChar('l'); + done_with_buffer = true; + } else { + // There will still be bytes to read after this request. + response.PutChar('m'); + buffer = buffer.take_front(xfer_length); + } + // Now write the data in encoded binary form. + response.PutEscapedBytes(buffer.data(), buffer.size()); + } + + if (done_with_buffer) + m_xfer_buffer_map.erase(buffer_it); + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QSaveRegisterState( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Thread); + + // Move past packet name. + packet.SetFilePos(strlen("QSaveRegisterState")); + + // Get the thread to use. + NativeThreadProtocol *thread = GetThreadFromSuffix(packet); + if (!thread) { + if (m_thread_suffix_supported) + return SendIllFormedResponse( + packet, "No thread specified in QSaveRegisterState packet"); + else + return SendIllFormedResponse(packet, + "No thread was is set with the Hg packet"); + } + + // Grab the register context for the thread. + NativeRegisterContext& reg_context = thread->GetRegisterContext(); + + // Save registers to a buffer. + WritableDataBufferSP register_data_sp; + Status error = reg_context.ReadAllRegisterValues(register_data_sp); + if (error.Fail()) { + LLDB_LOG(log, "pid {0} failed to save all register values: {1}", + m_current_process->GetID(), error); + return SendErrorResponse(0x75); + } + + // Allocate a new save id. + const uint32_t save_id = GetNextSavedRegistersID(); + assert((m_saved_registers_map.find(save_id) == m_saved_registers_map.end()) && + "GetNextRegisterSaveID() returned an existing register save id"); + + // Save the register data buffer under the save id. + { + std::lock_guard<std::mutex> guard(m_saved_registers_mutex); + m_saved_registers_map[save_id] = register_data_sp; + } + + // Write the response. + StreamGDBRemote response; + response.Printf("%" PRIu32, save_id); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QRestoreRegisterState( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Thread); + + // Parse out save id. + packet.SetFilePos(strlen("QRestoreRegisterState:")); + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse( + packet, "QRestoreRegisterState packet missing register save id"); + + const uint32_t save_id = packet.GetU32(0); + if (save_id == 0) { + LLDB_LOG(log, "QRestoreRegisterState packet has malformed save id, " + "expecting decimal uint32_t"); + return SendErrorResponse(0x76); + } + + // Get the thread to use. + NativeThreadProtocol *thread = GetThreadFromSuffix(packet); + if (!thread) { + if (m_thread_suffix_supported) + return SendIllFormedResponse( + packet, "No thread specified in QRestoreRegisterState packet"); + else + return SendIllFormedResponse(packet, + "No thread was is set with the Hg packet"); + } + + // Grab the register context for the thread. + NativeRegisterContext ®_context = thread->GetRegisterContext(); + + // Retrieve register state buffer, then remove from the list. + DataBufferSP register_data_sp; + { + std::lock_guard<std::mutex> guard(m_saved_registers_mutex); + + // Find the register set buffer for the given save id. + auto it = m_saved_registers_map.find(save_id); + if (it == m_saved_registers_map.end()) { + LLDB_LOG(log, + "pid {0} does not have a register set save buffer for id {1}", + m_current_process->GetID(), save_id); + return SendErrorResponse(0x77); + } + register_data_sp = it->second; + + // Remove it from the map. + m_saved_registers_map.erase(it); + } + + Status error = reg_context.WriteAllRegisterValues(register_data_sp); + if (error.Fail()) { + LLDB_LOG(log, "pid {0} failed to restore all register values: {1}", + m_current_process->GetID(), error); + return SendErrorResponse(0x77); + } + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vAttach( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + // Consume the ';' after vAttach. + packet.SetFilePos(strlen("vAttach")); + if (!packet.GetBytesLeft() || packet.GetChar() != ';') + return SendIllFormedResponse(packet, "vAttach missing expected ';'"); + + // Grab the PID to which we will attach (assume hex encoding). + lldb::pid_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); + if (pid == LLDB_INVALID_PROCESS_ID) + return SendIllFormedResponse(packet, + "vAttach failed to parse the process id"); + + // Attempt to attach. + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s attempting to attach to " + "pid %" PRIu64, + __FUNCTION__, pid); + + Status error = AttachToProcess(pid); + + if (error.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed to attach to " + "pid %" PRIu64 ": %s\n", + __FUNCTION__, pid, error.AsCString()); + return SendErrorResponse(error); + } + + // Notify we attached by sending a stop packet. + assert(m_current_process); + return SendStopReasonForState(*m_current_process, + m_current_process->GetState(), + /*force_synchronous=*/false); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vAttachWait( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + // Consume the ';' after the identifier. + packet.SetFilePos(strlen("vAttachWait")); + + if (!packet.GetBytesLeft() || packet.GetChar() != ';') + return SendIllFormedResponse(packet, "vAttachWait missing expected ';'"); + + // Allocate the buffer for the process name from vAttachWait. + std::string process_name; + if (!packet.GetHexByteString(process_name)) + return SendIllFormedResponse(packet, + "vAttachWait failed to parse process name"); + + LLDB_LOG(log, "attempting to attach to process named '{0}'", process_name); + + Status error = AttachWaitProcess(process_name, false); + if (error.Fail()) { + LLDB_LOG(log, "failed to attach to process named '{0}': {1}", process_name, + error); + return SendErrorResponse(error); + } + + // Notify we attached by sending a stop packet. + assert(m_current_process); + return SendStopReasonForState(*m_current_process, + m_current_process->GetState(), + /*force_synchronous=*/false); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qVAttachOrWaitSupported( + StringExtractorGDBRemote &packet) { + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vAttachOrWait( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + // Consume the ';' after the identifier. + packet.SetFilePos(strlen("vAttachOrWait")); + + if (!packet.GetBytesLeft() || packet.GetChar() != ';') + return SendIllFormedResponse(packet, "vAttachOrWait missing expected ';'"); + + // Allocate the buffer for the process name from vAttachWait. + std::string process_name; + if (!packet.GetHexByteString(process_name)) + return SendIllFormedResponse(packet, + "vAttachOrWait failed to parse process name"); + + LLDB_LOG(log, "attempting to attach to process named '{0}'", process_name); + + Status error = AttachWaitProcess(process_name, true); + if (error.Fail()) { + LLDB_LOG(log, "failed to attach to process named '{0}': {1}", process_name, + error); + return SendErrorResponse(error); + } + + // Notify we attached by sending a stop packet. + assert(m_current_process); + return SendStopReasonForState(*m_current_process, + m_current_process->GetState(), + /*force_synchronous=*/false); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vRun( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + llvm::StringRef s = packet.GetStringRef(); + if (!s.consume_front("vRun;")) + return SendErrorResponse(8); + + llvm::SmallVector<llvm::StringRef, 16> argv; + s.split(argv, ';'); + + for (llvm::StringRef hex_arg : argv) { + StringExtractor arg_ext{hex_arg}; + std::string arg; + arg_ext.GetHexByteString(arg); + m_process_launch_info.GetArguments().AppendArgument(arg); + LLDB_LOGF(log, "LLGSPacketHandler::%s added arg: \"%s\"", __FUNCTION__, + arg.c_str()); + } + + if (argv.empty()) + return SendErrorResponse(Status("No arguments")); + m_process_launch_info.GetExecutableFile().SetFile( + m_process_launch_info.GetArguments()[0].ref(), FileSpec::Style::native); + m_process_launch_error = LaunchProcess(); + if (m_process_launch_error.Fail()) + return SendErrorResponse(m_process_launch_error); + assert(m_current_process); + return SendStopReasonForState(*m_current_process, + m_current_process->GetState(), + /*force_synchronous=*/true); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_D(StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + if (!m_non_stop) + StopSTDIOForwarding(); + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + // Consume the ';' after D. + packet.SetFilePos(1); + if (packet.GetBytesLeft()) { + if (packet.GetChar() != ';') + return SendIllFormedResponse(packet, "D missing expected ';'"); + + // Grab the PID from which we will detach (assume hex encoding). + pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); + if (pid == LLDB_INVALID_PROCESS_ID) + return SendIllFormedResponse(packet, "D failed to parse the process id"); + } + + // Detach forked children if their PID was specified *or* no PID was requested + // (i.e. detach-all packet). + llvm::Error detach_error = llvm::Error::success(); + bool detached = false; + for (auto it = m_debugged_processes.begin(); + it != m_debugged_processes.end();) { + if (pid == LLDB_INVALID_PROCESS_ID || pid == it->first) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s detaching %" PRId64, + __FUNCTION__, it->first); + if (llvm::Error e = it->second.process_up->Detach().ToError()) + detach_error = llvm::joinErrors(std::move(detach_error), std::move(e)); + else { + if (it->second.process_up.get() == m_current_process) + m_current_process = nullptr; + if (it->second.process_up.get() == m_continue_process) + m_continue_process = nullptr; + it = m_debugged_processes.erase(it); + detached = true; + continue; + } + } + ++it; + } + + if (detach_error) + return SendErrorResponse(std::move(detach_error)); + if (!detached) + return SendErrorResponse(Status("PID %" PRIu64 " not traced", pid)); + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Thread); + + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(50); + + packet.SetFilePos(strlen("qThreadStopInfo")); + const lldb::tid_t tid = packet.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID); + if (tid == LLDB_INVALID_THREAD_ID) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed, could not " + "parse thread id from request \"%s\"", + __FUNCTION__, packet.GetStringRef().data()); + return SendErrorResponse(0x15); + } + return SendStopReplyPacketForThread(*m_current_process, tid, + /*force_synchronous=*/true); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo( + StringExtractorGDBRemote &) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); + + // Ensure we have a debugged process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(50); + LLDB_LOG(log, "preparing packet for pid {0}", m_current_process->GetID()); + + StreamString response; + const bool threads_with_valid_stop_info_only = false; + llvm::Expected<json::Value> threads_info = + GetJSONThreadsInfo(*m_current_process, threads_with_valid_stop_info_only); + if (!threads_info) { + LLDB_LOG_ERROR(log, threads_info.takeError(), + "failed to prepare a packet for pid {1}: {0}", + m_current_process->GetID()); + return SendErrorResponse(52); + } + + response.AsRawOstream() << *threads_info; + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); + return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo( + StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_current_process || + m_current_process->GetID() == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(68); + + packet.SetFilePos(strlen("qWatchpointSupportInfo")); + if (packet.GetBytesLeft() == 0) + return SendOKResponse(); + if (packet.GetChar() != ':') + return SendErrorResponse(67); + + auto hw_debug_cap = m_current_process->GetHardwareDebugSupportInfo(); + + StreamGDBRemote response; + if (hw_debug_cap == std::nullopt) + response.Printf("num:0;"); + else + response.Printf("num:%d;", hw_debug_cap->second); + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress( + StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_current_process || + m_current_process->GetID() == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(67); + + packet.SetFilePos(strlen("qFileLoadAddress:")); + if (packet.GetBytesLeft() == 0) + return SendErrorResponse(68); + + std::string file_name; + packet.GetHexByteString(file_name); + + lldb::addr_t file_load_address = LLDB_INVALID_ADDRESS; + Status error = + m_current_process->GetFileLoadAddress(file_name, file_load_address); + if (error.Fail()) + return SendErrorResponse(69); + + if (file_load_address == LLDB_INVALID_ADDRESS) + return SendErrorResponse(1); // File not loaded + + StreamGDBRemote response; + response.PutHex64(file_load_address); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QPassSignals( + StringExtractorGDBRemote &packet) { + std::vector<int> signals; + packet.SetFilePos(strlen("QPassSignals:")); + + // Read sequence of hex signal numbers divided by a semicolon and optionally + // spaces. + while (packet.GetBytesLeft() > 0) { + int signal = packet.GetS32(-1, 16); + if (signal < 0) + return SendIllFormedResponse(packet, "Failed to parse signal number."); + signals.push_back(signal); + + packet.SkipSpaces(); + char separator = packet.GetChar(); + if (separator == '\0') + break; // End of string + if (separator != ';') + return SendIllFormedResponse(packet, "Invalid separator," + " expected semicolon."); + } + + // Fail if we don't have a current process. + if (!m_current_process) + return SendErrorResponse(68); + + Status error = m_current_process->IgnoreSignals(signals); + if (error.Fail()) + return SendErrorResponse(69); + + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qMemTags( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + // Ensure we have a process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(1); + } + + // We are expecting + // qMemTags:<hex address>,<hex length>:<hex type> + + // Address + packet.SetFilePos(strlen("qMemTags:")); + const char *current_char = packet.Peek(); + if (!current_char || *current_char == ',') + return SendIllFormedResponse(packet, "Missing address in qMemTags packet"); + const lldb::addr_t addr = packet.GetHexMaxU64(/*little_endian=*/false, 0); + + // Length + char previous_char = packet.GetChar(); + current_char = packet.Peek(); + // If we don't have a separator or the length field is empty + if (previous_char != ',' || (current_char && *current_char == ':')) + return SendIllFormedResponse(packet, + "Invalid addr,length pair in qMemTags packet"); + + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse( + packet, "Too short qMemtags: packet (looking for length)"); + const size_t length = packet.GetHexMaxU64(/*little_endian=*/false, 0); + + // Type + const char *invalid_type_err = "Invalid type field in qMemTags: packet"; + if (packet.GetBytesLeft() < 1 || packet.GetChar() != ':') + return SendIllFormedResponse(packet, invalid_type_err); + + // Type is a signed integer but packed into the packet as its raw bytes. + // However, our GetU64 uses strtoull which allows +/-. We do not want this. + const char *first_type_char = packet.Peek(); + if (first_type_char && (*first_type_char == '+' || *first_type_char == '-')) + return SendIllFormedResponse(packet, invalid_type_err); + + // Extract type as unsigned then cast to signed. + // Using a uint64_t here so that we have some value outside of the 32 bit + // range to use as the invalid return value. + uint64_t raw_type = + packet.GetU64(std::numeric_limits<uint64_t>::max(), /*base=*/16); + + if ( // Make sure the cast below would be valid + raw_type > std::numeric_limits<uint32_t>::max() || + // To catch inputs like "123aardvark" that will parse but clearly aren't + // valid in this case. + packet.GetBytesLeft()) { + return SendIllFormedResponse(packet, invalid_type_err); + } + + // First narrow to 32 bits otherwise the copy into type would take + // the wrong 4 bytes on big endian. + uint32_t raw_type_32 = raw_type; + int32_t type = reinterpret_cast<int32_t &>(raw_type_32); + + StreamGDBRemote response; + std::vector<uint8_t> tags; + Status error = m_current_process->ReadMemoryTags(type, addr, length, tags); + if (error.Fail()) + return SendErrorResponse(1); + + // This m is here in case we want to support multi part replies in the future. + // In the same manner as qfThreadInfo/qsThreadInfo. + response.PutChar('m'); + response.PutBytesAsRawHex8(tags.data(), tags.size()); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QMemTags( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + // Ensure we have a process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationServerLLGS::%s failed, no process available", + __FUNCTION__); + return SendErrorResponse(1); + } + + // We are expecting + // QMemTags:<hex address>,<hex length>:<hex type>:<tags as hex bytes> + + // Address + packet.SetFilePos(strlen("QMemTags:")); + const char *current_char = packet.Peek(); + if (!current_char || *current_char == ',') + return SendIllFormedResponse(packet, "Missing address in QMemTags packet"); + const lldb::addr_t addr = packet.GetHexMaxU64(/*little_endian=*/false, 0); + + // Length + char previous_char = packet.GetChar(); + current_char = packet.Peek(); + // If we don't have a separator or the length field is empty + if (previous_char != ',' || (current_char && *current_char == ':')) + return SendIllFormedResponse(packet, + "Invalid addr,length pair in QMemTags packet"); + + if (packet.GetBytesLeft() < 1) + return SendIllFormedResponse( + packet, "Too short QMemtags: packet (looking for length)"); + const size_t length = packet.GetHexMaxU64(/*little_endian=*/false, 0); + + // Type + const char *invalid_type_err = "Invalid type field in QMemTags: packet"; + if (packet.GetBytesLeft() < 1 || packet.GetChar() != ':') + return SendIllFormedResponse(packet, invalid_type_err); + + // Our GetU64 uses strtoull which allows leading +/-, we don't want that. + const char *first_type_char = packet.Peek(); + if (first_type_char && (*first_type_char == '+' || *first_type_char == '-')) + return SendIllFormedResponse(packet, invalid_type_err); + + // The type is a signed integer but is in the packet as its raw bytes. + // So parse first as unsigned then cast to signed later. + // We extract to 64 bit, even though we only expect 32, so that we've + // got some invalid value we can check for. + uint64_t raw_type = + packet.GetU64(std::numeric_limits<uint64_t>::max(), /*base=*/16); + if (raw_type > std::numeric_limits<uint32_t>::max()) + return SendIllFormedResponse(packet, invalid_type_err); + + // First narrow to 32 bits. Otherwise the copy below would get the wrong + // 4 bytes on big endian. + uint32_t raw_type_32 = raw_type; + int32_t type = reinterpret_cast<int32_t &>(raw_type_32); + + // Tag data + if (packet.GetBytesLeft() < 1 || packet.GetChar() != ':') + return SendIllFormedResponse(packet, + "Missing tag data in QMemTags: packet"); + + // Must be 2 chars per byte + const char *invalid_data_err = "Invalid tag data in QMemTags: packet"; + if (packet.GetBytesLeft() % 2) + return SendIllFormedResponse(packet, invalid_data_err); + + // This is bytes here and is unpacked into target specific tags later + // We cannot assume that number of bytes == length here because the server + // can repeat tags to fill a given range. + std::vector<uint8_t> tag_data; + // Zero length writes will not have any tag data + // (but we pass them on because it will still check that tagging is enabled) + if (packet.GetBytesLeft()) { + size_t byte_count = packet.GetBytesLeft() / 2; + tag_data.resize(byte_count); + size_t converted_bytes = packet.GetHexBytes(tag_data, 0); + if (converted_bytes != byte_count) { + return SendIllFormedResponse(packet, invalid_data_err); + } + } + + Status status = + m_current_process->WriteMemoryTags(type, addr, length, tag_data); + return status.Success() ? SendOKResponse() : SendErrorResponse(1); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qSaveCore( + StringExtractorGDBRemote &packet) { + // Fail if we don't have a current process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(Status("Process not running.")); + + std::string path_hint; + + StringRef packet_str{packet.GetStringRef()}; + assert(packet_str.starts_with("qSaveCore")); + if (packet_str.consume_front("qSaveCore;")) { + for (auto x : llvm::split(packet_str, ';')) { + if (x.consume_front("path-hint:")) + StringExtractor(x).GetHexByteString(path_hint); + else + return SendErrorResponse(Status("Unsupported qSaveCore option")); + } + } + + llvm::Expected<std::string> ret = m_current_process->SaveCore(path_hint); + if (!ret) + return SendErrorResponse(ret.takeError()); + + StreamString response; + response.PutCString("core-path:"); + response.PutStringAsRawHex8(ret.get()); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QNonStop( + StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + + StringRef packet_str{packet.GetStringRef()}; + assert(packet_str.starts_with("QNonStop:")); + packet_str.consume_front("QNonStop:"); + if (packet_str == "0") { + if (m_non_stop) + StopSTDIOForwarding(); + for (auto &process_it : m_debugged_processes) { + if (process_it.second.process_up->IsRunning()) { + assert(m_non_stop); + Status error = process_it.second.process_up->Interrupt(); + if (error.Fail()) { + LLDB_LOG(log, + "while disabling nonstop, failed to halt process {0}: {1}", + process_it.first, error); + return SendErrorResponse(0x41); + } + // we must not send stop reasons after QNonStop + m_disabling_non_stop = true; + } + } + m_stdio_notification_queue.clear(); + m_stop_notification_queue.clear(); + m_non_stop = false; + // If we are stopping anything, defer sending the OK response until we're + // done. + if (m_disabling_non_stop) + return PacketResult::Success; + } else if (packet_str == "1") { + if (!m_non_stop) + StartSTDIOForwarding(); + m_non_stop = true; + } else + return SendErrorResponse(Status("Invalid QNonStop packet")); + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::HandleNotificationAck( + std::deque<std::string> &queue) { + // Per the protocol, the first message put into the queue is sent + // immediately. However, it remains the queue until the client ACKs it -- + // then we pop it and send the next message. The process repeats until + // the last message in the queue is ACK-ed, in which case the packet sends + // an OK response. + if (queue.empty()) + return SendErrorResponse(Status("No pending notification to ack")); + queue.pop_front(); + if (!queue.empty()) + return SendPacketNoLock(queue.front()); + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vStdio( + StringExtractorGDBRemote &packet) { + return HandleNotificationAck(m_stdio_notification_queue); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vStopped( + StringExtractorGDBRemote &packet) { + PacketResult ret = HandleNotificationAck(m_stop_notification_queue); + // If this was the last notification and all the processes exited, + // terminate the server. + if (m_stop_notification_queue.empty() && m_debugged_processes.empty()) { + m_exit_now = true; + m_mainloop.RequestTermination(); + } + return ret; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vCtrlC( + StringExtractorGDBRemote &packet) { + if (!m_non_stop) + return SendErrorResponse(Status("vCtrl is only valid in non-stop mode")); + + PacketResult interrupt_res = Handle_interrupt(packet); + // If interrupting the process failed, pass the result through. + if (interrupt_res != PacketResult::Success) + return interrupt_res; + // Otherwise, vCtrlC should issue an OK response (normal interrupts do not). + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_T(StringExtractorGDBRemote &packet) { + packet.SetFilePos(strlen("T")); + auto pid_tid = packet.GetPidTid(m_current_process ? m_current_process->GetID() + : LLDB_INVALID_PROCESS_ID); + if (!pid_tid) + return SendErrorResponse(llvm::make_error<StringError>( + inconvertibleErrorCode(), "Malformed thread-id")); + + lldb::pid_t pid = pid_tid->first; + lldb::tid_t tid = pid_tid->second; + + // Technically, this would also be caught by the PID check but let's be more + // explicit about the error. + if (pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(llvm::make_error<StringError>( + inconvertibleErrorCode(), "No current process and no PID provided")); + + // Check the process ID and find respective process instance. + auto new_process_it = m_debugged_processes.find(pid); + if (new_process_it == m_debugged_processes.end()) + return SendErrorResponse(1); + + // Check the thread ID + if (!new_process_it->second.process_up->GetThreadByID(tid)) + return SendErrorResponse(2); + + return SendOKResponse(); +} + +void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() { + Log *log = GetLog(LLDBLog::Process); + + // Tell the stdio connection to shut down. + if (m_stdio_communication.IsConnected()) { + auto connection = m_stdio_communication.GetConnection(); + if (connection) { + Status error; + connection->Disconnect(&error); + + if (error.Success()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s disconnect process " + "terminal stdio - SUCCESS", + __FUNCTION__); + } else { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s disconnect process " + "terminal stdio - FAIL: %s", + __FUNCTION__, error.AsCString()); + } + } + } +} + +NativeThreadProtocol *GDBRemoteCommunicationServerLLGS::GetThreadFromSuffix( + StringExtractorGDBRemote &packet) { + // We have no thread if we don't have a process. + if (!m_current_process || + m_current_process->GetID() == LLDB_INVALID_PROCESS_ID) + return nullptr; + + // If the client hasn't asked for thread suffix support, there will not be a + // thread suffix. Use the current thread in that case. + if (!m_thread_suffix_supported) { + const lldb::tid_t current_tid = GetCurrentThreadID(); + if (current_tid == LLDB_INVALID_THREAD_ID) + return nullptr; + else if (current_tid == 0) { + // Pick a thread. + return m_current_process->GetThreadAtIndex(0); + } else + return m_current_process->GetThreadByID(current_tid); + } + + Log *log = GetLog(LLDBLog::Thread); + + // Parse out the ';'. + if (packet.GetBytesLeft() < 1 || packet.GetChar() != ';') { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s gdb-remote parse " + "error: expected ';' prior to start of thread suffix: packet " + "contents = '%s'", + __FUNCTION__, packet.GetStringRef().data()); + return nullptr; + } + + if (!packet.GetBytesLeft()) + return nullptr; + + // Parse out thread: portion. + if (strncmp(packet.Peek(), "thread:", strlen("thread:")) != 0) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s gdb-remote parse " + "error: expected 'thread:' but not found, packet contents = " + "'%s'", + __FUNCTION__, packet.GetStringRef().data()); + return nullptr; + } + packet.SetFilePos(packet.GetFilePos() + strlen("thread:")); + const lldb::tid_t tid = packet.GetHexMaxU64(false, 0); + if (tid != 0) + return m_current_process->GetThreadByID(tid); + + return nullptr; +} + +lldb::tid_t GDBRemoteCommunicationServerLLGS::GetCurrentThreadID() const { + if (m_current_tid == 0 || m_current_tid == LLDB_INVALID_THREAD_ID) { + // Use whatever the debug process says is the current thread id since the + // protocol either didn't specify or specified we want any/all threads + // marked as the current thread. + if (!m_current_process) + return LLDB_INVALID_THREAD_ID; + return m_current_process->GetCurrentThreadID(); + } + // Use the specific current thread id set by the gdb remote protocol. + return m_current_tid; +} + +uint32_t GDBRemoteCommunicationServerLLGS::GetNextSavedRegistersID() { + std::lock_guard<std::mutex> guard(m_saved_registers_mutex); + return m_next_saved_registers_id++; +} + +void GDBRemoteCommunicationServerLLGS::ClearProcessSpecificData() { + Log *log = GetLog(LLDBLog::Process); + + LLDB_LOG(log, "clearing {0} xfer buffers", m_xfer_buffer_map.size()); + m_xfer_buffer_map.clear(); +} + +FileSpec +GDBRemoteCommunicationServerLLGS::FindModuleFile(const std::string &module_path, + const ArchSpec &arch) { + if (m_current_process) { + FileSpec file_spec; + if (m_current_process + ->GetLoadedModuleFileSpec(module_path.c_str(), file_spec) + .Success()) { + if (FileSystem::Instance().Exists(file_spec)) + return file_spec; + } + } + + return GDBRemoteCommunicationServerCommon::FindModuleFile(module_path, arch); +} + +std::string GDBRemoteCommunicationServerLLGS::XMLEncodeAttributeValue( + llvm::StringRef value) { + std::string result; + for (const char &c : value) { + switch (c) { + case '\'': + result += "'"; + break; + case '"': + result += """; + break; + case '<': + result += "<"; + break; + case '>': + result += ">"; + break; + default: + result += c; + break; + } + } + return result; +} + +std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures( + const llvm::ArrayRef<llvm::StringRef> client_features) { + std::vector<std::string> ret = + GDBRemoteCommunicationServerCommon::HandleFeatures(client_features); + ret.insert(ret.end(), { + "QThreadSuffixSupported+", + "QListThreadsInStopReply+", + "qXfer:features:read+", + "QNonStop+", + }); + + // report server-only features + using Extension = NativeProcessProtocol::Extension; + Extension plugin_features = m_process_manager.GetSupportedExtensions(); + if (bool(plugin_features & Extension::pass_signals)) + ret.push_back("QPassSignals+"); + if (bool(plugin_features & Extension::auxv)) + ret.push_back("qXfer:auxv:read+"); + if (bool(plugin_features & Extension::libraries_svr4)) + ret.push_back("qXfer:libraries-svr4:read+"); + if (bool(plugin_features & Extension::siginfo_read)) + ret.push_back("qXfer:siginfo:read+"); + if (bool(plugin_features & Extension::memory_tagging)) + ret.push_back("memory-tagging+"); + if (bool(plugin_features & Extension::savecore)) + ret.push_back("qSaveCore+"); + + // check for client features + m_extensions_supported = {}; + for (llvm::StringRef x : client_features) + m_extensions_supported |= + llvm::StringSwitch<Extension>(x) + .Case("multiprocess+", Extension::multiprocess) + .Case("fork-events+", Extension::fork) + .Case("vfork-events+", Extension::vfork) + .Default({}); + + m_extensions_supported &= plugin_features; + + // fork & vfork require multiprocess + if (!bool(m_extensions_supported & Extension::multiprocess)) + m_extensions_supported &= ~(Extension::fork | Extension::vfork); + + // report only if actually supported + if (bool(m_extensions_supported & Extension::multiprocess)) + ret.push_back("multiprocess+"); + if (bool(m_extensions_supported & Extension::fork)) + ret.push_back("fork-events+"); + if (bool(m_extensions_supported & Extension::vfork)) + ret.push_back("vfork-events+"); + + for (auto &x : m_debugged_processes) + SetEnabledExtensions(*x.second.process_up); + return ret; +} + +void GDBRemoteCommunicationServerLLGS::SetEnabledExtensions( + NativeProcessProtocol &process) { + NativeProcessProtocol::Extension flags = m_extensions_supported; + assert(!bool(flags & ~m_process_manager.GetSupportedExtensions())); + process.SetEnabledExtensions(flags); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendContinueSuccessResponse() { + if (m_non_stop) + return SendOKResponse(); + StartSTDIOForwarding(); + return PacketResult::Success; +} + +void GDBRemoteCommunicationServerLLGS::AppendThreadIDToResponse( + Stream &response, lldb::pid_t pid, lldb::tid_t tid) { + if (bool(m_extensions_supported & + NativeProcessProtocol::Extension::multiprocess)) + response.Format("p{0:x-}.", pid); + response.Format("{0:x-}", tid); +} + +std::string +lldb_private::process_gdb_remote::LLGSArgToURL(llvm::StringRef url_arg, + bool reverse_connect) { + // Try parsing the argument as URL. + if (std::optional<URI> url = URI::Parse(url_arg)) { + if (reverse_connect) + return url_arg.str(); + + // Translate the scheme from LLGS notation to ConnectionFileDescriptor. + // If the scheme doesn't match any, pass it through to support using CFD + // schemes directly. + std::string new_url = llvm::StringSwitch<std::string>(url->scheme) + .Case("tcp", "listen") + .Case("unix", "unix-accept") + .Case("unix-abstract", "unix-abstract-accept") + .Default(url->scheme.str()); + llvm::append_range(new_url, url_arg.substr(url->scheme.size())); + return new_url; + } + + std::string host_port = url_arg.str(); + // If host_and_port starts with ':', default the host to be "localhost" and + // expect the remainder to be the port. + if (url_arg.starts_with(":")) + host_port.insert(0, "localhost"); + + // Try parsing the (preprocessed) argument as host:port pair. + if (!llvm::errorToBool(Socket::DecodeHostAndPort(host_port).takeError())) + return (reverse_connect ? "connect://" : "listen://") + host_port; + + // If none of the above applied, interpret the argument as UNIX socket path. + return (reverse_connect ? "unix-connect://" : "unix-accept://") + + url_arg.str(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h new file mode 100644 index 000000000000..646b6a102abf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -0,0 +1,342 @@ +//===-- GDBRemoteCommunicationServerLLGS.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVERLLGS_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVERLLGS_H + +#include <mutex> +#include <unordered_map> +#include <unordered_set> + +#include "lldb/Core/Communication.h" +#include "lldb/Host/MainLoop.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/lldb-private-forward.h" + +#include "GDBRemoteCommunicationServerCommon.h" + +class StringExtractorGDBRemote; + +namespace lldb_private { + +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class GDBRemoteCommunicationServerLLGS + : public GDBRemoteCommunicationServerCommon, + public NativeProcessProtocol::NativeDelegate { +public: + // Constructors and Destructors + GDBRemoteCommunicationServerLLGS( + MainLoop &mainloop, + NativeProcessProtocol::Manager &process_manager); + + void SetLaunchInfo(const ProcessLaunchInfo &info); + + /// Launch a process with the current launch settings. + /// + /// This method supports running an lldb-gdbserver or similar + /// server in a situation where the startup code has been provided + /// with all the information for a child process to be launched. + /// + /// \return + /// An Status object indicating the success or failure of the + /// launch. + Status LaunchProcess() override; + + /// Attach to a process. + /// + /// This method supports attaching llgs to a process accessible via the + /// configured Platform. + /// + /// \return + /// An Status object indicating the success or failure of the + /// attach operation. + Status AttachToProcess(lldb::pid_t pid); + + /// Wait to attach to a process with a given name. + /// + /// This method supports waiting for the next instance of a process + /// with a given name and attaching llgs to that via the configured + /// Platform. + /// + /// \return + /// An Status object indicating the success or failure of the + /// attach operation. + Status AttachWaitProcess(llvm::StringRef process_name, bool include_existing); + + // NativeProcessProtocol::NativeDelegate overrides + void InitializeDelegate(NativeProcessProtocol *process) override; + + void ProcessStateChanged(NativeProcessProtocol *process, + lldb::StateType state) override; + + void DidExec(NativeProcessProtocol *process) override; + + void + NewSubprocess(NativeProcessProtocol *parent_process, + std::unique_ptr<NativeProcessProtocol> child_process) override; + + Status InitializeConnection(std::unique_ptr<Connection> connection); + + struct DebuggedProcess { + enum class Flag { + vkilled = (1u << 0), + + LLVM_MARK_AS_BITMASK_ENUM(vkilled) + }; + + std::unique_ptr<NativeProcessProtocol> process_up; + Flag flags; + }; + +protected: + MainLoop &m_mainloop; + MainLoop::ReadHandleUP m_network_handle_up; + NativeProcessProtocol::Manager &m_process_manager; + lldb::tid_t m_current_tid = LLDB_INVALID_THREAD_ID; + lldb::tid_t m_continue_tid = LLDB_INVALID_THREAD_ID; + NativeProcessProtocol *m_current_process; + NativeProcessProtocol *m_continue_process; + std::recursive_mutex m_debugged_process_mutex; + std::unordered_map<lldb::pid_t, DebuggedProcess> m_debugged_processes; + + Communication m_stdio_communication; + MainLoop::ReadHandleUP m_stdio_handle_up; + + llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> m_xfer_buffer_map; + std::mutex m_saved_registers_mutex; + std::unordered_map<uint32_t, lldb::DataBufferSP> m_saved_registers_map; + uint32_t m_next_saved_registers_id = 1; + bool m_thread_suffix_supported = false; + bool m_list_threads_in_stop_reply = false; + bool m_non_stop = false; + bool m_disabling_non_stop = false; + std::deque<std::string> m_stdio_notification_queue; + std::deque<std::string> m_stop_notification_queue; + + NativeProcessProtocol::Extension m_extensions_supported = {}; + + // Typically we would use a SmallVector for this data but in this context we + // don't know how much data we're recieving so we would have to heap allocate + // a lot, or have a very large stack frame. So it's a member instead. + uint8_t m_reg_bytes[RegisterValue::kMaxRegisterByteSize]; + + PacketResult SendONotification(const char *buffer, uint32_t len); + + PacketResult SendWResponse(NativeProcessProtocol *process); + + StreamString PrepareStopReplyPacketForThread(NativeThreadProtocol &thread); + + PacketResult SendStopReplyPacketForThread(NativeProcessProtocol &process, + lldb::tid_t tid, + bool force_synchronous); + + PacketResult SendStopReasonForState(NativeProcessProtocol &process, + lldb::StateType process_state, + bool force_synchronous); + + void EnqueueStopReplyPackets(lldb::tid_t thread_to_skip); + + PacketResult Handle_k(StringExtractorGDBRemote &packet); + + PacketResult Handle_vKill(StringExtractorGDBRemote &packet); + + PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_qC(StringExtractorGDBRemote &packet); + + PacketResult Handle_QSetDisableASLR(StringExtractorGDBRemote &packet); + + PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet); + + PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet); + + PacketResult Handle_QThreadSuffixSupported(StringExtractorGDBRemote &packet); + + PacketResult Handle_QListThreadsInStopReply(StringExtractorGDBRemote &packet); + + PacketResult ResumeProcess(NativeProcessProtocol &process, + const ResumeActionList &actions); + + PacketResult Handle_C(StringExtractorGDBRemote &packet); + + PacketResult Handle_c(StringExtractorGDBRemote &packet); + + PacketResult Handle_vCont(StringExtractorGDBRemote &packet); + + PacketResult Handle_vCont_actions(StringExtractorGDBRemote &packet); + + PacketResult Handle_stop_reason(StringExtractorGDBRemote &packet); + + PacketResult Handle_qRegisterInfo(StringExtractorGDBRemote &packet); + + void AddProcessThreads(StreamGDBRemote &response, + NativeProcessProtocol &process, bool &had_any); + + PacketResult Handle_qfThreadInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_qsThreadInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_p(StringExtractorGDBRemote &packet); + + PacketResult Handle_P(StringExtractorGDBRemote &packet); + + PacketResult Handle_H(StringExtractorGDBRemote &packet); + + PacketResult Handle_I(StringExtractorGDBRemote &packet); + + PacketResult Handle_interrupt(StringExtractorGDBRemote &packet); + + // Handles $m and $x packets. + PacketResult Handle_memory_read(StringExtractorGDBRemote &packet); + + PacketResult Handle_M(StringExtractorGDBRemote &packet); + PacketResult Handle__M(StringExtractorGDBRemote &packet); + PacketResult Handle__m(StringExtractorGDBRemote &packet); + + PacketResult + Handle_qMemoryRegionInfoSupported(StringExtractorGDBRemote &packet); + + PacketResult Handle_qMemoryRegionInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_Z(StringExtractorGDBRemote &packet); + + PacketResult Handle_z(StringExtractorGDBRemote &packet); + + PacketResult Handle_s(StringExtractorGDBRemote &packet); + + PacketResult Handle_qXfer(StringExtractorGDBRemote &packet); + + PacketResult Handle_QSaveRegisterState(StringExtractorGDBRemote &packet); + + PacketResult Handle_jLLDBTraceSupported(StringExtractorGDBRemote &packet); + + PacketResult Handle_jLLDBTraceStart(StringExtractorGDBRemote &packet); + + PacketResult Handle_jLLDBTraceStop(StringExtractorGDBRemote &packet); + + PacketResult Handle_jLLDBTraceGetState(StringExtractorGDBRemote &packet); + + PacketResult Handle_jLLDBTraceGetBinaryData(StringExtractorGDBRemote &packet); + + PacketResult Handle_QRestoreRegisterState(StringExtractorGDBRemote &packet); + + PacketResult Handle_vAttach(StringExtractorGDBRemote &packet); + + PacketResult Handle_vAttachWait(StringExtractorGDBRemote &packet); + + PacketResult Handle_qVAttachOrWaitSupported(StringExtractorGDBRemote &packet); + + PacketResult Handle_vAttachOrWait(StringExtractorGDBRemote &packet); + + PacketResult Handle_vRun(StringExtractorGDBRemote &packet); + + PacketResult Handle_D(StringExtractorGDBRemote &packet); + + PacketResult Handle_qThreadStopInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_jThreadsInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_qWatchpointSupportInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet); + + PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet); + + PacketResult Handle_qSaveCore(StringExtractorGDBRemote &packet); + + PacketResult Handle_QNonStop(StringExtractorGDBRemote &packet); + + PacketResult HandleNotificationAck(std::deque<std::string> &queue); + + PacketResult Handle_vStdio(StringExtractorGDBRemote &packet); + + PacketResult Handle_vStopped(StringExtractorGDBRemote &packet); + + PacketResult Handle_vCtrlC(StringExtractorGDBRemote &packet); + + PacketResult Handle_g(StringExtractorGDBRemote &packet); + + PacketResult Handle_qMemTags(StringExtractorGDBRemote &packet); + + PacketResult Handle_QMemTags(StringExtractorGDBRemote &packet); + + PacketResult Handle_T(StringExtractorGDBRemote &packet); + + void SetCurrentThreadID(lldb::tid_t tid); + + lldb::tid_t GetCurrentThreadID() const; + + void SetContinueThreadID(lldb::tid_t tid); + + lldb::tid_t GetContinueThreadID() const { return m_continue_tid; } + + Status SetSTDIOFileDescriptor(int fd); + + FileSpec FindModuleFile(const std::string &module_path, + const ArchSpec &arch) override; + + llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> + ReadXferObject(llvm::StringRef object, llvm::StringRef annex); + + static std::string XMLEncodeAttributeValue(llvm::StringRef value); + + std::vector<std::string> HandleFeatures( + const llvm::ArrayRef<llvm::StringRef> client_features) override; + + // Provide a response for successful continue action, i.e. send "OK" + // in non-stop mode, no response otherwise. + PacketResult SendContinueSuccessResponse(); + + void AppendThreadIDToResponse(Stream &response, lldb::pid_t pid, + lldb::tid_t tid); + +private: + llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> BuildTargetXml(); + + void HandleInferiorState_Exited(NativeProcessProtocol *process); + + void HandleInferiorState_Stopped(NativeProcessProtocol *process); + + NativeThreadProtocol *GetThreadFromSuffix(StringExtractorGDBRemote &packet); + + uint32_t GetNextSavedRegistersID(); + + void MaybeCloseInferiorTerminalConnection(); + + void ClearProcessSpecificData(); + + void RegisterPacketHandlers(); + + void DataAvailableCallback(); + + void SendProcessOutput(); + + void StartSTDIOForwarding(); + + void StopSTDIOForwarding(); + + // Call SetEnabledExtensions() with appropriate flags on the process. + void SetEnabledExtensions(NativeProcessProtocol &process); + + // For GDBRemoteCommunicationServerLLGS only + GDBRemoteCommunicationServerLLGS(const GDBRemoteCommunicationServerLLGS &) = + delete; + const GDBRemoteCommunicationServerLLGS & + operator=(const GDBRemoteCommunicationServerLLGS &) = delete; +}; + +std::string LLGSArgToURL(llvm::StringRef url_arg, bool reverse_connect); + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVERLLGS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp new file mode 100644 index 000000000000..65f1cc12ba30 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -0,0 +1,606 @@ +//===-- GDBRemoteCommunicationServerPlatform.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 "GDBRemoteCommunicationServerPlatform.h" + +#include <cerrno> + +#include <chrono> +#include <csignal> +#include <cstring> +#include <mutex> +#include <optional> +#include <sstream> +#include <thread> + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Threading.h" + +#include "lldb/Host/Config.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileAction.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/GDBRemote.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/TildeExpressionResolver.h" +#include "lldb/Utility/UriParser.h" + +#include "lldb/Utility/StringExtractorGDBRemote.h" + +using namespace lldb; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private; + +GDBRemoteCommunicationServerPlatform::PortMap::PortMap(uint16_t min_port, + uint16_t max_port) { + assert(min_port); + for (; min_port < max_port; ++min_port) + m_port_map[min_port] = LLDB_INVALID_PROCESS_ID; +} + +void GDBRemoteCommunicationServerPlatform::PortMap::AllowPort(uint16_t port) { + assert(port); + // Do not modify existing mappings + m_port_map.insert({port, LLDB_INVALID_PROCESS_ID}); +} + +llvm::Expected<uint16_t> +GDBRemoteCommunicationServerPlatform::PortMap::GetNextAvailablePort() { + if (m_port_map.empty()) + return 0; // Bind to port zero and get a port, we didn't have any + // limitations + + for (auto &pair : m_port_map) { + if (pair.second == LLDB_INVALID_PROCESS_ID) { + pair.second = ~(lldb::pid_t)LLDB_INVALID_PROCESS_ID; + return pair.first; + } + } + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "No free port found in port map"); +} + +bool GDBRemoteCommunicationServerPlatform::PortMap::AssociatePortWithProcess( + uint16_t port, lldb::pid_t pid) { + auto pos = m_port_map.find(port); + if (pos != m_port_map.end()) { + pos->second = pid; + return true; + } + return false; +} + +bool GDBRemoteCommunicationServerPlatform::PortMap::FreePort(uint16_t port) { + std::map<uint16_t, lldb::pid_t>::iterator pos = m_port_map.find(port); + if (pos != m_port_map.end()) { + pos->second = LLDB_INVALID_PROCESS_ID; + return true; + } + return false; +} + +bool GDBRemoteCommunicationServerPlatform::PortMap::FreePortForProcess( + lldb::pid_t pid) { + if (!m_port_map.empty()) { + for (auto &pair : m_port_map) { + if (pair.second == pid) { + pair.second = LLDB_INVALID_PROCESS_ID; + return true; + } + } + } + return false; +} + +bool GDBRemoteCommunicationServerPlatform::PortMap::empty() const { + return m_port_map.empty(); +} + +// GDBRemoteCommunicationServerPlatform constructor +GDBRemoteCommunicationServerPlatform::GDBRemoteCommunicationServerPlatform( + const Socket::SocketProtocol socket_protocol, const char *socket_scheme) + : GDBRemoteCommunicationServerCommon(), + m_socket_protocol(socket_protocol), m_socket_scheme(socket_scheme), + m_spawned_pids_mutex(), m_port_map(), m_port_offset(0) { + m_pending_gdb_server.pid = LLDB_INVALID_PROCESS_ID; + m_pending_gdb_server.port = 0; + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qC, + &GDBRemoteCommunicationServerPlatform::Handle_qC); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qGetWorkingDir, + &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer, + &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer, + &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess, + &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qProcessInfo, + &GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qPathComplete, + &GDBRemoteCommunicationServerPlatform::Handle_qPathComplete); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir, + &GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_jSignalsInfo, + &GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo); + + RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_interrupt, + [](StringExtractorGDBRemote packet, Status &error, + bool &interrupt, bool &quit) { + error.SetErrorString("interrupt received"); + interrupt = true; + return PacketResult::Success; + }); +} + +// Destructor +GDBRemoteCommunicationServerPlatform::~GDBRemoteCommunicationServerPlatform() = + default; + +Status GDBRemoteCommunicationServerPlatform::LaunchGDBServer( + const lldb_private::Args &args, std::string hostname, lldb::pid_t &pid, + std::optional<uint16_t> &port, std::string &socket_name) { + if (!port) { + llvm::Expected<uint16_t> available_port = m_port_map.GetNextAvailablePort(); + if (available_port) + port = *available_port; + else + return Status(available_port.takeError()); + } + + // Spawn a new thread to accept the port that gets bound after binding to + // port 0 (zero). + + // ignore the hostname send from the remote end, just use the ip address that + // we're currently communicating with as the hostname + + // Spawn a debugserver and try to get the port it listens to. + ProcessLaunchInfo debugserver_launch_info; + if (hostname.empty()) + hostname = "127.0.0.1"; + + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, "Launching debugserver with: %s:%u...", hostname.c_str(), + *port); + + // Do not run in a new session so that it can not linger after the platform + // closes. + debugserver_launch_info.SetLaunchInSeparateProcessGroup(false); + debugserver_launch_info.SetMonitorProcessCallback( + std::bind(&GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, + this, std::placeholders::_1)); + + std::ostringstream url; +// debugserver does not accept the URL scheme prefix. +#if !defined(__APPLE__) + url << m_socket_scheme << "://"; +#endif + uint16_t *port_ptr = &*port; + if (m_socket_protocol == Socket::ProtocolTcp) { + std::string platform_uri = GetConnection()->GetURI(); + std::optional<URI> parsed_uri = URI::Parse(platform_uri); + url << '[' << parsed_uri->hostname.str() << "]:" << *port; + } else { + socket_name = GetDomainSocketPath("gdbserver").GetPath(); + url << socket_name; + port_ptr = nullptr; + } + + Status error = StartDebugserverProcess( + url.str().c_str(), nullptr, debugserver_launch_info, port_ptr, &args, -1); + + pid = debugserver_launch_info.GetProcessID(); + if (pid != LLDB_INVALID_PROCESS_ID) { + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); + m_spawned_pids.insert(pid); + if (*port > 0) + m_port_map.AssociatePortWithProcess(*port, pid); + } else { + if (*port > 0) + m_port_map.FreePort(*port); + } + return error; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer( + StringExtractorGDBRemote &packet) { + // Spawn a local debugserver as a platform so we can then attach or launch a + // process... + + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, "GDBRemoteCommunicationServerPlatform::%s() called", + __FUNCTION__); + + ConnectionFileDescriptor file_conn; + std::string hostname; + packet.SetFilePos(::strlen("qLaunchGDBServer;")); + llvm::StringRef name; + llvm::StringRef value; + std::optional<uint16_t> port; + while (packet.GetNameColonValue(name, value)) { + if (name == "host") + hostname = std::string(value); + else if (name == "port") { + // Make the Optional valid so we can use its value + port = 0; + value.getAsInteger(0, *port); + } + } + + lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; + std::string socket_name; + Status error = + LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name); + if (error.Fail()) { + LLDB_LOGF(log, + "GDBRemoteCommunicationServerPlatform::%s() debugserver " + "launch failed: %s", + __FUNCTION__, error.AsCString()); + return SendErrorResponse(9); + } + + LLDB_LOGF(log, + "GDBRemoteCommunicationServerPlatform::%s() debugserver " + "launched successfully as pid %" PRIu64, + __FUNCTION__, debugserver_pid); + + StreamGDBRemote response; + assert(port); + response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid, + *port + m_port_offset); + if (!socket_name.empty()) { + response.PutCString("socket_name:"); + response.PutStringAsRawHex8(socket_name); + response.PutChar(';'); + } + + PacketResult packet_result = SendPacketNoLock(response.GetString()); + if (packet_result != PacketResult::Success) { + if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + Host::Kill(debugserver_pid, SIGINT); + } + return packet_result; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer( + StringExtractorGDBRemote &packet) { + namespace json = llvm::json; + + if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(4); + + json::Object server{{"port", m_pending_gdb_server.port}}; + + if (!m_pending_gdb_server.socket_name.empty()) + server.try_emplace("socket_name", m_pending_gdb_server.socket_name); + + json::Array server_list; + server_list.push_back(std::move(server)); + + StreamGDBRemote response; + response.AsRawOstream() << std::move(server_list); + + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetString().data(), + response.GetSize()); + return SendPacketNoLock(escaped_response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("qKillSpawnedProcess:")); + + lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); + + // verify that we know anything about this pid. Scope for locker + { + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { + // not a pid we know about + return SendErrorResponse(10); + } + } + + // go ahead and attempt to kill the spawned process + if (KillSpawnedProcess(pid)) + return SendOKResponse(); + else + return SendErrorResponse(11); +} + +bool GDBRemoteCommunicationServerPlatform::KillSpawnedProcess(lldb::pid_t pid) { + // make sure we know about this process + { + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return false; + } + + // first try a SIGTERM (standard kill) + Host::Kill(pid, SIGTERM); + + // check if that worked + for (size_t i = 0; i < 10; ++i) { + { + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { + // it is now killed + return true; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + { + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + + // the launched process still lives. Now try killing it again, this time + // with an unblockable signal. + Host::Kill(pid, SIGKILL); + + for (size_t i = 0; i < 10; ++i) { + { + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) { + // it is now killed + return true; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + // check one more time after the final sleep + { + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + + // no luck - the process still lives + return false; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qProcessInfo( + StringExtractorGDBRemote &packet) { + lldb::pid_t pid = m_process_launch_info.GetProcessID(); + m_process_launch_info.Clear(); + + if (pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(1); + + ProcessInstanceInfo proc_info; + if (!Host::GetProcessInfo(pid, proc_info)) + return SendErrorResponse(1); + + StreamString response; + CreateProcessInfoResponse_DebugServerStyle(proc_info, response); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qPathComplete( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("qPathComplete:")); + const bool only_dir = (packet.GetHexMaxU32(false, 0) == 1); + if (packet.GetChar() != ',') + return SendErrorResponse(85); + std::string path; + packet.GetHexByteString(path); + + StringList matches; + StandardTildeExpressionResolver resolver; + if (only_dir) + CommandCompletions::DiskDirectories(path, matches, resolver); + else + CommandCompletions::DiskFiles(path, matches, resolver); + + StreamString response; + response.PutChar('M'); + llvm::StringRef separator; + std::sort(matches.begin(), matches.end()); + for (const auto &match : matches) { + response << separator; + separator = ","; + // encode result strings into hex bytes to avoid unexpected error caused by + // special characters like '$'. + response.PutStringAsRawHex8(match.c_str()); + } + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir( + StringExtractorGDBRemote &packet) { + + llvm::SmallString<64> cwd; + if (std::error_code ec = llvm::sys::fs::current_path(cwd)) + return SendErrorResponse(ec.value()); + + StreamString response; + response.PutBytesAsRawHex8(cwd.data(), cwd.size()); + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_QSetWorkingDir( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("QSetWorkingDir:")); + std::string path; + packet.GetHexByteString(path); + + if (std::error_code ec = llvm::sys::fs::set_current_path(path)) + return SendErrorResponse(ec.value()); + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qC( + StringExtractorGDBRemote &packet) { + // NOTE: lldb should now be using qProcessInfo for process IDs. This path + // here + // should not be used. It is reporting process id instead of thread id. The + // correct answer doesn't seem to make much sense for lldb-platform. + // CONSIDER: flip to "unsupported". + lldb::pid_t pid = m_process_launch_info.GetProcessID(); + + StreamString response; + response.Printf("QC%" PRIx64, pid); + + // If we launch a process and this GDB server is acting as a platform, then + // we need to clear the process launch state so we can start launching + // another process. In order to launch a process a bunch or packets need to + // be sent: environment packets, working directory, disable ASLR, and many + // more settings. When we launch a process we then need to know when to clear + // this information. Currently we are selecting the 'qC' packet as that + // packet which seems to make the most sense. + if (pid != LLDB_INVALID_PROCESS_ID) { + m_process_launch_info.Clear(); + } + + return SendPacketNoLock(response.GetString()); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_jSignalsInfo( + StringExtractorGDBRemote &packet) { + StructuredData::Array signal_array; + + lldb::UnixSignalsSP signals = UnixSignals::CreateForHost(); + for (auto signo = signals->GetFirstSignalNumber(); + signo != LLDB_INVALID_SIGNAL_NUMBER; + signo = signals->GetNextSignalNumber(signo)) { + auto dictionary = std::make_shared<StructuredData::Dictionary>(); + + dictionary->AddIntegerItem("signo", signo); + dictionary->AddStringItem("name", signals->GetSignalAsStringRef(signo)); + + bool suppress, stop, notify; + signals->GetSignalInfo(signo, suppress, stop, notify); + dictionary->AddBooleanItem("suppress", suppress); + dictionary->AddBooleanItem("stop", stop); + dictionary->AddBooleanItem("notify", notify); + + signal_array.Push(dictionary); + } + + StreamString response; + signal_array.Dump(response); + return SendPacketNoLock(response.GetString()); +} + +void GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped( + lldb::pid_t pid) { + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); + m_port_map.FreePortForProcess(pid); + m_spawned_pids.erase(pid); +} + +Status GDBRemoteCommunicationServerPlatform::LaunchProcess() { + if (!m_process_launch_info.GetArguments().GetArgumentCount()) + return Status("%s: no process command line specified to launch", + __FUNCTION__); + + // specify the process monitor if not already set. This should generally be + // what happens since we need to reap started processes. + if (!m_process_launch_info.GetMonitorProcessCallback()) + m_process_launch_info.SetMonitorProcessCallback(std::bind( + &GDBRemoteCommunicationServerPlatform::DebugserverProcessReaped, this, + std::placeholders::_1)); + + Status error = Host::LaunchProcess(m_process_launch_info); + if (!error.Success()) { + fprintf(stderr, "%s: failed to launch executable %s", __FUNCTION__, + m_process_launch_info.GetArguments().GetArgumentAtIndex(0)); + return error; + } + + printf("Launched '%s' as process %" PRIu64 "...\n", + m_process_launch_info.GetArguments().GetArgumentAtIndex(0), + m_process_launch_info.GetProcessID()); + + // add to list of spawned processes. On an lldb-gdbserver, we would expect + // there to be only one. + const auto pid = m_process_launch_info.GetProcessID(); + if (pid != LLDB_INVALID_PROCESS_ID) { + // add to spawned pids + std::lock_guard<std::recursive_mutex> guard(m_spawned_pids_mutex); + m_spawned_pids.insert(pid); + } + + return error; +} + +void GDBRemoteCommunicationServerPlatform::SetPortMap(PortMap &&port_map) { + m_port_map = std::move(port_map); +} + +const FileSpec &GDBRemoteCommunicationServerPlatform::GetDomainSocketDir() { + static FileSpec g_domainsocket_dir; + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + const char *domainsocket_dir_env = + ::getenv("LLDB_DEBUGSERVER_DOMAINSOCKET_DIR"); + if (domainsocket_dir_env != nullptr) + g_domainsocket_dir = FileSpec(domainsocket_dir_env); + else + g_domainsocket_dir = HostInfo::GetProcessTempDir(); + }); + + return g_domainsocket_dir; +} + +FileSpec +GDBRemoteCommunicationServerPlatform::GetDomainSocketPath(const char *prefix) { + llvm::SmallString<128> socket_path; + llvm::SmallString<128> socket_name( + (llvm::StringRef(prefix) + ".%%%%%%").str()); + + FileSpec socket_path_spec(GetDomainSocketDir()); + socket_path_spec.AppendPathComponent(socket_name.c_str()); + + llvm::sys::fs::createUniqueFile(socket_path_spec.GetPath().c_str(), + socket_path); + return FileSpec(socket_path.c_str()); +} + +void GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) { + m_port_offset = port_offset; +} + +void GDBRemoteCommunicationServerPlatform::SetPendingGdbServer( + lldb::pid_t pid, uint16_t port, const std::string &socket_name) { + m_pending_gdb_server.pid = pid; + m_pending_gdb_server.port = port; + m_pending_gdb_server.socket_name = socket_name; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h new file mode 100644 index 000000000000..1853025466cf --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h @@ -0,0 +1,149 @@ +//===-- GDBRemoteCommunicationServerPlatform.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVERPLATFORM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVERPLATFORM_H + +#include <map> +#include <mutex> +#include <optional> +#include <set> + +#include "GDBRemoteCommunicationServerCommon.h" +#include "lldb/Host/Socket.h" + +#include "llvm/Support/Error.h" + +namespace lldb_private { +namespace process_gdb_remote { + +class GDBRemoteCommunicationServerPlatform + : public GDBRemoteCommunicationServerCommon { +public: + class PortMap { + public: + // This class is used to restrict the range of ports that + // platform created debugserver/gdbserver processes will + // communicate on. + + // Construct an empty map, where empty means any port is allowed. + PortMap() = default; + + // Make a port map with a range of free ports + // from min_port to max_port-1. + PortMap(uint16_t min_port, uint16_t max_port); + + // Add a port to the map. If it is already in the map do not modify + // its mapping. (used ports remain used, new ports start as free) + void AllowPort(uint16_t port); + + // If we are using a port map where we can only use certain ports, + // get the next available port. + // + // If we are using a port map and we are out of ports, return an error. + // + // If we aren't using a port map, return 0 to indicate we should bind to + // port 0 and then figure out which port we used. + llvm::Expected<uint16_t> GetNextAvailablePort(); + + // Tie a port to a process ID. Returns false if the port is not in the port + // map. If the port is already in use it will be moved to the given pid. + // FIXME: This is and GetNextAvailablePort make create a race condition if + // the portmap is shared between processes. + bool AssociatePortWithProcess(uint16_t port, lldb::pid_t pid); + + // Free the given port. Returns false if the port is not in the map. + bool FreePort(uint16_t port); + + // Free the port associated with the given pid. Returns false if there is + // no port associated with the pid. + bool FreePortForProcess(lldb::pid_t pid); + + // Returns true if there are no ports in the map, regardless of the state + // of those ports. Meaning a map with 1 used port is not empty. + bool empty() const; + + private: + std::map<uint16_t, lldb::pid_t> m_port_map; + }; + + GDBRemoteCommunicationServerPlatform( + const Socket::SocketProtocol socket_protocol, const char *socket_scheme); + + ~GDBRemoteCommunicationServerPlatform() override; + + Status LaunchProcess() override; + + // Set both ports to zero to let the platform automatically bind to + // a port chosen by the OS. + void SetPortMap(PortMap &&port_map); + + void SetPortOffset(uint16_t port_offset); + + void SetInferiorArguments(const lldb_private::Args &args); + + // Set port if you want to use a specific port number. + // Otherwise port will be set to the port that was chosen for you. + Status LaunchGDBServer(const lldb_private::Args &args, std::string hostname, + lldb::pid_t &pid, std::optional<uint16_t> &port, + std::string &socket_name); + + void SetPendingGdbServer(lldb::pid_t pid, uint16_t port, + const std::string &socket_name); + +protected: + const Socket::SocketProtocol m_socket_protocol; + const std::string m_socket_scheme; + std::recursive_mutex m_spawned_pids_mutex; + std::set<lldb::pid_t> m_spawned_pids; + + PortMap m_port_map; + uint16_t m_port_offset; + struct { + lldb::pid_t pid; + uint16_t port; + std::string socket_name; + } m_pending_gdb_server; + + PacketResult Handle_qLaunchGDBServer(StringExtractorGDBRemote &packet); + + PacketResult Handle_qQueryGDBServer(StringExtractorGDBRemote &packet); + + PacketResult Handle_qKillSpawnedProcess(StringExtractorGDBRemote &packet); + + PacketResult Handle_qPathComplete(StringExtractorGDBRemote &packet); + + PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet); + + PacketResult Handle_qGetWorkingDir(StringExtractorGDBRemote &packet); + + PacketResult Handle_QSetWorkingDir(StringExtractorGDBRemote &packet); + + PacketResult Handle_qC(StringExtractorGDBRemote &packet); + + PacketResult Handle_jSignalsInfo(StringExtractorGDBRemote &packet); + +private: + bool KillSpawnedProcess(lldb::pid_t pid); + + void DebugserverProcessReaped(lldb::pid_t pid); + + static const FileSpec &GetDomainSocketDir(); + + static FileSpec GetDomainSocketPath(const char *prefix); + + GDBRemoteCommunicationServerPlatform( + const GDBRemoteCommunicationServerPlatform &) = delete; + const GDBRemoteCommunicationServerPlatform & + operator=(const GDBRemoteCommunicationServerPlatform &) = delete; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECOMMUNICATIONSERVERPLATFORM_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteErrno.def b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteErrno.def new file mode 100644 index 000000000000..e26d23fdad0c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteErrno.def @@ -0,0 +1,39 @@ +//===-- GDBRemoteErrno.def --------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +// NOTE: NO INCLUDE GUARD DESIRED! + +// HANDLE_ERRNO(name, value) +#ifndef HANDLE_ERRNO +#error "HANDLE_ERRNO must be defined" +#endif + +// from gdb's include/gdb/fileio.h +HANDLE_ERRNO(EPERM, 1) +HANDLE_ERRNO(ENOENT, 2) +HANDLE_ERRNO(EINTR, 4) +HANDLE_ERRNO(EIO, 5) +HANDLE_ERRNO(EBADF, 9) +HANDLE_ERRNO(EACCES, 13) +HANDLE_ERRNO(EFAULT, 14) +HANDLE_ERRNO(EBUSY, 16) +HANDLE_ERRNO(EEXIST, 17) +HANDLE_ERRNO(ENODEV, 19) +HANDLE_ERRNO(ENOTDIR, 20) +HANDLE_ERRNO(EISDIR, 21) +HANDLE_ERRNO(EINVAL, 22) +HANDLE_ERRNO(ENFILE, 23) +HANDLE_ERRNO(EMFILE, 24) +HANDLE_ERRNO(EFBIG, 27) +HANDLE_ERRNO(ENOSPC, 28) +HANDLE_ERRNO(ESPIPE, 29) +HANDLE_ERRNO(EROFS, 30) +HANDLE_ERRNO(ENOSYS, 88) +HANDLE_ERRNO(ENAMETOOLONG, 91) + +#undef HANDLE_ERRNO diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp new file mode 100644 index 000000000000..e9bd65fad150 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -0,0 +1,777 @@ +//===-- GDBRemoteRegisterContext.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 "GDBRemoteRegisterContext.h" + +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "ThreadGDBRemote.h" +#include "Utility/ARM_DWARF_Registers.h" +#include "Utility/ARM_ehframe_Registers.h" +#include "lldb/Core/Architecture.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +// GDBRemoteRegisterContext constructor +GDBRemoteRegisterContext::GDBRemoteRegisterContext( + ThreadGDBRemote &thread, uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfoSP reg_info_sp, bool read_all_at_once, + bool write_all_at_once) + : RegisterContext(thread, concrete_frame_idx), + m_reg_info_sp(std::move(reg_info_sp)), m_reg_valid(), m_reg_data(), + m_read_all_at_once(read_all_at_once), + m_write_all_at_once(write_all_at_once), m_gpacket_cached(false) { + // Resize our vector of bools to contain one bool for every register. We will + // use these boolean values to know when a register value is valid in + // m_reg_data. + m_reg_valid.resize(m_reg_info_sp->GetNumRegisters()); + + // Make a heap based buffer that is big enough to store all registers + DataBufferSP reg_data_sp( + new DataBufferHeap(m_reg_info_sp->GetRegisterDataByteSize(), 0)); + m_reg_data.SetData(reg_data_sp); + m_reg_data.SetByteOrder(thread.GetProcess()->GetByteOrder()); +} + +// Destructor +GDBRemoteRegisterContext::~GDBRemoteRegisterContext() = default; + +void GDBRemoteRegisterContext::InvalidateAllRegisters() { + SetAllRegisterValid(false); +} + +void GDBRemoteRegisterContext::SetAllRegisterValid(bool b) { + m_gpacket_cached = b; + std::vector<bool>::iterator pos, end = m_reg_valid.end(); + for (pos = m_reg_valid.begin(); pos != end; ++pos) + *pos = b; +} + +size_t GDBRemoteRegisterContext::GetRegisterCount() { + return m_reg_info_sp->GetNumRegisters(); +} + +const RegisterInfo * +GDBRemoteRegisterContext::GetRegisterInfoAtIndex(size_t reg) { + return m_reg_info_sp->GetRegisterInfoAtIndex(reg); +} + +size_t GDBRemoteRegisterContext::GetRegisterSetCount() { + return m_reg_info_sp->GetNumRegisterSets(); +} + +const RegisterSet *GDBRemoteRegisterContext::GetRegisterSet(size_t reg_set) { + return m_reg_info_sp->GetRegisterSet(reg_set); +} + +bool GDBRemoteRegisterContext::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + // Read the register + if (ReadRegisterBytes(reg_info)) { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + if (m_reg_valid[reg] == false) + return false; + if (reg_info->value_regs && + reg_info->value_regs[0] != LLDB_INVALID_REGNUM && + reg_info->value_regs[1] != LLDB_INVALID_REGNUM) { + std::vector<char> combined_data; + uint32_t offset = 0; + for (int i = 0; reg_info->value_regs[i] != LLDB_INVALID_REGNUM; i++) { + const RegisterInfo *parent_reg = GetRegisterInfo( + eRegisterKindLLDB, reg_info->value_regs[i]); + if (!parent_reg) + return false; + combined_data.resize(offset + parent_reg->byte_size); + if (m_reg_data.CopyData(parent_reg->byte_offset, parent_reg->byte_size, + combined_data.data() + offset) != + parent_reg->byte_size) + return false; + offset += parent_reg->byte_size; + } + + Status error; + return value.SetFromMemoryData( + *reg_info, combined_data.data(), combined_data.size(), + m_reg_data.GetByteOrder(), error) == combined_data.size(); + } else { + const bool partial_data_ok = false; + Status error(value.SetValueFromData( + *reg_info, m_reg_data, reg_info->byte_offset, partial_data_ok)); + return error.Success(); + } + } + return false; +} + +bool GDBRemoteRegisterContext::PrivateSetRegisterValue( + uint32_t reg, llvm::ArrayRef<uint8_t> data) { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); + if (reg_info == nullptr) + return false; + + // Invalidate if needed + InvalidateIfNeeded(false); + + const size_t reg_byte_size = reg_info->byte_size; + memcpy(const_cast<uint8_t *>( + m_reg_data.PeekData(reg_info->byte_offset, reg_byte_size)), + data.data(), std::min(data.size(), reg_byte_size)); + bool success = data.size() >= reg_byte_size; + if (success) { + SetRegisterIsValid(reg, true); + } else if (data.size() > 0) { + // Only set register is valid to false if we copied some bytes, else leave + // it as it was. + SetRegisterIsValid(reg, false); + } + return success; +} + +bool GDBRemoteRegisterContext::PrivateSetRegisterValue(uint32_t reg, + uint64_t new_reg_val) { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); + if (reg_info == nullptr) + return false; + + // Early in process startup, we can get a thread that has an invalid byte + // order because the process hasn't been completely set up yet (see the ctor + // where the byte order is setfrom the process). If that's the case, we + // can't set the value here. + if (m_reg_data.GetByteOrder() == eByteOrderInvalid) { + return false; + } + + // Invalidate if needed + InvalidateIfNeeded(false); + + DataBufferSP buffer_sp(new DataBufferHeap(&new_reg_val, sizeof(new_reg_val))); + DataExtractor data(buffer_sp, endian::InlHostByteOrder(), sizeof(void *)); + + // If our register context and our register info disagree, which should never + // happen, don't overwrite past the end of the buffer. + if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size) + return false; + + // Grab a pointer to where we are going to put this register + uint8_t *dst = const_cast<uint8_t *>( + m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size)); + + if (dst == nullptr) + return false; + + if (data.CopyByteOrderedData(0, // src offset + reg_info->byte_size, // src length + dst, // dst + reg_info->byte_size, // dst length + m_reg_data.GetByteOrder())) // dst byte order + { + SetRegisterIsValid(reg, true); + return true; + } + return false; +} + +// Helper function for GDBRemoteRegisterContext::ReadRegisterBytes(). +bool GDBRemoteRegisterContext::GetPrimordialRegister( + const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) { + const uint32_t lldb_reg = reg_info->kinds[eRegisterKindLLDB]; + const uint32_t remote_reg = reg_info->kinds[eRegisterKindProcessPlugin]; + + if (DataBufferSP buffer_sp = + gdb_comm.ReadRegister(m_thread.GetProtocolID(), remote_reg)) + return PrivateSetRegisterValue( + lldb_reg, llvm::ArrayRef<uint8_t>(buffer_sp->GetBytes(), + buffer_sp->GetByteSize())); + return false; +} + +bool GDBRemoteRegisterContext::ReadRegisterBytes(const RegisterInfo *reg_info) { + ExecutionContext exe_ctx(CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == nullptr || thread == nullptr) + return false; + + GDBRemoteCommunicationClient &gdb_comm( + ((ProcessGDBRemote *)process)->GetGDBRemote()); + + InvalidateIfNeeded(false); + + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + + if (!GetRegisterIsValid(reg)) { + if (m_read_all_at_once && !m_gpacket_cached) { + if (DataBufferSP buffer_sp = + gdb_comm.ReadAllRegisters(m_thread.GetProtocolID())) { + memcpy(const_cast<uint8_t *>(m_reg_data.GetDataStart()), + buffer_sp->GetBytes(), + std::min(buffer_sp->GetByteSize(), m_reg_data.GetByteSize())); + if (buffer_sp->GetByteSize() >= m_reg_data.GetByteSize()) { + SetAllRegisterValid(true); + return true; + } else if (buffer_sp->GetByteSize() > 0) { + for (auto x : llvm::enumerate( + m_reg_info_sp->registers< + DynamicRegisterInfo::reg_collection_const_range>())) { + const struct RegisterInfo ®info = x.value(); + m_reg_valid[x.index()] = + (reginfo.byte_offset + reginfo.byte_size <= + buffer_sp->GetByteSize()); + } + + m_gpacket_cached = true; + if (GetRegisterIsValid(reg)) + return true; + } else { + Log *log(GetLog(GDBRLog::Thread | GDBRLog::Packets)); + LLDB_LOGF( + log, + "error: GDBRemoteRegisterContext::ReadRegisterBytes tried " + "to read the " + "entire register context at once, expected at least %" PRId64 + " bytes " + "but only got %" PRId64 " bytes.", + m_reg_data.GetByteSize(), buffer_sp->GetByteSize()); + return false; + } + } + } + if (reg_info->value_regs) { + // Process this composite register request by delegating to the + // constituent primordial registers. + + // Index of the primordial register. + bool success = true; + for (uint32_t idx = 0; success; ++idx) { + const uint32_t prim_reg = reg_info->value_regs[idx]; + if (prim_reg == LLDB_INVALID_REGNUM) + break; + // We have a valid primordial register as our constituent. Grab the + // corresponding register info. + const RegisterInfo *prim_reg_info = + GetRegisterInfo(eRegisterKindLLDB, prim_reg); + if (prim_reg_info == nullptr) + success = false; + else { + // Read the containing register if it hasn't already been read + if (!GetRegisterIsValid(prim_reg)) + success = GetPrimordialRegister(prim_reg_info, gdb_comm); + } + } + + if (success) { + // If we reach this point, all primordial register requests have + // succeeded. Validate this composite register. + SetRegisterIsValid(reg_info, true); + } + } else { + // Get each register individually + GetPrimordialRegister(reg_info, gdb_comm); + } + + // Make sure we got a valid register value after reading it + if (!GetRegisterIsValid(reg)) + return false; + } + + return true; +} + +bool GDBRemoteRegisterContext::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + DataExtractor data; + if (value.GetData(data)) { + if (reg_info->value_regs && + reg_info->value_regs[0] != LLDB_INVALID_REGNUM && + reg_info->value_regs[1] != LLDB_INVALID_REGNUM) { + uint32_t combined_size = 0; + for (int i = 0; reg_info->value_regs[i] != LLDB_INVALID_REGNUM; i++) { + const RegisterInfo *parent_reg = GetRegisterInfo( + eRegisterKindLLDB, reg_info->value_regs[i]); + if (!parent_reg) + return false; + combined_size += parent_reg->byte_size; + } + + if (data.GetByteSize() < combined_size) + return false; + + uint32_t offset = 0; + for (int i = 0; reg_info->value_regs[i] != LLDB_INVALID_REGNUM; i++) { + const RegisterInfo *parent_reg = GetRegisterInfo( + eRegisterKindLLDB, reg_info->value_regs[i]); + assert(parent_reg); + + DataExtractor parent_data{data, offset, parent_reg->byte_size}; + if (!WriteRegisterBytes(parent_reg, parent_data, 0)) + return false; + offset += parent_reg->byte_size; + } + assert(offset == combined_size); + return true; + } else + return WriteRegisterBytes(reg_info, data, 0); + } + return false; +} + +// Helper function for GDBRemoteRegisterContext::WriteRegisterBytes(). +bool GDBRemoteRegisterContext::SetPrimordialRegister( + const RegisterInfo *reg_info, GDBRemoteCommunicationClient &gdb_comm) { + StreamString packet; + StringExtractorGDBRemote response; + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + // Invalidate just this register + SetRegisterIsValid(reg, false); + + return gdb_comm.WriteRegister( + m_thread.GetProtocolID(), reg_info->kinds[eRegisterKindProcessPlugin], + {m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size), + reg_info->byte_size}); +} + +bool GDBRemoteRegisterContext::WriteRegisterBytes(const RegisterInfo *reg_info, + DataExtractor &data, + uint32_t data_offset) { + ExecutionContext exe_ctx(CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == nullptr || thread == nullptr) + return false; + + GDBRemoteCommunicationClient &gdb_comm( + ((ProcessGDBRemote *)process)->GetGDBRemote()); + + assert(m_reg_data.GetByteSize() >= + reg_info->byte_offset + reg_info->byte_size); + + // If our register context and our register info disagree, which should never + // happen, don't overwrite past the end of the buffer. + if (m_reg_data.GetByteSize() < reg_info->byte_offset + reg_info->byte_size) + return false; + + // Grab a pointer to where we are going to put this register + uint8_t *dst = const_cast<uint8_t *>( + m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size)); + + if (dst == nullptr) + return false; + + const bool should_reconfigure_registers = + RegisterWriteCausesReconfigure(reg_info->name); + + if (data.CopyByteOrderedData(data_offset, // src offset + reg_info->byte_size, // src length + dst, // dst + reg_info->byte_size, // dst length + m_reg_data.GetByteOrder())) // dst byte order + { + GDBRemoteClientBase::Lock lock(gdb_comm); + if (lock) { + if (m_write_all_at_once) { + // Invalidate all register values + InvalidateIfNeeded(true); + + // Set all registers in one packet + if (gdb_comm.WriteAllRegisters( + m_thread.GetProtocolID(), + {m_reg_data.GetDataStart(), size_t(m_reg_data.GetByteSize())})) + + { + if (should_reconfigure_registers) + ReconfigureRegisterInfo(); + + InvalidateAllRegisters(); + + return true; + } + } else { + bool success = true; + + if (reg_info->value_regs) { + // This register is part of another register. In this case we read + // the actual register data for any "value_regs", and once all that + // data is read, we will have enough data in our register context + // bytes for the value of this register + + // Invalidate this composite register first. + + for (uint32_t idx = 0; success; ++idx) { + const uint32_t reg = reg_info->value_regs[idx]; + if (reg == LLDB_INVALID_REGNUM) + break; + // We have a valid primordial register as our constituent. Grab the + // corresponding register info. + const RegisterInfo *value_reg_info = + GetRegisterInfo(eRegisterKindLLDB, reg); + if (value_reg_info == nullptr) + success = false; + else + success = SetPrimordialRegister(value_reg_info, gdb_comm); + } + } else { + // This is an actual register, write it + success = SetPrimordialRegister(reg_info, gdb_comm); + } + + // Check if writing this register will invalidate any other register + // values? If so, invalidate them + if (reg_info->invalidate_regs) { + for (uint32_t idx = 0, reg = reg_info->invalidate_regs[0]; + reg != LLDB_INVALID_REGNUM; + reg = reg_info->invalidate_regs[++idx]) + SetRegisterIsValid(ConvertRegisterKindToRegisterNumber( + eRegisterKindLLDB, reg), + false); + } + + if (success && should_reconfigure_registers && + ReconfigureRegisterInfo()) + InvalidateAllRegisters(); + + return success; + } + } else { + Log *log(GetLog(GDBRLog::Thread | GDBRLog::Packets)); + if (log) { + if (log->GetVerbose()) { + StreamString strm; + process->DumpPluginHistory(strm); + LLDB_LOGF(log, + "error: failed to get packet sequence mutex, not sending " + "write register for \"%s\":\n%s", + reg_info->name, strm.GetData()); + } else + LLDB_LOGF(log, + "error: failed to get packet sequence mutex, not sending " + "write register for \"%s\"", + reg_info->name); + } + } + } + return false; +} + +bool GDBRemoteRegisterContext::ReadAllRegisterValues( + RegisterCheckpoint ®_checkpoint) { + ExecutionContext exe_ctx(CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == nullptr || thread == nullptr) + return false; + + GDBRemoteCommunicationClient &gdb_comm( + ((ProcessGDBRemote *)process)->GetGDBRemote()); + + uint32_t save_id = 0; + if (gdb_comm.SaveRegisterState(thread->GetProtocolID(), save_id)) { + reg_checkpoint.SetID(save_id); + reg_checkpoint.GetData().reset(); + return true; + } else { + reg_checkpoint.SetID(0); // Invalid save ID is zero + return ReadAllRegisterValues(reg_checkpoint.GetData()); + } +} + +bool GDBRemoteRegisterContext::WriteAllRegisterValues( + const RegisterCheckpoint ®_checkpoint) { + uint32_t save_id = reg_checkpoint.GetID(); + if (save_id != 0) { + ExecutionContext exe_ctx(CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == nullptr || thread == nullptr) + return false; + + GDBRemoteCommunicationClient &gdb_comm( + ((ProcessGDBRemote *)process)->GetGDBRemote()); + + return gdb_comm.RestoreRegisterState(m_thread.GetProtocolID(), save_id); + } else { + return WriteAllRegisterValues(reg_checkpoint.GetData()); + } +} + +bool GDBRemoteRegisterContext::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + ExecutionContext exe_ctx(CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == nullptr || thread == nullptr) + return false; + + GDBRemoteCommunicationClient &gdb_comm( + ((ProcessGDBRemote *)process)->GetGDBRemote()); + + const bool use_g_packet = + !gdb_comm.AvoidGPackets((ProcessGDBRemote *)process); + + GDBRemoteClientBase::Lock lock(gdb_comm); + if (lock) { + if (gdb_comm.SyncThreadState(m_thread.GetProtocolID())) + InvalidateAllRegisters(); + + if (use_g_packet) { + if (DataBufferSP data_buffer = + gdb_comm.ReadAllRegisters(m_thread.GetProtocolID())) { + data_sp = std::make_shared<DataBufferHeap>(*data_buffer); + return true; + } + } + + // We're going to read each register + // individually and store them as binary data in a buffer. + const RegisterInfo *reg_info; + + for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != nullptr; + i++) { + if (reg_info + ->value_regs) // skip registers that are slices of real registers + continue; + ReadRegisterBytes(reg_info); + // ReadRegisterBytes saves the contents of the register in to the + // m_reg_data buffer + } + data_sp = std::make_shared<DataBufferHeap>( + m_reg_data.GetDataStart(), m_reg_info_sp->GetRegisterDataByteSize()); + return true; + } else { + + Log *log(GetLog(GDBRLog::Thread | GDBRLog::Packets)); + if (log) { + if (log->GetVerbose()) { + StreamString strm; + process->DumpPluginHistory(strm); + LLDB_LOGF(log, + "error: failed to get packet sequence mutex, not sending " + "read all registers:\n%s", + strm.GetData()); + } else + LLDB_LOGF(log, + "error: failed to get packet sequence mutex, not sending " + "read all registers"); + } + } + + data_sp.reset(); + return false; +} + +bool GDBRemoteRegisterContext::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + if (!data_sp || data_sp->GetBytes() == nullptr || data_sp->GetByteSize() == 0) + return false; + + ExecutionContext exe_ctx(CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == nullptr || thread == nullptr) + return false; + + GDBRemoteCommunicationClient &gdb_comm( + ((ProcessGDBRemote *)process)->GetGDBRemote()); + + const bool use_g_packet = + !gdb_comm.AvoidGPackets((ProcessGDBRemote *)process); + + GDBRemoteClientBase::Lock lock(gdb_comm); + if (lock) { + // The data_sp contains the G response packet. + if (use_g_packet) { + if (gdb_comm.WriteAllRegisters( + m_thread.GetProtocolID(), + {data_sp->GetBytes(), size_t(data_sp->GetByteSize())})) + return true; + + uint32_t num_restored = 0; + // We need to manually go through all of the registers and restore them + // manually + DataExtractor restore_data(data_sp, m_reg_data.GetByteOrder(), + m_reg_data.GetAddressByteSize()); + + const RegisterInfo *reg_info; + + // The g packet contents may either include the slice registers + // (registers defined in terms of other registers, e.g. eax is a subset + // of rax) or not. The slice registers should NOT be in the g packet, + // but some implementations may incorrectly include them. + // + // If the slice registers are included in the packet, we must step over + // the slice registers when parsing the packet -- relying on the + // RegisterInfo byte_offset field would be incorrect. If the slice + // registers are not included, then using the byte_offset values into the + // data buffer is the best way to find individual register values. + + uint64_t size_including_slice_registers = 0; + uint64_t size_not_including_slice_registers = 0; + uint64_t size_by_highest_offset = 0; + + for (uint32_t reg_idx = 0; + (reg_info = GetRegisterInfoAtIndex(reg_idx)) != nullptr; ++reg_idx) { + size_including_slice_registers += reg_info->byte_size; + if (reg_info->value_regs == nullptr) + size_not_including_slice_registers += reg_info->byte_size; + if (reg_info->byte_offset >= size_by_highest_offset) + size_by_highest_offset = reg_info->byte_offset + reg_info->byte_size; + } + + bool use_byte_offset_into_buffer; + if (size_by_highest_offset == restore_data.GetByteSize()) { + // The size of the packet agrees with the highest offset: + size in the + // register file + use_byte_offset_into_buffer = true; + } else if (size_not_including_slice_registers == + restore_data.GetByteSize()) { + // The size of the packet is the same as concatenating all of the + // registers sequentially, skipping the slice registers + use_byte_offset_into_buffer = true; + } else if (size_including_slice_registers == restore_data.GetByteSize()) { + // The slice registers are present in the packet (when they shouldn't + // be). Don't try to use the RegisterInfo byte_offset into the + // restore_data, it will point to the wrong place. + use_byte_offset_into_buffer = false; + } else { + // None of our expected sizes match the actual g packet data we're + // looking at. The most conservative approach here is to use the + // running total byte offset. + use_byte_offset_into_buffer = false; + } + + // In case our register definitions don't include the correct offsets, + // keep track of the size of each reg & compute offset based on that. + uint32_t running_byte_offset = 0; + for (uint32_t reg_idx = 0; + (reg_info = GetRegisterInfoAtIndex(reg_idx)) != nullptr; + ++reg_idx, running_byte_offset += reg_info->byte_size) { + // Skip composite aka slice registers (e.g. eax is a slice of rax). + if (reg_info->value_regs) + continue; + + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + + uint32_t register_offset; + if (use_byte_offset_into_buffer) { + register_offset = reg_info->byte_offset; + } else { + register_offset = running_byte_offset; + } + + const uint32_t reg_byte_size = reg_info->byte_size; + + const uint8_t *restore_src = + restore_data.PeekData(register_offset, reg_byte_size); + if (restore_src) { + SetRegisterIsValid(reg, false); + if (gdb_comm.WriteRegister( + m_thread.GetProtocolID(), + reg_info->kinds[eRegisterKindProcessPlugin], + {restore_src, reg_byte_size})) + ++num_restored; + } + } + return num_restored > 0; + } else { + // For the use_g_packet == false case, we're going to write each register + // individually. The data buffer is binary data in this case, instead of + // ascii characters. + + bool arm64_debugserver = false; + if (m_thread.GetProcess().get()) { + const ArchSpec &arch = + m_thread.GetProcess()->GetTarget().GetArchitecture(); + if (arch.IsValid() && (arch.GetMachine() == llvm::Triple::aarch64 || + arch.GetMachine() == llvm::Triple::aarch64_32) && + arch.GetTriple().getVendor() == llvm::Triple::Apple && + arch.GetTriple().getOS() == llvm::Triple::IOS) { + arm64_debugserver = true; + } + } + uint32_t num_restored = 0; + const RegisterInfo *reg_info; + for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != nullptr; + i++) { + if (reg_info->value_regs) // skip registers that are slices of real + // registers + continue; + // Skip the fpsr and fpcr floating point status/control register + // writing to work around a bug in an older version of debugserver that + // would lead to register context corruption when writing fpsr/fpcr. + if (arm64_debugserver && (strcmp(reg_info->name, "fpsr") == 0 || + strcmp(reg_info->name, "fpcr") == 0)) { + continue; + } + + SetRegisterIsValid(reg_info, false); + if (gdb_comm.WriteRegister(m_thread.GetProtocolID(), + reg_info->kinds[eRegisterKindProcessPlugin], + {data_sp->GetBytes() + reg_info->byte_offset, + reg_info->byte_size})) + ++num_restored; + } + return num_restored > 0; + } + } else { + Log *log(GetLog(GDBRLog::Thread | GDBRLog::Packets)); + if (log) { + if (log->GetVerbose()) { + StreamString strm; + process->DumpPluginHistory(strm); + LLDB_LOGF(log, + "error: failed to get packet sequence mutex, not sending " + "write all registers:\n%s", + strm.GetData()); + } else + LLDB_LOGF(log, + "error: failed to get packet sequence mutex, not sending " + "write all registers"); + } + } + return false; +} + +uint32_t GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + return m_reg_info_sp->ConvertRegisterKindToRegisterNumber(kind, num); +} + +bool GDBRemoteRegisterContext::RegisterWriteCausesReconfigure( + const llvm::StringRef name) { + ExecutionContext exe_ctx(CalculateThread()); + const Architecture *architecture = + exe_ctx.GetProcessRef().GetTarget().GetArchitecturePlugin(); + return architecture && architecture->RegisterWriteCausesReconfigure(name); +} + +bool GDBRemoteRegisterContext::ReconfigureRegisterInfo() { + ExecutionContext exe_ctx(CalculateThread()); + const Architecture *architecture = + exe_ctx.GetProcessRef().GetTarget().GetArchitecturePlugin(); + if (architecture) + return architecture->ReconfigureRegisterInfo(*(m_reg_info_sp.get()), + m_reg_data, *this); + return false; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h new file mode 100644 index 000000000000..6a90f911353f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -0,0 +1,141 @@ +//===-- GDBRemoteRegisterContext.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTEREGISTERCONTEXT_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTEREGISTERCONTEXT_H + +#include <vector> + +#include "lldb/Target/DynamicRegisterInfo.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private.h" + +#include "GDBRemoteCommunicationClient.h" + +class StringExtractor; + +namespace lldb_private { +namespace process_gdb_remote { + +class ThreadGDBRemote; +class ProcessGDBRemote; +class GDBRemoteDynamicRegisterInfo; + +typedef std::shared_ptr<GDBRemoteDynamicRegisterInfo> + GDBRemoteDynamicRegisterInfoSP; + +class GDBRemoteDynamicRegisterInfo final : public DynamicRegisterInfo { +public: + GDBRemoteDynamicRegisterInfo() : DynamicRegisterInfo() {} + + ~GDBRemoteDynamicRegisterInfo() override = default; + + void UpdateARM64SVERegistersInfos(uint64_t vg); + void UpdateARM64SMERegistersInfos(uint64_t svg); +}; + +class GDBRemoteRegisterContext : public RegisterContext { +public: + GDBRemoteRegisterContext(ThreadGDBRemote &thread, uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfoSP reg_info_sp, + bool read_all_at_once, bool write_all_at_once); + + ~GDBRemoteRegisterContext() override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) override; + + bool ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + bool WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + bool ReadAllRegisterValues(RegisterCheckpoint ®_checkpoint) override; + + bool + WriteAllRegisterValues(const RegisterCheckpoint ®_checkpoint) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + bool RegisterWriteCausesReconfigure(const llvm::StringRef name) override; + + bool ReconfigureRegisterInfo() override; + +protected: + friend class ThreadGDBRemote; + + bool ReadRegisterBytes(const RegisterInfo *reg_info); + + bool WriteRegisterBytes(const RegisterInfo *reg_info, DataExtractor &data, + uint32_t data_offset); + + bool PrivateSetRegisterValue(uint32_t reg, llvm::ArrayRef<uint8_t> data); + + bool PrivateSetRegisterValue(uint32_t reg, uint64_t val); + + void SetAllRegisterValid(bool b); + + bool GetRegisterIsValid(uint32_t reg) const { + assert(reg < m_reg_valid.size()); + if (reg < m_reg_valid.size()) + return m_reg_valid[reg]; + return false; + } + + void SetRegisterIsValid(const RegisterInfo *reg_info, bool valid) { + if (reg_info) + return SetRegisterIsValid(reg_info->kinds[lldb::eRegisterKindLLDB], + valid); + } + + void SetRegisterIsValid(uint32_t reg, bool valid) { + assert(reg < m_reg_valid.size()); + if (reg < m_reg_valid.size()) + m_reg_valid[reg] = valid; + } + + GDBRemoteDynamicRegisterInfoSP m_reg_info_sp; + std::vector<bool> m_reg_valid; + DataExtractor m_reg_data; + bool m_read_all_at_once; + bool m_write_all_at_once; + bool m_gpacket_cached; + +private: + // Helper function for ReadRegisterBytes(). + bool GetPrimordialRegister(const RegisterInfo *reg_info, + GDBRemoteCommunicationClient &gdb_comm); + // Helper function for WriteRegisterBytes(). + bool SetPrimordialRegister(const RegisterInfo *reg_info, + GDBRemoteCommunicationClient &gdb_comm); + + GDBRemoteRegisterContext(const GDBRemoteRegisterContext &) = delete; + const GDBRemoteRegisterContext & + operator=(const GDBRemoteRegisterContext &) = delete; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTEREGISTERCONTEXT_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterFallback.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterFallback.cpp new file mode 100644 index 000000000000..8068614c9350 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterFallback.cpp @@ -0,0 +1,101 @@ +//===-- GDBRemoteRegisterFallback.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 "GDBRemoteRegisterFallback.h" + +namespace lldb_private { +namespace process_gdb_remote { + +#define REG(name, size) \ + DynamicRegisterInfo::Register { \ + ConstString(#name), empty_alt_name, reg_set, size, LLDB_INVALID_INDEX32, \ + lldb::eEncodingUint, lldb::eFormatHex, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, {}, {} \ + } +#define R64(name) REG(name, 8) +#define R32(name) REG(name, 4) +#define R16(name) REG(name, 2) + +static std::vector<DynamicRegisterInfo::Register> GetRegisters_aarch64() { + ConstString empty_alt_name; + ConstString reg_set{"general purpose registers"}; + + std::vector<DynamicRegisterInfo::Register> registers{ + R64(x0), R64(x1), R64(x2), R64(x3), R64(x4), R64(x5), R64(x6), + R64(x7), R64(x8), R64(x9), R64(x10), R64(x11), R64(x12), R64(x13), + R64(x14), R64(x15), R64(x16), R64(x17), R64(x18), R64(x19), R64(x20), + R64(x21), R64(x22), R64(x23), R64(x24), R64(x25), R64(x26), R64(x27), + R64(x28), R64(x29), R64(x30), R64(sp), R64(pc), R32(cpsr), + }; + + return registers; +} + +static std::vector<DynamicRegisterInfo::Register> GetRegisters_msp430() { + ConstString empty_alt_name; + ConstString reg_set{"general purpose registers"}; + + std::vector<DynamicRegisterInfo::Register> registers{ + R16(pc), R16(sp), R16(r2), R16(r3), R16(fp), R16(r5), + R16(r6), R16(r7), R16(r8), R16(r9), R16(r10), R16(r11), + R16(r12), R16(r13), R16(r14), R16(r15)}; + + return registers; +} + +static std::vector<DynamicRegisterInfo::Register> GetRegisters_x86() { + ConstString empty_alt_name; + ConstString reg_set{"general purpose registers"}; + + std::vector<DynamicRegisterInfo::Register> registers{ + R32(eax), R32(ecx), R32(edx), R32(ebx), R32(esp), R32(ebp), + R32(esi), R32(edi), R32(eip), R32(eflags), R32(cs), R32(ss), + R32(ds), R32(es), R32(fs), R32(gs), + }; + + return registers; +} + +static std::vector<DynamicRegisterInfo::Register> GetRegisters_x86_64() { + ConstString empty_alt_name; + ConstString reg_set{"general purpose registers"}; + + std::vector<DynamicRegisterInfo::Register> registers{ + R64(rax), R64(rbx), R64(rcx), R64(rdx), R64(rsi), R64(rdi), + R64(rbp), R64(rsp), R64(r8), R64(r9), R64(r10), R64(r11), + R64(r12), R64(r13), R64(r14), R64(r15), R64(rip), R32(eflags), + R32(cs), R32(ss), R32(ds), R32(es), R32(fs), R32(gs), + }; + + return registers; +} + +#undef R32 +#undef R64 +#undef REG + +std::vector<DynamicRegisterInfo::Register> +GetFallbackRegisters(const ArchSpec &arch_to_use) { + switch (arch_to_use.GetMachine()) { + case llvm::Triple::aarch64: + return GetRegisters_aarch64(); + case llvm::Triple::msp430: + return GetRegisters_msp430(); + case llvm::Triple::x86: + return GetRegisters_x86(); + case llvm::Triple::x86_64: + return GetRegisters_x86_64(); + default: + break; + } + + return {}; +} + +} // namespace process_gdb_remote +} // namespace lldb_private diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterFallback.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterFallback.h new file mode 100644 index 000000000000..82e03c6b9b1c --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterFallback.h @@ -0,0 +1,26 @@ +//===-- GDBRemoteRegisterFallback.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTEREGISTERFALLBACK_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTEREGISTERFALLBACK_H + +#include <vector> + +#include "lldb/Target/DynamicRegisterInfo.h" +#include "lldb/Utility/ArchSpec.h" + +namespace lldb_private { +namespace process_gdb_remote { + +std::vector<DynamicRegisterInfo::Register> +GetFallbackRegisters(const ArchSpec &arch_to_use); + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTEREGISTERFALLBACK_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp new file mode 100644 index 000000000000..604c92369e9a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -0,0 +1,5876 @@ +//===-- ProcessGDBRemote.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Config.h" + +#include <cerrno> +#include <cstdlib> +#if LLDB_ENABLE_POSIX +#include <netinet/in.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <unistd.h> +#endif +#include <sys/stat.h> +#if defined(__APPLE__) +#include <sys/sysctl.h> +#endif +#include <ctime> +#include <sys/types.h> + +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/WatchpointAlgorithms.h" +#include "lldb/Breakpoint/WatchpointResource.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Value.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/PosixApi.h" +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/StreamFile.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/XML.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupBoolean.h" +#include "lldb/Interpreter/OptionGroupUInt64.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/Property.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/RegisterFlags.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/Timer.h" +#include <algorithm> +#include <csignal> +#include <map> +#include <memory> +#include <mutex> +#include <optional> +#include <sstream> +#include <thread> + +#include "GDBRemoteRegisterContext.h" +#include "GDBRemoteRegisterFallback.h" +#include "Plugins/Process/Utility/GDBRemoteSignals.h" +#include "Plugins/Process/Utility/InferiorCallPOSIX.h" +#include "Plugins/Process/Utility/StopInfoMachException.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "ThreadGDBRemote.h" +#include "lldb/Host/Host.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUGSERVER_BASENAME "debugserver" +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +LLDB_PLUGIN_DEFINE(ProcessGDBRemote) + +namespace lldb { +// Provide a function that can easily dump the packet history if we know a +// ProcessGDBRemote * value (which we can get from logs or from debugging). We +// need the function in the lldb namespace so it makes it into the final +// executable since the LLDB shared library only exports stuff in the lldb +// namespace. This allows you to attach with a debugger and call this function +// and get the packet history dumped to a file. +void DumpProcessGDBRemotePacketHistory(void *p, const char *path) { + auto file = FileSystem::Instance().Open( + FileSpec(path), File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate); + if (!file) { + llvm::consumeError(file.takeError()); + return; + } + StreamFile stream(std::move(file.get())); + ((Process *)p)->DumpPluginHistory(stream); +} +} // namespace lldb + +namespace { + +#define LLDB_PROPERTIES_processgdbremote +#include "ProcessGDBRemoteProperties.inc" + +enum { +#define LLDB_PROPERTIES_processgdbremote +#include "ProcessGDBRemotePropertiesEnum.inc" +}; + +class PluginProperties : public Properties { +public: + static llvm::StringRef GetSettingName() { + return ProcessGDBRemote::GetPluginNameStatic(); + } + + PluginProperties() : Properties() { + m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName()); + m_collection_sp->Initialize(g_processgdbremote_properties); + } + + ~PluginProperties() override = default; + + uint64_t GetPacketTimeout() { + const uint32_t idx = ePropertyPacketTimeout; + return GetPropertyAtIndexAs<uint64_t>( + idx, g_processgdbremote_properties[idx].default_uint_value); + } + + bool SetPacketTimeout(uint64_t timeout) { + const uint32_t idx = ePropertyPacketTimeout; + return SetPropertyAtIndex(idx, timeout); + } + + FileSpec GetTargetDefinitionFile() const { + const uint32_t idx = ePropertyTargetDefinitionFile; + return GetPropertyAtIndexAs<FileSpec>(idx, {}); + } + + bool GetUseSVR4() const { + const uint32_t idx = ePropertyUseSVR4; + return GetPropertyAtIndexAs<bool>( + idx, g_processgdbremote_properties[idx].default_uint_value != 0); + } + + bool GetUseGPacketForReading() const { + const uint32_t idx = ePropertyUseGPacketForReading; + return GetPropertyAtIndexAs<bool>(idx, true); + } +}; + +} // namespace + +static PluginProperties &GetGlobalPluginProperties() { + static PluginProperties g_settings; + return g_settings; +} + +// TODO Randomly assigning a port is unsafe. We should get an unused +// ephemeral port from the kernel and make sure we reserve it before passing it +// to debugserver. + +#if defined(__APPLE__) +#define LOW_PORT (IPPORT_RESERVED) +#define HIGH_PORT (IPPORT_HIFIRSTAUTO) +#else +#define LOW_PORT (1024u) +#define HIGH_PORT (49151u) +#endif + +llvm::StringRef ProcessGDBRemote::GetPluginDescriptionStatic() { + return "GDB Remote protocol based debugging plug-in."; +} + +void ProcessGDBRemote::Terminate() { + PluginManager::UnregisterPlugin(ProcessGDBRemote::CreateInstance); +} + +lldb::ProcessSP ProcessGDBRemote::CreateInstance( + lldb::TargetSP target_sp, ListenerSP listener_sp, + const FileSpec *crash_file_path, bool can_connect) { + lldb::ProcessSP process_sp; + if (crash_file_path == nullptr) + process_sp = std::shared_ptr<ProcessGDBRemote>( + new ProcessGDBRemote(target_sp, listener_sp)); + return process_sp; +} + +void ProcessGDBRemote::DumpPluginHistory(Stream &s) { + GDBRemoteCommunicationClient &gdb_comm(GetGDBRemote()); + gdb_comm.DumpHistory(s); +} + +std::chrono::seconds ProcessGDBRemote::GetPacketTimeout() { + return std::chrono::seconds(GetGlobalPluginProperties().GetPacketTimeout()); +} + +ArchSpec ProcessGDBRemote::GetSystemArchitecture() { + return m_gdb_comm.GetHostArchitecture(); +} + +bool ProcessGDBRemote::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) { + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + // We can't debug core files... + switch (exe_objfile->GetType()) { + case ObjectFile::eTypeInvalid: + case ObjectFile::eTypeCoreFile: + case ObjectFile::eTypeDebugInfo: + case ObjectFile::eTypeObjectFile: + case ObjectFile::eTypeSharedLibrary: + case ObjectFile::eTypeStubLibrary: + case ObjectFile::eTypeJIT: + return false; + case ObjectFile::eTypeExecutable: + case ObjectFile::eTypeDynamicLinker: + case ObjectFile::eTypeUnknown: + break; + } + return FileSystem::Instance().Exists(exe_module->GetFileSpec()); + } + // However, if there is no executable module, we return true since we might + // be preparing to attach. + return true; +} + +// ProcessGDBRemote constructor +ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp, + ListenerSP listener_sp) + : Process(target_sp, listener_sp), + m_debugserver_pid(LLDB_INVALID_PROCESS_ID), m_register_info_sp(nullptr), + m_async_broadcaster(nullptr, "lldb.process.gdb-remote.async-broadcaster"), + m_async_listener_sp( + Listener::MakeListener("lldb.process.gdb-remote.async-listener")), + m_async_thread_state_mutex(), m_thread_ids(), m_thread_pcs(), + m_jstopinfo_sp(), m_jthreadsinfo_sp(), m_continue_c_tids(), + m_continue_C_tids(), m_continue_s_tids(), m_continue_S_tids(), + m_max_memory_size(0), m_remote_stub_max_memory_size(0), + m_addr_to_mmap_size(), m_thread_create_bp_sp(), + m_waiting_for_attach(false), m_command_sp(), m_breakpoint_pc_offset(0), + m_initial_tid(LLDB_INVALID_THREAD_ID), m_allow_flash_writes(false), + m_erased_flash_ranges(), m_vfork_in_progress_count(0) { + m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, + "async thread should exit"); + m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, + "async thread continue"); + m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadDidExit, + "async thread did exit"); + + Log *log = GetLog(GDBRLog::Async); + + const uint32_t async_event_mask = + eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; + + if (m_async_listener_sp->StartListeningForEvents( + &m_async_broadcaster, async_event_mask) != async_event_mask) { + LLDB_LOGF(log, + "ProcessGDBRemote::%s failed to listen for " + "m_async_broadcaster events", + __FUNCTION__); + } + + const uint64_t timeout_seconds = + GetGlobalPluginProperties().GetPacketTimeout(); + if (timeout_seconds > 0) + m_gdb_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds)); + + m_use_g_packet_for_reading = + GetGlobalPluginProperties().GetUseGPacketForReading(); +} + +// Destructor +ProcessGDBRemote::~ProcessGDBRemote() { + // m_mach_process.UnregisterNotificationCallbacks (this); + Clear(); + // We need to call finalize on the process before destroying ourselves to + // make sure all of the broadcaster cleanup goes as planned. If we destruct + // this class, then Process::~Process() might have problems trying to fully + // destroy the broadcaster. + Finalize(true /* destructing */); + + // The general Finalize is going to try to destroy the process and that + // SHOULD shut down the async thread. However, if we don't kill it it will + // get stranded and its connection will go away so when it wakes up it will + // crash. So kill it for sure here. + StopAsyncThread(); + KillDebugserverProcess(); +} + +bool ProcessGDBRemote::ParsePythonTargetDefinition( + const FileSpec &target_definition_fspec) { + ScriptInterpreter *interpreter = + GetTarget().GetDebugger().GetScriptInterpreter(); + Status error; + StructuredData::ObjectSP module_object_sp( + interpreter->LoadPluginModule(target_definition_fspec, error)); + if (module_object_sp) { + StructuredData::DictionarySP target_definition_sp( + interpreter->GetDynamicSettings(module_object_sp, &GetTarget(), + "gdb-server-target-definition", error)); + + if (target_definition_sp) { + StructuredData::ObjectSP target_object( + target_definition_sp->GetValueForKey("host-info")); + if (target_object) { + if (auto host_info_dict = target_object->GetAsDictionary()) { + StructuredData::ObjectSP triple_value = + host_info_dict->GetValueForKey("triple"); + if (auto triple_string_value = triple_value->GetAsString()) { + std::string triple_string = + std::string(triple_string_value->GetValue()); + ArchSpec host_arch(triple_string.c_str()); + if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) { + GetTarget().SetArchitecture(host_arch); + } + } + } + } + m_breakpoint_pc_offset = 0; + StructuredData::ObjectSP breakpoint_pc_offset_value = + target_definition_sp->GetValueForKey("breakpoint-pc-offset"); + if (breakpoint_pc_offset_value) { + if (auto breakpoint_pc_int_value = + breakpoint_pc_offset_value->GetAsSignedInteger()) + m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue(); + } + + if (m_register_info_sp->SetRegisterInfo( + *target_definition_sp, GetTarget().GetArchitecture()) > 0) { + return true; + } + } + } + return false; +} + +static size_t SplitCommaSeparatedRegisterNumberString( + const llvm::StringRef &comma_separated_register_numbers, + std::vector<uint32_t> ®nums, int base) { + regnums.clear(); + for (llvm::StringRef x : llvm::split(comma_separated_register_numbers, ',')) { + uint32_t reg; + if (llvm::to_integer(x, reg, base)) + regnums.push_back(reg); + } + return regnums.size(); +} + +void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { + if (!force && m_register_info_sp) + return; + + m_register_info_sp = std::make_shared<GDBRemoteDynamicRegisterInfo>(); + + // Check if qHostInfo specified a specific packet timeout for this + // connection. If so then lets update our setting so the user knows what the + // timeout is and can see it. + const auto host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); + if (host_packet_timeout > std::chrono::seconds(0)) { + GetGlobalPluginProperties().SetPacketTimeout(host_packet_timeout.count()); + } + + // Register info search order: + // 1 - Use the target definition python file if one is specified. + // 2 - If the target definition doesn't have any of the info from the + // target.xml (registers) then proceed to read the target.xml. + // 3 - Fall back on the qRegisterInfo packets. + // 4 - Use hardcoded defaults if available. + + FileSpec target_definition_fspec = + GetGlobalPluginProperties().GetTargetDefinitionFile(); + if (!FileSystem::Instance().Exists(target_definition_fspec)) { + // If the filename doesn't exist, it may be a ~ not having been expanded - + // try to resolve it. + FileSystem::Instance().Resolve(target_definition_fspec); + } + if (target_definition_fspec) { + // See if we can get register definitions from a python file + if (ParsePythonTargetDefinition(target_definition_fspec)) + return; + + Debugger::ReportError("target description file " + + target_definition_fspec.GetPath() + + " failed to parse", + GetTarget().GetDebugger().GetID()); + } + + const ArchSpec &target_arch = GetTarget().GetArchitecture(); + const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); + const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); + + // Use the process' architecture instead of the host arch, if available + ArchSpec arch_to_use; + if (remote_process_arch.IsValid()) + arch_to_use = remote_process_arch; + else + arch_to_use = remote_host_arch; + + if (!arch_to_use.IsValid()) + arch_to_use = target_arch; + + if (GetGDBServerRegisterInfo(arch_to_use)) + return; + + char packet[128]; + std::vector<DynamicRegisterInfo::Register> registers; + uint32_t reg_num = 0; + for (StringExtractorGDBRemote::ResponseType response_type = + StringExtractorGDBRemote::eResponse; + response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { + const int packet_len = + ::snprintf(packet, sizeof(packet), "qRegisterInfo%x", reg_num); + assert(packet_len < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response) == + GDBRemoteCommunication::PacketResult::Success) { + response_type = response.GetResponseType(); + if (response_type == StringExtractorGDBRemote::eResponse) { + llvm::StringRef name; + llvm::StringRef value; + DynamicRegisterInfo::Register reg_info; + + while (response.GetNameColonValue(name, value)) { + if (name == "name") { + reg_info.name.SetString(value); + } else if (name == "alt-name") { + reg_info.alt_name.SetString(value); + } else if (name == "bitsize") { + if (!value.getAsInteger(0, reg_info.byte_size)) + reg_info.byte_size /= CHAR_BIT; + } else if (name == "offset") { + value.getAsInteger(0, reg_info.byte_offset); + } else if (name == "encoding") { + const Encoding encoding = Args::StringToEncoding(value); + if (encoding != eEncodingInvalid) + reg_info.encoding = encoding; + } else if (name == "format") { + if (!OptionArgParser::ToFormat(value.str().c_str(), reg_info.format, nullptr) + .Success()) + reg_info.format = + llvm::StringSwitch<Format>(value) + .Case("binary", eFormatBinary) + .Case("decimal", eFormatDecimal) + .Case("hex", eFormatHex) + .Case("float", eFormatFloat) + .Case("vector-sint8", eFormatVectorOfSInt8) + .Case("vector-uint8", eFormatVectorOfUInt8) + .Case("vector-sint16", eFormatVectorOfSInt16) + .Case("vector-uint16", eFormatVectorOfUInt16) + .Case("vector-sint32", eFormatVectorOfSInt32) + .Case("vector-uint32", eFormatVectorOfUInt32) + .Case("vector-float32", eFormatVectorOfFloat32) + .Case("vector-uint64", eFormatVectorOfUInt64) + .Case("vector-uint128", eFormatVectorOfUInt128) + .Default(eFormatInvalid); + } else if (name == "set") { + reg_info.set_name.SetString(value); + } else if (name == "gcc" || name == "ehframe") { + value.getAsInteger(0, reg_info.regnum_ehframe); + } else if (name == "dwarf") { + value.getAsInteger(0, reg_info.regnum_dwarf); + } else if (name == "generic") { + reg_info.regnum_generic = Args::StringToGenericRegister(value); + } else if (name == "container-regs") { + SplitCommaSeparatedRegisterNumberString(value, reg_info.value_regs, 16); + } else if (name == "invalidate-regs") { + SplitCommaSeparatedRegisterNumberString(value, reg_info.invalidate_regs, 16); + } + } + + assert(reg_info.byte_size != 0); + registers.push_back(reg_info); + } else { + break; // ensure exit before reg_num is incremented + } + } else { + break; + } + } + + if (registers.empty()) + registers = GetFallbackRegisters(arch_to_use); + + AddRemoteRegisters(registers, arch_to_use); +} + +Status ProcessGDBRemote::DoWillLaunch(lldb_private::Module *module) { + return WillLaunchOrAttach(); +} + +Status ProcessGDBRemote::DoWillAttachToProcessWithID(lldb::pid_t pid) { + return WillLaunchOrAttach(); +} + +Status ProcessGDBRemote::DoWillAttachToProcessWithName(const char *process_name, + bool wait_for_launch) { + return WillLaunchOrAttach(); +} + +Status ProcessGDBRemote::DoConnectRemote(llvm::StringRef remote_url) { + Log *log = GetLog(GDBRLog::Process); + + Status error(WillLaunchOrAttach()); + if (error.Fail()) + return error; + + error = ConnectToDebugserver(remote_url); + if (error.Fail()) + return error; + + StartAsyncThread(); + + lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); + if (pid == LLDB_INVALID_PROCESS_ID) { + // We don't have a valid process ID, so note that we are connected and + // could now request to launch or attach, or get remote process listings... + SetPrivateState(eStateConnected); + } else { + // We have a valid process + SetID(pid); + GetThreadList(); + StringExtractorGDBRemote response; + if (m_gdb_comm.GetStopReply(response)) { + SetLastStopPacket(response); + + Target &target = GetTarget(); + if (!target.GetArchitecture().IsValid()) { + if (m_gdb_comm.GetProcessArchitecture().IsValid()) { + target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); + } else { + if (m_gdb_comm.GetHostArchitecture().IsValid()) { + target.SetArchitecture(m_gdb_comm.GetHostArchitecture()); + } + } + } + + const StateType state = SetThreadStopInfo(response); + if (state != eStateInvalid) { + SetPrivateState(state); + } else + error.SetErrorStringWithFormat( + "Process %" PRIu64 " was reported after connecting to " + "'%s', but state was not stopped: %s", + pid, remote_url.str().c_str(), StateAsCString(state)); + } else + error.SetErrorStringWithFormat("Process %" PRIu64 + " was reported after connecting to '%s', " + "but no stop reply packet was received", + pid, remote_url.str().c_str()); + } + + LLDB_LOGF(log, + "ProcessGDBRemote::%s pid %" PRIu64 + ": normalizing target architecture initial triple: %s " + "(GetTarget().GetArchitecture().IsValid() %s, " + "m_gdb_comm.GetHostArchitecture().IsValid(): %s)", + __FUNCTION__, GetID(), + GetTarget().GetArchitecture().GetTriple().getTriple().c_str(), + GetTarget().GetArchitecture().IsValid() ? "true" : "false", + m_gdb_comm.GetHostArchitecture().IsValid() ? "true" : "false"); + + if (error.Success() && !GetTarget().GetArchitecture().IsValid() && + m_gdb_comm.GetHostArchitecture().IsValid()) { + // Prefer the *process'* architecture over that of the *host*, if + // available. + if (m_gdb_comm.GetProcessArchitecture().IsValid()) + GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); + else + GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture()); + } + + LLDB_LOGF(log, + "ProcessGDBRemote::%s pid %" PRIu64 + ": normalized target architecture triple: %s", + __FUNCTION__, GetID(), + GetTarget().GetArchitecture().GetTriple().getTriple().c_str()); + + return error; +} + +Status ProcessGDBRemote::WillLaunchOrAttach() { + Status error; + m_stdio_communication.Clear(); + return error; +} + +// Process Control +Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module, + ProcessLaunchInfo &launch_info) { + Log *log = GetLog(GDBRLog::Process); + Status error; + + LLDB_LOGF(log, "ProcessGDBRemote::%s() entered", __FUNCTION__); + + uint32_t launch_flags = launch_info.GetFlags().Get(); + FileSpec stdin_file_spec{}; + FileSpec stdout_file_spec{}; + FileSpec stderr_file_spec{}; + FileSpec working_dir = launch_info.GetWorkingDirectory(); + + const FileAction *file_action; + file_action = launch_info.GetFileActionForFD(STDIN_FILENO); + if (file_action) { + if (file_action->GetAction() == FileAction::eFileActionOpen) + stdin_file_spec = file_action->GetFileSpec(); + } + file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); + if (file_action) { + if (file_action->GetAction() == FileAction::eFileActionOpen) + stdout_file_spec = file_action->GetFileSpec(); + } + file_action = launch_info.GetFileActionForFD(STDERR_FILENO); + if (file_action) { + if (file_action->GetAction() == FileAction::eFileActionOpen) + stderr_file_spec = file_action->GetFileSpec(); + } + + if (log) { + if (stdin_file_spec || stdout_file_spec || stderr_file_spec) + LLDB_LOGF(log, + "ProcessGDBRemote::%s provided with STDIO paths via " + "launch_info: stdin=%s, stdout=%s, stderr=%s", + __FUNCTION__, + stdin_file_spec ? stdin_file_spec.GetPath().c_str() : "<null>", + stdout_file_spec ? stdout_file_spec.GetPath().c_str() : "<null>", + stderr_file_spec ? stderr_file_spec.GetPath().c_str() : "<null>"); + else + LLDB_LOGF(log, + "ProcessGDBRemote::%s no STDIO paths given via launch_info", + __FUNCTION__); + } + + const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; + if (stdin_file_spec || disable_stdio) { + // the inferior will be reading stdin from the specified file or stdio is + // completely disabled + m_stdin_forward = false; + } else { + m_stdin_forward = true; + } + + // ::LogSetBitMask (GDBR_LOG_DEFAULT); + // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | + // LLDB_LOG_OPTION_PREPEND_TIMESTAMP | + // LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); + // ::LogSetLogFile ("/dev/stdout"); + + error = EstablishConnectionIfNeeded(launch_info); + if (error.Success()) { + PseudoTerminal pty; + const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; + + PlatformSP platform_sp(GetTarget().GetPlatform()); + if (disable_stdio) { + // set to /dev/null unless redirected to a file above + if (!stdin_file_spec) + stdin_file_spec.SetFile(FileSystem::DEV_NULL, + FileSpec::Style::native); + if (!stdout_file_spec) + stdout_file_spec.SetFile(FileSystem::DEV_NULL, + FileSpec::Style::native); + if (!stderr_file_spec) + stderr_file_spec.SetFile(FileSystem::DEV_NULL, + FileSpec::Style::native); + } else if (platform_sp && platform_sp->IsHost()) { + // If the debugserver is local and we aren't disabling STDIO, lets use + // a pseudo terminal to instead of relying on the 'O' packets for stdio + // since 'O' packets can really slow down debugging if the inferior + // does a lot of output. + if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && + !errorToBool(pty.OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY))) { + FileSpec secondary_name(pty.GetSecondaryName()); + + if (!stdin_file_spec) + stdin_file_spec = secondary_name; + + if (!stdout_file_spec) + stdout_file_spec = secondary_name; + + if (!stderr_file_spec) + stderr_file_spec = secondary_name; + } + LLDB_LOGF( + log, + "ProcessGDBRemote::%s adjusted STDIO paths for local platform " + "(IsHost() is true) using secondary: stdin=%s, stdout=%s, " + "stderr=%s", + __FUNCTION__, + stdin_file_spec ? stdin_file_spec.GetPath().c_str() : "<null>", + stdout_file_spec ? stdout_file_spec.GetPath().c_str() : "<null>", + stderr_file_spec ? stderr_file_spec.GetPath().c_str() : "<null>"); + } + + LLDB_LOGF(log, + "ProcessGDBRemote::%s final STDIO paths after all " + "adjustments: stdin=%s, stdout=%s, stderr=%s", + __FUNCTION__, + stdin_file_spec ? stdin_file_spec.GetPath().c_str() : "<null>", + stdout_file_spec ? stdout_file_spec.GetPath().c_str() : "<null>", + stderr_file_spec ? stderr_file_spec.GetPath().c_str() : "<null>"); + + if (stdin_file_spec) + m_gdb_comm.SetSTDIN(stdin_file_spec); + if (stdout_file_spec) + m_gdb_comm.SetSTDOUT(stdout_file_spec); + if (stderr_file_spec) + m_gdb_comm.SetSTDERR(stderr_file_spec); + + m_gdb_comm.SetDisableASLR(launch_flags & eLaunchFlagDisableASLR); + m_gdb_comm.SetDetachOnError(launch_flags & eLaunchFlagDetachOnError); + + m_gdb_comm.SendLaunchArchPacket( + GetTarget().GetArchitecture().GetArchitectureName()); + + const char *launch_event_data = launch_info.GetLaunchEventData(); + if (launch_event_data != nullptr && *launch_event_data != '\0') + m_gdb_comm.SendLaunchEventDataPacket(launch_event_data); + + if (working_dir) { + m_gdb_comm.SetWorkingDir(working_dir); + } + + // Send the environment and the program + arguments after we connect + m_gdb_comm.SendEnvironment(launch_info.GetEnvironment()); + + { + // Scope for the scoped timeout object + GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, + std::chrono::seconds(10)); + + // Since we can't send argv0 separate from the executable path, we need to + // make sure to use the actual executable path found in the launch_info... + Args args = launch_info.GetArguments(); + if (FileSpec exe_file = launch_info.GetExecutableFile()) + args.ReplaceArgumentAtIndex(0, exe_file.GetPath(false)); + if (llvm::Error err = m_gdb_comm.LaunchProcess(args)) { + error.SetErrorStringWithFormatv("Cannot launch '{0}': {1}", + args.GetArgumentAtIndex(0), + llvm::fmt_consume(std::move(err))); + } else { + SetID(m_gdb_comm.GetCurrentProcessID()); + } + } + + if (GetID() == LLDB_INVALID_PROCESS_ID) { + LLDB_LOGF(log, "failed to connect to debugserver: %s", + error.AsCString()); + KillDebugserverProcess(); + return error; + } + + StringExtractorGDBRemote response; + if (m_gdb_comm.GetStopReply(response)) { + SetLastStopPacket(response); + + const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); + + if (process_arch.IsValid()) { + GetTarget().MergeArchitecture(process_arch); + } else { + const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); + if (host_arch.IsValid()) + GetTarget().MergeArchitecture(host_arch); + } + + SetPrivateState(SetThreadStopInfo(response)); + + if (!disable_stdio) { + if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd) + SetSTDIOFileDescriptor(pty.ReleasePrimaryFileDescriptor()); + } + } + } else { + LLDB_LOGF(log, "failed to connect to debugserver: %s", error.AsCString()); + } + return error; +} + +Status ProcessGDBRemote::ConnectToDebugserver(llvm::StringRef connect_url) { + Status error; + // Only connect if we have a valid connect URL + Log *log = GetLog(GDBRLog::Process); + + if (!connect_url.empty()) { + LLDB_LOGF(log, "ProcessGDBRemote::%s Connecting to %s", __FUNCTION__, + connect_url.str().c_str()); + std::unique_ptr<ConnectionFileDescriptor> conn_up( + new ConnectionFileDescriptor()); + if (conn_up) { + const uint32_t max_retry_count = 50; + uint32_t retry_count = 0; + while (!m_gdb_comm.IsConnected()) { + if (conn_up->Connect(connect_url, &error) == eConnectionStatusSuccess) { + m_gdb_comm.SetConnection(std::move(conn_up)); + break; + } + + retry_count++; + + if (retry_count >= max_retry_count) + break; + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + } + + if (!m_gdb_comm.IsConnected()) { + if (error.Success()) + error.SetErrorString("not connected to remote gdb server"); + return error; + } + + // We always seem to be able to open a connection to a local port so we need + // to make sure we can then send data to it. If we can't then we aren't + // actually connected to anything, so try and do the handshake with the + // remote GDB server and make sure that goes alright. + if (!m_gdb_comm.HandshakeWithServer(&error)) { + m_gdb_comm.Disconnect(); + if (error.Success()) + error.SetErrorString("not connected to remote gdb server"); + return error; + } + + m_gdb_comm.GetEchoSupported(); + m_gdb_comm.GetThreadSuffixSupported(); + m_gdb_comm.GetListThreadsInStopReplySupported(); + m_gdb_comm.GetHostInfo(); + m_gdb_comm.GetVContSupported('c'); + m_gdb_comm.GetVAttachOrWaitSupported(); + m_gdb_comm.EnableErrorStringInPacket(); + + // First dispatch any commands from the platform: + auto handle_cmds = [&] (const Args &args) -> void { + for (const Args::ArgEntry &entry : args) { + StringExtractorGDBRemote response; + m_gdb_comm.SendPacketAndWaitForResponse( + entry.c_str(), response); + } + }; + + PlatformSP platform_sp = GetTarget().GetPlatform(); + if (platform_sp) { + handle_cmds(platform_sp->GetExtraStartupCommands()); + } + + // Then dispatch any process commands: + handle_cmds(GetExtraStartupCommands()); + + return error; +} + +void ProcessGDBRemote::DidLaunchOrAttach(ArchSpec &process_arch) { + Log *log = GetLog(GDBRLog::Process); + BuildDynamicRegisterInfo(false); + + // See if the GDB server supports qHostInfo or qProcessInfo packets. Prefer + // qProcessInfo as it will be more specific to our process. + + const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); + if (remote_process_arch.IsValid()) { + process_arch = remote_process_arch; + LLDB_LOG(log, "gdb-remote had process architecture, using {0} {1}", + process_arch.GetArchitectureName(), + process_arch.GetTriple().getTriple()); + } else { + process_arch = m_gdb_comm.GetHostArchitecture(); + LLDB_LOG(log, + "gdb-remote did not have process architecture, using gdb-remote " + "host architecture {0} {1}", + process_arch.GetArchitectureName(), + process_arch.GetTriple().getTriple()); + } + + AddressableBits addressable_bits = m_gdb_comm.GetAddressableBits(); + SetAddressableBitMasks(addressable_bits); + + if (process_arch.IsValid()) { + const ArchSpec &target_arch = GetTarget().GetArchitecture(); + if (target_arch.IsValid()) { + LLDB_LOG(log, "analyzing target arch, currently {0} {1}", + target_arch.GetArchitectureName(), + target_arch.GetTriple().getTriple()); + + // If the remote host is ARM and we have apple as the vendor, then + // ARM executables and shared libraries can have mixed ARM + // architectures. + // You can have an armv6 executable, and if the host is armv7, then the + // system will load the best possible architecture for all shared + // libraries it has, so we really need to take the remote host + // architecture as our defacto architecture in this case. + + if ((process_arch.GetMachine() == llvm::Triple::arm || + process_arch.GetMachine() == llvm::Triple::thumb) && + process_arch.GetTriple().getVendor() == llvm::Triple::Apple) { + GetTarget().SetArchitecture(process_arch); + LLDB_LOG(log, + "remote process is ARM/Apple, " + "setting target arch to {0} {1}", + process_arch.GetArchitectureName(), + process_arch.GetTriple().getTriple()); + } else { + // Fill in what is missing in the triple + const llvm::Triple &remote_triple = process_arch.GetTriple(); + llvm::Triple new_target_triple = target_arch.GetTriple(); + if (new_target_triple.getVendorName().size() == 0) { + new_target_triple.setVendor(remote_triple.getVendor()); + + if (new_target_triple.getOSName().size() == 0) { + new_target_triple.setOS(remote_triple.getOS()); + + if (new_target_triple.getEnvironmentName().size() == 0) + new_target_triple.setEnvironment(remote_triple.getEnvironment()); + } + + ArchSpec new_target_arch = target_arch; + new_target_arch.SetTriple(new_target_triple); + GetTarget().SetArchitecture(new_target_arch); + } + } + + LLDB_LOG(log, + "final target arch after adjustments for remote architecture: " + "{0} {1}", + target_arch.GetArchitectureName(), + target_arch.GetTriple().getTriple()); + } else { + // The target doesn't have a valid architecture yet, set it from the + // architecture we got from the remote GDB server + GetTarget().SetArchitecture(process_arch); + } + } + + // Target and Process are reasonably initailized; + // load any binaries we have metadata for / set load address. + LoadStubBinaries(); + MaybeLoadExecutableModule(); + + // Find out which StructuredDataPlugins are supported by the debug monitor. + // These plugins transmit data over async $J packets. + if (StructuredData::Array *supported_packets = + m_gdb_comm.GetSupportedStructuredDataPlugins()) + MapSupportedStructuredDataPlugins(*supported_packets); + + // If connected to LLDB ("native-signals+"), use signal defs for + // the remote platform. If connected to GDB, just use the standard set. + if (!m_gdb_comm.UsesNativeSignals()) { + SetUnixSignals(std::make_shared<GDBRemoteSignals>()); + } else { + PlatformSP platform_sp = GetTarget().GetPlatform(); + if (platform_sp && platform_sp->IsConnected()) + SetUnixSignals(platform_sp->GetUnixSignals()); + else + SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture())); + } +} + +void ProcessGDBRemote::LoadStubBinaries() { + // The remote stub may know about the "main binary" in + // the context of a firmware debug session, and can + // give us a UUID and an address/slide of where the + // binary is loaded in memory. + UUID standalone_uuid; + addr_t standalone_value; + bool standalone_value_is_offset; + if (m_gdb_comm.GetProcessStandaloneBinary(standalone_uuid, standalone_value, + standalone_value_is_offset)) { + ModuleSP module_sp; + + if (standalone_uuid.IsValid()) { + const bool force_symbol_search = true; + const bool notify = true; + const bool set_address_in_target = true; + const bool allow_memory_image_last_resort = false; + DynamicLoader::LoadBinaryWithUUIDAndAddress( + this, "", standalone_uuid, standalone_value, + standalone_value_is_offset, force_symbol_search, notify, + set_address_in_target, allow_memory_image_last_resort); + } + } + + // The remote stub may know about a list of binaries to + // force load into the process -- a firmware type situation + // where multiple binaries are present in virtual memory, + // and we are only given the addresses of the binaries. + // Not intended for use with userland debugging, when we use + // a DynamicLoader plugin that knows how to find the loaded + // binaries, and will track updates as binaries are added. + + std::vector<addr_t> bin_addrs = m_gdb_comm.GetProcessStandaloneBinaries(); + if (bin_addrs.size()) { + UUID uuid; + const bool value_is_slide = false; + for (addr_t addr : bin_addrs) { + const bool notify = true; + // First see if this is a special platform + // binary that may determine the DynamicLoader and + // Platform to be used in this Process and Target. + if (GetTarget() + .GetDebugger() + .GetPlatformList() + .LoadPlatformBinaryAndSetup(this, addr, notify)) + continue; + + const bool force_symbol_search = true; + const bool set_address_in_target = true; + const bool allow_memory_image_last_resort = false; + // Second manually load this binary into the Target. + DynamicLoader::LoadBinaryWithUUIDAndAddress( + this, llvm::StringRef(), uuid, addr, value_is_slide, + force_symbol_search, notify, set_address_in_target, + allow_memory_image_last_resort); + } + } +} + +void ProcessGDBRemote::MaybeLoadExecutableModule() { + ModuleSP module_sp = GetTarget().GetExecutableModule(); + if (!module_sp) + return; + + std::optional<QOffsets> offsets = m_gdb_comm.GetQOffsets(); + if (!offsets) + return; + + bool is_uniform = + size_t(llvm::count(offsets->offsets, offsets->offsets[0])) == + offsets->offsets.size(); + if (!is_uniform) + return; // TODO: Handle non-uniform responses. + + bool changed = false; + module_sp->SetLoadAddress(GetTarget(), offsets->offsets[0], + /*value_is_offset=*/true, changed); + if (changed) { + ModuleList list; + list.Append(module_sp); + m_process->GetTarget().ModulesDidLoad(list); + } +} + +void ProcessGDBRemote::DidLaunch() { + ArchSpec process_arch; + DidLaunchOrAttach(process_arch); +} + +Status ProcessGDBRemote::DoAttachToProcessWithID( + lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { + Log *log = GetLog(GDBRLog::Process); + Status error; + + LLDB_LOGF(log, "ProcessGDBRemote::%s()", __FUNCTION__); + + // Clear out and clean up from any current state + Clear(); + if (attach_pid != LLDB_INVALID_PROCESS_ID) { + error = EstablishConnectionIfNeeded(attach_info); + if (error.Success()) { + m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); + + char packet[64]; + const int packet_len = + ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); + SetID(attach_pid); + auto data_sp = + std::make_shared<EventDataBytes>(llvm::StringRef(packet, packet_len)); + m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue, data_sp); + } else + SetExitStatus(-1, error.AsCString()); + } + + return error; +} + +Status ProcessGDBRemote::DoAttachToProcessWithName( + const char *process_name, const ProcessAttachInfo &attach_info) { + Status error; + // Clear out and clean up from any current state + Clear(); + + if (process_name && process_name[0]) { + error = EstablishConnectionIfNeeded(attach_info); + if (error.Success()) { + StreamString packet; + + m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); + + if (attach_info.GetWaitForLaunch()) { + if (!m_gdb_comm.GetVAttachOrWaitSupported()) { + packet.PutCString("vAttachWait"); + } else { + if (attach_info.GetIgnoreExisting()) + packet.PutCString("vAttachWait"); + else + packet.PutCString("vAttachOrWait"); + } + } else + packet.PutCString("vAttachName"); + packet.PutChar(';'); + packet.PutBytesAsRawHex8(process_name, strlen(process_name), + endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + + auto data_sp = std::make_shared<EventDataBytes>(packet.GetString()); + m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue, data_sp); + + } else + SetExitStatus(-1, error.AsCString()); + } + return error; +} + +llvm::Expected<TraceSupportedResponse> ProcessGDBRemote::TraceSupported() { + return m_gdb_comm.SendTraceSupported(GetInterruptTimeout()); +} + +llvm::Error ProcessGDBRemote::TraceStop(const TraceStopRequest &request) { + return m_gdb_comm.SendTraceStop(request, GetInterruptTimeout()); +} + +llvm::Error ProcessGDBRemote::TraceStart(const llvm::json::Value &request) { + return m_gdb_comm.SendTraceStart(request, GetInterruptTimeout()); +} + +llvm::Expected<std::string> +ProcessGDBRemote::TraceGetState(llvm::StringRef type) { + return m_gdb_comm.SendTraceGetState(type, GetInterruptTimeout()); +} + +llvm::Expected<std::vector<uint8_t>> +ProcessGDBRemote::TraceGetBinaryData(const TraceGetBinaryDataRequest &request) { + return m_gdb_comm.SendTraceGetBinaryData(request, GetInterruptTimeout()); +} + +void ProcessGDBRemote::DidExit() { + // When we exit, disconnect from the GDB server communications + m_gdb_comm.Disconnect(); +} + +void ProcessGDBRemote::DidAttach(ArchSpec &process_arch) { + // If you can figure out what the architecture is, fill it in here. + process_arch.Clear(); + DidLaunchOrAttach(process_arch); +} + +Status ProcessGDBRemote::WillResume() { + m_continue_c_tids.clear(); + m_continue_C_tids.clear(); + m_continue_s_tids.clear(); + m_continue_S_tids.clear(); + m_jstopinfo_sp.reset(); + m_jthreadsinfo_sp.reset(); + return Status(); +} + +Status ProcessGDBRemote::DoResume() { + Status error; + Log *log = GetLog(GDBRLog::Process); + LLDB_LOGF(log, "ProcessGDBRemote::Resume()"); + + ListenerSP listener_sp( + Listener::MakeListener("gdb-remote.resume-packet-sent")); + if (listener_sp->StartListeningForEvents( + &m_gdb_comm, GDBRemoteClientBase::eBroadcastBitRunPacketSent)) { + listener_sp->StartListeningForEvents( + &m_async_broadcaster, + ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); + + const size_t num_threads = GetThreadList().GetSize(); + + StreamString continue_packet; + bool continue_packet_error = false; + if (m_gdb_comm.HasAnyVContSupport()) { + std::string pid_prefix; + if (m_gdb_comm.GetMultiprocessSupported()) + pid_prefix = llvm::formatv("p{0:x-}.", GetID()); + + if (m_continue_c_tids.size() == num_threads || + (m_continue_c_tids.empty() && m_continue_C_tids.empty() && + m_continue_s_tids.empty() && m_continue_S_tids.empty())) { + // All threads are continuing + if (m_gdb_comm.GetMultiprocessSupported()) + continue_packet.Format("vCont;c:{0}-1", pid_prefix); + else + continue_packet.PutCString("c"); + } else { + continue_packet.PutCString("vCont"); + + if (!m_continue_c_tids.empty()) { + if (m_gdb_comm.GetVContSupported('c')) { + for (tid_collection::const_iterator + t_pos = m_continue_c_tids.begin(), + t_end = m_continue_c_tids.end(); + t_pos != t_end; ++t_pos) + continue_packet.Format(";c:{0}{1:x-}", pid_prefix, *t_pos); + } else + continue_packet_error = true; + } + + if (!continue_packet_error && !m_continue_C_tids.empty()) { + if (m_gdb_comm.GetVContSupported('C')) { + for (tid_sig_collection::const_iterator + s_pos = m_continue_C_tids.begin(), + s_end = m_continue_C_tids.end(); + s_pos != s_end; ++s_pos) + continue_packet.Format(";C{0:x-2}:{1}{2:x-}", s_pos->second, + pid_prefix, s_pos->first); + } else + continue_packet_error = true; + } + + if (!continue_packet_error && !m_continue_s_tids.empty()) { + if (m_gdb_comm.GetVContSupported('s')) { + for (tid_collection::const_iterator + t_pos = m_continue_s_tids.begin(), + t_end = m_continue_s_tids.end(); + t_pos != t_end; ++t_pos) + continue_packet.Format(";s:{0}{1:x-}", pid_prefix, *t_pos); + } else + continue_packet_error = true; + } + + if (!continue_packet_error && !m_continue_S_tids.empty()) { + if (m_gdb_comm.GetVContSupported('S')) { + for (tid_sig_collection::const_iterator + s_pos = m_continue_S_tids.begin(), + s_end = m_continue_S_tids.end(); + s_pos != s_end; ++s_pos) + continue_packet.Format(";S{0:x-2}:{1}{2:x-}", s_pos->second, + pid_prefix, s_pos->first); + } else + continue_packet_error = true; + } + + if (continue_packet_error) + continue_packet.Clear(); + } + } else + continue_packet_error = true; + + if (continue_packet_error) { + // Either no vCont support, or we tried to use part of the vCont packet + // that wasn't supported by the remote GDB server. We need to try and + // make a simple packet that can do our continue + const size_t num_continue_c_tids = m_continue_c_tids.size(); + const size_t num_continue_C_tids = m_continue_C_tids.size(); + const size_t num_continue_s_tids = m_continue_s_tids.size(); + const size_t num_continue_S_tids = m_continue_S_tids.size(); + if (num_continue_c_tids > 0) { + if (num_continue_c_tids == num_threads) { + // All threads are resuming... + m_gdb_comm.SetCurrentThreadForRun(-1); + continue_packet.PutChar('c'); + continue_packet_error = false; + } else if (num_continue_c_tids == 1 && num_continue_C_tids == 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 0) { + // Only one thread is continuing + m_gdb_comm.SetCurrentThreadForRun(m_continue_c_tids.front()); + continue_packet.PutChar('c'); + continue_packet_error = false; + } + } + + if (continue_packet_error && num_continue_C_tids > 0) { + if ((num_continue_C_tids + num_continue_c_tids) == num_threads && + num_continue_C_tids > 0 && num_continue_s_tids == 0 && + num_continue_S_tids == 0) { + const int continue_signo = m_continue_C_tids.front().second; + // Only one thread is continuing + if (num_continue_C_tids > 1) { + // More that one thread with a signal, yet we don't have vCont + // support and we are being asked to resume each thread with a + // signal, we need to make sure they are all the same signal, or we + // can't issue the continue accurately with the current support... + if (num_continue_C_tids > 1) { + continue_packet_error = false; + for (size_t i = 1; i < m_continue_C_tids.size(); ++i) { + if (m_continue_C_tids[i].second != continue_signo) + continue_packet_error = true; + } + } + if (!continue_packet_error) + m_gdb_comm.SetCurrentThreadForRun(-1); + } else { + // Set the continue thread ID + continue_packet_error = false; + m_gdb_comm.SetCurrentThreadForRun(m_continue_C_tids.front().first); + } + if (!continue_packet_error) { + // Add threads continuing with the same signo... + continue_packet.Printf("C%2.2x", continue_signo); + } + } + } + + if (continue_packet_error && num_continue_s_tids > 0) { + if (num_continue_s_tids == num_threads) { + // All threads are resuming... + m_gdb_comm.SetCurrentThreadForRun(-1); + + continue_packet.PutChar('s'); + + continue_packet_error = false; + } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && + num_continue_s_tids == 1 && num_continue_S_tids == 0) { + // Only one thread is stepping + m_gdb_comm.SetCurrentThreadForRun(m_continue_s_tids.front()); + continue_packet.PutChar('s'); + continue_packet_error = false; + } + } + + if (!continue_packet_error && num_continue_S_tids > 0) { + if (num_continue_S_tids == num_threads) { + const int step_signo = m_continue_S_tids.front().second; + // Are all threads trying to step with the same signal? + continue_packet_error = false; + if (num_continue_S_tids > 1) { + for (size_t i = 1; i < num_threads; ++i) { + if (m_continue_S_tids[i].second != step_signo) + continue_packet_error = true; + } + } + if (!continue_packet_error) { + // Add threads stepping with the same signo... + m_gdb_comm.SetCurrentThreadForRun(-1); + continue_packet.Printf("S%2.2x", step_signo); + } + } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && + num_continue_s_tids == 0 && num_continue_S_tids == 1) { + // Only one thread is stepping with signal + m_gdb_comm.SetCurrentThreadForRun(m_continue_S_tids.front().first); + continue_packet.Printf("S%2.2x", m_continue_S_tids.front().second); + continue_packet_error = false; + } + } + } + + if (continue_packet_error) { + error.SetErrorString("can't make continue packet for this resume"); + } else { + EventSP event_sp; + if (!m_async_thread.IsJoinable()) { + error.SetErrorString("Trying to resume but the async thread is dead."); + LLDB_LOGF(log, "ProcessGDBRemote::DoResume: Trying to resume but the " + "async thread is dead."); + return error; + } + + auto data_sp = + std::make_shared<EventDataBytes>(continue_packet.GetString()); + m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue, data_sp); + + if (!listener_sp->GetEvent(event_sp, std::chrono::seconds(5))) { + error.SetErrorString("Resume timed out."); + LLDB_LOGF(log, "ProcessGDBRemote::DoResume: Resume timed out."); + } else if (event_sp->BroadcasterIs(&m_async_broadcaster)) { + error.SetErrorString("Broadcast continue, but the async thread was " + "killed before we got an ack back."); + LLDB_LOGF(log, + "ProcessGDBRemote::DoResume: Broadcast continue, but the " + "async thread was killed before we got an ack back."); + return error; + } + } + } + + return error; +} + +void ProcessGDBRemote::ClearThreadIDList() { + std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); + m_thread_ids.clear(); + m_thread_pcs.clear(); +} + +size_t ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue( + llvm::StringRef value) { + m_thread_ids.clear(); + lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); + StringExtractorGDBRemote thread_ids{value}; + + do { + auto pid_tid = thread_ids.GetPidTid(pid); + if (pid_tid && pid_tid->first == pid) { + lldb::tid_t tid = pid_tid->second; + if (tid != LLDB_INVALID_THREAD_ID && + tid != StringExtractorGDBRemote::AllProcesses) + m_thread_ids.push_back(tid); + } + } while (thread_ids.GetChar() == ','); + + return m_thread_ids.size(); +} + +size_t ProcessGDBRemote::UpdateThreadPCsFromStopReplyThreadsValue( + llvm::StringRef value) { + m_thread_pcs.clear(); + for (llvm::StringRef x : llvm::split(value, ',')) { + lldb::addr_t pc; + if (llvm::to_integer(x, pc, 16)) + m_thread_pcs.push_back(pc); + } + return m_thread_pcs.size(); +} + +bool ProcessGDBRemote::UpdateThreadIDList() { + std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); + + if (m_jthreadsinfo_sp) { + // If we have the JSON threads info, we can get the thread list from that + StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); + if (thread_infos && thread_infos->GetSize() > 0) { + m_thread_ids.clear(); + m_thread_pcs.clear(); + thread_infos->ForEach([this](StructuredData::Object *object) -> bool { + StructuredData::Dictionary *thread_dict = object->GetAsDictionary(); + if (thread_dict) { + // Set the thread stop info from the JSON dictionary + SetThreadStopInfo(thread_dict); + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>("tid", tid)) + m_thread_ids.push_back(tid); + } + return true; // Keep iterating through all thread_info objects + }); + } + if (!m_thread_ids.empty()) + return true; + } else { + // See if we can get the thread IDs from the current stop reply packets + // that might contain a "threads" key/value pair + + if (m_last_stop_packet) { + // Get the thread stop info + StringExtractorGDBRemote &stop_info = *m_last_stop_packet; + const std::string &stop_info_str = std::string(stop_info.GetStringRef()); + + m_thread_pcs.clear(); + const size_t thread_pcs_pos = stop_info_str.find(";thread-pcs:"); + if (thread_pcs_pos != std::string::npos) { + const size_t start = thread_pcs_pos + strlen(";thread-pcs:"); + const size_t end = stop_info_str.find(';', start); + if (end != std::string::npos) { + std::string value = stop_info_str.substr(start, end - start); + UpdateThreadPCsFromStopReplyThreadsValue(value); + } + } + + const size_t threads_pos = stop_info_str.find(";threads:"); + if (threads_pos != std::string::npos) { + const size_t start = threads_pos + strlen(";threads:"); + const size_t end = stop_info_str.find(';', start); + if (end != std::string::npos) { + std::string value = stop_info_str.substr(start, end - start); + if (UpdateThreadIDsFromStopReplyThreadsValue(value)) + return true; + } + } + } + } + + bool sequence_mutex_unavailable = false; + m_gdb_comm.GetCurrentThreadIDs(m_thread_ids, sequence_mutex_unavailable); + if (sequence_mutex_unavailable) { + return false; // We just didn't get the list + } + return true; +} + +bool ProcessGDBRemote::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) { + // locker will keep a mutex locked until it goes out of scope + Log *log = GetLog(GDBRLog::Thread); + LLDB_LOGV(log, "pid = {0}", GetID()); + + size_t num_thread_ids = m_thread_ids.size(); + // The "m_thread_ids" thread ID list should always be updated after each stop + // reply packet, but in case it isn't, update it here. + if (num_thread_ids == 0) { + if (!UpdateThreadIDList()) + return false; + num_thread_ids = m_thread_ids.size(); + } + + ThreadList old_thread_list_copy(old_thread_list); + if (num_thread_ids > 0) { + for (size_t i = 0; i < num_thread_ids; ++i) { + tid_t tid = m_thread_ids[i]; + ThreadSP thread_sp( + old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); + if (!thread_sp) { + thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid); + LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.", + thread_sp.get(), thread_sp->GetID()); + } else { + LLDB_LOGV(log, "Found old thread: {0} for thread ID: {1:x}.", + thread_sp.get(), thread_sp->GetID()); + } + + SetThreadPc(thread_sp, i); + new_thread_list.AddThreadSortedByIndexID(thread_sp); + } + } + + // Whatever that is left in old_thread_list_copy are not present in + // new_thread_list. Remove non-existent threads from internal id table. + size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); + for (size_t i = 0; i < old_num_thread_ids; i++) { + ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex(i, false)); + if (old_thread_sp) { + lldb::tid_t old_thread_id = old_thread_sp->GetProtocolID(); + m_thread_id_to_index_id_map.erase(old_thread_id); + } + } + + return true; +} + +void ProcessGDBRemote::SetThreadPc(const ThreadSP &thread_sp, uint64_t index) { + if (m_thread_ids.size() == m_thread_pcs.size() && thread_sp.get() && + GetByteOrder() != eByteOrderInvalid) { + ThreadGDBRemote *gdb_thread = + static_cast<ThreadGDBRemote *>(thread_sp.get()); + RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); + if (reg_ctx_sp) { + uint32_t pc_regnum = reg_ctx_sp->ConvertRegisterKindToRegisterNumber( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + if (pc_regnum != LLDB_INVALID_REGNUM) { + gdb_thread->PrivateSetRegisterValue(pc_regnum, m_thread_pcs[index]); + } + } + } +} + +bool ProcessGDBRemote::GetThreadStopInfoFromJSON( + ThreadGDBRemote *thread, const StructuredData::ObjectSP &thread_infos_sp) { + // See if we got thread stop infos for all threads via the "jThreadsInfo" + // packet + if (thread_infos_sp) { + StructuredData::Array *thread_infos = thread_infos_sp->GetAsArray(); + if (thread_infos) { + lldb::tid_t tid; + const size_t n = thread_infos->GetSize(); + for (size_t i = 0; i < n; ++i) { + StructuredData::Dictionary *thread_dict = + thread_infos->GetItemAtIndex(i)->GetAsDictionary(); + if (thread_dict) { + if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>( + "tid", tid, LLDB_INVALID_THREAD_ID)) { + if (tid == thread->GetID()) + return (bool)SetThreadStopInfo(thread_dict); + } + } + } + } + } + return false; +} + +bool ProcessGDBRemote::CalculateThreadStopInfo(ThreadGDBRemote *thread) { + // See if we got thread stop infos for all threads via the "jThreadsInfo" + // packet + if (GetThreadStopInfoFromJSON(thread, m_jthreadsinfo_sp)) + return true; + + // See if we got thread stop info for any threads valid stop info reasons + // threads via the "jstopinfo" packet stop reply packet key/value pair? + if (m_jstopinfo_sp) { + // If we have "jstopinfo" then we have stop descriptions for all threads + // that have stop reasons, and if there is no entry for a thread, then it + // has no stop reason. + thread->GetRegisterContext()->InvalidateIfNeeded(true); + if (!GetThreadStopInfoFromJSON(thread, m_jstopinfo_sp)) { + // If a thread is stopped at a breakpoint site, set that as the stop + // reason even if it hasn't executed the breakpoint instruction yet. + // We will silently step over the breakpoint when we resume execution + // and miss the fact that this thread hit the breakpoint. + const size_t num_thread_ids = m_thread_ids.size(); + for (size_t i = 0; i < num_thread_ids; i++) { + if (m_thread_ids[i] == thread->GetID() && m_thread_pcs.size() > i) { + addr_t pc = m_thread_pcs[i]; + lldb::BreakpointSiteSP bp_site_sp = + thread->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp) { + if (bp_site_sp->ValidForThisThread(*thread)) { + thread->SetStopInfo( + StopInfo::CreateStopReasonWithBreakpointSiteID( + *thread, bp_site_sp->GetID())); + return true; + } + } + } + } + thread->SetStopInfo(StopInfoSP()); + } + return true; + } + + // Fall back to using the qThreadStopInfo packet + StringExtractorGDBRemote stop_packet; + if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet)) + return SetThreadStopInfo(stop_packet) == eStateStopped; + return false; +} + +void ProcessGDBRemote::ParseExpeditedRegisters( + ExpeditedRegisterMap &expedited_register_map, ThreadSP thread_sp) { + ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *>(thread_sp.get()); + RegisterContextSP gdb_reg_ctx_sp(gdb_thread->GetRegisterContext()); + + for (const auto &pair : expedited_register_map) { + StringExtractor reg_value_extractor(pair.second); + WritableDataBufferSP buffer_sp( + new DataBufferHeap(reg_value_extractor.GetStringRef().size() / 2, 0)); + reg_value_extractor.GetHexBytes(buffer_sp->GetData(), '\xcc'); + uint32_t lldb_regnum = gdb_reg_ctx_sp->ConvertRegisterKindToRegisterNumber( + eRegisterKindProcessPlugin, pair.first); + gdb_thread->PrivateSetRegisterValue(lldb_regnum, buffer_sp->GetData()); + } +} + +ThreadSP ProcessGDBRemote::SetThreadStopInfo( + lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map, + uint8_t signo, const std::string &thread_name, const std::string &reason, + const std::string &description, uint32_t exc_type, + const std::vector<addr_t> &exc_data, addr_t thread_dispatch_qaddr, + bool queue_vars_valid, // Set to true if queue_name, queue_kind and + // queue_serial are valid + LazyBool associated_with_dispatch_queue, addr_t dispatch_queue_t, + std::string &queue_name, QueueKind queue_kind, uint64_t queue_serial) { + + if (tid == LLDB_INVALID_THREAD_ID) + return nullptr; + + ThreadSP thread_sp; + // Scope for "locker" below + { + // m_thread_list_real does have its own mutex, but we need to hold onto the + // mutex between the call to m_thread_list_real.FindThreadByID(...) and the + // m_thread_list_real.AddThread(...) so it doesn't change on us + std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); + thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); + + if (!thread_sp) { + // Create the thread if we need to + thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid); + m_thread_list_real.AddThread(thread_sp); + } + } + + ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *>(thread_sp.get()); + RegisterContextSP reg_ctx_sp(gdb_thread->GetRegisterContext()); + + reg_ctx_sp->InvalidateIfNeeded(true); + + auto iter = std::find(m_thread_ids.begin(), m_thread_ids.end(), tid); + if (iter != m_thread_ids.end()) + SetThreadPc(thread_sp, iter - m_thread_ids.begin()); + + ParseExpeditedRegisters(expedited_register_map, thread_sp); + + if (reg_ctx_sp->ReconfigureRegisterInfo()) { + // Now we have changed the offsets of all the registers, so the values + // will be corrupted. + reg_ctx_sp->InvalidateAllRegisters(); + // Expedited registers values will never contain registers that would be + // resized by a reconfigure. So we are safe to continue using these + // values. + ParseExpeditedRegisters(expedited_register_map, thread_sp); + } + + thread_sp->SetName(thread_name.empty() ? nullptr : thread_name.c_str()); + + gdb_thread->SetThreadDispatchQAddr(thread_dispatch_qaddr); + // Check if the GDB server was able to provide the queue name, kind and serial + // number + if (queue_vars_valid) + gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial, + dispatch_queue_t, associated_with_dispatch_queue); + else + gdb_thread->ClearQueueInfo(); + + gdb_thread->SetAssociatedWithLibdispatchQueue(associated_with_dispatch_queue); + + if (dispatch_queue_t != LLDB_INVALID_ADDRESS) + gdb_thread->SetQueueLibdispatchQueueAddress(dispatch_queue_t); + + // Make sure we update our thread stop reason just once, but don't overwrite + // the stop info for threads that haven't moved: + StopInfoSP current_stop_info_sp = thread_sp->GetPrivateStopInfo(false); + if (thread_sp->GetTemporaryResumeState() == eStateSuspended && + current_stop_info_sp) { + thread_sp->SetStopInfo(current_stop_info_sp); + return thread_sp; + } + + if (!thread_sp->StopInfoIsUpToDate()) { + thread_sp->SetStopInfo(StopInfoSP()); + // If there's a memory thread backed by this thread, we need to use it to + // calculate StopInfo. + if (ThreadSP memory_thread_sp = m_thread_list.GetBackingThread(thread_sp)) + thread_sp = memory_thread_sp; + + if (exc_type != 0) { + const size_t exc_data_size = exc_data.size(); + + thread_sp->SetStopInfo( + StopInfoMachException::CreateStopReasonWithMachException( + *thread_sp, exc_type, exc_data_size, + exc_data_size >= 1 ? exc_data[0] : 0, + exc_data_size >= 2 ? exc_data[1] : 0, + exc_data_size >= 3 ? exc_data[2] : 0)); + } else { + bool handled = false; + bool did_exec = false; + // debugserver can send reason = "none" which is equivalent + // to no reason. + if (!reason.empty() && reason != "none") { + if (reason == "trace") { + addr_t pc = thread_sp->GetRegisterContext()->GetPC(); + lldb::BreakpointSiteSP bp_site_sp = + thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress( + pc); + + // If the current pc is a breakpoint site then the StopInfo should be + // set to Breakpoint Otherwise, it will be set to Trace. + if (bp_site_sp && bp_site_sp->ValidForThisThread(*thread_sp)) { + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonWithBreakpointSiteID( + *thread_sp, bp_site_sp->GetID())); + } else + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonToTrace(*thread_sp)); + handled = true; + } else if (reason == "breakpoint") { + addr_t pc = thread_sp->GetRegisterContext()->GetPC(); + lldb::BreakpointSiteSP bp_site_sp = + thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress( + pc); + if (bp_site_sp) { + // If the breakpoint is for this thread, then we'll report the hit, + // but if it is for another thread, we can just report no reason. + // We don't need to worry about stepping over the breakpoint here, + // that will be taken care of when the thread resumes and notices + // that there's a breakpoint under the pc. + handled = true; + if (bp_site_sp->ValidForThisThread(*thread_sp)) { + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonWithBreakpointSiteID( + *thread_sp, bp_site_sp->GetID())); + } else { + StopInfoSP invalid_stop_info_sp; + thread_sp->SetStopInfo(invalid_stop_info_sp); + } + } + } else if (reason == "trap") { + // Let the trap just use the standard signal stop reason below... + } else if (reason == "watchpoint") { + // We will have between 1 and 3 fields in the description. + // + // \a wp_addr which is the original start address that + // lldb requested be watched, or an address that the + // hardware reported. This address should be within the + // range of a currently active watchpoint region - lldb + // should be able to find a watchpoint with this address. + // + // \a wp_index is the hardware watchpoint register number. + // + // \a wp_hit_addr is the actual address reported by the hardware, + // which may be outside the range of a region we are watching. + // + // On MIPS, we may get a false watchpoint exception where an + // access to the same 8 byte granule as a watchpoint will trigger, + // even if the access was not within the range of the watched + // region. When we get a \a wp_hit_addr outside the range of any + // set watchpoint, continue execution without making it visible to + // the user. + // + // On ARM, a related issue where a large access that starts + // before the watched region (and extends into the watched + // region) may report a hit address before the watched region. + // lldb will not find the "nearest" watchpoint to + // disable/step/re-enable it, so one of the valid watchpoint + // addresses should be provided as \a wp_addr. + StringExtractor desc_extractor(description.c_str()); + // FIXME NativeThreadLinux::SetStoppedByWatchpoint sends this + // up as + // <address within wp range> <wp hw index> <actual accessed addr> + // but this is not reading the <wp hw index>. Seems like it + // wouldn't work on MIPS, where that third field is important. + addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); + addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); + watch_id_t watch_id = LLDB_INVALID_WATCH_ID; + bool silently_continue = false; + WatchpointResourceSP wp_resource_sp; + if (wp_hit_addr != LLDB_INVALID_ADDRESS) { + wp_resource_sp = + m_watchpoint_resource_list.FindByAddress(wp_hit_addr); + // On MIPS, \a wp_hit_addr outside the range of a watched + // region means we should silently continue, it is a false hit. + ArchSpec::Core core = GetTarget().GetArchitecture().GetCore(); + if (!wp_resource_sp && core >= ArchSpec::kCore_mips_first && + core <= ArchSpec::kCore_mips_last) + silently_continue = true; + } + if (!wp_resource_sp && wp_addr != LLDB_INVALID_ADDRESS) + wp_resource_sp = m_watchpoint_resource_list.FindByAddress(wp_addr); + if (!wp_resource_sp) { + Log *log(GetLog(GDBRLog::Watchpoints)); + LLDB_LOGF(log, "failed to find watchpoint"); + watch_id = LLDB_INVALID_SITE_ID; + } else { + // LWP_TODO: This is hardcoding a single Watchpoint in a + // Resource, need to add + // StopInfo::CreateStopReasonWithWatchpointResource which + // represents all watchpoints that were tripped at this stop. + watch_id = wp_resource_sp->GetConstituentAtIndex(0)->GetID(); + } + thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithWatchpointID( + *thread_sp, watch_id, silently_continue)); + handled = true; + } else if (reason == "exception") { + thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( + *thread_sp, description.c_str())); + handled = true; + } else if (reason == "exec") { + did_exec = true; + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonWithExec(*thread_sp)); + handled = true; + } else if (reason == "processor trace") { + thread_sp->SetStopInfo(StopInfo::CreateStopReasonProcessorTrace( + *thread_sp, description.c_str())); + } else if (reason == "fork") { + StringExtractor desc_extractor(description.c_str()); + lldb::pid_t child_pid = + desc_extractor.GetU64(LLDB_INVALID_PROCESS_ID); + lldb::tid_t child_tid = desc_extractor.GetU64(LLDB_INVALID_THREAD_ID); + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonFork(*thread_sp, child_pid, child_tid)); + handled = true; + } else if (reason == "vfork") { + StringExtractor desc_extractor(description.c_str()); + lldb::pid_t child_pid = + desc_extractor.GetU64(LLDB_INVALID_PROCESS_ID); + lldb::tid_t child_tid = desc_extractor.GetU64(LLDB_INVALID_THREAD_ID); + thread_sp->SetStopInfo(StopInfo::CreateStopReasonVFork( + *thread_sp, child_pid, child_tid)); + handled = true; + } else if (reason == "vforkdone") { + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonVForkDone(*thread_sp)); + handled = true; + } + } else if (!signo) { + addr_t pc = thread_sp->GetRegisterContext()->GetPC(); + lldb::BreakpointSiteSP bp_site_sp = + thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + + // If a thread is stopped at a breakpoint site, set that as the stop + // reason even if it hasn't executed the breakpoint instruction yet. + // We will silently step over the breakpoint when we resume execution + // and miss the fact that this thread hit the breakpoint. + if (bp_site_sp && bp_site_sp->ValidForThisThread(*thread_sp)) { + thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID( + *thread_sp, bp_site_sp->GetID())); + handled = true; + } + } + + if (!handled && signo && !did_exec) { + if (signo == SIGTRAP) { + // Currently we are going to assume SIGTRAP means we are either + // hitting a breakpoint or hardware single stepping. + handled = true; + addr_t pc = + thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset; + lldb::BreakpointSiteSP bp_site_sp = + thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress( + pc); + + if (bp_site_sp) { + // If the breakpoint is for this thread, then we'll report the hit, + // but if it is for another thread, we can just report no reason. + // We don't need to worry about stepping over the breakpoint here, + // that will be taken care of when the thread resumes and notices + // that there's a breakpoint under the pc. + if (bp_site_sp->ValidForThisThread(*thread_sp)) { + if (m_breakpoint_pc_offset != 0) + thread_sp->GetRegisterContext()->SetPC(pc); + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonWithBreakpointSiteID( + *thread_sp, bp_site_sp->GetID())); + } else { + StopInfoSP invalid_stop_info_sp; + thread_sp->SetStopInfo(invalid_stop_info_sp); + } + } else { + // If we were stepping then assume the stop was the result of the + // trace. If we were not stepping then report the SIGTRAP. + // FIXME: We are still missing the case where we single step over a + // trap instruction. + if (thread_sp->GetTemporaryResumeState() == eStateStepping) + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonToTrace(*thread_sp)); + else + thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( + *thread_sp, signo, description.c_str())); + } + } + if (!handled) + thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithSignal( + *thread_sp, signo, description.c_str())); + } + + if (!description.empty()) { + lldb::StopInfoSP stop_info_sp(thread_sp->GetStopInfo()); + if (stop_info_sp) { + const char *stop_info_desc = stop_info_sp->GetDescription(); + if (!stop_info_desc || !stop_info_desc[0]) + stop_info_sp->SetDescription(description.c_str()); + } else { + thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException( + *thread_sp, description.c_str())); + } + } + } + } + return thread_sp; +} + +lldb::ThreadSP +ProcessGDBRemote::SetThreadStopInfo(StructuredData::Dictionary *thread_dict) { + static constexpr llvm::StringLiteral g_key_tid("tid"); + static constexpr llvm::StringLiteral g_key_name("name"); + static constexpr llvm::StringLiteral g_key_reason("reason"); + static constexpr llvm::StringLiteral g_key_metype("metype"); + static constexpr llvm::StringLiteral g_key_medata("medata"); + static constexpr llvm::StringLiteral g_key_qaddr("qaddr"); + static constexpr llvm::StringLiteral g_key_dispatch_queue_t( + "dispatch_queue_t"); + static constexpr llvm::StringLiteral g_key_associated_with_dispatch_queue( + "associated_with_dispatch_queue"); + static constexpr llvm::StringLiteral g_key_queue_name("qname"); + static constexpr llvm::StringLiteral g_key_queue_kind("qkind"); + static constexpr llvm::StringLiteral g_key_queue_serial_number("qserialnum"); + static constexpr llvm::StringLiteral g_key_registers("registers"); + static constexpr llvm::StringLiteral g_key_memory("memory"); + static constexpr llvm::StringLiteral g_key_description("description"); + static constexpr llvm::StringLiteral g_key_signal("signal"); + + // Stop with signal and thread info + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + uint8_t signo = 0; + std::string value; + std::string thread_name; + std::string reason; + std::string description; + uint32_t exc_type = 0; + std::vector<addr_t> exc_data; + addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; + ExpeditedRegisterMap expedited_register_map; + bool queue_vars_valid = false; + addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; + LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; + std::string queue_name; + QueueKind queue_kind = eQueueKindUnknown; + uint64_t queue_serial_number = 0; + // Iterate through all of the thread dictionary key/value pairs from the + // structured data dictionary + + // FIXME: we're silently ignoring invalid data here + thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name, + &signo, &reason, &description, &exc_type, &exc_data, + &thread_dispatch_qaddr, &queue_vars_valid, + &associated_with_dispatch_queue, &dispatch_queue_t, + &queue_name, &queue_kind, &queue_serial_number]( + llvm::StringRef key, + StructuredData::Object *object) -> bool { + if (key == g_key_tid) { + // thread in big endian hex + tid = object->GetUnsignedIntegerValue(LLDB_INVALID_THREAD_ID); + } else if (key == g_key_metype) { + // exception type in big endian hex + exc_type = object->GetUnsignedIntegerValue(0); + } else if (key == g_key_medata) { + // exception data in big endian hex + StructuredData::Array *array = object->GetAsArray(); + if (array) { + array->ForEach([&exc_data](StructuredData::Object *object) -> bool { + exc_data.push_back(object->GetUnsignedIntegerValue()); + return true; // Keep iterating through all array items + }); + } + } else if (key == g_key_name) { + thread_name = std::string(object->GetStringValue()); + } else if (key == g_key_qaddr) { + thread_dispatch_qaddr = + object->GetUnsignedIntegerValue(LLDB_INVALID_ADDRESS); + } else if (key == g_key_queue_name) { + queue_vars_valid = true; + queue_name = std::string(object->GetStringValue()); + } else if (key == g_key_queue_kind) { + std::string queue_kind_str = std::string(object->GetStringValue()); + if (queue_kind_str == "serial") { + queue_vars_valid = true; + queue_kind = eQueueKindSerial; + } else if (queue_kind_str == "concurrent") { + queue_vars_valid = true; + queue_kind = eQueueKindConcurrent; + } + } else if (key == g_key_queue_serial_number) { + queue_serial_number = object->GetUnsignedIntegerValue(0); + if (queue_serial_number != 0) + queue_vars_valid = true; + } else if (key == g_key_dispatch_queue_t) { + dispatch_queue_t = object->GetUnsignedIntegerValue(0); + if (dispatch_queue_t != 0 && dispatch_queue_t != LLDB_INVALID_ADDRESS) + queue_vars_valid = true; + } else if (key == g_key_associated_with_dispatch_queue) { + queue_vars_valid = true; + bool associated = object->GetBooleanValue(); + if (associated) + associated_with_dispatch_queue = eLazyBoolYes; + else + associated_with_dispatch_queue = eLazyBoolNo; + } else if (key == g_key_reason) { + reason = std::string(object->GetStringValue()); + } else if (key == g_key_description) { + description = std::string(object->GetStringValue()); + } else if (key == g_key_registers) { + StructuredData::Dictionary *registers_dict = object->GetAsDictionary(); + + if (registers_dict) { + registers_dict->ForEach( + [&expedited_register_map](llvm::StringRef key, + StructuredData::Object *object) -> bool { + uint32_t reg; + if (llvm::to_integer(key, reg)) + expedited_register_map[reg] = + std::string(object->GetStringValue()); + return true; // Keep iterating through all array items + }); + } + } else if (key == g_key_memory) { + StructuredData::Array *array = object->GetAsArray(); + if (array) { + array->ForEach([this](StructuredData::Object *object) -> bool { + StructuredData::Dictionary *mem_cache_dict = + object->GetAsDictionary(); + if (mem_cache_dict) { + lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; + if (mem_cache_dict->GetValueForKeyAsInteger<lldb::addr_t>( + "address", mem_cache_addr)) { + if (mem_cache_addr != LLDB_INVALID_ADDRESS) { + llvm::StringRef str; + if (mem_cache_dict->GetValueForKeyAsString("bytes", str)) { + StringExtractor bytes(str); + bytes.SetFilePos(0); + + const size_t byte_size = bytes.GetStringRef().size() / 2; + WritableDataBufferSP data_buffer_sp( + new DataBufferHeap(byte_size, 0)); + const size_t bytes_copied = + bytes.GetHexBytes(data_buffer_sp->GetData(), 0); + if (bytes_copied == byte_size) + m_memory_cache.AddL1CacheData(mem_cache_addr, + data_buffer_sp); + } + } + } + } + return true; // Keep iterating through all array items + }); + } + + } else if (key == g_key_signal) + signo = object->GetUnsignedIntegerValue(LLDB_INVALID_SIGNAL_NUMBER); + return true; // Keep iterating through all dictionary key/value pairs + }); + + return SetThreadStopInfo(tid, expedited_register_map, signo, thread_name, + reason, description, exc_type, exc_data, + thread_dispatch_qaddr, queue_vars_valid, + associated_with_dispatch_queue, dispatch_queue_t, + queue_name, queue_kind, queue_serial_number); +} + +StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { + lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); + stop_packet.SetFilePos(0); + const char stop_type = stop_packet.GetChar(); + switch (stop_type) { + case 'T': + case 'S': { + // This is a bit of a hack, but it is required. If we did exec, we need to + // clear our thread lists and also know to rebuild our dynamic register + // info before we lookup and threads and populate the expedited register + // values so we need to know this right away so we can cleanup and update + // our registers. + const uint32_t stop_id = GetStopID(); + if (stop_id == 0) { + // Our first stop, make sure we have a process ID, and also make sure we + // know about our registers + if (GetID() == LLDB_INVALID_PROCESS_ID && pid != LLDB_INVALID_PROCESS_ID) + SetID(pid); + BuildDynamicRegisterInfo(true); + } + // Stop with signal and thread info + lldb::pid_t stop_pid = LLDB_INVALID_PROCESS_ID; + lldb::tid_t tid = LLDB_INVALID_THREAD_ID; + const uint8_t signo = stop_packet.GetHexU8(); + llvm::StringRef key; + llvm::StringRef value; + std::string thread_name; + std::string reason; + std::string description; + uint32_t exc_type = 0; + std::vector<addr_t> exc_data; + addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; + bool queue_vars_valid = + false; // says if locals below that start with "queue_" are valid + addr_t dispatch_queue_t = LLDB_INVALID_ADDRESS; + LazyBool associated_with_dispatch_queue = eLazyBoolCalculate; + std::string queue_name; + QueueKind queue_kind = eQueueKindUnknown; + uint64_t queue_serial_number = 0; + ExpeditedRegisterMap expedited_register_map; + AddressableBits addressable_bits; + while (stop_packet.GetNameColonValue(key, value)) { + if (key.compare("metype") == 0) { + // exception type in big endian hex + value.getAsInteger(16, exc_type); + } else if (key.compare("medata") == 0) { + // exception data in big endian hex + uint64_t x; + value.getAsInteger(16, x); + exc_data.push_back(x); + } else if (key.compare("thread") == 0) { + // thread-id + StringExtractorGDBRemote thread_id{value}; + auto pid_tid = thread_id.GetPidTid(pid); + if (pid_tid) { + stop_pid = pid_tid->first; + tid = pid_tid->second; + } else + tid = LLDB_INVALID_THREAD_ID; + } else if (key.compare("threads") == 0) { + std::lock_guard<std::recursive_mutex> guard( + m_thread_list_real.GetMutex()); + UpdateThreadIDsFromStopReplyThreadsValue(value); + } else if (key.compare("thread-pcs") == 0) { + m_thread_pcs.clear(); + // A comma separated list of all threads in the current + // process that includes the thread for this stop reply packet + lldb::addr_t pc; + while (!value.empty()) { + llvm::StringRef pc_str; + std::tie(pc_str, value) = value.split(','); + if (pc_str.getAsInteger(16, pc)) + pc = LLDB_INVALID_ADDRESS; + m_thread_pcs.push_back(pc); + } + } else if (key.compare("jstopinfo") == 0) { + StringExtractor json_extractor(value); + std::string json; + // Now convert the HEX bytes into a string value + json_extractor.GetHexByteString(json); + + // This JSON contains thread IDs and thread stop info for all threads. + // It doesn't contain expedited registers, memory or queue info. + m_jstopinfo_sp = StructuredData::ParseJSON(json); + } else if (key.compare("hexname") == 0) { + StringExtractor name_extractor(value); + std::string name; + // Now convert the HEX bytes into a string value + name_extractor.GetHexByteString(thread_name); + } else if (key.compare("name") == 0) { + thread_name = std::string(value); + } else if (key.compare("qaddr") == 0) { + value.getAsInteger(16, thread_dispatch_qaddr); + } else if (key.compare("dispatch_queue_t") == 0) { + queue_vars_valid = true; + value.getAsInteger(16, dispatch_queue_t); + } else if (key.compare("qname") == 0) { + queue_vars_valid = true; + StringExtractor name_extractor(value); + // Now convert the HEX bytes into a string value + name_extractor.GetHexByteString(queue_name); + } else if (key.compare("qkind") == 0) { + queue_kind = llvm::StringSwitch<QueueKind>(value) + .Case("serial", eQueueKindSerial) + .Case("concurrent", eQueueKindConcurrent) + .Default(eQueueKindUnknown); + queue_vars_valid = queue_kind != eQueueKindUnknown; + } else if (key.compare("qserialnum") == 0) { + if (!value.getAsInteger(0, queue_serial_number)) + queue_vars_valid = true; + } else if (key.compare("reason") == 0) { + reason = std::string(value); + } else if (key.compare("description") == 0) { + StringExtractor desc_extractor(value); + // Now convert the HEX bytes into a string value + desc_extractor.GetHexByteString(description); + } else if (key.compare("memory") == 0) { + // Expedited memory. GDB servers can choose to send back expedited + // memory that can populate the L1 memory cache in the process so that + // things like the frame pointer backchain can be expedited. This will + // help stack backtracing be more efficient by not having to send as + // many memory read requests down the remote GDB server. + + // Key/value pair format: memory:<addr>=<bytes>; + // <addr> is a number whose base will be interpreted by the prefix: + // "0x[0-9a-fA-F]+" for hex + // "0[0-7]+" for octal + // "[1-9]+" for decimal + // <bytes> is native endian ASCII hex bytes just like the register + // values + llvm::StringRef addr_str, bytes_str; + std::tie(addr_str, bytes_str) = value.split('='); + if (!addr_str.empty() && !bytes_str.empty()) { + lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; + if (!addr_str.getAsInteger(0, mem_cache_addr)) { + StringExtractor bytes(bytes_str); + const size_t byte_size = bytes.GetBytesLeft() / 2; + WritableDataBufferSP data_buffer_sp( + new DataBufferHeap(byte_size, 0)); + const size_t bytes_copied = + bytes.GetHexBytes(data_buffer_sp->GetData(), 0); + if (bytes_copied == byte_size) + m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); + } + } + } else if (key.compare("watch") == 0 || key.compare("rwatch") == 0 || + key.compare("awatch") == 0) { + // Support standard GDB remote stop reply packet 'TAAwatch:addr' + lldb::addr_t wp_addr = LLDB_INVALID_ADDRESS; + value.getAsInteger(16, wp_addr); + + WatchpointResourceSP wp_resource_sp = + m_watchpoint_resource_list.FindByAddress(wp_addr); + + // Rewrite gdb standard watch/rwatch/awatch to + // "reason:watchpoint" + "description:ADDR", + // which is parsed in SetThreadStopInfo. + reason = "watchpoint"; + StreamString ostr; + ostr.Printf("%" PRIu64, wp_addr); + description = std::string(ostr.GetString()); + } else if (key.compare("library") == 0) { + auto error = LoadModules(); + if (error) { + Log *log(GetLog(GDBRLog::Process)); + LLDB_LOG_ERROR(log, std::move(error), "Failed to load modules: {0}"); + } + } else if (key.compare("fork") == 0 || key.compare("vfork") == 0) { + // fork includes child pid/tid in thread-id format + StringExtractorGDBRemote thread_id{value}; + auto pid_tid = thread_id.GetPidTid(LLDB_INVALID_PROCESS_ID); + if (!pid_tid) { + Log *log(GetLog(GDBRLog::Process)); + LLDB_LOG(log, "Invalid PID/TID to fork: {0}", value); + pid_tid = {{LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID}}; + } + + reason = key.str(); + StreamString ostr; + ostr.Printf("%" PRIu64 " %" PRIu64, pid_tid->first, pid_tid->second); + description = std::string(ostr.GetString()); + } else if (key.compare("addressing_bits") == 0) { + uint64_t addressing_bits; + if (!value.getAsInteger(0, addressing_bits)) { + addressable_bits.SetAddressableBits(addressing_bits); + } + } else if (key.compare("low_mem_addressing_bits") == 0) { + uint64_t addressing_bits; + if (!value.getAsInteger(0, addressing_bits)) { + addressable_bits.SetLowmemAddressableBits(addressing_bits); + } + } else if (key.compare("high_mem_addressing_bits") == 0) { + uint64_t addressing_bits; + if (!value.getAsInteger(0, addressing_bits)) { + addressable_bits.SetHighmemAddressableBits(addressing_bits); + } + } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) { + uint32_t reg = UINT32_MAX; + if (!key.getAsInteger(16, reg)) + expedited_register_map[reg] = std::string(std::move(value)); + } + } + + if (stop_pid != LLDB_INVALID_PROCESS_ID && stop_pid != pid) { + Log *log = GetLog(GDBRLog::Process); + LLDB_LOG(log, + "Received stop for incorrect PID = {0} (inferior PID = {1})", + stop_pid, pid); + return eStateInvalid; + } + + if (tid == LLDB_INVALID_THREAD_ID) { + // A thread id may be invalid if the response is old style 'S' packet + // which does not provide the + // thread information. So update the thread list and choose the first + // one. + UpdateThreadIDList(); + + if (!m_thread_ids.empty()) { + tid = m_thread_ids.front(); + } + } + + SetAddressableBitMasks(addressable_bits); + + ThreadSP thread_sp = SetThreadStopInfo( + tid, expedited_register_map, signo, thread_name, reason, description, + exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, + associated_with_dispatch_queue, dispatch_queue_t, queue_name, + queue_kind, queue_serial_number); + + return eStateStopped; + } break; + + case 'W': + case 'X': + // process exited + return eStateExited; + + default: + break; + } + return eStateInvalid; +} + +void ProcessGDBRemote::RefreshStateAfterStop() { + std::lock_guard<std::recursive_mutex> guard(m_thread_list_real.GetMutex()); + + m_thread_ids.clear(); + m_thread_pcs.clear(); + + // Set the thread stop info. It might have a "threads" key whose value is a + // list of all thread IDs in the current process, so m_thread_ids might get + // set. + // Check to see if SetThreadStopInfo() filled in m_thread_ids? + if (m_thread_ids.empty()) { + // No, we need to fetch the thread list manually + UpdateThreadIDList(); + } + + // We might set some stop info's so make sure the thread list is up to + // date before we do that or we might overwrite what was computed here. + UpdateThreadListIfNeeded(); + + if (m_last_stop_packet) + SetThreadStopInfo(*m_last_stop_packet); + m_last_stop_packet.reset(); + + // If we have queried for a default thread id + if (m_initial_tid != LLDB_INVALID_THREAD_ID) { + m_thread_list.SetSelectedThreadByID(m_initial_tid); + m_initial_tid = LLDB_INVALID_THREAD_ID; + } + + // Let all threads recover from stopping and do any clean up based on the + // previous thread state (if any). + m_thread_list_real.RefreshStateAfterStop(); +} + +Status ProcessGDBRemote::DoHalt(bool &caused_stop) { + Status error; + + if (m_public_state.GetValue() == eStateAttaching) { + // We are being asked to halt during an attach. We used to just close our + // file handle and debugserver will go away, but with remote proxies, it + // is better to send a positive signal, so let's send the interrupt first... + caused_stop = m_gdb_comm.Interrupt(GetInterruptTimeout()); + m_gdb_comm.Disconnect(); + } else + caused_stop = m_gdb_comm.Interrupt(GetInterruptTimeout()); + return error; +} + +Status ProcessGDBRemote::DoDetach(bool keep_stopped) { + Status error; + Log *log = GetLog(GDBRLog::Process); + LLDB_LOGF(log, "ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); + + error = m_gdb_comm.Detach(keep_stopped); + if (log) { + if (error.Success()) + log->PutCString( + "ProcessGDBRemote::DoDetach() detach packet sent successfully"); + else + LLDB_LOGF(log, + "ProcessGDBRemote::DoDetach() detach packet send failed: %s", + error.AsCString() ? error.AsCString() : "<unknown error>"); + } + + if (!error.Success()) + return error; + + // Sleep for one second to let the process get all detached... + StopAsyncThread(); + + SetPrivateState(eStateDetached); + ResumePrivateStateThread(); + + // KillDebugserverProcess (); + return error; +} + +Status ProcessGDBRemote::DoDestroy() { + Log *log = GetLog(GDBRLog::Process); + LLDB_LOGF(log, "ProcessGDBRemote::DoDestroy()"); + + // Interrupt if our inferior is running... + int exit_status = SIGABRT; + std::string exit_string; + + if (m_gdb_comm.IsConnected()) { + if (m_public_state.GetValue() != eStateAttaching) { + llvm::Expected<int> kill_res = m_gdb_comm.KillProcess(GetID()); + + if (kill_res) { + exit_status = kill_res.get(); +#if defined(__APPLE__) + // For Native processes on Mac OS X, we launch through the Host + // Platform, then hand the process off to debugserver, which becomes + // the parent process through "PT_ATTACH". Then when we go to kill + // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then + // we call waitpid which returns with no error and the correct + // status. But amusingly enough that doesn't seem to actually reap + // the process, but instead it is left around as a Zombie. Probably + // the kernel is in the process of switching ownership back to lldb + // which was the original parent, and gets confused in the handoff. + // Anyway, so call waitpid here to finally reap it. + PlatformSP platform_sp(GetTarget().GetPlatform()); + if (platform_sp && platform_sp->IsHost()) { + int status; + ::pid_t reap_pid; + reap_pid = waitpid(GetID(), &status, WNOHANG); + LLDB_LOGF(log, "Reaped pid: %d, status: %d.\n", reap_pid, status); + } +#endif + ClearThreadIDList(); + exit_string.assign("killed"); + } else { + exit_string.assign(llvm::toString(kill_res.takeError())); + } + } else { + exit_string.assign("killed or interrupted while attaching."); + } + } else { + // If we missed setting the exit status on the way out, do it here. + // NB set exit status can be called multiple times, the first one sets the + // status. + exit_string.assign("destroying when not connected to debugserver"); + } + + SetExitStatus(exit_status, exit_string.c_str()); + + StopAsyncThread(); + KillDebugserverProcess(); + return Status(); +} + +void ProcessGDBRemote::SetLastStopPacket( + const StringExtractorGDBRemote &response) { + const bool did_exec = + response.GetStringRef().find(";reason:exec;") != std::string::npos; + if (did_exec) { + Log *log = GetLog(GDBRLog::Process); + LLDB_LOGF(log, "ProcessGDBRemote::SetLastStopPacket () - detected exec"); + + m_thread_list_real.Clear(); + m_thread_list.Clear(); + BuildDynamicRegisterInfo(true); + m_gdb_comm.ResetDiscoverableSettings(did_exec); + } + + m_last_stop_packet = response; +} + +void ProcessGDBRemote::SetUnixSignals(const UnixSignalsSP &signals_sp) { + Process::SetUnixSignals(std::make_shared<GDBRemoteSignals>(signals_sp)); +} + +// Process Queries + +bool ProcessGDBRemote::IsAlive() { + return m_gdb_comm.IsConnected() && Process::IsAlive(); +} + +addr_t ProcessGDBRemote::GetImageInfoAddress() { + // request the link map address via the $qShlibInfoAddr packet + lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr(); + + // the loaded module list can also provides a link map address + if (addr == LLDB_INVALID_ADDRESS) { + llvm::Expected<LoadedModuleInfoList> list = GetLoadedModuleList(); + if (!list) { + Log *log = GetLog(GDBRLog::Process); + LLDB_LOG_ERROR(log, list.takeError(), "Failed to read module list: {0}."); + } else { + addr = list->m_link_map; + } + } + + return addr; +} + +void ProcessGDBRemote::WillPublicStop() { + // See if the GDB remote client supports the JSON threads info. If so, we + // gather stop info for all threads, expedited registers, expedited memory, + // runtime queue information (iOS and MacOSX only), and more. Expediting + // memory will help stack backtracing be much faster. Expediting registers + // will make sure we don't have to read the thread registers for GPRs. + m_jthreadsinfo_sp = m_gdb_comm.GetThreadsInfo(); + + if (m_jthreadsinfo_sp) { + // Now set the stop info for each thread and also expedite any registers + // and memory that was in the jThreadsInfo response. + StructuredData::Array *thread_infos = m_jthreadsinfo_sp->GetAsArray(); + if (thread_infos) { + const size_t n = thread_infos->GetSize(); + for (size_t i = 0; i < n; ++i) { + StructuredData::Dictionary *thread_dict = + thread_infos->GetItemAtIndex(i)->GetAsDictionary(); + if (thread_dict) + SetThreadStopInfo(thread_dict); + } + } + } +} + +// Process Memory +size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size, + Status &error) { + GetMaxMemorySize(); + bool binary_memory_read = m_gdb_comm.GetxPacketSupported(); + // M and m packets take 2 bytes for 1 byte of memory + size_t max_memory_size = + binary_memory_read ? m_max_memory_size : m_max_memory_size / 2; + if (size > max_memory_size) { + // Keep memory read sizes down to a sane limit. This function will be + // called multiple times in order to complete the task by + // lldb_private::Process so it is ok to do this. + size = max_memory_size; + } + + char packet[64]; + int packet_len; + packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64, + binary_memory_read ? 'x' : 'm', (uint64_t)addr, + (uint64_t)size); + assert(packet_len + 1 < (int)sizeof(packet)); + UNUSED_IF_ASSERT_DISABLED(packet_len); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, + GetInterruptTimeout()) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsNormalResponse()) { + error.Clear(); + if (binary_memory_read) { + // The lower level GDBRemoteCommunication packet receive layer has + // already de-quoted any 0x7d character escaping that was present in + // the packet + + size_t data_received_size = response.GetBytesLeft(); + if (data_received_size > size) { + // Don't write past the end of BUF if the remote debug server gave us + // too much data for some reason. + data_received_size = size; + } + memcpy(buf, response.GetStringRef().data(), data_received_size); + return data_received_size; + } else { + return response.GetHexBytes( + llvm::MutableArrayRef<uint8_t>((uint8_t *)buf, size), '\xdd'); + } + } else if (response.IsErrorResponse()) + error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr); + else if (response.IsUnsupportedResponse()) + error.SetErrorStringWithFormat( + "GDB server does not support reading memory"); + else + error.SetErrorStringWithFormat( + "unexpected response to GDB server memory read packet '%s': '%s'", + packet, response.GetStringRef().data()); + } else { + error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); + } + return 0; +} + +bool ProcessGDBRemote::SupportsMemoryTagging() { + return m_gdb_comm.GetMemoryTaggingSupported(); +} + +llvm::Expected<std::vector<uint8_t>> +ProcessGDBRemote::DoReadMemoryTags(lldb::addr_t addr, size_t len, + int32_t type) { + // By this point ReadMemoryTags has validated that tagging is enabled + // for this target/process/address. + DataBufferSP buffer_sp = m_gdb_comm.ReadMemoryTags(addr, len, type); + if (!buffer_sp) { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Error reading memory tags from remote"); + } + + // Return the raw tag data + llvm::ArrayRef<uint8_t> tag_data = buffer_sp->GetData(); + std::vector<uint8_t> got; + got.reserve(tag_data.size()); + std::copy(tag_data.begin(), tag_data.end(), std::back_inserter(got)); + return got; +} + +Status ProcessGDBRemote::DoWriteMemoryTags(lldb::addr_t addr, size_t len, + int32_t type, + const std::vector<uint8_t> &tags) { + // By now WriteMemoryTags should have validated that tagging is enabled + // for this target/process. + return m_gdb_comm.WriteMemoryTags(addr, len, type, tags); +} + +Status ProcessGDBRemote::WriteObjectFile( + std::vector<ObjectFile::LoadableData> entries) { + Status error; + // Sort the entries by address because some writes, like those to flash + // memory, must happen in order of increasing address. + std::stable_sort( + std::begin(entries), std::end(entries), + [](const ObjectFile::LoadableData a, const ObjectFile::LoadableData b) { + return a.Dest < b.Dest; + }); + m_allow_flash_writes = true; + error = Process::WriteObjectFile(entries); + if (error.Success()) + error = FlashDone(); + else + // Even though some of the writing failed, try to send a flash done if some + // of the writing succeeded so the flash state is reset to normal, but + // don't stomp on the error status that was set in the write failure since + // that's the one we want to report back. + FlashDone(); + m_allow_flash_writes = false; + return error; +} + +bool ProcessGDBRemote::HasErased(FlashRange range) { + auto size = m_erased_flash_ranges.GetSize(); + for (size_t i = 0; i < size; ++i) + if (m_erased_flash_ranges.GetEntryAtIndex(i)->Contains(range)) + return true; + return false; +} + +Status ProcessGDBRemote::FlashErase(lldb::addr_t addr, size_t size) { + Status status; + + MemoryRegionInfo region; + status = GetMemoryRegionInfo(addr, region); + if (!status.Success()) + return status; + + // The gdb spec doesn't say if erasures are allowed across multiple regions, + // but we'll disallow it to be safe and to keep the logic simple by worring + // about only one region's block size. DoMemoryWrite is this function's + // primary user, and it can easily keep writes within a single memory region + if (addr + size > region.GetRange().GetRangeEnd()) { + status.SetErrorString("Unable to erase flash in multiple regions"); + return status; + } + + uint64_t blocksize = region.GetBlocksize(); + if (blocksize == 0) { + status.SetErrorString("Unable to erase flash because blocksize is 0"); + return status; + } + + // Erasures can only be done on block boundary adresses, so round down addr + // and round up size + lldb::addr_t block_start_addr = addr - (addr % blocksize); + size += (addr - block_start_addr); + if ((size % blocksize) != 0) + size += (blocksize - size % blocksize); + + FlashRange range(block_start_addr, size); + + if (HasErased(range)) + return status; + + // We haven't erased the entire range, but we may have erased part of it. + // (e.g., block A is already erased and range starts in A and ends in B). So, + // adjust range if necessary to exclude already erased blocks. + if (!m_erased_flash_ranges.IsEmpty()) { + // Assuming that writes and erasures are done in increasing addr order, + // because that is a requirement of the vFlashWrite command. Therefore, we + // only need to look at the last range in the list for overlap. + const auto &last_range = *m_erased_flash_ranges.Back(); + if (range.GetRangeBase() < last_range.GetRangeEnd()) { + auto overlap = last_range.GetRangeEnd() - range.GetRangeBase(); + // overlap will be less than range.GetByteSize() or else HasErased() + // would have been true + range.SetByteSize(range.GetByteSize() - overlap); + range.SetRangeBase(range.GetRangeBase() + overlap); + } + } + + StreamString packet; + packet.Printf("vFlashErase:%" PRIx64 ",%" PRIx64, range.GetRangeBase(), + (uint64_t)range.GetByteSize()); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + GetInterruptTimeout()) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsOKResponse()) { + m_erased_flash_ranges.Insert(range, true); + } else { + if (response.IsErrorResponse()) + status.SetErrorStringWithFormat("flash erase failed for 0x%" PRIx64, + addr); + else if (response.IsUnsupportedResponse()) + status.SetErrorStringWithFormat("GDB server does not support flashing"); + else + status.SetErrorStringWithFormat( + "unexpected response to GDB server flash erase packet '%s': '%s'", + packet.GetData(), response.GetStringRef().data()); + } + } else { + status.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + } + return status; +} + +Status ProcessGDBRemote::FlashDone() { + Status status; + // If we haven't erased any blocks, then we must not have written anything + // either, so there is no need to actually send a vFlashDone command + if (m_erased_flash_ranges.IsEmpty()) + return status; + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse("vFlashDone", response, + GetInterruptTimeout()) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsOKResponse()) { + m_erased_flash_ranges.Clear(); + } else { + if (response.IsErrorResponse()) + status.SetErrorStringWithFormat("flash done failed"); + else if (response.IsUnsupportedResponse()) + status.SetErrorStringWithFormat("GDB server does not support flashing"); + else + status.SetErrorStringWithFormat( + "unexpected response to GDB server flash done packet: '%s'", + response.GetStringRef().data()); + } + } else { + status.SetErrorStringWithFormat("failed to send flash done packet"); + } + return status; +} + +size_t ProcessGDBRemote::DoWriteMemory(addr_t addr, const void *buf, + size_t size, Status &error) { + GetMaxMemorySize(); + // M and m packets take 2 bytes for 1 byte of memory + size_t max_memory_size = m_max_memory_size / 2; + if (size > max_memory_size) { + // Keep memory read sizes down to a sane limit. This function will be + // called multiple times in order to complete the task by + // lldb_private::Process so it is ok to do this. + size = max_memory_size; + } + + StreamGDBRemote packet; + + MemoryRegionInfo region; + Status region_status = GetMemoryRegionInfo(addr, region); + + bool is_flash = + region_status.Success() && region.GetFlash() == MemoryRegionInfo::eYes; + + if (is_flash) { + if (!m_allow_flash_writes) { + error.SetErrorString("Writing to flash memory is not allowed"); + return 0; + } + // Keep the write within a flash memory region + if (addr + size > region.GetRange().GetRangeEnd()) + size = region.GetRange().GetRangeEnd() - addr; + // Flash memory must be erased before it can be written + error = FlashErase(addr, size); + if (!error.Success()) + return 0; + packet.Printf("vFlashWrite:%" PRIx64 ":", addr); + packet.PutEscapedBytes(buf, size); + } else { + packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); + packet.PutBytesAsRawHex8(buf, size, endian::InlHostByteOrder(), + endian::InlHostByteOrder()); + } + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response, + GetInterruptTimeout()) == + GDBRemoteCommunication::PacketResult::Success) { + if (response.IsOKResponse()) { + error.Clear(); + return size; + } else if (response.IsErrorResponse()) + error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, + addr); + else if (response.IsUnsupportedResponse()) + error.SetErrorStringWithFormat( + "GDB server does not support writing memory"); + else + error.SetErrorStringWithFormat( + "unexpected response to GDB server memory write packet '%s': '%s'", + packet.GetData(), response.GetStringRef().data()); + } else { + error.SetErrorStringWithFormat("failed to send packet: '%s'", + packet.GetData()); + } + return 0; +} + +lldb::addr_t ProcessGDBRemote::DoAllocateMemory(size_t size, + uint32_t permissions, + Status &error) { + Log *log = GetLog(LLDBLog::Process | LLDBLog::Expressions); + addr_t allocated_addr = LLDB_INVALID_ADDRESS; + + if (m_gdb_comm.SupportsAllocDeallocMemory() != eLazyBoolNo) { + allocated_addr = m_gdb_comm.AllocateMemory(size, permissions); + if (allocated_addr != LLDB_INVALID_ADDRESS || + m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolYes) + return allocated_addr; + } + + if (m_gdb_comm.SupportsAllocDeallocMemory() == eLazyBoolNo) { + // Call mmap() to create memory in the inferior.. + unsigned prot = 0; + if (permissions & lldb::ePermissionsReadable) + prot |= eMmapProtRead; + if (permissions & lldb::ePermissionsWritable) + prot |= eMmapProtWrite; + if (permissions & lldb::ePermissionsExecutable) + prot |= eMmapProtExec; + + if (InferiorCallMmap(this, allocated_addr, 0, size, prot, + eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) + m_addr_to_mmap_size[allocated_addr] = size; + else { + allocated_addr = LLDB_INVALID_ADDRESS; + LLDB_LOGF(log, + "ProcessGDBRemote::%s no direct stub support for memory " + "allocation, and InferiorCallMmap also failed - is stub " + "missing register context save/restore capability?", + __FUNCTION__); + } + } + + if (allocated_addr == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat( + "unable to allocate %" PRIu64 " bytes of memory with permissions %s", + (uint64_t)size, GetPermissionsAsCString(permissions)); + else + error.Clear(); + return allocated_addr; +} + +Status ProcessGDBRemote::DoGetMemoryRegionInfo(addr_t load_addr, + MemoryRegionInfo ®ion_info) { + + Status error(m_gdb_comm.GetMemoryRegionInfo(load_addr, region_info)); + return error; +} + +std::optional<uint32_t> ProcessGDBRemote::GetWatchpointSlotCount() { + return m_gdb_comm.GetWatchpointSlotCount(); +} + +std::optional<bool> ProcessGDBRemote::DoGetWatchpointReportedAfter() { + return m_gdb_comm.GetWatchpointReportedAfter(); +} + +Status ProcessGDBRemote::DoDeallocateMemory(lldb::addr_t addr) { + Status error; + LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); + + switch (supported) { + case eLazyBoolCalculate: + // We should never be deallocating memory without allocating memory first + // so we should never get eLazyBoolCalculate + error.SetErrorString( + "tried to deallocate memory without ever allocating memory"); + break; + + case eLazyBoolYes: + if (!m_gdb_comm.DeallocateMemory(addr)) + error.SetErrorStringWithFormat( + "unable to deallocate memory at 0x%" PRIx64, addr); + break; + + case eLazyBoolNo: + // Call munmap() to deallocate memory in the inferior.. + { + MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); + if (pos != m_addr_to_mmap_size.end() && + InferiorCallMunmap(this, addr, pos->second)) + m_addr_to_mmap_size.erase(pos); + else + error.SetErrorStringWithFormat( + "unable to deallocate memory at 0x%" PRIx64, addr); + } + break; + } + + return error; +} + +// Process STDIO +size_t ProcessGDBRemote::PutSTDIN(const char *src, size_t src_len, + Status &error) { + if (m_stdio_communication.IsConnected()) { + ConnectionStatus status; + m_stdio_communication.WriteAll(src, src_len, status, nullptr); + } else if (m_stdin_forward) { + m_gdb_comm.SendStdinNotification(src, src_len); + } + return 0; +} + +Status ProcessGDBRemote::EnableBreakpointSite(BreakpointSite *bp_site) { + Status error; + assert(bp_site != nullptr); + + // Get logging info + Log *log = GetLog(GDBRLog::Breakpoints); + user_id_t site_id = bp_site->GetID(); + + // Get the breakpoint address + const addr_t addr = bp_site->GetLoadAddress(); + + // Log that a breakpoint was requested + LLDB_LOGF(log, + "ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 + ") address = 0x%" PRIx64, + site_id, (uint64_t)addr); + + // Breakpoint already exists and is enabled + if (bp_site->IsEnabled()) { + LLDB_LOGF(log, + "ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 + ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", + site_id, (uint64_t)addr); + return error; + } + + // Get the software breakpoint trap opcode size + const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); + + // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this + // breakpoint type is supported by the remote stub. These are set to true by + // default, and later set to false only after we receive an unimplemented + // response when sending a breakpoint packet. This means initially that + // unless we were specifically instructed to use a hardware breakpoint, LLDB + // will attempt to set a software breakpoint. HardwareRequired() also queries + // a boolean variable which indicates if the user specifically asked for + // hardware breakpoints. If true then we will skip over software + // breakpoints. + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) && + (!bp_site->HardwareRequired())) { + // Try to send off a software breakpoint packet ($Z0) + uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( + eBreakpointSoftware, true, addr, bp_op_size, GetInterruptTimeout()); + if (error_no == 0) { + // The breakpoint was placed successfully + bp_site->SetEnabled(true); + bp_site->SetType(BreakpointSite::eExternal); + return error; + } + + // SendGDBStoppointTypePacket() will return an error if it was unable to + // set this breakpoint. We need to differentiate between a error specific + // to placing this breakpoint or if we have learned that this breakpoint + // type is unsupported. To do this, we must test the support boolean for + // this breakpoint type to see if it now indicates that this breakpoint + // type is unsupported. If they are still supported then we should return + // with the error code. If they are now unsupported, then we would like to + // fall through and try another form of breakpoint. + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) { + if (error_no != UINT8_MAX) + error.SetErrorStringWithFormat( + "error: %d sending the breakpoint request", error_no); + else + error.SetErrorString("error sending the breakpoint request"); + return error; + } + + // We reach here when software breakpoints have been found to be + // unsupported. For future calls to set a breakpoint, we will not attempt + // to set a breakpoint with a type that is known not to be supported. + LLDB_LOGF(log, "Software breakpoints are unsupported"); + + // So we will fall through and try a hardware breakpoint + } + + // The process of setting a hardware breakpoint is much the same as above. + // We check the supported boolean for this breakpoint type, and if it is + // thought to be supported then we will try to set this breakpoint with a + // hardware breakpoint. + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { + // Try to send off a hardware breakpoint packet ($Z1) + uint8_t error_no = m_gdb_comm.SendGDBStoppointTypePacket( + eBreakpointHardware, true, addr, bp_op_size, GetInterruptTimeout()); + if (error_no == 0) { + // The breakpoint was placed successfully + bp_site->SetEnabled(true); + bp_site->SetType(BreakpointSite::eHardware); + return error; + } + + // Check if the error was something other then an unsupported breakpoint + // type + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { + // Unable to set this hardware breakpoint + if (error_no != UINT8_MAX) + error.SetErrorStringWithFormat( + "error: %d sending the hardware breakpoint request " + "(hardware breakpoint resources might be exhausted or unavailable)", + error_no); + else + error.SetErrorString("error sending the hardware breakpoint request " + "(hardware breakpoint resources " + "might be exhausted or unavailable)"); + return error; + } + + // We will reach here when the stub gives an unsupported response to a + // hardware breakpoint + LLDB_LOGF(log, "Hardware breakpoints are unsupported"); + + // Finally we will falling through to a #trap style breakpoint + } + + // Don't fall through when hardware breakpoints were specifically requested + if (bp_site->HardwareRequired()) { + error.SetErrorString("hardware breakpoints are not supported"); + return error; + } + + // As a last resort we want to place a manual breakpoint. An instruction is + // placed into the process memory using memory write packets. + return EnableSoftwareBreakpoint(bp_site); +} + +Status ProcessGDBRemote::DisableBreakpointSite(BreakpointSite *bp_site) { + Status error; + assert(bp_site != nullptr); + addr_t addr = bp_site->GetLoadAddress(); + user_id_t site_id = bp_site->GetID(); + Log *log = GetLog(GDBRLog::Breakpoints); + LLDB_LOGF(log, + "ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 + ") addr = 0x%8.8" PRIx64, + site_id, (uint64_t)addr); + + if (bp_site->IsEnabled()) { + const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); + + BreakpointSite::Type bp_type = bp_site->GetType(); + switch (bp_type) { + case BreakpointSite::eSoftware: + error = DisableSoftwareBreakpoint(bp_site); + break; + + case BreakpointSite::eHardware: + if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, false, + addr, bp_op_size, + GetInterruptTimeout())) + error.SetErrorToGenericError(); + break; + + case BreakpointSite::eExternal: { + if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, false, + addr, bp_op_size, + GetInterruptTimeout())) + error.SetErrorToGenericError(); + } break; + } + if (error.Success()) + bp_site->SetEnabled(false); + } else { + LLDB_LOGF(log, + "ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 + ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", + site_id, (uint64_t)addr); + return error; + } + + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +// Pre-requisite: wp != NULL. +static GDBStoppointType +GetGDBStoppointType(const WatchpointResourceSP &wp_res_sp) { + assert(wp_res_sp); + bool read = wp_res_sp->WatchpointResourceRead(); + bool write = wp_res_sp->WatchpointResourceWrite(); + + assert((read || write) && + "WatchpointResource type is neither read nor write"); + if (read && write) + return eWatchpointReadWrite; + else if (read) + return eWatchpointRead; + else + return eWatchpointWrite; +} + +Status ProcessGDBRemote::EnableWatchpoint(WatchpointSP wp_sp, bool notify) { + Status error; + if (!wp_sp) { + error.SetErrorString("No watchpoint specified"); + return error; + } + user_id_t watchID = wp_sp->GetID(); + addr_t addr = wp_sp->GetLoadAddress(); + Log *log(GetLog(GDBRLog::Watchpoints)); + LLDB_LOGF(log, "ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", + watchID); + if (wp_sp->IsEnabled()) { + LLDB_LOGF(log, + "ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 + ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", + watchID, (uint64_t)addr); + return error; + } + + bool read = wp_sp->WatchpointRead(); + bool write = wp_sp->WatchpointWrite() || wp_sp->WatchpointModify(); + size_t size = wp_sp->GetByteSize(); + + ArchSpec target_arch = GetTarget().GetArchitecture(); + WatchpointHardwareFeature supported_features = + m_gdb_comm.GetSupportedWatchpointTypes(); + + std::vector<WatchpointResourceSP> resources = + WatchpointAlgorithms::AtomizeWatchpointRequest( + addr, size, read, write, supported_features, target_arch); + + // LWP_TODO: Now that we know the WP Resources needed to implement this + // Watchpoint, we need to look at currently allocated Resources in the + // Process and if they match, or are within the same memory granule, or + // overlapping memory ranges, then we need to combine them. e.g. one + // Watchpoint watching 1 byte at 0x1002 and a second watchpoint watching 1 + // byte at 0x1003, they must use the same hardware watchpoint register + // (Resource) to watch them. + + // This may mean that an existing resource changes its type (read to + // read+write) or address range it is watching, in which case the old + // watchpoint needs to be disabled and the new Resource addr/size/type + // watchpoint enabled. + + // If we modify a shared Resource to accomodate this newly added Watchpoint, + // and we are unable to set all of the Resources for it in the inferior, we + // will return an error for this Watchpoint and the shared Resource should + // be restored. e.g. this Watchpoint requires three Resources, one which + // is shared with another Watchpoint. We extend the shared Resouce to + // handle both Watchpoints and we try to set two new ones. But if we don't + // have sufficient watchpoint register for all 3, we need to show an error + // for creating this Watchpoint and we should reset the shared Resource to + // its original configuration because it is no longer shared. + + bool set_all_resources = true; + std::vector<WatchpointResourceSP> succesfully_set_resources; + for (const auto &wp_res_sp : resources) { + addr_t addr = wp_res_sp->GetLoadAddress(); + size_t size = wp_res_sp->GetByteSize(); + GDBStoppointType type = GetGDBStoppointType(wp_res_sp); + if (!m_gdb_comm.SupportsGDBStoppointPacket(type) || + m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, size, + GetInterruptTimeout())) { + set_all_resources = false; + break; + } else { + succesfully_set_resources.push_back(wp_res_sp); + } + } + if (set_all_resources) { + wp_sp->SetEnabled(true, notify); + for (const auto &wp_res_sp : resources) { + // LWP_TODO: If we expanded/reused an existing Resource, + // it's already in the WatchpointResourceList. + wp_res_sp->AddConstituent(wp_sp); + m_watchpoint_resource_list.Add(wp_res_sp); + } + return error; + } else { + // We failed to allocate one of the resources. Unset all + // of the new resources we did successfully set in the + // process. + for (const auto &wp_res_sp : succesfully_set_resources) { + addr_t addr = wp_res_sp->GetLoadAddress(); + size_t size = wp_res_sp->GetByteSize(); + GDBStoppointType type = GetGDBStoppointType(wp_res_sp); + m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, size, + GetInterruptTimeout()); + } + error.SetErrorString("Setting one of the watchpoint resources failed"); + } + return error; +} + +Status ProcessGDBRemote::DisableWatchpoint(WatchpointSP wp_sp, bool notify) { + Status error; + if (!wp_sp) { + error.SetErrorString("Watchpoint argument was NULL."); + return error; + } + + user_id_t watchID = wp_sp->GetID(); + + Log *log(GetLog(GDBRLog::Watchpoints)); + + addr_t addr = wp_sp->GetLoadAddress(); + + LLDB_LOGF(log, + "ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 + ") addr = 0x%8.8" PRIx64, + watchID, (uint64_t)addr); + + if (!wp_sp->IsEnabled()) { + LLDB_LOGF(log, + "ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 + ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", + watchID, (uint64_t)addr); + // See also 'class WatchpointSentry' within StopInfo.cpp. This disabling + // attempt might come from the user-supplied actions, we'll route it in + // order for the watchpoint object to intelligently process this action. + wp_sp->SetEnabled(false, notify); + return error; + } + + if (wp_sp->IsHardware()) { + bool disabled_all = true; + + std::vector<WatchpointResourceSP> unused_resources; + for (const auto &wp_res_sp : m_watchpoint_resource_list.Sites()) { + if (wp_res_sp->ConstituentsContains(wp_sp)) { + GDBStoppointType type = GetGDBStoppointType(wp_res_sp); + addr_t addr = wp_res_sp->GetLoadAddress(); + size_t size = wp_res_sp->GetByteSize(); + if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, size, + GetInterruptTimeout())) { + disabled_all = false; + } else { + wp_res_sp->RemoveConstituent(wp_sp); + if (wp_res_sp->GetNumberOfConstituents() == 0) + unused_resources.push_back(wp_res_sp); + } + } + } + for (auto &wp_res_sp : unused_resources) + m_watchpoint_resource_list.Remove(wp_res_sp->GetID()); + + wp_sp->SetEnabled(false, notify); + if (!disabled_all) + error.SetErrorString("Failure disabling one of the watchpoint locations"); + } + return error; +} + +void ProcessGDBRemote::Clear() { + m_thread_list_real.Clear(); + m_thread_list.Clear(); +} + +Status ProcessGDBRemote::DoSignal(int signo) { + Status error; + Log *log = GetLog(GDBRLog::Process); + LLDB_LOGF(log, "ProcessGDBRemote::DoSignal (signal = %d)", signo); + + if (!m_gdb_comm.SendAsyncSignal(signo, GetInterruptTimeout())) + error.SetErrorStringWithFormat("failed to send signal %i", signo); + return error; +} + +Status +ProcessGDBRemote::EstablishConnectionIfNeeded(const ProcessInfo &process_info) { + // Make sure we aren't already connected? + if (m_gdb_comm.IsConnected()) + return Status(); + + PlatformSP platform_sp(GetTarget().GetPlatform()); + if (platform_sp && !platform_sp->IsHost()) + return Status("Lost debug server connection"); + + auto error = LaunchAndConnectToDebugserver(process_info); + if (error.Fail()) { + const char *error_string = error.AsCString(); + if (error_string == nullptr) + error_string = "unable to launch " DEBUGSERVER_BASENAME; + } + return error; +} +#if !defined(_WIN32) +#define USE_SOCKETPAIR_FOR_LOCAL_CONNECTION 1 +#endif + +#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION +static bool SetCloexecFlag(int fd) { +#if defined(FD_CLOEXEC) + int flags = ::fcntl(fd, F_GETFD); + if (flags == -1) + return false; + return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); +#else + return false; +#endif +} +#endif + +Status ProcessGDBRemote::LaunchAndConnectToDebugserver( + const ProcessInfo &process_info) { + using namespace std::placeholders; // For _1, _2, etc. + + Status error; + if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) { + // If we locate debugserver, keep that located version around + static FileSpec g_debugserver_file_spec; + + ProcessLaunchInfo debugserver_launch_info; + // Make debugserver run in its own session so signals generated by special + // terminal key sequences (^C) don't affect debugserver. + debugserver_launch_info.SetLaunchInSeparateProcessGroup(true); + + const std::weak_ptr<ProcessGDBRemote> this_wp = + std::static_pointer_cast<ProcessGDBRemote>(shared_from_this()); + debugserver_launch_info.SetMonitorProcessCallback( + std::bind(MonitorDebugserverProcess, this_wp, _1, _2, _3)); + debugserver_launch_info.SetUserID(process_info.GetUserID()); + +#if defined(__APPLE__) + // On macOS 11, we need to support x86_64 applications translated to + // arm64. We check whether a binary is translated and spawn the correct + // debugserver accordingly. + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, + static_cast<int>(process_info.GetProcessID()) }; + struct kinfo_proc processInfo; + size_t bufsize = sizeof(processInfo); + if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, + &bufsize, NULL, 0) == 0 && bufsize > 0) { + if (processInfo.kp_proc.p_flag & P_TRANSLATED) { + FileSpec rosetta_debugserver("/Library/Apple/usr/libexec/oah/debugserver"); + debugserver_launch_info.SetExecutableFile(rosetta_debugserver, false); + } + } +#endif + + int communication_fd = -1; +#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION + // Use a socketpair on non-Windows systems for security and performance + // reasons. + int sockets[2]; /* the pair of socket descriptors */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { + error.SetErrorToErrno(); + return error; + } + + int our_socket = sockets[0]; + int gdb_socket = sockets[1]; + auto cleanup_our = llvm::make_scope_exit([&]() { close(our_socket); }); + auto cleanup_gdb = llvm::make_scope_exit([&]() { close(gdb_socket); }); + + // Don't let any child processes inherit our communication socket + SetCloexecFlag(our_socket); + communication_fd = gdb_socket; +#endif + + error = m_gdb_comm.StartDebugserverProcess( + nullptr, GetTarget().GetPlatform().get(), debugserver_launch_info, + nullptr, nullptr, communication_fd); + + if (error.Success()) + m_debugserver_pid = debugserver_launch_info.GetProcessID(); + else + m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { +#ifdef USE_SOCKETPAIR_FOR_LOCAL_CONNECTION + // Our process spawned correctly, we can now set our connection to use + // our end of the socket pair + cleanup_our.release(); + m_gdb_comm.SetConnection( + std::make_unique<ConnectionFileDescriptor>(our_socket, true)); +#endif + StartAsyncThread(); + } + + if (error.Fail()) { + Log *log = GetLog(GDBRLog::Process); + + LLDB_LOGF(log, "failed to start debugserver process: %s", + error.AsCString()); + return error; + } + + if (m_gdb_comm.IsConnected()) { + // Finish the connection process by doing the handshake without + // connecting (send NULL URL) + error = ConnectToDebugserver(""); + } else { + error.SetErrorString("connection failed"); + } + } + return error; +} + +void ProcessGDBRemote::MonitorDebugserverProcess( + std::weak_ptr<ProcessGDBRemote> process_wp, lldb::pid_t debugserver_pid, + int signo, // Zero for no signal + int exit_status // Exit value of process if signal is zero +) { + // "debugserver_pid" argument passed in is the process ID for debugserver + // that we are tracking... + Log *log = GetLog(GDBRLog::Process); + + LLDB_LOGF(log, + "ProcessGDBRemote::%s(process_wp, pid=%" PRIu64 + ", signo=%i (0x%x), exit_status=%i)", + __FUNCTION__, debugserver_pid, signo, signo, exit_status); + + std::shared_ptr<ProcessGDBRemote> process_sp = process_wp.lock(); + LLDB_LOGF(log, "ProcessGDBRemote::%s(process = %p)", __FUNCTION__, + static_cast<void *>(process_sp.get())); + if (!process_sp || process_sp->m_debugserver_pid != debugserver_pid) + return; + + // Sleep for a half a second to make sure our inferior process has time to + // set its exit status before we set it incorrectly when both the debugserver + // and the inferior process shut down. + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + // If our process hasn't yet exited, debugserver might have died. If the + // process did exit, then we are reaping it. + const StateType state = process_sp->GetState(); + + if (state != eStateInvalid && state != eStateUnloaded && + state != eStateExited && state != eStateDetached) { + StreamString stream; + if (signo == 0) + stream.Format(DEBUGSERVER_BASENAME " died with an exit status of {0:x8}", + exit_status); + else { + llvm::StringRef signal_name = + process_sp->GetUnixSignals()->GetSignalAsStringRef(signo); + const char *format_str = DEBUGSERVER_BASENAME " died with signal {0}"; + if (!signal_name.empty()) + stream.Format(format_str, signal_name); + else + stream.Format(format_str, signo); + } + process_sp->SetExitStatus(-1, stream.GetString()); + } + // Debugserver has exited we need to let our ProcessGDBRemote know that it no + // longer has a debugserver instance + process_sp->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; +} + +void ProcessGDBRemote::KillDebugserverProcess() { + m_gdb_comm.Disconnect(); + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { + Host::Kill(m_debugserver_pid, SIGINT); + m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + } +} + +void ProcessGDBRemote::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + DebuggerInitialize); + }); +} + +void ProcessGDBRemote::DebuggerInitialize(Debugger &debugger) { + if (!PluginManager::GetSettingForProcessPlugin( + debugger, PluginProperties::GetSettingName())) { + const bool is_global_setting = true; + PluginManager::CreateSettingForProcessPlugin( + debugger, GetGlobalPluginProperties().GetValueProperties(), + "Properties for the gdb-remote process plug-in.", is_global_setting); + } +} + +bool ProcessGDBRemote::StartAsyncThread() { + Log *log = GetLog(GDBRLog::Process); + + LLDB_LOGF(log, "ProcessGDBRemote::%s ()", __FUNCTION__); + + std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); + if (!m_async_thread.IsJoinable()) { + // Create a thread that watches our internal state and controls which + // events make it to clients (into the DCProcess event queue). + + llvm::Expected<HostThread> async_thread = + ThreadLauncher::LaunchThread("<lldb.process.gdb-remote.async>", [this] { + return ProcessGDBRemote::AsyncThread(); + }); + if (!async_thread) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Host), async_thread.takeError(), + "failed to launch host thread: {0}"); + return false; + } + m_async_thread = *async_thread; + } else + LLDB_LOGF(log, + "ProcessGDBRemote::%s () - Called when Async thread was " + "already running.", + __FUNCTION__); + + return m_async_thread.IsJoinable(); +} + +void ProcessGDBRemote::StopAsyncThread() { + Log *log = GetLog(GDBRLog::Process); + + LLDB_LOGF(log, "ProcessGDBRemote::%s ()", __FUNCTION__); + + std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex); + if (m_async_thread.IsJoinable()) { + m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); + + // This will shut down the async thread. + m_gdb_comm.Disconnect(); // Disconnect from the debug server. + + // Stop the stdio thread + m_async_thread.Join(nullptr); + m_async_thread.Reset(); + } else + LLDB_LOGF( + log, + "ProcessGDBRemote::%s () - Called when Async thread was not running.", + __FUNCTION__); +} + +thread_result_t ProcessGDBRemote::AsyncThread() { + Log *log = GetLog(GDBRLog::Process); + LLDB_LOGF(log, "ProcessGDBRemote::%s(pid = %" PRIu64 ") thread starting...", + __FUNCTION__, GetID()); + + EventSP event_sp; + + // We need to ignore any packets that come in after we have + // have decided the process has exited. There are some + // situations, for instance when we try to interrupt a running + // process and the interrupt fails, where another packet might + // get delivered after we've decided to give up on the process. + // But once we've decided we are done with the process we will + // not be in a state to do anything useful with new packets. + // So it is safer to simply ignore any remaining packets by + // explicitly checking for eStateExited before reentering the + // fetch loop. + + bool done = false; + while (!done && GetPrivateState() != eStateExited) { + LLDB_LOGF(log, + "ProcessGDBRemote::%s(pid = %" PRIu64 + ") listener.WaitForEvent (NULL, event_sp)...", + __FUNCTION__, GetID()); + + if (m_async_listener_sp->GetEvent(event_sp, std::nullopt)) { + const uint32_t event_type = event_sp->GetType(); + if (event_sp->BroadcasterIs(&m_async_broadcaster)) { + LLDB_LOGF(log, + "ProcessGDBRemote::%s(pid = %" PRIu64 + ") Got an event of type: %d...", + __FUNCTION__, GetID(), event_type); + + switch (event_type) { + case eBroadcastBitAsyncContinue: { + const EventDataBytes *continue_packet = + EventDataBytes::GetEventDataFromEvent(event_sp.get()); + + if (continue_packet) { + const char *continue_cstr = + (const char *)continue_packet->GetBytes(); + const size_t continue_cstr_len = continue_packet->GetByteSize(); + LLDB_LOGF(log, + "ProcessGDBRemote::%s(pid = %" PRIu64 + ") got eBroadcastBitAsyncContinue: %s", + __FUNCTION__, GetID(), continue_cstr); + + if (::strstr(continue_cstr, "vAttach") == nullptr) + SetPrivateState(eStateRunning); + StringExtractorGDBRemote response; + + StateType stop_state = + GetGDBRemote().SendContinuePacketAndWaitForResponse( + *this, *GetUnixSignals(), + llvm::StringRef(continue_cstr, continue_cstr_len), + GetInterruptTimeout(), response); + + // We need to immediately clear the thread ID list so we are sure + // to get a valid list of threads. The thread ID list might be + // contained within the "response", or the stop reply packet that + // caused the stop. So clear it now before we give the stop reply + // packet to the process using the + // SetLastStopPacket()... + ClearThreadIDList(); + + switch (stop_state) { + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + SetLastStopPacket(response); + SetPrivateState(stop_state); + break; + + case eStateExited: { + SetLastStopPacket(response); + ClearThreadIDList(); + response.SetFilePos(1); + + int exit_status = response.GetHexU8(); + std::string desc_string; + if (response.GetBytesLeft() > 0 && response.GetChar('-') == ';') { + llvm::StringRef desc_str; + llvm::StringRef desc_token; + while (response.GetNameColonValue(desc_token, desc_str)) { + if (desc_token != "description") + continue; + StringExtractor extractor(desc_str); + extractor.GetHexByteString(desc_string); + } + } + SetExitStatus(exit_status, desc_string.c_str()); + done = true; + break; + } + case eStateInvalid: { + // Check to see if we were trying to attach and if we got back + // the "E87" error code from debugserver -- this indicates that + // the process is not debuggable. Return a slightly more + // helpful error message about why the attach failed. + if (::strstr(continue_cstr, "vAttach") != nullptr && + response.GetError() == 0x87) { + SetExitStatus(-1, "cannot attach to process due to " + "System Integrity Protection"); + } else if (::strstr(continue_cstr, "vAttach") != nullptr && + response.GetStatus().Fail()) { + SetExitStatus(-1, response.GetStatus().AsCString()); + } else { + SetExitStatus(-1, "lost connection"); + } + done = true; + break; + } + + default: + SetPrivateState(stop_state); + break; + } // switch(stop_state) + } // if (continue_packet) + } // case eBroadcastBitAsyncContinue + break; + + case eBroadcastBitAsyncThreadShouldExit: + LLDB_LOGF(log, + "ProcessGDBRemote::%s(pid = %" PRIu64 + ") got eBroadcastBitAsyncThreadShouldExit...", + __FUNCTION__, GetID()); + done = true; + break; + + default: + LLDB_LOGF(log, + "ProcessGDBRemote::%s(pid = %" PRIu64 + ") got unknown event 0x%8.8x", + __FUNCTION__, GetID(), event_type); + done = true; + break; + } + } + } else { + LLDB_LOGF(log, + "ProcessGDBRemote::%s(pid = %" PRIu64 + ") listener.WaitForEvent (NULL, event_sp) => false", + __FUNCTION__, GetID()); + done = true; + } + } + + LLDB_LOGF(log, "ProcessGDBRemote::%s(pid = %" PRIu64 ") thread exiting...", + __FUNCTION__, GetID()); + + return {}; +} + +// uint32_t +// ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList +// &matches, std::vector<lldb::pid_t> &pids) +//{ +// // If we are planning to launch the debugserver remotely, then we need to +// fire up a debugserver +// // process and ask it for the list of processes. But if we are local, we +// can let the Host do it. +// if (m_local_debugserver) +// { +// return Host::ListProcessesMatchingName (name, matches, pids); +// } +// else +// { +// // FIXME: Implement talking to the remote debugserver. +// return 0; +// } +// +//} +// +bool ProcessGDBRemote::NewThreadNotifyBreakpointHit( + void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) { + // I don't think I have to do anything here, just make sure I notice the new + // thread when it starts to + // run so I can stop it if that's what I want to do. + Log *log = GetLog(LLDBLog::Step); + LLDB_LOGF(log, "Hit New Thread Notification breakpoint."); + return false; +} + +Status ProcessGDBRemote::UpdateAutomaticSignalFiltering() { + Log *log = GetLog(GDBRLog::Process); + LLDB_LOG(log, "Check if need to update ignored signals"); + + // QPassSignals package is not supported by the server, there is no way we + // can ignore any signals on server side. + if (!m_gdb_comm.GetQPassSignalsSupported()) + return Status(); + + // No signals, nothing to send. + if (m_unix_signals_sp == nullptr) + return Status(); + + // Signals' version hasn't changed, no need to send anything. + uint64_t new_signals_version = m_unix_signals_sp->GetVersion(); + if (new_signals_version == m_last_signals_version) { + LLDB_LOG(log, "Signals' version hasn't changed. version={0}", + m_last_signals_version); + return Status(); + } + + auto signals_to_ignore = + m_unix_signals_sp->GetFilteredSignals(false, false, false); + Status error = m_gdb_comm.SendSignalsToIgnore(signals_to_ignore); + + LLDB_LOG(log, + "Signals' version changed. old version={0}, new version={1}, " + "signals ignored={2}, update result={3}", + m_last_signals_version, new_signals_version, + signals_to_ignore.size(), error); + + if (error.Success()) + m_last_signals_version = new_signals_version; + + return error; +} + +bool ProcessGDBRemote::StartNoticingNewThreads() { + Log *log = GetLog(LLDBLog::Step); + if (m_thread_create_bp_sp) { + if (log && log->GetVerbose()) + LLDB_LOGF(log, "Enabled noticing new thread breakpoint."); + m_thread_create_bp_sp->SetEnabled(true); + } else { + PlatformSP platform_sp(GetTarget().GetPlatform()); + if (platform_sp) { + m_thread_create_bp_sp = + platform_sp->SetThreadCreationBreakpoint(GetTarget()); + if (m_thread_create_bp_sp) { + if (log && log->GetVerbose()) + LLDB_LOGF( + log, "Successfully created new thread notification breakpoint %i", + m_thread_create_bp_sp->GetID()); + m_thread_create_bp_sp->SetCallback( + ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true); + } else { + LLDB_LOGF(log, "Failed to create new thread notification breakpoint."); + } + } + } + return m_thread_create_bp_sp.get() != nullptr; +} + +bool ProcessGDBRemote::StopNoticingNewThreads() { + Log *log = GetLog(LLDBLog::Step); + if (log && log->GetVerbose()) + LLDB_LOGF(log, "Disabling new thread notification breakpoint."); + + if (m_thread_create_bp_sp) + m_thread_create_bp_sp->SetEnabled(false); + + return true; +} + +DynamicLoader *ProcessGDBRemote::GetDynamicLoader() { + if (m_dyld_up.get() == nullptr) + m_dyld_up.reset(DynamicLoader::FindPlugin(this, "")); + return m_dyld_up.get(); +} + +Status ProcessGDBRemote::SendEventData(const char *data) { + int return_value; + bool was_supported; + + Status error; + + return_value = m_gdb_comm.SendLaunchEventDataPacket(data, &was_supported); + if (return_value != 0) { + if (!was_supported) + error.SetErrorString("Sending events is not supported for this process."); + else + error.SetErrorStringWithFormat("Error sending event data: %d.", + return_value); + } + return error; +} + +DataExtractor ProcessGDBRemote::GetAuxvData() { + DataBufferSP buf; + if (m_gdb_comm.GetQXferAuxvReadSupported()) { + llvm::Expected<std::string> response = m_gdb_comm.ReadExtFeature("auxv", ""); + if (response) + buf = std::make_shared<DataBufferHeap>(response->c_str(), + response->length()); + else + LLDB_LOG_ERROR(GetLog(GDBRLog::Process), response.takeError(), "{0}"); + } + return DataExtractor(buf, GetByteOrder(), GetAddressByteSize()); +} + +StructuredData::ObjectSP +ProcessGDBRemote::GetExtendedInfoForThread(lldb::tid_t tid) { + StructuredData::ObjectSP object_sp; + + if (m_gdb_comm.GetThreadExtendedInfoSupported()) { + StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + SystemRuntime *runtime = GetSystemRuntime(); + if (runtime) { + runtime->AddThreadExtendedInfoPacketHints(args_dict); + } + args_dict->GetAsDictionary()->AddIntegerItem("thread", tid); + + StreamString packet; + packet << "jThreadExtendedInfo:"; + args_dict->Dump(packet, false); + + // FIXME the final character of a JSON dictionary, '}', is the escape + // character in gdb-remote binary mode. lldb currently doesn't escape + // these characters in its packet output -- so we add the quoted version of + // the } character here manually in case we talk to a debugserver which un- + // escapes the characters at packet read time. + packet << (char)(0x7d ^ 0x20); + + StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) == + GDBRemoteCommunication::PacketResult::Success) { + StringExtractorGDBRemote::ResponseType response_type = + response.GetResponseType(); + if (response_type == StringExtractorGDBRemote::eResponse) { + if (!response.Empty()) { + object_sp = StructuredData::ParseJSON(response.GetStringRef()); + } + } + } + } + return object_sp; +} + +StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( + lldb::addr_t image_list_address, lldb::addr_t image_count) { + + StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + args_dict->GetAsDictionary()->AddIntegerItem("image_list_address", + image_list_address); + args_dict->GetAsDictionary()->AddIntegerItem("image_count", image_count); + + return GetLoadedDynamicLibrariesInfos_sender(args_dict); +} + +StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos() { + StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + + args_dict->GetAsDictionary()->AddBooleanItem("fetch_all_solibs", true); + + return GetLoadedDynamicLibrariesInfos_sender(args_dict); +} + +StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos( + const std::vector<lldb::addr_t> &load_addresses) { + StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + StructuredData::ArraySP addresses(new StructuredData::Array); + + for (auto addr : load_addresses) + addresses->AddIntegerItem(addr); + + args_dict->GetAsDictionary()->AddItem("solib_addresses", addresses); + + return GetLoadedDynamicLibrariesInfos_sender(args_dict); +} + +StructuredData::ObjectSP +ProcessGDBRemote::GetLoadedDynamicLibrariesInfos_sender( + StructuredData::ObjectSP args_dict) { + StructuredData::ObjectSP object_sp; + + if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) { + // Scope for the scoped timeout object + GDBRemoteCommunication::ScopedTimeout timeout(m_gdb_comm, + std::chrono::seconds(10)); + + StreamString packet; + packet << "jGetLoadedDynamicLibrariesInfos:"; + args_dict->Dump(packet, false); + + // FIXME the final character of a JSON dictionary, '}', is the escape + // character in gdb-remote binary mode. lldb currently doesn't escape + // these characters in its packet output -- so we add the quoted version of + // the } character here manually in case we talk to a debugserver which un- + // escapes the characters at packet read time. + packet << (char)(0x7d ^ 0x20); + + StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) == + GDBRemoteCommunication::PacketResult::Success) { + StringExtractorGDBRemote::ResponseType response_type = + response.GetResponseType(); + if (response_type == StringExtractorGDBRemote::eResponse) { + if (!response.Empty()) { + object_sp = StructuredData::ParseJSON(response.GetStringRef()); + } + } + } + } + return object_sp; +} + +StructuredData::ObjectSP ProcessGDBRemote::GetDynamicLoaderProcessState() { + StructuredData::ObjectSP object_sp; + StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + + if (m_gdb_comm.GetDynamicLoaderProcessStateSupported()) { + StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); + if (m_gdb_comm.SendPacketAndWaitForResponse("jGetDyldProcessState", + response) == + GDBRemoteCommunication::PacketResult::Success) { + StringExtractorGDBRemote::ResponseType response_type = + response.GetResponseType(); + if (response_type == StringExtractorGDBRemote::eResponse) { + if (!response.Empty()) { + object_sp = StructuredData::ParseJSON(response.GetStringRef()); + } + } + } + } + return object_sp; +} + +StructuredData::ObjectSP ProcessGDBRemote::GetSharedCacheInfo() { + StructuredData::ObjectSP object_sp; + StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); + + if (m_gdb_comm.GetSharedCacheInfoSupported()) { + StreamString packet; + packet << "jGetSharedCacheInfo:"; + args_dict->Dump(packet, false); + + // FIXME the final character of a JSON dictionary, '}', is the escape + // character in gdb-remote binary mode. lldb currently doesn't escape + // these characters in its packet output -- so we add the quoted version of + // the } character here manually in case we talk to a debugserver which un- + // escapes the characters at packet read time. + packet << (char)(0x7d ^ 0x20); + + StringExtractorGDBRemote response; + response.SetResponseValidatorToJSON(); + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) == + GDBRemoteCommunication::PacketResult::Success) { + StringExtractorGDBRemote::ResponseType response_type = + response.GetResponseType(); + if (response_type == StringExtractorGDBRemote::eResponse) { + if (!response.Empty()) { + object_sp = StructuredData::ParseJSON(response.GetStringRef()); + } + } + } + } + return object_sp; +} + +Status ProcessGDBRemote::ConfigureStructuredData( + llvm::StringRef type_name, const StructuredData::ObjectSP &config_sp) { + return m_gdb_comm.ConfigureRemoteStructuredData(type_name, config_sp); +} + +// Establish the largest memory read/write payloads we should use. If the +// remote stub has a max packet size, stay under that size. +// +// If the remote stub's max packet size is crazy large, use a reasonable +// largeish default. +// +// If the remote stub doesn't advertise a max packet size, use a conservative +// default. + +void ProcessGDBRemote::GetMaxMemorySize() { + const uint64_t reasonable_largeish_default = 128 * 1024; + const uint64_t conservative_default = 512; + + if (m_max_memory_size == 0) { + uint64_t stub_max_size = m_gdb_comm.GetRemoteMaxPacketSize(); + if (stub_max_size != UINT64_MAX && stub_max_size != 0) { + // Save the stub's claimed maximum packet size + m_remote_stub_max_memory_size = stub_max_size; + + // Even if the stub says it can support ginormous packets, don't exceed + // our reasonable largeish default packet size. + if (stub_max_size > reasonable_largeish_default) { + stub_max_size = reasonable_largeish_default; + } + + // Memory packet have other overheads too like Maddr,size:#NN Instead of + // calculating the bytes taken by size and addr every time, we take a + // maximum guess here. + if (stub_max_size > 70) + stub_max_size -= 32 + 32 + 6; + else { + // In unlikely scenario that max packet size is less then 70, we will + // hope that data being written is small enough to fit. + Log *log(GetLog(GDBRLog::Comm | GDBRLog::Memory)); + if (log) + log->Warning("Packet size is too small. " + "LLDB may face problems while writing memory"); + } + + m_max_memory_size = stub_max_size; + } else { + m_max_memory_size = conservative_default; + } + } +} + +void ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize( + uint64_t user_specified_max) { + if (user_specified_max != 0) { + GetMaxMemorySize(); + + if (m_remote_stub_max_memory_size != 0) { + if (m_remote_stub_max_memory_size < user_specified_max) { + m_max_memory_size = m_remote_stub_max_memory_size; // user specified a + // packet size too + // big, go as big + // as the remote stub says we can go. + } else { + m_max_memory_size = user_specified_max; // user's packet size is good + } + } else { + m_max_memory_size = + user_specified_max; // user's packet size is probably fine + } + } +} + +bool ProcessGDBRemote::GetModuleSpec(const FileSpec &module_file_spec, + const ArchSpec &arch, + ModuleSpec &module_spec) { + Log *log = GetLog(LLDBLog::Platform); + + const ModuleCacheKey key(module_file_spec.GetPath(), + arch.GetTriple().getTriple()); + auto cached = m_cached_module_specs.find(key); + if (cached != m_cached_module_specs.end()) { + module_spec = cached->second; + return bool(module_spec); + } + + if (!m_gdb_comm.GetModuleInfo(module_file_spec, arch, module_spec)) { + LLDB_LOGF(log, "ProcessGDBRemote::%s - failed to get module info for %s:%s", + __FUNCTION__, module_file_spec.GetPath().c_str(), + arch.GetTriple().getTriple().c_str()); + return false; + } + + if (log) { + StreamString stream; + module_spec.Dump(stream); + LLDB_LOGF(log, "ProcessGDBRemote::%s - got module info for (%s:%s) : %s", + __FUNCTION__, module_file_spec.GetPath().c_str(), + arch.GetTriple().getTriple().c_str(), stream.GetData()); + } + + m_cached_module_specs[key] = module_spec; + return true; +} + +void ProcessGDBRemote::PrefetchModuleSpecs( + llvm::ArrayRef<FileSpec> module_file_specs, const llvm::Triple &triple) { + auto module_specs = m_gdb_comm.GetModulesInfo(module_file_specs, triple); + if (module_specs) { + for (const FileSpec &spec : module_file_specs) + m_cached_module_specs[ModuleCacheKey(spec.GetPath(), + triple.getTriple())] = ModuleSpec(); + for (const ModuleSpec &spec : *module_specs) + m_cached_module_specs[ModuleCacheKey(spec.GetFileSpec().GetPath(), + triple.getTriple())] = spec; + } +} + +llvm::VersionTuple ProcessGDBRemote::GetHostOSVersion() { + return m_gdb_comm.GetOSVersion(); +} + +llvm::VersionTuple ProcessGDBRemote::GetHostMacCatalystVersion() { + return m_gdb_comm.GetMacCatalystVersion(); +} + +namespace { + +typedef std::vector<std::string> stringVec; + +typedef std::vector<struct GdbServerRegisterInfo> GDBServerRegisterVec; +struct RegisterSetInfo { + ConstString name; +}; + +typedef std::map<uint32_t, RegisterSetInfo> RegisterSetMap; + +struct GdbServerTargetInfo { + std::string arch; + std::string osabi; + stringVec includes; + RegisterSetMap reg_set_map; +}; + +static FieldEnum::Enumerators ParseEnumEvalues(const XMLNode &enum_node) { + Log *log(GetLog(GDBRLog::Process)); + // We will use the last instance of each value. Also we preserve the order + // of declaration in the XML, as it may not be numerical. + // For example, hardware may intially release with two states that softwware + // can read from a register field: + // 0 = startup, 1 = running + // If in a future hardware release, the designers added a pre-startup state: + // 0 = startup, 1 = running, 2 = pre-startup + // Now it makes more sense to list them in this logical order as opposed to + // numerical order: + // 2 = pre-startup, 1 = startup, 0 = startup + // This only matters for "register info" but let's trust what the server + // chose regardless. + std::map<uint64_t, FieldEnum::Enumerator> enumerators; + + enum_node.ForEachChildElementWithName( + "evalue", [&enumerators, &log](const XMLNode &enumerator_node) { + std::optional<llvm::StringRef> name; + std::optional<uint64_t> value; + + enumerator_node.ForEachAttribute( + [&name, &value, &log](const llvm::StringRef &attr_name, + const llvm::StringRef &attr_value) { + if (attr_name == "name") { + if (attr_value.size()) + name = attr_value; + else + LLDB_LOG(log, "ProcessGDBRemote::ParseEnumEvalues " + "Ignoring empty name in evalue"); + } else if (attr_name == "value") { + uint64_t parsed_value = 0; + if (llvm::to_integer(attr_value, parsed_value)) + value = parsed_value; + else + LLDB_LOG(log, + "ProcessGDBRemote::ParseEnumEvalues " + "Invalid value \"{0}\" in " + "evalue", + attr_value.data()); + } else + LLDB_LOG(log, + "ProcessGDBRemote::ParseEnumEvalues Ignoring " + "unknown attribute " + "\"{0}\" in evalue", + attr_name.data()); + + // Keep walking attributes. + return true; + }); + + if (value && name) + enumerators.insert_or_assign( + *value, FieldEnum::Enumerator(*value, name->str())); + + // Find all evalue elements. + return true; + }); + + FieldEnum::Enumerators final_enumerators; + for (auto [_, enumerator] : enumerators) + final_enumerators.push_back(enumerator); + + return final_enumerators; +} + +static void +ParseEnums(XMLNode feature_node, + llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { + Log *log(GetLog(GDBRLog::Process)); + + // The top level element is "<enum...". + feature_node.ForEachChildElementWithName( + "enum", [log, ®isters_enum_types](const XMLNode &enum_node) { + std::string id; + + enum_node.ForEachAttribute([&id](const llvm::StringRef &attr_name, + const llvm::StringRef &attr_value) { + if (attr_name == "id") + id = attr_value; + + // There is also a "size" attribute that is supposed to be the size in + // bytes of the register this applies to. However: + // * LLDB doesn't need this information. + // * It is difficult to verify because you have to wait until the + // enum is applied to a field. + // + // So we will emit this attribute in XML for GDB's sake, but will not + // bother ingesting it. + + // Walk all attributes. + return true; + }); + + if (!id.empty()) { + FieldEnum::Enumerators enumerators = ParseEnumEvalues(enum_node); + if (!enumerators.empty()) { + LLDB_LOG(log, + "ProcessGDBRemote::ParseEnums Found enum type \"{0}\"", + id); + registers_enum_types.insert_or_assign( + id, std::make_unique<FieldEnum>(id, enumerators)); + } + } + + // Find all <enum> elements. + return true; + }); +} + +static std::vector<RegisterFlags::Field> ParseFlagsFields( + XMLNode flags_node, unsigned size, + const llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { + Log *log(GetLog(GDBRLog::Process)); + const unsigned max_start_bit = size * 8 - 1; + + // Process the fields of this set of flags. + std::vector<RegisterFlags::Field> fields; + flags_node.ForEachChildElementWithName("field", [&fields, max_start_bit, &log, + ®isters_enum_types]( + const XMLNode + &field_node) { + std::optional<llvm::StringRef> name; + std::optional<unsigned> start; + std::optional<unsigned> end; + std::optional<llvm::StringRef> type; + + field_node.ForEachAttribute([&name, &start, &end, &type, max_start_bit, + &log](const llvm::StringRef &attr_name, + const llvm::StringRef &attr_value) { + // Note that XML in general requires that each of these attributes only + // appears once, so we don't have to handle that here. + if (attr_name == "name") { + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlagsFields Found field node name \"{0}\"", + attr_value.data()); + name = attr_value; + } else if (attr_name == "start") { + unsigned parsed_start = 0; + if (llvm::to_integer(attr_value, parsed_start)) { + if (parsed_start > max_start_bit) { + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlagsFields Invalid start {0} in " + "field node, " + "cannot be > {1}", + parsed_start, max_start_bit); + } else + start = parsed_start; + } else { + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlagsFields Invalid start \"{0}\" in " + "field node", + attr_value.data()); + } + } else if (attr_name == "end") { + unsigned parsed_end = 0; + if (llvm::to_integer(attr_value, parsed_end)) + if (parsed_end > max_start_bit) { + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlagsFields Invalid end {0} in " + "field node, " + "cannot be > {1}", + parsed_end, max_start_bit); + } else + end = parsed_end; + else { + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlagsFields Invalid end \"{0}\" in " + "field node", + attr_value.data()); + } + } else if (attr_name == "type") { + type = attr_value; + } else { + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlagsFields Ignoring unknown attribute " + "\"{0}\" in field node", + attr_name.data()); + } + + return true; // Walk all attributes of the field. + }); + + if (name && start && end) { + if (*start > *end) + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlagsFields Start {0} > end {1} in field " + "\"{2}\", ignoring", + *start, *end, name->data()); + else { + if (RegisterFlags::Field::GetSizeInBits(*start, *end) > 64) + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlagsFields Ignoring field \"{2}\" " + "that has " + "size > 64 bits, this is not supported", + name->data()); + else { + // A field's type may be set to the name of an enum type. + const FieldEnum *enum_type = nullptr; + if (type && !type->empty()) { + auto found = registers_enum_types.find(*type); + if (found != registers_enum_types.end()) { + enum_type = found->second.get(); + + // No enumerator can exceed the range of the field itself. + uint64_t max_value = + RegisterFlags::Field::GetMaxValue(*start, *end); + for (const auto &enumerator : enum_type->GetEnumerators()) { + if (enumerator.m_value > max_value) { + enum_type = nullptr; + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlagsFields In enum \"{0}\" " + "evalue \"{1}\" with value {2} exceeds the maximum value " + "of field \"{3}\" ({4}), ignoring enum", + type->data(), enumerator.m_name, enumerator.m_value, + name->data(), max_value); + break; + } + } + } else { + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlagsFields Could not find type " + "\"{0}\" " + "for field \"{1}\", ignoring", + type->data(), name->data()); + } + } + + fields.push_back( + RegisterFlags::Field(name->str(), *start, *end, enum_type)); + } + } + } + + return true; // Iterate all "field" nodes. + }); + return fields; +} + +void ParseFlags( + XMLNode feature_node, + llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types, + const llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { + Log *log(GetLog(GDBRLog::Process)); + + feature_node.ForEachChildElementWithName( + "flags", + [&log, ®isters_flags_types, + ®isters_enum_types](const XMLNode &flags_node) -> bool { + LLDB_LOG(log, "ProcessGDBRemote::ParseFlags Found flags node \"{0}\"", + flags_node.GetAttributeValue("id").c_str()); + + std::optional<llvm::StringRef> id; + std::optional<unsigned> size; + flags_node.ForEachAttribute( + [&id, &size, &log](const llvm::StringRef &name, + const llvm::StringRef &value) { + if (name == "id") { + id = value; + } else if (name == "size") { + unsigned parsed_size = 0; + if (llvm::to_integer(value, parsed_size)) + size = parsed_size; + else { + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlags Invalid size \"{0}\" " + "in flags node", + value.data()); + } + } else { + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlags Ignoring unknown " + "attribute \"{0}\" in flags node", + name.data()); + } + return true; // Walk all attributes. + }); + + if (id && size) { + // Process the fields of this set of flags. + std::vector<RegisterFlags::Field> fields = + ParseFlagsFields(flags_node, *size, registers_enum_types); + if (fields.size()) { + // Sort so that the fields with the MSBs are first. + std::sort(fields.rbegin(), fields.rend()); + std::vector<RegisterFlags::Field>::const_iterator overlap = + std::adjacent_find(fields.begin(), fields.end(), + [](const RegisterFlags::Field &lhs, + const RegisterFlags::Field &rhs) { + return lhs.Overlaps(rhs); + }); + + // If no fields overlap, use them. + if (overlap == fields.end()) { + if (registers_flags_types.contains(*id)) { + // In theory you could define some flag set, use it with a + // register then redefine it. We do not know if anyone does + // that, or what they would expect to happen in that case. + // + // LLDB chooses to take the first definition and ignore the rest + // as waiting until everything has been processed is more + // expensive and difficult. This means that pointers to flag + // sets in the register info remain valid if later the flag set + // is redefined. If we allowed redefinitions, LLDB would crash + // when you tried to print a register that used the original + // definition. + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlags Definition of flags " + "\"{0}\" shadows " + "previous definition, using original definition instead.", + id->data()); + } else { + registers_flags_types.insert_or_assign( + *id, std::make_unique<RegisterFlags>(id->str(), *size, + std::move(fields))); + } + } else { + // If any fields overlap, ignore the whole set of flags. + std::vector<RegisterFlags::Field>::const_iterator next = + std::next(overlap); + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlags Ignoring flags because fields " + "{0} (start: {1} end: {2}) and {3} (start: {4} end: {5}) " + "overlap.", + overlap->GetName().c_str(), overlap->GetStart(), + overlap->GetEnd(), next->GetName().c_str(), next->GetStart(), + next->GetEnd()); + } + } else { + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlags Ignoring definition of flags " + "\"{0}\" because it contains no fields.", + id->data()); + } + } + + return true; // Keep iterating through all "flags" elements. + }); +} + +bool ParseRegisters( + XMLNode feature_node, GdbServerTargetInfo &target_info, + std::vector<DynamicRegisterInfo::Register> ®isters, + llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types, + llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { + if (!feature_node) + return false; + + Log *log(GetLog(GDBRLog::Process)); + + // Enums first because they are referenced by fields in the flags. + ParseEnums(feature_node, registers_enum_types); + for (const auto &enum_type : registers_enum_types) + enum_type.second->DumpToLog(log); + + ParseFlags(feature_node, registers_flags_types, registers_enum_types); + for (const auto &flags : registers_flags_types) + flags.second->DumpToLog(log); + + feature_node.ForEachChildElementWithName( + "reg", + [&target_info, ®isters, ®isters_flags_types, + log](const XMLNode ®_node) -> bool { + std::string gdb_group; + std::string gdb_type; + DynamicRegisterInfo::Register reg_info; + bool encoding_set = false; + bool format_set = false; + + // FIXME: we're silently ignoring invalid data here + reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, + &encoding_set, &format_set, ®_info, + log](const llvm::StringRef &name, + const llvm::StringRef &value) -> bool { + if (name == "name") { + reg_info.name.SetString(value); + } else if (name == "bitsize") { + if (llvm::to_integer(value, reg_info.byte_size)) + reg_info.byte_size = + llvm::divideCeil(reg_info.byte_size, CHAR_BIT); + } else if (name == "type") { + gdb_type = value.str(); + } else if (name == "group") { + gdb_group = value.str(); + } else if (name == "regnum") { + llvm::to_integer(value, reg_info.regnum_remote); + } else if (name == "offset") { + llvm::to_integer(value, reg_info.byte_offset); + } else if (name == "altname") { + reg_info.alt_name.SetString(value); + } else if (name == "encoding") { + encoding_set = true; + reg_info.encoding = Args::StringToEncoding(value, eEncodingUint); + } else if (name == "format") { + format_set = true; + if (!OptionArgParser::ToFormat(value.data(), reg_info.format, + nullptr) + .Success()) + reg_info.format = + llvm::StringSwitch<lldb::Format>(value) + .Case("vector-sint8", eFormatVectorOfSInt8) + .Case("vector-uint8", eFormatVectorOfUInt8) + .Case("vector-sint16", eFormatVectorOfSInt16) + .Case("vector-uint16", eFormatVectorOfUInt16) + .Case("vector-sint32", eFormatVectorOfSInt32) + .Case("vector-uint32", eFormatVectorOfUInt32) + .Case("vector-float32", eFormatVectorOfFloat32) + .Case("vector-uint64", eFormatVectorOfUInt64) + .Case("vector-uint128", eFormatVectorOfUInt128) + .Default(eFormatInvalid); + } else if (name == "group_id") { + uint32_t set_id = UINT32_MAX; + llvm::to_integer(value, set_id); + RegisterSetMap::const_iterator pos = + target_info.reg_set_map.find(set_id); + if (pos != target_info.reg_set_map.end()) + reg_info.set_name = pos->second.name; + } else if (name == "gcc_regnum" || name == "ehframe_regnum") { + llvm::to_integer(value, reg_info.regnum_ehframe); + } else if (name == "dwarf_regnum") { + llvm::to_integer(value, reg_info.regnum_dwarf); + } else if (name == "generic") { + reg_info.regnum_generic = Args::StringToGenericRegister(value); + } else if (name == "value_regnums") { + SplitCommaSeparatedRegisterNumberString(value, reg_info.value_regs, + 0); + } else if (name == "invalidate_regnums") { + SplitCommaSeparatedRegisterNumberString( + value, reg_info.invalidate_regs, 0); + } else { + LLDB_LOGF(log, + "ProcessGDBRemote::ParseRegisters unhandled reg " + "attribute %s = %s", + name.data(), value.data()); + } + return true; // Keep iterating through all attributes + }); + + if (!gdb_type.empty()) { + // gdb_type could reference some flags type defined in XML. + llvm::StringMap<std::unique_ptr<RegisterFlags>>::iterator it = + registers_flags_types.find(gdb_type); + if (it != registers_flags_types.end()) { + auto flags_type = it->second.get(); + if (reg_info.byte_size == flags_type->GetSize()) + reg_info.flags_type = flags_type; + else + LLDB_LOGF(log, + "ProcessGDBRemote::ParseRegisters Size of register " + "flags %s (%d bytes) for " + "register %s does not match the register size (%d " + "bytes). Ignoring this set of flags.", + flags_type->GetID().c_str(), flags_type->GetSize(), + reg_info.name.AsCString(), reg_info.byte_size); + } + + // There's a slim chance that the gdb_type name is both a flags type + // and a simple type. Just in case, look for that too (setting both + // does no harm). + if (!gdb_type.empty() && !(encoding_set || format_set)) { + if (llvm::StringRef(gdb_type).starts_with("int")) { + reg_info.format = eFormatHex; + reg_info.encoding = eEncodingUint; + } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") { + reg_info.format = eFormatAddressInfo; + reg_info.encoding = eEncodingUint; + } else if (gdb_type == "float") { + reg_info.format = eFormatFloat; + reg_info.encoding = eEncodingIEEE754; + } else if (gdb_type == "aarch64v" || + llvm::StringRef(gdb_type).starts_with("vec") || + gdb_type == "i387_ext" || gdb_type == "uint128") { + // lldb doesn't handle 128-bit uints correctly (for ymm*h), so + // treat them as vector (similarly to xmm/ymm) + reg_info.format = eFormatVectorOfUInt8; + reg_info.encoding = eEncodingVector; + } else { + LLDB_LOGF( + log, + "ProcessGDBRemote::ParseRegisters Could not determine lldb" + "format and encoding for gdb type %s", + gdb_type.c_str()); + } + } + } + + // Only update the register set name if we didn't get a "reg_set" + // attribute. "set_name" will be empty if we didn't have a "reg_set" + // attribute. + if (!reg_info.set_name) { + if (!gdb_group.empty()) { + reg_info.set_name.SetCString(gdb_group.c_str()); + } else { + // If no register group name provided anywhere, + // we'll create a 'general' register set + reg_info.set_name.SetCString("general"); + } + } + + if (reg_info.byte_size == 0) { + LLDB_LOGF(log, + "ProcessGDBRemote::%s Skipping zero bitsize register %s", + __FUNCTION__, reg_info.name.AsCString()); + } else + registers.push_back(reg_info); + + return true; // Keep iterating through all "reg" elements + }); + return true; +} + +} // namespace + +// This method fetches a register description feature xml file from +// the remote stub and adds registers/register groupsets/architecture +// information to the current process. It will call itself recursively +// for nested register definition files. It returns true if it was able +// to fetch and parse an xml file. +bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess( + ArchSpec &arch_to_use, std::string xml_filename, + std::vector<DynamicRegisterInfo::Register> ®isters) { + // request the target xml file + llvm::Expected<std::string> raw = m_gdb_comm.ReadExtFeature("features", xml_filename); + if (errorToBool(raw.takeError())) + return false; + + XMLDocument xml_document; + + if (xml_document.ParseMemory(raw->c_str(), raw->size(), + xml_filename.c_str())) { + GdbServerTargetInfo target_info; + std::vector<XMLNode> feature_nodes; + + // The top level feature XML file will start with a <target> tag. + XMLNode target_node = xml_document.GetRootElement("target"); + if (target_node) { + target_node.ForEachChildElement([&target_info, &feature_nodes]( + const XMLNode &node) -> bool { + llvm::StringRef name = node.GetName(); + if (name == "architecture") { + node.GetElementText(target_info.arch); + } else if (name == "osabi") { + node.GetElementText(target_info.osabi); + } else if (name == "xi:include" || name == "include") { + std::string href = node.GetAttributeValue("href"); + if (!href.empty()) + target_info.includes.push_back(href); + } else if (name == "feature") { + feature_nodes.push_back(node); + } else if (name == "groups") { + node.ForEachChildElementWithName( + "group", [&target_info](const XMLNode &node) -> bool { + uint32_t set_id = UINT32_MAX; + RegisterSetInfo set_info; + + node.ForEachAttribute( + [&set_id, &set_info](const llvm::StringRef &name, + const llvm::StringRef &value) -> bool { + // FIXME: we're silently ignoring invalid data here + if (name == "id") + llvm::to_integer(value, set_id); + if (name == "name") + set_info.name = ConstString(value); + return true; // Keep iterating through all attributes + }); + + if (set_id != UINT32_MAX) + target_info.reg_set_map[set_id] = set_info; + return true; // Keep iterating through all "group" elements + }); + } + return true; // Keep iterating through all children of the target_node + }); + } else { + // In an included XML feature file, we're already "inside" the <target> + // tag of the initial XML file; this included file will likely only have + // a <feature> tag. Need to check for any more included files in this + // <feature> element. + XMLNode feature_node = xml_document.GetRootElement("feature"); + if (feature_node) { + feature_nodes.push_back(feature_node); + feature_node.ForEachChildElement([&target_info]( + const XMLNode &node) -> bool { + llvm::StringRef name = node.GetName(); + if (name == "xi:include" || name == "include") { + std::string href = node.GetAttributeValue("href"); + if (!href.empty()) + target_info.includes.push_back(href); + } + return true; + }); + } + } + + // gdbserver does not implement the LLDB packets used to determine host + // or process architecture. If that is the case, attempt to use + // the <architecture/> field from target.xml, e.g.: + // + // <architecture>i386:x86-64</architecture> (seen from VMWare ESXi) + // <architecture>arm</architecture> (seen from Segger JLink on unspecified + // arm board) + if (!arch_to_use.IsValid() && !target_info.arch.empty()) { + // We don't have any information about vendor or OS. + arch_to_use.SetTriple(llvm::StringSwitch<std::string>(target_info.arch) + .Case("i386:x86-64", "x86_64") + .Case("riscv:rv64", "riscv64") + .Case("riscv:rv32", "riscv32") + .Default(target_info.arch) + + "--"); + + if (arch_to_use.IsValid()) + GetTarget().MergeArchitecture(arch_to_use); + } + + if (arch_to_use.IsValid()) { + for (auto &feature_node : feature_nodes) { + ParseRegisters(feature_node, target_info, registers, + m_registers_flags_types, m_registers_enum_types); + } + + for (const auto &include : target_info.includes) { + GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, include, + registers); + } + } + } else { + return false; + } + return true; +} + +void ProcessGDBRemote::AddRemoteRegisters( + std::vector<DynamicRegisterInfo::Register> ®isters, + const ArchSpec &arch_to_use) { + std::map<uint32_t, uint32_t> remote_to_local_map; + uint32_t remote_regnum = 0; + for (auto it : llvm::enumerate(registers)) { + DynamicRegisterInfo::Register &remote_reg_info = it.value(); + + // Assign successive remote regnums if missing. + if (remote_reg_info.regnum_remote == LLDB_INVALID_REGNUM) + remote_reg_info.regnum_remote = remote_regnum; + + // Create a mapping from remote to local regnos. + remote_to_local_map[remote_reg_info.regnum_remote] = it.index(); + + remote_regnum = remote_reg_info.regnum_remote + 1; + } + + for (DynamicRegisterInfo::Register &remote_reg_info : registers) { + auto proc_to_lldb = [&remote_to_local_map](uint32_t process_regnum) { + auto lldb_regit = remote_to_local_map.find(process_regnum); + return lldb_regit != remote_to_local_map.end() ? lldb_regit->second + : LLDB_INVALID_REGNUM; + }; + + llvm::transform(remote_reg_info.value_regs, + remote_reg_info.value_regs.begin(), proc_to_lldb); + llvm::transform(remote_reg_info.invalidate_regs, + remote_reg_info.invalidate_regs.begin(), proc_to_lldb); + } + + // Don't use Process::GetABI, this code gets called from DidAttach, and + // in that context we haven't set the Target's architecture yet, so the + // ABI is also potentially incorrect. + if (ABISP abi_sp = ABI::FindPlugin(shared_from_this(), arch_to_use)) + abi_sp->AugmentRegisterInfo(registers); + + m_register_info_sp->SetRegisterInfo(std::move(registers), arch_to_use); +} + +// query the target of gdb-remote for extended target information returns +// true on success (got register definitions), false on failure (did not). +bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { + // Make sure LLDB has an XML parser it can use first + if (!XMLDocument::XMLEnabled()) + return false; + + // check that we have extended feature read support + if (!m_gdb_comm.GetQXferFeaturesReadSupported()) + return false; + + // These hold register type information for the whole of target.xml. + // target.xml may include further documents that + // GetGDBServerRegisterInfoXMLAndProcess will recurse to fetch and process. + // That's why we clear the cache here, and not in + // GetGDBServerRegisterInfoXMLAndProcess. To prevent it being cleared on every + // include read. + m_registers_flags_types.clear(); + m_registers_enum_types.clear(); + std::vector<DynamicRegisterInfo::Register> registers; + if (GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, "target.xml", + registers) && + // Target XML is not required to include register information. + !registers.empty()) + AddRemoteRegisters(registers, arch_to_use); + + return m_register_info_sp->GetNumRegisters() > 0; +} + +llvm::Expected<LoadedModuleInfoList> ProcessGDBRemote::GetLoadedModuleList() { + // Make sure LLDB has an XML parser it can use first + if (!XMLDocument::XMLEnabled()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "XML parsing not available"); + + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "ProcessGDBRemote::%s", __FUNCTION__); + + LoadedModuleInfoList list; + GDBRemoteCommunicationClient &comm = m_gdb_comm; + bool can_use_svr4 = GetGlobalPluginProperties().GetUseSVR4(); + + // check that we have extended feature read support + if (can_use_svr4 && comm.GetQXferLibrariesSVR4ReadSupported()) { + // request the loaded library list + llvm::Expected<std::string> raw = comm.ReadExtFeature("libraries-svr4", ""); + if (!raw) + return raw.takeError(); + + // parse the xml file in memory + LLDB_LOGF(log, "parsing: %s", raw->c_str()); + XMLDocument doc; + + if (!doc.ParseMemory(raw->c_str(), raw->size(), "noname.xml")) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Error reading noname.xml"); + + XMLNode root_element = doc.GetRootElement("library-list-svr4"); + if (!root_element) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Error finding library-list-svr4 xml element"); + + // main link map structure + std::string main_lm = root_element.GetAttributeValue("main-lm"); + // FIXME: we're silently ignoring invalid data here + if (!main_lm.empty()) + llvm::to_integer(main_lm, list.m_link_map); + + root_element.ForEachChildElementWithName( + "library", [log, &list](const XMLNode &library) -> bool { + LoadedModuleInfoList::LoadedModuleInfo module; + + // FIXME: we're silently ignoring invalid data here + library.ForEachAttribute( + [&module](const llvm::StringRef &name, + const llvm::StringRef &value) -> bool { + uint64_t uint_value = LLDB_INVALID_ADDRESS; + if (name == "name") + module.set_name(value.str()); + else if (name == "lm") { + // the address of the link_map struct. + llvm::to_integer(value, uint_value); + module.set_link_map(uint_value); + } else if (name == "l_addr") { + // the displacement as read from the field 'l_addr' of the + // link_map struct. + llvm::to_integer(value, uint_value); + module.set_base(uint_value); + // base address is always a displacement, not an absolute + // value. + module.set_base_is_offset(true); + } else if (name == "l_ld") { + // the memory address of the libraries PT_DYNAMIC section. + llvm::to_integer(value, uint_value); + module.set_dynamic(uint_value); + } + + return true; // Keep iterating over all properties of "library" + }); + + if (log) { + std::string name; + lldb::addr_t lm = 0, base = 0, ld = 0; + bool base_is_offset; + + module.get_name(name); + module.get_link_map(lm); + module.get_base(base); + module.get_base_is_offset(base_is_offset); + module.get_dynamic(ld); + + LLDB_LOGF(log, + "found (link_map:0x%08" PRIx64 ", base:0x%08" PRIx64 + "[%s], ld:0x%08" PRIx64 ", name:'%s')", + lm, base, (base_is_offset ? "offset" : "absolute"), ld, + name.c_str()); + } + + list.add(module); + return true; // Keep iterating over all "library" elements in the root + // node + }); + + if (log) + LLDB_LOGF(log, "found %" PRId32 " modules in total", + (int)list.m_list.size()); + return list; + } else if (comm.GetQXferLibrariesReadSupported()) { + // request the loaded library list + llvm::Expected<std::string> raw = comm.ReadExtFeature("libraries", ""); + + if (!raw) + return raw.takeError(); + + LLDB_LOGF(log, "parsing: %s", raw->c_str()); + XMLDocument doc; + + if (!doc.ParseMemory(raw->c_str(), raw->size(), "noname.xml")) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Error reading noname.xml"); + + XMLNode root_element = doc.GetRootElement("library-list"); + if (!root_element) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Error finding library-list xml element"); + + // FIXME: we're silently ignoring invalid data here + root_element.ForEachChildElementWithName( + "library", [log, &list](const XMLNode &library) -> bool { + LoadedModuleInfoList::LoadedModuleInfo module; + + std::string name = library.GetAttributeValue("name"); + module.set_name(name); + + // The base address of a given library will be the address of its + // first section. Most remotes send only one section for Windows + // targets for example. + const XMLNode §ion = + library.FindFirstChildElementWithName("section"); + std::string address = section.GetAttributeValue("address"); + uint64_t address_value = LLDB_INVALID_ADDRESS; + llvm::to_integer(address, address_value); + module.set_base(address_value); + // These addresses are absolute values. + module.set_base_is_offset(false); + + if (log) { + std::string name; + lldb::addr_t base = 0; + bool base_is_offset; + module.get_name(name); + module.get_base(base); + module.get_base_is_offset(base_is_offset); + + LLDB_LOGF(log, "found (base:0x%08" PRIx64 "[%s], name:'%s')", base, + (base_is_offset ? "offset" : "absolute"), name.c_str()); + } + + list.add(module); + return true; // Keep iterating over all "library" elements in the root + // node + }); + + if (log) + LLDB_LOGF(log, "found %" PRId32 " modules in total", + (int)list.m_list.size()); + return list; + } else { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Remote libraries not supported"); + } +} + +lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress(const FileSpec &file, + lldb::addr_t link_map, + lldb::addr_t base_addr, + bool value_is_offset) { + DynamicLoader *loader = GetDynamicLoader(); + if (!loader) + return nullptr; + + return loader->LoadModuleAtAddress(file, link_map, base_addr, + value_is_offset); +} + +llvm::Error ProcessGDBRemote::LoadModules() { + using lldb_private::process_gdb_remote::ProcessGDBRemote; + + // request a list of loaded libraries from GDBServer + llvm::Expected<LoadedModuleInfoList> module_list = GetLoadedModuleList(); + if (!module_list) + return module_list.takeError(); + + // get a list of all the modules + ModuleList new_modules; + + for (LoadedModuleInfoList::LoadedModuleInfo &modInfo : module_list->m_list) { + std::string mod_name; + lldb::addr_t mod_base; + lldb::addr_t link_map; + bool mod_base_is_offset; + + bool valid = true; + valid &= modInfo.get_name(mod_name); + valid &= modInfo.get_base(mod_base); + valid &= modInfo.get_base_is_offset(mod_base_is_offset); + if (!valid) + continue; + + if (!modInfo.get_link_map(link_map)) + link_map = LLDB_INVALID_ADDRESS; + + FileSpec file(mod_name); + FileSystem::Instance().Resolve(file); + lldb::ModuleSP module_sp = + LoadModuleAtAddress(file, link_map, mod_base, mod_base_is_offset); + + if (module_sp.get()) + new_modules.Append(module_sp); + } + + if (new_modules.GetSize() > 0) { + ModuleList removed_modules; + Target &target = GetTarget(); + ModuleList &loaded_modules = m_process->GetTarget().GetImages(); + + for (size_t i = 0; i < loaded_modules.GetSize(); ++i) { + const lldb::ModuleSP loaded_module = loaded_modules.GetModuleAtIndex(i); + + bool found = false; + for (size_t j = 0; j < new_modules.GetSize(); ++j) { + if (new_modules.GetModuleAtIndex(j).get() == loaded_module.get()) + found = true; + } + + // The main executable will never be included in libraries-svr4, don't + // remove it + if (!found && + loaded_module.get() != target.GetExecutableModulePointer()) { + removed_modules.Append(loaded_module); + } + } + + loaded_modules.Remove(removed_modules); + m_process->GetTarget().ModulesDidUnload(removed_modules, false); + + new_modules.ForEach([&target](const lldb::ModuleSP module_sp) -> bool { + lldb_private::ObjectFile *obj = module_sp->GetObjectFile(); + if (!obj) + return true; + + if (obj->GetType() != ObjectFile::Type::eTypeExecutable) + return true; + + lldb::ModuleSP module_copy_sp = module_sp; + target.SetExecutableModule(module_copy_sp, eLoadDependentsNo); + return false; + }); + + loaded_modules.AppendIfNeeded(new_modules); + m_process->GetTarget().ModulesDidLoad(new_modules); + } + + return llvm::ErrorSuccess(); +} + +Status ProcessGDBRemote::GetFileLoadAddress(const FileSpec &file, + bool &is_loaded, + lldb::addr_t &load_addr) { + is_loaded = false; + load_addr = LLDB_INVALID_ADDRESS; + + std::string file_path = file.GetPath(false); + if (file_path.empty()) + return Status("Empty file name specified"); + + StreamString packet; + packet.PutCString("qFileLoadAddress:"); + packet.PutStringAsRawHex8(file_path); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != + GDBRemoteCommunication::PacketResult::Success) + return Status("Sending qFileLoadAddress packet failed"); + + if (response.IsErrorResponse()) { + if (response.GetError() == 1) { + // The file is not loaded into the inferior + is_loaded = false; + load_addr = LLDB_INVALID_ADDRESS; + return Status(); + } + + return Status( + "Fetching file load address from remote server returned an error"); + } + + if (response.IsNormalResponse()) { + is_loaded = true; + load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + return Status(); + } + + return Status( + "Unknown error happened during sending the load address packet"); +} + +void ProcessGDBRemote::ModulesDidLoad(ModuleList &module_list) { + // We must call the lldb_private::Process::ModulesDidLoad () first before we + // do anything + Process::ModulesDidLoad(module_list); + + // After loading shared libraries, we can ask our remote GDB server if it + // needs any symbols. + m_gdb_comm.ServeSymbolLookups(this); +} + +void ProcessGDBRemote::HandleAsyncStdout(llvm::StringRef out) { + AppendSTDOUT(out.data(), out.size()); +} + +static const char *end_delimiter = "--end--;"; +static const int end_delimiter_len = 8; + +void ProcessGDBRemote::HandleAsyncMisc(llvm::StringRef data) { + std::string input = data.str(); // '1' to move beyond 'A' + if (m_partial_profile_data.length() > 0) { + m_partial_profile_data.append(input); + input = m_partial_profile_data; + m_partial_profile_data.clear(); + } + + size_t found, pos = 0, len = input.length(); + while ((found = input.find(end_delimiter, pos)) != std::string::npos) { + StringExtractorGDBRemote profileDataExtractor( + input.substr(pos, found).c_str()); + std::string profile_data = + HarmonizeThreadIdsForProfileData(profileDataExtractor); + BroadcastAsyncProfileData(profile_data); + + pos = found + end_delimiter_len; + } + + if (pos < len) { + // Last incomplete chunk. + m_partial_profile_data = input.substr(pos); + } +} + +std::string ProcessGDBRemote::HarmonizeThreadIdsForProfileData( + StringExtractorGDBRemote &profileDataExtractor) { + std::map<uint64_t, uint32_t> new_thread_id_to_used_usec_map; + std::string output; + llvm::raw_string_ostream output_stream(output); + llvm::StringRef name, value; + + // Going to assuming thread_used_usec comes first, else bail out. + while (profileDataExtractor.GetNameColonValue(name, value)) { + if (name.compare("thread_used_id") == 0) { + StringExtractor threadIDHexExtractor(value); + uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0); + + bool has_used_usec = false; + uint32_t curr_used_usec = 0; + llvm::StringRef usec_name, usec_value; + uint32_t input_file_pos = profileDataExtractor.GetFilePos(); + if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) { + if (usec_name == "thread_used_usec") { + has_used_usec = true; + usec_value.getAsInteger(0, curr_used_usec); + } else { + // We didn't find what we want, it is probably an older version. Bail + // out. + profileDataExtractor.SetFilePos(input_file_pos); + } + } + + if (has_used_usec) { + uint32_t prev_used_usec = 0; + std::map<uint64_t, uint32_t>::iterator iterator = + m_thread_id_to_used_usec_map.find(thread_id); + if (iterator != m_thread_id_to_used_usec_map.end()) { + prev_used_usec = m_thread_id_to_used_usec_map[thread_id]; + } + + uint32_t real_used_usec = curr_used_usec - prev_used_usec; + // A good first time record is one that runs for at least 0.25 sec + bool good_first_time = + (prev_used_usec == 0) && (real_used_usec > 250000); + bool good_subsequent_time = + (prev_used_usec > 0) && + ((real_used_usec > 0) || (HasAssignedIndexIDToThread(thread_id))); + + if (good_first_time || good_subsequent_time) { + // We try to avoid doing too many index id reservation, resulting in + // fast increase of index ids. + + output_stream << name << ":"; + int32_t index_id = AssignIndexIDToThread(thread_id); + output_stream << index_id << ";"; + + output_stream << usec_name << ":" << usec_value << ";"; + } else { + // Skip past 'thread_used_name'. + llvm::StringRef local_name, local_value; + profileDataExtractor.GetNameColonValue(local_name, local_value); + } + + // Store current time as previous time so that they can be compared + // later. + new_thread_id_to_used_usec_map[thread_id] = curr_used_usec; + } else { + // Bail out and use old string. + output_stream << name << ":" << value << ";"; + } + } else { + output_stream << name << ":" << value << ";"; + } + } + output_stream << end_delimiter; + m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map; + + return output_stream.str(); +} + +void ProcessGDBRemote::HandleStopReply() { + if (GetStopID() != 0) + return; + + if (GetID() == LLDB_INVALID_PROCESS_ID) { + lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID(); + if (pid != LLDB_INVALID_PROCESS_ID) + SetID(pid); + } + BuildDynamicRegisterInfo(true); +} + +llvm::Expected<bool> ProcessGDBRemote::SaveCore(llvm::StringRef outfile) { + if (!m_gdb_comm.GetSaveCoreSupported()) + return false; + + StreamString packet; + packet.PutCString("qSaveCore;path-hint:"); + packet.PutStringAsRawHex8(outfile); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) == + GDBRemoteCommunication::PacketResult::Success) { + // TODO: grab error message from the packet? StringExtractor seems to + // be missing a method for that + if (response.IsErrorResponse()) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + llvm::formatv("qSaveCore returned an error")); + + std::string path; + + // process the response + for (auto x : llvm::split(response.GetStringRef(), ';')) { + if (x.consume_front("core-path:")) + StringExtractor(x).GetHexByteString(path); + } + + // verify that we've gotten what we need + if (path.empty()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "qSaveCore returned no core path"); + + // now transfer the core file + FileSpec remote_core{llvm::StringRef(path)}; + Platform &platform = *GetTarget().GetPlatform(); + Status error = platform.GetFile(remote_core, FileSpec(outfile)); + + if (platform.IsRemote()) { + // NB: we unlink the file on error too + platform.Unlink(remote_core); + if (error.Fail()) + return error.ToError(); + } + + return true; + } + + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Unable to send qSaveCore"); +} + +static const char *const s_async_json_packet_prefix = "JSON-async:"; + +static StructuredData::ObjectSP +ParseStructuredDataPacket(llvm::StringRef packet) { + Log *log = GetLog(GDBRLog::Process); + + if (!packet.consume_front(s_async_json_packet_prefix)) { + if (log) { + LLDB_LOGF( + log, + "GDBRemoteCommunicationClientBase::%s() received $J packet " + "but was not a StructuredData packet: packet starts with " + "%s", + __FUNCTION__, + packet.slice(0, strlen(s_async_json_packet_prefix)).str().c_str()); + } + return StructuredData::ObjectSP(); + } + + // This is an asynchronous JSON packet, destined for a StructuredDataPlugin. + StructuredData::ObjectSP json_sp = StructuredData::ParseJSON(packet); + if (log) { + if (json_sp) { + StreamString json_str; + json_sp->Dump(json_str, true); + json_str.Flush(); + LLDB_LOGF(log, + "ProcessGDBRemote::%s() " + "received Async StructuredData packet: %s", + __FUNCTION__, json_str.GetData()); + } else { + LLDB_LOGF(log, + "ProcessGDBRemote::%s" + "() received StructuredData packet:" + " parse failure", + __FUNCTION__); + } + } + return json_sp; +} + +void ProcessGDBRemote::HandleAsyncStructuredDataPacket(llvm::StringRef data) { + auto structured_data_sp = ParseStructuredDataPacket(data); + if (structured_data_sp) + RouteAsyncStructuredData(structured_data_sp); +} + +class CommandObjectProcessGDBRemoteSpeedTest : public CommandObjectParsed { +public: + CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "process plugin packet speed-test", + "Tests packet speeds of various sizes to determine " + "the performance characteristics of the GDB remote " + "connection. ", + nullptr), + m_option_group(), + m_num_packets(LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount, + "The number of packets to send of each varying size " + "(default is 1000).", + 1000), + m_max_send(LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount, + "The maximum number of bytes to send in a packet. Sizes " + "increase in powers of 2 while the size is less than or " + "equal to this option value. (default 1024).", + 1024), + m_max_recv(LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount, + "The maximum number of bytes to receive in a packet. Sizes " + "increase in powers of 2 while the size is less than or " + "equal to this option value. (default 1024).", + 1024), + m_json(LLDB_OPT_SET_1, false, "json", 'j', + "Print the output as JSON data for easy parsing.", false, true) { + m_option_group.Append(&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append(&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append(&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append(&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + ~CommandObjectProcessGDBRemoteSpeedTest() override = default; + + Options *GetOptions() override { return &m_option_group; } + + void DoExecute(Args &command, CommandReturnObject &result) override { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) { + ProcessGDBRemote *process = + (ProcessGDBRemote *)m_interpreter.GetExecutionContext() + .GetProcessPtr(); + if (process) { + StreamSP output_stream_sp = result.GetImmediateOutputStream(); + if (!output_stream_sp) + output_stream_sp = + StreamSP(m_interpreter.GetDebugger().GetAsyncOutputStream()); + result.SetImmediateOutputStream(output_stream_sp); + + const uint32_t num_packets = + (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue(); + const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue(); + const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue(); + const bool json = m_json.GetOptionValue().GetCurrentValue(); + const uint64_t k_recv_amount = + 4 * 1024 * 1024; // Receive amount in bytes + process->GetGDBRemote().TestPacketSpeed( + num_packets, max_send, max_recv, k_recv_amount, json, + output_stream_sp ? *output_stream_sp : result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishResult); + return; + } + } else { + result.AppendErrorWithFormat("'%s' takes no arguments", + m_cmd_name.c_str()); + } + result.SetStatus(eReturnStatusFailed); + } + +protected: + OptionGroupOptions m_option_group; + OptionGroupUInt64 m_num_packets; + OptionGroupUInt64 m_max_send; + OptionGroupUInt64 m_max_recv; + OptionGroupBoolean m_json; +}; + +class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { +private: +public: + CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "process plugin packet history", + "Dumps the packet history buffer. ", nullptr) {} + + ~CommandObjectProcessGDBRemotePacketHistory() override = default; + + void DoExecute(Args &command, CommandReturnObject &result) override { + ProcessGDBRemote *process = + (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) { + process->DumpPluginHistory(result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishResult); + return; + } + result.SetStatus(eReturnStatusFailed); + } +}; + +class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed { +private: +public: + CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "process plugin packet xfer-size", + "Maximum size that lldb will try to read/write one one chunk.", + nullptr) { + AddSimpleArgumentList(eArgTypeUnsignedInteger); + } + + ~CommandObjectProcessGDBRemotePacketXferSize() override = default; + + void DoExecute(Args &command, CommandReturnObject &result) override { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) { + result.AppendErrorWithFormat("'%s' takes an argument to specify the max " + "amount to be transferred when " + "reading/writing", + m_cmd_name.c_str()); + return; + } + + ProcessGDBRemote *process = + (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) { + const char *packet_size = command.GetArgumentAtIndex(0); + errno = 0; + uint64_t user_specified_max = strtoul(packet_size, nullptr, 10); + if (errno == 0 && user_specified_max != 0) { + process->SetUserSpecifiedMaxMemoryTransferSize(user_specified_max); + result.SetStatus(eReturnStatusSuccessFinishResult); + return; + } + } + result.SetStatus(eReturnStatusFailed); + } +}; + +class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { +private: +public: + CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "process plugin packet send", + "Send a custom packet through the GDB remote " + "protocol and print the answer. " + "The packet header and footer will automatically " + "be added to the packet prior to sending and " + "stripped from the result.", + nullptr) { + AddSimpleArgumentList(eArgTypeNone, eArgRepeatStar); + } + + ~CommandObjectProcessGDBRemotePacketSend() override = default; + + void DoExecute(Args &command, CommandReturnObject &result) override { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) { + result.AppendErrorWithFormat( + "'%s' takes a one or more packet content arguments", + m_cmd_name.c_str()); + return; + } + + ProcessGDBRemote *process = + (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) { + for (size_t i = 0; i < argc; ++i) { + const char *packet_cstr = command.GetArgumentAtIndex(0); + StringExtractorGDBRemote response; + process->GetGDBRemote().SendPacketAndWaitForResponse( + packet_cstr, response, process->GetInterruptTimeout()); + result.SetStatus(eReturnStatusSuccessFinishResult); + Stream &output_strm = result.GetOutputStream(); + output_strm.Printf(" packet: %s\n", packet_cstr); + std::string response_str = std::string(response.GetStringRef()); + + if (strstr(packet_cstr, "qGetProfileData") != nullptr) { + response_str = process->HarmonizeThreadIdsForProfileData(response); + } + + if (response_str.empty()) + output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); + else + output_strm.Printf("response: %s\n", response.GetStringRef().data()); + } + } + } +}; + +class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { +private: +public: + CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) + : CommandObjectRaw(interpreter, "process plugin packet monitor", + "Send a qRcmd packet through the GDB remote protocol " + "and print the response." + "The argument passed to this command will be hex " + "encoded into a valid 'qRcmd' packet, sent and the " + "response will be printed.") {} + + ~CommandObjectProcessGDBRemotePacketMonitor() override = default; + + void DoExecute(llvm::StringRef command, + CommandReturnObject &result) override { + if (command.empty()) { + result.AppendErrorWithFormat("'%s' takes a command string argument", + m_cmd_name.c_str()); + return; + } + + ProcessGDBRemote *process = + (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) { + StreamString packet; + packet.PutCString("qRcmd,"); + packet.PutBytesAsRawHex8(command.data(), command.size()); + + StringExtractorGDBRemote response; + Stream &output_strm = result.GetOutputStream(); + process->GetGDBRemote().SendPacketAndReceiveResponseWithOutputSupport( + packet.GetString(), response, process->GetInterruptTimeout(), + [&output_strm](llvm::StringRef output) { output_strm << output; }); + result.SetStatus(eReturnStatusSuccessFinishResult); + output_strm.Printf(" packet: %s\n", packet.GetData()); + const std::string &response_str = std::string(response.GetStringRef()); + + if (response_str.empty()) + output_strm.PutCString("response: \nerror: UNIMPLEMENTED\n"); + else + output_strm.Printf("response: %s\n", response.GetStringRef().data()); + } + } +}; + +class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { +private: +public: + CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "process plugin packet", + "Commands that deal with GDB remote packets.", + nullptr) { + LoadSubCommand( + "history", + CommandObjectSP( + new CommandObjectProcessGDBRemotePacketHistory(interpreter))); + LoadSubCommand( + "send", CommandObjectSP( + new CommandObjectProcessGDBRemotePacketSend(interpreter))); + LoadSubCommand( + "monitor", + CommandObjectSP( + new CommandObjectProcessGDBRemotePacketMonitor(interpreter))); + LoadSubCommand( + "xfer-size", + CommandObjectSP( + new CommandObjectProcessGDBRemotePacketXferSize(interpreter))); + LoadSubCommand("speed-test", + CommandObjectSP(new CommandObjectProcessGDBRemoteSpeedTest( + interpreter))); + } + + ~CommandObjectProcessGDBRemotePacket() override = default; +}; + +class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword { +public: + CommandObjectMultiwordProcessGDBRemote(CommandInterpreter &interpreter) + : CommandObjectMultiword( + interpreter, "process plugin", + "Commands for operating on a ProcessGDBRemote process.", + "process plugin <subcommand> [<subcommand-options>]") { + LoadSubCommand( + "packet", + CommandObjectSP(new CommandObjectProcessGDBRemotePacket(interpreter))); + } + + ~CommandObjectMultiwordProcessGDBRemote() override = default; +}; + +CommandObject *ProcessGDBRemote::GetPluginCommandObject() { + if (!m_command_sp) + m_command_sp = std::make_shared<CommandObjectMultiwordProcessGDBRemote>( + GetTarget().GetDebugger().GetCommandInterpreter()); + return m_command_sp.get(); +} + +void ProcessGDBRemote::DidForkSwitchSoftwareBreakpoints(bool enable) { + GetBreakpointSiteList().ForEach([this, enable](BreakpointSite *bp_site) { + if (bp_site->IsEnabled() && + (bp_site->GetType() == BreakpointSite::eSoftware || + bp_site->GetType() == BreakpointSite::eExternal)) { + m_gdb_comm.SendGDBStoppointTypePacket( + eBreakpointSoftware, enable, bp_site->GetLoadAddress(), + GetSoftwareBreakpointTrapOpcode(bp_site), GetInterruptTimeout()); + } + }); +} + +void ProcessGDBRemote::DidForkSwitchHardwareTraps(bool enable) { + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { + GetBreakpointSiteList().ForEach([this, enable](BreakpointSite *bp_site) { + if (bp_site->IsEnabled() && + bp_site->GetType() == BreakpointSite::eHardware) { + m_gdb_comm.SendGDBStoppointTypePacket( + eBreakpointHardware, enable, bp_site->GetLoadAddress(), + GetSoftwareBreakpointTrapOpcode(bp_site), GetInterruptTimeout()); + } + }); + } + + for (const auto &wp_res_sp : m_watchpoint_resource_list.Sites()) { + addr_t addr = wp_res_sp->GetLoadAddress(); + size_t size = wp_res_sp->GetByteSize(); + GDBStoppointType type = GetGDBStoppointType(wp_res_sp); + m_gdb_comm.SendGDBStoppointTypePacket(type, enable, addr, size, + GetInterruptTimeout()); + } +} + +void ProcessGDBRemote::DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) { + Log *log = GetLog(GDBRLog::Process); + + lldb::pid_t parent_pid = m_gdb_comm.GetCurrentProcessID(); + // Any valid TID will suffice, thread-relevant actions will set a proper TID + // anyway. + lldb::tid_t parent_tid = m_thread_ids.front(); + + lldb::pid_t follow_pid, detach_pid; + lldb::tid_t follow_tid, detach_tid; + + switch (GetFollowForkMode()) { + case eFollowParent: + follow_pid = parent_pid; + follow_tid = parent_tid; + detach_pid = child_pid; + detach_tid = child_tid; + break; + case eFollowChild: + follow_pid = child_pid; + follow_tid = child_tid; + detach_pid = parent_pid; + detach_tid = parent_tid; + break; + } + + // Switch to the process that is going to be detached. + if (!m_gdb_comm.SetCurrentThread(detach_tid, detach_pid)) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid"); + return; + } + + // Disable all software breakpoints in the forked process. + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) + DidForkSwitchSoftwareBreakpoints(false); + + // Remove hardware breakpoints / watchpoints from parent process if we're + // following child. + if (GetFollowForkMode() == eFollowChild) + DidForkSwitchHardwareTraps(false); + + // Switch to the process that is going to be followed + if (!m_gdb_comm.SetCurrentThread(follow_tid, follow_pid) || + !m_gdb_comm.SetCurrentThreadForRun(follow_tid, follow_pid)) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid"); + return; + } + + LLDB_LOG(log, "Detaching process {0}", detach_pid); + Status error = m_gdb_comm.Detach(false, detach_pid); + if (error.Fail()) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() detach packet send failed: {0}", + error.AsCString() ? error.AsCString() : "<unknown error>"); + return; + } + + // Hardware breakpoints/watchpoints are not inherited implicitly, + // so we need to readd them if we're following child. + if (GetFollowForkMode() == eFollowChild) { + DidForkSwitchHardwareTraps(true); + // Update our PID + SetID(child_pid); + } +} + +void ProcessGDBRemote::DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) { + Log *log = GetLog(GDBRLog::Process); + + LLDB_LOG( + log, + "ProcessGDBRemote::DidFork() called for child_pid: {0}, child_tid {1}", + child_pid, child_tid); + ++m_vfork_in_progress_count; + + // Disable all software breakpoints for the duration of vfork. + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) + DidForkSwitchSoftwareBreakpoints(false); + + lldb::pid_t detach_pid; + lldb::tid_t detach_tid; + + switch (GetFollowForkMode()) { + case eFollowParent: + detach_pid = child_pid; + detach_tid = child_tid; + break; + case eFollowChild: + detach_pid = m_gdb_comm.GetCurrentProcessID(); + // Any valid TID will suffice, thread-relevant actions will set a proper TID + // anyway. + detach_tid = m_thread_ids.front(); + + // Switch to the parent process before detaching it. + if (!m_gdb_comm.SetCurrentThread(detach_tid, detach_pid)) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid"); + return; + } + + // Remove hardware breakpoints / watchpoints from the parent process. + DidForkSwitchHardwareTraps(false); + + // Switch to the child process. + if (!m_gdb_comm.SetCurrentThread(child_tid, child_pid) || + !m_gdb_comm.SetCurrentThreadForRun(child_tid, child_pid)) { + LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid"); + return; + } + break; + } + + LLDB_LOG(log, "Detaching process {0}", detach_pid); + Status error = m_gdb_comm.Detach(false, detach_pid); + if (error.Fail()) { + LLDB_LOG(log, + "ProcessGDBRemote::DidFork() detach packet send failed: {0}", + error.AsCString() ? error.AsCString() : "<unknown error>"); + return; + } + + if (GetFollowForkMode() == eFollowChild) { + // Update our PID + SetID(child_pid); + } +} + +void ProcessGDBRemote::DidVForkDone() { + assert(m_vfork_in_progress_count > 0); + --m_vfork_in_progress_count; + + // Reenable all software breakpoints that were enabled before vfork. + if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) + DidForkSwitchSoftwareBreakpoints(true); +} + +void ProcessGDBRemote::DidExec() { + // If we are following children, vfork is finished by exec (rather than + // vforkdone that is submitted for parent). + if (GetFollowForkMode() == eFollowChild) { + if (m_vfork_in_progress_count > 0) + --m_vfork_in_progress_count; + } + Process::DidExec(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h new file mode 100644 index 000000000000..b44ffefcd0d3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -0,0 +1,497 @@ +//===-- ProcessGDBRemote.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_PROCESSGDBREMOTE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_PROCESSGDBREMOTE_H + +#include <atomic> +#include <map> +#include <mutex> +#include <optional> +#include <string> +#include <vector> + +#include "lldb/Core/LoadedModuleInfoList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Target/DynamicRegisterInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/GDBRemote.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractor.h" +#include "lldb/Utility/StringList.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-private-forward.h" + +#include "GDBRemoteCommunicationClient.h" +#include "GDBRemoteRegisterContext.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" + +namespace lldb_private { +namespace repro { +class Loader; +} +namespace process_gdb_remote { + +class ThreadGDBRemote; + +class ProcessGDBRemote : public Process, + private GDBRemoteClientBase::ContinueDelegate { +public: + ~ProcessGDBRemote() override; + + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void DebuggerInitialize(Debugger &debugger); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "gdb-remote"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static std::chrono::seconds GetPacketTimeout(); + + ArchSpec GetSystemArchitecture() override; + + // Check if a given Process + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + CommandObject *GetPluginCommandObject() override; + + void DumpPluginHistory(Stream &s) override; + + // Creating a new process, or attaching to an existing one + Status DoWillLaunch(Module *module) override; + + Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) override; + + void DidLaunch() override; + + Status DoWillAttachToProcessWithID(lldb::pid_t pid) override; + + Status DoWillAttachToProcessWithName(const char *process_name, + bool wait_for_launch) override; + + Status DoConnectRemote(llvm::StringRef remote_url) override; + + Status WillLaunchOrAttach(); + + Status DoAttachToProcessWithID(lldb::pid_t pid, + const ProcessAttachInfo &attach_info) override; + + Status + DoAttachToProcessWithName(const char *process_name, + const ProcessAttachInfo &attach_info) override; + + void DidAttach(ArchSpec &process_arch) override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // Process Control + Status WillResume() override; + + Status DoResume() override; + + Status DoHalt(bool &caused_stop) override; + + Status DoDetach(bool keep_stopped) override; + + bool DetachRequiresHalt() override { return true; } + + Status DoSignal(int signal) override; + + Status DoDestroy() override; + + void RefreshStateAfterStop() override; + + void SetUnixSignals(const lldb::UnixSignalsSP &signals_sp); + + // Process Queries + bool IsAlive() override; + + lldb::addr_t GetImageInfoAddress() override; + + void WillPublicStop() override; + + // Process Memory + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; + + Status + WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) override; + + size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size, + Status &error) override; + + lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, + Status &error) override; + + Status DoDeallocateMemory(lldb::addr_t ptr) override; + + // Process STDIO + size_t PutSTDIN(const char *buf, size_t buf_size, Status &error) override; + + // Process Breakpoints + Status EnableBreakpointSite(BreakpointSite *bp_site) override; + + Status DisableBreakpointSite(BreakpointSite *bp_site) override; + + // Process Watchpoints + Status EnableWatchpoint(lldb::WatchpointSP wp_sp, + bool notify = true) override; + + Status DisableWatchpoint(lldb::WatchpointSP wp_sp, + bool notify = true) override; + + std::optional<uint32_t> GetWatchpointSlotCount() override; + + llvm::Expected<TraceSupportedResponse> TraceSupported() override; + + llvm::Error TraceStop(const TraceStopRequest &request) override; + + llvm::Error TraceStart(const llvm::json::Value &request) override; + + llvm::Expected<std::string> TraceGetState(llvm::StringRef type) override; + + llvm::Expected<std::vector<uint8_t>> + TraceGetBinaryData(const TraceGetBinaryDataRequest &request) override; + + std::optional<bool> DoGetWatchpointReportedAfter() override; + + bool StartNoticingNewThreads() override; + + bool StopNoticingNewThreads() override; + + GDBRemoteCommunicationClient &GetGDBRemote() { return m_gdb_comm; } + + Status SendEventData(const char *data) override; + + // Override DidExit so we can disconnect from the remote GDB server + void DidExit() override; + + void SetUserSpecifiedMaxMemoryTransferSize(uint64_t user_specified_max); + + bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, + ModuleSpec &module_spec) override; + + void PrefetchModuleSpecs(llvm::ArrayRef<FileSpec> module_file_specs, + const llvm::Triple &triple) override; + + llvm::VersionTuple GetHostOSVersion() override; + llvm::VersionTuple GetHostMacCatalystVersion() override; + + llvm::Error LoadModules() override; + + llvm::Expected<LoadedModuleInfoList> GetLoadedModuleList() override; + + Status GetFileLoadAddress(const FileSpec &file, bool &is_loaded, + lldb::addr_t &load_addr) override; + + void ModulesDidLoad(ModuleList &module_list) override; + + StructuredData::ObjectSP + GetLoadedDynamicLibrariesInfos(lldb::addr_t image_list_address, + lldb::addr_t image_count) override; + + Status + ConfigureStructuredData(llvm::StringRef type_name, + const StructuredData::ObjectSP &config_sp) override; + + StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos() override; + + StructuredData::ObjectSP GetLoadedDynamicLibrariesInfos( + const std::vector<lldb::addr_t> &load_addresses) override; + + StructuredData::ObjectSP + GetLoadedDynamicLibrariesInfos_sender(StructuredData::ObjectSP args); + + StructuredData::ObjectSP GetSharedCacheInfo() override; + + StructuredData::ObjectSP GetDynamicLoaderProcessState() override; + + std::string HarmonizeThreadIdsForProfileData( + StringExtractorGDBRemote &inputStringExtractor); + + void DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) override; + void DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) override; + void DidVForkDone() override; + void DidExec() override; + + llvm::Expected<bool> SaveCore(llvm::StringRef outfile) override; + +protected: + friend class ThreadGDBRemote; + friend class GDBRemoteCommunicationClient; + friend class GDBRemoteRegisterContext; + + ProcessGDBRemote(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); + + bool SupportsMemoryTagging() override; + + /// Broadcaster event bits definitions. + enum { + eBroadcastBitAsyncContinue = (1 << 0), + eBroadcastBitAsyncThreadShouldExit = (1 << 1), + eBroadcastBitAsyncThreadDidExit = (1 << 2) + }; + + GDBRemoteCommunicationClient m_gdb_comm; + std::atomic<lldb::pid_t> m_debugserver_pid; + + std::optional<StringExtractorGDBRemote> m_last_stop_packet; + std::recursive_mutex m_last_stop_packet_mutex; + + GDBRemoteDynamicRegisterInfoSP m_register_info_sp; + Broadcaster m_async_broadcaster; + lldb::ListenerSP m_async_listener_sp; + HostThread m_async_thread; + std::recursive_mutex m_async_thread_state_mutex; + typedef std::vector<lldb::tid_t> tid_collection; + typedef std::vector<std::pair<lldb::tid_t, int>> tid_sig_collection; + typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap; + typedef std::map<uint32_t, std::string> ExpeditedRegisterMap; + tid_collection m_thread_ids; // Thread IDs for all threads. This list gets + // updated after stopping + std::vector<lldb::addr_t> m_thread_pcs; // PC values for all the threads. + StructuredData::ObjectSP m_jstopinfo_sp; // Stop info only for any threads + // that have valid stop infos + StructuredData::ObjectSP m_jthreadsinfo_sp; // Full stop info, expedited + // registers and memory for all + // threads if "jThreadsInfo" + // packet is supported + tid_collection m_continue_c_tids; // 'c' for continue + tid_sig_collection m_continue_C_tids; // 'C' for continue with signal + tid_collection m_continue_s_tids; // 's' for step + tid_sig_collection m_continue_S_tids; // 'S' for step with signal + uint64_t m_max_memory_size; // The maximum number of bytes to read/write when + // reading and writing memory + uint64_t m_remote_stub_max_memory_size; // The maximum memory size the remote + // gdb stub can handle + MMapMap m_addr_to_mmap_size; + lldb::BreakpointSP m_thread_create_bp_sp; + bool m_waiting_for_attach; + lldb::CommandObjectSP m_command_sp; + int64_t m_breakpoint_pc_offset; + lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach + bool m_use_g_packet_for_reading; + + bool m_allow_flash_writes; + using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>; + using FlashRange = FlashRangeVector::Entry; + FlashRangeVector m_erased_flash_ranges; + + // Number of vfork() operations being handled. + uint32_t m_vfork_in_progress_count; + + // Accessors + bool IsRunning(lldb::StateType state) { + return state == lldb::eStateRunning || IsStepping(state); + } + + bool IsStepping(lldb::StateType state) { + return state == lldb::eStateStepping; + } + + bool CanResume(lldb::StateType state) { return state == lldb::eStateStopped; } + + bool HasExited(lldb::StateType state) { return state == lldb::eStateExited; } + + void Clear(); + + bool DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) override; + + Status EstablishConnectionIfNeeded(const ProcessInfo &process_info); + + Status LaunchAndConnectToDebugserver(const ProcessInfo &process_info); + + void KillDebugserverProcess(); + + void BuildDynamicRegisterInfo(bool force); + + void SetLastStopPacket(const StringExtractorGDBRemote &response); + + bool ParsePythonTargetDefinition(const FileSpec &target_definition_fspec); + + DataExtractor GetAuxvData() override; + + StructuredData::ObjectSP GetExtendedInfoForThread(lldb::tid_t tid); + + void GetMaxMemorySize(); + + bool CalculateThreadStopInfo(ThreadGDBRemote *thread); + + size_t UpdateThreadPCsFromStopReplyThreadsValue(llvm::StringRef value); + + size_t UpdateThreadIDsFromStopReplyThreadsValue(llvm::StringRef value); + + bool StartAsyncThread(); + + void StopAsyncThread(); + + lldb::thread_result_t AsyncThread(); + + static void + MonitorDebugserverProcess(std::weak_ptr<ProcessGDBRemote> process_wp, + lldb::pid_t pid, int signo, int exit_status); + + lldb::StateType SetThreadStopInfo(StringExtractor &stop_packet); + + bool + GetThreadStopInfoFromJSON(ThreadGDBRemote *thread, + const StructuredData::ObjectSP &thread_infos_sp); + + lldb::ThreadSP SetThreadStopInfo(StructuredData::Dictionary *thread_dict); + + lldb::ThreadSP + SetThreadStopInfo(lldb::tid_t tid, + ExpeditedRegisterMap &expedited_register_map, uint8_t signo, + const std::string &thread_name, const std::string &reason, + const std::string &description, uint32_t exc_type, + const std::vector<lldb::addr_t> &exc_data, + lldb::addr_t thread_dispatch_qaddr, bool queue_vars_valid, + lldb_private::LazyBool associated_with_libdispatch_queue, + lldb::addr_t dispatch_queue_t, std::string &queue_name, + lldb::QueueKind queue_kind, uint64_t queue_serial); + + void ClearThreadIDList(); + + bool UpdateThreadIDList(); + + void DidLaunchOrAttach(ArchSpec &process_arch); + void LoadStubBinaries(); + void MaybeLoadExecutableModule(); + + Status ConnectToDebugserver(llvm::StringRef host_port); + + const char *GetDispatchQueueNameForThread(lldb::addr_t thread_dispatch_qaddr, + std::string &dispatch_queue_name); + + DynamicLoader *GetDynamicLoader() override; + + bool GetGDBServerRegisterInfoXMLAndProcess( + ArchSpec &arch_to_use, std::string xml_filename, + std::vector<DynamicRegisterInfo::Register> ®isters); + + // Convert DynamicRegisterInfo::Registers into RegisterInfos and add + // to the dynamic register list. + void AddRemoteRegisters(std::vector<DynamicRegisterInfo::Register> ®isters, + const ArchSpec &arch_to_use); + // Query remote GDBServer for register information + bool GetGDBServerRegisterInfo(ArchSpec &arch); + + lldb::ModuleSP LoadModuleAtAddress(const FileSpec &file, + lldb::addr_t link_map, + lldb::addr_t base_addr, + bool value_is_offset); + + Status UpdateAutomaticSignalFiltering() override; + + Status FlashErase(lldb::addr_t addr, size_t size); + + Status FlashDone(); + + bool HasErased(FlashRange range); + + llvm::Expected<std::vector<uint8_t>> + DoReadMemoryTags(lldb::addr_t addr, size_t len, int32_t type) override; + + Status DoWriteMemoryTags(lldb::addr_t addr, size_t len, int32_t type, + const std::vector<uint8_t> &tags) override; + + Status DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo ®ion_info) override; + +private: + // For ProcessGDBRemote only + std::string m_partial_profile_data; + std::map<uint64_t, uint32_t> m_thread_id_to_used_usec_map; + uint64_t m_last_signals_version = 0; + + static bool NewThreadNotifyBreakpointHit(void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + // ContinueDelegate interface + void HandleAsyncStdout(llvm::StringRef out) override; + void HandleAsyncMisc(llvm::StringRef data) override; + void HandleStopReply() override; + void HandleAsyncStructuredDataPacket(llvm::StringRef data) override; + + void SetThreadPc(const lldb::ThreadSP &thread_sp, uint64_t index); + using ModuleCacheKey = std::pair<std::string, std::string>; + // KeyInfo for the cached module spec DenseMap. + // The invariant is that all real keys will have the file and architecture + // set. + // The empty key has an empty file and an empty arch. + // The tombstone key has an invalid arch and an empty file. + // The comparison and hash functions take the file name and architecture + // triple into account. + struct ModuleCacheInfo { + static ModuleCacheKey getEmptyKey() { return ModuleCacheKey(); } + + static ModuleCacheKey getTombstoneKey() { return ModuleCacheKey("", "T"); } + + static unsigned getHashValue(const ModuleCacheKey &key) { + return llvm::hash_combine(key.first, key.second); + } + + static bool isEqual(const ModuleCacheKey &LHS, const ModuleCacheKey &RHS) { + return LHS == RHS; + } + }; + + llvm::DenseMap<ModuleCacheKey, ModuleSpec, ModuleCacheInfo> + m_cached_module_specs; + + ProcessGDBRemote(const ProcessGDBRemote &) = delete; + const ProcessGDBRemote &operator=(const ProcessGDBRemote &) = delete; + + // fork helpers + void DidForkSwitchSoftwareBreakpoints(bool enable); + void DidForkSwitchHardwareTraps(bool enable); + + void ParseExpeditedRegisters(ExpeditedRegisterMap &expedited_register_map, + lldb::ThreadSP thread_sp); + + // Lists of register fields generated from the remote's target XML. + // Pointers to these RegisterFlags will be set in the register info passed + // back to the upper levels of lldb. Doing so is safe because this class will + // live at least as long as the debug session. We therefore do not store the + // data directly in the map because the map may reallocate it's storage as new + // entries are added. Which would invalidate any pointers set in the register + // info up to that point. + llvm::StringMap<std::unique_ptr<RegisterFlags>> m_registers_flags_types; + + // Enum types are referenced by register fields. This does not store the data + // directly because the map may reallocate. Pointers to these are contained + // within instances of RegisterFlags. + llvm::StringMap<std::unique_ptr<FieldEnum>> m_registers_enum_types; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_PROCESSGDBREMOTE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp new file mode 100644 index 000000000000..3322f6b8048a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp @@ -0,0 +1,47 @@ +//===-- ProcessGDBRemoteLog.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 "ProcessGDBRemoteLog.h" +#include "ProcessGDBRemote.h" +#include "llvm/Support/Threading.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +static constexpr Log::Category g_categories[] = { + {{"async"}, {"log asynchronous activity"}, GDBRLog::Async}, + {{"break"}, {"log breakpoints"}, GDBRLog::Breakpoints}, + {{"comm"}, {"log communication activity"}, GDBRLog::Comm}, + {{"packets"}, {"log gdb remote packets"}, GDBRLog::Packets}, + {{"memory"}, {"log memory reads and writes"}, GDBRLog::Memory}, + {{"data-short"}, + {"log memory bytes for memory reads and writes for short transactions " + "only"}, + GDBRLog::MemoryDataShort}, + {{"data-long"}, + {"log memory bytes for memory reads and writes for all transactions"}, + GDBRLog::MemoryDataLong}, + {{"process"}, {"log process events and activities"}, GDBRLog::Process}, + {{"step"}, {"log step related activities"}, GDBRLog::Step}, + {{"thread"}, {"log thread events and activities"}, GDBRLog::Thread}, + {{"watch"}, {"log watchpoint related activities"}, GDBRLog::Watchpoints}, +}; + +static Log::Channel g_channel(g_categories, GDBRLog::Packets); + +template <> Log::Channel &lldb_private::LogChannelFor<GDBRLog>() { + return g_channel; +} + +void ProcessGDBRemoteLog::Initialize() { + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, []() { + Log::Register("gdb-remote", g_channel); + }); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h new file mode 100644 index 000000000000..66b2f00f1ea9 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h @@ -0,0 +1,45 @@ +//===-- ProcessGDBRemoteLog.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_PROCESSGDBREMOTELOG_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_PROCESSGDBREMOTELOG_H + +#include "lldb/Utility/Log.h" +#include "llvm/ADT/BitmaskEnum.h" + +namespace lldb_private { +namespace process_gdb_remote { + +enum class GDBRLog : Log::MaskType { + Async = Log::ChannelFlag<0>, + Breakpoints = Log::ChannelFlag<1>, + Comm = Log::ChannelFlag<2>, + Memory = Log::ChannelFlag<3>, // Log memory reads/writes calls + MemoryDataLong = Log::ChannelFlag<4>, // Log all memory reads/writes bytes + MemoryDataShort = Log::ChannelFlag<5>, // Log short memory reads/writes bytes + Packets = Log::ChannelFlag<6>, + Process = Log::ChannelFlag<7>, + Step = Log::ChannelFlag<8>, + Thread = Log::ChannelFlag<9>, + Watchpoints = Log::ChannelFlag<10>, + LLVM_MARK_AS_BITMASK_ENUM(Watchpoints) +}; +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +class ProcessGDBRemoteLog { +public: + static void Initialize(); +}; + +} // namespace process_gdb_remote + +template <> Log::Channel &LogChannelFor<process_gdb_remote::GDBRLog>(); + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_PROCESSGDBREMOTELOG_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td new file mode 100644 index 000000000000..520dad062e09 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td @@ -0,0 +1,24 @@ +include "../../../../include/lldb/Core/PropertiesBase.td" + +let Definition = "processgdbremote" in { + def PacketTimeout: Property<"packet-timeout", "UInt64">, + Global, +#ifdef LLDB_SANITIZED + DefaultUnsignedValue<60>, +#else + DefaultUnsignedValue<5>, +#endif + Desc<"Specify the default packet timeout in seconds.">; + def TargetDefinitionFile: Property<"target-definition-file", "FileSpec">, + Global, + DefaultStringValue<"">, + Desc<"The file that provides the description for remote target registers.">; + def UseSVR4: Property<"use-libraries-svr4", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, the libraries-svr4 feature will be used to get a hold of the process's loaded modules. This setting is only effective if lldb was build with xml support.">; + def UseGPacketForReading: Property<"use-g-packet-for-reading", "Boolean">, + Global, + DefaultFalse, + Desc<"Specify if the server should use 'g' packets to read registers.">; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp new file mode 100644 index 000000000000..3d23c074c1be --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -0,0 +1,368 @@ +//===-- ThreadGDBRemote.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 "ThreadGDBRemote.h" + +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/StringExtractorGDBRemote.h" + +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; + +// Thread Registers + +ThreadGDBRemote::ThreadGDBRemote(Process &process, lldb::tid_t tid) + : Thread(process, tid), m_thread_name(), m_dispatch_queue_name(), + m_thread_dispatch_qaddr(LLDB_INVALID_ADDRESS), + m_dispatch_queue_t(LLDB_INVALID_ADDRESS), m_queue_kind(eQueueKindUnknown), + m_queue_serial_number(LLDB_INVALID_QUEUE_ID), + m_associated_with_libdispatch_queue(eLazyBoolCalculate) { + Log *log = GetLog(GDBRLog::Thread); + LLDB_LOG(log, "this = {0}, pid = {1}, tid = {2}", this, process.GetID(), + GetID()); + // At this point we can clone reg_info for architectures supporting + // run-time update to register sizes and offsets.. + auto &gdb_process = static_cast<ProcessGDBRemote &>(process); + if (!gdb_process.m_register_info_sp->IsReconfigurable()) + m_reg_info_sp = gdb_process.m_register_info_sp; + else + m_reg_info_sp = std::make_shared<GDBRemoteDynamicRegisterInfo>( + *gdb_process.m_register_info_sp); +} + +ThreadGDBRemote::~ThreadGDBRemote() { + ProcessSP process_sp(GetProcess()); + Log *log = GetLog(GDBRLog::Thread); + LLDB_LOG(log, "this = {0}, pid = {1}, tid = {2}", this, + process_sp ? process_sp->GetID() : LLDB_INVALID_PROCESS_ID, GetID()); + DestroyThread(); +} + +const char *ThreadGDBRemote::GetName() { + if (m_thread_name.empty()) + return nullptr; + return m_thread_name.c_str(); +} + +void ThreadGDBRemote::ClearQueueInfo() { + m_dispatch_queue_name.clear(); + m_queue_kind = eQueueKindUnknown; + m_queue_serial_number = 0; + m_dispatch_queue_t = LLDB_INVALID_ADDRESS; + m_associated_with_libdispatch_queue = eLazyBoolCalculate; +} + +void ThreadGDBRemote::SetQueueInfo(std::string &&queue_name, + QueueKind queue_kind, uint64_t queue_serial, + addr_t dispatch_queue_t, + LazyBool associated_with_libdispatch_queue) { + m_dispatch_queue_name = queue_name; + m_queue_kind = queue_kind; + m_queue_serial_number = queue_serial; + m_dispatch_queue_t = dispatch_queue_t; + m_associated_with_libdispatch_queue = associated_with_libdispatch_queue; +} + +const char *ThreadGDBRemote::GetQueueName() { + // If our cached queue info is valid, then someone called + // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned + // from the stop reply packet. In this case we trust that the info is valid + // in m_dispatch_queue_name without refetching it + if (CachedQueueInfoIsValid()) { + if (m_dispatch_queue_name.empty()) + return nullptr; + else + return m_dispatch_queue_name.c_str(); + } + // Always re-fetch the dispatch queue name since it can change + + if (m_associated_with_libdispatch_queue == eLazyBoolNo) + return nullptr; + + if (m_thread_dispatch_qaddr != 0 && + m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { + ProcessSP process_sp(GetProcess()); + if (process_sp) { + SystemRuntime *runtime = process_sp->GetSystemRuntime(); + if (runtime) + m_dispatch_queue_name = + runtime->GetQueueNameFromThreadQAddress(m_thread_dispatch_qaddr); + else + m_dispatch_queue_name.clear(); + + if (!m_dispatch_queue_name.empty()) + return m_dispatch_queue_name.c_str(); + } + } + return nullptr; +} + +QueueKind ThreadGDBRemote::GetQueueKind() { + // If our cached queue info is valid, then someone called + // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned + // from the stop reply packet. In this case we trust that the info is valid + // in m_dispatch_queue_name without refetching it + if (CachedQueueInfoIsValid()) { + return m_queue_kind; + } + + if (m_associated_with_libdispatch_queue == eLazyBoolNo) + return eQueueKindUnknown; + + if (m_thread_dispatch_qaddr != 0 && + m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { + ProcessSP process_sp(GetProcess()); + if (process_sp) { + SystemRuntime *runtime = process_sp->GetSystemRuntime(); + if (runtime) + m_queue_kind = runtime->GetQueueKind(m_thread_dispatch_qaddr); + return m_queue_kind; + } + } + return eQueueKindUnknown; +} + +queue_id_t ThreadGDBRemote::GetQueueID() { + // If our cached queue info is valid, then someone called + // ThreadGDBRemote::SetQueueInfo(...) with valid information that was gleaned + // from the stop reply packet. In this case we trust that the info is valid + // in m_dispatch_queue_name without refetching it + if (CachedQueueInfoIsValid()) + return m_queue_serial_number; + + if (m_associated_with_libdispatch_queue == eLazyBoolNo) + return LLDB_INVALID_QUEUE_ID; + + if (m_thread_dispatch_qaddr != 0 && + m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { + ProcessSP process_sp(GetProcess()); + if (process_sp) { + SystemRuntime *runtime = process_sp->GetSystemRuntime(); + if (runtime) { + return runtime->GetQueueIDFromThreadQAddress(m_thread_dispatch_qaddr); + } + } + } + return LLDB_INVALID_QUEUE_ID; +} + +QueueSP ThreadGDBRemote::GetQueue() { + queue_id_t queue_id = GetQueueID(); + QueueSP queue; + if (queue_id != LLDB_INVALID_QUEUE_ID) { + ProcessSP process_sp(GetProcess()); + if (process_sp) { + queue = process_sp->GetQueueList().FindQueueByID(queue_id); + } + } + return queue; +} + +addr_t ThreadGDBRemote::GetQueueLibdispatchQueueAddress() { + if (m_dispatch_queue_t == LLDB_INVALID_ADDRESS) { + if (m_thread_dispatch_qaddr != 0 && + m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) { + ProcessSP process_sp(GetProcess()); + if (process_sp) { + SystemRuntime *runtime = process_sp->GetSystemRuntime(); + if (runtime) { + m_dispatch_queue_t = + runtime->GetLibdispatchQueueAddressFromThreadQAddress( + m_thread_dispatch_qaddr); + } + } + } + } + return m_dispatch_queue_t; +} + +void ThreadGDBRemote::SetQueueLibdispatchQueueAddress( + lldb::addr_t dispatch_queue_t) { + m_dispatch_queue_t = dispatch_queue_t; +} + +bool ThreadGDBRemote::ThreadHasQueueInformation() const { + return m_thread_dispatch_qaddr != 0 && + m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS && + m_dispatch_queue_t != LLDB_INVALID_ADDRESS && + m_queue_kind != eQueueKindUnknown && m_queue_serial_number != 0; +} + +LazyBool ThreadGDBRemote::GetAssociatedWithLibdispatchQueue() { + return m_associated_with_libdispatch_queue; +} + +void ThreadGDBRemote::SetAssociatedWithLibdispatchQueue( + LazyBool associated_with_libdispatch_queue) { + m_associated_with_libdispatch_queue = associated_with_libdispatch_queue; +} + +StructuredData::ObjectSP ThreadGDBRemote::FetchThreadExtendedInfo() { + StructuredData::ObjectSP object_sp; + const lldb::user_id_t tid = GetProtocolID(); + Log *log = GetLog(GDBRLog::Thread); + LLDB_LOGF(log, "Fetching extended information for thread %4.4" PRIx64, tid); + ProcessSP process_sp(GetProcess()); + if (process_sp) { + ProcessGDBRemote *gdb_process = + static_cast<ProcessGDBRemote *>(process_sp.get()); + object_sp = gdb_process->GetExtendedInfoForThread(tid); + } + return object_sp; +} + +void ThreadGDBRemote::WillResume(StateType resume_state) { + int signo = GetResumeSignal(); + const lldb::user_id_t tid = GetProtocolID(); + Log *log = GetLog(GDBRLog::Thread); + LLDB_LOGF(log, "Resuming thread: %4.4" PRIx64 " with state: %s.", tid, + StateAsCString(resume_state)); + + ProcessSP process_sp(GetProcess()); + if (process_sp) { + ProcessGDBRemote *gdb_process = + static_cast<ProcessGDBRemote *>(process_sp.get()); + switch (resume_state) { + case eStateSuspended: + case eStateStopped: + // Don't append anything for threads that should stay stopped. + break; + + case eStateRunning: + if (gdb_process->GetUnixSignals()->SignalIsValid(signo)) + gdb_process->m_continue_C_tids.push_back(std::make_pair(tid, signo)); + else + gdb_process->m_continue_c_tids.push_back(tid); + break; + + case eStateStepping: + if (gdb_process->GetUnixSignals()->SignalIsValid(signo)) + gdb_process->m_continue_S_tids.push_back(std::make_pair(tid, signo)); + else + gdb_process->m_continue_s_tids.push_back(tid); + break; + + default: + break; + } + } +} + +void ThreadGDBRemote::RefreshStateAfterStop() { + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The + // GDBRemoteRegisterContext class has been made smart enough to detect when + // it needs to invalidate which registers are valid by putting hooks in the + // register read and register supply functions where they check the process + // stop ID and do the right thing. + const bool force = false; + GetRegisterContext()->InvalidateIfNeeded(force); +} + +bool ThreadGDBRemote::ThreadIDIsValid(lldb::tid_t thread) { + return thread != 0; +} + +void ThreadGDBRemote::Dump(Log *log, uint32_t index) {} + +bool ThreadGDBRemote::ShouldStop(bool &step_more) { return true; } +lldb::RegisterContextSP ThreadGDBRemote::GetRegisterContext() { + if (!m_reg_context_sp) + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadGDBRemote::CreateRegisterContextForFrame(StackFrame *frame) { + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) { + ProcessSP process_sp(GetProcess()); + if (process_sp) { + ProcessGDBRemote *gdb_process = + static_cast<ProcessGDBRemote *>(process_sp.get()); + bool pSupported = + gdb_process->GetGDBRemote().GetpPacketSupported(GetID()); + bool read_all_registers_at_once = + !pSupported || gdb_process->m_use_g_packet_for_reading; + bool write_all_registers_at_once = !pSupported; + reg_ctx_sp = std::make_shared<GDBRemoteRegisterContext>( + *this, concrete_frame_idx, m_reg_info_sp, read_all_registers_at_once, + write_all_registers_at_once); + } + } else { + reg_ctx_sp = GetUnwinder().CreateRegisterContextForFrame(frame); + } + return reg_ctx_sp; +} + +bool ThreadGDBRemote::PrivateSetRegisterValue(uint32_t reg, + llvm::ArrayRef<uint8_t> data) { + GDBRemoteRegisterContext *gdb_reg_ctx = + static_cast<GDBRemoteRegisterContext *>(GetRegisterContext().get()); + assert(gdb_reg_ctx); + return gdb_reg_ctx->PrivateSetRegisterValue(reg, data); +} + +bool ThreadGDBRemote::PrivateSetRegisterValue(uint32_t reg, uint64_t regval) { + GDBRemoteRegisterContext *gdb_reg_ctx = + static_cast<GDBRemoteRegisterContext *>(GetRegisterContext().get()); + assert(gdb_reg_ctx); + return gdb_reg_ctx->PrivateSetRegisterValue(reg, regval); +} + +bool ThreadGDBRemote::CalculateStopInfo() { + ProcessSP process_sp(GetProcess()); + if (process_sp) + return static_cast<ProcessGDBRemote *>(process_sp.get()) + ->CalculateThreadStopInfo(this); + return false; +} + +llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> +ThreadGDBRemote::GetSiginfo(size_t max_size) const { + ProcessSP process_sp(GetProcess()); + if (!process_sp) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "no process"); + ProcessGDBRemote *gdb_process = + static_cast<ProcessGDBRemote *>(process_sp.get()); + if (!gdb_process->m_gdb_comm.GetQXferSigInfoReadSupported()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "qXfer:siginfo:read not supported"); + + llvm::Expected<std::string> response = + gdb_process->m_gdb_comm.ReadExtFeature("siginfo", ""); + if (!response) + return response.takeError(); + + return llvm::MemoryBuffer::getMemBufferCopy(response.get()); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h new file mode 100644 index 000000000000..5bc90a3dedce --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h @@ -0,0 +1,126 @@ +//===-- ThreadGDBRemote.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_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_THREADGDBREMOTE_H +#define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_THREADGDBREMOTE_H + +#include <string> + +#include "lldb/Target/Thread.h" +#include "lldb/Utility/StructuredData.h" + +#include "GDBRemoteRegisterContext.h" + +class StringExtractor; + +namespace lldb_private { +class Process; + +namespace process_gdb_remote { + +class ProcessGDBRemote; + +class ThreadGDBRemote : public Thread { +public: + ThreadGDBRemote(Process &process, lldb::tid_t tid); + + ~ThreadGDBRemote() override; + + void WillResume(lldb::StateType resume_state) override; + + void RefreshStateAfterStop() override; + + const char *GetName() override; + + const char *GetQueueName() override; + + lldb::QueueKind GetQueueKind() override; + + lldb::queue_id_t GetQueueID() override; + + lldb::QueueSP GetQueue() override; + + lldb::addr_t GetQueueLibdispatchQueueAddress() override; + + void SetQueueLibdispatchQueueAddress(lldb::addr_t dispatch_queue_t) override; + + bool ThreadHasQueueInformation() const override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + + void Dump(Log *log, uint32_t index); + + static bool ThreadIDIsValid(lldb::tid_t thread); + + bool ShouldStop(bool &step_more); + + const char *GetBasicInfoAsString(); + + void SetName(const char *name) override { + if (name && name[0]) + m_thread_name.assign(name); + else + m_thread_name.clear(); + } + + lldb::addr_t GetThreadDispatchQAddr() { return m_thread_dispatch_qaddr; } + + void SetThreadDispatchQAddr(lldb::addr_t thread_dispatch_qaddr) { + m_thread_dispatch_qaddr = thread_dispatch_qaddr; + } + + void ClearQueueInfo(); + + void SetQueueInfo(std::string &&queue_name, lldb::QueueKind queue_kind, + uint64_t queue_serial, lldb::addr_t dispatch_queue_t, + lldb_private::LazyBool associated_with_libdispatch_queue); + + lldb_private::LazyBool GetAssociatedWithLibdispatchQueue() override; + + void SetAssociatedWithLibdispatchQueue( + lldb_private::LazyBool associated_with_libdispatch_queue) override; + + StructuredData::ObjectSP FetchThreadExtendedInfo() override; + +protected: + friend class ProcessGDBRemote; + + std::string m_thread_name; + std::string m_dispatch_queue_name; + lldb::addr_t m_thread_dispatch_qaddr; + lldb::addr_t m_dispatch_queue_t; + lldb::QueueKind + m_queue_kind; // Queue info from stop reply/stop info for thread + uint64_t + m_queue_serial_number; // Queue info from stop reply/stop info for thread + lldb_private::LazyBool m_associated_with_libdispatch_queue; + + GDBRemoteDynamicRegisterInfoSP m_reg_info_sp; + + bool PrivateSetRegisterValue(uint32_t reg, llvm::ArrayRef<uint8_t> data); + + bool PrivateSetRegisterValue(uint32_t reg, uint64_t regval); + + bool CachedQueueInfoIsValid() const { + return m_queue_kind != lldb::eQueueKindUnknown; + } + void SetStopInfoFromPacket(StringExtractor &stop_packet, uint32_t stop_id); + + bool CalculateStopInfo() override; + + llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> + GetSiginfo(size_t max_size) const override; +}; + +} // namespace process_gdb_remote +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_THREADGDBREMOTE_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp new file mode 100644 index 000000000000..be9fae938e22 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -0,0 +1,711 @@ +//===-- MinidumpParser.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 "MinidumpParser.h" +#include "NtStructures.h" +#include "RegisterContextMinidump_x86_32.h" + +#include "Plugins/Process/Utility/LinuxProcMaps.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +// C includes +// C++ includes +#include <algorithm> +#include <map> +#include <optional> +#include <vector> +#include <utility> + +using namespace lldb_private; +using namespace minidump; + +llvm::Expected<MinidumpParser> +MinidumpParser::Create(const lldb::DataBufferSP &data_sp) { + auto ExpectedFile = llvm::object::MinidumpFile::create( + llvm::MemoryBufferRef(toStringRef(data_sp->GetData()), "minidump")); + if (!ExpectedFile) + return ExpectedFile.takeError(); + + return MinidumpParser(data_sp, std::move(*ExpectedFile)); +} + +MinidumpParser::MinidumpParser(lldb::DataBufferSP data_sp, + std::unique_ptr<llvm::object::MinidumpFile> file) + : m_data_sp(std::move(data_sp)), m_file(std::move(file)) {} + +llvm::ArrayRef<uint8_t> MinidumpParser::GetData() { + return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes(), + m_data_sp->GetByteSize()); +} + +llvm::ArrayRef<uint8_t> MinidumpParser::GetStream(StreamType stream_type) { + return m_file->getRawStream(stream_type).value_or(llvm::ArrayRef<uint8_t>()); +} + +UUID MinidumpParser::GetModuleUUID(const minidump::Module *module) { + auto cv_record = + GetData().slice(module->CvRecord.RVA, module->CvRecord.DataSize); + + // Read the CV record signature + const llvm::support::ulittle32_t *signature = nullptr; + Status error = consumeObject(cv_record, signature); + if (error.Fail()) + return UUID(); + + const CvSignature cv_signature = + static_cast<CvSignature>(static_cast<uint32_t>(*signature)); + + if (cv_signature == CvSignature::Pdb70) { + const UUID::CvRecordPdb70 *pdb70_uuid = nullptr; + Status error = consumeObject(cv_record, pdb70_uuid); + if (error.Fail()) + return UUID(); + if (GetArchitecture().GetTriple().isOSBinFormatELF()) { + if (pdb70_uuid->Age != 0) + return UUID(pdb70_uuid, sizeof(*pdb70_uuid)); + return UUID(&pdb70_uuid->Uuid, + sizeof(pdb70_uuid->Uuid)); + } + return UUID(*pdb70_uuid); + } else if (cv_signature == CvSignature::ElfBuildId) + return UUID(cv_record); + + return UUID(); +} + +llvm::ArrayRef<minidump::Thread> MinidumpParser::GetThreads() { + auto ExpectedThreads = GetMinidumpFile().getThreadList(); + if (ExpectedThreads) + return *ExpectedThreads; + + LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), ExpectedThreads.takeError(), + "Failed to read thread list: {0}"); + return {}; +} + +llvm::ArrayRef<uint8_t> +MinidumpParser::GetThreadContext(const LocationDescriptor &location) { + if (location.RVA + location.DataSize > GetData().size()) + return {}; + return GetData().slice(location.RVA, location.DataSize); +} + +llvm::ArrayRef<uint8_t> +MinidumpParser::GetThreadContext(const minidump::Thread &td) { + return GetThreadContext(td.Context); +} + +llvm::ArrayRef<uint8_t> +MinidumpParser::GetThreadContextWow64(const minidump::Thread &td) { + // On Windows, a 32-bit process can run on a 64-bit machine under WOW64. If + // the minidump was captured with a 64-bit debugger, then the CONTEXT we just + // grabbed from the mini_dump_thread is the one for the 64-bit "native" + // process rather than the 32-bit "guest" process we care about. In this + // case, we can get the 32-bit CONTEXT from the TEB (Thread Environment + // Block) of the 64-bit process. + auto teb_mem = GetMemory(td.EnvironmentBlock, sizeof(TEB64)); + if (teb_mem.empty()) + return {}; + + const TEB64 *wow64teb; + Status error = consumeObject(teb_mem, wow64teb); + if (error.Fail()) + return {}; + + // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure + // that includes the 32-bit CONTEXT (after a ULONG). See: + // https://msdn.microsoft.com/en-us/library/ms681670.aspx + auto context = + GetMemory(wow64teb->tls_slots[1] + 4, sizeof(MinidumpContext_x86_32)); + if (context.size() < sizeof(MinidumpContext_x86_32)) + return {}; + + return context; + // NOTE: We don't currently use the TEB for anything else. If we + // need it in the future, the 32-bit TEB is located according to the address + // stored in the first slot of the 64-bit TEB (wow64teb.Reserved1[0]). +} + +ArchSpec MinidumpParser::GetArchitecture() { + if (m_arch.IsValid()) + return m_arch; + + // Set the architecture in m_arch + llvm::Expected<const SystemInfo &> system_info = m_file->getSystemInfo(); + + if (!system_info) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Process), system_info.takeError(), + "Failed to read SystemInfo stream: {0}"); + return m_arch; + } + + // TODO what to do about big endiand flavors of arm ? + // TODO set the arm subarch stuff if the minidump has info about it + + llvm::Triple triple; + triple.setVendor(llvm::Triple::VendorType::UnknownVendor); + + switch (system_info->ProcessorArch) { + case ProcessorArchitecture::X86: + triple.setArch(llvm::Triple::ArchType::x86); + break; + case ProcessorArchitecture::AMD64: + triple.setArch(llvm::Triple::ArchType::x86_64); + break; + case ProcessorArchitecture::ARM: + triple.setArch(llvm::Triple::ArchType::arm); + break; + case ProcessorArchitecture::ARM64: + case ProcessorArchitecture::BP_ARM64: + triple.setArch(llvm::Triple::ArchType::aarch64); + break; + default: + triple.setArch(llvm::Triple::ArchType::UnknownArch); + break; + } + + // TODO add all of the OSes that Minidump/breakpad distinguishes? + switch (system_info->PlatformId) { + case OSPlatform::Win32S: + case OSPlatform::Win32Windows: + case OSPlatform::Win32NT: + case OSPlatform::Win32CE: + triple.setOS(llvm::Triple::OSType::Win32); + triple.setVendor(llvm::Triple::VendorType::PC); + break; + case OSPlatform::Linux: + triple.setOS(llvm::Triple::OSType::Linux); + break; + case OSPlatform::MacOSX: + triple.setOS(llvm::Triple::OSType::MacOSX); + triple.setVendor(llvm::Triple::Apple); + break; + case OSPlatform::IOS: + triple.setOS(llvm::Triple::OSType::IOS); + triple.setVendor(llvm::Triple::Apple); + break; + case OSPlatform::Android: + triple.setOS(llvm::Triple::OSType::Linux); + triple.setEnvironment(llvm::Triple::EnvironmentType::Android); + break; + default: { + triple.setOS(llvm::Triple::OSType::UnknownOS); + auto ExpectedCSD = m_file->getString(system_info->CSDVersionRVA); + if (!ExpectedCSD) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Process), ExpectedCSD.takeError(), + "Failed to CSD Version string: {0}"); + } else { + if (ExpectedCSD->find("Linux") != std::string::npos) + triple.setOS(llvm::Triple::OSType::Linux); + } + break; + } + } + m_arch.SetTriple(triple); + return m_arch; +} + +const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() { + llvm::ArrayRef<uint8_t> data = GetStream(StreamType::MiscInfo); + + if (data.size() == 0) + return nullptr; + + return MinidumpMiscInfo::Parse(data); +} + +std::optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() { + llvm::ArrayRef<uint8_t> data = GetStream(StreamType::LinuxProcStatus); + + if (data.size() == 0) + return std::nullopt; + + return LinuxProcStatus::Parse(data); +} + +std::optional<lldb::pid_t> MinidumpParser::GetPid() { + const MinidumpMiscInfo *misc_info = GetMiscInfo(); + if (misc_info != nullptr) { + return misc_info->GetPid(); + } + + std::optional<LinuxProcStatus> proc_status = GetLinuxProcStatus(); + if (proc_status) { + return proc_status->GetPid(); + } + + return std::nullopt; +} + +llvm::ArrayRef<minidump::Module> MinidumpParser::GetModuleList() { + auto ExpectedModules = GetMinidumpFile().getModuleList(); + if (ExpectedModules) + return *ExpectedModules; + + LLDB_LOG_ERROR(GetLog(LLDBLog::Modules), ExpectedModules.takeError(), + "Failed to read module list: {0}"); + return {}; +} + +static bool +CreateRegionsCacheFromLinuxMaps(MinidumpParser &parser, + std::vector<MemoryRegionInfo> ®ions) { + auto data = parser.GetStream(StreamType::LinuxMaps); + if (data.empty()) + return false; + + Log *log = GetLog(LLDBLog::Expressions); + ParseLinuxMapRegions( + llvm::toStringRef(data), + [®ions, &log](llvm::Expected<MemoryRegionInfo> region) -> bool { + if (region) + regions.push_back(*region); + else + LLDB_LOG_ERROR(log, region.takeError(), + "Reading memory region from minidump failed: {0}"); + return true; + }); + return !regions.empty(); +} + +/// Check for the memory regions starting at \a load_addr for a contiguous +/// section that has execute permissions that matches the module path. +/// +/// When we load a breakpad generated minidump file, we might have the +/// /proc/<pid>/maps text for a process that details the memory map of the +/// process that the minidump is describing. This checks the sorted memory +/// regions for a section that has execute permissions. A sample maps files +/// might look like: +/// +/// 00400000-00401000 r--p 00000000 fd:01 2838574 /tmp/a.out +/// 00401000-00402000 r-xp 00001000 fd:01 2838574 /tmp/a.out +/// 00402000-00403000 r--p 00002000 fd:01 2838574 /tmp/a.out +/// 00403000-00404000 r--p 00002000 fd:01 2838574 /tmp/a.out +/// 00404000-00405000 rw-p 00003000 fd:01 2838574 /tmp/a.out +/// ... +/// +/// This function should return true when given 0x00400000 and "/tmp/a.out" +/// is passed in as the path since it has a consecutive memory region for +/// "/tmp/a.out" that has execute permissions at 0x00401000. This will help us +/// differentiate if a file has been memory mapped into a process for reading +/// and breakpad ends up saving a minidump file that has two module entries for +/// a given file: one that is read only for the entire file, and then one that +/// is the real executable that is loaded into memory for execution. For memory +/// mapped files they will typically show up and r--p permissions and a range +/// matcning the entire range of the file on disk: +/// +/// 00800000-00805000 r--p 00000000 fd:01 2838574 /tmp/a.out +/// 00805000-00806000 r-xp 00001000 fd:01 1234567 /usr/lib/libc.so +/// +/// This function should return false when asked about 0x00800000 with +/// "/tmp/a.out" as the path. +/// +/// \param[in] path +/// The path to the module to check for in the memory regions. Only sequential +/// memory regions whose paths match this path will be considered when looking +/// for execute permissions. +/// +/// \param[in] regions +/// A sorted list of memory regions obtained from a call to +/// CreateRegionsCacheFromLinuxMaps. +/// +/// \param[in] base_of_image +/// The load address of this module from BaseOfImage in the modules list. +/// +/// \return +/// True if a contiguous region of memory belonging to the module with a +/// matching path exists that has executable permissions. Returns false if +/// \a regions is empty or if there are no regions with execute permissions +/// that match \a path. + +static bool CheckForLinuxExecutable(ConstString path, + const MemoryRegionInfos ®ions, + lldb::addr_t base_of_image) { + if (regions.empty()) + return false; + lldb::addr_t addr = base_of_image; + MemoryRegionInfo region = MinidumpParser::GetMemoryRegionInfo(regions, addr); + while (region.GetName() == path) { + if (region.GetExecutable() == MemoryRegionInfo::eYes) + return true; + addr += region.GetRange().GetByteSize(); + region = MinidumpParser::GetMemoryRegionInfo(regions, addr); + } + return false; +} + +std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() { + Log *log = GetLog(LLDBLog::Modules); + auto ExpectedModules = GetMinidumpFile().getModuleList(); + if (!ExpectedModules) { + LLDB_LOG_ERROR(log, ExpectedModules.takeError(), + "Failed to read module list: {0}"); + return {}; + } + + // Create memory regions from the linux maps only. We do this to avoid issues + // with breakpad generated minidumps where if someone has mmap'ed a shared + // library into memory to access its data in the object file, we can get a + // minidump with two mappings for a binary: one whose base image points to a + // memory region that is read + execute and one that is read only. + MemoryRegionInfos linux_regions; + if (CreateRegionsCacheFromLinuxMaps(*this, linux_regions)) + llvm::sort(linux_regions); + + // map module_name -> filtered_modules index + typedef llvm::StringMap<size_t> MapType; + MapType module_name_to_filtered_index; + + std::vector<const minidump::Module *> filtered_modules; + + for (const auto &module : *ExpectedModules) { + auto ExpectedName = m_file->getString(module.ModuleNameRVA); + if (!ExpectedName) { + LLDB_LOG_ERROR(log, ExpectedName.takeError(), + "Failed to get module name: {0}"); + continue; + } + + MapType::iterator iter; + bool inserted; + // See if we have inserted this module aready into filtered_modules. If we + // haven't insert an entry into module_name_to_filtered_index with the + // index where we will insert it if it isn't in the vector already. + std::tie(iter, inserted) = module_name_to_filtered_index.try_emplace( + *ExpectedName, filtered_modules.size()); + + if (inserted) { + // This module has not been seen yet, insert it into filtered_modules at + // the index that was inserted into module_name_to_filtered_index using + // "filtered_modules.size()" above. + filtered_modules.push_back(&module); + } else { + // We have a duplicate module entry. Check the linux regions to see if + // either module is not really a mapped executable. If one but not the + // other is a real mapped executable, prefer the executable one. This + // can happen when a process mmap's in the file for an executable in + // order to read bytes from the executable file. A memory region mapping + // will exist for the mmap'ed version and for the loaded executable, but + // only one will have a consecutive region that is executable in the + // memory regions. + auto dup_module = filtered_modules[iter->second]; + ConstString name(*ExpectedName); + bool is_executable = + CheckForLinuxExecutable(name, linux_regions, module.BaseOfImage); + bool dup_is_executable = + CheckForLinuxExecutable(name, linux_regions, dup_module->BaseOfImage); + + if (is_executable != dup_is_executable) { + if (is_executable) + filtered_modules[iter->second] = &module; + continue; + } + // This module has been seen. Modules are sometimes mentioned multiple + // times when they are mapped discontiguously, so find the module with + // the lowest "base_of_image" and use that as the filtered module. + if (module.BaseOfImage < dup_module->BaseOfImage) + filtered_modules[iter->second] = &module; + } + } + return filtered_modules; +} + +const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() { + auto ExpectedStream = GetMinidumpFile().getExceptionStream(); + if (ExpectedStream) + return &*ExpectedStream; + + LLDB_LOG_ERROR(GetLog(LLDBLog::Process), ExpectedStream.takeError(), + "Failed to read minidump exception stream: {0}"); + return nullptr; +} + +std::optional<minidump::Range> +MinidumpParser::FindMemoryRange(lldb::addr_t addr) { + llvm::ArrayRef<uint8_t> data64 = GetStream(StreamType::Memory64List); + Log *log = GetLog(LLDBLog::Modules); + + auto ExpectedMemory = GetMinidumpFile().getMemoryList(); + if (!ExpectedMemory) { + LLDB_LOG_ERROR(log, ExpectedMemory.takeError(), + "Failed to read memory list: {0}"); + } else { + for (const auto &memory_desc : *ExpectedMemory) { + const LocationDescriptor &loc_desc = memory_desc.Memory; + const lldb::addr_t range_start = memory_desc.StartOfMemoryRange; + const size_t range_size = loc_desc.DataSize; + + if (loc_desc.RVA + loc_desc.DataSize > GetData().size()) + return std::nullopt; + + if (range_start <= addr && addr < range_start + range_size) { + auto ExpectedSlice = GetMinidumpFile().getRawData(loc_desc); + if (!ExpectedSlice) { + LLDB_LOG_ERROR(log, ExpectedSlice.takeError(), + "Failed to get memory slice: {0}"); + return std::nullopt; + } + return minidump::Range(range_start, *ExpectedSlice); + } + } + } + + // Some Minidumps have a Memory64ListStream that captures all the heap memory + // (full-memory Minidumps). We can't exactly use the same loop as above, + // because the Minidump uses slightly different data structures to describe + // those + + if (!data64.empty()) { + llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list; + uint64_t base_rva; + std::tie(memory64_list, base_rva) = + MinidumpMemoryDescriptor64::ParseMemory64List(data64); + + if (memory64_list.empty()) + return std::nullopt; + + for (const auto &memory_desc64 : memory64_list) { + const lldb::addr_t range_start = memory_desc64.start_of_memory_range; + const size_t range_size = memory_desc64.data_size; + + if (base_rva + range_size > GetData().size()) + return std::nullopt; + + if (range_start <= addr && addr < range_start + range_size) { + return minidump::Range(range_start, + GetData().slice(base_rva, range_size)); + } + base_rva += range_size; + } + } + + return std::nullopt; +} + +llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr, + size_t size) { + // I don't have a sense of how frequently this is called or how many memory + // ranges a Minidump typically has, so I'm not sure if searching for the + // appropriate range linearly each time is stupid. Perhaps we should build + // an index for faster lookups. + std::optional<minidump::Range> range = FindMemoryRange(addr); + if (!range) + return {}; + + // There's at least some overlap between the beginning of the desired range + // (addr) and the current range. Figure out where the overlap begins and how + // much overlap there is. + + const size_t offset = addr - range->start; + + if (addr < range->start || offset >= range->range_ref.size()) + return {}; + + const size_t overlap = std::min(size, range->range_ref.size() - offset); + return range->range_ref.slice(offset, overlap); +} + +static bool +CreateRegionsCacheFromMemoryInfoList(MinidumpParser &parser, + std::vector<MemoryRegionInfo> ®ions) { + Log *log = GetLog(LLDBLog::Modules); + auto ExpectedInfo = parser.GetMinidumpFile().getMemoryInfoList(); + if (!ExpectedInfo) { + LLDB_LOG_ERROR(log, ExpectedInfo.takeError(), + "Failed to read memory info list: {0}"); + return false; + } + constexpr auto yes = MemoryRegionInfo::eYes; + constexpr auto no = MemoryRegionInfo::eNo; + for (const MemoryInfo &entry : *ExpectedInfo) { + MemoryRegionInfo region; + region.GetRange().SetRangeBase(entry.BaseAddress); + region.GetRange().SetByteSize(entry.RegionSize); + + MemoryProtection prot = entry.Protect; + region.SetReadable(bool(prot & MemoryProtection::NoAccess) ? no : yes); + region.SetWritable( + bool(prot & (MemoryProtection::ReadWrite | MemoryProtection::WriteCopy | + MemoryProtection::ExecuteReadWrite | + MemoryProtection::ExeciteWriteCopy)) + ? yes + : no); + region.SetExecutable( + bool(prot & (MemoryProtection::Execute | MemoryProtection::ExecuteRead | + MemoryProtection::ExecuteReadWrite | + MemoryProtection::ExeciteWriteCopy)) + ? yes + : no); + region.SetMapped(entry.State != MemoryState::Free ? yes : no); + regions.push_back(region); + } + return !regions.empty(); +} + +static bool +CreateRegionsCacheFromMemoryList(MinidumpParser &parser, + std::vector<MemoryRegionInfo> ®ions) { + Log *log = GetLog(LLDBLog::Modules); + auto ExpectedMemory = parser.GetMinidumpFile().getMemoryList(); + if (!ExpectedMemory) { + LLDB_LOG_ERROR(log, ExpectedMemory.takeError(), + "Failed to read memory list: {0}"); + return false; + } + regions.reserve(ExpectedMemory->size()); + for (const MemoryDescriptor &memory_desc : *ExpectedMemory) { + if (memory_desc.Memory.DataSize == 0) + continue; + MemoryRegionInfo region; + region.GetRange().SetRangeBase(memory_desc.StartOfMemoryRange); + region.GetRange().SetByteSize(memory_desc.Memory.DataSize); + region.SetReadable(MemoryRegionInfo::eYes); + region.SetMapped(MemoryRegionInfo::eYes); + regions.push_back(region); + } + regions.shrink_to_fit(); + return !regions.empty(); +} + +static bool +CreateRegionsCacheFromMemory64List(MinidumpParser &parser, + std::vector<MemoryRegionInfo> ®ions) { + llvm::ArrayRef<uint8_t> data = + parser.GetStream(StreamType::Memory64List); + if (data.empty()) + return false; + llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list; + uint64_t base_rva; + std::tie(memory64_list, base_rva) = + MinidumpMemoryDescriptor64::ParseMemory64List(data); + + if (memory64_list.empty()) + return false; + + regions.reserve(memory64_list.size()); + for (const auto &memory_desc : memory64_list) { + if (memory_desc.data_size == 0) + continue; + MemoryRegionInfo region; + region.GetRange().SetRangeBase(memory_desc.start_of_memory_range); + region.GetRange().SetByteSize(memory_desc.data_size); + region.SetReadable(MemoryRegionInfo::eYes); + region.SetMapped(MemoryRegionInfo::eYes); + regions.push_back(region); + } + regions.shrink_to_fit(); + return !regions.empty(); +} + +std::pair<MemoryRegionInfos, bool> MinidumpParser::BuildMemoryRegions() { + // We create the region cache using the best source. We start with + // the linux maps since they are the most complete and have names for the + // regions. Next we try the MemoryInfoList since it has + // read/write/execute/map data, and then fall back to the MemoryList and + // Memory64List to just get a list of the memory that is mapped in this + // core file + MemoryRegionInfos result; + const auto &return_sorted = [&](bool is_complete) { + llvm::sort(result); + return std::make_pair(std::move(result), is_complete); + }; + if (CreateRegionsCacheFromLinuxMaps(*this, result)) + return return_sorted(true); + if (CreateRegionsCacheFromMemoryInfoList(*this, result)) + return return_sorted(true); + if (CreateRegionsCacheFromMemoryList(*this, result)) + return return_sorted(false); + CreateRegionsCacheFromMemory64List(*this, result); + return return_sorted(false); +} + +#define ENUM_TO_CSTR(ST) \ + case StreamType::ST: \ + return #ST + +llvm::StringRef +MinidumpParser::GetStreamTypeAsString(StreamType stream_type) { + switch (stream_type) { + ENUM_TO_CSTR(Unused); + ENUM_TO_CSTR(ThreadList); + ENUM_TO_CSTR(ModuleList); + ENUM_TO_CSTR(MemoryList); + ENUM_TO_CSTR(Exception); + ENUM_TO_CSTR(SystemInfo); + ENUM_TO_CSTR(ThreadExList); + ENUM_TO_CSTR(Memory64List); + ENUM_TO_CSTR(CommentA); + ENUM_TO_CSTR(CommentW); + ENUM_TO_CSTR(HandleData); + ENUM_TO_CSTR(FunctionTable); + ENUM_TO_CSTR(UnloadedModuleList); + ENUM_TO_CSTR(MiscInfo); + ENUM_TO_CSTR(MemoryInfoList); + ENUM_TO_CSTR(ThreadInfoList); + ENUM_TO_CSTR(HandleOperationList); + ENUM_TO_CSTR(Token); + ENUM_TO_CSTR(JavascriptData); + ENUM_TO_CSTR(SystemMemoryInfo); + ENUM_TO_CSTR(ProcessVMCounters); + ENUM_TO_CSTR(LastReserved); + ENUM_TO_CSTR(BreakpadInfo); + ENUM_TO_CSTR(AssertionInfo); + ENUM_TO_CSTR(LinuxCPUInfo); + ENUM_TO_CSTR(LinuxProcStatus); + ENUM_TO_CSTR(LinuxLSBRelease); + ENUM_TO_CSTR(LinuxCMDLine); + ENUM_TO_CSTR(LinuxEnviron); + ENUM_TO_CSTR(LinuxAuxv); + ENUM_TO_CSTR(LinuxMaps); + ENUM_TO_CSTR(LinuxDSODebug); + ENUM_TO_CSTR(LinuxProcStat); + ENUM_TO_CSTR(LinuxProcUptime); + ENUM_TO_CSTR(LinuxProcFD); + ENUM_TO_CSTR(FacebookAppCustomData); + ENUM_TO_CSTR(FacebookBuildID); + ENUM_TO_CSTR(FacebookAppVersionName); + ENUM_TO_CSTR(FacebookJavaStack); + ENUM_TO_CSTR(FacebookDalvikInfo); + ENUM_TO_CSTR(FacebookUnwindSymbols); + ENUM_TO_CSTR(FacebookDumpErrorLog); + ENUM_TO_CSTR(FacebookAppStateLog); + ENUM_TO_CSTR(FacebookAbortReason); + ENUM_TO_CSTR(FacebookThreadName); + ENUM_TO_CSTR(FacebookLogcat); + } + return "unknown stream type"; +} + +MemoryRegionInfo +MinidumpParser::GetMemoryRegionInfo(const MemoryRegionInfos ®ions, + lldb::addr_t load_addr) { + MemoryRegionInfo region; + auto pos = llvm::upper_bound(regions, load_addr); + if (pos != regions.begin() && + std::prev(pos)->GetRange().Contains(load_addr)) { + return *std::prev(pos); + } + + if (pos == regions.begin()) + region.GetRange().SetRangeBase(0); + else + region.GetRange().SetRangeBase(std::prev(pos)->GetRange().GetRangeEnd()); + + if (pos == regions.end()) + region.GetRange().SetRangeEnd(UINT64_MAX); + else + region.GetRange().SetRangeEnd(pos->GetRange().GetRangeBase()); + + region.SetReadable(MemoryRegionInfo::eNo); + region.SetWritable(MemoryRegionInfo::eNo); + region.SetExecutable(MemoryRegionInfo::eNo); + region.SetMapped(MemoryRegionInfo::eNo); + return region; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h new file mode 100644 index 000000000000..050ba086f46f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpParser.h @@ -0,0 +1,113 @@ +//===-- MinidumpParser.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_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPPARSER_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPPARSER_H + +#include "MinidumpTypes.h" + +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBuffer.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/UUID.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/Minidump.h" + +// C includes + +// C++ includes +#include <cstring> +#include <optional> +#include <unordered_map> + +namespace lldb_private { + +namespace minidump { + +// Describes a range of memory captured in the Minidump +struct Range { + lldb::addr_t start; // virtual address of the beginning of the range + // range_ref - absolute pointer to the first byte of the range and size + llvm::ArrayRef<uint8_t> range_ref; + + Range(lldb::addr_t start, llvm::ArrayRef<uint8_t> range_ref) + : start(start), range_ref(range_ref) {} + + friend bool operator==(const Range &lhs, const Range &rhs) { + return lhs.start == rhs.start && lhs.range_ref == rhs.range_ref; + } +}; + +class MinidumpParser { +public: + static llvm::Expected<MinidumpParser> + Create(const lldb::DataBufferSP &data_buf_sp); + + llvm::ArrayRef<uint8_t> GetData(); + + llvm::ArrayRef<uint8_t> GetStream(StreamType stream_type); + + UUID GetModuleUUID(const minidump::Module *module); + + llvm::ArrayRef<minidump::Thread> GetThreads(); + + llvm::ArrayRef<uint8_t> GetThreadContext(const LocationDescriptor &location); + + llvm::ArrayRef<uint8_t> GetThreadContext(const minidump::Thread &td); + + llvm::ArrayRef<uint8_t> GetThreadContextWow64(const minidump::Thread &td); + + ArchSpec GetArchitecture(); + + const MinidumpMiscInfo *GetMiscInfo(); + + std::optional<LinuxProcStatus> GetLinuxProcStatus(); + + std::optional<lldb::pid_t> GetPid(); + + llvm::ArrayRef<minidump::Module> GetModuleList(); + + // There are cases in which there is more than one record in the ModuleList + // for the same module name.(e.g. when the binary has non contiguous segments) + // So this function returns a filtered module list - if it finds records that + // have the same name, it keeps the copy with the lowest load address. + std::vector<const minidump::Module *> GetFilteredModuleList(); + + const llvm::minidump::ExceptionStream *GetExceptionStream(); + + std::optional<Range> FindMemoryRange(lldb::addr_t addr); + + llvm::ArrayRef<uint8_t> GetMemory(lldb::addr_t addr, size_t size); + + /// Returns a list of memory regions and a flag indicating whether the list is + /// complete (includes all regions mapped into the process memory). + std::pair<MemoryRegionInfos, bool> BuildMemoryRegions(); + + static llvm::StringRef GetStreamTypeAsString(StreamType stream_type); + + llvm::object::MinidumpFile &GetMinidumpFile() { return *m_file; } + + static MemoryRegionInfo GetMemoryRegionInfo(const MemoryRegionInfos ®ions, + lldb::addr_t load_addr); + +private: + MinidumpParser(lldb::DataBufferSP data_sp, + std::unique_ptr<llvm::object::MinidumpFile> file); + + lldb::DataBufferSP m_data_sp; + std::unique_ptr<llvm::object::MinidumpFile> m_file; + ArchSpec m_arch; +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPPARSER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp new file mode 100644 index 000000000000..5b919828428f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp @@ -0,0 +1,79 @@ +//===-- MinidumpTypes.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 "MinidumpTypes.h" +#include <optional> + +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +// MinidumpMiscInfo +const MinidumpMiscInfo *MinidumpMiscInfo::Parse(llvm::ArrayRef<uint8_t> &data) { + const MinidumpMiscInfo *misc_info; + Status error = consumeObject(data, misc_info); + if (error.Fail()) + return nullptr; + + return misc_info; +} + +std::optional<lldb::pid_t> MinidumpMiscInfo::GetPid() const { + uint32_t pid_flag = static_cast<uint32_t>(MinidumpMiscInfoFlags::ProcessID); + if (flags1 & pid_flag) + return std::optional<lldb::pid_t>(process_id); + + return std::nullopt; +} + +// Linux Proc Status +// it's stored as an ascii string in the file +std::optional<LinuxProcStatus> +LinuxProcStatus::Parse(llvm::ArrayRef<uint8_t> &data) { + LinuxProcStatus result; + result.proc_status = + llvm::StringRef(reinterpret_cast<const char *>(data.data()), data.size()); + data = data.drop_front(data.size()); + + llvm::SmallVector<llvm::StringRef, 0> lines; + result.proc_status.split(lines, '\n', 42); + // /proc/$pid/status has 41 lines, but why not use 42? + for (auto line : lines) { + if (line.consume_front("Pid:")) { + line = line.trim(); + if (!line.getAsInteger(10, result.pid)) + return result; + } + } + + return std::nullopt; +} + +lldb::pid_t LinuxProcStatus::GetPid() const { return pid; } + +std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t> +MinidumpMemoryDescriptor64::ParseMemory64List(llvm::ArrayRef<uint8_t> &data) { + const llvm::support::ulittle64_t *mem_ranges_count; + Status error = consumeObject(data, mem_ranges_count); + if (error.Fail() || + *mem_ranges_count * sizeof(MinidumpMemoryDescriptor64) > data.size()) + return {}; + + const llvm::support::ulittle64_t *base_rva; + error = consumeObject(data, base_rva); + if (error.Fail()) + return {}; + + return std::make_pair( + llvm::ArrayRef( + reinterpret_cast<const MinidumpMemoryDescriptor64 *>(data.data()), + *mem_ranges_count), + *base_rva); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h new file mode 100644 index 000000000000..fe99abf9e24e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/MinidumpTypes.h @@ -0,0 +1,107 @@ +//===-- MinidumpTypes.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_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H + +#include "lldb/Utility/Status.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Minidump.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Endian.h" +#include <optional> + +// C includes +// C++ includes + +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms679293(v=vs.85).aspx +// https://chromium.googlesource.com/breakpad/breakpad/ + +namespace lldb_private { + +namespace minidump { + +using namespace llvm::minidump; + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +enum class CvSignature : uint32_t { + Pdb70 = 0x53445352, // RSDS + ElfBuildId = 0x4270454c, // BpEL (Breakpad/Crashpad minidumps) +}; + +enum class MinidumpMiscInfoFlags : uint32_t { + ProcessID = (1 << 0), + ProcessTimes = (1 << 1), + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ ProcessTimes) +}; + +template <typename T> +Status consumeObject(llvm::ArrayRef<uint8_t> &Buffer, const T *&Object) { + Status error; + if (Buffer.size() < sizeof(T)) { + error.SetErrorString("Insufficient buffer!"); + return error; + } + + Object = reinterpret_cast<const T *>(Buffer.data()); + Buffer = Buffer.drop_front(sizeof(T)); + return error; +} + +struct MinidumpMemoryDescriptor64 { + llvm::support::ulittle64_t start_of_memory_range; + llvm::support::ulittle64_t data_size; + + static std::pair<llvm::ArrayRef<MinidumpMemoryDescriptor64>, uint64_t> + ParseMemory64List(llvm::ArrayRef<uint8_t> &data); +}; +static_assert(sizeof(MinidumpMemoryDescriptor64) == 16, + "sizeof MinidumpMemoryDescriptor64 is not correct!"); + +// TODO misc2, misc3 ? +// Reference: +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680389(v=vs.85).aspx +struct MinidumpMiscInfo { + llvm::support::ulittle32_t size; + // flags1 represents what info in the struct is valid + llvm::support::ulittle32_t flags1; + llvm::support::ulittle32_t process_id; + llvm::support::ulittle32_t process_create_time; + llvm::support::ulittle32_t process_user_time; + llvm::support::ulittle32_t process_kernel_time; + + static const MinidumpMiscInfo *Parse(llvm::ArrayRef<uint8_t> &data); + + std::optional<lldb::pid_t> GetPid() const; +}; +static_assert(sizeof(MinidumpMiscInfo) == 24, + "sizeof MinidumpMiscInfo is not correct!"); + +// The /proc/pid/status is saved as an ascii string in the file +class LinuxProcStatus { +public: + llvm::StringRef proc_status; + lldb::pid_t pid; + + static std::optional<LinuxProcStatus> Parse(llvm::ArrayRef<uint8_t> &data); + + lldb::pid_t GetPid() const; + +private: + LinuxProcStatus() = default; +}; + +} // namespace minidump +} // namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_MINIDUMPTYPES_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h new file mode 100644 index 000000000000..1dafbe4a4f50 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/NtStructures.h @@ -0,0 +1,42 @@ +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_NTSTRUCTURES_H + +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_NTSTRUCTURES_H + +//===-- NtStructures.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_Plugins_Process_Minidump_NtStructures_h_ +#define liblldb_Plugins_Process_Minidump_NtStructures_h_ + +#include "llvm/Support/Endian.h" + +namespace lldb_private { + +namespace minidump { + +// This describes the layout of a TEB (Thread Environment Block) for a 64-bit +// process. It's adapted from the 32-bit TEB in winternl.h. Currently, we care +// only about the position of the tls_slots. +struct TEB64 { + llvm::support::ulittle64_t reserved1[12]; + llvm::support::ulittle64_t process_environment_block; + llvm::support::ulittle64_t reserved2[399]; + uint8_t reserved3[1952]; + llvm::support::ulittle64_t tls_slots[64]; + uint8_t reserved4[8]; + llvm::support::ulittle64_t reserved5[26]; + llvm::support::ulittle64_t reserved_for_ole; // Windows 2000 only + llvm::support::ulittle64_t reserved6[4]; + llvm::support::ulittle64_t tls_expansion_slots; +}; + +#endif // liblldb_Plugins_Process_Minidump_NtStructures_h_ +} // namespace minidump +} // namespace lldb_private + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp new file mode 100644 index 000000000000..13599f4a1553 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -0,0 +1,929 @@ +//===-- ProcessMinidump.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 "ProcessMinidump.h" + +#include "ThreadMinidump.h" + +#include "lldb/Core/DumpDataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupBoolean.h" +#include "lldb/Target/JITLoaderList.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Threading.h" + +#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h" +#include "Plugins/Process/Utility/StopInfoMachException.h" + +#include <memory> +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +LLDB_PLUGIN_DEFINE(ProcessMinidump) + +namespace { + +/// Duplicate the HashElfTextSection() from the breakpad sources. +/// +/// Breakpad, a Google crash log reporting tool suite, creates minidump files +/// for many different architectures. When using Breakpad to create ELF +/// minidumps, it will check for a GNU build ID when creating a minidump file +/// and if one doesn't exist in the file, it will say the UUID of the file is a +/// checksum of up to the first 4096 bytes of the .text section. Facebook also +/// uses breakpad and modified this hash to avoid collisions so we can +/// calculate and check for this as well. +/// +/// The breakpad code might end up hashing up to 15 bytes that immediately +/// follow the .text section in the file, so this code must do exactly what it +/// does so we can get an exact match for the UUID. +/// +/// \param[in] module_sp The module to grab the .text section from. +/// +/// \param[in,out] breakpad_uuid A vector that will receive the calculated +/// breakpad .text hash. +/// +/// \param[in,out] facebook_uuid A vector that will receive the calculated +/// facebook .text hash. +/// +void HashElfTextSection(ModuleSP module_sp, std::vector<uint8_t> &breakpad_uuid, + std::vector<uint8_t> &facebook_uuid) { + SectionList *sect_list = module_sp->GetSectionList(); + if (sect_list == nullptr) + return; + SectionSP sect_sp = sect_list->FindSectionByName(ConstString(".text")); + if (!sect_sp) + return; + constexpr size_t kMDGUIDSize = 16; + constexpr size_t kBreakpadPageSize = 4096; + // The breakpad code has a bug where it might access beyond the end of a + // .text section by up to 15 bytes, so we must ensure we round up to the + // next kMDGUIDSize byte boundary. + DataExtractor data; + const size_t text_size = sect_sp->GetFileSize(); + const size_t read_size = std::min<size_t>( + llvm::alignTo(text_size, kMDGUIDSize), kBreakpadPageSize); + sect_sp->GetObjectFile()->GetData(sect_sp->GetFileOffset(), read_size, data); + + breakpad_uuid.assign(kMDGUIDSize, 0); + facebook_uuid.assign(kMDGUIDSize, 0); + + // The only difference between the breakpad hash and the facebook hash is the + // hashing of the text section size into the hash prior to hashing the .text + // contents. + for (size_t i = 0; i < kMDGUIDSize; i++) + facebook_uuid[i] ^= text_size % 255; + + // This code carefully duplicates how the hash was created in Breakpad + // sources, including the error where it might has an extra 15 bytes past the + // end of the .text section if the .text section is less than a page size in + // length. + const uint8_t *ptr = data.GetDataStart(); + const uint8_t *ptr_end = data.GetDataEnd(); + while (ptr < ptr_end) { + for (unsigned i = 0; i < kMDGUIDSize; i++) { + breakpad_uuid[i] ^= ptr[i]; + facebook_uuid[i] ^= ptr[i]; + } + ptr += kMDGUIDSize; + } +} + +} // namespace + +llvm::StringRef ProcessMinidump::GetPluginDescriptionStatic() { + return "Minidump plug-in."; +} + +lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file, + bool can_connect) { + if (!crash_file || can_connect) + return nullptr; + + lldb::ProcessSP process_sp; + // Read enough data for the Minidump header + constexpr size_t header_size = sizeof(Header); + auto DataPtr = FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), + header_size, 0); + if (!DataPtr) + return nullptr; + + lldbassert(DataPtr->GetByteSize() == header_size); + if (identify_magic(toStringRef(DataPtr->GetData())) != llvm::file_magic::minidump) + return nullptr; + + auto AllData = + FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), -1, 0); + if (!AllData) + return nullptr; + + return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file, + std::move(AllData)); +} + +bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + return true; +} + +ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec &core_file, + DataBufferSP core_data) + : PostMortemProcess(target_sp, listener_sp, core_file), + m_core_data(std::move(core_data)), m_active_exception(nullptr), + m_is_wow64(false) {} + +ProcessMinidump::~ProcessMinidump() { + Clear(); + // We need to call finalize on the process before destroying ourselves to + // make sure all of the broadcaster cleanup goes as planned. If we destruct + // this class, then Process::~Process() might have problems trying to fully + // destroy the broadcaster. + Finalize(true /* destructing */); +} + +void ProcessMinidump::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + ProcessMinidump::CreateInstance); + }); +} + +void ProcessMinidump::Terminate() { + PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance); +} + +Status ProcessMinidump::DoLoadCore() { + auto expected_parser = MinidumpParser::Create(m_core_data); + if (!expected_parser) + return Status(expected_parser.takeError()); + m_minidump_parser = std::move(*expected_parser); + + Status error; + + // Do we support the minidump's architecture? + ArchSpec arch = GetArchitecture(); + switch (arch.GetMachine()) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + case llvm::Triple::arm: + case llvm::Triple::aarch64: + // Any supported architectures must be listed here and also supported in + // ThreadMinidump::CreateRegisterContextForFrame(). + break; + default: + error.SetErrorStringWithFormat("unsupported minidump architecture: %s", + arch.GetArchitectureName()); + return error; + } + GetTarget().SetArchitecture(arch, true /*set_platform*/); + + m_thread_list = m_minidump_parser->GetThreads(); + m_active_exception = m_minidump_parser->GetExceptionStream(); + + SetUnixSignals(UnixSignals::Create(GetArchitecture())); + + ReadModuleList(); + if (ModuleSP module = GetTarget().GetExecutableModule()) + GetTarget().MergeArchitecture(module->GetArchitecture()); + std::optional<lldb::pid_t> pid = m_minidump_parser->GetPid(); + if (!pid) { + Debugger::ReportWarning("unable to retrieve process ID from minidump file, " + "setting process ID to 1", + GetTarget().GetDebugger().GetID()); + pid = 1; + } + SetID(*pid); + + return error; +} + +Status ProcessMinidump::DoDestroy() { return Status(); } + +void ProcessMinidump::RefreshStateAfterStop() { + + if (!m_active_exception) + return; + + constexpr uint32_t BreakpadDumpRequested = 0xFFFFFFFF; + if (m_active_exception->ExceptionRecord.ExceptionCode == + BreakpadDumpRequested) { + // This "ExceptionCode" value is a sentinel that is sometimes used + // when generating a dump for a process that hasn't crashed. + + // TODO: The definition and use of this "dump requested" constant + // in Breakpad are actually Linux-specific, and for similar use + // cases on Mac/Windows it defines different constants, referring + // to them as "simulated" exceptions; consider moving this check + // down to the OS-specific paths and checking each OS for its own + // constant. + return; + } + + lldb::StopInfoSP stop_info; + lldb::ThreadSP stop_thread; + + Process::m_thread_list.SetSelectedThreadByID(m_active_exception->ThreadId); + stop_thread = Process::m_thread_list.GetSelectedThread(); + ArchSpec arch = GetArchitecture(); + + if (arch.GetTriple().getOS() == llvm::Triple::Linux) { + uint32_t signo = m_active_exception->ExceptionRecord.ExceptionCode; + + if (signo == 0) { + // No stop. + return; + } + + stop_info = StopInfo::CreateStopReasonWithSignal( + *stop_thread, signo); + } else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) { + stop_info = StopInfoMachException::CreateStopReasonWithMachException( + *stop_thread, m_active_exception->ExceptionRecord.ExceptionCode, 2, + m_active_exception->ExceptionRecord.ExceptionFlags, + m_active_exception->ExceptionRecord.ExceptionAddress, 0); + } else { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " + << llvm::format_hex( + m_active_exception->ExceptionRecord.ExceptionCode, 8) + << " encountered at address " + << llvm::format_hex( + m_active_exception->ExceptionRecord.ExceptionAddress, 8); + stop_info = StopInfo::CreateStopReasonWithException( + *stop_thread, desc_stream.str().c_str()); + } + + stop_thread->SetStopInfo(stop_info); +} + +bool ProcessMinidump::IsAlive() { return true; } + +bool ProcessMinidump::WarnBeforeDetach() const { return false; } + +size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // we have it all cached in our dump file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + + llvm::ArrayRef<uint8_t> mem = m_minidump_parser->GetMemory(addr, size); + if (mem.empty()) { + error.SetErrorString("could not parse memory info"); + return 0; + } + + std::memcpy(buf, mem.data(), mem.size()); + return mem.size(); +} + +ArchSpec ProcessMinidump::GetArchitecture() { + if (!m_is_wow64) { + return m_minidump_parser->GetArchitecture(); + } + + llvm::Triple triple; + triple.setVendor(llvm::Triple::VendorType::UnknownVendor); + triple.setArch(llvm::Triple::ArchType::x86); + triple.setOS(llvm::Triple::OSType::Win32); + return ArchSpec(triple); +} + +void ProcessMinidump::BuildMemoryRegions() { + if (m_memory_regions) + return; + m_memory_regions.emplace(); + bool is_complete; + std::tie(*m_memory_regions, is_complete) = + m_minidump_parser->BuildMemoryRegions(); + + if (is_complete) + return; + + MemoryRegionInfos to_add; + ModuleList &modules = GetTarget().GetImages(); + SectionLoadList &load_list = GetTarget().GetSectionLoadList(); + modules.ForEach([&](const ModuleSP &module_sp) { + SectionList *sections = module_sp->GetSectionList(); + for (size_t i = 0; i < sections->GetSize(); ++i) { + SectionSP section_sp = sections->GetSectionAtIndex(i); + addr_t load_addr = load_list.GetSectionLoadAddress(section_sp); + if (load_addr == LLDB_INVALID_ADDRESS) + continue; + MemoryRegionInfo::RangeType section_range(load_addr, + section_sp->GetByteSize()); + MemoryRegionInfo region = + MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr); + if (region.GetMapped() != MemoryRegionInfo::eYes && + region.GetRange().GetRangeBase() <= section_range.GetRangeBase() && + section_range.GetRangeEnd() <= region.GetRange().GetRangeEnd()) { + to_add.emplace_back(); + to_add.back().GetRange() = section_range; + to_add.back().SetLLDBPermissions(section_sp->GetPermissions()); + to_add.back().SetMapped(MemoryRegionInfo::eYes); + to_add.back().SetName(module_sp->GetFileSpec().GetPath().c_str()); + } + } + return true; + }); + m_memory_regions->insert(m_memory_regions->end(), to_add.begin(), + to_add.end()); + llvm::sort(*m_memory_regions); +} + +Status ProcessMinidump::DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo ®ion) { + BuildMemoryRegions(); + region = MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr); + return Status(); +} + +Status ProcessMinidump::GetMemoryRegions(MemoryRegionInfos ®ion_list) { + BuildMemoryRegions(); + region_list = *m_memory_regions; + return Status(); +} + +void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); } + +bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) { + for (const minidump::Thread &thread : m_thread_list) { + LocationDescriptor context_location = thread.Context; + + // If the minidump contains an exception context, use it + if (m_active_exception != nullptr && + m_active_exception->ThreadId == thread.ThreadId) { + context_location = m_active_exception->ThreadContext; + } + + llvm::ArrayRef<uint8_t> context; + if (!m_is_wow64) + context = m_minidump_parser->GetThreadContext(context_location); + else + context = m_minidump_parser->GetThreadContextWow64(thread); + + lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context)); + new_thread_list.AddThread(thread_sp); + } + return new_thread_list.GetSize(false) > 0; +} + +ModuleSP ProcessMinidump::GetOrCreateModule(UUID minidump_uuid, + llvm::StringRef name, + ModuleSpec module_spec) { + Log *log = GetLog(LLDBLog::DynamicLoader); + Status error; + + ModuleSP module_sp = + GetTarget().GetOrCreateModule(module_spec, true /* notify */, &error); + if (!module_sp) + return module_sp; + // We consider the module to be a match if the minidump UUID is a + // prefix of the actual UUID, or if either of the UUIDs are empty. + const auto dmp_bytes = minidump_uuid.GetBytes(); + const auto mod_bytes = module_sp->GetUUID().GetBytes(); + const bool match = dmp_bytes.empty() || mod_bytes.empty() || + mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes; + if (match) { + LLDB_LOG(log, "Partial uuid match for {0}.", name); + return module_sp; + } + + // Breakpad generates minindump files, and if there is no GNU build + // ID in the binary, it will calculate a UUID by hashing first 4096 + // bytes of the .text section and using that as the UUID for a module + // in the minidump. Facebook uses a modified breakpad client that + // uses a slightly modified this hash to avoid collisions. Check for + // UUIDs from the minindump that match these cases and accept the + // module we find if they do match. + std::vector<uint8_t> breakpad_uuid; + std::vector<uint8_t> facebook_uuid; + HashElfTextSection(module_sp, breakpad_uuid, facebook_uuid); + if (dmp_bytes == llvm::ArrayRef<uint8_t>(breakpad_uuid)) { + LLDB_LOG(log, "Breakpad .text hash match for {0}.", name); + return module_sp; + } + if (dmp_bytes == llvm::ArrayRef<uint8_t>(facebook_uuid)) { + LLDB_LOG(log, "Facebook .text hash match for {0}.", name); + return module_sp; + } + // The UUID wasn't a partial match and didn't match the .text hash + // so remove the module from the target, we will need to create a + // placeholder object file. + GetTarget().GetImages().Remove(module_sp); + module_sp.reset(); + return module_sp; +} + +void ProcessMinidump::ReadModuleList() { + std::vector<const minidump::Module *> filtered_modules = + m_minidump_parser->GetFilteredModuleList(); + + Log *log = GetLog(LLDBLog::DynamicLoader); + + for (auto module : filtered_modules) { + std::string name = cantFail(m_minidump_parser->GetMinidumpFile().getString( + module->ModuleNameRVA)); + const uint64_t load_addr = module->BaseOfImage; + const uint64_t load_size = module->SizeOfImage; + LLDB_LOG(log, "found module: name: {0} {1:x10}-{2:x10} size: {3}", name, + load_addr, load_addr + load_size, load_size); + + // check if the process is wow64 - a 32 bit windows process running on a + // 64 bit windows + if (llvm::StringRef(name).ends_with_insensitive("wow64.dll")) { + m_is_wow64 = true; + } + + const auto uuid = m_minidump_parser->GetModuleUUID(module); + auto file_spec = FileSpec(name, GetArchitecture().GetTriple()); + ModuleSpec module_spec(file_spec, uuid); + module_spec.GetArchitecture() = GetArchitecture(); + Status error; + // Try and find a module with a full UUID that matches. This function will + // add the module to the target if it finds one. + lldb::ModuleSP module_sp = GetTarget().GetOrCreateModule(module_spec, + true /* notify */, &error); + if (module_sp) { + LLDB_LOG(log, "Full uuid match for {0}.", name); + } else { + // We couldn't find a module with an exactly-matching UUID. Sometimes + // a minidump UUID is only a partial match or is a hash. So try again + // without specifying the UUID, then again without specifying the + // directory if that fails. This will allow us to find modules with + // partial matches or hash UUIDs in user-provided sysroots or search + // directories (target.exec-search-paths). + ModuleSpec partial_module_spec = module_spec; + partial_module_spec.GetUUID().Clear(); + module_sp = GetOrCreateModule(uuid, name, partial_module_spec); + if (!module_sp) { + partial_module_spec.GetFileSpec().ClearDirectory(); + module_sp = GetOrCreateModule(uuid, name, partial_module_spec); + } + } + if (module_sp) { + // Watch out for place holder modules that have different paths, but the + // same UUID. If the base address is different, create a new module. If + // we don't then we will end up setting the load address of a different + // ObjectFilePlaceholder and an assertion will fire. + auto *objfile = module_sp->GetObjectFile(); + if (objfile && + objfile->GetPluginName() == + ObjectFilePlaceholder::GetPluginNameStatic()) { + if (((ObjectFilePlaceholder *)objfile)->GetBaseImageAddress() != + load_addr) + module_sp.reset(); + } + } + if (!module_sp) { + // We failed to locate a matching local object file. Fortunately, the + // minidump format encodes enough information about each module's memory + // range to allow us to create placeholder modules. + // + // This enables most LLDB functionality involving address-to-module + // translations (ex. identifing the module for a stack frame PC) and + // modules/sections commands (ex. target modules list, ...) + LLDB_LOG(log, + "Unable to locate the matching object file, creating a " + "placeholder module for: {0}", + name); + + module_sp = Module::CreateModuleFromObjectFile<ObjectFilePlaceholder>( + module_spec, load_addr, load_size); + GetTarget().GetImages().Append(module_sp, true /* notify */); + } + + bool load_addr_changed = false; + module_sp->SetLoadAddress(GetTarget(), load_addr, false, + load_addr_changed); + } +} + +bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) { + info.Clear(); + info.SetProcessID(GetID()); + info.SetArchitecture(GetArchitecture()); + lldb::ModuleSP module_sp = GetTarget().GetExecutableModule(); + if (module_sp) { + const bool add_exe_file_as_first_arg = false; + info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(), + add_exe_file_as_first_arg); + } + return true; +} + +// For minidumps there's no runtime generated code so we don't need JITLoader(s) +// Avoiding them will also speed up minidump loading since JITLoaders normally +// try to set up symbolic breakpoints, which in turn may force loading more +// debug information than needed. +JITLoaderList &ProcessMinidump::GetJITLoaders() { + if (!m_jit_loaders_up) { + m_jit_loaders_up = std::make_unique<JITLoaderList>(); + } + return *m_jit_loaders_up; +} + +#define INIT_BOOL(VAR, LONG, SHORT, DESC) \ + VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true) +#define APPEND_OPT(VAR) \ + m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1) + +class CommandObjectProcessMinidumpDump : public CommandObjectParsed { +private: + OptionGroupOptions m_option_group; + OptionGroupBoolean m_dump_all; + OptionGroupBoolean m_dump_directory; + OptionGroupBoolean m_dump_linux_cpuinfo; + OptionGroupBoolean m_dump_linux_proc_status; + OptionGroupBoolean m_dump_linux_lsb_release; + OptionGroupBoolean m_dump_linux_cmdline; + OptionGroupBoolean m_dump_linux_environ; + OptionGroupBoolean m_dump_linux_auxv; + OptionGroupBoolean m_dump_linux_maps; + OptionGroupBoolean m_dump_linux_proc_stat; + OptionGroupBoolean m_dump_linux_proc_uptime; + OptionGroupBoolean m_dump_linux_proc_fd; + OptionGroupBoolean m_dump_linux_all; + OptionGroupBoolean m_fb_app_data; + OptionGroupBoolean m_fb_build_id; + OptionGroupBoolean m_fb_version; + OptionGroupBoolean m_fb_java_stack; + OptionGroupBoolean m_fb_dalvik; + OptionGroupBoolean m_fb_unwind; + OptionGroupBoolean m_fb_error_log; + OptionGroupBoolean m_fb_app_state; + OptionGroupBoolean m_fb_abort; + OptionGroupBoolean m_fb_thread; + OptionGroupBoolean m_fb_logcat; + OptionGroupBoolean m_fb_all; + + void SetDefaultOptionsIfNoneAreSet() { + if (m_dump_all.GetOptionValue().GetCurrentValue() || + m_dump_linux_all.GetOptionValue().GetCurrentValue() || + m_fb_all.GetOptionValue().GetCurrentValue() || + m_dump_directory.GetOptionValue().GetCurrentValue() || + m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() || + m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() || + m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() || + m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() || + m_dump_linux_environ.GetOptionValue().GetCurrentValue() || + m_dump_linux_auxv.GetOptionValue().GetCurrentValue() || + m_dump_linux_maps.GetOptionValue().GetCurrentValue() || + m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() || + m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() || + m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue() || + m_fb_app_data.GetOptionValue().GetCurrentValue() || + m_fb_build_id.GetOptionValue().GetCurrentValue() || + m_fb_version.GetOptionValue().GetCurrentValue() || + m_fb_java_stack.GetOptionValue().GetCurrentValue() || + m_fb_dalvik.GetOptionValue().GetCurrentValue() || + m_fb_unwind.GetOptionValue().GetCurrentValue() || + m_fb_error_log.GetOptionValue().GetCurrentValue() || + m_fb_app_state.GetOptionValue().GetCurrentValue() || + m_fb_abort.GetOptionValue().GetCurrentValue() || + m_fb_thread.GetOptionValue().GetCurrentValue() || + m_fb_logcat.GetOptionValue().GetCurrentValue()) + return; + // If no options were set, then dump everything + m_dump_all.GetOptionValue().SetCurrentValue(true); + } + bool DumpAll() const { + return m_dump_all.GetOptionValue().GetCurrentValue(); + } + bool DumpDirectory() const { + return DumpAll() || + m_dump_directory.GetOptionValue().GetCurrentValue(); + } + bool DumpLinux() const { + return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxCPUInfo() const { + return DumpLinux() || + m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxProcStatus() const { + return DumpLinux() || + m_dump_linux_proc_status.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxProcStat() const { + return DumpLinux() || + m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxLSBRelease() const { + return DumpLinux() || + m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxCMDLine() const { + return DumpLinux() || + m_dump_linux_cmdline.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxEnviron() const { + return DumpLinux() || + m_dump_linux_environ.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxAuxv() const { + return DumpLinux() || + m_dump_linux_auxv.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxMaps() const { + return DumpLinux() || + m_dump_linux_maps.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxProcUptime() const { + return DumpLinux() || + m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue(); + } + bool DumpLinuxProcFD() const { + return DumpLinux() || + m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebook() const { + return DumpAll() || m_fb_all.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookAppData() const { + return DumpFacebook() || m_fb_app_data.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookBuildID() const { + return DumpFacebook() || m_fb_build_id.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookVersionName() const { + return DumpFacebook() || m_fb_version.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookJavaStack() const { + return DumpFacebook() || m_fb_java_stack.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookDalvikInfo() const { + return DumpFacebook() || m_fb_dalvik.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookUnwindSymbols() const { + return DumpFacebook() || m_fb_unwind.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookErrorLog() const { + return DumpFacebook() || m_fb_error_log.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookAppStateLog() const { + return DumpFacebook() || m_fb_app_state.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookAbortReason() const { + return DumpFacebook() || m_fb_abort.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookThreadName() const { + return DumpFacebook() || m_fb_thread.GetOptionValue().GetCurrentValue(); + } + bool DumpFacebookLogcat() const { + return DumpFacebook() || m_fb_logcat.GetOptionValue().GetCurrentValue(); + } +public: + CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "process plugin dump", + "Dump information from the minidump file.", nullptr), + m_option_group(), + INIT_BOOL(m_dump_all, "all", 'a', + "Dump the everything in the minidump."), + INIT_BOOL(m_dump_directory, "directory", 'd', + "Dump the minidump directory map."), + INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C', + "Dump linux /proc/cpuinfo."), + INIT_BOOL(m_dump_linux_proc_status, "status", 's', + "Dump linux /proc/<pid>/status."), + INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r', + "Dump linux /etc/lsb-release."), + INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c', + "Dump linux /proc/<pid>/cmdline."), + INIT_BOOL(m_dump_linux_environ, "environ", 'e', + "Dump linux /proc/<pid>/environ."), + INIT_BOOL(m_dump_linux_auxv, "auxv", 'x', + "Dump linux /proc/<pid>/auxv."), + INIT_BOOL(m_dump_linux_maps, "maps", 'm', + "Dump linux /proc/<pid>/maps."), + INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S', + "Dump linux /proc/<pid>/stat."), + INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u', + "Dump linux process uptime."), + INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f', + "Dump linux /proc/<pid>/fd."), + INIT_BOOL(m_dump_linux_all, "linux", 'l', + "Dump all linux streams."), + INIT_BOOL(m_fb_app_data, "fb-app-data", 1, + "Dump Facebook application custom data."), + INIT_BOOL(m_fb_build_id, "fb-build-id", 2, + "Dump the Facebook build ID."), + INIT_BOOL(m_fb_version, "fb-version", 3, + "Dump Facebook application version string."), + INIT_BOOL(m_fb_java_stack, "fb-java-stack", 4, + "Dump Facebook java stack."), + INIT_BOOL(m_fb_dalvik, "fb-dalvik-info", 5, + "Dump Facebook Dalvik info."), + INIT_BOOL(m_fb_unwind, "fb-unwind-symbols", 6, + "Dump Facebook unwind symbols."), + INIT_BOOL(m_fb_error_log, "fb-error-log", 7, + "Dump Facebook error log."), + INIT_BOOL(m_fb_app_state, "fb-app-state-log", 8, + "Dump Facebook java stack."), + INIT_BOOL(m_fb_abort, "fb-abort-reason", 9, + "Dump Facebook abort reason."), + INIT_BOOL(m_fb_thread, "fb-thread-name", 10, + "Dump Facebook thread name."), + INIT_BOOL(m_fb_logcat, "fb-logcat", 11, + "Dump Facebook logcat."), + INIT_BOOL(m_fb_all, "facebook", 12, "Dump all Facebook streams.") { + APPEND_OPT(m_dump_all); + APPEND_OPT(m_dump_directory); + APPEND_OPT(m_dump_linux_cpuinfo); + APPEND_OPT(m_dump_linux_proc_status); + APPEND_OPT(m_dump_linux_lsb_release); + APPEND_OPT(m_dump_linux_cmdline); + APPEND_OPT(m_dump_linux_environ); + APPEND_OPT(m_dump_linux_auxv); + APPEND_OPT(m_dump_linux_maps); + APPEND_OPT(m_dump_linux_proc_stat); + APPEND_OPT(m_dump_linux_proc_uptime); + APPEND_OPT(m_dump_linux_proc_fd); + APPEND_OPT(m_dump_linux_all); + APPEND_OPT(m_fb_app_data); + APPEND_OPT(m_fb_build_id); + APPEND_OPT(m_fb_version); + APPEND_OPT(m_fb_java_stack); + APPEND_OPT(m_fb_dalvik); + APPEND_OPT(m_fb_unwind); + APPEND_OPT(m_fb_error_log); + APPEND_OPT(m_fb_app_state); + APPEND_OPT(m_fb_abort); + APPEND_OPT(m_fb_thread); + APPEND_OPT(m_fb_logcat); + APPEND_OPT(m_fb_all); + m_option_group.Finalize(); + } + + ~CommandObjectProcessMinidumpDump() override = default; + + Options *GetOptions() override { return &m_option_group; } + + void DoExecute(Args &command, CommandReturnObject &result) override { + const size_t argc = command.GetArgumentCount(); + if (argc > 0) { + result.AppendErrorWithFormat("'%s' take no arguments, only options", + m_cmd_name.c_str()); + return; + } + SetDefaultOptionsIfNoneAreSet(); + + ProcessMinidump *process = static_cast<ProcessMinidump *>( + m_interpreter.GetExecutionContext().GetProcessPtr()); + result.SetStatus(eReturnStatusSuccessFinishResult); + Stream &s = result.GetOutputStream(); + MinidumpParser &minidump = *process->m_minidump_parser; + if (DumpDirectory()) { + s.Printf("RVA SIZE TYPE StreamType\n"); + s.Printf("---------- ---------- ---------- --------------------------\n"); + for (const auto &stream_desc : minidump.GetMinidumpFile().streams()) + s.Printf( + "0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)stream_desc.Location.RVA, + (uint32_t)stream_desc.Location.DataSize, + (unsigned)(StreamType)stream_desc.Type, + MinidumpParser::GetStreamTypeAsString(stream_desc.Type).data()); + s.Printf("\n"); + } + auto DumpTextStream = [&](StreamType stream_type, + llvm::StringRef label) -> void { + auto bytes = minidump.GetStream(stream_type); + if (!bytes.empty()) { + if (label.empty()) + label = MinidumpParser::GetStreamTypeAsString(stream_type); + s.Printf("%s:\n%s\n\n", label.data(), bytes.data()); + } + }; + auto DumpBinaryStream = [&](StreamType stream_type, + llvm::StringRef label) -> void { + auto bytes = minidump.GetStream(stream_type); + if (!bytes.empty()) { + if (label.empty()) + label = MinidumpParser::GetStreamTypeAsString(stream_type); + s.Printf("%s:\n", label.data()); + DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle, + process->GetAddressByteSize()); + DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1, + bytes.size(), 16, 0, 0, 0); + s.Printf("\n\n"); + } + }; + + if (DumpLinuxCPUInfo()) + DumpTextStream(StreamType::LinuxCPUInfo, "/proc/cpuinfo"); + if (DumpLinuxProcStatus()) + DumpTextStream(StreamType::LinuxProcStatus, "/proc/PID/status"); + if (DumpLinuxLSBRelease()) + DumpTextStream(StreamType::LinuxLSBRelease, "/etc/lsb-release"); + if (DumpLinuxCMDLine()) + DumpTextStream(StreamType::LinuxCMDLine, "/proc/PID/cmdline"); + if (DumpLinuxEnviron()) + DumpTextStream(StreamType::LinuxEnviron, "/proc/PID/environ"); + if (DumpLinuxAuxv()) + DumpBinaryStream(StreamType::LinuxAuxv, "/proc/PID/auxv"); + if (DumpLinuxMaps()) + DumpTextStream(StreamType::LinuxMaps, "/proc/PID/maps"); + if (DumpLinuxProcStat()) + DumpTextStream(StreamType::LinuxProcStat, "/proc/PID/stat"); + if (DumpLinuxProcUptime()) + DumpTextStream(StreamType::LinuxProcUptime, "uptime"); + if (DumpLinuxProcFD()) + DumpTextStream(StreamType::LinuxProcFD, "/proc/PID/fd"); + if (DumpFacebookAppData()) + DumpTextStream(StreamType::FacebookAppCustomData, + "Facebook App Data"); + if (DumpFacebookBuildID()) { + auto bytes = minidump.GetStream(StreamType::FacebookBuildID); + if (bytes.size() >= 4) { + DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle, + process->GetAddressByteSize()); + lldb::offset_t offset = 0; + uint32_t build_id = data.GetU32(&offset); + s.Printf("Facebook Build ID:\n"); + s.Printf("%u\n", build_id); + s.Printf("\n"); + } + } + if (DumpFacebookVersionName()) + DumpTextStream(StreamType::FacebookAppVersionName, + "Facebook Version String"); + if (DumpFacebookJavaStack()) + DumpTextStream(StreamType::FacebookJavaStack, + "Facebook Java Stack"); + if (DumpFacebookDalvikInfo()) + DumpTextStream(StreamType::FacebookDalvikInfo, + "Facebook Dalvik Info"); + if (DumpFacebookUnwindSymbols()) + DumpBinaryStream(StreamType::FacebookUnwindSymbols, + "Facebook Unwind Symbols Bytes"); + if (DumpFacebookErrorLog()) + DumpTextStream(StreamType::FacebookDumpErrorLog, + "Facebook Error Log"); + if (DumpFacebookAppStateLog()) + DumpTextStream(StreamType::FacebookAppStateLog, + "Faceook Application State Log"); + if (DumpFacebookAbortReason()) + DumpTextStream(StreamType::FacebookAbortReason, + "Facebook Abort Reason"); + if (DumpFacebookThreadName()) + DumpTextStream(StreamType::FacebookThreadName, + "Facebook Thread Name"); + if (DumpFacebookLogcat()) + DumpTextStream(StreamType::FacebookLogcat, "Facebook Logcat"); + } +}; + +class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword { +public: + CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "process plugin", + "Commands for operating on a ProcessMinidump process.", + "process plugin <subcommand> [<subcommand-options>]") { + LoadSubCommand("dump", + CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter))); + } + + ~CommandObjectMultiwordProcessMinidump() override = default; +}; + +CommandObject *ProcessMinidump::GetPluginCommandObject() { + if (!m_command_sp) + m_command_sp = std::make_shared<CommandObjectMultiwordProcessMinidump>( + GetTarget().GetDebugger().GetCommandInterpreter()); + return m_command_sp.get(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h new file mode 100644 index 000000000000..3f3123a0a8b5 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.h @@ -0,0 +1,123 @@ +//===-- ProcessMinidump.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_SOURCE_PLUGINS_PROCESS_MINIDUMP_PROCESSMINIDUMP_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_PROCESSMINIDUMP_H + +#include "MinidumpParser.h" +#include "MinidumpTypes.h" + +#include "lldb/Target/PostMortemProcess.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/Status.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include <optional> + +namespace lldb_private { + +namespace minidump { + +class ProcessMinidump : public PostMortemProcess { +public: + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "minidump"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + ProcessMinidump(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const FileSpec &core_file, lldb::DataBufferSP code_data); + + ~ProcessMinidump() override; + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + CommandObject *GetPluginCommandObject() override; + + Status DoLoadCore() override; + + DynamicLoader *GetDynamicLoader() override { return nullptr; } + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + SystemRuntime *GetSystemRuntime() override { return nullptr; } + + Status DoDestroy() override; + + void RefreshStateAfterStop() override; + + bool IsAlive() override; + + bool WarnBeforeDetach() const override; + + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; + + ArchSpec GetArchitecture(); + + Status GetMemoryRegions( + lldb_private::MemoryRegionInfos ®ion_list) override; + + bool GetProcessInfo(ProcessInstanceInfo &info) override; + + Status WillResume() override { + Status error; + error.SetErrorStringWithFormatv( + "error: {0} does not support resuming processes", GetPluginName()); + return error; + } + + std::optional<MinidumpParser> m_minidump_parser; + +protected: + void Clear(); + + bool DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) override; + + Status DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + void ReadModuleList(); + + lldb::ModuleSP GetOrCreateModule(lldb_private::UUID minidump_uuid, + llvm::StringRef name, + lldb_private::ModuleSpec module_spec); + + JITLoaderList &GetJITLoaders() override; + +private: + lldb::DataBufferSP m_core_data; + llvm::ArrayRef<minidump::Thread> m_thread_list; + const minidump::ExceptionStream *m_active_exception; + lldb::CommandObjectSP m_command_sp; + bool m_is_wow64; + std::optional<MemoryRegionInfos> m_memory_regions; + + void BuildMemoryRegions(); +}; + +} // namespace minidump +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_PROCESSMINIDUMP_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp new file mode 100644 index 000000000000..0004d5d8d07e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.cpp @@ -0,0 +1,550 @@ +//===-- RegisterContextMinidump_ARM.cpp -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMinidump_ARM.h" + +#include "Utility/ARM_DWARF_Registers.h" +#include "Utility/ARM_ehframe_Registers.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-enumerations.h" + +// C includes +#include <cassert> + +// C++ includes + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +#define INV LLDB_INVALID_REGNUM +#define OFFSET(r) (offsetof(RegisterContextMinidump_ARM::Context, r)) + +#define DEF_R(i) \ + { \ + "r" #i, nullptr, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \ + {ehframe_r##i, dwarf_r##i, INV, INV, reg_r##i}, nullptr, nullptr, \ + nullptr, \ + } + +#define DEF_R_ARG(i, n) \ + { \ + "r" #i, "arg" #n, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \ + {ehframe_r##i, dwarf_r##i, LLDB_REGNUM_GENERIC_ARG1 + i, INV, \ + reg_r##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEF_D(i) \ + { \ + "d" #i, nullptr, 8, OFFSET(d) + i * 8, eEncodingVector, \ + eFormatVectorOfUInt8, {dwarf_d##i, dwarf_d##i, INV, INV, reg_d##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEF_S(i) \ + { \ + "s" #i, nullptr, 4, OFFSET(s) + i * 4, eEncodingIEEE754, eFormatFloat, \ + {dwarf_s##i, dwarf_s##i, INV, INV, reg_s##i}, nullptr, nullptr, \ + nullptr, \ + } + +#define DEF_Q(i) \ + { \ + "q" #i, nullptr, 16, OFFSET(q) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {dwarf_q##i, dwarf_q##i, INV, INV, reg_q##i}, \ + nullptr, nullptr, nullptr, \ + } + +// Zero based LLDB register numbers for this register context +enum { + // General Purpose Registers + reg_r0, + reg_r1, + reg_r2, + reg_r3, + reg_r4, + reg_r5, + reg_r6, + reg_r7, + reg_r8, + reg_r9, + reg_r10, + reg_r11, + reg_r12, + reg_sp, + reg_lr, + reg_pc, + reg_cpsr, + // Floating Point Registers + reg_fpscr, + reg_d0, + reg_d1, + reg_d2, + reg_d3, + reg_d4, + reg_d5, + reg_d6, + reg_d7, + reg_d8, + reg_d9, + reg_d10, + reg_d11, + reg_d12, + reg_d13, + reg_d14, + reg_d15, + reg_d16, + reg_d17, + reg_d18, + reg_d19, + reg_d20, + reg_d21, + reg_d22, + reg_d23, + reg_d24, + reg_d25, + reg_d26, + reg_d27, + reg_d28, + reg_d29, + reg_d30, + reg_d31, + reg_s0, + reg_s1, + reg_s2, + reg_s3, + reg_s4, + reg_s5, + reg_s6, + reg_s7, + reg_s8, + reg_s9, + reg_s10, + reg_s11, + reg_s12, + reg_s13, + reg_s14, + reg_s15, + reg_s16, + reg_s17, + reg_s18, + reg_s19, + reg_s20, + reg_s21, + reg_s22, + reg_s23, + reg_s24, + reg_s25, + reg_s26, + reg_s27, + reg_s28, + reg_s29, + reg_s30, + reg_s31, + reg_q0, + reg_q1, + reg_q2, + reg_q3, + reg_q4, + reg_q5, + reg_q6, + reg_q7, + reg_q8, + reg_q9, + reg_q10, + reg_q11, + reg_q12, + reg_q13, + reg_q14, + reg_q15, + k_num_regs +}; + +static RegisterInfo g_reg_info_apple_fp = { + "fp", + "r7", + 4, + OFFSET(r) + 7 * 4, + eEncodingUint, + eFormatHex, + {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, INV, reg_r7}, + nullptr, + nullptr, + nullptr, +}; + +static RegisterInfo g_reg_info_fp = { + "fp", + "r11", + 4, + OFFSET(r) + 11 * 4, + eEncodingUint, + eFormatHex, + {ehframe_r11, dwarf_r11, LLDB_REGNUM_GENERIC_FP, INV, reg_r11}, + nullptr, + nullptr, + nullptr, +}; + +// Register info definitions for this register context +static RegisterInfo g_reg_infos[] = { + DEF_R_ARG(0, 1), + DEF_R_ARG(1, 2), + DEF_R_ARG(2, 3), + DEF_R_ARG(3, 4), + DEF_R(4), + DEF_R(5), + DEF_R(6), + DEF_R(7), + DEF_R(8), + DEF_R(9), + DEF_R(10), + DEF_R(11), + DEF_R(12), + {"sp", + "r13", + 4, + OFFSET(r) + 13 * 4, + eEncodingUint, + eFormatHex, + {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, INV, reg_sp}, + nullptr, + nullptr, + nullptr, + }, + {"lr", + "r14", + 4, + OFFSET(r) + 14 * 4, + eEncodingUint, + eFormatHex, + {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, INV, reg_lr}, + nullptr, + nullptr, + nullptr, + }, + {"pc", + "r15", + 4, + OFFSET(r) + 15 * 4, + eEncodingUint, + eFormatHex, + {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, INV, reg_pc}, + nullptr, + nullptr, + nullptr, + }, + {"cpsr", + "psr", + 4, + OFFSET(cpsr), + eEncodingUint, + eFormatHex, + {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, INV, reg_cpsr}, + nullptr, + nullptr, + nullptr, + }, + {"fpscr", + nullptr, + 8, + OFFSET(fpscr), + eEncodingUint, + eFormatHex, + {INV, INV, INV, INV, reg_fpscr}, + nullptr, + nullptr, + nullptr, + }, + DEF_D(0), + DEF_D(1), + DEF_D(2), + DEF_D(3), + DEF_D(4), + DEF_D(5), + DEF_D(6), + DEF_D(7), + DEF_D(8), + DEF_D(9), + DEF_D(10), + DEF_D(11), + DEF_D(12), + DEF_D(13), + DEF_D(14), + DEF_D(15), + DEF_D(16), + DEF_D(17), + DEF_D(18), + DEF_D(19), + DEF_D(20), + DEF_D(21), + DEF_D(22), + DEF_D(23), + DEF_D(24), + DEF_D(25), + DEF_D(26), + DEF_D(27), + DEF_D(28), + DEF_D(29), + DEF_D(30), + DEF_D(31), + DEF_S(0), + DEF_S(1), + DEF_S(2), + DEF_S(3), + DEF_S(4), + DEF_S(5), + DEF_S(6), + DEF_S(7), + DEF_S(8), + DEF_S(9), + DEF_S(10), + DEF_S(11), + DEF_S(12), + DEF_S(13), + DEF_S(14), + DEF_S(15), + DEF_S(16), + DEF_S(17), + DEF_S(18), + DEF_S(19), + DEF_S(20), + DEF_S(21), + DEF_S(22), + DEF_S(23), + DEF_S(24), + DEF_S(25), + DEF_S(26), + DEF_S(27), + DEF_S(28), + DEF_S(29), + DEF_S(30), + DEF_S(31), + DEF_Q(0), + DEF_Q(1), + DEF_Q(2), + DEF_Q(3), + DEF_Q(4), + DEF_Q(5), + DEF_Q(6), + DEF_Q(7), + DEF_Q(8), + DEF_Q(9), + DEF_Q(10), + DEF_Q(11), + DEF_Q(12), + DEF_Q(13), + DEF_Q(14), + DEF_Q(15)}; + +constexpr size_t k_num_reg_infos = std::size(g_reg_infos); + +// ARM general purpose registers. +const uint32_t g_gpr_regnums[] = { + reg_r0, + reg_r1, + reg_r2, + reg_r3, + reg_r4, + reg_r5, + reg_r6, + reg_r7, + reg_r8, + reg_r9, + reg_r10, + reg_r11, + reg_r12, + reg_sp, + reg_lr, + reg_pc, + reg_cpsr, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +const uint32_t g_fpu_regnums[] = { + reg_fpscr, + reg_d0, + reg_d1, + reg_d2, + reg_d3, + reg_d4, + reg_d5, + reg_d6, + reg_d7, + reg_d8, + reg_d9, + reg_d10, + reg_d11, + reg_d12, + reg_d13, + reg_d14, + reg_d15, + reg_d16, + reg_d17, + reg_d18, + reg_d19, + reg_d20, + reg_d21, + reg_d22, + reg_d23, + reg_d24, + reg_d25, + reg_d26, + reg_d27, + reg_d28, + reg_d29, + reg_d30, + reg_d31, + reg_s0, + reg_s1, + reg_s2, + reg_s3, + reg_s4, + reg_s5, + reg_s6, + reg_s7, + reg_s8, + reg_s9, + reg_s10, + reg_s11, + reg_s12, + reg_s13, + reg_s14, + reg_s15, + reg_s16, + reg_s17, + reg_s18, + reg_s19, + reg_s20, + reg_s21, + reg_s22, + reg_s23, + reg_s24, + reg_s25, + reg_s26, + reg_s27, + reg_s28, + reg_s29, + reg_s30, + reg_s31, + reg_q0, + reg_q1, + reg_q2, + reg_q3, + reg_q4, + reg_q5, + reg_q6, + reg_q7, + reg_q8, + reg_q9, + reg_q10, + reg_q11, + reg_q12, + reg_q13, + reg_q14, + reg_q15, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +// Skip the last LLDB_INVALID_REGNUM in each count below by subtracting 1 +constexpr size_t k_num_gpr_regs = std::size(g_gpr_regnums) - 1; +constexpr size_t k_num_fpu_regs = std::size(g_fpu_regnums) - 1; + +static RegisterSet g_reg_sets[] = { + {"General Purpose Registers", "gpr", k_num_gpr_regs, g_gpr_regnums}, + {"Floating Point Registers", "fpu", k_num_fpu_regs, g_fpu_regnums}, +}; + +constexpr size_t k_num_reg_sets = std::size(g_reg_sets); + +RegisterContextMinidump_ARM::RegisterContextMinidump_ARM( + lldb_private::Thread &thread, const DataExtractor &data, bool apple) + : RegisterContext(thread, 0), m_apple(apple) { + lldb::offset_t offset = 0; + m_regs.context_flags = data.GetU32(&offset); + for (unsigned i = 0; i < std::size(m_regs.r); ++i) + m_regs.r[i] = data.GetU32(&offset); + m_regs.cpsr = data.GetU32(&offset); + m_regs.fpscr = data.GetU64(&offset); + for (unsigned i = 0; i < std::size(m_regs.d); ++i) + m_regs.d[i] = data.GetU64(&offset); + lldbassert(k_num_regs == k_num_reg_infos); +} + +size_t RegisterContextMinidump_ARM::GetRegisterCountStatic() { + return k_num_regs; +} + +// Used for unit testing so we can verify register info is filled in for +// all register flavors (DWARF, EH Frame, generic, etc). +size_t RegisterContextMinidump_ARM::GetRegisterCount() { + return GetRegisterCountStatic(); +} + +// Used for unit testing so we can verify register info is filled in. +const RegisterInfo * +RegisterContextMinidump_ARM::GetRegisterInfoAtIndexStatic(size_t reg, + bool apple) { + if (reg < k_num_reg_infos) { + if (apple) { + if (reg == reg_r7) + return &g_reg_info_apple_fp; + } else { + if (reg == reg_r11) + return &g_reg_info_fp; + } + return &g_reg_infos[reg]; + } + return nullptr; +} + +const RegisterInfo * +RegisterContextMinidump_ARM::GetRegisterInfoAtIndex(size_t reg) { + return GetRegisterInfoAtIndexStatic(reg, m_apple); +} + +size_t RegisterContextMinidump_ARM::GetRegisterSetCount() { + return k_num_reg_sets; +} + +const RegisterSet *RegisterContextMinidump_ARM::GetRegisterSet(size_t set) { + if (set < k_num_reg_sets) + return &g_reg_sets[set]; + return nullptr; +} + +const char *RegisterContextMinidump_ARM::GetRegisterName(unsigned reg) { + if (reg < k_num_reg_infos) + return g_reg_infos[reg].name; + return nullptr; +} + +bool RegisterContextMinidump_ARM::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + reg_value.SetFromMemoryData( + *reg_info, (const uint8_t *)&m_regs + reg_info->byte_offset, + reg_info->byte_size, lldb::eByteOrderLittle, error); + return error.Success(); +} + +bool RegisterContextMinidump_ARM::WriteRegister(const RegisterInfo *, + const RegisterValue &) { + return false; +} + +uint32_t RegisterContextMinidump_ARM::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + for (size_t i = 0; i < k_num_regs; ++i) { + if (g_reg_infos[i].kinds[kind] == num) + return i; + } + return LLDB_INVALID_REGNUM; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h new file mode 100644 index 000000000000..857f9c0a3767 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM.h @@ -0,0 +1,98 @@ +//===-- RegisterContextMinidump_ARM.h ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM_H + +#include "MinidumpTypes.h" + +#include "Plugins/Process/Utility/RegisterInfoInterface.h" + +#include "lldb/Target/RegisterContext.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" + +// C includes +// C++ includes + +namespace lldb_private { + +namespace minidump { + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +class RegisterContextMinidump_ARM : public lldb_private::RegisterContext { +public: + RegisterContextMinidump_ARM(lldb_private::Thread &thread, + const DataExtractor &data, bool apple); + + ~RegisterContextMinidump_ARM() override = default; + + void InvalidateAllRegisters() override { + // Do nothing... registers are always valid... + } + + // Used for unit testing. + static size_t GetRegisterCountStatic(); + // Used for unit testing. + static const lldb_private::RegisterInfo * + GetRegisterInfoAtIndexStatic(size_t reg, bool apple); + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + // Reference: see breakpad/crashpad source + struct QRegValue { + uint64_t lo; + uint64_t hi; + }; + + struct Context { + uint32_t context_flags; + uint32_t r[16]; + uint32_t cpsr; + uint64_t fpscr; + union { + uint64_t d[32]; + uint32_t s[32]; + QRegValue q[16]; + }; + uint32_t extra[8]; + }; + +protected: + enum class Flags : uint32_t { + ARM_Flag = 0x40000000, + Integer = ARM_Flag | 0x00000002, + FloatingPoint = ARM_Flag | 0x00000004, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint) + }; + Context m_regs; + const bool m_apple; // True if this is an Apple ARM where FP is R7 +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp new file mode 100644 index 000000000000..a0476c962070 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.cpp @@ -0,0 +1,833 @@ +//===-- RegisterContextMinidump_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMinidump_ARM64.h" + +#include "Utility/ARM64_DWARF_Registers.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/lldb-enumerations.h" + +// C includes +#include <cassert> + +// C++ includes + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +#define INV LLDB_INVALID_REGNUM +#define OFFSET(r) (offsetof(RegisterContextMinidump_ARM64::Context, r)) + +#define DEF_X(i) \ + { \ + "x" #i, nullptr, 8, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \ + {arm64_dwarf::x##i, arm64_dwarf::x##i, INV, INV, reg_x##i}, \ + nullptr, nullptr, nullptr, \ + } + +#define DEF_W(i) \ + { \ + "w" #i, nullptr, 4, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \ + {INV, INV, INV, INV, reg_w##i}, nullptr, nullptr, nullptr, \ + } + +#define DEF_X_ARG(i, n) \ + { \ + "x" #i, "arg" #n, 8, OFFSET(x) + i * 8, eEncodingUint, eFormatHex, \ + {arm64_dwarf::x##i, arm64_dwarf::x##i, LLDB_REGNUM_GENERIC_ARG1 + i, \ + INV, reg_x##i}, nullptr, nullptr, nullptr, \ + } + +#define DEF_V(i) \ + { \ + "v" #i, nullptr, 16, OFFSET(v) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {arm64_dwarf::v##i, arm64_dwarf::v##i, INV, INV, \ + reg_v##i}, nullptr, nullptr, nullptr, \ + } + +#define DEF_D(i) \ + { \ + "d" #i, nullptr, 8, OFFSET(v) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_d##i}, nullptr, \ + nullptr, nullptr, \ + } + +#define DEF_S(i) \ + { \ + "s" #i, nullptr, 4, OFFSET(v) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_s##i}, nullptr, \ + nullptr, nullptr, \ + } + +#define DEF_H(i) \ + { \ + "h" #i, nullptr, 2, OFFSET(v) + i * 16, eEncodingVector, \ + eFormatVectorOfUInt8, {INV, INV, INV, INV, reg_h##i}, nullptr, \ + nullptr, nullptr, \ + } + +// Zero based LLDB register numbers for this register context +enum { + // General Purpose Registers + reg_x0 = 0, + reg_x1, + reg_x2, + reg_x3, + reg_x4, + reg_x5, + reg_x6, + reg_x7, + reg_x8, + reg_x9, + reg_x10, + reg_x11, + reg_x12, + reg_x13, + reg_x14, + reg_x15, + reg_x16, + reg_x17, + reg_x18, + reg_x19, + reg_x20, + reg_x21, + reg_x22, + reg_x23, + reg_x24, + reg_x25, + reg_x26, + reg_x27, + reg_x28, + reg_fp, + reg_lr, + reg_sp, + reg_pc, + reg_w0, + reg_w1, + reg_w2, + reg_w3, + reg_w4, + reg_w5, + reg_w6, + reg_w7, + reg_w8, + reg_w9, + reg_w10, + reg_w11, + reg_w12, + reg_w13, + reg_w14, + reg_w15, + reg_w16, + reg_w17, + reg_w18, + reg_w19, + reg_w20, + reg_w21, + reg_w22, + reg_w23, + reg_w24, + reg_w25, + reg_w26, + reg_w27, + reg_w28, + reg_w29, + reg_w30, + reg_w31, + reg_cpsr, + // Floating Point Registers + reg_fpsr, + reg_fpcr, + reg_v0, + reg_v1, + reg_v2, + reg_v3, + reg_v4, + reg_v5, + reg_v6, + reg_v7, + reg_v8, + reg_v9, + reg_v10, + reg_v11, + reg_v12, + reg_v13, + reg_v14, + reg_v15, + reg_v16, + reg_v17, + reg_v18, + reg_v19, + reg_v20, + reg_v21, + reg_v22, + reg_v23, + reg_v24, + reg_v25, + reg_v26, + reg_v27, + reg_v28, + reg_v29, + reg_v30, + reg_v31, + reg_d0, + reg_d1, + reg_d2, + reg_d3, + reg_d4, + reg_d5, + reg_d6, + reg_d7, + reg_d8, + reg_d9, + reg_d10, + reg_d11, + reg_d12, + reg_d13, + reg_d14, + reg_d15, + reg_d16, + reg_d17, + reg_d18, + reg_d19, + reg_d20, + reg_d21, + reg_d22, + reg_d23, + reg_d24, + reg_d25, + reg_d26, + reg_d27, + reg_d28, + reg_d29, + reg_d30, + reg_d31, + reg_s0, + reg_s1, + reg_s2, + reg_s3, + reg_s4, + reg_s5, + reg_s6, + reg_s7, + reg_s8, + reg_s9, + reg_s10, + reg_s11, + reg_s12, + reg_s13, + reg_s14, + reg_s15, + reg_s16, + reg_s17, + reg_s18, + reg_s19, + reg_s20, + reg_s21, + reg_s22, + reg_s23, + reg_s24, + reg_s25, + reg_s26, + reg_s27, + reg_s28, + reg_s29, + reg_s30, + reg_s31, + reg_h0, + reg_h1, + reg_h2, + reg_h3, + reg_h4, + reg_h5, + reg_h6, + reg_h7, + reg_h8, + reg_h9, + reg_h10, + reg_h11, + reg_h12, + reg_h13, + reg_h14, + reg_h15, + reg_h16, + reg_h17, + reg_h18, + reg_h19, + reg_h20, + reg_h21, + reg_h22, + reg_h23, + reg_h24, + reg_h25, + reg_h26, + reg_h27, + reg_h28, + reg_h29, + reg_h30, + reg_h31, + k_num_regs +}; + +// Register info definitions for this register context +static RegisterInfo g_reg_infos[] = { + DEF_X_ARG(0, 1), + DEF_X_ARG(1, 2), + DEF_X_ARG(2, 3), + DEF_X_ARG(3, 4), + DEF_X_ARG(4, 5), + DEF_X_ARG(5, 6), + DEF_X_ARG(6, 7), + DEF_X_ARG(7, 8), + DEF_X(8), + DEF_X(9), + DEF_X(10), + DEF_X(11), + DEF_X(12), + DEF_X(13), + DEF_X(14), + DEF_X(15), + DEF_X(16), + DEF_X(17), + DEF_X(18), + DEF_X(19), + DEF_X(20), + DEF_X(21), + DEF_X(22), + DEF_X(23), + DEF_X(24), + DEF_X(25), + DEF_X(26), + DEF_X(27), + DEF_X(28), + {"fp", + "x29", + 8, + OFFSET(x) + 29 * 8, + eEncodingUint, + eFormatHex, + {arm64_dwarf::x29, arm64_dwarf::x29, LLDB_REGNUM_GENERIC_FP, INV, reg_fp}, + nullptr, + nullptr, + nullptr, + }, + {"lr", + "x30", + 8, + OFFSET(x) + 30 * 8, + eEncodingUint, + eFormatHex, + {arm64_dwarf::x30, arm64_dwarf::x30, LLDB_REGNUM_GENERIC_RA, INV, reg_lr}, + nullptr, + nullptr, + nullptr, + }, + {"sp", + "x31", + 8, + OFFSET(x) + 31 * 8, + eEncodingUint, + eFormatHex, + {arm64_dwarf::x31, arm64_dwarf::x31, LLDB_REGNUM_GENERIC_SP, INV, reg_sp}, + nullptr, + nullptr, + nullptr, + }, + {"pc", + nullptr, + 8, + OFFSET(pc), + eEncodingUint, + eFormatHex, + {arm64_dwarf::pc, arm64_dwarf::pc, LLDB_REGNUM_GENERIC_PC, INV, reg_pc}, + nullptr, + nullptr, + nullptr, + }, + // w0 - w31 + DEF_W(0), + DEF_W(1), + DEF_W(2), + DEF_W(3), + DEF_W(4), + DEF_W(5), + DEF_W(6), + DEF_W(7), + DEF_W(8), + DEF_W(9), + DEF_W(10), + DEF_W(11), + DEF_W(12), + DEF_W(13), + DEF_W(14), + DEF_W(15), + DEF_W(16), + DEF_W(17), + DEF_W(18), + DEF_W(19), + DEF_W(20), + DEF_W(21), + DEF_W(22), + DEF_W(23), + DEF_W(24), + DEF_W(25), + DEF_W(26), + DEF_W(27), + DEF_W(28), + DEF_W(29), + DEF_W(30), + DEF_W(31), + {"cpsr", + "psr", + 4, + OFFSET(cpsr), + eEncodingUint, + eFormatHex, + {INV, arm64_dwarf::cpsr, LLDB_REGNUM_GENERIC_FLAGS, INV, reg_cpsr}, + nullptr, + nullptr, + nullptr, + }, + {"fpsr", + nullptr, + 4, + OFFSET(fpsr), + eEncodingUint, + eFormatHex, + {INV, INV, INV, INV, reg_fpsr}, + nullptr, + nullptr, + nullptr, + }, + {"fpcr", + nullptr, + 4, + OFFSET(fpcr), + eEncodingUint, + eFormatHex, + {INV, INV, INV, INV, reg_fpcr}, + nullptr, + nullptr, + nullptr, + }, + // v0 - v31 + DEF_V(0), + DEF_V(1), + DEF_V(2), + DEF_V(3), + DEF_V(4), + DEF_V(5), + DEF_V(6), + DEF_V(7), + DEF_V(8), + DEF_V(9), + DEF_V(10), + DEF_V(11), + DEF_V(12), + DEF_V(13), + DEF_V(14), + DEF_V(15), + DEF_V(16), + DEF_V(17), + DEF_V(18), + DEF_V(19), + DEF_V(20), + DEF_V(21), + DEF_V(22), + DEF_V(23), + DEF_V(24), + DEF_V(25), + DEF_V(26), + DEF_V(27), + DEF_V(28), + DEF_V(29), + DEF_V(30), + DEF_V(31), + // d0 - d31 + DEF_D(0), + DEF_D(1), + DEF_D(2), + DEF_D(3), + DEF_D(4), + DEF_D(5), + DEF_D(6), + DEF_D(7), + DEF_D(8), + DEF_D(9), + DEF_D(10), + DEF_D(11), + DEF_D(12), + DEF_D(13), + DEF_D(14), + DEF_D(15), + DEF_D(16), + DEF_D(17), + DEF_D(18), + DEF_D(19), + DEF_D(20), + DEF_D(21), + DEF_D(22), + DEF_D(23), + DEF_D(24), + DEF_D(25), + DEF_D(26), + DEF_D(27), + DEF_D(28), + DEF_D(29), + DEF_D(30), + DEF_D(31), + // s0 - s31 + DEF_S(0), + DEF_S(1), + DEF_S(2), + DEF_S(3), + DEF_S(4), + DEF_S(5), + DEF_S(6), + DEF_S(7), + DEF_S(8), + DEF_S(9), + DEF_S(10), + DEF_S(11), + DEF_S(12), + DEF_S(13), + DEF_S(14), + DEF_S(15), + DEF_S(16), + DEF_S(17), + DEF_S(18), + DEF_S(19), + DEF_S(20), + DEF_S(21), + DEF_S(22), + DEF_S(23), + DEF_S(24), + DEF_S(25), + DEF_S(26), + DEF_S(27), + DEF_S(28), + DEF_S(29), + DEF_S(30), + DEF_S(31), + // h0 - h31 + DEF_H(0), + DEF_H(1), + DEF_H(2), + DEF_H(3), + DEF_H(4), + DEF_H(5), + DEF_H(6), + DEF_H(7), + DEF_H(8), + DEF_H(9), + DEF_H(10), + DEF_H(11), + DEF_H(12), + DEF_H(13), + DEF_H(14), + DEF_H(15), + DEF_H(16), + DEF_H(17), + DEF_H(18), + DEF_H(19), + DEF_H(20), + DEF_H(21), + DEF_H(22), + DEF_H(23), + DEF_H(24), + DEF_H(25), + DEF_H(26), + DEF_H(27), + DEF_H(28), + DEF_H(29), + DEF_H(30), + DEF_H(31), +}; + +constexpr size_t k_num_reg_infos = std::size(g_reg_infos); + +// ARM64 general purpose registers. +const uint32_t g_gpr_regnums[] = { + reg_x0, + reg_x1, + reg_x2, + reg_x3, + reg_x4, + reg_x5, + reg_x6, + reg_x7, + reg_x8, + reg_x9, + reg_x10, + reg_x11, + reg_x12, + reg_x13, + reg_x14, + reg_x15, + reg_x16, + reg_x17, + reg_x18, + reg_x19, + reg_x20, + reg_x21, + reg_x22, + reg_x23, + reg_x24, + reg_x25, + reg_x26, + reg_x27, + reg_x28, + reg_fp, + reg_lr, + reg_sp, + reg_w0, + reg_w1, + reg_w2, + reg_w3, + reg_w4, + reg_w5, + reg_w6, + reg_w7, + reg_w8, + reg_w9, + reg_w10, + reg_w11, + reg_w12, + reg_w13, + reg_w14, + reg_w15, + reg_w16, + reg_w17, + reg_w18, + reg_w19, + reg_w20, + reg_w21, + reg_w22, + reg_w23, + reg_w24, + reg_w25, + reg_w26, + reg_w27, + reg_w28, + reg_w29, + reg_w30, + reg_w31, + reg_pc, + reg_cpsr, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +const uint32_t g_fpu_regnums[] = { + reg_v0, + reg_v1, + reg_v2, + reg_v3, + reg_v4, + reg_v5, + reg_v6, + reg_v7, + reg_v8, + reg_v9, + reg_v10, + reg_v11, + reg_v12, + reg_v13, + reg_v14, + reg_v15, + reg_v16, + reg_v17, + reg_v18, + reg_v19, + reg_v20, + reg_v21, + reg_v22, + reg_v23, + reg_v24, + reg_v25, + reg_v26, + reg_v27, + reg_v28, + reg_v29, + reg_v30, + reg_v31, + reg_d0, + reg_d1, + reg_d2, + reg_d3, + reg_d4, + reg_d5, + reg_d6, + reg_d7, + reg_d8, + reg_d9, + reg_d10, + reg_d11, + reg_d12, + reg_d13, + reg_d14, + reg_d15, + reg_d16, + reg_d17, + reg_d18, + reg_d19, + reg_d20, + reg_d21, + reg_d22, + reg_d23, + reg_d24, + reg_d25, + reg_d26, + reg_d27, + reg_d28, + reg_d29, + reg_d30, + reg_d31, + reg_s0, + reg_s1, + reg_s2, + reg_s3, + reg_s4, + reg_s5, + reg_s6, + reg_s7, + reg_s8, + reg_s9, + reg_s10, + reg_s11, + reg_s12, + reg_s13, + reg_s14, + reg_s15, + reg_s16, + reg_s17, + reg_s18, + reg_s19, + reg_s20, + reg_s21, + reg_s22, + reg_s23, + reg_s24, + reg_s25, + reg_s26, + reg_s27, + reg_s28, + reg_s29, + reg_s30, + reg_s31, + reg_h0, + reg_h1, + reg_h2, + reg_h3, + reg_h4, + reg_h5, + reg_h6, + reg_h7, + reg_h8, + reg_h9, + reg_h10, + reg_h11, + reg_h12, + reg_h13, + reg_h14, + reg_h15, + reg_h16, + reg_h17, + reg_h18, + reg_h19, + reg_h20, + reg_h21, + reg_h22, + reg_h23, + reg_h24, + reg_h25, + reg_h26, + reg_h27, + reg_h28, + reg_h29, + reg_h30, + reg_h31, + reg_fpsr, + reg_fpcr, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; + +// Skip the last LLDB_INVALID_REGNUM in each count below by subtracting 1 +constexpr size_t k_num_gpr_regs = std::size(g_gpr_regnums) - 1; +constexpr size_t k_num_fpu_regs = std::size(g_fpu_regnums) - 1; + +static RegisterSet g_reg_sets[] = { + {"General Purpose Registers", "gpr", k_num_gpr_regs, g_gpr_regnums}, + {"Floating Point Registers", "fpu", k_num_fpu_regs, g_fpu_regnums}, +}; + +constexpr size_t k_num_reg_sets = std::size(g_reg_sets); + +RegisterContextMinidump_ARM64::RegisterContextMinidump_ARM64( + lldb_private::Thread &thread, const DataExtractor &data) + : RegisterContext(thread, 0) { + lldb::offset_t offset = 0; + m_regs.context_flags = data.GetU64(&offset); + for (unsigned i = 0; i < 32; ++i) + m_regs.x[i] = data.GetU64(&offset); + m_regs.pc = data.GetU64(&offset); + m_regs.cpsr = data.GetU32(&offset); + m_regs.fpsr = data.GetU32(&offset); + m_regs.fpcr = data.GetU32(&offset); + auto regs_data = data.GetData(&offset, sizeof(m_regs.v)); + if (regs_data) + memcpy(m_regs.v, regs_data, sizeof(m_regs.v)); + static_assert(k_num_regs == k_num_reg_infos); +} +size_t RegisterContextMinidump_ARM64::GetRegisterCount() { return k_num_regs; } + +const RegisterInfo * +RegisterContextMinidump_ARM64::GetRegisterInfoAtIndex(size_t reg) { + if (reg < k_num_reg_infos) + return &g_reg_infos[reg]; + return nullptr; +} + +size_t RegisterContextMinidump_ARM64::GetRegisterSetCount() { + return k_num_reg_sets; +} + +const RegisterSet *RegisterContextMinidump_ARM64::GetRegisterSet(size_t set) { + if (set < k_num_reg_sets) + return &g_reg_sets[set]; + return nullptr; +} + +const char *RegisterContextMinidump_ARM64::GetRegisterName(unsigned reg) { + if (reg < k_num_reg_infos) + return g_reg_infos[reg].name; + return nullptr; +} + +bool RegisterContextMinidump_ARM64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + reg_value.SetFromMemoryData( + *reg_info, (const uint8_t *)&m_regs + reg_info->byte_offset, + reg_info->byte_size, lldb::eByteOrderLittle, error); + return error.Success(); +} + +bool RegisterContextMinidump_ARM64::WriteRegister(const RegisterInfo *, + const RegisterValue &) { + return false; +} + +uint32_t RegisterContextMinidump_ARM64::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + for (size_t i = 0; i < k_num_regs; ++i) { + if (g_reg_infos[i].kinds[kind] == num) + return i; + } + return LLDB_INVALID_REGNUM; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h new file mode 100644 index 000000000000..58cf8d62fb86 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_ARM64.h @@ -0,0 +1,83 @@ +//===-- RegisterContextMinidump_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM64_H + +#include "MinidumpTypes.h" + +#include "Plugins/Process/Utility/RegisterInfoInterface.h" +#include "lldb/Target/RegisterContext.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" + +// C includes +// C++ includes + +namespace lldb_private { + +namespace minidump { + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +class RegisterContextMinidump_ARM64 : public lldb_private::RegisterContext { +public: + RegisterContextMinidump_ARM64(lldb_private::Thread &thread, + const DataExtractor &data); + + ~RegisterContextMinidump_ARM64() override = default; + + void InvalidateAllRegisters() override { + // Do nothing... registers are always valid... + } + + size_t GetRegisterCount() override; + + const lldb_private::RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const lldb_private::RegisterSet *GetRegisterSet(size_t set) override; + + const char *GetRegisterName(unsigned reg); + + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + // Reference: see breakpad/crashpad source + struct Context { + uint64_t context_flags; + uint64_t x[32]; + uint64_t pc; + uint32_t cpsr; + uint32_t fpsr; + uint32_t fpcr; + uint8_t v[32 * 16]; // 32 128-bit floating point registers + }; + + enum class Flags : uint32_t { + ARM64_Flag = 0x80000000, + Integer = ARM64_Flag | 0x00000002, + FloatingPoint = ARM64_Flag | 0x00000004, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ FloatingPoint) + }; + +protected: + Context m_regs; +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_ARM64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp new file mode 100644 index 000000000000..7681002c6fb8 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.cpp @@ -0,0 +1,96 @@ +//===-- RegisterContextMinidump_x86_32.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 "RegisterContextMinidump_x86_32.h" + +#include "lldb/Utility/DataBufferHeap.h" + +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +static void writeRegister(const void *reg_src, + llvm::MutableArrayRef<uint8_t> reg_dest) { + memcpy(reg_dest.data(), reg_src, reg_dest.size()); +} + +lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_32( + llvm::ArrayRef<uint8_t> source_data, + RegisterInfoInterface *target_reg_interface) { + + const RegisterInfo *reg_info = target_reg_interface->GetRegisterInfo(); + + lldb::WritableDataBufferSP result_context_buf( + new DataBufferHeap(target_reg_interface->GetGPRSize(), 0)); + uint8_t *result_base = result_context_buf->GetBytes(); + + if (source_data.size() < sizeof(MinidumpContext_x86_32)) + return nullptr; + + const MinidumpContext_x86_32 *context; + consumeObject(source_data, context); + + const MinidumpContext_x86_32_Flags context_flags = + static_cast<MinidumpContext_x86_32_Flags>( + static_cast<uint32_t>(context->context_flags)); + auto x86_32_Flag = MinidumpContext_x86_32_Flags::x86_32_Flag; + auto ControlFlag = MinidumpContext_x86_32_Flags::Control; + auto IntegerFlag = MinidumpContext_x86_32_Flags::Integer; + auto SegmentsFlag = MinidumpContext_x86_32_Flags::Segments; + + if ((context_flags & x86_32_Flag) != x86_32_Flag) { + return nullptr; + } + + if ((context_flags & ControlFlag) == ControlFlag) { + writeRegister(&context->ebp, + reg_info[lldb_ebp_i386].mutable_data(result_base)); + writeRegister(&context->eip, + reg_info[lldb_eip_i386].mutable_data(result_base)); + writeRegister(&context->cs, + reg_info[lldb_cs_i386].mutable_data(result_base)); + writeRegister(&context->eflags, + reg_info[lldb_eflags_i386].mutable_data(result_base)); + writeRegister(&context->esp, + reg_info[lldb_esp_i386].mutable_data(result_base)); + writeRegister(&context->ss, + reg_info[lldb_ss_i386].mutable_data(result_base)); + } + + if ((context_flags & SegmentsFlag) == SegmentsFlag) { + writeRegister(&context->ds, + reg_info[lldb_ds_i386].mutable_data(result_base)); + writeRegister(&context->es, + reg_info[lldb_es_i386].mutable_data(result_base)); + writeRegister(&context->fs, + reg_info[lldb_fs_i386].mutable_data(result_base)); + writeRegister(&context->gs, + reg_info[lldb_gs_i386].mutable_data(result_base)); + } + + if ((context_flags & IntegerFlag) == IntegerFlag) { + writeRegister(&context->eax, + reg_info[lldb_eax_i386].mutable_data(result_base)); + writeRegister(&context->ecx, + reg_info[lldb_ecx_i386].mutable_data(result_base)); + writeRegister(&context->edx, + reg_info[lldb_edx_i386].mutable_data(result_base)); + writeRegister(&context->ebx, + reg_info[lldb_ebx_i386].mutable_data(result_base)); + writeRegister(&context->esi, + reg_info[lldb_esi_i386].mutable_data(result_base)); + writeRegister(&context->edi, + reg_info[lldb_edi_i386].mutable_data(result_base)); + } + + // TODO parse the floating point registers + + return result_context_buf; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h new file mode 100644 index 000000000000..4dffc4f9db0e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_32.h @@ -0,0 +1,135 @@ +//===-- RegisterContextMinidump_x86_32.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_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_32_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_32_H + +#include "MinidumpTypes.h" + +#include "Plugins/Process/Utility/RegisterInfoInterface.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "lldb/Target/RegisterContext.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/Support/Endian.h" + +// C includes +// C++ includes + +namespace lldb_private { + +namespace minidump { + +// This function receives an ArrayRef pointing to the bytes of the Minidump +// register context and returns a DataBuffer that's ordered by the offsets +// specified in the RegisterInfoInterface argument +// This way we can reuse the already existing register contexts +lldb::DataBufferSP +ConvertMinidumpContext_x86_32(llvm::ArrayRef<uint8_t> source_data, + RegisterInfoInterface *target_reg_interface); + +// Reference: see breakpad/crashpad source or WinNT.h +struct MinidumpFloatingSaveAreaX86 { + llvm::support::ulittle32_t control_word; + llvm::support::ulittle32_t status_word; + llvm::support::ulittle32_t tag_word; + llvm::support::ulittle32_t error_offset; + llvm::support::ulittle32_t error_selector; + llvm::support::ulittle32_t data_offset; + llvm::support::ulittle32_t data_selector; + + enum { + RegisterAreaSize = 80, + }; + // register_area contains eight 80-bit (x87 "long double") quantities for + // floating-point registers %st0 (%mm0) through %st7 (%mm7). + uint8_t register_area[RegisterAreaSize]; + llvm::support::ulittle32_t cr0_npx_state; +}; + +struct MinidumpContext_x86_32 { + // The context_flags field determines which parts + // of the structure are populated (have valid values) + llvm::support::ulittle32_t context_flags; + + // The next 6 registers are included with + // MinidumpContext_x86_32_Flags::DebugRegisters + llvm::support::ulittle32_t dr0; + llvm::support::ulittle32_t dr1; + llvm::support::ulittle32_t dr2; + llvm::support::ulittle32_t dr3; + llvm::support::ulittle32_t dr6; + llvm::support::ulittle32_t dr7; + + // The next field is included with + // MinidumpContext_x86_32_Flags::FloatingPoint + MinidumpFloatingSaveAreaX86 float_save; + + // The next 4 registers are included with + // MinidumpContext_x86_32_Flags::Segments + llvm::support::ulittle32_t gs; + llvm::support::ulittle32_t fs; + llvm::support::ulittle32_t es; + llvm::support::ulittle32_t ds; + + // The next 6 registers are included with + // MinidumpContext_x86_32_Flags::Integer + llvm::support::ulittle32_t edi; + llvm::support::ulittle32_t esi; + llvm::support::ulittle32_t ebx; + llvm::support::ulittle32_t edx; + llvm::support::ulittle32_t ecx; + llvm::support::ulittle32_t eax; + + // The next 6 registers are included with + // MinidumpContext_x86_32_Flags::Control + llvm::support::ulittle32_t ebp; + llvm::support::ulittle32_t eip; + llvm::support::ulittle32_t cs; // WinNT.h says "must be sanitized" + llvm::support::ulittle32_t eflags; // WinNT.h says "must be sanitized" + llvm::support::ulittle32_t esp; + llvm::support::ulittle32_t ss; + + // The next field is included with + // MinidumpContext_x86_32_Flags::ExtendedRegisters + // It contains vector (MMX/SSE) registers. It is laid out in the + // format used by the fxsave and fsrstor instructions, so it includes + // a copy of the x87 floating-point registers as well. See FXSAVE in + // "Intel Architecture Software Developer's Manual, Volume 2." + enum { + ExtendedRegistersSize = 512, + }; + uint8_t extended_registers[ExtendedRegistersSize]; +}; + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +// For context_flags. These values indicate the type of +// context stored in the structure. The high 24 bits identify the CPU, the +// low 8 bits identify the type of context saved. +enum class MinidumpContext_x86_32_Flags : uint32_t { + x86_32_Flag = 0x00010000, // CONTEXT_i386, CONTEXT_i486 + Control = x86_32_Flag | 0x00000001, + Integer = x86_32_Flag | 0x00000002, + Segments = x86_32_Flag | 0x00000004, + FloatingPoint = x86_32_Flag | 0x00000008, + DebugRegisters = x86_32_Flag | 0x00000010, + ExtendedRegisters = x86_32_Flag | 0x00000020, + XState = x86_32_Flag | 0x00000040, + + Full = Control | Integer | Segments, + All = Full | FloatingPoint | DebugRegisters | ExtendedRegisters, + + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ All) +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_32_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp new file mode 100644 index 000000000000..917140cab297 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp @@ -0,0 +1,110 @@ +//===-- RegisterContextMinidump_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 +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMinidump_x86_64.h" + +#include "lldb/Utility/DataBufferHeap.h" + +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +static llvm::MutableArrayRef<uint8_t> getDestRegister(uint8_t *context, + const RegisterInfo ®) { + auto bytes = reg.mutable_data(context); + + switch (reg.kinds[lldb::eRegisterKindLLDB]) { + case lldb_cs_x86_64: + case lldb_ds_x86_64: + case lldb_es_x86_64: + case lldb_fs_x86_64: + case lldb_gs_x86_64: + case lldb_ss_x86_64: + return bytes.take_front(2); + break; + case lldb_rflags_x86_64: + return bytes.take_front(4); + break; + default: + return bytes.take_front(8); + break; + } +} + +static void writeRegister(const void *reg_src, uint8_t *context, + const RegisterInfo ®) { + llvm::MutableArrayRef<uint8_t> reg_dest = getDestRegister(context, reg); + memcpy(reg_dest.data(), reg_src, reg_dest.size()); +} + +lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContext_x86_64( + llvm::ArrayRef<uint8_t> source_data, + RegisterInfoInterface *target_reg_interface) { + + const RegisterInfo *reg_info = target_reg_interface->GetRegisterInfo(); + + lldb::WritableDataBufferSP result_context_buf( + new DataBufferHeap(target_reg_interface->GetGPRSize(), 0)); + uint8_t *result_base = result_context_buf->GetBytes(); + + if (source_data.size() < sizeof(MinidumpContext_x86_64)) + return nullptr; + + const MinidumpContext_x86_64 *context; + consumeObject(source_data, context); + + const MinidumpContext_x86_64_Flags context_flags = + static_cast<MinidumpContext_x86_64_Flags>( + static_cast<uint32_t>(context->context_flags)); + auto x86_64_Flag = MinidumpContext_x86_64_Flags::x86_64_Flag; + auto ControlFlag = MinidumpContext_x86_64_Flags::Control; + auto IntegerFlag = MinidumpContext_x86_64_Flags::Integer; + auto SegmentsFlag = MinidumpContext_x86_64_Flags::Segments; + + if ((context_flags & x86_64_Flag) != x86_64_Flag) + return nullptr; + + if ((context_flags & ControlFlag) == ControlFlag) { + writeRegister(&context->cs, result_base, reg_info[lldb_cs_x86_64]); + writeRegister(&context->ss, result_base, reg_info[lldb_ss_x86_64]); + writeRegister(&context->eflags, result_base, reg_info[lldb_rflags_x86_64]); + writeRegister(&context->rsp, result_base, reg_info[lldb_rsp_x86_64]); + writeRegister(&context->rip, result_base, reg_info[lldb_rip_x86_64]); + } + + if ((context_flags & SegmentsFlag) == SegmentsFlag) { + writeRegister(&context->ds, result_base, reg_info[lldb_ds_x86_64]); + writeRegister(&context->es, result_base, reg_info[lldb_es_x86_64]); + writeRegister(&context->fs, result_base, reg_info[lldb_fs_x86_64]); + writeRegister(&context->gs, result_base, reg_info[lldb_gs_x86_64]); + } + + if ((context_flags & IntegerFlag) == IntegerFlag) { + writeRegister(&context->rax, result_base, reg_info[lldb_rax_x86_64]); + writeRegister(&context->rcx, result_base, reg_info[lldb_rcx_x86_64]); + writeRegister(&context->rdx, result_base, reg_info[lldb_rdx_x86_64]); + writeRegister(&context->rbx, result_base, reg_info[lldb_rbx_x86_64]); + writeRegister(&context->rbp, result_base, reg_info[lldb_rbp_x86_64]); + writeRegister(&context->rsi, result_base, reg_info[lldb_rsi_x86_64]); + writeRegister(&context->rdi, result_base, reg_info[lldb_rdi_x86_64]); + writeRegister(&context->r8, result_base, reg_info[lldb_r8_x86_64]); + writeRegister(&context->r9, result_base, reg_info[lldb_r9_x86_64]); + writeRegister(&context->r10, result_base, reg_info[lldb_r10_x86_64]); + writeRegister(&context->r11, result_base, reg_info[lldb_r11_x86_64]); + writeRegister(&context->r12, result_base, reg_info[lldb_r12_x86_64]); + writeRegister(&context->r13, result_base, reg_info[lldb_r13_x86_64]); + writeRegister(&context->r14, result_base, reg_info[lldb_r14_x86_64]); + writeRegister(&context->r15, result_base, reg_info[lldb_r15_x86_64]); + } + + // TODO parse the floating point registers + + return result_context_buf; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h new file mode 100644 index 000000000000..d920ea9d823f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h @@ -0,0 +1,180 @@ +//===-- RegisterContextMinidump_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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_64_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_64_H + +#include "MinidumpTypes.h" + +#include "Plugins/Process/Utility/RegisterInfoInterface.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "lldb/Target/RegisterContext.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/Support/Endian.h" + +// C includes +// C++ includes + +namespace lldb_private { + +namespace minidump { + +// This function receives an ArrayRef pointing to the bytes of the Minidump +// register context and returns a DataBuffer that's ordered by the offsets +// specified in the RegisterInfoInterface argument +// This way we can reuse the already existing register contexts +lldb::DataBufferSP +ConvertMinidumpContext_x86_64(llvm::ArrayRef<uint8_t> source_data, + RegisterInfoInterface *target_reg_interface); + +struct Uint128 { + llvm::support::ulittle64_t high; + llvm::support::ulittle64_t low; +}; + +// Reference: see breakpad/crashpad source or WinNT.h +struct MinidumpXMMSaveArea32AMD64 { + llvm::support::ulittle16_t control_word; + llvm::support::ulittle16_t status_word; + uint8_t tag_word; + uint8_t reserved1; + llvm::support::ulittle16_t error_opcode; + llvm::support::ulittle32_t error_offset; + llvm::support::ulittle16_t error_selector; + llvm::support::ulittle16_t reserved2; + llvm::support::ulittle32_t data_offset; + llvm::support::ulittle16_t data_selector; + llvm::support::ulittle16_t reserved3; + llvm::support::ulittle32_t mx_csr; + llvm::support::ulittle32_t mx_csr_mask; + Uint128 float_registers[8]; + Uint128 xmm_registers[16]; + uint8_t reserved4[96]; +}; + +struct MinidumpContext_x86_64 { + // Register parameter home addresses. + llvm::support::ulittle64_t p1_home; + llvm::support::ulittle64_t p2_home; + llvm::support::ulittle64_t p3_home; + llvm::support::ulittle64_t p4_home; + llvm::support::ulittle64_t p5_home; + llvm::support::ulittle64_t p6_home; + + // The context_flags field determines which parts + // of the structure are populated (have valid values) + llvm::support::ulittle32_t context_flags; + llvm::support::ulittle32_t mx_csr; + + // The next register is included with + // MinidumpContext_x86_64_Flags::Control + llvm::support::ulittle16_t cs; + + // The next 4 registers are included with + // MinidumpContext_x86_64_Flags::Segments + llvm::support::ulittle16_t ds; + llvm::support::ulittle16_t es; + llvm::support::ulittle16_t fs; + llvm::support::ulittle16_t gs; + + // The next 2 registers are included with + // MinidumpContext_x86_64_Flags::Control + llvm::support::ulittle16_t ss; + llvm::support::ulittle32_t eflags; + + // The next 6 registers are included with + // MinidumpContext_x86_64_Flags::DebugRegisters + llvm::support::ulittle64_t dr0; + llvm::support::ulittle64_t dr1; + llvm::support::ulittle64_t dr2; + llvm::support::ulittle64_t dr3; + llvm::support::ulittle64_t dr6; + llvm::support::ulittle64_t dr7; + + // The next 4 registers are included with + // MinidumpContext_x86_64_Flags::Integer + llvm::support::ulittle64_t rax; + llvm::support::ulittle64_t rcx; + llvm::support::ulittle64_t rdx; + llvm::support::ulittle64_t rbx; + + // The next register is included with + // MinidumpContext_x86_64_Flags::Control + llvm::support::ulittle64_t rsp; + + // The next 11 registers are included with + // MinidumpContext_x86_64_Flags::Integer + llvm::support::ulittle64_t rbp; + llvm::support::ulittle64_t rsi; + llvm::support::ulittle64_t rdi; + llvm::support::ulittle64_t r8; + llvm::support::ulittle64_t r9; + llvm::support::ulittle64_t r10; + llvm::support::ulittle64_t r11; + llvm::support::ulittle64_t r12; + llvm::support::ulittle64_t r13; + llvm::support::ulittle64_t r14; + llvm::support::ulittle64_t r15; + + // The next register is included with + // MinidumpContext_x86_64_Flags::Control + llvm::support::ulittle64_t rip; + + // The next set of registers are included with + // MinidumpContext_x86_64_Flags:FloatingPoint + union FPR { + MinidumpXMMSaveArea32AMD64 flt_save; + struct { + Uint128 header[2]; + Uint128 legacy[8]; + Uint128 xmm[16]; + } sse_registers; + }; + + enum { + VRCount = 26, + }; + + Uint128 vector_register[VRCount]; + llvm::support::ulittle64_t vector_control; + + // The next 5 registers are included with + // MinidumpContext_x86_64_Flags::DebugRegisters + llvm::support::ulittle64_t debug_control; + llvm::support::ulittle64_t last_branch_to_rip; + llvm::support::ulittle64_t last_branch_from_rip; + llvm::support::ulittle64_t last_exception_to_rip; + llvm::support::ulittle64_t last_exception_from_rip; +}; + +// For context_flags. These values indicate the type of +// context stored in the structure. The high 24 bits identify the CPU, the +// low 8 bits identify the type of context saved. +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +enum class MinidumpContext_x86_64_Flags : uint32_t { + x86_64_Flag = 0x00100000, + Control = x86_64_Flag | 0x00000001, + Integer = x86_64_Flag | 0x00000002, + Segments = x86_64_Flag | 0x00000004, + FloatingPoint = x86_64_Flag | 0x00000008, + DebugRegisters = x86_64_Flag | 0x00000010, + XState = x86_64_Flag | 0x00000040, + + Full = Control | Integer | FloatingPoint, + All = Full | Segments | DebugRegisters, + + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ All) +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_REGISTERCONTEXTMINIDUMP_X86_64_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp new file mode 100644 index 000000000000..1fbc52815238 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp @@ -0,0 +1,118 @@ +//===-- ThreadMinidump.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 "ThreadMinidump.h" + +#include "ProcessMinidump.h" + +#include "RegisterContextMinidump_ARM.h" +#include "RegisterContextMinidump_ARM64.h" +#include "RegisterContextMinidump_x86_32.h" +#include "RegisterContextMinidump_x86_64.h" + +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h" +#include "Plugins/Process/elf-core/RegisterUtilities.h" + +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/Log.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +ThreadMinidump::ThreadMinidump(Process &process, const minidump::Thread &td, + llvm::ArrayRef<uint8_t> gpregset_data) + : Thread(process, td.ThreadId), m_thread_reg_ctx_sp(), + m_gpregset_data(gpregset_data) {} + +ThreadMinidump::~ThreadMinidump() = default; + +void ThreadMinidump::RefreshStateAfterStop() {} + +RegisterContextSP ThreadMinidump::GetRegisterContext() { + if (!m_reg_context_sp) { + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + } + return m_reg_context_sp; +} + +RegisterContextSP +ThreadMinidump::CreateRegisterContextForFrame(StackFrame *frame) { + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) { + if (m_thread_reg_ctx_sp) + return m_thread_reg_ctx_sp; + + ProcessMinidump *process = + static_cast<ProcessMinidump *>(GetProcess().get()); + ArchSpec arch = process->GetArchitecture(); + RegisterInfoInterface *reg_interface = nullptr; + + // TODO write other register contexts and add them here + switch (arch.GetMachine()) { + case llvm::Triple::x86: { + reg_interface = new RegisterContextLinux_i386(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_32(m_gpregset_data, reg_interface); + DataExtractor gpregset(buf, lldb::eByteOrderLittle, 4); + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>( + *this, reg_interface, gpregset, + llvm::ArrayRef<lldb_private::CoreNote>()); + break; + } + case llvm::Triple::x86_64: { + reg_interface = new RegisterContextLinux_x86_64(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContext_x86_64(m_gpregset_data, reg_interface); + DataExtractor gpregset(buf, lldb::eByteOrderLittle, 8); + m_thread_reg_ctx_sp = std::make_shared<RegisterContextCorePOSIX_x86_64>( + *this, reg_interface, gpregset, + llvm::ArrayRef<lldb_private::CoreNote>()); + break; + } + case llvm::Triple::aarch64: { + DataExtractor data(m_gpregset_data.data(), m_gpregset_data.size(), + lldb::eByteOrderLittle, 8); + m_thread_reg_ctx_sp = + std::make_shared<RegisterContextMinidump_ARM64>(*this, data); + break; + } + case llvm::Triple::arm: { + DataExtractor data(m_gpregset_data.data(), m_gpregset_data.size(), + lldb::eByteOrderLittle, 8); + const bool apple = arch.GetTriple().getVendor() == llvm::Triple::Apple; + m_thread_reg_ctx_sp = + std::make_shared<RegisterContextMinidump_ARM>(*this, data, apple); + break; + } + default: + break; + } + + reg_ctx_sp = m_thread_reg_ctx_sp; + } else if (m_unwinder_up) { + reg_ctx_sp = m_unwinder_up->CreateRegisterContextForFrame(frame); + } + + return reg_ctx_sp; +} + +bool ThreadMinidump::CalculateStopInfo() { return false; } diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h new file mode 100644 index 000000000000..aed7cfbc1b16 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ThreadMinidump.h @@ -0,0 +1,45 @@ +//===-- ThreadMinidump.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_SOURCE_PLUGINS_PROCESS_MINIDUMP_THREADMINIDUMP_H +#define LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_THREADMINIDUMP_H + +#include "MinidumpTypes.h" + +#include "lldb/Target/Thread.h" + + +namespace lldb_private { + +namespace minidump { + +class ThreadMinidump : public Thread { +public: + ThreadMinidump(Process &process, const minidump::Thread &td, + llvm::ArrayRef<uint8_t> gpregset_data); + + ~ThreadMinidump() override; + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + +protected: + lldb::RegisterContextSP m_thread_reg_ctx_sp; + llvm::ArrayRef<uint8_t> m_gpregset_data; + + bool CalculateStopInfo() override; +}; + +} // namespace minidump +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PROCESS_MINIDUMP_THREADMINIDUMP_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp new file mode 100644 index 000000000000..66f861350d14 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp @@ -0,0 +1,540 @@ +//===-- ScriptedProcess.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 "ScriptedProcess.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" + +#include "lldb/Host/OptionParser.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionGroupBoolean.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Queue.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/ScriptedMetadata.h" +#include "lldb/Utility/State.h" + +#include <mutex> + +LLDB_PLUGIN_DEFINE(ScriptedProcess) + +using namespace lldb; +using namespace lldb_private; + +llvm::StringRef ScriptedProcess::GetPluginDescriptionStatic() { + return "Scripted Process plug-in."; +} + +static constexpr lldb::ScriptLanguage g_supported_script_languages[] = { + ScriptLanguage::eScriptLanguagePython, +}; + +bool ScriptedProcess::IsScriptLanguageSupported(lldb::ScriptLanguage language) { + llvm::ArrayRef<lldb::ScriptLanguage> supported_languages = + llvm::ArrayRef(g_supported_script_languages); + + return llvm::is_contained(supported_languages, language); +} + +lldb::ProcessSP ScriptedProcess::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *file, + bool can_connect) { + if (!target_sp || + !IsScriptLanguageSupported(target_sp->GetDebugger().GetScriptLanguage())) + return nullptr; + + ScriptedMetadata scripted_metadata(target_sp->GetProcessLaunchInfo()); + + Status error; + auto process_sp = std::shared_ptr<ScriptedProcess>( + new ScriptedProcess(target_sp, listener_sp, scripted_metadata, error)); + + if (error.Fail() || !process_sp || !process_sp->m_interface_up) { + LLDB_LOGF(GetLog(LLDBLog::Process), "%s", error.AsCString()); + return nullptr; + } + + return process_sp; +} + +bool ScriptedProcess::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + return true; +} + +ScriptedProcess::ScriptedProcess(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const ScriptedMetadata &scripted_metadata, + Status &error) + : Process(target_sp, listener_sp), m_scripted_metadata(scripted_metadata) { + + if (!target_sp) { + error.SetErrorStringWithFormat("ScriptedProcess::%s () - ERROR: %s", + __FUNCTION__, "Invalid target"); + return; + } + + ScriptInterpreter *interpreter = + target_sp->GetDebugger().GetScriptInterpreter(); + + if (!interpreter) { + error.SetErrorStringWithFormat("ScriptedProcess::%s () - ERROR: %s", + __FUNCTION__, + "Debugger has no Script Interpreter"); + return; + } + + // Create process instance interface + m_interface_up = interpreter->CreateScriptedProcessInterface(); + if (!m_interface_up) { + error.SetErrorStringWithFormat( + "ScriptedProcess::%s () - ERROR: %s", __FUNCTION__, + "Script interpreter couldn't create Scripted Process Interface"); + return; + } + + ExecutionContext exe_ctx(target_sp, /*get_process=*/false); + + // Create process script object + auto obj_or_err = GetInterface().CreatePluginObject( + m_scripted_metadata.GetClassName(), exe_ctx, + m_scripted_metadata.GetArgsSP()); + + if (!obj_or_err) { + llvm::consumeError(obj_or_err.takeError()); + error.SetErrorString("Failed to create script object."); + return; + } + + StructuredData::GenericSP object_sp = *obj_or_err; + + if (!object_sp || !object_sp->IsValid()) { + error.SetErrorStringWithFormat("ScriptedProcess::%s () - ERROR: %s", + __FUNCTION__, + "Failed to create valid script object"); + return; + } +} + +ScriptedProcess::~ScriptedProcess() { + Clear(); + // If the interface is not valid, we can't call Finalize(). When that happens + // it means that the Scripted Process instanciation failed and the + // CreateProcess function returns a nullptr, so no one besides this class + // should have access to that bogus process object. + if (!m_interface_up) + return; + // We need to call finalize on the process before destroying ourselves to + // make sure all of the broadcaster cleanup goes as planned. If we destruct + // this class, then Process::~Process() might have problems trying to fully + // destroy the broadcaster. + Finalize(true /* destructing */); +} + +void ScriptedProcess::Initialize() { + static llvm::once_flag g_once_flag; + + llvm::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); + }); +} + +void ScriptedProcess::Terminate() { + PluginManager::UnregisterPlugin(ScriptedProcess::CreateInstance); +} + +Status ScriptedProcess::DoLoadCore() { + ProcessLaunchInfo launch_info = GetTarget().GetProcessLaunchInfo(); + + return DoLaunch(nullptr, launch_info); +} + +Status ScriptedProcess::DoLaunch(Module *exe_module, + ProcessLaunchInfo &launch_info) { + LLDB_LOGF(GetLog(LLDBLog::Process), "ScriptedProcess::%s launching process", __FUNCTION__); + + /* MARK: This doesn't reflect how lldb actually launches a process. + In reality, it attaches to debugserver, then resume the process. + That's not true in all cases. If debugserver is remote, lldb + asks debugserver to launch the process for it. */ + Status error = GetInterface().Launch(); + SetPrivateState(eStateStopped); + return error; +} + +void ScriptedProcess::DidLaunch() { m_pid = GetInterface().GetProcessID(); } + +void ScriptedProcess::DidResume() { + // Update the PID again, in case the user provided a placeholder pid at launch + m_pid = GetInterface().GetProcessID(); +} + +Status ScriptedProcess::DoResume() { + LLDB_LOGF(GetLog(LLDBLog::Process), "ScriptedProcess::%s resuming process", __FUNCTION__); + + return GetInterface().Resume(); +} + +Status ScriptedProcess::DoAttach(const ProcessAttachInfo &attach_info) { + Status error = GetInterface().Attach(attach_info); + SetPrivateState(eStateRunning); + SetPrivateState(eStateStopped); + if (error.Fail()) + return error; + // NOTE: We need to set the PID before finishing to attach otherwise we will + // hit an assert when calling the attach completion handler. + DidLaunch(); + + return {}; +} + +Status +ScriptedProcess::DoAttachToProcessWithID(lldb::pid_t pid, + const ProcessAttachInfo &attach_info) { + return DoAttach(attach_info); +} + +Status ScriptedProcess::DoAttachToProcessWithName( + const char *process_name, const ProcessAttachInfo &attach_info) { + return DoAttach(attach_info); +} + +void ScriptedProcess::DidAttach(ArchSpec &process_arch) { + process_arch = GetArchitecture(); +} + +Status ScriptedProcess::DoDestroy() { return Status(); } + +bool ScriptedProcess::IsAlive() { return GetInterface().IsAlive(); } + +size_t ScriptedProcess::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) { + lldb::DataExtractorSP data_extractor_sp = + GetInterface().ReadMemoryAtAddress(addr, size, error); + + if (!data_extractor_sp || !data_extractor_sp->GetByteSize() || error.Fail()) + return 0; + + offset_t bytes_copied = data_extractor_sp->CopyByteOrderedData( + 0, data_extractor_sp->GetByteSize(), buf, size, GetByteOrder()); + + if (!bytes_copied || bytes_copied == LLDB_INVALID_OFFSET) + return ScriptedInterface::ErrorWithMessage<size_t>( + LLVM_PRETTY_FUNCTION, "Failed to copy read memory to buffer.", error); + + // FIXME: We should use the diagnostic system to report a warning if the + // `bytes_copied` is different from `size`. + + return bytes_copied; +} + +size_t ScriptedProcess::DoWriteMemory(lldb::addr_t vm_addr, const void *buf, + size_t size, Status &error) { + lldb::DataExtractorSP data_extractor_sp = std::make_shared<DataExtractor>( + buf, size, GetByteOrder(), GetAddressByteSize()); + + if (!data_extractor_sp || !data_extractor_sp->GetByteSize()) + return 0; + + lldb::offset_t bytes_written = + GetInterface().WriteMemoryAtAddress(vm_addr, data_extractor_sp, error); + + if (!bytes_written || bytes_written == LLDB_INVALID_OFFSET) + return ScriptedInterface::ErrorWithMessage<size_t>( + LLVM_PRETTY_FUNCTION, "Failed to copy write buffer to memory.", error); + + // FIXME: We should use the diagnostic system to report a warning if the + // `bytes_written` is different from `size`. + + return bytes_written; +} + +Status ScriptedProcess::EnableBreakpointSite(BreakpointSite *bp_site) { + assert(bp_site != nullptr); + + if (bp_site->IsEnabled()) { + return {}; + } + + if (bp_site->HardwareRequired()) { + return Status("Scripted Processes don't support hardware breakpoints"); + } + + Status error; + GetInterface().CreateBreakpoint(bp_site->GetLoadAddress(), error); + + return error; +} + +ArchSpec ScriptedProcess::GetArchitecture() { + return GetTarget().GetArchitecture(); +} + +Status ScriptedProcess::DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo ®ion) { + Status error; + if (auto region_or_err = + GetInterface().GetMemoryRegionContainingAddress(load_addr, error)) + region = *region_or_err; + + return error; +} + +Status ScriptedProcess::GetMemoryRegions(MemoryRegionInfos ®ion_list) { + Status error; + lldb::addr_t address = 0; + + while (auto region_or_err = + GetInterface().GetMemoryRegionContainingAddress(address, error)) { + if (error.Fail()) + break; + + MemoryRegionInfo &mem_region = *region_or_err; + auto range = mem_region.GetRange(); + address += range.GetRangeBase() + range.GetByteSize(); + region_list.push_back(mem_region); + } + + return error; +} + +void ScriptedProcess::Clear() { Process::m_thread_list.Clear(); } + +bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) { + // TODO: Implement + // This is supposed to get the current set of threads, if any of them are in + // old_thread_list then they get copied to new_thread_list, and then any + // actually new threads will get added to new_thread_list. + m_thread_plans.ClearThreadCache(); + + Status error; + StructuredData::DictionarySP thread_info_sp = GetInterface().GetThreadsInfo(); + + if (!thread_info_sp) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + "Couldn't fetch thread list from Scripted Process.", error); + + // Because `StructuredData::Dictionary` uses a `std::map<ConstString, + // ObjectSP>` for storage, each item is sorted based on the key alphabetical + // order. Since `GetThreadsInfo` provides thread indices as the key element, + // thread info comes ordered alphabetically, instead of numerically, so we + // need to sort the thread indices before creating thread. + + StructuredData::ArraySP keys = thread_info_sp->GetKeys(); + + std::map<size_t, StructuredData::ObjectSP> sorted_threads; + auto sort_keys = [&sorted_threads, + &thread_info_sp](StructuredData::Object *item) -> bool { + if (!item) + return false; + + llvm::StringRef key = item->GetStringValue(); + size_t idx = 0; + + // Make sure the provided index is actually an integer + if (!llvm::to_integer(key, idx)) + return false; + + sorted_threads[idx] = thread_info_sp->GetValueForKey(key); + return true; + }; + + size_t thread_count = thread_info_sp->GetSize(); + + if (!keys->ForEach(sort_keys) || sorted_threads.size() != thread_count) + // Might be worth showing the unsorted thread list instead of return early. + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, "Couldn't sort thread list.", error); + + auto create_scripted_thread = + [this, &error, &new_thread_list]( + const std::pair<size_t, StructuredData::ObjectSP> pair) -> bool { + size_t idx = pair.first; + StructuredData::ObjectSP object_sp = pair.second; + + if (!object_sp) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, "Invalid thread info object", error); + + auto thread_or_error = + ScriptedThread::Create(*this, object_sp->GetAsGeneric()); + + if (!thread_or_error) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, toString(thread_or_error.takeError()), error); + + ThreadSP thread_sp = thread_or_error.get(); + lldbassert(thread_sp && "Couldn't initialize scripted thread."); + + RegisterContextSP reg_ctx_sp = thread_sp->GetRegisterContext(); + if (!reg_ctx_sp) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Invalid Register Context for thread " + llvm::Twine(idx)) + .str(), + error); + + new_thread_list.AddThread(thread_sp); + + return true; + }; + + llvm::for_each(sorted_threads, create_scripted_thread); + + return new_thread_list.GetSize(false) > 0; +} + +void ScriptedProcess::RefreshStateAfterStop() { + // Let all threads recover from stopping and do any clean up based on the + // previous thread state (if any). + m_thread_list.RefreshStateAfterStop(); +} + +bool ScriptedProcess::GetProcessInfo(ProcessInstanceInfo &info) { + info.Clear(); + info.SetProcessID(GetID()); + info.SetArchitecture(GetArchitecture()); + lldb::ModuleSP module_sp = GetTarget().GetExecutableModule(); + if (module_sp) { + const bool add_exe_file_as_first_arg = false; + info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(), + add_exe_file_as_first_arg); + } + return true; +} + +lldb_private::StructuredData::ObjectSP +ScriptedProcess::GetLoadedDynamicLibrariesInfos() { + Status error; + auto error_with_message = [&error](llvm::StringRef message) { + return ScriptedInterface::ErrorWithMessage<bool>(LLVM_PRETTY_FUNCTION, + message.data(), error); + }; + + StructuredData::ArraySP loaded_images_sp = GetInterface().GetLoadedImages(); + + if (!loaded_images_sp || !loaded_images_sp->GetSize()) + return ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>( + LLVM_PRETTY_FUNCTION, "No loaded images.", error); + + ModuleList module_list; + Target &target = GetTarget(); + + auto reload_image = [&target, &module_list, &error_with_message]( + StructuredData::Object *obj) -> bool { + StructuredData::Dictionary *dict = obj->GetAsDictionary(); + + if (!dict) + return error_with_message("Couldn't cast image object into dictionary."); + + ModuleSpec module_spec; + llvm::StringRef value; + + bool has_path = dict->HasKey("path"); + bool has_uuid = dict->HasKey("uuid"); + if (!has_path && !has_uuid) + return error_with_message("Dictionary should have key 'path' or 'uuid'"); + if (!dict->HasKey("load_addr")) + return error_with_message("Dictionary is missing key 'load_addr'"); + + if (has_path) { + dict->GetValueForKeyAsString("path", value); + module_spec.GetFileSpec().SetPath(value); + } + + if (has_uuid) { + dict->GetValueForKeyAsString("uuid", value); + module_spec.GetUUID().SetFromStringRef(value); + } + module_spec.GetArchitecture() = target.GetArchitecture(); + + ModuleSP module_sp = + target.GetOrCreateModule(module_spec, true /* notify */); + + if (!module_sp) + return error_with_message("Couldn't create or get module."); + + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + lldb::offset_t slide = LLDB_INVALID_OFFSET; + dict->GetValueForKeyAsInteger("load_addr", load_addr); + dict->GetValueForKeyAsInteger("slide", slide); + if (load_addr == LLDB_INVALID_ADDRESS) + return error_with_message( + "Couldn't get valid load address or slide offset."); + + if (slide != LLDB_INVALID_OFFSET) + load_addr += slide; + + bool changed = false; + module_sp->SetLoadAddress(target, load_addr, false /*=value_is_offset*/, + changed); + + if (!changed && !module_sp->GetObjectFile()) + return error_with_message("Couldn't set the load address for module."); + + dict->GetValueForKeyAsString("path", value); + FileSpec objfile(value); + module_sp->SetFileSpecAndObjectName(objfile, objfile.GetFilename()); + + return module_list.AppendIfNeeded(module_sp); + }; + + if (!loaded_images_sp->ForEach(reload_image)) + return ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>( + LLVM_PRETTY_FUNCTION, "Couldn't reload all images.", error); + + target.ModulesDidLoad(module_list); + + return loaded_images_sp; +} + +lldb_private::StructuredData::DictionarySP ScriptedProcess::GetMetadata() { + StructuredData::DictionarySP metadata_sp = GetInterface().GetMetadata(); + + Status error; + if (!metadata_sp || !metadata_sp->GetSize()) + return ScriptedInterface::ErrorWithMessage<StructuredData::DictionarySP>( + LLVM_PRETTY_FUNCTION, "No metadata.", error); + + return metadata_sp; +} + +void ScriptedProcess::UpdateQueueListIfNeeded() { + CheckScriptedInterface(); + for (ThreadSP thread_sp : Threads()) { + if (const char *queue_name = thread_sp->GetQueueName()) { + QueueSP queue_sp = std::make_shared<Queue>( + m_process->shared_from_this(), thread_sp->GetQueueID(), queue_name); + m_queue_list.AddQueue(queue_sp); + } + } +} + +ScriptedProcessInterface &ScriptedProcess::GetInterface() const { + CheckScriptedInterface(); + return *m_interface_up; +} + +void *ScriptedProcess::GetImplementation() { + StructuredData::GenericSP object_instance_sp = + GetInterface().GetScriptObjectInstance(); + if (object_instance_sp && + object_instance_sp->GetType() == eStructuredDataTypeGeneric) + return object_instance_sp->GetAsGeneric()->GetValue(); + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedProcess.h b/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedProcess.h new file mode 100644 index 000000000000..0335364b4010 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedProcess.h @@ -0,0 +1,136 @@ +//===-- ScriptedProcess.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_SOURCE_PLUGINS_SCRIPTED_PROCESS_H +#define LLDB_SOURCE_PLUGINS_SCRIPTED_PROCESS_H + +#include "lldb/Target/Process.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/ScriptedMetadata.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Status.h" + +#include "ScriptedThread.h" + +#include <mutex> + +namespace lldb_private { +class ScriptedProcess : public Process { +public: + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path, + bool can_connect); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "ScriptedProcess"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + ~ScriptedProcess() override; + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + DynamicLoader *GetDynamicLoader() override { return nullptr; } + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + Status DoLoadCore() override; + + Status DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) override; + + void DidLaunch() override; + + void DidResume() override; + + Status DoResume() override; + + Status DoAttachToProcessWithID(lldb::pid_t pid, + const ProcessAttachInfo &attach_info) override; + + Status + DoAttachToProcessWithName(const char *process_name, + const ProcessAttachInfo &attach_info) override; + + void DidAttach(ArchSpec &process_arch) override; + + Status DoDestroy() override; + + void RefreshStateAfterStop() override; + + bool IsAlive() override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; + + size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, + Status &error) override; + + Status EnableBreakpointSite(BreakpointSite *bp_site) override; + + ArchSpec GetArchitecture(); + + Status + GetMemoryRegions(lldb_private::MemoryRegionInfos ®ion_list) override; + + bool GetProcessInfo(ProcessInstanceInfo &info) override; + + lldb_private::StructuredData::ObjectSP + GetLoadedDynamicLibrariesInfos() override; + + lldb_private::StructuredData::DictionarySP GetMetadata() override; + + void UpdateQueueListIfNeeded() override; + + void *GetImplementation() override; + + void ForceScriptedState(lldb::StateType state) override { + // If we're about to stop, we should fetch the loaded dynamic libraries + // dictionary before emitting the private stop event to avoid having the + // module loading happen while the process state is changing. + if (StateIsStoppedState(state, true)) + GetLoadedDynamicLibrariesInfos(); + SetPrivateState(state); + } + +protected: + ScriptedProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const ScriptedMetadata &scripted_metadata, Status &error); + + void Clear(); + + bool DoUpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) override; + + Status DoGetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + Status DoAttach(const ProcessAttachInfo &attach_info); + +private: + friend class ScriptedThread; + + inline void CheckScriptedInterface() const { + lldbassert(m_interface_up && "Invalid scripted process interface."); + } + + ScriptedProcessInterface &GetInterface() const; + static bool IsScriptLanguageSupported(lldb::ScriptLanguage language); + + // Member variables. + const ScriptedMetadata m_scripted_metadata; + lldb::ScriptedProcessInterfaceUP m_interface_up; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_PROCESS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp new file mode 100644 index 000000000000..88a4ca3b0389 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -0,0 +1,370 @@ +//===-- ScriptedThread.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 "ScriptedThread.h" + +#include "Plugins/Process/Utility/RegisterContextThreadMemory.h" +#include "Plugins/Process/Utility/StopInfoMachException.h" +#include "lldb/Target/OperatingSystem.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include <memory> +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +void ScriptedThread::CheckInterpreterAndScriptObject() const { + lldbassert(m_script_object_sp && "Invalid Script Object."); + lldbassert(GetInterface() && "Invalid Scripted Thread Interface."); +} + +llvm::Expected<std::shared_ptr<ScriptedThread>> +ScriptedThread::Create(ScriptedProcess &process, + StructuredData::Generic *script_object) { + if (!process.IsValid()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Invalid scripted process."); + + process.CheckScriptedInterface(); + + auto scripted_thread_interface = + process.GetInterface().CreateScriptedThreadInterface(); + if (!scripted_thread_interface) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Failed to create scripted thread interface."); + + llvm::StringRef thread_class_name; + if (!script_object) { + std::optional<std::string> class_name = + process.GetInterface().GetScriptedThreadPluginName(); + if (!class_name || class_name->empty()) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Failed to get scripted thread class name."); + thread_class_name = *class_name; + } + + ExecutionContext exe_ctx(process); + auto obj_or_err = scripted_thread_interface->CreatePluginObject( + thread_class_name, exe_ctx, process.m_scripted_metadata.GetArgsSP(), + script_object); + + if (!obj_or_err) { + llvm::consumeError(obj_or_err.takeError()); + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Failed to create script object."); + } + + StructuredData::GenericSP owned_script_object_sp = *obj_or_err; + + if (!owned_script_object_sp->IsValid()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "Created script object is invalid."); + + lldb::tid_t tid = scripted_thread_interface->GetThreadID(); + + return std::make_shared<ScriptedThread>(process, scripted_thread_interface, + tid, owned_script_object_sp); +} + +ScriptedThread::ScriptedThread(ScriptedProcess &process, + ScriptedThreadInterfaceSP interface_sp, + lldb::tid_t tid, + StructuredData::GenericSP script_object_sp) + : Thread(process, tid), m_scripted_process(process), + m_scripted_thread_interface_sp(interface_sp), + m_script_object_sp(script_object_sp) {} + +ScriptedThread::~ScriptedThread() { DestroyThread(); } + +const char *ScriptedThread::GetName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> thread_name = GetInterface()->GetName(); + if (!thread_name) + return nullptr; + return ConstString(thread_name->c_str()).AsCString(); +} + +const char *ScriptedThread::GetQueueName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> queue_name = GetInterface()->GetQueue(); + if (!queue_name) + return nullptr; + return ConstString(queue_name->c_str()).AsCString(); +} + +void ScriptedThread::WillResume(StateType resume_state) {} + +void ScriptedThread::ClearStackFrames() { Thread::ClearStackFrames(); } + +RegisterContextSP ScriptedThread::GetRegisterContext() { + if (!m_reg_context_sp) + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + return m_reg_context_sp; +} + +RegisterContextSP +ScriptedThread::CreateRegisterContextForFrame(StackFrame *frame) { + const uint32_t concrete_frame_idx = + frame ? frame->GetConcreteFrameIndex() : 0; + + if (concrete_frame_idx) + return GetUnwinder().CreateRegisterContextForFrame(frame); + + lldb::RegisterContextSP reg_ctx_sp; + Status error; + + std::optional<std::string> reg_data = GetInterface()->GetRegisterContext(); + if (!reg_data) + return ScriptedInterface::ErrorWithMessage<lldb::RegisterContextSP>( + LLVM_PRETTY_FUNCTION, "Failed to get scripted thread registers data.", + error, LLDBLog::Thread); + + DataBufferSP data_sp( + std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size())); + + if (!data_sp->GetByteSize()) + return ScriptedInterface::ErrorWithMessage<lldb::RegisterContextSP>( + LLVM_PRETTY_FUNCTION, "Failed to copy raw registers data.", error, + LLDBLog::Thread); + + std::shared_ptr<RegisterContextMemory> reg_ctx_memory = + std::make_shared<RegisterContextMemory>( + *this, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS); + if (!reg_ctx_memory) + return ScriptedInterface::ErrorWithMessage<lldb::RegisterContextSP>( + LLVM_PRETTY_FUNCTION, "Failed to create a register context.", error, + LLDBLog::Thread); + + reg_ctx_memory->SetAllRegisterData(data_sp); + m_reg_context_sp = reg_ctx_memory; + + return m_reg_context_sp; +} + +bool ScriptedThread::LoadArtificialStackFrames() { + StructuredData::ArraySP arr_sp = GetInterface()->GetStackFrames(); + + Status error; + if (!arr_sp) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, "Failed to get scripted thread stackframes.", + error, LLDBLog::Thread); + + size_t arr_size = arr_sp->GetSize(); + if (arr_size > std::numeric_limits<uint32_t>::max()) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine( + "StackFrame array size (" + llvm::Twine(arr_size) + + llvm::Twine( + ") is greater than maximum authorized for a StackFrameList.")) + .str(), + error, LLDBLog::Thread); + + StackFrameListSP frames = GetStackFrameList(); + + for (size_t idx = 0; idx < arr_size; idx++) { + std::optional<StructuredData::Dictionary *> maybe_dict = + arr_sp->GetItemAtIndexAsDictionary(idx); + if (!maybe_dict) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine( + "Couldn't get artificial stackframe dictionary at index (" + + llvm::Twine(idx) + llvm::Twine(") from stackframe array.")) + .str(), + error, LLDBLog::Thread); + StructuredData::Dictionary *dict = *maybe_dict; + + lldb::addr_t pc; + if (!dict->GetValueForKeyAsInteger("pc", pc)) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + "Couldn't find value for key 'pc' in stackframe dictionary.", error, + LLDBLog::Thread); + + Address symbol_addr; + symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget()); + + lldb::addr_t cfa = LLDB_INVALID_ADDRESS; + bool cfa_is_valid = false; + const bool behaves_like_zeroth_frame = false; + SymbolContext sc; + symbol_addr.CalculateSymbolContext(&sc); + + StackFrameSP synth_frame_sp = std::make_shared<StackFrame>( + this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, + StackFrame::Kind::Artificial, behaves_like_zeroth_frame, &sc); + + if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp)) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't add frame (" + llvm::Twine(idx) + + llvm::Twine(") to ScriptedThread StackFrameList.")) + .str(), + error, LLDBLog::Thread); + } + + return true; +} + +bool ScriptedThread::CalculateStopInfo() { + StructuredData::DictionarySP dict_sp = GetInterface()->GetStopReason(); + + Status error; + if (!dict_sp) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, "Failed to get scripted thread stop info.", error, + LLDBLog::Thread); + + lldb::StopInfoSP stop_info_sp; + lldb::StopReason stop_reason_type; + + if (!dict_sp->GetValueForKeyAsInteger("type", stop_reason_type)) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + "Couldn't find value for key 'type' in stop reason dictionary.", error, + LLDBLog::Thread); + + StructuredData::Dictionary *data_dict; + if (!dict_sp->GetValueForKeyAsDictionary("data", data_dict)) + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + "Couldn't find value for key 'data' in stop reason dictionary.", error, + LLDBLog::Thread); + + switch (stop_reason_type) { + case lldb::eStopReasonNone: + return true; + case lldb::eStopReasonBreakpoint: { + lldb::break_id_t break_id; + data_dict->GetValueForKeyAsInteger("break_id", break_id, + LLDB_INVALID_BREAK_ID); + stop_info_sp = + StopInfo::CreateStopReasonWithBreakpointSiteID(*this, break_id); + } break; + case lldb::eStopReasonSignal: { + uint32_t signal; + llvm::StringRef description; + if (!data_dict->GetValueForKeyAsInteger("signal", signal)) { + signal = LLDB_INVALID_SIGNAL_NUMBER; + return false; + } + data_dict->GetValueForKeyAsString("desc", description); + stop_info_sp = + StopInfo::CreateStopReasonWithSignal(*this, signal, description.data()); + } break; + case lldb::eStopReasonTrace: { + stop_info_sp = StopInfo::CreateStopReasonToTrace(*this); + } break; + case lldb::eStopReasonException: { +#if defined(__APPLE__) + StructuredData::Dictionary *mach_exception; + if (data_dict->GetValueForKeyAsDictionary("mach_exception", + mach_exception)) { + llvm::StringRef value; + mach_exception->GetValueForKeyAsString("type", value); + auto exc_type = + StopInfoMachException::MachException::ExceptionCode(value.data()); + + if (!exc_type) + return false; + + uint32_t exc_data_size = 0; + llvm::SmallVector<uint64_t, 3> raw_codes; + + StructuredData::Array *exc_rawcodes; + mach_exception->GetValueForKeyAsArray("rawCodes", exc_rawcodes); + if (exc_rawcodes) { + auto fetch_data = [&raw_codes](StructuredData::Object *obj) { + if (!obj) + return false; + raw_codes.push_back(obj->GetUnsignedIntegerValue()); + return true; + }; + + exc_rawcodes->ForEach(fetch_data); + exc_data_size = raw_codes.size(); + } + + stop_info_sp = StopInfoMachException::CreateStopReasonWithMachException( + *this, *exc_type, exc_data_size, + exc_data_size >= 1 ? raw_codes[0] : 0, + exc_data_size >= 2 ? raw_codes[1] : 0, + exc_data_size >= 3 ? raw_codes[2] : 0); + + break; + } +#endif + stop_info_sp = + StopInfo::CreateStopReasonWithException(*this, "EXC_BAD_ACCESS"); + } break; + default: + return ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Unsupported stop reason type (" + + llvm::Twine(stop_reason_type) + llvm::Twine(").")) + .str(), + error, LLDBLog::Thread); + } + + if (!stop_info_sp) + return false; + + SetStopInfo(stop_info_sp); + return true; +} + +void ScriptedThread::RefreshStateAfterStop() { + GetRegisterContext()->InvalidateIfNeeded(/*force=*/false); + LoadArtificialStackFrames(); +} + +lldb::ScriptedThreadInterfaceSP ScriptedThread::GetInterface() const { + return m_scripted_thread_interface_sp; +} + +std::shared_ptr<DynamicRegisterInfo> ScriptedThread::GetDynamicRegisterInfo() { + CheckInterpreterAndScriptObject(); + + if (!m_register_info_sp) { + StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); + + Status error; + if (!reg_info) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, "Failed to get scripted thread registers info.", + error, LLDBLog::Thread); + + m_register_info_sp = DynamicRegisterInfo::Create( + *reg_info, m_scripted_process.GetTarget().GetArchitecture()); + } + + return m_register_info_sp; +} + +StructuredData::ObjectSP ScriptedThread::FetchThreadExtendedInfo() { + CheckInterpreterAndScriptObject(); + + Status error; + StructuredData::ArraySP extended_info_sp = GetInterface()->GetExtendedInfo(); + + if (!extended_info_sp || !extended_info_sp->GetSize()) + return ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>( + LLVM_PRETTY_FUNCTION, "No extended information found", error); + + return extended_info_sp; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedThread.h b/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedThread.h new file mode 100644 index 000000000000..cd224d60ceef --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Process/scripted/ScriptedThread.h @@ -0,0 +1,80 @@ +//===-- ScriptedThread.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_SOURCE_PLUGINS_SCRIPTED_THREAD_H +#define LLDB_SOURCE_PLUGINS_SCRIPTED_THREAD_H + +#include <string> + +#include "ScriptedProcess.h" + +#include "Plugins/Process/Utility/RegisterContextMemory.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target//DynamicRegisterInfo.h" +#include "lldb/Target/Thread.h" + +namespace lldb_private { +class ScriptedProcess; +} + +namespace lldb_private { + +class ScriptedThread : public lldb_private::Thread { + +public: + ScriptedThread(ScriptedProcess &process, + lldb::ScriptedThreadInterfaceSP interface_sp, lldb::tid_t tid, + StructuredData::GenericSP script_object_sp = nullptr); + + ~ScriptedThread() override; + + static llvm::Expected<std::shared_ptr<ScriptedThread>> + Create(ScriptedProcess &process, + StructuredData::Generic *script_object = nullptr); + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; + + bool LoadArtificialStackFrames(); + + bool CalculateStopInfo() override; + + const char *GetInfo() override { return nullptr; } + + const char *GetName() override; + + const char *GetQueueName() override; + + void WillResume(lldb::StateType resume_state) override; + + void RefreshStateAfterStop() override; + + void ClearStackFrames() override; + + StructuredData::ObjectSP FetchThreadExtendedInfo() override; + +private: + void CheckInterpreterAndScriptObject() const; + lldb::ScriptedThreadInterfaceSP GetInterface() const; + + ScriptedThread(const ScriptedThread &) = delete; + const ScriptedThread &operator=(const ScriptedThread &) = delete; + + std::shared_ptr<DynamicRegisterInfo> GetDynamicRegisterInfo(); + + const ScriptedProcess &m_scripted_process; + lldb::ScriptedThreadInterfaceSP m_scripted_thread_interface_sp = nullptr; + lldb_private::StructuredData::GenericSP m_script_object_sp = nullptr; + std::shared_ptr<DynamicRegisterInfo> m_register_info_sp = nullptr; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_THREAD_H |
