This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
Re: [rfa:ppc64] Fix 64-bit PPC ELF function calls
- From: Andrew Cagney <ac131313 at redhat dot com>
- To: gdb-patches at sources dot redhat dot com
- Date: Mon, 22 Sep 2003 13:59:41 -0400
- Subject: Re: [rfa:ppc64] Fix 64-bit PPC ELF function calls
- References: <3F6E368C.30009@redhat.com>
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).
I've attached a simplified revision.
Since the two potential places (left/right register ends) for storing
struct parameters didn't overlap, modified the code so that it stored
the value at both ends ...
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>
* 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.
* ppc-tdep.h (ppc64_sysv_abi_push_dummy_call): Declare.
(ppc64_sysv_abi_broken_push_dummy_call): Declare.
Index: ppc-sysv-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppc-sysv-tdep.c,v
retrieving revision 1.12
diff -u -r1.12 ppc-sysv-tdep.c
--- ppc-sysv-tdep.c 19 Sep 2003 16:22:39 -0000 1.12
+++ ppc-sysv-tdep.c 22 Sep 2003 17:57:05 -0000
@@ -325,3 +325,241 @@
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 ();
+ /* 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)
+ {
+ char regval[MAX_REGISTER_SIZE];
+ 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 should
+ be left aligned. Unfortunatly, 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! */
+ memset (regval, 0, sizeof regval);
+ /* Left aligned. */
+ 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);
+ }
+ greg++;
+ }
+ if (write_pass)
+ /* WARNING: cagney/2003-09-21: Strictly speaking, this
+ isn't necessary, unfortunatly, GCC appears to get
+ struct parameter passing wrong putting odd sized
+ structs in memory instead of a register. Work
+ around this by always writing the value to memory.
+ Fortunatly, doing this simplifies the cost. */
+ 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;
+}
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 22 Sep 2003 17:57:05 -0000
@@ -42,6 +42,13 @@
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: rs6000-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/rs6000-tdep.c,v
retrieving revision 1.161
diff -u -r1.161 rs6000-tdep.c
--- rs6000-tdep.c 17 Sep 2003 14:24:30 -0000 1.161
+++ rs6000-tdep.c 22 Sep 2003 17:57:05 -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);