Files
haikuports/dev-util/gdb/patches/gdb-16.3.patchset
Jérôme Duval f68061bfc8 gdb: bump to 16.3
2026-03-08 23:23:04 +01:00

7476 lines
225 KiB
Plaintext

From e524cecc46de3ec07141e8ffa5e23739a6714a95 Mon Sep 17 00:00:00 2001
From: Trung Nguyen <me@trungnt2910.com>
Date: Mon, 29 Jul 2024 22:55:42 +1000
Subject: gdb/remote: fix assertion failure during startup
Ensure that calls to `remote_state::mark_async_event_handler` are only
made when the target is in async mode:
- Replace the check for `target_can_async_p` in
`remote_target::queued_stop_reply` with `target_is_async_p`.
- Add a check in `remote_notif_stop_can_get_pending_events`.
During early startup, `process_initial_stop_replies` was called before
asynchronous mode was enabled. The function then called
`remote_target::queued_stop_reply`, which then tried to mark the event
handler on all targets claiming async support, regardless of whether
async mode had been enabled or not. This triggered an assertion fail if
there had been queued stop replies.
The bug can be reproduced when running in non-stop mode while connected
to a custom build of `gdbserver`. This build always returns an extra
library stop notification due to the system libraries loading extra
add-ons right after program startup.
gdb/gdb -ex "set non-stop on" -ex "set debug remote 1" \
-ex "target remote $VM_HOST:6666"
Remote debugging using 192.168.154.176:6666
[remote] start_remote_1: enter
[remote] Sending packet: $qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;QThreadOptions+;no-resumed+;memory-tagging+;xmlRegisters=i386#72
[remote] Received Ack
[remote] Packet received: PacketSize=2001f;QPassSignals+;QProgramSignals+;QStartupWithShell+;QEnvironmentHexEncoded+;QEnvironmentReset+;QEnvironmentUnset+;QSetWorkingDir+;QCatchSyscalls+;qXfer:libraries:read+;qXfer:features:read+;QStartNoAckMode+;qXfer:osdata:read+;multiprocess+;fork-events+;exec-events+;QNonStop+;QDisableRandomization+;qXfer:threads:read+;BreakpointCommands+;qXfer:exec-file:read+;vContSupported+;QThreadEvents+;no-resumed+
[remote] packet_ok: Packet qSupported (supported-packets) is supported
[remote] Sending packet: $vCont?#49
[remote] Received Ack
[remote] Packet received: vCont;c;C;t
[remote] packet_ok: Packet vCont (verbose-resume) is supported
[remote] Sending packet: $vMustReplyEmpty#3a
[remote] Received Ack
[remote] Packet received:
[remote] Sending packet: $QStartNoAckMode#b0
[remote] Received Ack
[remote] Packet received: OK
[remote] Sending packet: $QNonStop:1#8d
[remote] Packet received: OK
[remote] Sending packet: $qXfer:threads:read::0,1000#92
[remote] Notification received: Stop:T0006:20310801007f0000;07:a8300801007f0000;10:a13c110000000000;thread:p85a.0;library:;
[remote] Packet received: l<threads>\n<thread id="p85a.0" name="cat"/>\n</threads>\n
[remote] Sending packet: $vStopped#55
[remote] Packet received: OK
[remote] Sending packet: $qAttached:85a#97
[remote] Packet received: 0
[remote] packet_ok: Packet qAttached (query-attached) is supported
[remote] Sending packet: $qXfer:exec-file:read:85a:0,1000#e7
[remote] Packet received: l/boot/system/bin/cat
[remote] Sending packet: $vFile:setfs:0#bf
[remote] Packet received:
[remote] packet_ok: Packet vFile:setfs (hostio-setfs) is NOT supported
[remote] Sending packet: $vFile:open:6a7573742070726f62696e67,0,1c0#ed
[remote] Packet received: F-1,2
[remote] packet_ok: Packet vFile:open (hostio-open) is supported
Reading /boot/system/bin/cat from remote target...
warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead.
[remote] Sending packet: $QPassSignals:#f3
[remote] Packet received: OK
[remote] wait: enter
../../gdb-haiku/gdb/remote.c:504: internal-error: mark_async_event_handler: Assertion `this->is_async_p ()' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
----- Backtrace -----
0x7f6328d6b0b5 gdb_internal_backtrace_1
../../gdb-haiku/gdb/bt-utils.c:121
0x7f6328d6b0b5 _Z22gdb_internal_backtracev
../../gdb-haiku/gdb/bt-utils.c:167
0x7f6329145214 internal_vproblem
../../gdb-haiku/gdb/utils.c:420
0x7f6329145540 _Z15internal_verrorPKciS0_P13__va_list_tag
../../gdb-haiku/gdb/utils.c:500
0x7f632929ecc4 _Z18internal_error_locPKciS0_z
../../gdb-haiku/gdbsupport/errors.cc:57
0x7f632902f48d _ZN12remote_state24mark_async_event_handlerEv
../../gdb-haiku/gdb/remote.c:504
0x7f63290375d4 _ZN12remote_state24mark_async_event_handlerEv
../../gdb-haiku/gdb/remote.c:7912
0x7f63290375d4 _ZN13remote_target17queued_stop_replyE6ptid_t
../../gdb-haiku/gdb/remote.c:7908
0x7f6329042b21 _ZN13remote_target7wait_nsE6ptid_tP17target_waitstatus10enum_flagsI16target_wait_flagE
../../gdb-haiku/gdb/remote.c:8602
0x7f6329043466 _ZN13remote_target4waitE6ptid_tP17target_waitstatus10enum_flagsI16target_wait_flagE
../../gdb-haiku/gdb/remote.c:8804
0x7f63290f1c45 _Z11target_wait6ptid_tP17target_waitstatus10enum_flagsI16target_wait_flagE
../../gdb-haiku/gdb/target.c:2579
0x7f63290460bb _ZN13remote_target28process_initial_stop_repliesEi
../../gdb-haiku/gdb/remote.c:4906
0x7f632904c847 _ZN13remote_target14start_remote_1Eii
../../gdb-haiku/gdb/remote.c:5365
0x7f632904ce3c _ZN13remote_target12start_remoteEii
../../gdb-haiku/gdb/remote.c:5417
0x7f632904ce3c _ZN13remote_target6open_1EPKcii
../../gdb-haiku/gdb/remote.c:6279
0x7f63290f2ba8 open_target
../../gdb-haiku/gdb/target.c:837
0x7f6328d9f9b4 _Z8cmd_funcP16cmd_list_elementPKci
../../gdb-haiku/gdb/cli/cli-decode.c:2741
0x7f6329102e64 _Z15execute_commandPKci
diff --git a/gdb/remote.c b/gdb/remote.c
index e00ddc0..d2f1c6e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -7738,8 +7738,11 @@ remote_notif_stop_can_get_pending_events (remote_target *remote,
instead. If we fetch all queued events from stub, remote stub
may exit and we have no chance to process them back in
remote_wait_ns. */
- remote_state *rs = remote->get_remote_state ();
- rs->mark_async_event_handler ();
+ if (target_is_async_p ())
+ {
+ remote_state *rs = remote->get_remote_state ();
+ rs->mark_async_event_handler ();
+ }
return 0;
}
@@ -7936,7 +7939,7 @@ remote_target::queued_stop_reply (ptid_t ptid)
remote_state *rs = get_remote_state ();
stop_reply_up r = remote_notif_remove_queued_reply (ptid);
- if (!rs->stop_reply_queue.empty () && target_can_async_p ())
+ if (!rs->stop_reply_queue.empty () && target_is_async_p ())
{
/* There's still at least an event left. */
rs->mark_async_event_handler ();
--
2.51.0
From 1d046da9f6aeee3b1137dcc5100dbac1049b8c91 Mon Sep 17 00:00:00 2001
From: Trung Nguyen <57174311+trungnt2910@users.noreply.github.com>
Date: Tue, 28 May 2024 22:31:22 +1000
Subject: gdbserver: Initial Haiku support
diff --git a/gdb/nat/haiku-debug.c b/gdb/nat/haiku-debug.c
new file mode 100644
index 0000000..d1796a4
--- /dev/null
+++ b/gdb/nat/haiku-debug.c
@@ -0,0 +1,43 @@
+/* Haiku re-exports for debugging functions with conflicting names.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "gdbsupport/common-defs.h"
+
+extern decltype (debug_printf) haiku_debug_printf;
+extern decltype (debug_vprintf) haiku_debug_vprintf;
+
+/* Re-export of debug_printf. */
+
+void
+haiku_debug_printf (const char *format, ...)
+{
+ va_list ap;
+
+ va_start (ap, format);
+ debug_vprintf (format, ap);
+ va_end (ap);
+}
+
+/* Re-export of debug_vprintf. */
+
+void
+haiku_debug_vprintf (const char *format, va_list ap)
+{
+ debug_vprintf (format, ap);
+}
diff --git a/gdb/nat/haiku-nat.c b/gdb/nat/haiku-nat.c
new file mode 100644
index 0000000..30f0092
--- /dev/null
+++ b/gdb/nat/haiku-nat.c
@@ -0,0 +1,2808 @@
+/* Internal interfaces for the Haiku code.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Wrap this to prevent name clashes with a similarly named function in
+ Haiku's system headers. */
+#define debug_printf haiku_debug_printf
+#define debug_vprintf haiku_debug_vprintf
+
+#include "gdbsupport/common-defs.h"
+#include "gdbsupport/event-pipe.h"
+#include "gdbsupport/gdb_signals.h"
+
+#include "diagnostics.h"
+#include "target/waitstatus.h"
+
+#undef debug_printf
+#undef debug_vprintf
+/* Now we can safely include Haiku headers. */
+
+#include "nat/haiku-nat.h"
+#include "nat/haiku-nub-message.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <set>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <debugger.h>
+#include <elf.h>
+
+#define RETURN_IF_FAIL(exp) \
+ do \
+ { \
+ status_t status = (exp); \
+ if (status < B_OK) \
+ return status; \
+ } \
+ while (0)
+
+#define RETURN_VALUE_AND_SET_ERRNO_IF_FAIL(exp, val) \
+ do \
+ { \
+ status_t status = (exp); \
+ if (status < B_OK) \
+ { \
+ errno = status; \
+ return (val); \
+ } \
+ } \
+ while (0)
+
+#define RETURN_AND_SET_ERRNO_IF_FAIL(exp) \
+ RETURN_VALUE_AND_SET_ERRNO_IF_FAIL (exp, -1)
+
+/* ELF definitions. */
+
+#if B_HAIKU_32_BIT
+typedef Elf32_Sym elf_sym;
+#define ELF_ST_TYPE ELF32_ST_TYPE
+#elif B_HAIKU_64_BIT
+typedef Elf64_Sym elf_sym;
+#define ELF_ST_TYPE ELF64_ST_TYPE
+#endif
+
+/* Private structures. */
+
+/* Derived from headers/private/system/vfs_defs.h. */
+struct fd_info
+{
+ int number;
+ int32 open_mode;
+ dev_t device;
+ ino_t node;
+};
+
+/* Derived from headers/private/net/net_stat.h. */
+struct net_stat
+{
+ int family;
+ int type;
+ int protocol;
+ char state[B_OS_NAME_LENGTH];
+ team_id owner;
+ struct sockaddr_storage address;
+ struct sockaddr_storage peer;
+ size_t receive_queue_size;
+ size_t send_queue_size;
+};
+
+/* Private syscalls from headers/private/system/syscalls.h.
+ Import them as weak symbols only since their names may change anytime. */
+
+extern "C" status_t _kern_entry_ref_to_path (dev_t device, ino_t inode,
+ const char *leaf, char *userPath,
+ size_t pathLength)
+ __attribute__ ((weak));
+
+extern "C" status_t _kern_get_next_fd_info (team_id team, uint32 *_cookie,
+ fd_info *info, size_t infoSize)
+ __attribute__ ((weak));
+
+extern "C" status_t _kern_get_next_socket_stat (int family, uint32 *cookie,
+ struct net_stat *stat)
+ __attribute__ ((weak));
+
+extern "C" status_t _kern_read_kernel_image_symbols (
+ image_id id, elf_sym *symbolTable, int32 *_symbolCount, char *stringTable,
+ size_t *_stringTableSize, addr_t *_imageDelta) __attribute__ ((weak));
+
+namespace haiku_nat
+{
+
+class team_debug_context;
+
+/* Expose this instead of forward declaring
+ the whole team_debug_context. */
+template <debug_nub_message message>
+[[nodiscard]]
+std::enable_if_t<std::is_same_v<haiku_nub_message_reply<message>, void>,
+ status_t> team_send (const team_debug_context *context,
+ haiku_nub_message_data<message> &&data);
+
+template <debug_nub_message message>
+[[nodiscard]]
+std::enable_if_t<!std::is_same_v<haiku_nub_message_reply<message>, void>,
+ status_t> team_send (const team_debug_context *context,
+ haiku_nub_message_data<message> &&data,
+ haiku_nub_message_reply<message> &reply);
+
+/* Utility function, defined below. */
+static void convert_image_info (const ::image_info &haiku_info,
+ image_info &info);
+
+class thread_debug_context
+{
+private:
+ enum signal_status
+ {
+ /* This signal is recorded in an actual signal event
+ and will arrive to the thread when resumed. */
+ SIGNAL_ACTUAL,
+ /* This signal is forcasted to be sent if the current event is not ignored.
+ (e.g. an exception has occurred).
+ It is not from an actual signal event. */
+ SIGNAL_FORECASTED,
+ /* This signal (often SIGTRAP) is faked in the interface we provide to GDB.
+ It will not and should not be sent for this event. */
+ SIGNAL_FAKED
+ };
+
+ team_debug_context *m_team = nullptr;
+ thread_id m_thread = -1;
+ bool m_stopped = false;
+ bool m_deleted = false;
+ /* Created instead of attached or otherwise existing. */
+ bool m_created = false;
+ /* True if main executable has been unloaded. */
+ bool m_unloaded = false;
+ /* True if we have forced this thread to stop through target_stop. */
+ bool m_force_stopped = false;
+ /* If non-zero, the signal that Haiku would send this thread when resumed. */
+ int m_signal = 0;
+ signal_status m_signal_status;
+ struct
+ {
+ debug_cpu_state data;
+ bool valid = false;
+ bool dirty = false;
+ } m_cpu_state;
+ std::queue<std::shared_ptr<target_waitstatus> > m_events;
+
+public:
+ thread_debug_context () = default;
+ thread_debug_context (const thread_debug_context &other) = delete;
+ thread_debug_context (thread_debug_context &&other)
+ : m_team (other.m_team), m_thread (other.m_thread),
+ m_stopped (other.m_stopped), m_deleted (other.m_deleted),
+ m_created (other.m_created), m_unloaded (other.m_unloaded),
+ m_force_stopped (other.m_force_stopped), m_signal (other.m_signal),
+ m_signal_status (other.m_signal_status),
+ m_cpu_state (other.m_cpu_state), m_events (std::move (other.m_events))
+ {
+ other.m_team = nullptr;
+ other.m_thread = -1;
+ other.m_stopped = false;
+ other.m_deleted = false;
+ other.m_created = false;
+ other.m_unloaded = false;
+ other.m_force_stopped = false;
+ other.m_signal = 0;
+ other.m_cpu_state.valid = false;
+ }
+
+ [[nodiscard]]
+ status_t
+ initialize (thread_id thread, team_debug_context *team, bool created)
+ {
+ if (m_thread >= 0)
+ return B_NOT_ALLOWED;
+ m_thread = thread;
+ m_team = team;
+ m_created = created;
+ return B_OK;
+ }
+
+ bool
+ has_events () const
+ {
+ return m_thread >= 0 && !m_events.empty ();
+ }
+ bool
+ can_resume () const
+ {
+ return m_stopped && (has_events () || !m_deleted);
+ }
+ bool
+ is_stopped () const
+ {
+ return m_stopped;
+ }
+ bool
+ is_deleted () const
+ {
+ return m_deleted;
+ }
+
+ thread_id
+ thread () const
+ {
+ return m_thread;
+ }
+
+ [[nodiscard]]
+ status_t
+ enqueue (debug_debugger_message message,
+ const debug_debugger_message_data &data,
+ const std::function<
+ status_t (const std::shared_ptr<target_waitstatus> &)> callback)
+ {
+ if (m_thread < 0)
+ return B_NOT_INITIALIZED;
+
+ std::shared_ptr<target_waitstatus> gdbstatus;
+
+ const auto make = [&] () -> target_waitstatus & {
+ gdbstatus = std::make_shared<target_waitstatus> ();
+ return *gdbstatus;
+ };
+
+ const auto add = [&] () {
+ RETURN_IF_FAIL (callback (gdbstatus));
+ m_events.emplace (std::move (gdbstatus));
+ return B_OK;
+ };
+
+ const auto store_cpu = [&] (const debug_cpu_state &state) {
+ m_cpu_state.data = state;
+ m_cpu_state.valid = true;
+ m_cpu_state.dirty = false;
+ };
+
+ /* For all known Haiku debugger events,
+ the related thread should be stopped. */
+ m_stopped = true;
+
+ /* Default signal status. */
+ m_signal = SIGTRAP;
+ m_signal_status = SIGNAL_FAKED;
+
+ switch (message)
+ {
+ case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
+ haiku_nat_debug_printf ("THREAD_DEBUGGED: team=%i, thread=%i",
+ data.origin.team, data.origin.thread);
+
+ if (m_created)
+ {
+ make ().set_thread_created ();
+ RETURN_IF_FAIL (add ());
+
+ m_created = false;
+ }
+ else if (m_force_stopped)
+ {
+ /* A thread that has been requested to stop by GDB with
+ target_stop, and it stopped cleanly, so report as SIG0. */
+ make ().set_stopped (GDB_SIGNAL_0);
+ RETURN_IF_FAIL (add ());
+
+ m_force_stopped = false;
+ }
+ else
+ {
+ make ().set_stopped (GDB_SIGNAL_TRAP);
+ RETURN_IF_FAIL (add ());
+ }
+ break;
+ case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
+ haiku_nat_debug_printf (
+ "DEBUGGER_CALL: team=%i, thread=%i, message=%p", data.origin.team,
+ data.origin.thread, data.debugger_call.message);
+ make ().set_stopped (GDB_SIGNAL_TRAP);
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
+ haiku_nat_debug_printf ("BREAKPOINT_HIT: team=%i, thread=%i",
+ data.origin.team, data.origin.thread);
+
+ store_cpu (data.breakpoint_hit.cpu_state);
+
+ make ().set_stopped (GDB_SIGNAL_TRAP);
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
+ haiku_nat_debug_printf ("WATCHPOINT_HIT: team=%i, thread=%i",
+ data.origin.team, data.origin.thread);
+
+ store_cpu (data.watchpoint_hit.cpu_state);
+
+ make ().set_stopped (GDB_SIGNAL_TRAP);
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_SINGLE_STEP:
+ haiku_nat_debug_printf ("SINGLE_STEP: team=%i, thread=%i",
+ data.origin.team, data.origin.thread);
+
+ store_cpu (data.single_step.cpu_state);
+
+ make ().set_stopped (GDB_SIGNAL_TRAP);
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
+ haiku_nat_debug_printf ("PRE_SYSCALL: team=%i, thread=%i, syscall=%i",
+ data.origin.team, data.origin.thread,
+ data.pre_syscall.syscall);
+ make ().set_syscall_entry (data.pre_syscall.syscall);
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_POST_SYSCALL:
+ haiku_nat_debug_printf ("POST_SYSCALL: team=%i, thread=%i, syscall=%i",
+ data.origin.team, data.origin.thread,
+ data.post_syscall.syscall);
+ make ().set_syscall_return (data.post_syscall.syscall);
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
+ haiku_nat_debug_printf (
+ "SIGNAL_RECEIVED: team=%i, thread=%i, signal=%i, deadly=%i",
+ data.origin.team, data.origin.thread, data.signal_received.signal,
+ data.signal_received.deadly);
+
+ m_signal = data.signal_received.signal;
+ m_signal_status = SIGNAL_ACTUAL;
+
+ /* Do NOT set the signalled event here, even when the signal is marked
+ "deadly" by Haiku. GDB may still interrupt these signals and do
+ something else, keeping the inferior alive. This is how debugger
+ pause and interrupt operations work. */
+ make ().set_stopped (
+ gdb_signal_from_host (data.signal_received.signal));
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
+ haiku_nat_debug_printf (
+ "EXCEPTION_OCCURRED: team=%i, thread=%i, exception=%i, signal=%i",
+ data.origin.team, data.origin.thread,
+ (int)data.exception_occurred.exception,
+ data.exception_occurred.signal);
+
+ m_signal = data.exception_occurred.signal;
+ m_signal_status = SIGNAL_FORECASTED;
+
+ make ().set_stopped (
+ gdb_signal_from_host (data.exception_occurred.signal));
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_TEAM_CREATED:
+ haiku_nat_debug_printf (
+ "TEAM_CREATED: team=%i, thread=%i, new_team=%i", data.origin.team,
+ data.origin.thread, data.team_created.new_team);
+
+ make ().set_forked (ptid_t (data.team_created.new_team));
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_TEAM_DELETED:
+ haiku_nat_debug_printf ("TEAM_DELETED: team=%i, status=%i",
+ data.origin.team, data.team_deleted.status);
+
+ /* Thread should also be gone with the team. */
+ m_deleted = true;
+
+ if (data.team_deleted.signal >= 0)
+ make ().set_signalled (
+ gdb_signal_from_host (data.team_deleted.signal));
+ else
+ make ().set_exited (data.team_deleted.status);
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_TEAM_EXEC:
+ haiku_nat_debug_printf (
+ "TEAM_EXEC: team=%i, thread=%i, image_event=%i", data.origin.team,
+ data.origin.thread, data.team_exec.image_event);
+
+ /* This event does not give us the full path of the executable,
+ which the corresponding GDB event requires.
+
+ Furthermore, after this event, the new process does not take
+ control yet. We would need to wait for runtime_loader to
+ complete its rituals and finally fire up a IMAGE_CREATED
+ event for the main app executable. */
+ m_unloaded = true;
+
+ make ().set_spurious ();
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_THREAD_CREATED:
+ haiku_nat_debug_printf (
+ "THREAD_CREATED: team=%i, thread=%i, new_thread=%i",
+ data.origin.team, data.origin.thread,
+ data.thread_created.new_thread);
+
+ /* Ignore this event. GDB expects THREAD_CREATED to be owned by
+ the new thread, not the old one. We report THREAD_CREATED on the
+ first event owned by the new thread, which is THREAD_DEBUGGED. */
+
+ make ().set_spurious ();
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_THREAD_DELETED:
+ haiku_nat_debug_printf (
+ "THREAD_DELETED: team=%i, thread=%i, status=%i", data.origin.team,
+ data.origin.thread, data.thread_deleted.status);
+
+ /* There might still be events for this thread, but we can no longer
+ resume or otherwise communicate with the thread. */
+ m_deleted = true;
+
+ make ().set_thread_exited (data.thread_deleted.status);
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
+ haiku_nat_debug_printf (
+ "IMAGE_CREATED: team=%i, thread=%i, image_event=%i, name=%s",
+ data.origin.team, data.origin.thread,
+ data.image_created.image_event, data.image_created.info.name);
+
+ if (m_unloaded)
+ {
+ /* The app is fully loaded. Emit an EXECD event. */
+ if (data.image_created.info.type == B_APP_IMAGE)
+ {
+ m_unloaded = false;
+
+ make ().set_execd (
+ make_unique_xstrdup (data.image_created.info.name));
+ RETURN_IF_FAIL (add ());
+
+ /* Cause GDB to refresh its library list. */
+ make ().set_loaded ();
+ RETURN_IF_FAIL (add ());
+ }
+ else
+ {
+ /* Continue ignoring until we have our executable. */
+ make ().set_spurious ();
+ RETURN_IF_FAIL (add ());
+ }
+ }
+ else
+ {
+ make ().set_loaded ();
+ RETURN_IF_FAIL (add ());
+ }
+
+ break;
+ case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
+ haiku_nat_debug_printf (
+ "IMAGE_DELETED: team=%i, thread=%i, image_event=%i, name=%s",
+ data.origin.team, data.origin.thread,
+ data.image_deleted.image_event, data.image_deleted.info.name);
+
+ if (m_unloaded)
+ {
+ make ().set_spurious ();
+ RETURN_IF_FAIL (add ());
+ }
+ else
+ {
+ /* Send TARGET_WAITKIND_LOADED here as well, as it causes the
+ shared libraries list to be updated. */
+ make ().set_loaded ();
+ RETURN_IF_FAIL (add ());
+ }
+ break;
+ case B_DEBUGGER_MESSAGE_PROFILER_UPDATE:
+ haiku_nat_debug_printf ("PROFILER_UPDATE: team=%i, thread=%i",
+ data.origin.team, data.origin.thread);
+
+ /* How did we even get here? */
+ make ().set_spurious ();
+ RETURN_IF_FAIL (add ());
+ break;
+ case B_DEBUGGER_MESSAGE_HANDED_OVER:
+ haiku_nat_debug_printf (
+ "HANDED_OVER: team=%i, thread=%i, causing_thread=%i",
+ data.origin.team, data.origin.thread,
+ data.handed_over.causing_thread);
+
+ /* How did we even get here? */
+ make ().set_spurious ();
+ RETURN_IF_FAIL (add ());
+ break;
+ default:
+ haiku_nat_debug_printf ("Unimplemented debugger message code: %i",
+ message);
+
+ make ().set_spurious ();
+ RETURN_IF_FAIL (add ());
+ break;
+ }
+
+ return B_OK;
+ }
+
+ [[nodiscard]]
+ status_t
+ dequeue (target_waitstatus *ourstatus)
+ {
+ if (m_thread < 0)
+ return B_NOT_INITIALIZED;
+ if (m_events.empty ())
+ return B_BUSY;
+
+ /* Not a real dequeue request, just peeking. */
+ if (ourstatus == nullptr)
+ return B_OK;
+
+ *ourstatus = std::move (*m_events.front ());
+ m_events.pop ();
+ return B_OK;
+ }
+
+ [[nodiscard]]
+ status_t
+ resume (resume_kind kind = resume_continue, int sig = 0)
+ {
+ if (m_thread < 0 || m_team == nullptr)
+ return B_NOT_INITIALIZED;
+ if (kind == resume_stop)
+ return B_BAD_VALUE;
+ if (!is_stopped ())
+ return B_BUSY;
+
+ /* Let GDB run the wait loop again. */
+ if (has_events ())
+ return B_OK;
+
+ if (is_deleted ())
+ return B_BAD_THREAD_ID;
+
+ uint32 handle_event = B_THREAD_DEBUG_HANDLE_EVENT;
+ bool step = kind == resume_step;
+ int signal_to_send = 0;
+ int signal_to_mute = 0;
+
+ if (sig != 0)
+ {
+ if (m_signal != 0)
+ {
+ if (sig == m_signal)
+ {
+ /* Signal GDB wants is the same as what Haiku would send. */
+ /* We do not need to do anything. */
+
+ /*
+ TODO: Is this neccessary? Work out what waddlesplash meant.
+ if (m_signal_status == SIGNAL_FORECASTED)
+ {
+ // the signal has not yet been sent, so we need to ignore
+ // it only in this case, but not in the other ones
+ signal_to_mute = m_signal;
+ }
+ */
+ }
+ else
+ {
+ /* Signal GDB wants is not the same as what Haiku intends. */
+ /* If the signal is not faked, we need to ignore the event. */
+ if (m_signal_status != SIGNAL_FAKED)
+ handle_event = B_THREAD_DEBUG_IGNORE_EVENT;
+
+ /* The event has already been ignored,
+ so we don't need to mute the signal. */
+ }
+ }
+ }
+ else
+ {
+ if (m_signal != 0)
+ {
+ /* Haiku intends to send a signal,
+ but GDB does not want to send anything. */
+ /* Ignore the event if that signal is not fake. */
+ if (m_signal_status != SIGNAL_FAKED)
+ handle_event = B_THREAD_DEBUG_IGNORE_EVENT;
+ }
+ else
+ {
+ /* Neither Haiku nor GDB wants to send a signal. */
+ }
+ }
+
+ /* Flush CPU state if dirty. */
+ if (m_cpu_state.valid && m_cpu_state.dirty)
+ {
+ RETURN_IF_FAIL (team_send<B_DEBUG_MESSAGE_SET_CPU_STATE> (
+ m_team, { .thread = m_thread, .cpu_state = m_cpu_state.data }));
+
+ m_cpu_state.dirty = false;
+ }
+
+ /* Mute Haiku's pending signal if necessary. */
+ if (signal_to_mute != 0)
+ {
+ RETURN_IF_FAIL (team_send<B_DEBUG_MESSAGE_SET_SIGNAL_MASKS> (
+ m_team,
+ { .thread = m_thread,
+ .ignore_mask = 0,
+ .ignore_once_mask = B_DEBUG_SIGNAL_TO_MASK (signal_to_mute),
+ .ignore_op = B_DEBUG_SIGNAL_MASK_OR,
+ .ignore_once_op = B_DEBUG_SIGNAL_MASK_OR }));
+ }
+
+ /* Send what GDB wants. */
+ if (signal_to_send != 0)
+ {
+ if (send_signal (m_thread, signal_to_send) < 0)
+ {
+ haiku_nat_debug_printf (
+ "Failed to send signal %i to thread %i: %s", signal_to_send,
+ m_thread, strerror (errno));
+ return errno;
+ }
+ }
+
+ /* Actually resume the thread. */
+ RETURN_IF_FAIL (team_send<B_DEBUG_MESSAGE_CONTINUE_THREAD> (
+ m_team, { .thread = m_thread,
+ .handle_event = handle_event,
+ .single_step = step }));
+
+ haiku_nat_debug_printf (
+ "Sent CONTINUE_THREAD message to thread %i (handle_event=%i, "
+ "single_step=%i)",
+ m_thread, handle_event, (int)step);
+
+ m_stopped = false;
+
+ m_cpu_state.valid = false;
+
+ return B_OK;
+ }
+
+ [[nodiscard]]
+ status_t
+ stop ()
+ {
+ if (is_stopped ())
+ return B_OK;
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (debug_thread (m_thread));
+ m_force_stopped = true;
+
+ return B_OK;
+ }
+
+ [[nodiscard]]
+ status_t
+ get_cpu_state (debug_cpu_state &state, bool direct = false)
+ {
+ if (m_thread < 0)
+ return B_NOT_INITIALIZED;
+
+ if (!is_stopped ())
+ return B_BUSY;
+
+ if (!direct && m_cpu_state.valid)
+ {
+ state = m_cpu_state.data;
+ return B_OK;
+ }
+
+ if (is_deleted ())
+ return B_BAD_THREAD_ID;
+
+ debug_nub_get_cpu_state_reply reply;
+
+ RETURN_IF_FAIL (team_send<B_DEBUG_MESSAGE_GET_CPU_STATE> (
+ m_team, { .thread = m_thread }, reply));
+
+ state = m_cpu_state.data = reply.cpu_state;
+ m_cpu_state.valid = true;
+ m_cpu_state.dirty = false;
+
+ return B_OK;
+ }
+
+ [[nodiscard]]
+ status_t
+ set_cpu_state (const debug_cpu_state &state, bool direct = false)
+ {
+ if (m_thread < 0)
+ return B_NOT_INITIALIZED;
+
+ if (!is_stopped ())
+ return B_BUSY;
+
+ m_cpu_state.data = state;
+ m_cpu_state.valid = true;
+ m_cpu_state.dirty = true;
+
+ if (!direct)
+ return B_OK;
+
+ if (is_deleted ())
+ return B_BAD_THREAD_ID;
+
+ RETURN_IF_FAIL (team_send<B_DEBUG_MESSAGE_SET_CPU_STATE> (
+ m_team, { .thread = m_thread, .cpu_state = m_cpu_state.data }));
+
+ m_cpu_state.dirty = false;
+
+ return B_OK;
+ }
+};
+
+class team_debug_context
+{
+private:
+ team_id m_team = -1;
+ port_id m_nub_port = -1;
+ port_id m_debugger_port = -1;
+ port_id m_reply_port = -1;
+ int32 m_debug_flags = 0;
+ ::image_info m_app_image = { .id = -1 };
+ std::map<thread_id, thread_debug_context> m_threads;
+ std::set<thread_id> m_created_threads;
+ std::queue<std::pair<thread_id, std::weak_ptr<target_waitstatus> > >
+ m_events;
+
+ /* Cleans all invalid events at the front of the queue.
+ Returns true if there are no valid events left. */
+ bool
+ clean_events ()
+ {
+ while (!m_events.empty () && m_events.front ().second.expired ())
+ m_events.pop ();
+ return m_events.empty ();
+ }
+
+ /* Deletes all ports. */
+ void
+ delete_debug_ports ()
+ {
+ if (m_nub_port >= 0)
+ delete_port (m_nub_port);
+ m_nub_port = -1;
+ if (m_reply_port >= 0)
+ delete_port (m_reply_port);
+ m_reply_port = -1;
+ if (m_debugger_port >= 0)
+ delete_port (m_debugger_port);
+ m_debugger_port = -1;
+ }
+
+ [[nodiscard]]
+ status_t
+ set_debug_flags (int32 flags)
+ {
+ if (is_detached ())
+ return B_NOT_ALLOWED;
+
+ if (flags == m_debug_flags)
+ return B_OK;
+
+ RETURN_IF_FAIL (send<B_DEBUG_MESSAGE_SET_TEAM_FLAGS> ({ .flags = flags }));
+
+ m_debug_flags = flags;
+
+ return B_OK;
+ }
+
+public:
+ team_debug_context () = default;
+ team_debug_context (const team_debug_context &other) = delete;
+ team_debug_context (team_debug_context &&other)
+ : m_team (other.m_team), m_nub_port (other.m_nub_port),
+ m_debugger_port (other.m_debugger_port),
+ m_reply_port (other.m_reply_port), m_debug_flags (other.m_debug_flags),
+ m_app_image (std::move (other.m_app_image)),
+ m_threads (std::move (other.m_threads)),
+ m_created_threads (std::move (other.m_created_threads)),
+ m_events (std::move (other.m_events))
+ {
+ other.m_team = -1;
+ other.m_nub_port = -1;
+ other.m_debugger_port = -1;
+ other.m_reply_port = -1;
+ other.m_debug_flags = 0;
+ other.m_app_image.id = -1;
+ }
+
+ [[nodiscard]]
+ status_t
+ initialize (team_id team, bool load_existing)
+ {
+ if (m_team >= 0)
+ return B_NOT_ALLOWED;
+
+ /* Create the debugger port. */
+ /* 10 is a value taken from waddlesplash's port. */
+ m_debugger_port = create_port (10, "gdb debug");
+ if (m_debugger_port < 0)
+ {
+ haiku_nat_debug_printf ("Failed to create debugger port: %s",
+ strerror (m_debugger_port));
+ return m_debugger_port;
+ }
+
+ m_reply_port = create_port (10, "gdb debug reply");
+ if (m_reply_port < 0)
+ {
+ haiku_nat_debug_printf ("Failed to create debugger reply port: %s",
+ strerror (m_reply_port));
+ return m_reply_port;
+ }
+
+ /* Install ourselves as the team debugger. */
+ m_nub_port = install_team_debugger (team, m_debugger_port);
+ if (m_nub_port < 0)
+ {
+ haiku_nat_debug_printf (
+ "Failed to install ourselves as debugger for team %i: %s", team,
+ strerror (errno));
+ return m_nub_port;
+ }
+
+ /* Set the team debug flags. */
+ RETURN_IF_FAIL (set_debug_flags (
+ B_TEAM_DEBUG_SIGNALS |
+ /* Only set the syscall debug flags when appropriate.
+ These events come very often and can flood the debugger
+ with large unneccessary messages. */
+ /* B_TEAM_DEBUG_PRE_SYSCALL | B_TEAM_DEBUG_POST_SYSCALL | */
+ B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_THREADS | B_TEAM_DEBUG_IMAGES
+ | B_TEAM_DEBUG_STOP_NEW_THREADS));
+
+ /* We have successfully initialized, now record the team. */
+ m_team = team;
+
+ if (load_existing)
+ {
+ /* Load existing images. */
+ for_each_image (team, [&] (const image_info &info) {
+ image_created (ptid_t (team, 0, team), info);
+ return 0;
+ });
+
+ /* Debug and stop existing threads. */
+ for_each_thread (team, [&] (const thread_info &info) {
+ if (debug_thread (info.tid) == B_OK)
+ {
+ auto [thread_it, thread_is_new]
+ = m_threads.try_emplace (info.tid);
+ gdb_assert (thread_is_new);
+ thread_debug_context &thread_context = thread_it->second;
+
+ status_t status
+ = thread_context.initialize (info.tid, this, false);
+ gdb_assert (status == B_OK);
+ }
+ return 0;
+ });
+ }
+
+ haiku_nat_debug_printf (
+ "Attached team debugger: team=%i, debugger_port=%i, "
+ "reply_port=%i, nub_port=%i",
+ m_team, m_debugger_port, m_reply_port, m_nub_port);
+
+ return B_OK;
+ }
+
+ /* Checks whether this team has any events queued. */
+ bool
+ has_events ()
+ {
+ if (m_team < 0)
+ return false;
+ return !clean_events ();
+ }
+
+ /* Checks whether this team is deleted or has been otherwise detached. */
+ bool
+ is_detached () const
+ {
+ return (m_team >= 0) && (m_debugger_port < 0);
+ }
+
+ /* Checks whether m_app_image is valid. */
+ bool
+ has_stored_app_image () const
+ {
+ return m_app_image.id != -1;
+ }
+
+ /* Getters. */
+
+ team_id
+ team () const
+ {
+ return m_team;
+ }
+
+ port_id
+ debugger_port () const
+ {
+ return m_debugger_port;
+ }
+
+ const ::image_info &
+ app_image () const
+ {
+ return m_app_image;
+ }
+
+ /* Message operations. */
+
+ template <debug_nub_message message>
+ [[nodiscard]]
+ std::enable_if_t<std::is_same_v<haiku_nub_message_reply<message>, void>,
+ status_t>
+ send (const haiku_nub_message_data<message> &data) const
+ {
+ return haiku_send_nub_message<message> (m_nub_port, data);
+ }
+
+ template <debug_nub_message message>
+ [[nodiscard]]
+ std::enable_if_t<!std::is_same_v<haiku_nub_message_reply<message>, void>,
+ haiku_nub_message_reply<message> >
+ send (haiku_nub_message_data<message> &&data) const
+ {
+ data.reply_port = m_reply_port;
+ return haiku_send_nub_message<message> (m_nub_port, data);
+ }
+
+ template <debug_nub_message message>
+ [[nodiscard]]
+ std::enable_if_t<!std::is_same_v<haiku_nub_message_reply<message>, void>,
+ status_t>
+ send (haiku_nub_message_data<message> &&data,
+ haiku_nub_message_reply<message> &reply) const
+ {
+ data.reply_port = m_reply_port;
+ return haiku_send_nub_message<message> (m_nub_port, data, reply);
+ }
+
+ [[nodiscard]]
+ ssize_t
+ read (debug_debugger_message &message, debug_debugger_message_data &data,
+ bool block = true) const
+ {
+ if (m_team < 0)
+ return B_NOT_INITIALIZED;
+
+ if (is_detached ())
+ return B_NOT_ALLOWED;
+
+ ssize_t bytes_read;
+ int32 code;
+
+ do
+ {
+ bytes_read
+ = read_port_etc (m_debugger_port, &code, &data, sizeof (data),
+ (block ? 0 : B_RELATIVE_TIMEOUT), 0);
+ }
+ while (bytes_read == B_INTERRUPTED);
+
+ message = (debug_debugger_message)code;
+
+ return bytes_read;
+ }
+
+ bool
+ thread_alive (ptid_t ptid) const
+ {
+ if (m_team < 0)
+ return false;
+
+ if (is_detached ())
+ return false;
+
+ if (ptid.tid_p ())
+ {
+ auto it = m_threads.find (ptid.tid ());
+ if (it == m_threads.end ())
+ return false;
+
+ return !it->second.is_deleted ();
+ }
+
+ for (const auto &[thread, thread_context] : m_threads)
+ {
+ if (!thread_context.is_deleted ())
+ return true;
+ }
+
+ return false;
+ }
+
+ [[nodiscard]]
+ status_t
+ get_cpu_state (ptid_t ptid, debug_cpu_state &state, bool direct = false)
+ {
+ if (m_team < 0)
+ return B_NOT_INITIALIZED;
+
+ auto it = m_threads.find (ptid.tid ());
+ if (it == m_threads.end ())
+ return B_BAD_THREAD_ID;
+
+ return it->second.get_cpu_state (state, direct);
+ }
+
+ [[nodiscard]]
+ status_t
+ set_cpu_state (ptid_t ptid, const debug_cpu_state &state,
+ bool direct = false)
+ {
+ if (m_team < 0)
+ return B_NOT_INITIALIZED;
+
+ auto it = m_threads.find (ptid.tid ());
+ if (it == m_threads.end ())
+ return B_BAD_THREAD_ID;
+
+ return it->second.set_cpu_state (state, direct);
+ }
+
+ /* Resumes each thread in the current team unless it stil has pending
+ events. */
+ [[nodiscard]]
+ status_t
+ resume ()
+ {
+ if (m_team < 0)
+ return B_NOT_INITIALIZED;
+
+ for (auto &[thread, thread_context] : m_threads)
+ RETURN_IF_FAIL (thread_context.resume ());
+
+ return B_OK;
+ }
+
+ /* GDB interface. */
+
+ /* Implement the resume target_ops method. */
+ [[nodiscard]]
+ status_t
+ resume (ptid_t ptid, resume_kind kind, int sig)
+ {
+ if (m_team < 0)
+ return B_NOT_INITIALIZED;
+
+ bool catching_syscalls = is_catching_syscalls_for (ptid_t (m_team));
+
+ int resume_flags = m_debug_flags;
+ if (catching_syscalls)
+ resume_flags |= B_TEAM_DEBUG_PRE_SYSCALL | B_TEAM_DEBUG_POST_SYSCALL;
+ else
+ resume_flags &= ~(B_TEAM_DEBUG_PRE_SYSCALL | B_TEAM_DEBUG_POST_SYSCALL);
+
+ RETURN_IF_FAIL (set_debug_flags (resume_flags));
+
+ bool any_thread = !ptid.tid_p ();
+
+ if (any_thread)
+ {
+ for (auto &[thread, thread_context] : m_threads)
+ if (thread_context.can_resume ())
+ RETURN_IF_FAIL (thread_context.resume (kind, sig));
+ }
+ else
+ {
+ auto it = m_threads.find (ptid.tid ());
+ if (it == m_threads.end ())
+ return B_BAD_THREAD_ID;
+ thread_debug_context &thread_context = it->second;
+ RETURN_IF_FAIL (thread_context.resume (kind, sig));
+ }
+
+ return B_OK;
+ }
+
+ /* Implement the wait target_ops method with a few differences:
+ - The requested ptid is passed by parameter and the resulting ptid is
+ passed back there on return.
+ - The function accepts a NULL ourstatus to peek and enqueue the next port
+ event without dequeuing it from the thread_debug_context. */
+ [[nodiscard]]
+ status_t
+ wait (ptid_t &ptid, target_waitstatus *ourstatus,
+ target_wait_flags target_options)
+ {
+ if (m_team < 0)
+ return B_NOT_INITIALIZED;
+
+ const auto thread_dequeue = [&] (thread_debug_context &thread_context) {
+ thread_id thread = thread_context.thread ();
+
+ RETURN_IF_FAIL (thread_context.dequeue (ourstatus));
+ ptid = ptid_t (m_team, 0, thread);
+
+ /* In many cases, the dequeued event is at the front of the global
+ queue. */
+ clean_events ();
+
+ if (thread_context.is_deleted () && !thread_context.has_events ())
+ {
+ haiku_nat_debug_printf (
+ "removing deleted thread context: team=%i, thread=%i", m_team,
+ thread_context.thread ());
+
+ m_threads.erase (thread);
+ }
+
+ return B_OK;
+ };
+
+ bool any_thread = !ptid.tid_p ();
+
+ /* Try to read queued events. */
+ if (any_thread)
+ {
+ if (!clean_events ())
+ {
+ auto [thread, weak_event] = std::move (m_events.front ());
+ m_events.pop ();
+
+ return thread_dequeue (m_threads.at (thread));
+ }
+ }
+ else
+ {
+ thread_id thread = ptid.tid ();
+ auto it = m_threads.find (thread);
+ if (it == m_threads.end ())
+ return B_BAD_THREAD_ID;
+ thread_debug_context &thread_context = it->second;
+ if (thread_context.has_events ())
+ return thread_dequeue (thread_context);
+ }
+
+ debug_debugger_message message;
+ debug_debugger_message_data data;
+
+ bool block = !(target_options & TARGET_WNOHANG).raw ();
+
+ /* There are no suitable queued events, read some more. */
+ while (true)
+ {
+ ssize_t bytes_read = read (message, data, block);
+
+ if (!block && (bytes_read == B_WOULD_BLOCK))
+ {
+ ptid = null_ptid;
+ return B_OK;
+ }
+ else if (bytes_read < B_OK)
+ return bytes_read;
+
+ gdb_assert (data.origin.team == m_team);
+
+ haiku_nat_debug_printf ("Received debug message type %i.", message);
+
+ /* Internal bookkeeping. */
+ switch (message)
+ {
+ case B_DEBUGGER_MESSAGE_TEAM_DELETED:
+ /* Detach to prevent further read loops. */
+ detach (true);
+
+ /* Set any thread value so that the event gets handled immediately
+ by the code below. */
+ data.origin.thread = any_thread ? m_team : ptid.tid ();
+ break;
+ case B_DEBUGGER_MESSAGE_THREAD_CREATED:
+ m_created_threads.insert (data.thread_created.new_thread);
+ break;
+ case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
+ {
+ image_info info;
+ convert_image_info (data.image_created.info, info);
+ info.team = data.origin.team;
+
+ image_created (ptid_t (data.origin.team, 0, data.origin.thread),
+ info);
+
+ if (data.image_created.info.type == B_APP_IMAGE)
+ m_app_image = data.image_created.info;
+ }
+ break;
+ case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
+ {
+ if (data.image_deleted.info.type == B_APP_IMAGE)
+ m_app_image.id = -1;
+
+ image_info info;
+ convert_image_info (data.image_deleted.info, info);
+ info.team = data.origin.team;
+
+ image_deleted (ptid_t (data.origin.team, 0, data.origin.thread),
+ info);
+ }
+ break;
+ case B_DEBUGGER_MESSAGE_TEAM_EXEC:
+ m_app_image.id = -1;
+
+ /* Destroy the whole existing address space. */
+ image_deleted (ptid_t (data.origin.team, 0, data.origin.thread),
+ { .name = nullptr });
+ break;
+ case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
+ {
+ CORE_ADDR message_addr = (CORE_ADDR)data.debugger_call.message;
+ std::string message_string;
+
+ debug_nub_read_memory_reply read_memory_reply;
+ status_t read_memory_status = B_OK;
+ size_t chars_read = 0;
+
+ while (true)
+ {
+ read_memory_status = send<B_DEBUG_MESSAGE_READ_MEMORY> (
+ { .address = (void *)message_addr,
+ B_MAX_READ_WRITE_MEMORY_SIZE },
+ read_memory_reply);
+
+ /* Message invalid, or nothing more to read. */
+ if (read_memory_reply.size == 0)
+ break;
+
+ chars_read = strnlen (read_memory_reply.data,
+ read_memory_reply.size);
+
+ message_string.insert (message_string.end (),
+ read_memory_reply.data,
+ read_memory_reply.data + chars_read);
+
+ /* Nothing else to read. */
+ if (chars_read < B_MAX_READ_WRITE_MEMORY_SIZE)
+ break;
+ }
+
+ if (read_memory_status < B_OK && message_string.empty ())
+ message_string
+ = string_printf ("Thread %i called debugger(), but failed "
+ "to get the debugger message.",
+ (int)data.origin.thread);
+ else
+ message_string = string_printf (
+ "Thread %i called debugger(): %s", (int)data.origin.thread,
+ message_string.c_str ());
+
+ debugger_output (message_string.c_str ());
+ }
+ break;
+ case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
+ {
+ /* Best thing we can do, since Haiku does not provide any way to
+ get the size! */
+ char buffer[1024];
+ get_debug_exception_string (data.exception_occurred.exception,
+ buffer, sizeof (buffer));
+
+ std::string message_string
+ = string_printf ("Thread %i caused an exception: %s",
+ (int)data.origin.thread, buffer);
+
+ debugger_output (message_string.c_str ());
+ }
+ break;
+ case B_DEBUGGER_MESSAGE_HANDED_OVER:
+ /* This event is sent in only two cases:
+ - We called B_DEBUG_MESSAGE_PREPARE_HANDOVER, and another team
+ called install_team_debugger(). This should be impossible for
+ GDB.
+ - We started debugging a team that has previously been attached
+ by someone else, usually the debug server.
+
+ Since the event is asynchronous (data.origin.thread = -1, no
+ threads are stopped), we should silently ignore it, without
+ even queuing a message to any thread_context.
+ */
+ continue;
+ }
+
+ thread_id thread = data.origin.thread;
+ gdb_assert (thread >= 0);
+
+ auto [thread_it, thread_is_new] = m_threads.try_emplace (thread);
+ thread_debug_context &thread_context = thread_it->second;
+
+ if (thread_is_new)
+ {
+ RETURN_IF_FAIL (thread_context.initialize (
+ thread, this,
+ /* created = */ m_created_threads.count (thread) > 0));
+ m_created_threads.erase (thread);
+ }
+
+ RETURN_IF_FAIL (thread_context.enqueue (
+ message, data,
+ [&] (const std::shared_ptr<target_waitstatus> &event) {
+ m_events.emplace (thread, std::weak_ptr (event));
+ return B_OK;
+ }));
+
+ /* At this point we have at least one event to dequeue. */
+ if (any_thread || thread == ptid.tid ())
+ return thread_dequeue (thread_context);
+ }
+
+ /* How did we get here? */
+ return B_BAD_VALUE;
+ }
+
+ /* Implement the stop target_ops method. */
+ status_t
+ stop (ptid_t ptid)
+ {
+ /* Stops specific thread if tid is non-zero.
+ Otherwise stops whole process. */
+ bool all_threads = !ptid.tid_p ();
+
+ if (all_threads)
+ {
+ for (auto &[thread, thread_context] : m_threads)
+ RETURN_IF_FAIL (thread_context.stop ());
+ }
+ else
+ {
+ auto it = m_threads.find (ptid.tid ());
+ if (it == m_threads.end ())
+ return B_BAD_THREAD_ID;
+ thread_debug_context &thread_context = it->second;
+ RETURN_IF_FAIL (thread_context.stop ());
+ }
+
+ return B_OK;
+ }
+
+ /* Implement the detach target_ops method. */
+ status_t
+ detach (bool force = false)
+ {
+ if (m_team < 0)
+ return B_NO_INIT;
+ if (is_detached ())
+ return B_BAD_VALUE;
+
+ status_t status = remove_team_debugger (m_team);
+
+ haiku_nat_debug_printf ("Removed team debugger for team %i, status=%s",
+ m_team, strerror (status));
+
+ if (status < B_OK && !force)
+ return status;
+
+ delete_debug_ports ();
+
+ return B_OK;
+ }
+
+ /* Cleanup. */
+ ~team_debug_context ()
+ {
+ if (m_team >= 0 && !is_detached ())
+ {
+ haiku_nat_debug_printf ("Team %i has not been detached but forgotten.",
+ m_team);
+ detach (true);
+ }
+ }
+};
+
+template <debug_nub_message message>
+[[nodiscard]]
+std::enable_if_t<std::is_same_v<haiku_nub_message_reply<message>, void>,
+ status_t>
+team_send (const team_debug_context *context,
+ haiku_nub_message_data<message> &&data)
+{
+ return context->send<message> (
+ std::forward<haiku_nub_message_data<message> > (data));
+}
+
+template <debug_nub_message message>
+[[nodiscard]]
+std::enable_if_t<!std::is_same_v<haiku_nub_message_reply<message>, void>,
+ status_t>
+team_send (const team_debug_context *context,
+ haiku_nub_message_data<message> &&data,
+ haiku_nub_message_reply<message> &reply)
+{
+ return context->send<message> (
+ std::forward<haiku_nub_message_data<message> > (data), reply);
+}
+
+static std::map<team_id, std::shared_ptr<team_debug_context> >
+ team_debug_contexts;
+
+static std::mutex team_debug_ports_lock;
+static std::set<port_id> team_debug_ports;
+
+static event_pipe pipe_to_event_loop;
+static event_pipe pipe_to_worker;
+static thread_id async_worker_thread = -1;
+
+[[nodiscard]]
+static status_t
+get_context (team_id team, std::shared_ptr<team_debug_context> &context)
+{
+ auto it = team_debug_contexts.find (team);
+ if (it == team_debug_contexts.end ())
+ return B_BAD_TEAM_ID;
+ context = it->second;
+ return B_OK;
+}
+
+static status_t
+delete_context (const std::shared_ptr<team_debug_context> &context)
+{
+ if (team_debug_contexts.erase (context->team ()) == 0)
+ return B_BAD_TEAM_ID;
+ return B_OK;
+}
+
+/* See haiku-nat.h. */
+
+int
+attach (pid_t pid, bool is_ours)
+{
+ haiku_nat_debug_printf ("pid=%i", pid);
+
+ std::shared_ptr<team_debug_context> context
+ = std::make_shared<team_debug_context> ();
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (context->initialize (pid, !is_ours));
+
+ team_debug_ports_lock.lock ();
+ team_debug_ports.insert (context->debugger_port ());
+ team_debug_ports_lock.unlock ();
+
+ /* Record the debug entry and also release the pointer. */
+ team_debug_contexts.try_emplace (pid, std::move (context));
+
+ if (is_async_p ())
+ pipe_to_worker.mark ();
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+void
+wait_for_debugger ()
+{
+ HAIKU_NAT_SCOPED_DEBUG_ENTER_EXIT;
+
+ ::wait_for_debugger ();
+}
+
+/* See haiku-nat.h. */
+
+int
+get_cpu_state (ptid_t ptid, void *buffer)
+{
+ haiku_nat_debug_printf ("ptid=%s, buffer=%p", ptid.to_string ().c_str (),
+ buffer);
+
+ std::shared_ptr<team_debug_context> context;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (ptid.pid (), context));
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (
+ context->get_cpu_state (ptid, *(debug_cpu_state *)buffer));
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+set_cpu_state (ptid_t ptid, const void *buffer)
+{
+ haiku_nat_debug_printf ("ptid=%s, buffer=%p", ptid.to_string ().c_str (),
+ buffer);
+
+ std::shared_ptr<team_debug_context> context;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (ptid.pid (), context));
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (
+ context->set_cpu_state (ptid, *(const debug_cpu_state *)buffer));
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+resume (ptid_t ptid, resume_kind kind, int sig)
+{
+ haiku_nat_debug_printf ("ptid=%s, resume_kind=%i, sig=%i",
+ ptid.to_string ().c_str (), (int)kind, sig);
+
+ if (is_async_p ())
+ pipe_to_worker.mark ();
+
+ if (ptid == minus_one_ptid)
+ {
+ for (auto &[pid, context] : team_debug_contexts)
+ {
+ RETURN_AND_SET_ERRNO_IF_FAIL (context->resume (ptid, kind, sig));
+ }
+ }
+ else
+ {
+ std::shared_ptr<team_debug_context> context;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (ptid.pid (), context));
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (context->resume (ptid, kind, sig));
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+ptid_t
+wait (ptid_t ptid, struct target_waitstatus *ourstatus,
+ target_wait_flags target_options)
+{
+ haiku_nat_debug_printf ("ptid=%s, target_options=%i",
+ ptid.to_string ().c_str (),
+ (int)target_options.raw ());
+
+ gdb_assert (ourstatus != nullptr);
+
+ if (is_async_p ())
+ pipe_to_event_loop.flush ();
+
+ std::shared_ptr<team_debug_context> chosen_context;
+
+ if (ptid == minus_one_ptid)
+ {
+ /* Wait for any process. */
+ bool block = !(target_options & TARGET_WNOHANG).raw ();
+
+ std::vector<std::weak_ptr<team_debug_context> > contexts;
+ std::vector<object_wait_info> wait_infos;
+
+ while (!chosen_context)
+ {
+ std::size_t context_count = team_debug_contexts.size ();
+
+ contexts.clear ();
+ contexts.reserve (context_count);
+
+ wait_infos.clear ();
+ wait_infos.reserve (context_count);
+
+ for (const auto &[team, context] : team_debug_contexts)
+ {
+ if (context->has_events ())
+ {
+ chosen_context = context;
+ break;
+ }
+ contexts.emplace_back (context);
+ wait_infos.emplace_back (
+ object_wait_info{ .object = context->debugger_port (),
+ .type = B_OBJECT_TYPE_PORT,
+ .events = B_EVENT_READ });
+ }
+
+ if (chosen_context)
+ break;
+
+ ssize_t count;
+
+ do
+ {
+ count = wait_for_objects_etc (wait_infos.data (),
+ wait_infos.size (),
+ block ? 0 : B_RELATIVE_TIMEOUT, 0);
+ }
+ while (count == B_INTERRUPTED);
+
+ if (!block && (count == B_WOULD_BLOCK || count == 0))
+ {
+ ourstatus->set_ignore ();
+ return null_ptid;
+ }
+ else if (count < 0)
+ {
+ errno = count;
+ return minus_one_ptid;
+ }
+
+ std::shared_ptr<team_debug_context> current_context;
+ ptid_t wptid;
+ status_t status;
+
+ for (std::size_t i = 0; i < context_count; ++i)
+ {
+ if (contexts[i].expired ())
+ continue;
+ if ((wait_infos[i].events & B_EVENT_READ) == 0)
+ continue;
+ current_context = contexts[i].lock ();
+
+ /* Peek to see if the port holds an event we actually want
+ instead of something like HANDED_OVER. */
+ wptid = ptid;
+ status = current_context->wait (wptid, nullptr, TARGET_WNOHANG);
+
+ /* The current one cannot immediately give a valid event. */
+ if (status < B_OK || wptid == null_ptid)
+ continue;
+
+ chosen_context = std::move (current_context);
+ break;
+ }
+ }
+
+ ptid = ptid_t (chosen_context->team ());
+
+ haiku_nat_debug_printf ("chosen ptid=%s", ptid.to_string ().c_str ());
+ }
+ else
+ {
+ /* Wait for the specified process only. */
+ RETURN_VALUE_AND_SET_ERRNO_IF_FAIL (
+ get_context (ptid.pid (), chosen_context), minus_one_ptid);
+ }
+
+ RETURN_VALUE_AND_SET_ERRNO_IF_FAIL (
+ chosen_context->wait (ptid, ourstatus, target_options), minus_one_ptid);
+
+ if (chosen_context->is_detached () && !chosen_context->has_events ())
+ {
+ haiku_nat_debug_printf ("removing deleted team context: team=%i",
+ chosen_context->team ());
+ /* There's nothing left. Remove this team from our records. */
+ delete_context (chosen_context);
+ }
+ else
+ {
+ /* There might be more events on the ports.
+
+ A false positive is harmless as long as GDB intends to call us again.
+ We should not mark if the team is about to exit (and therefore stopped
+ being waited by GDB). Otherwise, nothing will flush the event loop
+ pipe, and GDB will enter an infinite loop.
+
+ A false negative makes GDB hang forever. */
+ if (ptid != null_ptid && is_async_p ())
+ {
+ /* There might be queued events in the debug context.
+ Notify the main loop directly. */
+ pipe_to_event_loop.mark ();
+
+ /* There might be unread events in the debugger ports.
+ Notify the worker thread as well. */
+ pipe_to_worker.mark ();
+ }
+ }
+
+ haiku_nat_debug_printf ("ptid=%s, ourstatus=%s", ptid.to_string ().c_str (),
+ ourstatus->to_string ().c_str ());
+
+ return ptid;
+}
+
+/* See haiku-nat.h. */
+
+int
+kill (pid_t pid)
+{
+ haiku_nat_debug_printf ("pid=%i", pid);
+
+ std::shared_ptr<team_debug_context> context;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (pid, context));
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (kill_team (pid));
+
+ /* Prevent future attempts to get events for the killed team. */
+ delete_context (context);
+
+ ptid_t ptid;
+ target_waitstatus ourstatus;
+
+ /* Wait for the child to die. */
+ while (!context->is_detached ())
+ {
+ ptid = ptid_t (pid);
+
+ if (!context->has_events ())
+ std::ignore = context->resume (ptid, resume_continue, 0);
+
+ gdb_assert (context->wait (ptid, &ourstatus, 0) == B_OK);
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+detach (pid_t pid)
+{
+ haiku_nat_debug_printf ("pid=%i", pid);
+
+ std::shared_ptr<team_debug_context> context;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (pid, context));
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (context->detach ());
+
+ delete_context (context);
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+bool
+thread_alive (ptid_t ptid)
+{
+ haiku_nat_debug_printf ("ptid=%s", ptid.to_string ().c_str ());
+
+ std::shared_ptr<team_debug_context> context;
+ RETURN_VALUE_AND_SET_ERRNO_IF_FAIL (get_context (ptid.pid (), context),
+ false);
+
+ return context->thread_alive (ptid);
+}
+
+/* See haiku-nat.h. */
+
+int
+read_memory (pid_t pid, CORE_ADDR memaddr, unsigned char *myaddr,
+ int *sizeLeft)
+{
+ haiku_nat_debug_printf ("pid=%i, memaddr=%p, myaddr=%p, size=%i", pid,
+ (void *)memaddr, myaddr, *sizeLeft);
+
+ std::shared_ptr<team_debug_context> context;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (pid, context));
+
+ debug_nub_read_memory_reply reply;
+
+ while (*sizeLeft > 0)
+ {
+ int32 read_size
+ = (int32)std::min (*sizeLeft, (int)B_MAX_READ_WRITE_MEMORY_SIZE);
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (
+ context->send<B_DEBUG_MESSAGE_READ_MEMORY> (
+ { .address = (void *)memaddr, .size = read_size }, reply));
+
+ memcpy (myaddr, reply.data, reply.size);
+ memaddr += reply.size;
+ myaddr += reply.size;
+ *sizeLeft -= reply.size;
+ }
+
+ haiku_nat_debug_printf ("pid=%i, memaddr=%p success", pid, (void *)memaddr);
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+write_memory (pid_t pid, CORE_ADDR memaddr, const unsigned char *myaddr,
+ int *sizeLeft)
+{
+ haiku_nat_debug_printf ("pid=%i, memaddr=%p, myaddr=%p, size=%i", pid,
+ (void *)memaddr, myaddr, *sizeLeft);
+
+ std::shared_ptr<team_debug_context> context;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (pid, context));
+
+ debug_nub_write_memory data;
+ debug_nub_write_memory_reply reply;
+
+ while (*sizeLeft > 0)
+ {
+ data.address = (void *)memaddr;
+ data.size = std::min (*sizeLeft, (int)B_MAX_READ_WRITE_MEMORY_SIZE);
+ memcpy (data.data, myaddr, data.size);
+
+ /* TODO: Rollback if attempt failed? */
+ RETURN_AND_SET_ERRNO_IF_FAIL (
+ context->send<B_DEBUG_MESSAGE_WRITE_MEMORY> (std::move (data),
+ reply));
+
+ memaddr += reply.size;
+ myaddr += reply.size;
+ *sizeLeft -= reply.size;
+ }
+
+ haiku_nat_debug_printf ("pid=%i, memaddr=%p success", pid, (void *)memaddr);
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+read_offsets (pid_t pid, CORE_ADDR *text, CORE_ADDR *data)
+{
+ haiku_nat_debug_printf ("pid=%i", pid);
+
+ CORE_ADDR raw_text;
+ size_t raw_text_size;
+ CORE_ADDR raw_data;
+
+ std::shared_ptr<team_debug_context> context;
+ if (get_context (pid, context) == B_OK && context->has_stored_app_image ())
+ {
+ haiku_nat_debug_printf ("reading offests from cached image");
+
+ /* Prioritize the cached image. This might be useful right after an
+ IMAGE_CREATED event, when we have all the information we need but the
+ same info is not yet visible to get_next_image_info. */
+ raw_text = (CORE_ADDR)context->app_image ().text;
+ raw_text_size = context->app_image ().text_size;
+ raw_data = (CORE_ADDR)context->app_image ().data;
+ }
+ else
+ {
+ haiku_nat_debug_printf ("reading offests from system");
+
+ if (for_each_image (
+ pid,
+ [&] (const image_info &info) {
+ if (!info.is_main_executable)
+ return 0;
+
+ raw_text = info.text;
+ raw_text_size = info.text_size;
+ raw_data = info.data;
+
+ return 1;
+ },
+ true)
+ < 0)
+ return -1;
+ }
+
+ *text = raw_text;
+ *data = raw_data - raw_text_size;
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+bool
+thread_stopped (ptid_t ptid)
+{
+ haiku_nat_debug_printf ("ptid=%s", ptid.to_string ().c_str ());
+
+ std::shared_ptr<team_debug_context> context;
+ RETURN_VALUE_AND_SET_ERRNO_IF_FAIL (get_context (ptid.pid (), context),
+ false);
+
+ status_t status = context
+ ->send<B_DEBUG_MESSAGE_GET_CPU_STATE> (
+ { .thread = (thread_id)ptid.tid () })
+ .error;
+
+ haiku_nat_debug_printf ("ptid=%s, status=%s", ptid.to_string ().c_str (),
+ strerror (status));
+
+ if (status >= B_OK)
+ {
+ /* Operation only succeeds when thread is stopped. */
+ return true;
+ }
+ else if (status == B_BAD_THREAD_STATE)
+ {
+ /* This occurs when thread is not stopped. */
+ return false;
+ }
+ else
+ {
+ /* Some other error. */
+ errno = status;
+ return false;
+ }
+}
+
+/* See haiku-nat.h. */
+
+const char *
+pid_to_exec_file (pid_t pid)
+{
+ haiku_nat_debug_printf ("pid=%i", pid);
+
+ std::shared_ptr<team_debug_context> context;
+ if (get_context (pid, context) == B_OK && context->has_stored_app_image ())
+ {
+ return context->app_image ().name;
+ }
+
+ const char *result = nullptr;
+
+ for_each_image (
+ pid,
+ [&] (const image_info &info) {
+ if (!info.is_main_executable)
+ return 0;
+
+ result = info.name;
+
+ return 1;
+ },
+ true);
+
+ return result;
+}
+
+/* See haiku-nat.h. */
+
+const char *
+thread_name (ptid_t ptid)
+{
+ haiku_nat_debug_printf ("ptid=%s", ptid.to_string ().c_str ());
+
+ static ::thread_info info;
+ RETURN_VALUE_AND_SET_ERRNO_IF_FAIL (get_thread_info (ptid.tid (), &info),
+ nullptr);
+
+ haiku_nat_debug_printf ("ptid=%s, name=%s", ptid.to_string ().c_str (),
+ info.name);
+
+ return info.name;
+}
+
+/* See haiku-nat.h. */
+
+std::string
+pid_to_str (ptid_t ptid)
+{
+ haiku_nat_debug_printf ("ptid=%s", ptid.to_string ().c_str ());
+
+ union
+ {
+ ::team_info team;
+ ::thread_info thread;
+ };
+
+ bool team_valid = get_team_info (ptid.pid (), &team) == B_OK;
+
+ std::string result
+ = team_valid ? string_printf ("team %d (%s)", ptid.pid (), team.name)
+ : string_printf ("team %d", ptid.pid ());
+
+ if (!ptid.tid_p ())
+ return result;
+
+ bool thread_valid
+ = team_valid && (get_thread_info (ptid.tid (), &thread) == B_OK);
+
+ result += thread_valid
+ ? string_printf (" thread %ld (%s)", ptid.tid (), thread.name)
+ : string_printf (" thread %ld", ptid.tid ());
+
+ return result;
+}
+
+/* See haiku-nat.h. */
+
+int
+stop (ptid_t ptid)
+{
+ haiku_nat_debug_printf ("ptid=%s", ptid.to_string ().c_str ());
+
+ std::shared_ptr<team_debug_context> context;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (ptid.pid (), context));
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (context->stop (ptid));
+
+ return 0;
+}
+
+#define RETURN_OR_CONTINUE(exp) \
+ do \
+ { \
+ switch (exp) \
+ { \
+ case -1: \
+ return -1; \
+ case 0: \
+ continue; \
+ case 1: \
+ return 0; \
+ } \
+ } \
+ while (0)
+
+/* See haiku-nat.h */
+
+bool
+is_async_p ()
+{
+ return async_worker_thread != -1;
+}
+
+/* See haiku-nat.h */
+
+int
+async (bool enable)
+{
+ haiku_nat_debug_printf ("enable=%s", enable ? "true" : "false");
+
+ /* TODO: We might want to share some code and infrastructure here with
+ wait(). Ideally both should be backed by kernel-side event queues with
+ all the debugger ports added.
+
+ We can look at eliminating the worker thread all together in favor of
+ accessing the private _kern_event_queue_create syscall and returning that
+ FD as the async_wait_fd when Haiku finally adds support for polling event
+ queue FDs. See https://dev.haiku-os.org/ticket/18954. */
+
+ if (enable == is_async_p ())
+ return 0;
+
+ if (!enable)
+ {
+ RETURN_AND_SET_ERRNO_IF_FAIL (
+ send_signal (async_worker_thread, SIGKILLTHR));
+ async_worker_thread = -1;
+ pipe_to_worker.close_pipe ();
+ pipe_to_event_loop.close_pipe ();
+ }
+ else
+ {
+ constexpr auto pipe_close
+ = [] (event_pipe *pipe) { pipe->close_pipe (); };
+
+ std::unique_ptr<event_pipe, decltype (pipe_close)>
+ pipe_to_event_loop_closer (&pipe_to_event_loop, pipe_close);
+
+ std::unique_ptr<event_pipe, decltype (pipe_close)>
+ pipe_to_worker_closer (&pipe_to_event_loop, pipe_close);
+
+ if (!pipe_to_event_loop.open_pipe ())
+ return -1;
+
+ if (!pipe_to_worker.open_pipe ())
+ return -1;
+
+ async_worker_thread = spawn_thread (
+ [] (void *data) -> status_t {
+ std::vector<object_wait_info> wait_infos;
+ bool wait_for_ports = true;
+
+ while (true)
+ {
+ wait_infos.clear ();
+ wait_infos.emplace_back (
+ object_wait_info{ .object = pipe_to_worker.event_fd (),
+ .type = B_OBJECT_TYPE_FD,
+ .events = B_EVENT_READ });
+
+ if (wait_for_ports)
+ {
+ std::lock_guard lock (team_debug_ports_lock);
+
+ for (port_id port : team_debug_ports)
+ {
+ wait_infos.emplace_back (
+ object_wait_info{ .object = port,
+ .type = B_OBJECT_TYPE_PORT,
+ .events = B_EVENT_READ });
+ }
+ }
+
+ ssize_t event_count;
+
+ do
+ {
+ event_count = wait_for_objects (wait_infos.data (),
+ wait_infos.size ());
+ }
+ while (event_count == B_INTERRUPTED);
+
+ gdb_assert (event_count > 0);
+
+ if (wait_infos[0].events & B_EVENT_READ)
+ {
+ /* The main thread has requested us to continue. */
+ --event_count;
+ pipe_to_worker.flush ();
+ wait_for_ports = true;
+ }
+ else
+ {
+ if (wait_infos[0].events != 0)
+ {
+ /* Other events in the pipe we are uninterested in. */
+ --event_count;
+ }
+
+ /* Wait for the main thread to actually read the ports
+ before waiting again. */
+ wait_for_ports = false;
+ }
+
+ /* There are ports to process in this iteration. */
+ if (event_count > 0)
+ {
+ std::lock_guard lock (team_debug_ports_lock);
+
+ for (size_t i = 1; i < wait_infos.size (); ++i)
+ {
+ const object_wait_info &wait_info = wait_infos[i];
+ /* Remove dead ports. */
+ if (wait_info.events & B_EVENT_INVALID)
+ {
+ team_debug_ports.erase (wait_info.object);
+ --event_count;
+ }
+ }
+ }
+
+ /* There are some events to read from these ports. */
+ if (event_count > 0)
+ pipe_to_event_loop.mark ();
+ }
+ },
+ "gdb debugger port listener", B_NORMAL_PRIORITY, nullptr);
+
+ if (async_worker_thread < 0)
+ {
+ errno = async_worker_thread;
+ async_worker_thread = -1;
+ return -1;
+ }
+
+ status_t status = resume_thread (async_worker_thread);
+ if (status < B_OK)
+ {
+ send_signal (async_worker_thread, SIGKILLTHR);
+ errno = status;
+ async_worker_thread = -1;
+ return -1;
+ }
+
+ /* Always trigger an initial event. */
+ pipe_to_event_loop.mark ();
+
+ pipe_to_event_loop_closer.release ();
+ pipe_to_worker_closer.release ();
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h */
+
+int
+async_wait_fd ()
+{
+ return pipe_to_event_loop.event_fd ();
+}
+
+static void
+convert_image_info (const ::image_info &haiku_info, image_info &info)
+{
+ info.id = haiku_info.id;
+ info.text = (CORE_ADDR)haiku_info.text;
+ info.text_size = (ULONGEST)haiku_info.text_size;
+ info.data = (CORE_ADDR)haiku_info.data;
+ info.data_size = (ULONGEST)haiku_info.data_size;
+ info.name = haiku_info.name;
+ info.sequence = haiku_info.sequence;
+ info.init_order = haiku_info.init_order;
+ info.is_main_executable = haiku_info.type == B_APP_IMAGE;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_image (pid_t pid,
+ const std::function<int (const image_info &info)> &callback,
+ bool needs_one)
+{
+ static ::image_info haiku_info;
+ static image_info info;
+
+ int32 cookie = 0;
+
+ while (get_next_image_info (pid, &cookie, &haiku_info) == B_OK)
+ {
+ convert_image_info (haiku_info, info);
+ info.team = pid;
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+
+ if (needs_one)
+ {
+ errno = B_ENTRY_NOT_FOUND;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_area (pid_t pid,
+ const std::function<int (const area_info &info)> &callback)
+{
+ static ::area_info haiku_info;
+ static area_info info;
+
+ ssize_t cookie = 0;
+
+ while (get_next_area_info (pid, &cookie, &haiku_info) == B_OK)
+ {
+ info.id = haiku_info.area;
+ info.name = haiku_info.name;
+ info.size = haiku_info.size;
+ info.can_read = haiku_info.protection & B_READ_AREA;
+ info.can_write = haiku_info.protection & B_WRITE_AREA;
+ info.can_exec = haiku_info.protection & B_EXECUTE_AREA;
+ info.is_stack = haiku_info.protection & B_STACK_AREA;
+ info.can_clone = haiku_info.protection & B_CLONEABLE_AREA;
+ info.team = haiku_info.team;
+ info.ram_size = haiku_info.ram_size;
+ info.copy_count = haiku_info.copy_count;
+ info.in_count = haiku_info.in_count;
+ info.out_count = haiku_info.out_count;
+ info.address = (CORE_ADDR)haiku_info.address;
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_thread (pid_t pid,
+ const std::function<int (const thread_info &info)> &callback)
+{
+ static ::thread_info haiku_info;
+ static thread_info info;
+
+ int32 cookie = 0;
+
+ while (get_next_thread_info (pid, &cookie, &haiku_info) == B_OK)
+ {
+ info.tid = haiku_info.thread;
+ info.team = haiku_info.team;
+ info.name = haiku_info.name;
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_fd (pid_t pid,
+ const std::function<int (const fd_info &info)> &callback)
+{
+ if (_kern_get_next_fd_info == nullptr)
+ {
+ haiku_nat_debug_printf ("Failed to issue get_next_fd_info syscall.");
+ errno = ENOSYS;
+ return -1;
+ }
+
+ static ::fd_info haiku_info;
+ static fd_info info;
+ static char path[B_PATH_NAME_LENGTH + 1];
+
+ uint32 cookie = 0;
+
+ while (
+ _kern_get_next_fd_info (pid, &cookie, &haiku_info, sizeof (haiku_info))
+ == B_OK)
+ {
+ info.number = haiku_info.number;
+ info.device = haiku_info.device;
+ info.node = haiku_info.node;
+ info.team = pid;
+
+ switch (haiku_info.open_mode & O_RWMASK)
+ {
+ case O_RDONLY:
+ info.can_read = true;
+ info.can_write = false;
+ break;
+ case O_WRONLY:
+ info.can_read = false;
+ info.can_write = true;
+ break;
+ case O_RDWR:
+ info.can_read = true;
+ info.can_write = true;
+ break;
+ }
+
+ info.name = nullptr;
+
+ if (_kern_entry_ref_to_path != nullptr)
+ {
+ /* This only works for directories. */
+ if (_kern_entry_ref_to_path (haiku_info.device, haiku_info.node,
+ nullptr, path, B_PATH_NAME_LENGTH)
+ >= B_OK)
+ {
+ path[B_PATH_NAME_LENGTH] = '\0';
+ info.name = path;
+ }
+ }
+ else
+ {
+ haiku_nat_debug_printf (
+ "Failed to issue entry_ref_to_path syscall.");
+ }
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_port (pid_t pid,
+ const std::function<int (const port_info &info)> &callback)
+{
+ static ::port_info haiku_info;
+ static port_info info;
+
+ int32 cookie = 0;
+
+ while (get_next_port_info (pid, &cookie, &haiku_info) == B_OK)
+ {
+ info.id = haiku_info.port;
+ info.team = haiku_info.team;
+ info.name = haiku_info.name;
+ info.capacity = haiku_info.capacity;
+ info.queue_count = haiku_info.queue_count;
+ info.total_count = haiku_info.total_count;
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_sem (pid_t pid,
+ const std::function<int (const sem_info &info)> &callback)
+{
+ static ::sem_info haiku_info;
+ static sem_info info;
+
+ int32 cookie = 0;
+
+ while (get_next_sem_info (pid, &cookie, &haiku_info) == B_OK)
+ {
+ info.id = haiku_info.sem;
+ info.team = haiku_info.team;
+ info.name = haiku_info.name;
+ info.count = haiku_info.count;
+ info.latest_holder = haiku_info.latest_holder;
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+
+ return 0;
+}
+
+static void
+convert_team_info (const ::team_info &haiku_info, team_info &info)
+{
+ info.pid = haiku_info.team;
+ info.args = haiku_info.args;
+ info.thread_count = haiku_info.thread_count;
+ info.image_count = haiku_info.image_count;
+ info.area_count = haiku_info.area_count;
+ info.debugger_nub_thread = haiku_info.debugger_nub_thread;
+ info.debugger_nub_port = haiku_info.debugger_nub_port;
+ info.uid = haiku_info.uid;
+ info.gid = haiku_info.gid;
+ info.real_uid = haiku_info.real_uid;
+ info.real_gid = haiku_info.real_gid;
+ info.group_id = haiku_info.group_id;
+ info.session_id = haiku_info.session_id;
+ info.parent = haiku_info.parent;
+ info.name = haiku_info.name;
+ info.start_time = haiku_info.start_time;
+}
+
+/* See haiku-nat.h. */
+
+const team_info *
+get_team (pid_t pid)
+{
+ static ::team_info haiku_info;
+ static team_info info;
+
+ RETURN_VALUE_AND_SET_ERRNO_IF_FAIL (get_team_info (pid, &haiku_info),
+ nullptr);
+
+ convert_team_info (haiku_info, info);
+
+ return &info;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_team (const std::function<int (const team_info &info)> &callback)
+{
+ static ::team_info haiku_info;
+ static team_info info;
+
+ int32 cookie = 0;
+
+ while (get_next_team_info (&cookie, &haiku_info) == B_OK)
+ {
+ convert_team_info (haiku_info, info);
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_commpage_symbol (
+ const std::function<int (const commpage_symbol_info &info)> &callback)
+{
+ if (_kern_read_kernel_image_symbols == nullptr)
+ {
+ haiku_nat_debug_printf (
+ "Failed to issue read_kernel_image_symbols syscall.");
+ errno = ENOSYS;
+ return -1;
+ }
+
+ image_id commpage_image = -1;
+ if (for_each_image (
+ B_SYSTEM_TEAM,
+ [&] (const image_info &image_info) {
+ if (strcmp (image_info.name, "commpage") == 0)
+ {
+ commpage_image = image_info.id;
+ return 1;
+ }
+ return 0;
+ },
+ true)
+ < 0)
+ {
+ return -1;
+ }
+
+ int32 symbol_count = 0;
+ size_t string_table_size = 0;
+ RETURN_AND_SET_ERRNO_IF_FAIL (
+ _kern_read_kernel_image_symbols (commpage_image, nullptr, &symbol_count,
+ nullptr, &string_table_size, nullptr));
+
+ std::vector<elf_sym> symbols (symbol_count);
+ /* An additional guaranteed null terminator. */
+ std::vector<char> string_table (string_table_size + 1);
+
+ RETURN_AND_SET_ERRNO_IF_FAIL (_kern_read_kernel_image_symbols (
+ commpage_image, symbols.data (), &symbol_count, string_table.data (),
+ &string_table_size, nullptr));
+
+ if (symbols.size () > symbol_count)
+ symbols.resize (symbol_count);
+
+ string_table.back () = '\0';
+
+ static commpage_symbol_info info;
+
+ for (const auto &sym : symbols)
+ {
+ info.name = string_table.data () + sym.st_name;
+ info.value = sym.st_value;
+ info.size = sym.st_size;
+ info.is_function = ELF_ST_TYPE (sym.st_info) == STT_FUNC;
+ info.is_object = ELF_ST_TYPE (sym.st_info) == STT_OBJECT;
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_cpu (const std::function<int (const cpu_info &info)> &callback)
+{
+ system_info sysinfo;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_system_info (&sysinfo));
+
+ size_t cpu_count = sysinfo.cpu_count;
+
+ std::vector< ::cpu_info> haiku_cpu_infos (cpu_count);
+ RETURN_AND_SET_ERRNO_IF_FAIL (
+ get_cpu_info (0, cpu_count, haiku_cpu_infos.data ()));
+
+ uint32 node_count = 0;
+ RETURN_AND_SET_ERRNO_IF_FAIL (get_cpu_topology_info (nullptr, &node_count));
+
+ std::vector< ::cpu_topology_node_info> haiku_cpu_nodes (node_count);
+ RETURN_AND_SET_ERRNO_IF_FAIL (
+ get_cpu_topology_info (haiku_cpu_nodes.data (), &node_count));
+
+ if (haiku_cpu_nodes.size () > node_count)
+ haiku_cpu_nodes.resize (node_count);
+
+ static cpu_info info;
+
+ for (const auto &node : haiku_cpu_nodes)
+ {
+ switch (node.type)
+ {
+ case B_TOPOLOGY_ROOT:
+ switch (node.data.root.platform)
+ {
+ case B_CPU_x86:
+ info.platform = "BePC";
+ break;
+ case B_CPU_x86_64:
+ info.platform = "x86_64";
+ break;
+ case B_CPU_PPC:
+ info.platform = "ppc";
+ break;
+ case B_CPU_PPC_64:
+ info.platform = "ppc64";
+ break;
+ case B_CPU_M68K:
+ info.platform = "m68k";
+ break;
+ case B_CPU_ARM:
+ info.platform = "arm";
+ break;
+ case B_CPU_ARM_64:
+ info.platform = "aarch64";
+ break;
+ case B_CPU_RISC_V:
+ info.platform = "riscv64";
+ break;
+ case B_CPU_UNKNOWN:
+ default:
+ info.platform = "unknown";
+ break;
+ }
+ break;
+ case B_TOPOLOGY_PACKAGE:
+ switch (node.data.package.vendor)
+ {
+ case B_CPU_VENDOR_AMD:
+ info.vendor = "AMD";
+ break;
+ case B_CPU_VENDOR_INTEL:
+ info.vendor = "Intel";
+ break;
+ case B_CPU_UNKNOWN:
+ default:
+ info.vendor = "Unknown";
+ }
+ info.cache_line_size = node.data.package.cache_line_size;
+ break;
+ case B_TOPOLOGY_CORE:
+ info.model = node.data.core.model;
+ info.default_frequency = node.data.core.default_frequency;
+ break;
+ case B_TOPOLOGY_SMT:
+ {
+ /* The leaf node, corresponding to exactly one core. */
+ const ::cpu_info &haiku_info = haiku_cpu_infos.at (node.id);
+
+ info.id = node.id;
+ info.current_frequency = haiku_info.current_frequency;
+ info.active_time = haiku_info.active_time;
+ info.enabled = haiku_info.enabled;
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* See haiku-nat.h. */
+
+int
+for_each_socket (const std::function<int (const socket_info &info)> &callback)
+{
+ if (_kern_get_next_socket_stat == nullptr)
+ {
+ haiku_nat_debug_printf ("Failed to issue get_next_socket_stat syscall.");
+ errno = ENOSYS;
+ return -1;
+ }
+
+ static net_stat haiku_info;
+ static socket_info info;
+
+ std::string family_str;
+ std::string type_str;
+ std::string address_str;
+ std::string peer_str;
+
+ uint32 cookie = 0;
+ while (_kern_get_next_socket_stat (-1, &cookie, &haiku_info) == B_OK)
+ {
+ switch (haiku_info.family)
+ {
+ case AF_UNIX:
+ {
+ family_str = "unix";
+
+ const sockaddr_un *address = (sockaddr_un *)&haiku_info.address;
+ address_str = address->sun_path;
+
+ const sockaddr_un *peer = (sockaddr_un *)&haiku_info.peer;
+ peer_str = peer->sun_path;
+ }
+ break;
+ case AF_INET:
+ {
+ family_str = "inet";
+
+ static const auto format_address = [] (const sockaddr_in *addr) {
+ std::string result;
+
+ if (addr->sin_addr.s_addr == INADDR_ANY)
+ result = "*";
+ else
+ result = inet_ntoa (addr->sin_addr);
+
+ result += ":";
+
+ if (addr->sin_port == 0)
+ result += "*";
+ else
+ result += std::to_string (ntohs (addr->sin_port));
+
+ return result;
+ };
+
+ address_str = format_address ((sockaddr_in *)&haiku_info.address);
+ peer_str = format_address ((sockaddr_in *)&haiku_info.peer);
+ }
+ break;
+ case AF_INET6:
+ family_str = "inet6";
+
+ static const auto format_address = [] (const sockaddr_in6 *addr) {
+ std::string result;
+
+ static char buffer[INET6_ADDRSTRLEN];
+
+ if (memcmp (&addr->sin6_addr, &in6addr_any, sizeof (in6_addr))
+ == 0)
+ result = "*";
+ else
+ result = inet_ntop (AF_INET6, &addr->sin6_addr, buffer,
+ sizeof (buffer));
+
+ result = "[" + result + "]:";
+
+ if (addr->sin6_port == 0)
+ result += "*";
+ else
+ result += std::to_string (ntohs (addr->sin6_port));
+
+ return result;
+ };
+
+ address_str = format_address ((sockaddr_in6 *)&haiku_info.address);
+ peer_str = format_address ((sockaddr_in6 *)&haiku_info.peer);
+
+ break;
+ default:
+ family_str = std::to_string (haiku_info.family);
+ address_str = peer_str = "-";
+ break;
+ }
+
+ switch (haiku_info.type)
+ {
+ case SOCK_STREAM:
+ switch (haiku_info.family)
+ {
+ case AF_INET:
+ case AF_INET6:
+ type_str = "tcp";
+ break;
+ default:
+ type_str = "stream";
+ }
+ break;
+ case SOCK_DGRAM:
+ switch (haiku_info.family)
+ {
+ case AF_INET:
+ case AF_INET6:
+ type_str = "udp";
+ break;
+ default:
+ type_str = "dgram";
+ }
+ break;
+ case SOCK_RAW:
+ type_str = "raw";
+ break;
+ case SOCK_SEQPACKET:
+ type_str = "seqpacket";
+ break;
+ case SOCK_MISC:
+ type_str = "misc";
+ break;
+ }
+
+ info.family = family_str.c_str ();
+ info.type = type_str.c_str ();
+ info.state = haiku_info.state;
+ info.team = haiku_info.owner;
+ info.address = address_str.c_str ();
+ info.peer = peer_str.c_str ();
+ info.receive_queue_size = haiku_info.receive_queue_size;
+ info.send_queue_size = haiku_info.send_queue_size;
+
+ RETURN_OR_CONTINUE (callback (info));
+ }
+
+ return 0;
+}
+
+}
diff --git a/gdb/nat/haiku-nat.h b/gdb/nat/haiku-nat.h
new file mode 100644
index 0000000..4a057a4
--- /dev/null
+++ b/gdb/nat/haiku-nat.h
@@ -0,0 +1,429 @@
+/* Internal interfaces for the Haiku code.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef NAT_HAIKU_NAT_H
+#define NAT_HAIKU_NAT_H
+
+#include <functional>
+
+#include <unistd.h>
+
+#include "target/resume.h"
+#include "target/wait.h"
+
+namespace haiku_nat
+{
+
+/* Attach gdb as the debugger for the process with the specified PID.
+ Returns -1 on failure and 0 on success. */
+
+extern int attach (pid_t pid, bool is_ours);
+
+/* Halts the current process until a debugger has been attached. */
+
+extern void wait_for_debugger ();
+
+/* Fetch registers from the inferior process. */
+
+extern int get_cpu_state (ptid_t ptid, void *buffer);
+
+/* Store registers to the inferior process. */
+
+extern int set_cpu_state (ptid_t ptid, const void *buffer);
+
+/* Implement the resume target_ops method.
+
+ Resume the inferior process. */
+
+extern int resume (ptid_t ptid, resume_kind kind, int sig);
+
+/* Implement the wait target_ops method.
+
+ Wait for the inferior process or thread to change state. Store
+ status through argument pointer STATUS.
+
+ PTID = -1 to wait for any pid to do something, PTID(pid,0,0) to
+ wait for any thread of process pid to do something. Return ptid
+ of child, or -1 in case of error; store status through argument
+ pointer STATUS. OPTIONS is a bit set of options defined as
+ TARGET_W* above. If options contains TARGET_WNOHANG and there's
+ no child stop to report, return is
+ null_ptid/TARGET_WAITKIND_IGNORE. */
+
+[[nodiscard]]
+extern ptid_t wait (ptid_t ptid, struct target_waitstatus *ourstatus,
+ target_wait_flags target_options);
+
+/* Implement the kill target_ops method.
+
+ Kill process PROC. Return -1 on failure, and 0 on success. */
+
+[[nodiscard]]
+extern int kill (pid_t pid);
+
+/* Implement the detach target_ops method.
+
+ Detach from process PROC. Return -1 on failure, and 0 on success. */
+
+[[nodiscard]]
+extern int detach (pid_t pid);
+
+/* Implement the thread_alive target_ops method.
+
+ Return true iff the thread with process ID PID is alive. */
+
+[[nodiscard]]
+extern bool thread_alive (ptid_t ptid);
+
+/* Implement the read_memory target_ops method.
+
+ Read LEN bytes at MEMADDR into a buffer at MYADDR.
+
+ Returns 0 on success and -1 on failure. */
+
+[[nodiscard]]
+extern int read_memory (pid_t pid, CORE_ADDR memaddr, unsigned char *myaddr,
+ int *sizeLeft);
+
+/* Implement the write_memory target_ops method.
+
+ Write LEN bytes from the buffer at MYADDR to MEMADDR.
+
+ Returns 0 on success and -1 on failure. */
+
+[[nodiscard]]
+extern int write_memory (pid_t pid, CORE_ADDR memaddr,
+ const unsigned char *myaddr, int *sizeLeft);
+
+/* Implement the read_offsets target_ops method.
+
+ Reports the text, data offsets of the executable. This is
+ needed for Haiku where the executable is relocated during load
+ time. */
+
+[[nodiscard]]
+extern int read_offsets (pid_t pid, CORE_ADDR *text, CORE_ADDR *data);
+
+/* Implement the thread_stopped target_ops method.
+
+ Return true if THREAD is known to be stopped now. */
+
+[[nodiscard]]
+extern bool thread_stopped (ptid_t ptid);
+
+/* Implement the pid_to_exec_file target_ops method.
+
+ Return the full absolute name of the executable file that was
+ run to create the process PID. If the executable file cannot
+ be determined, NULL is returned. Otherwise, a pointer to a
+ character string containing the pathname is returned. This
+ string should be copied into a buffer by the client if the string
+ will not be immediately used, or if it must persist. */
+
+[[nodiscard]]
+extern const char *pid_to_exec_file (pid_t pid);
+
+/* Implement the thread_name target_ops method.
+
+ Return the thread's name, or NULL if the target is unable to
+ determine it. The returned value must not be freed by the
+ caller. */
+
+[[nodiscard]]
+extern const char *thread_name (ptid_t ptid);
+
+/* Implement the pid_to_str target_ops method.
+
+ Converts a process id to a string. Usually, the string just
+ contains `process xyz', but on some systems it may contain
+ `process xyz thread abc'. */
+
+[[nodiscard]]
+extern std::string pid_to_str (ptid_t ptid);
+
+/* Implement the stop target_ops method.
+
+ Make target stop in a continuable fashion. */
+
+[[nodiscard]]
+extern int stop (ptid_t ptid);
+
+/* Implement the is_async_p target_ops method.
+
+ Is the target in asynchronous execution mode? */
+
+[[nodiscard]]
+extern bool is_async_p ();
+
+/* Implement the async target_ops method.
+
+ Enables/disables async target events. */
+
+extern int async (bool enable);
+
+/* Implement the async_wait_fd target_ops method. */
+
+[[nodiscard]]
+extern int async_wait_fd ();
+
+struct image_info
+{
+ LONGEST id;
+ CORE_ADDR text;
+ ULONGEST text_size;
+ CORE_ADDR data;
+ ULONGEST data_size;
+ const char *name;
+ LONGEST sequence;
+ LONGEST init_order;
+ pid_t team;
+ bool is_main_executable;
+};
+
+/* Calls the callback for each loaded image of the process with the specified
+ PID.
+ The callback should return -1 on error, 0 if the loop should continue,
+ or 1 if the loop should end.
+ If needs_one is true, the function returns an error if none of the callback
+ invocations returns 1.
+
+ Returns 0 on success and -1 on failure. */
+extern int
+for_each_image (pid_t pid,
+ const std::function<int (const image_info &info)> &callback,
+ bool needs_one = false);
+
+struct area_info
+{
+ LONGEST id;
+ const char *name;
+ ULONGEST size;
+ bool can_read : 1;
+ bool can_write : 1;
+ bool can_exec : 1;
+ bool is_stack : 1;
+ bool can_clone : 1;
+ pid_t team;
+ ULONGEST ram_size;
+ ULONGEST copy_count;
+ ULONGEST in_count;
+ ULONGEST out_count;
+ CORE_ADDR address;
+};
+
+/* Calls the callback for each mapped area of the process with the specified
+ PID.
+ The callback should return -1 on error, 0 if the loop should continue,
+ or 1 if the loop should end.
+
+ Returns 0 on success and -1 on failure. */
+extern int
+for_each_area (pid_t pid,
+ const std::function<int (const area_info &info)> &callback);
+
+struct thread_info
+{
+ ptid_t::tid_type tid;
+ pid_t team;
+ const char *name;
+};
+
+/* Calls the callback for each active thread of the process with the specified
+ PID.
+ The callback should return -1 on error, 0 if the loop should continue,
+ or 1 if the loop should end.
+
+ Returns 0 on success and -1 on failure. */
+extern int
+for_each_thread (pid_t pid,
+ const std::function<int (const thread_info &info)> &callback);
+
+struct fd_info
+{
+ int number;
+ bool can_read : 1;
+ bool can_write : 1;
+ LONGEST device;
+ LONGEST node;
+ pid_t team;
+ const char *name;
+};
+
+/* Calls the callback for each open file of the process with the specified PID.
+ The callback should return -1 on error, 0 if the loop should continue,
+ or 1 if the loop should end.
+
+ Returns 0 on success and -1 on failure. */
+extern int
+for_each_fd (pid_t pid,
+ const std::function<int (const fd_info &info)> &callback);
+
+struct port_info
+{
+ LONGEST id;
+ pid_t team;
+ const char *name;
+ LONGEST capacity;
+ LONGEST queue_count;
+ LONGEST total_count;
+};
+
+/* Calls the callback for each open port of the process with the specified PID.
+ The callback should return -1 on error, 0 if the loop should continue,
+ or 1 if the loop should end.
+
+ Returns 0 on success and -1 on failure. */
+extern int
+for_each_port (pid_t pid,
+ const std::function<int (const port_info &info)> &callback);
+
+struct sem_info
+{
+ LONGEST id;
+ pid_t team;
+ const char *name;
+ LONGEST count;
+ ptid_t::tid_type latest_holder;
+};
+
+/* Calls the callback for each semaphore of the process with the specified PID.
+ The callback should return -1 on error, 0 if the loop should continue,
+ or 1 if the loop should end.
+
+ Returns 0 on success and -1 on failure. */
+extern int
+for_each_sem (pid_t pid,
+ const std::function<int (const sem_info &info)> &callback);
+
+struct team_info
+{
+ pid_t pid;
+ const char *args;
+ ULONGEST thread_count;
+ ULONGEST image_count;
+ ULONGEST area_count;
+ LONGEST debugger_nub_thread;
+ LONGEST debugger_nub_port;
+ LONGEST uid;
+ LONGEST gid;
+ LONGEST real_uid;
+ LONGEST real_gid;
+ pid_t group_id;
+ pid_t session_id;
+ pid_t parent;
+ const char *name;
+ LONGEST start_time;
+};
+
+/* Gets the team with the specified ID.
+
+ Returns a static buffer with the information on success
+ and nullptr on failure. */
+[[nodiscard]]
+const team_info *get_team (pid_t pid);
+
+/* Calls the callback for each active team on the system.
+ The callback should return -1 on error, 0 if the loop should continue,
+ or 1 if the loop should end.
+
+ Returns 0 on success and -1 on failure. */
+extern int
+for_each_team (const std::function<int (const team_info &info)> &callback);
+
+struct commpage_symbol_info
+{
+ const char *name;
+ ULONGEST value;
+ ULONGEST size;
+ bool is_function : 1;
+ bool is_object : 1;
+};
+
+/* Calls the callback for each known symbol on the commpage.
+ The callback should return -1 on error, 0 if the loop should continue,
+ or 1 if the loop should end.
+
+ Returns 0 on success and -1 on failure. */
+extern int for_each_commpage_symbol (
+ const std::function<int (const commpage_symbol_info &info)> &callback);
+
+struct cpu_info
+{
+ LONGEST id;
+ const char *platform;
+ const char *vendor;
+ ULONGEST cache_line_size;
+ ULONGEST model;
+ ULONGEST default_frequency;
+ ULONGEST current_frequency;
+ ULONGEST active_time;
+ bool enabled;
+};
+
+/* Calls the callback for each CPU on the system.
+ The callback should return -1 on error, 0 if the loop should continue,
+ or 1 if the loop should end.
+
+ Returns 0 on success and -1 on failure. */
+extern int
+for_each_cpu (const std::function<int (const cpu_info &info)> &callback);
+
+struct socket_info
+{
+ const char *family;
+ const char *type;
+ const char *state;
+ pid_t team;
+ const char *address;
+ const char *peer;
+ ULONGEST receive_queue_size;
+ ULONGEST send_queue_size;
+};
+
+extern int
+for_each_socket (const std::function<int (const socket_info &info)> &callback);
+
+/* Utility functions that are meant to be supplied by the embedding
+ application. */
+
+void debugger_output (const char *message);
+
+void image_created (ptid_t ptid, const image_info &info);
+
+void image_deleted (ptid_t ptid, const image_info &info);
+
+bool is_catching_syscalls_for (ptid_t pid);
+
+}
+
+/* Tracing utility functions. */
+
+extern bool debug_haiku_nat;
+
+/* Print a haiku-nat debug statement. */
+
+#define haiku_nat_debug_printf(fmt, ...) \
+ debug_prefixed_printf_cond (debug_haiku_nat, "haiku-nat", fmt, ##__VA_ARGS__)
+
+/* Print "haiku-nat" enter/exit debug statements. */
+
+#define HAIKU_NAT_SCOPED_DEBUG_ENTER_EXIT \
+ scoped_debug_enter_exit (debug_haiku_nat, "haiku-nat")
+
+#endif /* NAT_HAIKU_NAT_H */
diff --git a/gdb/nat/haiku-nub-message.c b/gdb/nat/haiku-nub-message.c
new file mode 100644
index 0000000..815377d
--- /dev/null
+++ b/gdb/nat/haiku-nub-message.c
@@ -0,0 +1,50 @@
+/* Haiku nub messages support.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "haiku-nub-message.h"
+
+status_t
+haiku_send_nub_message (port_id nub_port, port_id reply_port,
+ debug_nub_message message, const void *data,
+ int data_size, void *reply, int reply_size)
+{
+ /* Send message. */
+ while (true)
+ {
+ status_t result = write_port (nub_port, message, data, data_size);
+ if (result == B_OK)
+ break;
+ if (result != B_INTERRUPTED)
+ return result;
+ }
+
+ if (!reply)
+ return B_OK;
+
+ /* Read reply. */
+ while (true)
+ {
+ int32 code;
+ ssize_t bytesRead = read_port (reply_port, &code, reply, reply_size);
+ if (bytesRead > 0)
+ return B_OK;
+ if (bytesRead != B_INTERRUPTED)
+ return bytesRead;
+ }
+}
diff --git a/gdb/nat/haiku-nub-message.h b/gdb/nat/haiku-nub-message.h
new file mode 100644
index 0000000..04212a3
--- /dev/null
+++ b/gdb/nat/haiku-nub-message.h
@@ -0,0 +1,141 @@
+/* Haiku nub messages support.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef NAT_HAIKU_NUB_MESSAGE_H
+#define NAT_HAIKU_NUB_MESSAGE_H
+
+#include "gnulib/config.h"
+
+#include <type_traits>
+
+#include <debugger.h>
+
+extern status_t haiku_send_nub_message (port_id nub_port, port_id reply_port,
+ debug_nub_message message,
+ const void *data, int data_size,
+ void *reply, int reply_size);
+
+template <debug_nub_message message> class haiku_nub_message_traits
+{
+};
+
+#define HAIKU_ASSOCIATE_MESSAGE_DATA(message, data) \
+ template <> class haiku_nub_message_traits<message> \
+ { \
+ public: \
+ typedef data data_type; \
+ typedef void reply_type; \
+ }
+
+#define HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY(message, data) \
+ template <> class haiku_nub_message_traits<message> \
+ { \
+ public: \
+ typedef data data_type; \
+ typedef data##_reply reply_type; \
+ }
+
+HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY (B_DEBUG_MESSAGE_READ_MEMORY,
+ debug_nub_read_memory);
+HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY (B_DEBUG_MESSAGE_WRITE_MEMORY,
+ debug_nub_write_memory);
+HAIKU_ASSOCIATE_MESSAGE_DATA (B_DEBUG_MESSAGE_SET_TEAM_FLAGS,
+ debug_nub_set_team_flags);
+HAIKU_ASSOCIATE_MESSAGE_DATA (B_DEBUG_MESSAGE_SET_THREAD_FLAGS,
+ debug_nub_set_thread_flags);
+HAIKU_ASSOCIATE_MESSAGE_DATA (B_DEBUG_MESSAGE_CONTINUE_THREAD,
+ debug_nub_continue_thread);
+HAIKU_ASSOCIATE_MESSAGE_DATA (B_DEBUG_MESSAGE_SET_CPU_STATE,
+ debug_nub_set_cpu_state);
+HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY (B_DEBUG_MESSAGE_GET_CPU_STATE,
+ debug_nub_get_cpu_state);
+HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY (B_DEBUG_MESSAGE_SET_BREAKPOINT,
+ debug_nub_set_breakpoint);
+HAIKU_ASSOCIATE_MESSAGE_DATA (B_DEBUG_MESSAGE_CLEAR_BREAKPOINT,
+ debug_nub_clear_breakpoint);
+HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY (B_DEBUG_MESSAGE_SET_WATCHPOINT,
+ debug_nub_set_watchpoint);
+HAIKU_ASSOCIATE_MESSAGE_DATA (B_DEBUG_MESSAGE_CLEAR_WATCHPOINT,
+ debug_nub_clear_watchpoint);
+HAIKU_ASSOCIATE_MESSAGE_DATA (B_DEBUG_MESSAGE_SET_SIGNAL_MASKS,
+ debug_nub_set_signal_masks);
+HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY (B_DEBUG_MESSAGE_GET_SIGNAL_MASKS,
+ debug_nub_get_signal_masks);
+HAIKU_ASSOCIATE_MESSAGE_DATA (B_DEBUG_MESSAGE_SET_SIGNAL_HANDLER,
+ debug_nub_set_signal_handler);
+HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY (B_DEBUG_MESSAGE_GET_SIGNAL_HANDLER,
+ debug_nub_get_signal_handler);
+HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY (B_DEBUG_START_PROFILER,
+ debug_nub_start_profiler);
+HAIKU_ASSOCIATE_MESSAGE_DATA (B_DEBUG_STOP_PROFILER, debug_nub_stop_profiler);
+HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY (B_DEBUG_WRITE_CORE_FILE,
+ debug_nub_write_core_file);
+
+#undef HAIKU_ASSOCIATE_MESSAGE_DATA
+#undef HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY
+
+template <debug_nub_message message>
+using haiku_nub_message_data =
+ typename haiku_nub_message_traits<message>::data_type;
+
+template <debug_nub_message message>
+using haiku_nub_message_reply =
+ typename haiku_nub_message_traits<message>::reply_type;
+
+template <debug_nub_message message>
+std::enable_if_t<std::is_same_v<haiku_nub_message_reply<message>, void>,
+ status_t>
+haiku_send_nub_message (port_id nub_port,
+ const haiku_nub_message_data<message> &data)
+{
+ return haiku_send_nub_message (nub_port, -1, message, &data, sizeof (data),
+ nullptr, 0);
+}
+
+template <debug_nub_message message>
+std::enable_if_t<!std::is_same_v<haiku_nub_message_reply<message>, void>,
+ haiku_nub_message_reply<message> >
+haiku_send_nub_message (port_id nub_port,
+ const haiku_nub_message_data<message> &data)
+{
+ haiku_nub_message_reply<message> reply;
+ status_t result
+ = haiku_send_nub_message (nub_port, data.reply_port, message, &data,
+ sizeof (data), &reply, sizeof (reply));
+ if (result >= B_OK)
+ return reply;
+
+ reply.error = result;
+ return reply;
+}
+
+template <debug_nub_message message>
+std::enable_if_t<!std::is_same_v<haiku_nub_message_reply<message>, void>,
+ status_t>
+haiku_send_nub_message (port_id nub_port,
+ const haiku_nub_message_data<message> &data,
+ haiku_nub_message_reply<message> &reply)
+{
+ status_t result
+ = haiku_send_nub_message (nub_port, data.reply_port, message, &data,
+ sizeof (data), &reply, sizeof (reply));
+ return (result < B_OK) ? result : reply.error;
+}
+
+#endif /* NAT_HAIKU_NUB_MESSAGE_H */
diff --git a/gdb/nat/haiku-osdata.c b/gdb/nat/haiku-osdata.c
new file mode 100644
index 0000000..1f295c4
--- /dev/null
+++ b/gdb/nat/haiku-osdata.c
@@ -0,0 +1,445 @@
+/* Haiku-specific functions to retrieve OS data.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "gdbsupport/common-defs.h"
+#include "gdbsupport/xml-utils.h"
+
+#include "diagnostics.h"
+
+#include "nat/haiku-nat.h"
+#include "nat/haiku-osdata.h"
+
+using namespace haiku_nat;
+
+template <typename InfoT>
+int
+all_teams (int (*for_each) (pid_t, const std::function<int (const InfoT &)> &),
+ const std::function<int (const InfoT &)> &callback)
+{
+ return for_each_team (
+ [&] (const team_info &info) { return for_each (info.pid, callback); });
+}
+
+static int
+for_each_image (pid_t pid,
+ const std::function<int (const image_info &info)> &callback)
+{
+ return for_each_image (pid, callback, false);
+}
+
+static struct osdata_type
+{
+ const char *type;
+ const char *title;
+ const char *description;
+ std::string (*take_snapshot) ();
+ std::string buffer;
+} osdata_table[] = {
+ {
+ .type = "types",
+ .title = "Types",
+ .description = "Listing of info os types you can list",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"types\">\n";
+
+ /* Start the below loop at 1, as we do not want to list
+ ourselves. */
+ for (int i = 1; osdata_table[i].type; ++i)
+ string_xml_appendf (buffer,
+ "<item>"
+ "<column name=\"Type\">%s</column>"
+ "<column name=\"Description\">%s</column>"
+ "<column name=\"Title\">%s</column>"
+ "</item>",
+ osdata_table[i].type,
+ osdata_table[i].description,
+ osdata_table[i].title);
+
+ buffer += "</osdata>\n";
+
+ return buffer;
+ },
+ },
+ {
+ .type = "areas",
+ .title = "Areas",
+ .description = "Listing of all areas",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"areas\">\n";
+
+ all_teams<area_info> (for_each_area, [&] (const area_info &info) {
+ std::string prot;
+ if (info.can_read)
+ prot += "r";
+ if (info.can_write)
+ prot += "w";
+ if (info.can_exec)
+ prot += "x";
+ if (info.is_stack)
+ prot += "s";
+ if (info.can_clone)
+ prot += "c";
+
+ string_xml_appendf (
+ buffer,
+ "<item>"
+ "<column name=\"team\">%s</column>"
+ "<column name=\"id\">%s</column>"
+ "<column name=\"name\">%s</column>"
+ "<column name=\"address\">%s</column>"
+ "<column name=\"size\">%s</column>"
+ "<column name=\"prot\">%s</column>"
+ "<column name=\"ram\">%s</column>"
+ "<column name=\"#-cow\">%s</column>"
+ "<column name=\"#-in\">%s</column>"
+ "<column name=\"#-out\">%s</column>"
+ "</item>",
+ plongest (info.team), plongest (info.id), info.name,
+ core_addr_to_string (info.address), pulongest (info.size),
+ prot.c_str (), pulongest (info.ram_size),
+ pulongest (info.copy_count), pulongest (info.in_count),
+ pulongest (info.out_count));
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ {
+ .type = "comm",
+ .title = "Commpage symbols",
+ .description = "Listing of all symbols on the system commpage",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"comm\">\n";
+
+ for_each_commpage_symbol ([&] (const commpage_symbol_info &info) {
+ std::string type;
+ if (info.is_function)
+ type += "f";
+ if (info.is_object)
+ type += "o";
+
+ /* BE CAREFUL WHAT WE RETURN HERE!
+ This operation is used mainly by haiku-tdep.c to synthesize the
+ commpage object. Changing the format might break GDB itself. */
+ string_xml_appendf (buffer,
+ "<item>"
+ "<column name=\"name\">%s</column>"
+ "<column name=\"value\">%s</column>"
+ "<column name=\"size\">%s</column>"
+ "<column name=\"type\">%s</column>"
+ "</item>",
+ info.name, pulongest (info.value),
+ pulongest (info.size), type.c_str ());
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ {
+ .type = "cpus",
+ .title = "CPUs",
+ .description = "Listing of all CPUs/cores on the system",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"cpus\">\n";
+
+ for_each_cpu ([&] (const cpu_info &info) {
+ string_xml_appendf (
+ buffer,
+ "<item>"
+ "<column name=\"id\">%s</column>"
+ "<column name=\"platform\">%s</column>"
+ "<column name=\"vendor\">%s</column>"
+ "<column name=\"cache\">%s</column>"
+ "<column name=\"model\">%s</column>"
+ "<column name=\"default freq.\">%s</column>"
+ "<column name=\"current freq.\">%s</column>"
+ "<column name=\"active time\">%s</column>"
+ "<column name=\"enabled\">%s</column>"
+ "</item>",
+ plongest (info.id), info.platform, info.vendor,
+ pulongest (info.cache_line_size), pulongest (info.model),
+ pulongest (info.default_frequency),
+ pulongest (info.current_frequency), pulongest (info.active_time),
+ info.enabled ? "true" : "false");
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ {
+ .type = "files",
+ .title = "File descriptors",
+ .description = "Listing of all file descriptors",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"files\">\n";
+
+ all_teams<fd_info> (for_each_fd, [&] (const fd_info &info) {
+ std::string mode;
+ if (info.can_read)
+ mode += "r";
+ if (info.can_write)
+ mode += "w";
+
+ string_xml_appendf (
+ buffer,
+ "<item>"
+ "<column name=\"team\">%s</column>"
+ "<column name=\"file descriptor\">%s</column>"
+ "<column name=\"mode\">%s</column>"
+ "<column name=\"device\">%s</column>"
+ "<column name=\"node\">%s</column>"
+ "<column name=\"name\">%s</column>"
+ "</item>",
+ plongest (info.team), plongest (info.number), mode.c_str (),
+ plongest (info.device), plongest (info.node),
+ (info.name != nullptr) ? info.name : "(unknown)");
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ {
+ .type = "images",
+ .title = "Images",
+ .description = "Listing of all images",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"images\">\n";
+
+ all_teams<image_info> (for_each_image, [&] (const image_info &info) {
+ string_xml_appendf (
+ buffer,
+ "<item>"
+ "<column name=\"team\">%s</column>"
+ "<column name=\"id\">%s</column>"
+ "<column name=\"text\">%s</column>"
+ "<column name=\"data\">%s</column>"
+ "<column name=\"seq#\">%s</column>"
+ "<column name=\"init#\">%s</column>"
+ "<column name=\"name\">%s</column>"
+ "</item>",
+ plongest (info.team), plongest (info.id),
+ core_addr_to_string (info.text), core_addr_to_string (info.data),
+ plongest (info.sequence), plongest (info.init_order), info.name);
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ {
+ .type = "ports",
+ .title = "Ports",
+ .description = "Listing of all ports",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"ports\">\n";
+
+ all_teams<port_info> (for_each_port, [&] (const port_info &info) {
+ string_xml_appendf (buffer,
+ "<item>"
+ "<column name=\"team\">%s</column>"
+ "<column name=\"id\">%s</column>"
+ "<column name=\"name\">%s</column>"
+ "<column name=\"capacity\">%s</column>"
+ "<column name=\"queued\">%s</column>"
+ "<column name=\"total\">%s</column>"
+ "</item>",
+ plongest (info.team), plongest (info.id),
+ info.name, plongest (info.capacity),
+ plongest (info.queue_count),
+ plongest (info.total_count));
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ {
+ .type = "sems",
+ .title = "Semaphores",
+ .description = "Listing of all semaphores",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"sems\">\n";
+
+ all_teams<sem_info> (for_each_sem, [&] (const sem_info &info) {
+ string_xml_appendf (buffer,
+ "<item>"
+ "<column name=\"team\">%s</column>"
+ "<column name=\"id\">%s</column>"
+ "<column name=\"name\">%s</column>"
+ "<column name=\"count\">%s</column>"
+ "<column name=\"holder\">%s</column>"
+ "</item>",
+ plongest (info.team), plongest (info.id),
+ info.name, plongest (info.count),
+ plongest (info.latest_holder));
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ {
+ .type = "sockets",
+ .title = "Sockets",
+ .description = "Listing of all sockets",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"sockets\">\n";
+
+ for_each_socket ([&] (const socket_info &info) {
+ string_xml_appendf (buffer,
+ "<item>"
+ "<column name=\"team\">%s</column>"
+ "<column name=\"family\">%s</column>"
+ "<column name=\"protocol\">%s</column>"
+ "<column name=\"local address\">%s</column>"
+ "<column name=\"remote address\">%s</column>"
+ "<column name=\"state\">%s</column>"
+ "<column name=\"recv-q\">%s</column>"
+ "<column name=\"send-q\">%s</column>"
+ "</item>",
+ plongest (info.team), info.family, info.type,
+ info.address, info.peer, info.state,
+ pulongest (info.receive_queue_size),
+ pulongest (info.send_queue_size));
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ {
+ .type = "teams",
+ .title = "Teams",
+ .description = "Listing of all teams",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"teams\">\n";
+
+ for_each_team ([&] (const team_info &info) {
+ string_xml_appendf (buffer,
+ "<item>"
+ "<column name=\"id\">%s</column>"
+ "<column name=\"uid\">%s</column>"
+ "<column name=\"command\">%s</column>"
+ "</item>",
+ pulongest (info.pid), pulongest (info.uid),
+ info.args);
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ {
+ .type = "threads",
+ .title = "Threads",
+ .description = "Listing of all threads",
+ .take_snapshot = [] () -> std::string {
+ std::string buffer = "<osdata type=\"threads\">\n";
+
+ all_teams<thread_info> (
+ for_each_thread, [&] (const thread_info &info) {
+ string_xml_appendf (buffer,
+ "<item>"
+ "<column name=\"team\">%s</column>"
+ "<column name=\"id\">%s</column>"
+ "<column name=\"name\">%s</column>"
+ "</item>",
+ plongest (info.team), plongest (info.tid),
+ info.name);
+
+ return 0;
+ });
+
+ buffer += "</osdata>\n";
+ return buffer;
+ },
+ },
+ /* TODO: There are also private syscalls for disk info,
+ but let's just ignore them for now. */
+ { NULL }
+};
+
+/* Copies up to LEN bytes in READBUF from offset OFFSET in OSD->BUFFER.
+ If OFFSET is zero, first calls OSD->TAKE_SNAPSHOT. */
+
+static LONGEST
+common_getter (struct osdata_type *osd, gdb_byte *readbuf, ULONGEST offset,
+ ULONGEST len)
+{
+ gdb_assert (readbuf);
+
+ if (offset == 0)
+ osd->buffer = osd->take_snapshot ();
+
+ if (offset >= osd->buffer.size ())
+ {
+ /* Done. Get rid of the buffer. */
+ osd->buffer.clear ();
+ return 0;
+ }
+
+ len = std::min (len, osd->buffer.size () - offset);
+ memcpy (readbuf, &osd->buffer[offset], len);
+
+ return len;
+}
+
+LONGEST
+haiku_common_xfer_osdata (const char *annex, gdb_byte *readbuf,
+ ULONGEST offset, ULONGEST len)
+{
+ if (!annex || *annex == '\0')
+ {
+ return common_getter (&osdata_table[0], readbuf, offset, len);
+ }
+ else
+ {
+ int i;
+
+ for (i = 0; osdata_table[i].type; ++i)
+ {
+ if (strcmp (annex, osdata_table[i].type) == 0)
+ return common_getter (&osdata_table[i], readbuf, offset, len);
+ }
+
+ return 0;
+ }
+}
diff --git a/gdb/nat/haiku-osdata.h b/gdb/nat/haiku-osdata.h
new file mode 100644
index 0000000..01e87e4
--- /dev/null
+++ b/gdb/nat/haiku-osdata.h
@@ -0,0 +1,26 @@
+/* Haiku-specific functions to retrieve OS data.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef NAT_HAIKU_OSDATA_H
+#define NAT_HAIKU_OSDATA_H
+
+extern LONGEST haiku_common_xfer_osdata (const char *annex, gdb_byte *readbuf,
+ ULONGEST offset, ULONGEST len);
+
+#endif /* NAT_HAIKU_OSDATA_H */
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 365dcf2..7460650 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -600,6 +600,12 @@ gdbreplay.o: gdbreplay.cc
$(ECHO_CXX) $(COMPILE.pre) $(INTERNAL_CFLAGS) $(CXXFLAGS) \
-include gdbsupport/common-defs.h $(COMPILE.post) $<
+# Rule for Haiku files. This is the same as COMPILE, but does not include
+# server.h to avoid name clashes with Haiku system structs.
+nat/haiku-%.o: ../gdb/nat/haiku-%.c
+ $(ECHO_CXX) $(COMPILE.pre) $(INTERNAL_CFLAGS) $(CXXFLAGS) \
+ $(COMPILE.post) -x c++ $<
+
#
# Dependency tracking.
#
diff --git a/gdbserver/configure b/gdbserver/configure
index 32980e5..6855a18 100755
--- a/gdbserver/configure
+++ b/gdbserver/configure
@@ -8722,7 +8722,7 @@ return socketpair ();
return 0;
}
_ACEOF
-for ac_lib in '' socket; do
+for ac_lib in '' socket network; do
if test -z "$ac_lib"; then
ac_res="none required"
else
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index ffc8494..48cc5e7 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -405,6 +405,15 @@ case "${gdbserver_host}" in
srv_tgtobj="${srv_tgtobj} nat/netbsd-nat.o"
srv_tgtobj="${srv_tgtobj} arch/amd64.o"
;;
+ x86_64-*-haiku*) srv_regobj=""
+ srv_tgtobj="haiku-low.o haiku-amd64-low.o fork-child.o"
+ srv_tgtobj="${srv_tgtobj} nat/fork-inferior.o"
+ srv_tgtobj="${srv_tgtobj} nat/haiku-debug.o"
+ srv_tgtobj="${srv_tgtobj} nat/haiku-nat.o"
+ srv_tgtobj="${srv_tgtobj} nat/haiku-nub-message.o"
+ srv_tgtobj="${srv_tgtobj} nat/haiku-osdata.o"
+ srv_tgtobj="${srv_tgtobj} arch/amd64.o"
+ ;;
xtensa*-*-linux*) srv_regobj=reg-xtensa.o
srv_tgtobj="$srv_linux_obj linux-xtensa-low.o"
diff --git a/gdbserver/haiku-amd64-low.cc b/gdbserver/haiku-amd64-low.cc
new file mode 100644
index 0000000..0e6e0ad
--- /dev/null
+++ b/gdbserver/haiku-amd64-low.cc
@@ -0,0 +1,262 @@
+/* Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "target.h"
+
+#include "haiku-low.h"
+
+#include "arch/amd64.h"
+#include "gdbsupport/x86-xstate.h"
+#include "tdesc.h"
+#include "x86-tdesc.h"
+
+#include "nat/haiku-nat.h"
+
+#include <array>
+
+/* Very conservative inclusion of Haiku headers to prevent name clashes. */
+#include <SupportDefs.h>
+#include <arch/x86_64/arch_debugger.h>
+
+/* Register numbers of various important registers. */
+
+enum amd64_regnum
+{
+ AMD64_RAX_REGNUM, /* %rax */
+ AMD64_RBX_REGNUM, /* %rbx */
+ AMD64_RCX_REGNUM, /* %rcx */
+ AMD64_RDX_REGNUM, /* %rdx */
+ AMD64_RSI_REGNUM, /* %rsi */
+ AMD64_RDI_REGNUM, /* %rdi */
+ AMD64_RBP_REGNUM, /* %rbp */
+ AMD64_RSP_REGNUM, /* %rsp */
+ AMD64_R8_REGNUM, /* %r8 */
+ AMD64_R9_REGNUM, /* %r9 */
+ AMD64_R10_REGNUM, /* %r10 */
+ AMD64_R11_REGNUM, /* %r11 */
+ AMD64_R12_REGNUM, /* %r12 */
+ AMD64_R13_REGNUM, /* %r13 */
+ AMD64_R14_REGNUM, /* %r14 */
+ AMD64_R15_REGNUM, /* %r15 */
+ AMD64_RIP_REGNUM, /* %rip */
+ AMD64_EFLAGS_REGNUM, /* %eflags */
+ AMD64_CS_REGNUM, /* %cs */
+ AMD64_SS_REGNUM, /* %ss */
+ AMD64_DS_REGNUM, /* %ds */
+ AMD64_ES_REGNUM, /* %es */
+ AMD64_FS_REGNUM, /* %fs */
+ AMD64_GS_REGNUM, /* %gs */
+ AMD64_ST0_REGNUM = 24, /* %st0 */
+ AMD64_ST1_REGNUM, /* %st1 */
+ AMD64_FCTRL_REGNUM = AMD64_ST0_REGNUM + 8,
+ AMD64_FSTAT_REGNUM = AMD64_ST0_REGNUM + 9,
+ AMD64_FTAG_REGNUM = AMD64_ST0_REGNUM + 10,
+ AMD64_XMM0_REGNUM = 40, /* %xmm0 */
+ AMD64_XMM1_REGNUM, /* %xmm1 */
+ AMD64_MXCSR_REGNUM = AMD64_XMM0_REGNUM + 16,
+ AMD64_YMM0H_REGNUM, /* %ymm0h */
+ AMD64_YMM15H_REGNUM = AMD64_YMM0H_REGNUM + 15,
+ AMD64_BND0R_REGNUM = AMD64_YMM15H_REGNUM + 1,
+ AMD64_BND3R_REGNUM = AMD64_BND0R_REGNUM + 3,
+ AMD64_BNDCFGU_REGNUM,
+ AMD64_BNDSTATUS_REGNUM,
+ AMD64_XMM16_REGNUM,
+ AMD64_XMM31_REGNUM = AMD64_XMM16_REGNUM + 15,
+ AMD64_YMM16H_REGNUM,
+ AMD64_YMM31H_REGNUM = AMD64_YMM16H_REGNUM + 15,
+ AMD64_K0_REGNUM,
+ AMD64_K7_REGNUM = AMD64_K0_REGNUM + 7,
+ AMD64_ZMM0H_REGNUM,
+ AMD64_ZMM31H_REGNUM = AMD64_ZMM0H_REGNUM + 31,
+ AMD64_PKRU_REGNUM,
+ AMD64_FSBASE_REGNUM,
+ AMD64_GSBASE_REGNUM
+};
+
+/* Number of general purpose registers. */
+#define AMD64_NUM_GREGS 24
+
+#define AMD64_NUM_REGS (AMD64_GSBASE_REGNUM + 1)
+
+/* At haiku_amd64_reg_offsets[REGNUM] you'll find the offset in `struct
+ debug_cpu_state' where the GDB register REGNUM is stored. */
+static constexpr auto haiku_amd64_reg_offsets = [] () constexpr {
+ std::array<int, AMD64_NUM_GREGS> result = {};
+
+ /* Set up the register offset table. */
+#define HAIKU_DECLARE_REG_OFFSET(gdbreg, haikureg) \
+ result[AMD64_##gdbreg##_REGNUM] \
+ = offsetof (struct x86_64_debug_cpu_state, haikureg)
+
+ HAIKU_DECLARE_REG_OFFSET (RAX, rax);
+ HAIKU_DECLARE_REG_OFFSET (RBX, rbx);
+ HAIKU_DECLARE_REG_OFFSET (RCX, rcx);
+ HAIKU_DECLARE_REG_OFFSET (RDX, rdx);
+ HAIKU_DECLARE_REG_OFFSET (RSI, rsi);
+ HAIKU_DECLARE_REG_OFFSET (RDI, rdi);
+ HAIKU_DECLARE_REG_OFFSET (RBP, rbp);
+ HAIKU_DECLARE_REG_OFFSET (RSP, rsp);
+ HAIKU_DECLARE_REG_OFFSET (R8, r8);
+ HAIKU_DECLARE_REG_OFFSET (R9, r9);
+ HAIKU_DECLARE_REG_OFFSET (R10, r10);
+ HAIKU_DECLARE_REG_OFFSET (R11, r11);
+ HAIKU_DECLARE_REG_OFFSET (R12, r12);
+ HAIKU_DECLARE_REG_OFFSET (R13, r13);
+ HAIKU_DECLARE_REG_OFFSET (R14, r14);
+ HAIKU_DECLARE_REG_OFFSET (R15, r15);
+ HAIKU_DECLARE_REG_OFFSET (RIP, rip);
+ HAIKU_DECLARE_REG_OFFSET (EFLAGS, rflags);
+ HAIKU_DECLARE_REG_OFFSET (CS, cs);
+ HAIKU_DECLARE_REG_OFFSET (SS, ss);
+ HAIKU_DECLARE_REG_OFFSET (DS, ds);
+ HAIKU_DECLARE_REG_OFFSET (ES, es);
+ HAIKU_DECLARE_REG_OFFSET (FS, fs);
+ HAIKU_DECLARE_REG_OFFSET (GS, gs);
+
+#undef HAIKU_DECLARE_REG_OFFSET
+
+ return result;
+}();
+
+/* Haiku target op definitions for the amd64 architecture. */
+
+class haiku_amd64_target : public haiku_process_target
+{
+public:
+ void fetch_registers (regcache *regcache, int regno) override;
+
+ void store_registers (regcache *regcache, int regno) override;
+
+ const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override;
+
+protected:
+ virtual void low_arch_setup (process_info *process) override;
+};
+
+/* Implement the fetch_registers target_ops method. */
+
+void
+haiku_amd64_target::fetch_registers (struct regcache *regcache, int regno)
+{
+ char regs[sizeof (x86_64_debug_cpu_state)];
+
+ if (haiku_nat::get_cpu_state (ptid_of (current_thread), &regs) < 0)
+ {
+ /* This happens when the inferior is killed by another process
+ while being stopped. The nub port has been deleted, so we cannot
+ send the required message to get the CPU state. */
+ haiku_nat_debug_printf ("Failed to get actual CPU state: %s",
+ strerror (errno));
+ memset (regs, 0, sizeof (regs));
+ }
+
+ if (regno == -1)
+ {
+ for (int i = 0; i < AMD64_NUM_GREGS; ++i)
+ supply_register (regcache, i, regs + haiku_amd64_reg_offsets[i]);
+ }
+ else
+ {
+ if (regno < AMD64_NUM_GREGS)
+ supply_register (regcache, regno,
+ regs + haiku_amd64_reg_offsets[regno]);
+ else
+ {
+ /* For the main GDB codebase, there is a helper function,
+ amd64_supply_fxsave that does just what we want.
+ However, this function is not linked to gdbserver.
+
+ We can fetch these registers by hand, but NetBSD seems fine with
+ just the general purpose ones, so keep it stubbed for now. */
+ haiku_nat_debug_printf ("Trying to fetch unimplemented register #%i",
+ regno);
+ }
+ }
+}
+
+/* Implement the store_registers target_ops method. */
+
+void
+haiku_amd64_target::store_registers (struct regcache *regcache, int regno)
+{
+ char regs[sizeof (x86_64_debug_cpu_state)];
+
+ if (haiku_nat::get_cpu_state (ptid_of (current_thread), &regs) < 0)
+ {
+ haiku_nat_debug_printf ("Failed to get actual CPU state: %s",
+ strerror (errno));
+ return;
+ }
+
+ if (regno == -1)
+ {
+ for (int i = 0; i < AMD64_NUM_GREGS; ++i)
+ collect_register (regcache, i, regs + haiku_amd64_reg_offsets[i]);
+ }
+ else
+ {
+ if (regno < AMD64_NUM_GREGS)
+ collect_register (regcache, regno,
+ regs + haiku_amd64_reg_offsets[regno]);
+ else
+ {
+ haiku_nat_debug_printf ("Trying to store unimplemented register #%i",
+ regno);
+ }
+ }
+
+ if (haiku_nat::set_cpu_state (ptid_of (current_thread), &regs) < 0)
+ perror_with_name (("haiku_nat::set_cpu_state"));
+}
+
+const gdb_byte *
+haiku_amd64_target::sw_breakpoint_from_kind (int kind, int *size)
+{
+ /* From <private/kernel/arch/x86/arch_user_debugger.h> */
+
+ /* DEBUG_SOFTWARE_BREAKPOINT_SIZE */
+ *size = 1;
+ /* DEBUG_SOFTWARE_BREAKPOINT */
+ static const gdb_byte x86_software_breakpoint[] = { 0xcc };
+ return x86_software_breakpoint;
+}
+
+/* Architecture-specific setup for the current process. */
+
+void
+haiku_amd64_target::low_arch_setup (process_info *process)
+{
+ if (process == nullptr)
+ process = current_process ();
+
+ /* Set up the target description. */
+ target_desc *tdesc = amd64_create_target_description (X86_XSTATE_AVX_MASK,
+ false, false, false);
+
+ init_target_desc (tdesc, amd64_expedite_regs);
+
+ process->tdesc = tdesc;
+}
+
+/* The singleton target ops object. */
+
+static haiku_amd64_target the_haiku_amd64_target;
+
+/* The Haiku target ops object. */
+
+haiku_process_target *the_haiku_target = &the_haiku_amd64_target;
diff --git a/gdbserver/haiku-low.cc b/gdbserver/haiku-low.cc
new file mode 100644
index 0000000..41295d5
--- /dev/null
+++ b/gdbserver/haiku-low.cc
@@ -0,0 +1,609 @@
+/* Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "target.h"
+
+#include "haiku-low.h"
+#include "nat/haiku-nat.h"
+#include "nat/haiku-osdata.h"
+
+#include "gdbsupport/common-debug.h"
+#include "gdbsupport/common-inferior.h"
+#include "gdbsupport/eintr.h"
+#include "nat/fork-inferior.h"
+
+int using_threads = 1;
+
+#ifdef DEVELOPMENT
+bool debug_haiku_nat = true;
+#else
+bool debug_haiku_nat = false;
+#endif
+
+/* Implement the create_inferior method of the target_ops vector. */
+
+int
+haiku_process_target::create_inferior (const char *program,
+ const std::vector<char *> &program_args)
+{
+ haiku_nat_debug_printf ("program=%s", program);
+
+ static const auto haiku_traceme = [] () {
+ haiku_nat_debug_printf ("haiku_traceme");
+ /* This happens before the child calls exec().
+ The debugger is responsible for resuming the inferior before it
+ loads the desired target. */
+ haiku_nat::wait_for_debugger ();
+ };
+
+ static const auto haiku_init_trace = [] (int pid) {
+ haiku_nat_debug_printf ("haiku_init_trace: pid=%i", pid);
+ if (haiku_nat::attach (pid, true) < 0)
+ trace_start_error_with_name (("haiku_nat::attach"));
+
+ /* At this stage, the child is being stopped for the first debugger event.
+ It has NOT exec'ed into the desired target yet, but is still a gdbserver
+ stuck in a wait_for_debugger() call. */
+
+ /* Consume the initial event. */
+ target_waitstatus ourstatus;
+ if (haiku_nat::wait (ptid_t (pid), &ourstatus, 0) == minus_one_ptid)
+ perror_with_name (("haiku_nat::wait"));
+
+ /* Allows the child to proceed to exec. */
+ if (haiku_nat::resume (ptid_t (pid), resume_continue, 0) < 0)
+ perror_with_name (("haiku_nat::continue_process"));
+ };
+
+ std::string str_program_args = construct_inferior_arguments (program_args);
+
+ client_state &cs = get_client_state ();
+ if (cs.disable_randomization)
+ get_environ ()->set ("DISABLE_ASLR", "1");
+ else
+ get_environ ()->unset ("DISABLE_ASLR");
+
+ pid_t pid = fork_inferior (program, str_program_args.c_str (),
+ get_environ ()->envp (), haiku_traceme,
+ haiku_init_trace, nullptr, nullptr, nullptr);
+
+ add_process (pid, 0);
+
+ post_fork_inferior (pid, program);
+
+ return pid;
+}
+
+/* Implement the post_create_inferior target_ops method. */
+
+void
+haiku_process_target::post_create_inferior ()
+{
+ low_arch_setup ();
+}
+
+/* Implement the attach target_ops method. */
+
+int
+haiku_process_target::attach (unsigned long pid)
+{
+ /* Add the process soon since haiku_nat::attach will
+ invoke our callback to report loaded libraries. */
+ process_info *process = add_process (pid, 1);
+
+ if (haiku_nat::attach (pid, false) < 0)
+ perror_with_name (("haiku_nat::attach"));
+
+ low_arch_setup (process);
+
+ return 0;
+}
+
+/* Implement the resume target_ops method. */
+
+void
+haiku_process_target::resume (struct thread_resume *resume_info, size_t n)
+{
+ for (size_t i = 0; i < n; ++i)
+ {
+ if (resume_info->thread.tid_p ())
+ {
+ thread_info *info = find_thread_ptid (ptid_t (
+ resume_info->thread.pid (), 0, resume_info->thread.tid ()));
+ if (info != nullptr)
+ regcache_invalidate_thread (info);
+ }
+ else if (resume_info->thread.pid () > 0)
+ regcache_invalidate_pid (resume_info->thread.pid ());
+ else
+ regcache_invalidate ();
+
+ /* TODO: What does the step_range_[start/end] mean? */
+ if (haiku_nat::resume (resume_info->thread, resume_info->kind,
+ resume_info->sig)
+ < 0)
+ {
+ haiku_nat_debug_printf ("Failed to actually resume the thread: %s",
+ safe_strerror (errno));
+ }
+ }
+}
+
+/* Implement the wait target_ops method. */
+
+ptid_t
+haiku_process_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
+ target_wait_flags target_options)
+{
+ haiku_nat_debug_printf (
+ "ptid=%s, ourstatus=%s, target_options=%i", ptid.to_string ().c_str (),
+ ourstatus->to_string ().c_str (), (int)target_options.raw ());
+
+ const auto attach_child = [&] () {
+ pid_t pid = ourstatus->child_ptid ().pid ();
+
+ process_info *process = add_process (pid, 0);
+
+ /* The new process might have other images pre-loaded.
+ Therefore, the second parameter should be false. */
+ if (haiku_nat::attach (pid, false) < 0)
+ perror_with_name (("haiku_nat::attach"));
+
+ low_arch_setup (process);
+
+ /* Add at least the child's main thread. Otherwise, gdbserver would
+ think we have no more inferiors attached and quit. */
+ add_thread (ptid_t (pid, 0, pid), nullptr);
+ };
+
+ client_state &cs = get_client_state ();
+
+ while (true)
+ {
+ ptid_t wptid = haiku_nat::wait (ptid, ourstatus, target_options);
+
+ if (wptid == minus_one_ptid)
+ perror_with_name (("haiku_nat::wait"));
+
+ if (wptid == null_ptid)
+ {
+ gdb_assert (target_options & TARGET_WNOHANG);
+ return null_ptid;
+ }
+
+ /* Register thread in the gdbcore if a thread was not reported earlier.
+ This is required after ::create_inferior, when the gdbcore does not
+ know about the first internal thread.
+ This may also happen on attach, when an event is registered on a
+ thread that was not fully initialized during the attach stage. */
+ if (wptid.tid () != 0 && !find_thread_ptid (wptid)
+ && ourstatus->kind () != TARGET_WAITKIND_THREAD_EXITED)
+ add_thread (wptid, nullptr);
+
+ switch (ourstatus->kind ())
+ {
+ case TARGET_WAITKIND_EXITED:
+ case TARGET_WAITKIND_STOPPED:
+ case TARGET_WAITKIND_SIGNALLED:
+ case TARGET_WAITKIND_SYSCALL_ENTRY:
+ case TARGET_WAITKIND_SYSCALL_RETURN:
+ /* Pass the result to the generic code. */
+ return wptid;
+ case TARGET_WAITKIND_LOADED:
+ find_process_pid (wptid.pid ())->dlls_changed = true;
+
+ /* Pass the result to the generic code.
+
+ gdbserver core will absorb this event and convert it into a
+ "stopped" event with GDB_SIGNAL_0.
+
+ However, with dlls_changed set to true, when replying to the
+ client, the message will be overwritten with a libraries changed
+ notification, preventing GDB from actually breaking. */
+ return wptid;
+ case TARGET_WAITKIND_FORKED:
+ if (cs.report_fork_events)
+ {
+ attach_child ();
+ return wptid;
+ }
+ break;
+ case TARGET_WAITKIND_VFORKED:
+ if (cs.report_vfork_events)
+ {
+ attach_child ();
+ return wptid;
+ }
+ break;
+ case TARGET_WAITKIND_VFORK_DONE:
+ if (cs.report_vfork_events)
+ return wptid;
+ break;
+ case TARGET_WAITKIND_EXECD:
+ /* Always report exec events since startup relies on them. */
+ return wptid;
+ case TARGET_WAITKIND_SPURIOUS:
+ /* Spurious events are unhandled by the gdbserver core. */
+ /* Set wptid to -1 to continue waiting from any thread. */
+ wptid = minus_one_ptid;
+ break;
+ case TARGET_WAITKIND_THREAD_CREATED:
+ if (cs.report_thread_events)
+ return wptid;
+ break;
+ case TARGET_WAITKIND_THREAD_EXITED:
+ remove_thread (find_thread_ptid (wptid));
+
+ if (cs.report_thread_events)
+ return wptid;
+
+ /* The thread is dead so we cannot resume the the same wptid. */
+ wptid = ptid;
+ break;
+ default:
+ gdb_assert_not_reached ("Unknown stopped status");
+ }
+
+ haiku_nat_debug_printf ("Event ignored: %s",
+ ourstatus->to_string ().c_str ());
+
+ if (haiku_nat::resume (wptid, resume_continue, 0) < 0)
+ perror_with_name (("haiku_nat::resume"));
+ }
+}
+
+/* Implement the kill target_ops method. */
+
+int
+haiku_process_target::kill (process_info *process)
+{
+ if (haiku_nat::kill (pid_of (process)) < 0)
+ return -1;
+
+ mourn (process);
+ return 0;
+}
+
+/* Implement the detach target_ops method. */
+
+int
+haiku_process_target::detach (process_info *process)
+{
+ if (haiku_nat::detach (pid_of (process)) < 0)
+ return -1;
+
+ mourn (process);
+ return 0;
+}
+
+/* Implement the mourn target_ops method. */
+
+void
+haiku_process_target::mourn (struct process_info *proc)
+{
+ for_each_thread (pid_of (proc), remove_thread);
+
+ remove_process (proc);
+}
+
+/* Implement the join target_ops method. */
+
+void
+haiku_process_target::join (int pid)
+{
+ gdb::handle_eintr (-1, ::waitpid, pid, nullptr, 0);
+}
+
+/* Implement the thread_alive target_ops method. */
+
+bool
+haiku_process_target::thread_alive (ptid_t ptid)
+{
+ return haiku_nat::thread_alive (ptid);
+}
+
+/* Implement the read_memory target_ops method. */
+
+int
+haiku_process_target::read_memory (CORE_ADDR memaddr, unsigned char *myaddr,
+ int size)
+{
+ if (haiku_nat::read_memory (pid_of (current_process ()), memaddr, myaddr,
+ &size)
+ < 0)
+ {
+ haiku_nat_debug_printf ("haiku_nat::read_memory failed: %s",
+ safe_strerror (errno));
+ return errno;
+ }
+ return 0;
+}
+
+/* Implement the write_memory target_ops method. */
+
+int
+haiku_process_target::write_memory (CORE_ADDR memaddr,
+ const unsigned char *myaddr, int size)
+{
+ if (haiku_nat::write_memory (pid_of (current_process ()), memaddr, myaddr,
+ &size)
+ < 0)
+ {
+ haiku_nat_debug_printf ("haiku_nat::write_memory failed: %s",
+ safe_strerror (errno));
+ return errno;
+ }
+ return 0;
+}
+
+/* Implement the request_interrupt target_ops method. */
+
+void
+haiku_process_target::request_interrupt ()
+{
+ thread_info *thread = get_first_thread ();
+
+ if (thread == nullptr)
+ return;
+
+ ::kill (pid_of (thread), SIGINT);
+}
+
+/* Implement the read_offsets target_ops method. */
+
+int
+haiku_process_target::read_offsets (CORE_ADDR *text, CORE_ADDR *data)
+{
+ if (haiku_nat::read_offsets (pid_of (current_process ()), text, data) < 0)
+ return 0;
+ return 1;
+}
+
+/* Implement the qxfer_osdata target_ops method. */
+
+int
+haiku_process_target::qxfer_osdata (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+{
+ if (writebuf != nullptr)
+ return -2;
+ return haiku_common_xfer_osdata (annex, readbuf, offset, len);
+}
+
+/* Implement the async target_ops method. */
+
+bool
+haiku_process_target::async (bool enable)
+{
+ bool previous_enable = haiku_nat::is_async_p ();
+
+ if (previous_enable != enable)
+ {
+ if (enable)
+ {
+ if (haiku_nat::async (true) < 0)
+ warning ("haiku_nat::async failed: %s", safe_strerror (errno));
+ else
+ {
+ add_file_handler (haiku_nat::async_wait_fd (),
+ handle_target_event, NULL, "haiku-low");
+ }
+ }
+ else
+ {
+ /* Unregister this before async_wait_fd gets invalidated. */
+ delete_file_handler (haiku_nat::async_wait_fd ());
+ haiku_nat::async (false);
+ }
+ }
+
+ return previous_enable;
+}
+
+/* Implement the start_non_stop target_ops method. */
+
+int
+haiku_process_target::start_non_stop (bool enable)
+{
+ async (enable);
+
+ if (haiku_nat::is_async_p () != enable)
+ return -1;
+
+ /* TODO: Technically we do NOT support all-stop mode, since we do not lock
+ the whole team when an event occurs. However, not accepting all-stop would
+ result in annoying messages when using the GDB frontend with default
+ configuration. */
+ return 0;
+}
+
+/* Implement the thread_stopped target_ops method. */
+
+bool
+haiku_process_target::thread_stopped (thread_info *thread)
+{
+ return haiku_nat::thread_stopped (ptid_of (thread));
+}
+
+/* Implement the pid_to_exec_file target_ops method. */
+
+const char *
+haiku_process_target::pid_to_exec_file (int pid)
+{
+ return haiku_nat::pid_to_exec_file (pid);
+}
+
+/* Implement the thread_name target_ops method. */
+
+const char *
+haiku_process_target::thread_name (ptid_t thread)
+{
+ return haiku_nat::thread_name (thread);
+}
+
+/* Report supported features. */
+
+bool
+haiku_process_target::supports_qxfer_osdata ()
+{
+ return true;
+}
+
+bool
+haiku_process_target::supports_non_stop ()
+{
+ return true;
+}
+
+bool
+haiku_process_target::supports_multi_process ()
+{
+ return true;
+}
+
+bool
+haiku_process_target::supports_fork_events ()
+{
+ return true;
+}
+
+bool
+haiku_process_target::supports_exec_events ()
+{
+ return true;
+}
+
+bool
+haiku_process_target::supports_read_offsets ()
+{
+ return true;
+}
+
+bool
+haiku_process_target::supports_thread_stopped ()
+{
+ return true;
+}
+
+bool
+haiku_process_target::supports_disable_randomization ()
+{
+ return true;
+}
+
+bool
+haiku_process_target::supports_pid_to_exec_file ()
+{
+ return true;
+}
+
+bool
+haiku_process_target::supports_catch_syscall ()
+{
+ return true;
+}
+
+/* Supply other required functions */
+
+namespace haiku_nat
+{
+
+void
+debugger_output (const char *message)
+{
+ monitor_output (message);
+}
+
+void
+image_created (ptid_t ptid, const image_info &info)
+{
+ haiku_nat_debug_printf ("ptid=%s, name=%s, text=%p",
+ ptid.to_string ().c_str (), info.name,
+ (void *)info.text);
+
+ if (info.is_main_executable)
+ return;
+
+ process_info *process = find_process_pid (ptid.pid ());
+
+ if (process == nullptr)
+ return;
+
+ process->all_dlls.emplace_back (info.name, info.text);
+
+ /* DO NOT set info->dlls_changed here, since gdbserver will clobber an event.
+ Instead, do it in wait after haiku_nat::wait gives a LOADED event. */
+}
+
+void
+image_deleted (ptid_t ptid, const image_info &info)
+{
+ haiku_nat_debug_printf ("ptid=%s, name=%s", ptid.to_string ().c_str (),
+ info.name);
+
+ if (info.is_main_executable)
+ return;
+
+ process_info *process = find_process_pid (ptid.pid ());
+
+ if (process == nullptr)
+ return;
+
+ if (info.name == nullptr)
+ {
+ /* Delete all images. */
+ process->all_dlls.clear ();
+ }
+ else
+ {
+ for (auto it = process->all_dlls.begin ();
+ it != process->all_dlls.end ();)
+ {
+ if (it->name == info.name)
+ {
+ auto next = std::next (it);
+ process->all_dlls.erase (it);
+ it = next;
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ }
+}
+
+bool
+is_catching_syscalls_for (ptid_t ptid)
+{
+ process_info *process = find_process_pid (ptid.pid ());
+
+ if (process == nullptr)
+ return false;
+
+ return !process->syscalls_to_catch.empty ();
+}
+
+}
+
+void
+initialize_low ()
+{
+ set_target_ops (the_haiku_target);
+}
diff --git a/gdbserver/haiku-low.h b/gdbserver/haiku-low.h
new file mode 100644
index 0000000..1167305
--- /dev/null
+++ b/gdbserver/haiku-low.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDBSERVER_HAIKU_LOW_H
+#define GDBSERVER_HAIKU_LOW_H
+
+/* Target ops definitions for a Haiku target. */
+
+class haiku_process_target : public process_stratum_target
+{
+public:
+ int create_inferior (const char *program,
+ const std::vector<char *> &program_args) override;
+
+ void post_create_inferior () override;
+
+ int attach (unsigned long pid) override;
+
+ int kill (process_info *proc) override;
+
+ int detach (process_info *proc) override;
+
+ void mourn (process_info *proc) override;
+
+ void join (int pid) override;
+
+ bool thread_alive (ptid_t pid) override;
+
+ void resume (thread_resume *resume_info, size_t n) override;
+
+ ptid_t wait (ptid_t ptid, target_waitstatus *status,
+ target_wait_flags options) override;
+
+ int read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) override;
+
+ int write_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+ int len) override;
+
+ void request_interrupt () override;
+
+ int read_offsets (CORE_ADDR *text, CORE_ADDR *data) override;
+
+ int qxfer_osdata (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf, CORE_ADDR offset,
+ int len) override;
+
+ bool async (bool enable) override;
+
+ int start_non_stop (bool enable) override;
+
+ bool thread_stopped (thread_info *thread) override;
+
+ const char *pid_to_exec_file (int pid) override;
+
+ const char *thread_name (ptid_t thread) override;
+
+ bool supports_qxfer_osdata () override;
+
+ bool supports_non_stop () override;
+
+ bool supports_multi_process () override;
+
+ bool supports_fork_events () override;
+
+ bool supports_exec_events () override;
+
+ bool supports_read_offsets () override;
+
+ bool supports_thread_stopped () override;
+
+ bool supports_disable_randomization () override;
+
+ bool supports_pid_to_exec_file () override;
+
+ bool supports_catch_syscall () override;
+
+protected:
+ /* The architecture-specific "low" methods are listed below. */
+
+ /* Architecture-specific setup for the current process. */
+ virtual void low_arch_setup (process_info *process = nullptr) = 0;
+};
+
+extern haiku_process_target *the_haiku_target;
+
+#endif /* GDBSERVER_HAIKU_LOW_H */
diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc
index 67225c5..133b4f1 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -111,6 +111,10 @@ static int listen_desc = -1;
# define write(fd, buf, len) send (fd, (char *) buf, len, 0)
#endif
+#if defined(SIGPOLL) && !defined(SIGIO)
+# define SIGIO SIGPOLL
+#endif
+
int
gdb_connected (void)
{
diff --git a/gdbsupport/signals.cc b/gdbsupport/signals.cc
index 2cf641b..be2df0b 100644
--- a/gdbsupport/signals.cc
+++ b/gdbsupport/signals.cc
@@ -333,6 +333,11 @@ gdb_signal_from_host (int hostsig)
return GDB_SIGNAL_LIBRT;
#endif
+#if defined (SIGKILLTHR)
+ if (hostsig == SIGKILLTHR)
+ return GDB_SIGNAL_SIGKILLTHR;
+#endif
+
#if defined (REALTIME_LO)
if (hostsig >= REALTIME_LO && hostsig < REALTIME_HI)
{
@@ -590,6 +595,11 @@ do_gdb_signal_to_host (enum gdb_signal oursig,
return SIGLIBRT;
#endif
+#if defined (SIGKILLTHR)
+ case GDB_SIGNAL_SIGKILLTHR:
+ return SIGKILLTHR;
+#endif
+
default:
#if defined (REALTIME_LO)
retsig = 0;
diff --git a/include/gdb/signals.def b/include/gdb/signals.def
index 677b01e..d21d339 100644
--- a/include/gdb/signals.def
+++ b/include/gdb/signals.def
@@ -196,7 +196,9 @@ SET (GDB_EXC_BREAKPOINT, 150, "EXC_BREAKPOINT", "Breakpoint")
SET (GDB_SIGNAL_LIBRT, 151, "SIGLIBRT", "librt internal signal")
+SET (GDB_SIGNAL_SIGKILLTHR, 152, "SIGKILLTHR", "Thread killed")
+
/* If you are adding a new signal, add it just above this comment. */
/* Last and unused enum value, for sizing arrays, etc. */
-SET (GDB_SIGNAL_LAST, 152, NULL, "GDB_SIGNAL_LAST")
+SET (GDB_SIGNAL_LAST, 153, NULL, "GDB_SIGNAL_LAST")
--
2.51.0
From 3d16f65608526c71104e8bd77d7a57088950fd44 Mon Sep 17 00:00:00 2001
From: Trung Nguyen <me@trungnt2910.com>
Date: Mon, 1 Jul 2024 10:32:51 +0700
Subject: gdb: Initial Haiku support
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6d627b6..509a1af 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -613,6 +613,14 @@ GDB_CFLAGS = \
-DLOCALEDIR="\"$(localedir)\"" \
$(DEFS)
+# Special rule for Haiku-specific files to avoid name clashes.
+nat/haiku-%.o: GDB_CFLAGS = \
+ -I. \
+ -I$(srcdir) \
+ -I$(srcdir)/config \
+ -DLOCALEDIR="\"$(localedir)\"" \
+ $(DEFS)
+
# MH_CFLAGS, if defined, has host-dependent CFLAGS from the config directory.
GLOBAL_CFLAGS = $(MH_CFLAGS)
@@ -739,6 +747,7 @@ ALL_64_TARGET_OBS = \
amd64-darwin-tdep.o \
amd64-dicos-tdep.o \
amd64-fbsd-tdep.o \
+ amd64-haiku-tdep.o \
amd64-linux-tdep.o \
amd64-netbsd-tdep.o \
amd64-obsd-tdep.o \
@@ -821,6 +830,7 @@ ALL_TARGET_OBS = \
ft32-tdep.o \
glibc-tdep.o \
h8300-tdep.o \
+ haiku-tdep.o \
hppa-bsd-tdep.o \
hppa-linux-tdep.o \
hppa-netbsd-tdep.o \
@@ -832,6 +842,7 @@ ALL_TARGET_OBS = \
i386-fbsd-tdep.o \
i386-gnu-tdep.o \
i386-go32-tdep.o \
+ i386-haiku-tdep.o \
i386-linux-tdep.o \
i386-netbsd-tdep.o \
i386-obsd-tdep.o \
@@ -886,6 +897,7 @@ ALL_TARGET_OBS = \
solib-darwin.o \
solib-dsbt.o \
solib-frv.o \
+ solib-haiku.o \
solib-svr4.o \
sparc-linux-tdep.o \
sparc-netbsd-tdep.o \
@@ -1495,6 +1507,7 @@ HFILES_NO_SRCDIR = \
solib.h \
solib-aix.h \
solib-darwin.h \
+ solib-haiku.h \
solib-svr4.h \
solib-target.h \
solist.h \
@@ -1698,6 +1711,8 @@ ALLDEPFILES = \
amd64-dicos-tdep.c \
amd64-fbsd-nat.c \
amd64-fbsd-tdep.c \
+ amd64-haiku-nat.c \
+ amd64-haiku-tdep.c \
amd64-linux-nat.c \
amd64-linux-tdep.c \
amd64-nat.c \
@@ -1738,6 +1753,8 @@ ALLDEPFILES = \
glibc-tdep.c \
go32-nat.c \
h8300-tdep.c \
+ haiku-nat.c \
+ haiku-tdep.c \
hppa-bsd-tdep.c \
hppa-linux-nat.c \
hppa-linux-tdep.c \
@@ -1755,6 +1772,8 @@ ALLDEPFILES = \
i386-fbsd-tdep.c \
i386-gnu-nat.c \
i386-gnu-tdep.c \
+ i386-haiku-nat.c \
+ i386-haiku-tdep.c \
i386-linux-nat.c \
i386-linux-tdep.c \
i386-netbsd-nat.c \
@@ -1848,6 +1867,7 @@ ALLDEPFILES = \
sh-tdep.c \
sol2-tdep.c \
solib-aix.c \
+ solib-haiku.c \
solib-rocm.c \
solib-svr4.c \
sparc-linux-nat.c \
diff --git a/gdb/amd64-haiku-nat.c b/gdb/amd64-haiku-nat.c
new file mode 100644
index 0000000..e622e66
--- /dev/null
+++ b/gdb/amd64-haiku-nat.c
@@ -0,0 +1,153 @@
+/* Native-dependent code for Haiku/amd64.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "amd64-tdep.h"
+#include "haiku-nat.h"
+#include "nat/haiku-nat.h"
+
+/* Very conservative inclusion of Haiku headers to prevent name clashes. */
+typedef uint64_t uint64;
+#include <arch/x86_64/arch_debugger.h>
+
+/* At haiku_amd64_reg_offsets[REGNUM] you'll find the offset in `struct
+ debug_cpu_state' where the GDB register REGNUM is stored. */
+static constexpr auto haiku_amd64_reg_offsets = [] () constexpr {
+ std::array<int, AMD64_NUM_GREGS> result = {};
+
+ /* Set up the register offset table. */
+#define HAIKU_DECLARE_REG_OFFSET(gdbreg, haikureg) \
+ result[AMD64_##gdbreg##_REGNUM] \
+ = offsetof (struct x86_64_debug_cpu_state, haikureg)
+
+ HAIKU_DECLARE_REG_OFFSET (RAX, rax);
+ HAIKU_DECLARE_REG_OFFSET (RBX, rbx);
+ HAIKU_DECLARE_REG_OFFSET (RCX, rcx);
+ HAIKU_DECLARE_REG_OFFSET (RDX, rdx);
+ HAIKU_DECLARE_REG_OFFSET (RSI, rsi);
+ HAIKU_DECLARE_REG_OFFSET (RDI, rdi);
+ HAIKU_DECLARE_REG_OFFSET (RBP, rbp);
+ HAIKU_DECLARE_REG_OFFSET (RSP, rsp);
+ HAIKU_DECLARE_REG_OFFSET (R8, r8);
+ HAIKU_DECLARE_REG_OFFSET (R9, r9);
+ HAIKU_DECLARE_REG_OFFSET (R10, r10);
+ HAIKU_DECLARE_REG_OFFSET (R11, r11);
+ HAIKU_DECLARE_REG_OFFSET (R12, r12);
+ HAIKU_DECLARE_REG_OFFSET (R13, r13);
+ HAIKU_DECLARE_REG_OFFSET (R14, r14);
+ HAIKU_DECLARE_REG_OFFSET (R15, r15);
+ HAIKU_DECLARE_REG_OFFSET (RIP, rip);
+ HAIKU_DECLARE_REG_OFFSET (EFLAGS, rflags);
+ HAIKU_DECLARE_REG_OFFSET (CS, cs);
+ HAIKU_DECLARE_REG_OFFSET (SS, ss);
+ HAIKU_DECLARE_REG_OFFSET (DS, ds);
+ HAIKU_DECLARE_REG_OFFSET (ES, es);
+ HAIKU_DECLARE_REG_OFFSET (FS, fs);
+ HAIKU_DECLARE_REG_OFFSET (GS, gs);
+
+#undef HAIKU_DECLARE_REG_OFFSET
+
+ return result;
+}();
+
+struct amd64_haiku_nat_target final : public haiku_nat_target
+{
+ void fetch_registers (struct regcache *, int) override;
+ void store_registers (struct regcache *, int) override;
+};
+
+void
+amd64_haiku_nat_target::fetch_registers (struct regcache *regcache, int regno)
+{
+ union
+ {
+ char data[sizeof (x86_64_debug_cpu_state)];
+ x86_64_debug_cpu_state state;
+ };
+
+ if (haiku_nat::get_cpu_state (regcache->ptid (), &state) < 0)
+ {
+ /* This happens when the inferior is killed by another process
+ while being stopped. The nub port has been deleted, so we cannot
+ send the required message to get the CPU state. */
+ haiku_nat_debug_printf ("Failed to get actual CPU state: %s",
+ strerror (errno));
+ memset (&state, 0, sizeof (state));
+ }
+
+ if (regno == -1)
+ {
+ for (int i = 0; i < AMD64_NUM_GREGS; ++i)
+ regcache->raw_supply (i, data + haiku_amd64_reg_offsets[i]);
+ amd64_supply_fxsave (regcache, regno, &state.extended_registers);
+ }
+ else
+ {
+ if (regno < AMD64_NUM_GREGS)
+ regcache->raw_supply (regno, data + haiku_amd64_reg_offsets[regno]);
+ else
+ amd64_supply_fxsave (regcache, regno, &state.extended_registers);
+ }
+}
+
+void
+amd64_haiku_nat_target::store_registers (struct regcache *regcache, int regno)
+{
+ union
+ {
+ char data[sizeof (x86_64_debug_cpu_state)];
+ x86_64_debug_cpu_state state;
+ };
+
+ if (haiku_nat::get_cpu_state (regcache->ptid (), &state) < 0)
+ {
+ haiku_nat_debug_printf ("Failed to get actual CPU state: %s",
+ strerror (errno));
+ return;
+ }
+
+ if (regno == -1)
+ {
+ for (int i = 0; i < AMD64_NUM_GREGS; ++i)
+ regcache->raw_collect (i, data + haiku_amd64_reg_offsets[i]);
+ amd64_collect_fxsave (regcache, regno, &state.extended_registers);
+ }
+ else
+ {
+ if (regno < AMD64_NUM_GREGS)
+ regcache->raw_collect (regno, data + haiku_amd64_reg_offsets[regno]);
+ else
+ amd64_collect_fxsave (regcache, regno, &state.extended_registers);
+ }
+
+ if (haiku_nat::set_cpu_state (regcache->ptid (), &state) < 0)
+ perror_with_name (("haiku_nat::set_cpu_state"));
+}
+
+static amd64_haiku_nat_target the_amd64_haiku_nat_target;
+
+void _initialize_amd64_haiku_nat ();
+void
+_initialize_amd64_haiku_nat ()
+{
+ haiku_target = &the_amd64_haiku_nat_target;
+
+ add_inf_child_target (&the_amd64_haiku_nat_target);
+}
diff --git a/gdb/amd64-haiku-tdep.c b/gdb/amd64-haiku-tdep.c
new file mode 100644
index 0000000..086e631
--- /dev/null
+++ b/gdb/amd64-haiku-tdep.c
@@ -0,0 +1,144 @@
+/* Target-dependent code for Haiku/amd64.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "amd64-tdep.h"
+#include "extract-store-integer.h"
+#include "haiku-tdep.h"
+#include "solib.h"
+
+static int
+amd64_haiku_sigtramp_p (const frame_info_ptr &this_frame)
+{
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ const char *solib_name
+ = solib_name_from_address (get_frame_program_space (this_frame), pc);
+
+ if (solib_name == nullptr || strcmp (solib_name, "commpage") != 0)
+ return false;
+
+ const char *name;
+ find_pc_partial_function (pc, &name, NULL, NULL);
+
+ if (name == nullptr || strcmp (name, "commpage_signal_handler") != 0)
+ return false;
+
+ return true;
+}
+
+/* Offset to mcontext_t in signal_frame_data,
+ from headers/private/kernel/ksignal.h.
+
+ The struct is private so it may change anytime.
+ However, the first two members of the struct are siginfo_t and ucontext_t,
+ which are public and relatively stable. */
+#define AMD64_HAIKU_SIGNAL_FRAME_DATA_MCONTEXT_OFFSET 96
+
+static CORE_ADDR
+amd64_haiku_sigcontext_addr (const frame_info_ptr &this_frame)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ CORE_ADDR bp;
+ gdb_byte buf[8];
+
+ get_frame_register (this_frame, AMD64_RBP_REGNUM, buf);
+ bp = extract_unsigned_integer (buf, 8, byte_order);
+
+ /* Layout of the stack before function call:
+ - signal_frame_data
+ - frame->ip (8 bytes)
+ - frame->bp (8 bytes). Not written by the kernel,
+ but the signal handler has a "push %rbp" instruction. */
+ return bp + 8 + 8 + AMD64_HAIKU_SIGNAL_FRAME_DATA_MCONTEXT_OFFSET;
+}
+
+/* From struct vregs at arch/x86_64/signal.h. */
+static int amd64_haiku_sc_reg_offset[] = {
+ 0 * 8, /* %rax */
+ 1 * 8, /* %rbx */
+ 2 * 8, /* %rcx */
+ 3 * 8, /* %rdx */
+ 5 * 8, /* %rsi */
+ 4 * 8, /* %rdi */
+ 6 * 8, /* %rbp */
+ 15 * 8, /* %rsp */
+ 7 * 8, /* %r8 */
+ 8 * 8, /* %r9 */
+ 9 * 8, /* %r10 */
+ 10 * 8, /* %r11 */
+ 11 * 8, /* %r12 */
+ 12 * 8, /* %r13 */
+ 13 * 8, /* %r14 */
+ 14 * 8, /* %r15 */
+ 16 * 8, /* %rip */
+ 17 * 8, /* %eflags */
+
+ -1, /* %cs */
+ -1, /* %ss */
+ -1, /* %ds */
+ -1, /* %es */
+ -1, /* %fs */
+ -1 /* %gs */
+};
+
+static void
+amd64_haiku_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+
+ amd64_init_abi (info, gdbarch,
+ amd64_target_description (X86_XSTATE_SSE_MASK, true));
+ haiku_init_abi (info, gdbarch);
+
+ tdep->sigtramp_p = amd64_haiku_sigtramp_p;
+ tdep->sigcontext_addr = amd64_haiku_sigcontext_addr;
+ tdep->sc_reg_offset = amd64_haiku_sc_reg_offset;
+ tdep->sc_num_regs = ARRAY_SIZE (amd64_haiku_sc_reg_offset);
+
+ /* The offset of the PC in the jmp_buf structure.
+ Found at src/system/libroot/posix/arch/x86_64/setjmp_internal.h. */
+ tdep->jb_pc_offset = 0;
+}
+
+static enum gdb_osabi
+amd64_haiku_osabi_sniffer (bfd *abfd)
+{
+ const char *target_name = bfd_get_target (abfd);
+
+ if (strcmp (target_name, "elf64-x86-64") != 0)
+ return GDB_OSABI_UNKNOWN;
+
+ if (!haiku_check_required_symbols (abfd))
+ return GDB_OSABI_UNKNOWN;
+
+ return GDB_OSABI_HAIKU;
+}
+
+void _initialize_amd64_haiku_tdep ();
+void
+_initialize_amd64_haiku_tdep ()
+{
+ gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour,
+ amd64_haiku_osabi_sniffer);
+
+ gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, GDB_OSABI_HAIKU,
+ amd64_haiku_init_abi);
+}
diff --git a/gdb/configure b/gdb/configure
index 1531f62..4ead228 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -20016,7 +20016,7 @@ return socketpair ();
return 0;
}
_ACEOF
-for ac_lib in '' socket; do
+for ac_lib in '' socket network; do
if test -z "$ac_lib"; then
ac_res="none required"
else
diff --git a/gdb/configure.host b/gdb/configure.host
index 22855cd..52fb68b 100644
--- a/gdb/configure.host
+++ b/gdb/configure.host
@@ -111,6 +111,7 @@ i[34567]86-*-msdosdjgpp*) gdb_host=go32 ;;
i[34567]86-*-linux*) gdb_host=linux ;;
i[34567]86-*-gnu*) gdb_host=i386gnu ;;
i[34567]86-*-openbsd*) gdb_host=obsd ;;
+i[34567]86-*-haiku*) gdb_host=haiku ;;
i[34567]86-*-solaris2* | x86_64-*-solaris2*)
gdb_host=sol2 ;;
i[34567]86-*-cygwin*) gdb_host=cygwin ;;
@@ -179,6 +180,7 @@ x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu)
x86_64-*-netbsd* | x86_64-*-knetbsd*-gnu)
gdb_host=nbsd64 ;;
x86_64-*-openbsd*) gdb_host=obsd64 ;;
+x86_64-*-haiku*) gdb_host=haiku64 ;;
x86_64-*-mingw*) gdb_host=mingw64
gdb_host_obs=mingw-hdep.o
;;
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 9e78091..fffac27 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -74,6 +74,11 @@ case ${gdb_host} in
obsd*)
NATDEPFILES='fork-child.o nat/fork-inferior.o inf-ptrace.o'
;;
+ haiku*)
+ NATDEPFILES='fork-child.o nat/fork-inferior.o \
+ nat/haiku-debug.o nat/haiku-nat.o nat/haiku-nub-message.o \
+ nat/haiku-osdata.o haiku-nat.o'
+ ;;
cygwin*)
NATDEPFILES='x86-nat.o nat/x86-dregs.o windows-nat.o nat/windows-nat.o'
;;
@@ -475,6 +480,26 @@ case ${gdb_host} in
;;
esac
;;
+ haiku)
+ case ${gdb_host_cpu} in
+ i386)
+ # Host: Haiku/i386 ELF
+ NATDEPFILES="${NATDEPFILES} i386-haiku-nat.o"
+ LOADLIBES='-lnetwork -lposix_error_mapper'
+ MH_CFLAGS='-DB_USE_POSITIVE_POSIX_ERRORS'
+ ;;
+ esac
+ ;;
+ haiku64)
+ case ${gdb_host_cpu} in
+ i386)
+ # Host: Haiku/amd64
+ NATDEPFILES="${NATDEPFILES} amd64-haiku-nat.o"
+ LOADLIBES='-lnetwork -lposix_error_mapper'
+ MH_CFLAGS='-DB_USE_POSITIVE_POSIX_ERRORS'
+ ;;
+ esac
+ ;;
ppc64-linux)
case ${gdb_host_cpu} in
powerpc)
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index f7b9e32..eb65890 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -128,6 +128,8 @@ case "${targ}" in
os_obs="netbsd-tdep.o solib-svr4.o";;
*-*-openbsd*)
os_obs="obsd-tdep.o solib-svr4.o";;
+*-*-haiku*)
+ os_obs="haiku-tdep.o solib-haiku.o symfile-mem.o";;
esac
# 3. Get the rest of objects.
@@ -305,6 +307,10 @@ i[34567]86-*-openbsd*)
# Target: OpenBSD/i386
gdb_target_obs="i386-bsd-tdep.o i386-obsd-tdep.o bsd-uthread.o"
;;
+i[34567]86-*-haiku*)
+ # Target: Haiku/i386
+ gdb_target_obs="i386-haiku-tdep.o"
+ ;;
i[34567]86-*-solaris2* | x86_64-*-solaris2*)
# Target: Solaris x86_64
gdb_target_obs="${i386_tobjs} ${amd64_tobjs} \
@@ -731,6 +737,11 @@ x86_64-*-openbsd*)
i386-bsd-tdep.o i386-obsd-tdep.o \
bsd-uthread.o"
;;
+x86_64-*-haiku*)
+ # Target: Haiku/amd64
+ gdb_target_obs="amd64-haiku-tdep.o ${i386_tobjs} \
+ i386-haiku-tdep.o"
+ ;;
x86_64-*-rtems*)
gdb_target_obs="${amd64_tobjs} ${i386_tobjs} i386-bsd-tdep.o"
;;
diff --git a/gdb/haiku-nat.c b/gdb/haiku-nat.c
new file mode 100644
index 0000000..3faab24
--- /dev/null
+++ b/gdb/haiku-nat.c
@@ -0,0 +1,778 @@
+/* Native-dependent code for Haiku.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "inferior.h"
+
+#include "cli/cli-cmds.h"
+#include "exec.h"
+#include "gdb/inf-loop.h"
+#include "gdbcore.h"
+#include "gdbsupport/buildargv.h"
+#include "gdbsupport/event-loop.h"
+#include "haiku-nat.h"
+#include "nat/fork-inferior.h"
+#include "nat/haiku-nat.h"
+#include "nat/haiku-osdata.h"
+#include "objfiles.h"
+#include "observable.h"
+#include "solib.h"
+
+haiku_nat_target *haiku_target;
+
+bool debug_haiku_nat = false;
+
+static void haiku_enable_breakpoints_if_ready (inferior *inf);
+
+void
+haiku_nat_target::create_inferior (const char *exec_file,
+ const std::string &allargs, char **env,
+ int from_tty)
+{
+ haiku_nat_debug_printf ("exec_file=%s", exec_file);
+
+ inferior *inf = current_inferior ();
+
+ /* Do not change either targets above or the same target if already present.
+ The reason is the target stack is shared across multiple inferiors. */
+ int ops_already_pushed = inf->target_is_pushed (this);
+
+ target_unpush_up unpusher;
+ if (!ops_already_pushed)
+ {
+ /* Clear possible core file with its process_stratum. */
+ inf->push_target (this);
+ unpusher.reset (this);
+ }
+
+ if (disable_randomization)
+ inf->environment.set ("DISABLE_ASLR", "1");
+
+ static const auto haiku_traceme = [] () {
+ /* This happens before the child calls exec().
+ The debugger is responsible for resuming the inferior before it
+ loads the desired target. */
+ haiku_nat::wait_for_debugger ();
+ };
+
+ static const auto haiku_init_trace = [] (int pid) {
+ haiku_nat_debug_printf ("haiku_init_trace: pid=%i", pid);
+ if (haiku_nat::attach (pid, true) < 0)
+ trace_start_error_with_name (("haiku_nat::attach"));
+
+ /* At this stage, the child is being stopped for the first debugger event.
+ It has NOT exec'ed into the desired target yet, but is still a gdbserver
+ stuck in a wait_for_debugger() call. */
+
+ /* Consume the initial event. */
+ target_waitstatus ourstatus;
+ if (haiku_nat::wait (ptid_t (pid), &ourstatus, 0) == minus_one_ptid)
+ perror_with_name (("haiku_nat::wait"));
+
+ /* Allows the child to proceed to exec. */
+ if (haiku_nat::resume (ptid_t (pid), resume_continue, 0) < 0)
+ perror_with_name (("haiku_nat::continue_process"));
+ };
+
+ /* Do not use env here, the pointer might have been invalidated. */
+ pid_t pid = fork_inferior (exec_file, allargs, inf->environment.envp (),
+ haiku_traceme, haiku_init_trace, nullptr, nullptr,
+ nullptr);
+
+ /* We have something that executes now. We'll be running through
+ the shell at this point (if startup-with-shell is true), but the
+ pid shouldn't change. */
+ thread_info *thr = add_thread_silent (this, ptid_t (pid, 0, pid));
+ switch_to_thread (thr);
+
+ unpusher.release ();
+
+ disable_breakpoints_before_startup ();
+
+ gdb_startup_inferior (pid, START_INFERIOR_TRAPS_EXPECTED);
+
+ /* Don't wait for the callbacks. Here, we know that the inferior has exec'ed
+ into the requested image. If we wait further, post_create_inferior will
+ perform lots of operations that interally triggers breakpoint_re_set,
+ which ignores the executing_startup flag. */
+ haiku_enable_breakpoints_if_ready (inf);
+}
+
+void
+haiku_nat_target::attach (const char *args, int from_tty)
+{
+ inferior *inf = current_inferior ();
+
+ /* Do not change either targets above or the same target if already present.
+ The reason is the target stack is shared across multiple inferiors. */
+ int ops_already_pushed = inf->target_is_pushed (this);
+
+ pid_t pid = parse_pid_to_attach (args);
+
+ if (pid == getpid ()) /* Trying to masturbate? */
+ error (_ ("I refuse to debug myself!"));
+
+ target_unpush_up unpusher;
+ if (!ops_already_pushed)
+ {
+ /* target_pid_to_str already uses the target. Also clear possible core
+ file with its process_stratum. */
+ inf->push_target (this);
+ unpusher.reset (this);
+ }
+
+ target_announce_attach (from_tty, pid);
+
+ if (haiku_nat::attach (pid, false) < 0)
+ perror_with_name (("haiku_nat::attach"));
+
+ inferior_appeared (inf, pid);
+ inf->attach_flag = true;
+
+ const haiku_nat::team_info *team_info = haiku_nat::get_team (pid);
+ gdb_assert (team_info != nullptr);
+
+ haiku_nat::for_each_thread (pid, [&] (const haiku_nat::thread_info &info) {
+ if (info.tid == team_info->debugger_nub_thread)
+ return 0;
+
+ thread_info *thr = add_thread (this, ptid_t (pid, 0, info.tid));
+ /* Don't consider the thread stopped until we've processed its
+ initial stop. */
+ set_executing (this, thr->ptid, true);
+
+ if (info.tid == info.team)
+ switch_to_thread (thr);
+
+ return 0;
+ });
+
+ gdb_assert (inferior_ptid != null_ptid);
+
+ unpusher.release ();
+}
+
+void
+haiku_nat_target::detach (inferior *inf, int from_tty)
+{
+ target_announce_detach (from_tty);
+
+ if (haiku_nat::detach (inf->pid) < 0)
+ perror ("haiku_nat::detach");
+
+ switch_to_no_thread ();
+ detach_inferior (inf);
+
+ maybe_unpush_target ();
+}
+
+void
+haiku_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signal)
+{
+ if (haiku_nat::resume (ptid, step ? resume_step : resume_continue,
+ gdb_signal_to_host (signal))
+ < 0)
+ perror_with_name ("haiku_nat_target::resume");
+}
+
+ptid_t
+haiku_nat_target::wait (ptid_t ptid, target_waitstatus *ourstatus,
+ target_wait_flags target_options)
+{
+ haiku_nat_debug_printf (
+ "ptid=%s, ourstatus=%s, target_options=%i", ptid.to_string ().c_str (),
+ ourstatus->to_string ().c_str (), (int)target_options.raw ());
+
+ ptid_t wptid = haiku_nat::wait (ptid, ourstatus, target_options);
+
+ if (wptid == minus_one_ptid)
+ perror_with_name (("haiku_nat::wait"));
+
+ if (wptid.tid () != 0 && !find_thread (wptid)
+ && ourstatus->kind () != TARGET_WAITKIND_THREAD_EXITED)
+ add_thread (this, wptid);
+
+ invalidate_target_mem_regions ();
+
+ return wptid;
+}
+
+void
+haiku_nat_target::files_info ()
+{
+ struct inferior *inf = current_inferior ();
+
+ gdb_printf (_ ("\tUsing the running image of %s %s.\n"),
+ inf->attach_flag ? "attached" : "child",
+ target_pid_to_str (ptid_t (inf->pid)).c_str ());
+}
+
+void
+haiku_nat_target::kill ()
+{
+ if (haiku_nat::kill (inferior_ptid.pid ()) < 0)
+ {
+ haiku_nat_debug_printf ("Failed to actually kill the process: %s",
+ safe_strerror (errno));
+ }
+
+ target_mourn_inferior (inferior_ptid);
+}
+
+void
+haiku_nat_target::follow_exec (inferior *follow_inf, ptid_t ptid,
+ const char *execd_pathname)
+{
+ inf_child_target::follow_exec (follow_inf, ptid, execd_pathname);
+
+ /* nat/haiku-nat.c currently does not report the EXEC event when
+ the corresponding native event is generated, but after the
+ main executable image has been loaded.
+
+ This means when the event is generated, all initial shared
+ libraries have been registered. However, GDB treats the
+ EXEC event as if the program has a clean address space and
+ nukes the solib list and loaded symbols.
+
+ We therefore call the below function to force GDB to load
+ the needed symbols again. */
+ handle_solib_event ();
+
+ invalidate_target_mem_regions ();
+}
+
+bool
+haiku_nat_target::thread_alive (ptid_t ptid)
+{
+ return haiku_nat::thread_alive (ptid);
+}
+
+void
+haiku_nat_target::update_thread_list ()
+{
+ delete_exited_threads ();
+}
+
+std::string
+haiku_nat_target::pid_to_str (ptid_t ptid)
+{
+ return haiku_nat::pid_to_str (ptid);
+}
+
+const char *
+haiku_nat_target::thread_name (thread_info *thr)
+{
+ return haiku_nat::thread_name (thr->ptid);
+}
+
+void
+haiku_nat_target::stop (ptid_t ptid)
+{
+ if (haiku_nat::stop (ptid) < 0)
+ perror_with_name ("haiku_nat::stop");
+}
+
+const char *
+haiku_nat_target::pid_to_exec_file (int pid)
+{
+ return haiku_nat::pid_to_exec_file (pid);
+}
+
+bool
+haiku_nat_target::can_async_p ()
+{
+ return true;
+}
+
+bool
+haiku_nat_target::is_async_p ()
+{
+ return haiku_nat::is_async_p ();
+}
+
+void
+haiku_nat_target::async (bool enable)
+{
+ if (enable == is_async_p ())
+ return;
+
+ if (enable)
+ {
+ if (haiku_nat::async (true) < 0)
+ perror_with_name ("haiku_nat::async");
+ else
+ {
+ add_file_handler (
+ haiku_nat::async_wait_fd (),
+ [] (int error, gdb_client_data client_data) {
+ inferior_event_handler (INF_REG_EVENT);
+ },
+ nullptr, "haiku-nat");
+ }
+ }
+ else
+ {
+ /* Unregister this before async_wait_fd gets invalidated. */
+ delete_file_handler (haiku_nat::async_wait_fd ());
+ haiku_nat::async (false);
+ }
+}
+
+int
+haiku_nat_target::async_wait_fd ()
+{
+ return haiku_nat::async_wait_fd ();
+}
+
+bool
+haiku_nat_target::supports_non_stop ()
+{
+ return true;
+}
+
+bool
+haiku_nat_target::always_non_stop_p ()
+{
+ return true;
+}
+
+enum target_xfer_status
+haiku_nat_target::xfer_partial (enum target_object object, const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len)
+{
+ ptid_t ptid = inferior_ptid;
+
+ switch (object)
+ {
+ case TARGET_OBJECT_MEMORY:
+ {
+ int sizeLeft = std::min ((ULONGEST)INT_MAX, len);
+
+ if (writebuf != nullptr)
+ std::ignore = haiku_nat::write_memory (
+ ptid.pid (), (CORE_ADDR)offset, writebuf, &sizeLeft);
+ else
+ std::ignore = haiku_nat::read_memory (ptid.pid (), (CORE_ADDR)offset,
+ readbuf, &sizeLeft);
+
+ *xfered_len = std::min ((ULONGEST)INT_MAX, len) - sizeLeft;
+
+ return (*xfered_len > 0) ? TARGET_XFER_OK : TARGET_XFER_EOF;
+ }
+ break;
+ case TARGET_OBJECT_LIBRARIES:
+ {
+ if (writebuf != nullptr)
+ return TARGET_XFER_UNAVAILABLE;
+
+ if (current_inferior () == nullptr)
+ return TARGET_XFER_E_IO;
+
+ std::string document = "<library-list version=\"1.0\">\n";
+ haiku_nat::for_each_image (
+ current_inferior ()->pid, [&] (const haiku_nat::image_info &info) {
+ if (!info.is_main_executable)
+ {
+ document += string_printf (
+ " <library name=\"%s\">"
+ "<segment address=\"%s\"/></library>\n",
+ info.name,
+ paddress (current_inferior ()->arch (), info.text));
+ }
+ return 0;
+ });
+ document += "</library-list>\n";
+
+ if (offset >= document.size ())
+ return TARGET_XFER_EOF;
+
+ len = std::min (len, document.size () - offset);
+ memcpy (readbuf, document.c_str () + offset, len);
+
+ *xfered_len = len;
+
+ return TARGET_XFER_OK;
+ }
+ break;
+ case TARGET_OBJECT_OSDATA:
+ {
+ if (writebuf != nullptr)
+ return TARGET_XFER_UNAVAILABLE;
+
+ *xfered_len = haiku_common_xfer_osdata (annex, readbuf, offset, len);
+
+ return (*xfered_len > 0) ? TARGET_XFER_OK : TARGET_XFER_EOF;
+ }
+ default:
+ haiku_nat_debug_printf ("Unimplemented xfer object: %i", (int)object);
+ }
+
+ return inf_child_target::xfer_partial (object, annex, readbuf, writebuf,
+ offset, len, xfered_len);
+}
+
+std::vector<mem_region>
+haiku_nat_target::memory_map ()
+{
+ std::vector<mem_region> result;
+
+ haiku_nat::for_each_area (
+ current_inferior ()->pid, [&] (const haiku_nat::area_info &info) {
+ /* While some regions appear read-only to the user,
+ as the debugger, we can write anywhere.
+
+ If this is set otherwise, software breakpoints in read-only
+ regions (such as shared libraries) will not work. */
+ result.emplace_back (info.address, info.address + info.size, MEM_RW);
+ return 0;
+ });
+
+ return result;
+}
+
+bool
+haiku_nat_target::supports_multi_process ()
+{
+ return true;
+}
+
+bool
+haiku_nat_target::supports_disable_randomization ()
+{
+ return true;
+}
+
+bool
+haiku_nat_target::info_proc (const char *args, enum info_proc_what what)
+{
+ pid_t pid;
+ bool do_cmdline = false;
+ bool do_exe = false;
+ bool do_mappings = false;
+ bool do_status = false;
+
+ switch (what)
+ {
+ case IP_MINIMAL:
+ do_cmdline = true;
+ do_exe = true;
+ break;
+ case IP_STAT:
+ case IP_STATUS:
+ do_status = true;
+ break;
+ case IP_MAPPINGS:
+ do_mappings = true;
+ break;
+ case IP_CMDLINE:
+ do_cmdline = true;
+ break;
+ case IP_EXE:
+ do_exe = true;
+ break;
+ case IP_CWD:
+ /* There is no obvious method of getting the CWD of a different team.
+ _kern_get_extended_team_info might provide what we want, but the
+ syscall stores the result in a private class "KMessage" instead of
+ normal structs. */
+ return false;
+ case IP_ALL:
+ do_cmdline = true;
+ do_exe = true;
+ do_mappings = true;
+ do_status = true;
+ break;
+ default:
+ error (_ ("Not supported on this target."));
+ }
+
+ gdb_argv built_argv (args);
+ if (built_argv.count () == 0)
+ {
+ pid = inferior_ptid.pid ();
+ if (pid == 0)
+ error (_ ("No current team: you must name one."));
+ }
+ else if (built_argv.count () == 1 && isdigit (built_argv[0][0]))
+ pid = strtol (built_argv[0], NULL, 10);
+ else
+ error (_ ("Invalid arguments."));
+
+ gdb_printf (_ ("team %d\n"), pid);
+
+ const haiku_nat::team_info *info = nullptr;
+
+ if (do_cmdline || do_status)
+ info = haiku_nat::get_team (pid);
+
+ if (do_cmdline)
+ {
+ if (info != nullptr)
+ gdb_printf ("cmdline = '%s'\n", info->args);
+ else
+ warning (_ ("unable to fetch command line"));
+ }
+
+ if (do_exe)
+ {
+ const char *exe = pid_to_exec_file (pid);
+ if (exe != nullptr)
+ gdb_printf ("exe = '%s'\n", exe);
+ else
+ warning (_ ("unable to fetch executable path name"));
+ }
+
+ if (do_mappings)
+ {
+ bool first = true;
+ if (haiku_nat::for_each_area (
+ pid,
+ [&] (const haiku_nat::area_info &area_info) {
+ if (first)
+ {
+ gdb_printf (_ ("Mapped areas:\n\n"));
+ gdb_printf ("%6s %18s %10s %10s %6s %6s %5s %5s %s\n",
+ "ID", "address", "size", "alloc.", "prot",
+ "#-cow", "#-in", "#-out", "name");
+ first = false;
+ }
+
+ std::string prot;
+ if (area_info.can_read)
+ prot += "r";
+ if (area_info.can_write)
+ prot += "w";
+ if (area_info.can_exec)
+ prot += "x";
+ if (area_info.is_stack)
+ prot += "s";
+ if (area_info.can_clone)
+ prot += "c";
+
+ gdb_printf ("%6s %18s %10s %10s %6s %6s %5s %5s %s\n",
+ plongest (area_info.id),
+ core_addr_to_string (area_info.address),
+ phex_nz (area_info.size, 0),
+ phex_nz (area_info.ram_size, 0), prot.c_str (),
+ pulongest (area_info.copy_count),
+ pulongest (area_info.in_count),
+ pulongest (area_info.out_count), area_info.name);
+
+ return 0;
+ })
+ < 0)
+ {
+ warning (_ ("unable to fetch virtual memory map"));
+ }
+ }
+
+ if (do_status)
+ {
+ if (info != nullptr)
+ {
+ gdb_printf ("Name: %s\n", info->name);
+ gdb_printf ("Parent team: %s\n", plongest (info->parent));
+ gdb_printf ("Process group: %s\n", plongest (info->group_id));
+ gdb_printf ("Session id: %s\n", plongest (info->session_id));
+ gdb_printf ("User IDs (real, effective): %s %s\n",
+ plongest (info->real_uid), plongest (info->uid));
+ gdb_printf ("Group IDs (real, effective): %s %s\n",
+ plongest (info->real_gid), plongest (info->gid));
+ gdb_printf ("Thread count: %s\n", pulongest (info->thread_count));
+ gdb_printf ("Image count: %s\n", pulongest (info->image_count));
+ gdb_printf ("Area count: %s\n", pulongest (info->area_count));
+ gdb_printf ("Debugger nub thread: %s\n",
+ plongest (info->debugger_nub_thread));
+ gdb_printf ("Debugger nub port: %s\n",
+ plongest (info->debugger_nub_port));
+ }
+ else
+ warning (_ ("unable to fetch team information"));
+ }
+
+ return true;
+}
+
+/* Utilities. */
+
+static void
+haiku_relocate_main_executable (inferior *inf)
+{
+ CORE_ADDR text;
+ CORE_ADDR data;
+
+ if (haiku_nat::read_offsets (inf->pid, &text, &data) < 0)
+ return;
+
+ CORE_ADDR displacement = text;
+
+ if (inf->pspace->exec_bfd ())
+ {
+ asection *asect;
+
+ bfd *exec_bfd = inf->pspace->exec_bfd ();
+ for (asect = exec_bfd->sections; asect != NULL; asect = asect->next)
+ exec_set_section_address (bfd_get_filename (exec_bfd), asect->index,
+ bfd_section_vma (asect) + displacement);
+ }
+
+ if (inf->pspace->symfile_object_file == nullptr)
+ symbol_file_add_main (inf->pspace->exec_filename.get (),
+ SYMFILE_DEFER_BP_RESET);
+
+ objfile *objf = inf->pspace->symfile_object_file;
+ /* The call above should ensure that this is filled in. */
+ gdb_assert (objf != nullptr);
+ objfile_rebase (objf, displacement);
+
+ haiku_nat_debug_printf ("rebased: %s", core_addr_to_string (displacement));
+}
+
+static void
+haiku_enable_breakpoints_if_ready (inferior *inf)
+{
+ if (strcmp (haiku_nat::pid_to_exec_file (inf->pid),
+ inf->pspace->exec_filename.get ())
+ != 0)
+ {
+ /* Not ready yet. The inferior is still executing a wrapper
+ (usually bash). */
+ return;
+ }
+
+ /* Refresh the regions so that write operations can be done correctly. */
+ invalidate_target_mem_regions ();
+
+ /* We can get correct offsets and relocate now. */
+ haiku_relocate_main_executable (inf);
+
+ enable_breakpoints_after_startup ();
+}
+
+/* Supply other required functions. */
+
+namespace haiku_nat
+{
+
+void
+debugger_output (const char *message)
+{
+ gdb_printf ("%s\n", message);
+}
+
+void
+image_created (ptid_t ptid, const image_info &info)
+{
+ haiku_nat_debug_printf ("ptid=%s, name=%s, text=%p",
+ ptid.to_string ().c_str (), info.name,
+ (void *)info.text);
+
+ /* To be handled by solib-haiku.c. */
+}
+
+void
+image_deleted (ptid_t ptid, const image_info &info)
+{
+ haiku_nat_debug_printf ("ptid=%s, name=%s", ptid.to_string ().c_str (),
+ info.name);
+
+ if (info.is_main_executable)
+ {
+ /* This means all images have been deleted. This usually signals that
+ the Haiku team just called exec.
+
+ We want to disable breakpoints for now to prevent those pointing to
+ the main executable from causing issues with unrelocated addresses.
+ Then, after the creation or exec call completes and the new inferior
+ gets finalized, we can relocate and enable these breakpoints again.
+
+ We also cannot disable the breakpoints later than this. After the
+ event, images for the new executable starts loading. Disabling the
+ breakpoints causes GDB to write bogus data back to the fresh
+ binaries. */
+
+ disable_breakpoints_before_startup ();
+ invalidate_target_mem_regions ();
+ }
+
+ /* The rest to be handled by solib-haiku.c. */
+}
+
+bool
+is_catching_syscalls_for (ptid_t ptid)
+{
+ inferior *inf = find_inferior_ptid (haiku_target, ptid);
+ if (inf == nullptr)
+ return false;
+
+ std::optional<scoped_restore_current_thread> maybe_restore_thread
+ = maybe_switch_inferior (inf);
+
+ return catch_syscall_enabled () > 0;
+}
+
+}
+
+/* Initialization. */
+
+void _initialize_haiku_nat ();
+void
+_initialize_haiku_nat ()
+{
+ /* We cannot do this in target_op's own callbacks, since they are called too
+ early after attaching or an exec event. At that point, symfile_object_file
+ remains invalid.
+
+ Previous ports put this in Haiku's solib_create_inferior_hook callback.
+ However, this callback is also shared by remote targets and therefore
+ assumes gathering information from the address space instead of the host
+ OS, which is what haiku_nat::read_offsets does under the hood. With the
+ old implementation, GDB connected to gdbserver debugging PID X on the
+ target would attempt to use haiku_nat::read_offsets on the same PID X
+ on the local machine - this is undesired. */
+
+ gdb::observers::inferior_created.attach (
+ [] (inferior *inf) {
+ if (inf->target_is_pushed (haiku_target))
+ haiku_enable_breakpoints_if_ready (inf);
+ },
+ "haiku");
+
+ gdb::observers::inferior_execd.attach (
+ [] (inferior *exec, inferior *foll) {
+ if (foll->target_is_pushed (haiku_target))
+ haiku_enable_breakpoints_if_ready (foll);
+ },
+ "haiku");
+
+ add_setshow_boolean_cmd (
+ "haiku-nat", class_maintenance, &debug_haiku_nat,
+ _ ("Set debugging of Haiku native target."),
+ _ ("Show debugging of Haiku native target."), _ ("\
+When on, print debug messages relating to the Haiku native target."),
+ nullptr,
+ [] (struct ui_file *file, int from_tty, struct cmd_list_element *c,
+ const char *value) {
+ gdb_printf (file, _ ("Debugging of Haiku native targets is %s.\n"),
+ value);
+ },
+ &setdebuglist, &showdebuglist);
+}
diff --git a/gdb/haiku-nat.h b/gdb/haiku-nat.h
new file mode 100644
index 0000000..6e900fb
--- /dev/null
+++ b/gdb/haiku-nat.h
@@ -0,0 +1,75 @@
+/* Native-dependent code for Haiku.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "inf-child.h"
+
+/* A prototype Haiku target. */
+
+struct haiku_nat_target : public inf_child_target
+{
+ void create_inferior (const char *, const std::string &, char **,
+ int) override;
+
+ void attach (const char *, int) override;
+
+ void detach (inferior *, int) override;
+
+ void resume (ptid_t, int, enum gdb_signal) override;
+
+ ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
+
+ void files_info () override;
+
+ void kill () override;
+
+ void follow_exec (inferior *, ptid_t, const char *) override;
+
+ bool thread_alive (ptid_t) override;
+ void update_thread_list () override;
+ std::string pid_to_str (ptid_t) override;
+
+ const char *thread_name (thread_info *) override;
+
+ void stop (ptid_t) override;
+
+ const char *pid_to_exec_file (int) override;
+
+ bool can_async_p () override;
+ bool is_async_p () override;
+ void async (bool) override;
+ int async_wait_fd () override;
+
+ bool supports_non_stop () override;
+ bool always_non_stop_p () override;
+
+ enum target_xfer_status xfer_partial (enum target_object, const char *,
+ gdb_byte *, const gdb_byte *, ULONGEST,
+ ULONGEST, ULONGEST *) override;
+
+ std::vector<mem_region> memory_map () override;
+
+ bool supports_multi_process () override;
+
+ bool supports_disable_randomization () override;
+
+ bool info_proc (const char *, enum info_proc_what) override;
+};
+
+/* The final/concrete instance. */
+extern haiku_nat_target *haiku_target;
diff --git a/gdb/haiku-tdep.c b/gdb/haiku-tdep.c
new file mode 100644
index 0000000..3b866a5
--- /dev/null
+++ b/gdb/haiku-tdep.c
@@ -0,0 +1,193 @@
+/* Common target-dependent code for Haiku systems.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "bfd.h"
+#include "elf-bfd.h"
+#include "gdbarch.h"
+#include "haiku-tdep.h"
+#include "inferior.h"
+#include "osdata.h"
+#include "solib-haiku.h"
+
+/* See haiku-tdep.h. */
+
+void
+haiku_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ set_gdbarch_so_ops (gdbarch, &haiku_so_ops);
+}
+
+/* See haiku-tdep.h. */
+
+bool
+haiku_check_required_symbols (bfd *abfd)
+{
+ long storage_needed = bfd_get_symtab_upper_bound (abfd);
+ if (storage_needed <= 0)
+ return false;
+
+ gdb::unique_xmalloc_ptr<asymbol *> symbol_table (
+ (asymbol **)xmalloc (storage_needed));
+ long number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table.get ());
+
+ if (number_of_symbols <= 0)
+ return false;
+
+ for (long i = 0; i < number_of_symbols; ++i)
+ {
+ const char *name = bfd_asymbol_name (symbol_table.get ()[i]);
+
+ if (strcmp (name, "_gSharedObjectHaikuVersion") == 0)
+ return true;
+ }
+
+ return false;
+}
+
+/* See haiku-tdep.h. */
+
+gdb_bfd_ref_ptr
+haiku_bfd_open_commpage ()
+{
+ /* Get any valid BFD object as a template.
+ Otherwise, GDB will complain with a segfault. */
+ bfd *tmpbfd = current_inferior ()->pspace->exec_bfd ();
+ if (tmpbfd == nullptr)
+ tmpbfd = current_inferior ()->pspace->core_bfd ();
+ if (tmpbfd == nullptr)
+ return nullptr;
+
+ /* Create a hollow BFD object. */
+ bfd *nbfd = bfd_create ("commpage", tmpbfd);
+ if (nbfd == nullptr)
+ return nullptr;
+
+ /* Close in case of failure. */
+ std::unique_ptr<bfd, decltype (&bfd_close)> bfd_deleter (nbfd, bfd_close);
+
+ /* Prepare the BFD for writing. */
+ if (!bfd_make_writable (nbfd))
+ return nullptr;
+
+ asection *section = bfd_make_section (nbfd, ".text");
+ section->size = HAIKU_COMMPAGE_SIZE;
+
+ /* Read the commpage symbols from the target. */
+ std::unique_ptr<osdata> comm_data = get_osdata ("comm");
+ gdb_assert (comm_data->type == "comm");
+
+ size_t sym_count = comm_data->items.size ();
+
+ asymbol **symtab
+ = (asymbol **)bfd_alloc (nbfd, (sym_count + 1) * sizeof (asymbol *));
+
+ for (size_t i = 0; i < sym_count; ++i)
+ {
+ elf_symbol_type *sym = (elf_symbol_type *)bfd_make_empty_symbol (nbfd);
+ sym->symbol.section = section;
+
+ for (const auto &[name, value] : comm_data->items[i].columns)
+ {
+ if (name == "name")
+ {
+ char *tmp = (char *)bfd_alloc (nbfd, value.size () + 1);
+ memcpy (tmp, value.c_str (), value.size () + 1);
+ bfd_set_asymbol_name (&sym->symbol, tmp);
+ }
+ else if (name == "value")
+ {
+ sym->symbol.value = strtoulst (value.c_str (), nullptr, 10);
+ sym->internal_elf_sym.st_value = sym->symbol.value;
+ }
+ else if (name == "size")
+ {
+ sym->internal_elf_sym.st_size
+ = strtoulst (value.c_str (), nullptr, 10);
+ }
+ else if (name == "type")
+ {
+ sym->symbol.flags = BSF_GLOBAL;
+ for (char flag : value)
+ {
+ switch (flag)
+ {
+ case 'f':
+ sym->symbol.flags |= BSF_FUNCTION;
+ break;
+ case 'o':
+ sym->symbol.flags |= BSF_OBJECT;
+ break;
+ }
+ }
+ }
+ }
+
+ symtab[i] = (asymbol *)sym;
+ }
+
+ symtab[sym_count] = nullptr;
+
+ /* Write the symbol table. */
+ if (!bfd_set_symtab (nbfd, symtab, sym_count))
+ return nullptr;
+
+ /* Prepare the BFD for reading by GDB. */
+ if (!bfd_make_readable (nbfd))
+ return nullptr;
+
+ bfd_deleter.release ();
+
+ return gdb_bfd_ref_ptr::new_reference (nbfd);
+}
+
+/* See haiku-tdep.h. */
+
+CORE_ADDR
+haiku_get_commpage_address ()
+{
+ /* Read the images from the target. */
+ std::unique_ptr<osdata> images = get_osdata ("images");
+ gdb_assert (images->type == "images");
+
+ std::string current_team = std::to_string (current_inferior ()->pid);
+
+ for (const auto &item : images->items)
+ {
+ bool matches_team = false;
+ bool matches_name = false;
+ const char *text_value = nullptr;
+
+ for (const auto &[name, value] : item.columns)
+ {
+ if (name == "team")
+ matches_team = value == current_team;
+ else if (name == "name")
+ matches_name = value == "commpage";
+ else if (name == "text")
+ text_value = value.c_str ();
+ }
+
+ if (matches_team && matches_name && text_value != nullptr)
+ return string_to_core_addr (text_value);
+ }
+
+ return 0;
+}
diff --git a/gdb/haiku-tdep.h b/gdb/haiku-tdep.h
new file mode 100644
index 0000000..b53e3fb
--- /dev/null
+++ b/gdb/haiku-tdep.h
@@ -0,0 +1,44 @@
+/* Common target-dependent definitions for Haiku systems.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef HAIKU_TDEP_H
+#define HAIKU_TDEP_H
+
+#include "gdb_bfd.h"
+
+/* Derived from headers/private/system/commpage_defs.h. */
+#define HAIKU_COMMPAGE_SIZE (0x8000)
+
+/* Haiku specific set of ABI-related routines. */
+
+void haiku_init_abi (struct gdbarch_info, struct gdbarch *);
+
+/* Used by OS ABI sniffers to check for Haiku-specific symbols. */
+
+bool haiku_check_required_symbols (struct bfd *);
+
+/* Opens the virtual commpage image. */
+
+gdb_bfd_ref_ptr haiku_bfd_open_commpage ();
+
+/* Gets the commpage address from the target. */
+
+CORE_ADDR haiku_get_commpage_address ();
+
+#endif /* HAIKU_TDEP_H */
diff --git a/gdb/i386-haiku-nat.c b/gdb/i386-haiku-nat.c
new file mode 100644
index 0000000..b41fcca
--- /dev/null
+++ b/gdb/i386-haiku-nat.c
@@ -0,0 +1,39 @@
+/* Native-dependent code for Haiku/i386.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "haiku-nat.h"
+
+struct i386_haiku_nat_target final : public haiku_nat_target
+{
+ void fetch_registers (struct regcache *, int) override;
+ void store_registers (struct regcache *, int) override;
+};
+
+static i386_haiku_nat_target the_i386_haiku_nat_target;
+
+void _initialize_i386_haiku_nat ();
+void
+_initialize_i386_haiku_nat ()
+{
+ haiku_target = &the_i386_haiku_nat_target;
+
+ add_inf_child_target (&the_i386_haiku_nat_target);
+}
diff --git a/gdb/i386-haiku-tdep.c b/gdb/i386-haiku-tdep.c
new file mode 100644
index 0000000..fd8011f
--- /dev/null
+++ b/gdb/i386-haiku-tdep.c
@@ -0,0 +1,61 @@
+/* Target-dependent code for Haiku/i386.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "haiku-tdep.h"
+#include "i386-tdep.h"
+
+static void
+i386_haiku_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+
+ i386_elf_init_abi (info, gdbarch);
+ haiku_init_abi (info, gdbarch);
+
+ /* The offset of the PC in the jmp_buf structure.
+ Found at src/system/libroot/posix/arch/x86/setjmp_internal.h. */
+ tdep->jb_pc_offset = 20;
+}
+
+static enum gdb_osabi
+i386_haiku_osabi_sniffer (bfd *abfd)
+{
+ const char *target_name = bfd_get_target (abfd);
+
+ if (strcmp (target_name, "elf32-i386") != 0)
+ return GDB_OSABI_UNKNOWN;
+
+ if (!haiku_check_required_symbols (abfd))
+ return GDB_OSABI_UNKNOWN;
+
+ return GDB_OSABI_HAIKU;
+}
+
+void _initialize_i386_haiku_tdep ();
+void
+_initialize_i386_haiku_tdep ()
+{
+ gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour,
+ i386_haiku_osabi_sniffer);
+
+ gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_HAIKU,
+ i386_haiku_init_abi);
+}
diff --git a/gdb/solib-haiku.c b/gdb/solib-haiku.c
new file mode 100644
index 0000000..ba9e548
--- /dev/null
+++ b/gdb/solib-haiku.c
@@ -0,0 +1,115 @@
+/* Handle shared libraries for GDB, the GNU Debugger.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "exec.h"
+#include "haiku-tdep.h"
+#include "inferior.h"
+#include "objfiles.h"
+#include "solib-haiku.h"
+#include "solib-target.h"
+#include "solist.h"
+
+/* For other targets, the solib implementation usually reads hints from the
+ dynamic linker in the active address space, which could be anything from a
+ core file to a live inferior.
+
+ Haiku's runtime_loader does not export such information. The nearest
+ we have is the static variable sLoadedImages. We therefore have to rely on
+ what the target reports.
+
+ This is basically a wrapper around solib-target.c. */
+
+static void
+haiku_relocate_section_addresses (solib &so, struct target_section *sec)
+{
+ if (so.so_name == "commpage")
+ {
+ CORE_ADDR commpage_address = haiku_get_commpage_address ();
+ sec->addr = commpage_address;
+ sec->endaddr = commpage_address + HAIKU_COMMPAGE_SIZE;
+
+ so.addr_low = commpage_address;
+ so.addr_high = commpage_address + HAIKU_COMMPAGE_SIZE;
+ }
+ else
+ {
+ solib_target_so_ops.relocate_section_addresses (so, sec);
+ }
+}
+
+static void
+haiku_clear_so (const solib &so)
+{
+ if (solib_target_so_ops.clear_so != nullptr)
+ solib_target_so_ops.clear_so (so);
+}
+
+static void
+haiku_clear_solib (program_space *pspace)
+{
+ if (solib_target_so_ops.clear_solib != nullptr)
+ solib_target_so_ops.clear_solib (pspace);
+}
+
+static void
+haiku_solib_create_inferior_hook (int from_tty)
+{
+ solib_target_so_ops.solib_create_inferior_hook (from_tty);
+}
+
+static intrusive_list<solib>
+haiku_current_sos ()
+{
+ return solib_target_so_ops.current_sos ();
+}
+
+static int
+haiku_open_symbol_file_object (int from_tty)
+{
+ return solib_target_so_ops.open_symbol_file_object (from_tty);
+}
+
+static int
+haiku_in_dynsym_resolve_code (CORE_ADDR pc)
+{
+ /* No dynamic resolving implemented in Haiku yet.
+ Return what the generic code has to say. */
+ return solib_target_so_ops.in_dynsym_resolve_code (pc);
+}
+
+static gdb_bfd_ref_ptr
+haiku_bfd_open (const char *pathname)
+{
+ if (strcmp (pathname, "commpage") == 0)
+ return haiku_bfd_open_commpage ();
+ return solib_target_so_ops.bfd_open (pathname);
+}
+
+const struct solib_ops haiku_so_ops = {
+ .relocate_section_addresses = haiku_relocate_section_addresses,
+ .clear_so = haiku_clear_so,
+ .clear_solib = haiku_clear_solib,
+ .solib_create_inferior_hook = haiku_solib_create_inferior_hook,
+ .current_sos = haiku_current_sos,
+ .open_symbol_file_object = haiku_open_symbol_file_object,
+ .in_dynsym_resolve_code = haiku_in_dynsym_resolve_code,
+ .bfd_open = haiku_bfd_open,
+};
diff --git a/gdb/solib-haiku.h b/gdb/solib-haiku.h
new file mode 100644
index 0000000..5f6a90f
--- /dev/null
+++ b/gdb/solib-haiku.h
@@ -0,0 +1,27 @@
+/* Handle shared libraries for GDB, the GNU Debugger.
+
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SOLIB_HAIKU_H
+#define SOLIB_HAIKU_H
+
+struct solib_ops;
+
+extern const struct solib_ops haiku_so_ops;
+
+#endif /* solib-haiku.h */
diff --git a/gdbsupport/osabi.def b/gdbsupport/osabi.def
index 637da26..ae994f8 100644
--- a/gdbsupport/osabi.def
+++ b/gdbsupport/osabi.def
@@ -41,6 +41,7 @@ GDB_OSABI_DEF (LINUX, "GNU/Linux", "linux(-gnu[^-]*)?")
GDB_OSABI_DEF (FREEBSD, "FreeBSD", nullptr)
GDB_OSABI_DEF (NETBSD, "NetBSD", nullptr)
GDB_OSABI_DEF (OPENBSD, "OpenBSD", nullptr)
+GDB_OSABI_DEF (HAIKU, "Haiku", nullptr)
GDB_OSABI_DEF (WINCE, "WindowsCE", nullptr)
GDB_OSABI_DEF (GO32, "DJGPP", nullptr)
GDB_OSABI_DEF (CYGWIN, "Cygwin", nullptr)
--
2.51.0
From 6401432b070fe0196a6ff7caa438132198f6b259 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= <jerome.duval@gmail.com>
Date: Sat, 7 Mar 2026 21:55:17 +0100
Subject: gdb: Update Haiku support for 16.x
diff --git a/gdb/haiku-nat.c b/gdb/haiku-nat.c
index 3faab24..c1d768c 100644
--- a/gdb/haiku-nat.c
+++ b/gdb/haiku-nat.c
@@ -635,7 +635,7 @@ haiku_relocate_main_executable (inferior *inf)
}
if (inf->pspace->symfile_object_file == nullptr)
- symbol_file_add_main (inf->pspace->exec_filename.get (),
+ symbol_file_add_main (inf->pspace->exec_filename (),
SYMFILE_DEFER_BP_RESET);
objfile *objf = inf->pspace->symfile_object_file;
@@ -650,7 +650,7 @@ static void
haiku_enable_breakpoints_if_ready (inferior *inf)
{
if (strcmp (haiku_nat::pid_to_exec_file (inf->pid),
- inf->pspace->exec_filename.get ())
+ inf->pspace->exec_filename ())
!= 0)
{
/* Not ready yet. The inferior is still executing a wrapper
diff --git a/gdb/haiku-tdep.h b/gdb/haiku-tdep.h
index b53e3fb..2efc2f6 100644
--- a/gdb/haiku-tdep.h
+++ b/gdb/haiku-tdep.h
@@ -17,8 +17,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#ifndef HAIKU_TDEP_H
-#define HAIKU_TDEP_H
+#ifndef GDB_HAIKU_TDEP_H
+#define GDB_HAIKU_TDEP_H
#include "gdb_bfd.h"
@@ -41,4 +41,4 @@ gdb_bfd_ref_ptr haiku_bfd_open_commpage ();
CORE_ADDR haiku_get_commpage_address ();
-#endif /* HAIKU_TDEP_H */
+#endif /* GDB_HAIKU_TDEP_H */
diff --git a/gdb/nat/haiku-nat.h b/gdb/nat/haiku-nat.h
index 4a057a4..189027f 100644
--- a/gdb/nat/haiku-nat.h
+++ b/gdb/nat/haiku-nat.h
@@ -17,8 +17,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#ifndef NAT_HAIKU_NAT_H
-#define NAT_HAIKU_NAT_H
+#ifndef GDB_NAT_HAIKU_NAT_H
+#define GDB_NAT_HAIKU_NAT_H
#include <functional>
@@ -426,4 +426,4 @@ extern bool debug_haiku_nat;
#define HAIKU_NAT_SCOPED_DEBUG_ENTER_EXIT \
scoped_debug_enter_exit (debug_haiku_nat, "haiku-nat")
-#endif /* NAT_HAIKU_NAT_H */
+#endif /* GDB_NAT_HAIKU_NAT_H */
diff --git a/gdb/nat/haiku-nub-message.h b/gdb/nat/haiku-nub-message.h
index 04212a3..fc41efe 100644
--- a/gdb/nat/haiku-nub-message.h
+++ b/gdb/nat/haiku-nub-message.h
@@ -17,8 +17,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#ifndef NAT_HAIKU_NUB_MESSAGE_H
-#define NAT_HAIKU_NUB_MESSAGE_H
+#ifndef GDB_NAT_HAIKU_NUB_MESSAGE_H
+#define GDB_NAT_HAIKU_NUB_MESSAGE_H
#include "gnulib/config.h"
@@ -138,4 +138,4 @@ haiku_send_nub_message (port_id nub_port,
return (result < B_OK) ? result : reply.error;
}
-#endif /* NAT_HAIKU_NUB_MESSAGE_H */
+#endif /* GDB_NAT_HAIKU_NUB_MESSAGE_H */
diff --git a/gdb/nat/haiku-osdata.h b/gdb/nat/haiku-osdata.h
index 01e87e4..182ecfa 100644
--- a/gdb/nat/haiku-osdata.h
+++ b/gdb/nat/haiku-osdata.h
@@ -17,10 +17,10 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#ifndef NAT_HAIKU_OSDATA_H
-#define NAT_HAIKU_OSDATA_H
+#ifndef GDB_NAT_HAIKU_OSDATA_H
+#define GDB_NAT_HAIKU_OSDATA_H
extern LONGEST haiku_common_xfer_osdata (const char *annex, gdb_byte *readbuf,
ULONGEST offset, ULONGEST len);
-#endif /* NAT_HAIKU_OSDATA_H */
+#endif /* GDB_NAT_HAIKU_OSDATA_H */
diff --git a/gdb/solib-haiku.c b/gdb/solib-haiku.c
index ba9e548..0ec56b5 100644
--- a/gdb/solib-haiku.c
+++ b/gdb/solib-haiku.c
@@ -75,7 +75,7 @@ haiku_solib_create_inferior_hook (int from_tty)
solib_target_so_ops.solib_create_inferior_hook (from_tty);
}
-static intrusive_list<solib>
+static owning_intrusive_list<solib>
haiku_current_sos ()
{
return solib_target_so_ops.current_sos ();
@@ -112,4 +112,9 @@ const struct solib_ops haiku_so_ops = {
.open_symbol_file_object = haiku_open_symbol_file_object,
.in_dynsym_resolve_code = haiku_in_dynsym_resolve_code,
.bfd_open = haiku_bfd_open,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ default_find_solib_addr,
};
diff --git a/gdb/solib-haiku.h b/gdb/solib-haiku.h
index 5f6a90f..bd023f5 100644
--- a/gdb/solib-haiku.h
+++ b/gdb/solib-haiku.h
@@ -17,11 +17,11 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#ifndef SOLIB_HAIKU_H
-#define SOLIB_HAIKU_H
+#ifndef GDB_SOLIB_HAIKU_H
+#define GDB_SOLIB_HAIKU_H
struct solib_ops;
extern const struct solib_ops haiku_so_ops;
-#endif /* solib-haiku.h */
+#endif /* GDB_SOLIB_HAIKU_H */
--
2.51.0
From 5a6a53cd454f6d400bd5c46698a847d00fd9fb2a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= <jerome.duval@gmail.com>
Date: Sat, 7 Mar 2026 21:56:27 +0100
Subject: gdbserver: Update Haiku support for 16.x
diff --git a/gdbserver/haiku-amd64-low.cc b/gdbserver/haiku-amd64-low.cc
index 0e6e0ad..3503d5c 100644
--- a/gdbserver/haiku-amd64-low.cc
+++ b/gdbserver/haiku-amd64-low.cc
@@ -155,7 +155,7 @@ haiku_amd64_target::fetch_registers (struct regcache *regcache, int regno)
{
char regs[sizeof (x86_64_debug_cpu_state)];
- if (haiku_nat::get_cpu_state (ptid_of (current_thread), &regs) < 0)
+ if (haiku_nat::get_cpu_state (current_thread->id, &regs) < 0)
{
/* This happens when the inferior is killed by another process
while being stopped. The nub port has been deleted, so we cannot
@@ -196,7 +196,7 @@ haiku_amd64_target::store_registers (struct regcache *regcache, int regno)
{
char regs[sizeof (x86_64_debug_cpu_state)];
- if (haiku_nat::get_cpu_state (ptid_of (current_thread), &regs) < 0)
+ if (haiku_nat::get_cpu_state (current_thread->id, &regs) < 0)
{
haiku_nat_debug_printf ("Failed to get actual CPU state: %s",
strerror (errno));
@@ -220,7 +220,7 @@ haiku_amd64_target::store_registers (struct regcache *regcache, int regno)
}
}
- if (haiku_nat::set_cpu_state (ptid_of (current_thread), &regs) < 0)
+ if (haiku_nat::set_cpu_state (current_thread->id, &regs) < 0)
perror_with_name (("haiku_nat::set_cpu_state"));
}
@@ -248,7 +248,7 @@ haiku_amd64_target::low_arch_setup (process_info *process)
target_desc *tdesc = amd64_create_target_description (X86_XSTATE_AVX_MASK,
false, false, false);
- init_target_desc (tdesc, amd64_expedite_regs);
+ init_target_desc (tdesc, amd64_expedite_regs, GDB_OSABI_HAIKU);
process->tdesc = tdesc;
}
diff --git a/gdbserver/haiku-low.cc b/gdbserver/haiku-low.cc
index 41295d5..b0551b8 100644
--- a/gdbserver/haiku-low.cc
+++ b/gdbserver/haiku-low.cc
@@ -168,7 +168,7 @@ haiku_process_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
/* Add at least the child's main thread. Otherwise, gdbserver would
think we have no more inferiors attached and quit. */
- add_thread (ptid_t (pid, 0, pid), nullptr);
+ find_process_pid (pid)->add_thread (ptid_t (pid, 0, pid), nullptr);
};
client_state &cs = get_client_state ();
@@ -193,7 +193,7 @@ haiku_process_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
thread that was not fully initialized during the attach stage. */
if (wptid.tid () != 0 && !find_thread_ptid (wptid)
&& ourstatus->kind () != TARGET_WAITKIND_THREAD_EXITED)
- add_thread (wptid, nullptr);
+ find_process_pid (wptid.pid())->add_thread (wptid, nullptr);
switch (ourstatus->kind ())
{
@@ -247,7 +247,9 @@ haiku_process_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
return wptid;
break;
case TARGET_WAITKIND_THREAD_EXITED:
- remove_thread (find_thread_ptid (wptid));
+ {
+ thread_info *info = find_thread_ptid (wptid);
+ info->process ()->remove_thread (info);
if (cs.report_thread_events)
return wptid;
@@ -255,6 +257,7 @@ haiku_process_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
/* The thread is dead so we cannot resume the the same wptid. */
wptid = ptid;
break;
+ }
default:
gdb_assert_not_reached ("Unknown stopped status");
}
@@ -272,7 +275,7 @@ haiku_process_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
int
haiku_process_target::kill (process_info *process)
{
- if (haiku_nat::kill (pid_of (process)) < 0)
+ if (haiku_nat::kill (process->pid) < 0)
return -1;
mourn (process);
@@ -284,7 +287,7 @@ haiku_process_target::kill (process_info *process)
int
haiku_process_target::detach (process_info *process)
{
- if (haiku_nat::detach (pid_of (process)) < 0)
+ if (haiku_nat::detach (process->pid) < 0)
return -1;
mourn (process);
@@ -296,7 +299,10 @@ haiku_process_target::detach (process_info *process)
void
haiku_process_target::mourn (struct process_info *proc)
{
- for_each_thread (pid_of (proc), remove_thread);
+ proc->for_each_thread ([proc] (thread_info *thread)
+ {
+ proc->remove_thread (thread);
+ });
remove_process (proc);
}
@@ -306,7 +312,7 @@ haiku_process_target::mourn (struct process_info *proc)
void
haiku_process_target::join (int pid)
{
- gdb::handle_eintr (-1, ::waitpid, pid, nullptr, 0);
+ gdb::waitpid (pid, nullptr, 0);
}
/* Implement the thread_alive target_ops method. */
@@ -323,7 +329,7 @@ int
haiku_process_target::read_memory (CORE_ADDR memaddr, unsigned char *myaddr,
int size)
{
- if (haiku_nat::read_memory (pid_of (current_process ()), memaddr, myaddr,
+ if (haiku_nat::read_memory (current_process ()->pid, memaddr, myaddr,
&size)
< 0)
{
@@ -340,7 +346,7 @@ int
haiku_process_target::write_memory (CORE_ADDR memaddr,
const unsigned char *myaddr, int size)
{
- if (haiku_nat::write_memory (pid_of (current_process ()), memaddr, myaddr,
+ if (haiku_nat::write_memory (current_process ()->pid, memaddr, myaddr,
&size)
< 0)
{
@@ -361,7 +367,7 @@ haiku_process_target::request_interrupt ()
if (thread == nullptr)
return;
- ::kill (pid_of (thread), SIGINT);
+ ::kill (thread->id.pid(), SIGINT);
}
/* Implement the read_offsets target_ops method. */
@@ -369,7 +375,7 @@ haiku_process_target::request_interrupt ()
int
haiku_process_target::read_offsets (CORE_ADDR *text, CORE_ADDR *data)
{
- if (haiku_nat::read_offsets (pid_of (current_process ()), text, data) < 0)
+ if (haiku_nat::read_offsets (current_process ()->pid, text, data) < 0)
return 0;
return 1;
}
@@ -438,7 +444,7 @@ haiku_process_target::start_non_stop (bool enable)
bool
haiku_process_target::thread_stopped (thread_info *thread)
{
- return haiku_nat::thread_stopped (ptid_of (thread));
+ return haiku_nat::thread_stopped (thread->id);
}
/* Implement the pid_to_exec_file target_ops method. */
--
2.51.0