diff options
author | Kevin Buettner <kevinb@redhat.com> | 2000-02-22 01:20:32 +0000 |
---|---|---|
committer | Kevin Buettner <kevinb@redhat.com> | 2000-02-22 01:20:32 +0000 |
commit | c877c8e605d04635abeb06dfdd001c63f404227c (patch) | |
tree | 3fc1dc159041b140e1e5e4f3a3c0a3b3724852bd | |
parent | 2559291b00ed01bbd3fb05db965f51c398f1fcad (diff) | |
download | gdb-c877c8e605d04635abeb06dfdd001c63f404227c.zip gdb-c877c8e605d04635abeb06dfdd001c63f404227c.tar.gz gdb-c877c8e605d04635abeb06dfdd001c63f404227c.tar.bz2 |
Changes for GNU/Linux PPC native port of gdb.
-rw-r--r-- | gdb/config/powerpc/linux.mt | 3 | ||||
-rw-r--r-- | gdb/config/powerpc/nm-linux.h | 64 | ||||
-rw-r--r-- | gdb/config/powerpc/tm-linux.h | 113 | ||||
-rw-r--r-- | gdb/config/powerpc/xm-linux.h | 46 | ||||
-rw-r--r-- | gdb/config/rs6000/tm-rs6000.h | 9 | ||||
-rw-r--r-- | gdb/ppc-linux-nat.c | 76 | ||||
-rw-r--r-- | gdb/ppc-linux-tdep.c | 599 | ||||
-rw-r--r-- | gdb/rs6000-tdep.c | 15 |
8 files changed, 911 insertions, 14 deletions
diff --git a/gdb/config/powerpc/linux.mt b/gdb/config/powerpc/linux.mt new file mode 100644 index 0000000..a7c1bed --- /dev/null +++ b/gdb/config/powerpc/linux.mt @@ -0,0 +1,3 @@ +# Target: Motorola PPC on Linux +TDEPFILES= rs6000-tdep.o ppc-linux-tdep.o +TM_FILE= tm-linux.h diff --git a/gdb/config/powerpc/nm-linux.h b/gdb/config/powerpc/nm-linux.h new file mode 100644 index 0000000..78b78b9 --- /dev/null +++ b/gdb/config/powerpc/nm-linux.h @@ -0,0 +1,64 @@ +/* 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 + +/* Support for Linuxthreads. */ + +#ifdef __STDC__ +struct objfile; +#endif + +extern void +linuxthreads_new_objfile PARAMS ((struct objfile *objfile)); +#define target_new_objfile(OBJFILE) linuxthreads_new_objfile (OBJFILE) + +extern char * +linuxthreads_pid_to_str PARAMS ((int pid)); +#define target_pid_to_str(PID) linuxthreads_pid_to_str (PID) + +extern int +linuxthreads_prepare_to_proceed PARAMS ((int step)); +#define PREPARE_TO_PROCEED(select_it) linuxthreads_prepare_to_proceed (1) + + +#endif /* #ifndef NM_LINUX_H */ diff --git a/gdb/config/powerpc/tm-linux.h b/gdb/config/powerpc/tm-linux.h new file mode 100644 index 0000000..13a5327 --- /dev/null +++ b/gdb/config/powerpc/tm-linux.h @@ -0,0 +1,113 @@ +/* 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" + +#undef PUSH_ARGUMENTS + +/* We can single step on linux */ +#undef SOFTWARE_SINGLE_STEP +#define SOFTWARE_SINGLE_STEP(p,q) abort() /* Will never execute! */ +#undef SOFTWARE_SINGLE_STEP_P +#define SOFTWARE_SINGLE_STEP_P 0 + +/* 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" + +extern CORE_ADDR ppc_linux_skip_trampoline_code (CORE_ADDR pc); +#undef SKIP_TRAMPOLINE_CODE +#define SKIP_TRAMPOLINE_CODE(pc) ppc_linux_skip_trampoline_code (pc) + +extern int ppc_linux_in_sigtramp (CORE_ADDR pc, char *func_name); +#undef IN_SIGTRAMP +#define IN_SIGTRAMP(pc,func_name) ppc_linux_in_sigtramp (pc,func_name) + +extern unsigned long ppc_linux_frame_saved_pc (struct frame_info *); +#undef FRAME_SAVED_PC +#define FRAME_SAVED_PC(FRAME) ppc_linux_frame_saved_pc (FRAME) + +extern void ppc_linux_init_extra_frame_info (int fromleaf, struct frame_info *); +#undef INIT_EXTRA_FRAME_INFO +#define INIT_EXTRA_FRAME_INFO(fromleaf, fi) \ + ppc_linux_init_extra_frame_info (fromleaf, fi) + +extern int ppc_linux_frameless_function_invocation (struct frame_info *); +#undef FRAMELESS_FUNCTION_INVOCATION +#define FRAMELESS_FUNCTION_INVOCATION(FI) \ + (ppc_linux_frameless_function_invocation (FI)) + +extern void ppc_linux_frame_init_saved_regs (struct frame_info *); +#undef FRAME_INIT_SAVED_REGS +#define FRAME_INIT_SAVED_REGS(FI) ppc_linux_frame_init_saved_regs (FI) + +CORE_ADDR ppc_linux_frame_chain (struct frame_info *); +#undef FRAME_CHAIN +#define FRAME_CHAIN(thisframe) ppc_linux_frame_chain (thisframe) + +CORE_ADDR ppc_sysv_abi_push_arguments PARAMS ((int, struct value **, CORE_ADDR, int, CORE_ADDR)); +#undef PUSH_ARGUMENTS +#define PUSH_ARGUMENTS(nargs, args, sp, struct_return, struct_addr) \ + (ppc_sysv_abi_push_arguments((nargs), (args), (sp), (struct_return), (struct_addr))) + +#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 + +#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 + +/* Some versions of Linux have real-time signal support in the C library, and + some don't. We have to include this file to find out. */ +#include <signal.h> + +#ifdef __SIGRTMIN +#define REALTIME_LO __SIGRTMIN +#define REALTIME_HI (__SIGRTMAX + 1) +#else +#define REALTIME_LO 32 +#define REALTIME_HI 64 +#endif + +#endif /* #ifndef TM_LINUX_H */ diff --git a/gdb/config/powerpc/xm-linux.h b/gdb/config/powerpc/xm-linux.h index 754f858..0ca4c60 100644 --- a/gdb/config/powerpc/xm-linux.h +++ b/gdb/config/powerpc/xm-linux.h @@ -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 --git a/gdb/config/rs6000/tm-rs6000.h b/gdb/config/rs6000/tm-rs6000.h index 3c34431..89a0bd1 100644 --- a/gdb/config/rs6000/tm-rs6000.h +++ b/gdb/config/rs6000/tm-rs6000.h @@ -380,10 +380,9 @@ CORE_ADDR rs6000_frame_chain PARAMS ((struct frame_info *)); by FI does not have a frame on the stack associated with it. If it does not, FRAMELESS is set to 1, else 0. */ +extern int rs6000_frameless_function_invocation (struct frame_info *); #define FRAMELESS_FUNCTION_INVOCATION(FI) \ - (frameless_function_invocation (FI)) - -extern int frameless_function_invocation PARAMS ((struct frame_info *)); + (rs6000_frameless_function_invocation (FI)) #define INIT_FRAME_PC_FIRST(fromleaf, prev) \ prev->pc = (fromleaf ? SAVED_PC_AFTER_CALL (prev->next) : \ @@ -407,9 +406,9 @@ extern void rs6000_init_extra_frame_info (int fromleaf, struct frame_info *); #define DEFAULT_LR_SAVE 8 /* Return saved PC from a frame */ -#define FRAME_SAVED_PC(FRAME) frame_saved_pc (FRAME) +#define FRAME_SAVED_PC(FRAME) rs6000_frame_saved_pc (FRAME) -extern unsigned long frame_saved_pc PARAMS ((struct frame_info *)); +extern unsigned long rs6000_frame_saved_pc (struct frame_info *); extern CORE_ADDR rs6000_frame_args_address PARAMS ((struct frame_info *)); #define FRAME_ARGS_ADDRESS(FI) rs6000_frame_args_address (FI) diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c new file mode 100644 index 0000000..1522f96 --- /dev/null +++ b/gdb/ppc-linux-nat.c @@ -0,0 +1,76 @@ +/* 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 "gdbcore.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_UISA_SP_REGNUM; regi <= LAST_UISA_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 --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c new file mode 100644 index 0000000..c9a6812 --- /dev/null +++ b/gdb/ppc-linux-tdep.c @@ -0,0 +1,599 @@ +/* Target-dependent code for GDB, the GNU debugger. + Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000 + 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 "gdbcore.h" +#include "gdbcmd.h" +#include "symfile.h" +#include "objfiles.h" + +/* The following two instructions are used in the signal trampoline + code on linux/ppc */ +#define INSTR_LI_R0_0x7777 0x38007777 +#define INSTR_SC 0x44000002 + +/* Since the *-tdep.c files are platform independent (i.e, they may be + used to build cross platform debuggers), we can't include system + headers. Therefore, details concerning the sigcontext structure + must be painstakingly rerecorded. What's worse, if these details + ever change in the header files, they'll have to be changed here + as well. */ + +/* __SIGNAL_FRAMESIZE from <asm/ptrace.h> */ +#define PPC_LINUX_SIGNAL_FRAMESIZE 64 + +/* From <asm/sigcontext.h>, offsetof(struct sigcontext_struct, regs) == 0x1c */ +#define PPC_LINUX_REGS_PTR_OFFSET (PPC_LINUX_SIGNAL_FRAMESIZE + 0x1c) + +/* From <asm/sigcontext.h>, + offsetof(struct sigcontext_struct, handler) == 0x14 */ +#define PPC_LINUX_HANDLER_PTR_OFFSET (PPC_LINUX_SIGNAL_FRAMESIZE + 0x14) + +/* From <asm/ptrace.h>, values for PT_NIP, PT_R1, and PT_LNK */ +#define PPC_LINUX_PT_R0 0 +#define PPC_LINUX_PT_R1 1 +#define PPC_LINUX_PT_R2 2 +#define PPC_LINUX_PT_R3 3 +#define PPC_LINUX_PT_R4 4 +#define PPC_LINUX_PT_R5 5 +#define PPC_LINUX_PT_R6 6 +#define PPC_LINUX_PT_R7 7 +#define PPC_LINUX_PT_R8 8 +#define PPC_LINUX_PT_R9 9 +#define PPC_LINUX_PT_R10 10 +#define PPC_LINUX_PT_R11 11 +#define PPC_LINUX_PT_R12 12 +#define PPC_LINUX_PT_R13 13 +#define PPC_LINUX_PT_R14 14 +#define PPC_LINUX_PT_R15 15 +#define PPC_LINUX_PT_R16 16 +#define PPC_LINUX_PT_R17 17 +#define PPC_LINUX_PT_R18 18 +#define PPC_LINUX_PT_R19 19 +#define PPC_LINUX_PT_R20 20 +#define PPC_LINUX_PT_R21 21 +#define PPC_LINUX_PT_R22 22 +#define PPC_LINUX_PT_R23 23 +#define PPC_LINUX_PT_R24 24 +#define PPC_LINUX_PT_R25 25 +#define PPC_LINUX_PT_R26 26 +#define PPC_LINUX_PT_R27 27 +#define PPC_LINUX_PT_R28 28 +#define PPC_LINUX_PT_R29 29 +#define PPC_LINUX_PT_R30 30 +#define PPC_LINUX_PT_R31 31 +#define PPC_LINUX_PT_NIP 32 +#define PPC_LINUX_PT_MSR 33 +#define PPC_LINUX_PT_CTR 35 +#define PPC_LINUX_PT_LNK 36 +#define PPC_LINUX_PT_XER 37 +#define PPC_LINUX_PT_CCR 38 +#define PPC_LINUX_PT_MQ 39 +#define PPC_LINUX_PT_FPR0 48 /* each FP reg occupies 2 slots in this space */ +#define PPC_LINUX_PT_FPR31 (PPC_LINUX_PT_FPR0 + 2*31) +#define PPC_LINUX_PT_FPSCR (PPC_LINUX_PT_FPR0 + 2*32 + 1) + +/* 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 first or 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. (I added the test for the + first instruction long after the fact, just in case the observed + behavior is ever fixed.) + + 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 +ppc_linux_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 (!ppc_linux_at_sigtramp_return_path (lr)) + 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 + PPC_LINUX_HANDLER_PTR_OFFSET, buf, + sizeof (buf)) != 0) + return 0; + + handler = extract_unsigned_integer (buf, 4); + + return (pc == handler || pc == handler + 4); +} + +/* + * 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 though if we could find a way to do this + * via some simple address comparisons. + */ +int +ppc_linux_at_sigtramp_return_path (CORE_ADDR pc) +{ + 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)); +} + +CORE_ADDR +ppc_linux_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; + long 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); */ + symidx = symidx >> 8; + + /* compute the address of the symbol */ + sym = symtab + symidx * 4; + + /* 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); +} + +/* The rs6000 version of FRAME_SAVED_PC will almost work for us. The + signal handler details are different, so we'll handle those here + and call the rs6000 version to do the rest. */ +unsigned long +ppc_linux_frame_saved_pc (struct frame_info *fi) +{ + if (fi->signal_handler_caller) + { + CORE_ADDR regs_addr = + read_memory_integer (fi->frame + PPC_LINUX_REGS_PTR_OFFSET, 4); + /* return the NIP in the regs array */ + return read_memory_integer (regs_addr + 4 * PPC_LINUX_PT_NIP, 4); + } + + return rs6000_frame_saved_pc (fi); +} + +void +ppc_linux_init_extra_frame_info (int fromleaf, struct frame_info *fi) +{ + rs6000_init_extra_frame_info (fromleaf, fi); + + 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 */ + if (ppc_linux_at_sigtramp_return_path (fi->pc)) + fi->signal_handler_caller = 1; + else + fi->signal_handler_caller = 0; + } +} + +int +ppc_linux_frameless_function_invocation (struct frame_info *fi) +{ + /* We'll find the wrong thing if we let + rs6000_frameless_function_invocation () search for a signal trampoline */ + if (ppc_linux_at_sigtramp_return_path (fi->pc)) + return 0; + else + return rs6000_frameless_function_invocation (fi); +} + +void +ppc_linux_frame_init_saved_regs (struct frame_info *fi) +{ + if (fi->signal_handler_caller) + { + CORE_ADDR regs_addr; + int i; + if (fi->saved_regs) + return; + + frame_saved_regs_zalloc (fi); + + regs_addr = + read_memory_integer (fi->frame + PPC_LINUX_REGS_PTR_OFFSET, 4); + fi->saved_regs[PC_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_NIP; + fi->saved_regs[PS_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_MSR; + fi->saved_regs[CR_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_CCR; + fi->saved_regs[LR_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_LNK; + fi->saved_regs[CTR_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_CTR; + fi->saved_regs[XER_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_XER; + fi->saved_regs[MQ_REGNUM] = regs_addr + 4 * PPC_LINUX_PT_MQ; + for (i = 0; i < 32; i++) + fi->saved_regs[GP0_REGNUM + i] = regs_addr + 4 * PPC_LINUX_PT_R0 + 4 * i; + for (i = 0; i < 32; i++) + fi->saved_regs[FP0_REGNUM + i] = regs_addr + 4 * PPC_LINUX_PT_FPR0 + 8 * i; + } + else + rs6000_frame_init_saved_regs (fi); +} + +CORE_ADDR +ppc_linux_frame_chain (struct frame_info *thisframe) +{ + /* Kernel properly constructs the frame chain for the handler */ + if (thisframe->signal_handler_caller) + return read_memory_integer ((thisframe)->frame, 4); + else + return rs6000_frame_chain (thisframe); +} + +/* FIXME: Move the following to rs6000-tdep.c (or some other file where + it may be used generically by ports which use either the SysV ABI or + the EABI */ + +/* 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 +ppc_sysv_abi_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]; + CORE_ADDR saved_sp; + + 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; + } + } + + /* Get current SP location */ + saved_sp = read_sp (); + + 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, saved_sp); + 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; +} diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c index 5b6dcad..29ca4ca 100644 --- a/gdb/rs6000-tdep.c +++ b/gdb/rs6000-tdep.c @@ -1,5 +1,5 @@ /* Target-dependent code for GDB, the GNU debugger. - Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000 Free Software Foundation, Inc. This file is part of GDB. @@ -1238,8 +1238,7 @@ skip_trampoline_code (pc) /* Determines whether the function FI has a frame on the stack or not. */ int -frameless_function_invocation (fi) - struct frame_info *fi; +rs6000_frameless_function_invocation (struct frame_info *fi) { CORE_ADDR func_start; struct rs6000_framedata fdata; @@ -1273,8 +1272,7 @@ frameless_function_invocation (fi) /* Return the PC saved in a frame */ unsigned long -frame_saved_pc (fi) - struct frame_info *fi; +rs6000_frame_saved_pc (struct frame_info *fi) { CORE_ADDR func_start; struct rs6000_framedata fdata; @@ -1302,14 +1300,13 @@ frame_saved_pc (fi) if (fi->next->signal_handler_caller) return read_memory_integer (fi->next->frame + SIG_FRAME_LR_OFFSET, 4); else - return read_memory_integer (rs6000_frame_chain (fi) + DEFAULT_LR_SAVE, - 4); + return read_memory_integer (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); + return read_memory_integer (FRAME_CHAIN (fi) + fdata.lr_offset, 4); } /* If saved registers of frame FI are not known yet, read and cache them. @@ -1480,7 +1477,7 @@ rs6000_frame_chain (thisframe) fp = read_memory_integer (thisframe->frame + SIG_FRAME_FP_OFFSET, 4); else if (thisframe->next != NULL && thisframe->next->signal_handler_caller - && frameless_function_invocation (thisframe)) + && FRAMELESS_FUNCTION_INVOCATION (thisframe)) /* A frameless function interrupted by a signal did not change the frame pointer. */ fp = FRAME_FP (thisframe); |