* objfiles.h (pc_in_section): New prototype.
[deliverable/binutils-gdb.git] / gdb / hppa-hpux-tdep.c
index 5d26c7c5442b570f562bf21f508266774f7b7243..88e59ba3e9c8f283d10b781f6187e0e93d854fa2 100644 (file)
-/* Target-dependent code for HPUX running on PA-RISC, for GDB.
+/* Target-dependent code for HP-UX on PA-RISC.
 
-   Copyright 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002-2013 Free Software Foundation, Inc.
 
-This file is part of GDB.
+   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 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 3 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.
+   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.  */
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
 #include "arch-utils.h"
 #include "gdbcore.h"
 #include "osabi.h"
-#include "gdb_string.h"
 #include "frame.h"
+#include "frame-unwind.h"
+#include "trad-frame.h"
+#include "symtab.h"
+#include "objfiles.h"
+#include "inferior.h"
+#include "infcall.h"
+#include "observer.h"
+#include "hppa-tdep.h"
+#include "solib-som.h"
+#include "solib-pa64.h"
+#include "regset.h"
+#include "regcache.h"
+#include "exceptions.h"
+
+#include "gdb_string.h"
+
+#define IS_32BIT_TARGET(_gdbarch) \
+       ((gdbarch_tdep (_gdbarch))->bytes_per_address == 4)
+
+/* Bit in the `ss_flag' member of `struct save_state' that indicates
+   that the 64-bit register values are live.  From
+   <machine/save_state.h>.  */
+#define HPPA_HPUX_SS_WIDEREGS          0x40
+
+/* Offsets of various parts of `struct save_state'.  From
+   <machine/save_state.h>.  */
+#define HPPA_HPUX_SS_FLAGS_OFFSET      0
+#define HPPA_HPUX_SS_NARROW_OFFSET     4
+#define HPPA_HPUX_SS_FPBLOCK_OFFSET    256
+#define HPPA_HPUX_SS_WIDE_OFFSET        640
+
+/* The size of `struct save_state.  */
+#define HPPA_HPUX_SAVE_STATE_SIZE      1152
+
+/* The size of `struct pa89_save_state', which corresponds to PA-RISC
+   1.1, the lowest common denominator that we support.  */
+#define HPPA_HPUX_PA89_SAVE_STATE_SIZE 512
+
 
 /* Forward declarations.  */
 extern void _initialize_hppa_hpux_tdep (void);
 extern initialize_file_ftype _initialize_hppa_hpux_tdep;
 
-/* FIXME: brobecker 2002-12-25.  The following functions will eventually
-   become static, after the multiarching conversion is done.  */
-int hppa_hpux_pc_in_sigtramp (CORE_ADDR pc, char *name);
-void hppa_hpux_frame_saved_pc_in_sigtramp (struct frame_info *fi,
-                                           CORE_ADDR *tmp);
-void hppa_hpux_frame_base_before_sigtramp (struct frame_info *fi,
-                                           CORE_ADDR *tmp);
-void hppa_hpux_frame_find_saved_regs_in_sigtramp
-      (struct frame_info *fi, CORE_ADDR *fsr);
+/* Return one if PC is in the call path of a trampoline, else return zero.
 
-int
-hppa_hpux_pc_in_sigtramp (CORE_ADDR pc, char *name)
+   Note we return one for *any* call trampoline (long-call, arg-reloc), not
+   just shared library trampolines (import, export).  */
+
+static int
+hppa32_hpux_in_solib_call_trampoline (struct gdbarch *gdbarch, CORE_ADDR pc)
 {
-  /* Actually, for a PA running HPUX the kernel calls the signal handler
-     without an intermediate trampoline.  Luckily the kernel always sets
-     the return pointer for the signal handler to point to _sigreturn.  */
-  return (name && (strcmp ("_sigreturn", name) == 0));
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct bound_minimal_symbol minsym;
+  struct unwind_table_entry *u;
+
+  /* First see if PC is in one of the two C-library trampolines.  */
+  if (pc == hppa_symbol_address("$$dyncall") 
+      || pc == hppa_symbol_address("_sr4export"))
+    return 1;
+
+  minsym = lookup_minimal_symbol_by_pc (pc);
+  if (minsym.minsym
+      && strcmp (SYMBOL_LINKAGE_NAME (minsym.minsym), ".stub") == 0)
+    return 1;
+
+  /* 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, byte_order);
+
+         /* 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."));
+      return 0;
+    }
+
+  /* Unknown stub type.  For now, just return zero.  */
+  return 0;
 }
 
-/* For hppa_hpux_frame_saved_pc_in_sigtramp, 
-   hppa_hpux_frame_base_before_sigtramp and
-   hppa_hpux_frame_find_saved_regs_in_sigtramp:
+static int
+hppa64_hpux_in_solib_call_trampoline (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
 
-   The signal context structure pointer is always saved at the base
-   of the frame which "calls" the signal handler.  We only want to find
-   the hardware save state structure, which lives 10 32bit words into
-   sigcontext structure.
+  /* 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.
 
-   Within the hardware save state structure, registers are found in the
-   same order as the register numbers in GDB.
+     The heuristic is simple.  Try to lookup the current PC value in th
+     minimal symbol table.  If that fails, then assume we are not in a
+     stub and return.
 
-   At one time we peeked at %r31 rather than the PC queues to determine
-   what instruction took the fault.  This was done on purpose, but I don't
-   remember why.  Looking at the PC queues is really the right way, and
-   I don't remember why that didn't work when this code was originally
-   written.  */
+     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.
 
