forked from OSchip/llvm-project
[lldb] Initial version of FreeBSD remote process plugin
Add a new FreeBSD Process plugin using client/server model. This plugin is based on the one used by NetBSD. It currently supports a subset of functionality for amd64. It is automatically used when spawning lldb-server. It can also be used by lldb client by setting FREEBSD_REMOTE_PLUGIN environment variable (to any value). The code is capable of debugging simple single-threaded programs. It supports general purpose, debug and FPU registers (up to XMM) of amd64, basic signalling, software breakpoints. Adding the support for the plugin involves removing some dead code from FreeBSDPlatform plugin (that was not ever used because CanDebugProcess() returned false), and replacing it with appropriate code from NetBSD platform support. Differential Revision: https://reviews.llvm.org/D88796
This commit is contained in:
parent
e547b1e243
commit
1a600266c3
|
@ -246,58 +246,15 @@ PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode(Target &target,
|
|||
}
|
||||
}
|
||||
|
||||
Status PlatformFreeBSD::LaunchProcess(ProcessLaunchInfo &launch_info) {
|
||||
Status error;
|
||||
if (IsHost()) {
|
||||
error = Platform::LaunchProcess(launch_info);
|
||||
} else {
|
||||
if (m_remote_platform_sp)
|
||||
error = m_remote_platform_sp->LaunchProcess(launch_info);
|
||||
else
|
||||
error.SetErrorString("the platform is not currently connected");
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
lldb::ProcessSP PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info,
|
||||
Debugger &debugger, Target *target,
|
||||
Status &error) {
|
||||
lldb::ProcessSP process_sp;
|
||||
if (IsHost()) {
|
||||
if (target == nullptr) {
|
||||
TargetSP new_target_sp;
|
||||
ArchSpec emptyArchSpec;
|
||||
|
||||
error = debugger.GetTargetList().CreateTarget(
|
||||
debugger, "", emptyArchSpec, eLoadDependentsNo, m_remote_platform_sp,
|
||||
new_target_sp);
|
||||
target = new_target_sp.get();
|
||||
} else
|
||||
error.Clear();
|
||||
|
||||
if (target && error.Success()) {
|
||||
debugger.GetTargetList().SetSelectedTarget(target);
|
||||
// The freebsd always currently uses the GDB remote debugger plug-in so
|
||||
// even when debugging locally we are debugging remotely! Just like the
|
||||
// darwin plugin.
|
||||
process_sp = target->CreateProcess(
|
||||
attach_info.GetListenerForProcess(debugger), "gdb-remote", nullptr);
|
||||
|
||||
if (process_sp)
|
||||
error = process_sp->Attach(attach_info);
|
||||
}
|
||||
} else {
|
||||
if (m_remote_platform_sp)
|
||||
process_sp =
|
||||
m_remote_platform_sp->Attach(attach_info, debugger, target, error);
|
||||
else
|
||||
error.SetErrorString("the platform is not currently connected");
|
||||
}
|
||||
return process_sp;
|
||||
}
|
||||
|
||||
// FreeBSD processes cannot yet be launched by spawning and attaching.
|
||||
bool PlatformFreeBSD::CanDebugProcess() {
|
||||
if (getenv("FREEBSD_REMOTE_PLUGIN")) {
|
||||
if (IsHost()) {
|
||||
return true;
|
||||
} else {
|
||||
// If we're connected, we can debug.
|
||||
return IsConnected();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,11 +49,6 @@ public:
|
|||
size_t GetSoftwareBreakpointTrapOpcode(Target &target,
|
||||
BreakpointSite *bp_site) override;
|
||||
|
||||
Status LaunchProcess(ProcessLaunchInfo &launch_info) override;
|
||||
|
||||
lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger,
|
||||
Target *target, Status &error) override;
|
||||
|
||||
void CalculateTrapHandlerSymbolNames() override;
|
||||
|
||||
MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr,
|
||||
|
|
|
@ -2,6 +2,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android")
|
|||
add_subdirectory(Linux)
|
||||
add_subdirectory(POSIX)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
add_subdirectory(FreeBSDRemote)
|
||||
add_subdirectory(FreeBSD)
|
||||
add_subdirectory(POSIX)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
|
||||
|
|
|
@ -79,12 +79,14 @@ ProcessFreeBSD::CreateInstance(lldb::TargetSP target_sp,
|
|||
}
|
||||
|
||||
void ProcessFreeBSD::Initialize() {
|
||||
static llvm::once_flag g_once_flag;
|
||||
if (!getenv("FREEBSD_REMOTE_PLUGIN")) {
|
||||
static llvm::once_flag g_once_flag;
|
||||
|
||||
llvm::call_once(g_once_flag, []() {
|
||||
PluginManager::RegisterPlugin(GetPluginNameStatic(),
|
||||
GetPluginDescriptionStatic(), CreateInstance);
|
||||
});
|
||||
llvm::call_once(g_once_flag, []() {
|
||||
PluginManager::RegisterPlugin(GetPluginNameStatic(),
|
||||
GetPluginDescriptionStatic(), CreateInstance);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
lldb_private::ConstString ProcessFreeBSD::GetPluginNameStatic() {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
add_lldb_library(lldbPluginProcessFreeBSDRemote
|
||||
NativeProcessFreeBSD.cpp
|
||||
NativeRegisterContextFreeBSD.cpp
|
||||
NativeRegisterContextFreeBSD_x86_64.cpp
|
||||
NativeThreadFreeBSD.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbHost
|
||||
lldbSymbol
|
||||
lldbTarget
|
||||
lldbUtility
|
||||
lldbPluginProcessPOSIX
|
||||
lldbPluginProcessUtility
|
||||
LINK_COMPONENTS
|
||||
Support
|
||||
)
|
|
@ -0,0 +1,781 @@
|
|||
//===-- 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;
|
||||
}
|
||||
|
||||
// Public Static Methods
|
||||
|
||||
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
|
||||
NativeProcessFreeBSD::Factory::Launch(ProcessLaunchInfo &launch_info,
|
||||
NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop) const {
|
||||
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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<NativeProcessFreeBSD> process_up(new NativeProcessFreeBSD(
|
||||
pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate,
|
||||
Info.GetArchitecture(), mainloop));
|
||||
|
||||
status = process_up->ReinitializeThreads();
|
||||
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::Factory::Attach(
|
||||
lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop) const {
|
||||
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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(), mainloop));
|
||||
|
||||
Status status = process_up->Attach();
|
||||
if (!status.Success())
|
||||
return status.ToError();
|
||||
|
||||
return std::move(process_up);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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 == SIGTRAP);
|
||||
|
||||
LLDB_LOG(log, "got SIGTRAP, pid = {0}, lwpid = {1}, si_code = {2}", pid,
|
||||
info.pl_lwpid, info.pl_siginfo.si_code);
|
||||
|
||||
NativeThreadFreeBSD *thread = nullptr;
|
||||
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());
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
switch (info.pl_siginfo.si_code) {
|
||||
case TRAP_BRKPT:
|
||||
if (thread) {
|
||||
thread->SetStoppedByBreakpoint();
|
||||
FixupBreakpointPCAsNeeded(*thread);
|
||||
}
|
||||
SetState(StateType::eStateStopped, true);
|
||||
break;
|
||||
case TRAP_TRACE:
|
||||
if (thread)
|
||||
thread->SetStoppedByTrace();
|
||||
SetState(StateType::eStateStopped, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NativeProcessFreeBSD::MonitorSignal(lldb::pid_t pid, int signal) {
|
||||
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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);
|
||||
else
|
||||
thread.SetStoppedWithNoReason();
|
||||
}
|
||||
SetState(StateType::eStateStopped, true);
|
||||
}
|
||||
|
||||
Status NativeProcessFreeBSD::PtraceWrapper(int req, lldb::pid_t pid, void *addr,
|
||||
int data, int *result) {
|
||||
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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.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;
|
||||
}
|
||||
|
||||
Status NativeProcessFreeBSD::Resume(const ResumeActionList &resume_actions) {
|
||||
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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;
|
||||
|
||||
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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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 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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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();
|
||||
}
|
||||
|
||||
Status NativeProcessFreeBSD::AllocateMemory(size_t size, uint32_t permissions,
|
||||
lldb::addr_t &addr) {
|
||||
return Status("Unimplemented");
|
||||
}
|
||||
|
||||
Status NativeProcessFreeBSD::DeallocateMemory(lldb::addr_t addr) {
|
||||
return Status("Unimplemented");
|
||||
}
|
||||
|
||||
lldb::addr_t NativeProcessFreeBSD::GetSharedLibraryInfoAddress() {
|
||||
// punt on this for now
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
size_t NativeProcessFreeBSD::UpdateThreads() { return m_threads.size(); }
|
||||
|
||||
Status NativeProcessFreeBSD::SetBreakpoint(lldb::addr_t addr, uint32_t size,
|
||||
bool hardware) {
|
||||
if (hardware)
|
||||
return Status("NativeProcessFreeBSD does not support hardware breakpoints");
|
||||
else
|
||||
return SetSoftwareBreakpoint(addr, size);
|
||||
}
|
||||
|
||||
Status NativeProcessFreeBSD::GetLoadedModuleFileSpec(const char *module_path,
|
||||
FileSpec &file_spec) {
|
||||
return Status("Unimplemented");
|
||||
}
|
||||
|
||||
Status
|
||||
NativeProcessFreeBSD::GetFileLoadAddress(const llvm::StringRef &file_name,
|
||||
lldb::addr_t &load_addr) {
|
||||
load_addr = LLDB_INVALID_ADDRESS;
|
||||
return Status();
|
||||
}
|
||||
|
||||
void NativeProcessFreeBSD::SigchldHandler() {
|
||||
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
|
||||
// Process all pending waitpid notifications.
|
||||
int status;
|
||||
::pid_t wait_pid =
|
||||
llvm::sys::RetryAfterSignal(-1, waitpid, GetID(), &status, WNOHANG);
|
||||
|
||||
if (wait_pid == 0)
|
||||
return; // We are done.
|
||||
|
||||
if (wait_pid == -1) {
|
||||
Status error(errno, eErrorTypePOSIX);
|
||||
LLDB_LOG(log, "waitpid ({0}, &status, _) failed: {1}", GetID(), error);
|
||||
}
|
||||
|
||||
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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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 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 */
|
||||
status = ReinitializeThreads();
|
||||
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.
|
||||
SetState(StateType::eStateStopped);
|
||||
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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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::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;
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
//===-- 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 "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 {
|
||||
public:
|
||||
class Factory : public NativeProcessProtocol::Factory {
|
||||
public:
|
||||
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
|
||||
Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop) const override;
|
||||
|
||||
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
|
||||
Attach(lldb::pid_t pid, NativeDelegate &native_delegate,
|
||||
MainLoop &mainloop) 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;
|
||||
|
||||
Status AllocateMemory(size_t size, uint32_t permissions,
|
||||
lldb::addr_t &addr) override;
|
||||
|
||||
Status DeallocateMemory(lldb::addr_t addr) 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;
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
MainLoop::SignalHandleUP m_sigchld_handle;
|
||||
ArchSpec m_arch;
|
||||
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);
|
||||
|
||||
Status PopulateMemoryRegionCache();
|
||||
void SigchldHandler();
|
||||
|
||||
Status Attach();
|
||||
Status ReinitializeThreads();
|
||||
};
|
||||
|
||||
} // namespace process_freebsd
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // #ifndef liblldb_NativeProcessFreeBSD_H_
|
|
@ -0,0 +1,39 @@
|
|||
//===-- 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/FreeBSDRemote/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
|
||||
|
||||
NativeRegisterContextFreeBSD::NativeRegisterContextFreeBSD(
|
||||
NativeThreadProtocol &native_thread,
|
||||
RegisterInfoInterface *reg_info_interface_p)
|
||||
: NativeRegisterContextRegisterInfo(native_thread, reg_info_interface_p) {}
|
||||
|
||||
Status NativeRegisterContextFreeBSD::DoRegisterSet(int ptrace_req, void *buf) {
|
||||
return NativeProcessFreeBSD::PtraceWrapper(ptrace_req, GetProcessPid(), buf,
|
||||
m_thread.GetID());
|
||||
}
|
||||
|
||||
NativeProcessFreeBSD &NativeRegisterContextFreeBSD::GetProcess() {
|
||||
return static_cast<NativeProcessFreeBSD &>(m_thread.GetProcess());
|
||||
}
|
||||
|
||||
::pid_t NativeRegisterContextFreeBSD::GetProcessPid() {
|
||||
return GetProcess().GetID();
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
//===-- 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 "lldb/Host/common/NativeThreadProtocol.h"
|
||||
|
||||
#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
|
||||
|
||||
namespace lldb_private {
|
||||
namespace process_freebsd {
|
||||
|
||||
class NativeProcessFreeBSD;
|
||||
|
||||
class NativeRegisterContextFreeBSD : public NativeRegisterContextRegisterInfo {
|
||||
public:
|
||||
NativeRegisterContextFreeBSD(NativeThreadProtocol &native_thread,
|
||||
RegisterInfoInterface *reg_info_interface_p);
|
||||
|
||||
// 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,
|
||||
NativeThreadProtocol &native_thread);
|
||||
virtual Status
|
||||
CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) = 0;
|
||||
|
||||
virtual Status ClearWatchpointHit(uint32_t wp_index) = 0;
|
||||
|
||||
protected:
|
||||
Status DoRegisterSet(int req, void *buf);
|
||||
virtual NativeProcessFreeBSD &GetProcess();
|
||||
virtual ::pid_t GetProcessPid();
|
||||
};
|
||||
|
||||
} // namespace process_freebsd
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // #ifndef lldb_NativeRegisterContextFreeBSD_h
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,101 @@
|
|||
//===-- 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 "Plugins/Process/FreeBSDRemote/NativeRegisterContextFreeBSD.h"
|
||||
#include "Plugins/Process/Utility/RegisterContext_x86.h"
|
||||
#include "Plugins/Process/Utility/lldb-x86-register-enums.h"
|
||||
|
||||
namespace lldb_private {
|
||||
namespace process_freebsd {
|
||||
|
||||
class NativeProcessFreeBSD;
|
||||
|
||||
class NativeRegisterContextFreeBSD_x86_64
|
||||
: public NativeRegisterContextFreeBSD {
|
||||
public:
|
||||
NativeRegisterContextFreeBSD_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::DataBufferSP &data_sp) override;
|
||||
|
||||
Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
|
||||
|
||||
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;
|
||||
|
||||
Status
|
||||
CopyHardwareWatchpointsFrom(NativeRegisterContextFreeBSD &source) override;
|
||||
|
||||
private:
|
||||
// Private member types.
|
||||
enum { GPRegSet, FPRegSet, DBRegSet };
|
||||
|
||||
// Private member variables.
|
||||
struct reg m_gpr;
|
||||
#if defined(__x86_64__)
|
||||
struct fpreg m_fpr;
|
||||
#else
|
||||
struct xmmreg m_fpr;
|
||||
#endif
|
||||
struct dbreg m_dbr;
|
||||
|
||||
int GetSetForNativeRegNum(int reg_num) const;
|
||||
int GetDR(int num) const;
|
||||
|
||||
Status ReadRegisterSet(uint32_t set);
|
||||
Status WriteRegisterSet(uint32_t set);
|
||||
};
|
||||
|
||||
} // namespace process_freebsd
|
||||
} // namespace lldb_private
|
||||
|
||||
#endif // #ifndef lldb_NativeRegisterContextFreeBSD_x86_64_h
|
||||
|
||||
#endif // defined(__x86_64__)
|
|
@ -0,0 +1,216 @@
|
|||
//===-- 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>
|
||||
// clang-format on
|
||||
|
||||
#include <sstream>
|
||||
|
||||
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, m_process.GetID(),
|
||||
nullptr, GetID());
|
||||
if (!ret.Success())
|
||||
return ret;
|
||||
ret = NativeProcessFreeBSD::PtraceWrapper(PT_CLEARSTEP, m_process.GetID(),
|
||||
nullptr, GetID());
|
||||
if (ret.Success())
|
||||
SetRunning();
|
||||
return ret;
|
||||
}
|
||||
|
||||
Status NativeThreadFreeBSD::SingleStep() {
|
||||
Status ret = NativeProcessFreeBSD::PtraceWrapper(PT_RESUME, m_process.GetID(),
|
||||
nullptr, GetID());
|
||||
if (!ret.Success())
|
||||
return ret;
|
||||
ret = NativeProcessFreeBSD::PtraceWrapper(PT_SETSTEP, m_process.GetID(),
|
||||
nullptr, GetID());
|
||||
if (ret.Success())
|
||||
SetStepping();
|
||||
return ret;
|
||||
}
|
||||
|
||||
Status NativeThreadFreeBSD::Suspend() {
|
||||
Status ret = NativeProcessFreeBSD::PtraceWrapper(
|
||||
PT_SUSPEND, m_process.GetID(), nullptr, GetID());
|
||||
if (ret.Success())
|
||||
SetStopped();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void NativeThreadFreeBSD::SetStoppedBySignal(uint32_t signo,
|
||||
const siginfo_t *info) {
|
||||
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD));
|
||||
LLDB_LOG(log, "tid = {0} in called with signal {1}", GetID(), signo);
|
||||
|
||||
SetStopped();
|
||||
|
||||
m_stop_info.reason = StopReason::eStopReasonSignal;
|
||||
m_stop_info.details.signal.signo = signo;
|
||||
|
||||
m_stop_description.clear();
|
||||
if (info) {
|
||||
switch (signo) {
|
||||
case SIGSEGV:
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGILL:
|
||||
const auto reason = GetCrashReason(*info);
|
||||
m_stop_description = GetCrashReasonString(reason, *info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NativeThreadFreeBSD::SetStoppedByBreakpoint() {
|
||||
SetStopped();
|
||||
m_stop_info.reason = StopReason::eStopReasonBreakpoint;
|
||||
m_stop_info.details.signal.signo = SIGTRAP;
|
||||
}
|
||||
|
||||
void NativeThreadFreeBSD::SetStoppedByTrace() {
|
||||
SetStopped();
|
||||
m_stop_info.reason = StopReason::eStopReasonTrace;
|
||||
m_stop_info.details.signal.signo = SIGTRAP;
|
||||
}
|
||||
|
||||
void NativeThreadFreeBSD::SetStoppedByExec() {
|
||||
SetStopped();
|
||||
m_stop_info.reason = StopReason::eStopReasonExec;
|
||||
m_stop_info.details.signal.signo = SIGTRAP;
|
||||
}
|
||||
|
||||
void NativeThreadFreeBSD::SetStoppedByWatchpoint(uint32_t wp_index) {
|
||||
SetStopped();
|
||||
}
|
||||
|
||||
void NativeThreadFreeBSD::SetStoppedWithNoReason() {
|
||||
SetStopped();
|
||||
|
||||
m_stop_info.reason = StopReason::eStopReasonNone;
|
||||
m_stop_info.details.signal.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() { return ""; }
|
||||
|
||||
lldb::StateType NativeThreadFreeBSD::GetState() { return m_state; }
|
||||
|
||||
bool NativeThreadFreeBSD::GetStopReason(ThreadStopInfo &stop_info,
|
||||
std::string &description) {
|
||||
Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_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) {
|
||||
return Status("not implemented");
|
||||
}
|
||||
|
||||
Status NativeThreadFreeBSD::RemoveWatchpoint(lldb::addr_t addr) {
|
||||
auto wp = m_watchpoint_index_map.find(addr);
|
||||
if (wp == m_watchpoint_index_map.end())
|
||||
return Status();
|
||||
return Status("not implemented");
|
||||
}
|
||||
|
||||
Status NativeThreadFreeBSD::SetHardwareBreakpoint(lldb::addr_t addr,
|
||||
size_t size) {
|
||||
if (m_state == eStateLaunching)
|
||||
return Status();
|
||||
|
||||
Status error = RemoveHardwareBreakpoint(addr);
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
return Status("not implemented");
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
return Status("not implemented");
|
||||
}
|
||||
|
||||
Status NativeThreadFreeBSD::CopyWatchpointsFrom(NativeThreadFreeBSD &source) {
|
||||
return Status("not implemented");
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
//===-- 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/FreeBSDRemote/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;
|
||||
|
||||
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 SetStoppedWithNoReason();
|
||||
void SetStopped();
|
||||
void SetRunning();
|
||||
void SetStepping();
|
||||
|
||||
Status 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_
|
|
@ -843,7 +843,7 @@ GDBRemoteCommunicationServerCommon::Handle_qSupported(
|
|||
response.PutCString(";QListThreadsInStopReply+");
|
||||
response.PutCString(";qEcho+");
|
||||
response.PutCString(";qXfer:features:read+");
|
||||
#if defined(__linux__) || defined(__NetBSD__)
|
||||
#if defined(__linux__) || defined(__NetBSD__) || defined(__FreeBSD__)
|
||||
response.PutCString(";QPassSignals+");
|
||||
response.PutCString(";qXfer:auxv:read+");
|
||||
response.PutCString(";qXfer:libraries-svr4:read+");
|
||||
|
|
|
@ -133,3 +133,6 @@ if platform.system() == 'NetBSD' and os.geteuid() != 0:
|
|||
can_set_dbregs = False
|
||||
if can_set_dbregs:
|
||||
config.available_features.add('dbregs-set')
|
||||
|
||||
# pass control variable through
|
||||
llvm_config.with_system_environment('FREEBSD_REMOTE_PLUGIN')
|
||||
|
|
|
@ -4,6 +4,12 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux|Android")
|
|||
list(APPEND LLDB_PLUGINS lldbPluginProcessLinux)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
list(APPEND LLDB_PLUGINS
|
||||
lldbPluginProcessFreeBSDRemote
|
||||
lldbPluginProcessFreeBSD)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
|
||||
list(APPEND LLDB_PLUGINS lldbPluginProcessNetBSD)
|
||||
endif()
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
|
||||
#if defined(__linux__)
|
||||
#include "Plugins/Process/Linux/NativeProcessLinux.h"
|
||||
#elif defined(__FreeBSD__)
|
||||
#include "Plugins/Process/FreeBSDRemote/NativeProcessFreeBSD.h"
|
||||
#elif defined(__NetBSD__)
|
||||
#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"
|
||||
#elif defined(_WIN32)
|
||||
|
@ -61,6 +63,8 @@ using namespace lldb_private::process_gdb_remote;
|
|||
namespace {
|
||||
#if defined(__linux__)
|
||||
typedef process_linux::NativeProcessLinux::Factory NativeProcessFactory;
|
||||
#elif defined(__FreeBSD__)
|
||||
typedef process_freebsd::NativeProcessFreeBSD::Factory NativeProcessFactory;
|
||||
#elif defined(__NetBSD__)
|
||||
typedef process_netbsd::NativeProcessNetBSD::Factory NativeProcessFactory;
|
||||
#elif defined(_WIN32)
|
||||
|
|
Loading…
Reference in New Issue