Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: "Jérôme Duval" <jerome.duval@gmail.com>
To: gdb-patches@sourceware.org
Cc: Trung Nguyen <me@trungnt2910.com>
Subject: [PATCH 2/8] gdb: Initial Haiku support
Date: Thu, 12 Mar 2026 18:23:30 +0100	[thread overview]
Message-ID: <20260312172336.15450-3-jerome.duval@gmail.com> (raw)
In-Reply-To: <20260312172336.15450-1-jerome.duval@gmail.com>

From: Trung Nguyen <me@trungnt2910.com>

---
 gdb/Makefile.in        |  28 ++
 gdb/NEWS               |   2 +
 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/solib-haiku.c      | 115 ++++++
 gdb/solib-haiku.h      |  27 ++
 gdbsupport/osabi.def   |   1 +
 17 files changed, 1699 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 ca9e6be4..607bc5ee 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -618,6 +618,22 @@ 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)
+
+# 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)
 
@@ -745,6 +761,7 @@ ALL_64_TARGET_OBS = \
 	amd64-dicos-tdep.o \
 	amd64-fbsd-tdep.o \
 	amd64-gnu-tdep.o \
+	amd64-haiku-tdep.o \
 	amd64-linux-tdep.o \
 	amd64-netbsd-tdep.o \
 	amd64-obsd-tdep.o \
@@ -828,6 +845,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 \
@@ -839,6 +857,7 @@ ALL_TARGET_OBS = \
 	i386-fbsd-tdep.o \
 	i386-gnu-tdep.o \
 	i386-go32-tdep.o \
+	i386-haiku-tdep.o \
 	i386-linux-tdep.o \
 	i386-netbsd-tdep.o \
 	i386-obsd-tdep.o \
@@ -893,6 +912,7 @@ ALL_TARGET_OBS = \
 	solib-darwin.o \
 	solib-dsbt.o \
 	solib-frv.o \
+	solib-haiku.o \
 	solib-svr4.o \
 	solib-svr4-linux.o \
 	sparc-linux-tdep.o \
@@ -1663,6 +1683,7 @@ HFILES_NO_SRCDIR = \
 	solib-dsbt.h \
 	solib-frv.h \
 	solib.h \
+	solib-haiku.h \
 	solib-svr4.h \
 	solib-svr4-linux.h \
 	solib-target.h \
@@ -1803,6 +1824,8 @@ ALLDEPFILES = \
 	amd64-fbsd-nat.c \
 	amd64-fbsd-tdep.c \
 	amd64-gnu-tdep.c \
+	amd64-haiku-nat.c \
+	amd64-haiku-tdep.c \
 	amd64-linux-nat.c \
 	amd64-linux-tdep.c \
 	amd64-nat.c \
@@ -1843,6 +1866,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 \
@@ -1859,6 +1884,8 @@ ALLDEPFILES = \
 	i386-fbsd-nat.c \
 	i386-fbsd-tdep.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 \
@@ -1953,6 +1980,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/NEWS b/gdb/NEWS
index e46a5108..99da6ac5 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -72,6 +72,8 @@ GNU/Linux/MicroBlaze (gdbserver) microblazeel-*linux*
 
 AArch64 MinGW                   aarch64-*-mingw*
 
+Haiku/amd64                     x86_64-*-haiku*
+
 * New commands
 
 set local-environment
diff --git a/gdb/amd64-haiku-nat.c b/gdb/amd64-haiku-nat.c
new file mode 100644
index 00000000..e622e660
--- /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 00000000..086e6318
--- /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 12c54521..618dd00a 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -20290,7 +20290,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 e83b944c..be8d81af 100644
--- a/gdb/configure.host
+++ b/gdb/configure.host
@@ -114,6 +114,7 @@ i[34567]86-*-msdosdjgpp*) gdb_host=go32 ;;
 i[34567]86-*-linux*)	gdb_host=linux ;;
 i[34567]86-*-gnu*)	gdb_host=i386gnu ;;
 i[34567]86-*-openbsd*)	gdb_host=obsd ;;
+i[34567]86-*-haiku*)	gdb_host=haiku ;;
 i[34567]86-*-solaris2* | x86_64-*-solaris2*)
 			gdb_host=sol2 ;;
 i[34567]86-*-cygwin*)	gdb_host=cygwin ;;
@@ -182,6 +183,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 38dd4179..bc7e70e2 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'
 	;;
@@ -504,6 +509,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 ba418653..82ef688e 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -128,6 +128,8 @@ case "${targ}" in
 	os_obs="netbsd-tdep.o solib-svr4.o";;
 *-*-openbsd*)
 	os_obs="obsd-tdep.o solib-svr4.o";;
+*-*-haiku*)
+	os_obs="haiku-tdep.o solib-haiku.o symfile-mem.o";;
 esac
 
 # 3. Get the rest of objects.
@@ -315,6 +317,10 @@ i[34567]86-*-openbsd*)
 	# Target: OpenBSD/i386
 	gdb_target_obs="i386-bsd-tdep.o i386-obsd-tdep.o bsd-uthread.o"
 	;;