-void
-hppa_hpux_frame_saved_pc_in_sigtramp (struct frame_info *fi, CORE_ADDR *tmp)
+     Finally peek at the instructions to see if they look like a stub.  */
+  struct bound_minimal_symbol minsym;
+  asection *sec;
+  CORE_ADDR addr;
+  int insn;
+
+  minsym = lookup_minimal_symbol_by_pc (pc);
+  if (! minsym.minsym)
+    return 0;
+
+  sec = SYMBOL_OBJ_SECTION (minsym.objfile, minsym.minsym)->the_bfd_section;
+
+  if (bfd_get_section_vma (sec->owner, sec) <= pc
+      && pc < (bfd_get_section_vma (sec->owner, sec)
+                + bfd_section_size (sec->owner, sec)))
+      return 0;
+
+  /* We might be in a stub.  Peek at the instructions.  Stubs are 3
+     instructions long.  */
+  insn = read_memory_integer (pc, 4, byte_order);
+
+  /* Find out where we think we are within the stub.  */
+  if ((insn & 0xffffc00e) == 0x53610000)
+    addr = pc;
+  else if ((insn & 0xffffffff) == 0xe820d000)
+    addr = pc - 4;
+  else if ((insn & 0xffffc00e) == 0x537b0000)
+    addr = pc - 8;
+  else
+    return 0;
+
+  /* Now verify each insn in the range looks like a stub instruction.  */
+  insn = read_memory_integer (addr, 4, byte_order);
+  if ((insn & 0xffffc00e) != 0x53610000)
+    return 0;
+       
+  /* Now verify each insn in the range looks like a stub instruction.  */
+  insn = read_memory_integer (addr + 4, 4, byte_order);
+  if ((insn & 0xffffffff) != 0xe820d000)
+    return 0;
+    
+  /* Now verify each insn in the range looks like a stub instruction.  */
+  insn = read_memory_integer (addr + 8, 4, byte_order);
+  if ((insn & 0xffffc00e) != 0x537b0000)
+    return 0;
+
+  /* Looks like a stub.  */
+  return 1;
+}
+
+/* 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).  */
+
+static int
+hppa_hpux_in_solib_return_trampoline (struct gdbarch *gdbarch,
+                                     CORE_ADDR pc, const char *name)
 {
-  *tmp = read_memory_integer (get_frame_base (fi) + (43 * 4), 4);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  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, byte_order);
+
+         /* 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."));
+      return 0;
+    }
+
+  /* Unknown stub type.  For now, just return zero.  */
+  return 0;
+
 }
 
-void
-hppa_hpux_frame_base_before_sigtramp (struct frame_info *fi,
-                                      CORE_ADDR *tmp)
+/* 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.  */
+
+static CORE_ADDR
+hppa_hpux_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
 {
-  *tmp = read_memory_integer (get_frame_base (fi) + (40 * 4), 4);
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+  long orig_pc = pc;
+  long prev_inst, curr_inst, loc;
+  struct bound_minimal_symbol msym;
+  struct unwind_table_entry *u;
+
+  /* Addresses passed to dyncall may *NOT* be the actual address
+     of the function.  So we may have to do something special.  */
+  if (pc == hppa_symbol_address("$$dyncall"))
+    {
+      pc = (CORE_ADDR) get_frame_register_unsigned (frame, 22);
+
+      /* 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 & ~0x3, word_size,
+                                             byte_order);
+    }
+  if (pc == hppa_symbol_address("$$dyncall_external"))
+    {
+      pc = (CORE_ADDR) get_frame_register_unsigned (frame, 22);
+      pc = (CORE_ADDR) read_memory_integer (pc & ~0x3, word_size, byte_order);
+    }
+  else if (pc == hppa_symbol_address("_sr4export"))
+    pc = (CORE_ADDR) get_frame_register_unsigned (frame, 22);
+
+  /* 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.  */
+  /* 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.minsym == NULL
+         || MSYMBOL_TYPE (msym.minsym) != mst_solib_trampoline)
+       return orig_pc == pc ? 0 : pc & ~0x3;
+
+      else if (msym.minsym != NULL
+              && MSYMBOL_TYPE (msym.minsym) == 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
+               && strcmp (SYMBOL_LINKAGE_NAME (msymbol),
+                           SYMBOL_LINKAGE_NAME (msym.minsym)) == 0)
+             {
+               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.minsym) = mst_text;
+             return orig_pc == pc ? 0 : pc & ~0x3;
+           }
+       }
+
+/*--------------------------------------------------------------------------*/
+    }
+
+  /* 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 & ~0x3;
+       }
+
+      prev_inst = curr_inst;
+      curr_inst = read_memory_integer (loc, 4, byte_order);
+
+      /* 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 (hppa_extract_21 (prev_inst) 
+                   + hppa_extract_17 (curr_inst)) & ~0x3;
+         else
+           {
+             warning (_("Unable to find ldil X,%%r1 "
+                        "before ble Y(%%sr4,%%r1)."));
+             return orig_pc == pc ? 0 : pc & ~0x3;
+           }
+       }
+
+      /* 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 bound_minimal_symbol stubsym;
+         struct minimal_symbol *libsym;
+
+         stubsym = lookup_minimal_symbol_by_pc (loc);
+         if (stubsym.minsym == NULL)
+           {
+             warning (_("Unable to find symbol for 0x%lx"), loc);
+             return orig_pc == pc ? 0 : pc & ~0x3;
+           }
+
+         libsym = lookup_minimal_symbol (SYMBOL_LINKAGE_NAME (stubsym.minsym),
+                                         NULL, NULL);
+         if (libsym == NULL)
+           {
+             warning (_("Unable to find library symbol for %s."),
+                      SYMBOL_PRINT_NAME (stubsym.minsym));
+             return orig_pc == pc ? 0 : pc & ~0x3;
+           }
+
+         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 + hppa_extract_17 (curr_inst) + 8) & ~0x3;
+
+      /* 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)
+           {
+             CORE_ADDR sp;
+             sp = get_frame_register_unsigned (frame, HPPA_SP_REGNUM);
+             return read_memory_integer (sp - 8, 4, byte_order) & ~0x3;
+           }
+         else
+           {
+             warning (_("Unable to find restore of %%rp before bv (%%rp)."));
+             return orig_pc == pc ? 0 : pc & ~0x3;
+           }
+       }
+
+      /* 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
+                 (get_frame_register_unsigned (frame, HPPA_SP_REGNUM) - 24,
+                  word_size, byte_order)) & ~0x3;
+       }
+
+      /* 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
+                 (get_frame_register_unsigned (frame, HPPA_SP_REGNUM) - 24,
+                  word_size, byte_order)) & ~0x3;
+       }
+
+      /* Haven't found the branch yet, but we're still in the stub.
+         Keep looking.  */
+      loc += 4;
+    }
 }
 
