From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 12949 invoked by alias); 22 Sep 2003 17:59:46 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 12940 invoked from network); 22 Sep 2003 17:59:44 -0000 Received: from unknown (HELO localhost.redhat.com) (207.219.125.131) by sources.redhat.com with SMTP; 22 Sep 2003 17:59:44 -0000 Received: from redhat.com (localhost [127.0.0.1]) by localhost.redhat.com (Postfix) with ESMTP id 13A002B8D for ; Mon, 22 Sep 2003 13:59:42 -0400 (EDT) Message-ID: <3F6F388D.5020706@redhat.com> Date: Mon, 22 Sep 2003 17:59:00 -0000 From: Andrew Cagney User-Agent: Mozilla/5.0 (X11; U; NetBSD macppc; en-US; rv:1.0.2) Gecko/20030820 X-Accept-Language: en-us, en MIME-Version: 1.0 To: gdb-patches@sources.redhat.com Subject: Re: [rfa:ppc64] Fix 64-bit PPC ELF function calls References: <3F6E368C.30009@redhat.com> Content-Type: multipart/mixed; boundary="------------070403080306030603060302" X-SW-Source: 2003-09/txt/msg00474.txt.bz2 This is a multi-part message in MIME format. --------------070403080306030603060302 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Content-length: 877 > 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. > > --------------070403080306030603060302 Content-Type: text/plain; name="diffs" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="diffs" Content-length: 11072 2003-09-21 Andrew Cagney * 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); --------------070403080306030603060302--