/* Target-dependent code for PowerPC systems running FreeBSD. Copyright (C) 2013-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 "arch-utils.h" #include "frame.h" #include "gdbcore.h" #include "frame-unwind.h" #include "gdbtypes.h" #include "osabi.h" #include "regcache.h" #include "regset.h" #include "symtab.h" #include "target.h" #include "trad-frame.h" #include "ppc-tdep.h" #include "ppc64-tdep.h" #include "ppc-fbsd-tdep.h" #include "fbsd-tdep.h" #include "solib-svr4.h" #include "inferior.h" /* 32-bit regset descriptions. */ static const struct ppc_reg_offsets ppc32_fbsd_reg_offsets = { /* General-purpose registers. */ /* .r0_offset = */ 0, /* .gpr_size = */ 4, /* .xr_size = */ 4, /* .pc_offset = */ 144, /* .ps_offset = */ -1, /* .cr_offset = */ 132, /* .lr_offset = */ 128, /* .ctr_offset = */ 140, /* .xer_offset = */ 136, /* .mq_offset = */ -1, /* Floating-point registers. */ /* .f0_offset = */ 0, /* .fpscr_offset = */ 256, /* .fpscr_size = */ 8 }; /* 64-bit regset descriptions. */ static const struct ppc_reg_offsets ppc64_fbsd_reg_offsets = { /* General-purpose registers. */ /* .r0_offset = */ 0, /* .gpr_size = */ 8, /* .xr_size = */ 8, /* .pc_offset = */ 288, /* .ps_offset = */ -1, /* .cr_offset = */ 264, /* .lr_offset = */ 256, /* .ctr_offset = */ 280, /* .xer_offset = */ 272, /* .mq_offset = */ -1, /* Floating-point registers. */ /* .f0_offset = */ 0, /* .fpscr_offset = */ 256, /* .fpscr_size = */ 8 }; /* 32-bit general-purpose register set. */ static const struct regset ppc32_fbsd_gregset = { &ppc32_fbsd_reg_offsets, ppc_supply_gregset, ppc_collect_gregset }; /* 64-bit general-purpose register set. */ static const struct regset ppc64_fbsd_gregset = { &ppc64_fbsd_reg_offsets, ppc_supply_gregset, ppc_collect_gregset }; /* 32-/64-bit floating-point register set. */ static const struct regset ppc32_fbsd_fpregset = { &ppc32_fbsd_reg_offsets, ppc_supply_fpregset, ppc_collect_fpregset }; const struct regset * ppc_fbsd_gregset (int wordsize) { return wordsize == 8 ? &ppc64_fbsd_gregset : &ppc32_fbsd_gregset; } const struct regset * ppc_fbsd_fpregset (void) { return &ppc32_fbsd_fpregset; } /* Iterate over core file register note sections. */ static void ppcfbsd_iterate_over_regset_sections (struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache) { ppc_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); if (tdep->wordsize == 4) cb (".reg", 148, 148, &ppc32_fbsd_gregset, NULL, cb_data); else cb (".reg", 296, 296, &ppc64_fbsd_gregset, NULL, cb_data); cb (".reg2", 264, 264, &ppc32_fbsd_fpregset, NULL, cb_data); } /* Default page size. */ static const int ppcfbsd_page_size = 4096; /* Offset for sigreturn(2). */ static const int ppcfbsd_sigreturn_offset[] = { 0xc, /* FreeBSD 32-bit */ -1 }; /* Signal trampolines. */ static int ppcfbsd_sigtramp_frame_sniffer (const struct frame_unwind *self, const frame_info_ptr &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 & ~(ppcfbsd_page_size - 1)); const int *offset; const char *name; /* A stack trampoline is detected if no name is associated to the current pc and if it points inside a trampoline sequence. */ find_pc_partial_function (pc, &name, NULL, NULL); /* If we have a name, we have no trampoline, return. */ if (name) return 0; for (offset = ppcfbsd_sigreturn_offset; *offset != -1; offset++) { gdb_byte buf[2 * PPC_INSN_SIZE]; unsigned long insn; if (!safe_frame_unwind_memory (this_frame, start_pc + *offset, {buf, sizeof buf})) continue; /* Check for "li r0,SYS_sigreturn". */ insn = extract_unsigned_integer (buf, PPC_INSN_SIZE, byte_order); if (insn != 0x380001a1) continue; /* Check for "sc". */ insn = extract_unsigned_integer (buf + PPC_INSN_SIZE, PPC_INSN_SIZE, byte_order); if (insn != 0x44000002) continue; return 1; } return 0; } static struct trad_frame_cache * ppcfbsd_sigtramp_frame_cache (const frame_info_ptr &this_frame, void **this_cache) { struct gdbarch *gdbarch = get_frame_arch (this_frame); ppc_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); struct trad_frame_cache *cache; CORE_ADDR addr, base, func; gdb_byte buf[PPC_INSN_SIZE]; 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 &= ~(ppcfbsd_page_size - 1); if (!safe_frame_unwind_memory (this_frame, func, {buf, sizeof buf})) return cache; base = get_frame_register_unsigned (this_frame, gdbarch_sp_regnum (gdbarch)); addr = base + 0x10 + 2 * tdep->wordsize; for (i = 0; i < ppc_num_gprs; i++, addr += tdep->wordsize) { int regnum = i + tdep->ppc_gp0_regnum; trad_frame_set_reg_addr (cache, regnum, addr); } trad_frame_set_reg_addr (cache, tdep->ppc_lr_regnum, addr); addr += tdep->wordsize; trad_frame_set_reg_addr (cache, tdep->ppc_cr_regnum, addr); addr += tdep->wordsize; trad_frame_set_reg_addr (cache, tdep->ppc_xer_regnum, addr); addr += tdep->wordsize; trad_frame_set_reg_addr (cache, tdep->ppc_ctr_regnum, addr); addr += tdep->wordsize; trad_frame_set_reg_addr (cache, gdbarch_pc_regnum (gdbarch), addr); /* SRR0? */ addr += tdep->wordsize; /* Construct the frame ID using the function start. */ trad_frame_set_id (cache, frame_id_build (base, func)); return cache; } static void ppcfbsd_sigtramp_frame_this_id (const frame_info_ptr &this_frame, void **this_cache, struct frame_id *this_id) { struct trad_frame_cache *cache = ppcfbsd_sigtramp_frame_cache (this_frame, this_cache); trad_frame_get_id (cache, this_id); } static struct value * ppcfbsd_sigtramp_frame_prev_register (const frame_info_ptr &this_frame, void **this_cache, int regnum) { struct trad_frame_cache *cache = ppcfbsd_sigtramp_frame_cache (this_frame, this_cache); return trad_frame_get_register (cache, this_frame, regnum); } static const struct frame_unwind ppcfbsd_sigtramp_frame_unwind = { "ppc freebsd sigtramp", SIGTRAMP_FRAME, default_frame_unwind_stop_reason, ppcfbsd_sigtramp_frame_this_id, ppcfbsd_sigtramp_frame_prev_register, NULL, ppcfbsd_sigtramp_frame_sniffer }; static enum return_value_convention ppcfbsd_return_value (struct gdbarch *gdbarch, struct value *function, struct type *valtype, struct regcache *regcache, gdb_byte *readbuf, const gdb_byte *writebuf) { return ppc_sysv_abi_broken_return_value (gdbarch, function, valtype, regcache, readbuf, writebuf); } /* Implement the "get_thread_local_address" gdbarch method. */ static CORE_ADDR ppcfbsd_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid, CORE_ADDR lm_addr, CORE_ADDR offset) { ppc_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); int tp_offset, tp_regnum; regcache *regcache = get_thread_arch_regcache (current_inferior (), ptid, gdbarch); if (tdep->wordsize == 4) { tp_offset = 0x7008; tp_regnum = PPC_R0_REGNUM + 2; } else { tp_offset = 0x7010; tp_regnum = PPC_R0_REGNUM + 13; } target_fetch_registers (regcache, tp_regnum); ULONGEST tp; if (regcache->cooked_read (tp_regnum, &tp) != REG_VALID) error (_("Unable to fetch tcb pointer")); /* tp points to the end of the TCB block. The first member of the TCB is the pointer to the DTV array. */ CORE_ADDR dtv_addr = tp - tp_offset; return fbsd_get_thread_local_address (gdbarch, dtv_addr, lm_addr, offset); } static void ppcfbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { ppc_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); /* Generic FreeBSD support. */ fbsd_init_abi (info, gdbarch); /* FreeBSD doesn't support the 128-bit `long double' from the psABI. */ set_gdbarch_long_double_bit (gdbarch, 64); set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double); if (tdep->wordsize == 4) { set_gdbarch_return_value (gdbarch, ppcfbsd_return_value); set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_ilp32_fetch_link_map_offsets); frame_unwind_append_unwinder (gdbarch, &ppcfbsd_sigtramp_frame_unwind); set_gdbarch_gcore_bfd_target (gdbarch, "elf32-powerpc"); } if (tdep->wordsize == 8) { set_gdbarch_convert_from_func_ptr_addr (gdbarch, ppc64_convert_from_func_ptr_addr); set_gdbarch_elf_make_msymbol_special (gdbarch, ppc64_elf_make_msymbol_special); set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code); set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_lp64_fetch_link_map_offsets); set_gdbarch_gcore_bfd_target (gdbarch, "elf64-powerpc"); } set_gdbarch_iterate_over_regset_sections (gdbarch, ppcfbsd_iterate_over_regset_sections); set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); set_gdbarch_get_thread_local_address (gdbarch, ppcfbsd_get_thread_local_address); } void _initialize_ppcfbsd_tdep (); void _initialize_ppcfbsd_tdep () { gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc, GDB_OSABI_FREEBSD, ppcfbsd_init_abi); gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc64, GDB_OSABI_FREEBSD, ppcfbsd_init_abi); gdbarch_register_osabi (bfd_arch_rs6000, 0, GDB_OSABI_FREEBSD, ppcfbsd_init_abi); }