diff --git a/dev-util/gdb/gdb-15.1.recipe b/dev-util/gdb/gdb-15.1.recipe new file mode 100644 index 000000000..042e34d39 --- /dev/null +++ b/dev-util/gdb/gdb-15.1.recipe @@ -0,0 +1,120 @@ +SUMMARY="The GNU debugger" +DESCRIPTION="GDB is the GNU debugger. It allows you to see what is going on \ +'inside' another program while it executes -- or what another program was \ +doing at the moment it crashed. + +GDB can do four main kinds of things (plus other things in support of \ +these) to help you catch bugs in the act: + + * Start your program, specifying anything that might affect its behavior. + * Make your program stop on specified conditions. + * Examine what has happened, when your program has stopped. + * Change things in your program, so you can experiment with correcting \ +the effects of one bug and go on to learn about another. + +The program being debugged can be written in Ada, C, C++, Objective-C, \ +Pascal (and many other languages). Those programs might be executing \ +on the same machine as GDB (native) or on another machine (remote). \ +GDB can run on most popular UNIX and Microsoft Windows variants." +HOMEPAGE="https://sourceware.org/gdb/" +COPYRIGHT="2024 Free Software Foundation, Inc." +LICENSE="GNU GPL v2 + GNU GPL v3" +REVISION="1" +SOURCE_URI="https://ftp.gnu.org/gnu/gdb/gdb-$portVersion.tar.xz" +CHECKSUM_SHA256="38254eacd4572134bca9c5a5aa4d4ca564cbbd30c369d881f733fb6b903354f2" +PATCHES="gdb-$portVersion.patchset" + +ARCHITECTURES="x86_64 ?x86 !x86_gcc2" + +binutilsVersion="2.42" + +PROVIDES=" + gdb = $portVersion + cmd:gdb = $portVersion + cmd:gdb_add_index = $portVersion + cmd:gdbserver = $portVersion + devel:libbfd = $binutilsVersion + devel:libctf = $binutilsVersion + devel:libctf_nobfd = $binutilsVersion + devel:libopcodes = $binutilsVersion + devel:libsframe = $binutilsVersion + " + +REQUIRES=" + haiku + lib:libexpat + lib:libgmp + lib:libiconv + lib:libisl + lib:libmpfr + lib:libncurses + lib:libpython3.10 + lib:libreadline + lib:libz + " + +BUILD_REQUIRES=" + devel:libexpat + devel:libgmp + devel:libiconv + devel:libisl + devel:libmpfr + devel:libncurses + devel:libpython3.10 + devel:libreadline + devel:libz + " +BUILD_PREREQUIRES=" + haiku_devel + cmd:awk + cmd:bison + cmd:cmp + cmd:flex + cmd:gcc + cmd:ld + cmd:m4 + cmd:make + cmd:makeinfo + cmd:python3 + cmd:yacc + " + +TEST_REQUIRES=" + cmd:runtest + " + +BUILD() +{ + export CPPFLAGS="-DB_USE_POSITIVE_POSIX_ERRORS" + export HAIKU_USE_VENDOR_DIRECTORIES=1 + + runConfigure --omit-dirs "docDir dataRootDir" ./configure \ + --disable-nls --enable-gdb --enable-gdbserver \ + --with-system-zlib --with-system-readline \ + --with-python=python3 + + make $jobArgs +} + +INSTALL() +{ + make install + + # symlink so that GDB could look for library .debuginfo files + ln -s $debugInfoDir $libDir/.debug + + prepareInstalledDevelLibs \ + libbfd \ + libctf \ + libctf-nobfd \ + libopcodes \ + libsframe + + rm $developLibDir/*.la +} + +TEST() +{ + make check +} diff --git a/dev-util/gdb/patches/gdb-15.1.patchset b/dev-util/gdb/patches/gdb-15.1.patchset new file mode 100644 index 000000000..45108d483 --- /dev/null +++ b/dev-util/gdb/patches/gdb-15.1.patchset @@ -0,0 +1,7181 @@ +From 9c9cd5cf820ea4f83b72409dcaaa797f351d2ea1 Mon Sep 17 00:00:00 2001 +From: Trung Nguyen +Date: Mon, 29 Jul 2024 22:55:42 +1000 +Subject: [PATCH 1/3] 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\n\n\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 +--- + gdb/remote.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/gdb/remote.c b/gdb/remote.c +index 42b446c7e27..4e81ccc33bc 100644 +--- a/gdb/remote.c ++++ b/gdb/remote.c +@@ -7704,8 +7704,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; + } + +@@ -7902,7 +7905,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.39.2 + + +From 15e3d88803128d4fafc23335c0e5e6cab0b29cee 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: [PATCH 2/3] gdbserver: Initial Haiku support + +--- + gdb/nat/haiku-debug.c | 43 + + gdb/nat/haiku-nat.c | 2784 ++++++++++++++++++++++++++++++++++ + gdb/nat/haiku-nat.h | 429 ++++++ + gdb/nat/haiku-nub-message.c | 50 + + gdb/nat/haiku-nub-message.h | 141 ++ + gdb/nat/haiku-osdata.c | 445 ++++++ + gdb/nat/haiku-osdata.h | 26 + + gdbserver/Makefile.in | 6 + + gdbserver/configure | 2 +- + gdbserver/configure.srv | 9 + + gdbserver/haiku-amd64-low.cc | 262 ++++ + gdbserver/haiku-low.cc | 609 ++++++++ + gdbserver/haiku-low.h | 100 ++ + gdbserver/remote-utils.cc | 4 + + gdbsupport/signals.cc | 10 + + include/gdb/signals.def | 4 +- + 16 files changed, 4922 insertions(+), 2 deletions(-) + create mode 100644 gdb/nat/haiku-debug.c + create mode 100644 gdb/nat/haiku-nat.c + create mode 100644 gdb/nat/haiku-nat.h + create mode 100644 gdb/nat/haiku-nub-message.c + create mode 100644 gdb/nat/haiku-nub-message.h + create mode 100644 gdb/nat/haiku-osdata.c + create mode 100644 gdb/nat/haiku-osdata.h + create mode 100644 gdbserver/haiku-amd64-low.cc + create mode 100644 gdbserver/haiku-low.cc + create mode 100644 gdbserver/haiku-low.h + +diff --git a/gdb/nat/haiku-debug.c b/gdb/nat/haiku-debug.c +new file mode 100644 +index 00000000000..d1796a403f6 +--- /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 . */ ++ ++#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 00000000000..313a676c990 +--- /dev/null ++++ b/gdb/nat/haiku-nat.c +@@ -0,0 +1,2784 @@ ++/* 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 . */ ++ ++/* 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 ++[[nodiscard]] ++std::enable_if_t, void>, ++ status_t> team_send (const team_debug_context *context, ++ haiku_nub_message_data &&data); ++ ++template ++[[nodiscard]] ++std::enable_if_t, void>, ++ status_t> team_send (const team_debug_context *context, ++ haiku_nub_message_data &&data, ++ haiku_nub_message_reply &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 > 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 &)> callback) ++ { ++ if (m_thread < 0) ++ return B_NOT_INITIALIZED; ++ ++ std::shared_ptr gdbstatus; ++ ++ const auto make = [&] () -> target_waitstatus & { ++ gdbstatus = std::make_shared (); ++ 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) ++ { ++ /* Make sure we don't report a THREAD_DEBUGGED event caused by our ++ own attempt to stop a thread using debug_thread. */ ++ make ().set_spurious (); ++ 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 ( ++ 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 ( ++ 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 ( ++ 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 ( ++ 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 ( ++ 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 m_threads; ++ std::set m_created_threads; ++ std::queue > > ++ 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 ({ .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) { ++ debug_thread (info.tid); ++ 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 ++ [[nodiscard]] ++ std::enable_if_t, void>, ++ status_t> ++ send (const haiku_nub_message_data &data) const ++ { ++ return haiku_send_nub_message (m_nub_port, data); ++ } ++ ++ template ++ [[nodiscard]] ++ std::enable_if_t, void>, ++ haiku_nub_message_reply > ++ send (haiku_nub_message_data &&data) const ++ { ++ data.reply_port = m_reply_port; ++ return haiku_send_nub_message (m_nub_port, data); ++ } ++ ++ template ++ [[nodiscard]] ++ std::enable_if_t, void>, ++ status_t> ++ send (haiku_nub_message_data &&data, ++ haiku_nub_message_reply &reply) const ++ { ++ data.reply_port = m_reply_port; ++ return haiku_send_nub_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 ( ++ { .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 &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 ++[[nodiscard]] ++std::enable_if_t, void>, ++ status_t> ++team_send (const team_debug_context *context, ++ haiku_nub_message_data &&data) ++{ ++ return context->send ( ++ std::forward > (data)); ++} ++ ++template ++[[nodiscard]] ++std::enable_if_t, void>, ++ status_t> ++team_send (const team_debug_context *context, ++ haiku_nub_message_data &&data, ++ haiku_nub_message_reply &reply) ++{ ++ return context->send ( ++ std::forward > (data), reply); ++} ++ ++static std::map > ++ team_debug_contexts; ++ ++static std::mutex team_debug_ports_lock; ++static std::set 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 &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 &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 context ++ = std::make_shared (); ++ ++ RETURN_AND_SET_ERRNO_IF_FAIL (context->initialize (pid, !is_ours)); ++ ++ { ++ std::unique_lock lock (team_debug_ports_lock); ++ team_debug_ports.insert (context->debugger_port ()); ++ } ++ ++ /* 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 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 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); ++ ++ std::unique_lock lock (team_debug_ports_lock); ++ 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)); ++ team_debug_ports.insert (context->debugger_port ()); ++ } ++ } ++ else ++ { ++ std::shared_ptr context; ++ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (ptid.pid (), context)); ++ ++ RETURN_AND_SET_ERRNO_IF_FAIL (context->resume (ptid, kind, sig)); ++ team_debug_ports.insert (context->debugger_port ()); ++ } ++ ++ 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 chosen_context; ++ ++ if (ptid == minus_one_ptid) ++ { ++ /* Wait for any process. */ ++ bool block = !(target_options & TARGET_WNOHANG).raw (); ++ ++ std::vector > contexts; ++ std::vector 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 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 ()) ++ { ++ pipe_to_event_loop.mark (); ++ ++ team_debug_ports_lock.lock (); ++ team_debug_ports.insert (chosen_context->debugger_port ()); ++ if (is_async_p ()) ++ pipe_to_worker.mark (); ++ team_debug_ports_lock.unlock (); ++ } ++ } ++ ++ 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 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 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 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 context; ++ RETURN_AND_SET_ERRNO_IF_FAIL (get_context (pid, context)); ++ ++ debug_nub_read_memory_reply reply; ++ ++ while (*sizeLeft > 0) ++ { ++ RETURN_AND_SET_ERRNO_IF_FAIL ( ++ context->send ( ++ { .address = (void *)memaddr, .size = (int32)*sizeLeft }, ++ 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 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 (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 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 context; ++ RETURN_VALUE_AND_SET_ERRNO_IF_FAIL (get_context (ptid.pid (), context), ++ false); ++ ++ status_t status = context ++ ->send ( ++ { .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 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 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 ++ pipe_to_event_loop_closer (&pipe_to_event_loop, pipe_close); ++ ++ std::unique_ptr ++ 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 wait_infos; ++ ++ 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 }); ++ ++ team_debug_ports_lock.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 }); ++ } ++ team_debug_ports_lock.unlock (); ++ ++ ssize_t ports_readable; ++ ++ do ++ { ++ ports_readable = wait_for_objects (wait_infos.data (), ++ wait_infos.size ()); ++ } ++ while (ports_readable == B_INTERRUPTED); ++ ++ gdb_assert (ports_readable > 0); ++ ++ if (wait_infos[0].events & B_EVENT_READ) ++ { ++ /* Woke up because a new team has been added. */ ++ --ports_readable; ++ pipe_to_worker.flush (); ++ } ++ ++ /* Remove dead ports. */ ++ team_debug_ports_lock.lock (); ++ for (size_t i = 1; i < wait_infos.size (); ++i) ++ { ++ const object_wait_info &wait_info = wait_infos[i]; ++ if (wait_info.events & B_EVENT_INVALID) ++ { ++ team_debug_ports.erase (wait_info.object); ++ --ports_readable; ++ } ++ /* These events are one-shot. They will be re-added by ++ resume(). Otherwise, the worker thread loop would just ++ go on and on. */ ++ if (wait_info.events & B_EVENT_READ) ++ team_debug_ports.erase (wait_info.object); ++ } ++ team_debug_ports_lock.unlock (); ++ ++ /* There are some events to read. */ ++ if (ports_readable > 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 &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 &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 &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 &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 &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 &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 &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 &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 symbols (symbol_count); ++ /* An additional guaranteed null terminator. */ ++ std::vector 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 &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 &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 00000000000..4a057a45523 +--- /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 . */ ++ ++#ifndef NAT_HAIKU_NAT_H ++#define NAT_HAIKU_NAT_H ++ ++#include ++ ++#include ++ ++#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 &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 &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 &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 &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 &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 &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 &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 &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 &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 &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 00000000000..815377d5826 +--- /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 . */ ++ ++#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 00000000000..04212a3a06c +--- /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 . */ ++ ++#ifndef NAT_HAIKU_NUB_MESSAGE_H ++#define NAT_HAIKU_NUB_MESSAGE_H ++ ++#include "gnulib/config.h" ++ ++#include ++ ++#include ++ ++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 class haiku_nub_message_traits ++{ ++}; ++ ++#define HAIKU_ASSOCIATE_MESSAGE_DATA(message, data) \ ++ template <> class haiku_nub_message_traits \ ++ { \ ++ public: \ ++ typedef data data_type; \ ++ typedef void reply_type; \ ++ } ++ ++#define HAIKU_ASSOCIATE_MESSAGE_DATA_WITH_REPLY(message, data) \ ++ template <> class haiku_nub_message_traits \ ++ { \ ++ 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 ++using haiku_nub_message_data = ++ typename haiku_nub_message_traits::data_type; ++ ++template ++using haiku_nub_message_reply = ++ typename haiku_nub_message_traits::reply_type; ++ ++template ++std::enable_if_t, void>, ++ status_t> ++haiku_send_nub_message (port_id nub_port, ++ const haiku_nub_message_data &data) ++{ ++ return haiku_send_nub_message (nub_port, -1, message, &data, sizeof (data), ++ nullptr, 0); ++} ++ ++template ++std::enable_if_t, void>, ++ haiku_nub_message_reply > ++haiku_send_nub_message (port_id nub_port, ++ const haiku_nub_message_data &data) ++{ ++ haiku_nub_message_reply 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 ++std::enable_if_t, void>, ++ status_t> ++haiku_send_nub_message (port_id nub_port, ++ const haiku_nub_message_data &data, ++ haiku_nub_message_reply &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 00000000000..1f295c454d8 +--- /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 . */ ++ ++#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 ++int ++all_teams (int (*for_each) (pid_t, const std::function &), ++ const std::function &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 &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 = "\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, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ osdata_table[i].type, ++ osdata_table[i].description, ++ osdata_table[i].title); ++ ++ buffer += "\n"; ++ ++ return buffer; ++ }, ++ }, ++ { ++ .type = "areas", ++ .title = "Areas", ++ .description = "Listing of all areas", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\n"; ++ ++ all_teams (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, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ 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 += "\n"; ++ return buffer; ++ }, ++ }, ++ { ++ .type = "comm", ++ .title = "Commpage symbols", ++ .description = "Listing of all symbols on the system commpage", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\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, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ info.name, pulongest (info.value), ++ pulongest (info.size), type.c_str ()); ++ ++ return 0; ++ }); ++ ++ buffer += "\n"; ++ return buffer; ++ }, ++ }, ++ { ++ .type = "cpus", ++ .title = "CPUs", ++ .description = "Listing of all CPUs/cores on the system", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\n"; ++ ++ for_each_cpu ([&] (const cpu_info &info) { ++ string_xml_appendf ( ++ buffer, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ 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 += "\n"; ++ return buffer; ++ }, ++ }, ++ { ++ .type = "files", ++ .title = "File descriptors", ++ .description = "Listing of all file descriptors", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\n"; ++ ++ all_teams (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, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ plongest (info.team), plongest (info.number), mode.c_str (), ++ plongest (info.device), plongest (info.node), ++ (info.name != nullptr) ? info.name : "(unknown)"); ++ ++ return 0; ++ }); ++ ++ buffer += "\n"; ++ return buffer; ++ }, ++ }, ++ { ++ .type = "images", ++ .title = "Images", ++ .description = "Listing of all images", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\n"; ++ ++ all_teams (for_each_image, [&] (const image_info &info) { ++ string_xml_appendf ( ++ buffer, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ 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 += "\n"; ++ return buffer; ++ }, ++ }, ++ { ++ .type = "ports", ++ .title = "Ports", ++ .description = "Listing of all ports", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\n"; ++ ++ all_teams (for_each_port, [&] (const port_info &info) { ++ string_xml_appendf (buffer, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ plongest (info.team), plongest (info.id), ++ info.name, plongest (info.capacity), ++ plongest (info.queue_count), ++ plongest (info.total_count)); ++ ++ return 0; ++ }); ++ ++ buffer += "\n"; ++ return buffer; ++ }, ++ }, ++ { ++ .type = "sems", ++ .title = "Semaphores", ++ .description = "Listing of all semaphores", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\n"; ++ ++ all_teams (for_each_sem, [&] (const sem_info &info) { ++ string_xml_appendf (buffer, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ plongest (info.team), plongest (info.id), ++ info.name, plongest (info.count), ++ plongest (info.latest_holder)); ++ ++ return 0; ++ }); ++ ++ buffer += "\n"; ++ return buffer; ++ }, ++ }, ++ { ++ .type = "sockets", ++ .title = "Sockets", ++ .description = "Listing of all sockets", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\n"; ++ ++ for_each_socket ([&] (const socket_info &info) { ++ string_xml_appendf (buffer, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ 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 += "\n"; ++ return buffer; ++ }, ++ }, ++ { ++ .type = "teams", ++ .title = "Teams", ++ .description = "Listing of all teams", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\n"; ++ ++ for_each_team ([&] (const team_info &info) { ++ string_xml_appendf (buffer, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ pulongest (info.pid), pulongest (info.uid), ++ info.args); ++ ++ return 0; ++ }); ++ ++ buffer += "\n"; ++ return buffer; ++ }, ++ }, ++ { ++ .type = "threads", ++ .title = "Threads", ++ .description = "Listing of all threads", ++ .take_snapshot = [] () -> std::string { ++ std::string buffer = "\n"; ++ ++ all_teams ( ++ for_each_thread, [&] (const thread_info &info) { ++ string_xml_appendf (buffer, ++ "" ++ "%s" ++ "%s" ++ "%s" ++ "", ++ plongest (info.team), plongest (info.tid), ++ info.name); ++ ++ return 0; ++ }); ++ ++ buffer += "\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 00000000000..01e87e4d2ed +--- /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 . */ ++ ++#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 6148ccf9121..b33d3e8f37b 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 3abc647acda..ff0718eea6f 100755 +--- a/gdbserver/configure ++++ b/gdbserver/configure +@@ -8704,7 +8704,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 9e861a75088..34740727037 100644 +--- a/gdbserver/configure.srv ++++ b/gdbserver/configure.srv +@@ -400,6 +400,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 00000000000..0e6e0adeade +--- /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 . */ ++ ++#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 ++ ++/* Very conservative inclusion of Haiku headers to prevent name clashes. */ ++#include ++#include ++ ++/* 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 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), ®s) < 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), ®s) < 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), ®s) < 0) ++ perror_with_name (("haiku_nat::set_cpu_state")); ++} ++ ++const gdb_byte * ++haiku_amd64_target::sw_breakpoint_from_kind (int kind, int *size) ++{ ++ /* From */ ++ ++ /* 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 00000000000..41295d58561 +--- /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 . */ ++ ++#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 &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 00000000000..1167305586a +--- /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 . */ ++ ++#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 &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 5a8eb9ae52a..7dabee6a72d 100644 +--- a/gdbserver/remote-utils.cc ++++ b/gdbserver/remote-utils.cc +@@ -109,6 +109,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 4989b3fc28b..a46d74c37bc 100644 +--- a/gdbsupport/signals.cc ++++ b/gdbsupport/signals.cc +@@ -335,6 +335,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) + { +@@ -592,6 +597,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 677b01e7f40..d21d3390df2 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.39.2 + + +From 460898e590738b7a23667c32bbdbd875f8006ebb Mon Sep 17 00:00:00 2001 +From: Trung Nguyen +Date: Mon, 1 Jul 2024 10:32:51 +0700 +Subject: [PATCH 3/3] gdb: Initial Haiku support + +--- + gdb/Makefile.in | 20 ++ + gdb/amd64-haiku-nat.c | 153 ++++++++ + gdb/amd64-haiku-tdep.c | 144 ++++++++ + gdb/configure | 2 +- + gdb/configure.host | 2 + + gdb/configure.nat | 25 ++ + gdb/configure.tgt | 11 + + gdb/haiku-nat.c | 766 +++++++++++++++++++++++++++++++++++++++++ + gdb/haiku-nat.h | 75 ++++ + gdb/haiku-tdep.c | 193 +++++++++++ + gdb/haiku-tdep.h | 44 +++ + gdb/i386-haiku-nat.c | 39 +++ + gdb/i386-haiku-tdep.c | 61 ++++ + gdb/osabi.c | 1 + + gdb/osabi.h | 1 + + gdb/solib-haiku.c | 115 +++++++ + gdb/solib-haiku.h | 27 ++ + 17 files changed, 1678 insertions(+), 1 deletion(-) + create mode 100644 gdb/amd64-haiku-nat.c + create mode 100644 gdb/amd64-haiku-tdep.c + create mode 100644 gdb/haiku-nat.c + create mode 100644 gdb/haiku-nat.h + create mode 100644 gdb/haiku-tdep.c + create mode 100644 gdb/haiku-tdep.h + create mode 100644 gdb/i386-haiku-nat.c + create mode 100644 gdb/i386-haiku-tdep.c + create mode 100644 gdb/solib-haiku.c + create mode 100644 gdb/solib-haiku.h + +diff --git a/gdb/Makefile.in b/gdb/Makefile.in +index 84bc54b303e..9138849cc0a 100644 +--- a/gdb/Makefile.in ++++ b/gdb/Makefile.in +@@ -611,6 +611,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) + +@@ -737,6 +745,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 \ +@@ -815,6 +824,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 \ +@@ -826,6 +836,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-nto-tdep.o \ +@@ -884,6 +895,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 \ +@@ -1688,6 +1701,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 \ +@@ -1728,6 +1743,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 \ +@@ -1745,6 +1762,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 \ +@@ -1840,6 +1859,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 00000000000..e622e660dcf +--- /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 . */ ++ ++#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 ++ ++/* 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 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 00000000000..086e63183af +--- /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 . */ ++ ++#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 (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 66a7ad8d256..7073871283f 100755 +--- a/gdb/configure ++++ b/gdb/configure +@@ -19997,7 +19997,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 da71675b201..0dbbb699405 100644 +--- a/gdb/configure.host ++++ b/gdb/configure.host +@@ -112,6 +112,7 @@ i[34567]86-*-linux*) gdb_host=linux ;; + i[34567]86-*-gnu*) gdb_host=i386gnu ;; + i[3456]86-*-nto*) gdb_host=nto ;; + 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 ;; +@@ -180,6 +181,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 8b98511cef7..6e7670c9cfa 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' + ;; +@@ -479,6 +484,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 47a674201f9..37eb03a1437 100644 +--- a/gdb/configure.tgt ++++ b/gdb/configure.tgt +@@ -127,6 +127,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. +@@ -304,6 +306,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-*-nto*) + # Target: Intel 386 running qnx6. + gdb_target_obs="solib-svr4.o \ +@@ -741,6 +747,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 00000000000..b60882c8ae6 +--- /dev/null ++++ b/gdb/haiku-nat.c +@@ -0,0 +1,766 @@ ++/* 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 . */ ++ ++#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; ++ ++ /* Always add a main thread. */ ++ thread_info *thr = add_thread_silent (this, ptid_t (pid, 0, pid)); ++ switch_to_thread (thr); ++ ++ /* Don't consider the thread stopped until we've processed its ++ initial stop. */ ++ set_executing (this, thr->ptid, true); ++ ++ 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 = "\n"; ++ haiku_nat::for_each_image ( ++ current_inferior ()->pid, [&] (const haiku_nat::image_info &info) { ++ if (!info.is_main_executable) ++ { ++ document += string_printf ( ++ " " ++ "\n", ++ info.name, ++ paddress (current_inferior ()->arch (), info.text)); ++ } ++ return 0; ++ }); ++ document += "\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 ++haiku_nat_target::memory_map () ++{ ++ std::vector 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 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 00000000000..6e900fb365b +--- /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 . */ ++ ++#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 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 00000000000..3b866a580e3 +--- /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 . */ ++ ++#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 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_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 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 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 00000000000..b53e3fbe6fe +--- /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 . */ ++ ++#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 00000000000..b41fcca1689 +--- /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 . */ ++ ++#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 00000000000..fd8011f758c +--- /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 . */ ++ ++#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 (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/osabi.c b/gdb/osabi.c +index d494d899623..011731cdd6e 100644 +--- a/gdb/osabi.c ++++ b/gdb/osabi.c +@@ -68,6 +68,7 @@ static const struct osabi_names gdb_osabi_names[] = + { "FreeBSD", NULL }, + { "NetBSD", NULL }, + { "OpenBSD", NULL }, ++ { "Haiku", NULL }, + { "WindowsCE", NULL }, + { "DJGPP", NULL }, + { "QNX-Neutrino", NULL }, +diff --git a/gdb/osabi.h b/gdb/osabi.h +index c1a85d1b9a3..29f8b20783c 100644 +--- a/gdb/osabi.h ++++ b/gdb/osabi.h +@@ -33,6 +33,7 @@ enum gdb_osabi + GDB_OSABI_FREEBSD, + GDB_OSABI_NETBSD, + GDB_OSABI_OPENBSD, ++ GDB_OSABI_HAIKU, + GDB_OSABI_WINCE, + GDB_OSABI_GO32, + GDB_OSABI_QNXNTO, +diff --git a/gdb/solib-haiku.c b/gdb/solib-haiku.c +new file mode 100644 +index 00000000000..ba9e548f9e4 +--- /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 . */ ++ ++#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 ++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 00000000000..5f6a90fef2c +--- /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 . */ ++ ++#ifndef SOLIB_HAIKU_H ++#define SOLIB_HAIKU_H ++ ++struct solib_ops; ++ ++extern const struct solib_ops haiku_so_ops; ++ ++#endif /* solib-haiku.h */ +-- +2.39.2 +