2004-10-08 Andrew Cagney <cagney@gnu.org>
[deliverable/binutils-gdb.git] / gdb / ppc-linux-tdep.c
index f2d76b8b11b3ebbb60f4f63f226817c2a076639f..206b78aca0255e298c2d52a3412778a76323fe88 100644 (file)
@@ -1,7 +1,7 @@
 /* Target-dependent code for GDB, the GNU debugger.
 
    Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996,
-   1997, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   1997, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #include "regcache.h"
 #include "value.h"
 #include "osabi.h"
-
+#include "regset.h"
 #include "solib-svr4.h"
 #include "ppc-tdep.h"
+#include "trad-frame.h"
+#include "frame-unwind.h"
 
 /* The following instructions are used in the signal trampoline code
    on GNU/Linux PPC. The kernel used to use magic syscalls 0x6666 and
@@ -112,7 +114,7 @@ static int ppc_linux_at_sigtramp_return_path (CORE_ADDR pc);
 /* Determine if pc is in a signal trampoline...
 
    Ha!  That's not what this does at all.  wait_for_inferior in
-   infrun.c calls PC_IN_SIGTRAMP in order to detect entry into a
+   infrun.c calls get_frame_type() in order to detect entry into a
    signal trampoline just after delivery of a signal.  But on
    GNU/Linux, signal trampolines are used for the return path only.
    The kernel sets things up so that the signal handler is called
@@ -144,20 +146,9 @@ static int ppc_linux_at_sigtramp_return_path (CORE_ADDR pc);
    signal is delivered while stepping, the next instruction that
    would've been stepped over isn't, instead a signal is delivered and
    the first instruction of the handler is stepped over instead.  That
-   puts us on the second instruction.  (I added the test for the
-   first instruction long after the fact, just in case the observed
-   behavior is ever fixed.)
-
-   PC_IN_SIGTRAMP is called from blockframe.c as well in order to set
-   the frame's type (if a SIGTRAMP_FRAME).  Because of our strange
-   definition of in_sigtramp below, we can't rely on the frame's type
-   getting set correctly from within blockframe.c.  This is why we
-   take pains to set it in init_extra_frame_info().
-
-   NOTE: cagney/2002-11-10: I suspect the real problem here is that
-   the get_prev_frame() only initializes the frame's type after the
-   call to INIT_FRAME_INFO.  get_prev_frame() should be fixed, this
-   code shouldn't be working its way around a bug :-(.  */
+   puts us on the second instruction.  (I added the test for the first
+   instruction long after the fact, just in case the observed behavior
+   is ever fixed.)  */
 
 int
 ppc_linux_in_sigtramp (CORE_ADDR pc, char *func_name)
@@ -188,7 +179,7 @@ ppc_linux_in_sigtramp (CORE_ADDR pc, char *func_name)
   return (pc == handler || pc == handler + 4);
 }
 
-static inline int
+static int
 insn_is_sigreturn (unsigned long pcinsn)
 {
   switch(pcinsn)
@@ -331,118 +322,13 @@ ppc_linux_skip_trampoline_code (CORE_ADDR pc)
   /* This might not work right if we have multiple symbols with the
      same name; the only way to really get it right is to perform
      the same sort of lookup as the dynamic linker. */
-  msymbol = lookup_minimal_symbol_text (symname, NULL, NULL);
+  msymbol = lookup_minimal_symbol_text (symname, NULL);
   if (!msymbol)
     return 0;
 
   return SYMBOL_VALUE_ADDRESS (msymbol);
 }
 
