diff options
author | Guy Martin <gmsoft@gentoo.org> | 2003-03-24 13:55:16 +0000 |
---|---|---|
committer | Guy Martin <gmsoft@gentoo.org> | 2003-03-24 13:55:16 +0000 |
commit | 08d1af5101365df0582bcbd1a5fe8e4c218a08f9 (patch) | |
tree | 28df2f1fc6009da34b41f924ac38ff3cf13aa2f0 /sys-devel/gdb | |
parent | Added patch for hppa support. Added hppa to keywords. (diff) | |
download | gentoo-2-08d1af5101365df0582bcbd1a5fe8e4c218a08f9.tar.gz gentoo-2-08d1af5101365df0582bcbd1a5fe8e4c218a08f9.tar.bz2 gentoo-2-08d1af5101365df0582bcbd1a5fe8e4c218a08f9.zip |
Oops I forgot cvs add before commit :)
Diffstat (limited to 'sys-devel/gdb')
-rw-r--r-- | sys-devel/gdb/files/gdb-5.3-hppa-01.patch | 5579 | ||||
-rw-r--r-- | sys-devel/gdb/files/gdb-5.3-hppa-02.patch | 24 | ||||
-rw-r--r-- | sys-devel/gdb/files/gdb-5.3-hppa-03.patch | 67 |
3 files changed, 5670 insertions, 0 deletions
diff --git a/sys-devel/gdb/files/gdb-5.3-hppa-01.patch b/sys-devel/gdb/files/gdb-5.3-hppa-01.patch new file mode 100644 index 000000000000..7c3f79929152 --- /dev/null +++ b/sys-devel/gdb/files/gdb-5.3-hppa-01.patch @@ -0,0 +1,5579 @@ +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 <unistd.h> ++ ++#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 <signal.h> ++#include <sys/ptrace.h> ++#include <sys/wait.h> ++#ifdef HAVE_SYS_REG_H ++#include <sys/reg.h> ++#endif ++#include <sys/user.h> ++ ++#include <asm/offset.h> ++#include <sys/procfs.h> ++ ++/* 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 <asm/ucontext.h> ++#include <asm/rt_sigframe.h> ++#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 ++} diff --git a/sys-devel/gdb/files/gdb-5.3-hppa-02.patch b/sys-devel/gdb/files/gdb-5.3-hppa-02.patch new file mode 100644 index 000000000000..aced5e630a7f --- /dev/null +++ b/sys-devel/gdb/files/gdb-5.3-hppa-02.patch @@ -0,0 +1,24 @@ +--- gdb-5.2.cvs20020401/bfd/elf64-hppa.c~ Sun Mar 31 19:09:41 2002 ++++ gdb-5.2.cvs20020401/bfd/elf64-hppa.c Sat Apr 20 09:35:54 2002 +@@ -372,7 +372,8 @@ + i_ehdrp = elf_elfheader (abfd); + if (strcmp (bfd_get_target (abfd), "elf64-hppa-linux") == 0) + { +- if (i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_LINUX) ++ if (i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_LINUX && ++ i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_NONE) + return false; + } + else +--- gdb-5.2.cvs20020401/bfd/elf32-hppa.c~ Sun Mar 31 19:09:41 2002 ++++ gdb-5.2.cvs20020401/bfd/elf32-hppa.c Sat Apr 20 09:35:54 2002 +@@ -1038,7 +1038,8 @@ + i_ehdrp = elf_elfheader (abfd); + if (strcmp (bfd_get_target (abfd), "elf32-hppa-linux") == 0) + { +- if (i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_LINUX) ++ if (i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_LINUX && ++ i_ehdrp->e_ident[EI_OSABI] != ELFOSABI_NONE) + return false; + } + else diff --git a/sys-devel/gdb/files/gdb-5.3-hppa-03.patch b/sys-devel/gdb/files/gdb-5.3-hppa-03.patch new file mode 100644 index 000000000000..788fd183a77e --- /dev/null +++ b/sys-devel/gdb/files/gdb-5.3-hppa-03.patch @@ -0,0 +1,67 @@ +package: gdb +version: 5.2.cvs20020401-6 + +The attached patch fixes gdb on hppa so that it will print function +returns (e.g. "print foo(6)"), where previously it would crash the +inferior with protection faults. + +There are still some problems, for example, print strlen("foo") will +fail. To evaluate that, gdb has first got to get the inferior to call +malloc so there is somewhere to store the string. For a dynamically +linked program, gdb is finding a symbol 'malloc' in ld-2.2.5.so and +trying to call that, with a bogus dp value. If you link the program +statically, gdb finds the right 'malloc' to call and it works. + +Richard + + + +diff -ur gdb-5.2.cvs20020401.ori/gdb/pa-linux-tdep.c gdb-5.2.cvs20020401/gdb/pa-linux-tdep.c +--- gdb-5.2.cvs20020401.ori/gdb/pa-linux-tdep.c Fri May 31 17:57:21 2002 ++++ gdb-5.2.cvs20020401/gdb/pa-linux-tdep.c Fri May 31 18:25:22 2002 +@@ -38,8 +38,8 @@ + 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); ++ write_register_pid (PA_PCOQ_HEAD_REGNUM, pc | 3, ptid); ++ write_register_pid (PA_PCOQ_TAIL_REGNUM, (pc + 4) | 3, ptid); + } + + static CORE_ADDR +@@ -426,8 +426,8 @@ + /* 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); ++ pcoqh = read_register (PA_PCOQ_HEAD_REGNUM) & ~3; ++ pcoqt = read_register (PA_PCOQ_TAIL_REGNUM) & ~3; + + if (target_read_memory (pcoqh, buf, 4) != 0) + error ("Couldn't modify instruction address queue\n"); +@@ -547,8 +547,8 @@ + /* 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); ++ pcoqh = read_register (PA_PCOQ_HEAD_REGNUM) & ~3; ++ pcoqt = read_register (PA_PCOQ_TAIL_REGNUM) & ~3; + + if (target_read_memory (pcoqh, buf, 4) != 0) + error ("Couldn't modify instruction address queue\n"); +diff -ur gdb-5.2.cvs20020401.ori/gdb/pa-tdep.c gdb-5.2.cvs20020401/gdb/pa-tdep.c +--- gdb-5.2.cvs20020401.ori/gdb/pa-tdep.c Fri May 31 17:57:21 2002 ++++ gdb-5.2.cvs20020401/gdb/pa-tdep.c Fri May 31 01:01:42 2002 +@@ -2098,6 +2098,7 @@ + int reg_size = REGISTER_SIZE; + + fp = FRAME_FP (frame); ++ FRAME_INIT_SAVED_REGS(frame); + fsr = frame->saved_regs; + + #ifndef NO_PC_SPACE_QUEUE_RESTORE + + |