[rfc/rfa] Dummy calls for hppa-hpux

Randolph Chung randolph@tausq.org
Mon Dec 13 17:39:00 GMT 2004


this is an implementation of the scheme discussed in:
  http://sources.redhat.com/ml/gdb-patches/2004-12/msg00269.html

the attached patch adds support for hppa64-hpux dummy calls, as well
as fixes some issues with the current hppa-hpux dummy call
implementation. In particular, nested dummy calls will now work, as
well as interspace calls (in common situations).

the patch notes several FIXMEs for cases where the logic will fail.
There are several corner cases which don't work yet. I will need to 
write some testcases for those cases.

callfuncs.exp results are:
hppa2.0w-hp-hpux11.11
# of expected passes            93
# of unexpected failures        9
# of unknown successes          3


hppa64-hp-hpux11.11
# of expected passes            35
# of unexpected failures        64
# of known failures             3

the large number of failures in hppa64-hp-hpux11.11 are due to a
cascading failure early on in the test (we have some problems passing
function pointers into functions); however, since basic infcalls work
now, i suspect a much larger portion of gdb testcases will benefit.

the patch is somewhat large and some parts of it are difficult to read
because of what "diff" did to it.  i've duplicated the interesting 
bits of code below for easier reviewing.

comments? scary code? :-)

==== excerpt from hppa-hpux-tdep.c, read from bottom up ====

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 (CORE_ADDR start, CORE_ADDR end, 
			  unsigned int *patterns, int count)
{
  unsigned int *buf;
  int offset, i;
  int region, insns;

  region = end - start + 4;
  insns = region / 4;
  buf = (unsigned int *) alloca (region);

  read_memory (start, (char *) buf, region);

  for (i = 0; i < insns; i++)
    buf[i] = extract_unsigned_integer (&buf[i], 4);

  for (offset = 0; offset <= insns - count; offset++)
    {
      for (i = 0; i < count; i++)
        {
	  if ((buf[offset + i] & patterns[i]) != patterns[i])
	    break;
	}
      if (i == count)
        break;
    }
    
  if (offset <= insns - count)
    return start + offset * 4;
  else
    return 0;
}