-/* The rs6000 version of FRAME_SAVED_PC will almost work for us.  The
-   signal handler details are different, so we'll handle those here
-   and call the rs6000 version to do the rest. */
-CORE_ADDR
-ppc_linux_frame_saved_pc (struct frame_info *fi)
-{
-  if ((get_frame_type (fi) == SIGTRAMP_FRAME))
-    {
-      CORE_ADDR regs_addr =
-       read_memory_integer (get_frame_base (fi)
-                            + PPC_LINUX_REGS_PTR_OFFSET, 4);
-      /* return the NIP in the regs array */
-      return read_memory_integer (regs_addr + 4 * PPC_LINUX_PT_NIP, 4);
-    }
-  else if (get_next_frame (fi)
-          && (get_frame_type (get_next_frame (fi)) == SIGTRAMP_FRAME))
-    {
-      CORE_ADDR regs_addr =
-       read_memory_integer (get_frame_base (get_next_frame (fi))
-                            + PPC_LINUX_REGS_PTR_OFFSET, 4);
-      /* return LNK in the regs array */
-      return read_memory_integer (regs_addr + 4 * PPC_LINUX_PT_LNK, 4);
-    }
-  else
-    return rs6000_frame_saved_pc (fi);
-}
-
-void
-ppc_linux_init_extra_frame_info (int fromleaf, struct frame_info *fi)
-{
-  rs6000_init_extra_frame_info (fromleaf, fi);
-
-  if (get_next_frame (fi) != 0)
-    {
-      /* We're called from get_prev_frame_info; check to see if
-         this is a signal frame by looking to see if the pc points
-         at trampoline code */
-      if (ppc_linux_at_sigtramp_return_path (get_frame_pc (fi)))
-       deprecated_set_frame_type (fi, SIGTRAMP_FRAME);
-      else
-       /* FIXME: cagney/2002-11-10: Is this double bogus?  What
-           happens if the frame has previously been marked as a dummy?  */
-       deprecated_set_frame_type (fi, NORMAL_FRAME);
-    }
-}
-
-int
-ppc_linux_frameless_function_invocation (struct frame_info *fi)
-{
-  /* We'll find the wrong thing if we let 
-     rs6000_frameless_function_invocation () search for a signal trampoline */
-  if (ppc_linux_at_sigtramp_return_path (get_frame_pc (fi)))
-    return 0;
-  else
-    return rs6000_frameless_function_invocation (fi);
-}
-
-void
-ppc_linux_frame_init_saved_regs (struct frame_info *fi)
-{
-  if ((get_frame_type (fi) == SIGTRAMP_FRAME))
-    {
-      CORE_ADDR regs_addr;
-      int i;
-      if (get_frame_saved_regs (fi))
-       return;
-
-      frame_saved_regs_zalloc (fi);
-
-      regs_addr =
-       read_memory_integer (get_frame_base (fi)
-                            + PPC_LINUX_REGS_PTR_OFFSET, 4);
-      get_frame_saved_regs (fi)[PC_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_NIP;
-      get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_ps_regnum] =
-        regs_addr + 4 * PPC_LINUX_PT_MSR;
-      get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_cr_regnum] =
-        regs_addr + 4 * PPC_LINUX_PT_CCR;
-      get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_lr_regnum] =
-        regs_addr + 4 * PPC_LINUX_PT_LNK;
-      get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_ctr_regnum] =
-        regs_addr + 4 * PPC_LINUX_PT_CTR;
-      get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_xer_regnum] =
-        regs_addr + 4 * PPC_LINUX_PT_XER;
-      get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_mq_regnum] =
-       regs_addr + 4 * PPC_LINUX_PT_MQ;
-      for (i = 0; i < 32; i++)
-       get_frame_saved_regs (fi)[gdbarch_tdep (current_gdbarch)->ppc_gp0_regnum + i] =
-         regs_addr + 4 * PPC_LINUX_PT_R0 + 4 * i;
-      for (i = 0; i < 32; i++)
-       get_frame_saved_regs (fi)[FP0_REGNUM + i] = regs_addr + 4 * PPC_LINUX_PT_FPR0 + 8 * i;
-    }
-  else
-    rs6000_frame_init_saved_regs (fi);
-}
-
-CORE_ADDR
-ppc_linux_frame_chain (struct frame_info *thisframe)
-{
-  /* Kernel properly constructs the frame chain for the handler */
-  if ((get_frame_type (thisframe) == SIGTRAMP_FRAME))
-    return read_memory_integer (get_frame_base (thisframe), 4);
-  else
-    return rs6000_frame_chain (thisframe);
-}
-
 /* ppc_linux_memory_remove_breakpoints attempts to remove a breakpoint
    in much the same fashion as memory_remove_breakpoint in mem-break.c,
    but is careful not to write back the previous contents if the code
@@ -591,6 +477,26 @@ ppc_linux_memory_remove_breakpoint (CORE_ADDR addr, char *contents_cache)
   return val;
 }
 
+/* For historic reasons, PPC 32 GNU/Linux follows PowerOpen rather
+   than the 32 bit SYSV R4 ABI structure return convention - all
+   structures, no matter their size, are put in memory.  Vectors,
+   which were added later, do get returned in a register though.  */
+
+static enum return_value_convention
+ppc_linux_return_value (struct gdbarch *gdbarch, struct type *valtype,
+                       struct regcache *regcache, void *readbuf,
+                       const void *writebuf)
+{  
+  if ((TYPE_CODE (valtype) == TYPE_CODE_STRUCT
+       || TYPE_CODE (valtype) == TYPE_CODE_UNION)
+      && !((TYPE_LENGTH (valtype) == 16 || TYPE_LENGTH (valtype) == 8)
+          && TYPE_VECTOR (valtype)))
+    return RETURN_VALUE_STRUCT_CONVENTION;
+  else
+    return ppc_sysv_abi_return_value (gdbarch, valtype, regcache, readbuf,
+                                     writebuf);
+}
+
 /* Fetch (and possibly build) an appropriate link_map_offsets
    structure for GNU/Linux PPC targets using the struct offsets
    defined in link.h (but without actual reference to that file).
@@ -731,6 +637,16 @@ insn_ds_field (unsigned int insn)
 }
 
 
+/* 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)
+{
+  /* The first word of the descriptor is the entry point.  */
+  return (CORE_ADDR) read_memory_unsigned_integer (desc, 8);
+}
+
+
 /* Pattern for the standard linkage function.  These are built by
    build_plt_stub in elf64-ppc.c, whose GLINK argument is always
    zero.  */
