summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuy Martin <gmsoft@gentoo.org>2003-03-24 13:55:16 +0000
committerGuy Martin <gmsoft@gentoo.org>2003-03-24 13:55:16 +0000
commit08d1af5101365df0582bcbd1a5fe8e4c218a08f9 (patch)
tree28df2f1fc6009da34b41f924ac38ff3cf13aa2f0 /sys-devel/gdb
parentAdded patch for hppa support. Added hppa to keywords. (diff)
downloadgentoo-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.patch5579
-rw-r--r--sys-devel/gdb/files/gdb-5.3-hppa-02.patch24
-rw-r--r--sys-devel/gdb/files/gdb-5.3-hppa-03.patch67
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,
++ &registers[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 *) &registers[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
+
+