2003-10-10 Andrew Cagney <cagney@redhat.com>
authorAndrew Cagney <cagney@redhat.com>
Fri, 10 Oct 2003 18:29:13 +0000 (18:29 +0000)
committerAndrew Cagney <cagney@redhat.com>
Fri, 10 Oct 2003 18:29:13 +0000 (18:29 +0000)
* Makefile.in (ppc-sysv-tdep.o): Add $(gdb_assert_h).
* rs6000-tdep.c (rs6000_gdbarch_init): When 64 bit SysV ABI, set
push_dummy_call to ppc64_sysv_abi_push_dummy_call.
* ppc-sysv-tdep.c: Include "gdb_assert.h".
(ppc64_sysv_abi_push_dummy_call): New function.
(ppc64_sysv_abi_broken_push_dummy_call): New function.
* ppc-tdep.h (ppc64_sysv_abi_push_dummy_call): Declare.
(ppc64_sysv_abi_broken_push_dummy_call): Declare.

gdb/ChangeLog
gdb/Makefile.in
gdb/ppc-sysv-tdep.c
gdb/ppc-tdep.h
gdb/rs6000-tdep.c

index 0d7dbfe..41f7dfe 100644 (file)
@@ -1,3 +1,14 @@
+2003-10-10  Andrew Cagney  <cagney@redhat.com>
+
+       * Makefile.in (ppc-sysv-tdep.o): Add $(gdb_assert_h).
+       * rs6000-tdep.c (rs6000_gdbarch_init): When 64 bit SysV ABI, set
+       push_dummy_call to ppc64_sysv_abi_push_dummy_call.
+       * ppc-sysv-tdep.c: Include "gdb_assert.h".
+       (ppc64_sysv_abi_push_dummy_call): New function.
+       (ppc64_sysv_abi_broken_push_dummy_call): New function.
+       * ppc-tdep.h (ppc64_sysv_abi_push_dummy_call): Declare.
+       (ppc64_sysv_abi_broken_push_dummy_call): Declare.
+
 2003-10-10  Kei Sakamoto  <sakamoto.kei@renesas.com>
 
        * NEWS: Replace "Hitachi" and "Mitsubishi" with "Renesas".
index 5aa199d..7854d1d 100644 (file)
@@ -2115,7 +2115,7 @@ ppcnbsd-tdep.o: ppcnbsd-tdep.c $(defs_h) $(gdbcore_h) $(regcache_h) \
        $(target_h) $(breakpoint_h) $(value_h) $(osabi_h) $(ppc_tdep_h) \
        $(ppcnbsd_tdep_h) $(nbsd_tdep_h) $(solib_svr4_h)
 ppc-sysv-tdep.o: ppc-sysv-tdep.c $(defs_h) $(gdbcore_h) $(inferior_h) \
-       $(regcache_h) $(value_h) $(gdb_string_h) $(ppc_tdep_h)
+       $(regcache_h) $(value_h) $(gdb_string_h) $(gdb_assert_h) $(ppc_tdep_h)
 printcmd.o: printcmd.c $(defs_h) $(gdb_string_h) $(frame_h) $(symtab_h) \
        $(gdbtypes_h) $(value_h) $(language_h) $(expression_h) $(gdbcore_h) \
        $(gdbcmd_h) $(target_h) $(breakpoint_h) $(demangle_h) $(valprint_h) \
index 82c58da..0b15366 100644 (file)
@@ -26,7 +26,7 @@
 #include "regcache.h"
 #include "value.h"
 #include "gdb_string.h"
