From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 32025 invoked by alias); 21 Sep 2003 23:38:54 -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 32014 invoked from network); 21 Sep 2003 23:38:52 -0000 Received: from unknown (HELO localhost.redhat.com) (207.219.125.105) by sources.redhat.com with SMTP; 21 Sep 2003 23:38:52 -0000 Received: from redhat.com (localhost [127.0.0.1]) by localhost.redhat.com (Postfix) with ESMTP id 1FD8C2B89 for ; Sun, 21 Sep 2003 19:38:53 -0400 (EDT) Message-ID: <3F6E368C.30009@redhat.com> Date: Sun, 21 Sep 2003 23:38: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: [rfa:ppc64] Fix 64-bit PPC ELF function calls Content-Type: multipart/mixed; boundary="------------080100030209040708030205" X-SW-Source: 2003-09/txt/msg00451.txt.bz2 This is a multi-part message in MIME format. --------------080100030209040708030205 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Content-length: 638 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. --------------080100030209040708030205 Content-Type: text/plain; name="diffs" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="diffs" Content-length: 13248 2003-09-21 Andrew Cagney * 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); --------------080100030209040708030205--