[lldb] [llvm] [lldb] [lldb-server] Introduce Host/qnx and NativeProcessQNX (PR #97630)
Ayush Sahay via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 3 13:21:01 PDT 2024
https://github.com/ayushsahay1837 created https://github.com/llvm/llvm-project/pull/97630
This change provisions the _QNX_ host and process plugins, and is the fifth and final in a series of changes that look to facilitate remote debug of _AArch64_ targets on _QNX_.
_QNX Neutrino Real-Time Operating System_ is a commercial Unix-like real-time operating system primarily targeting the embedded systems market including automotive, medical devices, robotics, transportation, and industrial embedded systems.
The series of changes in question looks to provision support for –
- Launching a debuggee
- Attaching to a debuggee
- Having the debuggee come up stopped at the entry point
- Setting breakpoints
- Stopping at breakpoints
- Reading/writing contents of/to the debuggee's memory
- Reading/writing contents of/to the debuggee's registers
- Reading/writing contents of/to the debuggee's variables
- Resuming the debuggee's execution
- Single-stepping the debuggee's execution
- Interrupting the debuggee's execution
- Dumping information pertaining to the debuggee's stack trace
Kindly note that _ptrace_ isn't available on _QNX_. Instead, _devctl_ can be leveraged to observe and control the execution of a process under debug on _QNX_.
Any additional support (including the facilitation of execution of tests) will be the subject of future work.
>From 3452438c5abc7b1e00548bce2142ece395753fc2 Mon Sep 17 00:00:00 2001
From: Ayush Sahay <quic_asahay at quicinc.com>
Date: Thu, 11 Jan 2024 20:00:02 +0530
Subject: [PATCH] [lldb] [lldb-server] Introduce Host/qnx and NativeProcessQNX
Provision the QNX host and process plugins.
---
lldb/cmake/modules/LLDBConfig.cmake | 2 +-
lldb/include/lldb/Host/HostInfo.h | 3 +
lldb/include/lldb/Host/posix/MainLoopPosix.h | 20 +-
lldb/include/lldb/Host/qnx/HostInfoQNX.h | 24 +
lldb/include/lldb/Host/qnx/Support.h | 33 +
lldb/source/Host/CMakeLists.txt | 7 +
lldb/source/Host/common/ProcessLaunchInfo.cpp | 3 +
lldb/source/Host/common/PseudoTerminal.cpp | 24 +
lldb/source/Host/common/TCPSocket.cpp | 3 +
lldb/source/Host/posix/MainLoopPosix.cpp | 58 +-
.../Host/posix/ProcessLauncherPosixFork.cpp | 192 ++-
lldb/source/Host/qnx/Host.cpp | 183 +++
lldb/source/Host/qnx/HostInfoQNX.cpp | 22 +
lldb/source/Host/qnx/Support.cpp | 41 +
lldb/source/Plugins/Process/CMakeLists.txt | 3 +
.../source/Plugins/Process/QNX/CMakeLists.txt | 16 +
.../Plugins/Process/QNX/NativeProcessQNX.cpp | 1066 +++++++++++++++++
.../Plugins/Process/QNX/NativeProcessQNX.h | 157 +++
.../Process/QNX/NativeRegisterContextQNX.cpp | 12 +
.../Process/QNX/NativeRegisterContextQNX.h | 37 +
.../QNX/NativeRegisterContextQNX_arm64.cpp | 344 ++++++
.../QNX/NativeRegisterContextQNX_arm64.h | 66 +
.../Plugins/Process/QNX/NativeThreadQNX.cpp | 141 +++
.../Plugins/Process/QNX/NativeThreadQNX.h | 73 ++
.../Plugins/Process/Utility/CMakeLists.txt | 1 +
.../Plugins/Process/Utility/QNXSignals.cpp | 140 +++
.../Plugins/Process/Utility/QNXSignals.h | 27 +
lldb/source/Target/UnixSignals.cpp | 3 +
lldb/source/Utility/CMakeLists.txt | 4 +
lldb/tools/lldb-server/CMakeLists.txt | 4 +
lldb/tools/lldb-server/lldb-gdbserver.cpp | 4 +
llvm/include/llvm/Support/ExitCodes.h | 10 +-
llvm/lib/Support/Unix/Path.inc | 12 +-
llvm/lib/Support/Unix/Signals.inc | 6 +
34 files changed, 2725 insertions(+), 16 deletions(-)
create mode 100644 lldb/include/lldb/Host/qnx/HostInfoQNX.h
create mode 100644 lldb/include/lldb/Host/qnx/Support.h
create mode 100644 lldb/source/Host/qnx/Host.cpp
create mode 100644 lldb/source/Host/qnx/HostInfoQNX.cpp
create mode 100644 lldb/source/Host/qnx/Support.cpp
create mode 100644 lldb/source/Plugins/Process/QNX/CMakeLists.txt
create mode 100644 lldb/source/Plugins/Process/QNX/NativeProcessQNX.cpp
create mode 100644 lldb/source/Plugins/Process/QNX/NativeProcessQNX.h
create mode 100644 lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX.cpp
create mode 100644 lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX.h
create mode 100644 lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX_arm64.cpp
create mode 100644 lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX_arm64.h
create mode 100644 lldb/source/Plugins/Process/QNX/NativeThreadQNX.cpp
create mode 100644 lldb/source/Plugins/Process/QNX/NativeThreadQNX.h
create mode 100644 lldb/source/Plugins/Process/Utility/QNXSignals.cpp
create mode 100644 lldb/source/Plugins/Process/Utility/QNXSignals.h
diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake
index a60921990cf77..139f1c98bd5a1 100644
--- a/lldb/cmake/modules/LLDBConfig.cmake
+++ b/lldb/cmake/modules/LLDBConfig.cmake
@@ -299,7 +299,7 @@ endif()
# Figure out if lldb could use lldb-server. If so, then we'll
# ensure we build lldb-server when an lldb target is being built.
-if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD|Windows")
+if (CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|FreeBSD|Linux|NetBSD|Windows|QNX")
set(LLDB_CAN_USE_LLDB_SERVER ON)
else()
set(LLDB_CAN_USE_LLDB_SERVER OFF)
diff --git a/lldb/include/lldb/Host/HostInfo.h b/lldb/include/lldb/Host/HostInfo.h
index b7010d69d88e7..82fa911ff1204 100644
--- a/lldb/include/lldb/Host/HostInfo.h
+++ b/lldb/include/lldb/Host/HostInfo.h
@@ -55,6 +55,9 @@
#elif defined(__APPLE__)
#include "lldb/Host/macosx/HostInfoMacOSX.h"
#define HOST_INFO_TYPE HostInfoMacOSX
+#elif defined(__QNX__)
+#include "lldb/Host/qnx/HostInfoQNX.h"
+#define HOST_INFO_TYPE HostInfoQNX
#else
#include "lldb/Host/posix/HostInfoPosix.h"
#define HOST_INFO_TYPE HostInfoPosix
diff --git a/lldb/include/lldb/Host/posix/MainLoopPosix.h b/lldb/include/lldb/Host/posix/MainLoopPosix.h
index 07497b7b8c259..6ff1c71f4d0c0 100644
--- a/lldb/include/lldb/Host/posix/MainLoopPosix.h
+++ b/lldb/include/lldb/Host/posix/MainLoopPosix.h
@@ -63,15 +63,30 @@ class MainLoopPosix : public MainLoopBase {
class SignalHandle {
public:
~SignalHandle() { m_mainloop.UnregisterSignal(m_signo, m_callback_it); }
+#if defined(__QNX__)
+ std::weak_ptr<siginfo_t> GetSiginfo() const { return m_siginfo; }
+#endif
private:
SignalHandle(MainLoopPosix &mainloop, int signo,
+#if defined(__QNX__)
+ std::list<Callback>::iterator callback_it,
+ std::weak_ptr<siginfo_t> siginfo)
+ : m_mainloop(mainloop), m_signo(signo), m_callback_it(callback_it),
+ m_siginfo(siginfo) {
+ }
+#else
std::list<Callback>::iterator callback_it)
- : m_mainloop(mainloop), m_signo(signo), m_callback_it(callback_it) {}
+ : m_mainloop(mainloop), m_signo(signo), m_callback_it(callback_it) {
+ }
+#endif
MainLoopPosix &m_mainloop;
int m_signo;
std::list<Callback>::iterator m_callback_it;
+#if defined(__QNX__)
+ std::weak_ptr<siginfo_t> m_siginfo;
+#endif
friend class MainLoopPosix;
SignalHandle(const SignalHandle &) = delete;
@@ -81,6 +96,9 @@ class MainLoopPosix : public MainLoopBase {
struct SignalInfo {
std::list<Callback> callbacks;
struct sigaction old_action;
+#if defined(__QNX__)
+ std::shared_ptr<siginfo_t> siginfo;
+#endif
bool was_blocked : 1;
};
class RunImpl;
diff --git a/lldb/include/lldb/Host/qnx/HostInfoQNX.h b/lldb/include/lldb/Host/qnx/HostInfoQNX.h
new file mode 100644
index 0000000000000..94fd51279ed53
--- /dev/null
+++ b/lldb/include/lldb/Host/qnx/HostInfoQNX.h
@@ -0,0 +1,24 @@
+//===-- HostInfoQNX.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_Host_qnx_HostInfoQNX_h_
+#define lldb_Host_qnx_HostInfoQNX_h_
+
+#include "lldb/Host/posix/HostInfoPosix.h"
+
+namespace lldb_private {
+
+class HostInfoQNX : public HostInfoPosix {
+public:
+ static llvm::VersionTuple GetOSVersion();
+ static std::optional<std::string> GetOSBuildString();
+ static FileSpec GetProgramFileSpec();
+};
+} // namespace lldb_private
+
+#endif
diff --git a/lldb/include/lldb/Host/qnx/Support.h b/lldb/include/lldb/Host/qnx/Support.h
new file mode 100644
index 0000000000000..975c45c4d2ba6
--- /dev/null
+++ b/lldb/include/lldb/Host/qnx/Support.h
@@ -0,0 +1,33 @@
+//===-- Support.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_HOST_QNX_SUPPORT_H
+#define LLDB_HOST_QNX_SUPPORT_H
+
+#include <memory>
+
+#include "lldb/Host/File.h"
+#include "lldb/lldb-forward.h"
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+namespace lldb_private {
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+getProcFile(::pid_t pid, const llvm::Twine &file);
+
+llvm::Expected<lldb::FileUP>
+openProcFile(::pid_t pid, const llvm::Twine &file, File::OpenOptions options,
+ uint32_t permissions = lldb::eFilePermissionsFileDefault,
+ bool should_close_fd = true);
+
+} // namespace lldb_private
+
+#endif // #ifndef LLDB_HOST_QNX_SUPPORT_H
diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt
index c2e091ee8555b..205705a9a28df 100644
--- a/lldb/source/Host/CMakeLists.txt
+++ b/lldb/source/Host/CMakeLists.txt
@@ -133,6 +133,13 @@ else()
openbsd/Host.cpp
openbsd/HostInfoOpenBSD.cpp
)
+
+ elseif (CMAKE_SYSTEM_NAME MATCHES "QNX")
+ add_host_subdirectory(qnx
+ qnx/Host.cpp
+ qnx/HostInfoQNX.cpp
+ qnx/Support.cpp
+ )
endif()
endif()
diff --git a/lldb/source/Host/common/ProcessLaunchInfo.cpp b/lldb/source/Host/common/ProcessLaunchInfo.cpp
index a1866b2a99fd8..d43b55ba0b3e6 100644
--- a/lldb/source/Host/common/ProcessLaunchInfo.cpp
+++ b/lldb/source/Host/common/ProcessLaunchInfo.cpp
@@ -213,7 +213,10 @@ llvm::Error ProcessLaunchInfo::SetUpPtyRedirection() {
// We really shouldn't be specifying platform specific flags that are
// intended for a system call in generic code. But this will have to
// do for now.
+#if !defined(__QNX__)
+ // O_CLOEXEC is NOT a valid flag for posix_openpt on QNX.
open_flags |= O_CLOEXEC;
+#endif
#endif
if (llvm::Error Err = m_pty->OpenFirstAvailablePrimary(open_flags))
return Err;
diff --git a/lldb/source/Host/common/PseudoTerminal.cpp b/lldb/source/Host/common/PseudoTerminal.cpp
index d53327973eb27..bd183679655d2 100644
--- a/lldb/source/Host/common/PseudoTerminal.cpp
+++ b/lldb/source/Host/common/PseudoTerminal.cpp
@@ -72,6 +72,24 @@ llvm::Error PseudoTerminal::OpenFirstAvailablePrimary(int oflag) {
std::error_code(errno, std::generic_category()));
}
+#if defined(FD_CLOEXEC)
+ // Enable the close-on-exec flag for the primary file descriptor if it hasn't
+ // already been enabled.
+ // NB: In multithreaded programs, using fcntl to set the close-on-exec flag at
+ // the same time as another thread performs a fork plus execve risks a race
+ // condition that may unintentionally leak the file descriptor to the program
+ // executed in the child process but, apparently, we don't launch processes
+ // for debugging from within multithreaded contexts at the moment.
+ int flags = ::fcntl(m_primary_fd, F_GETFD);
+ if (flags == -1)
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ if (!(flags & FD_CLOEXEC))
+ if (::fcntl(m_primary_fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+#endif
+
// Grant access to the secondary pseudo terminal
if (::grantpt(m_primary_fd) < 0) {
std::error_code EC(errno, std::generic_category());
@@ -124,7 +142,13 @@ std::string PseudoTerminal::GetSecondaryName() const {
buf[0] = '\0';
int r = ptsname_r(m_primary_fd, buf, sizeof(buf));
UNUSED_IF_ASSERT_DISABLED(r);
+#if defined(__QNX__)
+ // ptsname_r returns a pointer to a null-terminated string containing the
+ // pathname of the corresponding slave device on success on QNX.
+ assert(r != 0);
+#else
assert(r == 0);
+#endif
return buf;
#if defined(__APPLE__)
} else {
diff --git a/lldb/source/Host/common/TCPSocket.cpp b/lldb/source/Host/common/TCPSocket.cpp
index df4737216ed3a..5efcffbb29d0f 100644
--- a/lldb/source/Host/common/TCPSocket.cpp
+++ b/lldb/source/Host/common/TCPSocket.cpp
@@ -26,6 +26,9 @@
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
+#if defined(__QNX__)
+#include <sys/select.h>
+#endif
#endif
#if defined(_WIN32)
diff --git a/lldb/source/Host/posix/MainLoopPosix.cpp b/lldb/source/Host/posix/MainLoopPosix.cpp
index 5fe4d015251c8..37edc016e99b8 100644
--- a/lldb/source/Host/posix/MainLoopPosix.cpp
+++ b/lldb/source/Host/posix/MainLoopPosix.cpp
@@ -20,12 +20,14 @@
#include <vector>
// Multiplexing is implemented using kqueue on systems that support it (BSD
-// variants including OSX). On linux we use ppoll, while android uses pselect
-// (ppoll is present but not implemented properly). On windows we use WSApoll
-// (which does not support signals).
+// variants including OSX). On linux we use ppoll, while android and QNX use
+// pselect (ppoll is present on Android but not implemented properly). On
+// windows we use WSApoll (which does not support signals).
#if HAVE_SYS_EVENT_H
#include <sys/event.h>
+#elif defined(__QNX__)
+#include <sys/select.h>
#elif defined(__ANDROID__)
#include <sys/syscall.h>
#else
@@ -36,10 +38,17 @@ using namespace lldb;
using namespace lldb_private;
static sig_atomic_t g_signal_flags[NSIG];
+#if defined(__QNX__)
+static llvm::DenseMap<int, siginfo_t> g_signal_info;
+#endif
static void SignalHandler(int signo, siginfo_t *info, void *) {
assert(signo < NSIG);
g_signal_flags[signo] = 1;
+#if defined(__QNX__)
+ siginfo_t siginfo(*info);
+ g_signal_info.insert({signo, siginfo});
+#endif
}
class MainLoopPosix::RunImpl {
@@ -59,7 +68,7 @@ class MainLoopPosix::RunImpl {
int num_events = -1;
#else
-#ifdef __ANDROID__
+#if defined(__ANDROID__) || defined(__QNX__)
fd_set read_fd_set;
#else
std::vector<struct pollfd> read_fds;
@@ -113,7 +122,7 @@ void MainLoopPosix::RunImpl::ProcessEvents() {
}
#else
MainLoopPosix::RunImpl::RunImpl(MainLoopPosix &loop) : loop(loop) {
-#ifndef __ANDROID__
+#if !defined(__ANDROID__) && !defined(__QNX__)
read_fds.reserve(loop.m_read_fds.size());
#endif
}
@@ -129,7 +138,7 @@ sigset_t MainLoopPosix::RunImpl::get_sigmask() {
return sigmask;
}
-#ifdef __ANDROID__
+#if defined(__ANDROID__) || defined(__QNX__)
Status MainLoopPosix::RunImpl::Poll() {
// ppoll(2) is not supported on older all android versions. Also, older
// versions android (API <= 19) implemented pselect in a non-atomic way, as a
@@ -144,6 +153,7 @@ Status MainLoopPosix::RunImpl::Poll() {
nfds = std::max(nfds, fd.first + 1);
}
+#if defined(__ANDROID__)
union {
sigset_t set;
uint64_t pad;
@@ -157,6 +167,11 @@ Status MainLoopPosix::RunImpl::Poll() {
} extra_data = {&kernel_sigset, sizeof(kernel_sigset)};
if (syscall(__NR_pselect6, nfds, &read_fd_set, nullptr, nullptr, nullptr,
&extra_data) == -1) {
+#else
+ // QNX.
+ sigset_t sigmask = get_sigmask();
+ if (pselect(nfds, &read_fd_set, nullptr, nullptr, nullptr, &sigmask) == -1) {
+#endif
if (errno != EINTR)
return Status(errno, eErrorTypePOSIX);
else
@@ -188,7 +203,7 @@ Status MainLoopPosix::RunImpl::Poll() {
#endif
void MainLoopPosix::RunImpl::ProcessEvents() {
-#ifdef __ANDROID__
+#if defined(__ANDROID__) || defined(__QNX__)
// Collect first all readable file descriptors into a separate vector and
// then iterate over it to invoke callbacks. Iterating directly over
// loop.m_read_fds is not possible because the callbacks can modify the
@@ -213,14 +228,28 @@ void MainLoopPosix::RunImpl::ProcessEvents() {
std::vector<int> signals;
for (const auto &entry : loop.m_signals)
+#if defined(__QNX__)
+ if (g_signal_flags[entry.first] != 0) {
+ signals.push_back(entry.first);
+ *(entry.second.siginfo.get()) = g_signal_info.find(entry.first)->second;
+ }
+#else
if (g_signal_flags[entry.first] != 0)
signals.push_back(entry.first);
+#endif
for (const auto &signal : signals) {
if (loop.m_terminate_request)
return;
g_signal_flags[signal] = 0;
+#if defined(__QNX__)
+ g_signal_info.erase(signal);
+#endif
loop.ProcessSignal(signal);
+#if defined(__QNX__)
+ memset(loop.m_signals.find(signal)->second.siginfo.get(), 0,
+ sizeof(siginfo_t));
+#endif
}
}
#endif
@@ -282,7 +311,12 @@ MainLoopPosix::RegisterSignal(int signo, const Callback &callback,
if (signal_it != m_signals.end()) {
auto callback_it = signal_it->second.callbacks.insert(
signal_it->second.callbacks.end(), callback);
+#if defined(__QNX__)
+ std::weak_ptr<siginfo_t> siginfo = signal_it->second.siginfo;
+ return SignalHandleUP(new SignalHandle(*this, signo, callback_it, siginfo));
+#else
return SignalHandleUP(new SignalHandle(*this, signo, callback_it));
+#endif
}
SignalInfo info;
@@ -315,11 +349,21 @@ MainLoopPosix::RegisterSignal(int signo, const Callback &callback,
ret = pthread_sigmask(HAVE_SYS_EVENT_H ? SIG_UNBLOCK : SIG_BLOCK,
&new_action.sa_mask, &old_set);
assert(ret == 0 && "pthread_sigmask failed");
+#if defined(__QNX__)
+ info.siginfo = std::make_shared<siginfo_t>();
+#endif
info.was_blocked = sigismember(&old_set, signo);
auto insert_ret = m_signals.insert({signo, info});
+#if defined(__QNX__)
+ std::weak_ptr<siginfo_t> siginfo = info.siginfo;
+#endif
return SignalHandleUP(new SignalHandle(
+#if defined(__QNX__)
+ *this, signo, insert_ret.first->second.callbacks.begin(), siginfo));
+#else
*this, signo, insert_ret.first->second.callbacks.begin()));
+#endif
}
void MainLoopPosix::UnregisterReadObject(IOObject::WaitableHandle handle) {
diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp
index 0a832ebad13a7..b539dedcb8bcf 100644
--- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp
+++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp
@@ -17,7 +17,11 @@
#include "llvm/Support/Errno.h"
#include <climits>
+#if defined(__QNX__)
+#include <spawn.h>
+#else
#include <sys/ptrace.h>
+#endif
#include <sys/wait.h>
#include <unistd.h>
@@ -35,6 +39,10 @@
#include <sys/personality.h>
#endif
+#if !defined(EOK)
+#define EOK 0 /* No error */
+#endif
+
using namespace lldb;
using namespace lldb_private;
@@ -193,7 +201,9 @@ struct ForkLaunchInfo {
}
// Start tracing this child that is about to exec.
+#if !defined(__QNX__)
if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)
+#endif
ExitWithError(error_fd, "ptrace");
}
@@ -222,6 +232,148 @@ struct ForkLaunchInfo {
// End of code running in the child process.
+#if defined(__QNX__)
+static ::pid_t PosixSpawn(const ForkLaunchInfo &info,
+ posix_spawn_file_actions_t &file_actions,
+ posix_spawnattr_t &attr, Status &error) {
+ uint32_t flags = 0;
+ sigset_t set;
+ ::pid_t pid;
+ int ret;
+
+ if (info.separate_process_group) {
+ flags |= POSIX_SPAWN_SETPGROUP;
+ if (::posix_spawnattr_setpgroup(&attr, 0) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawnattr_setpgroup failed with "
+ "error message: {0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ }
+
+ for (const ForkFileAction &action : info.actions) {
+ switch (action.action) {
+ case FileAction::eFileActionClose:
+ if (::posix_spawn_file_actions_addclose(&file_actions, action.fd) !=
+ EOK) {
+ error.SetErrorStringWithFormatv("posix_spawn_file_actions_addclose "
+ "failed with error message: {0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ break;
+ case FileAction::eFileActionDuplicate:
+ if (::posix_spawn_file_actions_adddup2(&file_actions, action.fd,
+ action.arg) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawn_file_actions_adddup2 "
+ "failed with error message: {0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ break;
+ case FileAction::eFileActionOpen:
+ if (::posix_spawn_file_actions_addopen(&file_actions, action.fd,
+ action.path.c_str(), action.arg,
+ 0666) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawn_file_actions_addopen "
+ "failed with error message: {0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ break;
+ case FileAction::eFileActionNone:
+ break;
+ }
+ }
+
+ // Change the working directory.
+ if (!info.wd.empty()) {
+ flags |= POSIX_SPAWN_SETCWD;
+
+ int dirfd =
+ llvm::sys::RetryAfterSignal(-1, ::open, info.wd.c_str(), O_DIRECTORY);
+
+ if (dirfd == -1) {
+ error.SetErrorStringWithFormatv("open failed with error message: {0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+
+ if (::posix_spawnattr_setcwd_np(&attr, dirfd) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawnattr_setcwd_np failed with "
+ "error message: {0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ }
+
+ if (info.disable_aslr) {
+ if (::posix_spawnattr_setaslr(&attr, POSIX_SPAWN_ASLR_DISABLE) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawnattr_setaslr failed with "
+ "error message: {0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ }
+
+ // Clear the signal mask to prevent the child from being affected by any
+ // masking done by the parent.
+ flags |= POSIX_SPAWN_SETSIGMASK;
+
+ if (sigemptyset(&set) != 0) {
+ error.SetErrorStringWithFormatv("sigemptyset failed with error message: "
+ "{0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+
+ if (::posix_spawnattr_setsigmask(&attr, &set) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawnattr_setsigmask failed with "
+ "error message: {0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+
+ if (info.debug) {
+ // Hold the child process as if it had received a SIGSTOP as soon as it was
+ // spawned.
+ flags |= POSIX_SPAWN_HOLD;
+
+ // Do not inherit setgid powers.
+ flags |= POSIX_SPAWN_RESETIDS;
+
+ // posix_spawn returns EBADF when requested to close all open file
+ // descriptors other than STDIN, STDOUT, and STDERR.
+ // TODO: Close everything besides STDIN, STDOUT, and STDERR that doesn't
+ // have any file action to avoid leaking descriptors.
+ }
+
+ if (::posix_spawnattr_setxflags(&attr, flags) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawnattr_setxflags failed with "
+ "error message: {0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+
+ ret = ::posix_spawn(&pid, info.argv[0], &file_actions, &attr,
+ const_cast<char *const *>(info.argv), info.envp);
+
+ if (ret != EOK) {
+ // posix_spawn failed.
+ error.SetErrorStringWithFormatv("posix_spawn failed with error message: "
+ "{0}",
+ llvm::sys::StrError());
+ return LLDB_INVALID_PROCESS_ID;
+ }
+
+ // If an error occurs after posix_spawn returns successfully, then the child
+ // process exits with status 127.
+ // TODO: Wait for the child process if it exits on account of any error.
+
+ return pid; // No error. We're done.
+}
+#endif
+
ForkFileAction::ForkFileAction(const FileAction &act)
: action(act.GetAction()), fd(act.GetFD()), path(act.GetPath().str()),
arg(act.GetActionArgument()) {}
@@ -257,15 +409,53 @@ ForkLaunchInfo::ForkLaunchInfo(const ProcessLaunchInfo &info)
HostProcess
ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
Status &error) {
+#if !defined(__QNX__)
// A pipe used by the child process to report errors.
PipePosix pipe;
const bool child_processes_inherit = false;
error = pipe.CreateNew(child_processes_inherit);
if (error.Fail())
return HostProcess();
+#endif
const ForkLaunchInfo fork_launch_info(launch_info);
+#if defined(__QNX__)
+ // A call to execve doesn't have the child process stopped by default on QNX.
+ // So, use posix_spawn instead.
+ posix_spawn_file_actions_t file_actions;
+ posix_spawnattr_t attr;
+ ::pid_t pid;
+
+ if (::posix_spawn_file_actions_init(&file_actions) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawn_file_actions_init failed with "
+ "error message: {0}",
+ llvm::sys::StrError());
+ return HostProcess();
+ }
+
+ if (::posix_spawnattr_init(&attr) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawnattr_init failed with error "
+ "message: {0}",
+ llvm::sys::StrError());
+ return HostProcess();
+ }
+ pid = PosixSpawn(fork_launch_info, file_actions, attr, error);
+
+ if (::posix_spawn_file_actions_destroy(&file_actions) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawn_file_actions_destroy failed "
+ "with error message: {0}",
+ llvm::sys::StrError());
+ }
+
+ if (::posix_spawnattr_destroy(&attr) != EOK) {
+ error.SetErrorStringWithFormatv("posix_spawnattr_destroy failed with error "
+ "message: {0}",
+ llvm::sys::StrError());
+ }
+
+ return HostProcess(pid);
+#else
::pid_t pid = ::fork();
if (pid == -1) {
// Fork failed
@@ -278,7 +468,6 @@ ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
pipe.CloseReadFileDescriptor();
ChildFunc(pipe.ReleaseWriteFileDescriptor(), fork_launch_info);
}
-
// parent process
pipe.CloseWriteFileDescriptor();
@@ -302,4 +491,5 @@ ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);
return HostProcess();
+#endif
}
diff --git a/lldb/source/Host/qnx/Host.cpp b/lldb/source/Host/qnx/Host.cpp
new file mode 100644
index 0000000000000..11d9a3f78ff92
--- /dev/null
+++ b/lldb/source/Host/qnx/Host.cpp
@@ -0,0 +1,183 @@
+//===-- source/Host/qnx/Host.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 <dirent.h>
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/qnx/Support.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/ProcessInfo.h"
+
+#include "llvm/Object/ELF.h"
+
+extern "C" {
+extern char **environ;
+}
+
+using namespace lldb;
+using namespace lldb_private;
+
+static bool GetExecutableFile(::pid_t pid, ProcessInstanceInfo &process_info) {
+ auto buffer_or_error = getProcFile(pid, "exefile");
+
+ if (!buffer_or_error) {
+ return false;
+ }
+
+ llvm::StringRef exe_path = buffer_or_error.get()->getBuffer();
+
+ if (exe_path.empty()) {
+ return false;
+ }
+
+ process_info.GetExecutableFile().SetFile(exe_path, FileSpec::Style::native);
+
+ return true;
+}
+
+static bool GetArchitecture(ProcessInstanceInfo &process_info) {
+ Log *log = GetLog(LLDBLog::Host);
+
+ FileSpec executable_file = process_info.GetExecutableFile();
+
+ auto buffer_sp =
+ FileSystem::Instance().CreateDataBuffer(executable_file, 0x20, 0);
+ if (!buffer_sp) {
+ process_info.SetArchitecture(ArchSpec());
+ return false;
+ }
+
+ uint8_t exe_class =
+ llvm::object::getElfArchType(
+ {reinterpret_cast<const char *>(buffer_sp->GetBytes()),
+ size_t(buffer_sp->GetByteSize())})
+ .first;
+
+ switch (exe_class) {
+ case llvm::ELF::ELFCLASS32:
+ process_info.SetArchitecture(
+ HostInfo::GetArchitecture(HostInfo::eArchKind32));
+ return true;
+ case llvm::ELF::ELFCLASS64:
+ process_info.SetArchitecture(
+ HostInfo::GetArchitecture(HostInfo::eArchKind64));
+ return true;
+ default:
+ LLDB_LOG(log, "Unknown elf class ({0}) in file {1}", exe_class,
+ executable_file.GetPath());
+ process_info.SetArchitecture(ArchSpec());
+ return false;
+ }
+}
+
+static bool GetProcessArgs(::pid_t pid, ProcessInstanceInfo &process_info) {
+ auto buffer_or_error = getProcFile(pid, "cmdline");
+
+ if (!buffer_or_error)
+ return false;
+
+ std::unique_ptr<llvm::MemoryBuffer> cmdline = std::move(*buffer_or_error);
+
+ llvm::StringRef arg0, rest;
+ std::tie(arg0, rest) = cmdline->getBuffer().split('\0');
+ process_info.SetArg0(arg0);
+ while (!rest.empty()) {
+ llvm::StringRef arg;
+ std::tie(arg, rest) = rest.split('\0');
+ process_info.GetArguments().AppendArgument(arg);
+ }
+
+ return true;
+}
+
+static bool IsDirNumeric(const char *dname) {
+ for (; *dname; dname++) {
+ if (!isdigit(*dname))
+ return false;
+ }
+ return true;
+}
+
+uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos) {
+ static const std::string procdir = "/proc/";
+
+ DIR *dirproc = opendir(procdir.c_str());
+ if (dirproc) {
+ struct dirent *direntry = nullptr;
+ const lldb::pid_t our_pid = getpid();
+
+ while ((direntry = readdir(dirproc)) != nullptr) {
+ struct stat statp;
+ std::string path = procdir + std::string(direntry->d_name);
+
+ if (stat(path.c_str(), &statp) == -1)
+ continue;
+
+ if (!S_ISDIR(statp.st_mode) || !IsDirNumeric(direntry->d_name))
+ continue;
+
+ lldb::pid_t pid = std::stoi(direntry->d_name);
+
+ // Skip this process.
+ if (pid == our_pid)
+ continue;
+
+ ProcessInstanceInfo process_info;
+ if (!GetProcessInfo(pid, process_info))
+ continue;
+
+ // TODO: Skip if process is under debug.
+
+ // TODO: Skip if process is a zombie.
+
+ // TODO: Match user if we're not matching all users and not running as
+ // root.
+
+ if (match_info.Matches(process_info)) {
+ process_infos.push_back(process_info);
+ }
+ }
+
+ closedir(dirproc);
+ }
+
+ return process_infos.size();
+}
+
+bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ Log *log = GetLog(LLDBLog::Host);
+
+ process_info.SetProcessID(pid);
+
+ int ret = true;
+
+ if (!GetExecutableFile(pid, process_info)) {
+ LLDB_LOG(log, "Failed to retrieve {0}'s executable file", pid);
+ ret = false;
+ }
+
+ if (!GetArchitecture(process_info)) {
+ LLDB_LOG(log, "Failed to retrieve {0}'s architecture", pid);
+ ret = false;
+ }
+
+ if (!GetProcessArgs(pid, process_info)) {
+ LLDB_LOG(log, "Failed to retrieve {0}'s arguments", pid);
+ ret = false;
+ }
+
+ // TODO: Get the process's environment, state, real group ID, effective group
+ // ID, user ID, effective user ID, and parent's process ID.
+
+ return ret;
+}
+
+Environment Host::GetEnvironment() { return Environment(environ); }
diff --git a/lldb/source/Host/qnx/HostInfoQNX.cpp b/lldb/source/Host/qnx/HostInfoQNX.cpp
new file mode 100644
index 0000000000000..b3dc7e9ab0421
--- /dev/null
+++ b/lldb/source/Host/qnx/HostInfoQNX.cpp
@@ -0,0 +1,22 @@
+//===-- HostInfoQNX.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/qnx/HostInfoQNX.h"
+
+using namespace lldb_private;
+
+llvm::VersionTuple HostInfoQNX::GetOSVersion() { return llvm::VersionTuple(); }
+
+std::optional<std::string> HostInfoQNX::GetOSBuildString() {
+ return std::nullopt;
+}
+
+FileSpec HostInfoQNX::GetProgramFileSpec() {
+ static FileSpec g_program_filespec;
+ return g_program_filespec;
+}
diff --git a/lldb/source/Host/qnx/Support.cpp b/lldb/source/Host/qnx/Support.cpp
new file mode 100644
index 0000000000000..fbb23af941833
--- /dev/null
+++ b/lldb/source/Host/qnx/Support.cpp
@@ -0,0 +1,41 @@
+//===-- Support.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/qnx/Support.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace lldb;
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+lldb_private::getProcFile(::pid_t pid, const llvm::Twine &file) {
+ Log *log = GetLog(LLDBLog::Host);
+ std::string path = ("/proc/" + llvm::Twine(pid) + "/" + file).str();
+ auto ret = llvm::MemoryBuffer::getFileAsStream(path);
+ if (!ret)
+ LLDB_LOG(log, "Failed to open {0}: {1}", path, ret.getError().message());
+ return ret;
+}
+
+llvm::Expected<FileUP> lldb_private::openProcFile(::pid_t pid,
+ const llvm::Twine &file,
+ File::OpenOptions options,
+ uint32_t permissions,
+ bool should_close_fd) {
+ Log *log = GetLog(LLDBLog::Host);
+ std::string path = ("/proc/" + llvm::Twine(pid) + "/" + file).str();
+ auto ret = FileSystem::Instance().Open(FileSpec(llvm::StringRef(path)),
+ options, permissions, should_close_fd);
+ if (!ret) {
+ LLDB_LOG_ERROR(log, ret.takeError(), "Failed to open {0}: {1}", path);
+ }
+ return ret;
+}
diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt
index a51d0f7afd175..705ff6d611269 100644
--- a/lldb/source/Plugins/Process/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/CMakeLists.txt
@@ -11,6 +11,9 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
add_subdirectory(Windows/Common)
elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_subdirectory(MacOSX-Kernel)
+elseif (CMAKE_SYSTEM_NAME MATCHES "QNX")
+ add_subdirectory(QNX)
+ add_subdirectory(POSIX)
endif()
add_subdirectory(scripted)
add_subdirectory(gdb-remote)
diff --git a/lldb/source/Plugins/Process/QNX/CMakeLists.txt b/lldb/source/Plugins/Process/QNX/CMakeLists.txt
new file mode 100644
index 0000000000000..66e7cf3262f1e
--- /dev/null
+++ b/lldb/source/Plugins/Process/QNX/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_lldb_library(lldbPluginProcessQNX
+ NativeProcessQNX.cpp
+ NativeRegisterContextQNX.cpp
+ NativeRegisterContextQNX_arm64.cpp
+ NativeThreadQNX.cpp
+
+ LINK_LIBS
+ lldbHost
+ lldbSymbol
+ lldbTarget
+ lldbUtility
+ lldbPluginProcessPOSIX
+ lldbPluginProcessUtility
+ LINK_COMPONENTS
+ Support
+ )
diff --git a/lldb/source/Plugins/Process/QNX/NativeProcessQNX.cpp b/lldb/source/Plugins/Process/QNX/NativeProcessQNX.cpp
new file mode 100644
index 0000000000000..04c9234aedcb8
--- /dev/null
+++ b/lldb/source/Plugins/Process/QNX/NativeProcessQNX.cpp
@@ -0,0 +1,1066 @@
+//===-- NativeProcessQNX.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 "NativeProcessQNX.h"
+
+#include <fcntl.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+#include <sys/neutrino.h>
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostProcess.h"
+#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
+#include "lldb/Host/qnx/Support.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/State.h"
+
+#include "llvm/Support/Errno.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_qnx;
+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;
+}
+
+NativeProcessQNX::Manager::Manager(MainLoop &mainloop)
+ : NativeProcessProtocol::Manager(mainloop) {
+ Status error;
+ m_sigchld_handle = mainloop.RegisterSignal(
+ SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error);
+ if (!m_sigchld_handle || error.Fail())
+ return;
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessQNX::Manager::Launch(ProcessLaunchInfo &launch_info,
+ NativeDelegate &native_delegate) {
+ Log *log = GetLog(POSIXLog::Process);
+
+ Status error;
+ ::pid_t pid = ProcessLauncherPosixFork()
+ .LaunchProcess(launch_info, error)
+ .GetProcessId();
+ LLDB_LOG(log, "pid = {0:x}", pid);
+ if (error.Fail()) {
+ LLDB_LOG(log, "failed to launch process: {0}", error);
+ return error.ToError();
+ }
+
+ LLDB_LOG(log, "Inferior started; in stopped state now");
+
+ ProcessInstanceInfo info;
+ if (!Host::GetProcessInfo(pid, info)) {
+ return llvm::make_error<StringError>("cannot get process architecture",
+ llvm::inconvertibleErrorCode());
+ }
+
+ // Set the architecture to the executable's architecture.
+ LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid,
+ info.GetArchitecture().GetArchitectureName());
+
+ std::unique_ptr<NativeProcessQNX> process_up(new NativeProcessQNX(
+ pid, launch_info.GetPTY().ReleasePrimaryFileDescriptor(), native_delegate,
+ *this, info.GetArchitecture(), error));
+
+ if (error.Fail())
+ return error.ToError();
+
+ error = process_up->SetupTrace();
+ if (error.Fail())
+ return error.ToError();
+
+ for (const auto &thread : process_up->m_threads)
+ static_cast<NativeThreadQNX &>(*thread).SetStoppedBySignal(SIGSTOP);
+ process_up->SetState(StateType::eStateStopped, false);
+
+ return std::move(process_up);
+}
+
+llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
+NativeProcessQNX::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());
+ }
+
+ Status error;
+
+ std::unique_ptr<NativeProcessQNX> process_up(new NativeProcessQNX(
+ pid, -1, native_delegate, *this, info.GetArchitecture(), error));
+
+ if (!error.Success()) {
+ return error.ToError();
+ }
+
+ error.Clear();
+
+ error = process_up->Attach();
+ if (!error.Success())
+ return error.ToError();
+
+ return std::move(process_up);
+}
+
+NativeProcessQNX::Extension
+NativeProcessQNX::Manager::GetSupportedExtensions() const {
+ // TODO: Enable the multiprocess, fork-events, and vfork-events extensions.
+ NativeProcessQNX::Extension supported =
+ Extension::pass_signals | Extension::auxv | Extension::libraries_svr4 |
+ Extension::siginfo_read;
+
+ return supported;
+}
+
+void NativeProcessQNX::Manager::SigchldHandler() {
+ // We can identify a debuggee that has stopped/terminated via siginfo_t.si_pid
+ // or a call to wait*. We have the server deliver a SIGCHLD to the debugger
+ // whenever a debuggee reaches a point of interest, though. So, siginfo_t.
+ // si_pid doesn't point to the debuggee anymore. Moreover, wait* doesn't wait
+ // for waitable debuggees that aren't children on QNX. So, we have siginfo_t.
+ // si_value.sival_int set to the debuggee's PID, MainLoopPosix::RegisterSignal
+ // provision a mechanism to save the signal information, and MainLoopPosix::
+ // SignalHandle expose the saved signal information.
+ ::pid_t pid;
+
+ if (m_sigchld_handle && !m_sigchld_handle.get()->GetSiginfo().expired())
+ pid = m_sigchld_handle.get()->GetSiginfo().lock()->si_value.sival_int;
+ else
+ return;
+
+ auto process_it =
+ std::find_if(m_processes.begin(), m_processes.end(),
+ [pid](auto process) { return process->GetID() == pid; });
+
+ if (process_it == m_processes.end()) {
+ return;
+ }
+
+ procfs_status proc_status;
+ Status error =
+ DevctlWrapper((*process_it)->GetFileDescriptor(), DCMD_PROC_STATUS,
+ &proc_status, sizeof(procfs_status), nullptr);
+
+ if (error.Fail())
+ return;
+
+ switch (proc_status.why) {
+ // TODO: Monitor fork, vfork, and spawn.
+ case _DEBUG_WHY_REQUESTED:
+ (*process_it)->MonitorInterrupt();
+ break;
+ case _DEBUG_WHY_SIGNALLED:
+ (*process_it)->MonitorCallback(proc_status);
+ break;
+ case _DEBUG_WHY_TERMINATED:
+ (*process_it)->MonitorExited(proc_status);
+ break;
+ case _DEBUG_WHY_THREAD:
+ (*process_it)->MonitorThread(proc_status);
+ break;
+ default:
+ break;
+ }
+}
+
+Status NativeProcessQNX::Resume(const ResumeActionList &resume_actions) {
+ Log *log = GetLog(POSIXLog::Process);
+
+ Status error;
+ procfs_run proc_run;
+
+ memset(&proc_run, 0, sizeof(procfs_run));
+ proc_run.flags =
+ _DEBUG_RUN_CLRSIG | _DEBUG_RUN_TRACE | _DEBUG_RUN_ARM | _DEBUG_RUN_THREAD;
+ sigfillset(&(proc_run.trace));
+
+ for (const auto &abs_thread : m_threads) {
+ if (!abs_thread) {
+ error.SetErrorString("thread list should not contain NULL threads");
+ return error;
+ }
+ NativeThreadQNX &thread = static_cast<NativeThreadQNX &>(*abs_thread);
+
+ const ResumeAction *action =
+ resume_actions.GetActionForThread(thread.GetID(), true);
+
+ if (action == nullptr) {
+ LLDB_LOG(log, "No action specified for thread {0} of process {1}",
+ thread.GetID(), GetID());
+ continue;
+ }
+
+ LLDB_LOG(
+ log,
+ "Processing resume action (state {0}, signal {1}) for thread {2} of "
+ "process {3}",
+ action->state, action->signal, thread.GetID(), GetID());
+
+ switch (action->state) {
+ case eStateRunning:
+ error = thread.Resume();
+ break;
+
+ case eStateStepping:
+ proc_run.flags |= _DEBUG_RUN_STEP | _DEBUG_RUN_CURTID;
+ proc_run.tid = thread.GetID();
+ error = thread.SingleStep();
+ break;
+
+ default:
+ error.SetErrorStringWithFormatv(
+ "NativeProcessQNX::%s (): Unexpected state %s specified for pid "
+ "%" PRIu64 ", tid %" PRIu64,
+ __FUNCTION__, StateAsCString(action->state), GetID(), thread.GetID());
+ return error;
+ }
+
+ if (error.Fail())
+ return error;
+
+ if (action->signal != LLDB_INVALID_SIGNAL_NUMBER) {
+ sigdelset(&(proc_run.trace), action->signal);
+ error = Signal(action->signal);
+
+ if (error.Fail())
+ return error;
+ }
+ }
+
+ error = DevctlWrapper(m_fd, DCMD_PROC_RUN, &proc_run, sizeof(procfs_run),
+ nullptr);
+
+ if (error.Success())
+ SetState(eStateRunning, true);
+
+ return error;
+}
+
+Status NativeProcessQNX::Halt() {
+ procfs_status status;
+
+ return DevctlWrapper(m_fd, DCMD_PROC_STOP, &status, sizeof(procfs_status),
+ nullptr);
+}
+
+Status NativeProcessQNX::Detach() {
+ Status error = m_file_up.get()->Close();
+
+ if (error.Fail())
+ return error;
+
+ m_fd = -1;
+
+ return error;
+}
+
+Status NativeProcessQNX::Signal(int signo) {
+ procfs_signal sig;
+
+ sig.tid = 0;
+ sig.signo = signo;
+ sig.code = 0;
+ sig.value = 0;
+
+ return DevctlWrapper(m_fd, DCMD_PROC_SIGNAL, &sig, sizeof(procfs_signal),
+ nullptr);
+}
+
+Status NativeProcessQNX::Interrupt() { return Halt(); }
+
+Status NativeProcessQNX::Kill() {
+ Log *log = GetLog(POSIXLog::Process);
+
+ 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, "Current state of process {0}: {1}; ignoring", 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;
+ }
+
+ error = Signal(SIGKILL);
+ if (error.Fail())
+ return error;
+
+ // Resume the debuggee.
+ procfs_run proc_run;
+ memset(&proc_run, 0, sizeof(procfs_run));
+ proc_run.flags =
+ _DEBUG_RUN_CLRSIG | _DEBUG_RUN_TRACE | _DEBUG_RUN_ARM | _DEBUG_RUN_THREAD;
+ sigfillset(&(proc_run.trace));
+ sigdelset(&(proc_run.trace), SIGKILL);
+
+ return DevctlWrapper(m_fd, DCMD_PROC_RUN, &proc_run, sizeof(procfs_run),
+ nullptr);
+}
+
+Status NativeProcessQNX::GetMemoryRegionInfo(lldb::addr_t load_addr,
+ MemoryRegionInfo &range_info) {
+ Status error;
+
+ if (m_supports_mem_region == LazyBool::eLazyBoolNo) {
+ // We're done.
+ error.SetErrorString("unsupported");
+ return error;
+ }
+
+ error = PopulateMemoryRegionCache();
+ if (error.Fail())
+ return error;
+
+ lldb::addr_t prev_base_address = 0;
+ // TODO: There can be a ton of memory regions in case of numerous threads. The
+ // memory regions are expected to be sorted, though. So, identify the last
+ // region that is <= target memory address via binary search.
+ for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end();
+ ++it) {
+ MemoryRegionInfo &proc_entry_info = it->first;
+ // Sanity check the assumption that memory map entries are ascending.
+ if (proc_entry_info.GetRange().GetRangeBase() < prev_base_address) {
+ error.SetErrorString("unexpectedly detected descending memory map "
+ "entries");
+ return error;
+ }
+ prev_base_address = proc_entry_info.GetRange().GetRangeBase();
+ // If the target memory address comes before this entry, then indicate
+ // distance to the 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 memory address lies within the memory region that we're
+ // processing here.
+ range_info = proc_entry_info;
+ return error;
+ }
+ // The target memory address lies beyond the region that we've just parsed.
+ }
+ // If we've made it so far, then we didn't find any entry that contained the
+ // given address. So, set the load_addr as the base address and the amount of
+ // bytes between the load address and the end of the memory as the 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 NativeProcessQNX::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ size_t &bytes_read) {
+ Log *log = GetLog(POSIXLog::Memory);
+
+ LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+ bytes_read = size;
+ off_t offset = static_cast<off_t>(addr);
+
+ return m_file_up->Read(buf, bytes_read, offset);
+}
+
+Status NativeProcessQNX::WriteMemory(lldb::addr_t addr, const void *buf,
+ size_t size, size_t &bytes_written) {
+ Log *log = GetLog(POSIXLog::Memory);
+
+ LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+ bytes_written = size;
+ off_t offset = static_cast<off_t>(addr);
+
+ // TODO: Determine whether or not we need to lock m_file.
+ return m_file_up->Write(buf, bytes_written, offset);
+}
+
+size_t NativeProcessQNX::UpdateThreads() {
+ // The list of a debuggee's threads, and its threads' states are expected to
+ // always be up to date. So, just return the thread count.
+ return m_threads.size();
+}
+
+Status NativeProcessQNX::SetBreakpoint(lldb::addr_t addr, uint32_t size,
+ bool hardware) {
+ // TODO: Provision support for setting hardware breakpoints.
+
+ (void)hardware;
+
+ procfs_break bkpt;
+ memset(&bkpt, 0x0, sizeof(procfs_break));
+ bkpt.type = _DEBUG_BREAK_EXEC;
+ bkpt.addr = static_cast<_Uintptr64t>(addr);
+ bkpt.size = 0;
+
+ return DevctlWrapper(m_fd, DCMD_PROC_BREAK, &bkpt, sizeof(procfs_break),
+ nullptr);
+}
+
+Status NativeProcessQNX::RemoveBreakpoint(lldb::addr_t addr, bool hardware) {
+ // TODO: Provision support for removing hardware breakpoints.
+
+ (void)hardware;
+
+ procfs_break bkpt;
+ memset(&bkpt, 0x0, sizeof(procfs_break));
+ bkpt.type = _DEBUG_BREAK_EXEC;
+ bkpt.addr = static_cast<_Uintptr64t>(addr);
+ bkpt.size = -1;
+
+ return DevctlWrapper(m_fd, DCMD_PROC_BREAK, &bkpt, sizeof(procfs_break),
+ nullptr);
+}
+
+Status NativeProcessQNX::GetLoadedModuleFileSpec(const char *module_path,
+ FileSpec &file_spec) {
+ return Status("not implemented");
+}
+
+Status NativeProcessQNX::GetFileLoadAddress(const llvm::StringRef &file_name,
+ lldb::addr_t &load_addr) {
+ return Status("not implemented");
+}
+
+Status NativeProcessQNX::DevctlWrapper(int fd, int dcmd, void *dev_data_ptr,
+ size_t n_bytes, int *dev_info_ptr) {
+ Log *log = GetLog(POSIXLog::Ptrace);
+ Status error;
+ int ret;
+
+ ret = devctl(fd, dcmd, static_cast<caddr_t>(dev_data_ptr),
+ static_cast<size_t>(n_bytes), static_cast<int *>(dev_info_ptr));
+
+ if (ret != EOK)
+ error.SetError(ret, lldb::eErrorTypePOSIX);
+
+ LLDB_LOG(log, "devctl({0}, {1}, {2}, {3}, {4}) = {5:x}", fd, dcmd,
+ dev_data_ptr, n_bytes, dev_info_ptr, ret);
+
+ if (error.Fail())
+ LLDB_LOG(log, "devctl failed: {0}", error);
+
+ return error;
+}
+
+NativeProcessQNX::NativeProcessQNX(::pid_t pid, int terminal_fd,
+ NativeDelegate &delegate, Manager &manager,
+ const ArchSpec &arch, Status &error)
+ : NativeProcessELF(pid, terminal_fd, delegate), m_manager(manager),
+ m_arch(arch), m_file_up(nullptr), m_fd(-1) {
+ manager.AddProcess(*this);
+
+ if (m_terminal_fd != -1) {
+ error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
+ if (!error.Success()) {
+ return;
+ }
+ }
+
+ llvm::Expected<FileUP> file_up =
+ openProcFile(m_pid, "as", File::eOpenOptionReadWrite);
+
+ if (!file_up) {
+ error = file_up.takeError();
+ return;
+ }
+
+ m_file_up = std::move(file_up.get());
+ m_fd = m_file_up.get()->GetDescriptor();
+}
+
+NativeThreadQNX &NativeProcessQNX::AddThread(lldb::tid_t thread_id) {
+ Log *log = GetLog(POSIXLog::Thread);
+
+ LLDB_LOG(log, "Adding thread {0} for process {1}", thread_id, GetID());
+
+ NativeThreadQNX *thread =
+ static_cast<NativeThreadQNX *>(GetThreadByIDUnlocked(thread_id));
+
+ if (!thread) {
+ // If this is the first thread, then save it as the current thread.
+ if (m_threads.empty())
+ SetCurrentThreadID(thread_id);
+
+ m_threads.push_back(std::make_unique<NativeThreadQNX>(*this, thread_id));
+ return static_cast<NativeThreadQNX &>(*m_threads.back());
+ }
+
+ return static_cast<NativeThreadQNX &>(*thread);
+}
+
+void NativeProcessQNX::RemoveThread(lldb::tid_t thread_id) {
+ Log *log = GetLog(POSIXLog::Thread);
+
+ LLDB_LOG(log, "Removing thread {0} for process {1}", thread_id, GetID());
+
+ if (!GetThreadByIDUnlocked(thread_id)) {
+ LLDB_LOG(log, "Attempting to remove a thread that doesn't exist");
+ return;
+ }
+
+ 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());
+}
+
+void NativeProcessQNX::MonitorInterrupt() {
+ // Interrupt all threads attached to the process.
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadQNX &>(*thread).SetStoppedBySignal(SIGINT, nullptr);
+
+ SetState(StateType::eStateStopped, true);
+}
+
+void NativeProcessQNX::MonitorCallback(procfs_status &proc_status) {
+ switch (proc_status.what) {
+ case SIGTRAP:
+ MonitorSIGTRAP(proc_status);
+ case SIGSTOP:
+ MonitorSIGSTOP();
+ default:
+ MonitorSignal(proc_status);
+ }
+}
+
+void NativeProcessQNX::MonitorSIGTRAP(procfs_status &proc_status) {
+ Log *log = GetLog(POSIXLog::Process);
+
+ NativeThreadQNX *thread = nullptr;
+ lldb::tid_t tid = static_cast<lldb::tid_t>(proc_status.tid);
+
+ for (const auto &t : m_threads) {
+ if (t->GetID() == tid)
+ thread = static_cast<NativeThreadQNX *>(t.get());
+ static_cast<NativeThreadQNX *>(t.get())->SetStoppedWithNoReason();
+ }
+
+ if (!thread)
+ LLDB_LOG(log, "Couldn't find thread {0} for pid {1}", tid, GetID());
+
+ switch (proc_status.info.si_code) {
+ case TRAP_BRKPT:
+ if (thread) {
+ thread->SetStoppedByBreakpoint();
+ FixupBreakpointPCAsNeeded(*thread);
+ SetCurrentThreadID(thread->GetID());
+ }
+
+ SetState(StateType::eStateStopped, true);
+ return;
+
+ case TRAP_TRACE:
+ if (thread) {
+ thread->SetStoppedByTrace();
+ SetCurrentThreadID(thread->GetID());
+ }
+
+ SetState(StateType::eStateStopped, true);
+ return;
+
+ default:
+ break;
+ }
+
+ // Encounterd either a user-generated SIGTRAP or an unknown event that would
+ // otherwise leave the debugger hanging.
+ LLDB_LOG(log, "Unknown SIGTRAP; passing to generic handler");
+ MonitorSignal(proc_status);
+}
+
+void NativeProcessQNX::MonitorSIGSTOP() {
+ // Stop all threads attached to the process.
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadQNX &>(*thread).SetStoppedBySignal(SIGSTOP,
+ nullptr);
+
+ SetState(StateType::eStateStopped, true);
+}
+
+void NativeProcessQNX::MonitorSignal(procfs_status &proc_status) {
+ Log *log = GetLog(POSIXLog::Process);
+
+ int signo = proc_status.what;
+ LLDB_LOG(log, "Received signal {0} ({1})", Host::GetSignalAsCString(signo),
+ signo);
+
+ // Check if the debugger should just ignore this signal and resume the
+ // debuggee.
+ if (m_signals_to_ignore.contains(signo)) {
+ LLDB_LOG(log, "Ignoring signal {0} ({1})", Host::GetSignalAsCString(signo),
+ signo);
+
+ procfs_run proc_run;
+ memset(&proc_run, 0, sizeof(procfs_run));
+ proc_run.flags = _DEBUG_RUN_CLRSIG | _DEBUG_RUN_TRACE | _DEBUG_RUN_ARM |
+ _DEBUG_RUN_THREAD;
+ sigfillset(&(proc_run.trace));
+
+ Status error = DevctlWrapper(m_fd, DCMD_PROC_RUN, &proc_run,
+ sizeof(procfs_run), nullptr);
+
+ if (error.Fail())
+ SetState(StateType::eStateInvalid);
+
+ return;
+ }
+
+ // Stop all threads attached to the process.
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadQNX &>(*thread).SetStoppedBySignal(signo, nullptr);
+
+ SetState(StateType::eStateStopped, true);
+}
+
+void NativeProcessQNX::MonitorExited(procfs_status &proc_status) {
+ Log *log = GetLog(POSIXLog::Process);
+
+ WaitStatus status(WaitStatus::Exit, proc_status.what);
+
+ LLDB_LOG(log, "Got exit signal({0}) , pid = {1}", status, proc_status.pid);
+
+ // Stop tracking all threads attached to the process.
+ m_threads.clear();
+
+ SetExitStatus(status, true);
+
+ // Notify delegate that our process has exited.
+ SetState(StateType::eStateExited, true);
+}
+
+void NativeProcessQNX::MonitorThread(procfs_status &proc_status) {
+ switch (proc_status.what) {
+ case _DEBUG_WHAT_DESTROYED:
+ RemoveThread(proc_status.blocked.thread_event.tid);
+ break;
+ case _DEBUG_WHAT_CREATED:
+ AddThread(proc_status.blocked.thread_event.tid);
+ break;
+ }
+
+ // Resume the debuggee.
+ procfs_run proc_run;
+ memset(&proc_run, 0, sizeof(procfs_run));
+ proc_run.flags =
+ _DEBUG_RUN_CLRSIG | _DEBUG_RUN_TRACE | _DEBUG_RUN_ARM | _DEBUG_RUN_THREAD;
+ sigfillset(&(proc_run.trace));
+
+ Status error = DevctlWrapper(m_fd, DCMD_PROC_RUN, &proc_run,
+ sizeof(procfs_run), nullptr);
+
+ if (error.Fail())
+ SetState(StateType::eStateInvalid);
+}
+
+Status NativeProcessQNX::PopulateMemoryRegionCache() {
+ Log *log = GetLog(POSIXLog::Process);
+
+ Status error;
+
+ if (!m_mem_region_cache.empty()) {
+ LLDB_LOG(log, "Reusing {0} cached memory region entries",
+ m_mem_region_cache.size());
+ return error;
+ }
+
+ // If our cache is empty, then pull the latest memory regions. There should
+ // always be at least one memory region if memory region handling is
+ // supported.
+
+ int proc_map_ents;
+ error = DevctlWrapper(m_fd, DCMD_PROC_MAPINFO, NULL, 0, &proc_map_ents);
+
+ if (error.Fail())
+ return error;
+
+ procfs_mapinfo *proc_map_info =
+ (procfs_mapinfo *)malloc(sizeof(procfs_mapinfo) * proc_map_ents);
+
+ if (!proc_map_info) {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ error = DevctlWrapper(m_fd, DCMD_PROC_MAPINFO, proc_map_info,
+ sizeof(procfs_mapinfo) * proc_map_ents, &proc_map_ents);
+
+ if (error.Fail())
+ return error;
+
+ for (int idx = 0; idx < proc_map_ents; idx++) {
+ MemoryRegionInfo info;
+ info.Clear();
+
+ info.GetRange().SetRangeBase(proc_map_info[idx].vaddr);
+ info.GetRange().SetRangeEnd(proc_map_info[idx].vaddr +
+ proc_map_info[idx].size);
+ info.SetMapped(MemoryRegionInfo::OptionalBool::eYes);
+
+ if (proc_map_info[idx].flags & PROT_READ)
+ info.SetReadable(MemoryRegionInfo::OptionalBool::eYes);
+ else
+ info.SetReadable(MemoryRegionInfo::OptionalBool::eNo);
+
+ if (proc_map_info[idx].flags & PROT_WRITE)
+ info.SetWritable(MemoryRegionInfo::OptionalBool::eYes);
+ else
+ info.SetWritable(MemoryRegionInfo::OptionalBool::eNo);
+
+ if (proc_map_info[idx].flags & PROT_EXEC)
+ info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes);
+ else
+ info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo);
+
+ // 'path' in 'procfs_debuginfo' is a one-byte array. If you want to get the
+ // path, then you need to allocate more space for it.
+ struct {
+ procfs_debuginfo debug_info;
+ char buffer[_POSIX_PATH_MAX];
+ } map;
+
+ map.debug_info.vaddr = proc_map_info[idx].vaddr;
+ error = DevctlWrapper(m_fd, DCMD_PROC_MAPDEBUG, &map, sizeof(map), NULL);
+
+ if (error.Fail())
+ return error;
+
+ if (map.debug_info.path)
+ info.SetName(map.debug_info.path);
+
+ m_mem_region_cache.emplace_back(info,
+ FileSpec(info.GetName().GetCString()));
+ }
+
+ if (m_mem_region_cache.empty()) {
+ // Couldn't find any entries. This shouldn't happen. Assume that 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;
+ error.SetErrorString("Not supported");
+ return error;
+ }
+
+ LLDB_LOG(log, "Read {0} memory region entries for process {1}",
+ m_mem_region_cache.size(), GetID());
+ // We support memory retrieval; remember that.
+ m_supports_mem_region = LazyBool::eLazyBoolYes;
+
+ return error;
+}
+
+Status NativeProcessQNX::Attach() {
+ // Attach to the requested process. An attach will cause the process to stop
+ // with a SIGSTOP.
+ procfs_status proc_status;
+ Status error = DevctlWrapper(m_fd, DCMD_PROC_STOP, &proc_status,
+ sizeof(proc_status), nullptr);
+ if (error.Fail())
+ return error;
+
+ error = DevctlWrapper(m_fd, DCMD_PROC_WAITSTOP, &proc_status,
+ sizeof(proc_status), nullptr);
+ if (error.Fail())
+ return error;
+
+ // Initialize threads and tracing status.
+ // NB: This needs to be called before we set the threads' states.
+ error = SetupTrace();
+ if (error.Fail())
+ return error;
+
+ for (const auto &thread : m_threads)
+ static_cast<NativeThreadQNX &>(*thread).SetStoppedBySignal(SIGSTOP);
+
+ // Let our process instance know that the thread has stopped.
+ SetCurrentThreadID(m_threads.front()->GetID());
+ SetState(StateType::eStateStopped, false);
+ return error;
+}
+
+llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+NativeProcessQNX::GetAuxvData() const {
+ // This is what a process's stack looks like after initialization on QNX -
+ //
+ // |________________|
+ // | |
+ // addr -> | argc | _Uint64t (Number of arguments)
+ // |________________|
+ // | |
+ // | argv[0] | _Uint64t* (Program Name)
+ // |________________|
+ // | |
+ // | argv[1] | _Uint64t*
+ // |________________|
+ // | |
+ // | argv[..] | _Uint64t*
+ // |________________|
+ // | |
+ // | argv[argc - 1] | _Uint64t*
+ // |________________|
+ // | |
+ // | argv[argc] | _Uint64t* (NULL)
+ // |________________|
+ // | |
+ // | envp[0] | _Uint64t*
+ // |________________|
+ // | |
+ // | envp[1] | _Uint64t*
+ // |________________|
+ // | |
+ // | envp[..] | _Uint64t*
+ // |________________|
+ // | |
+ // | envp[term] | _Uint64t* (NULL)
+ // |________________|
+ // | |
+ // | auxv[0] | auxv_t
+ // |________________|
+ // | |
+ // | auxv[1] | auxv_t
+ // |________________|
+ // | |
+ // | auxv[..] | auxv_t
+ // |________________|
+ // | |
+ // | auxv[term] | auxv_t (AT_NULL)
+ // |________________|
+ // | |
+ //
+ // So, seek past argc, argv, and envp to read auxv.
+
+ procfs_info proc_info;
+ Status error = DevctlWrapper(m_fd, DCMD_PROC_INFO, &proc_info,
+ sizeof(proc_info), nullptr);
+ if (error.Fail())
+ return std::error_code(error.GetError(), std::generic_category());
+
+ // Get a pointer to the initial stack.
+ _Uint8t *addr = reinterpret_cast<_Uint8t *>(proc_info.initial_stack);
+
+ // Read argc, and seek past it.
+ _Uint64t argc;
+ uint32_t addr_size = GetAddressByteSize();
+ size_t bytes_read;
+
+ error = ReadMemory(reinterpret_cast<lldb::addr_t>(addr),
+ reinterpret_cast<void *>(&argc), sizeof(argc), bytes_read);
+
+ if (error.Fail())
+ return std::error_code(error.GetError(), std::generic_category());
+
+ if (bytes_read != sizeof(argc))
+ return errorToErrorCode(llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Attempted to read {0} bytes at {1:x} but only read {2}", sizeof(argc),
+ addr, bytes_read));
+
+ addr += addr_size;
+
+ // Seek past argv.
+ addr += ((argc + 1) * addr_size);
+
+ // Seek past envp.
+ _Uint64t envp;
+ do {
+ error =
+ ReadMemory(reinterpret_cast<lldb::addr_t>(addr),
+ reinterpret_cast<void *>(&envp), sizeof(envp), bytes_read);
+
+ if (error.Fail())
+ return std::error_code(error.GetError(), std::generic_category());
+
+ if (bytes_read != sizeof(envp))
+ return errorToErrorCode(llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Attempted to read {0} bytes at {1:x} but only read {2}",
+ sizeof(envp), addr, bytes_read));
+
+ addr += addr_size;
+ } while (envp != 0);
+
+ // Read auxv.
+ auxv_t entry;
+ DataBufferHeap auxv_data(static_cast<lldb::offset_t>(0),
+ static_cast<uint8_t>(0));
+ do {
+ error =
+ ReadMemory(reinterpret_cast<lldb::addr_t>(addr),
+ reinterpret_cast<void *>(&entry), sizeof(entry), bytes_read);
+
+ if (error.Fail())
+ return std::error_code(error.GetError(), std::generic_category());
+
+ if (bytes_read != sizeof(entry))
+ return errorToErrorCode(llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Attempted to read {0} bytes at {1:x} but only read {2}",
+ sizeof(entry), addr, bytes_read));
+
+ auxv_data.AppendData(&entry, bytes_read);
+ addr += sizeof(entry);
+ } while (entry.a_type != AT_NULL);
+
+ std::unique_ptr<WritableMemoryBuffer> auxv =
+ llvm::WritableMemoryBuffer::getNewMemBuffer(auxv_data.GetByteSize());
+
+ std::memcpy(auxv->getBufferStart(), auxv_data.GetBytes(),
+ auxv->getBufferSize());
+
+ return auxv;
+}
+
+Status NativeProcessQNX::SetupTrace() {
+ // The debugger no longer receives SIGCHLD from the debuggee upon establishing
+ // a file descriptor to /proc/$pid/as. So, have the server deliver a SIGCHLD
+ // to the debugger whenever the debuggee reaches a point of interest. Also,
+ // have siginfo_t.si_value.sival_int set to the debuggee's PID so that we can
+ // identify the debuggee in NativeProcessQNX::Manager::SigchldHandler.
+ struct sigevent event;
+ Status error;
+ SIGEV_SIGNAL_VALUE_INT_INIT(&event, SIGCHLD, m_pid);
+ if (MsgRegisterEvent(&event, m_fd) != EOK) {
+ error.SetErrorToErrno();
+ return error;
+ }
+ error = DevctlWrapper(m_fd, DCMD_PROC_EVENT, &event, sizeof(struct sigevent),
+ nullptr);
+ if (error.Fail())
+ return error;
+
+ // Let the debuggee run on detach (closure of last file descriptor to
+ // /proc/$pid/as).
+ int flags = _DEBUG_FLAG_RLC;
+ error =
+ DevctlWrapper(m_fd, DCMD_PROC_CLEAR_FLAG, &flags, sizeof(flags), nullptr);
+ if (error.Fail())
+ return error;
+
+ // TODO: Indicate interest in getting notified when the debuggee forks,
+ // vforks, spawns, or execs.
+
+ // The child process is held as if it had received a SIGSTOP as soon as it had
+ // been spawned. To resume this process, we must send it a SIGCONT. However,
+ // we don't want it to resume execution here.
+ Halt();
+ Signal(SIGCONT);
+
+ return ReinitializeThreads();
+}
+
+Status NativeProcessQNX::ReinitializeThreads() {
+ // Clear old threads.
+ m_threads.clear();
+
+ procfs_info proc_info;
+ Status error = DevctlWrapper(m_fd, DCMD_PROC_INFO, &proc_info,
+ sizeof(proc_info), nullptr);
+ if (error.Fail())
+ return error;
+
+ _Uint32t num_threads = proc_info.num_threads;
+
+ // QNX returns information about the thread specified in the tid member of
+ // status if it has it; otherwise, it will either return information on the
+ // next available thread ID, or return something other than EOK to indicate
+ // that it's done.
+
+ procfs_status proc_status;
+ proc_status.tid = 0;
+
+ std::vector<pthread_t> tids;
+
+ for (int idx = 0; idx < num_threads; ++idx) {
+ // QNX starts numbering threads from 1.
+ proc_status.tid++;
+
+ error = DevctlWrapper(m_fd, DCMD_PROC_TIDSTATUS, &proc_status,
+ sizeof(proc_status), nullptr);
+
+ if (error.Fail())
+ return error;
+
+ tids.push_back(proc_status.tid);
+ }
+
+ // Reinitialize threads from scratch and register them with the process.
+ for (pthread_t tid : tids) {
+ if (tid < 1) {
+ error.SetErrorString("tid < 1");
+ return error;
+ }
+ AddThread(tid);
+ }
+
+ return error;
+}
+
+Status NativeProcessQNX::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ size_t &bytes_read) const {
+ Log *log = GetLog(POSIXLog::Memory);
+
+ LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
+
+ bytes_read = size;
+ off_t offset = static_cast<off_t>(addr);
+
+ return m_file_up->Read(buf, bytes_read, offset);
+}
diff --git a/lldb/source/Plugins/Process/QNX/NativeProcessQNX.h b/lldb/source/Plugins/Process/QNX/NativeProcessQNX.h
new file mode 100644
index 0000000000000..fe24d772ffdda
--- /dev/null
+++ b/lldb/source/Plugins/Process/QNX/NativeProcessQNX.h
@@ -0,0 +1,157 @@
+//===-- NativeProcessQNX.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_NativeProcessQNX_H_
+#define liblldb_NativeProcessQNX_H_
+
+#include <sys/procfs.h>
+
+#include "Plugins/Process/POSIX/NativeProcessELF.h"
+#include "Plugins/Process/Utility/NativeProcessSoftwareSingleStep.h"
+
+#include "lldb/lldb-forward.h"
+
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+#include "NativeThreadQNX.h"
+
+namespace lldb_private {
+namespace process_qnx {
+/// \class NativeProcessQNX
+/// Manages communication with the inferior (debuggee) process.
+///
+/// Upon construction, this class prepares and launches an inferior process
+/// for debugging.
+///
+/// Changes in the inferior process state are broadcasted.
+class NativeProcessQNX : public NativeProcessELF {
+public:
+ class Manager : public NativeProcessProtocol::Manager {
+ public:
+ Manager(MainLoop &mainloop);
+
+ 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;
+
+ void AddProcess(NativeProcessQNX &process) { m_processes.insert(&process); }
+
+ void RemoveProcess(NativeProcessQNX &process) {
+ m_processes.erase(&process);
+ }
+
+ private:
+ MainLoop::SignalHandleUP m_sigchld_handle;
+
+ llvm::SmallPtrSet<NativeProcessQNX *, 2> m_processes;
+
+ void SigchldHandler();
+ };
+
+ // NativeProcessProtocol interface.
+
+ ~NativeProcessQNX() override { m_manager.RemoveProcess(*this); }
+
+ 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;
+
+ Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) 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 DevctlWrapper(int fd, int dcmd, void *dev_data_ptr = nullptr,
+ size_t n_bytes = 0, int *dev_info_ptr = nullptr);
+
+ int GetFileDescriptor() const { return m_fd; }
+
+private:
+ Manager &m_manager;
+ ArchSpec m_arch;
+ LazyBool m_supports_mem_region = eLazyBoolCalculate;
+ std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache;
+ lldb::FileUP m_file_up;
+ int m_fd;
+
+ // Private instance methods.
+
+ NativeProcessQNX(::pid_t pid, int terminal_fd, NativeDelegate &delegate,
+ Manager &manager, const ArchSpec &arch, Status &status);
+
+ NativeThreadQNX &AddThread(lldb::tid_t thread_id);
+
+ void RemoveThread(lldb::tid_t thread_id);
+
+ void MonitorInterrupt();
+
+ void MonitorCallback(procfs_status &proc_status);
+
+ void MonitorSIGTRAP(procfs_status &proc_status);
+
+ void MonitorSIGSTOP();
+
+ void MonitorSignal(procfs_status &proc_status);
+
+ void MonitorExited(procfs_status &proc_status);
+
+ void MonitorThread(procfs_status &proc_status);
+
+ Status PopulateMemoryRegionCache();
+
+ Status Attach();
+
+ Status SetupTrace();
+
+ Status ReinitializeThreads();
+
+ Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+ size_t &bytes_read) const;
+};
+
+} // namespace process_qnx
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeProcessQNX_H_
diff --git a/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX.cpp b/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX.cpp
new file mode 100644
index 0000000000000..22ce63be75bc5
--- /dev/null
+++ b/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX.cpp
@@ -0,0 +1,12 @@
+//===-- NativeRegisterContextQNX.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 "NativeRegisterContextQNX.h"
+
+using namespace lldb_private;
+using namespace lldb_private::process_qnx;
diff --git a/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX.h b/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX.h
new file mode 100644
index 0000000000000..7c7e191a94eda
--- /dev/null
+++ b/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX.h
@@ -0,0 +1,37 @@
+//===-- NativeRegisterContextQNX.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_NativeRegisterContextQNX_h
+#define lldb_NativeRegisterContextQNX_h
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
+
+namespace lldb_private {
+namespace process_qnx {
+
+class NativeThreadQNX;
+
+class NativeRegisterContextQNX
+ : public virtual NativeRegisterContextRegisterInfo {
+public:
+ // This function is implemented in the NativeRegisterContextQNX_*
+ // subclasses to create a new instance of the host specific
+ // NativeRegisterContextQNX. The implementations can't collide as only one
+ // NativeRegisterContextQNX_* variant should be compiled into the final
+ // executable.
+ static NativeRegisterContextQNX *
+ CreateHostNativeRegisterContextQNX(const ArchSpec &target_arch,
+ NativeThreadProtocol &native_thread);
+};
+
+} // namespace process_qnx
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextQNX_h
diff --git a/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX_arm64.cpp b/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX_arm64.cpp
new file mode 100644
index 0000000000000..75821f10b0bd8
--- /dev/null
+++ b/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX_arm64.cpp
@@ -0,0 +1,344 @@
+//===-- NativeRegisterContextQNX_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__) && defined(__QNX__)
+
+#include "NativeRegisterContextQNX_arm64.h"
+
+#include <sys/procfs.h>
+
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "Plugins/Process/QNX/NativeProcessQNX.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_qnx;
+
+NativeRegisterContextQNX *
+NativeRegisterContextQNX::CreateHostNativeRegisterContextQNX(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+ return new NativeRegisterContextQNX_arm64(target_arch, native_thread);
+}
+
+NativeRegisterContextQNX_arm64::NativeRegisterContextQNX_arm64(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
+ : NativeRegisterContextRegisterInfo(
+ native_thread, new RegisterInfoPOSIX_arm64(target_arch, 0)) {}
+
+uint32_t NativeRegisterContextQNX_arm64::GetRegisterSetCount() const {
+ return GetRegisterInfo().GetRegisterSetCount();
+}
+
+uint32_t NativeRegisterContextQNX_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;
+}
+
+const RegisterSet *
+NativeRegisterContextQNX_arm64::GetRegisterSet(uint32_t set_index) const {
+ return GetRegisterInfo().GetRegisterSet(set_index);
+}
+
+Status
+NativeRegisterContextQNX_arm64::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue ®_value) {
+ Status error;
+
+ if (!reg_info) {
+ error.SetErrorString("reg_info is NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+ if (reg == LLDB_INVALID_REGNUM) {
+ error.SetErrorStringWithFormat(
+ "no lldb regnum for %s",
+ reg_info && reg_info->name ? reg_info->name : "<unknown register>");
+ return error;
+ }
+
+ uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg);
+
+ switch (set) {
+ // TODO: Read alternate and performance registers.
+ case RegisterInfoPOSIX_arm64::GPRegSet:
+ if (reg_info->byte_offset + reg_info->byte_size >
+ sizeof(AARCH64_CPU_REGISTERS)) {
+ error.SetErrorString("reg_info->byte_offset + reg_info->byte_size > "
+ "sizeof(AARCH64_CPU_REGISTERS)");
+ return error;
+ }
+ error = ReadGPR();
+ if (error.Fail())
+ return error;
+ reg_value.SetBytes(reinterpret_cast<_Uint8t *>(&m_cpu_reg_data) +
+ reg_info->byte_offset,
+ reg_info->byte_size, endian::InlHostByteOrder());
+ break;
+
+ case RegisterInfoPOSIX_arm64::FPRegSet:
+ if (reg_info->byte_offset + reg_info->byte_size >
+ sizeof(AARCH64_FPU_REGISTERS)) {
+ error.SetErrorString("reg_info->byte_offset + reg_info->byte_size > "
+ "sizeof(AARCH64_FPU_REGISTERS)");
+ return error;
+ }
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+ reg_value.SetBytes(reinterpret_cast<_Uint8t *>(&m_fpu_reg_data) +
+ reg_info->byte_offset,
+ reg_info->byte_size, endian::InlHostByteOrder());
+ break;
+
+ default:
+ error.SetErrorString("unrecognized register set");
+ }
+
+ return error;
+}
+
+Status
+NativeRegisterContextQNX_arm64::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue ®_value) {
+ Status error;
+
+ if (!reg_info) {
+ error.SetErrorString("reg_info is NULL");
+ return error;
+ }
+
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+
+ if (reg == LLDB_INVALID_REGNUM) {
+ error.SetErrorStringWithFormat(
+ "no lldb regnum for %s",
+ reg_info && reg_info->name ? reg_info->name : "<unknown register>");
+ return error;
+ }
+
+ uint32_t set = GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg);
+
+ switch (set) {
+ // TODO: Write to alternate and performance registers.
+ case RegisterInfoPOSIX_arm64::GPRegSet:
+ if (reg_info->byte_offset + reg_info->byte_size >
+ sizeof(AARCH64_CPU_REGISTERS)) {
+ error.SetErrorString("reg_info->byte_offset + reg_info->byte_size > "
+ "sizeof(AARCH64_CPU_REGISTERS)");
+ return error;
+ }
+ ::memcpy(&m_cpu_reg_data.gpr + reg_info->byte_offset, reg_value.GetBytes(),
+ reg_info->byte_size);
+ error = WriteGPR();
+ if (error.Fail())
+ return error;
+ break;
+
+ case RegisterInfoPOSIX_arm64::FPRegSet:
+ if (reg_info->byte_offset + reg_info->byte_size >
+ sizeof(AARCH64_FPU_REGISTERS)) {
+ error.SetErrorString("reg_info->byte_offset + reg_info->byte_size > "
+ "sizeof(AARCH64_FPU_REGISTERS)");
+ return error;
+ }
+ ::memcpy(&m_fpu_reg_data.reg + reg_info->byte_offset, reg_value.GetBytes(),
+ reg_info->byte_size);
+ error = WriteFPR();
+ if (error.Fail())
+ return error;
+ break;
+
+ default:
+ error.SetErrorString("unrecognized register set");
+ }
+
+ return error;
+}
+
+Status NativeRegisterContextQNX_arm64::ReadAllRegisterValues(
+ lldb::WritableDataBufferSP &data_sp) {
+ // TODO: Read alternate and performance registers.
+ Status error;
+ uint32_t reg_data_byte_size = sizeof(AARCH64_CPU_REGISTERS);
+
+ error = ReadGPR();
+ if (error.Fail())
+ return error;
+
+ reg_data_byte_size += sizeof(AARCH64_FPU_REGISTERS);
+
+ error = ReadFPR();
+ if (error.Fail())
+ return error;
+
+ data_sp.reset(new DataBufferHeap(reg_data_byte_size, 0));
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy(dst, &m_cpu_reg_data.gpr, sizeof(AARCH64_CPU_REGISTERS));
+ dst += sizeof(AARCH64_CPU_REGISTERS);
+ ::memcpy(dst, &m_fpu_reg_data.reg, sizeof(AARCH64_FPU_REGISTERS));
+
+ return error;
+}
+
+Status NativeRegisterContextQNX_arm64::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ Status error;
+
+ if (!data_sp) {
+ error.SetErrorString("data_sp is NULL");
+ return error;
+ }
+
+ if (data_sp->GetByteSize() !=
+ (sizeof(AARCH64_CPU_REGISTERS) + sizeof(AARCH64_FPU_REGISTERS))) {
+ error.SetErrorStringWithFormat(
+ "data_sp->GetByteSize() != "
+ "(sizeof(AARCH64_CPU_REGISTERS) + sizeof(AARCH64_FPU_REGISTERS)), "
+ "expected %" PRIu64 ", is %" PRIu64,
+ sizeof(AARCH64_CPU_REGISTERS) + sizeof(AARCH64_FPU_REGISTERS),
+ data_sp->GetByteSize());
+ return error;
+ }
+
+ const uint8_t *src = data_sp->GetBytes();
+ if (src == nullptr) {
+ error.SetErrorString("DataBuffer::GetBytes() returned nullptr");
+ return error;
+ }
+ ::memcpy(&m_cpu_reg_data.gpr, src, sizeof(AARCH64_CPU_REGISTERS));
+ error = WriteGPR();
+ if (error.Fail())
+ return error;
+
+ src += sizeof(AARCH64_CPU_REGISTERS);
+ ::memcpy(&m_fpu_reg_data.reg, src, sizeof(AARCH64_FPU_REGISTERS));
+ error = WriteFPR();
+ if (error.Fail())
+ return error;
+
+ return error;
+}
+
+Status NativeRegisterContextQNX_arm64::ReadGPR() {
+ procfs_greg greg;
+ memset(&greg, 0x0, sizeof(procfs_greg));
+
+ pthread_t tid = static_cast<pthread_t>(GetThreadID());
+ int greg_size;
+ Status error = NativeProcessQNX::DevctlWrapper(
+ static_cast<NativeProcessQNX &>(GetThread().GetProcess())
+ .GetFileDescriptor(),
+ DCMD_PROC_CURTHREAD, &tid, sizeof(tid), nullptr);
+
+ if (error.Fail())
+ return error;
+
+ error = NativeProcessQNX::DevctlWrapper(
+ static_cast<NativeProcessQNX &>(GetThread().GetProcess())
+ .GetFileDescriptor(),
+ DCMD_PROC_GETGREG, &greg, sizeof(procfs_greg), &greg_size);
+
+ if (error.Fail())
+ return error;
+
+ if (greg_size != sizeof(AARCH64_CPU_REGISTERS)) {
+ error.SetErrorString("greg_size != sizeof(AARCH64_CPU_REGISTERS)");
+ return error;
+ }
+
+ memcpy(&m_cpu_reg_data, &greg, sizeof(AARCH64_CPU_REGISTERS));
+
+ return error;
+}
+
+Status NativeRegisterContextQNX_arm64::WriteGPR() {
+ procfs_greg greg;
+ memcpy(&greg, &m_cpu_reg_data, sizeof(AARCH64_CPU_REGISTERS));
+
+ pthread_t tid = static_cast<pthread_t>(GetThreadID());
+ Status error = NativeProcessQNX::DevctlWrapper(
+ static_cast<NativeProcessQNX &>(GetThread().GetProcess())
+ .GetFileDescriptor(),
+ DCMD_PROC_CURTHREAD, &tid, sizeof(tid), nullptr);
+
+ if (error.Fail())
+ return error;
+
+ return NativeProcessQNX::DevctlWrapper(
+ static_cast<NativeProcessQNX &>(GetThread().GetProcess())
+ .GetFileDescriptor(),
+ DCMD_PROC_SETGREG, &greg, sizeof(procfs_greg), nullptr);
+}
+
+Status NativeRegisterContextQNX_arm64::ReadFPR() {
+ // If the thread hasn't used any floating-point arithmetic, then the read may
+ // fail because an FPU context hasn't been allocated yet.
+ procfs_fpreg fpreg;
+ memset(&fpreg, 0x0, sizeof(procfs_fpreg));
+
+ pthread_t tid = static_cast<pthread_t>(GetThreadID());
+ int fpreg_size;
+ Status error = NativeProcessQNX::DevctlWrapper(
+ static_cast<NativeProcessQNX &>(GetThread().GetProcess())
+ .GetFileDescriptor(),
+ DCMD_PROC_CURTHREAD, &tid, sizeof(tid), nullptr);
+
+ if (error.Fail())
+ return error;
+
+ error = NativeProcessQNX::DevctlWrapper(
+ static_cast<NativeProcessQNX &>(GetThread().GetProcess())
+ .GetFileDescriptor(),
+ DCMD_PROC_GETFPREG, &fpreg, sizeof(procfs_fpreg), &fpreg_size);
+
+ if (error.Fail())
+ return error;
+
+ if (fpreg_size != sizeof(AARCH64_FPU_REGISTERS)) {
+ error.SetErrorString("fpreg_size != sizeof(AARCH64_FPU_REGISTERS)");
+ return error;
+ }
+
+ memcpy(&m_fpu_reg_data, &fpreg, sizeof(AARCH64_FPU_REGISTERS));
+
+ return error;
+}
+
+Status NativeRegisterContextQNX_arm64::WriteFPR() {
+ procfs_fpreg fpreg;
+ memcpy(&fpreg, &m_fpu_reg_data, sizeof(AARCH64_FPU_REGISTERS));
+
+ pthread_t tid = static_cast<pthread_t>(GetThreadID());
+ Status error = NativeProcessQNX::DevctlWrapper(
+ static_cast<NativeProcessQNX &>(GetThread().GetProcess())
+ .GetFileDescriptor(),
+ DCMD_PROC_CURTHREAD, &tid, sizeof(tid), nullptr);
+
+ if (error.Fail())
+ return error;
+
+ return NativeProcessQNX::DevctlWrapper(
+ static_cast<NativeProcessQNX &>(GetThread().GetProcess())
+ .GetFileDescriptor(),
+ DCMD_PROC_SETFPREG, &fpreg, sizeof(procfs_fpreg), nullptr);
+}
+
+RegisterInfoPOSIX_arm64 &
+NativeRegisterContextQNX_arm64::GetRegisterInfo() const {
+ return static_cast<RegisterInfoPOSIX_arm64 &>(*m_register_info_interface_up);
+}
+
+#endif // defined(__aarch64__) && defined(__QNX__)
diff --git a/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX_arm64.h b/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX_arm64.h
new file mode 100644
index 0000000000000..b37aee637d1cb
--- /dev/null
+++ b/lldb/source/Plugins/Process/QNX/NativeRegisterContextQNX_arm64.h
@@ -0,0 +1,66 @@
+//===-- NativeRegisterContextQNX_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__) && defined(__QNX__)
+
+#ifndef lldb_NativeRegisterContextQNX_arm64_h
+#define lldb_NativeRegisterContextQNX_arm64_h
+
+#include <aarch64/context.h>
+
+#include "Plugins/Process/QNX/NativeRegisterContextQNX.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+
+namespace lldb_private {
+namespace process_qnx {
+
+class NativeProcessQNX;
+
+class NativeRegisterContextQNX_arm64 : public NativeRegisterContextQNX {
+public:
+ NativeRegisterContextQNX_arm64(const ArchSpec &target_arch,
+ NativeThreadProtocol &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;
+
+protected:
+ Status ReadGPR();
+
+ Status WriteGPR();
+
+ Status ReadFPR();
+
+ Status WriteFPR();
+
+private:
+ AARCH64_CPU_REGISTERS m_cpu_reg_data;
+ AARCH64_FPU_REGISTERS m_fpu_reg_data;
+
+ RegisterInfoPOSIX_arm64 &GetRegisterInfo() const;
+};
+
+} // namespace process_qnx
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextQNX_arm64_h
+
+#endif // defined(__aarch64__) && defined(__QNX__)
diff --git a/lldb/source/Plugins/Process/QNX/NativeThreadQNX.cpp b/lldb/source/Plugins/Process/QNX/NativeThreadQNX.cpp
new file mode 100644
index 0000000000000..313e975b66ae8
--- /dev/null
+++ b/lldb/source/Plugins/Process/QNX/NativeThreadQNX.cpp
@@ -0,0 +1,141 @@
+//===-- NativeThreadQNX.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 "NativeThreadQNX.h"
+#include "NativeRegisterContextQNX.h"
+
+#include "NativeProcessQNX.h"
+
+#include "Plugins/Process/POSIX/CrashReason.h"
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_qnx;
+
+NativeThreadQNX::NativeThreadQNX(NativeProcessQNX &process, lldb::tid_t tid)
+ : NativeThreadProtocol(process, tid), m_state(StateType::eStateInvalid),
+ m_stop_info(),
+ m_reg_context_up(
+ NativeRegisterContextQNX::CreateHostNativeRegisterContextQNX(
+ process.GetArchitecture(), *this)),
+ m_stop_description() {}
+
+Status NativeThreadQNX::Resume() {
+ m_state = StateType::eStateRunning;
+ m_stop_info.reason = StopReason::eStopReasonNone;
+ return Status();
+}
+
+Status NativeThreadQNX::SingleStep() {
+ m_state = StateType::eStateStepping;
+ m_stop_info.reason = StopReason::eStopReasonNone;
+ return Status();
+}
+
+void NativeThreadQNX::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 NativeThreadQNX::SetStoppedByBreakpoint() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonBreakpoint;
+ m_stop_info.signo = SIGTRAP;
+}
+
+void NativeThreadQNX::SetStoppedByTrace() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonTrace;
+ m_stop_info.signo = SIGTRAP;
+}
+
+void NativeThreadQNX::SetStoppedWithNoReason() {
+ SetStopped();
+ m_stop_info.reason = StopReason::eStopReasonNone;
+ m_stop_info.signo = 0;
+}
+
+void NativeThreadQNX::SetStopped() {
+ const StateType new_state = StateType::eStateStopped;
+ m_state = new_state;
+ m_stop_description.clear();
+}
+
+std::string NativeThreadQNX::GetName() { return ""; }
+
+lldb::StateType NativeThreadQNX::GetState() { return m_state; }
+
+bool NativeThreadQNX::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!");
+}
+
+NativeRegisterContextQNX &NativeThreadQNX::GetRegisterContext() {
+ return *m_reg_context_up;
+}
+
+Status NativeThreadQNX::SetWatchpoint(lldb::addr_t addr, size_t size,
+ uint32_t watch_flags, bool hardware) {
+ return Status("not implemented");
+}
+
+Status NativeThreadQNX::RemoveWatchpoint(lldb::addr_t addr) {
+ return Status("not implemented");
+}
+
+Status NativeThreadQNX::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) {
+ return Status("not implemented");
+}
+
+Status NativeThreadQNX::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+ return Status("not implemented");
+}
diff --git a/lldb/source/Plugins/Process/QNX/NativeThreadQNX.h b/lldb/source/Plugins/Process/QNX/NativeThreadQNX.h
new file mode 100644
index 0000000000000..f1fd97f05b90e
--- /dev/null
+++ b/lldb/source/Plugins/Process/QNX/NativeThreadQNX.h
@@ -0,0 +1,73 @@
+//===-- NativeThreadQNX.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_NativeThreadQNX_H_
+#define liblldb_NativeThreadQNX_H_
+
+#include "lldb/Host/common/NativeThreadProtocol.h"
+
+#include "Plugins/Process/QNX/NativeRegisterContextQNX.h"
+
+namespace lldb_private {
+namespace process_qnx {
+
+class NativeProcessQNX;
+
+class NativeThreadQNX : public NativeThreadProtocol {
+ friend class NativeProcessQNX;
+
+public:
+ NativeThreadQNX(NativeProcessQNX &process, lldb::tid_t tid);
+
+ // NativeThreadProtocol Interface
+ std::string GetName() override;
+
+ lldb::StateType GetState() override;
+
+ bool GetStopReason(ThreadStopInfo &stop_info,
+ std::string &description) override;
+
+ NativeRegisterContextQNX &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();
+
+ void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr);
+
+ void SetStoppedByBreakpoint();
+
+ void SetStoppedByTrace();
+
+ void SetStoppedWithNoReason();
+
+ void SetStopped();
+
+ // Member Variables
+ lldb::StateType m_state;
+ ThreadStopInfo m_stop_info;
+ std::unique_ptr<NativeRegisterContextQNX> m_reg_context_up;
+ std::string m_stop_description;
+};
+
+} // namespace process_qnx
+} // namespace lldb_private
+
+#endif // #ifndef liblldb_NativeThreadQNX_H_
diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
index 5df4a9e5ac5c8..3eb0523cf28c4 100644
--- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
@@ -13,6 +13,7 @@ add_lldb_library(lldbPluginProcessUtility
NativeRegisterContextDBReg_x86.cpp
NativeRegisterContextRegisterInfo.cpp
NetBSDSignals.cpp
+ QNXSignals.cpp
RegisterContext_x86.cpp
RegisterContextDarwin_arm.cpp
RegisterContextDarwin_arm64.cpp
diff --git a/lldb/source/Plugins/Process/Utility/QNXSignals.cpp b/lldb/source/Plugins/Process/Utility/QNXSignals.cpp
new file mode 100644
index 0000000000000..a79ed9ca9bf0a
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/QNXSignals.cpp
@@ -0,0 +1,140 @@
+//===-- QNXSignals.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 "QNXSignals.h"
+
+#if defined(__QNX__)
+#include <signal.h>
+#include <sys/siginfo.h>
+
+#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 #code_value); \
+ 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 // defined(__QNX__)
+
+using namespace lldb_private;
+
+QNXSignals::QNXSignals() : UnixSignals() { Reset(); }
+
+void QNXSignals::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)");
+ ADD_SIGCODE(SIGTRAP, 5, TRAP_BRKPT, 1, "Break Point");
+ ADD_SIGCODE(SIGTRAP, 5, TRAP_TRACE, 2, "Trace");
+ ADD_SIGCODE(SIGTRAP, 5, TRAP_KDBRK, 3, "Kdebug Break Point");
+ ADD_SIGCODE(SIGTRAP, 5, TRAP_CRASH, 4, "Crash");
+
+ AddSignal(6, "SIGIOT", false, true, true, "IOT trap/abort()", "SIGABRT");
+ AddSignal(7, "SIGEMT", false, true, true, "EMT instruction/Mutex deadlock", "SIGDEADLK");
+
+ 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, "Invalid floating point operation");
+ ADD_SIGCODE(SIGFPE, 8, FPE_FLTSUB, 8, "Subscript out of range");
+ ADD_SIGCODE(SIGFPE, 8, FPE_NOFPU, 9, "No FPU or emulator");
+ ADD_SIGCODE(SIGFPE, 8, FPE_NOMEM, 10, "No kernel space for FPU save area");
+
+ AddSignal(9, "SIGKILL", false, true, true, "kill");
+
+ AddSignal(10, "SIGBUS", false, true, true, "bus error");
+ ADD_SIGCODE(SIGBUS, 10, BUS_ADRALN, 1, "Invalid address alignment");
+ ADD_SIGCODE(SIGBUS, 10, BUS_ADRERR, 2, "Non-existant physical address");
+ ADD_SIGCODE(SIGBUS, 10, BUS_OBJERR, 3, "Object specific hardware error (e.g. NMI parity error)");
+ ADD_SIGCODE(SIGBUS, 10, BUS_BADPAGE, 4, "N/A");
+ ADD_SIGCODE(SIGBUS, 10, BUS_ENDOBJ, 5, "N/A");
+ ADD_SIGCODE(SIGBUS, 10, BUS_EOTHER, 6, "Other not translated error faults");
+ ADD_SIGCODE(SIGBUS, 10, BUS_ENOENT, 7, "N/A");
+ ADD_SIGCODE(SIGBUS, 10, BUS_EAGAIN, 8, "Transient fault - many times a no memory condition");
+ ADD_SIGCODE(SIGBUS, 10, BUS_ENOMEM, 9, "No memory available to execute request");
+ ADD_SIGCODE(SIGBUS, 10, BUS_EFAULT, 10, "N/A");
+ ADD_SIGCODE(SIGBUS, 10, BUS_EINVAL, 11, "Invalid argument(s) passed");
+ ADD_SIGCODE(SIGBUS, 10, BUS_EACCES, 12, "N/A");
+ ADD_SIGCODE(SIGBUS, 10, BUS_EBADF, 13, "Invalid file descriptor");
+ ADD_SIGCODE(SIGBUS, 10, BUS_SRVERR, 50, "N/A");
+
+ AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation");
+ ADD_SIGCODE(SIGSEGV, 11, SEGV_MAPERR, 1, "Address not mapped");
+ ADD_SIGCODE(SIGSEGV, 11, SEGV_ACCERR, 2, "No permissions");
+ ADD_SIGCODE(SIGSEGV, 11, SEGV_STKERR, 3, "Stack exception");
+ ADD_SIGCODE(SIGSEGV, 11, SEGV_GPERR, 4, "General protection");
+ ADD_SIGCODE(SIGSEGV, 11, SEGV_IRQERR, 5, "Interrupt handler fault");
+
+ 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, "SIGUSR1", false, true, true, "user defined signal 1");
+ AddSignal(17, "SIGUSR2", false, true, true, "user defined signal 2");
+
+ AddSignal(18, "SIGCHLD", false, false, true, "child status has changed", "SIGCLD");
+ ADD_SIGCODE(SIGCHLD, 18, CLD_EXITED, 1, "Child has exited");
+ ADD_SIGCODE(SIGCHLD, 18, CLD_KILLED, 2, "Child was killed");
+ ADD_SIGCODE(SIGCHLD, 18, CLD_DUMPED, 3, "Child terminated abnormally");
+ ADD_SIGCODE(SIGCHLD, 18, CLD_TRAPPED, 4, "Traced child has trapped");
+ ADD_SIGCODE(SIGCHLD, 18, CLD_STOPPED, 5, "Child has stopped");
+ ADD_SIGCODE(SIGCHLD, 18, CLD_CONTINUED, 6, "Stopped child had continued");
+
+ AddSignal(19, "SIGPWR", false, true, true, "power failure");
+ AddSignal(20, "SIGWINCH", false, true, true, "window size changes");
+ AddSignal(21, "SIGURG", false, true, true, "urgent data on socket");
+ AddSignal(22, "SIGPOLL", false, true, true, "Pollable event / input/output ready", "SIGIO");
+ AddSignal(23, "SIGSTOP", true, true, true, "process stop");
+ AddSignal(24, "SIGTSTP", false, true, true, "tty stop");
+ AddSignal(25, "SIGCONT", false, false, true, "process continue");
+ AddSignal(36, "SIGTTIN", false, true, true, "background tty read");
+ AddSignal(27, "SIGTTOU", false, true, true, "background tty write");
+ AddSignal(28, "SIGVTALRM", false, true, true, "virtual time alarm");
+ AddSignal(29, "SIGPROF", false, false, false, "profiling time alarm");
+ AddSignal(30, "SIGXCPU", false, true, true, "CPU resource exceeded");
+ AddSignal(31, "SIGXFSZ", false, true, true, "file size limit exceeded");
+ AddSignal(41, "SIGRTMIN", false, false, false, "real time signal 0");
+ AddSignal(42, "SIGRTMIN+1", false, false, false, "real time signal 1");
+ AddSignal(43, "SIGRTMIN+2", false, false, false, "real time signal 2");
+ AddSignal(44, "SIGRTMIN+3", false, false, false, "real time signal 3");
+ AddSignal(45, "SIGRTMIN+4", false, false, false, "real time signal 4");
+ AddSignal(46, "SIGRTMIN+5", false, false, false, "real time signal 5");
+ AddSignal(47, "SIGRTMIN+6", false, false, false, "real time signal 6");
+ AddSignal(48, "SIGRTMIN+7", false, false, false, "real time signal 7");
+ AddSignal(49, "SIGRTMAX-7", false, false, false, "real time signal 8");
+ AddSignal(50, "SIGRTMAX-6", false, false, false, "real time signal 9");
+ AddSignal(51, "SIGRTMAX-5", false, false, false, "real time signal 10");
+ AddSignal(52, "SIGRTMAX-4", false, false, false, "real time signal 11");
+ AddSignal(53, "SIGRTMAX-3", false, false, false, "real time signal 12");
+ AddSignal(54, "SIGRTMAX-2", false, false, false, "real time signal 13");
+ AddSignal(55, "SIGRTMAX-1", false, false, false, "real time signal 14");
+ AddSignal(56, "SIGRTMAX", false, false, false, "real time signal 15");
+ // clang-format on
+}
diff --git a/lldb/source/Plugins/Process/Utility/QNXSignals.h b/lldb/source/Plugins/Process/Utility/QNXSignals.h
new file mode 100644
index 0000000000000..7534e46cbb708
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/QNXSignals.h
@@ -0,0 +1,27 @@
+//===-- QNXSignals.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_QNXSIGNALS_H
+#define LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_QNXSIGNALS_H
+
+#include "lldb/Target/UnixSignals.h"
+
+namespace lldb_private {
+
+/// QNX specific set of Unix signals.
+class QNXSignals : public UnixSignals {
+public:
+ QNXSignals();
+
+private:
+ void Reset() override;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_QNXSIGNALS_H
diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp
index e3c7a83ece073..fff2481943e4a 100644
--- a/lldb/source/Target/UnixSignals.cpp
+++ b/lldb/source/Target/UnixSignals.cpp
@@ -10,6 +10,7 @@
#include "Plugins/Process/Utility/FreeBSDSignals.h"
#include "Plugins/Process/Utility/LinuxSignals.h"
#include "Plugins/Process/Utility/NetBSDSignals.h"
+#include "Plugins/Process/Utility/QNXSignals.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Utility/ArchSpec.h"
#include <optional>
@@ -36,6 +37,8 @@ lldb::UnixSignalsSP UnixSignals::Create(const ArchSpec &arch) {
return std::make_shared<FreeBSDSignals>();
case llvm::Triple::NetBSD:
return std::make_shared<NetBSDSignals>();
+ case llvm::Triple::QNX:
+ return std::make_shared<QNXSignals>();
default:
return std::make_shared<UnixSignals>();
}
diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt
index e9954d66cd1a5..f38599cc52472 100644
--- a/lldb/source/Utility/CMakeLists.txt
+++ b/lldb/source/Utility/CMakeLists.txt
@@ -23,6 +23,10 @@ if (NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB )
list(APPEND LLDB_SYSTEM_LIBS atomic)
endif()
+if (CMAKE_SYSTEM_NAME MATCHES "QNX")
+ list(APPEND LLDB_SYSTEM_LIBS socket)
+endif()
+
add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES
AddressableBits.cpp
ArchSpec.cpp
diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt
index 9030ed709a647..d7687e6a73941 100644
--- a/lldb/tools/lldb-server/CMakeLists.txt
+++ b/lldb/tools/lldb-server/CMakeLists.txt
@@ -24,6 +24,10 @@ else()
list(APPEND LLDB_PLUGINS lldbPluginObjectFileELF)
endif()
+if(CMAKE_SYSTEM_NAME MATCHES "QNX")
+ list(APPEND LLDB_PLUGINS lldbPluginProcessQNX)
+endif()
+
if(APPLE_EMBEDDED)
if(LLDB_CODESIGN_IDENTITY)
# Use explicit LLDB identity
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index 563284730bc70..bde3ad9d9c0d3 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -45,6 +45,8 @@
#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"
#elif defined(_WIN32)
#include "Plugins/Process/Windows/Common/NativeProcessWindows.h"
+#elif defined(__QNX__)
+#include "Plugins/Process/QNX/NativeProcessQNX.h"
#endif
#ifndef LLGS_PROGRAM_NAME
@@ -70,6 +72,8 @@ typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager;
typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager;
#elif defined(_WIN32)
typedef NativeProcessWindows::Manager NativeProcessManager;
+#elif defined(__QNX__)
+typedef process_qnx::NativeProcessQNX::Manager NativeProcessManager;
#else
// Dummy implementation to make sure the code compiles
class NativeProcessManager : public NativeProcessProtocol::Manager {
diff --git a/llvm/include/llvm/Support/ExitCodes.h b/llvm/include/llvm/Support/ExitCodes.h
index 4eb5dedc688bc..f8876ac4e3712 100644
--- a/llvm/include/llvm/Support/ExitCodes.h
+++ b/llvm/include/llvm/Support/ExitCodes.h
@@ -20,11 +20,11 @@
#if HAVE_SYSEXITS_H
#include <sysexits.h>
-#elif __MVS__ || defined(_WIN32)
-// <sysexits.h> does not exist on z/OS and Windows. The only value used in LLVM
-// is EX_IOERR, which is used to signal a special error condition (broken pipe).
-// Define the macro with its usual value from BSD systems, which is chosen to
-// not clash with more standard exit codes like 1.
+#elif __MVS__ || defined(_WIN32) || defined(__QNX__)
+// <sysexits.h> does not exist on z/OS, Windows, and QNX. The only value used in
+// LLVM is EX_IOERR, which is used to signal a special error condition (broken
+// pipe). Define the macro with its usual value from BSD systems, which is
+// chosen to not clash with more standard exit codes like 1.
#define EX_IOERR 74
#elif LLVM_ON_UNIX
#error Exit code EX_IOERR not available
diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index 6e679f74869f0..9cc889ea79c80 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -79,6 +79,9 @@ extern char **environ;
#define STATVFS statvfs
#define FSTATVFS fstatvfs
#define STATVFS_F_FRSIZE(vfs) vfs.f_frsize
+#if defined(__QNX__)
+#include <sys/mount.h>
+#endif
#else
#if defined(__OpenBSD__) || defined(__FreeBSD__)
#include <sys/mount.h>
@@ -112,7 +115,7 @@ typedef uint_t uint;
#endif
#if defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__) || \
- defined(__MVS__)
+ defined(__MVS__) || defined(__QNX__)
#define STATVFS_F_FLAG(vfs) (vfs).f_flag
#else
#define STATVFS_F_FLAG(vfs) (vfs).f_flags
@@ -555,6 +558,13 @@ static bool is_local_impl(struct STATVFS &Vfs) {
// The file system can have an arbitrary structure on z/OS; must go with the
// conservative answer.
return false;
+#elif defined(__QNX__)
+ // statvfs::f_basetype contains the null-terminated name of the target file
+ // system.
+ StringRef fstype(Vfs.f_basetype);
+ // TODO: Confirm if NFS_FS_TYPE and CIFS_FS_TYPE are the only remote file
+ // systems.
+ return !fstype.equals(NFS_FS_TYPE) && !fstype.equals(CIFS_FS_TYPE);
#else
return !!(STATVFS_F_FLAG(Vfs) & MNT_LOCAL);
#endif
diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index 298fde1a387cc..4a6c0e34cae46 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -312,11 +312,17 @@ static void RegisterHandlers() { // Not signal-safe.
switch (Kind) {
case SignalKind::IsKill:
NewHandler.sa_handler = SignalHandler;
+#if defined(HAVE_SIGALTSTACK)
NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK;
+#else
+ NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND;
+#endif
break;
case SignalKind::IsInfo:
NewHandler.sa_handler = InfoSignalHandler;
+#if defined(HAVE_SIGALTSTACK)
NewHandler.sa_flags = SA_ONSTACK;
+#endif
break;
}
sigemptyset(&NewHandler.sa_mask);
More information about the llvm-commits
mailing list