+i[34567]86-*-haiku*)
+	# Target: Haiku/i386
+	gdb_target_obs="i386-haiku-tdep.o"
+	;;
 i[34567]86-*-solaris2* | x86_64-*-solaris2*)
 	# Target: Solaris x86_64
 	gdb_target_obs="${i386_tobjs} ${amd64_tobjs} \
@@ -754,6 +760,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 00000000..3faab245
--- /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 00000000..6e900fb3
--- /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 00000000..3b866a58
--- /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 00000000..b53e3fbe
--- /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 00000000..b41fcca1
--- /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 00000000..fd8011f7
--- /dev/null
+++ b/gdb/i386-haiku-tdep.c
@@ -0,0 +1,61 @@
+/* Target-dependent code for Haiku/i386.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "haiku-tdep.h"
+#include "i386-tdep.h"
+
+static void
+i386_haiku_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+
+  i386_elf_init_abi (info, gdbarch);
+  haiku_init_abi (info, gdbarch);
+
+  /* The offset of the PC in the jmp_buf structure.
+     Found at src/system/libroot/posix/arch/x86/setjmp_internal.h.  */
+  tdep->jb_pc_offset = 20;
+}
+
+static enum gdb_osabi
+i386_haiku_osabi_sniffer (bfd *abfd)
+{
+  const char *target_name = bfd_get_target (abfd);
+
+  if (strcmp (target_name, "elf32-i386") != 0)
+    return GDB_OSABI_UNKNOWN;
+
+  if (!haiku_check_required_symbols (abfd))
+    return GDB_OSABI_UNKNOWN;
+
+  return GDB_OSABI_HAIKU;
+}
+
+void _initialize_i386_haiku_tdep ();
+void
+_initialize_i386_haiku_tdep ()
+{
+  gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour,
+                                  i386_haiku_osabi_sniffer);
+
+  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_HAIKU,
+                          i386_haiku_init_abi);
+}
diff --git a/gdb/solib-haiku.c b/gdb/solib-haiku.c
new file mode 100644
index 00000000..ba9e548f
--- /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 00000000..5f6a90fe
--- /dev/null
+++ b/gdb/solib-haiku.h
@@ -0,0 +1,27 @@
+/* Handle shared libraries for GDB, the GNU Debugger.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef SOLIB_HAIKU_H
+#define SOLIB_HAIKU_H
+
+struct solib_ops;
+
+extern const struct solib_ops haiku_so_ops;
+
+#endif /* solib-haiku.h */
diff --git a/gdbsupport/osabi.def b/gdbsupport/osabi.def
index 230c21f0..41d3bb6e 100644
--- a/gdbsupport/osabi.def
+++ b/gdbsupport/osabi.def
@@ -41,6 +41,7 @@ GDB_OSABI_DEF (LINUX, "GNU/Linux", "linux(-gnu[^-]*)?")
 GDB_OSABI_DEF (FREEBSD, "FreeBSD", nullptr)
 GDB_OSABI_DEF (NETBSD, "NetBSD", nullptr)
 GDB_OSABI_DEF (OPENBSD, "OpenBSD", nullptr)
+GDB_OSABI_DEF (HAIKU, "Haiku", nullptr)
 GDB_OSABI_DEF (WINCE, "WindowsCE", nullptr)
 GDB_OSABI_DEF (GO32, "DJGPP", nullptr)
 GDB_OSABI_DEF (CYGWIN, "Cygwin", nullptr)
-- 
2.43.0


  parent reply	other threads:[~2026-03-12 17:26 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-12 17:23 [PATCH 0/8] Support for Haiku/x86-64 in GDB Jérôme Duval
2026-03-12 17:23 ` [PATCH 1/8] gdbserver: Initial Haiku support Jérôme Duval
2026-03-12 19:06   ` Kevin Buettner
2026-03-12 17:23 ` Jérôme Duval [this message]
2026-03-12 18:31   ` [PATCH 2/8] gdb: " Eli Zaretskii
2026-03-12 17:23 ` [PATCH 3/8] gdb: Update Haiku support for 16.x Jérôme Duval
2026-03-12 17:23 ` [PATCH 4/8] gdbserver: " Jérôme Duval
2026-03-12 17:23 ` [PATCH 5/8] gdb: Update Haiku support for 17.x Jérôme Duval
2026-03-12 17:58   ` Tom Tromey
2026-03-12 17:23 ` [PATCH 6/8] gdbserver: " Jérôme Duval
2026-03-12 17:23 ` [PATCH 7/8] gdb: Update Haiku support for 18.x Jérôme Duval
2026-03-12 17:23 ` [PATCH 8/8] gdbserver: " Jérôme Duval
2026-03-12 17:37 ` [PATCH 0/8] Support for Haiku/x86-64 in GDB Tom Tromey
     [not found]   ` <CAPZRpdPY4Uzwu5BinMEDcU=0SQEXU0ca31-p_X_ZOJzdGcobkQ@mail.gmail.com>
2026-03-12 21:42     ` Trung Nguyen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260312172336.15450-3-jerome.duval@gmail.com \
    --to=jerome.duval@gmail.com \
    --cc=gdb-patches@sourceware.org \
    --cc=me@trungnt2910.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox