ports/devel/gdb/patches/patch-gdb_aarch64-obsd-tdep_c

339 lines
10 KiB
Text
Raw Normal View History

2023-08-16 22:26:55 +00:00
Index: gdb/aarch64-obsd-tdep.c
--- gdb/aarch64-obsd-tdep.c.orig
+++ gdb/aarch64-obsd-tdep.c
@@ -0,0 +1,334 @@
+/* Target-dependent code for OpenBSD/aarch64.
+
+ Copyright (C) 2006-2017 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "gdbarch.h"
+#include "frame-unwind.h"
+#include "osabi.h"
+#include "regset.h"
+#include "trad-frame.h"
+#include "tramp-frame.h"
+
+#include "aarch64-tdep.h"
+#include "aarch64-obsd-tdep.h"
+#include "obsd-tdep.h"
+#include "solib-svr4.h"
+
+/* Signal trampolines. */
+
+/* Since OpenBSD 3.2, the sigtramp routine is mapped at a random page
+ in virtual memory. The randomness makes it somewhat tricky to
+ detect it, but fortunately we can rely on the fact that the start
+ of the sigtramp routine is page-aligned. We recognize the
+ trampoline by looking for the code that invokes the sigreturn
+ system call. The offset where we can find that code varies from
+ release to release.
+
+ By the way, the mapping mentioned above is read-only, so you cannot
+ place a breakpoint in the signal trampoline. */
+
+/* Default page size. */
+static const CORE_ADDR aarch64obsd_page_size = 4096;
+
+/* Offset for sigreturn(2). */
+static const int aarch64obsd_sigreturn_offset[] = {
+ 0xb4, /* OpenBSD 6.2 */
+ 0x08, /* OpenBSD 6.1 */
+ -1
+};
+
+static int
+aarch64obsd_sigtramp_frame_sniffer (const struct frame_unwind *self,
+ struct frame_info *this_frame,
+ void **this_cache)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ CORE_ADDR start_pc = (pc & ~(aarch64obsd_page_size - 1));
+ const int *offset;
+ const char *name;
+
+ find_pc_partial_function (pc, &name, NULL, NULL);
+ if (name)
+ return 0;
+
+ for (offset = aarch64obsd_sigreturn_offset; *offset != -1; offset++)
+ {
+ gdb_byte buf[8];
+ unsigned long insn;
+
+ if (!safe_frame_unwind_memory (this_frame, start_pc + *offset,
+ buf, sizeof buf))
+ continue;
+
+ /* Check for "mov x8, #SYS_sigreturn". */
+ insn = extract_unsigned_integer (buf, 4, byte_order);
+ if (insn != 0xd2800ce8)
+ continue;
+
+ /* Check for "svc 0". */
+ insn = extract_unsigned_integer (buf + 4, 4, byte_order);
+ if (insn != 0xd4000001)
+ continue;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ In 6.1 the sp points to the struct sigframe. Since 6.2 the
+ sigtramp routine saves floating point registers on the stack
+ before the struct sigframe so that needs to be skipped to look
+ at sigframe. A sigframe looks like this:
+
+ struct sigframe {
+ int sf_signum;
+ struct sigcontext sf_sc;
+ siginfo_t sf_si;
+ };
+
+ struct sigcontext {
+ int __sc_unused;
+ int sc_mask;
+
+ unsigned long sc_sp;
+ unsigned long sc_lr;
+ unsigned long sc_elr;
+ unsigned long sc_spsr;
+ unsigned long sc_x[30];
+
+ long sc_cookie;
+ };
+
+*/
+
+#define AARCH64_SIGCONTEXT_REG_SIZE 8
+#define AARCH64_SIGFRAME_SIGCONTEXT_OFFSET 8
+#define AARCH64_SIGCONTEXT_SP_OFFSET 8
+#define AARCH64_SIGCONTEXT_LR_OFFSET 16
+#define AARCH64_SIGCONTEXT_PC_OFFSET 24
+#define AARCH64_SIGCONTEXT_SPSR_OFFSET 32
+#define AARCH64_SIGCONTEXT_X0_OFFSET 40
+
+static struct trad_frame_cache *
+aarch64obsd_sigtramp_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ struct trad_frame_cache *cache;
+ CORE_ADDR sp, sigcontext_addr, x0_addr, func;
+ gdb_byte buf[4];
+ unsigned long insn, sigframe_offset = 0;
+ int i;
+
+ if (*this_cache)
+ return (struct trad_frame_cache *) *this_cache;
+
+ cache = trad_frame_cache_zalloc (this_frame);
+ *this_cache = cache;
+
+ func = get_frame_pc (this_frame);
+ func &= ~(aarch64obsd_page_size - 1);
+ if (!safe_frame_unwind_memory (this_frame, func, buf, sizeof buf))
+ return cache;
+
+ /* Calculate the offset where we can find `struct sigframe'. In 6.1
+ no adjustment is needed so we look at the first instruction to see
+ if it matches 6.2+. If it is a 'sub sp, sp, #0xNNN' instruction,
+ use the amount of stack space to skip from it. */
+ insn = extract_unsigned_integer (buf, 4, byte_order);
+ if ((insn & 0xffc003ff) == 0xd10003ff)
+ sigframe_offset += ((insn & 0x003ffc00) >> 10);
+
+ sp = get_frame_register_unsigned (this_frame, AARCH64_SP_REGNUM);
+ sigcontext_addr = sp + sigframe_offset + AARCH64_SIGFRAME_SIGCONTEXT_OFFSET;
+ x0_addr = sigcontext_addr + AARCH64_SIGCONTEXT_X0_OFFSET;
+
+ trad_frame_set_reg_addr (cache, AARCH64_SP_REGNUM,
+ sigcontext_addr + AARCH64_SIGCONTEXT_SP_OFFSET);
+ trad_frame_set_reg_addr (cache, AARCH64_LR_REGNUM,
+ sigcontext_addr + AARCH64_SIGCONTEXT_LR_OFFSET);
+ trad_frame_set_reg_addr (cache, AARCH64_PC_REGNUM,
+ sigcontext_addr + AARCH64_SIGCONTEXT_PC_OFFSET);
+ trad_frame_set_reg_addr (cache, AARCH64_CPSR_REGNUM,
+ sigcontext_addr + AARCH64_SIGCONTEXT_SPSR_OFFSET);
+ for (i = 0; i < 30; i++)
+ {
+ trad_frame_set_reg_addr (cache, AARCH64_X0_REGNUM + i,
+ x0_addr + i * AARCH64_SIGCONTEXT_REG_SIZE);
+ }
+
+ trad_frame_set_id (cache, frame_id_build (sp, func));
+
+ return cache;
+}
+
+static void
+aarch64obsd_sigtramp_frame_this_id (struct frame_info *this_frame,
+ void **this_cache,
+ struct frame_id *this_id)
+{
+ struct trad_frame_cache *cache =
+ aarch64obsd_sigtramp_frame_cache (this_frame, this_cache);
+
+ trad_frame_get_id (cache, this_id);
+}
+
+static struct value *
+aarch64obsd_sigtramp_frame_prev_register (struct frame_info *this_frame,
+ void **this_cache, int regnum)
+{
+ struct trad_frame_cache *cache =
+ aarch64obsd_sigtramp_frame_cache (this_frame, this_cache);
+
+ return trad_frame_get_register (cache, this_frame, regnum);
+}
+
+static const struct frame_unwind aarch64obsd_sigtramp_frame_unwind = {
+ SIGTRAMP_FRAME,
+ default_frame_unwind_stop_reason,
+ aarch64obsd_sigtramp_frame_this_id,
+ aarch64obsd_sigtramp_frame_prev_register,
+ NULL,
+ aarch64obsd_sigtramp_frame_sniffer
+};
+
+/* The general-purpose regset consists of 31 X registers, plus SP, PC,
+ and SPSR and TPIDR registers. */
+#define AARCH64_OBSD_SIZEOF_GREGSET (35 * X_REGISTER_SIZE)
+
+/* The fp regset consists of 32 V registers, plus FPSR and FPCR which
+ are 4 bytes wide each, and the whole structure is padded to 128 bit
+ alignment. */
+#define AARCH64_OBSD_SIZEOF_FPREGSET (33 * V_REGISTER_SIZE)
+
+/* The pauth regset consists of 2 64-bit masks. */
+#define AARCH64_OBSD_SIZEOF_PAUTH (2 * X_REGISTER_SIZE)
+
+/* Register maps. */
+
+static const struct regcache_map_entry aarch64_obsd_gregmap[] =
+ {
+ { 31, AARCH64_X0_REGNUM, 8 }, /* x0 ... x30 */
+ { 1, AARCH64_SP_REGNUM, 8 },
+ { 1, AARCH64_PC_REGNUM, 8 },
+ { 1, AARCH64_CPSR_REGNUM, 8 },
+ { 0 }
+ };
+
+static const struct regcache_map_entry aarch64_obsd_fpregmap[] =
+ {
+ { 32, AARCH64_V0_REGNUM, 16 }, /* v0 ... v31 */
+ { 1, AARCH64_FPSR_REGNUM, 4 },
+ { 1, AARCH64_FPCR_REGNUM, 4 },
+ { 0 }
+ };
+
+/* Register set definitions. */
+
+const struct regset aarch64_obsd_gregset =
+ {
+ aarch64_obsd_gregmap,
+ regcache_supply_regset, regcache_collect_regset
+ };
+
+const struct regset aarch64_obsd_fpregset =
+ {
+ aarch64_obsd_fpregmap,
+ regcache_supply_regset, regcache_collect_regset
+ };
+
+static void
+aarch64_obsd_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ cb (".reg", AARCH64_OBSD_SIZEOF_GREGSET, AARCH64_OBSD_SIZEOF_GREGSET,
+ &aarch64_obsd_gregset, NULL, cb_data);
+ cb (".reg2", AARCH64_OBSD_SIZEOF_FPREGSET, AARCH64_OBSD_SIZEOF_FPREGSET,
+ &aarch64_obsd_fpregset, NULL, cb_data);
+
+ if (tdep->has_pauth ())
+ {
+ /* Create this on the fly in order to handle the variable location. */
+ const struct regcache_map_entry pauth_regmap[] =
+ {
+ { 2, AARCH64_PAUTH_DMASK_REGNUM (tdep->pauth_reg_base), 8},
+ { 0 }
+ };
+
+ const struct regset aarch64_obsd_pauth_regset =
+ {
+ pauth_regmap, regcache_supply_regset, regcache_collect_regset
+ };
+
+ cb (".reg-aarch-pauth", AARCH64_OBSD_SIZEOF_PAUTH,
+ AARCH64_OBSD_SIZEOF_PAUTH, &aarch64_obsd_pauth_regset,
+ NULL, cb_data);
+ }
+}
+
+static const struct target_desc *
+aarch64_obsd_core_read_description (struct gdbarch *gdbarch,
+ struct target_ops *target, bfd *abfd)
+{
+ return aarch64_read_description (0, true);
+}
+
+
+static void
+aarch64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ obsd_init_abi (info, gdbarch);
+
+ /* OpenBSD/aarch64 uses SVR4-style shared libraries. */
+ set_solib_svr4_fetch_link_map_offsets
+ (gdbarch, svr4_lp64_fetch_link_map_offsets);
+ set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver);
+
+ frame_unwind_append_unwinder (gdbarch, &aarch64obsd_sigtramp_frame_unwind);
+
+ /* Enable longjmp. */
+ tdep->jb_pc = 13;
+
+ set_gdbarch_iterate_over_regset_sections
+ (gdbarch, aarch64_obsd_iterate_over_regset_sections);
+ set_gdbarch_core_read_description
+ (gdbarch, aarch64_obsd_core_read_description);
+}
+
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_aarch64obsd_tdep;
+
+void
+_initialize_aarch64obsd_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_OPENBSD,
+ aarch64obsd_init_abi);
+}