This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[rfa:ppc64] Fix 64-bit PPC ELF function calls


Hello,

This patch implements a 4-bit PPC ELF specific push_dummy_call method. It then adds a work-around (*_broken_push_dummy_call) for what would appear to be GCC bugs. This fixes ~200 failures (give or take the 3 fixed by the workaround).

Ok to commit?

Andrew

PS: The apparent bugs are:

- small odd structs get passed in memory instead of a register (ref structs.exp:Fun3).
- small even structs get passed right, instead of left, aligned in the register (ref structs.exp:Fun[12]).


PS: Backtraces are a bit sick.

PPS: Oh, note the "hack" to find the TOC from the function's entry point address. Without it malloc() fails.
2003-09-21  Andrew Cagney  <cagney@redhat.com>

	* ppc-linux-tdep.c (ppc_linux_init_abi): When 64-bit, set
	"push_dummy_call" to "ppc64_sysv_abi_broken_push_dummy_call".
	* 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 (ppc64_sysv_abi_push_dummy_call): New function.
	(ppc64_sysv_abi_broken_push_dummy_call): New function.
	(do_ppc64_sysv_abi_push_dummy_call): New function.
	* ppc-tdep.h (ppc64_sysv_abi_push_dummy_call): Declare.
	(ppc64_sysv_abi_broken_push_dummy_call): Declare.

Index: ppc-linux-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc-linux-tdep.c,v
retrieving revision 1.40
diff -u -r1.40 ppc-linux-tdep.c
--- ppc-linux-tdep.c	16 Sep 2003 23:33:17 -0000	1.40
+++ ppc-linux-tdep.c	21 Sep 2003 23:31:14 -0000
@@ -1074,6 +1074,12 @@
       set_gdbarch_in_solib_call_trampoline
         (gdbarch, ppc64_in_solib_call_trampoline);
       set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code);
+
+      /* GCC, on GNU/Linux appears to screw up passing small struct
+         parameters.  Odd sized ones end up in memory (when they
+         should be in a register) and even-sized ones end up right
+         shifted (they should be left shifted) in a register.  */
+      set_gdbarch_push_dummy_call (gdbarch, ppc64_sysv_abi_broken_push_dummy_call);
     }
 }
 
Index: ppc-sysv-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc-sysv-tdep.c,v
retrieving revision 1.11
diff -u -r1.11 ppc-sysv-tdep.c
--- ppc-sysv-tdep.c	16 Sep 2003 23:33:17 -0000	1.11
+++ ppc-sysv-tdep.c	21 Sep 2003 23:31:14 -0000
@@ -340,3 +340,266 @@
 
   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. */
+
+static CORE_ADDR
+do_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 broken_gcc)
+{
+  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 ();
+  /* The address of the top of the parameter save region (typically
+     points at the local variable region).  On entry, the SP is
+     pointing at this.  */
+  const CORE_ADDR param_top = sp;
+  /* Address, on the stack, of the next general (integer, struct,
+     float, ...) parameter.  */
+  CORE_ADDR gparam = 0;
+  /* Address, on the stack, of the next vector.  */
+  CORE_ADDR vparam = 0;
+  /* First (computation) or second (write) pass over the parameter
+     list.  */
+  int write_pass;
+
+  /* Go through the argument list twice.
+
+     Pass 1: Figure out how much stack space is required for arguments
+     and pushed values.
+
+     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;
+
+      /* 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 parameter space 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);
+	  /* Floats and Doubles go in f1 .. f13.  They also consume a
+             left aligned GREG,, and can end up in memory.  */
+	  if (TYPE_CODE (type) == TYPE_CODE_FLT
+	      && TYPE_LENGTH (type) <= 8)
+	    {
+	      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)
+		    {
+		      /* It goes into the register, left aligned (as
+                         far as I can tell).  */
+		      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)
+	    {
+	      /* Vectors go in the vector registers v2 .. v13, or when
+                 that runs out, a vector annex which goes after all
+                 the registers.  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);
+		}
+	    }
+	  /* Scalars get sign[un]extended and go in gpr3 .. gpr10.
+             They can also end up in memory.  */
+	  else if ((TYPE_CODE (type) == TYPE_CODE_INT
+		    || TYPE_CODE (type) == TYPE_CODE_ENUM)
+		   && TYPE_LENGTH (type) <= 8)
+	    {
+	      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)
+		    {
+		      int len = TYPE_LENGTH (type) - byte;
+		      if (len > tdep->wordsize)
+			len = tdep->wordsize;
+		      /* WARNING: cagney/2003-09-21: As best I can
+			 tell, the ABI specifies that the value be
+			 left aligned (it's talking about slot numbers
+			 and writing an entire word is as easy as a
+			 part word).  Unfortunatly, GCC doesn't do
+			 this.  It instead right aligns the value in a
+			 struct (if even), and gets the value from
+			 memory (if odd).  Arrrgh!  */
+		      if (broken_gcc)
+			regcache_cooked_write_part (regcache,
+						    tdep->ppc_gp0_regnum + greg,
+						    tdep->wordsize - len, len,
+						    val + byte);
+		      else
+			regcache_cooked_write_part (regcache,
+						    tdep->ppc_gp0_regnum + greg,
+						    0, len, val + byte);
+		    }
+		  greg++;
+		}
+	      if (write_pass)
+		/* WARNING: cagney/2003-09-21: Strictly speaking, this
+                   isn't necessary, however, since it makes the logic
+                   simpler it can't hurt eh?.  Oh, and bofore I
+                   forget, and GCC screws up the passing of odd sized
+                   struct parameters putting them in memory instead of
+                   in a register - this memory write hides the problem
+                   :-/ */
+		write_memory (gparam, val, TYPE_LENGTH (type));
+	      /* Always consume parameter stack space.  */
+	      gparam = align_up (gparam + TYPE_LENGTH (type), tdep->wordsize);
+	    }
+	}
+
+      /* Compute the actual stack space requirements.  48 is lifted
+         direct from the ABI, it is ment to hold holds things like the
+         LR and CR.  */
+      if (!write_pass)
+	{
+	  vparam = align_down (param_top - vparam, 16);
+	  gparam = align_down (vparam - gparam, 16);
+	  sp = align_down (gparam - 48, 16);
+	}
+    }
+
+  /* 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
+     F'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;
+}
+
+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)
+{
+  return do_ppc64_sysv_abi_push_dummy_call (gdbarch, func_addr, regcache, bp_addr,
+					    nargs, args, sp, struct_return,
+					    struct_addr, 0);
+}
+
+CORE_ADDR
+ppc64_sysv_abi_broken_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)
+{
+  return do_ppc64_sysv_abi_push_dummy_call (gdbarch, func_addr, regcache, bp_addr,
+					    nargs, args, sp, struct_return,
+					    struct_addr, 1);
+}
+ 
Index: ppc-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/ppc-tdep.h,v
retrieving revision 1.18
diff -u -r1.18 ppc-tdep.h
--- ppc-tdep.h	14 Sep 2003 02:04:44 -0000	1.18
+++ ppc-tdep.h	21 Sep 2003 23:31:14 -0000
@@ -42,6 +42,20 @@
 					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);
+CORE_ADDR ppc64_sysv_abi_broken_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: rs6000-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/rs6000-tdep.c,v
retrieving revision 1.160
diff -u -r1.160 rs6000-tdep.c
--- rs6000-tdep.c	16 Sep 2003 23:33:17 -0000	1.160
+++ rs6000-tdep.c	21 Sep 2003 23:31:15 -0000
@@ -2949,6 +2949,8 @@
      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);
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]