/* Target-dependent code for GDB, the GNU debugger.
Copyright (C) 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
- 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
This file is part of GDB.
#include "osabi.h"
#include "regset.h"
#include "solib-svr4.h"
+#include "solib-spu.h"
#include "ppc-tdep.h"
#include "ppc-linux-tdep.h"
#include "trad-frame.h"
#include "frame-unwind.h"
#include "tramp-frame.h"
+#include "observer.h"
+#include "auxv.h"
+#include "elf/common.h"
#include "features/rs6000/powerpc-32l.c"
#include "features/rs6000/powerpc-altivec32l.c"
+#include "features/rs6000/powerpc-cell32l.c"
+#include "features/rs6000/powerpc-vsx32l.c"
+#include "features/rs6000/powerpc-isa205-32l.c"
+#include "features/rs6000/powerpc-isa205-altivec32l.c"
+#include "features/rs6000/powerpc-isa205-vsx32l.c"
#include "features/rs6000/powerpc-64l.c"
#include "features/rs6000/powerpc-altivec64l.c"
+#include "features/rs6000/powerpc-cell64l.c"
+#include "features/rs6000/powerpc-vsx64l.c"
+#include "features/rs6000/powerpc-isa205-64l.c"
+#include "features/rs6000/powerpc-isa205-altivec64l.c"
+#include "features/rs6000/powerpc-isa205-vsx64l.c"
#include "features/rs6000/powerpc-e500l.c"
else in the event that some other platform has similar needs with
regard to removing breakpoints in some potentially self modifying
code. */
-int
+static int
ppc_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
/* If DESC is the address of a 64-bit PowerPC GNU/Linux function
descriptor, return the descriptor's entry point. */
static CORE_ADDR
-ppc64_desc_entry_point (CORE_ADDR desc)
+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);
+ return (CORE_ADDR) read_memory_unsigned_integer (desc, 8, byte_order);
}
ppc64_standard_linkage1_target (struct frame_info *frame,
CORE_ADDR pc, unsigned int *insn)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (frame));
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
/* The address of the function descriptor this linkage function
references. */
+ insn_ds_field (insn[2]));
/* The first word of the descriptor is the entry point. Return that. */
- return ppc64_desc_entry_point (desc);
+ return ppc64_desc_entry_point (gdbarch, desc);
}
+static struct core_regset_section ppc_linux_vsx_regset_sections[] =
+{
+ { ".reg", 268 },
+ { ".reg2", 264 },
+ { ".reg-ppc-vmx", 544 },
+ { ".reg-ppc-vsx", 256 },
+ { NULL, 0}
+};
+
+static struct core_regset_section ppc_linux_vmx_regset_sections[] =
+{
+ { ".reg", 268 },
+ { ".reg2", 264 },
+ { ".reg-ppc-vmx", 544 },
+ { NULL, 0}
+};
+
+static struct core_regset_section ppc_linux_fp_regset_sections[] =
+{
+ { ".reg", 268 },
+ { ".reg2", 264 },
+ { NULL, 0}
+};
+
static CORE_ADDR
ppc64_standard_linkage2_target (struct frame_info *frame,
CORE_ADDR pc, unsigned int *insn)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (frame));
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
/* The address of the function descriptor this linkage function
references. */
+ insn_ds_field (insn[2]));
/* The first word of the descriptor is the entry point. Return that. */
- return ppc64_desc_entry_point (desc);
+ 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_tdep *tdep = gdbarch_tdep (get_frame_arch (frame));
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
/* The address of the function descriptor this linkage function
references. */
+ insn_ds_field (insn[1]));
/* The first word of the descriptor is the entry point. Return that. */
- return ppc64_desc_entry_point (desc);
+ return ppc64_desc_entry_point (gdbarch, desc);
}
CORE_ADDR addr,
struct target_ops *targ)
{
- struct section_table *s = target_section_by_addr (targ, addr);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ struct target_section *s = target_section_by_addr (targ, addr);
/* Check if ADDR points to a function descriptor. */
if (s && strcmp (s->the_bfd_section->name, ".opd") == 0)
- return get_target_memory_unsigned (targ, addr, 8);
+ {
+ /* 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;
}
NULL
};
+static const struct regset ppc32_linux_vsxregset = {
+ &ppc32_linux_reg_offsets,
+ ppc_supply_vsxregset,
+ ppc_collect_vsxregset,
+ NULL
+};
+
const struct regset *
ppc_linux_gregset (int wordsize)
{
return &ppc32_linux_fpregset;
if (strcmp (sect_name, ".reg-ppc-vmx") == 0)
return &ppc32_linux_vrregset;
+ if (strcmp (sect_name, ".reg-ppc-vsx") == 0)
+ return &ppc32_linux_vsxregset;
return NULL;
}
int i;
struct gdbarch *gdbarch = get_frame_arch (this_frame);
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
base = get_frame_register_unsigned (this_frame,
gdbarch_sp_regnum (gdbarch));
regs = base + offset;
/* Use that to find the address of the corresponding register
buffers. */
- gpregs = read_memory_unsigned_integer (regs, tdep->wordsize);
+ gpregs = read_memory_unsigned_integer (regs, tdep->wordsize, byte_order);
fpregs = gpregs + 48 * tdep->wordsize;
/* General purpose. */
};
+/* Address to use for displaced stepping. When debugging a stand-alone
+ SPU executable, entry_point_address () will point to an SPU local-store
+ address and is thus not usable as displaced stepping location. We use
+ the auxiliary vector to determine the PowerPC-side entry point address
+ instead. */
+
+static CORE_ADDR ppc_linux_entry_point_addr = 0;
+
+static void
+ppc_linux_inferior_created (struct target_ops *target, int from_tty)
+{
+ ppc_linux_entry_point_addr = 0;
+}
+
+static CORE_ADDR
+ppc_linux_displaced_step_location (struct gdbarch *gdbarch)
+{
+ if (ppc_linux_entry_point_addr == 0)
+ {
+ CORE_ADDR addr;
+
+ /* Determine entry point from target auxiliary vector. */
+ if (target_auxv_search (¤t_target, AT_ENTRY, &addr) <= 0)
+ error (_("Cannot find AT_ENTRY auxiliary vector entry."));
+
+ /* Make certain that the address points at real code, and not a
+ function descriptor. */
+ addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr,
+ ¤t_target);
+
+ /* Inferior calls also use the entry point as a breakpoint location.
+ We don't want displaced stepping to interfere with those
+ breakpoints, so leave space. */
+ ppc_linux_entry_point_addr = addr + 2 * PPC_INSN_SIZE;
+ }
+
+ return ppc_linux_entry_point_addr;
+}
+
+
/* Return 1 if PPC_ORIG_R3_REGNUM and PPC_TRAP_REGNUM are usable. */
int
ppc_linux_trap_reg_p (struct gdbarch *gdbarch)
regcache_cooked_write_unsigned (regcache, PPC_TRAP_REGNUM, -1);
}
+static int
+ppc_linux_spu_section (bfd *abfd, asection *asect, void *user_data)
+{
+ return strncmp (bfd_section_name (abfd, asect), "SPU/", 4) == 0;
+}
+
static const struct target_desc *
ppc_linux_core_read_description (struct gdbarch *gdbarch,
struct target_ops *target,
bfd *abfd)
{
+ asection *cell = bfd_sections_find_if (abfd, ppc_linux_spu_section, NULL);
asection *altivec = bfd_get_section_by_name (abfd, ".reg-ppc-vmx");
+ asection *vsx = bfd_get_section_by_name (abfd, ".reg-ppc-vsx");
asection *section = bfd_get_section_by_name (abfd, ".reg");
if (! section)
return NULL;
switch (bfd_section_size (abfd, section))
{
case 48 * 4:
- return altivec? tdesc_powerpc_altivec32l : tdesc_powerpc_32l;
+ if (cell)
+ return tdesc_powerpc_cell32l;
+ else if (vsx)
+ return tdesc_powerpc_vsx32l;
+ else if (altivec)
+ return tdesc_powerpc_altivec32l;
+ else
+ return tdesc_powerpc_32l;
case 48 * 8:
- return altivec? tdesc_powerpc_altivec64l : tdesc_powerpc_64l;
+ if (cell)
+ return tdesc_powerpc_cell64l;
+ else if (vsx)
+ return tdesc_powerpc_vsx64l;
+ else if (altivec)
+ return tdesc_powerpc_altivec64l;
+ else
+ return tdesc_powerpc_64l;
default:
return NULL;
/* Trampolines. */
tramp_frame_prepend_unwinder (gdbarch, &ppc32_linux_sigaction_tramp_frame);
tramp_frame_prepend_unwinder (gdbarch, &ppc32_linux_sighandler_tramp_frame);
+
+ /* BFD target for core files. */
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE)
+ set_gdbarch_gcore_bfd_target (gdbarch, "elf32-powerpcle");
+ else
+ set_gdbarch_gcore_bfd_target (gdbarch, "elf32-powerpc");
}
if (tdep->wordsize == 8)
/* Trampolines. */
tramp_frame_prepend_unwinder (gdbarch, &ppc64_linux_sigaction_tramp_frame);
tramp_frame_prepend_unwinder (gdbarch, &ppc64_linux_sighandler_tramp_frame);
+
+ /* BFD target for core files. */
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE)
+ set_gdbarch_gcore_bfd_target (gdbarch, "elf64-powerpcle");
+ else
+ set_gdbarch_gcore_bfd_target (gdbarch, "elf64-powerpc");
}
set_gdbarch_regset_from_core_section (gdbarch, ppc_linux_regset_from_core_section);
set_gdbarch_core_read_description (gdbarch, ppc_linux_core_read_description);
+ /* Supported register sections. */
+ if (tdesc_find_feature (info.target_desc,
+ "org.gnu.gdb.power.vsx"))
+ set_gdbarch_core_regset_sections (gdbarch, ppc_linux_vsx_regset_sections);
+ else if (tdesc_find_feature (info.target_desc,
+ "org.gnu.gdb.power.altivec"))
+ set_gdbarch_core_regset_sections (gdbarch, ppc_linux_vmx_regset_sections);
+ else
+ set_gdbarch_core_regset_sections (gdbarch, ppc_linux_fp_regset_sections);
+
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
PPC_TRAP_REGNUM, "trap");
}
}
+
+ /* Enable Cell/B.E. if supported by the target. */
+ if (tdesc_compatible_p (info.target_desc,
+ bfd_lookup_arch (bfd_arch_spu, bfd_mach_spu)))
+ {
+ /* Cell/B.E. multi-architecture support. */
+ set_spu_solib_ops (gdbarch);
+
+ /* The default displaced_step_at_entry_point doesn't work for
+ SPU stand-alone executables. */
+ set_gdbarch_displaced_step_location (gdbarch,
+ ppc_linux_displaced_step_location);
+ }
}
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_ppc_linux_tdep;
+
void
_initialize_ppc_linux_tdep (void)
{
gdbarch_register_osabi (bfd_arch_rs6000, bfd_mach_rs6k, GDB_OSABI_LINUX,
ppc_linux_init_abi);
+ /* Attach to inferior_created observer. */
+ observer_attach_inferior_created (ppc_linux_inferior_created);
+
/* Initialize the Linux target descriptions. */
initialize_tdesc_powerpc_32l ();
initialize_tdesc_powerpc_altivec32l ();
+ initialize_tdesc_powerpc_cell32l ();
+ initialize_tdesc_powerpc_vsx32l ();
+ initialize_tdesc_powerpc_isa205_32l ();
+ initialize_tdesc_powerpc_isa205_altivec32l ();
+ initialize_tdesc_powerpc_isa205_vsx32l ();
initialize_tdesc_powerpc_64l ();
initialize_tdesc_powerpc_altivec64l ();
+ initialize_tdesc_powerpc_cell64l ();
+ initialize_tdesc_powerpc_vsx64l ();
+ initialize_tdesc_powerpc_isa205_64l ();
+ initialize_tdesc_powerpc_isa205_altivec64l ();
+ initialize_tdesc_powerpc_isa205_vsx64l ();
initialize_tdesc_powerpc_e500l ();
}