#include "libxcoff.h"
#include "elf-bfd.h"
+#include "elf/ppc.h"
#include "solib-svr4.h"
#include "ppc-tdep.h"
#include "features/rs6000/powerpc-e500.c"
#include "features/rs6000/rs6000.c"
+/* The list of available "set powerpc ..." and "show powerpc ..."
+ commands. */
+static struct cmd_list_element *setpowerpccmdlist = NULL;
+static struct cmd_list_element *showpowerpccmdlist = NULL;
+
+static enum auto_boolean powerpc_soft_float_global = AUTO_BOOLEAN_AUTO;
+
+/* The vector ABI to use. Keep this in sync with powerpc_vector_abi. */
+static const char *powerpc_vector_strings[] =
+{
+ "auto",
+ "generic",
+ "altivec",
+ "spe",
+ NULL
+};
+
+/* A variable that can be configured by the user. */
+static enum powerpc_vector_abi powerpc_vector_abi_global = POWERPC_VEC_AUTO;
+static const char *powerpc_vector_abi_string = "auto";
+
/* If the kernel has to deliver a signal, it pushes a sigcontext
structure on the stack and then calls the signal handler, passing
the address of the sigcontext in an argument register. Usually
&& tdep->ppc_fpscr_regnum >= 0);
}
+/* Return non-zero if the architecture described by GDBARCH has
+ Altivec registers (vr0 --- vr31, vrsave and vscr). */
+int
+ppc_altivec_support_p (struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ return (tdep->ppc_vr0_regnum >= 0
+ && tdep->ppc_vrsave_regnum >= 0);
+}
/* Check that TABLE[GDB_REGNO] is not already initialized, and then
set it to SIM_REGNO.
/* Given a GDB register number REG, return the corresponding SIM
register number. */
static int
-rs6000_register_sim_regno (int reg)
+rs6000_register_sim_regno (struct gdbarch *gdbarch, int reg)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
int sim_regno;
if (tdep->sim_regno == NULL)
- init_sim_regno_table (current_gdbarch);
+ init_sim_regno_table (gdbarch);
gdb_assert (0 <= reg
- && reg <= gdbarch_num_regs (current_gdbarch)
- + gdbarch_num_pseudo_regs (current_gdbarch));
+ && reg <= gdbarch_num_regs (gdbarch)
+ + gdbarch_num_pseudo_regs (gdbarch));
sim_regno = tdep->sim_regno[reg];
if (sim_regno >= 0)
return -1;
}
+static int
+ppc_vrreg_offset (struct gdbarch_tdep *tdep,
+ const struct ppc_reg_offsets *offsets,
+ int regnum)
+{
+ if (regnum >= tdep->ppc_vr0_regnum
+ && regnum < tdep->ppc_vr0_regnum + ppc_num_vrs)
+ return offsets->vr0_offset + (regnum - tdep->ppc_vr0_regnum) * 16;
+
+ if (regnum == tdep->ppc_vrsave_regnum - 1)
+ return offsets->vscr_offset;
+
+ if (regnum == tdep->ppc_vrsave_regnum)
+ return offsets->vrsave_offset;
+
+ return -1;
+}
+
/* Supply register REGNUM in the general-purpose register set REGSET
from the buffer specified by GREGS and LEN to register cache
REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */
regnum == tdep->ppc_fpscr_regnum ? offsets->fpscr_size : 8);
}
+/* Supply register REGNUM in the Altivec register set REGSET
+ from the buffer specified by VRREGS and LEN to register cache
+ REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */
+
+void
+ppc_supply_vrregset (const struct regset *regset, struct regcache *regcache,
+ int regnum, const void *vrregs, size_t len)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep;
+ const struct ppc_reg_offsets *offsets;
+ size_t offset;
+
+ if (!ppc_altivec_support_p (gdbarch))
+ return;
+
+ tdep = gdbarch_tdep (gdbarch);
+ offsets = regset->descr;
+ if (regnum == -1)
+ {
+ int i;
+
+ for (i = tdep->ppc_vr0_regnum, offset = offsets->vr0_offset;
+ i < tdep->ppc_vr0_regnum + ppc_num_vrs;
+ i++, offset += 16)
+ ppc_supply_reg (regcache, i, vrregs, offset, 16);
+
+ ppc_supply_reg (regcache, (tdep->ppc_vrsave_regnum - 1),
+ vrregs, offsets->vscr_offset, 4);
+
+ ppc_supply_reg (regcache, tdep->ppc_vrsave_regnum,
+ vrregs, offsets->vrsave_offset, 4);
+ return;
+ }
+
+ offset = ppc_vrreg_offset (tdep, offsets, regnum);
+ if (regnum != tdep->ppc_vrsave_regnum
+ && regnum != tdep->ppc_vrsave_regnum - 1)
+ ppc_supply_reg (regcache, regnum, vrregs, offset, 16);
+ else
+ ppc_supply_reg (regcache, regnum,
+ vrregs, offset, 4);
+}
+
/* Collect register REGNUM in the general-purpose register set
REGSET from register cache REGCACHE into the buffer specified by
GREGS and LEN. If REGNUM is -1, do this for all registers in
ppc_collect_reg (regcache, regnum, fpregs, offset,
regnum == tdep->ppc_fpscr_regnum ? offsets->fpscr_size : 8);
}
+
+/* Collect register REGNUM in the Altivec register set
+ REGSET from register cache REGCACHE into the buffer specified by
+ VRREGS and LEN. If REGNUM is -1, do this for all registers in
+ REGSET. */
+
+void
+ppc_collect_vrregset (const struct regset *regset,
+ const struct regcache *regcache,
+ int regnum, void *vrregs, size_t len)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep;
+ const struct ppc_reg_offsets *offsets;
+ size_t offset;
+
+ if (!ppc_altivec_support_p (gdbarch))
+ return;
+
+ tdep = gdbarch_tdep (gdbarch);
+ offsets = regset->descr;
+ if (regnum == -1)
+ {
+ int i;
+
+ for (i = tdep->ppc_vr0_regnum, offset = offsets->vr0_offset;
+ i < tdep->ppc_vr0_regnum + ppc_num_vrs;
+ i++, offset += 16)
+ ppc_collect_reg (regcache, i, vrregs, offset, 16);
+
+ ppc_collect_reg (regcache, (tdep->ppc_vrsave_regnum - 1),
+ vrregs, offsets->vscr_offset, 4);
+
+ ppc_collect_reg (regcache, tdep->ppc_vrsave_regnum,
+ vrregs, offsets->vrsave_offset, 4);
+ return;
+ }
+
+ offset = ppc_vrreg_offset (tdep, offsets, regnum);
+ if (regnum != tdep->ppc_vrsave_regnum
+ && regnum != tdep->ppc_vrsave_regnum - 1)
+ ppc_collect_reg (regcache, regnum, vrregs, offset, 16);
+ else
+ ppc_collect_reg (regcache, regnum,
+ vrregs, offset, 4);
+}
\f
/* Read a LEN-byte address from debugged memory address MEMADDR. */
/* Sequence of bytes for breakpoint instruction. */
const static unsigned char *
-rs6000_breakpoint_from_pc (CORE_ADDR *bp_addr, int *bp_size)
+rs6000_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr,
+ int *bp_size)
{
static unsigned char big_breakpoint[] = { 0x7d, 0x82, 0x10, 0x08 };
static unsigned char little_breakpoint[] = { 0x08, 0x10, 0x82, 0x7d };
*bp_size = 4;
- if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
return big_breakpoint;
else
return little_breakpoint;
{
CORE_ADDR dummy;
int breakp_sz;
- const gdb_byte *breakp = rs6000_breakpoint_from_pc (&dummy, &breakp_sz);
+ const gdb_byte *breakp
+ = rs6000_breakpoint_from_pc (get_frame_arch (frame), &dummy, &breakp_sz);
int ii, insn;
CORE_ADDR loc;
CORE_ADDR breaks[2];
}
-/* return pc value after skipping a function prologue and also return
- information about a function frame.
-
- in struct rs6000_framedata fdata:
- - frameless is TRUE, if function does not have a frame.
- - nosavedpc is TRUE, if function does not save %pc value in its frame.
- - offset is the initial size of this stack frame --- the amount by
- which we decrement the sp to allocate the frame.
- - saved_gpr is the number of the first saved gpr.
- - saved_fpr is the number of the first saved fpr.
- - saved_vr is the number of the first saved vr.
- - saved_ev is the number of the first saved ev.
- - alloca_reg is the number of the register used for alloca() handling.
- Otherwise -1.
- - gpr_offset is the offset of the first saved gpr from the previous frame.
- - fpr_offset is the offset of the first saved fpr from the previous frame.
- - vr_offset is the offset of the first saved vr from the previous frame.
- - ev_offset is the offset of the first saved ev from the previous frame.
- - lr_offset is the offset of the saved lr
- - cr_offset is the offset of the saved cr
- - vrsave_offset is the offset of the saved vrsave register
- */
-
#define SIGNED_SHORT(x) \
((sizeof (short) == 2) \
? ((int)(short)(x)) \
return 0;
}
+/* return pc value after skipping a function prologue and also return
+ information about a function frame.
+
+ in struct rs6000_framedata fdata:
+ - frameless is TRUE, if function does not have a frame.
+ - nosavedpc is TRUE, if function does not save %pc value in its frame.
+ - offset is the initial size of this stack frame --- the amount by
+ which we decrement the sp to allocate the frame.
+ - saved_gpr is the number of the first saved gpr.
+ - saved_fpr is the number of the first saved fpr.
+ - saved_vr is the number of the first saved vr.
+ - saved_ev is the number of the first saved ev.
+ - alloca_reg is the number of the register used for alloca() handling.
+ Otherwise -1.
+ - gpr_offset is the offset of the first saved gpr from the previous frame.
+ - fpr_offset is the offset of the first saved fpr from the previous frame.
+ - vr_offset is the offset of the first saved vr from the previous frame.
+ - ev_offset is the offset of the first saved ev from the previous frame.
+ - lr_offset is the offset of the saved lr
+ - cr_offset is the offset of the saved cr
+ - vrsave_offset is the offset of the saved vrsave register
+ */
+
static CORE_ADDR
skip_prologue (CORE_ADDR pc, CORE_ADDR lim_pc, struct rs6000_framedata *fdata)
{
is an anonymous register. */
static const char *
-rs6000_register_name (int regno)
+rs6000_register_name (struct gdbarch *gdbarch, int regno)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
/* The upper half "registers" have names in the XML description,
but we present only the low GPRs and the full 64-bit registers
return spe_regnames[regno - tdep->ppc_ev0_regnum];
}
- return tdesc_register_name (regno);
+ return tdesc_register_name (gdbarch, regno);
}
/* Return the GDB type object for the "standard" data type of data in
double, we need a conversion if the memory format is float. */
static int
-rs6000_convert_register_p (int regnum, struct type *type)
+rs6000_convert_register_p (struct gdbarch *gdbarch, int regnum,
+ struct type *type)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
return (tdep->ppc_fp0_regnum >= 0
&& regnum >= tdep->ppc_fp0_regnum
/* Convert a DBX STABS register number to a GDB register number. */
static int
-rs6000_stab_reg_to_regnum (int num)
+rs6000_stab_reg_to_regnum (struct gdbarch *gdbarch, int num)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
if (0 <= num && num <= 31)
return tdep->ppc_gp0_regnum + num;
/* Convert a Dwarf 2 register number to a GDB register number. */
static int
-rs6000_dwarf2_reg_to_regnum (int num)
+rs6000_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int num)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
if (0 <= num && num <= 31)
return tdep->ppc_gp0_regnum + num;
return &rs6000_frame_base;
}
+/* DWARF-2 frame support. Used to handle the detection of
+ clobbered registers during function calls. */
+
+static void
+ppc_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
+ struct dwarf2_frame_state_reg *reg,
+ struct frame_info *next_frame)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ /* PPC32 and PPC64 ABI's are the same regarding volatile and
+ non-volatile registers. We will use the same code for both. */
+
+ /* Call-saved GP registers. */
+ if ((regnum >= tdep->ppc_gp0_regnum + 14
+ && regnum <= tdep->ppc_gp0_regnum + 31)
+ || (regnum == tdep->ppc_gp0_regnum + 1))
+ reg->how = DWARF2_FRAME_REG_SAME_VALUE;
+
+ /* Call-clobbered GP registers. */
+ if ((regnum >= tdep->ppc_gp0_regnum + 3
+ && regnum <= tdep->ppc_gp0_regnum + 12)
+ || (regnum == tdep->ppc_gp0_regnum))
+ reg->how = DWARF2_FRAME_REG_UNDEFINED;
+
+ /* Deal with FP registers, if supported. */
+ if (tdep->ppc_fp0_regnum >= 0)
+ {
+ /* Call-saved FP registers. */
+ if ((regnum >= tdep->ppc_fp0_regnum + 14
+ && regnum <= tdep->ppc_fp0_regnum + 31))
+ reg->how = DWARF2_FRAME_REG_SAME_VALUE;
+
+ /* Call-clobbered FP registers. */
+ if ((regnum >= tdep->ppc_fp0_regnum
+ && regnum <= tdep->ppc_fp0_regnum + 13))
+ reg->how = DWARF2_FRAME_REG_UNDEFINED;
+ }
+
+ /* Deal with ALTIVEC registers, if supported. */
+ if (tdep->ppc_vr0_regnum > 0 && tdep->ppc_vrsave_regnum > 0)
+ {
+ /* Call-saved Altivec registers. */
+ if ((regnum >= tdep->ppc_vr0_regnum + 20
+ && regnum <= tdep->ppc_vr0_regnum + 31)
+ || regnum == tdep->ppc_vrsave_regnum)
+ reg->how = DWARF2_FRAME_REG_SAME_VALUE;
+
+ /* Call-clobbered Altivec registers. */
+ if ((regnum >= tdep->ppc_vr0_regnum
+ && regnum <= tdep->ppc_vr0_regnum + 19))
+ reg->how = DWARF2_FRAME_REG_UNDEFINED;
+ }
+
+ /* Handle PC register and Stack Pointer correctly. */
+ if (regnum == gdbarch_pc_regnum (gdbarch))
+ reg->how = DWARF2_FRAME_REG_RA;
+ else if (regnum == gdbarch_sp_regnum (gdbarch))
+ reg->how = DWARF2_FRAME_REG_CFA;
+}
+
+
/* Initialize the current architecture based on INFO. If possible, re-use an
architecture from ARCHES, which is a list of architectures already created
during this debugging session.
bfd abfd;
int sysv_abi;
asection *sect;
+ enum auto_boolean soft_float_flag = powerpc_soft_float_global;
+ int soft_float;
+ enum powerpc_vector_abi vector_abi = powerpc_vector_abi_global;
int have_fpu = 1, have_spe = 0, have_mq = 0, have_altivec = 0;
int tdesc_wordsize = -1;
const struct target_desc *tdesc = info.target_desc;
return NULL;
}
+#ifdef HAVE_ELF
+ if (soft_float_flag == AUTO_BOOLEAN_AUTO && from_elf_exec)
+ {
+ switch (bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_GNU,
+ Tag_GNU_Power_ABI_FP))
+ {
+ case 1:
+ soft_float_flag = AUTO_BOOLEAN_FALSE;
+ break;
+ case 2:
+ soft_float_flag = AUTO_BOOLEAN_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (vector_abi == POWERPC_VEC_AUTO && from_elf_exec)
+ {
+ switch (bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_GNU,
+ Tag_GNU_Power_ABI_Vector))
+ {
+ case 1:
+ vector_abi = POWERPC_VEC_GENERIC;
+ break;
+ case 2:
+ vector_abi = POWERPC_VEC_ALTIVEC;
+ break;
+ case 3:
+ vector_abi = POWERPC_VEC_SPE;
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+
+ if (soft_float_flag == AUTO_BOOLEAN_TRUE)
+ soft_float = 1;
+ else if (soft_float_flag == AUTO_BOOLEAN_FALSE)
+ soft_float = 0;
+ else
+ soft_float = !have_fpu;
+
+ /* If we have a hard float binary or setting but no floating point
+ registers, downgrade to soft float anyway. We're still somewhat
+ useful in this scenario. */
+ if (!soft_float && !have_fpu)
+ soft_float = 1;
+
+ /* Similarly for vector registers. */
+ if (vector_abi == POWERPC_VEC_ALTIVEC && !have_altivec)
+ vector_abi = POWERPC_VEC_GENERIC;
+
+ if (vector_abi == POWERPC_VEC_SPE && !have_spe)
+ vector_abi = POWERPC_VEC_GENERIC;
+
+ if (vector_abi == POWERPC_VEC_AUTO)
+ {
+ if (have_altivec)
+ vector_abi = POWERPC_VEC_ALTIVEC;
+ else if (have_spe)
+ vector_abi = POWERPC_VEC_SPE;
+ else
+ vector_abi = POWERPC_VEC_GENERIC;
+ }
+
+ /* Do not limit the vector ABI based on available hardware, since we
+ do not yet know what hardware we'll decide we have. Yuck! FIXME! */
+
/* Find a candidate among extant architectures. */
for (arches = gdbarch_list_lookup_by_info (arches, &info);
arches != NULL;
meaningful, because 64-bit CPUs can run in 32-bit mode. So, perform
separate word size check. */
tdep = gdbarch_tdep (arches->gdbarch);
+ if (tdep && tdep->soft_float != soft_float)
+ continue;
+ if (tdep && tdep->vector_abi != vector_abi)
+ continue;
if (tdep && tdep->wordsize == wordsize)
{
if (tdesc_data != NULL)
tdep = XCALLOC (1, struct gdbarch_tdep);
tdep->wordsize = wordsize;
+ tdep->soft_float = soft_float;
+ tdep->vector_abi = vector_abi;
gdbarch = gdbarch_alloc (&info, tdep);
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
set_gdbarch_breakpoint_from_pc (gdbarch, rs6000_breakpoint_from_pc);
+ /* The value of symbols of type N_SO and N_FUN maybe null when
+ it shouldn't be. */
+ set_gdbarch_sofun_address_maybe_missing (gdbarch, 1);
+
/* Handles single stepping of atomic sequences. */
set_gdbarch_software_single_step (gdbarch, deal_with_atomic_sequence);
frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
dwarf2_frame_set_adjust_regnum (gdbarch, rs6000_adjust_frame_regnum);
+ /* Frame handling. */
+ dwarf2_frame_set_init_reg (gdbarch, ppc_dwarf2_frame_init_reg);
+
/* Hook in ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);
/* FIXME: Dump gdbarch_tdep. */
}
+/* PowerPC-specific commands. */
+
+static void
+set_powerpc_command (char *args, int from_tty)
+{
+ printf_unfiltered (_("\
+\"set powerpc\" must be followed by an appropriate subcommand.\n"));
+ help_list (setpowerpccmdlist, "set powerpc ", all_commands, gdb_stdout);
+}
+
+static void
+show_powerpc_command (char *args, int from_tty)
+{
+ cmd_show_list (showpowerpccmdlist, from_tty, "");
+}
+
+static void
+powerpc_set_soft_float (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ struct gdbarch_info info;
+
+ /* Update the architecture. */
+ gdbarch_info_init (&info);
+ if (!gdbarch_update_p (info))
+ internal_error (__FILE__, __LINE__, "could not update architecture");
+}
+
+static void
+powerpc_set_vector_abi (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ struct gdbarch_info info;
+ enum powerpc_vector_abi vector_abi;
+
+ for (vector_abi = POWERPC_VEC_AUTO;
+ vector_abi != POWERPC_VEC_LAST;
+ vector_abi++)
+ if (strcmp (powerpc_vector_abi_string,
+ powerpc_vector_strings[vector_abi]) == 0)
+ {
+ powerpc_vector_abi_global = vector_abi;
+ break;
+ }
+
+ if (vector_abi == POWERPC_VEC_LAST)
+ internal_error (__FILE__, __LINE__, _("Invalid vector ABI accepted: %s."),
+ powerpc_vector_abi_string);
+
+ /* Update the architecture. */
+ gdbarch_info_init (&info);
+ if (!gdbarch_update_p (info))
+ internal_error (__FILE__, __LINE__, "could not update architecture");
+}
+
/* Initialization code. */
extern initialize_file_ftype _initialize_rs6000_tdep; /* -Wmissing-prototypes */
initialize_tdesc_powerpc_860 ();
initialize_tdesc_powerpc_e500 ();
initialize_tdesc_rs6000 ();
+
+ /* Add root prefix command for all "set powerpc"/"show powerpc"
+ commands. */
+ add_prefix_cmd ("powerpc", no_class, set_powerpc_command,
+ _("Various PowerPC-specific commands."),
+ &setpowerpccmdlist, "set powerpc ", 0, &setlist);
+
+ add_prefix_cmd ("powerpc", no_class, show_powerpc_command,
+ _("Various PowerPC-specific commands."),
+ &showpowerpccmdlist, "show powerpc ", 0, &showlist);
+
+ /* Add a command to allow the user to force the ABI. */
+ add_setshow_auto_boolean_cmd ("soft-float", class_support,
+ &powerpc_soft_float_global,
+ _("Set whether to use a soft-float ABI."),
+ _("Show whether to use a soft-float ABI."),
+ NULL,
+ powerpc_set_soft_float, NULL,
+ &setpowerpccmdlist, &showpowerpccmdlist);
+
+ add_setshow_enum_cmd ("vector-abi", class_support, powerpc_vector_strings,
+ &powerpc_vector_abi_string,
+ _("Set the vector ABI."),
+ _("Show the vector ABI."),
+ NULL, powerpc_set_vector_abi, NULL,
+ &setpowerpccmdlist, &showpowerpccmdlist);
}