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 . */ + +#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); +}