-
+#include "gdb_assert.h"
 #include "ppc-tdep.h"
 
 /* Pass the arguments in either registers, or in the stack. Using the
@@ -317,6 +317,295 @@ ppc_sysv_abi_use_struct_convention (int gcc_p, struct type *value_type)
   return (TYPE_LENGTH (value_type) > 8);
 }
 
+/* Pass the arguments in either registers, or in the stack. Using the
+   ppc 64 bit SysV ABI.
+
+   This implements a dumbed down version of the ABI.  It always writes
+   values to memory, GPR and FPR, even when not necessary.  Doing this
+   greatly simplifies the logic. */
+
+CORE_ADDR
+ppc64_sysv_abi_push_dummy_call (struct gdbarch *gdbarch, CORE_ADDR func_addr,
+                               struct regcache *regcache, CORE_ADDR bp_addr,
+                               int nargs, struct value **args, CORE_ADDR sp,
+                               int struct_return, CORE_ADDR struct_addr)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+  /* By this stage in the proceedings, SP has been decremented by "red
+     zone size" + "struct return size".  Fetch the stack-pointer from
+     before this and use that as the BACK_CHAIN.  */
+  const CORE_ADDR back_chain = read_sp ();
+  /* See for-loop comment below.  */
+  int write_pass;
+  /* Size of the Altivec's vector parameter region, the final value is
+     computed in the for-loop below.  */
+  LONGEST vparam_size = 0;
+  /* Size of the general parameter region, the final value is computed
+     in the for-loop below.  */
+  LONGEST gparam_size = 0;
+  /* Kevin writes ... I don't mind seeing tdep->wordsize used in the
+     calls to align_up(), align_down(), etc.  because this makes it
+     easier to reuse this code (in a copy/paste sense) in the future,
+     but it is a 64-bit ABI and asserting that the wordsize is 8 bytes
+     at some point makes it easier to verify that this function is
+     correct without having to do a non-local analysis to figure out
+     the possible values of tdep->wordsize.  */
+  gdb_assert (tdep->wordsize == 8);
+
+  /* Go through the argument list twice.
+
+     Pass 1: Compute the function call's stack space and register
+     requirements.
+
+     Pass 2: Replay the same computation but this time also write the
+     values out to the target.  */
+
+  for (write_pass = 0; write_pass < 2; write_pass++)
+    {
+      int argno;
+      /* Next available floating point register for float and double
+         arguments.  */
+      int freg = 1;
+      /* Next available general register for non-vector (but possibly
+         float) arguments.  */
+      int greg = 3;
+      /* Next available vector register for vector arguments.  */
+      int vreg = 2;
+      /* The address, at which the next general purpose parameter
+         (integer, struct, float, ...) should be saved.  */
+      CORE_ADDR gparam;
+      /* Address, at which the next Altivec vector parameter should be
+         saved.  */
+      CORE_ADDR vparam;
+
+      if (!write_pass)
+       {
+         /* During the first pass, GPARAM and VPARAM are more like
+            offsets (start address zero) than addresses.  That way
+            the accumulate the total stack space each region
+            requires.  */
+         gparam = 0;
+         vparam = 0;
+       }
+      else
+       {
+         /* Decrement the stack pointer making space for the Altivec
+            and general on-stack parameters.  Set vparam and gparam
+            to their corresponding regions.  */
+         vparam = align_down (sp - vparam_size, 16);
+         gparam = align_down (vparam - gparam_size, 16);
+         /* Add in space for the TOC, link editor double word,
+            compiler double word, LR save area, CR save area.  */
+         sp = align_down (gparam - 48, 16);
+       }
+
+      /* If the function is returning a `struct', then there is an
+         extra hidden parameter (which will be passed in r3)
+         containing the address of that struct..  In that case we
+         should advance one word and start from r4 register to copy
+         parameters.  This also consumes one on-stack parameter slot.  */
+      if (struct_return)
+       {
+         if (write_pass)
+           regcache_cooked_write_signed (regcache,
+                                         tdep->ppc_gp0_regnum + greg,
+                                         struct_addr);
+         greg++;
+         gparam = align_up (gparam + tdep->wordsize, tdep->wordsize);
+       }
+
+      for (argno = 0; argno < nargs; argno++)
+       {
+         struct value *arg = args[argno];
+         struct type *type = check_typedef (VALUE_TYPE (arg));
+         char *val = VALUE_CONTENTS (arg);
+         if (TYPE_CODE (type) == TYPE_CODE_FLT && TYPE_LENGTH (type) <= 8)
+           {
+             /* Floats and Doubles go in f1 .. f13.  They also
+                consume a left aligned GREG,, and can end up in
+                memory.  */
+             if (write_pass)
+               {
+                 if (ppc_floating_point_unit_p (current_gdbarch)
+                     && freg <= 13)
+                   {
+                     char regval[MAX_REGISTER_SIZE];
+                     struct type *regtype = register_type (gdbarch,
+                                                           FP0_REGNUM);
+                     convert_typed_floating (val, type, regval, regtype);
+                     regcache_cooked_write (regcache, FP0_REGNUM + freg,
+                                            regval);
+                   }
+                 if (greg <= 10)
+                   {
+                     /* The ABI states "Single precision floating
+                        point values are mapped to the first word in
+                        a single doubleword" and "... floating point
+                        values mapped to the first eight doublewords
+                        of the parameter save area are also passed in
+                        general registers").
+
+                        This code interprets that to mean: store it,
+                        left aligned, in the general register.  */
+                     char regval[MAX_REGISTER_SIZE];
+                     memset (regval, 0, sizeof regval);
+                     memcpy (regval, val, TYPE_LENGTH (type));
+                     regcache_cooked_write (regcache,
+                                            tdep->ppc_gp0_regnum + greg,
+                                            regval);
+                   }
+                 write_memory (gparam, val, TYPE_LENGTH (type));
+               }
+             /* Always consume parameter stack space.  */
+             freg++;
+             greg++;
+             gparam = align_up (gparam + TYPE_LENGTH (type), tdep->wordsize);
+           }
+         else if (TYPE_LENGTH (type) == 16 && TYPE_VECTOR (type)
+                  && TYPE_CODE (type) == TYPE_CODE_ARRAY
+                  && tdep->ppc_vr0_regnum >= 0)
+           {
+             /* In the Altivec ABI, vectors go in the vector
+                registers v2 .. v13, or when that runs out, a vector
+                annex which goes above all the normal parameters.
+                NOTE: cagney/2003-09-21: This is a guess based on the
+                PowerOpen Altivec ABI.  */
+             if (vreg <= 13)
+               {
+                 if (write_pass)
+                   regcache_cooked_write (regcache,
+                                          tdep->ppc_vr0_regnum + vreg, val);
+                 vreg++;
+               }
+             else
+               {
+                 if (write_pass)
+                   write_memory (vparam, val, TYPE_LENGTH (type));
+                 vparam = align_up (vparam + TYPE_LENGTH (type), 16);
+               }
+           }
+         else if ((TYPE_CODE (type) == TYPE_CODE_INT
+                   || TYPE_CODE (type) == TYPE_CODE_ENUM)
+                  && TYPE_LENGTH (type) <= 8)
+           {
+             /* Scalars get sign[un]extended and go in gpr3 .. gpr10.
+                They can also end up in memory.  */
+             if (write_pass)
+               {
+                 /* Sign extend the value, then store it unsigned.  */
+                 ULONGEST word = unpack_long (type, val);
+                 if (greg <= 10)
+                   regcache_cooked_write_unsigned (regcache,
+                                                   tdep->ppc_gp0_regnum +
+                                                   greg, word);
+                 write_memory_unsigned_integer (gparam, tdep->wordsize,
+                                                word);
+               }
+             greg++;
+             gparam = align_up (gparam + TYPE_LENGTH (type), tdep->wordsize);
+           }
+         else
+           {
+             int byte;
+             for (byte = 0; byte < TYPE_LENGTH (type);
+                  byte += tdep->wordsize)
+               {
+                 if (write_pass && greg <= 10)
+                   {
+                     char regval[MAX_REGISTER_SIZE];
+                     int len = TYPE_LENGTH (type) - byte;
+                     if (len > tdep->wordsize)
+                       len = tdep->wordsize;
+                     memset (regval, 0, sizeof regval);
+                     /* WARNING: cagney/2003-09-21: As best I can
+                        tell, the ABI specifies that the value should
+                        be left aligned.  Unfortunately, GCC doesn't
+                        do this - it instead right aligns even sized
+                        values and puts odd sized values on the
+                        stack.  Work around that by putting both a
+                        left and right aligned value into the
+                        register (hopefully no one notices :-^).
+                        Arrrgh!  */
+                     /* Left aligned (8 byte values such as pointers
+                        fill the buffer).  */
+                     memcpy (regval, val + byte, len);
+                     /* Right aligned (but only if even).  */
+                     if (len == 1 || len == 2 || len == 4)
+                       memcpy (regval + tdep->wordsize - len,
+                               val + byte, len);
+                     regcache_cooked_write (regcache, greg, regval);
+                   }
+                 greg++;
+               }
+             if (write_pass)
+               /* WARNING: cagney/2003-09-21: Strictly speaking, this
+                  isn't necessary, unfortunately, GCC appears to get
+                  "struct convention" parameter passing wrong putting
+                  odd sized structures in memory instead of in a
+                  register.  Work around this by always writing the
+                  value to memory.  Fortunately, doing this
+                  simplifies the code.  */
+               write_memory (gparam, val, TYPE_LENGTH (type));
+             /* Always consume parameter stack space.  */
+             gparam = align_up (gparam + TYPE_LENGTH (type), tdep->wordsize);
+           }
+       }
+
+      if (!write_pass)
+       {
+         /* Save the true region sizes ready for the second pass.  */
+         vparam_size = vparam;
+         /* Make certain that the general parameter save area is at
+            least the minimum 8 registers (or doublewords) in size.  */
+         if (greg < 8)
+           gparam_size = 8 * tdep->wordsize;
+         else
+           gparam_size = gparam;
+       }
+    }
+
+  /* Update %sp.   */
+  regcache_cooked_write_signed (regcache, SP_REGNUM, sp);
+
+  /* Write the backchain (it occupies WORDSIZED bytes).  */
+  write_memory_signed_integer (sp, tdep->wordsize, back_chain);
+
+  /* Point the inferior function call's return address at the dummy's
+     breakpoint.  */
+  regcache_cooked_write_signed (regcache, tdep->ppc_lr_regnum, bp_addr);
+
+  /* Find a value for the TOC register.  Every symbol should have both
+     ".FN" and "FN" in the minimal symbol table.  "FN" points at the
+     FN's descriptor, while ".FN" points at the entry point (which
+     matches FUNC_ADDR).  Need to reverse from FUNC_ADDR back to the
+     FN's descriptor address.  */
+  {
+    /* Find the minimal symbol that corresponds to FUNC_ADDR (should
+       have the name ".FN").  */
+    struct minimal_symbol *dot_fn = lookup_minimal_symbol_by_pc (func_addr);
+    if (dot_fn != NULL && SYMBOL_LINKAGE_NAME (dot_fn)[0] == '.')
+      {
+       /* Now find the corresponding "FN" (dropping ".") minimal
+          symbol's address.  */
+       struct minimal_symbol *fn =
+         lookup_minimal_symbol (SYMBOL_LINKAGE_NAME (dot_fn) + 1, NULL,
+                                NULL);
+       if (fn != NULL)
+         {
+           /* Got the address of that descriptor.  The TOC is the
+              second double word.  */
+           CORE_ADDR toc =
+             read_memory_unsigned_integer (SYMBOL_VALUE_ADDRESS (fn) +
+                                           tdep->wordsize, tdep->wordsize);
+           regcache_cooked_write_unsigned (regcache,
+                                           tdep->ppc_gp0_regnum + 2, toc);
+         }
+      }
+  }
+
+  return sp;
+}
+
 
 /* The 64 bit ABI retun value convention.
 
index 07395a4..4e6788c 100644 (file)
@@ -43,6 +43,13 @@ CORE_ADDR ppc_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
                                        struct value **args, CORE_ADDR sp,
                                        int struct_return,
                                        CORE_ADDR struct_addr);
+CORE_ADDR ppc64_sysv_abi_push_dummy_call (struct gdbarch *gdbarch,
+                                         CORE_ADDR func_addr,
+                                         struct regcache *regcache,
+                                         CORE_ADDR bp_addr, int nargs,
+                                         struct value **args, CORE_ADDR sp,
+                                         int struct_return,
+                                         CORE_ADDR struct_addr);
 int ppc_linux_memory_remove_breakpoint (CORE_ADDR addr, char *contents_cache);
 struct link_map_offsets *ppc_linux_svr4_fetch_link_map_offsets (void);
 void ppc_linux_supply_gregset (char *buf);
index bc0cff9..3a0d483 100644 (file)
@@ -2959,6 +2959,8 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
      revisited.  */
   if (sysv_abi && wordsize == 4)
     set_gdbarch_push_dummy_call (gdbarch, ppc_sysv_abi_push_dummy_call);
+  else if (sysv_abi && wordsize == 8)
+    set_gdbarch_push_dummy_call (gdbarch, ppc64_sysv_abi_push_dummy_call);
   else
     set_gdbarch_push_dummy_call (gdbarch, rs6000_push_dummy_call);