Currently Debian local - Add HPPA/Linux support. Should be submitted upstream "eventually" by "someone". [Note that it contains some changes since what the PA folks last sent me, in order to continue applying to current CVS] diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/nm-linux.h gdb-5.2.cvs20020818/gdb/config/pa/nm-linux.h --- gdb-5.2.cvs20020818.orig/gdb/config/pa/nm-linux.h 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/nm-linux.h 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,66 @@ +/* Native support for GNU/Linux, for GDB, the GNU debugger. + Copyright (C) 2000, 2001 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 PA_NM_LINUX_H +#define PA_NM_LINUX_H + +#include "nm-linux.h" + +#define CANNOT_FETCH_REGISTER(regno) pa_cannot_fetch_register(regno) +extern int pa_cannot_fetch_register (int regno); + +#define CANNOT_STORE_REGISTER(regno) pa_cannot_store_register(regno) +extern int pa_cannot_store_register (int regno); + +#ifdef GDBSERVER +#define REGISTER_U_ADDR(addr, blockend, regno) \ + (addr) = pa_register_u_addr ((blockend),(regno)); + +extern int pa_register_u_addr(int, int); +#endif /* GDBSERVER */ + +#define U_REGS_OFFSET 0 + +#define PTRACE_ARG3_TYPE long +#define PTRACE_XFER_TYPE long + +/* Hardware watchpoints */ + +#define TARGET_HAS_HARDWARE_WATCHPOINTS + +#define TARGET_CAN_USE_HARDWARE_WATCHPOINT(type, cnt, ot) \ + (type == bp_hardware_watchpoint) + +#define HAVE_STEPPABLE_WATCHPOINT 1 + +#define STOPPED_BY_WATCHPOINT(W) \ + pa_linux_stopped_by_watchpoint (PIDGET(inferior_ptid)) +extern CORE_ADDR pa_linux_stopped_by_watchpoint (int); + +#define target_insert_watchpoint(addr, len, type) \ + pa_linux_insert_watchpoint (PIDGET(inferior_ptid), addr, len, type) +extern int pa_linux_insert_watchpoint (int pid, CORE_ADDR addr, + int len, int rw); + +#define target_remove_watchpoint(addr, len, type) \ + pa_linux_remove_watchpoint (PIDGET(inferior_ptid), addr, len) +extern int pa_linux_remove_watchpoint (int pid, CORE_ADDR addr, int len); + +#endif diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/pa-hpux.mh gdb-5.2.cvs20020818/gdb/config/pa/pa-hpux.mh --- gdb-5.2.cvs20020818.orig/gdb/config/pa/pa-hpux.mh 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/pa-hpux.mh 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,9 @@ +# Host: Hewlett-Packard PA-RISC machine, running HPUX + +XM_FILE= xm-hppah.h +XDEPFILES= + +NAT_FILE= nm-hppah.h +NATDEPFILES= hppah-nat.o corelow.o core-aout.o inftarg.o fork-child.o somread.o infptrace.o hp-psymtab-read.o hp-symtab-read.o somsolib.o + +HOST_IPC=-DBSD_IPC -DPOSIX_WAIT diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/pa-hpux.mt gdb-5.2.cvs20020818/gdb/config/pa/pa-hpux.mt --- gdb-5.2.cvs20020818.orig/gdb/config/pa/pa-hpux.mt 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/pa-hpux.mt 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,3 @@ +# Target: HP PA-RISC running hpux +TDEPFILES= pa-tdep.o pa-hpux-tdep.o +TM_FILE= tm-hpux.h diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/pa-linux.mh gdb-5.2.cvs20020818/gdb/config/pa/pa-linux.mh --- gdb-5.2.cvs20020818.orig/gdb/config/pa/pa-linux.mh 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/pa-linux.mh 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,10 @@ +# Host: Hewlett-Packard PA-RISC machine, running Linux +XDEPFILES= +XM_FILE= xm-linux.h +NAT_FILE= nm-linux.h +NATDEPFILES= pa-linux-nat.o corelow.o core-aout.o core-regset.o linux-proc.o \ + fork-child.o infptrace.o inftarg.o lin-lwp.o proc-service.o thread-db.o gcore.o + +GDBSERVER_DEPFILES= low-hppalinux.o + +XM_CLIBS= -ldl diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/pa-linux.mt gdb-5.2.cvs20020818/gdb/config/pa/pa-linux.mt --- gdb-5.2.cvs20020818.orig/gdb/config/pa/pa-linux.mt 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/pa-linux.mt 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,5 @@ +# Target: HP PA-RISC running linux +TDEPFILES= pa-tdep.o pa-linux-tdep.o solib.o solib-svr4.o solib-legacy.o +TM_FILE= tm-linux.h + +GDBSERVER_DEPFILES= low-linux.o diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-hpux.h gdb-5.2.cvs20020818/gdb/config/pa/tm-hpux.h --- gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-hpux.h 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/tm-hpux.h 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,41 @@ +/* Definitions to target GDB to HPUX on HPPA. + Copyright 1992, 1993, 1994, 1995, 1998, 1999, 2000, 2001 + 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 PA_TM_HPUX_H +#define PA_TM_HPUX_H + +#include "pa/tm-pa.h" +#include "pa/tm-pa32.h" +#include "somsolib.h" + +/* For HP-UX on PA-RISC we have an implementation + for the exception handling target op. */ +#define CHILD_ENABLE_EXCEPTION_CALLBACK +#define CHILD_GET_CURRENT_EXCEPTION_EVENT + +#ifndef TYPE_PROCEDURE +#define TYPE_PROCEDURE 3 +#endif + +struct gdbarch; +void pa_hpux_initialize_tdep (struct gdbarch *, int); + +#endif diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-hpux64.h gdb-5.2.cvs20020818/gdb/config/pa/tm-hpux64.h --- gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-hpux64.h 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/tm-hpux64.h 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,40 @@ +/* Definitions to target GDB to HPUX on HPPA. + Copyright 1999, 2000, 2001 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 PA_TM_HPUX64_H +#define PA_TM_HPUX64_H + +#include "pa/tm-pa.h" +#include "pa/tm-pa64.h" +#include "pa64solib.h" + +/* For HP-UX on PA-RISC we have an implementation + for the exception handling target op. */ +#define CHILD_ENABLE_EXCEPTION_CALLBACK +#define CHILD_GET_CURRENT_EXCEPTION_EVENT + +#ifndef TYPE_PROCEDURE +#define TYPE_PROCEDURE 3 +#endif + +struct gdbarch; +void pa_hpux_initialize_tdep (struct gdbarch *, int); + +#endif diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-linux.h gdb-5.2.cvs20020818/gdb/config/pa/tm-linux.h --- gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-linux.h 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/tm-linux.h 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,33 @@ +/* Definitions to target GDB to GNU/Linux on HPPA Linux. + Copyright 2000, 2001 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 PA_TM_LINUX_H +#define PA_TM_LINUX_H + +#include "tm-linux.h" +#include "pa/tm-pa.h" +#include "pa/tm-pa32.h" + +#define PA_LINUX_TARGET + +struct gdbarch; +void pa_linux_initialize_tdep (struct gdbarch *, int); + +#endif diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-pa.h gdb-5.2.cvs20020818/gdb/config/pa/tm-pa.h --- gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-pa.h 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/tm-pa.h 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,466 @@ +/* Definitions to target GDB to any Hewlett-Packard PA-RISC machine. + Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, + 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + + Contributed by the Center for Software Science at the + University of Utah (pa-gdb-bugs@cs.utah.edu). + + 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 PA_TM_PA_H +#define PA_TM_PA_H + + +#if !defined(GDBSERVER) + +#define GDB_MULTI_ARCH 1 + +#else /* Defines needed for GDBSERVER. */ + +/* Target system byte order. */ + +#define TARGET_BYTE_ORDER BIG_ENDIAN + +/* Some pseudo register numbers. */ + +#define PC_REGNUM PA_PCOQ_HEAD_REGNUM +#define NPC_REGNUM PA_PCOQ_TAIL_REGNUM +#define SP_REGNUM PA_GR31_REGNUM +#define FP_REGNUM PA_GR3_REGNUM + +#endif + +/* Number of machine registers. Well, sort of. It really just + specifies the range of register numbers known to gdb. */ + +#define NUM_REGS 128 + +/* Register numbers of various registers, */ + +/* General registers. */ +#define PA_GR0_REGNUM 0 +#define PA_GR1_REGNUM (PA_GR0_REGNUM+1) +#define PA_GR2_REGNUM (PA_GR0_REGNUM+2) +#define PA_GR3_REGNUM (PA_GR0_REGNUM+3) +#define PA_GR4_REGNUM (PA_GR0_REGNUM+4) +#define PA_GR5_REGNUM (PA_GR0_REGNUM+5) +#define PA_GR6_REGNUM (PA_GR0_REGNUM+6) +#define PA_GR7_REGNUM (PA_GR0_REGNUM+7) +#define PA_GR8_REGNUM (PA_GR0_REGNUM+8) +#define PA_GR9_REGNUM (PA_GR0_REGNUM+9) +#define PA_GR10_REGNUM (PA_GR0_REGNUM+10) +#define PA_GR11_REGNUM (PA_GR0_REGNUM+11) +#define PA_GR12_REGNUM (PA_GR0_REGNUM+12) +#define PA_GR13_REGNUM (PA_GR0_REGNUM+13) +#define PA_GR14_REGNUM (PA_GR0_REGNUM+14) +#define PA_GR15_REGNUM (PA_GR0_REGNUM+15) +#define PA_GR16_REGNUM (PA_GR0_REGNUM+16) +#define PA_GR17_REGNUM (PA_GR0_REGNUM+17) +#define PA_GR18_REGNUM (PA_GR0_REGNUM+18) +#define PA_GR19_REGNUM (PA_GR0_REGNUM+19) +#define PA_GR20_REGNUM (PA_GR0_REGNUM+20) +#define PA_GR21_REGNUM (PA_GR0_REGNUM+21) +#define PA_GR22_REGNUM (PA_GR0_REGNUM+22) +#define PA_GR23_REGNUM (PA_GR0_REGNUM+23) +#define PA_GR24_REGNUM (PA_GR0_REGNUM+24) +#define PA_GR25_REGNUM (PA_GR0_REGNUM+25) +#define PA_GR26_REGNUM (PA_GR0_REGNUM+26) +#define PA_GR27_REGNUM (PA_GR0_REGNUM+27) +#define PA_GR28_REGNUM (PA_GR0_REGNUM+28) +#define PA_GR29_REGNUM (PA_GR0_REGNUM+29) +#define PA_GR30_REGNUM (PA_GR0_REGNUM+30) +#define PA_GR31_REGNUM (PA_GR0_REGNUM+31) + +/* Control registers. The peculiar layout is to match HPUX interrupt save + state. */ +#define PA_CR11_REGNUM 32 +#define PA_PCOQ_HEAD_REGNUM 33 /* CR18 */ +#define PA_PCSQ_HEAD_REGNUM 34 /* CR17 */ +#define PA_PCOQ_TAIL_REGNUM 35 /* CR18 */ +#define PA_PCSQ_TAIL_REGNUM 36 /* CR17 */ +#define PA_CR15_REGNUM 37 +#define PA_CR19_REGNUM 38 +#define PA_CR20_REGNUM 39 +#define PA_CR21_REGNUM 40 +#define PA_CR22_REGNUM 41 +#define PA_CR31_REGNUM 42 + +/* Space registers. */ +#define PA_SR4_REGNUM 43 +#define PA_SR0_REGNUM 44 +#define PA_SR1_REGNUM 45 +#define PA_SR2_REGNUM 46 +#define PA_SR3_REGNUM 47 +#define PA_SR5_REGNUM 48 +#define PA_SR6_REGNUM 49 +#define PA_SR7_REGNUM 50 + +/* More control regs. */ +#define PA_CR0_REGNUM 51 +#define PA_CR8_REGNUM 52 +#define PA_CR9_REGNUM 53 +#define PA_CR10_REGNUM 54 +#define PA_CR12_REGNUM 55 +#define PA_CR13_REGNUM 56 +#define PA_CR24_REGNUM 57 +#define PA_CR25_REGNUM 58 +#define PA_CR26_REGNUM 59 +#define PA_CR27_REGNUM 60 +#define PA_CR28_REGNUM 61 +#define PA_CR29_REGNUM 62 +#define PA_CR30_REGNUM 63 + +/* Floating point registers. */ +#define PA_FR0_REGNUM 64 +#define PA_FR1_REGNUM (PA_FR0_REGNUM+2) +#define PA_FR2_REGNUM (PA_FR0_REGNUM+4) +#define PA_FR3_REGNUM (PA_FR0_REGNUM+6) +#define PA_FR4_REGNUM (PA_FR0_REGNUM+8) +#define PA_FR5_REGNUM (PA_FR0_REGNUM+10) +#define PA_FR6_REGNUM (PA_FR0_REGNUM+12) +#define PA_FR7_REGNUM (PA_FR0_REGNUM+14) +#define PA_FR31_REGNUM (PA_FR0_REGNUM+62) + +/* Some aliases. */ +#define PA_FLAGS_REGNUM PA_GR0_REGNUM +#define PA_SAR_REGNUM PA_CR11_REGNUM +#define PA_IPSW_REGNUM PA_CR22_REGNUM + +/* + * Processor Status Word Masks + */ + +#define PSW_T 0x01000000 /* Taken Branch Trap Enable */ +#define PSW_H 0x00800000 /* Higher-Privilege Transfer Trap Enable */ +#define PSW_L 0x00400000 /* Lower-Privilege Transfer Trap Enable */ +#define PSW_N 0x00200000 /* PC Queue Front Instruction Nullified */ +#define PSW_X 0x00100000 /* Data Memory Break Disable */ +#define PSW_B 0x00080000 /* Taken Branch in Previous Cycle */ +#define PSW_C 0x00040000 /* Code Address Translation Enable */ +#define PSW_V 0x00020000 /* Divide Step Correction */ +#define PSW_M 0x00010000 /* High-Priority Machine Check Disable */ +#define PSW_CB 0x0000ff00 /* Carry/Borrow Bits */ +#define PSW_R 0x00000010 /* Recovery Counter Enable */ +#define PSW_Q 0x00000008 /* Interruption State Collection Enable */ +#define PSW_P 0x00000004 /* Protection ID Validation Enable */ +#define PSW_D 0x00000002 /* Data Address Translation Enable */ +#define PSW_I 0x00000001 /* External, Power Failure, Low-Priority */ + /* Machine Check Interruption Enable */ + +/* By default assume we don't have to worry about software floating point. */ +#ifndef SOFT_FLOAT +#define SOFT_FLOAT 0 +#endif + +struct gdbarch_tdep + { + int os_ident; /* From the ELF header, one of the ELFOSABI_ + constants: ELFOSABI_LINUX, ELFOSABI_HPUX, + etc. */ + unsigned int is_elf:1; + unsigned int is_elf64:1; + int (*in_syscall) (const CORE_ADDR *); + int (*in_interrupt_handler) (CORE_ADDR); + int (*in_sigtramp) (CORE_ADDR, const char *); + CORE_ADDR (*frame_saved_pc_in_interrupt) (const struct frame_info *); + CORE_ADDR (*frame_base_before_interrupt) (const struct frame_info *); + void (*frame_find_saved_regs_in_interrupt) (struct frame_info *, + CORE_ADDR *); + CORE_ADDR (*frame_saved_pc_in_sigtramp) (const struct frame_info *); + CORE_ADDR (*frame_base_before_sigtramp) (struct frame_info *); + void (*frame_find_saved_regs_in_sigtramp) (struct frame_info *, + CORE_ADDR *); + }; + +#define PA_IN_SYSCALL(flags) \ + (gdbarch_tdep (current_gdbarch)->in_syscall (flags)) +#define PA_IN_INTERRUPT_HANDLER(pc) \ + (gdbarch_tdep (current_gdbarch)->in_interrupt_handler (pc)) +#define IN_SIGTRAMP(pc, func_name) \ + (gdbarch_tdep (current_gdbarch)->in_sigtramp (pc, func_name)) + + +/* + * Unwind table and descriptor. + */ + +struct unwind_table_entry + { + CORE_ADDR region_start; + CORE_ADDR region_end; + + unsigned int Cannot_unwind:1; /* 0 */ + unsigned int Millicode:1; /* 1 */ + unsigned int Millicode_save_sr0:1; /* 2 */ + unsigned int Region_description:2; /* 3..4 */ + unsigned int reserved1:1; /* 5 */ + unsigned int Entry_SR:1; /* 6 */ + unsigned int Entry_FR:4; /* number saved *//* 7..10 */ + unsigned int Entry_GR:5; /* number saved *//* 11..15 */ + unsigned int Args_stored:1; /* 16 */ + unsigned int Variable_Frame:1; /* 17 */ + unsigned int Separate_Package_Body:1; /* 18 */ + unsigned int Frame_Extension_Millicode:1; /* 19 */ + unsigned int Stack_Overflow_Check:1; /* 20 */ + unsigned int Two_Instruction_SP_Increment:1; /* 21 */ + unsigned int Ada_Region:1; /* 22 */ + unsigned int cxx_info:1; /* 23 */ + unsigned int cxx_try_catch:1; /* 24 */ + unsigned int sched_entry_seq:1; /* 25 */ + unsigned int reserved2:1; /* 26 */ + unsigned int Save_SP:1; /* 27 */ + unsigned int Save_RP:1; /* 28 */ + unsigned int Save_MRP_in_frame:1; /* 29 */ + unsigned int extn_ptr_defined:1; /* 30 */ + unsigned int Cleanup_defined:1; /* 31 */ + + unsigned int MPE_XL_interrupt_marker:1; /* 0 */ + unsigned int HP_UX_interrupt_marker:1; /* 1 */ + unsigned int Large_frame:1; /* 2 */ + unsigned int Pseudo_SP_Set:1; /* 3 */ + unsigned int reserved4:1; /* 4 */ + unsigned int Total_frame_size:27; /* 5..31 */ + + /* This is *NOT* part of an actual unwind_descriptor in an object + file. It is *ONLY* part of the "internalized" descriptors that + we create from those in a file. + */ + struct + { + unsigned int stub_type:4; /* 0..3 */ + unsigned int padding:28; /* 4..31 */ + } + stub_unwind; + }; + +/* HP linkers also generate unwinds for various linker-generated stubs. + GDB reads in the stubs from the $UNWIND_END$ subspace, then + "converts" them into normal unwind entries using some of the reserved + fields to store the stub type. */ + +struct stub_unwind_entry + { + /* The offset within the executable for the associated stub. */ + unsigned stub_offset; + + /* The type of stub this unwind entry describes. */ + char type; + + /* Unknown. Not needed by GDB at this time. */ + char prs_info; + + /* Length (in instructions) of the associated stub. */ + short stub_length; + }; + +/* Sizes (in bytes) of the native unwind entries. */ +#define UNWIND_ENTRY_SIZE 16 +#define STUB_UNWIND_ENTRY_SIZE 8 + +/* The gaps represent linker stubs used in MPE and space for future + expansion. */ +enum unwind_stub_types + { + LONG_BRANCH = 1, + PARAMETER_RELOCATION = 2, + EXPORT = 10, + IMPORT = 11, + IMPORT_SHLIB = 12, + }; + +struct unwind_table_entry *find_unwind_entry (CORE_ADDR); + +/* We use the objfile->obj_private pointer for two things: + + * 1. An unwind table; + * + * 2. A pointer to any associated shared library object. + * + * #defines are used to help refer to these objects. + */ + +/* Info about the unwind table associated with an object file. + + * This is hung off of the "objfile->obj_private" pointer, and + * is allocated in the objfile's psymbol obstack. This allows + * us to have unique unwind info for each executable and shared + * library that we are debugging. + */ +struct obj_unwind_info + { + struct unwind_table_entry *table; /* Pointer to unwind info */ + struct unwind_table_entry *cache; /* Pointer to last entry we found */ + int last; /* Index of last entry */ + }; + +enum dyncall_enum + { + sr4export = 0, dyncall, dyncall_external, last_dyncall_enum + }; + +typedef struct obj_private_struct + { + struct obj_unwind_info *unwind_info; + struct so_list *so_info; + CORE_ADDR dp; + CORE_ADDR dyn[last_dyncall_enum]; + } +obj_private_data_t; + +#define OBJ_PRIVATE_ALLOC pa_obj_private_alloc +struct objfile; +obj_private_data_t *pa_obj_private_alloc (struct objfile *); + + +/* Used to match stub code sequences. */ +struct stub_struc + { + unsigned int insn; + unsigned int mask; + int offset; + }; + +enum stub_type { + pa_stub_none, + pa_stub_long_branch, + pa_stub_long_branch_shared, + pa_stub_import, + pa_stub_import_shared, + pa_stub_import_multi, + pa_stub_import_multi_shared, + pa_stub_lazy_link, + pa_stub_export, + pa64_stub_import +}; + +enum stub_type is_pa_stub (CORE_ADDR, const struct stub_struc *, CORE_ADDR *); + +/* INIT_EXTRA_FRAME_INFO needs the PC. */ +#define INIT_FRAME_PC(FROMLEAF, PREV) /* nothing */ +#define INIT_FRAME_PC_FIRST(FROMLEAF, PREV) \ + (PREV)->pc = ((FROMLEAF) ? SAVED_PC_AFTER_CALL ((PREV)->next) \ + : (PREV)->next ? FRAME_SAVED_PC ((PREV)->next) \ + : PA_IN_SYSCALL (NULL) ? read_register (PA_GR31_REGNUM) & ~3 \ + : read_pc ()) + +struct frame_extra_info + { + CORE_ADDR sp_adjust_insn; + CORE_ADDR fp_adjust_insn; + CORE_ADDR rp_save_insn; + }; + + +/* If PC is in some function-call trampoline code, return the PC + where the function itself actually starts. If not, return NULL. */ + +#undef SKIP_TRAMPOLINE_CODE +#define SKIP_TRAMPOLINE_CODE(pc) pa_skip_trampoline_code (pc, NULL) +extern CORE_ADDR pa_skip_trampoline_code (CORE_ADDR, char *); + +/* Return non-zero if we are in an appropriate trampoline. */ +#undef IN_SOLIB_CALL_TRAMPOLINE +#define IN_SOLIB_CALL_TRAMPOLINE(pc, name) \ + pa_in_solib_call_trampoline (pc, name) +extern int pa_in_solib_call_trampoline (CORE_ADDR, char *); + +#define IN_SOLIB_RETURN_TRAMPOLINE(pc, name) \ + pa_in_solib_return_trampoline (pc, name) +extern int pa_in_solib_return_trampoline (CORE_ADDR, char *); + +/* elz: Return a large value, which is stored on the stack at addr. + This is defined only for the hppa, at this moment. + EXTRACT_STRUCT_VALUE_ADDRESS is not called anymore, because it assumes + that on exit from a called function which returns a large structure on + the stack, the address of the ret structure is still in register 28. + Unfortunately this register is usually overwritten by the called + function itself, on hppa. This is specified in the calling convention + doc. As far as I know, the only way to get the return value is to have + the caller tell us where it told the callee to put it, rather than have + the callee tell us. */ +#define VALUE_RETURNED_FROM_STACK(valtype,addr) \ + pa_value_returned_from_stack (valtype, addr) +extern struct value *pa_value_returned_from_stack (struct type *, CORE_ADDR); + +/* Sometimes we may pluck out a minimal symbol that has a negative + address. + + An example of this occurs when an a.out is linked against a foo.sl. + The foo.sl defines a global bar(), and the a.out declares a signature + for bar(). However, the a.out doesn't directly call bar(), but passes + its address in another call. + + If you have this scenario and attempt to "break bar" before running, + gdb will find a minimal symbol for bar() in the a.out. But that + symbol's address will be negative. What this appears to denote is + an index backwards from the base of the procedure linkage table (PLT) + into the data linkage table (DLT), the end of which is contiguous + with the start of the PLT. This is clearly not a valid address for + us to set a breakpoint on. + + Note that one must be careful in how one checks for a negative address. + 0xc0000000 is a legitimate address of something in a shared text + segment, for example. Since I don't know what the possible range + is of these "really, truly negative" addresses that come from the + minimal symbols, I'm resorting to the gross hack of checking the + top byte of the address for all 1's. Sigh. + */ +#define PC_REQUIRES_RUN_BEFORE_USE(pc) \ + (! target_has_stack && (pc & 0xFF000000)) + +/* When fetching register values from an inferior or a core file, + clean them up using this macro. BUF is a char pointer to + the raw value of the register in the registers[] array. */ + +#define CLEAN_UP_REGISTER_VALUE(regno, buf) \ + do { \ + if ((regno) == PA_PCOQ_HEAD_REGNUM || (regno) == PA_PCOQ_TAIL_REGNUM) \ + (buf)[sizeof(CORE_ADDR) -1] &= ~0x3; \ + } while (0) + +/* PA specific macro to see if the current instruction is nullified. */ +#ifndef INSTRUCTION_NULLIFIED +#define INSTRUCTION_NULLIFIED \ + (((int) read_register (PA_IPSW_REGNUM) & PSW_N) && ! PA_IN_SYSCALL (NULL)) +#endif + +/* The low two bits of the PC on the PA contain the privilege level. Some + genius implementing a (non-GCC) compiler apparently decided this means + that "addresses" in a text section therefore include a privilege level, + and thus symbol tables should contain these bits. This seems like a + bonehead thing to do--anyway, it seems to work for our purposes to just + ignore those bits. */ +#define SMASH_TEXT_ADDRESS(addr) ((addr) &= ~0x3) + +/* For a number of horrible reasons we may have to adjust the location + of variables on the stack. Ugh. */ +#define HPREAD_ADJUST_STACK_ADDRESS(ADDR) hpread_adjust_stack_address(ADDR) + +extern int hpread_adjust_stack_address (CORE_ADDR); + +/* Here's how to step off a permanent breakpoint. */ +#define SKIP_PERMANENT_BREAKPOINT pa_skip_permanent_breakpoint +extern void pa_skip_permanent_breakpoint (void); + +/* On HP-UX, certain system routines (millicode) have names beginning + with $ or $$, e.g. $$dyncall, which handles inter-space procedure + calls on PA-RISC. Tell the expression parser to check for those + when parsing tokens that begin with "$". */ +#define SYMBOLS_CAN_START_WITH_DOLLAR 1 + +#endif diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-pa32.h gdb-5.2.cvs20020818/gdb/config/pa/tm-pa32.h --- gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-pa32.h 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/tm-pa32.h 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,49 @@ +/* Definitions specific to 32 bit Hewlett-Packard PA-RISC machines. + Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, + 1998, 1999, 2000, 2001 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. */ + +#if defined(GDBSERVER) + +/* Say how long (ordinary) registers are. This is a piece of bogosity + used in push_word and a few other places; REGISTER_RAW_SIZE is the + real way to know how big a register is. */ + +#define REGISTER_SIZE 4 + +/* Total amount of space needed to store our copies of the machine's + register state, the array `registers'. */ +#define REGISTER_BYTES (NUM_REGS * 4) + +/* Number of bytes of storage in the actual machine representation + for register N. On the PA-RISC, all regs are 4 bytes, including + the FP registers (they're accessed as two 4 byte halves). */ + +#define REGISTER_RAW_SIZE(N) 4 + +/* Index within `registers' of the first byte of the space for + register N. */ + +#define REGISTER_BYTE(N) (N) * 4 + +/* Largest value REGISTER_RAW_SIZE can have. */ + +#define MAX_REGISTER_RAW_SIZE 4 + +#endif diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-pa64.h gdb-5.2.cvs20020818/gdb/config/pa/tm-pa64.h --- gdb-5.2.cvs20020818.orig/gdb/config/pa/tm-pa64.h 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/tm-pa64.h 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,52 @@ +/* Definitions specific to 32 bit Hewlett-Packard PA-RISC machines. + Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, + 1998, 1999, 2000, 2001 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. */ + +#if defined(GDBSERVER) + +/* Say how long (ordinary) registers are. This is a piece of bogosity + used in push_word and a few other places; REGISTER_RAW_SIZE is the + real way to know how big a register is. */ + +#define REGISTER_SIZE 8 + +/* Total amount of space needed to store our copies of the machine's + register state, the array `registers'. */ +#define REGISTER_BYTES (PA_FR0_REGNUM * 8 + (NUM_REGS - PA_FR0_REGNUM) * 4) + +/* Number of bytes of storage in the actual machine representation + for register N. On a 64 bit PA-RISC, all regs are 8 bytes, including + the FP registers (but the FP regs are stored as two 4 byte halves). */ + +#define REGISTER_RAW_SIZE(N) ((N) < PA_FR0_REGNUM ? 8 : 4) + +/* Index within `registers' of the first byte of the space for + register N. */ + +#define REGISTER_BYTE(N) \ + ((N) < PA_FR0_REGNUM \ + ? (N) * 8 \ + : ((N) - PA_FR0_REGNUM) * 4 + PA_FR0_REGNUM * 8) + +/* Largest value REGISTER_RAW_SIZE can have. */ + +#define MAX_REGISTER_RAW_SIZE 8 + +#endif diff -Npur gdb-5.2.cvs20020818.orig/gdb/config/pa/xm-linux.h gdb-5.2.cvs20020818/gdb/config/pa/xm-linux.h --- gdb-5.2.cvs20020818.orig/gdb/config/pa/xm-linux.h 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/config/pa/xm-linux.h 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,37 @@ +/* Native support for GNU/Linux, for GDB, the GNU debugger. + Copyright (C) 2000, 2001 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 PA_XM_LINUX_H +#define PA_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 + +#endif diff -Npur gdb-5.2.cvs20020818.orig/gdb/configure.host gdb-5.2.cvs20020818/gdb/configure.host --- gdb-5.2.cvs20020818.orig/gdb/configure.host 2002-08-19 10:37:29.000000000 -0400 +++ gdb-5.2.cvs20020818/gdb/configure.host 2002-08-19 10:39:17.000000000 -0400 @@ -49,6 +49,8 @@ hppa*64*-*-hpux11*) gdb_host=hpux11w ;; hppa*-*-hpux11*) gdb_host=hpux11 ;; hppa*-*-hpux*) gdb_host=hppahpux ;; hppa*-*-osf*) gdb_host=hppaosf ;; +hppa*64*-*-linux* | parisc*64*-*-linux*) gdb_host=pa64-linux ;; +hppa*-*-linux* | parisc*-*-linux*) gdb_host=pa-linux ;; i[3456]86-ncr-*) gdb_host=ncr3000 ;; i[3456]86-sequent-bsd*) gdb_host=symmetry ;; # dynix diff -Npur gdb-5.2.cvs20020818.orig/gdb/configure.tgt gdb-5.2.cvs20020818/gdb/configure.tgt --- gdb-5.2.cvs20020818.orig/gdb/configure.tgt 2002-08-19 10:37:29.000000000 -0400 +++ gdb-5.2.cvs20020818/gdb/configure.tgt 2002-08-19 10:39:17.000000000 -0400 @@ -83,6 +83,8 @@ hppa*64*-*-hpux11*) gdb_target=hppa64 ;; hppa*-*-hpux*) gdb_target=hppahpux ;; hppa*-*-hiux*) gdb_target=hppahpux ;; hppa*-*-osf*) gdb_target=hppaosf ;; +hppa*64*-*-linux* | parisc*64*-*-linux*) gdb_target=pa64-linux ;; +hppa*-*-linux* | parisc*-*-linux*) gdb_target=pa-linux ;; hppa*-*-*) gdb_target=hppa ;; i[3456]86-sequent-bsd*) gdb_target=symmetry ;; diff -Npur gdb-5.2.cvs20020818.orig/gdb/pa-linux-nat.c gdb-5.2.cvs20020818/gdb/pa-linux-nat.c --- gdb-5.2.cvs20020818.orig/gdb/pa-linux-nat.c 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/pa-linux-nat.c 2002-08-19 10:39:17.000000000 -0400 @@ -0,0 +1,354 @@ +/* Functions specific to running gdb native on HPPA running Linux. + Copyright 2000, 2001 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 "inferior.h" +#include "target.h" +#include "gdbcore.h" +#include "regcache.h" + +#include +#include +#include +#ifdef HAVE_SYS_REG_H +#include +#endif +#include + +#include +#include + +/* Prototypes for supply_gregset etc. */ +#include "gregset.h" + +/* These must match the order of the register names. + + Some sort of lookup table is needed because the offsets associated + with the registers are all over the board. */ + +static const int u_offsets[NUM_REGS] = + { + /* general registers */ + -1, + PT_GR1, + PT_GR2, + PT_GR3, + PT_GR4, + PT_GR5, + PT_GR6, + PT_GR7, + PT_GR8, + PT_GR9, + PT_GR10, + PT_GR11, + PT_GR12, + PT_GR13, + PT_GR14, + PT_GR15, + PT_GR16, + PT_GR17, + PT_GR18, + PT_GR19, + PT_GR20, + PT_GR21, + PT_GR22, + PT_GR23, + PT_GR24, + PT_GR25, + PT_GR26, + PT_GR27, + PT_GR28, + PT_GR29, + PT_GR30, + PT_GR31, + + PT_SAR, + PT_IAOQ0, + PT_IASQ0, + PT_IAOQ1, + PT_IASQ1, + -1, /* eiem */ + PT_IIR, + PT_ISR, + PT_IOR, + PT_PSW, + -1, /* goto */ + + PT_SR4, + PT_SR0, + PT_SR1, + PT_SR2, + PT_SR3, + PT_SR5, + PT_SR6, + PT_SR7, + + -1, /* cr0 */ + -1, /* pid0 */ + -1, /* pid1 */ + -1, /* ccr */ + -1, /* pid2 */ + -1, /* pid3 */ + -1, /* cr24 */ + -1, /* cr25 */ + -1, /* cr26 */ + PT_CR27, + -1, /* cr28 */ + -1, /* cr29 */ + -1, /* cr30 */ + + /* Floating point regs. */ + PT_FR0, PT_FR0 + 4, + PT_FR1, PT_FR1 + 4, + PT_FR2, PT_FR2 + 4, + PT_FR3, PT_FR3 + 4, + PT_FR4, PT_FR4 + 4, + PT_FR5, PT_FR5 + 4, + PT_FR6, PT_FR6 + 4, + PT_FR7, PT_FR7 + 4, + PT_FR8, PT_FR8 + 4, + PT_FR9, PT_FR9 + 4, + PT_FR10, PT_FR10 + 4, + PT_FR11, PT_FR11 + 4, + PT_FR12, PT_FR12 + 4, + PT_FR13, PT_FR13 + 4, + PT_FR14, PT_FR14 + 4, + PT_FR15, PT_FR15 + 4, + PT_FR16, PT_FR16 + 4, + PT_FR17, PT_FR17 + 4, + PT_FR18, PT_FR18 + 4, + PT_FR19, PT_FR19 + 4, + PT_FR20, PT_FR20 + 4, + PT_FR21, PT_FR21 + 4, + PT_FR22, PT_FR22 + 4, + PT_FR23, PT_FR23 + 4, + PT_FR24, PT_FR24 + 4, + PT_FR25, PT_FR25 + 4, + PT_FR26, PT_FR26 + 4, + PT_FR27, PT_FR27 + 4, + PT_FR28, PT_FR28 + 4, + PT_FR29, PT_FR29 + 4, + PT_FR30, PT_FR30 + 4, + PT_FR31, PT_FR31 + 4, + }; + +CORE_ADDR +register_addr (int regno, CORE_ADDR blockend) +{ + CORE_ADDR addr; + + if ((unsigned) regno >= NUM_REGS) + error ("Invalid register number %d.", regno); + + if (u_offsets[regno] == -1) + addr = 0; + else + { + addr = (CORE_ADDR) u_offsets[regno]; + /* If this is a 64 bit kernel, but we are debugging a 32 bit + task, then we want to pick up the low word of the register. */ + if (PT_GR2 - PT_GR1 == 8 && regno < PA_FR0_REGNUM) + addr += (PT_GR2 - PT_GR1) - REGISTER_RAW_SIZE (regno); + } + + return addr; +} + +int pa_cannot_fetch_register (regno) + int regno; +{ + return (unsigned int) regno >= NUM_REGS || u_offsets[regno] == -1; +} + +int pa_cannot_store_register (regno) + int regno; +{ + return ((unsigned int) regno >= NUM_REGS + || regno == PA_GR0_REGNUM + || regno == PA_PCSQ_HEAD_REGNUM + || (regno >= PA_PCSQ_TAIL_REGNUM && regno < PA_IPSW_REGNUM) + || (regno > PA_IPSW_REGNUM && regno < PA_FR4_REGNUM)); +} + +static const int greg_map[] = + { + PA_GR0_REGNUM, + PA_GR1_REGNUM, + PA_GR2_REGNUM, + PA_GR3_REGNUM, + PA_GR4_REGNUM, + PA_GR5_REGNUM, + PA_GR6_REGNUM, + PA_GR7_REGNUM, + PA_GR8_REGNUM, + PA_GR9_REGNUM, + PA_GR10_REGNUM, + PA_GR11_REGNUM, + PA_GR12_REGNUM, + PA_GR13_REGNUM, + PA_GR14_REGNUM, + PA_GR15_REGNUM, + PA_GR16_REGNUM, + PA_GR17_REGNUM, + PA_GR18_REGNUM, + PA_GR19_REGNUM, + PA_GR20_REGNUM, + PA_GR21_REGNUM, + PA_GR22_REGNUM, + PA_GR23_REGNUM, + PA_GR24_REGNUM, + PA_GR25_REGNUM, + PA_GR26_REGNUM, + PA_GR27_REGNUM, + PA_GR28_REGNUM, + PA_GR29_REGNUM, + PA_GR30_REGNUM, + PA_GR31_REGNUM, + PA_SR0_REGNUM, + PA_SR1_REGNUM, + PA_SR2_REGNUM, + PA_SR3_REGNUM, + PA_SR4_REGNUM, + PA_SR5_REGNUM, + PA_SR6_REGNUM, + PA_SR7_REGNUM, + PA_PCOQ_HEAD_REGNUM, + PA_PCOQ_TAIL_REGNUM, + PA_PCSQ_HEAD_REGNUM, + PA_PCSQ_TAIL_REGNUM, + PA_CR11_REGNUM, + PA_CR19_REGNUM, + PA_CR20_REGNUM, + PA_CR21_REGNUM, + PA_CR22_REGNUM, + PA_CR0_REGNUM, + PA_CR24_REGNUM, + PA_CR25_REGNUM, + PA_CR26_REGNUM, + PA_CR27_REGNUM, + PA_CR28_REGNUM, + PA_CR29_REGNUM, + PA_CR30_REGNUM, + PA_CR31_REGNUM, + PA_CR8_REGNUM, + PA_CR9_REGNUM, + PA_CR12_REGNUM, + PA_CR13_REGNUM, + PA_CR10_REGNUM, + PA_CR15_REGNUM + }; + +void +supply_gregset (gdb_gregset_t *gregsetp) +{ + int i; + greg_t *regp = (greg_t *) gregsetp; + + for (i = 0; i < sizeof (greg_map) / sizeof (greg_map[0]); i++, regp++) + { + int regno = greg_map[i]; + /* When running a 64 bit kernel, a greg_t may be larger than the + actual register, so just pick off the LS bits of big-endian word. */ + supply_register (regno, + ((char *) (regp + 1)) - REGISTER_RAW_SIZE (regno)); + } +} + +void +fill_gregset (gdb_gregset_t *gregsetp, int regno) +{ + int i; + greg_t *regp = (greg_t *) gregsetp; + + memset (gregsetp, 0, sizeof (*gregsetp)); + for (i = 0; i < sizeof (greg_map) / sizeof (greg_map[0]); i++, regp++) + { + int regi = greg_map[i]; + + if (regno == -1 || regi == regno) + { + int rawsize = REGISTER_RAW_SIZE (regi); + memcpy (((char *) (regp + 1)) - rawsize, + registers + REGISTER_BYTE (regi), + rawsize); + } + } +} + +/* Given a pointer to a floating point register set in /proc format + (fpregset_t *), unpack the register contents and supply them as gdb's + idea of the current floating point register values. */ + +void +supply_fpregset (gdb_fpregset_t *fpregsetp) +{ + register int regi; + char *from; + + for (regi = 0; regi <= 31; regi++) + { + from = (char *) &((*fpregsetp)[regi]); + supply_register (2*regi + PA_FR0_REGNUM, from); + supply_register (2*regi + PA_FR0_REGNUM + 1, from + 4); + } +} + +/* Given a pointer to a floating point register set in /proc format + (fpregset_t *), update the register specified by REGNO from gdb's idea + of the current floating point register set. If REGNO is -1, update + them all. */ + +void +fill_fpregset (gdb_fpregset_t *fpregsetp, int regno) +{ + if (regno == -1) + memcpy (fpregsetp, + ®isters[REGISTER_BYTE (PA_FR0_REGNUM)], + 32 * 2 * REGISTER_RAW_SIZE (PA_FR0_REGNUM)); + else + { + /* Gross. fpregset_t is double, registers[x] has single + precision reg. */ + char *from = (char *) ®isters[REGISTER_BYTE (regno)]; + char *to = (char *) &((*fpregsetp)[(regno - PA_FR0_REGNUM) / 2]); + if ((regno - PA_FR0_REGNUM) & 1) + to += 4; + memcpy (to, from, REGISTER_RAW_SIZE (regno)); + } +} + +int +pa_linux_insert_watchpoint (int pid, CORE_ADDR addr, int len, int rw) +{ + return -1; +} + +int +pa_linux_remove_watchpoint (int pid, CORE_ADDR addr, int len) +{ + return -1; +} + +CORE_ADDR +pa_linux_stopped_by_watchpoint (int pid) +{ + return 0; +} + diff -Npur gdb-5.2.cvs20020818.orig/gdb/pa-linux-tdep.c gdb-5.2.cvs20020818/gdb/pa-linux-tdep.c --- gdb-5.2.cvs20020818.orig/gdb/pa-linux-tdep.c 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/pa-linux-tdep.c 2002-08-19 10:45:56.000000000 -0400 @@ -0,0 +1,695 @@ +/* Functions specific to gdb targetted to HPPA running Linux. + Copyright 2000, 2001 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 "value.h" +#include "inferior.h" +#include "gdbcore.h" +#include "symfile.h" +#include "objfiles.h" +#include "arch-utils.h" +#include "regcache.h" +#include "tm.h" +#include "elf/common.h" + +static CORE_ADDR +pa_read_pc (ptid_t ptid) +{ + return (CORE_ADDR) read_register_pid (PA_PCOQ_HEAD_REGNUM, ptid) & ~3; +} + +static void +pa_write_pc (CORE_ADDR pc, ptid_t ptid) +{ + write_register_pid (PA_PCOQ_HEAD_REGNUM, pc, ptid); + write_register_pid (PA_PCOQ_TAIL_REGNUM, pc + 4, ptid); +} + +static CORE_ADDR +pa_read_fp (void) +{ + return read_register (PA_GR3_REGNUM); +} + +static CORE_ADDR +pa_read_sp (void) +{ + return read_register (PA_GR30_REGNUM); +} + +static void +pa_write_sp (CORE_ADDR val) +{ + write_register (PA_GR30_REGNUM, val); +} + +/* These functions deal with saving and restoring register state + around a function call in the inferior. They keep the stack + double-word aligned; eventually, on an hp700, the stack will have + to be aligned to a 64-byte boundary. */ + +static void +pa_push_dummy_frame (void) +{ + CORE_ADDR sp, pc, pcspace; + register int regnum; + char reg_buffer[8]; + LONGEST int_buffer; + int reg_size = REGISTER_SIZE; + + pc = TARGET_READ_PC (inferior_ptid); + pcspace = read_register (PA_PCSQ_HEAD_REGNUM); + + /* Space for "arguments"; the RP goes in here. */ + sp = read_register (PA_GR30_REGNUM) + 48; + read_register_gen (PA_GR2_REGNUM, reg_buffer); + + /* The 32bit and 64bit ABIs save the return pointer into different + stack slots. */ + if (reg_size == 8) + write_memory (sp - 16, reg_buffer, 8); + else + write_memory (sp - 20, reg_buffer, 4); + + int_buffer = TARGET_READ_FP (); + write_register (PA_GR3_REGNUM, sp); + + sp = push_word (sp, int_buffer); + sp += reg_size; + + for (regnum = PA_GR1_REGNUM; regnum <= PA_GR31_REGNUM; regnum++) + if (regnum != PA_GR2_REGNUM && regnum != PA_GR3_REGNUM) + { + read_register_gen (regnum, reg_buffer); + sp = push_bytes (sp, reg_buffer, reg_size); + } + + /* This is not necessary for the 64bit ABI. In fact it is dangerous. */ + if (reg_size != 8) + sp += reg_size; + + for (regnum = PA_FR0_REGNUM; regnum < NUM_REGS; regnum++) + { + read_register_gen (regnum, reg_buffer); + sp = push_bytes (sp, reg_buffer, 4); + } + read_register_gen (PA_IPSW_REGNUM, reg_buffer); + sp = push_bytes (sp, reg_buffer, reg_size); + read_register_gen (PA_SAR_REGNUM, reg_buffer); + sp = push_bytes (sp, reg_buffer, reg_size); + sp = push_word (sp, pc); + sp = push_word (sp, pcspace); + sp = push_word (sp, pc + 4); + sp = push_word (sp, pcspace); + write_register (PA_GR30_REGNUM, sp); +} + +/* Called to determine if PC is in an interrupt handler of some + kind. */ +static int +pa_linux_in_interrupt_handler (CORE_ADDR pc) +{ + /* gdb won't get control in a kernel ISR, so no need to worry here. */ + return 0; +} + +static CORE_ADDR +pa_linux_frame_saved_pc_in_interrupt (const struct frame_info *frame) +{ + return 0; +} + +static CORE_ADDR +pa_hpux_frame_base_before_interrupt (const struct frame_info *frame) +{ + /* return r30 from the interrupt save state. */ + return 0; +} + +static void +pa_linux_frame_find_saved_regs_in_interrupt (struct frame_info *frame, + CORE_ADDR *saved_regs) +{ +} + +#define LINUX_GATEWAY_ADDR 0x100 +#define END_LINUX_GATEWAY_ADDR 0x1000 + +/* Called to determine if the register state indicates we are in + a syscall. */ +static int +pa_linux_in_syscall (const CORE_ADDR *saved_regs) +{ + int flags; + CORE_ADDR pc; + + if (saved_regs && saved_regs[PA_IPSW_REGNUM]) + flags = read_memory_integer (saved_regs[PA_IPSW_REGNUM], + REGISTER_SIZE); + else + flags = read_register (PA_IPSW_REGNUM); + + if ((flags & PSW_C) == 0) + return 1; + + if (saved_regs && saved_regs[PA_PCOQ_HEAD_REGNUM]) + pc = read_memory_integer (saved_regs[PA_PCOQ_HEAD_REGNUM], + REGISTER_SIZE); + else + pc = read_register (PA_PCOQ_HEAD_REGNUM); + pc &= ~3; + + if (pc >= LINUX_GATEWAY_ADDR && pc < END_LINUX_GATEWAY_ADDR) + return 1; + + return 0; +} + +static const struct stub_struc pa_linux_sigtramp[] = + { + { 0x34190000, 0xfffffffd, 0 }, /* ldi x,%r25 ; x=!!in_syscall */ + { 0x3414015a, 0xffffffff, 4 }, /* ldi __NR_rt_sigreturn,%r20 */ + { 0xe4008200, 0xffffffff, 8 }, /* be,l 0x100(%sr2,%r0),%sr0,%r31 */ + { 0x08000240, 0xffffffff, 12}, /* nop */ + + { (unsigned) -1, 0, -999 } /* sentinel */ + }; + +int +pa_linux_in_sigtramp (CORE_ADDR pc, const char *name) +{ + if (is_pa_stub (pc, pa_linux_sigtramp, NULL)) + return 1; + + if (pc >= LINUX_GATEWAY_ADDR && pc < END_LINUX_GATEWAY_ADDR + && read_register (PA_GR20_REGNUM) == 0xad) /* __NR_rt_sigreturn */ + return 1; + + return 0; +} + +/* Where to find the start of the register save area in a signal frame. */ +#define PA_LINUX_SIGCONTEXT(REGSIZE) \ + (((4 * 4 /* tramp */ \ + + 128 /* struct siginfo */ \ + + ((2 /* struct ucontext.uc_flags,uc_link */ \ + + 3) /* struct ucontext.uc_stack */ \ + * (REGSIZE))) + 7) & -8) + +#define PA_LINUX_SIGCONTEXT_GR(REGSIZE) \ + (PA_LINUX_SIGCONTEXT (REGSIZE) + (REGSIZE)) + +#define PA_LINUX_SIGCONTEXT_FR(REGSIZE) \ + ((PA_LINUX_SIGCONTEXT_GR (REGSIZE) + 32 * (REGSIZE) + 7) & -8) + +#define PA_LINUX_SIGCONTEXT_PCSQ(REGSIZE) \ + (PA_LINUX_SIGCONTEXT_FR(REGSIZE) + 32 * 8) + +#define PA_LINUX_SIGCONTEXT_PCOQ(REGSIZE) \ + (PA_LINUX_SIGCONTEXT_PCSQ(REGSIZE) + 2 * (REGSIZE)) + +#define PA_LINUX_SIGCONTEXT_SAR(REGSIZE) \ + (PA_LINUX_SIGCONTEXT_PCOQ(REGSIZE) + 2 * (REGSIZE)) + +static CORE_ADDR +pa_linux_frame_saved_pc_in_sigtramp (const struct frame_info *frame) +{ + int regsize = REGISTER_SIZE; + CORE_ADDR addr; + + /* read pcoqh in sigcontext structure */ + addr = frame->frame + PA_LINUX_SIGCONTEXT_PCOQ (regsize); + return read_memory_integer (addr, regsize) & ~3; +} + +static CORE_ADDR +pa_linux_frame_base_before_sigtramp (struct frame_info *frame) +{ + CORE_ADDR start_pc; + + if (is_pa_stub (frame->pc, pa_linux_sigtramp, &start_pc)) + { + /* Fudge alert: fix up frame->frame here as pa_frame_chain gets + it wrong. The problem being that linux doesn't set r3 on + entry to signal handlers, and find_proc_framesize returns -1 + for the signal handler trampoline as there is no unwind info. + It would be possible to make a special find_proc_framesize. + For now, this seems to work. */ + frame->frame = start_pc; + +#if 0 + { + CORE_ADDR addr; + int regsize = REGISTER_SIZE; + + /* read r30 in sigcontext structure */ + addr = start_pc + PA_LINUX_SIGCONTEXT_GR (regsize) + 30 * regsize; + return read_memory_integer (addr, regsize); + } +#else + /* We may as well just return the start pc, as it's the same as + our saved r30 anyway. */ + return start_pc; +#endif + } + return 0; +} + +static void +pa_linux_frame_find_saved_regs_in_sigtramp (struct frame_info *frame, + CORE_ADDR *saved_regs) +{ + int i; + int regsize = REGISTER_SIZE; + CORE_ADDR addr; + + addr = frame->frame + PA_LINUX_SIGCONTEXT_GR (regsize); + + for (i = PA_GR0_REGNUM; i <= PA_GR31_REGNUM; i++) + { + if (i == PA_GR30_REGNUM) + saved_regs[i] = read_memory_integer (addr, regsize); + else + saved_regs[i] = addr; + addr += regsize; + } + addr = (addr + 7) & -8; + for (i = PA_FR0_REGNUM; i < NUM_REGS; i++) + { + saved_regs[i] = addr; + addr += 4; + } + saved_regs[PA_PCSQ_HEAD_REGNUM] = addr; addr += regsize; + saved_regs[PA_PCSQ_TAIL_REGNUM] = addr; addr += regsize; + saved_regs[PA_PCOQ_HEAD_REGNUM] = addr; addr += regsize; + saved_regs[PA_PCOQ_TAIL_REGNUM] = addr; addr += regsize; + saved_regs[PA_SAR_REGNUM] = addr; +} + +/* Attempt to find (and return) the global pointer for the given file. + + This code searchs for the .dynamic section in OBJFILE. Once it finds + the addresses at which the .dynamic section lives in the child process, + it scans the Elf64_Dyn or Elf32_Dyn entries for a DT_PLTGOT tag. If it + finds one of these, the corresponding d_un.d_ptr value is the global + pointer. */ + +static CORE_ADDR +generic_elf_find_global_pointer (struct objfile *objfile) +{ + struct obj_section *osect; + + ALL_OBJFILE_OSECTIONS (objfile, osect) + if (strcmp (osect->the_bfd_section->name, ".dynamic") == 0) + { + CORE_ADDR addr; + int dtag_size = REGISTER_SIZE; + + addr = osect->addr; + while (addr < osect->endaddr) + { + int status; + LONGEST tag; + char buf[8]; + + status = target_read_memory (addr, buf, dtag_size); + if (status != 0) + break; + tag = extract_signed_integer (buf, dtag_size); + + if (tag == DT_PLTGOT) + { + CORE_ADDR global_pointer; + + status = target_read_memory (addr + dtag_size, buf, dtag_size); + if (status != 0) + break; + + global_pointer = extract_address (buf, dtag_size); + + /* The payoff... */ + return global_pointer; + } + + if (tag == DT_NULL) + break; + + addr += 2 * dtag_size; + } + break; + } + return 0; +} + +/* This the pa-linux64 call dummy + + Call stack frame has already been built by gdb. Since we could be + calling a varargs function, and we do not have the benefit of a stub to + put things in the right place, we load the first 8 word of arguments + into both the general and fp registers. + + fldd -64(0,%r29),%fr4 + fldd -56(0,%r29),%fr5 + fldd -48(0,%r29),%fr6 + fldd -40(0,%r29),%fr7 + fldd -32(0,%r29),%fr8 + fldd -24(0,%r29),%fr9 + fldd -16(0,%r29),%fr10 + fldd -8(0,%r29),%fr11 + ldd -64(%r29), %r26 + ldd -56(%r29), %r25 + ldd -48(%r29), %r24 + ldd -40(%r29), %r23 + ldd -32(%r29), %r22 + ldd -24(%r29), %r21 + ldd -16(%r29), %r20 + bve,l (%r1),%r2 + ldd -8(%r29), %r19 + mtsp %r21, %sr0 ; Code used when popping frame + ble 0(%sr0, %r22) + nop +*/ + +/* Call dummys are sized and written out in word sized hunks. So we have + to pack the instructions into words. */ + +static const LONGEST pa_linux64_dummy[] = + { + 0x53a43f8353a53f93LL, 0x53a63fa353a73fb3LL, + 0x53a83fc353a93fd3LL, 0x2fa1100a2fb1100bLL, + 0x53ba3f8153b93f91LL, 0x53b83fa153b73fb1LL, + 0x53b63fc153b53fd1LL, 0x0fa110d4e820f000LL, + 0x0fb110d300151820LL, 0xe6c0000008000240LL + }; + +/* Insert the specified number of args and function address + into a dummy call sequence, DUMMY, stored at PC. */ + +static CORE_ADDR +pa64_fix_call_dummy (char *dummy, CORE_ADDR pc, CORE_ADDR fun, int nargs, + struct value **args, struct type *type, int gcc_p) +{ + CORE_ADDR pcoqh, pcoqt; + struct target_waitstatus w; + char buf[8]; + int status; + struct objfile *objfile; + static const char stub[8] = + { + 0xe8, 0x20, 0xd0, 0x00, /* BVE (r1) */ + 0x08, 0x00, 0x02, 0x40 /* NOP */ + }; + + /* We can not modify the instruction address queues directly, so we start + up the inferior and execute a couple of instructions to set them so + that they point to the call dummy in the stack. */ + pcoqh = read_register (PA_PCOQ_HEAD_REGNUM); + pcoqt = read_register (PA_PCOQ_TAIL_REGNUM); + + if (target_read_memory (pcoqh, buf, 4) != 0) + error ("Couldn't modify instruction address queue\n"); + + if (target_read_memory (pcoqt, buf + 4, 4) != 0) + error ("Couldn't modify instruction address queue\n"); + + if (target_write_memory (pcoqh, stub, 4) != 0) + error ("Couldn't modify instruction address queue\n"); + + if (target_write_memory (pcoqt, stub + 4, 4) != 0) + { + target_write_memory (pcoqh, buf, 4); + error ("Couldn't modify instruction address queue\n"); + } + + write_register (PA_GR1_REGNUM, pc); + + /* Single step twice, the BVE instruction will set the instruction + address queue such that it points to the PC value written immediately + above (ie the call dummy). */ + resume (1, 0); + target_wait (inferior_ptid, &w); + resume (1, 0); + target_wait (inferior_ptid, &w); + + /* Restore the two instructions at the old PC locations. */ + target_write_memory (pcoqh, buf, 4); + target_write_memory (pcoqt, buf + 4, 4); + + /* The call dummy wants the ultimate destination address in + register %r1. */ + write_register (PA_GR1_REGNUM, fun); + + /* We need to see if this objfile has a different DP value than our + own (it could be a shared library for example). */ + ALL_OBJFILES (objfile) + { + struct obj_section *s; + obj_private_data_t *obj_private; + + /* See if FUN is in any section within this shared library. */ + for (s = objfile->sections; s < objfile->sections_end; s++) + if (s->addr <= fun && fun < s->endaddr) + break; + + if (s >= objfile->sections_end) + continue; + + obj_private = (obj_private_data_t *) objfile->obj_private; + + /* The DP value may be different for each objfile. But within an + objfile each function uses the same dp value. Thus we do not need + to grope around the opd section looking for dp values. */ + + if (obj_private->dp == 0) + { + obj_private->dp = generic_elf_find_global_pointer (objfile); + if (obj_private->dp == 0) + obj_private->dp = -1; + } + if (obj_private->dp != -1) + write_register (PA_GR27_REGNUM, obj_private->dp); + break; + } + return pc; +} + +/* This is the pa-linux32 call dummy. The function address is in r22. + + Call stack frame has already been built by gdb. Since we could be + calling a varargs function, and we do not have the benefit of a stub to + put things in the right place, we load the first 4 word of arguments + into both the general and fp registers. + + ldo -36(%sp), %r1 + ldw -36(%sp), %arg0 + ldw -40(%sp), %arg1 + ldw -44(%sp), %arg2 + ldw -48(%sp), %arg3 + fldws 0(%r1), %fr4 + fldds -4(%r1), %fr5 + fldws -8(%r1), %fr6 + fldds -12(%r1), %fr7 + ble 0(%sr3, %r22) + copy %r31, %r2 + mtsp %r21, %sr0 ; Code used when popping a dummy frame. + ble,n 0(%sr0, %r22) + nop */ + +static const LONGEST pa_linux32_dummy[] = + { + 0x37c13fb9, 0x4bda3fb9, 0x4bd93fb1, 0x4bd83fa9, + 0x4bd73fa1, 0x24201004, 0x2c391005, 0x24311006, + 0x2c291007, 0xe6c0c000, 0x081f0242, 0x00151820, + 0xe6c00002, 0x08000240 + }; + +/* Insert the specified number of args and function address + into a dummy call sequence, DUMMY, stored at PC. */ + +static CORE_ADDR +pa_fix_call_dummy (char *dummy, CORE_ADDR pc, CORE_ADDR fun, int nargs, + struct value **args, struct type *type, int gcc_p) +{ + CORE_ADDR pcoqh, pcoqt; + struct target_waitstatus w; + char buf[8]; + int status; + struct objfile *objfile; + static const char stub[8] = + { + 0xe4, 0x20, 0xc0, 0x00, /* ble 0(%sr3,%r1)) */ + 0x08, 0x00, 0x02, 0x40 /* nop */ + }; + + /* We can not modify the instruction address queues directly, so we start + up the inferior and execute a couple of instructions to set them so + that they point to the call dummy in the stack. */ + pcoqh = read_register (PA_PCOQ_HEAD_REGNUM); + pcoqt = read_register (PA_PCOQ_TAIL_REGNUM); + + if (target_read_memory (pcoqh, buf, 4) != 0) + error ("Couldn't modify instruction address queue\n"); + + if (target_read_memory (pcoqt, buf + 4, 4) != 0) + error ("Couldn't modify instruction address queue\n"); + + if (target_write_memory (pcoqh, stub, 4) != 0) + error ("Couldn't modify instruction address queue\n"); + + if (target_write_memory (pcoqt, stub + 4, 4) != 0) + { + target_write_memory (pcoqh, buf, 4); + error ("Couldn't modify instruction address queue\n"); + } + + write_register (PA_GR1_REGNUM, pc); + + /* Single step twice, the BLE instruction will set the instruction + address queue such that it points to the PC value written immediately + above (ie the call dummy). */ + resume (1, 0); + target_wait (inferior_ptid, &w); + resume (1, 0); + target_wait (inferior_ptid, &w); + + /* Restore the two instructions at the old PC locations. */ + target_write_memory (pcoqh, buf, 4); + target_write_memory (pcoqt, buf + 4, 4); + + /* The call dummy wants the ultimate destination address in + register %r22. */ + if (fun & 2) + { + /* It's a plabel. */ + int gp; + + fun &= ~3; + gp = read_memory_integer (fun + 4, 4); + write_register (PA_GR19_REGNUM, gp); + fun = read_memory_integer (fun, 4); + write_register (PA_GR22_REGNUM, fun); + } + else + { + write_register (PA_GR22_REGNUM, fun); + + /* We need to see if this objfile has a different DP value than our + own (it could be a shared library for example). */ + ALL_OBJFILES (objfile) + { + struct obj_section *s; + obj_private_data_t *obj_private; + + /* See if FUN is in any section within this shared library. */ + for (s = objfile->sections; s < objfile->sections_end; s++) + if (s->addr <= fun && fun < s->endaddr) + break; + + if (s >= objfile->sections_end) + continue; + + obj_private = (obj_private_data_t *) objfile->obj_private; + + /* The DP value may be different for each objfile. But within an + objfile each function uses the same dp value. */ + + if (obj_private->dp == 0) + { + obj_private->dp = generic_elf_find_global_pointer (objfile); + if (obj_private->dp == 0) + obj_private->dp = -1; + } + if (obj_private->dp != -1) + write_register (PA_GR19_REGNUM, obj_private->dp); + break; + } + } + return pc; +} + +#if defined (PA_XM_LINUX_H) && 0 +/* For native compiles, check that our defines match the kernel. */ +#include +#include +#endif + +void +pa_linux_initialize_tdep (struct gdbarch *gdbarch, int is_elf64) +{ +#if defined (PA_XM_LINUX_H) && 0 +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((char *) &((TYPE *)0)->MEMBER - (char *)0) +#endif + if (offsetof (struct rt_sigframe, uc.uc_mcontext) + != PA_LINUX_SIGCONTEXT (sizeof (unsigned long)) + || (offsetof (struct rt_sigframe, uc.uc_mcontext.sc_gr) + != PA_LINUX_SIGCONTEXT_GR (sizeof (unsigned long))) + || (offsetof (struct rt_sigframe, uc.uc_mcontext.sc_fr) + != PA_LINUX_SIGCONTEXT_FR (sizeof (unsigned long))) + || (offsetof (struct rt_sigframe, uc.uc_mcontext.sc_iasq) + != PA_LINUX_SIGCONTEXT_PCSQ (sizeof (unsigned long))) + || (offsetof (struct rt_sigframe, uc.uc_mcontext.sc_iaoq) + != PA_LINUX_SIGCONTEXT_PCOQ (sizeof (unsigned long))) + || (offsetof (struct rt_sigframe, uc.uc_mcontext.sc_sar) + != PA_LINUX_SIGCONTEXT_SAR (sizeof (unsigned long)))) + internal_error (__FILE__, __LINE__, + "kernel struct rt_sigframe has changed"); +#endif + + set_gdbarch_read_pc (gdbarch, pa_read_pc); + set_gdbarch_write_pc (gdbarch, pa_write_pc); + set_gdbarch_read_fp (gdbarch, pa_read_fp); + set_gdbarch_read_sp (gdbarch, pa_read_sp); + set_gdbarch_write_sp (gdbarch, pa_write_sp); + set_gdbarch_push_dummy_frame (gdbarch, pa_push_dummy_frame); + + gdbarch_tdep (gdbarch)->in_interrupt_handler = pa_linux_in_interrupt_handler; + gdbarch_tdep (gdbarch)->frame_saved_pc_in_interrupt + = pa_linux_frame_saved_pc_in_interrupt; + gdbarch_tdep (gdbarch)->in_syscall = pa_linux_in_syscall; + gdbarch_tdep (gdbarch)->in_sigtramp = pa_linux_in_sigtramp; + gdbarch_tdep (gdbarch)->frame_saved_pc_in_sigtramp + = pa_linux_frame_saved_pc_in_sigtramp; + gdbarch_tdep (gdbarch)->frame_base_before_sigtramp + = pa_linux_frame_base_before_sigtramp; + gdbarch_tdep (gdbarch)->frame_find_saved_regs_in_sigtramp + = pa_linux_frame_find_saved_regs_in_sigtramp; + + set_gdbarch_call_dummy_location (gdbarch, ON_STACK); + /* call_dummy_address only needed for AT_ENTRY_POINT call dummies. */ + set_gdbarch_call_dummy_start_offset (gdbarch, 0); + set_gdbarch_call_dummy_breakpoint_offset (gdbarch, is_elf64 ? 0x44 : 0x2c); + set_gdbarch_call_dummy_breakpoint_offset_p (gdbarch, 1); + set_gdbarch_call_dummy_length (gdbarch, is_elf64 ? 0x50 : 0x38); + set_gdbarch_pc_in_call_dummy (gdbarch, pc_in_call_dummy_on_stack); + set_gdbarch_call_dummy_p (gdbarch, 1); + set_gdbarch_call_dummy_words (gdbarch, (is_elf64 + ? pa_linux64_dummy + : pa_linux32_dummy)); + set_gdbarch_sizeof_call_dummy_words (gdbarch, (is_elf64 + ? sizeof (pa_linux64_dummy) + : sizeof (pa_linux32_dummy))); + /* call_dummy_stack_adjust unused. */ + set_gdbarch_call_dummy_stack_adjust_p (gdbarch, 0); + set_gdbarch_fix_call_dummy (gdbarch, (is_elf64 + ? pa64_fix_call_dummy + : pa_fix_call_dummy)); +} + diff -Npur gdb-5.2.cvs20020818.orig/gdb/pa-tdep.c gdb-5.2.cvs20020818/gdb/pa-tdep.c --- gdb-5.2.cvs20020818.orig/gdb/pa-tdep.c 1969-12-31 19:00:00.000000000 -0500 +++ gdb-5.2.cvs20020818/gdb/pa-tdep.c 2002-08-19 10:45:28.000000000 -0400 @@ -0,0 +1,3629 @@ +/* Target-dependent code for the HP PA architecture, for GDB. + Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, + 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + + Contributed by the Center for Software Science at the + University of Utah (pa-gdb-bugs@cs.utah.edu). + + 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 "value.h" + +/* For argument passing to the inferior */ +#include "symtab.h" + +#include "gdb_stat.h" +#include "gdb_wait.h" + +#include "gdbcore.h" +#include "gdbcmd.h" +#include "target.h" +#include "symfile.h" +#include "objfiles.h" +#include "arch-utils.h" +#include "floatformat.h" +#include "regcache.h" + +#include "elf-bfd.h" +#include "elf/common.h" +#include "tm.h" + +#if DEBUG +static int framedebug = 0; +static int grokdebug = 0; +#ifndef DEBUG_PA_FRAME +#define DEBUG_PA_FRAME framedebug +#endif +#ifndef DEBUG_PA_GROK +#define DEBUG_PA_GROK grokdebug +#endif +#else +#ifndef DEBUG_PA_FRAME +#define DEBUG_PA_FRAME 0 +#endif +#ifndef DEBUG_PA_GROK +#define DEBUG_PA_GROK 0 +#endif +#endif + +/* To support detection of the pseudo-initial frame that threads have. */ +#define THREAD_INITIAL_FRAME_SYMBOL "__pthread_exit" +#define THREAD_INITIAL_FRAME_SYM_LEN sizeof(THREAD_INITIAL_FRAME_SYMBOL) + +/* Array of register names. These should match register numbers + defined in tm-pa.h. The peculiar layout is to match HPUX, BSD and OSF + 32-bit interrupt frame register save state. */ +static const char *pa_register_names[NUM_REGS] = + { + "flags", "r1", "rp", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "dp", "ret0", "ret1", "sp", "r31", + + "sar", "pcoqh", "pcsqh", "pcoqt", "pcsqt", "eiem", "iir", "isr", + /* cr11 cr18 cr17 cr18 cr17 cr15 cr19 cr20 */ + + "ior", "ipsw", "cr31", "sr4", "sr0", "sr1", "sr2", "sr3", + /* cr21 cr22 thandler? */ + + "sr5", "sr6", "sr7", "rcnt", "ptid1", "ptid2", "ccr", "ptid3", + /* cr0 cr8 cr9 cr10 cr12 */ + + "ptid4", "cr24", "cr25", "cr26", "cr27", "cr28", "cr29", "cr30", + /* cr13 mpsfu_high mpsfu_low mpsfu_ovflo */ + + "fpsr", "fpe1", "fpe2", "fpe3", "fpe4", "fpe5", "fpe6", "fpe7", + "fr4", "fr4R", "fr5", "fr5R", "fr6", "fr6R", "fr7", "fr7R", + "fr8", "fr8R", "fr9", "fr9R", "fr10", "fr10R", "fr11", "fr11R", + "fr12", "fr12R", "fr13", "fr13R", "fr14", "fr14R", "fr15", "fr15R", + "fr16", "fr16R", "fr17", "fr17R", "fr18", "fr18R", "fr19", "fr19R", + "fr20", "fr20R", "fr21", "fr21R", "fr22", "fr22R", "fr23", "fr23R", + "fr24", "fr24R", "fr25", "fr25R", "fr26", "fr26R", "fr27", "fr27R", + "fr28", "fr28R", "fr29", "fr29R", "fr30", "fr30R", "fr31", "fr31R", + }; + + +static const char * +pa_register_name (int reg) +{ + return pa_register_names[reg]; +} + +int +pa_register_byte (int reg) +{ + return 4 * reg; +} + +int +pa64_register_byte (int reg) +{ + return reg < PA_FR0_REGNUM ? 8 * reg : 4 * PA_FR0_REGNUM + 4 * reg; +} + +int +pa_register_raw_size (int reg) +{ + return 4; +} + +int +pa64_register_raw_size (int reg) +{ + return reg < PA_FR0_REGNUM ? 8 : 4; +} + +struct type * +pa_register_virtual_type (int reg) +{ + if (reg >= PA_FR4_REGNUM) + return builtin_type_float; + else + return builtin_type_long; +} + +struct type * +pa64_register_virtual_type (int reg) +{ + if (reg < PA_FR0_REGNUM) + return builtin_type_unsigned_long_long; + else if (reg >= PA_FR4_REGNUM) + return builtin_type_float; + else + return builtin_type_long; +} + + +/* Routines to extract various sized constants out of hppa + instructions. */ + +static inline int +sign_extend (int x, int len) +{ + int signbit = (1 << (len - 1)); + int mask = (signbit << 1) - 1; + return ((x & mask) ^ signbit) - signbit; +} + +/* For many immediate values the sign bit is the low bit! */ + +static inline int +low_sign_extend (int x, int len) +{ + int mask = (1 << (len - 1)) - 1; + return ((x >> 1) & mask) - ((x & 1) << (len - 1)); +} + +/* This macro gets bit fields using HP's numbering (MSB = 0) */ +#ifndef GET_FIELD +#define GET_FIELD(X, FROM, TO) \ + ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1)) +#endif + +/* extract a 21 bit constant */ + +static int +extract_21 (int word) +{ + int val; + + val = GET_FIELD (word, 20 + 11, 20 + 11); + val <<= 11; + val |= GET_FIELD (word, 9 + 11, 19 + 11); + val <<= 2; + val |= GET_FIELD (word, 5 + 11, 6 + 11); + val <<= 5; + val |= GET_FIELD (word, 0 + 11, 4 + 11); + val <<= 2; + val |= GET_FIELD (word, 7 + 11, 8 + 11); + return sign_extend (val, 21) << 11; +} + +/* extract a 17 bit constant from branch instructions, returning the + 19 bit signed value. */ + +static int +extract_17 (unsigned word) +{ + return sign_extend (GET_FIELD (word, 19, 28) | + GET_FIELD (word, 29, 29) << 10 | + GET_FIELD (word, 11, 15) << 11 | + (word & 0x1) << 16, 17) << 2; +} + +/* extract a 14 bit immediate field */ + +static int +extract_14 (unsigned word) +{ + return low_sign_extend (word, 14); +} + +/* extract a 16 bit immediate field */ + +static int +extract_16a (unsigned word) +{ + int low = low_sign_extend (word & 0x3ff9, 14); + return low ^ (word & 0xc000); +} + +/* Compare the start address for two unwind entries returning 1 if + the first address is larger than the second, -1 if the second is + larger than the first, and zero if they are equal. */ + +static int +compare_unwind_entries (const void *arg1, const void *arg2) +{ + const struct unwind_table_entry *a = arg1; + const struct unwind_table_entry *b = arg2; + + if (a->region_start > b->region_start) + return 1; + else if (a->region_start < b->region_start) + return -1; + else + return 0; +} + +static void +record_text_segment_lowaddr (bfd *abfd, asection *section, PTR info) +{ + if ((section->flags & (SEC_ALLOC | SEC_LOAD | SEC_READONLY)) + == (SEC_ALLOC | SEC_LOAD | SEC_READONLY)) + { + bfd_vma value = section->vma - section->filepos; + CORE_ADDR *low_text_segment_address = (CORE_ADDR *) info; + + if (value < *low_text_segment_address) + *low_text_segment_address = value; + } +} + +static void +internalize_unwinds (struct objfile *objfile, struct unwind_table_entry *table, + asection *section, unsigned int entries, unsigned int size, + CORE_ADDR text_offset) +{ + /* We will read the unwind entries into temporary memory, then + fill in the actual unwind table. */ + if (size > 0) + { + unsigned long tmp; + unsigned i; + char *buf = alloca (size); + + /* If ELF, then unwinds are supposed to be segment relative + offsets instead of absolute addresses. + + Note that when loading a shared library (text_offset != 0) the + unwinds are already relative to the text_offset that will be + passed in. */ + if (text_offset == 0 && gdbarch_tdep (current_gdbarch)->is_elf) + { + CORE_ADDR low_text_segment_address; + + low_text_segment_address = -1; + bfd_map_over_sections (objfile->obfd, + record_text_segment_lowaddr, + (PTR) &low_text_segment_address); + + text_offset += low_text_segment_address; + } + + bfd_get_section_contents (objfile->obfd, section, buf, 0, size); + + /* Now internalize the information being careful to handle host/target + endian issues. */ + for (i = 0; i < entries; i++) + { + table[i].region_start = bfd_get_32 (objfile->obfd, (bfd_byte *) buf); + table[i].region_start += text_offset; + buf += 4; + table[i].region_end = bfd_get_32 (objfile->obfd, (bfd_byte *) buf); + table[i].region_end += text_offset; + buf += 4; + tmp = bfd_get_32 (objfile->obfd, (bfd_byte *) buf); + buf += 4; + table[i].Cannot_unwind = (tmp >> 31) & 0x1; + table[i].Millicode = (tmp >> 30) & 0x1; + table[i].Millicode_save_sr0 = (tmp >> 29) & 0x1; + table[i].Region_description = (tmp >> 27) & 0x3; + table[i].reserved1 = (tmp >> 26) & 0x1; + table[i].Entry_SR = (tmp >> 25) & 0x1; + table[i].Entry_FR = (tmp >> 21) & 0xf; + table[i].Entry_GR = (tmp >> 16) & 0x1f; + table[i].Args_stored = (tmp >> 15) & 0x1; + table[i].Variable_Frame = (tmp >> 14) & 0x1; + table[i].Separate_Package_Body = (tmp >> 13) & 0x1; + table[i].Frame_Extension_Millicode = (tmp >> 12) & 0x1; + table[i].Stack_Overflow_Check = (tmp >> 11) & 0x1; + table[i].Two_Instruction_SP_Increment = (tmp >> 10) & 0x1; + table[i].Ada_Region = (tmp >> 9) & 0x1; + table[i].cxx_info = (tmp >> 8) & 0x1; + table[i].cxx_try_catch = (tmp >> 7) & 0x1; + table[i].sched_entry_seq = (tmp >> 6) & 0x1; + table[i].reserved2 = (tmp >> 5) & 0x1; + table[i].Save_SP = (tmp >> 4) & 0x1; + table[i].Save_RP = (tmp >> 3) & 0x1; + table[i].Save_MRP_in_frame = (tmp >> 2) & 0x1; + table[i].extn_ptr_defined = (tmp >> 1) & 0x1; + table[i].Cleanup_defined = tmp & 0x1; + tmp = bfd_get_32 (objfile->obfd, (bfd_byte *) buf); + buf += 4; + table[i].MPE_XL_interrupt_marker = (tmp >> 31) & 0x1; + table[i].HP_UX_interrupt_marker = (tmp >> 30) & 0x1; + table[i].Large_frame = (tmp >> 29) & 0x1; + table[i].Pseudo_SP_Set = (tmp >> 28) & 0x1; + table[i].reserved4 = (tmp >> 27) & 0x1; + table[i].Total_frame_size = tmp & 0x7ffffff; + + /* Stub unwinds are handled elsewhere. */ + table[i].stub_unwind.stub_type = 0; + table[i].stub_unwind.padding = 0; + } + } +} + +obj_private_data_t * +pa_obj_private_alloc (struct objfile *objfile) +{ + obj_private_data_t *obj_private; + obj_private = (obj_private_data_t *) + obstack_alloc (&objfile->psymbol_obstack, sizeof (obj_private_data_t)); + + memset (obj_private, 0, sizeof (obj_private)); + obj_private->unwind_info = NULL; + obj_private->so_info = NULL; + objfile->obj_private = (PTR) obj_private; + return obj_private; +} + +/* Read in the backtrace information stored in the unwind section of + the object file. This info is used mainly by find_unwind_entry to find + out the stack frame size and frame pointer used by procedures. We put + everything on the psymbol obstack in the objfile so that it automatically + gets freed when the objfile is destroyed. */ + +static void +read_unwind_info (struct objfile *objfile) +{ + asection *unwind_sec, *stub_unwind_sec; + unsigned unwind_size, stub_unwind_size, total_size; + unsigned index, unwind_entries; + unsigned stub_entries, total_entries; + CORE_ADDR text_offset; + struct obj_unwind_info *ui; + obj_private_data_t *obj_private; + + text_offset = ANOFFSET (objfile->section_offsets, 0); + ui = (struct obj_unwind_info *) obstack_alloc (&objfile->psymbol_obstack, + sizeof (struct obj_unwind_info)); + + ui->table = NULL; + ui->cache = NULL; + ui->last = -1; + + /* For reasons unknown the HP PA64 tools generate multiple unwinder + sections in a single executable. So we just iterate over every + section in the BFD looking for unwinder sections intead of trying + to do a lookup with bfd_get_section_by_name. + + First determine the total size of the unwind tables so that we + can allocate memory in a nice big hunk. */ + total_entries = 0; + for (unwind_sec = objfile->obfd->sections; + unwind_sec; + unwind_sec = unwind_sec->next) + { + if (strcmp (unwind_sec->name, "$UNWIND_START$") == 0 + || strcmp (unwind_sec->name, ".PARISC.unwind") == 0) + { + unwind_size = bfd_section_size (objfile->obfd, unwind_sec); + unwind_entries = unwind_size / UNWIND_ENTRY_SIZE; + + total_entries += unwind_entries; + } + } + + /* Now compute the size of the stub unwinds. Note the ELF tools do not + use stub unwinds at the current time. */ + stub_unwind_size = 0; + stub_entries = 0; + stub_unwind_sec = bfd_get_section_by_name (objfile->obfd, "$UNWIND_END$"); + + if (stub_unwind_sec) + { + stub_unwind_size = bfd_section_size (objfile->obfd, stub_unwind_sec); + stub_entries = stub_unwind_size / STUB_UNWIND_ENTRY_SIZE; + } + + /* Compute total number of unwind entries and their total size. */ + total_entries += stub_entries; + total_size = total_entries * sizeof (struct unwind_table_entry); + + /* Allocate memory for the unwind table. */ + ui->table = (struct unwind_table_entry *) + obstack_alloc (&objfile->psymbol_obstack, total_size); + ui->last = total_entries - 1; + + /* Now read in each unwind section and internalize the standard unwind + entries. */ + index = 0; + for (unwind_sec = objfile->obfd->sections; + unwind_sec; + unwind_sec = unwind_sec->next) + { + if (strcmp (unwind_sec->name, "$UNWIND_START$") == 0 + || strcmp (unwind_sec->name, ".PARISC.unwind") == 0) + { + unwind_size = bfd_section_size (objfile->obfd, unwind_sec); + unwind_entries = unwind_size / UNWIND_ENTRY_SIZE; + + internalize_unwinds (objfile, &ui->table[index], unwind_sec, + unwind_entries, unwind_size, text_offset); + index += unwind_entries; + } + } + + /* Now read in and internalize the stub unwind entries. */ + if (stub_unwind_size > 0) + { + unsigned int i; + char *buf = alloca (stub_unwind_size); + + /* Read in the stub unwind entries. */ + bfd_get_section_contents (objfile->obfd, stub_unwind_sec, buf, + 0, stub_unwind_size); + + /* Now convert them into regular unwind entries. */ + for (i = 0; i < stub_entries; i++, index++) + { + /* Clear out the next unwind entry. */ + memset (&ui->table[index], 0, sizeof (struct unwind_table_entry)); + + /* Convert offset & size into region_start and region_end. + Stuff away the stub type into "reserved" fields. */ + ui->table[index].region_start = bfd_get_32 (objfile->obfd, + (bfd_byte *) buf); + ui->table[index].region_start += text_offset; + buf += 4; + ui->table[index].stub_unwind.stub_type = bfd_get_8 (objfile->obfd, + (bfd_byte *) buf); + buf += 2; + ui->table[index].region_end + = ui->table[index].region_start + 4 * + (bfd_get_16 (objfile->obfd, (bfd_byte *) buf) - 1); + buf += 2; + } + + } + + /* Unwind table needs to be kept sorted. */ + qsort (ui->table, total_entries, sizeof (struct unwind_table_entry), + compare_unwind_entries); + + /* Keep a pointer to the unwind information. */ + obj_private = (obj_private_data_t *) objfile->obj_private; + if (obj_private == NULL) + obj_private = (PTR) pa_obj_private_alloc (objfile); + + obj_private->unwind_info = ui; +} + +/* Lookup the unwind (stack backtrace) info for the given PC. We search all + of the objfiles seeking the unwind table entry for this PC. Each objfile + contains a sorted list of struct unwind_table_entry. Since we do a binary + search of the unwind tables, we depend upon them to be sorted. */ + +struct unwind_table_entry * +find_unwind_entry (CORE_ADDR pc) +{ + int first, middle, last; + struct objfile *objfile; + + /* A function at address 0? Not in HP-UX! Or linux, for that matter. */ + if (pc == (CORE_ADDR) 0) + return NULL; + + ALL_OBJFILES (objfile) + { + struct obj_unwind_info *ui; + ui = NULL; + if (objfile->obj_private) + ui = ((obj_private_data_t *) (objfile->obj_private))->unwind_info; + + if (!ui) + { + read_unwind_info (objfile); + if (objfile->obj_private == NULL) + error ("Internal error reading unwind information."); + ui = ((obj_private_data_t *) (objfile->obj_private))->unwind_info; + } + + /* First, check the cache */ + + if (ui->cache + && pc >= ui->cache->region_start + && pc <= ui->cache->region_end) + return ui->cache; + + /* Not in the cache, do a binary search */ + + first = 0; + last = ui->last; + + while (first <= last) + { + middle = (first + last) / 2; + if (pc >= ui->table[middle].region_start + && pc <= ui->table[middle].region_end) + { + ui->cache = &ui->table[middle]; + return &ui->table[middle]; + } + + if (pc < ui->table[middle].region_start) + last = middle - 1; + else + first = middle + 1; + } + } /* ALL_OBJFILES() */ + return NULL; +} + +static const struct stub_struc pa32_stubs[] = + { + /* Long branch stub. */ + { 0x20200000, 0xffe00000, 0 }, /* ldil LR'XXX,%r1 */ + { 0xe0202002, 0xffe0e002, 4 }, /* be,n RR'XXX(%sr4,%r1) */ + + /* PIC long branch stub. */ + { 0xe8200000, 0xffffffff, /* b,l .+8,%r1 */ + -pa_stub_long_branch }, /* type of previous stub */ + { 0x28200000, 0xffe00000, 4 }, /* addil LR'XXX,%r1,%r1 */ + { 0xe0202002, 0xffe0e002, 8 }, /* be,n RR'XXX(%sr4,%r1) */ + + /* Import stub. */ + { 0x2b600000, 0xffe00000, /* addil LR'XXX,%dp,%r1 */ + -pa_stub_long_branch_shared }, + { 0x48350000, 0xffffc000, 4 }, /* ldw RR'XXX(%sr0,%r1),%r21 */ + { 0xeaa0c000, 0xffffffff, 8 }, /* bv %r0(%r21) */ + { 0x48330000, 0xffffc000, 12 }, /* ldw RR'XXX+4(%sr0,%r1),%r19 */ + + /* Import stub for shared lib. */ + { 0x2a600000, 0xffe00000, /* addil LR'XXX,%r19,%r1 */ + -pa_stub_import }, + { 0x48350000, 0xffffc000, 4 }, /* ldw RR'XXX(%sr0,%r1),%r21 */ + { 0xeaa0c000, 0xffffffff, 8 }, /* bv %r0(%r21) */ + { 0x48330000, 0xffffc000, 12 }, /* ldw RR'XXX+4(%sr0,%r1),%r19 */ + + /* Multiple sub-space import stub. */ + { 0x2b600000, 0xffe00000, /* addil LR'XXX,%dp */ + -pa_stub_import_shared }, + { 0x48350000, 0xffffc000, 4 }, /* ldw RR'XXX(%r1),%r21 */ + { 0x48330000, 0xffffc000, 8 }, /* ldw RR'XXX+4(%r1),%r19 */ + { 0x02a010a1, 0xffffffff, 12 }, /* ldsid (%r21),%r1 */ + { 0x00011820, 0xffffffff, 16 }, /* mtsp %r1,%sr0 */ + { 0xe2a00000, 0xffffffff, 20 }, /* be 0(%sr0,%r21) */ + { 0x6bc23fd1, 0xffffffff, 24 }, /* stw %rp,-24(%sp) */ + + /* Multiple sub-space import stub for shared lib. */ + { 0x2a600000, 0xffe00000, /* addil LR'XXX,%r19 */ + -pa_stub_import_multi }, + { 0x48350000, 0xffffc000, 4 }, /* ldw RR'XXX(%r1),%r21 */ + { 0x48330000, 0xffffc000, 8 }, /* ldw RR'XXX+4(%r1),%r19 */ + { 0x02a010a1, 0xffffffff, 12 }, /* ldsid (%r21),%r1 */ + { 0x00011820, 0xffffffff, 16 }, /* mtsp %r1,%sr0 */ + { 0xe2a00000, 0xffffffff, 20 }, /* be 0(%sr0,%r21) */ + { 0x6bc23fd1, 0xffffffff, 24 }, /* stw %rp,-24(%sp) */ + + /* ELF32 .plt lazy linking stub. */ + { 0x0e801096, 0xffffffff, /* 1: ldw 0(%r20),%r22 */ + -pa_stub_import_multi_shared }, + { 0xeac0c000, 0xffffffff, 4 }, /* bv %r0(%r22) */ + { 0x0e881095, 0xffffffff, 8 }, /* ldw 4(%r20),%r21 */ + { 0xea9f1fdd, 0xffffffff, 12 }, /* b,l 1b,%r20 */ + { 0xd6801c1e, 0xffffffff, 16 }, /* depi 0,31,2,%r20 */ + +#define EXPORT_STUB_ENT 32 + /* Export stub. */ + { 0xe8400002, 0xffe0e002, /* bl,n X,%rp */ + -pa_stub_lazy_link }, + { 0x08000240, 0xffffffff, 4 }, /* nop */ + { 0x4bc23fd1, 0xffffffff, 8 }, /* ldw -24(%sp),%rp */ + { 0x004010a1, 0xffffffff, 12 }, /* ldsid (%rp),%r1 */ + { 0x00011820, 0xffffffff, 16 }, /* mtsp %r1,%sr0 */ + { 0xe0400002, 0xffffffff, 20 }, /* be,n 0(%sr0,%rp) */ + + { (unsigned) -1, 0, /* sentinel */ + -pa_stub_export } + }; + +static const struct stub_struc pa64_stubs[] = + { + { 0x53610000, 0xffffc00e, 0 }, /* ldd pltoff(%r27),%r1 */ + { 0xe820d000, 0xffffffff, 4 }, /* bve (%r1) */ + { 0x537b0000, 0xffffc00e, 8 }, /* ldd pltoff+8(%r27),%r27 */ + + { (unsigned) -1, 0, /* sentinel */ + -pa64_stub_import } + }; + +enum stub_type +is_pa_stub (CORE_ADDR pc, const struct stub_struc *srch, CORE_ADDR *start) +{ + unsigned int insn; + + insn = read_memory_integer (pc, 4); + if (DEBUG_PA_GROK) + printf ("is_pa_stub (%#lx, %p), insn=%#x = ", (long) pc, srch, insn); + + for (; srch->mask != 0; srch++) + { + if ((insn & srch->mask) == srch->insn) + { + /* Possible match, check the whole stub. */ + int i; + CORE_ADDR addr; + const struct stub_struc *stub; + + i = srch->offset; + if (i < 0) + i = 0; + stub = ((const struct stub_struc *) + ((const char *) srch - i * (sizeof (pa32_stubs[0]) / 4))); + addr = pc - i; + if (start) + *start = addr; + do + { + unsigned int x = read_memory_integer (addr, 4); + if ((x & stub->mask) != stub->insn) + goto no_match; + stub++; + addr += 4; + } + while (stub->offset > 0); + if (DEBUG_PA_GROK) + printf ("matches %d\n", -stub->offset); + return -stub->offset; + } + no_match: ; + } + if (DEBUG_PA_GROK) + printf ("no\n"); + return pa_stub_none; +} + +/* Called when no unwind descriptor was found for PC. Returns 1 if it + appears that PC is in a linker stub. */ + +static enum stub_type +pc_in_linker_stub (CORE_ADDR pc, CORE_ADDR *start) +{ + const struct stub_struc *srch = pa32_stubs; + + if (gdbarch_tdep (current_gdbarch)->is_elf64) + srch = pa64_stubs; + + return is_pa_stub (pc, srch, start); +} + +/* Called when no unwind descriptor was found for PC. Returns 1 if it + appears that PC is in a linker export stub. */ + +static int +pc_in_linker_call_stub (CORE_ADDR pc) +{ + if (gdbarch_tdep (current_gdbarch)->is_elf) + return 0; + return is_pa_stub (pc, pa32_stubs + EXPORT_STUB_ENT, NULL) != pa_stub_none; +} + +static int +find_return_regnum (CORE_ADDR pc) +{ + struct unwind_table_entry *u; + + u = find_unwind_entry (pc); + + if (u && u->Millicode) + return PA_GR31_REGNUM; + + return PA_GR2_REGNUM; +} + +/* Return size of frame, or -1 if we should use a frame pointer. */ +static int +find_proc_framesize (CORE_ADDR pc) +{ + struct unwind_table_entry *u; + int ret = -1; + + if (DEBUG_PA_FRAME) + printf ("find_proc_framesize (%#lx)\n", (long) pc); + + /* This may indicate a bug in our callers... */ + if (pc == (CORE_ADDR) 0) + return ret; + + u = find_unwind_entry (pc); + if (!u) + { + /* Linker stubs have a zero size frame. */ + if (pc_in_linker_stub (pc, NULL) != pa_stub_none) + ret = 0; + } + /* If Save_SP is set, and we're not in an interrupt or signal caller, + then we have a frame pointer. Use it. Otherwise find the size + from unwind info. */ + else if (!u->Save_SP + || PA_IN_INTERRUPT_HANDLER (pc) + || IN_SIGTRAMP (pc, SYMBOL_NAME (lookup_minimal_symbol_by_pc (pc)))) + { + ret = u->Total_frame_size << 3; + } + if (DEBUG_PA_FRAME) + printf (" unwind = %p, returning %#x\n", u, ret); + + return ret; +} + +/* Return offset from sp at which rp is saved, or 0 if not saved. */ +static int +rp_saved (CORE_ADDR pc) +{ + struct unwind_table_entry *u; + + /* A function at, and thus a return PC from, address 0? Not in HP-UX! */ + if (pc == (CORE_ADDR) 0) + return 0; + + u = find_unwind_entry (pc); + + if (!u) + { + if (pc_in_linker_call_stub (pc)) + /* This is the so-called RP'. */ + return -24; + else + return 0; + } + + if (u->Save_RP) + return (REGISTER_SIZE == 8 ? -16 : -20); + + switch (u->stub_unwind.stub_type) + { + case EXPORT: + case IMPORT: + return -24; + case PARAMETER_RELOCATION: + return -8; + } + return 0; +} + +/* Returns non-zero if the function invocation represented by FI does not + have a frame on the stack associated with it. */ +static int pa_frameless_function_invocation (struct frame_info *fi) +{ + int ret; + + if (pc_in_linker_stub (fi->pc, NULL) != pa_stub_none) + ret = 1; + else + { + struct unwind_table_entry *u; + + u = find_unwind_entry (fi->pc); + ret = (u != 0 && u->Total_frame_size == 0 + && u->stub_unwind.stub_type == 0); + } + + if (DEBUG_PA_FRAME) + printf ("frameless_func (%p) = %#x\n", fi, ret); + return ret; +} + +static CORE_ADDR +pa_saved_pc_after_call (struct frame_info *frame) +{ + int ret_regnum; + CORE_ADDR pc; + struct unwind_table_entry *u; + + if (DEBUG_PA_FRAME) + printf ("saved_pc_after_call (%p)\n", frame); + + ret_regnum = find_return_regnum (frame->pc); + pc = read_register (ret_regnum) & ~3; + + /* If PC is in a linker stub, then we need to dig the address + the stub will return to out of the stack. */ + u = find_unwind_entry (pc); + if (u && u->stub_unwind.stub_type != 0) + pc = FRAME_SAVED_PC (frame); + + return pc; +} + +static CORE_ADDR +pa_frame_saved_pc (struct frame_info *frame) +{ + CORE_ADDR pc = frame->pc; + struct unwind_table_entry *u; + CORE_ADDR old_pc = 0; + int spun_around_loop = 0; + int rp_offset = 0; + + if (DEBUG_PA_FRAME) + { + printf ("frame_saved_pc (%p)\n", frame); + printf (" frame->prev = %p, frame->next = %p\n", + frame->prev, frame->next); + printf (" frame->frame = %#lx, frame->pc = %#lx, frame->extra = %p\n", + (long) frame->frame, (long) frame->pc, frame->extra_info); + } + + if (PA_IN_INTERRUPT_HANDLER (pc)) + { + CORE_ADDR rp; + rp = gdbarch_tdep (current_gdbarch)->frame_saved_pc_in_interrupt (frame); + if (DEBUG_PA_FRAME) + printf (" in interrupt, returning %#lx\n", (long) rp & ~3); + return rp & ~3; + } + + /* Deal with signal handler caller frames too. */ + if (frame->signal_handler_caller + && gdbarch_tdep (current_gdbarch)->frame_saved_pc_in_sigtramp) + { + CORE_ADDR rp; + rp = gdbarch_tdep (current_gdbarch)->frame_saved_pc_in_sigtramp (frame); + if (DEBUG_PA_FRAME) + printf (" in signal handler, returning %#lx\n", (long) rp & ~3); + return rp & ~3; + } + + /* Are we in a call dummy? */ + if (frame->pc >= frame->frame + && frame->pc < (frame->frame + + CALL_DUMMY_LENGTH + /* Account for register saves. See + find_dummy_frame_regs and push_dummy_frame. */ + + ((32 + 6) * REGISTER_SIZE) + + (NUM_REGS - PA_FR0_REGNUM) * 4)) + { + CORE_ADDR rp; + rp = read_memory_integer (frame->frame - (REGISTER_SIZE == 8 ? 16 : 20), + REGISTER_SIZE); + if (DEBUG_PA_FRAME) + printf (" in dummy, returning %#lx\n", (long) rp & ~3); + return rp & ~3; + } + + if (FRAMELESS_FUNCTION_INVOCATION (frame)) + { + int ret_regnum; + + ret_regnum = find_return_regnum (pc); + + if (DEBUG_PA_FRAME > 1) + printf (" frameless, ret reg = %#x\n", ret_regnum); + + /* If the next frame is an interrupt frame or a signal + handler caller, then we need to look in the saved + register area to get the return pointer (the values + in the registers may not correspond to anything useful). */ + if (frame->next + && (frame->next->signal_handler_caller + || PA_IN_INTERRUPT_HANDLER (frame->next->pc))) + { + if (!frame->next->saved_regs) + FRAME_INIT_SAVED_REGS (frame->next); + if (PA_IN_SYSCALL (frame->next->saved_regs)) + { + pc = read_memory_integer (frame->next->saved_regs[PA_GR31_REGNUM], + REGISTER_SIZE) & ~3; + + /* Syscalls are really two frames. The syscall stub itself + with a return pointer in %rp and the kernel call with + a return pointer in %r31. We return the %rp variant + if %r31 is the same as frame->pc. */ + if (pc == frame->pc) + pc = read_memory_integer (frame->next->saved_regs[PA_GR2_REGNUM], + REGISTER_SIZE) & ~3; + } + else + pc = read_memory_integer (frame->next->saved_regs[PA_GR2_REGNUM], + REGISTER_SIZE) & ~3; + } + else + pc = read_register (ret_regnum) & ~3; + } + else + { + spun_around_loop = 0; + old_pc = pc; + + rp_offset = 0; + if (frame->next + || !frame->extra_info + || pc > frame->extra_info->rp_save_insn) + { + restart: + rp_offset = rp_saved (pc); + } + + if (DEBUG_PA_FRAME > 1) + printf (" framed, rp_offset = %#x\n", rp_offset); + + /* Similar to code in frameless function case. If the next + frame is a signal or interrupt handler, then dig the right + information out of the saved register info. */ + if (rp_offset == 0 + && frame->next + && (frame->next->signal_handler_caller + || PA_IN_INTERRUPT_HANDLER (frame->next->pc))) + { + if (!frame->next->saved_regs) + FRAME_INIT_SAVED_REGS (frame->next); + if (PA_IN_SYSCALL (frame->next->saved_regs)) + { + pc = read_memory_integer (frame->next->saved_regs[PA_GR31_REGNUM], + REGISTER_SIZE) & ~3; + + /* Syscalls are really two frames. The syscall stub + itself with a return pointer in %rp and the kernel call + with a return pointer in %r31. We return the %rp + variant if %r31 is the same as frame->pc. */ + if (pc == frame->pc) + pc = read_memory_integer (frame->next->saved_regs[PA_GR2_REGNUM], + REGISTER_SIZE) & ~3; + } + else + pc = read_memory_integer (frame->next->saved_regs[PA_GR2_REGNUM], + REGISTER_SIZE) & ~3; + } + else if (rp_offset == 0 && frame->next) + { + /* Can't find pc save position, but frame->next set implies + r2 has been trashed. I suppose we could dig r2 out of + saved regs, but this point here is typically reached on + _start, so it's reasonable to say we don't have a saved + pc. */ + pc = 0; + } + else if (rp_offset == 0) + { + old_pc = pc; + pc = read_register (PA_GR2_REGNUM) & ~3; + } + else + { + old_pc = pc; + pc = read_memory_integer (frame->frame + rp_offset, + REGISTER_SIZE) & ~3; + } + } + + /* If PC is inside a linker stub, then dig out the address the stub + will return to. + + Don't do this for long branch stubs. Why? For some unknown reason + _start is marked as a long branch stub in hpux10. */ + if (pc) + { + u = find_unwind_entry (pc); + if (u && u->stub_unwind.stub_type != 0 + && u->stub_unwind.stub_type != LONG_BRANCH) + { + unsigned int insn; + + /* If this is a dynamic executable, and we're in a signal + handler, then the call chain will eventually point us + into the stub for _sigreturn. Unlike most cases, we'll + be pointed to the branch to the real sigreturn rather + than the code after the real branch!. Else, try to dig + the address the stub will return to in the normal + fashion. */ + insn = read_memory_integer (pc, 4); + if (DEBUG_PA_FRAME > 1) + printf (" stub, insn = %#x\n", insn); + + if ((insn & 0xfc00e000) == 0xe8000000) + return (pc + extract_17 (insn) + 8) & ~3; + else + { + if (old_pc == pc) + spun_around_loop++; + + if (spun_around_loop > 1) + { + /* We're just about to go around the loop again with + no more hope of success. Die. */ + error ("Unable to find return pc for this frame"); + } + else + goto restart; + } + } + } + + if (DEBUG_PA_FRAME) + printf (" returning %#lx\n", (long) pc); + + return pc; +} + +/* For the given instruction (INST), return any adjustment it makes + to the stack pointer or zero for no adjustment. + + This only handles instructions commonly found in prologues. */ + +static int +prologue_inst_adjust_sp (unsigned int inst, unsigned int high21) +{ + /* The most common way to perform a stack adjustment ldo X(sp),sp */ + if ((inst & 0xffffc000) == 0x37de0000) + return extract_14 (inst); + + /* stwm X,D(sp) */ + if ((inst & 0xffe00000) == 0x6fc00000) + return extract_14 (inst); + + /* std,ma X,D(sp) */ + if ((inst & 0xffe00008) == 0x73c00008) + return ((inst & 0x1ff8) >> 1) - ((inst & 1) << 13); + + /* Second insn of "addil high21,%r30; ldo low11,(%r1),%r30" pair. */ + if ((inst & 0xffff0000) == 0x343e0000) + return high21 + extract_14 (inst); + + /* fstws as used by the HP compilers. */ + if ((inst & 0xffffffe0) == 0x2fd01220) + return low_sign_extend (inst >> 16, 5); + + /* No adjustment. */ + return 0; +} + +/* Return nonzero if INST is a branch of some kind, else return zero. */ + +static inline int +is_branch (unsigned int inst) +{ + /* Return true on opcodes + 0x20,0x21,0x22,0x23, 0x28,0x29,0x2a,0x2b + 0x30,0x31,0x32,0x33, 0x38,0x39,0x3a,0x3b. */ + return ((inst & (0x24 << 26)) == (0x20 << 26) + /* Or on opcodes 0x27, 0x2f. */ + || (inst & (0x37 << 26)) == (0x27 << 26)); +} + +/* Return the register number for a GR which is saved by INST or + zero if INST does not save a GR. */ + +static int +inst_saves_gr (unsigned int inst) +{ + if ( + /* Does it look like a non-indexed stb, sth, stw, stwm? + ie. opcodes 0x18, 0x19, 0x1a, 0x1b. */ + (inst & (0x3c << 26)) == (0x18 << 26) + + /* How about a non-indexed std? */ + || (inst & ((0x3f << 26) | 2)) == ((0x1c << 26) | 0) + + /* or a pa2 stwm? */ + || (inst & ((0x3f << 26) | 6)) == ((0x1f << 26) | 4) + + /* Or possibly an indexed stb, stw, sth or std? */ + || ((inst & ((0x3f << 26) | (1 << 12) | (3 << 8))) + == ((0x03 << 26) | (1 << 12) | (2 << 8))) + ) + return (inst >> 16) & 0x1f; + + return 0; +} + +/* Return the register number for a FR which is saved by INST or + zero if INST does not save a FR. We don't distinguish between left + and right float reg saves here. */ + +static int +inst_saves_fr (unsigned int inst) +{ + if ( + /* Is this a long displacement fstd? */ + (inst & ((0x3f << 26) | 2)) == ((0x1c << 26) | 2) + + /* or a long displacement fstw? */ + || (inst & ((0x3f << 26) | 4)) == ((0x1f << 26) | 0) + || (inst & (0x3f << 26)) == (0x1e << 26) + ) + return (inst >> 16) & 0x1f; + + /* How about a short or indexed fstd or fstw? */ + if ((inst & ((0x3d << 26) | (1 << 9))) == ((0x09 << 26) | (1 << 9))) + return inst & 0x1f; + + return 0; +} + +/* Run through the prologue instructions for the function given by PC, + storing information about register saves in FRAME_INFO. Return the pc + at the end of the prologue. */ +CORE_ADDR +pa_grok_prologue (CORE_ADDR pc, struct frame_info *frame_info) +{ + char buf[4]; + CORE_ADDR orig_pc = pc; + CORE_ADDR *fsr = 0; + unsigned long inst, stack_remaining, save_gr, save_fr, save_rp, save_sp; + unsigned long args_stored, status, i, restart_gr, restart_fr; + struct unwind_table_entry *u; + unsigned int r1_val, r1_base; + int final_iteration; + + if (frame_info) + fsr = frame_info->saved_regs; + + if (DEBUG_PA_GROK) + printf ("grok_prologue (%#lx, %p), fsr = %p, pc = %#lx\n", + (long) pc, frame_info, fsr, + (long) (frame_info ? frame_info->pc : 0)); + + u = find_unwind_entry (pc); + if (!u) + { + if (DEBUG_PA_GROK) + printf (" no unwind, returning %#lx\n", (long) pc); + return pc; + } + + /* If we are not at the beginning of a function, then return now. */ + if ((pc & ~3) != u->region_start) + { + if (DEBUG_PA_GROK) + printf (" not at region_start, returning %#lx\n", (long) pc); + return pc; + } + + restart_gr = 0; + restart_fr = 0; + +restart: + r1_val = 0; + r1_base = 0; + + /* This is how much of a frame adjustment we need to account for. */ + stack_remaining = u->Total_frame_size << 3; + + /* Magic register saves we want to know about. */ + save_rp = u->Save_RP; + save_sp = u->Save_SP; + + /* An indication that args may be stored into the stack. Unfortunately + the HPUX compilers tend to set this in cases where no args were + stored too!. Heh, and gcc tends to *not* set it when args *are* + stored. So for the case where we care about arg store, ie. when + looking for the end of the prologue, assume there are args + stored. */ + args_stored = u->Args_stored || frame_info == 0; + + /* Turn the Entry_GR field into a bitmask. */ + save_gr = 1 << (u->Entry_GR + 3); + if (u->Save_SP) + { + /* Frame pointer (GR3) gets saved into a special location. */ + save_gr -= 1 << 4; + if (u->Entry_GR == 0) + save_gr = 0; + } + else + { + save_gr -= 1 << 3; + } + save_gr &= ~restart_gr; + + /* Turn the Entry_FR field into a bitmask too. */ + save_fr = (1 << (u->Entry_FR + 12)) - (1 << 12); + save_fr &= ~restart_fr; + + /* The frame always represents the value of %sp at entry to the + current function (and is thus equivalent to the "saved" stack + pointer. */ + if (fsr) + fsr[PA_GR30_REGNUM] = frame_info->frame; + + if (DEBUG_PA_GROK > 1) + printf (" save_gr=%#lx, save_fr=%#lx, save_rp=%#lx, save_sp=%#lx, stack=%#lx, args=%#lx\n", + save_gr, save_fr, save_rp, save_sp, stack_remaining, args_stored); + + /* Loop until we find everything of interest or hit a branch. + + For unoptimized GCC code and for any HP CC code this will never ever + examine any user instructions. + + For optimized GCC code we're faced with problems. GCC will schedule + its prologue and make prologue instructions available for delay slot + filling. The end result is user code gets mixed in with the prologue + and a prologue instruction may be in the delay slot of the first branch + or call. + + Some unexpected things are expected with debugging optimized code, so + we allow this routine to walk past user instructions in optimized + GCC code. */ + + for (final_iteration = 0; + ((!fsr || pc <= frame_info->pc + 4) + && ((save_gr || save_fr || save_rp || save_sp + || stack_remaining > 0 || args_stored))); + pc += 4) + { + unsigned int gr_num, fr_num; + unsigned long old_stack_remaining, old_save_gr, old_save_fr; + unsigned long old_save_rp, old_save_sp, next_inst; + + /* Save copies of all the triggers so we can compare them later + (only for HPC). */ + old_save_gr = save_gr; + old_save_fr = save_fr; + old_save_rp = save_rp; + old_save_sp = save_sp; + old_stack_remaining = stack_remaining; + + status = target_read_memory (pc, buf, 4); + + /* Yow! */ + if (status != 0) + return pc; + + inst = extract_unsigned_integer (buf, 4); + + /* Note the interesting effects of this instruction. */ + /* Save r1 value from addil high21,%r30 or addil high21,%r3. */ + if ((inst & 0xffe00000) == ((0x0a << 26) | (30 << 21)) + || (inst & 0xffe00000) == ((0x0a << 26) | (3 << 21))) + { + r1_val = extract_21 (inst); + r1_base = (inst >> 21) & 0x1f; + continue; + } + /* ldo offset(%r30),%r1 or ldo offset(%r3),%r1 */ + else if ((inst & 0xffff0000) == ((0x0d << 26) | (30 << 21) | (1 << 16)) + || (inst & 0xffff0000) == ((0x0d << 26) | (3 << 21) | (1 << 16))) + { + r1_val = extract_14 (inst); + r1_base = (inst >> 21) & 0x1f; + continue; + } + /* ldo offset(%r1),%r1 */ + else if ((inst & 0xffff0000) == ((0x0d << 26) | (1 << 21) | (1 << 16))) + { + r1_val += extract_14 (inst); + continue; + } + else + { + int stack_adj = prologue_inst_adjust_sp (inst, r1_val); + + stack_remaining -= stack_adj; + if (stack_adj && frame_info && frame_info->extra_info) + { + frame_info->extra_info->sp_adjust_insn = pc; + if (DEBUG_PA_GROK > 1) + printf (" found sp_adjust_insn = %#lx\n", (long) pc); + } + /* copy %sp,%r3 */ + if (inst == 0x081e0243 && frame_info && frame_info->extra_info) + { + frame_info->extra_info->fp_adjust_insn = pc; + if (DEBUG_PA_GROK > 1) + printf (" found fp_adjust_insn = %#lx\n", (long) pc); + } + } + + /* There are limited ways to store the return pointer into the + stack. */ + if (inst == 0x6bc23fd9) /* stw rp,-0x14(sr0,sp) */ + { + save_rp = 0; + if (fsr) + fsr[PA_GR2_REGNUM] = frame_info->frame - 20; + if (frame_info && frame_info->extra_info) + frame_info->extra_info->rp_save_insn = pc; + if (DEBUG_PA_GROK > 1) + printf (" found rp_save_insn = %#lx\n", (long) pc); + continue; + } + else if (inst == 0x0fc212c1) /* std rp,-0x10(sr0,sp) */ + { + save_rp = 0; + if (fsr) + fsr[PA_GR2_REGNUM] = frame_info->frame - 16; + if (frame_info && frame_info->extra_info) + frame_info->extra_info->rp_save_insn = pc; + if (DEBUG_PA_GROK > 1) + printf (" found rp_save_insn = %#lx\n", (long) pc); + continue; + } + + /* Note if we saved SP into the stack. This also happens to indicate + the location of the saved frame pointer. At this time the HP + compilers never bother to save SP into the stack. */ + if ((inst & 0xffffc000) == 0x6fc10000 /* stw,ma r1,N(sr0,sp) */ + || (inst & 0xffffc00c) == 0x73c10008) /* std,ma r1,N(sr0,sp) */ + { + if (fsr) + fsr[PA_GR3_REGNUM] = frame_info->frame; + save_sp = 0; + continue; + } + + /* Are we loading some register with an offset from the argument + pointer? */ + if ((inst & 0xffe00000) == 0x37a00000 /* ldo offs(ret1),rn */ + || (inst & 0xffffffe0) == 0x081d0240 /* copy ret1,rn */) + continue; + + /* Account for general and floating-point register saves. */ + gr_num = inst_saves_gr (inst); + save_gr &= ~(1 << gr_num); + if (fsr + && (gr_num > 3 || (gr_num == 3 && !u->Save_SP)) + && gr_num < u->Entry_GR + 3) + { + /* stwm with a positive displacement is a post modify. */ + if ((inst & ((0x3f << 26) | 1)) == ((0x1b << 26) | 0)) + fsr[gr_num] = frame_info->frame; + /* A std has explicit post_modify forms. */ + else if ((inst & 0xfc0000c0) == ((0x1c << 26) | 8)) + fsr[gr_num] = frame_info->frame; + else + { + CORE_ADDR offset; + int base_reg; + + if ((inst & (0x3f << 26)) == 0x1c << 26) + offset = ((inst >> 1) & 0x1ff8) - ((inst & 1) << 13); + else if ((inst & (0x3f << 26)) == 0x03 << 26) + offset = low_sign_extend (inst, 5); + else + offset = extract_14 (inst); + + base_reg = (inst >> 21) & 0x1f; + + /* Handle code with and without frame pointers. */ + if (base_reg == 1) + offset += r1_val; + if ((base_reg == 1 && r1_base == 30) + || base_reg == 30) + offset += u->Total_frame_size << 3; + + fsr[gr_num] = frame_info->frame + offset; + } + } + + fr_num = inst_saves_fr (inst); + save_fr &= ~(1 << fr_num); + + /* GCC handles callee saved FP regs a little differently. + + It emits an instruction to put the value of the start of + the FP store area into %r1. It then uses fstds,ma with + a basereg of %r1 for the stores. + + HP CC emits them at the current stack pointer modifying + the stack pointer as it stores each register. */ + + if (fsr + && fr_num >= 12 && fr_num <= 21) + { + int base_reg = (inst >> 21) & 0x1f; + + if (base_reg == 30) + { + /* HP CC FP register store. */ + fsr[2 * fr_num + PA_FR0_REGNUM] + = (frame_info->frame + + (u->Total_frame_size << 3) - old_stack_remaining); + } + else if (base_reg == 1) + { + int offset = r1_val; + + if (r1_base == 30) + offset += u->Total_frame_size << 3; + + fsr[2 * fr_num + PA_FR0_REGNUM] + = frame_info->frame + offset; + + /* I suppose we should really check the offset in the + instruction, but it will always be eight. */ + r1_val += 8; + } + } + + /* Quit if we hit any kind of branch the previous iteration. */ + if (final_iteration) + { + pc -= 4; + break; + } + + /* We want to look precisely one instruction beyond the branch + if we have not found everything yet. */ + if (is_branch (inst)) + { + /* If the branch delay slot is nullified, don't look at it. */ + if ((inst & 2) != 0 + && inst >= (0x38 << 26) && inst < (0x3b << 26)) + break; + final_iteration = 1; + } + + /* Ugh. Also account for argument stores into the stack. + Unfortunately args_stored only tells us that some arguments + where stored into the stack. Not how many or what kind! + + This is a kludge as on the HP compiler sets this bit and it + never does prologue scheduling. So once we see one, skip past + all of them. */ + if (args_stored + && ((gr_num >= (REGISTER_SIZE == 8 ? 19 : 23) && gr_num <= 26) + || (fr_num >= 4 && fr_num <= (REGISTER_SIZE == 8 ? 11 : 7)))) + { + do + { + pc += 4; + status = target_read_memory (pc, buf, 4); + if (status != 0) + return pc; + inst = extract_unsigned_integer (buf, 4); + gr_num = inst_saves_gr (inst); + fr_num = inst_saves_fr (inst); + } + while ((gr_num >= (REGISTER_SIZE == 8 ? 19 : 23) && gr_num <= 26) + || (fr_num >= 4 && fr_num <= (REGISTER_SIZE == 8 ? 11 : 7)) + || (inst & 0xfc000000) == (0x0d << 26) /* ldo */); + + args_stored = 0; + pc -= 4; + continue; + } + + /* What a crock. The HP compilers set args_stored even if no + arguments were stored into the stack (boo hiss). This could + cause this code to then skip a bunch of user insns (up to the + first branch). + + To combat this we try to identify when args_stored was bogusly + set and clear it. We only do this when args_stored is nonzero, + all other resources are accounted for, and nothing changed on + this pass. */ + if (args_stored + && !(save_gr || save_fr || save_rp || save_sp || stack_remaining > 0) + && old_save_gr == save_gr && old_save_fr == save_fr + && old_save_rp == save_rp && old_save_sp == save_sp + && old_stack_remaining == stack_remaining) + { + if (DEBUG_PA_GROK > 1) + printf (" toodleoo"); + break; + } + } + + if (DEBUG_PA_GROK > 2 && fsr) + { + int i; + for (i = PA_GR0_REGNUM; i <= PA_GR31_REGNUM; i++) + printf (" [r%d]=%#lx", i - PA_GR0_REGNUM, fsr[i]); + for (i = PA_FR0_REGNUM; i <= PA_FR31_REGNUM; i += 2) + printf (" [fr%d]=%#lx,%#lx", (i-PA_FR0_REGNUM) / 2, fsr[i], fsr[i+1]); + printf ("\n"); + } + if (DEBUG_PA_GROK) + printf (" pc=%#lx\n", (long) pc); + + /* We've got a tenative location for the end of the prologue. However + because of limitations in the unwind descriptor mechanism we may + have gone too far into user code looking for the save of a register + that does not exist. So, if there were registers we expected to be + saved but they never were, mask them out and restart. + + This should only happen in optimized code, and should be very rare. */ + if (frame_info == NULL + && (save_gr || save_fr) && !(restart_fr || restart_gr)) + { + pc = orig_pc; + restart_gr = save_gr; + restart_fr = save_fr; + goto restart; + } + + return pc; +} + +static void +pa_init_extra_frame_info (int fromleaf, struct frame_info *frame) +{ + int framesize; + + if (DEBUG_PA_FRAME) + { + printf ("init_extra_frame_info (%d, %p)\n", fromleaf, frame); + printf (" frame->prev = %p, frame->next = %p\n", + frame->prev, frame->next); + printf (" frame->frame = %#lx, frame->pc = %#lx\n", + (long) frame->frame, (long) frame->pc); + } + + frame->extra_info = frame_obstack_alloc (sizeof (*frame->extra_info)); + memset (frame->extra_info, 0, sizeof (*frame->extra_info)); + + /* If the next frame represents a frameless function invocation + then we have to do some adjustments that are normally done by + FRAME_CHAIN. (FRAME_CHAIN is not called in this case.) + + One might think frameless innermost frames should have + a frame->frame that is the same as the parent's frame->frame. + That is wrong; frame->frame in that case should be the *high* + address of the parent's frame. It's complicated as hell to + explain, but the parent *always* creates some stack space for + the child. So the child actually does have a frame of some + sorts, and its base is the high address in its parent's frame. */ + if (fromleaf) + { + framesize = find_proc_framesize (FRAME_SAVED_PC (frame->next)); + + /* Now adjust our base frame accordingly. If we have a frame pointer + use it, else subtract the size of this frame from the current + frame. (we always want frame->frame to point at the lowest address + in the frame). */ + if (framesize == -1) + frame->frame = TARGET_READ_FP (); + else + frame->frame -= framesize; + + if (DEBUG_PA_FRAME) + printf (" framesize = %#x, frame->frame = %#lx\n", + framesize, (long) frame->frame); + return; + } + + /* Adjustments for innermost frames. */ + if (!frame->next) + { + if (pc_in_linker_stub (frame->pc, NULL)) + { + /* stubs always have frame->frame == sp. */ + framesize = 0; + } + else + { + CORE_ADDR start_pc; + + /* gcc schedules the function prologue when optimizing, + which can lead to code from the function body moving + before the stack adjustment. That means we may have a + breakpoint set before the stack has been adjusted. In + any case, we can stepi anywhere. Thus, for innermost + frames we need to rummage through the prologue to see + exactly where we are. For other frames in the call + chain, we don't need to worry as no function will be + called before the prologue has fully executed. */ + start_pc = get_pc_function_start (frame->pc); + pa_grok_prologue (start_pc, frame); + framesize = find_proc_framesize (frame->pc); + } + + if (DEBUG_PA_FRAME> 1) + printf (" sp_adjust_insn = %#lx, fp_adjust_insn = %#lx, rp_save_insn = %#lx\n", + (long) frame->extra_info->sp_adjust_insn, + (long) frame->extra_info->fp_adjust_insn, + (long) frame->extra_info->rp_save_insn); + + if (framesize == -1) + { + if (frame->pc > frame->extra_info->fp_adjust_insn) + frame->frame = TARGET_READ_FP (); + else + frame->frame = read_register (PA_GR30_REGNUM); + } + else + { + frame->frame = read_register (PA_GR30_REGNUM); + if (frame->pc > frame->extra_info->sp_adjust_insn) + frame->frame -= framesize; + } + + if (DEBUG_PA_FRAME) + printf (" framesize = %#x, frame->frame = %#lx\n", + framesize, + (long) frame->frame); + } +} + +/* We always have some sort of frame. See above. */ +static int +pa_prologue_frameless_p (CORE_ADDR ip) +{ + return 0; +} + +/* Given a GDB frame, determine the address of the calling function's + frame. This will be used to create a new GDB frame struct, and + then INIT_FRAME_PC_FIRST and INIT_EXTRA_FRAME_INFO will be called + for the new frame. + + This may involve searching through prologues for several functions + at boundaries where GCC calls HP C code, or where code which has + a frame pointer calls code without a frame pointer. + + In the case of the PA-RISC, the frame's nominal address is the address + of a 4-byte word containing the calling frame's address (previous FP). */ + +static CORE_ADDR +pa_frame_chain (struct frame_info *frame) +{ + int my_framesize, caller_framesize; + struct unwind_table_entry *u; + CORE_ADDR frame_base; + struct frame_info *tmp_frame; + CORE_ADDR caller_pc; + struct minimal_symbol *min_frame_symbol; + struct symbol *frame_symbol; + char *frame_symbol_name; + CORE_ADDR ret; + + if (DEBUG_PA_FRAME) + { + printf ("frame_chain (%p)\n", frame); + printf (" frame->frame = %#lx, frame->pc = %#lx\n", + (long) frame->frame, (long) frame->pc); + } + + /* If this is a threaded application, and we see the + routine "__pthread_exit", treat it as the stack root + for this thread. */ + min_frame_symbol = lookup_minimal_symbol_by_pc (frame->pc); + frame_symbol = find_pc_function (frame->pc); + + if ((min_frame_symbol != 0) /* && (frame_symbol == 0) */ ) + { + /* The test above for "no user function name" would defend + against the slim likelihood that a user might define a + routine named "__pthread_exit" and then try to debug it. + + If it weren't commented out, and you tried to debug the + pthread library itself, you'd get errors. + + So for today, we don't make that check. */ + frame_symbol_name = SYMBOL_NAME (min_frame_symbol); + if (frame_symbol_name != 0) + { + if (0 == strncmp (frame_symbol_name, + THREAD_INITIAL_FRAME_SYMBOL, + THREAD_INITIAL_FRAME_SYM_LEN)) + { + /* Pretend we've reached the bottom of the stack. */ + return (CORE_ADDR) 0; + } + } + } /* End of hacky code for threads. */ + + if (PA_IN_INTERRUPT_HANDLER (frame->pc)) + frame_base = gdbarch_tdep (current_gdbarch)->frame_base_before_interrupt (frame); + else if (frame->signal_handler_caller + && gdbarch_tdep (current_gdbarch)->frame_base_before_sigtramp) + frame_base = gdbarch_tdep (current_gdbarch)->frame_base_before_sigtramp (frame); + else + frame_base = frame->frame; + + /* Get frame sizes for the current frame and the frame of the + caller. */ + my_framesize = find_proc_framesize (frame->pc); + caller_pc = FRAME_SAVED_PC (frame); + + if (DEBUG_PA_FRAME > 1) + printf (" frame_base = %#lx, my_framesize = %#x, caller_pc = %#lx\n", + (long) frame_base, my_framesize, (long) caller_pc); + + /* If we can't determine the caller's PC, then it's not likely we can + really determine anything meaningful about its frame. We'll consider + this to be stack bottom. */ + if (caller_pc == (CORE_ADDR) 0) + return (CORE_ADDR) 0; + + caller_framesize = find_proc_framesize (caller_pc); + + if (DEBUG_PA_FRAME > 1) + printf (" caller_framesize = %#x\n", caller_framesize); + + /* If caller does not have a frame pointer, then its frame + can be found at current_frame - caller_framesize. */ + if (caller_framesize != -1) + { + ret = frame_base - caller_framesize; + if (DEBUG_PA_FRAME) + printf (" returning %#lx\n", (long) ret); + return ret; + } + + /* Both caller and callee have frame pointers. + The previous frame pointer is found at the top of the current frame. */ + if (caller_framesize == -1 && my_framesize == -1) + { + ret = read_memory_integer (frame_base, REGISTER_SIZE); + if (DEBUG_PA_FRAME) + printf (" returning %#lx\n", (long) ret); + return ret; + } + + /* Caller has a frame pointer, but callee does not. This is a little + more difficult as GCC and HP C lay out locals and callee register save + areas very differently. + + The previous frame pointer could be in a register, or in one of + several areas on the stack. + + Walk from the current frame to the innermost frame examining + unwind descriptors to determine if %r3 ever gets saved into the + stack. If so return whatever value got saved into the stack. + If it was never saved in the stack, then the value in %r3 is still + valid, so use it. + + We use information from unwind descriptors to determine if %r3 + is saved into the stack (Entry_GR field has this information). */ + + for (tmp_frame = frame; tmp_frame; tmp_frame = tmp_frame->next) + { + u = find_unwind_entry (tmp_frame->pc); + + if (!u) + { + /* We could find this information by examining prologues. I don't + think anyone has actually written any tools (not even "strip") + which leave them out of an executable, so maybe this is a moot + point. */ + /* ??rehrauer: Actually, it's quite possible to stepi your way into + code that doesn't have unwind entries. For example, stepping into + the dynamic linker will give you a PC that has none. Thus, I've + disabled this warning. */ +#if 0 + warning ("Unable to find unwind for PC 0x%x -- Help!", tmp_frame->pc); +#endif + ret = 0; + if (DEBUG_PA_FRAME) + printf (" returning %#lx\n", (long) ret); + return ret; + } + + if (u->Save_SP + || tmp_frame->signal_handler_caller + || PA_IN_INTERRUPT_HANDLER (tmp_frame->pc)) + break; + + /* Entry_GR specifies the number of callee-saved general registers + saved in the stack. It starts at %r3, so %r3 would be 1. */ + if (u->Entry_GR >= 1) + { + /* The unwind entry claims that r3 is saved here. However, + in optimized code, GCC often doesn't actually save r3. + We'll discover this if we look at the prologue. */ + if (!tmp_frame->saved_regs) + FRAME_INIT_SAVED_REGS (tmp_frame); + if (tmp_frame->saved_regs[PA_GR3_REGNUM]) + break; + } + } + + if (tmp_frame) + { + /* We may have walked down the chain into a function with a frame + pointer. */ + if (DEBUG_PA_FRAME > 1) + printf (" reading fp from frame %p", tmp_frame); + if (u->Save_SP + && !tmp_frame->signal_handler_caller + && !PA_IN_INTERRUPT_HANDLER (tmp_frame->pc)) + { + if (DEBUG_PA_FRAME > 1) + printf ("->frame\n"); + ret = read_memory_integer (tmp_frame->frame, REGISTER_SIZE); + } + /* %r3 was saved somewhere in the stack. Dig it out. */ + else + { + /* Sick. + + For optimization purposes many kernels don't save the + callee saved registers into the save_state structure upon + entry into the kernel for a syscall; the optimization + is usually turned off if the process is being traced so + that the debugger can get full register state for the + process. + + This scheme works well except for two cases: + + * Attaching to a process when the process is in the + kernel performing a system call (debugger can't get + full register state for the inferior process since + the process wasn't being traced when it entered the + system call). + + * Register state is not complete if the system call + causes the process to core dump. + + The following heinous code is an attempt to deal with + the lack of register state in a core dump. It will + fail miserably if the function which performs the + system call has a variable sized stack frame. */ + + /* Abominable hack. */ + if (current_target.to_has_execution == 0 + && PA_IN_SYSCALL (tmp_frame->saved_regs) + && (u = find_unwind_entry (FRAME_SAVED_PC (frame))) != NULL) + { + ret = frame_base - (u->Total_frame_size << 3); + } + else + { + if (DEBUG_PA_FRAME > 1) + printf ("->saved_regs\n"); + ret = read_memory_integer (tmp_frame->saved_regs[PA_GR3_REGNUM], + REGISTER_SIZE); + } + } + } + else + { + /* Get the innermost frame. */ + tmp_frame = frame; + while (tmp_frame->next != NULL) + tmp_frame = tmp_frame->next; + + /* Abominable hack. See above. */ + if (current_target.to_has_execution == 0 + && PA_IN_SYSCALL (tmp_frame->saved_regs)) + { + u = find_unwind_entry (FRAME_SAVED_PC (frame)); + if (u) + ret = frame_base - (u->Total_frame_size << 3); + else if (tmp_frame->saved_regs[PA_GR3_REGNUM]) + ret = read_memory_integer (tmp_frame->saved_regs[PA_GR3_REGNUM], + REGISTER_SIZE); + else + ret = TARGET_READ_FP (); + } + else + { + /* The value in %r3 was never saved into the stack (thus %r3 + still holds the value of the previous frame pointer). */ + if (DEBUG_PA_FRAME > 1) + printf (" reading fp reg\n"); + ret = TARGET_READ_FP (); + } + } + if (DEBUG_PA_FRAME) + printf (" returning %#lx\n", (long) ret); + return ret; +} + +/* Return the base address used when calculating arg symbol offsets. This + doesn't have anything to do with the actual start of the args. */ +static CORE_ADDR +pa_frame_args_address (struct frame_info *fi) +{ + return fi->frame; +} + +/* As for above, but for local vars. */ +static CORE_ADDR +pa_frame_locals_address (struct frame_info *fi) +{ + return fi->frame; +} + +/* Stack grows upward. */ +static int +pa_inner_than (CORE_ADDR lhs, CORE_ADDR rhs) +{ + return lhs > rhs; +} + +/* On hppa the stack must always be kept 64-bit aligned. */ +static CORE_ADDR +pa_stack_align (CORE_ADDR sp) +{ + return (sp+7) & -8; +} + +/* On hppa64 the stack must always be kept 128-bit aligned. */ +static CORE_ADDR +pa64_stack_align (CORE_ADDR sp) +{ + return (sp+15) & -16; +} + +/* On the PA, any pass-by-value structure > 8 bytes is actually + passed via a pointer regardless of its type or the compiler + used. */ +static int +pa_reg_struct_has_addr (int gcc_p, struct type *type) +{ + return TYPE_LENGTH (type) > 8; +} + +static CORE_ADDR +pa_read_sp (void) +{ + return read_register (PA_GR30_REGNUM); +} + +static void +pa_write_sp (CORE_ADDR val) +{ + write_register (PA_GR30_REGNUM, val); +} + +void +pa_extract_return_value (struct type *type, struct regcache *regcache, char *valbuf) +{ + /* Extract from an array REGBUF containing the (raw) register state + a function return value of type TYPE, and copy that, in virtual + format, into VALBUF. */ + + if (TYPE_CODE (type) == TYPE_CODE_FLT && !SOFT_FLOAT) + regcache_cooked_read_using_offset_hack (regcache, + REGISTER_BYTE (PA_FR4_REGNUM), + TYPE_LENGTH (type), valbuf); + else + regcache_cooked_read_using_offset_hack (regcache, + REGISTER_BYTE (PA_GR28_REGNUM) + + 3 - ((TYPE_LENGTH (type) - 1) & 3), + TYPE_LENGTH (type), valbuf); +} + +void +pa64_extract_return_value (struct type *type, struct regcache *regcache, char *valbuf) +{ + /* Floats are returned in FR4R, doubles in FR4 + integral values are in r28, padded on the left + aggregates less that 65 bits are in r28, right padded + aggregates up to 128 bits are in r28 and r29, right padded. */ + if (TYPE_CODE (type) == TYPE_CODE_FLT && !SOFT_FLOAT) + regcache_cooked_read_using_offset_hack (regcache, + REGISTER_BYTE (PA_FR4_REGNUM) + 8 - TYPE_LENGTH (type), + TYPE_LENGTH (type), valbuf); + else if (is_integral_type (type) || SOFT_FLOAT) + regcache_cooked_read_using_offset_hack (regcache, + REGISTER_BYTE (PA_GR28_REGNUM) + 8 - TYPE_LENGTH (type), + TYPE_LENGTH (type), valbuf); + else if (TYPE_LENGTH (type) <= 16) + regcache_cooked_read_using_offset_hack (regcache, + REGISTER_BYTE (PA_GR28_REGNUM), + TYPE_LENGTH (type), valbuf); +} + +static void +find_dummy_frame_regs (struct frame_info *frame, CORE_ADDR *frame_saved_regs) +{ + CORE_ADDR fp = frame->frame; + int i; + int reg_size = REGISTER_SIZE; + + /* The 32bit and 64bit ABIs save RP into different locations. */ + if (reg_size == 8) + frame_saved_regs[PA_GR2_REGNUM] = fp - 16; + else + frame_saved_regs[PA_GR2_REGNUM] = fp - 20; + + frame_saved_regs[PA_GR3_REGNUM] = fp; + + fp += 2 * reg_size; + + for (i = PA_GR1_REGNUM; i <= PA_GR31_REGNUM; i++) + if (i != PA_GR2_REGNUM && i != PA_GR3_REGNUM) + { + frame_saved_regs[i] = fp; + fp += reg_size; + } + + /* This is not necessary or desirable for the 64bit ABI. */ + if (reg_size != 8) + fp += reg_size; + + for (i = PA_FR0_REGNUM; i < NUM_REGS; i++, fp += 4) + frame_saved_regs[i] = fp; + + frame_saved_regs[PA_IPSW_REGNUM] = fp; fp += reg_size; + frame_saved_regs[PA_SAR_REGNUM] = fp; fp += reg_size; + frame_saved_regs[PA_PCOQ_HEAD_REGNUM] = fp; fp += reg_size; + frame_saved_regs[PA_PCSQ_HEAD_REGNUM] = fp; fp += reg_size; + frame_saved_regs[PA_PCOQ_TAIL_REGNUM] = fp; fp += reg_size; + frame_saved_regs[PA_PCSQ_TAIL_REGNUM] = fp; +} + +/* After returning to a dummy on the stack, restore the instruction + queue space registers. */ + +static int +restore_pc_queue (CORE_ADDR *fsr) +{ + CORE_ADDR pc = read_pc (); + CORE_ADDR new_pc = read_memory_integer (fsr[PA_PCOQ_HEAD_REGNUM], + REGISTER_SIZE); + struct target_waitstatus w; + int insn_count; + + /* HPUX doesn't let us set the space registers or the space + registers of the PC queue through ptrace. Boo, hiss. + + But the call dummy has this sequence of instructions at the break: + mtsp r21, sr0 + ble,n 0(sr0, r22) + nop + + So, load up the registers and single step until we are in the + right place. */ + + write_register (PA_GR21_REGNUM, + read_memory_integer (fsr[PA_PCSQ_HEAD_REGNUM], + REGISTER_SIZE)); + write_register (PA_GR22_REGNUM, new_pc); + + for (insn_count = 0; insn_count < 3; insn_count++) + { + /* FIXME: What if the inferior gets a signal right now? Want to + merge this into wait_for_inferior (as a special kind of + watchpoint? By setting a breakpoint at the end? Is there + any other choice? Is there *any* way to do this stuff with + ptrace() or some equivalent?). */ + resume (1, 0); + target_wait (inferior_ptid, &w); + + if (w.kind == TARGET_WAITKIND_SIGNALLED) + { + stop_signal = w.value.sig; + terminal_ours_for_output (); + printf_unfiltered ("\nProgram terminated with signal %s, %s.\n", + target_signal_to_name (stop_signal), + target_signal_to_string (stop_signal)); + gdb_flush (gdb_stdout); + return 0; + } + } + target_terminal_ours (); + target_fetch_registers (-1); + return 1; +} + +void +pa_pop_frame (void) +{ + struct frame_info *frame = get_current_frame (); + CORE_ADDR fp, npc, target_pc; + int regnum; + CORE_ADDR *fsr; + char reg_buffer[8]; + int reg_size = REGISTER_SIZE; + + fp = FRAME_FP (frame); + fsr = frame->saved_regs; + +#ifndef NO_PC_SPACE_QUEUE_RESTORE + if (fsr[PA_IPSW_REGNUM]) /* Restoring a call dummy frame */ + restore_pc_queue (fsr); +#endif + + for (regnum = PA_GR31_REGNUM; regnum > PA_GR0_REGNUM; regnum--) + if (fsr[regnum]) + { + read_memory (fsr[regnum], reg_buffer, reg_size); + write_register_gen (regnum, reg_buffer); + } + + for (regnum = NUM_REGS - 1; regnum >= PA_FR0_REGNUM; regnum--) + if (fsr[regnum]) + { + read_memory (fsr[regnum], reg_buffer, 4); + write_register_gen (regnum, reg_buffer); + } + + if (fsr[PA_IPSW_REGNUM]) + { + read_memory (fsr[PA_IPSW_REGNUM], reg_buffer, reg_size); + write_register_gen (PA_IPSW_REGNUM, reg_buffer); + } + + if (fsr[PA_SAR_REGNUM]) + { + read_memory (fsr[PA_SAR_REGNUM], reg_buffer, reg_size); + write_register_gen (PA_SAR_REGNUM, reg_buffer); + } + + /* If the PC was explicitly saved, then just restore it. */ + if (fsr[PA_PCOQ_TAIL_REGNUM]) + { + npc = read_memory_integer (fsr[PA_PCOQ_TAIL_REGNUM], reg_size); + write_register (PA_PCOQ_TAIL_REGNUM, npc); + npc = read_memory_integer (fsr[PA_PCOQ_HEAD_REGNUM], reg_size); + write_register (PA_PCOQ_HEAD_REGNUM, npc); + } + /* Else use the value in %rp to set the new PC. */ + else + { + npc = read_register (PA_GR2_REGNUM); + write_pc (npc); + } + + read_memory (fp, reg_buffer, reg_size); + write_register_gen (PA_GR3_REGNUM, reg_buffer); + + if (fsr[PA_IPSW_REGNUM]) /* call dummy */ + write_register (PA_GR30_REGNUM, fp - 48); + else + write_register (PA_GR30_REGNUM, fp); + + /* The PC we just restored may be inside a return trampoline. If so + we want to restart the inferior and run it through the trampoline. + + Do this by setting a momentary breakpoint at the location the + trampoline returns to. + + Don't skip through the trampoline if we're popping a dummy frame. */ + target_pc = SKIP_TRAMPOLINE_CODE (npc & ~3) & ~3; + if (target_pc && !fsr[PA_IPSW_REGNUM]) + { + struct symtab_and_line sal; + struct breakpoint *breakpoint; + struct cleanup *old_chain; + + /* Set up our breakpoint. Set it to be silent as the MI code + for "return_command" will print the frame we returned to. */ + sal = find_pc_line (target_pc, 0); + sal.pc = target_pc; + breakpoint = set_momentary_breakpoint (sal, NULL, bp_finish); + breakpoint->silent = 1; + + /* So we can clean things up. */ + old_chain = make_cleanup_delete_breakpoint (breakpoint); + + /* Start up the inferior. */ + clear_proceed_status (); + proceed_to_finish = 1; + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0); + + /* Perform our cleanups. */ + do_cleanups (old_chain); + } + flush_cached_frames (); +} + +/* This function pushes a stack frame with arguments as part of the + inferior function calling mechanism. + + This is the version for the PA64, in which later arguments appear + at higher addresses. (The stack always grows towards higher + addresses.) + + We simply allocate the appropriate amount of stack space and put + arguments into their proper slots. The call dummy code will copy + arguments into registers as needed by the ABI. + + This ABI also requires that the caller provide an argument pointer + to the callee, so we do that too. */ + +#define PA64_REGISTER_SIZE 8 +#define REG_PARM_STACK_SPACE64 (8 * 8) + +CORE_ADDR +pa64_push_arguments (int nargs, struct value **args, CORE_ADDR sp, int + struct_return, CORE_ADDR struct_addr) +{ + /* array of arguments' offsets */ + int *offset = (int *) alloca (nargs * sizeof (int)); + + /* array of arguments' lengths: real lengths in bytes, not aligned to + word size */ + int *lengths = (int *) alloca (nargs * sizeof (int)); + + /* The value of SP as it was passed into this function after + aligning. */ + CORE_ADDR orig_sp = STACK_ALIGN (sp); + + /* The number of stack bytes occupied by the current argument. */ + int bytes_reserved; + + /* The total number of bytes reserved for the arguments. */ + int cum_bytes_reserved = 0; + + /* Similarly, but aligned. */ + int cum_bytes_aligned = 0; + int i; + + /* Iterate over each argument provided by the user. */ + for (i = 0; i < nargs; i++) + { + struct type *arg_type = VALUE_TYPE (args[i]); + + /* Integral scalar values smaller than a register are padded on + the left. We do this by promoting them to full-width, + although the ABI says to pad them with garbage. */ + if (is_integral_type (arg_type) + && TYPE_LENGTH (arg_type) < PA64_REGISTER_SIZE) + { + args[i] = value_cast ((TYPE_UNSIGNED (arg_type) + ? builtin_type_unsigned_long + : builtin_type_long), + args[i]); + arg_type = VALUE_TYPE (args[i]); + } + + lengths[i] = TYPE_LENGTH (arg_type); + + /* Align the size of the argument to the word size for this + target. */ + bytes_reserved = ((lengths[i] + PA64_REGISTER_SIZE - 1) + & -PA64_REGISTER_SIZE); + + offset[i] = cum_bytes_reserved; + + /* Aggregates larger than eight bytes (the only types larger + than eight bytes we have) are aligned on a 16-byte boundary, + possibly padded on the right with garbage. This may leave an + empty word on the stack, and thus an unused register, as per + the ABI. */ + if (bytes_reserved > 8) + { + /* Round up the offset to a multiple of two slots. */ + int new_offset = ((offset[i] + 2*PA64_REGISTER_SIZE-1) + & -(2*PA64_REGISTER_SIZE)); + + /* Note the space we've wasted, if any. */ + bytes_reserved += new_offset - offset[i]; + offset[i] = new_offset; + } + + cum_bytes_reserved += bytes_reserved; + } + + /* CUM_BYTES_RESERVED already accounts for all the arguments + passed by the user. However, the ABIs mandate minimum stack space + allocations for outgoing arguments. + + The ABIs also mandate minimum stack alignments which we must + preserve. */ + cum_bytes_aligned = STACK_ALIGN (cum_bytes_reserved); + sp += max (cum_bytes_aligned, REG_PARM_STACK_SPACE64); + + /* Now write each of the args at the proper offset down the stack. */ + for (i = 0; i < nargs; i++) + write_memory (orig_sp + offset[i], VALUE_CONTENTS (args[i]), lengths[i]); + + /* For the PA64 we must pass a pointer to the outgoing argument list. + The ABI mandates that the pointer should point to the first byte of + storage beyond the register flushback area. */ + write_register (PA_GR29_REGNUM, orig_sp + REG_PARM_STACK_SPACE64); + + /* The stack will have 64 bytes of additional space for a frame marker. */ + return sp + 64; +} + +/* This function pushes a stack frame with arguments as part of the + inferior function calling mechanism. + + This is the version of the function for the 32-bit PA machines, in + which later arguments appear at lower addresses. (The stack always + grows towards higher addresses.) + + We simply allocate the appropriate amount of stack space and put + arguments into their proper slots. The call dummy code will copy + arguments into registers as needed by the ABI. */ + +#define PA32_REGISTER_SIZE 4 +#define REG_PARM_STACK_SPACE32 (4 * 4) + +CORE_ADDR +pa_push_arguments (int nargs, struct value **args, CORE_ADDR sp, int + struct_return, CORE_ADDR struct_addr) +{ + /* array of arguments' offsets */ + int *offset = (int *) alloca (nargs * sizeof (int)); + + /* array of arguments' lengths: real lengths in bytes, not aligned to + word size */ + int *lengths = (int *) alloca (nargs * sizeof (int)); + + /* The number of stack bytes occupied by the current argument. */ + int bytes_reserved; + + /* The total number of bytes reserved for the arguments. */ + int cum_bytes_reserved = 0; + + /* Similarly, but aligned. */ + int cum_bytes_aligned = 0; + int i; + + /* Iterate over each argument provided by the user. */ + for (i = 0; i < nargs; i++) + { + lengths[i] = TYPE_LENGTH (VALUE_TYPE (args[i])); + + /* Align the size of the argument to the word size for this + target. */ + bytes_reserved = ((lengths[i] + PA32_REGISTER_SIZE - 1) + & -PA32_REGISTER_SIZE); + + offset[i] = cum_bytes_reserved + lengths[i]; + + /* If the argument is a double word argument, then it needs to be + double word aligned. */ + if ((bytes_reserved == 2 * PA32_REGISTER_SIZE) + && (offset[i] % 2 * PA32_REGISTER_SIZE)) + { + int new_offset = 0; + /* BYTES_RESERVED is already aligned to the word, so we put + the argument at one word more down the stack. + + This will leave one empty word on the stack, and one unused + register as mandated by the ABI. */ + new_offset = ((offset[i] + 2 * PA32_REGISTER_SIZE - 1) + & -(2 * PA32_REGISTER_SIZE)); + + if ((new_offset - offset[i]) >= 2 * PA32_REGISTER_SIZE) + { + bytes_reserved += PA32_REGISTER_SIZE; + offset[i] += PA32_REGISTER_SIZE; + } + } + + cum_bytes_reserved += bytes_reserved; + } + + /* CUM_BYTES_RESERVED already accounts for all the arguments passed + by the user. However, the ABI mandates minimum stack space + allocations for outgoing arguments. + + The ABI also mandates minimum stack alignments which we must + preserve. */ + cum_bytes_aligned = STACK_ALIGN (cum_bytes_reserved); + sp += max (cum_bytes_aligned, REG_PARM_STACK_SPACE32); + + /* Now write each of the args at the proper offset down the stack. + ?!? We need to promote values to a full register instead of skipping + words in the stack. */ + for (i = 0; i < nargs; i++) + write_memory (sp - offset[i], VALUE_CONTENTS (args[i]), lengths[i]); + + /* The stack will have 32 bytes of additional space for a frame marker. */ + return sp + 32; +} + +/* We don't need to push the return address as we use a call dummy. */ +CORE_ADDR +pa_push_return_address (CORE_ADDR pc, CORE_ADDR sp) +{ + return sp; +} + +/* elz: This function returns a value which is built looking at the given + address. It is called from call_function_by_hand, in case we need to + return a value which is larger than 64 bits, and it is stored in the + stack rather than in the registers r28 and r29 or fr4. This function + does the same stuff as value_being_returned in values.c, but gets the + value from the stack rather than from the buffer where all the registers + were saved when the function called completed. */ +struct value * +pa_value_returned_from_stack (struct type *valtype, CORE_ADDR addr) +{ + struct value *val; + + val = allocate_value (valtype); + CHECK_TYPEDEF (valtype); + target_read_memory (addr, VALUE_CONTENTS_RAW (val), TYPE_LENGTH (valtype)); + + return val; +} + +/* Store the address of the place in which to copy the structure the + subroutine will return. This is called from call_function. */ + +static void +pa_store_struct_return (CORE_ADDR addr, CORE_ADDR sp) +{ + write_register (PA_GR28_REGNUM, addr); +} + +/* Write into appropriate registers a function return value + of type TYPE, given in virtual format. + + For software floating point the return value goes into the integer + registers. But we don't have any flag to key this on, so we always + store the value into the integer registers, and if it's a float value, + then we put it in the float registers too. */ + +static void +pa_store_return_value (struct type *type, char *valbuf) +{ + write_register_bytes (REGISTER_BYTE (PA_GR28_REGNUM), + valbuf, TYPE_LENGTH (type)); + if (!SOFT_FLOAT) + write_register_bytes ((TYPE_CODE (type) == TYPE_CODE_FLT + ? REGISTER_BYTE (PA_FR4_REGNUM) + : REGISTER_BYTE (PA_GR28_REGNUM)), + valbuf, TYPE_LENGTH (type)); +} + +/* Extract from an array REGBUF containing the (raw) register state + the address in which a function should return its structure value, + as a CORE_ADDR. */ + +static CORE_ADDR +pa_extract_struct_value_address (char *regbuf) +{ + return extract_unsigned_integer (regbuf + REGISTER_BYTE (PA_GR28_REGNUM), + REGISTER_RAW_SIZE (PA_GR28_REGNUM)); +} + + /* Decide whether the function returning a value of type VALUE_TYPE + will put it on the stack or in the registers. */ +static int +pa_use_struct_convention (int gcc_p, struct type *value_type) +{ + return TYPE_LENGTH (value_type) > 2 * REGISTER_SIZE; +} + +/* Determines the address of all registers in the current stack frame + storing each in frame->saved_regs. This includes special registers + such as pc and fp saved in special ways in the stack frame. sp is + even more special: the address we return for it IS the sp for the + next frame. */ + +static void +pa_frame_init_saved_regs (struct frame_info *frame_info) +{ + CORE_ADDR pc; + struct unwind_table_entry *u; + unsigned long inst, stack_remaining, save_gr, save_fr, save_rp, save_sp; + int status, i, reg; + char buf[4]; + int fp_loc = -1; + int final_iteration; + + if (DEBUG_PA_FRAME) + printf ("frame_init_saved_regs (%p), pc = %#lx : ", + frame_info, frame_info->pc); + + if (frame_info->saved_regs) + { + if (DEBUG_PA_FRAME) + printf ("already done\n"); + return; + } + + frame_saved_regs_zalloc (frame_info); + + /* Interrupt handlers are special. */ + if (PA_IN_INTERRUPT_HANDLER (frame_info->pc)) + { + if (DEBUG_PA_FRAME) + printf ("in interrupt\n"); + gdbarch_tdep (current_gdbarch)->frame_find_saved_regs_in_interrupt + (frame_info, frame_info->saved_regs); + return; + } + + /* So are signal handler callers. */ + if (frame_info->signal_handler_caller + && gdbarch_tdep (current_gdbarch)->frame_find_saved_regs_in_sigtramp) + { + if (DEBUG_PA_FRAME) + printf ("in sigtramp\n"); + gdbarch_tdep (current_gdbarch)->frame_find_saved_regs_in_sigtramp + (frame_info, frame_info->saved_regs); + return; + } + + /* Call dummy frames always look the same, so there's no need to + examine the dummy code to determine locations of saved registers; + instead, let find_dummy_frame_regs fill in the correct offsets + for the saved registers. */ + if (frame_info->pc >= frame_info->frame + && frame_info->pc <= (frame_info->frame + + CALL_DUMMY_LENGTH + /* Account for register saves. */ + + ((32 + 6) * REGISTER_SIZE) + + (NUM_REGS - PA_FR0_REGNUM) * 4)) + { + if (DEBUG_PA_FRAME) + printf ("in dummy\n"); + find_dummy_frame_regs (frame_info, frame_info->saved_regs); + return; + } + + if (DEBUG_PA_FRAME) + printf ("grok prologue\n"); + + /* Get the starting address of the function referred to by the PC + saved in frame. */ + pc = get_pc_function_start (frame_info->pc); + + /* Now rummage through the function machine instructions to find out + where registers are saved. */ + pa_grok_prologue (pc, frame_info); +} + +static void +pa_print_fp_reg (int i) +{ + char buf[8]; + + /* Get 32bits of data. */ + frame_register_read (selected_frame, i, buf); + + printf_filtered ("%-8s(single precision) ", REGISTER_NAME (i)); + val_print (REGISTER_VIRTUAL_TYPE (i), buf, 0, 0, gdb_stdout, 0, + 1, 0, Val_pretty_default); + printf_filtered ("\n"); + + /* If "i" is even, then this register can also be a double-precision + FP register. Dump it out as such. */ + if ((i % 2) == 0) + { + /* Get the data in raw format for the 2nd half. */ + frame_register_read (selected_frame, i + 1, buf + 4); + + /* Dump it as a double. */ + printf_filtered ("%-8s(double precision) ", REGISTER_NAME (i)); + val_print (builtin_type_double, buf, 0, 0, gdb_stdout, 0, + 1, 0, Val_pretty_default); + printf_filtered ("\n"); + } +} + +/* "Info all-reg" command */ + +static void +pa_print_registers (int fpregs) +{ + int i, j; + int columns = 2; + int rows = PA_FR4_REGNUM / columns; + + for (i = 0; i < rows; i++) + { + for (j = 0; j < columns; j++) + { + /* We display registers in column-major order. */ + int regnum = i + j * rows; + char buf[8]; + ULONGEST reg_val; + + frame_register_read (selected_frame, regnum, buf); + reg_val = extract_unsigned_integer (buf, + REGISTER_RAW_SIZE (regnum)); + + /* Even fancier % formats to prevent leading zeros + and still maintain the output in columns. */ + + printf_unfiltered ("%10s: %-19s", REGISTER_NAME (regnum), + phex_nz (reg_val, sizeof (ULONGEST))); + } + printf_unfiltered ("\n"); + } + + if (fpregs) + for (i = PA_FR4_REGNUM; i < NUM_REGS; i++) + pa_print_fp_reg (i); +} + +/* Print the register regnum, or all registers if regnum is -1 */ + +static void +pa_do_registers_info (int regnum, int fpregs) +{ + if (regnum == -1) + pa_print_registers (fpregs); + else if (regnum < PA_FR4_REGNUM) + { + char buf[sizeof (ULONGEST)]; + ULONGEST reg_val; + + frame_register_read (selected_frame, regnum, buf); + reg_val = extract_unsigned_integer (buf, + REGISTER_RAW_SIZE (regnum)); + + printf_unfiltered ("%s %s\n", REGISTER_NAME (regnum), + phex_nz (reg_val, sizeof (ULONGEST))); + } + else + /* Note that real floating point values only start at + PA_FR4_REGNUM. FP0 and up are just status and error + registers, which have integral (bit) values. */ + pa_print_fp_reg (regnum); +} + +/* Finds the addresses of the dynamic call routines for this object + file. If PC isn't in any of the object files, return NULL. */ + +static obj_private_data_t * +pa_find_dyn (CORE_ADDR pc) +{ + struct obj_section *pc_sec; + struct objfile *pc_obj; + obj_private_data_t *obj_private; + CORE_ADDR *addr; + static const char *syms[] = { + "_sr4export", "$$dyncall", "$$dyncall_external" + }; + const char **sym; + + pc_sec = find_pc_section (pc); + if (!pc_sec) + return NULL; + + pc_obj = pc_sec->objfile; + obj_private = (obj_private_data_t *) pc_obj->obj_private; + if (obj_private == NULL) + obj_private = pa_obj_private_alloc (pc_obj); + + for (addr = obj_private->dyn, sym = syms; + sym < syms + sizeof (syms) / sizeof (syms[0]); + addr++, sym++) + { + if (*addr == 0) + { + struct minimal_symbol *minsym; + CORE_ADDR val; + + /* First look in this object file, but if we didn't find it, + look globally. */ + minsym = lookup_minimal_symbol (*sym, NULL, pc_obj); + if (!minsym) + minsym = lookup_minimal_symbol (*sym, NULL, NULL); + if (minsym) + val = SYMBOL_VALUE_ADDRESS (minsym); + else + val = -1; + *addr = val; + } + } + return obj_private; +} + +/* Return one if PC is in the call path of a trampoline, else return zero. + + Note we return one for *any* call trampoline (long-call, arg-reloc), not + just shared library trampolines (import, export). */ + +int +pa_in_solib_call_trampoline (CORE_ADDR pc, char *name) +{ + obj_private_data_t *obj_private; + struct minimal_symbol *minsym; + struct unwind_table_entry *u; + + if (DEBUG_PA_GROK) + printf ("in_solib_call_tramp (%#lx, %s)\n", pc, name); + + if (gdbarch_tdep (current_gdbarch)->is_elf) + { + /* PA64 has a completely different stub/trampoline scheme. Is + it better? Maybe. It's certainly harder to determine with + any certainty that we are in a stub because we can not refer + to the unwinders to help. + + The heuristic is simple. Try to lookup the current PC value + in the minimal symbol table. If that fails, then assume we + are not in a stub and return. + + Then see if the PC value falls within the section bounds for + the section containing the minimal symbol we found in the + first step. If it does, then assume we are not in a stub and + return. + + Finally peek at the instructions to see if they look like a + stub. */ +#if 0 + /* I think this is bogus. Stub sections get merged into the + .text section, so the sec->vma test will not differentiate + between stub/non-stub code. ADM */ + asection *sec; + + minsym = lookup_minimal_symbol_by_pc (pc); + if (! minsym) + return 0; + + sec = SYMBOL_BFD_SECTION (minsym); + + if (sec->vma <= pc + && sec->vma + sec->_cooked_size < pc) + return 0; +#endif + + /* Are we in the plt stub that supports lazy dynamic linking? */ + if (in_plt_section (pc, name)) + { + if (DEBUG_PA_GROK) + printf (" was in plt\n"); + return 1; + } + + /* We might be in a stub. */ + if (pc_in_linker_stub (pc, NULL) != pa_stub_none) + { + if (DEBUG_PA_GROK) + printf (" was in stub\n"); + return 1; + } + } + + obj_private = pa_find_dyn (pc); + + if (obj_private != NULL + && (pc == obj_private->dyn[dyncall] + || pc == obj_private->dyn[sr4export])) + { + if (DEBUG_PA_GROK) + printf (" was dyn_call\n"); + return 1; + } + + minsym = lookup_minimal_symbol_by_pc (pc); + if (minsym) + { + CORE_ADDR addr = SYMBOL_VALUE_ADDRESS (minsym); + if (obj_private != NULL + && (addr == obj_private->dyn[dyncall] + || addr == obj_private->dyn[sr4export])) + { + if (DEBUG_PA_GROK) + printf (" was in dyn_call\n"); + return 1; + } + + if (strcmp (SYMBOL_NAME (minsym), ".stub") == 0) + { + if (DEBUG_PA_GROK) + printf (" was stub sym\n"); + return 1; + } + } + + if (gdbarch_tdep (current_gdbarch)->is_elf) + { + if (DEBUG_PA_GROK) + printf (" nup\n"); + return 0; + } + + /* Get the unwind descriptor corresponding to PC, return zero + if no unwind was found. */ + u = find_unwind_entry (pc); + if (!u) + return 0; + + /* If this isn't a linker stub, then return now. */ + if (u->stub_unwind.stub_type == 0) + return 0; + + /* By definition a long-branch stub is a call stub. */ + if (u->stub_unwind.stub_type == LONG_BRANCH) + return 1; + + /* The call and return path execute the same instructions within + an IMPORT stub! So an IMPORT stub is both a call and return + trampoline. */ + if (u->stub_unwind.stub_type == IMPORT) + return 1; + + /* Parameter relocation stubs always have a call path and may have a + return path. */ + if (u->stub_unwind.stub_type == PARAMETER_RELOCATION + || u->stub_unwind.stub_type == EXPORT) + { + CORE_ADDR addr; + + /* Search forward from the current PC until we hit a branch + or the end of the stub. */ + for (addr = pc; addr <= u->region_end; addr += 4) + { + unsigned long insn; + + insn = read_memory_integer (addr, 4); + + /* Does it look like a bl? If so then it's the call path, if + we find a bv or be first, then we're on the return path. */ + if ((insn & 0xfc00e000) == 0xe8000000) + return 1; + else if ((insn & 0xfc00e001) == 0xe800c000 + || (insn & 0xfc000000) == 0xe0000000) + return 0; + } + + /* Should never happen. */ + warning ("Unable to find branch in parameter relocation stub.\n"); + return 0; + } + + /* Unknown stub type. For now, just return zero. */ + return 0; +} + +/* Return one if PC is in the return path of a trampoline, else return zero. + + Note we return one for *any* call trampoline (long-call, arg-reloc), not + just shared library trampolines (import, export). */ + +int +pa_in_solib_return_trampoline (CORE_ADDR pc, char *name) +{ + struct unwind_table_entry *u; + + /* Get the unwind descriptor corresponding to PC, return zero + if no unwind was found. */ + u = find_unwind_entry (pc); + if (!u) + { + return 0; + } + + /* If this isn't a linker stub or it's just a long branch stub, then + return zero. */ + if (u->stub_unwind.stub_type == 0 || u->stub_unwind.stub_type == LONG_BRANCH) + return 0; + + /* The call and return path execute the same instructions within + an IMPORT stub! So an IMPORT stub is both a call and return + trampoline. */ + if (u->stub_unwind.stub_type == IMPORT) + return 1; + + /* Parameter relocation stubs always have a call path and may have a + return path. */ + if (u->stub_unwind.stub_type == PARAMETER_RELOCATION + || u->stub_unwind.stub_type == EXPORT) + { + CORE_ADDR addr; + + /* Search forward from the current PC until we hit a branch + or the end of the stub. */ + for (addr = pc; addr <= u->region_end; addr += 4) + { + unsigned long insn; + + insn = read_memory_integer (addr, 4); + + /* Does it look like a bl? If so then it's the call path, if + we find a bv or be first, then we're on the return path. */ + if ((insn & 0xfc00e000) == 0xe8000000) + return 0; + else if ((insn & 0xfc00e001) == 0xe800c000 + || (insn & 0xfc000000) == 0xe0000000) + return 1; + } + + /* Should never happen. */ + warning ("Unable to find branch in parameter relocation stub.\n"); + return 0; + } + + /* Unknown stub type. For now, just return zero. */ + return 0; +} + +/* Figure out if PC is in a trampoline, and if so find out where + the trampoline will jump to. If not in a trampoline, return zero. + + Simple code examination probably is not a good idea since the code + sequences in trampolines can also appear in user code. + + We use unwinds and information from the minimal symbol table to + determine when we're in a trampoline. This won't work for ELF + (yet) since it doesn't create stub unwind entries. Whether or + not ELF will create stub unwinds or normal unwinds for linker + stubs is still being debated. + + This should handle simple calls through dyncall or sr4export, + long calls, argument relocation stubs, and dyncall/sr4export + calling an argument relocation stub. It even handles some stubs + used in dynamic executables. */ + +CORE_ADDR +pa_skip_trampoline_code (CORE_ADDR pc, char *name) +{ + obj_private_data_t *obj_private; + CORE_ADDR orig_pc = pc; + int prev_inst, curr_inst; + CORE_ADDR loc; + struct minimal_symbol *msym; + struct unwind_table_entry *u; + + if (DEBUG_PA_GROK) + printf ("skip_trampoline (%#lx, %s)\n", pc, name); + + obj_private = pa_find_dyn (pc); + + /* Addresses passed to dyncall may *NOT* be the actual address + of the function. So we may have to do something special. */ + if (obj_private != NULL) + { + if (pc == obj_private->dyn[dyncall]) + { + pc = (CORE_ADDR) read_register (PA_GR22_REGNUM); + + /* If bit 30 (counting from the left) is on, then pc is the + address of the PLT entry for this function, not the + address of the function itself. Bit 31 has meaning too, + but only for MPE. */ + if (pc & 0x2) + pc = (CORE_ADDR) read_memory_integer (pc & ~3, REGISTER_SIZE); + } + else if (pc == obj_private->dyn[dyncall_external]) + { + pc = (CORE_ADDR) read_register (PA_GR22_REGNUM); + pc = (CORE_ADDR) read_memory_integer (pc & ~3, REGISTER_SIZE); + } + else if (pc == obj_private->dyn[sr4export]) + { + pc = (CORE_ADDR) (read_register (PA_GR22_REGNUM)); + } + } + + /* Get the unwind descriptor corresponding to PC, return zero + if no unwind was found. */ + u = find_unwind_entry (pc); + if (!u) + { + CORE_ADDR stub_start, addr = 0; + enum stub_type stubt = pc_in_linker_stub (pc, &stub_start); + + switch (stubt) + { + default: + break; + + case pa_stub_long_branch_shared: + addr = stub_start + 8; + stub_start += 4; + /* Fall thru */ + + case pa_stub_long_branch: + addr += extract_21 (read_memory_integer (stub_start, 4)); + addr += extract_17 (read_memory_integer (stub_start + 4, 4)); + break; + + case pa_stub_import: + case pa_stub_import_multi: + addr = read_register (PA_GR27_REGNUM); + goto handle_stub_import; + + case pa_stub_import_shared: + case pa_stub_import_multi_shared: + addr = read_register (PA_GR19_REGNUM); + handle_stub_import: + addr += extract_21 (read_memory_integer (stub_start, 4)); + addr += extract_14 (read_memory_integer (stub_start + 4, 4)); + addr = read_memory_integer (addr, 4); + if (pc_in_linker_stub (addr, &stub_start) != pa_stub_lazy_link) + break; + /* Fall thru */ + + case pa_stub_lazy_link: + addr = read_memory_integer (stub_start + 20, 4); + break; + + case pa_stub_export: + addr = stub_start + 8; + if (pc < addr) + addr += extract_17 (read_memory_integer (stub_start, 4)); + else + addr = read_memory_integer (read_register (PA_GR30_REGNUM) - 24, 4); + break; + + case pa64_stub_import: + addr = read_register (PA_GR27_REGNUM); + addr += extract_16a (read_memory_integer (stub_start, 4)); + addr = read_memory_integer (addr, 8); + break; + } + + if (DEBUG_PA_GROK) + printf (" type = %d, returning %#lx\n", stubt, addr & ~3); + + return addr & ~3; + } + + /* If this isn't a linker stub, then return now. */ + /* elz: attention here! (FIXME) because of a compiler/linker + error, some stubs which should have a non zero stub_unwind.stub_type + have unfortunately a value of zero. So this function would return here + as if we were not in a trampoline. To fix this, we go look at the partial + symbol information, which reports this guy as a stub. + (FIXME): Unfortunately, we are not that lucky: it turns out that the + partial symbol information is also wrong sometimes. This is because + when it is entered (somread.c::som_symtab_read()) it can happen that + if the type of the symbol (from the som) is Entry, and the symbol is + in a shared library, then it can also be a trampoline. This would + be OK, except that I believe the way they decide if we are ina shared library + does not work. SOOOO..., even if we have a regular function w/o trampolines + its minimal symbol can be assigned type mst_solib_trampoline. + Also, if we find that the symbol is a real stub, then we fix the unwind + descriptor, and define the stub type to be EXPORT. + Hopefully this is correct most of the times. */ + if (u->stub_unwind.stub_type == 0) + { + +/* elz: NOTE (FIXME!) once the problem with the unwind information is fixed + we can delete all the code which appears between the lines */ +/*--------------------------------------------------------------------------*/ + msym = lookup_minimal_symbol_by_pc (pc); + + if (msym == NULL || MSYMBOL_TYPE (msym) != mst_solib_trampoline) + return orig_pc == pc ? 0 : pc & ~3; + + else if (msym != NULL && MSYMBOL_TYPE (msym) == mst_solib_trampoline) + { + struct objfile *objfile; + struct minimal_symbol *msymbol; + int function_found = 0; + + /* go look if there is another minimal symbol with the same name as + this one, but with type mst_text. This would happen if the msym + is an actual trampoline, in which case there would be another + symbol with the same name corresponding to the real function */ + + ALL_MSYMBOLS (objfile, msymbol) + { + if (MSYMBOL_TYPE (msymbol) == mst_text + && STREQ (SYMBOL_NAME (msymbol), SYMBOL_NAME (msym))) + { + function_found = 1; + break; + } + } + + if (function_found) + /* the type of msym is correct (mst_solib_trampoline), but + the unwind info is wrong, so set it to the correct value */ + u->stub_unwind.stub_type = EXPORT; + else + /* the stub type info in the unwind is correct (this is not a + trampoline), but the msym type information is wrong, it + should be mst_text. So we need to fix the msym, and also + get out of this function */ + { + MSYMBOL_TYPE (msym) = mst_text; + return orig_pc == pc ? 0 : pc & ~3; + } + } + +/*--------------------------------------------------------------------------*/ + } + + /* It's a stub. Search for a branch and figure out where it goes. + Note we have to handle multi insn branch sequences like ldil;ble. + Most (all?) other branches can be determined by examining the contents + of certain registers and the stack. */ + + loc = pc; + curr_inst = 0; + prev_inst = 0; + while (1) + { + /* Make sure we haven't walked outside the range of this stub. */ + if (u != find_unwind_entry (loc)) + { + warning ("Unable to find branch in linker stub"); + return orig_pc == pc ? 0 : pc & ~3; + } + + prev_inst = curr_inst; + curr_inst = read_memory_integer (loc, 4); + + /* Does it look like a branch external using %r1? Then it's the + branch from the stub to the actual function. */ + if ((curr_inst & 0xffe0e000) == 0xe0202000) + { + /* Yup. See if the previous instruction loaded + a value into %r1. If so compute and return the jump address. */ + if ((prev_inst & 0xffe00000) == 0x20200000) + return (extract_21 (prev_inst) + extract_17 (curr_inst)) & ~3; + else + { + warning ("Unable to find ldil X,%%r1 before ble Y(%%sr4,%%r1)."); + return orig_pc == pc ? 0 : pc & ~3; + } + } + + /* Does it look like a be 0(sr0,%r21)? OR + Does it look like a be, n 0(sr0,%r21)? OR + Does it look like a bve (r21)? (this is on PA2.0) + Does it look like a bve, n(r21)? (this is also on PA2.0) + That's the branch from an + import stub to an export stub. + + It is impossible to determine the target of the branch via + simple examination of instructions and/or data (consider + that the address in the plabel may be the address of the + bind-on-reference routine in the dynamic loader). + + So we have try an alternative approach. + + Get the name of the symbol at our current location; it should + be a stub symbol with the same name as the symbol in the + shared library. + + Then lookup a minimal symbol with the same name; we should + get the minimal symbol for the target routine in the shared + library as those take precedence of import/export stubs. */ + if ((curr_inst == 0xe2a00000) || + (curr_inst == 0xe2a00002) || + (curr_inst == 0xeaa0d000) || + (curr_inst == 0xeaa0d002)) + { + struct minimal_symbol *stubsym, *libsym; + + stubsym = lookup_minimal_symbol_by_pc (loc); + if (stubsym == NULL) + { + warning ("Unable to find symbol for 0x%lx", (long) loc); + return orig_pc == pc ? 0 : pc & ~3; + } + + libsym = lookup_minimal_symbol (SYMBOL_NAME (stubsym), NULL, NULL); + if (libsym == NULL) + { + warning ("Unable to find library symbol for %s\n", + SYMBOL_NAME (stubsym)); + return orig_pc == pc ? 0 : pc & ~3; + } + + return SYMBOL_VALUE (libsym); + } + + /* Does it look like bl X,%rp or bl X,%r0? Another way to do a + branch from the stub to the actual function. */ + /*elz */ + else if ((curr_inst & 0xffe0e000) == 0xe8400000 + || (curr_inst & 0xffe0e000) == 0xe8000000 + || (curr_inst & 0xffe0e000) == 0xe800A000) + return (loc + extract_17 (curr_inst) + 8) & ~3; + + /* Does it look like bv (rp)? Note this depends on the + current stack pointer being the same as the stack + pointer in the stub itself! This is a branch on from the + stub back to the original caller. */ + /*else if ((curr_inst & 0xffe0e000) == 0xe840c000) */ + else if ((curr_inst & 0xffe0f000) == 0xe840c000) + { + /* Yup. See if the previous instruction loaded + rp from sp - 8. */ + if (prev_inst == 0x4bc23ff1) + return (read_memory_integer + (read_register (PA_GR30_REGNUM) - 8, 4)) & ~3; + else + { + warning ("Unable to find restore of %%rp before bv (%%rp)."); + return orig_pc == pc ? 0 : pc & ~3; + } + } + + /* elz: added this case to capture the new instruction + at the end of the return part of an export stub used by + the PA2.0: BVE, n (rp) */ + else if ((curr_inst & 0xffe0f000) == 0xe840d000) + { + return (read_memory_integer + (read_register (PA_GR30_REGNUM) - 24, REGISTER_SIZE)) & ~3; + } + + /* What about be,n 0(sr0,%rp)? It's just another way we return to + the original caller from the stub. Used in dynamic executables. */ + else if (curr_inst == 0xe0400002) + { + /* The value we jump to is sitting in sp - 24. But that's + loaded several instructions before the be instruction. + I guess we could check for the previous instruction being + mtsp %r1,%sr0 if we want to do sanity checking. */ + return (read_memory_integer + (read_register (PA_GR30_REGNUM) - 24, REGISTER_SIZE)) & ~3; + } + + /* Haven't found the branch yet, but we're still in the stub. + Keep looking. */ + loc += 4; + } +} + +/* Return the address of the PC after the last prologue instruction if + we can determine it from the debug symbols. Else return zero. */ + +static CORE_ADDR +after_prologue (CORE_ADDR pc) +{ + struct symtab_and_line sal; + CORE_ADDR func_addr, func_end; + struct symbol *f; + asection *sect; + + if (DEBUG_PA_GROK) + printf ("after_prologue (%#lx)\n", pc); + + sect = find_pc_overlay (pc); + + /* If we can not find the symbol in the partial symbol table, then + there is no hope we can determine the function's start address + with this code. */ + if (!find_pc_sect_partial_function (pc, sect, NULL, &func_addr, &func_end)) + return 0; + + if (DEBUG_PA_GROK) + printf (" func_addr = %#lx, func_end = %#lx\n", func_addr, func_end); + + /* Get the line associated with FUNC_ADDR. */ + sal = find_pc_sect_line (func_addr, sect, 0); + + /* There are only two cases to consider. First, the end of the source line + is within the function bounds. In that case we return the end of the + source line. Second is the end of the source line extends beyond the + bounds of the current function. We need to use the slow code to + examine instructions in that case. + + Anything else is simply a bug elsewhere. Fixing it here is absolutely + the wrong thing to do. In fact, it should be entirely possible for this + function to always return zero since the slow instruction scanning code + is supposed to *always* work. If it does not, then it is a bug. */ + if (sal.end > func_addr && sal.end < func_end) + return sal.end; + return 0; +} + +/* Advance PC across any function entry prologue instructions + to reach some "real" code. + Returns either PC itself if the code at PC does not look like a + function prologue; otherwise returns an address that (if we're + lucky) follows the prologue. */ + +static CORE_ADDR +pa_skip_prologue (CORE_ADDR pc) +{ + CORE_ADDR post_prologue_pc; + + if (DEBUG_PA_GROK) + printf ("skip_prologue (%#lx)\n", pc); + + /* See if we can determine the end of the prologue via the symbol table. + If so, then return either PC, or the PC after the prologue, whichever + is greater. */ + + post_prologue_pc = after_prologue (pc); + + if (DEBUG_PA_GROK > 1) + printf (" post_prologue_pc = %#lx\n", post_prologue_pc); + + /* If after_prologue returned a useful address, then use it. Else + fall back on the instruction skipping code. + + Some folks have claimed this causes problems because the breakpoint + may be the first instruction of the prologue. If that happens, then + the instruction skipping code has a bug that needs to be fixed. */ + if (post_prologue_pc != 0) + return pc >= post_prologue_pc ? pc : post_prologue_pc; + + return pa_grok_prologue (pc, NULL); +} + +/* Sequence of bytes for breakpoint instruction. */ +static const unsigned char * +pa_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr) +{ + static const unsigned char breakpoint[4] = {0x00, 0x01, 0x00, 0x04}; + *lenptr = sizeof (breakpoint); + return breakpoint; +} + +static void +pa_remote_translate_xfer_address (CORE_ADDR memaddr, int nr_bytes, + CORE_ADDR *targ_addr, int *targ_len) +{ + *targ_addr = memaddr; + *targ_len = nr_bytes; +} + +void +pa_skip_permanent_breakpoint (void) +{ + /* To step over a breakpoint instruction on the PA takes some + fiddling with the instruction address queue. + + When we stop at a breakpoint, the IA queue front (the instruction + we're executing now) points at the breakpoint instruction, and + the IA queue back (the next instruction to execute) points to + whatever instruction we would execute after the breakpoint, if it + were an ordinary instruction. This is the case even if the + breakpoint is in the delay slot of a branch instruction. + + Clearly, to step past the breakpoint, we need to set the queue + front to the back. But what do we put in the back? What + instruction comes after that one? Because of the branch delay + slot, the next insn is always at the back + 4. */ + int tail; + + tail = read_register (PA_PCOQ_TAIL_REGNUM); + write_register (PA_PCOQ_HEAD_REGNUM, tail); + write_register (PA_PCSQ_HEAD_REGNUM, read_register (PA_PCSQ_TAIL_REGNUM)); + + write_register (PA_PCOQ_TAIL_REGNUM, tail + 4); + /* We can leave the tail's space the same, since there's no jump. */ +} + +static void +unwind_command (char *exp, int from_tty) +{ + CORE_ADDR address; + struct unwind_table_entry *u; + + /* If we have an expression, evaluate it and use it as the address. */ + + if (exp != 0 && *exp != 0) + address = parse_and_eval_address (exp); + else + return; + + u = find_unwind_entry (address); + + if (!u) + { + printf_unfiltered ("Can't find unwind table entry for %s\n", exp); + return; + } + + printf_unfiltered ("unwind_table_entry (%p):\n", u); + + printf_unfiltered ("\tregion_start = "); + print_address (u->region_start, gdb_stdout); + + printf_unfiltered ("\n\tregion_end = "); + print_address (u->region_end, gdb_stdout); + +#ifdef __STDC__ +#define pif(FLD) if (u->FLD) printf_unfiltered (" "#FLD); +#else +#define pif(FLD) if (u->FLD) printf_unfiltered (" FLD"); +#endif + + printf_unfiltered ("\n\tflags ="); + pif (Cannot_unwind); + pif (Millicode); + pif (Millicode_save_sr0); + pif (Entry_SR); + pif (Args_stored); + pif (Variable_Frame); + pif (Separate_Package_Body); + pif (Frame_Extension_Millicode); + pif (Stack_Overflow_Check); + pif (Two_Instruction_SP_Increment); + pif (Ada_Region); + pif (Save_SP); + pif (Save_RP); + pif (Save_MRP_in_frame); + pif (extn_ptr_defined); + pif (Cleanup_defined); + pif (MPE_XL_interrupt_marker); + pif (HP_UX_interrupt_marker); + pif (Large_frame); + + putchar_unfiltered ('\n'); + +#ifdef __STDC__ +#define pin(FLD) printf_unfiltered ("\t"#FLD" = 0x%x\n", u->FLD); +#else +#define pin(FLD) printf_unfiltered ("\tFLD = 0x%x\n", u->FLD); +#endif + + pin (Region_description); + pin (Entry_FR); + pin (Entry_GR); + pin (Total_frame_size); +} + +void pa_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + fprintf_unfiltered (file, + "gdbarch_dump_tdep: OS_IDENT = %#x\n", + tdep->os_ident); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: IS_ELF = %d\n", + tdep->is_elf); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: IS_ELF64 = %d\n", + tdep->is_elf64); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: IN_SYSCALL = %p\n", + tdep->in_syscall); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: IN_INTERRUPT_HANDLER = %p\n", + tdep->in_interrupt_handler); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: IN_SIGTRAMP = %p\n", + tdep->in_sigtramp); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: FRAME_SAVED_PC_IN_INTERRUPT = %p\n", + tdep->frame_saved_pc_in_interrupt); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: FRAME_BASE_BEFORE_INTERRUPT = %p\n", + tdep->frame_base_before_interrupt); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: FRAME_FIND_SAVED_REGS_IN_INTERRUPT = %p\n", + tdep->frame_find_saved_regs_in_interrupt); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: FRAME_SAVED_PC_IN_SIGTRAMP = %p\n", + tdep->frame_saved_pc_in_sigtramp); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: FRAME_BASE_BEFORE_SIGTRAMP = %p\n", + tdep->frame_base_before_sigtramp); + fprintf_unfiltered (file, + "gdbarch_dump_tdep: FRAME_FIND_SAVED_REGS_IN_SIGTRAMP = %p\n", + tdep->frame_find_saved_regs_in_sigtramp); +} + +static struct gdbarch * +pa_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + struct gdbarch *gdbarch; + struct gdbarch_tdep *tdep; + int os_ident = -1; + unsigned int is_elf = 0; + unsigned int is_elf64 = 0; + + if (info.abfd != NULL + && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour) + { + is_elf = 1; + is_elf64 = elf_elfheader (info.abfd)->e_ident[EI_CLASS] == ELFCLASS64; + os_ident = elf_elfheader (info.abfd)->e_ident[EI_OSABI]; + } + + for (arches = gdbarch_list_lookup_by_info (arches, &info); + arches != NULL; + arches = gdbarch_list_lookup_by_info (arches->next, &info)) + { + if (gdbarch_tdep (arches->gdbarch)->os_ident == os_ident) + return arches->gdbarch; + } + + tdep = xmalloc (sizeof (struct gdbarch_tdep)); + gdbarch = gdbarch_alloc (&info, tdep); + tdep->os_ident = os_ident; + tdep->is_elf = is_elf; + tdep->is_elf64 = is_elf64; + + set_gdbarch_short_bit (gdbarch, 16); + set_gdbarch_int_bit (gdbarch, 32); + set_gdbarch_long_bit (gdbarch, is_elf64 ? 64: 32); + set_gdbarch_long_long_bit (gdbarch, 64); + set_gdbarch_float_bit (gdbarch, 32); + set_gdbarch_double_bit (gdbarch, 64); + set_gdbarch_long_double_bit (gdbarch, 64); + set_gdbarch_ptr_bit (gdbarch, is_elf64 ? 64 : 32); + /* default addr_bit, bfd_vma_bit. */ + /* set_gdbarch_ieee_float (gdbarch, 1); */ + + set_gdbarch_num_regs (gdbarch, NUM_REGS); + /* default num_pseudo_regs. */ + set_gdbarch_sp_regnum (gdbarch, PA_GR31_REGNUM); + set_gdbarch_fp_regnum (gdbarch, PA_GR3_REGNUM); + set_gdbarch_pc_regnum (gdbarch, PA_PCOQ_HEAD_REGNUM); + set_gdbarch_npc_regnum (gdbarch, PA_PCOQ_TAIL_REGNUM); + set_gdbarch_fp0_regnum (gdbarch, PA_FR0_REGNUM); + + set_gdbarch_register_name (gdbarch, pa_register_name); + set_gdbarch_register_size (gdbarch, is_elf64 ? 8 : 4); + set_gdbarch_register_bytes (gdbarch, (is_elf64 + ? (NUM_REGS * 8 + - (NUM_REGS - PA_FR0_REGNUM) * 4) + : NUM_REGS * 4)); + set_gdbarch_register_byte (gdbarch, (is_elf64 + ? pa64_register_byte + : pa_register_byte)); + set_gdbarch_register_raw_size (gdbarch, (is_elf64 + ? pa64_register_raw_size + : pa_register_raw_size)); + set_gdbarch_max_register_raw_size (gdbarch, is_elf64 ? 8 : 4); + set_gdbarch_register_virtual_size (gdbarch, (is_elf64 + ? pa64_register_raw_size + : pa_register_raw_size)); + set_gdbarch_max_register_virtual_size (gdbarch, is_elf64 ? 8 : 4); + set_gdbarch_register_virtual_type (gdbarch, (is_elf64 + ? pa64_register_virtual_type + : pa_register_virtual_type)); + set_gdbarch_do_registers_info (gdbarch, pa_do_registers_info); + + /* default register_sim_regno. */ + set_gdbarch_use_generic_dummy_frames (gdbarch, 0); + + set_gdbarch_believe_pcc_promotion (gdbarch, 1); + /* default believe_pcc_promotion_type. */ + + set_gdbarch_coerce_float_to_double (gdbarch, standard_coerce_float_to_double); + set_gdbarch_get_saved_register (gdbarch, generic_get_saved_register); + /* default register_convertible. */ + /* default register_convert_to_virtual. */ + /* default register_convert_to_raw. */ + /* default fetch_pseudo_register. */ + /* default store_pseudo_register. */ + /* default pointer_to_address. */ + /* default address_to_pointer. */ + /* default return_value_on_stack. */ + set_gdbarch_extract_return_value (gdbarch, (is_elf64 + ? pa64_extract_return_value + : pa_extract_return_value)); + set_gdbarch_push_arguments (gdbarch, (is_elf64 + ? pa64_push_arguments + : pa_push_arguments)); + set_gdbarch_push_return_address (gdbarch, pa_push_return_address); + set_gdbarch_pop_frame (gdbarch, pa_pop_frame); + set_gdbarch_store_struct_return (gdbarch, pa_store_struct_return); + set_gdbarch_deprecated_store_return_value (gdbarch, pa_store_return_value); + set_gdbarch_deprecated_extract_struct_value_address (gdbarch, + pa_extract_struct_value_address); + set_gdbarch_use_struct_convention (gdbarch, pa_use_struct_convention); + set_gdbarch_frame_init_saved_regs (gdbarch, pa_frame_init_saved_regs); + set_gdbarch_init_extra_frame_info (gdbarch, pa_init_extra_frame_info); + set_gdbarch_skip_prologue (gdbarch, pa_skip_prologue); + set_gdbarch_prologue_frameless_p (gdbarch, pa_prologue_frameless_p); + set_gdbarch_inner_than (gdbarch, pa_inner_than); + set_gdbarch_breakpoint_from_pc (gdbarch, pa_breakpoint_from_pc); + /* default memory_insert_breakpoint. */ + /* default memory_remove_breakpoint. */ + set_gdbarch_decr_pc_after_break (gdbarch, 0); + set_gdbarch_function_start_offset (gdbarch, 0); + set_gdbarch_remote_translate_xfer_address (gdbarch, + pa_remote_translate_xfer_address); + set_gdbarch_frame_args_skip (gdbarch, 0); + set_gdbarch_frameless_function_invocation (gdbarch, + pa_frameless_function_invocation); + set_gdbarch_frame_chain (gdbarch, pa_frame_chain); + set_gdbarch_frame_chain_valid (gdbarch, generic_func_frame_chain_valid); + set_gdbarch_frame_saved_pc (gdbarch, pa_frame_saved_pc); + set_gdbarch_frame_args_address (gdbarch, pa_frame_args_address); + set_gdbarch_frame_locals_address (gdbarch, pa_frame_locals_address); + set_gdbarch_saved_pc_after_call (gdbarch, pa_saved_pc_after_call); + set_gdbarch_frame_num_args (gdbarch, frame_num_args_unknown); + set_gdbarch_stack_align (gdbarch, (is_elf64 + ? pa64_stack_align + : pa_stack_align)); + /* Default extra_stack_alignment_needed. */ + set_gdbarch_reg_struct_has_addr (gdbarch, pa_reg_struct_has_addr); + /* Default save_dummy_frame_tos */ + set_gdbarch_float_format (gdbarch, &floatformat_ieee_single_big); + set_gdbarch_double_format (gdbarch, &floatformat_ieee_double_big); + set_gdbarch_long_double_format (gdbarch, &floatformat_ieee_double_big); + /* Default convert_from_func_ptr_addr. */ + +#if 0 + if (os_ident == ELFOSABI_LINUX) +#endif + pa_linux_initialize_tdep (gdbarch, is_elf64); +#if 0 + /* Disable hpux support as som stuff only compiles native. Sheesh. */ + else + pa_hpux_initialize_tdep (gdbarch, is_elf64); +#endif + + return gdbarch; +} + +void +_initialize_pa_tdep (void) +{ + gdbarch_register (bfd_arch_hppa, pa_gdbarch_init, pa_dump_tdep); + + tm_print_insn = print_insn_hppa; + tm_print_insn_info.bytes_per_line = 4; + + add_cmd ("unwind", class_maintenance, unwind_command, + "Print unwind table entry at given address.", + &maintenanceprintlist); +#if DEBUG + add_show_from_set (add_set_cmd ("frame", class_maintenance, var_zinteger, + (char *) &framedebug, + "Set frame debugging.\n\ +When non-zero, frame debugging is enabled.", + &setdebuglist), + &showdebuglist); + add_show_from_set (add_set_cmd ("grok", class_maintenance, var_zinteger, + (char *) &grokdebug, + "Set prologue debugging.\n\ +When non-zero, prologue debugging is enabled.", + &setdebuglist), + &showdebuglist); +#endif +}