[PATCH] FreeBSD powerpc support

Andreas Tobler
2012-11-19T21:44:07Z

[-- Attachment #1: Type: text/plain, Size: 1867 bytes --]

Hi all,

After a longer timeout here again :)

The attached patch adds support for FreeBSD PowerPC, 32-bit and 64-bit.
It is derived from ppcobsd* and ppc-linux with FreeBSD additions.

There is room for improvement :) And I will continue, but I'd like to
see this patch in the gdb cvs because it will be much easier for me fix
outstanding issues when I can work with cvs iso. of local revisions.
Also, other people might have a use of this work, even if not complete.

Currently missing/incomplete:
- Altivec support, kernel bits are missing.
- HW watchpoints, also kernel bits are missing.
- thread support.
- tls support.
- some sig tests.

Here the summary from 32-bit:

=== gdb Summary ===

# of expected passes 16337
# of unexpected failures 1107
# of expected failures 68
# of known failures 49
# of untested testcases 63
# of unresolved testcases 97
# of unsupported tests 141

Here the summary from 64-bit:
=== gdb Summary ===

# of expected passes 16203
# of unexpected failures 1192
# of unexpected successes 1
# of expected failures 69
# of known failures 48
# of untested testcases 61
# of unresolved testcases 2
# of unsupported tests 141

Note on the results, around 800 failures (c++) are due to compiling with
g++-4.2.1. Using a newer g++ will make them pass. This is on all
supported FreeBSD platforms the same.

I appreciate comments and corrections.

TIA,
Andreas

2012-11-19 Andreas Tobler <andreast@fgznet.ch>

* Makefile.in (ALL_TARGET_OBS): Add powerpc FreeBSD files.
* configure.host: Add powerpc*-*-freebsd* target.
* configure.tgt: Add target info for powerpc*-*-freebsd*.
* ppcfbsd-nat.c, ppcfbsd-tdep.h, ppcfbsd-tdep.c: New files.
* config/powerpc/fbsd.mh: New file.