@@ -770,12 +686,12 @@ static struct insn_pattern ppc64_standard_linkage[] =
   (sizeof (ppc64_standard_linkage) / sizeof (ppc64_standard_linkage[0]))
 
 
-/* Recognize a 64-bit PowerPC Linux linkage function --- what GDB
+/* Recognize a 64-bit PowerPC GNU/Linux linkage function --- what GDB
    calls a "solib trampoline".  */
 static int
 ppc64_in_solib_call_trampoline (CORE_ADDR pc, char *name)
 {
-  /* Detecting solib call trampolines on PPC64 Linux is a pain.
+  /* Detecting solib call trampolines on PPC64 GNU/Linux is a pain.
 
      It's not specifically solib call trampolines that are the issue.
      Any call from one function to another function that uses a
@@ -787,7 +703,7 @@ ppc64_in_solib_call_trampoline (CORE_ADDR pc, char *name)
      also an inter-TOC call, and requires a trampoline --- so "solib
      call trampolines" are just a special case.
 
-     The 64-bit PowerPC Linux ABI calls these call trampolines
+     The 64-bit PowerPC GNU/Linux ABI calls these call trampolines
      "linkage functions".  Since they need to be near the functions
      that call them, they all appear in .text, not in any special
      section.  The .plt section just contains an array of function
@@ -865,7 +781,7 @@ ppc64_standard_linkage_target (CORE_ADDR pc, unsigned int *insn)
        + insn_ds_field (insn[2]));
 
   /* The first word of the descriptor is the entry point.  Return that.  */
-  return (CORE_ADDR) read_memory_unsigned_integer (desc, 8);
+  return ppc64_desc_entry_point (desc);
 }
 
 
@@ -884,117 +800,291 @@ ppc64_skip_trampoline_code (CORE_ADDR pc)
 }
 
 
-enum {
-  ELF_NGREG = 48,
-  ELF_NFPREG = 33,
-  ELF_NVRREG = 33
-};
+/* Support for CONVERT_FROM_FUNC_PTR_ADDR (ARCH, ADDR, TARG) on PPC64
+   GNU/Linux.
+
+   Usually a function pointer's representation is simply the address
+   of the function. On GNU/Linux on the 64-bit PowerPC however, a
+   function pointer is represented by a pointer to a TOC entry. This
+   TOC entry contains three words, the first word is the address of
+   the function, the second word is the TOC pointer (r2), and the
+   third word is the static chain value.  Throughout GDB it is
+   currently assumed that a function pointer contains the address of
+   the function, which is not easy to fix.  In addition, the
+   conversion of a function address to a function pointer would
+   require allocation of a TOC entry in the inferior's memory space,
+   with all its drawbacks.  To be able to call C++ virtual methods in
+   the inferior (which are called via function pointers),
+   find_function_addr uses this function to get the function address
+   from a function pointer.  */
+
+/* If ADDR points at what is clearly a function descriptor, transform
+   it into the address of the corresponding function.  Be
+   conservative, otherwize GDB will do the transformation on any
+   random addresses such as occures when there is no symbol table.  */
 