static CORE_ADDR
hppa32_hpux_search_dummy_call_sequence (struct gdbarch *gdbarch, CORE_ADDR pc,
					int *argreg)
{
  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;
  char 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.\n");

  /* 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 (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 (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 (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));
  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)
{
  struct objfile *obj;
  struct obj_section *sec;
  struct hppa_objfile_private *priv;
  CORE_ADDR addr;
  struct minimal_symbol *msym;
  int i;

  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.\n");

  /* 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.  */
  for (i = 0, msym = obj->msymbols; i < obj->minimal_symbol_count; i++, msym++)
    {
      CORE_ADDR begin, end;
      char *name;
      unsigned int insns[2];
      int offset;

      find_pc_partial_function (SYMBOL_VALUE_ADDRESS (msym), &name,
      				&begin, &end);

      if (*name == 0 || begin == 0 || end == 0)
        continue;

      if (target_read_memory (end - sizeof (insns), (char *)insns, sizeof (insns)) == 0)
        {
	  for (offset = 0; offset < ARRAY_SIZE (insns); offset++)
	    {
	      unsigned int insn;

	      insn = extract_unsigned_integer (&insns[offset], 4);
	      if (insn == 0xe840d002) /* bve,n (rp) */
	        {
		  addr = (end - sizeof (insns)) + (offset * 4);
		  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 minimal_symbol *funsym, *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), 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 (CORE_ADDR addr)
{
  int sr;
  /* The space register to use is encoded in the top 2 bits of the address.  */
  sr = addr >> (gdbarch_tdep (current_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 the 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;
  int i;

  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 (sec->addr);
      if (!u || u->stub_unwind.stub_type == 0)
        return sec->addr;

      /* 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);
      for (i = 0, msym = sec->objfile->msymbols;
      	   i < sec->objfile->minimal_symbol_count;
	   i++, 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.\n");
  return addr - 4;
}

static CORE_ADDR
hppa_hpux_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp,
			   CORE_ADDR funcaddr, int using_gcc,
			   struct value **args, int nargs,
			   struct type *value_type,
			   CORE_ADDR *real_pc, CORE_ADDR *bp_addr)
{
  CORE_ADDR pc, stubaddr;
  int argreg;

  pc = read_pc ();

  /* 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 (pc) == hppa_hpux_sr_for_addr (funcaddr))
    {
      /* Intraspace call.  */
      *bp_addr = hppa_hpux_find_dummy_bpaddr (pc);
      *real_pc = funcaddr;
      regcache_cooked_write_unsigned (current_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))
    {
      static unsigned int hppa32_tramp[] = {
        0x0fdf1291, /* stw r31,-8(,sp) */
        0x02c010a1, /* ldsid (,r22),r1 */
        0x00011820, /* mtsp r1,sr0 */
        0xe6c00000, /* be,l 0(sr0,r22),%sr0,%r31 */
        0x081f0242, /* copy r31,rp */
        0x0fd11082, /* ldw -8(,sp),rp */
        0x004010a1, /* ldsid (,rp),r1 */
        0x00011820, /* mtsp r1,sr0 */
        0xe0400000, /* be 0(sr0,rp) */
        0x08000240  /* 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 (current_regcache, 22, stubaddr);

      write_memory (sp, (char *)&hppa32_tramp, sizeof (hppa32_tramp));

      *bp_addr = hppa_hpux_find_dummy_bpaddr (pc);
      regcache_cooked_write_unsigned (current_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.\n");

      regcache_cooked_write_unsigned (current_regcache, argreg, sp);

      sp += sizeof (hppa32_tramp);
    }
  else
    {
      static unsigned int hppa64_tramp[] = {
        0xeac0f000, /* bve,l (r22),%r2 */
        0x0fdf12d1, /* std r31,-8(,sp) */
        0x0fd110c2, /* ldd -8(,sp),rp */
        0xe840d002, /* bve,n (rp) */
        0x08000240  /* nop */
      };

      /* for hppa64, we don't need to call through a stub; all functions
         return via a bve.  */
      regcache_cooked_write_unsigned (current_regcache, 22, funcaddr);
      write_memory (sp, (char *)&hppa64_tramp, sizeof (hppa64_tramp));

      *bp_addr = pc - 4;
      regcache_cooked_write_unsigned (current_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.\n");

      regcache_cooked_write_unsigned (current_regcache, argreg, sp);

      sp += sizeof (hppa64_tramp);
    }

  sp = gdbarch_frame_align (gdbarch, sp);

  return sp;
}

==== end of excerpt ====

complete patch is attached.

randolph
-- 
Randolph Chung
Debian GNU/Linux Developer, hppa/ia64 ports
http://www.tausq.org/
-------------- next part --------------
2004-12-13  Randolph Chung  <tausq@debian.org>

	* hppa-hpux-tdep.c (IS_32BIT_TARGET): New.
	(in_opd_section): New.
	(hppa32_hpux_find_global_pointer): Rename from 
	hppa_hpux_som_find_global_pointer.
	(hppa64_hpux_find_global_pointer): New.
	(ldsid_pattern): New.
	(hppa_hpux_search_pattern): New.
	(hppa32_hpux_search_dummy_call_sequence): New.
	(hppa64_hpux_search_dummy_call_sequence): New.
	(hppa_hpux_find_import_stub_for_addr): New.
	(hppa_hpux_sr_for_addr): New.
	(hppa_hpux_find_dummy_bpaddr): New.
	(hppa_hpux_init_abi): Use IS_32BIT_TARGET predicate.
	(hppa_hpux_som_init_abi): Set find_global_pointer method to
	hppa32_hpux_find_global_pointer instead of
	hppa_hpux_som_find_global_pointer.
	(hppa_hpux_elf_init_abi): Set find_global_pointer method.
	* hppa-tdep.c (hppa_init_objfile_priv_data): New.
	(read_unwind_info): Use function to initialize objfile-private data.
	(hppa32_push_dummy_call): Don't automatically set the RP if there is
	a push_dummy_code method.
	(hppa64_push_dummy_call): Retrieve and set the global pointer.
	Don't automatically set the RP if there is a push_dummy_code method.
	* hppa-tdep.h (hppa_objfile_private): Add dummy_call_sequence_reg and
	dummy_call_sequence_addr members.
	(hppa_init_objfile_priv_data): New prototype.

Index: hppa-hpux-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/hppa-hpux-tdep.c,v
retrieving revision 1.35
diff -u -p -r1.35 hppa-hpux-tdep.c
--- hppa-hpux-tdep.c	13 Dec 2004 08:47:07 -0000	1.35
+++ hppa-hpux-tdep.c	13 Dec 2004 17:07:00 -0000
@@ -45,6 +45,9 @@
 #define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
 #endif
 
+#define IS_32BIT_TARGET(_gdbarch) \
+	((gdbarch_tdep (_gdbarch))->bytes_per_address == 4)
+
 /* Forward declarations.  */
 extern void _initialize_hppa_hpux_tdep (void);
 extern initialize_file_ftype _initialize_hppa_hpux_tdep;
@@ -57,6 +60,20 @@ typedef struct
   }
 args_for_find_stub;
 
+static int
+in_opd_section (CORE_ADDR pc)
+{
+  struct obj_section *s;
+  int retval = 0;
+
+  s = find_pc_section (pc);
+
+  retval = (s != NULL
+	    && s->the_bfd_section->name != NULL
+	    && strcmp (s->the_bfd_section->name, ".opd") == 0);
+  return (retval);
+}
+
 /* 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
@@ -1229,7 +1248,7 @@ hppa_hpux_sigtramp_unwind_sniffer (struc
 }
 
 static CORE_ADDR
-hppa_hpux_som_find_global_pointer (struct value *function)
+hppa32_hpux_find_global_pointer (struct value *function)
 {
   CORE_ADDR faddr;
   
@@ -1252,169 +1271,484 @@ hppa_hpux_som_find_global_pointer (struc
 }
 
 static CORE_ADDR
-hppa_hpux_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp,
-			   CORE_ADDR funcaddr, int using_gcc,
-			   struct value **args, int nargs,
-			   struct type *value_type,
-			   CORE_ADDR *real_pc, CORE_ADDR *bp_addr)
+hppa64_hpux_find_global_pointer (struct value *function)
 {
-  /* FIXME: tausq/2004-06-09: This needs much more testing.  It is broken
-     for pa64, but we should be able to get it to work with a little bit
-     of work. gdb-6.1 has a lot of code to handle various cases; I've tried to
-     simplify it and avoid compile-time conditionals.  */
-
-  /* On HPUX, functions in the main executable and in libraries can be located
-     in different spaces.  In order for us to be able to select the right 
-     space for the function call, we need to go through an instruction seqeunce
-     to select the right space for the target function, call it, and then
-     restore the space on return.
-
-     There are two helper routines that can be used for this task -- if
-     an application is linked with gcc, it will contain a __gcc_plt_call
-     helper function.  __gcc_plt_call, when passed the entry point of an
-     import stub, will do the necessary space setting/restoration for the
-     target function.
-
-     For programs that are compiled/linked with the HP compiler, a similar
-     function called __d_plt_call exists; __d_plt_call expects a PLABEL instead
-     of an import stub as an argument.
-
-     // *INDENT-OFF*
-     To summarize, the call flow is:
-       current function -> dummy frame -> __gcc_plt_call (import stub) 
-                        -> target function
-     or
-       current function -> dummy frame -> __d_plt_call (plabel)
-                        -> target function
-     // *INDENT-ON*
-
-     In general the "funcaddr" argument passed to push_dummy_code is the actual
-     entry point of the target function.  For __gcc_plt_call, we need to 
-     locate the import stub for the corresponding function.  Failing that,
-     we construct a dummy "import stub" on the stack to pass as an argument.
-     For __d_plt_call, we similarly synthesize a PLABEL on the stack to
-     pass to the helper function.
-
-     An additional twist is that, in order for us to restore the space register
-     to its starting state, we need __gcc_plt_call/__d_plt_call to return
-     to the instruction where we started the call.  However, if we put
-     the breakpoint there, gdb will complain because it will find two 
-     frames on the stack with the same (sp, pc) (with the dummy frame in 
-     between).  Currently, we set the return pointer to (pc - 4) of the 
-     current function.  FIXME: This is not an ideal solution; possibly if the 
-     current pc is at the beginning of a page, this will cause a page fault. 
-     Need to understand this better and figure out a better way to fix it.  */
-
-  struct minimal_symbol *sym;
-
-  /* Nonzero if we will use GCC's PLT call routine.  This routine must be
-     passed an import stub, not a PLABEL.  It is also necessary to get %r19
-     before performing the call.  (This is done by push_dummy_call.)  */
-  int use_gcc_plt_call = 1;
-
-  /* See if __gcc_plt_call is available; if not we will use the HP version
-     instead.  */
-  sym = lookup_minimal_symbol ("__gcc_plt_call", NULL, NULL);
-  if (sym == NULL)
-    use_gcc_plt_call = 0;
-
-  /* If using __gcc_plt_call, we need to make sure we pass in an import
-     stub.  funcaddr can be pointing to an export stub or a real function,
-     so we try to resolve it to the import stub.  */
-  if (use_gcc_plt_call)
-    {
-      struct objfile *objfile;
-      struct minimal_symbol *funsym, *stubsym;
-      CORE_ADDR stubaddr = 0;
-
-      funsym = lookup_minimal_symbol_by_pc (funcaddr);
-      if (!funsym)
-        error ("Unable to find symbol for target function.\n");
+  CORE_ADDR faddr;
+  char buf[32];
 
-      ALL_OBJFILES (objfile)
+  faddr = value_as_address (function);
+
+  if (in_opd_section (faddr))
+    {
+      target_read_memory (faddr, buf, sizeof (buf));
+      return extract_unsigned_integer (&buf[24], 8);
+    }
+  else
+    {
+      return gdbarch_tdep (current_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 (CORE_ADDR start, CORE_ADDR end, 
+			  unsigned int *patterns, int count)
+{
+  unsigned int *buf;
+  int offset, i;
+  int region, insns;
+
+  region = end - start + 4;
+  insns = region / 4;
+  buf = (unsigned int *) alloca (region);
+
+  read_memory (start, (char *) buf, region);
+
+  for (i = 0; i < insns; i++)
+    buf[i] = extract_unsigned_integer (&buf[i], 4);
+
+  for (offset = 0; offset <= insns - count; offset++)
+    {
+      for (i = 0; i < count; i++)
         {
-	  stubsym = lookup_minimal_symbol_solib_trampoline
-	    (SYMBOL_LINKAGE_NAME (funsym), objfile);
+	  if ((buf[offset + i] & patterns[i]) != patterns[i])
+	    break;
+	}
+      if (i == count)
+        break;
+    }
+    
+  if (offset <= insns - count)
+    return start + offset * 4;
+  else
+    return 0;
+}
 
-          if (stubsym)
-	    {
-	      struct unwind_table_entry *u;
+static CORE_ADDR
+hppa32_hpux_search_dummy_call_sequence (struct gdbarch *gdbarch, CORE_ADDR pc,
+					int *argreg)
+{
+  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;
+  char buf[4];
+  unsigned int insn;
+
+  sec = find_pc_section (pc);
+  obj = sec->objfile;
+  priv = objfile_data (obj, hppa_objfile_priv_data);
 
-	      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;
+  if (!priv)
+    priv = hppa_init_objfile_priv_data (obj);
+  if (!priv)
+    error ("Internal error creating objfile private data.\n");
+
+  /* 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 (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 (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;
 
-      if (stubaddr != 0)
+      find_pc_partial_function (addr, NULL, &start, &end);
+      if (start != 0 && end != 0)
         {
-          /* Argument to __gcc_plt_call is passed in r22.  */
-          regcache_cooked_write_unsigned (current_regcache, 22, stubaddr);
+	  addr = hppa_hpux_search_pattern (start, end, ldsid_pattern,
+					   ARRAY_SIZE (ldsid_pattern));
+	  if (addr)
+	    goto found_pattern;
         }
-      else
+    }
+
+  /* Can't find a suitable sequence.  */
+  return 0;
+
+found_pattern:
+  target_read_memory (addr, buf, sizeof (buf));
+  insn = extract_unsigned_integer (buf, sizeof (buf));
+  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)
+{
+  struct objfile *obj;
+  struct obj_section *sec;
+  struct hppa_objfile_private *priv;
+  CORE_ADDR addr;
+  struct minimal_symbol *msym;
+  int i;
+
+  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.\n");
+
+  /* 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.  */
+  for (i = 0, msym = obj->msymbols; i < obj->minimal_symbol_count; i++, msym++)
+    {
+      CORE_ADDR begin, end;
+      char *name;
+      unsigned int insns[2];
+      int offset;
+
+      find_pc_partial_function (SYMBOL_VALUE_ADDRESS (msym), &name,
+      				&begin, &end);
+
+      if (*name == 0 || begin == 0 || end == 0)
+        continue;
+
+      if (target_read_memory (end - sizeof (insns), (char *)insns, sizeof (insns)) == 0)
         {
-	  /* No import stub found; let's synthesize one.  */
+	  for (offset = 0; offset < ARRAY_SIZE (insns); offset++)
+	    {
+	      unsigned int insn;
+
+	      insn = extract_unsigned_integer (&insns[offset], 4);
+	      if (insn == 0xe840d002) /* bve,n (rp) */
+	        {
+		  addr = (end - sizeof (insns)) + (offset * 4);
+		  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 minimal_symbol *funsym, *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), 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 (CORE_ADDR addr)
+{
+  int sr;
+  /* The space register to use is encoded in the top 2 bits of the address.  */
+  sr = addr >> (gdbarch_tdep (current_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 the 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;
+  int i;
+
+  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 (sec->addr);
+      if (!u || u->stub_unwind.stub_type == 0)
+        return sec->addr;
+
+      /* 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);
+      for (i = 0, msym = sec->objfile->msymbols;
+      	   i < sec->objfile->minimal_symbol_count;
+	   i++, 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.\n");
+  return addr - 4;
+}
+
+static CORE_ADDR
+hppa_hpux_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp,
+			   CORE_ADDR funcaddr, int using_gcc,
+			   struct value **args, int nargs,
+			   struct type *value_type,
+			   CORE_ADDR *real_pc, CORE_ADDR *bp_addr)
+{
+  CORE_ADDR pc, stubaddr;
+  int argreg;
+
+  pc = read_pc ();
 
-	  /* ldsid %r21, %r1 */
-	  write_memory_unsigned_integer (sp, 4, 0x02a010a1);
-	  /* mtsp %r1,%sr0 */
-	  write_memory_unsigned_integer (sp + 4, 4, 0x00011820);
-	  /* be 0(%sr0, %r21) */
-	  write_memory_unsigned_integer (sp + 8, 4, 0xe2a00000);
-          /* nop */
-          write_memory_unsigned_integer (sp + 12, 4, 0x08000240);
-
-          regcache_cooked_write_unsigned (current_regcache, 21, funcaddr);
-          regcache_cooked_write_unsigned (current_regcache, 22, sp);
-	}
-
-      /* We set the breakpoint address and r31 to (close to) where the current
-         pc is; when __gcc_plt_call returns, it will restore pcsqh to the
-	 current value based on this.  The -4 is needed for frame unwinding
-	 to work properly -- we need to land in a different function than
-	 the current function.  */
-      *bp_addr = (read_register (HPPA_PCOQ_HEAD_REGNUM) & ~3) - 4;
+  /* 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 (pc) == hppa_hpux_sr_for_addr (funcaddr))
+    {
+      /* Intraspace call.  */
+      *bp_addr = hppa_hpux_find_dummy_bpaddr (pc);
+      *real_pc = funcaddr;
+      regcache_cooked_write_unsigned (current_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))
+    {
+      static unsigned int hppa32_tramp[] = {
+        0x0fdf1291, /* stw r31,-8(,sp) */
+        0x02c010a1, /* ldsid (,r22),r1 */
+        0x00011820, /* mtsp r1,sr0 */
+        0xe6c00000, /* be,l 0(sr0,r22),%sr0,%r31 */
+        0x081f0242, /* copy r31,rp */
+        0x0fd11082, /* ldw -8(,sp),rp */
+        0x004010a1, /* ldsid (,rp),r1 */
+        0x00011820, /* mtsp r1,sr0 */
+        0xe0400000, /* be 0(sr0,rp) */
+        0x08000240  /* 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 (current_regcache, 22, stubaddr);
+
+      write_memory (sp, (char *)&hppa32_tramp, sizeof (hppa32_tramp));
+
+      *bp_addr = hppa_hpux_find_dummy_bpaddr (pc);
       regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr);
 
-      /* Continue from __gcc_plt_call.  */
-      *real_pc = SYMBOL_VALUE (sym);
+      *real_pc = hppa32_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg);
+      if (*real_pc == 0)
+        error ("Cannot make interspace call from here.\n");
+
+      regcache_cooked_write_unsigned (current_regcache, argreg, sp);
+
+      sp += sizeof (hppa32_tramp);
     }
   else
     {
-      ULONGEST gp;
+      static unsigned int hppa64_tramp[] = {
+        0xeac0f000, /* bve,l (r22),%r2 */
+        0x0fdf12d1, /* std r31,-8(,sp) */
+        0x0fd110c2, /* ldd -8(,sp),rp */
+        0xe840d002, /* bve,n (rp) */
+        0x08000240  /* nop */
+      };
+
+      /* for hppa64, we don't need to call through a stub; all functions
+         return via a bve.  */
+      regcache_cooked_write_unsigned (current_regcache, 22, funcaddr);
+      write_memory (sp, (char *)&hppa64_tramp, sizeof (hppa64_tramp));
 
-      /* Use __d_plt_call as a fallback; __d_plt_call expects to be called 
-         with a plabel, so we need to build one.  */
+      *bp_addr = pc - 4;
+      regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr);
 
-      sym = lookup_minimal_symbol ("__d_plt_call", NULL, NULL);
-      if (sym == NULL)
-        error("Can't find an address for __d_plt_call or __gcc_plt_call "
-	      "trampoline\nSuggest linking executable with -g or compiling "
-	      "with gcc.");
+      *real_pc = hppa64_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg);
+      if (*real_pc == 0)
+        error ("Cannot make interspace call from here.\n");
 
-      gp = gdbarch_tdep (gdbarch)->find_global_pointer (funcaddr);
-      write_memory_unsigned_integer (sp, 4, funcaddr);
-      write_memory_unsigned_integer (sp + 4, 4, gp);
+      regcache_cooked_write_unsigned (current_regcache, argreg, sp);
 
-      /* plabel is passed in r22 */
-      regcache_cooked_write_unsigned (current_regcache, 22, sp);
+      sp += sizeof (hppa64_tramp);
     }
 
-  /* Pushed one stack frame, which has to be 64-byte aligned.  */
-  sp += 64;
+  sp = gdbarch_frame_align (gdbarch, sp);
 
   return sp;
 }
+
 
 
 /* Bit in the `ss_flag' member of `struct save_state' that indicates
@@ -1656,7 +1991,7 @@ hppa_hpux_init_abi (struct gdbarch_info 
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  if (tdep->bytes_per_address == 4)
+  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;
@@ -1689,7 +2023,8 @@ hppa_hpux_som_init_abi (struct gdbarch_i
 
   tdep->is_elf = 0;
 
-  tdep->find_global_pointer = hppa_hpux_som_find_global_pointer;
+  tdep->find_global_pointer = hppa32_hpux_find_global_pointer;
+
   hppa_hpux_init_abi (info, gdbarch);
   som_solib_select (tdep);
 }
@@ -1700,6 +2035,8 @@ hppa_hpux_elf_init_abi (struct gdbarch_i
   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 (tdep);
 }
Index: hppa-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/hppa-tdep.c,v
retrieving revision 1.191
diff -u -p -r1.191 hppa-tdep.c
--- hppa-tdep.c	13 Dec 2004 04:06:15 -0000	1.191
+++ hppa-tdep.c	13 Dec 2004 17:07:00 -0000
@@ -273,6 +273,20 @@ hppa_symbol_address(const char *sym)
   else
     return (CORE_ADDR)-1;
 }
+
+struct hppa_objfile_private *
+hppa_init_objfile_priv_data (struct objfile *objfile)
+{
+  struct hppa_objfile_private *priv;
+
+  priv = (struct hppa_objfile_private *)
+  	 obstack_alloc (&objfile->objfile_obstack,
+	 		sizeof (struct hppa_objfile_private));
+  set_objfile_data (objfile, hppa_objfile_priv_data, priv);
+  memset (priv, 0, sizeof (*priv));
+
+  return priv;
+}
 
 
 /* Compare the start address for two unwind entries returning 1 if 
@@ -529,15 +544,8 @@ read_unwind_info (struct objfile *objfil
   obj_private = (struct hppa_objfile_private *) 
 	        objfile_data (objfile, hppa_objfile_priv_data);
   if (obj_private == NULL)
-    {
-      obj_private = (struct hppa_objfile_private *)
-	obstack_alloc (&objfile->objfile_obstack, 
-                       sizeof (struct hppa_objfile_private));
-      set_objfile_data (objfile, hppa_objfile_priv_data, obj_private);
-      obj_private->unwind_info = NULL;
-      obj_private->so_info = NULL;
-      obj_private->dp = 0;
-    }
+    obj_private = hppa_init_objfile_priv_data (objfile);
+
   obj_private->unwind_info = ui;
 }
 
@@ -874,7 +882,8 @@ hppa32_push_dummy_call (struct gdbarch *
     write_register (19, gp);
 
   /* Set the return address.  */
-  regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, bp_addr);
+  if (!gdbarch_push_dummy_code_p (gdbarch))
+    regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, bp_addr);
 
   /* Update the Stack Pointer.  */
   regcache_cooked_write_unsigned (regcache, HPPA_SP_REGNUM, param_end);
@@ -914,6 +923,10 @@ hppa64_push_dummy_call (struct gdbarch *
      been pushed.  */
   CORE_ADDR new_sp = 0;
 
+  /* Global pointer (r27) of the function we are trying to call.  */
+  CORE_ADDR gp;
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
   /* Two passes.  First pass computes the location of everything,
      second pass writes the bytes out.  */
   int write_pass;
@@ -995,8 +1008,14 @@ hppa64_push_dummy_call (struct gdbarch *
   if (struct_return)
     write_register (28, struct_addr);
 
+  gp = tdep->find_global_pointer (function);
+
+  if (gp != 0)
+    write_register (27, gp);
+
   /* Set the return address.  */
-  regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, bp_addr);
+  if (!gdbarch_push_dummy_code_p (gdbarch))
+    regcache_cooked_write_unsigned (regcache, HPPA_RP_REGNUM, bp_addr);
 
   /* Update the Stack Pointer.  */
   regcache_cooked_write_unsigned (regcache, HPPA_SP_REGNUM, param_end + 64);
Index: hppa-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/hppa-tdep.h,v
retrieving revision 1.17
diff -u -p -r1.17 hppa-tdep.h
--- hppa-tdep.h	13 Dec 2004 04:06:15 -0000	1.17
+++ hppa-tdep.h	13 Dec 2004 17:07:00 -0000
@@ -204,6 +204,9 @@ struct hppa_objfile_private
     struct hppa_unwind_info *unwind_info;	/* a pointer */
     struct so_list *so_info;	/* a pointer  */
     CORE_ADDR dp;
+
+    int dummy_call_sequence_reg;
+    CORE_ADDR dummy_call_sequence_addr;
   };
 
 extern const struct objfile_data *hppa_objfile_priv_data;
@@ -231,4 +234,7 @@ extern void hppa_write_pc (CORE_ADDR pc,
 extern CORE_ADDR hppa_unwind_pc (struct gdbarch *gdbarch,
 				 struct frame_info *next_frame);
 
+extern struct hppa_objfile_private *
+hppa_init_objfile_priv_data (struct objfile *objfile);
+
 #endif  /* HPPA_TDEP_H */


More information about the Gdb-patches mailing list