[-- Attachment #2: gdb_ppc_20121119-1.diff --]
[-- Type: text/plain, Size: 56199 bytes --]

Index: ./ppcfbsd-nat.c
--- /dev/null
+++ ./ppcfbsd-nat.c
@@ -0,0 +1,200 @@
+/* Native-dependent code for PowerPC's running FreeBSD, for GDB.
+
+ Copyright (C) 2002, 2004, 2012
+ 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 "gdbcore.h"
+#include "inferior.h"
+#include "regcache.h"
+
+#include "gdb_assert.h"
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/procfs.h>
+#include <sys/ptrace.h>
+#include <sys/signal.h>
+#include <machine/frame.h>
+#include <machine/pcb.h>
+#include <machine/reg.h>
+
+#include "fbsd-nat.h"
+#include "gregset.h"
+#include "ppc-tdep.h"
+#include "ppcfbsd-tdep.h"
+#include "inf-ptrace.h"
+#include "bsd-kvm.h"
+
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
+{
+ const struct regset *regset = ppc_fbsd_gregset (sizeof (long));
+
+ ppc_supply_gregset (regset, regcache, -1, gregsetp, sizeof (*gregsetp));
+}
+
+void
+fill_gregset (const struct regcache *regcache,
+ gdb_gregset_t *gregsetp, int regno)
+{
+ const struct regset *regset = ppc_fbsd_gregset (sizeof (long));
+
+ if (regno == -1)
+ memset (gregsetp, 0, sizeof (*gregsetp));
+ ppc_collect_gregset (regset, regcache, regno, gregsetp, sizeof (*gregsetp));
+}
+
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t * fpregsetp)
+{
+ const struct regset *regset = ppc_fbsd_fpregset ();
+
+ ppc_supply_fpregset (regset, regcache, -1,
+ fpregsetp, sizeof (*fpregsetp));
+}
+
+void
+fill_fpregset (const struct regcache *regcache,
+ gdb_fpregset_t *fpregsetp, int regno)
+{
+ const struct regset *regset = ppc_fbsd_fpregset ();
+
+ ppc_collect_fpregset (regset, regcache, regno,
+ fpregsetp, sizeof (*fpregsetp));
+}
+
+static int
+getfpregs_supplies (struct gdbarch *gdbarch, int regno)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* FIXME: jimb/2004-05-05: Some PPC variants don't have floating
+ point registers. Traditionally, GDB's register set has still
+ listed the floating point registers for such machines, so this
+ code is harmless. However, the new E500 port actually omits the
+ floating point registers entirely from the register set --- they
+ don't even have register numbers assigned to them.
+
+ It's not clear to me how best to update this code, so this assert
+ will alert the first person to encounter the NetBSD/E500
+ combination to the problem. */
+ gdb_assert (ppc_floating_point_unit_p (gdbarch));
+
+ return ((regno >= tdep->ppc_fp0_regnum
+ && regno < tdep->ppc_fp0_regnum + ppc_num_fprs)
+ || regno == tdep->ppc_fpscr_regnum);
+}
+
+static void
+ppcfbsd_fetch_inferior_registers (struct target_ops *ops,
+ struct regcache *regcache, int regno)
+{
+ gdb_gregset_t regs;
+
+ if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+ (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+ perror_with_name (_("Couldn't get registers"));
+
+ supply_gregset (regcache, &regs);
+
+ if (regno == -1 || getfpregs_supplies (get_regcache_arch (regcache), regno))
+ {
+ const struct regset *fpregset = ppc_fbsd_fpregset ();
+ gdb_fpregset_t fpregs;
+
+ if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+ (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+ perror_with_name (_("Couldn't get FP registers"));
+
+ ppc_supply_fpregset (fpregset, regcache, regno, &fpregs, sizeof fpregs);
+ }
+}
+
+static void
+ppcfbsd_store_inferior_registers (struct target_ops *ops,
+ struct regcache *regcache, int regno)
+{
+ gdb_gregset_t regs;
+
+ if (ptrace (PT_GETREGS, PIDGET (inferior_ptid),
+ (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+ perror_with_name (_("Couldn't get registers"));
+
+ fill_gregset (regcache, &regs, regno);
+
+ if (ptrace (PT_SETREGS, PIDGET (inferior_ptid),
+ (PTRACE_TYPE_ARG3) &regs, 0) == -1)
+ perror_with_name (_("Couldn't write registers"));
+
+ if (regno == -1 || getfpregs_supplies (get_regcache_arch (regcache), regno))
+ {
+ gdb_fpregset_t fpregs;
+
+ if (ptrace (PT_GETFPREGS, PIDGET (inferior_ptid),
+ (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+ perror_with_name (_("Couldn't get FP registers"));
+
+ fill_fpregset (regcache, &fpregs, regno);
+
+ if (ptrace (PT_SETFPREGS, PIDGET (inferior_ptid),
+ (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
+ perror_with_name (_("Couldn't set FP registers"));
+ }
+}
+
+static int
+ppcfbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int i, regnum;
+
+ /* The stack pointer shouldn't be zero. */
+ if (pcb->pcb_sp == 0)
+ return 0;
+
+ regcache_raw_supply (regcache, gdbarch_sp_regnum (gdbarch), &pcb->pcb_sp);
+ regcache_raw_supply (regcache, tdep->ppc_cr_regnum, &pcb->pcb_cr);
+ regcache_raw_supply (regcache, tdep->ppc_lr_regnum, &pcb->pcb_lr);
+ for (i = 0, regnum = tdep->ppc_gp0_regnum + 14; i < 20; i++, regnum++)
+ regcache_raw_supply (regcache, regnum, &pcb->pcb_context[i]);
+
+ return 1;
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+void _initialize_ppcfbsd_nat (void);
+
+void
+_initialize_ppcfbsd_nat (void)
+{
+ struct target_ops *t;
+
+ /* Add in local overrides. */
+ t = inf_ptrace_target ();
+ t->to_fetch_registers = ppcfbsd_fetch_inferior_registers;
+ t->to_store_registers = ppcfbsd_store_inferior_registers;
+ t->to_pid_to_exec_file = fbsd_pid_to_exec_file;
+ t->to_find_memory_regions = fbsd_find_memory_regions;
+ t->to_make_corefile_notes = fbsd_make_corefile_notes;
+ add_target (t);
+
+ /* Support debugging kernel virtual memory images. */
+ bsd_kvm_add_target (ppcfbsd_supply_pcb);
+}
Index: ./ppcfbsd-tdep.c
--- /dev/null
+++ ./ppcfbsd-tdep.c
@@ -0,0 +1,727 @@
+/* Target-dependent code for PowerPC systems running FreeBSD.
+
+ Copyright (C) 2002, 2003, 2004, 2012
+ 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 "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 "gdb_assert.h"
+#include "gdb_string.h"
+
+#include "ppc-tdep.h"
+#include "ppcfbsd-tdep.h"
+#include "solib-svr4.h"
+
+#include "features/rs6000/powerpc-32l.c"
+#include "features/rs6000/powerpc-64l.c"
+
+/* Macros for matching instructions. Note that, since all the
+ operands are masked off before they're or-ed into the instruction,
+ you can use -1 to make masks. */
+
+#define insn_d(opcd, rts, ra, d) \
+ ((((opcd) & 0x3f) << 26) \
+ | (((rts) & 0x1f) << 21) \
+ | (((ra) & 0x1f) << 16) \
+ | ((d) & 0xffff))
+
+#define insn_ds(opcd, rts, ra, d, xo) \
+ ((((opcd) & 0x3f) << 26) \
+ | (((rts) & 0x1f) << 21) \
+ | (((ra) & 0x1f) << 16) \
+ | ((d) & 0xfffc) \
+ | ((xo) & 0x3))
+
+#define insn_xfx(opcd, rts, spr, xo) \
+ ((((opcd) & 0x3f) << 26) \
+ | (((rts) & 0x1f) << 21) \
+ | (((spr) & 0x1f) << 16) \
+ | (((spr) & 0x3e0) << 6) \
+ | (((xo) & 0x3ff) << 1))
+
+/* Read a PPC instruction from memory. PPC instructions are always
+ * big-endian, no matter what endianness the program is running in, so
+ * we can't use read_memory_integer or one of its friends here.
+ */
+static unsigned int
+read_insn (CORE_ADDR pc)
+{
+ unsigned char buf[4];
+
+ read_memory (pc, buf, 4);
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+
+/* An instruction to match. */
+struct insn_pattern
+{
+ unsigned int mask; /* mask the insn with this... */
+ unsigned int data; /* ...and see if it matches this. */
+ int optional; /* If non-zero, this insn may be absent. */
+};
+
+/* Return non-zero if the instructions at PC match the series
+ described in PATTERN, or zero otherwise. PATTERN is an array of
+ 'struct insn_pattern' objects, terminated by an entry whose mask is
+ zero.
+
+ When the match is successful, fill INSN[i] with what PATTERN[i]
+ matched. If PATTERN[i] is optional, and the instruction wasn't
+ present, set INSN[i] to 0 (which is not a valid PPC instruction).
+ INSN should have as many elements as PATTERN. Note that, if
+ PATTERN contains optional instructions which aren't present in
+ memory, then INSN will have holes, so INSN[i] isn't necessarily the
+ i'th instruction in memory. */
+static int
+insns_match_pattern (CORE_ADDR pc, struct insn_pattern *pattern,
+ unsigned int *insn)
+{
+ int i;
+
+ for (i = 0; pattern[i].mask; i++)
+ {
+ insn[i] = read_insn (pc);
+ if ((insn[i] & pattern[i].mask) == pattern[i].data)
+ pc += 4;
+ else if (pattern[i].optional)
+ insn[i] = 0;
+ else
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Return the 'd' field of the d-form instruction INSN, properly
+ sign-extended. */
+static CORE_ADDR
+insn_d_field (unsigned int insn)
+{
+ return ((((CORE_ADDR) insn & 0xffff) ^ 0x8000) - 0x8000);
+}
+
+/* Return the 'ds' field of the ds-form instruction INSN, with the two
+ zero bits concatenated at the right, and properly
+ sign-extended. */
+static CORE_ADDR
+insn_ds_field (unsigned int insn)
+{
+ return ((((CORE_ADDR) insn & 0xfffc) ^ 0x8000) - 0x8000);
+}
+
+/* If DESC is the address of a 64-bit PowerPC FreeBSD function
+ descriptor, return the descriptor's entry point. */
+static CORE_ADDR
+ppc64_desc_entry_point (struct gdbarch *gdbarch, CORE_ADDR desc)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ /* The first word of the descriptor is the entry point. */
+ return (CORE_ADDR) read_memory_unsigned_integer (desc, 8, byte_order);
+}
+
+/* Pattern for the standard linkage function. These are built by
+ build_plt_stub in elf64-ppc.c, whose GLINK argument is always
+ zero. */
+static struct insn_pattern ppc64_standard_linkage1[] =
+ {
+ /* addis r12, r2, <any> */
+ { insn_d (-1, -1, -1, 0), insn_d (15, 12, 2, 0), 0 },
+
+ /* std r2, 40(r1) */
+ { -1, insn_ds (62, 2, 1, 40, 0), 0 },
+
+ /* ld r11, <any>(r12) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 0 },
+
+ /* addis r12, r12, 1 <optional> */
+ { insn_d (-1, -1, -1, -1), insn_d (15, 12, 12, 1), 1 },
+
+ /* ld r2, <any>(r12) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 2, 12, 0, 0), 0 },
+
+ /* addis r12, r12, 1 <optional> */
+ { insn_d (-1, -1, -1, -1), insn_d (15, 12, 12, 1), 1 },
+
+ /* mtctr r11 */
+ { insn_xfx (-1, -1, -1, -1), insn_xfx (31, 11, 9, 467), 0 },
+
+ /* ld r11, <any>(r12) <optional> */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 1 },
+
+ /* bctr */
+ { -1, 0x4e800420, 0 },
+
+ { 0, 0, 0 }
+ };
+
+#define PPC64_STANDARD_LINKAGE1_LEN \
+ (sizeof (ppc64_standard_linkage1) / sizeof (ppc64_standard_linkage1[0]))
+
+static struct insn_pattern ppc64_standard_linkage2[] =
+ {
+ /* addis r12, r2, <any> */
+ { insn_d (-1, -1, -1, 0), insn_d (15, 12, 2, 0), 0 },
+
+ /* std r2, 40(r1) */
+ { -1, insn_ds (62, 2, 1, 40, 0), 0 },
+
+ /* ld r11, <any>(r12) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 0 },
+
+ /* addi r12, r12, <any> <optional> */
+ { insn_d (-1, -1, -1, 0), insn_d (14, 12, 12, 0), 1 },
+
+ /* mtctr r11 */
+ { insn_xfx (-1, -1, -1, -1), insn_xfx (31, 11, 9, 467), 0 },
+
+ /* ld r2, <any>(r12) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 2, 12, 0, 0), 0 },
+
+ /* ld r11, <any>(r12) <optional> */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 1 },
+
+ /* bctr */
+ { -1, 0x4e800420, 0 },
+
+ { 0, 0, 0 }
+ };
+
+#define PPC64_STANDARD_LINKAGE2_LEN \
+ (sizeof (ppc64_standard_linkage2) / sizeof (ppc64_standard_linkage2[0]))
+
+static struct insn_pattern ppc64_standard_linkage3[] =
+ {
+ /* std r2, 40(r1) */
+ { -1, insn_ds (62, 2, 1, 40, 0), 0 },
+
+ /* ld r11, <any>(r2) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 2, 0, 0), 0 },
+
+ /* addi r2, r2, <any> <optional> */
+ { insn_d (-1, -1, -1, 0), insn_d (14, 2, 2, 0), 1 },
+
+ /* mtctr r11 */
+ { insn_xfx (-1, -1, -1, -1), insn_xfx (31, 11, 9, 467), 0 },
+
+ /* ld r11, <any>(r2) <optional> */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 2, 0, 0), 1 },
+
+ /* ld r2, <any>(r2) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 2, 2, 0, 0), 0 },
+
+ /* bctr */
+ { -1, 0x4e800420, 0 },
+
+ { 0, 0, 0 }
+ };
+
+#define PPC64_STANDARD_LINKAGE3_LEN \
+ (sizeof (ppc64_standard_linkage3) / sizeof (ppc64_standard_linkage3[0]))
+
+
+/* When the dynamic linker is doing lazy symbol resolution, the first
+ call to a function in another object will go like this:
+
+ - The user's function calls the linkage function:
+
+ 100007c4: 4b ff fc d5 bl 10000498
+ 100007c8: e8 41 00 28 ld r2,40(r1)
+
+ - The linkage function loads the entry point (and other stuff) from
+ the function descriptor in the PLT, and jumps to it:
+
+ 10000498: 3d 82 00 00 addis r12,r2,0
+ 1000049c: f8 41 00 28 std r2,40(r1)
+ 100004a0: e9 6c 80 98 ld r11,-32616(r12)
+ 100004a4: e8 4c 80 a0 ld r2,-32608(r12)
+ 100004a8: 7d 69 03 a6 mtctr r11
+ 100004ac: e9 6c 80 a8 ld r11,-32600(r12)
+ 100004b0: 4e 80 04 20 bctr
+
+ - But since this is the first time that PLT entry has been used, it
+ sends control to its glink entry. That loads the number of the
+ PLT entry and jumps to the common glink0 code:
+
+ 10000c98: 38 00 00 00 li r0,0
+ 10000c9c: 4b ff ff dc b 10000c78
+
+ - The common glink0 code then transfers control to the dynamic
+ linker's fixup code:
+
+ 10000c78: e8 41 00 28 ld r2,40(r1)
+ 10000c7c: 3d 82 00 00 addis r12,r2,0
+ 10000c80: e9 6c 80 80 ld r11,-32640(r12)
+ 10000c84: e8 4c 80 88 ld r2,-32632(r12)
+ 10000c88: 7d 69 03 a6 mtctr r11
+ 10000c8c: e9 6c 80 90 ld r11,-32624(r12)
+ 10000c90: 4e 80 04 20 bctr
+
+ Eventually, this code will figure out how to skip all of this,
+ including the dynamic linker. At the moment, we just get through
+ the linkage function. */
+
+/* If the current thread is about to execute a series of instructions
+ at PC matching the ppc64_standard_linkage pattern, and INSN is the result
+ from that pattern match, return the code address to which the
+ standard linkage function will send them. (This doesn't deal with
+ dynamic linker lazy symbol resolution stubs.) */
+static CORE_ADDR
+ppc64_standard_linkage1_target (struct frame_info *frame,
+ CORE_ADDR pc, unsigned int *insn)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* The address of the function descriptor this linkage function
+ references. */
+ CORE_ADDR desc
+ = ((CORE_ADDR) get_frame_register_unsigned (frame,
+ tdep->ppc_gp0_regnum + 2)
+ + (insn_d_field (insn[0]) << 16)
+ + insn_ds_field (insn[2]));
+
+ /* The first word of the descriptor is the entry point. Return that. */
+ return ppc64_desc_entry_point (gdbarch, desc);
+}
+
+static CORE_ADDR
+ppc64_standard_linkage2_target (struct frame_info *frame,
+ CORE_ADDR pc, unsigned int *insn)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* The address of the function descriptor this linkage function
+ references. */
+ CORE_ADDR desc
+ = ((CORE_ADDR) get_frame_register_unsigned (frame,
+ tdep->ppc_gp0_regnum + 2)
+ + (insn_d_field (insn[0]) << 16)
+ + insn_ds_field (insn[2]));
+
+ /* The first word of the descriptor is the entry point. Return that. */
+ return ppc64_desc_entry_point (gdbarch, desc);
+}
+
+static CORE_ADDR
+ppc64_standard_linkage3_target (struct frame_info *frame,
+ CORE_ADDR pc, unsigned int *insn)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* The address of the function descriptor this linkage function
+ references. */
+ CORE_ADDR desc
+ = ((CORE_ADDR) get_frame_register_unsigned (frame,
+ tdep->ppc_gp0_regnum + 2)
+ + insn_ds_field (insn[1]));
+
+ /* The first word of the descriptor is the entry point. Return that. */
+ return ppc64_desc_entry_point (gdbarch, desc);
+}
+
+
+/* Given that we've begun executing a call trampoline at PC, return
+ the entry point of the function the trampoline will go to. */
+static CORE_ADDR
+ppc64_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
+{
+ unsigned int ppc64_standard_linkage1_insn[PPC64_STANDARD_LINKAGE1_LEN];
+ unsigned int ppc64_standard_linkage2_insn[PPC64_STANDARD_LINKAGE2_LEN];
+ unsigned int ppc64_standard_linkage3_insn[PPC64_STANDARD_LINKAGE3_LEN];
+ CORE_ADDR target;
+
+ if (insns_match_pattern (pc, ppc64_standard_linkage1,
+ ppc64_standard_linkage1_insn))
+ pc = ppc64_standard_linkage1_target (frame, pc,
+ ppc64_standard_linkage1_insn);
+ else if (insns_match_pattern (pc, ppc64_standard_linkage2,
+ ppc64_standard_linkage2_insn))
+ pc = ppc64_standard_linkage2_target (frame, pc,
+ ppc64_standard_linkage2_insn);
+ else if (insns_match_pattern (pc, ppc64_standard_linkage3,
+ ppc64_standard_linkage3_insn))
+ pc = ppc64_standard_linkage3_target (frame, pc,
+ ppc64_standard_linkage3_insn);
+ else
+ return 0;
+
+ /* The PLT descriptor will either point to the already resolved target
+ address, or else to a glink stub. As the latter carry synthetic @plt
+ symbols, find_solib_trampoline_target should be able to resolve them. */
+ target = find_solib_trampoline_target (frame, pc);
+ return target ? target : pc;
+}
+
+/* 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,
+#ifdef NOTYET
+ /* AltiVec registers. */
+ /* .vr0_offset = */ 0,
+ /* .vscr_offset = */ 512 + 12,
+ /* .vrsave_offset = */ 512
+#endif
+ };
+
+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,
+#ifdef NOYET
+ /* AltiVec registers. */
+ /* .vr0_offset = */ 0,
+ /* .vscr_offset = */ 512 + 12,
+ /* .vrsave_offset = */ 528
+#endif
+ };
+
+/* FreeBSD/powerpc register set. */
+static const struct regset ppc32_fbsd_gregset = {
+ &ppc32_fbsd_reg_offsets,
+ ppc_supply_gregset,
+ ppc_collect_gregset,
+ NULL
+};
+
+static const struct regset ppc64_fbsd_gregset = {
+ &ppc64_fbsd_reg_offsets,
+ ppc_supply_gregset,
+ ppc_collect_gregset,
+ NULL
+};
+
+static 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;
+}
+
+/* Return the appropriate register set for the core section identified
+ by SECT_NAME and SECT_SIZE. */
+
+static const struct regset *
+ppcfbsd_regset_from_core_section (struct gdbarch *gdbarch,
+ const char *sect_name, size_t sect_size)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ if (strcmp (sect_name, ".reg") == 0 && sect_size >= 148)
+ {
+ if (tdep->wordsize == 4)
+ return &ppc32_fbsd_gregset;
+ else
+ return &ppc64_fbsd_gregset;
+ }
+ if (strcmp (sect_name, ".reg2") == 0 && sect_size >= 264)
+ return &ppc32_fbsd_fpregset;
+ return NULL;
+}
+
+/* Default page size. */
+static const int ppcfbsd_page_size = 4096;
+
+/* Offset for sigreturn(2). */
+static const int ppcfbsd_sigreturn_offset[] = {
+ 0xc, /* FreeBSD */
+ -1
+};
+
+/* Signal trampolines. */
+
+static int
+ppcfbsd_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 & ~(ppcfbsd_page_size - 1));
+ const int *offset;
+ const char *name;
+
+ find_pc_partial_function (pc, &name, NULL, NULL);
+ 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 (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 addr, base, func;
+ gdb_byte buf[PPC_INSN_SIZE];
+ int i;
+
+ if (*this_cache)
+ return *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 (struct frame_info *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 (struct frame_info *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 = {
+ 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);
+}
+static CORE_ADDR
+ppc64_fbsd_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ)
+{
+ struct gdbarch_tdep *tdep;
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ struct target_section *s = target_section_by_addr (targ, addr);
+ tdep = gdbarch_tdep (gdbarch);
+
+ /* Check if ADDR points to a function descriptor. */
+ if (s && strcmp (s->the_bfd_section->name, ".opd") == 0)
+ {
+ /* There may be relocations that need to be applied to the .opd
+ section. Unfortunately, this function may be called at a time
+ where these relocations have not yet been performed -- this can
+ happen for example shortly after a library has been loaded with
+ dlopen, but ld.so has not yet applied the relocations.
+
+ To cope with both the case where the relocation has been applied,
+ and the case where it has not yet been applied, we do *not* read
+ the (maybe) relocated value from target memory, but we instead
+ read the non-relocated value from the BFD, and apply the relocation
+ offset manually.
+
+ This makes the assumption that all .opd entries are always relocated
+ by the same offset the section itself was relocated. This should
+ always be the case for GNU/Linux executables and shared libraries.
+ Note that other kind of object files (e.g. those added via
+ add-symbol-files) will currently never end up here anyway, as this
+ function accesses *target* sections only; only the main exec and
+ shared libraries are ever added to the target. */
+
+ gdb_byte buf[8];
+ int res;
+
+ res = bfd_get_section_contents (s->bfd, s->the_bfd_section,
+ &buf, addr - s->addr, 8);
+ if (res != 0)
+ return extract_unsigned_integer (buf, 8, byte_order)
+ - bfd_section_vma (s->bfd, s->the_bfd_section) + s->addr;
+ }
+
+ return addr;
+}
+
+static void
+ppcfbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (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_fbsd_convert_from_func_ptr_addr);
+ 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_regset_from_core_section
+ (gdbarch, ppcfbsd_regset_from_core_section);
+
+ set_gdbarch_fetch_tls_load_module_address (gdbarch,
+ svr4_fetch_objfile_link_map);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+void _initialize_ppcfbsd_tdep (void);
+
+void
+_initialize_ppcfbsd_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc, GDB_OSABI_FREEBSD_ELF,
+ ppcfbsd_init_abi);
+ gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc64,
+ GDB_OSABI_FREEBSD_ELF,
+ ppcfbsd_init_abi);
+ gdbarch_register_osabi (bfd_arch_rs6000, 0, GDB_OSABI_FREEBSD_ELF,
+ ppcfbsd_init_abi);
+ /* Initialize the FreeBSD target descriptions. */
+ initialize_tdesc_powerpc_32l ();
+ initialize_tdesc_powerpc_64l ();
+}
Index: ./ppcfbsd-tdep.c
--- /dev/null
+++ ./ppcfbsd-tdep.c
@@ -0,0 +1,727 @@
+/* Target-dependent code for PowerPC systems running FreeBSD.
+
+ Copyright (C) 2002, 2003, 2004, 2012
+ 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 "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 "gdb_assert.h"
+#include "gdb_string.h"
+
+#include "ppc-tdep.h"
+#include "ppcfbsd-tdep.h"
+#include "solib-svr4.h"
+
+#include "features/rs6000/powerpc-32l.c"
+#include "features/rs6000/powerpc-64l.c"
+
+/* Macros for matching instructions. Note that, since all the
+ operands are masked off before they're or-ed into the instruction,
+ you can use -1 to make masks. */
+
+#define insn_d(opcd, rts, ra, d) \
+ ((((opcd) & 0x3f) << 26) \
+ | (((rts) & 0x1f) << 21) \
+ | (((ra) & 0x1f) << 16) \
+ | ((d) & 0xffff))
+
+#define insn_ds(opcd, rts, ra, d, xo) \
+ ((((opcd) & 0x3f) << 26) \
+ | (((rts) & 0x1f) << 21) \
+ | (((ra) & 0x1f) << 16) \
+ | ((d) & 0xfffc) \
+ | ((xo) & 0x3))
+
+#define insn_xfx(opcd, rts, spr, xo) \
+ ((((opcd) & 0x3f) << 26) \
+ | (((rts) & 0x1f) << 21) \
+ | (((spr) & 0x1f) << 16) \
+ | (((spr) & 0x3e0) << 6) \
+ | (((xo) & 0x3ff) << 1))
+
+/* Read a PPC instruction from memory. PPC instructions are always
+ * big-endian, no matter what endianness the program is running in, so
+ * we can't use read_memory_integer or one of its friends here.
+ */
+static unsigned int
+read_insn (CORE_ADDR pc)
+{
+ unsigned char buf[4];
+
+ read_memory (pc, buf, 4);
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+
+/* An instruction to match. */
+struct insn_pattern
+{
+ unsigned int mask; /* mask the insn with this... */
+ unsigned int data; /* ...and see if it matches this. */
+ int optional; /* If non-zero, this insn may be absent. */
+};
+
+/* Return non-zero if the instructions at PC match the series
+ described in PATTERN, or zero otherwise. PATTERN is an array of
+ 'struct insn_pattern' objects, terminated by an entry whose mask is
+ zero.
+
+ When the match is successful, fill INSN[i] with what PATTERN[i]
+ matched. If PATTERN[i] is optional, and the instruction wasn't
+ present, set INSN[i] to 0 (which is not a valid PPC instruction).
+ INSN should have as many elements as PATTERN. Note that, if
+ PATTERN contains optional instructions which aren't present in
+ memory, then INSN will have holes, so INSN[i] isn't necessarily the
+ i'th instruction in memory. */
+static int
+insns_match_pattern (CORE_ADDR pc, struct insn_pattern *pattern,
+ unsigned int *insn)
+{
+ int i;
+
+ for (i = 0; pattern[i].mask; i++)
+ {
+ insn[i] = read_insn (pc);
+ if ((insn[i] & pattern[i].mask) == pattern[i].data)
+ pc += 4;
+ else if (pattern[i].optional)
+ insn[i] = 0;
+ else
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Return the 'd' field of the d-form instruction INSN, properly
+ sign-extended. */
+static CORE_ADDR
+insn_d_field (unsigned int insn)
+{
+ return ((((CORE_ADDR) insn & 0xffff) ^ 0x8000) - 0x8000);
+}
+
+/* Return the 'ds' field of the ds-form instruction INSN, with the two
+ zero bits concatenated at the right, and properly
+ sign-extended. */
+static CORE_ADDR
+insn_ds_field (unsigned int insn)
+{
+ return ((((CORE_ADDR) insn & 0xfffc) ^ 0x8000) - 0x8000);
+}
+
+/* If DESC is the address of a 64-bit PowerPC FreeBSD function
+ descriptor, return the descriptor's entry point. */
+static CORE_ADDR
+ppc64_desc_entry_point (struct gdbarch *gdbarch, CORE_ADDR desc)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ /* The first word of the descriptor is the entry point. */
+ return (CORE_ADDR) read_memory_unsigned_integer (desc, 8, byte_order);
+}
+
+/* Pattern for the standard linkage function. These are built by
+ build_plt_stub in elf64-ppc.c, whose GLINK argument is always
+ zero. */
+static struct insn_pattern ppc64_standard_linkage1[] =
+ {
+ /* addis r12, r2, <any> */
+ { insn_d (-1, -1, -1, 0), insn_d (15, 12, 2, 0), 0 },
+
+ /* std r2, 40(r1) */
+ { -1, insn_ds (62, 2, 1, 40, 0), 0 },
+
+ /* ld r11, <any>(r12) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 0 },
+
+ /* addis r12, r12, 1 <optional> */
+ { insn_d (-1, -1, -1, -1), insn_d (15, 12, 12, 1), 1 },
+
+ /* ld r2, <any>(r12) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 2, 12, 0, 0), 0 },
+
+ /* addis r12, r12, 1 <optional> */
+ { insn_d (-1, -1, -1, -1), insn_d (15, 12, 12, 1), 1 },
+
+ /* mtctr r11 */
+ { insn_xfx (-1, -1, -1, -1), insn_xfx (31, 11, 9, 467), 0 },
+
+ /* ld r11, <any>(r12) <optional> */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 1 },
+
+ /* bctr */
+ { -1, 0x4e800420, 0 },
+
+ { 0, 0, 0 }
+ };
+
+#define PPC64_STANDARD_LINKAGE1_LEN \
+ (sizeof (ppc64_standard_linkage1) / sizeof (ppc64_standard_linkage1[0]))
+
+static struct insn_pattern ppc64_standard_linkage2[] =
+ {
+ /* addis r12, r2, <any> */
+ { insn_d (-1, -1, -1, 0), insn_d (15, 12, 2, 0), 0 },
+
+ /* std r2, 40(r1) */
+ { -1, insn_ds (62, 2, 1, 40, 0), 0 },
+
+ /* ld r11, <any>(r12) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 0 },
+
+ /* addi r12, r12, <any> <optional> */
+ { insn_d (-1, -1, -1, 0), insn_d (14, 12, 12, 0), 1 },
+
+ /* mtctr r11 */
+ { insn_xfx (-1, -1, -1, -1), insn_xfx (31, 11, 9, 467), 0 },
+
+ /* ld r2, <any>(r12) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 2, 12, 0, 0), 0 },
+
+ /* ld r11, <any>(r12) <optional> */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 12, 0, 0), 1 },
+
+ /* bctr */
+ { -1, 0x4e800420, 0 },
+
+ { 0, 0, 0 }
+ };
+
+#define PPC64_STANDARD_LINKAGE2_LEN \
+ (sizeof (ppc64_standard_linkage2) / sizeof (ppc64_standard_linkage2[0]))
+
+static struct insn_pattern ppc64_standard_linkage3[] =
+ {
+ /* std r2, 40(r1) */
+ { -1, insn_ds (62, 2, 1, 40, 0), 0 },
+
+ /* ld r11, <any>(r2) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 2, 0, 0), 0 },
+
+ /* addi r2, r2, <any> <optional> */
+ { insn_d (-1, -1, -1, 0), insn_d (14, 2, 2, 0), 1 },
+
+ /* mtctr r11 */
+ { insn_xfx (-1, -1, -1, -1), insn_xfx (31, 11, 9, 467), 0 },
+
+ /* ld r11, <any>(r2) <optional> */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 11, 2, 0, 0), 1 },
+
+ /* ld r2, <any>(r2) */
+ { insn_ds (-1, -1, -1, 0, -1), insn_ds (58, 2, 2, 0, 0), 0 },
+
+ /* bctr */
+ { -1, 0x4e800420, 0 },
+
+ { 0, 0, 0 }
+ };
+
+#define PPC64_STANDARD_LINKAGE3_LEN \
+ (sizeof (ppc64_standard_linkage3) / sizeof (ppc64_standard_linkage3[0]))
+
+
+/* When the dynamic linker is doing lazy symbol resolution, the first
+ call to a function in another object will go like this:
+
+ - The user's function calls the linkage function:
+
+ 100007c4: 4b ff fc d5 bl 10000498
+ 100007c8: e8 41 00 28 ld r2,40(r1)
+
+ - The linkage function loads the entry point (and other stuff) from
+ the function descriptor in the PLT, and jumps to it:
+
+ 10000498: 3d 82 00 00 addis r12,r2,0
+ 1000049c: f8 41 00 28 std r2,40(r1)
+ 100004a0: e9 6c 80 98 ld r11,-32616(r12)
+ 100004a4: e8 4c 80 a0 ld r2,-32608(r12)
+ 100004a8: 7d 69 03 a6 mtctr r11
+ 100004ac: e9 6c 80 a8 ld r11,-32600(r12)
+ 100004b0: 4e 80 04 20 bctr
+
+ - But since this is the first time that PLT entry has been used, it
+ sends control to its glink entry. That loads the number of the
+ PLT entry and jumps to the common glink0 code:
+
+ 10000c98: 38 00 00 00 li r0,0
+ 10000c9c: 4b ff ff dc b 10000c78
+
+ - The common glink0 code then transfers control to the dynamic
+ linker's fixup code:
+
+ 10000c78: e8 41 00 28 ld r2,40(r1)
+ 10000c7c: 3d 82 00 00 addis r12,r2,0
+ 10000c80: e9 6c 80 80 ld r11,-32640(r12)
+ 10000c84: e8 4c 80 88 ld r2,-32632(r12)
+ 10000c88: 7d 69 03 a6 mtctr r11
+ 10000c8c: e9 6c 80 90 ld r11,-32624(r12)
+ 10000c90: 4e 80 04 20 bctr
+
+ Eventually, this code will figure out how to skip all of this,
+ including the dynamic linker. At the moment, we just get through
+ the linkage function. */
+
+/* If the current thread is about to execute a series of instructions
+ at PC matching the ppc64_standard_linkage pattern, and INSN is the result
+ from that pattern match, return the code address to which the
+ standard linkage function will send them. (This doesn't deal with
+ dynamic linker lazy symbol resolution stubs.) */
+static CORE_ADDR
+ppc64_standard_linkage1_target (struct frame_info *frame,
+ CORE_ADDR pc, unsigned int *insn)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* The address of the function descriptor this linkage function
+ references. */
+ CORE_ADDR desc
+ = ((CORE_ADDR) get_frame_register_unsigned (frame,
+ tdep->ppc_gp0_regnum + 2)
+ + (insn_d_field (insn[0]) << 16)
+ + insn_ds_field (insn[2]));
+
+ /* The first word of the descriptor is the entry point. Return that. */
+ return ppc64_desc_entry_point (gdbarch, desc);
+}
+
+static CORE_ADDR
+ppc64_standard_linkage2_target (struct frame_info *frame,
+ CORE_ADDR pc, unsigned int *insn)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* The address of the function descriptor this linkage function
+ references. */
+ CORE_ADDR desc
+ = ((CORE_ADDR) get_frame_register_unsigned (frame,
+ tdep->ppc_gp0_regnum + 2)
+ + (insn_d_field (insn[0]) << 16)
+ + insn_ds_field (insn[2]));
+
+ /* The first word of the descriptor is the entry point. Return that. */
+ return ppc64_desc_entry_point (gdbarch, desc);
+}
+
+static CORE_ADDR
+ppc64_standard_linkage3_target (struct frame_info *frame,
+ CORE_ADDR pc, unsigned int *insn)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* The address of the function descriptor this linkage function
+ references. */
+ CORE_ADDR desc
+ = ((CORE_ADDR) get_frame_register_unsigned (frame,
+ tdep->ppc_gp0_regnum + 2)
+ + insn_ds_field (insn[1]));
+
+ /* The first word of the descriptor is the entry point. Return that. */
+ return ppc64_desc_entry_point (gdbarch, desc);
+}
+
+
+/* Given that we've begun executing a call trampoline at PC, return
+ the entry point of the function the trampoline will go to. */
+static CORE_ADDR
+ppc64_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
+{
+ unsigned int ppc64_standard_linkage1_insn[PPC64_STANDARD_LINKAGE1_LEN];
+ unsigned int ppc64_standard_linkage2_insn[PPC64_STANDARD_LINKAGE2_LEN];
+ unsigned int ppc64_standard_linkage3_insn[PPC64_STANDARD_LINKAGE3_LEN];
+ CORE_ADDR target;
+
+ if (insns_match_pattern (pc, ppc64_standard_linkage1,
+ ppc64_standard_linkage1_insn))
+ pc = ppc64_standard_linkage1_target (frame, pc,
+ ppc64_standard_linkage1_insn);
+ else if (insns_match_pattern (pc, ppc64_standard_linkage2,
+ ppc64_standard_linkage2_insn))
+ pc = ppc64_standard_linkage2_target (frame, pc,
+ ppc64_standard_linkage2_insn);
+ else if (insns_match_pattern (pc, ppc64_standard_linkage3,
+ ppc64_standard_linkage3_insn))
+ pc = ppc64_standard_linkage3_target (frame, pc,
+ ppc64_standard_linkage3_insn);
+ else
+ return 0;
+
+ /* The PLT descriptor will either point to the already resolved target
+ address, or else to a glink stub. As the latter carry synthetic @plt
+ symbols, find_solib_trampoline_target should be able to resolve them. */
+ target = find_solib_trampoline_target (frame, pc);
+ return target ? target : pc;
+}
+
+/* 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,
+#ifdef NOTYET
+ /* AltiVec registers. */
+ /* .vr0_offset = */ 0,
+ /* .vscr_offset = */ 512 + 12,
+ /* .vrsave_offset = */ 512
+#endif
+ };
+
+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,
+#ifdef NOYET
+ /* AltiVec registers. */
+ /* .vr0_offset = */ 0,
+ /* .vscr_offset = */ 512 + 12,
+ /* .vrsave_offset = */ 528
+#endif
+ };
+
+/* FreeBSD/powerpc register set. */
+static const struct regset ppc32_fbsd_gregset = {
+ &ppc32_fbsd_reg_offsets,
+ ppc_supply_gregset,
+ ppc_collect_gregset,
+ NULL
+};
+
+static const struct regset ppc64_fbsd_gregset = {
+ &ppc64_fbsd_reg_offsets,
+ ppc_supply_gregset,
+ ppc_collect_gregset,
+ NULL
+};
+
+static 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;
+}
+
+/* Return the appropriate register set for the core section identified
+ by SECT_NAME and SECT_SIZE. */
+
+static const struct regset *
+ppcfbsd_regset_from_core_section (struct gdbarch *gdbarch,
+ const char *sect_name, size_t sect_size)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ if (strcmp (sect_name, ".reg") == 0 && sect_size >= 148)
+ {
+ if (tdep->wordsize == 4)
+ return &ppc32_fbsd_gregset;
+ else
+ return &ppc64_fbsd_gregset;
+ }
+ if (strcmp (sect_name, ".reg2") == 0 && sect_size >= 264)
+ return &ppc32_fbsd_fpregset;
+ return NULL;
+}
+
+/* Default page size. */
+static const int ppcfbsd_page_size = 4096;
+
+/* Offset for sigreturn(2). */
+static const int ppcfbsd_sigreturn_offset[] = {
+ 0xc, /* FreeBSD */
+ -1
+};
+
+/* Signal trampolines. */
+
+static int
+ppcfbsd_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 & ~(ppcfbsd_page_size - 1));
+ const int *offset;
+ const char *name;
+
+ find_pc_partial_function (pc, &name, NULL, NULL);
+ 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 (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 addr, base, func;
+ gdb_byte buf[PPC_INSN_SIZE];
+ int i;
+
+ if (*this_cache)
+ return *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 (struct frame_info *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 (struct frame_info *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 = {
+ 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);
+}
+static CORE_ADDR
+ppc64_fbsd_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ)
+{
+ struct gdbarch_tdep *tdep;
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ struct target_section *s = target_section_by_addr (targ, addr);
+ tdep = gdbarch_tdep (gdbarch);
+
+ /* Check if ADDR points to a function descriptor. */
+ if (s && strcmp (s->the_bfd_section->name, ".opd") == 0)
+ {
+ /* There may be relocations that need to be applied to the .opd
+ section. Unfortunately, this function may be called at a time
+ where these relocations have not yet been performed -- this can
+ happen for example shortly after a library has been loaded with
+ dlopen, but ld.so has not yet applied the relocations.
+
+ To cope with both the case where the relocation has been applied,
+ and the case where it has not yet been applied, we do *not* read
+ the (maybe) relocated value from target memory, but we instead
+ read the non-relocated value from the BFD, and apply the relocation
+ offset manually.
+
+ This makes the assumption that all .opd entries are always relocated
+ by the same offset the section itself was relocated. This should
+ always be the case for GNU/Linux executables and shared libraries.
+ Note that other kind of object files (e.g. those added via
+ add-symbol-files) will currently never end up here anyway, as this
+ function accesses *target* sections only; only the main exec and
+ shared libraries are ever added to the target. */
+
+ gdb_byte buf[8];
+ int res;
+
+ res = bfd_get_section_contents (s->bfd, s->the_bfd_section,
+ &buf, addr - s->addr, 8);
+ if (res != 0)
+ return extract_unsigned_integer (buf, 8, byte_order)
+ - bfd_section_vma (s->bfd, s->the_bfd_section) + s->addr;
+ }
+
+ return addr;
+}
+
+static void
+ppcfbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (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_fbsd_convert_from_func_ptr_addr);
+ 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_regset_from_core_section
+ (gdbarch, ppcfbsd_regset_from_core_section);
+
+ set_gdbarch_fetch_tls_load_module_address (gdbarch,
+ svr4_fetch_objfile_link_map);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+void _initialize_ppcfbsd_tdep (void);
+
+void
+_initialize_ppcfbsd_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc, GDB_OSABI_FREEBSD_ELF,
+ ppcfbsd_init_abi);
+ gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc64,
+ GDB_OSABI_FREEBSD_ELF,
+ ppcfbsd_init_abi);
+ gdbarch_register_osabi (bfd_arch_rs6000, 0, GDB_OSABI_FREEBSD_ELF,
+ ppcfbsd_init_abi);
+ /* Initialize the FreeBSD target descriptions. */
+ initialize_tdesc_powerpc_32l ();
+ initialize_tdesc_powerpc_64l ();
+}
Index: config/powerpc/fbsd.mh
--- /dev/null
+++ config/powerpc/fbsd.mh
@@ -0,0 +1,5 @@
+# Host: FreeBSD/powerpc
+NATDEPFILES= fbsd-nat.o fork-child.o inf-ptrace.o ppcfbsd-nat.o bsd-kvm.o
+
+LOADLIBES= -lkvm
+
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1217
diff -u -r1.1217 Makefile.in
--- Makefile.in 15 Nov 2012 16:12:16 -0000 1.1217
+++ Makefile.in 19 Nov 2012 21:24:15 -0000
@@ -557,7 +557,8 @@
moxie-tdep.o \
mt-tdep.o \
nto-tdep.o \
- ppc-linux-tdep.o ppcnbsd-tdep.o ppcobsd-tdep.o ppc-sysv-tdep.o \
+ ppc-linux-tdep.o ppcfbsd-tdep.o ppcnbsd-tdep.o ppcobsd-tdep.o \
+ ppc-sysv-tdep.o \
rl78-tdep.o \
rs6000-aix-tdep.o rs6000-tdep.o \
rx-tdep.o \
@@ -769,7 +770,7 @@
osf-share/cma_sched.h \
common/gdb_signals.h common/gdb_thread_db.h common/gdb_vecs.h \
common/i386-xstate.h common/linux-ptrace.h \
-proc-utils.h arm-tdep.h ax-gdb.h ppcnbsd-tdep.h \
+proc-utils.h arm-tdep.h ax-gdb.h ppcfbsd-tdep.h ppcnbsd-tdep.h \
cli-out.h gdb_expat.h breakpoint.h infcall.h obsd-tdep.h \
exec.h m32r-tdep.h osabi.h gdbcore.h solib-som.h amd64bsd-nat.h \
i386bsd-nat.h xml-support.h xml-tdesc.h alphabsd-tdep.h gdb_obstack.h \
@@ -1479,6 +1480,7 @@
somread.c solib-som.c \
posix-hdep.c \
ppc-sysv-tdep.c ppc-linux-nat.c ppc-linux-tdep.c \
+ ppcfbsd-nat.c ppcfbsd-tdep.c \
ppcnbsd-nat.c ppcnbsd-tdep.c \
ppcobsd-nat.c ppcobsd-tdep.c \
procfs.c \
Index: configure.host
===================================================================
RCS file: /cvs/src/src/gdb/configure.host,v
retrieving revision 1.107
diff -u -r1.107 configure.host
--- configure.host 30 May 2012 19:41:34 -0000 1.107
+++ configure.host 19 Nov 2012 21:24:15 -0000
@@ -125,6 +125,7 @@

powerpc-*-aix* | rs6000-*-*)
gdb_host=aix ;;
+powerpc*-*-freebsd*) gdb_host=fbsd ;;
powerpc-*-linux*) gdb_host=linux ;;
powerpc-*-netbsd* | powerpc-*-knetbsd*-gnu)
gdb_host=nbsd ;;
Index: configure.tgt
===================================================================
RCS file: /cvs/src/src/gdb/configure.tgt,v
retrieving revision 1.256
diff -u -r1.256 configure.tgt
--- configure.tgt 1 Aug 2012 19:34:29 -0000 1.256
+++ configure.tgt 19 Nov 2012 21:24:15 -0000
@@ -383,6 +383,12 @@
gdb_target_obs="mt-tdep.o"
;;

+powerpc*-*-freebsd*)
+ # Target: FreeBSD/powerpc
+ gdb_target_obs="rs6000-tdep.o ppc-sysv-tdep.o ppcfbsd-tdep.o \
+ solib-svr4.o"
+ ;;
+
powerpc-*-netbsd* | powerpc-*-knetbsd*-gnu)
# Target: NetBSD/powerpc
gdb_target_obs="rs6000-tdep.o ppc-sysv-tdep.o ppcnbsd-tdep.o \