From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id meXSNlr3smkDPCYAWB0awg (envelope-from ) for ; Thu, 12 Mar 2026 13:26:50 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=JoKsws4C; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id D8A84378002; Thu, 12 Mar 2026 13:26:50 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-2.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_MSPIKE_H2,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 417D51E089 for ; Thu, 12 Mar 2026 13:26:48 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id AA3C24BB58F8 for ; Thu, 12 Mar 2026 17:26:47 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AA3C24BB58F8 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=JoKsws4C Received: from mail-wr1-x42c.google.com (mail-wr1-x42c.google.com [IPv6:2a00:1450:4864:20::42c]) by sourceware.org (Postfix) with ESMTPS id 24A034BA23E4 for ; Thu, 12 Mar 2026 17:25:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 24A034BA23E4 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 24A034BA23E4 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::42c ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773336349; cv=none; b=G5jkhSjhGVdev4JSDPWHd0u4z08iIRZJIuQ3dKrI3zEQQKKTByr1mDCRxocasMg1dQHKW53P4FYcviqpe9FUSjpZyFHHHAJ8VeLywI9IAEQBNh8JIpXpsW+w+gOgbLVbEwJqfTckT7hOtdOC2nOJBtLxyteTKsUVSrSRS+6VKm4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773336349; c=relaxed/simple; bh=SbxYqJk5Sfn7Ldi/oC1YXgWyU7BZrXfDRSF3Nr8sMYY=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=XAD73I3tjqIB0PDm3jgJ1LfpnEOC7wsyVoyLMo24P7QpLOsowFeGpTG+9EpJJVIzOyjaDF7KcnCKye6Nb21iK70ONtowVYYkOPFauPe0KVZZ3HYL3qKFk8kRPMu4NcalE8Gmc1RsNmRyWFSndaF3zfyco+BcLfnH/oWgEsVmHOc= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 24A034BA23E4 Received: by mail-wr1-x42c.google.com with SMTP id ffacd0b85a97d-439c944bb62so1037060f8f.3 for ; Thu, 12 Mar 2026 10:25:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773336348; x=1773941148; 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=cNlGl3k5fNPgVLODe2sEPTbq/TDnp+ZLmVrBCYs7UQ0=; b=JoKsws4Cy+lFeImyDazwYlwFTEzE0+zObPZTm5I4/tL0dPhzRTev7YGLXL2ixLoxBZ 5gobMY3YP7F3u1jaNAXb0QQ/mK2n3o7P+Sl6zG6HRZ5H4kQ/JYQh1U2Yvrgc7Gm0yjvn QsbgOQtnfUvCFmuXYDY0Hwpq3/PIj6kcvNOL8amGlku8fvyM+Or9+HgcVOVkPLj6+ppL WNpmzc7brHz7QRzTqSV0TnmsEt+1Uzcem18FoDfzuEk7xAs66S/8BT38bKkTQQzPSqLL YJdSqsGIP5EuAYCc+yd/AiWUcqXVob7GOGex4v+q5rkmKbol8MEjwdZXoi53OX+MFY3Q iBOQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1773336348; x=1773941148; 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=cNlGl3k5fNPgVLODe2sEPTbq/TDnp+ZLmVrBCYs7UQ0=; b=fLNZZX6cm52XNl22W/JumJWiYvX+TuS+NZS7UEq6bZ4Nmw7wPfAhIVRN4dODaW/SqY CpB0NsIuxQR33bbp/msA8R7t5tm1ZQmOMHJ3qpJrIbf+i9bT+9daVnIxV7YSaGQiRwCt BbbqKko68cuLqWjPst9eTKVdIY0xcCOl7mc8FsMps8BA7gv2F/BUxjHkPreqZVikzRpy 7zm/w7ckf4JcNv/29wJ6VmEPtSu5+Qa2vKAcTkUwx4YfVtS+IfxbUbttAwUC7PZa8qlI Bq30vyC7QyWGZrWHMxr//KDcpqINHbZO1ecyGB6ZGaKoiD+tJJalhBTMlCw00tqsNjGS TYrw== X-Gm-Message-State: AOJu0YwaKXDNimUJXS5SofPWZkFRlvwA/iKS7DwnRYX1iUJxzPlnKkHY IPnYOCGIhh6CxqOKVceU8nbmtyarg2s6ShKo3uW8MXMgOZ6P5GdHdtIPTUSFZw== X-Gm-Gg: ATEYQzwhHRRD5nJsGpGMYeRcmFoQqyn468ymmO+uRyOm4n6yJOo9VNjdtLW9lS6sEDz dIjgegBpyRFLSex5tGZVB6gQTV6JUreEmgxAp6eMbDHFwill4CWUjW8ZFPsvCko7Pdw9pT4lkz3 1Qt37OapiSqktKunLBXy9Ug5MEYoTNwcTKupkiI0ZsmzzpgpfSzyubqnZDrh4Q+XqkWP4fwUX4s 930IJRCwIdbCDAHcEQ2CkwmBRLb8qjguyE2BTbEy22A49Vq9SqOeLkX4MKSWkAcLAxP8eftIyn3 f909zG4k0e5pQHTZWJSZYg0Qb9Vn4fJ2Db5DKflgbc7GcBKZLhhQ/z1gYFyoaCacMFQfKvT58gv X5c8mV7NEY8skDFzAI8k1ZArtiUpKPoHouykYMqtqmbB+AO516qBYk5GluvDjXPWG2LFNI5OToG x90G0U2+ia/uM0Y6UZE7P7tx5ZjVtbRbYqNehFnLz9Rjxajw== X-Received: by 2002:a05:6000:2404:b0:439:ab3d:1c2e with SMTP id ffacd0b85a97d-43a04d7b3c1mr1002645f8f.10.1773336346328; Thu, 12 Mar 2026 10:25:46 -0700 (PDT) Received: from korli-neo50s.fritz.box ([2a02:1748:dd5c:c9e0:d9f5:e2d7:f7e3:a577]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-439fe20b544sm10357893f8f.20.2026.03.12.10.25.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2026 10:25:46 -0700 (PDT) From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= To: gdb-patches@sourceware.org Cc: Trung Nguyen Subject: [PATCH 2/8] gdb: Initial Haiku support Date: Thu, 12 Mar 2026 18:23:30 +0100 Message-ID: <20260312172336.15450-3-jerome.duval@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260312172336.15450-1-jerome.duval@gmail.com> References: <20260312172336.15450-1-jerome.duval@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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~public-inbox=simark.ca@sourceware.org From: Trung Nguyen --- 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 . */ + +#include "defs.h" + +#include "amd64-tdep.h" +#include "haiku-nat.h" +#include "nat/haiku-nat.h" + +/* Very conservative inclusion of Haiku headers to prevent name clashes. */ +typedef uint64_t uint64; +#include + +/* At haiku_amd64_reg_offsets[REGNUM] you'll find the offset in `struct + debug_cpu_state' where the GDB register REGNUM is stored. */ +static constexpr auto haiku_amd64_reg_offsets = [] () constexpr { + std::array result = {}; + + /* Set up the register offset table. */ +#define HAIKU_DECLARE_REG_OFFSET(gdbreg, haikureg) \ + result[AMD64_##gdbreg##_REGNUM] \ + = offsetof (struct x86_64_debug_cpu_state, haikureg) + + HAIKU_DECLARE_REG_OFFSET (RAX, rax); + HAIKU_DECLARE_REG_OFFSET (RBX, rbx); + HAIKU_DECLARE_REG_OFFSET (RCX, rcx); + HAIKU_DECLARE_REG_OFFSET (RDX, rdx); + HAIKU_DECLARE_REG_OFFSET (RSI, rsi); + HAIKU_DECLARE_REG_OFFSET (RDI, rdi); + HAIKU_DECLARE_REG_OFFSET (RBP, rbp); + HAIKU_DECLARE_REG_OFFSET (RSP, rsp); + HAIKU_DECLARE_REG_OFFSET (R8, r8); + HAIKU_DECLARE_REG_OFFSET (R9, r9); + HAIKU_DECLARE_REG_OFFSET (R10, r10); + HAIKU_DECLARE_REG_OFFSET (R11, r11); + HAIKU_DECLARE_REG_OFFSET (R12, r12); + HAIKU_DECLARE_REG_OFFSET (R13, r13); + HAIKU_DECLARE_REG_OFFSET (R14, r14); + HAIKU_DECLARE_REG_OFFSET (R15, r15); + HAIKU_DECLARE_REG_OFFSET (RIP, rip); + HAIKU_DECLARE_REG_OFFSET (EFLAGS, rflags); + HAIKU_DECLARE_REG_OFFSET (CS, cs); + HAIKU_DECLARE_REG_OFFSET (SS, ss); + HAIKU_DECLARE_REG_OFFSET (DS, ds); + HAIKU_DECLARE_REG_OFFSET (ES, es); + HAIKU_DECLARE_REG_OFFSET (FS, fs); + HAIKU_DECLARE_REG_OFFSET (GS, gs); + +#undef HAIKU_DECLARE_REG_OFFSET + + return result; +}(); + +struct amd64_haiku_nat_target final : public haiku_nat_target +{ + void fetch_registers (struct regcache *, int) override; + void store_registers (struct regcache *, int) override; +}; + +void +amd64_haiku_nat_target::fetch_registers (struct regcache *regcache, int regno) +{ + union + { + char data[sizeof (x86_64_debug_cpu_state)]; + x86_64_debug_cpu_state state; + }; + + if (haiku_nat::get_cpu_state (regcache->ptid (), &state) < 0) + { + /* This happens when the inferior is killed by another process + while being stopped. The nub port has been deleted, so we cannot + send the required message to get the CPU state. */ + haiku_nat_debug_printf ("Failed to get actual CPU state: %s", + strerror (errno)); + memset (&state, 0, sizeof (state)); + } + + if (regno == -1) + { + for (int i = 0; i < AMD64_NUM_GREGS; ++i) + regcache->raw_supply (i, data + haiku_amd64_reg_offsets[i]); + amd64_supply_fxsave (regcache, regno, &state.extended_registers); + } + else + { + if (regno < AMD64_NUM_GREGS) + regcache->raw_supply (regno, data + haiku_amd64_reg_offsets[regno]); + else + amd64_supply_fxsave (regcache, regno, &state.extended_registers); + } +} + +void +amd64_haiku_nat_target::store_registers (struct regcache *regcache, int regno) +{ + union + { + char data[sizeof (x86_64_debug_cpu_state)]; + x86_64_debug_cpu_state state; + }; + + if (haiku_nat::get_cpu_state (regcache->ptid (), &state) < 0) + { + haiku_nat_debug_printf ("Failed to get actual CPU state: %s", + strerror (errno)); + return; + } + + if (regno == -1) + { + for (int i = 0; i < AMD64_NUM_GREGS; ++i) + regcache->raw_collect (i, data + haiku_amd64_reg_offsets[i]); + amd64_collect_fxsave (regcache, regno, &state.extended_registers); + } + else + { + if (regno < AMD64_NUM_GREGS) + regcache->raw_collect (regno, data + haiku_amd64_reg_offsets[regno]); + else + amd64_collect_fxsave (regcache, regno, &state.extended_registers); + } + + if (haiku_nat::set_cpu_state (regcache->ptid (), &state) < 0) + perror_with_name (("haiku_nat::set_cpu_state")); +} + +static amd64_haiku_nat_target the_amd64_haiku_nat_target; + +void _initialize_amd64_haiku_nat (); +void +_initialize_amd64_haiku_nat () +{ + haiku_target = &the_amd64_haiku_nat_target; + + add_inf_child_target (&the_amd64_haiku_nat_target); +} diff --git a/gdb/amd64-haiku-tdep.c b/gdb/amd64-haiku-tdep.c new file mode 100644 index 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 . */ + +#include "defs.h" + +#include "amd64-tdep.h" +#include "extract-store-integer.h" +#include "haiku-tdep.h" +#include "solib.h" + +static int +amd64_haiku_sigtramp_p (const frame_info_ptr &this_frame) +{ + CORE_ADDR pc = get_frame_pc (this_frame); + const char *solib_name + = solib_name_from_address (get_frame_program_space (this_frame), pc); + + if (solib_name == nullptr || strcmp (solib_name, "commpage") != 0) + return false; + + const char *name; + find_pc_partial_function (pc, &name, NULL, NULL); + + if (name == nullptr || strcmp (name, "commpage_signal_handler") != 0) + return false; + + return true; +} + +/* Offset to mcontext_t in signal_frame_data, + from headers/private/kernel/ksignal.h. + + The struct is private so it may change anytime. + However, the first two members of the struct are siginfo_t and ucontext_t, + which are public and relatively stable. */ +#define AMD64_HAIKU_SIGNAL_FRAME_DATA_MCONTEXT_OFFSET 96 + +static CORE_ADDR +amd64_haiku_sigcontext_addr (const frame_info_ptr &this_frame) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + CORE_ADDR bp; + gdb_byte buf[8]; + + get_frame_register (this_frame, AMD64_RBP_REGNUM, buf); + bp = extract_unsigned_integer (buf, 8, byte_order); + + /* Layout of the stack before function call: + - signal_frame_data + - frame->ip (8 bytes) + - frame->bp (8 bytes). Not written by the kernel, + but the signal handler has a "push %rbp" instruction. */ + return bp + 8 + 8 + AMD64_HAIKU_SIGNAL_FRAME_DATA_MCONTEXT_OFFSET; +} + +/* From struct vregs at arch/x86_64/signal.h. */ +static int amd64_haiku_sc_reg_offset[] = { + 0 * 8, /* %rax */ + 1 * 8, /* %rbx */ + 2 * 8, /* %rcx */ + 3 * 8, /* %rdx */ + 5 * 8, /* %rsi */ + 4 * 8, /* %rdi */ + 6 * 8, /* %rbp */ + 15 * 8, /* %rsp */ + 7 * 8, /* %r8 */ + 8 * 8, /* %r9 */ + 9 * 8, /* %r10 */ + 10 * 8, /* %r11 */ + 11 * 8, /* %r12 */ + 12 * 8, /* %r13 */ + 13 * 8, /* %r14 */ + 14 * 8, /* %r15 */ + 16 * 8, /* %rip */ + 17 * 8, /* %eflags */ + + -1, /* %cs */ + -1, /* %ss */ + -1, /* %ds */ + -1, /* %es */ + -1, /* %fs */ + -1 /* %gs */ +}; + +static void +amd64_haiku_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + i386_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + amd64_init_abi (info, gdbarch, + amd64_target_description (X86_XSTATE_SSE_MASK, true)); + haiku_init_abi (info, gdbarch); + + tdep->sigtramp_p = amd64_haiku_sigtramp_p; + tdep->sigcontext_addr = amd64_haiku_sigcontext_addr; + tdep->sc_reg_offset = amd64_haiku_sc_reg_offset; + tdep->sc_num_regs = ARRAY_SIZE (amd64_haiku_sc_reg_offset); + + /* The offset of the PC in the jmp_buf structure. + Found at src/system/libroot/posix/arch/x86_64/setjmp_internal.h. */ + tdep->jb_pc_offset = 0; +} + +static enum gdb_osabi +amd64_haiku_osabi_sniffer (bfd *abfd) +{ + const char *target_name = bfd_get_target (abfd); + + if (strcmp (target_name, "elf64-x86-64") != 0) + return GDB_OSABI_UNKNOWN; + + if (!haiku_check_required_symbols (abfd)) + return GDB_OSABI_UNKNOWN; + + return GDB_OSABI_HAIKU; +} + +void _initialize_amd64_haiku_tdep (); +void +_initialize_amd64_haiku_tdep () +{ + gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour, + amd64_haiku_osabi_sniffer); + + gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, GDB_OSABI_HAIKU, + amd64_haiku_init_abi); +} diff --git a/gdb/configure b/gdb/configure index 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 . */ + +#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 = "\n"; + haiku_nat::for_each_image ( + current_inferior ()->pid, [&] (const haiku_nat::image_info &info) { + if (!info.is_main_executable) + { + document += string_printf ( + " " + "\n", + info.name, + paddress (current_inferior ()->arch (), info.text)); + } + return 0; + }); + document += "\n"; + + if (offset >= document.size ()) + return TARGET_XFER_EOF; + + len = std::min (len, document.size () - offset); + memcpy (readbuf, document.c_str () + offset, len); + + *xfered_len = len; + + return TARGET_XFER_OK; + } + break; + case TARGET_OBJECT_OSDATA: + { + if (writebuf != nullptr) + return TARGET_XFER_UNAVAILABLE; + + *xfered_len = haiku_common_xfer_osdata (annex, readbuf, offset, len); + + return (*xfered_len > 0) ? TARGET_XFER_OK : TARGET_XFER_EOF; + } + default: + haiku_nat_debug_printf ("Unimplemented xfer object: %i", (int)object); + } + + return inf_child_target::xfer_partial (object, annex, readbuf, writebuf, + offset, len, xfered_len); +} + +std::vector +haiku_nat_target::memory_map () +{ + std::vector result; + + haiku_nat::for_each_area ( + current_inferior ()->pid, [&] (const haiku_nat::area_info &info) { + /* While some regions appear read-only to the user, + as the debugger, we can write anywhere. + + If this is set otherwise, software breakpoints in read-only + regions (such as shared libraries) will not work. */ + result.emplace_back (info.address, info.address + info.size, MEM_RW); + return 0; + }); + + return result; +} + +bool +haiku_nat_target::supports_multi_process () +{ + return true; +} + +bool +haiku_nat_target::supports_disable_randomization () +{ + return true; +} + +bool +haiku_nat_target::info_proc (const char *args, enum info_proc_what what) +{ + pid_t pid; + bool do_cmdline = false; + bool do_exe = false; + bool do_mappings = false; + bool do_status = false; + + switch (what) + { + case IP_MINIMAL: + do_cmdline = true; + do_exe = true; + break; + case IP_STAT: + case IP_STATUS: + do_status = true; + break; + case IP_MAPPINGS: + do_mappings = true; + break; + case IP_CMDLINE: + do_cmdline = true; + break; + case IP_EXE: + do_exe = true; + break; + case IP_CWD: + /* There is no obvious method of getting the CWD of a different team. + _kern_get_extended_team_info might provide what we want, but the + syscall stores the result in a private class "KMessage" instead of + normal structs. */ + return false; + case IP_ALL: + do_cmdline = true; + do_exe = true; + do_mappings = true; + do_status = true; + break; + default: + error (_ ("Not supported on this target.")); + } + + gdb_argv built_argv (args); + if (built_argv.count () == 0) + { + pid = inferior_ptid.pid (); + if (pid == 0) + error (_ ("No current team: you must name one.")); + } + else if (built_argv.count () == 1 && isdigit (built_argv[0][0])) + pid = strtol (built_argv[0], NULL, 10); + else + error (_ ("Invalid arguments.")); + + gdb_printf (_ ("team %d\n"), pid); + + const haiku_nat::team_info *info = nullptr; + + if (do_cmdline || do_status) + info = haiku_nat::get_team (pid); + + if (do_cmdline) + { + if (info != nullptr) + gdb_printf ("cmdline = '%s'\n", info->args); + else + warning (_ ("unable to fetch command line")); + } + + if (do_exe) + { + const char *exe = pid_to_exec_file (pid); + if (exe != nullptr) + gdb_printf ("exe = '%s'\n", exe); + else + warning (_ ("unable to fetch executable path name")); + } + + if (do_mappings) + { + bool first = true; + if (haiku_nat::for_each_area ( + pid, + [&] (const haiku_nat::area_info &area_info) { + if (first) + { + gdb_printf (_ ("Mapped areas:\n\n")); + gdb_printf ("%6s %18s %10s %10s %6s %6s %5s %5s %s\n", + "ID", "address", "size", "alloc.", "prot", + "#-cow", "#-in", "#-out", "name"); + first = false; + } + + std::string prot; + if (area_info.can_read) + prot += "r"; + if (area_info.can_write) + prot += "w"; + if (area_info.can_exec) + prot += "x"; + if (area_info.is_stack) + prot += "s"; + if (area_info.can_clone) + prot += "c"; + + gdb_printf ("%6s %18s %10s %10s %6s %6s %5s %5s %s\n", + plongest (area_info.id), + core_addr_to_string (area_info.address), + phex_nz (area_info.size, 0), + phex_nz (area_info.ram_size, 0), prot.c_str (), + pulongest (area_info.copy_count), + pulongest (area_info.in_count), + pulongest (area_info.out_count), area_info.name); + + return 0; + }) + < 0) + { + warning (_ ("unable to fetch virtual memory map")); + } + } + + if (do_status) + { + if (info != nullptr) + { + gdb_printf ("Name: %s\n", info->name); + gdb_printf ("Parent team: %s\n", plongest (info->parent)); + gdb_printf ("Process group: %s\n", plongest (info->group_id)); + gdb_printf ("Session id: %s\n", plongest (info->session_id)); + gdb_printf ("User IDs (real, effective): %s %s\n", + plongest (info->real_uid), plongest (info->uid)); + gdb_printf ("Group IDs (real, effective): %s %s\n", + plongest (info->real_gid), plongest (info->gid)); + gdb_printf ("Thread count: %s\n", pulongest (info->thread_count)); + gdb_printf ("Image count: %s\n", pulongest (info->image_count)); + gdb_printf ("Area count: %s\n", pulongest (info->area_count)); + gdb_printf ("Debugger nub thread: %s\n", + plongest (info->debugger_nub_thread)); + gdb_printf ("Debugger nub port: %s\n", + plongest (info->debugger_nub_port)); + } + else + warning (_ ("unable to fetch team information")); + } + + return true; +} + +/* Utilities. */ + +static void +haiku_relocate_main_executable (inferior *inf) +{ + CORE_ADDR text; + CORE_ADDR data; + + if (haiku_nat::read_offsets (inf->pid, &text, &data) < 0) + return; + + CORE_ADDR displacement = text; + + if (inf->pspace->exec_bfd ()) + { + asection *asect; + + bfd *exec_bfd = inf->pspace->exec_bfd (); + for (asect = exec_bfd->sections; asect != NULL; asect = asect->next) + exec_set_section_address (bfd_get_filename (exec_bfd), asect->index, + bfd_section_vma (asect) + displacement); + } + + if (inf->pspace->symfile_object_file == nullptr) + symbol_file_add_main (inf->pspace->exec_filename.get (), + SYMFILE_DEFER_BP_RESET); + + objfile *objf = inf->pspace->symfile_object_file; + /* The call above should ensure that this is filled in. */ + gdb_assert (objf != nullptr); + objfile_rebase (objf, displacement); + + haiku_nat_debug_printf ("rebased: %s", core_addr_to_string (displacement)); +} + +static void +haiku_enable_breakpoints_if_ready (inferior *inf) +{ + if (strcmp (haiku_nat::pid_to_exec_file (inf->pid), + inf->pspace->exec_filename.get ()) + != 0) + { + /* Not ready yet. The inferior is still executing a wrapper + (usually bash). */ + return; + } + + /* Refresh the regions so that write operations can be done correctly. */ + invalidate_target_mem_regions (); + + /* We can get correct offsets and relocate now. */ + haiku_relocate_main_executable (inf); + + enable_breakpoints_after_startup (); +} + +/* Supply other required functions. */ + +namespace haiku_nat +{ + +void +debugger_output (const char *message) +{ + gdb_printf ("%s\n", message); +} + +void +image_created (ptid_t ptid, const image_info &info) +{ + haiku_nat_debug_printf ("ptid=%s, name=%s, text=%p", + ptid.to_string ().c_str (), info.name, + (void *)info.text); + + /* To be handled by solib-haiku.c. */ +} + +void +image_deleted (ptid_t ptid, const image_info &info) +{ + haiku_nat_debug_printf ("ptid=%s, name=%s", ptid.to_string ().c_str (), + info.name); + + if (info.is_main_executable) + { + /* This means all images have been deleted. This usually signals that + the Haiku team just called exec. + + We want to disable breakpoints for now to prevent those pointing to + the main executable from causing issues with unrelocated addresses. + Then, after the creation or exec call completes and the new inferior + gets finalized, we can relocate and enable these breakpoints again. + + We also cannot disable the breakpoints later than this. After the + event, images for the new executable starts loading. Disabling the + breakpoints causes GDB to write bogus data back to the fresh + binaries. */ + + disable_breakpoints_before_startup (); + invalidate_target_mem_regions (); + } + + /* The rest to be handled by solib-haiku.c. */ +} + +bool +is_catching_syscalls_for (ptid_t ptid) +{ + inferior *inf = find_inferior_ptid (haiku_target, ptid); + if (inf == nullptr) + return false; + + std::optional maybe_restore_thread + = maybe_switch_inferior (inf); + + return catch_syscall_enabled () > 0; +} + +} + +/* Initialization. */ + +void _initialize_haiku_nat (); +void +_initialize_haiku_nat () +{ + /* We cannot do this in target_op's own callbacks, since they are called too + early after attaching or an exec event. At that point, symfile_object_file + remains invalid. + + Previous ports put this in Haiku's solib_create_inferior_hook callback. + However, this callback is also shared by remote targets and therefore + assumes gathering information from the address space instead of the host + OS, which is what haiku_nat::read_offsets does under the hood. With the + old implementation, GDB connected to gdbserver debugging PID X on the + target would attempt to use haiku_nat::read_offsets on the same PID X + on the local machine - this is undesired. */ + + gdb::observers::inferior_created.attach ( + [] (inferior *inf) { + if (inf->target_is_pushed (haiku_target)) + haiku_enable_breakpoints_if_ready (inf); + }, + "haiku"); + + gdb::observers::inferior_execd.attach ( + [] (inferior *exec, inferior *foll) { + if (foll->target_is_pushed (haiku_target)) + haiku_enable_breakpoints_if_ready (foll); + }, + "haiku"); + + add_setshow_boolean_cmd ( + "haiku-nat", class_maintenance, &debug_haiku_nat, + _ ("Set debugging of Haiku native target."), + _ ("Show debugging of Haiku native target."), _ ("\ +When on, print debug messages relating to the Haiku native target."), + nullptr, + [] (struct ui_file *file, int from_tty, struct cmd_list_element *c, + const char *value) { + gdb_printf (file, _ ("Debugging of Haiku native targets is %s.\n"), + value); + }, + &setdebuglist, &showdebuglist); +} diff --git a/gdb/haiku-nat.h b/gdb/haiku-nat.h new file mode 100644 index 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 . */ + +#include "inf-child.h" + +/* A prototype Haiku target. */ + +struct haiku_nat_target : public inf_child_target +{ + void create_inferior (const char *, const std::string &, char **, + int) override; + + void attach (const char *, int) override; + + void detach (inferior *, int) override; + + void resume (ptid_t, int, enum gdb_signal) override; + + ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override; + + void files_info () override; + + void kill () override; + + void follow_exec (inferior *, ptid_t, const char *) override; + + bool thread_alive (ptid_t) override; + void update_thread_list () override; + std::string pid_to_str (ptid_t) override; + + const char *thread_name (thread_info *) override; + + void stop (ptid_t) override; + + const char *pid_to_exec_file (int) override; + + bool can_async_p () override; + bool is_async_p () override; + void async (bool) override; + int async_wait_fd () override; + + bool supports_non_stop () override; + bool always_non_stop_p () override; + + enum target_xfer_status xfer_partial (enum target_object, const char *, + gdb_byte *, const gdb_byte *, ULONGEST, + ULONGEST, ULONGEST *) override; + + std::vector memory_map () override; + + bool supports_multi_process () override; + + bool supports_disable_randomization () override; + + bool info_proc (const char *, enum info_proc_what) override; +}; + +/* The final/concrete instance. */ +extern haiku_nat_target *haiku_target; diff --git a/gdb/haiku-tdep.c b/gdb/haiku-tdep.c new file mode 100644 index 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 . */ + +#include "defs.h" + +#include "bfd.h" +#include "elf-bfd.h" +#include "gdbarch.h" +#include "haiku-tdep.h" +#include "inferior.h" +#include "osdata.h" +#include "solib-haiku.h" + +/* See haiku-tdep.h. */ + +void +haiku_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + set_gdbarch_so_ops (gdbarch, &haiku_so_ops); +} + +/* See haiku-tdep.h. */ + +bool +haiku_check_required_symbols (bfd *abfd) +{ + long storage_needed = bfd_get_symtab_upper_bound (abfd); + if (storage_needed <= 0) + return false; + + gdb::unique_xmalloc_ptr symbol_table ( + (asymbol **)xmalloc (storage_needed)); + long number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table.get ()); + + if (number_of_symbols <= 0) + return false; + + for (long i = 0; i < number_of_symbols; ++i) + { + const char *name = bfd_asymbol_name (symbol_table.get ()[i]); + + if (strcmp (name, "_gSharedObjectHaikuVersion") == 0) + return true; + } + + return false; +} + +/* See haiku-tdep.h. */ + +gdb_bfd_ref_ptr +haiku_bfd_open_commpage () +{ + /* Get any valid BFD object as a template. + Otherwise, GDB will complain with a segfault. */ + bfd *tmpbfd = current_inferior ()->pspace->exec_bfd (); + if (tmpbfd == nullptr) + tmpbfd = current_inferior ()->pspace->core_bfd (); + if (tmpbfd == nullptr) + return nullptr; + + /* Create a hollow BFD object. */ + bfd *nbfd = bfd_create ("commpage", tmpbfd); + if (nbfd == nullptr) + return nullptr; + + /* Close in case of failure. */ + std::unique_ptr bfd_deleter (nbfd, bfd_close); + + /* Prepare the BFD for writing. */ + if (!bfd_make_writable (nbfd)) + return nullptr; + + asection *section = bfd_make_section (nbfd, ".text"); + section->size = HAIKU_COMMPAGE_SIZE; + + /* Read the commpage symbols from the target. */ + std::unique_ptr comm_data = get_osdata ("comm"); + gdb_assert (comm_data->type == "comm"); + + size_t sym_count = comm_data->items.size (); + + asymbol **symtab + = (asymbol **)bfd_alloc (nbfd, (sym_count + 1) * sizeof (asymbol *)); + + for (size_t i = 0; i < sym_count; ++i) + { + elf_symbol_type *sym = (elf_symbol_type *)bfd_make_empty_symbol (nbfd); + sym->symbol.section = section; + + for (const auto &[name, value] : comm_data->items[i].columns) + { + if (name == "name") + { + char *tmp = (char *)bfd_alloc (nbfd, value.size () + 1); + memcpy (tmp, value.c_str (), value.size () + 1); + bfd_set_asymbol_name (&sym->symbol, tmp); + } + else if (name == "value") + { + sym->symbol.value = strtoulst (value.c_str (), nullptr, 10); + sym->internal_elf_sym.st_value = sym->symbol.value; + } + else if (name == "size") + { + sym->internal_elf_sym.st_size + = strtoulst (value.c_str (), nullptr, 10); + } + else if (name == "type") + { + sym->symbol.flags = BSF_GLOBAL; + for (char flag : value) + { + switch (flag) + { + case 'f': + sym->symbol.flags |= BSF_FUNCTION; + break; + case 'o': + sym->symbol.flags |= BSF_OBJECT; + break; + } + } + } + } + + symtab[i] = (asymbol *)sym; + } + + symtab[sym_count] = nullptr; + + /* Write the symbol table. */ + if (!bfd_set_symtab (nbfd, symtab, sym_count)) + return nullptr; + + /* Prepare the BFD for reading by GDB. */ + if (!bfd_make_readable (nbfd)) + return nullptr; + + bfd_deleter.release (); + + return gdb_bfd_ref_ptr::new_reference (nbfd); +} + +/* See haiku-tdep.h. */ + +CORE_ADDR +haiku_get_commpage_address () +{ + /* Read the images from the target. */ + std::unique_ptr images = get_osdata ("images"); + gdb_assert (images->type == "images"); + + std::string current_team = std::to_string (current_inferior ()->pid); + + for (const auto &item : images->items) + { + bool matches_team = false; + bool matches_name = false; + const char *text_value = nullptr; + + for (const auto &[name, value] : item.columns) + { + if (name == "team") + matches_team = value == current_team; + else if (name == "name") + matches_name = value == "commpage"; + else if (name == "text") + text_value = value.c_str (); + } + + if (matches_team && matches_name && text_value != nullptr) + return string_to_core_addr (text_value); + } + + return 0; +} diff --git a/gdb/haiku-tdep.h b/gdb/haiku-tdep.h new file mode 100644 index 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 . */ + +#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 . */ + +#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 . */ + +#include "defs.h" + +#include "haiku-tdep.h" +#include "i386-tdep.h" + +static void +i386_haiku_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + i386_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + i386_elf_init_abi (info, gdbarch); + haiku_init_abi (info, gdbarch); + + /* The offset of the PC in the jmp_buf structure. + Found at src/system/libroot/posix/arch/x86/setjmp_internal.h. */ + tdep->jb_pc_offset = 20; +} + +static enum gdb_osabi +i386_haiku_osabi_sniffer (bfd *abfd) +{ + const char *target_name = bfd_get_target (abfd); + + if (strcmp (target_name, "elf32-i386") != 0) + return GDB_OSABI_UNKNOWN; + + if (!haiku_check_required_symbols (abfd)) + return GDB_OSABI_UNKNOWN; + + return GDB_OSABI_HAIKU; +} + +void _initialize_i386_haiku_tdep (); +void +_initialize_i386_haiku_tdep () +{ + gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour, + i386_haiku_osabi_sniffer); + + gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_HAIKU, + i386_haiku_init_abi); +} diff --git a/gdb/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 . */ + +#include "defs.h" + +#include "exec.h" +#include "haiku-tdep.h" +#include "inferior.h" +#include "objfiles.h" +#include "solib-haiku.h" +#include "solib-target.h" +#include "solist.h" + +/* For other targets, the solib implementation usually reads hints from the + dynamic linker in the active address space, which could be anything from a + core file to a live inferior. + + Haiku's runtime_loader does not export such information. The nearest + we have is the static variable sLoadedImages. We therefore have to rely on + what the target reports. + + This is basically a wrapper around solib-target.c. */ + +static void +haiku_relocate_section_addresses (solib &so, struct target_section *sec) +{ + if (so.so_name == "commpage") + { + CORE_ADDR commpage_address = haiku_get_commpage_address (); + sec->addr = commpage_address; + sec->endaddr = commpage_address + HAIKU_COMMPAGE_SIZE; + + so.addr_low = commpage_address; + so.addr_high = commpage_address + HAIKU_COMMPAGE_SIZE; + } + else + { + solib_target_so_ops.relocate_section_addresses (so, sec); + } +} + +static void +haiku_clear_so (const solib &so) +{ + if (solib_target_so_ops.clear_so != nullptr) + solib_target_so_ops.clear_so (so); +} + +static void +haiku_clear_solib (program_space *pspace) +{ + if (solib_target_so_ops.clear_solib != nullptr) + solib_target_so_ops.clear_solib (pspace); +} + +static void +haiku_solib_create_inferior_hook (int from_tty) +{ + solib_target_so_ops.solib_create_inferior_hook (from_tty); +} + +static intrusive_list +haiku_current_sos () +{ + return solib_target_so_ops.current_sos (); +} + +static int +haiku_open_symbol_file_object (int from_tty) +{ + return solib_target_so_ops.open_symbol_file_object (from_tty); +} + +static int +haiku_in_dynsym_resolve_code (CORE_ADDR pc) +{ + /* No dynamic resolving implemented in Haiku yet. + Return what the generic code has to say. */ + return solib_target_so_ops.in_dynsym_resolve_code (pc); +} + +static gdb_bfd_ref_ptr +haiku_bfd_open (const char *pathname) +{ + if (strcmp (pathname, "commpage") == 0) + return haiku_bfd_open_commpage (); + return solib_target_so_ops.bfd_open (pathname); +} + +const struct solib_ops haiku_so_ops = { + .relocate_section_addresses = haiku_relocate_section_addresses, + .clear_so = haiku_clear_so, + .clear_solib = haiku_clear_solib, + .solib_create_inferior_hook = haiku_solib_create_inferior_hook, + .current_sos = haiku_current_sos, + .open_symbol_file_object = haiku_open_symbol_file_object, + .in_dynsym_resolve_code = haiku_in_dynsym_resolve_code, + .bfd_open = haiku_bfd_open, +}; diff --git a/gdb/solib-haiku.h b/gdb/solib-haiku.h new file mode 100644 index 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 . */ + +#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