-enum {
-  ELF_GREGSET_SIZE = (ELF_NGREG * 4),
-  ELF_FPREGSET_SIZE = (ELF_NFPREG * 8)
-};
+static CORE_ADDR
+ppc64_linux_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+                                       CORE_ADDR addr,
+                                       struct target_ops *targ)
+{
+  struct section_table *s = target_section_by_addr (targ, addr);
 
-void
-ppc_linux_supply_gregset (char *buf)
+  /* 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);
+
+  return addr;
+}
+
+static void
+right_supply_register (struct regcache *regcache, int wordsize, int regnum,
+                      const bfd_byte *buf)
 {
-  int regi;
-  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); 
-
-  for (regi = 0; regi < 32; regi++)
-    supply_register (regi, buf + 4 * regi);
-
-  supply_register (PC_REGNUM, buf + 4 * PPC_LINUX_PT_NIP);
-  supply_register (tdep->ppc_lr_regnum, buf + 4 * PPC_LINUX_PT_LNK);
-  supply_register (tdep->ppc_cr_regnum, buf + 4 * PPC_LINUX_PT_CCR);
-  supply_register (tdep->ppc_xer_regnum, buf + 4 * PPC_LINUX_PT_XER);
-  supply_register (tdep->ppc_ctr_regnum, buf + 4 * PPC_LINUX_PT_CTR);
-  if (tdep->ppc_mq_regnum != -1)
-    supply_register (tdep->ppc_mq_regnum, buf + 4 * PPC_LINUX_PT_MQ);
-  supply_register (tdep->ppc_ps_regnum, buf + 4 * PPC_LINUX_PT_MSR);
+  regcache_raw_supply (regcache, regnum,
+                      (buf + wordsize - register_size (current_gdbarch, regnum)));
 }
 
+/* Extract the register values found in the WORDSIZED ABI GREGSET,
+   storing their values in REGCACHE.  Note that some are left-aligned,
+   while others are right aligned.  */
+
 void
-ppc_linux_supply_fpregset (char *buf)
+ppc_linux_supply_gregset (struct regcache *regcache,
+                         int regnum, const void *gregs, size_t size,
+                         int wordsize)
 {
   int regi;
-  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); 
-
-  for (regi = 0; regi < 32; regi++)
-    supply_register (FP0_REGNUM + regi, buf + 8 * regi);
+  struct gdbarch *regcache_arch = get_regcache_arch (regcache); 
+  struct gdbarch_tdep *regcache_tdep = gdbarch_tdep (regcache_arch);
+  const bfd_byte *buf = gregs;
+
+  for (regi = 0; regi < ppc_num_gprs; regi++)
+    right_supply_register (regcache, wordsize,
+                           regcache_tdep->ppc_gp0_regnum + regi,
+                           buf + wordsize * regi);
+
+  right_supply_register (regcache, wordsize, gdbarch_pc_regnum (regcache_arch),
+                        buf + wordsize * PPC_LINUX_PT_NIP);
+  right_supply_register (regcache, wordsize, regcache_tdep->ppc_lr_regnum,
+                        buf + wordsize * PPC_LINUX_PT_LNK);
+  regcache_raw_supply (regcache, regcache_tdep->ppc_cr_regnum,
+                      buf + wordsize * PPC_LINUX_PT_CCR);
+  regcache_raw_supply (regcache, regcache_tdep->ppc_xer_regnum,
+                      buf + wordsize * PPC_LINUX_PT_XER);
+  regcache_raw_supply (regcache, regcache_tdep->ppc_ctr_regnum,
+                      buf + wordsize * PPC_LINUX_PT_CTR);
+  if (regcache_tdep->ppc_mq_regnum != -1)
+    right_supply_register (regcache, wordsize, regcache_tdep->ppc_mq_regnum,
+                          buf + wordsize * PPC_LINUX_PT_MQ);
+  right_supply_register (regcache, wordsize, regcache_tdep->ppc_ps_regnum,
+                        buf + wordsize * PPC_LINUX_PT_MSR);
+}
 