-void
-hppa_hpux_frame_find_saved_regs_in_sigtramp (struct frame_info *fi,
-                                            CORE_ADDR *fsr)
+static void
+hppa_skip_permanent_breakpoint (struct regcache *regcache)
+{
+  /* 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.  */
+
+  ULONGEST pcoq_tail, pcsq_tail;
+  regcache_cooked_read_unsigned (regcache, HPPA_PCOQ_TAIL_REGNUM, &pcoq_tail);
+  regcache_cooked_read_unsigned (regcache, HPPA_PCSQ_TAIL_REGNUM, &pcsq_tail);
+
+  regcache_cooked_write_unsigned (regcache, HPPA_PCOQ_HEAD_REGNUM, pcoq_tail);
+  regcache_cooked_write_unsigned (regcache, HPPA_PCSQ_HEAD_REGNUM, pcsq_tail);
+
+  regcache_cooked_write_unsigned (regcache,
+                                 HPPA_PCOQ_TAIL_REGNUM, pcoq_tail + 4);
+  /* We can leave the tail's space the same, since there's no jump.  */
+}
+
+
+/* Signal frames.  */
+struct hppa_hpux_sigtramp_unwind_cache
+{
+  CORE_ADDR base;
+  struct trad_frame_saved_reg *saved_regs;
+};
+
+static int hppa_hpux_tramp_reg[] = {
+  HPPA_SAR_REGNUM,
+  HPPA_PCOQ_HEAD_REGNUM,
+  HPPA_PCSQ_HEAD_REGNUM,
+  HPPA_PCOQ_TAIL_REGNUM,
+  HPPA_PCSQ_TAIL_REGNUM,
+  HPPA_EIEM_REGNUM,
+  HPPA_IIR_REGNUM,
+  HPPA_ISR_REGNUM,
+  HPPA_IOR_REGNUM,
+  HPPA_IPSW_REGNUM,
+  -1,
+  HPPA_SR4_REGNUM,
+  HPPA_SR4_REGNUM + 1,
+  HPPA_SR4_REGNUM + 2,
+  HPPA_SR4_REGNUM + 3,
+  HPPA_SR4_REGNUM + 4,
+  HPPA_SR4_REGNUM + 5,
+  HPPA_SR4_REGNUM + 6,
+  HPPA_SR4_REGNUM + 7,
+  HPPA_RCR_REGNUM,
+  HPPA_PID0_REGNUM,
+  HPPA_PID1_REGNUM,
+  HPPA_CCR_REGNUM,
+  HPPA_PID2_REGNUM,
+  HPPA_PID3_REGNUM,
+  HPPA_TR0_REGNUM,
+  HPPA_TR0_REGNUM + 1,
+  HPPA_TR0_REGNUM + 2,
+  HPPA_CR27_REGNUM
+};
+
+static struct hppa_hpux_sigtramp_unwind_cache *
+hppa_hpux_sigtramp_frame_unwind_cache (struct frame_info *this_frame,
+                                      void **this_cache)
+
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct hppa_hpux_sigtramp_unwind_cache *info;
+  unsigned int flag;
+  CORE_ADDR sp, scptr, off;
+  int i, incr, szoff;
+
+  if (*this_cache)
+    return *this_cache;
+
+  info = FRAME_OBSTACK_ZALLOC (struct hppa_hpux_sigtramp_unwind_cache);
+  *this_cache = info;
+  info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+
+  sp = get_frame_register_unsigned (this_frame, HPPA_SP_REGNUM);
+
+  if (IS_32BIT_TARGET (gdbarch))
+    scptr = sp - 1352;
+  else
+    scptr = sp - 1520;
+
+  off = scptr;
+
+  /* See /usr/include/machine/save_state.h for the structure of the
+     save_state_t structure.  */
+  
+  flag = read_memory_unsigned_integer (scptr + HPPA_HPUX_SS_FLAGS_OFFSET,
+                                      4, byte_order);
+
+  if (!(flag & HPPA_HPUX_SS_WIDEREGS))
+    {
+      /* Narrow registers.  */
+      off = scptr + HPPA_HPUX_SS_NARROW_OFFSET;
+      incr = 4;
+      szoff = 0;
+    }
+  else
+    {
+      /* Wide registers.  */
+      off = scptr + HPPA_HPUX_SS_WIDE_OFFSET + 8;
+      incr = 8;
+      szoff = (tdep->bytes_per_address == 4 ? 4 : 0);
+    }
+
+  for (i = 1; i < 32; i++)
+    {
+      info->saved_regs[HPPA_R0_REGNUM + i].addr = off + szoff;
+      off += incr;
+    }
+
+  for (i = 0; i < ARRAY_SIZE (hppa_hpux_tramp_reg); i++)
+    {
+      if (hppa_hpux_tramp_reg[i] > 0)
+        info->saved_regs[hppa_hpux_tramp_reg[i]].addr = off + szoff;
+
+      off += incr;
+    }
+
+  /* TODO: fp regs */
+
+  info->base = get_frame_register_unsigned (this_frame, HPPA_SP_REGNUM);
+
+  return info;
+}
+
+static void
+hppa_hpux_sigtramp_frame_this_id (struct frame_info *this_frame,
+                                  void **this_prologue_cache,
+                                  struct frame_id *this_id)
+{
+  struct hppa_hpux_sigtramp_unwind_cache *info
+    = hppa_hpux_sigtramp_frame_unwind_cache (this_frame, this_prologue_cache);
+
+  *this_id = frame_id_build (info->base, get_frame_pc (this_frame));
+}
+
+static struct value *
+hppa_hpux_sigtramp_frame_prev_register (struct frame_info *this_frame,
+                                       void **this_prologue_cache,
+                                       int regnum)
+{
+  struct hppa_hpux_sigtramp_unwind_cache *info
+    = hppa_hpux_sigtramp_frame_unwind_cache (this_frame, this_prologue_cache);
+
+  return hppa_frame_prev_register_helper (this_frame,
+                                         info->saved_regs, regnum);
+}
+
+static int
+hppa_hpux_sigtramp_unwind_sniffer (const struct frame_unwind *self,
+                                   struct frame_info *this_frame,
+                                   void **this_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct unwind_table_entry *u;
+  CORE_ADDR pc = get_frame_pc (this_frame);
+
+  u = find_unwind_entry (pc);
+
+  /* If this is an export stub, try to get the unwind descriptor for
+     the actual function itself.  */
+  if (u && u->stub_unwind.stub_type == EXPORT)
+    {
+      gdb_byte buf[HPPA_INSN_SIZE];
+      unsigned long insn;
+
+      if (!safe_frame_unwind_memory (this_frame, u->region_start,
+                                    buf, sizeof buf))
+       return 0;
+
+      insn = extract_unsigned_integer (buf, sizeof buf, byte_order);
+      if ((insn & 0xffe0e000) == 0xe8400000)
+       u = find_unwind_entry(u->region_start + hppa_extract_17 (insn) + 8);
+    }
+
+  if (u && u->HP_UX_interrupt_marker)
+    return 1;
+
+  return 0;
+}
+
+static const struct frame_unwind hppa_hpux_sigtramp_frame_unwind = {
+  SIGTRAMP_FRAME,
+  default_frame_unwind_stop_reason,
+  hppa_hpux_sigtramp_frame_this_id,
+  hppa_hpux_sigtramp_frame_prev_register,
+  NULL,
+  hppa_hpux_sigtramp_unwind_sniffer
+};
+
+static CORE_ADDR
+hppa32_hpux_find_global_pointer (struct gdbarch *gdbarch,
+                                struct value *function)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR faddr;
+  
+  faddr = value_as_address (function);
+
+  /* Is this a plabel? If so, dereference it to get the gp value.  */
+  if (faddr & 2)
+    {
+      int status;
+      gdb_byte buf[4];
+
+      faddr &= ~3;
+
+      status = target_read_memory (faddr + 4, buf, sizeof (buf));
+      if (status == 0)
+       return extract_unsigned_integer (buf, sizeof (buf), byte_order);
+    }
+
+  return gdbarch_tdep (gdbarch)->solib_get_got_by_pc (faddr);
+}
+
+static CORE_ADDR
+hppa64_hpux_find_global_pointer (struct gdbarch *gdbarch,
+                                struct value *function)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR faddr;
+  gdb_byte buf[32];
+
+  faddr = value_as_address (function);
+
+  if (pc_in_section (faddr, ".opd"))
+    {
+      target_read_memory (faddr, buf, sizeof (buf));
+      return extract_unsigned_integer (&buf[24], 8, byte_order);
+    }
+  else
+    {
+      return gdbarch_tdep (gdbarch)->solib_get_got_by_pc (faddr);
+    }
+}
+
+static unsigned int ldsid_pattern[] = {
+  0x000010a0, /* ldsid (rX),rY */
+  0x00001820, /* mtsp rY,sr0 */
+  0xe0000000  /* be,n (sr0,rX) */
+};
+
+static CORE_ADDR
+hppa_hpux_search_pattern (struct gdbarch *gdbarch,
+                         CORE_ADDR start, CORE_ADDR end,
+                         unsigned int *patterns, int count)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int num_insns = (end - start + HPPA_INSN_SIZE) / HPPA_INSN_SIZE;
+  unsigned int *insns;
+  gdb_byte *buf;
+  int offset, i;
+
+  buf = alloca (num_insns * HPPA_INSN_SIZE);
+  insns = alloca (num_insns * sizeof (unsigned int));
+
+  read_memory (start, buf, num_insns * HPPA_INSN_SIZE);
+  for (i = 0; i < num_insns; i++, buf += HPPA_INSN_SIZE)
+    insns[i] = extract_unsigned_integer (buf, HPPA_INSN_SIZE, byte_order);
+
+  for (offset = 0; offset <= num_insns - count; offset++)
+    {
+      for (i = 0; i < count; i++)
+        {
+         if ((insns[offset + i] & patterns[i]) != patterns[i])
+           break;
+       }
+      if (i == count)
+        break;
+    }
+
+  if (offset <= num_insns - count)
+    return start + offset * HPPA_INSN_SIZE;
+  else
+    return 0;
+}
+
+static CORE_ADDR
+hppa32_hpux_search_dummy_call_sequence (struct gdbarch *gdbarch, CORE_ADDR pc,
+                                       int *argreg)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct objfile *obj;
+  struct obj_section *sec;
+  struct hppa_objfile_private *priv;
+  struct frame_info *frame;
+  struct unwind_table_entry *u;
+  CORE_ADDR addr, rp;
+  gdb_byte buf[4];
+  unsigned int insn;
+
+  sec = find_pc_section (pc);
+  obj = sec->objfile;
+  priv = objfile_data (obj, hppa_objfile_priv_data);
+
+  if (!priv)
+    priv = hppa_init_objfile_priv_data (obj);
+  if (!priv)
+    error (_("Internal error creating objfile private data."));
+
+  /* Use the cached value if we have one.  */
+  if (priv->dummy_call_sequence_addr != 0)
+    {
+      *argreg = priv->dummy_call_sequence_reg;
+      return priv->dummy_call_sequence_addr;
+    }
+
+  /* First try a heuristic; if we are in a shared library call, our return
+     pointer is likely to point at an export stub.  */
+  frame = get_current_frame ();
+  rp = frame_unwind_register_unsigned (frame, 2);
+  u = find_unwind_entry (rp);
+  if (u && u->stub_unwind.stub_type == EXPORT)
+    {
+      addr = hppa_hpux_search_pattern (gdbarch,
+                                      u->region_start, u->region_end,
+                                      ldsid_pattern, 
+                                      ARRAY_SIZE (ldsid_pattern));
+      if (addr)
+       goto found_pattern;
+    }
+
+  /* Next thing to try is to look for an export stub.  */
+  if (priv->unwind_info)
+    {
+      int i;
+
+      for (i = 0; i < priv->unwind_info->last; i++)
+        {
+         struct unwind_table_entry *u;
+         u = &priv->unwind_info->table[i];
+         if (u->stub_unwind.stub_type == EXPORT)
+           {
+             addr = hppa_hpux_search_pattern (gdbarch,
+                                              u->region_start, u->region_end,
+                                              ldsid_pattern, 
+                                              ARRAY_SIZE (ldsid_pattern));
+             if (addr)
+               {
+                 goto found_pattern;
+               }
+           }
+       }
+    }
+
+  /* Finally, if this is the main executable, try to locate a sequence 
+     from noshlibs */
+  addr = hppa_symbol_address ("noshlibs");
+  sec = find_pc_section (addr);
+
+  if (sec && sec->objfile == obj)
+    {
+      CORE_ADDR start, end;
+
+      find_pc_partial_function (addr, NULL, &start, &end);
+      if (start != 0 && end != 0)
+        {
+         addr = hppa_hpux_search_pattern (gdbarch, start, end, ldsid_pattern,
+                                          ARRAY_SIZE (ldsid_pattern));
+         if (addr)
+           goto found_pattern;
+        }
+    }
+
+  /* Can't find a suitable sequence.  */
+  return 0;
+
+found_pattern:
+  target_read_memory (addr, buf, sizeof (buf));
+  insn = extract_unsigned_integer (buf, sizeof (buf), byte_order);
+  priv->dummy_call_sequence_addr = addr;
+  priv->dummy_call_sequence_reg = (insn >> 21) & 0x1f;
+
+  *argreg = priv->dummy_call_sequence_reg;
+  return priv->dummy_call_sequence_addr;
+}
+
+static CORE_ADDR
+hppa64_hpux_search_dummy_call_sequence (struct gdbarch *gdbarch, CORE_ADDR pc,
+                                       int *argreg)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct objfile *obj;
+  struct obj_section *sec;
+  struct hppa_objfile_private *priv;
+  CORE_ADDR addr;
+  struct minimal_symbol *msym;
+
+  sec = find_pc_section (pc);
+  obj = sec->objfile;
+  priv = objfile_data (obj, hppa_objfile_priv_data);
+
+  if (!priv)
+    priv = hppa_init_objfile_priv_data (obj);
+  if (!priv)
+    error (_("Internal error creating objfile private data."));
+
+  /* Use the cached value if we have one.  */
+  if (priv->dummy_call_sequence_addr != 0)
+    {
+      *argreg = priv->dummy_call_sequence_reg;
+      return priv->dummy_call_sequence_addr;
+    }
+
+  /* FIXME: Without stub unwind information, locating a suitable sequence is
+     fairly difficult.  For now, we implement a very naive and inefficient
+     scheme; try to read in blocks of code, and look for a "bve,n (rp)" 
+     instruction.  These are likely to occur at the end of functions, so
+     we only look at the last two instructions of each function.  */
+  ALL_OBJFILE_MSYMBOLS (obj, msym)
+    {
+      CORE_ADDR begin, end;
+      const char *name;
+      gdb_byte buf[2 * HPPA_INSN_SIZE];
+      int offset;
+
+      find_pc_partial_function (SYMBOL_VALUE_ADDRESS (msym), &name,
+                               &begin, &end);
+
+      if (name == NULL || begin == 0 || end == 0)
+        continue;
+
+      if (target_read_memory (end - sizeof (buf), buf, sizeof (buf)) == 0)
+        {
+         for (offset = 0; offset < sizeof (buf); offset++)
+           {
+             unsigned int insn;
+
+             insn = extract_unsigned_integer (buf + offset,
+                                              HPPA_INSN_SIZE, byte_order);
+             if (insn == 0xe840d002) /* bve,n (rp) */
+               {
+                 addr = (end - sizeof (buf)) + offset;
+                 goto found_pattern;
+               }
+           }
+       }
+    }
+
+  /* Can't find a suitable sequence.  */
+  return 0;
+
+found_pattern:
+  priv->dummy_call_sequence_addr = addr;
+  /* Right now we only look for a "bve,l (rp)" sequence, so the register is 
+     always HPPA_RP_REGNUM.  */
+  priv->dummy_call_sequence_reg = HPPA_RP_REGNUM;
+
+  *argreg = priv->dummy_call_sequence_reg;
+  return priv->dummy_call_sequence_addr;
+}
+
+static CORE_ADDR
+hppa_hpux_find_import_stub_for_addr (CORE_ADDR funcaddr)
+{
+  struct objfile *objfile;
+  struct bound_minimal_symbol funsym;
+  struct minimal_symbol *stubsym;
+  CORE_ADDR stubaddr;
+
+  funsym = lookup_minimal_symbol_by_pc (funcaddr);
+  stubaddr = 0;
+
+  ALL_OBJFILES (objfile)
+    {
+      stubsym = lookup_minimal_symbol_solib_trampoline
+       (SYMBOL_LINKAGE_NAME (funsym.minsym), objfile);
+
+      if (stubsym)
+       {
+         struct unwind_table_entry *u;
+
+         u = find_unwind_entry (SYMBOL_VALUE (stubsym));
+         if (u == NULL 
+             || (u->stub_unwind.stub_type != IMPORT
+                 && u->stub_unwind.stub_type != IMPORT_SHLIB))
+           continue;
+
+          stubaddr = SYMBOL_VALUE (stubsym);
+
+         /* If we found an IMPORT stub, then we can stop searching;
+            if we found an IMPORT_SHLIB, we want to continue the search
+            in the hopes that we will find an IMPORT stub.  */
+         if (u->stub_unwind.stub_type == IMPORT)
+           break;
+       }
+    }
+
+  return stubaddr;
+}
+
+static int
+hppa_hpux_sr_for_addr (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  int sr;
+  /* The space register to use is encoded in the top 2 bits of the address.  */
+  sr = addr >> (gdbarch_tdep (gdbarch)->bytes_per_address * 8 - 2);
+  return sr + 4;
+}
+
+static CORE_ADDR
+hppa_hpux_find_dummy_bpaddr (CORE_ADDR addr)
+{
+  /* In order for us to restore the space register to its starting state, 
+     we need the dummy trampoline to return to an instruction address in 
+     the same space as where we started the call.  We used to place the 
+     breakpoint near the current pc, however, this breaks nested dummy calls 
+     as the nested call will hit the breakpoint address and terminate 
+     prematurely.  Instead, we try to look for an address in the same space to 
+     put the breakpoint.  
+     
+     This is similar in spirit to putting the breakpoint at the "entry point"
+     of an executable.  */
+
+  struct obj_section *sec;
+  struct unwind_table_entry *u;
+  struct minimal_symbol *msym;
+  CORE_ADDR func;
+
+  sec = find_pc_section (addr);
+  if (sec)
+    {
+      /* First try the lowest address in the section; we can use it as long
+         as it is "regular" code (i.e. not a stub).  */
+      u = find_unwind_entry (obj_section_addr (sec));
+      if (!u || u->stub_unwind.stub_type == 0)
+        return obj_section_addr (sec);
+
+      /* Otherwise, we need to find a symbol for a regular function.  We
+         do this by walking the list of msymbols in the objfile.  The symbol
+        we find should not be the same as the function that was passed in.  */
+
+      /* FIXME: this is broken, because we can find a function that will be
+         called by the dummy call target function, which will still not 
+        work.  */
+
+      find_pc_partial_function (addr, NULL, &func, NULL);
+      ALL_OBJFILE_MSYMBOLS (sec->objfile, msym)
+       {
+         u = find_unwind_entry (SYMBOL_VALUE_ADDRESS (msym));
+         if (func != SYMBOL_VALUE_ADDRESS (msym) 
+             && (!u || u->stub_unwind.stub_type == 0))
+           return SYMBOL_VALUE_ADDRESS (msym);
+       }
+    }
+
+  warning (_("Cannot find suitable address to place dummy breakpoint; nested "
+            "calls may fail."));
+  return addr - 4;
+}
+
+static CORE_ADDR
+hppa_hpux_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp,
+                          CORE_ADDR funcaddr,
+                          struct value **args, int nargs,
+                          struct type *value_type,
+                          CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
+                          struct regcache *regcache)
+{
+  CORE_ADDR pc, stubaddr;
+  int argreg = 0;
+
+  pc = regcache_read_pc (regcache);
+
+  /* Note: we don't want to pass a function descriptor here; push_dummy_call
+     fills in the PIC register for us.  */
+  funcaddr = gdbarch_convert_from_func_ptr_addr (gdbarch, funcaddr, NULL);
+
+  /* The simple case is where we call a function in the same space that we are
+     currently in; in that case we don't really need to do anything.  */
+  if (hppa_hpux_sr_for_addr (gdbarch, pc)
+      == hppa_hpux_sr_for_addr (gdbarch, funcaddr))
+    {
+      /* Intraspace call.  */
+      *bp_addr = hppa_hpux_find_dummy_bpaddr (pc);
+      *real_pc = funcaddr;
+      regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, *bp_addr);
+
+      return sp;
+    }
+
+  /* In order to make an interspace call, we need to go through a stub.
+     gcc supplies an appropriate stub called "__gcc_plt_call", however, if
+     an application is compiled with HP compilers then this stub is not
+     available.  We used to fallback to "__d_plt_call", however that stub
+     is not entirely useful for us because it doesn't do an interspace
+     return back to the caller.  Also, on hppa64-hpux, there is no 
+     __gcc_plt_call available.  In order to keep the code uniform, we
+     instead don't use either of these stubs, but instead write our own
+     onto the stack.
+
+     A problem arises since the stack is located in a different space than
+     code, so in order to branch to a stack stub, we will need to do an
+     interspace branch.  Previous versions of gdb did this by modifying code
+     at the current pc and doing single-stepping to set the pcsq.  Since this
+     is highly undesirable, we use a different scheme:
+
+     All we really need to do the branch to the stub is a short instruction
+     sequence like this:
+      
+     PA1.1:
+               ldsid (rX),r1
+               mtsp r1,sr0
+               be,n (sr0,rX)
+
+     PA2.0:
+               bve,n (sr0,rX)
+
+     Instead of writing these sequences ourselves, we can find it in
+     the instruction stream that belongs to the current space.  While this
+     seems difficult at first, we are actually guaranteed to find the sequences
+     in several places:
+
+     For 32-bit code:
+     - in export stubs for shared libraries
+     - in the "noshlibs" routine in the main module
+
+     For 64-bit code:
+     - at the end of each "regular" function
+
+     We cache the address of these sequences in the objfile's private data
+     since these operations can potentially be quite expensive.
+
+     So, what we do is:
+     - write a stack trampoline
+     - look for a suitable instruction sequence in the current space
+     - point the sequence at the trampoline
+     - set the return address of the trampoline to the current space 
+       (see hppa_hpux_find_dummy_call_bpaddr)
+     - set the continuing address of the "dummy code" as the sequence.  */
+
+  if (IS_32BIT_TARGET (gdbarch))
+    {
+#define INSN(I1, I2, I3, I4) 0x ## I1, 0x ## I2, 0x ## I3, 0x ## I4
+     static const gdb_byte hppa32_tramp[] = {
+       INSN(0f,df,12,91), /* stw r31,-8(,sp) */
+       INSN(02,c0,10,a1), /* ldsid (,r22),r1 */
+       INSN(00,01,18,20), /* mtsp r1,sr0 */
+       INSN(e6,c0,00,00), /* be,l 0(sr0,r22),%sr0,%r31 */
+       INSN(08,1f,02,42), /* copy r31,rp */
+       INSN(0f,d1,10,82), /* ldw -8(,sp),rp */
+       INSN(00,40,10,a1), /* ldsid (,rp),r1 */
+       INSN(00,01,18,20), /* mtsp r1,sr0 */
+       INSN(e0,40,00,00), /* be 0(sr0,rp) */
+       INSN(08,00,02,40)  /* nop */
+      };
+
+      /* for hppa32, we must call the function through a stub so that on
+         return it can return to the space of our trampoline.  */
+      stubaddr = hppa_hpux_find_import_stub_for_addr (funcaddr);
+      if (stubaddr == 0)
+        error (_("Cannot call external function not referenced by application "
+              "(no import stub).\n"));
+      regcache_cooked_write_unsigned (regcache, 22, stubaddr);
+
+      write_memory (sp, hppa32_tramp, sizeof (hppa32_tramp));
+
+      *bp_addr = hppa_hpux_find_dummy_bpaddr (pc);
+      regcache_cooked_write_unsigned (regcache, 31, *bp_addr);
+
+      *real_pc = hppa32_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg);
+      if (*real_pc == 0)
+        error (_("Cannot make interspace call from here."));
+
+      regcache_cooked_write_unsigned (regcache, argreg, sp);
+
+      sp += sizeof (hppa32_tramp);
+    }
+  else
+    {
+      static const gdb_byte hppa64_tramp[] = {
+       INSN(ea,c0,f0,00), /* bve,l (r22),%r2 */
+       INSN(0f,df,12,d1), /* std r31,-8(,sp) */
+       INSN(0f,d1,10,c2), /* ldd -8(,sp),rp */
+       INSN(e8,40,d0,02), /* bve,n (rp) */
+       INSN(08,00,02,40)  /* nop */
+      };
+#undef INSN
+
+      /* for hppa64, we don't need to call through a stub; all functions
+         return via a bve.  */
+      regcache_cooked_write_unsigned (regcache, 22, funcaddr);
+      write_memory (sp, hppa64_tramp, sizeof (hppa64_tramp));
+
+      *bp_addr = pc - 4;
+      regcache_cooked_write_unsigned (regcache, 31, *bp_addr);
+
+      *real_pc = hppa64_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg);
+      if (*real_pc == 0)
+        error (_("Cannot make interspace call from here."));
+
+      regcache_cooked_write_unsigned (regcache, argreg, sp);
+
+      sp += sizeof (hppa64_tramp);
+    }
+
+  sp = gdbarch_frame_align (gdbarch, sp);
+
+  return sp;
+}
+
+\f
+
+static void
+hppa_hpux_supply_ss_narrow (struct regcache *regcache,
+                           int regnum, const gdb_byte *save_state)
+{
+  const gdb_byte *ss_narrow = save_state + HPPA_HPUX_SS_NARROW_OFFSET;
+  int i, offset = 0;
+
+  for (i = HPPA_R1_REGNUM; i < HPPA_FP0_REGNUM; i++)
+    {
+      if (regnum == i || regnum == -1)
+       regcache_raw_supply (regcache, i, ss_narrow + offset);
+
+      offset += 4;
+    }
+}
+
+static void
+hppa_hpux_supply_ss_fpblock (struct regcache *regcache,
+                            int regnum, const gdb_byte *save_state)
 {
-  int i;
-  const CORE_ADDR tmp = get_frame_base (fi) + (10 * 4);
+  const gdb_byte *ss_fpblock = save_state + HPPA_HPUX_SS_FPBLOCK_OFFSET;
+  int i, offset = 0;
 
-  for (i = 0; i < NUM_REGS; i++)
+  /* FIXME: We view the floating-point state as 64 single-precision
+     registers for 32-bit code, and 32 double-precision register for
+     64-bit code.  This distinction is artificial and should be
+     eliminated.  If that ever happens, we should remove the if-clause
+     below.  */
+
+  if (register_size (get_regcache_arch (regcache), HPPA_FP0_REGNUM) == 4)
+    {
+      for (i = HPPA_FP0_REGNUM; i < HPPA_FP0_REGNUM + 64; i++)
+       {
+         if (regnum == i || regnum == -1)
+           regcache_raw_supply (regcache, i, ss_fpblock + offset);
+
+         offset += 4;
+       }
+    }
+  else
     {
-      if (i == SP_REGNUM)
-       fsr[SP_REGNUM] = read_memory_integer (tmp + SP_REGNUM * 4, 4);
-      else
-       fsr[i] = tmp + i * 4;
+      for (i = HPPA_FP0_REGNUM; i < HPPA_FP0_REGNUM + 32; i++)
+       {
+         if (regnum == i || regnum == -1)
+           regcache_raw_supply (regcache, i, ss_fpblock + offset);
+
+         offset += 8;
+       }
+    }
+}
+
+static void
+hppa_hpux_supply_ss_wide (struct regcache *regcache,
+                         int regnum, const gdb_byte *save_state)
+{
+  const gdb_byte *ss_wide = save_state + HPPA_HPUX_SS_WIDE_OFFSET;
+  int i, offset = 8;
+
+  if (register_size (get_regcache_arch (regcache), HPPA_R1_REGNUM) == 4)
+    offset += 4;
+
+  for (i = HPPA_R1_REGNUM; i < HPPA_FP0_REGNUM; i++)
+    {
+      if (regnum == i || regnum == -1)
+       regcache_raw_supply (regcache, i, ss_wide + offset);
+
+      offset += 8;
+    }
+}
+
+static void
+hppa_hpux_supply_save_state (const struct regset *regset,
+                            struct regcache *regcache,
+                            int regnum, const void *regs, size_t len)
+{
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  const gdb_byte *proc_info = regs;
+  const gdb_byte *save_state = proc_info + 8;
+  ULONGEST flags;
+
+  flags = extract_unsigned_integer (save_state + HPPA_HPUX_SS_FLAGS_OFFSET,
+                                   4, byte_order);
+  if (regnum == -1 || regnum == HPPA_FLAGS_REGNUM)
+    {
+      size_t size = register_size (gdbarch, HPPA_FLAGS_REGNUM);
+      gdb_byte buf[8];
+
+      store_unsigned_integer (buf, size, byte_order, flags);
+      regcache_raw_supply (regcache, HPPA_FLAGS_REGNUM, buf);
+    }
+
+  /* If the SS_WIDEREGS flag is set, we really do need the full
+     `struct save_state'.  */
+  if (flags & HPPA_HPUX_SS_WIDEREGS && len < HPPA_HPUX_SAVE_STATE_SIZE)
+    error (_("Register set contents too small"));
+
+  if (flags & HPPA_HPUX_SS_WIDEREGS)
+    hppa_hpux_supply_ss_wide (regcache, regnum, save_state);
+  else
+    hppa_hpux_supply_ss_narrow (regcache, regnum, save_state);
+
+  hppa_hpux_supply_ss_fpblock (regcache, regnum, save_state);
+}
+
+/* HP-UX register set.  */
+
+static struct regset hppa_hpux_regset =
+{
+  NULL,
+  hppa_hpux_supply_save_state
+};
+
+static const struct regset *
+hppa_hpux_regset_from_core_section (struct gdbarch *gdbarch,
+                                   const char *sect_name, size_t sect_size)
+{
+  if (strcmp (sect_name, ".reg") == 0
+      && sect_size >= HPPA_HPUX_PA89_SAVE_STATE_SIZE + 8)
+    return &hppa_hpux_regset;
+
+  return NULL;
+}
+\f
+
+/* Bit in the `ss_flag' member of `struct save_state' that indicates
+   the state was saved from a system call.  From
+   <machine/save_state.h>.  */
+#define HPPA_HPUX_SS_INSYSCALL 0x02
+
+static CORE_ADDR
+hppa_hpux_read_pc (struct regcache *regcache)
+{
+  ULONGEST flags;
+
+  /* If we're currently in a system call return the contents of %r31.  */
+  regcache_cooked_read_unsigned (regcache, HPPA_FLAGS_REGNUM, &flags);
+  if (flags & HPPA_HPUX_SS_INSYSCALL)
+    {
+      ULONGEST pc;
+      regcache_cooked_read_unsigned (regcache, HPPA_R31_REGNUM, &pc);
+      return pc & ~0x3;
+    }
+
+  return hppa_read_pc (regcache);
+}
+
+static void
+hppa_hpux_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+  ULONGEST flags;
+
+  /* If we're currently in a system call also write PC into %r31.  */
+  regcache_cooked_read_unsigned (regcache, HPPA_FLAGS_REGNUM, &flags);
+  if (flags & HPPA_HPUX_SS_INSYSCALL)
+    regcache_cooked_write_unsigned (regcache, HPPA_R31_REGNUM, pc | 0x3);
+
+  hppa_write_pc (regcache, pc);
+}
+
+static CORE_ADDR
+hppa_hpux_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  ULONGEST flags;
+
+  /* If we're currently in a system call return the contents of %r31.  */
+  flags = frame_unwind_register_unsigned (next_frame, HPPA_FLAGS_REGNUM);
+  if (flags & HPPA_HPUX_SS_INSYSCALL)
+    return frame_unwind_register_unsigned (next_frame, HPPA_R31_REGNUM) & ~0x3;
+
+  return hppa_unwind_pc (gdbarch, next_frame);
+}
+\f
+
+/* Given the current value of the pc, check to see if it is inside a stub, and
+   if so, change the value of the pc to point to the caller of the stub.
+   THIS_FRAME is the current frame in the current list of frames.
+   BASE contains to stack frame base of the current frame.
+   SAVE_REGS is the register file stored in the frame cache.  */
+static void
+hppa_hpux_unwind_adjust_stub (struct frame_info *this_frame, CORE_ADDR base,
+                             struct trad_frame_saved_reg *saved_regs)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+  struct value *pcoq_head_val;
+  ULONGEST pcoq_head;
+  CORE_ADDR stubpc;
+  struct unwind_table_entry *u;
+
+  pcoq_head_val = trad_frame_get_prev_register (this_frame, saved_regs, 
+                                               HPPA_PCOQ_HEAD_REGNUM);
+  pcoq_head =
+    extract_unsigned_integer (value_contents_all (pcoq_head_val),
+                             register_size (gdbarch, HPPA_PCOQ_HEAD_REGNUM),
+                             byte_order);
+
+  u = find_unwind_entry (pcoq_head);
+  if (u && u->stub_unwind.stub_type == EXPORT)
+    {
+      stubpc = read_memory_integer (base - 24, word_size, byte_order);
+      trad_frame_set_value (saved_regs, HPPA_PCOQ_HEAD_REGNUM, stubpc);
+    }
+  else if (hppa_symbol_address ("__gcc_plt_call") 
+           == get_pc_function_start (pcoq_head))
+    {
+      stubpc = read_memory_integer (base - 8, word_size, byte_order);
+      trad_frame_set_value (saved_regs, HPPA_PCOQ_HEAD_REGNUM, stubpc);
     }
 }
 
 static void
 hppa_hpux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
