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