From patchwork Wed Apr 8 16:32:08 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?SsOpcsO0bWUgRHV2YWw=?= X-Patchwork-Id: 132820 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 40BBC4BA2E13 for ; Wed, 8 Apr 2026 16:33:13 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 40BBC4BA2E13 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=jsRZ7nNi X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com [IPv6:2a00:1450:4864:20::42b]) by sourceware.org (Postfix) with ESMTPS id 693474BA2E0D for ; Wed, 8 Apr 2026 16:32:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 693474BA2E0D Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 693474BA2E0D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::42b ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1775665949; cv=none; b=Tpxh5v0noFrf63bN1mSJPZXzRsrXrRoDC1JbbtkH4iskvFE2AlelzuzZUUaQ7p7b0qAv1wtph/xmJt6vbtIxD1+NewhsBMmccFOzMNyBong/UDcbfMa6jhEXi7Q1bVpqSMC+bMf+73qUTQ2uYO+NPnJCojWsfrx9byWYxCVRomI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1775665949; c=relaxed/simple; bh=RcQBog65krVZcGMAtAuOhgT42hKKgqtQTnnkHhKW0os=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=WtvQssPDBY62tuUa/kU5OcwUZwnoUUGOxMcBbFsepQGsv2vVLQu3vriMcMsnOixAydVXgAN8lURfZZJwN9xxz2ScvCkhGMU1I7yHIxIErCHhZmm/DDqytz/KzbpvJmeOmKQUZaR4RKEmDKOzxNsNkyKUXqP2w6wPDgI+plsrOfQ= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 693474BA2E0D Received: by mail-wr1-x42b.google.com with SMTP id ffacd0b85a97d-43d0deb7ad5so202f8f.2 for ; Wed, 08 Apr 2026 09:32:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775665948; x=1776270748; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=N8g1rm96kQmM7njMt0IlapDiBGmS39+qI0Lo++0gRzc=; b=jsRZ7nNi1h0dqtu3FR8WSE1GBcB/6xqEUHS6r8SNFO3PnOJBE6pnvg7HDdwBkcmnud lg0mJO37uiFA+GfTxcNfw0PPqe9WnDX57UsWyCrH9aqiBpoHg/34y6//+S2OsSHF+r6l Facc8P//clF9D6oXoHWnzCJMI7WclnvkjMrZD5mnjuIFLR7KvI4nz4g9OL3yHejFvDpG QN9MrJkvCY9Msg5GtpnuCEEQNULVdEDm+a6bNhpyfMw/BGr/jufjuheqD5rqVVmzYeEa VrNenw3sUZJPtTfGgz4ai4KxWe0J9WgUGNeJXYNFmpJYA6Ij2JCpLUnKAKtwXdAiMarD DEyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775665948; x=1776270748; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=N8g1rm96kQmM7njMt0IlapDiBGmS39+qI0Lo++0gRzc=; b=bnKytFTukVwk5kDWZfs/SShSIY+nh7Sqn/i3qiciZ2L/YCWYxA+g34sIYwZaHUVNi3 n5rRi+fKRfO9vg23VTXh8XgcHgS03jcVgTy0d1EELxIy4z9BCZjevs5+Tj5KaaDdc+zL NiB/ir6jLh18E4+t03U8Jiv94Tj6GsSyq688S2UIrvOkj1oBiA4Uv9yA9C8bqM3/eK8o Oo0paXpxHoE2SY7XWYHpZIMkA3YfliRSddQRwJrZp1uNcg7FBhII+oIRI0ZUhljllR2o +HVXaNhJoz/FTe2f0za2CcHDKmnU2Fz0134HG/hdNEK3yfVRaKQ3eGSNB8r55zvOxQ60 54NQ== X-Gm-Message-State: AOJu0YzTXMCB4ns/oSr7h2XnANaVlFem5k0fSHC1a6CUF3UoJOSbwG8v 82zXWWDAHtscaF7wE1L3l7L0SSiD7R4zbzNkizoITSPC45DnAha7C+LnHPwn3A== X-Gm-Gg: AeBDiesXXZMwcQO5nhEUSWqKro45hBR8F0CbtEiejDmfhe7A1oZhzS8zrNsqAgVNXeC NORlnakaGb1h4gwT+slm2TTjSyLCMbgo8psNlMSx6hb9/IUgvFa9MO+7BQxw/cWlUN73GD/ajFh 2V8MQV8rquMWvRA7N83iuABRUu4Z+mT76mVaVs85DUuhOwC2YJm799aLsM53YvqVpAYgkyWYXPP orWrrlp+0hncM9SmKi8NEzCOt4T1lJkGpV3hyF4TYgvxwDC5zMoF1oIF4KXgTAxh/wzVCtFv99i R/9GhT45sgmqIFzRx2nikbglZgw6/ieHRwwAmzvJjo8bxWAGckumZD/m+5aVhqBYeM/YQCOWH2o ZOssU64af6pQbj2rR3goXPlguQ1q/6urHIzq4L24Z2K+ulO3AoeyuWj65dZ/DTGyIMfUJ1gHjJn VBBQxcPKH6ZKdfFi1BFW4bsoSVipO//+KYrQigboFepLG3DQ== X-Received: by 2002:a05:6000:144a:b0:439:b55d:b0e5 with SMTP id ffacd0b85a97d-43d292cd508mr33394238f8f.28.1775665947325; Wed, 08 Apr 2026 09:32:27 -0700 (PDT) Received: from korli-neo50s.fritz.box ([2a02:1748:dd5c:c9e0:3b9a:93a3:a611:b878]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43d1e2a6f08sm62223960f8f.6.2026.04.08.09.32.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Apr 2026 09:32:26 -0700 (PDT) From: =?utf-8?b?SsOpcsO0bWUgRHV2YWw=?= To: gdb-patches@sourceware.org Cc: me@trungnt2910.com, =?utf-8?b?SsOpcsO0bWUgRHV2YWw=?= Subject: [PATCH v3 2/2] gdb: Haiku support Date: Wed, 8 Apr 2026 18:32:08 +0200 Message-ID: <20260408163208.6147-3-jerome.duval@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260408163208.6147-1-jerome.duval@gmail.com> References: <20260408163208.6147-1-jerome.duval@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~patchwork=sourceware.org@sourceware.org Initial support was done by Trung Nguyen for GDB 15.1 for GSoC 2024: See blog entries https://www.haiku-os.org/tags/gdb Original Port repository: https://github.com/trungnt2910/gdb-haiku I mostly adapted to the next major releases. Reviewed-By: Eli Zaretskii --- gdb/Makefile.in | 25 ++ gdb/NEWS | 2 + gdb/amd64-haiku-nat.c | 151 +++++++ gdb/amd64-haiku-tdep.c | 142 +++++++ gdb/configure | 2 +- gdb/configure.host | 2 + gdb/configure.nat | 15 + gdb/configure.tgt | 6 + gdb/haiku-nat.c | 776 ++++++++++++++++++++++++++++++++++++ gdb/haiku-nat.h | 75 ++++ gdb/haiku-tdep.c | 194 +++++++++ gdb/haiku-tdep.h | 44 ++ gdb/nat/haiku-nat.h | 6 +- gdb/nat/haiku-nub-message.h | 6 +- gdb/nat/haiku-osdata.h | 6 +- gdb/solib-haiku.c | 118 ++++++ gdb/solib-haiku.h | 29 ++ gdbsupport/osabi.def | 1 + 18 files changed, 1590 insertions(+), 10 deletions(-) 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/solib-haiku.c create mode 100644 gdb/solib-haiku.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 2488d789..9978b430 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 \ @@ -893,6 +911,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 \ @@ -1669,6 +1688,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 \ @@ -1809,6 +1829,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 \ @@ -1849,6 +1871,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 \ @@ -1959,6 +1983,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 4cf91053..70808aa5 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..12820f45 --- /dev/null +++ b/gdb/amd64-haiku-nat.c @@ -0,0 +1,151 @@ +/* Native-dependent code for Haiku/amd64. + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" + +#include "amd64-tdep.h" +#include "haiku-nat.h" +#include "nat/haiku-nat.h" + +/* Very conservative inclusion of Haiku headers to prevent name clashes. */ +typedef uint64_t uint64; +#include + +/* At haiku_amd64_reg_offsets[REGNUM] you'll find the offset in `struct + debug_cpu_state' where the GDB register REGNUM is stored. */ +static constexpr auto haiku_amd64_reg_offsets = [] () constexpr { + std::array result = {}; + + /* Set up the register offset table. */ +#define HAIKU_DECLARE_REG_OFFSET(gdbreg, haikureg) \ + result[AMD64_##gdbreg##_REGNUM] \ + = offsetof (struct x86_64_debug_cpu_state, haikureg) + + HAIKU_DECLARE_REG_OFFSET (RAX, rax); + HAIKU_DECLARE_REG_OFFSET (RBX, rbx); + HAIKU_DECLARE_REG_OFFSET (RCX, rcx); + HAIKU_DECLARE_REG_OFFSET (RDX, rdx); + HAIKU_DECLARE_REG_OFFSET (RSI, rsi); + HAIKU_DECLARE_REG_OFFSET (RDI, rdi); + HAIKU_DECLARE_REG_OFFSET (RBP, rbp); + HAIKU_DECLARE_REG_OFFSET (RSP, rsp); + HAIKU_DECLARE_REG_OFFSET (R8, r8); + HAIKU_DECLARE_REG_OFFSET (R9, r9); + HAIKU_DECLARE_REG_OFFSET (R10, r10); + HAIKU_DECLARE_REG_OFFSET (R11, r11); + HAIKU_DECLARE_REG_OFFSET (R12, r12); + HAIKU_DECLARE_REG_OFFSET (R13, r13); + HAIKU_DECLARE_REG_OFFSET (R14, r14); + HAIKU_DECLARE_REG_OFFSET (R15, r15); + HAIKU_DECLARE_REG_OFFSET (RIP, rip); + HAIKU_DECLARE_REG_OFFSET (EFLAGS, rflags); + HAIKU_DECLARE_REG_OFFSET (CS, cs); + HAIKU_DECLARE_REG_OFFSET (SS, ss); + HAIKU_DECLARE_REG_OFFSET (DS, ds); + HAIKU_DECLARE_REG_OFFSET (ES, es); + HAIKU_DECLARE_REG_OFFSET (FS, fs); + HAIKU_DECLARE_REG_OFFSET (GS, gs); + +#undef HAIKU_DECLARE_REG_OFFSET + + return result; +}(); + +struct amd64_haiku_nat_target final : public haiku_nat_target +{ + void fetch_registers (regcache *, int) override; + void store_registers (regcache *, int) override; +}; + +void +amd64_haiku_nat_target::fetch_registers (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 (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; + +INIT_GDB_FILE (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..ab25fb2d --- /dev/null +++ b/gdb/amd64-haiku-tdep.c @@ -0,0 +1,142 @@ +/* Target-dependent code for Haiku/amd64. + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" + +#include "amd64-tdep.h" +#include "extract-store-integer.h" +#include "haiku-tdep.h" +#include "solib.h" + +static int +amd64_haiku_sigtramp_p (const frame_info_ptr &this_frame) +{ + CORE_ADDR pc = get_frame_pc (this_frame); + const char *solib_name + = solib_name_from_address (get_frame_program_space (this_frame), pc); + + if (solib_name == nullptr || strcmp (solib_name, "commpage") != 0) + return false; + + const char *name; + find_pc_partial_function (pc, &name, NULL, NULL); + + if (name == nullptr || strcmp (name, "commpage_signal_handler") != 0) + return false; + + return true; +} + +/* Offset to mcontext_t in signal_frame_data, + from headers/private/kernel/ksignal.h. + + The struct is private so it may change anytime. + However, the first two members of the struct are siginfo_t and ucontext_t, + which are public and relatively stable. */ +#define AMD64_HAIKU_SIGNAL_FRAME_DATA_MCONTEXT_OFFSET 96 + +static CORE_ADDR +amd64_haiku_sigcontext_addr (const frame_info_ptr &this_frame) +{ + 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 (gdbarch_info info, gdbarch *gdbarch) +{ + i386_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + amd64_init_abi (info, gdbarch, + amd64_target_description (X86_XSTATE_SSE_MASK, true)); + haiku_init_abi (info, gdbarch); + + tdep->sigtramp_p = amd64_haiku_sigtramp_p; + tdep->sigcontext_addr = amd64_haiku_sigcontext_addr; + tdep->sc_reg_offset = amd64_haiku_sc_reg_offset; + tdep->sc_num_regs = ARRAY_SIZE (amd64_haiku_sc_reg_offset); + + /* The offset of the PC in the jmp_buf structure. + Found at src/system/libroot/posix/arch/x86_64/setjmp_internal.h. */ + tdep->jb_pc_offset = 0; +} + +static enum gdb_osabi +amd64_haiku_osabi_sniffer (bfd *abfd) +{ + const char *target_name = bfd_get_target (abfd); + + if (strcmp (target_name, "elf64-x86-64") != 0) + return GDB_OSABI_UNKNOWN; + + if (!haiku_check_required_symbols (abfd)) + return GDB_OSABI_UNKNOWN; + + return GDB_OSABI_HAIKU; +} + +INIT_GDB_FILE (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 2ff36178..f25c47b1 100755 --- a/gdb/configure +++ b/gdb/configure @@ -20292,7 +20292,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..286d2fe7 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,16 @@ case ${gdb_host} in ;; 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..70c17ff4 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. @@ -754,6 +756,10 @@ 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}" + ;; 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..74328753 --- /dev/null +++ b/gdb/haiku-nat.c @@ -0,0 +1,776 @@ +/* Native-dependent code for Haiku. + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" +#include "inferior.h" + +#include "cli/cli-cmds.h" +#include "exec.h" +#include "gdb/inf-loop.h" +#include "gdbcore.h" +#include "gdbsupport/buildargv.h" +#include "gdbsupport/event-loop.h" +#include "haiku-nat.h" +#include "nat/fork-inferior.h" +#include "nat/haiku-nat.h" +#include "nat/haiku-osdata.h" +#include "objfiles.h" +#include "observable.h" +#include "solib.h" + +haiku_nat_target *haiku_target; + +bool debug_haiku_nat = false; + +static void haiku_enable_breakpoints_if_ready (inferior *inf); + +void +haiku_nat_target::create_inferior (const char *exec_file, + const std::string &allargs, char **env, + int from_tty) +{ + haiku_nat_debug_printf ("exec_file=%s", exec_file); + + inferior *inf = current_inferior (); + + /* Do not change either targets above or the same target if already present. + The reason is the target stack is shared across multiple inferiors. */ + int ops_already_pushed = inf->target_is_pushed (this); + + target_unpush_up unpusher; + if (!ops_already_pushed) + { + /* Clear possible core file with its process_stratum. */ + inf->push_target (this); + unpusher.reset (this); + } + + if (disable_randomization) + inf->environment.set ("DISABLE_ASLR", "1"); + + static const auto haiku_traceme = [] () { + /* This happens before the child calls exec(). + The debugger is responsible for resuming the inferior before it + loads the desired target. */ + haiku_nat::wait_for_debugger (); + }; + + static const auto haiku_init_trace = [] (int pid) { + haiku_nat_debug_printf ("haiku_init_trace: pid=%i", pid); + if (haiku_nat::attach (pid, true) < 0) + trace_start_error_with_name (("haiku_nat::attach")); + + /* At this stage, the child is being stopped for the first debugger event. + It has NOT exec'ed into the desired target yet, but is still a gdbserver + stuck in a wait_for_debugger() call. */ + + /* Consume the initial event. */ + target_waitstatus ourstatus; + if (haiku_nat::wait (ptid_t (pid), &ourstatus, 0) == minus_one_ptid) + perror_with_name (_("haiku_nat::wait")); + + /* Allows the child to proceed to exec. */ + if (haiku_nat::resume (ptid_t (pid), resume_continue, 0) < 0) + perror_with_name (_("haiku_nat::resume")); + }; + + /* 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_internal_state (this, thr->ptid, THREAD_INT_RUNNING); + + 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 = "\n"; + haiku_nat::for_each_image ( + current_inferior ()->pid, [&] (const haiku_nat::image_info &info) { + if (!info.is_main_executable) + { + document += string_printf ( + " " + "\n", + info.name, + paddress (current_inferior ()->arch (), info.text)); + } + return 0; + }); + document += "\n"; + + if (offset >= document.size ()) + return TARGET_XFER_EOF; + + len = std::min (len, document.size () - offset); + memcpy (readbuf, document.c_str () + offset, len); + + *xfered_len = len; + + return TARGET_XFER_OK; + } + break; + case TARGET_OBJECT_OSDATA: + { + if (writebuf != nullptr) + return TARGET_XFER_UNAVAILABLE; + + *xfered_len = haiku_common_xfer_osdata (annex, readbuf, offset, len); + + return (*xfered_len > 0) ? TARGET_XFER_OK : TARGET_XFER_EOF; + } + default: + haiku_nat_debug_printf ("Unimplemented xfer object: %i", (int)object); + } + + return inf_child_target::xfer_partial (object, annex, readbuf, writebuf, + offset, len, xfered_len); +} + +std::vector +haiku_nat_target::memory_map () +{ + std::vector result; + + haiku_nat::for_each_area ( + current_inferior ()->pid, [&] (const haiku_nat::area_info &info) { + /* While some regions appear read-only to the user, + as the debugger, we can write anywhere. + + If this is set otherwise, software breakpoints in read-only + regions (such as shared libraries) will not work. */ + result.emplace_back (info.address, info.address + info.size, MEM_RW); + return 0; + }); + + return result; +} + +bool +haiku_nat_target::supports_multi_process () +{ + return true; +} + +bool +haiku_nat_target::supports_disable_randomization () +{ + return true; +} + +bool +haiku_nat_target::info_proc (const char *args, enum info_proc_what what) +{ + pid_t pid; + bool do_cmdline = false; + bool do_exe = false; + bool do_mappings = false; + bool do_status = false; + + switch (what) + { + case IP_MINIMAL: + do_cmdline = true; + do_exe = true; + break; + case IP_STAT: + case IP_STATUS: + do_status = true; + break; + case IP_MAPPINGS: + do_mappings = true; + break; + case IP_CMDLINE: + do_cmdline = true; + break; + case IP_EXE: + do_exe = true; + break; + case IP_CWD: + /* There is no obvious method of getting the CWD of a different team. + _kern_get_extended_team_info might provide what we want, but the + syscall stores the result in a private class "KMessage" instead of + normal structs. */ + return false; + case IP_ALL: + do_cmdline = true; + do_exe = true; + do_mappings = true; + do_status = true; + break; + default: + error (_ ("Not supported on this target.")); + } + + gdb_argv built_argv (args); + if (built_argv.count () == 0) + { + pid = inferior_ptid.pid (); + if (pid == 0) + error (_ ("No current team: you must name one.")); + } + else if (built_argv.count () == 1 && isdigit (built_argv[0][0])) + pid = strtol (built_argv[0], NULL, 10); + else + error (_ ("Invalid arguments.")); + + gdb_printf (_ ("team %d\n"), pid); + + const haiku_nat::team_info *info = nullptr; + + if (do_cmdline || do_status) + info = haiku_nat::get_team (pid); + + if (do_cmdline) + { + if (info != nullptr) + gdb_printf ("cmdline = '%s'\n", info->args); + else + warning (_ ("unable to fetch command line")); + } + + if (do_exe) + { + const char *exe = pid_to_exec_file (pid); + if (exe != nullptr) + gdb_printf ("exe = '%s'\n", exe); + else + warning (_ ("unable to fetch executable path name")); + } + + if (do_mappings) + { + bool first = true; + if (haiku_nat::for_each_area ( + pid, + [&] (const haiku_nat::area_info &area_info) { + if (first) + { + gdb_printf (_ ("Mapped areas:\n\n")); + gdb_printf ("%6s %18s %10s %10s %6s %6s %5s %5s %s\n", + "ID", "address", "size", "alloc.", "prot", + "#-cow", "#-in", "#-out", "name"); + first = false; + } + + std::string prot; + if (area_info.can_read) + prot += "r"; + if (area_info.can_write) + prot += "w"; + if (area_info.can_exec) + prot += "x"; + if (area_info.is_stack) + prot += "s"; + if (area_info.can_clone) + prot += "c"; + + gdb_printf ("%6s %18s %10s %10s %6s %6s %5s %5s %s\n", + plongest (area_info.id), + core_addr_to_string (area_info.address), + phex_nz (area_info.size, 0), + phex_nz (area_info.ram_size, 0), prot.c_str (), + pulongest (area_info.copy_count), + pulongest (area_info.in_count), + pulongest (area_info.out_count), area_info.name); + + return 0; + }) + < 0) + { + warning (_ ("unable to fetch virtual memory map")); + } + } + + if (do_status) + { + if (info != nullptr) + { + gdb_printf ("Name: %s\n", info->name); + gdb_printf ("Parent team: %s\n", plongest (info->parent)); + gdb_printf ("Process group: %s\n", plongest (info->group_id)); + gdb_printf ("Session id: %s\n", plongest (info->session_id)); + gdb_printf ("User IDs (real, effective): %s %s\n", + plongest (info->real_uid), plongest (info->uid)); + gdb_printf ("Group IDs (real, effective): %s %s\n", + plongest (info->real_gid), plongest (info->gid)); + gdb_printf ("Thread count: %s\n", pulongest (info->thread_count)); + gdb_printf ("Image count: %s\n", pulongest (info->image_count)); + gdb_printf ("Area count: %s\n", pulongest (info->area_count)); + gdb_printf ("Debugger nub thread: %s\n", + plongest (info->debugger_nub_thread)); + gdb_printf ("Debugger nub port: %s\n", + plongest (info->debugger_nub_port)); + } + else + warning (_ ("unable to fetch team information")); + } + + return true; +} + +/* Utilities. */ + +static void +haiku_relocate_main_executable (inferior *inf) +{ + CORE_ADDR text; + CORE_ADDR data; + + if (haiku_nat::read_offsets (inf->pid, &text, &data) < 0) + return; + + CORE_ADDR displacement = text; + + if (inf->pspace->exec_bfd ()) + { + asection *asect; + + bfd *exec_bfd = inf->pspace->exec_bfd (); + for (asect = exec_bfd->sections; asect != NULL; asect = asect->next) + exec_set_section_address (bfd_get_filename (exec_bfd), asect->index, + bfd_section_vma (asect) + displacement); + } + + if (inf->pspace->symfile_object_file == nullptr) + symbol_file_add_main (inf->pspace->exec_filename (), + 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 ()) + != 0) + { + /* Not ready yet. The inferior is still executing a wrapper + (usually bash). */ + return; + } + + /* Refresh the regions so that write operations can be done correctly. */ + invalidate_target_mem_regions (); + + /* We can get correct offsets and relocate now. */ + haiku_relocate_main_executable (inf); + + enable_breakpoints_after_startup (); +} + +/* Supply other required functions. */ + +namespace haiku_nat +{ + +void +debugger_output (const char *message) +{ + gdb_printf ("%s\n", message); +} + +void +image_created (ptid_t ptid, const image_info &info) +{ + haiku_nat_debug_printf ("ptid=%s, name=%s, text=%p", + ptid.to_string ().c_str (), info.name, + (void *)info.text); + + /* To be handled by solib-haiku.c. */ +} + +void +image_deleted (ptid_t ptid, const image_info &info) +{ + haiku_nat_debug_printf ("ptid=%s, name=%s", ptid.to_string ().c_str (), + info.name); + + if (info.is_main_executable) + { + /* This means all images have been deleted. This usually signals that + the Haiku team just called exec. + + We want to disable breakpoints for now to prevent those pointing to + the main executable from causing issues with unrelocated addresses. + Then, after the creation or exec call completes and the new inferior + gets finalized, we can relocate and enable these breakpoints again. + + We also cannot disable the breakpoints later than this. After the + event, images for the new executable starts loading. Disabling the + breakpoints causes GDB to write bogus data back to the fresh + binaries. */ + + disable_breakpoints_before_startup (); + invalidate_target_mem_regions (); + } + + /* The rest to be handled by solib-haiku.c. */ +} + +bool +is_catching_syscalls_for (ptid_t ptid) +{ + inferior *inf = find_inferior_ptid (haiku_target, ptid); + if (inf == nullptr) + return false; + + std::optional maybe_restore_thread + = maybe_switch_inferior (inf); + + return catch_syscall_enabled () > 0; +} + +} + +/* Initialization. */ + +INIT_GDB_FILE (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..01e2e6de --- /dev/null +++ b/gdb/haiku-nat.h @@ -0,0 +1,75 @@ +/* Native-dependent code for Haiku. + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "inf-child.h" + +/* A prototype Haiku target. */ + +struct haiku_nat_target : public inf_child_target +{ + void create_inferior (const char *, const std::string &, char **, + int) override; + + void attach (const char *, int) override; + + void detach (inferior *, int) override; + + void resume (ptid_t, int, enum gdb_signal) override; + + ptid_t wait (ptid_t, target_waitstatus *, target_wait_flags) override; + + void files_info () override; + + void kill () override; + + void follow_exec (inferior *, ptid_t, const char *) override; + + bool thread_alive (ptid_t) override; + void update_thread_list () override; + std::string pid_to_str (ptid_t) override; + + const char *thread_name (thread_info *) override; + + void stop (ptid_t) override; + + const char *pid_to_exec_file (int) override; + + bool can_async_p () override; + bool is_async_p () override; + void async (bool) override; + int async_wait_fd () override; + + bool supports_non_stop () override; + bool always_non_stop_p () override; + + enum target_xfer_status xfer_partial (enum target_object, const char *, + gdb_byte *, const gdb_byte *, ULONGEST, + ULONGEST, ULONGEST *) override; + + std::vector memory_map () override; + + bool supports_multi_process () override; + + bool supports_disable_randomization () override; + + bool info_proc (const char *, enum info_proc_what) override; +}; + +/* The final/concrete instance. */ +extern haiku_nat_target *haiku_target; diff --git a/gdb/haiku-tdep.c b/gdb/haiku-tdep.c new file mode 100644 index 00000000..8985d416 --- /dev/null +++ b/gdb/haiku-tdep.c @@ -0,0 +1,194 @@ +/* Common target-dependent code for Haiku systems. + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" + +#include "bfd.h" +#include "elf-bfd.h" +#include "gdbarch.h" +#include "gdbcore.h" +#include "haiku-tdep.h" +#include "inferior.h" +#include "osdata.h" +#include "solib-haiku.h" + +/* See haiku-tdep.h. */ + +void +haiku_init_abi (gdbarch_info info, gdbarch *gdbarch) +{ + set_gdbarch_make_solib_ops (gdbarch, make_haiku_solib_ops); +} + +/* See haiku-tdep.h. */ + +bool +haiku_check_required_symbols (bfd *abfd) +{ + long storage_needed = bfd_get_symtab_upper_bound (abfd); + if (storage_needed <= 0) + return false; + + gdb::unique_xmalloc_ptr symbol_table ( + (asymbol **)xmalloc (storage_needed)); + long number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table.get ()); + + if (number_of_symbols <= 0) + return false; + + for (long i = 0; i < number_of_symbols; ++i) + { + const char *name = bfd_asymbol_name (symbol_table.get ()[i]); + + if (strcmp (name, "_gSharedObjectHaikuVersion") == 0) + return true; + } + + return false; +} + +/* See haiku-tdep.h. */ + +gdb_bfd_ref_ptr +haiku_bfd_open_commpage () +{ + /* Get any valid BFD object as a template. + Otherwise, GDB will complain with a segfault. */ + bfd *tmpbfd = current_inferior ()->pspace->exec_bfd (); + if (tmpbfd == nullptr) + tmpbfd = get_inferior_core_bfd (current_inferior ()); + if (tmpbfd == nullptr) + return nullptr; + + /* Create a hollow BFD object. */ + bfd *nbfd = bfd_create ("commpage", tmpbfd); + if (nbfd == nullptr) + return nullptr; + + /* Close in case of failure. */ + std::unique_ptr bfd_deleter (nbfd, bfd_close); + + /* Prepare the BFD for writing. */ + if (!bfd_make_writable (nbfd)) + return nullptr; + + asection *section = bfd_make_section (nbfd, ".text"); + section->size = HAIKU_COMMPAGE_SIZE; + + /* Read the commpage symbols from the target. */ + std::unique_ptr comm_data = get_osdata ("comm"); + gdb_assert (comm_data->type == "comm"); + + size_t sym_count = comm_data->items.size (); + + asymbol **symtab + = (asymbol **)bfd_alloc (nbfd, (sym_count + 1) * sizeof (asymbol *)); + + for (size_t i = 0; i < sym_count; ++i) + { + elf_symbol_type *sym = (elf_symbol_type *)bfd_make_empty_symbol (nbfd); + sym->symbol.section = section; + + for (const auto &[name, value] : comm_data->items[i].columns) + { + if (name == "name") + { + char *tmp = (char *)bfd_alloc (nbfd, value.size () + 1); + memcpy (tmp, value.c_str (), value.size () + 1); + bfd_set_asymbol_name (&sym->symbol, tmp); + } + else if (name == "value") + { + sym->symbol.value = strtoulst (value.c_str (), nullptr, 10); + sym->internal_elf_sym.st_value = sym->symbol.value; + } + else if (name == "size") + { + sym->internal_elf_sym.st_size + = strtoulst (value.c_str (), nullptr, 10); + } + else if (name == "type") + { + sym->symbol.flags = BSF_GLOBAL; + for (char flag : value) + { + switch (flag) + { + case 'f': + sym->symbol.flags |= BSF_FUNCTION; + break; + case 'o': + sym->symbol.flags |= BSF_OBJECT; + break; + } + } + } + } + + symtab[i] = (asymbol *)sym; + } + + symtab[sym_count] = nullptr; + + /* Write the symbol table. */ + if (!bfd_set_symtab (nbfd, symtab, sym_count)) + return nullptr; + + /* Prepare the BFD for reading by GDB. */ + if (!bfd_make_readable (nbfd)) + return nullptr; + + bfd_deleter.release (); + + return gdb_bfd_ref_ptr::new_reference (nbfd); +} + +/* See haiku-tdep.h. */ + +CORE_ADDR +haiku_get_commpage_address () +{ + /* Read the images from the target. */ + std::unique_ptr images = get_osdata ("images"); + gdb_assert (images->type == "images"); + + std::string current_team = std::to_string (current_inferior ()->pid); + + for (const auto &item : images->items) + { + bool matches_team = false; + bool matches_name = false; + const char *text_value = nullptr; + + for (const auto &[name, value] : item.columns) + { + if (name == "team") + matches_team = value == current_team; + else if (name == "name") + matches_name = value == "commpage"; + else if (name == "text") + text_value = value.c_str (); + } + + if (matches_team && matches_name && text_value != nullptr) + return string_to_core_addr (text_value); + } + + return 0; +} diff --git a/gdb/haiku-tdep.h b/gdb/haiku-tdep.h new file mode 100644 index 00000000..58b3a09c --- /dev/null +++ b/gdb/haiku-tdep.h @@ -0,0 +1,44 @@ +/* Common target-dependent definitions for Haiku systems. + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef GDB_HAIKU_TDEP_H +#define GDB_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, gdbarch *); + +/* Used by OS ABI sniffers to check for Haiku-specific symbols. */ + +bool haiku_check_required_symbols (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 /* GDB_HAIKU_TDEP_H */ diff --git a/gdb/nat/haiku-nat.h b/gdb/nat/haiku-nat.h index 0825be9d..0838064a 100644 --- a/gdb/nat/haiku-nat.h +++ b/gdb/nat/haiku-nat.h @@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef NAT_HAIKU_NAT_H -#define NAT_HAIKU_NAT_H +#ifndef GDB_NAT_HAIKU_NAT_H +#define GDB_NAT_HAIKU_NAT_H #include @@ -426,4 +426,4 @@ extern bool debug_haiku_nat; #define HAIKU_NAT_SCOPED_DEBUG_ENTER_EXIT \ scoped_debug_enter_exit (debug_haiku_nat, "haiku-nat") -#endif /* NAT_HAIKU_NAT_H */ +#endif /* GDB_NAT_HAIKU_NAT_H */ diff --git a/gdb/nat/haiku-nub-message.h b/gdb/nat/haiku-nub-message.h index 5fdc409b..eb4cefcf 100644 --- a/gdb/nat/haiku-nub-message.h +++ b/gdb/nat/haiku-nub-message.h @@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef NAT_HAIKU_NUB_MESSAGE_H -#define NAT_HAIKU_NUB_MESSAGE_H +#ifndef GDB_NAT_HAIKU_NUB_MESSAGE_H +#define GDB_NAT_HAIKU_NUB_MESSAGE_H #include "gnulib/config.h" @@ -138,4 +138,4 @@ haiku_send_nub_message (port_id nub_port, return (result < B_OK) ? result : reply.error; } -#endif /* NAT_HAIKU_NUB_MESSAGE_H */ +#endif /* GDB_NAT_HAIKU_NUB_MESSAGE_H */ diff --git a/gdb/nat/haiku-osdata.h b/gdb/nat/haiku-osdata.h index 176814e7..7ead2ca8 100644 --- a/gdb/nat/haiku-osdata.h +++ b/gdb/nat/haiku-osdata.h @@ -17,10 +17,10 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#ifndef NAT_HAIKU_OSDATA_H -#define NAT_HAIKU_OSDATA_H +#ifndef GDB_NAT_HAIKU_OSDATA_H +#define GDB_NAT_HAIKU_OSDATA_H extern LONGEST haiku_common_xfer_osdata (const char *annex, gdb_byte *readbuf, ULONGEST offset, ULONGEST len); -#endif /* NAT_HAIKU_OSDATA_H */ +#endif /* GDB_NAT_HAIKU_OSDATA_H */ diff --git a/gdb/solib-haiku.c b/gdb/solib-haiku.c new file mode 100644 index 00000000..31de8158 --- /dev/null +++ b/gdb/solib-haiku.c @@ -0,0 +1,118 @@ +/* Handle shared libraries for GDB, the GNU Debugger. + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" + +#include "exec.h" +#include "haiku-tdep.h" +#include "inferior.h" +#include "objfiles.h" +#include "solib.h" +#include "solib-haiku.h" +#include "solib-target.h" + + +/* solib_ops for Haiku systems. */ + +struct haiku_solib_ops : public target_solib_ops +{ + using target_solib_ops::target_solib_ops; + + void relocate_section_addresses (solib &so, target_section *) const override; + void clear_solib (program_space *pspace) const override; + void create_inferior_hook (int from_tty) const override; + owning_intrusive_list current_sos () const override; + bool open_symbol_file_object (int from_tty) const override; + bool in_dynsym_resolve_code (CORE_ADDR pc) const override; + gdb_bfd_ref_ptr bfd_open (const char *pathname) const override; +}; + +/* See solib-haiku.h. */ + +solib_ops_up +make_haiku_solib_ops (program_space *pspace) +{ + return std::make_unique (pspace); +} + + +/* 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. */ + +void +haiku_solib_ops::relocate_section_addresses (solib &so, + target_section *sec) const +{ + if (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; + } +} + +void +haiku_solib_ops::clear_solib (program_space *pspace) const +{ + target_solib_ops::clear_solib (pspace); +} + +void +haiku_solib_ops::create_inferior_hook (int from_tty) const +{ + target_solib_ops::create_inferior_hook (from_tty); +} + +owning_intrusive_list +haiku_solib_ops::current_sos () const +{ + return target_solib_ops::current_sos (); +} + +bool +haiku_solib_ops::open_symbol_file_object (int from_tty) const +{ + return target_solib_ops::open_symbol_file_object (from_tty); +} + +bool +haiku_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const +{ + /* No dynamic resolving implemented in Haiku yet. + Return what the generic code has to say. */ + return target_solib_ops::in_dynsym_resolve_code (pc); +} + +gdb_bfd_ref_ptr +haiku_solib_ops::bfd_open (const char *pathname) const +{ + if (strcmp (pathname, "commpage") == 0) + return haiku_bfd_open_commpage (); + return target_solib_ops::bfd_open (pathname); +} diff --git a/gdb/solib-haiku.h b/gdb/solib-haiku.h new file mode 100644 index 00000000..d8786a83 --- /dev/null +++ b/gdb/solib-haiku.h @@ -0,0 +1,29 @@ +/* Handle shared libraries for GDB, the GNU Debugger. + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef GDB_SOLIB_HAIKU_H +#define GDB_SOLIB_HAIKU_H + +#include "solib.h" + +/* Return a new solib_ops for Haiku systems. */ + +extern solib_ops_up make_haiku_solib_ops (program_space *pspace); + +#endif /* GDB_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)