-  set_gdbarch_pc_in_sigtramp (gdbarch, hppa_hpux_pc_in_sigtramp);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if (IS_32BIT_TARGET (gdbarch))
+    tdep->in_solib_call_trampoline = hppa32_hpux_in_solib_call_trampoline;
+  else
+    tdep->in_solib_call_trampoline = hppa64_hpux_in_solib_call_trampoline;
+
+  tdep->unwind_adjust_stub = hppa_hpux_unwind_adjust_stub;
+
+  set_gdbarch_in_solib_return_trampoline
+    (gdbarch, hppa_hpux_in_solib_return_trampoline);
+  set_gdbarch_skip_trampoline_code (gdbarch, hppa_hpux_skip_trampoline_code);
+
+  set_gdbarch_push_dummy_code (gdbarch, hppa_hpux_push_dummy_code);
+  set_gdbarch_call_dummy_location (gdbarch, ON_STACK);
+
+  set_gdbarch_read_pc (gdbarch, hppa_hpux_read_pc);
+  set_gdbarch_write_pc (gdbarch, hppa_hpux_write_pc);
+  set_gdbarch_unwind_pc (gdbarch, hppa_hpux_unwind_pc);
+  set_gdbarch_skip_permanent_breakpoint
+    (gdbarch, hppa_skip_permanent_breakpoint);
+
+  set_gdbarch_regset_from_core_section
+    (gdbarch, hppa_hpux_regset_from_core_section);
+
+  frame_unwind_append_unwinder (gdbarch, &hppa_hpux_sigtramp_frame_unwind);
 }
 
 static void
 hppa_hpux_som_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  tdep->is_elf = 0;