-  /* The FPSCR is stored in the low order word of the last doubleword in the
-     fpregset.  */
-  supply_register (tdep->ppc_fpscr_regnum, buf + 8 * 32 + 4);
+static void
+ppc32_linux_supply_gregset (const struct regset *regset,
+                           struct regcache *regcache,
+                           int regnum, const void *gregs, size_t size)
+{
+  ppc_linux_supply_gregset (regcache, regnum, gregs, size, 4);
 }
 
-/*
-  Use a local version of this function to get the correct types for regsets.
-*/
+static struct regset ppc32_linux_gregset = {
+  NULL, ppc32_linux_supply_gregset
+};
 
-static void
-fetch_core_registers (char *core_reg_sect,
-                     unsigned core_reg_size,
-                     int which,
-                     CORE_ADDR reg_addr)
+struct ppc_linux_sigtramp_cache
 {
-  if (which == 0)
+  CORE_ADDR base;
+  struct trad_frame_saved_reg *saved_regs;
+};
+
+static struct ppc_linux_sigtramp_cache *
+ppc_linux_sigtramp_cache (struct frame_info *next_frame, void **this_cache)
+{
+  CORE_ADDR regs;
+  CORE_ADDR gpregs;
+  CORE_ADDR fpregs;
+  int i;
+  struct ppc_linux_sigtramp_cache *cache;
+  struct gdbarch *gdbarch = get_frame_arch (next_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if ((*this_cache) != NULL)
+    return (*this_cache);
+  cache = FRAME_OBSTACK_ZALLOC (struct ppc_linux_sigtramp_cache);
+  (*this_cache) = cache;
+  cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+
+  cache->base = frame_unwind_register_unsigned (next_frame, SP_REGNUM);
+
+  /* Find the register pointer, which gives the address of the
+     register buffers.  */
+  if (tdep->wordsize == 4)
+    regs = (cache->base
+           + 0xd0 /* Offset to ucontext_t.  */
+           + 0x30 /* Offset to .reg.  */);
+  else
+    regs = (cache->base
+           + 0x80 /* Offset to ucontext_t.  */
+           + 0xe0 /* Offset to .reg.  */);
+  /* And the corresponding register buffers.  */
+  gpregs = read_memory_unsigned_integer (regs, tdep->wordsize);
+  fpregs = gpregs + 48 * tdep->wordsize;
+
+  /* General purpose.  */
+  for (i = 0; i < ppc_num_gprs; i++)
     {
-      if (core_reg_size == ELF_GREGSET_SIZE)
-       ppc_linux_supply_gregset (core_reg_sect);
-      else
-       warning ("wrong size gregset struct in core file");
+      int regnum = i + tdep->ppc_gp0_regnum;
+      cache->saved_regs[regnum].addr = gpregs + i * tdep->wordsize;
     }
-  else if (which == 2)
+  cache->saved_regs[PC_REGNUM].addr = gpregs + 32 * tdep->wordsize;
+  cache->saved_regs[tdep->ppc_ctr_regnum].addr = gpregs + 35 * tdep->wordsize;
+  cache->saved_regs[tdep->ppc_lr_regnum].addr = gpregs + 36 * tdep->wordsize;
+  cache->saved_regs[tdep->ppc_xer_regnum].addr = gpregs + 37 * tdep->wordsize;
+  cache->saved_regs[tdep->ppc_cr_regnum].addr = gpregs + 38 * tdep->wordsize;
+
+  /* Floating point registers.  */
+  if (ppc_floating_point_unit_p (gdbarch))
     {
-      if (core_reg_size == ELF_FPREGSET_SIZE)
-       ppc_linux_supply_fpregset (core_reg_sect);
-      else
-       warning ("wrong size fpregset struct in core file");
+      for (i = 0; i < ppc_num_fprs; i++)
+        {
+          int regnum = i + tdep->ppc_fp0_regnum;
+          cache->saved_regs[regnum].addr = fpregs + i * tdep->wordsize;
+        }
+      cache->saved_regs[tdep->ppc_fpscr_regnum].addr
+        = fpregs + 32 * tdep->wordsize;
     }
+
+  return cache;
 }
 
-/* Register that we are able to handle ELF file formats using standard
-   procfs "regset" structures.  */
+static void
+ppc_linux_sigtramp_this_id (struct frame_info *next_frame, void **this_cache,
+                         struct frame_id *this_id)
+{
+  struct ppc_linux_sigtramp_cache *info
+    = ppc_linux_sigtramp_cache (next_frame, this_cache);
+  (*this_id) = frame_id_build (info->base, frame_pc_unwind (next_frame));
+}
 
-static struct core_fns ppc_linux_regset_core_fns =
+static void
+ppc_linux_sigtramp_prev_register (struct frame_info *next_frame,
+                               void **this_cache,
+                               int regnum, int *optimizedp,
+                               enum lval_type *lvalp, CORE_ADDR *addrp,
+                               int *realnump, void *valuep)
 {
-  bfd_target_elf_flavour,      /* core_flavour */
-  default_check_format,                /* check_format */
-  default_core_sniffer,                /* core_sniffer */
-  fetch_core_registers,                /* core_read_registers */
-  NULL                         /* next */
+  struct ppc_linux_sigtramp_cache *info
+    = ppc_linux_sigtramp_cache (next_frame, this_cache);
+  trad_frame_get_prev_register (next_frame, info->saved_regs, regnum,
+                               optimizedp, lvalp, addrp, realnump, valuep);
+}
+
+static const struct frame_unwind ppc_linux_sigtramp_unwind =
+{
+  SIGTRAMP_FRAME,
+  ppc_linux_sigtramp_this_id,
+  ppc_linux_sigtramp_prev_register
 };
 
+static const struct frame_unwind *
+ppc_linux_sigtramp_sniffer (struct frame_info *next_frame)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (next_frame));
+  if (frame_pc_unwind (next_frame)
+      > frame_unwind_register_unsigned (next_frame, SP_REGNUM))
+    /* Assume anything that is vaguely on the stack is a signal
+       trampoline.  */
+    return &ppc_linux_sigtramp_unwind;
+  else
+    return NULL;
+}
+
+static void
+ppc64_linux_supply_gregset (const struct regset *regset,
+                           struct regcache * regcache,
+                           int regnum, const void *gregs, size_t size)
+{
+  ppc_linux_supply_gregset (regcache, regnum, gregs, size, 8);
+}
+
+static struct regset ppc64_linux_gregset = {
+  NULL, ppc64_linux_supply_gregset
+};
+
+void
+ppc_linux_supply_fpregset (const struct regset *regset,
+                          struct regcache * regcache,
+                          int regnum, const void *fpset, size_t size)
+{
+  int regi;
+  struct gdbarch *regcache_arch = get_regcache_arch (regcache); 
+  struct gdbarch_tdep *regcache_tdep = gdbarch_tdep (regcache_arch);
+  const bfd_byte *buf = fpset;
+
+  if (! ppc_floating_point_unit_p (regcache_arch))
+    return;
+
+  for (regi = 0; regi < ppc_num_fprs; regi++)
+    regcache_raw_supply (regcache, 
+                         regcache_tdep->ppc_fp0_regnum + regi,
+                         buf + 8 * regi);
+
+  /* The FPSCR is stored in the low order word of the last
+     doubleword in the fpregset.  */
+  regcache_raw_supply (regcache, regcache_tdep->ppc_fpscr_regnum,
+                       buf + 8 * 32 + 4);
+}
+
+static struct regset ppc_linux_fpregset = { NULL, ppc_linux_supply_fpregset };
+
+static const struct regset *
+ppc_linux_regset_from_core_section (struct gdbarch *core_arch,
+                                   const char *sect_name, size_t sect_size)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (core_arch);
+  if (strcmp (sect_name, ".reg") == 0)
+    {
+      if (tdep->wordsize == 4)
+       return &ppc32_linux_gregset;
+      else
+       return &ppc64_linux_gregset;
+    }
+  if (strcmp (sect_name, ".reg2") == 0)
+    return &ppc_linux_fpregset;
+  return NULL;
+}
+
 static void
 ppc_linux_init_abi (struct gdbarch_info info,
                     struct gdbarch *gdbarch)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  /* Until November 2001, gcc was not complying to the SYSV ABI for
-     returning structures less than or equal to 8 bytes in size. It was
-     returning everything in memory. When this was corrected, it wasn't
-     fixed for native platforms.  */
-  set_gdbarch_use_struct_convention (gdbarch,
-                                   ppc_sysv_abi_broken_use_struct_convention);
-
   if (tdep->wordsize == 4)
     {
-      /* Note: kevinb/2002-04-12: See note in rs6000_gdbarch_init regarding
-        *_push_arguments().  The same remarks hold for the methods below.  */
-      set_gdbarch_frameless_function_invocation (gdbarch,
-        ppc_linux_frameless_function_invocation);
-      set_gdbarch_deprecated_frame_chain (gdbarch, ppc_linux_frame_chain);
-      set_gdbarch_deprecated_frame_saved_pc (gdbarch, ppc_linux_frame_saved_pc);
-
-      set_gdbarch_deprecated_frame_init_saved_regs (gdbarch,
-                                         ppc_linux_frame_init_saved_regs);
-      set_gdbarch_deprecated_init_extra_frame_info (gdbarch,
-                                         ppc_linux_init_extra_frame_info);
+      /* NOTE: jimb/2004-03-26: The System V ABI PowerPC Processor
+         Supplement says that long doubles are sixteen bytes long.
+         However, as one of the known warts of its ABI, PPC GNU/Linux
+         uses eight-byte long doubles.  GCC only recently got 128-bit
+         long double support on PPC, so it may be changing soon.  The
+         Linux[sic] Standards Base says that programs that use 'long
+         double' on PPC GNU/Linux are non-conformant.  */
+      set_gdbarch_long_double_bit (gdbarch, 8 * TARGET_CHAR_BIT);
+
+      /* Until November 2001, gcc did not comply with the 32 bit SysV
+        R4 ABI requirement that structures less than or equal to 8
+        bytes should be returned in registers.  Instead GCC was using
+        the the AIX/PowerOpen ABI - everything returned in memory
+        (well ignoring vectors that is).  When this was corrected, it
+        wasn't fixed for GNU/Linux native platform.  Use the
+        PowerOpen struct convention.  */
+      set_gdbarch_return_value (gdbarch, ppc_linux_return_value);
 
       set_gdbarch_memory_remove_breakpoint (gdbarch,
                                             ppc_linux_memory_remove_breakpoint);
+
       /* Shared library handling.  */
       set_gdbarch_in_solib_call_trampoline (gdbarch, in_plt_section);
       set_gdbarch_skip_trampoline_code (gdbarch,
@@ -1005,16 +1095,31 @@ ppc_linux_init_abi (struct gdbarch_info info,
   
   if (tdep->wordsize == 8)
     {
+      /* Handle PPC64 GNU/Linux function pointers (which are really
+         function descriptors).  */
+      set_gdbarch_convert_from_func_ptr_addr
+        (gdbarch, ppc64_linux_convert_from_func_ptr_addr);
+
       set_gdbarch_in_solib_call_trampoline
         (gdbarch, ppc64_in_solib_call_trampoline);
       set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
+
+      /* PPC64 malloc's entry-point is called ".malloc".  */
+      set_gdbarch_name_of_malloc (gdbarch, ".malloc");
     }
+  set_gdbarch_regset_from_core_section (gdbarch, ppc_linux_regset_from_core_section);
+  frame_unwind_append_sniffer (gdbarch, ppc_linux_sigtramp_sniffer);
 }
 
 void
 _initialize_ppc_linux_tdep (void)
 {
-  gdbarch_register_osabi (bfd_arch_powerpc, 0, GDB_OSABI_LINUX,
-                         ppc_linux_init_abi);
-  add_core_fns (&ppc_linux_regset_core_fns);
+  /* Register for all sub-familes of the POWER/PowerPC: 32-bit and
+     64-bit PowerPC, and the older rs6k.  */
+  gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc, GDB_OSABI_LINUX,
+                         ppc_linux_init_abi);
+  gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc64, GDB_OSABI_LINUX,
+                         ppc_linux_init_abi);
+  gdbarch_register_osabi (bfd_arch_rs6000, bfd_mach_rs6k, GDB_OSABI_LINUX,
+                         ppc_linux_init_abi);
 }
This page took 0.031931 seconds and 4 git commands to generate.