This is the mail archive of the gdb-patches@sourceware.cygnus.com mailing list for the GDB project. See the GDB home page for more information.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
Forwarded so that this is recorded in the gdb-patches logfile. -----Forwarded message from "Kevin B. Hendricks" <kbhend@dogwood.tyler.wm.edu>----- Date: Thu, 2 Apr 1998 22:29:50 -0500 To: Jason Molenda <jsm@cygnus.com> From: "Kevin B. Hendricks" <kbhend@dogwood.tyler.wm.edu> Subject: Re: New GDB 4.17 prerelease snapshot ready Content-Type: text/plain; charset="us-ascii" Hi, In case you are interested, here are the patches that Kevin Buettner devised to get gdb 4.16.85 configured for PPC linux (mklinux, etc). Sorry for how long this is. I just finished a successful build with this patch applied to gdb 4.16.97. I will test it this weekend. If this patch is redundant or not desired, I apologize in advance (I personally hate it when people send things to me unsolicited). If so, simply discard it. Otherwise I hope it helps. Thanks, Kevin Hendricks, Hi Kevin, Let me know if you have problems with the patch sent in this form. I can send them as some sort of attachment if you wish. Kevin diff -urN gdb-4.16.85-orig/gdb/config/powerpc/linux.mh gdb-4.16.85/gdb/config/powerpc/linux.mh --- gdb-4.16.85-orig/gdb/config/powerpc/linux.mh Fri Nov 1 08:53:30 1996 +++ gdb-4.16.85/gdb/config/powerpc/linux.mh Tue Feb 10 14:14:30 1998 @@ -4,8 +4,8 @@ XDEPFILES= ser-tcp.o XM_CLIBS= -NAT_FILE= ../nm-sysv4.h -NATDEPFILES= solib.o corelow.o # infptrace.o inftarg.o fork-child.o core-aout.o core-regset.o +NAT_FILE= nm-linux.h +NATDEPFILES= infptrace.o solib.o inftarg.o fork-child.o corelow.o core-aout.o core-regset.o ppclinux-nat.o GDBSERVER_DEPFILES= low-linux.o diff -urN gdb-4.16.85-orig/gdb/config/powerpc/linux.mt gdb-4.16.85/gdb/config/powerpc/linux.mt --- gdb-4.16.85-orig/gdb/config/powerpc/linux.mt Wed Dec 31 17:00:00 1969 +++ gdb-4.16.85/gdb/config/powerpc/linux.mt Tue Feb 10 14:17:20 1998 @@ -0,0 +1,3 @@ +# Target: Motorola PPC w/ ELF +TDEPFILES= ppclinux-tdep.o +TM_FILE= tm-linux.h diff -urN gdb-4.16.85-orig/gdb/config/powerpc/nm-linux.h gdb-4.16.85/gdb/config/powerpc/nm-linux.h --- gdb-4.16.85-orig/gdb/config/powerpc/nm-linux.h Wed Dec 31 17:00:00 1969 +++ gdb-4.16.85/gdb/config/powerpc/nm-linux.h Tue Feb 10 14:17:20 1998 @@ -0,0 +1,45 @@ +/* IBM PowerPC native-dependent macros for GDB, the GNU debugger. + Copyright 1995 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef NM_LINUX_H +#define NM_LINUX_H + +/* Return sizeof user struct to callers in less machine dependent routines */ + +#define KERNEL_U_SIZE kernel_u_size() +extern int kernel_u_size PARAMS ((void)); + +/* Tell gdb that we can attach and detach other processes */ +#define ATTACH_DETACH + +#define U_REGS_OFFSET 0 + +#define REGISTER_U_ADDR(addr, blockend, regno) \ + (addr) = ppc_register_u_addr ((blockend),(regno)); + +/* No <sys/reg.h> */ + +#define NO_SYS_REG_H + +#ifdef HAVE_LINK_H +#include "solib.h" /* Support for shared libraries. */ +#define SVR4_SHARED_LIBS +#endif + +#endif /* #ifndef NM_LINUX_H */ diff -urN gdb-4.16.85-orig/gdb/config/powerpc/tm-linux.h gdb-4.16.85/gdb/config/powerpc/tm-linux.h --- gdb-4.16.85-orig/gdb/config/powerpc/tm-linux.h Wed Dec 31 17:00:00 1969 +++ gdb-4.16.85/gdb/config/powerpc/tm-linux.h Tue Feb 10 15:05:43 1998 @@ -0,0 +1,135 @@ +/* Definitions to target GDB to Linux on 386. + Copyright 1992, 1993 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef TM_LINUX_H +#define TM_LINUX_H + +#include "powerpc/tm-ppc-eabi.h" + +/* We can single step on linux */ +#undef NO_SINGLE_STEP + +/* Make sure nexti gets the help it needs for debugging assembly code + without symbols */ + +#define AT_SUBROUTINE_CALL_INSTRUCTION_TARGET(prevpc,stoppc) \ + at_subroutine_call_instruction_target(prevpc,stoppc) +extern int at_subroutine_call_instruction_target(); + +/* We _want_ the SVR4 section offset calculations (see syms_from_objfile() + in symfile.c) */ +#undef IBM6000_TARGET + +/* Offset to saved PC in sigcontext, from <linux/signal.h>. */ +#define SIGCONTEXT_PC_OFFSET 184 + +/* Avoid warning from redefinition in tm-sysv4.h */ +#undef SKIP_TRAMPOLINE_CODE + +/* We need this file for the SOLIB_TRAMPOLINE stuff. */ +#include "tm-sysv4.h" + +#undef SKIP_TRAMPOLINE_CODE +extern CORE_ADDR skip_trampoline_code (CORE_ADDR pc); +#define SKIP_TRAMPOLINE_CODE(pc) skip_trampoline_code (pc) + +#undef IN_SIGTRAMP +extern int in_sigtramp (CORE_ADDR pc, char *func_name); +#define IN_SIGTRAMP(pc,func_name) in_sigtramp(pc,func_name) + +#define CANNOT_FETCH_REGISTER(regno) ((regno) == MQ_REGNUM) +#define CANNOT_STORE_REGISTER(regno) ((regno) == MQ_REGNUM) + +/* Linux doesn't use the PowerOpen ABI for function pointer representation */ +#undef CONVERT_FROM_FUNC_PTR_ADDR + +#undef INIT_EXTRA_FRAME_INFO(fromleaf, fi) +#define INIT_EXTRA_FRAME_INFO(fromleaf, fi) \ + init_extra_frame_info (fromleaf, fi) +extern void init_extra_frame_info (); + +#undef FRAME_FIND_SAVED_REGS +struct frame_saved_regs; +struct frame_info; +void frame_find_saved_regs PARAMS((struct frame_info *fi, + struct frame_saved_regs *fsr)); +#define FRAME_FIND_SAVED_REGS(frame_info, frame_saved_regs) \ + frame_find_saved_regs (frame_info, &frame_saved_regs) + +#undef INIT_FRAME_PC_FIRST +#define INIT_FRAME_PC_FIRST(fromleaf, prev) \ + init_frame_pc_first(fromleaf, prev) + +#if 0 /* If skip_prologue() isn't too greedy, we don't need this */ +/* There is some problem with the debugging symbols generated by the + compiler such that the debugging symbol for the first line of a + function overlap with the function prologue. */ +#define PROLOGUE_FIRSTLINE_OVERLAP +#endif + + +/* CALL_DUMMY: This sequence of words is the instructions: + + mflr r0 // 0x7c0802a6 + // save fpr's + stfd r?, num(r1) // 0xd8010000 there should be 32 of this?? + // save gpr's + stm r0, num(r1) // 0xbc010000 + stu r1, num(r1) // 0x94210000 + + // load absolute address 0x12345678 to r0 + liu r0, 0x1234 // 0x3c001234 + oril r0, r0,0x5678 // 0x60005678 + mtctr r0 // 0x7c0903a6 ctr <- r0 + bctrl // 0x4e800421 jump subroutine 0x12345678 (%ctr) + cror 0xf, 0xf, 0xf // 0x4def7b82 + brpt // 0x7d821008, breakpoint + cror 0xf, 0xf, 0xf // 0x4def7b82 (for 8 byte alignment) + + + We actually start executing by loading function address first, since + the pushing of the registers is done by PUSH_DUMMY_FRAME. If this + were real code, the arguments for the function called by the `bctrl' + would be pushed between the `stu' and the `bctrl', and we could + allow it to execute through. But the arguments have to be pushed by + GDB after the PUSH_DUMMY_FRAME is done, and we cannot allow to push + the registers again. +*/ + +#undef CALL_DUMMY +#define CALL_DUMMY {0x7c0802a6, 0xd8010000, 0xbc010000, 0x94210000, \ + 0x3c001234, 0x60005678, 0x7c0903a6, 0x4e800421, \ + 0x4def7b82, 0x7d821008, 0x4def7b82 } + + +/* keep this as multiple of 8 (%sp requires 8 byte alignment) */ +#undef CALL_DUMMY_LENGTH +#define CALL_DUMMY_LENGTH 44 + +#undef CALL_DUMMY_START_OFFSET +#define CALL_DUMMY_START_OFFSET 16 + +#undef FIX_CALL_DUMMY +#define FIX_CALL_DUMMY(dummyname, pc, fun, nargs, args, type, gcc_p) \ + ppclinux_fix_call_dummy (dummyname, pc, fun, nargs, args, type, gcc_p) +extern void ppclinux_fix_call_dummy PARAMS ((char *, CORE_ADDR, CORE_ADDR, + int, struct value **, + struct type *, int)); + +#endif /* #ifndef TM_LINUX_H */ diff -urN gdb-4.16.85-orig/gdb/config/powerpc/xm-linux.h gdb-4.16.85/gdb/config/powerpc/xm-linux.h --- gdb-4.16.85-orig/gdb/config/powerpc/xm-linux.h Tue Jul 2 12:09:59 1996 +++ gdb-4.16.85/gdb/config/powerpc/xm-linux.h Tue Feb 10 14:17:20 1998 @@ -1,3 +1,49 @@ +/* Native support for linux, for GDB, the GNU debugger. + Copyright (C) 1986, 1987, 1989, 1992 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef XM_LINUX_H +#define XM_LINUX_H + +#define HOST_BYTE_ORDER BIG_ENDIAN + +#define HAVE_TERMIOS + +/* This is the amount to subtract from u.u_ar0 + to get the offset in the core file of the register values. */ +#define KERNEL_U_ADDR 0x0 + +#define NEED_POSIX_SETPGID + +/* Need R_OK etc, but USG isn't defined. */ +#include <unistd.h> + +/* If you expect to use the mmalloc package to obtain mapped symbol files, + for now you have to specify some parameters that determine how gdb places + the mappings in it's address space. See the comments in map_to_address() + for details. This is expected to only be a short term solution. Yes it + is a kludge. + FIXME: Make this more automatic. */ + +#define MMAP_BASE_ADDRESS 0x20000000 /* First mapping here */ +#define MMAP_INCREMENT 0x01000000 /* Increment to next mapping */ + +#endif /* #ifndef XM_LINUX_H */ /* Host definitions for a Sun 4, for GDB, the GNU debugger. Copyright 1996 Free Software Foundation, Inc. diff -urN gdb-4.16.85-orig/gdb/configure.tgt gdb-4.16.85/gdb/configure.tgt --- gdb-4.16.85-orig/gdb/configure.tgt Wed Feb 4 15:33:00 1998 +++ gdb-4.16.85/gdb/configure.tgt Tue Feb 10 14:32:30 1998 @@ -210,7 +210,8 @@ powerpc-*-aix*) gdb_target=aix ;; powerpcle-*-cygwin32) gdb_target=cygwin32 ;; powerpcle-*-solaris*) gdb_target=solaris ;; -powerpc-*-eabi* | powerpc-*-linux* | powerpc-*-sysv* | powerpc-*-elf*) +powerpc-*-linux*) gdb_target=linux ;; +powerpc-*-eabi* | powerpc-*-sysv* | powerpc-*-elf*) if test -f ../sim/ppc/Makefile; then gdb_target=ppc-sim else diff -urN gdb-4.16.85-orig/gdb/infrun.c gdb-4.16.85/gdb/infrun.c --- gdb-4.16.85-orig/gdb/infrun.c Fri Jan 30 13:33:23 1998 +++ gdb-4.16.85/gdb/infrun.c Tue Feb 10 15:36:10 1998 @@ -125,6 +125,16 @@ #define INSTRUCTION_NULLIFIED 0 #endif +/* Hook for determining whether we've performed a subroutine call while + debugging assembly language code about which little is known. + (I.e, no symbols are available for determining starting addresses + of subroutines.) Defining this hook properly will make "nexti" + behave better in such code. */ + +#ifndef AT_SUBROUTINE_CALL_INSTRUCTION_TARGET +#define AT_SUBROUTINE_CALL_INSTRUCTION_TARGET(prevpc,stoppc) 1 +#endif + /* Tables of how to react to signals; the user sets them. */ static unsigned char *signal_stop; @@ -1359,7 +1369,9 @@ { /* It's a subroutine call. */ - if (step_over_calls == 0) + if (step_over_calls == 0 + || (step_range_end == 1 + && !AT_SUBROUTINE_CALL_INSTRUCTION_TARGET (prev_pc, stop_pc))) { /* I presume that step_over_calls is only 0 when we're supposed to be stepping at the assembly language level diff -urN gdb-4.16.85-orig/gdb/ppclinux-nat.c gdb-4.16.85/gdb/ppclinux-nat.c --- gdb-4.16.85-orig/gdb/ppclinux-nat.c Wed Dec 31 17:00:00 1969 +++ gdb-4.16.85/gdb/ppclinux-nat.c Tue Feb 10 14:17:20 1998 @@ -0,0 +1,88 @@ +/* PPC linux native support. + Copyright (C) 1988, 1989, 1991, 1992, 1994, 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "language.h" +#include "gdbcore.h" + +#include "bfd.h" /* Binary File Description */ +#include "symtab.h" +#include "symfile.h" +#include "objfiles.h" +#include "gdb-stabs.h" +#include "target.h" + +#include "gdb_stat.h" +#include "obstack.h" +#include "gdb_string.h" + + +#include <sys/types.h> +#include <sys/param.h> +#include <signal.h> +#include <sys/user.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <sys/procfs.h> + +int +kernel_u_size () +{ + return (sizeof (struct user)); +} + +static int regmap[] = + {PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7, + PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_R13, PT_R14, PT_R15, + PT_R16, PT_R17, PT_R18, PT_R19, PT_R20, PT_R21, PT_R22, PT_R23, + PT_R24, PT_R25, PT_R26, PT_R27, PT_R28, PT_R29, PT_R30, PT_R31, + PT_FPR0, PT_FPR0+2, PT_FPR0+4, PT_FPR0+6, PT_FPR0+8, PT_FPR0+10,PT_FPR0+12,PT_FPR0+14, + PT_FPR0+16,PT_FPR0+18,PT_FPR0+20,PT_FPR0+22,PT_FPR0+24,PT_FPR0+26,PT_FPR0+28,PT_ FPR0+30, + PT_FPR0+32,PT_FPR0+34,PT_FPR0+36,PT_FPR0+38,PT_FPR0+40,PT_FPR0+42,PT_FPR0+44,PT_ FPR0+46, + PT_FPR0+48,PT_FPR0+50,PT_FPR0+52,PT_FPR0+54,PT_FPR0+56,PT_FPR0+58,PT_FPR0+60,PT_ FPR0+62, + PT_NIP, PT_MSR, PT_CCR, PT_LNK, PT_CTR, PT_XER, PT_MQ }; + + +int ppc_register_u_addr(int ustart, int regnum) +{ + return (ustart + 4 * regmap[regnum]); +} + +supply_gregset(gregset_t *gregsetp) +{ + int regi; + register greg_t *regp = (greg_t *) gregsetp; + + for (regi=0; regi < 32; regi++) + supply_register (regi, (char *) (regp + regi)); + + for (regi = FIRST_SP_REGNUM; regi <= LAST_SP_REGNUM; regi++) + supply_register (regi, (char *) (regp + regmap[regi])); +} + +supply_fpregset(fpregset_t *fpregsetp) +{ + int regi; + for (regi=0; regi < 32; regi++) { + supply_register(FP0_REGNUM+regi, (char *) (*fpregsetp + regi)); + } +} diff -urN gdb-4.16.85-orig/gdb/ppclinux-tdep.c gdb-4.16.85/gdb/ppclinux-tdep.c --- gdb-4.16.85-orig/gdb/ppclinux-tdep.c Wed Dec 31 17:00:00 1969 +++ gdb-4.16.85/gdb/ppclinux-tdep.c Tue Feb 10 15:28:00 1998 @@ -0,0 +1,1350 @@ +/* Target-dependent code for GDB, the GNU debugger. + Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995 + Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "symtab.h" +#include "target.h" +#include "language.h" +#include "gdbcore.h" +#include "symfile.h" +#include "objfiles.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <signal.h> +#include <sys/user.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <sys/procfs.h> +#include <link.h> +#include <elf.h> +#include <asm/ptrace.h> /* for struct pt_regs */ +#include <asm/sigcontext.h> /* for struct sigcontext_struct */ + +#ifndef __SIGNAL_FRAMESIZE +/* On linux-pmac, this is in <asm/ptrace.h> */ +#define __SIGNAL_FRAMESIZE 64 +#endif + +#define REGS_PTR_OFFSET \ + (__SIGNAL_FRAMESIZE + offsetof(struct sigcontext_struct, regs)) + +#define INSTR_LI_R0_0x7777 0x38007777 +#define INSTR_SC 0x44000002 + +extern struct obstack frame_cache_obstack; + +/* Static function prototypes */ + +static void frame_get_cache_fsr PARAMS ((struct frame_info *fi, + struct rs6000_framedata *fdatap)); + + +/* Determine whether or not instruction at prevpc was a subroutine + branch and if so whether or not stoppc is the subroutine branch + target. */ + +int +at_subroutine_call_instruction_target(prevpc,stoppc) + CORE_ADDR prevpc; + CORE_ADDR stoppc; +{ + int instr; + int opcode, ext_op, lk; + + instr = read_memory_integer (prevpc, 4); + + opcode = (instr >> 26) & 0x1f; + ext_op = (instr >> 1) & 0x3ff; + lk = instr & 1; + + /* All the following does is check to make sure that we were on + a branch instruction and that stoppc is not immediately after + the instruction which we were just at. We are not really checking + the branch target, but that isn't really necessary for this + subroutine to work. I guess this could be a possible FIXME. */ + + return ((prevpc+4 != stoppc) + && lk == 1 + && (opcode == 18 + || opcode == 16 + || (opcode == 19 && (ext_op == 16 || ext_op == 528)))); +} + + +/* return pc value after skipping a function prologue and also return + information about a function frame. + + in struct rs6000_frameinfo fdata: + - frameless is TRUE, if function does not have a frame. + - nosavedpc is TRUE, if function does not save %pc value in its frame. + - offset is the number of bytes used in the frame to save registers. + - saved_gpr is the number of the first saved gpr. + - saved_fpr is the number of the first saved fpr. + - alloca_reg is the number of the register used for alloca() handling. + Otherwise -1. + - gpr_offset is the offset of the saved gprs + - fpr_offset is the offset of the saved fprs + - lr_offset is the offset of the saved lr + - cr_offset is the offset of the saved cr + */ + +#define SIGNED_SHORT(x) \ + ((sizeof (short) == 2) \ + ? ((int)(short)(x)) \ + : ((int)((((x) & 0xffff) ^ 0x8000) - 0x8000))) + +#define GET_SRC_REG(x) (((x) >> 21) & 0x1f) + +CORE_ADDR +skip_prologue (pc, fdata) + CORE_ADDR pc; + struct rs6000_framedata *fdata; +{ + CORE_ADDR orig_pc = pc; + char buf[4]; + unsigned long op; + long offset = 0; + int lr_reg = 0; + int cr_reg = 0; + int reg; + int framep = 0; + int minimal_toc_loaded = 0; + static struct rs6000_framedata zero_frame; + + *fdata = zero_frame; + fdata->saved_gpr = -1; + fdata->saved_fpr = -1; + fdata->alloca_reg = -1; + fdata->frameless = 1; + fdata->nosavedpc = 1; + + if (target_read_memory (pc, buf, 4)) + return pc; /* Can't access it -- assume no prologue. */ + + /* Assume that subsequent fetches can fail with low probability. */ + pc -= 4; + for (;;) + { + pc += 4; + op = read_memory_integer (pc, 4); + + if ((op & 0xfc1fffff) == 0x7c0802a6) { /* mflr Rx */ + lr_reg = (op & 0x03e00000) | 0x90010000; + continue; + + } else if ((op & 0xfc1fffff) == 0x7c000026) { /* mfcr Rx */ + cr_reg = (op & 0x03e00000) | 0x90010000; + continue; + + } else if ((op & 0xfc1f0000) == 0xd8010000) { /* stfd Rx,NUM(r1) */ + reg = GET_SRC_REG (op); + if (fdata->saved_fpr == -1 || fdata->saved_fpr > reg) { + fdata->saved_fpr = reg; + fdata->fpr_offset = SIGNED_SHORT (op) + offset; + } + continue; + + } else if (((op & 0xfc1f0000) == 0xbc010000) || /* stm Rx, NUM(r1) */ + ((op & 0xfc1f0000) == 0x90010000 && /* st rx,NUM(r1), rx >= r13 */ + (op & 0x03e00000) >= 0x01a00000)) { + + reg = GET_SRC_REG (op); + if (fdata->saved_gpr == -1 || fdata->saved_gpr > reg) { + fdata->saved_gpr = reg; + fdata->gpr_offset = SIGNED_SHORT (op) + offset; + } + continue; + + } else if ((op & 0xffff0000) == 0x3c000000) { /* addis 0,0,NUM, used for >= 32k frames */ + fdata->offset = (op & 0x0000ffff) << 16; + fdata->frameless = 0; + continue; + + } else if ((op & 0xffff0000) == 0x60000000) { /* ori 0,0,NUM, 2nd half of >= 32k frames */ + fdata->offset |= (op & 0x0000ffff); + fdata->frameless = 0; + continue; + + } else if (lr_reg && (op & 0xffff0000) == lr_reg) { /* st Rx,NUM(r1) where Rx == lr */ + fdata->lr_offset = SIGNED_SHORT (op) + offset; + fdata->nosavedpc = 0; + lr_reg = 0; + continue; + + } else if (cr_reg && (op & 0xffff0000) == cr_reg) { /* st Rx,NUM(r1) where Rx == cr */ + fdata->cr_offset = SIGNED_SHORT (op) + offset; + cr_reg = 0; + continue; + + } else if (op == 0x48000005) { /* bl .+4 used in -mrelocatable */ + continue; + + } else if (op == 0x48000004) { /* b .+4 (xlc) */ + break; + + } else if (((op & 0xffff0000) == 0x801e0000 || /* lwz 0,NUM(r30), used in V.4 -mrelocatable */ + op == 0x7fc0f214) && /* add r30,r0,r30, used in V.4 -mrelocatable */ + lr_reg == 0x901e0000) { + continue; + + } else if ((op & 0xffff0000) == 0x3fc00000 || /* addis 30,0,foo@ha, used in V.4 -mminimal-toc */ + (op & 0xffff0000) == 0x3bde0000) { /* addi 30,30,foo@l */ + continue; + + } else if ((op & 0xfc000000) == 0x48000000 /* bl foo, to get the GOT */ + && read_memory_integer(pc+(((((long) op)<<6)>>6) & ~3), 4) + == 0x4e800021 /* blrl */ + && (read_memory_integer(pc+4,4) & 0xfc1fffff) == 0x7c0802a6 /* mflr */) { + pc += 4; /* skip the mflr instruction */ + continue; + + } else if ((op & 0xfc000000) == 0x48000000) { /* bl foo, to save fprs??? */ + + fdata->frameless = 0; + /* Don't skip over the subroutine call if it is not within the first + three instructions of the prologue. */ + if ((pc - orig_pc) > 8) + break; + + op = read_memory_integer (pc+4, 4); + + /* At this point, make sure this is not a trampoline function + (a function that simply calls another functions, and nothing else). + If the next is not a nop, this branch was part of the function + prologue. */ + + if (op == 0x4def7b82 || op == 0) /* crorc 15, 15, 15 */ + break; /* don't skip over this branch */ + + continue; + + /* update stack pointer */ + } else if ((op & 0xffff0000) == 0x94210000) { /* stu r1,NUM(r1) */ + fdata->frameless = 0; + fdata->offset = SIGNED_SHORT (op); + offset = fdata->offset; + continue; + + } else if (op == 0x7c21016e) { /* stwux 1,1,0 */ + fdata->frameless = 0; + offset = fdata->offset; + continue; + + /* Load up minimal toc pointer */ + } else if ((op >> 22) == 0x20f + && ! minimal_toc_loaded) { /* l r31,... or l r30,... */ + minimal_toc_loaded = 1; + continue; + + /* store parameters in stack */ + } else if ((op & 0xfc1f0000) == 0x90010000 || /* st rx,NUM(r1) */ + (op & 0xfc1f0000) == 0xd8010000 || /* stfd Rx,NUM(r1) */ + (op & 0xfc1f0000) == 0xfc010000) { /* frsp, fp?,NUM(r1) */ + continue; + + /* store parameters in stack via frame pointer */ + } else if (framep && + (op & 0xfc1f0000) == 0x901f0000 || /* st rx,NUM(r1) */ + (op & 0xfc1f0000) == 0xd81f0000 || /* stfd Rx,NUM(r1) */ + (op & 0xfc1f0000) == 0xfc1f0000) { /* frsp, fp?,NUM(r1) */ + continue; + + /* Set up frame pointer */ + } else if (op == 0x603f0000 /* oril r31, r1, 0x0 */ + || op == 0x7c3f0b78) { /* mr r31, r1 */ + fdata->frameless = 0; + framep = 1; + fdata->alloca_reg = 31; + continue; + + /* Another way to set up the frame pointer. */ + } else if ((op & 0xfc1fffff) == 0x38010000) { /* addi rX, r1, 0x0 */ + fdata->frameless = 0; + framep = 1; + fdata->alloca_reg = (op & ~0x38010000) >> 21; + continue; + + } else { + break; + } + } + + fdata->offset = - fdata->offset; + return fdata->frameless ? orig_pc : pc; +} + + +/************************************************************************* + Support for creating pushing a dummy frame into the stack, and popping + frames, etc. +*************************************************************************/ + +/* The total size of dummy frame is 436, which is; + + 32 gpr's - 128 bytes + 32 fpr's - 256 " + 7 the rest - 28 " +*/ + +#define DUMMY_FRAME_SIZE 412 + +extern int stop_stack_dummy; + +/* sp_before_dummy is used to communicate the value of the stack + pointer for backchaining purposes to push_arguments. Its value + is not (and must not be) relied on in pop_dummy_frame(). */ + +static CORE_ADDR sp_before_dummy; + +/* push a dummy frame into stack, save all register. Currently we are saving + only gpr's and fpr's, which is not good enough! FIXMEmgo */ + +void +push_dummy_frame () +{ + /* stack pointer. */ + CORE_ADDR sp; + /* Same thing, target byte order. */ + char sp_targ[4]; + + /* link register. */ + CORE_ADDR pc; + /* Same thing, target byte order. */ + char pc_targ[4]; + + int ii; + + target_fetch_registers (-1); + + + sp_before_dummy = sp = read_register(SP_REGNUM); + pc = read_register(PC_REGNUM); + store_address (pc_targ, 4, pc); + + /* Be careful! If the stack pointer is not decremented first, then kernel + thinks he is free to use the space underneath it. And kernel actually + uses that area for IPC purposes when executing ptrace(2) calls. So + before writing register values into the new frame, decrement and update + %sp first in order to secure your frame. */ + + /* FIXME: We don't check if the stack really has this much space. + This is a problem on the ppc simulator (which only grants one page + (4096 bytes) by default. */ + + write_register (SP_REGNUM, sp-DUMMY_FRAME_SIZE); + + /* gdb relies on the state of current_frame. We'd better update it, + otherwise things like do_registers_info() wouldn't work properly! */ + + flush_cached_frames (); + + /* save program counter in link register's space. */ + write_memory (sp + DEFAULT_LR_SAVE, pc_targ, 4); + + /* save all floating point and general purpose registers here. */ + + /* fpr's, f0..f31 */ + for (ii = 0; ii < 32; ++ii) + write_memory (sp-8-(ii*8), ®isters[REGISTER_BYTE (31-ii+FP0_REGNUM)], 8); + + /* gpr's r0..r31 */ + for (ii=1; ii <=32; ++ii) + write_memory (sp-256-(ii*4), ®isters[REGISTER_BYTE (32-ii)], 4); + + /* so far, 32*2 + 32 words = 384 bytes have been written. + 7 extra registers in our register set: pc, ps, cnd, lr, cnt, xer, mq */ + + for (ii=1; ii <= (LAST_SP_REGNUM-FIRST_SP_REGNUM+1); ++ii) { + write_memory (sp-384-(ii*4), + ®isters[REGISTER_BYTE (FPLAST_REGNUM + ii)], 4); + } + + /* Save sp or so called back chain right here. */ + store_address (sp_targ, 4, sp); + write_memory (sp-DUMMY_FRAME_SIZE, sp_targ, 4); + sp -= DUMMY_FRAME_SIZE; + +} + + +/* Pop a dummy frame. + + With the SYSV PPC ABI, when we push a dummy frame, we save all of the + registers. This is usually done before user calls a function explicitly. + + After a dummy frame is pushed, some instructions are copied into stack, + and stack pointer is decremented even more. +*/ + +pop_dummy_frame () +{ + CORE_ADDR sp, pc; + int ii; + + sp = read_memory_integer(read_register(SP_REGNUM), 4); + + /* restore all fpr's. */ + for (ii = 1; ii <= 32; ++ii) + read_memory (sp-(ii*8), ®isters[REGISTER_BYTE (32-ii+FP0_REGNUM)], 8); + + /* restore all gpr's */ + for (ii=1; ii <= 32; ++ii) { + read_memory (sp-256-(ii*4), ®isters[REGISTER_BYTE (32-ii)], 4); + } + + /* restore the rest of the registers. */ + for (ii=1; ii <=(LAST_SP_REGNUM-FIRST_SP_REGNUM+1); ++ii) + read_memory (sp-384-(ii*4), + ®isters[REGISTER_BYTE (FPLAST_REGNUM + ii)], 4); + + /* when a dummy frame was being pushed, we had to decrement %sp first, in + order to secure astack space. Thus, saved %sp (or %r1) value, is not the + one we should restore. Change it with the one we need. */ + + *(int*)®isters [REGISTER_BYTE(FP_REGNUM)] = sp; + + /* Now we can restore all registers. */ + + target_store_registers (-1); + pc = read_pc (); + flush_cached_frames (); +} + + +/* pop the innermost frame, go back to the caller. */ + +void +pop_frame () +{ + CORE_ADDR pc, lr, sp, prev_sp; /* %pc, %lr, %sp */ + struct rs6000_framedata fdata; + struct frame_info *frame = get_current_frame (); + int addr, ii; + + pc = read_pc (); + sp = FRAME_FP (frame); + + if (stop_stack_dummy) { + pop_dummy_frame (); + return; + } + + /* Make sure that all registers are valid. */ + read_register_bytes (0, NULL, REGISTER_BYTES); + + /* figure out previous %pc value. If the function is frameless, it is + still in the link register, otherwise walk the frames and retrieve the + saved %pc value in the previous frame. */ + + addr = get_pc_function_start (frame->pc) + FUNCTION_START_OFFSET; + (void) skip_prologue (addr, &fdata); + + if (fdata.frameless) + prev_sp = sp; + else + prev_sp = read_memory_integer (sp, 4); + if (fdata.lr_offset == 0) + lr = read_register (LR_REGNUM); + else + lr = read_memory_integer (prev_sp + fdata.lr_offset, 4); + + /* reset %pc value. */ + write_register (PC_REGNUM, lr); + + /* reset register values if any was saved earlier. */ + addr = prev_sp - fdata.offset; + + if (fdata.saved_gpr != -1) + for (ii = fdata.saved_gpr; ii <= 31; ++ii) { + read_memory (addr, ®isters [REGISTER_BYTE (ii)], 4); + addr += 4; + } + + if (fdata.saved_fpr != -1) + for (ii = fdata.saved_fpr; ii <= 31; ++ii) { + read_memory (addr, ®isters [REGISTER_BYTE (ii+FP0_REGNUM)], 8); + addr += 8; + } + + write_register (SP_REGNUM, prev_sp); + target_store_registers (-1); + flush_cached_frames (); +} + +/* fixup the call sequence of a dummy function, with the real function address. + its argumets will be passed by gdb. */ + +void +ppclinux_fix_call_dummy (dummyname, pc, fun, nargs, args, type, gcc_p) + char *dummyname; + CORE_ADDR pc; + CORE_ADDR fun; + int nargs; /* not used */ + value_ptr *args; + struct type *type; + int gcc_p; +{ +#define TARGET_ADDR_OFFSET 16 + + int ii; + CORE_ADDR target_addr; + + target_addr = fun; + + ii = *(int*)((char*)dummyname + TARGET_ADDR_OFFSET); + ii = (ii & 0xffff0000) | (target_addr >> 16); + *(int*)((char*)dummyname + TARGET_ADDR_OFFSET) = ii; + + ii = *(int*)((char*)dummyname + TARGET_ADDR_OFFSET+4); + ii = (ii & 0xffff0000) | (target_addr & 0x0000ffff); + *(int*)((char*)dummyname + TARGET_ADDR_OFFSET+4) = ii; +} + + +/* round2 rounds x up to the nearest multiple of s assuming that s is a + power of 2 */ + +#undef round2 +#define round2(x,s) ((((long) (x) - 1) & ~(long)((s)-1)) + (s)) + +/* Pass the arguments in either registers, or in the stack. Using the + ppc sysv ABI, the first eight words of the argument list (that might + be less than eight parameters if some parameters occupy more than one + word) are passed in r3..r10 registers. float and double parameters are + passed in fpr's, in addition to that. Rest of the parameters if any + are passed in user stack. + + If the function is returning a structure, then the return address is passed + in r3, then the first 7 words of the parametes can be passed in registers, + starting from r4. */ + +CORE_ADDR +push_arguments (nargs, args, sp, struct_return, struct_addr) + int nargs; + value_ptr *args; + CORE_ADDR sp; + int struct_return; + CORE_ADDR struct_addr; +{ + int argno; + int greg, freg; + int argstkspace; + int structstkspace; + int argoffset; + int structoffset; + value_ptr arg; + struct type *type; + int len; + char old_sp_buf[4]; + + greg = struct_return ? 4 : 3; + freg = 1; + argstkspace = 0; + structstkspace = 0; + + /* Figure out how much new stack space is required for arguments + which don't fit in registers. Unlike the PowerOpen ABI, the + SysV ABI doesn't reserve any extra space for parameters which + are put in registers. */ + for (argno = 0; argno < nargs; argno++) { + arg = args[argno]; + type = check_typedef(VALUE_TYPE(arg)); + len = TYPE_LENGTH(type); + + if (TYPE_CODE(type) == TYPE_CODE_FLT) { + if (freg <= 8) + freg++; + else { + /* SysV ABI converts floats to doubles when placed in + memory and requires 8 byte alignment */ + if (argstkspace & 0x4) + argstkspace += 4; + argstkspace += 8; + } + } + else if (TYPE_CODE(type) == TYPE_CODE_INT && len == 8) { /* long long */ + if (greg > 9) { + greg = 11; + if (argstkspace & 0x4) + argstkspace += 4; + argstkspace += 8; + } + else { + if ((greg & 1) == 0) + greg++; + greg += 2; + } + + } + else { + if ( len > 4 + || TYPE_CODE(type) == TYPE_CODE_STRUCT + || TYPE_CODE(type) == TYPE_CODE_UNION ) { + /* Rounding to the nearest multiple of 8 may not be necessary, + but it is safe. Particularly since we don't know the + field types of the structure */ + structstkspace += round2(len,8); + } + if (greg <= 10) { + greg++; + } + else { + argstkspace += 4; + } + } + } + + sp -= argstkspace + structstkspace; + + /* Allocate space for backchain and callee's saved lr */ + sp -= 8; + + /* Make sure that we maintain 16 byte alignment */ + sp &= ~0x0f; + + /* Update %sp before proceeding any further */ + write_register (SP_REGNUM, sp); + + /* write the backchain */ + store_address(old_sp_buf, 4, sp_before_dummy); + write_memory(sp, old_sp_buf, 4); + + argoffset = 8; + structoffset = argoffset + argstkspace; + freg = 1; + greg = 3; + /* Now fill in the registers and stack... */ + for (argno = 0; argno < nargs; argno++) { + arg = args[argno]; + type = check_typedef(VALUE_TYPE(arg)); + len = TYPE_LENGTH(type); + + if (TYPE_CODE(type) == TYPE_CODE_FLT) { + if (freg <= 8) { + if (len > 8) + printf_unfiltered ( +"Fatal Error: a floating point parameter #%d with a size > 8 is found!\n", argno); + memcpy(®isters[REGISTER_BYTE(FP0_REGNUM + freg)], + VALUE_CONTENTS (arg), len); + freg++; + } + else { + /* SysV ABI converts floats to doubles when placed in + memory and requires 8 byte alignment */ + /* FIXME: Convert floats to doubles */ + if (argoffset & 0x4) + argoffset += 4; + write_memory (sp+argoffset, (char *) VALUE_CONTENTS (arg), len); + argoffset += 8; + } + } + else if (TYPE_CODE(type) == TYPE_CODE_INT && len == 8) { /* long long */ + if (greg > 9) { + greg = 11; + if (argoffset & 0x4) + argoffset += 4; + write_memory (sp+argoffset, (char *) VALUE_CONTENTS (arg), len); + argoffset += 8; + } + else { + if ((greg & 1) == 0) + greg++; + + memcpy (®isters[REGISTER_BYTE(greg)], + VALUE_CONTENTS (arg), 4); + memcpy (®isters[REGISTER_BYTE(greg+1)], + VALUE_CONTENTS (arg) + 4, 4); + greg += 2; + } + } + else { + char val_buf[4]; + if ( len > 4 + || TYPE_CODE(type) == TYPE_CODE_STRUCT + || TYPE_CODE(type) == TYPE_CODE_UNION ) { + + write_memory(sp+structoffset, VALUE_CONTENTS(arg), len); + store_address(val_buf, 4, sp+structoffset); + structoffset += round2(len,8); + } + else { + memset(val_buf, 0, 4); + memcpy(val_buf, VALUE_CONTENTS(arg), len); + } + if (greg <= 10) { + *(int*)®isters[REGISTER_BYTE(greg)] = 0; + memcpy (®isters[REGISTER_BYTE(greg)], val_buf, 4); + greg++; + } + else { + write_memory(sp+argoffset, val_buf, 4); + argoffset += 4; + } + } + } + + target_store_registers (-1); + return sp; +} + + +/* a given return value in `regbuf' with a type `valtype', extract and copy its + value into `valbuf' */ + +void +extract_return_value (valtype, regbuf, valbuf) + struct type *valtype; + char regbuf[REGISTER_BYTES]; + char *valbuf; +{ + int offset = 0; + + if (TYPE_CODE (valtype) == TYPE_CODE_FLT) { + + double dd; float ff; + /* floats and doubles are returned in fpr1. fpr's have a size of 8 bytes. + We need to truncate the return value into float size (4 byte) if + necessary. */ + + if (TYPE_LENGTH (valtype) > 4) /* this is a double */ + memcpy (valbuf, ®buf[REGISTER_BYTE (FP0_REGNUM + 1)], + TYPE_LENGTH (valtype)); + else { /* float */ + memcpy (&dd, ®buf[REGISTER_BYTE (FP0_REGNUM + 1)], 8); + ff = (float)dd; + memcpy (valbuf, &ff, sizeof(float)); + } + } + else { + /* return value is copied starting from r3. */ + if (TARGET_BYTE_ORDER == BIG_ENDIAN + && TYPE_LENGTH (valtype) < REGISTER_RAW_SIZE (3)) + offset = REGISTER_RAW_SIZE (3) - TYPE_LENGTH (valtype); + + memcpy (valbuf, regbuf + REGISTER_BYTE (3) + offset, + TYPE_LENGTH (valtype)); + } +} + + +/* keep structure return address in this variable. + FIXME: This is a horrid kludge which should not be allowed to continue + living. This only allows a single nested call to a structure-returning + function. Come on, guys! -- gnu@cygnus.com, Aug 92 */ + +CORE_ADDR rs6000_struct_return_address; + +/* Determines whether the function FI has a frame on the stack or not. */ + +int +frameless_function_invocation (fi) + struct frame_info *fi; +{ + CORE_ADDR func_start; + struct rs6000_framedata fdata; + + if (fi->next != NULL && !fi->next->signal_handler_caller) + /* Don't even think about framelessness except on the innermost frame. + or in a frame previous to a signal handler caller */ + return 0; + + if (in_sigtramp2(fi->pc, "")) + /* We'll find the wrong thing below if we search for a signal trampoline */ + return 0; + + func_start = get_pc_function_start (fi->pc) + FUNCTION_START_OFFSET; + + /* If we failed to find the start of the function, it is a mistake + to inspect the instructions. */ + + if (!func_start) + return 0; + + (void) skip_prologue (func_start, &fdata); + return fdata.frameless; +} + +/* Return the PC saved in a frame */ + +unsigned long +frame_saved_pc (fi) + struct frame_info *fi; +{ + CORE_ADDR func_start; + struct rs6000_framedata fdata; + int frameless; + + if (fi->signal_handler_caller) + { + CORE_ADDR regs_addr = read_memory_integer (fi->frame + REGS_PTR_OFFSET, 4); + /* return the NIP in the regs array */ + return read_memory_integer(regs_addr + 4 * PT_NIP, 4); + } + + func_start = get_pc_function_start (fi->pc) + FUNCTION_START_OFFSET; + + /* If we failed to find the start of the function, it is a mistake + to inspect the instructions. */ + if (!func_start) + return 0; + + (void) skip_prologue (func_start, &fdata); + + if (fdata.lr_offset == 0 && fi->next != NULL) + return read_memory_integer (rs6000_frame_chain (fi) + DEFAULT_LR_SAVE, 4); + + if (fdata.lr_offset == 0) + return read_register (LR_REGNUM); + + return read_memory_integer (rs6000_frame_chain (fi) + fdata.lr_offset, 4); +} + +/* If saved registers of frame FI are not known yet, read and cache them. + &FDATAP contains rs6000_framedata; TDATAP can be NULL, + in which case the framedata are read. */ + +static void +frame_get_cache_fsr (fi, fdatap) + struct frame_info *fi; + struct rs6000_framedata *fdatap; +{ + int ii; + CORE_ADDR frame_addr; + struct rs6000_framedata work_fdata; + + if (fi->cache_fsr) + return; + + if (fdatap == NULL) { + fdatap = &work_fdata; + (void) skip_prologue (get_pc_function_start (fi->pc), fdatap); + } + + fi->cache_fsr = (struct frame_saved_regs *) + obstack_alloc (&frame_cache_obstack, sizeof (struct frame_saved_regs)); + memset (fi->cache_fsr, '\0', sizeof (struct frame_saved_regs)); + + if (fi->prev && fi->prev->frame) + frame_addr = fi->prev->frame; + else + frame_addr = read_memory_integer (fi->frame, 4); + + /* if != -1, fdatap->saved_fpr is the smallest number of saved_fpr. + All fpr's from saved_fpr to fp31 are saved. */ + + if (fdatap->saved_fpr >= 0) { + int fpr_offset = frame_addr + fdatap->fpr_offset; + for (ii = fdatap->saved_fpr; ii < 32; ii++) { + fi->cache_fsr->regs [FP0_REGNUM + ii] = fpr_offset; + fpr_offset += 8; + } + } + + /* if != -1, fdatap->saved_gpr is the smallest number of saved_gpr. + All gpr's from saved_gpr to gpr31 are saved. */ + + if (fdatap->saved_gpr >= 0) { + int gpr_offset = frame_addr + fdatap->gpr_offset; + for (ii = fdatap->saved_gpr; ii < 32; ii++) { + fi->cache_fsr->regs [ii] = gpr_offset; + gpr_offset += 4; + } + } + + /* If != 0, fdatap->cr_offset is the offset from the frame that holds + the CR. */ + if (fdatap->cr_offset != 0) + fi->cache_fsr->regs [CR_REGNUM] = frame_addr + fdatap->cr_offset; + + /* If != 0, fdatap->lr_offset is the offset from the frame that holds + the LR. */ + if (fdatap->lr_offset != 0) + fi->cache_fsr->regs [LR_REGNUM] = frame_addr + fdatap->lr_offset; +} + +/* Return the address of a frame. This is the inital %sp value when the frame + was first allocated. For functions calling alloca(), it might be saved in + an alloca register. */ + +CORE_ADDR +frame_initial_stack_address (fi) + struct frame_info *fi; +{ + CORE_ADDR tmpaddr; + struct rs6000_framedata fdata; + struct frame_info *callee_fi; + + /* if the initial stack pointer (frame address) of this frame is known, + just return it. */ + + if (fi->initial_sp) + return fi->initial_sp; + + /* If we're in a signal handler caller, fi->frame is fine */ + if (fi->signal_handler_caller) { + fi->initial_sp = fi->frame; + return fi->initial_sp; + } + + /* find out if this function is using an alloca register.. */ + + (void) skip_prologue (get_pc_function_start (fi->pc), &fdata); + + /* if saved registers of this frame are not known yet, read and cache them. */ + + if (!fi->cache_fsr) + frame_get_cache_fsr (fi, &fdata); + + /* If no alloca register used, then fi->frame is the value of the %sp for + this frame, and it is good enough. */ + + if (fdata.alloca_reg < 0) { + fi->initial_sp = fi->frame; + return fi->initial_sp; + } + + /* This function has an alloca register. If this is the top-most frame + (with the lowest address), the value in alloca register is good. */ + + if (!fi->next) + return fi->initial_sp = read_register (fdata.alloca_reg); + + /* Otherwise, this is a caller frame. Callee has usually already saved + registers, but there are exceptions (such as when the callee + has no parameters). Find the address in which caller's alloca + register is saved. */ + + for (callee_fi = fi->next; callee_fi; callee_fi = callee_fi->next) { + + if (!callee_fi->cache_fsr) + frame_get_cache_fsr (callee_fi, NULL); + + /* this is the address in which alloca register is saved. */ + + tmpaddr = callee_fi->cache_fsr->regs [fdata.alloca_reg]; + if (tmpaddr) { + fi->initial_sp = read_memory_integer (tmpaddr, 4); + return fi->initial_sp; + } + + /* Go look into deeper levels of the frame chain to see if any one of + the callees has saved alloca register. */ + } + + /* If alloca register was not saved, by the callee (or any of its callees) + then the value in the register is still good. */ + + return fi->initial_sp = read_register (fdata.alloca_reg); +} + + +CORE_ADDR +rs6000_frame_chain (thisframe) + struct frame_info *thisframe; +{ + CORE_ADDR fp; + if (inside_entry_file ((thisframe)->pc)) + return 0; + /* The following #if 0 code should not be needed any longer because the + kernel now properly constructs the chain for the signal frame */ +#if 0 + if (thisframe->signal_handler_caller) + { + CORE_ADDR regs_addr; + regs_addr = read_memory_integer (thisframe->frame + REGS_PTR_OFFSET, 4); + /* fetch address of saved r1 from the regs array */ + fp = read_memory_integer(regs_addr + 4 * PT_R1, 4); + } + else +#endif + fp = read_memory_integer ((thisframe)->frame, 4); + + return fp; +} + +int +gdb_print_insn_powerpc (memaddr, info) + bfd_vma memaddr; + disassemble_info *info; +{ + if (TARGET_BYTE_ORDER == BIG_ENDIAN) + return print_insn_big_powerpc (memaddr, info); + else + return print_insn_little_powerpc (memaddr, info); +} + +void +init_extra_frame_info (fromleaf, fi) + int fromleaf; + struct frame_info *fi; +{ + fi->initial_sp = 0; + fi->cache_fsr = 0; + if (fi->next != 0) { + /* We're called from get_prev_frame_info; check to see if + this is a signal frame by looking to see if the pc points + at trampoline code */ + char buf[8]; + if (target_read_memory(fi->pc, buf, sizeof(buf)) != 0) + return; + if ( extract_unsigned_integer(buf,4) == INSTR_LI_R0_0x7777 + || extract_unsigned_integer(buf+4,4) == INSTR_SC ) + fi->signal_handler_caller = 1; + else + fi->signal_handler_caller = 0; + } +} + +/* Some of the following code was swiped from tm-rs6000.h. */ + +void +frame_find_saved_regs(struct frame_info *fi, struct frame_saved_regs *fsr) +{ + int ii; + CORE_ADDR frame_addr, func_start; + struct rs6000_framedata fdata; + + if (fi->signal_handler_caller) { + CORE_ADDR regs_addr = read_memory_integer (fi->frame + REGS_PTR_OFFSET, 4); + memset (fsr, '\0', sizeof (*fsr)); + fsr->regs[PC_REGNUM] = regs_addr + 4 * PT_NIP; + fsr->regs[PS_REGNUM] = regs_addr + 4 * PT_MSR; + fsr->regs[CR_REGNUM] = regs_addr + 4 * PT_CCR; + fsr->regs[LR_REGNUM] = regs_addr + 4 * PT_LNK; + fsr->regs[CTR_REGNUM] = regs_addr + 4 * PT_CTR; + fsr->regs[XER_REGNUM] = regs_addr + 4 * PT_XER; + fsr->regs[MQ_REGNUM] = regs_addr + 4 * PT_MQ; + for (ii=0; ii<32; ii++) { + fsr->regs[GP0_REGNUM+ii] = regs_addr + 4*ii; + } + for (ii=0; ii<32; ii++) { + fsr->regs[FP0_REGNUM+ii] = regs_addr + 4*ELF_NGREG + 8*ii; + } + return; + } + + /* find the start of the function and collect info about its frame. */ + + func_start = get_pc_function_start (fi->pc) + FUNCTION_START_OFFSET; + (void) skip_prologue (func_start, &fdata); + memset (fsr, '\0', sizeof (*fsr)); + + /* if there were any saved registers, figure out parent's stack pointer. */ + /* the following is true only if the frame doesn't have a call to alloca(), + FIXME. */ + if (fdata.saved_fpr == 0 && fdata.saved_gpr == 0 && + fdata.lr_offset == 0 && fdata.cr_offset == 0) { + frame_addr = 0; + + } else if (fi->prev && fi->prev->frame) { + frame_addr = fi->prev->frame; + + } else { + frame_addr = read_memory_integer (fi->frame, 4); + } + + /* if != -1, fdata.saved_fpr is the smallest number of saved_fpr. All + fpr's from saved_fpr to f31 are saved. */ + if (fdata.saved_fpr >= 0) { + int fpr_offset = frame_addr + fdata.fpr_offset; + for (ii = fdata.saved_fpr; ii < 32; ii++) { + fsr->regs [FP0_REGNUM + ii] = fpr_offset; + fpr_offset += 8; + } + } + + /* if != -1, fdata.saved_gpr is the smallest number of saved_gpr. All + gpr's from saved_gpr to r31 are saved. */ + if (fdata.saved_gpr >= 0) { + int gpr_offset = frame_addr + fdata.gpr_offset; + for (ii = fdata.saved_gpr; ii < 32; ii++) { + fsr->regs [ii] = gpr_offset; + gpr_offset += 4; + } + } + + /* If != 0, fdata.cr_offset is the offset from the frame that holds + the CR */ + if (fdata.cr_offset != 0) { + fsr->regs [CR_REGNUM] = frame_addr + fdata.cr_offset; + } + + /* If != 0, fdata.cr_offset is the offset from the frame that holds + the LR */ + if (fdata.lr_offset != 0) { + fsr->regs [LR_REGNUM] = frame_addr + fdata.lr_offset; + } +} + +void +init_frame_pc_first(int fromleaf, struct frame_info *fi) +{ + if (fromleaf) { + if ( fi->next + && fi->next->next + && fi->next->next->signal_handler_caller) { + /* next next frame is a signal handler caller... + This is a bit confusing, so an explanation is in order. + fi is the frame set we are determining pc for. fi->next + is the frameless callee of fi. And fi->next->next is the + frame of the signal trampoline code. */ + CORE_ADDR regs_addr = + read_memory_integer (fi->next->next->frame + REGS_PTR_OFFSET, 4); + fi->pc = read_memory_integer(regs_addr + 4 * PT_LNK, 4); + } + else { + /* normal leaf case; frame is at the top */ + fi->pc = SAVED_PC_AFTER_CALL(fi->next); + } + } + else { + if (fi->next) { + /* not top-most frame */ + fi->pc = FRAME_SAVED_PC(fi->next); + } + else { + /* top-most frame; just fetch current pc value */ + fi->pc = read_pc(); + } + } +} + +CORE_ADDR +skip_trampoline_code (CORE_ADDR pc) +{ + char buf[4]; + struct obj_section *sect; + struct objfile *objfile; + unsigned long insn; + CORE_ADDR plt_start = 0; + CORE_ADDR symtab = 0; + CORE_ADDR strtab = 0; + int num_slots = -1; + int reloc_index = -1; + CORE_ADDR plt_table; + CORE_ADDR reloc; + CORE_ADDR sym; + Elf32_Word symidx; + char symname[1024]; + struct minimal_symbol *msymbol; + + /* Find the section pc is in; return if not in .plt */ + sect = find_pc_section(pc); + if (!sect || strcmp(sect->the_bfd_section->name, ".plt") != 0) + return 0; + + objfile = sect->objfile; + + /* Pick up the instruction at pc. It had better be of the + form + li r11, IDX + + where IDX is an index into the plt_table. */ + + if (target_read_memory(pc, buf, 4) != 0) + return 0; + insn = extract_unsigned_integer(buf, 4); + + if ( (insn & 0xffff0000) != 0x39600000 /* li r11, VAL */ ) + return 0; + + reloc_index = (insn << 16) >> 16; + + /* Find the objfile that pc is in and obtain the information + necessary for finding the symbol name. */ + for (sect = objfile->sections; sect < objfile->sections_end; ++sect) { + const char *secname = sect->the_bfd_section->name; + if (strcmp(secname, ".plt") == 0) { + plt_start = sect->addr; + } + else if (strcmp(secname, ".rela.plt") == 0) { + num_slots = ((int) sect->endaddr - (int) sect->addr) / 12; + } + else if (strcmp(secname, ".dynsym") == 0) { + symtab = sect->addr; + } + else if (strcmp(secname, ".dynstr") == 0) { + strtab = sect->addr; + } + } + + /* Make sure we have all the information we need. */ + if (plt_start == 0 || num_slots == -1 || symtab == 0 || strtab == 0) + return 0; + + /* Compute the value of the plt table */ + plt_table = plt_start + 72 + 8*num_slots; + + /* Get address of the relocation entry (Elf32_Rela) */ + if (target_read_memory(plt_table + reloc_index, buf, 4) != 0) + return 0; + reloc = extract_address(buf, 4); + + sect = find_pc_section(reloc); + if (!sect) + return 0; + + if (strcmp(sect->the_bfd_section->name, ".text") == 0) { + return reloc; + } + + /* Now get the r_info field which is the relocation type and symbol + index. */ + if (target_read_memory(reloc+4, buf, 4) != 0) + return 0; + symidx = extract_unsigned_integer(buf, 4); + + /* Shift out the relocation type leaving just the symbol index */ + symidx = ELF32_R_SYM(symidx); + + /* compute the address of the symbol */ + sym = symtab + symidx * sizeof(Elf32_Sym); + + /* Fetch the string table index */ + if (target_read_memory(sym, buf, 4) != 0) + return 0; + symidx = extract_unsigned_integer(buf, 4); + + /* Fetch the string; we don't know how long it is. Is it possible + that the following will fail because we're trying to fetch too + much? */ + if (target_read_memory(strtab+symidx, symname, sizeof(symname)) != 0) + return 0; + + /* This might not work right if we have multiple symbols with the + same name; the only way to really get it right is to perform + the same sort of lookup as the dynamic linker. */ + msymbol = lookup_minimal_symbol_text(symname, NULL, NULL); + if (!msymbol) + return 0; + + return SYMBOL_VALUE_ADDRESS (msymbol); +} + +/* + * Determine if pc is in a signal trampoline... + * + * Ha! That's not what this does at all. wait_for_inferior in infrun.c + * calls IN_SIGTRAMP in order to detect entry into a signal trampoline + * just after delivery of a signal. But on linux, signal trampolines + * are used for the return path only. The kernel sets things up so that + * the signal handler is called directly. + * + * If we use in_sigtramp2() in place of in_sigtramp() (see below) + * we'll (often) end up with stop_pc in the trampoline and prev_pc in + * the (now exited) handler. The code there will cause a temporary + * breakpoint to be set on prev_pc which is not very likely to get hit + * again. + * + * If this is confusing, think of it this way... the code in + * wait_for_inferior() needs to be able to detect entry into a signal + * trampoline just after a signal is delivered, not after the handler + * has been run. + * + * So, we define in_sigtramp() below to return 1 if the following is + * true: + * + * 1) The previous frame is a real signal trampoline. + * + * - and - + * + * 2) pc is at the second instruction of the corresponding handler. + * + * Why the second instruction? It seems that wait_for_inferior() + * never sees the first instruction when single stepping. When a + * signal is delivered while stepping, the next instruction that + * would've been stepped over isn't, instead a signal is delivered and + * the first instruction of the handler is stepped over instead. That + * puts us on the second instruction. + * + * IN_SIGTRAMP is called from blockframe.c as well in order to set + * the signal_handler_caller flag. Because of our strange definition + * of in_sigtramp below, we can't rely on signal_handler_caller getting + * set correctly from within blockframe.c. This is why we take pains + * to set it in init_extra_frame_info(). + */ + +int in_sigtramp(CORE_ADDR pc, char *func_name) +{ + CORE_ADDR lr; + CORE_ADDR sp; + CORE_ADDR tramp_sp; + char buf[4]; + CORE_ADDR handler; + + lr = read_register(LR_REGNUM); + if (!in_sigtramp2(lr,0)) + return 0; + + sp = read_register(SP_REGNUM); + + if (target_read_memory(sp, buf, sizeof(buf)) != 0) + return 0; + + tramp_sp = extract_unsigned_integer(buf, 4); + + if (target_read_memory(tramp_sp + __SIGNAL_FRAMESIZE + + offsetof(struct sigcontext_struct, handler), + buf, sizeof(buf)) != 0) + return 0; + + handler = extract_unsigned_integer(buf, 4); + + return (pc == handler + 4); +} + +/* + * in_sigtramp2 is how I think in_sigtramp ought to be written on this + * platform. (See above for why it is not.) I actually call it from + * a few places, so here it is. + * + * The signal handler trampoline is on the stack and consists of exactly + * two instructions. The easiest and most accurate way of determining + * whether the pc is in one of these trampolines is by inspecting the + * instructions. It'd be faster if we could find a way to do this by + * some simple address comparisons. + */ +int in_sigtramp2(CORE_ADDR pc, char *func_name) +{ + char buf[12]; + unsigned long pcinsn; + if (target_read_memory(pc-4, buf, sizeof(buf)) != 0) + return 0; + + /* extract the instruction at the pc */ + pcinsn = extract_unsigned_integer(buf+4,4); + + return ( + ( pcinsn == INSTR_LI_R0_0x7777 + && extract_unsigned_integer(buf+8,4) == INSTR_SC) + || + ( pcinsn == INSTR_SC + && extract_unsigned_integer(buf,4) == INSTR_LI_R0_0x7777) ); +} + +void +_initialize_ppclinux_tdep () +{ + tm_print_insn = gdb_print_insn_powerpc; +} -- Kevin Buettner kev@primenet.com ---------------------------------------------------------- Kevin B. Hendricks Associate Professor, Operations & Information Technology School of Business, College of William & Mary Williamsburg, VA 23187, kbhend@dogwood.tyler.wm.edu http://business.tyler.wm.edu -----End of forwarded message-----