+
+  tdep->find_global_pointer = hppa32_hpux_find_global_pointer;
+
   hppa_hpux_init_abi (info, gdbarch);
+  som_solib_select (gdbarch);
 }
 
 static void
 hppa_hpux_elf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  tdep->is_elf = 1;
+  tdep->find_global_pointer = hppa64_hpux_find_global_pointer;
+
   hppa_hpux_init_abi (info, gdbarch);
+  pa64_solib_select (gdbarch);
+}
+
+static enum gdb_osabi
+hppa_hpux_core_osabi_sniffer (bfd *abfd)
+{
+  if (strcmp (bfd_get_target (abfd), "hpux-core") == 0)
+    return GDB_OSABI_HPUX_SOM;
+  else if (strcmp (bfd_get_target (abfd), "elf64-hppa") == 0)
+    {
+      asection *section;
+      
+      section = bfd_get_section_by_name (abfd, ".kernel");
+      if (section)
+        {
+         bfd_size_type size;
+         char *contents;
+
+         size = bfd_section_size (abfd, section);
+         contents = alloca (size);
+         if (bfd_get_section_contents (abfd, section, contents, 
+                                       (file_ptr) 0, size)
+             && strcmp (contents, "HP-UX") == 0)
+           return GDB_OSABI_HPUX_ELF;
+       }
+    }
+
+  return GDB_OSABI_UNKNOWN;
 }
 
 void
 _initialize_hppa_hpux_tdep (void)
 {
+  /* BFD doesn't set a flavour for HP-UX style core files.  It doesn't
+     set the architecture either.  */
+  gdbarch_register_osabi_sniffer (bfd_arch_unknown,
+                                 bfd_target_unknown_flavour,
+                                 hppa_hpux_core_osabi_sniffer);
+  gdbarch_register_osabi_sniffer (bfd_arch_hppa,
+                                  bfd_target_elf_flavour,
+                                 hppa_hpux_core_osabi_sniffer);
+
   gdbarch_register_osabi (bfd_arch_hppa, 0, GDB_OSABI_HPUX_SOM,
                           hppa_hpux_som_init_abi);
-  gdbarch_register_osabi (bfd_arch_hppa, 0, GDB_OSABI_HPUX_ELF,
+  gdbarch_register_osabi (bfd_arch_hppa, bfd_mach_hppa20w, GDB_OSABI_HPUX_ELF,
                           hppa_hpux_elf_init_abi);
 }
This page took 0.042524 seconds and 4 git commands to generate.