/* Target-dependent code for HP-UX on PA-RISC.
- Copyright 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
This file is part of GDB.
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. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
#include "defs.h"
#include "arch-utils.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 "exceptions.h"
#include "gdb_string.h"
#include <dl.h>
#include <machine/save_state.h>
-#include "hppa-tdep.h"
-
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
#endif
+#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;
}
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
}
/* Should never happen. */
- warning ("Unable to find branch in parameter relocation stub.\n");
+ warning (_("Unable to find branch in parameter relocation stub."));
return 0;
}
}
/* Should never happen. */
- warning ("Unable to find branch in parameter relocation stub.\n");
+ warning (_("Unable to find branch in parameter relocation stub."));
return 0;
}
/* 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");
+ warning (_("Unable to find branch in linker stub"));
return orig_pc == pc ? 0 : pc & ~0x3;
}
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).");
+ warning (_("Unable to find ldil X,%%r1 before ble Y(%%sr4,%%r1)."));
return orig_pc == pc ? 0 : pc & ~0x3;
}
}
stubsym = lookup_minimal_symbol_by_pc (loc);
if (stubsym == NULL)
{
- warning ("Unable to find symbol for 0x%lx", loc);
+ warning (_("Unable to find symbol for 0x%lx"), loc);
return orig_pc == pc ? 0 : pc & ~0x3;
}
libsym = lookup_minimal_symbol (DEPRECATED_SYMBOL_NAME (stubsym), NULL, NULL);
if (libsym == NULL)
{
- warning ("Unable to find library symbol for %s\n",
+ warning (_("Unable to find library symbol for %s."),
DEPRECATED_SYMBOL_NAME (stubsym));
return orig_pc == pc ? 0 : pc & ~0x3;
}
(read_register (HPPA_SP_REGNUM) - 8, 4)) & ~0x3;
else
{
- warning ("Unable to find restore of %%rp before bv (%%rp).");
+ warning (_("Unable to find restore of %%rp before bv (%%rp)."));
return orig_pc == pc ? 0 : pc & ~0x3;
}
}
GDB can set a breakpoint on this callback and find out what
exception event has occurred. */
-/* The name of the hook to be set to point to the callback function */
+/* The name of the hook to be set to point to the callback function. */
static char HP_ACC_EH_notify_hook[] = "__eh_notify_hook";
-/* The name of the function to be used to set the hook value */
+/* The name of the function to be used to set the hook value. */
static char HP_ACC_EH_set_hook_value[] = "__eh_set_hook_value";
/* The name of the callback function in end.o */
static char HP_ACC_EH_notify_callback[] = "__d_eh_notify_callback";
-/* Name of function in end.o on which a break is set (called by above) */
+/* Name of function in end.o on which a break is set (called by above). */
static char HP_ACC_EH_break[] = "__d_eh_break";
-/* Name of flag (in end.o) that enables catching throws */
+/* Name of flag (in end.o) that enables catching throws. */
static char HP_ACC_EH_catch_throw[] = "__d_eh_catch_throw";
-/* Name of flag (in end.o) that enables catching catching */
+/* Name of flag (in end.o) that enables catching catching. */
static char HP_ACC_EH_catch_catch[] = "__d_eh_catch_catch";
-/* The enum used by aCC */
+/* The enum used by aCC. */
typedef enum
{
__EH_NOTIFY_THROW,
ourselves.
0 => success
1 => failure */
-int
+static int
setup_d_pid_in_inferior (void)
{
CORE_ADDR anaddr;
msymbol = lookup_minimal_symbol ("__d_pid", NULL, symfile_objfile);
if (msymbol == NULL)
{
- warning ("Unable to find __d_pid symbol in object file.");
- warning ("Suggest linking executable with -g (links in /opt/langtools/lib/end.o).");
+ warning (_("Unable to find __d_pid symbol in object file.\n"
+ "Suggest linking executable with -g (links in /opt/langtools/lib/end.o)."));
return 1;
}
store_unsigned_integer (buf, 4, PIDGET (inferior_ptid)); /* FIXME 32x64? */
if (target_write_memory (anaddr, buf, 4)) /* FIXME 32x64? */
{
- warning ("Unable to write __d_pid");
- warning ("Suggest linking executable with -g (links in /opt/langtools/lib/end.o).");
+ warning (_("Unable to write __d_pid.\n"
+ "Suggest linking executable with -g (links in /opt/langtools/lib/end.o)."));
return 1;
}
return 0;
end.c
man entry for shl_findsym */
-CORE_ADDR
+static CORE_ADDR
find_stub_with_shl_get (struct minimal_symbol *function, CORE_ADDR handle)
{
struct symbol *get_sym, *symbol2;
target_read_memory (value_return_addr, (char *) &stub_addr, sizeof (stub_addr));
if (stub_addr <= 0)
- error ("call to __d_shl_get failed, error code is %d", err_value);
+ error (_("call to __d_shl_get failed, error code is %d"), err_value);
return (stub_addr);
}
}
/* Initialize exception catchpoint support by looking for the
- necessary hooks/callbacks in end.o, etc., and set the hook value to
- point to the required debug function
+ necessary hooks/callbacks in end.o, etc., and set the hook value
+ to point to the required debug function.
Return 0 => failure
1 => success */
/* Detect and disallow recursion. On HP-UX with aCC, infinite
recursion is a possibility because finding the hook for exception
callbacks involves making a call in the inferior, which means
- re-inserting breakpoints which can re-invoke this code */
+ re-inserting breakpoints which can re-invoke this code. */
static int recurse = 0;
if (recurse > 0)
if (!deprecated_hp_som_som_object_present)
return 0;
- /* We have a SOM executable with SOM debug info; find the hooks */
+ /* We have a SOM executable with SOM debug info; find the hooks. */
/* First look for the notify hook provided by aCC runtime libs */
/* If we find this symbol, we conclude that the executable must
should *not* be tried as an alternative.
ASSUMPTION: Only HP aCC code will have __eh_notify_hook defined.
- ASSUMPTION: HP aCC and g++ modules cannot be linked together. */
+ ASSUMPTION: HP aCC and g++ modules cannot be linked together. */
/* libCsup has this hook; it'll usually be non-debuggable */
msym = lookup_minimal_symbol (HP_ACC_EH_notify_hook, NULL, NULL);
}
else
{
- warning ("Unable to find exception callback hook (%s).", HP_ACC_EH_notify_hook);
- warning ("Executable may not have been compiled debuggable with HP aCC.");
- warning ("GDB will be unable to intercept exception events.");
+ warning (_("\
+Unable to find exception callback hook (%s).\n\
+Executable may not have been compiled debuggable with HP aCC.\n\
+GDB will be unable to intercept exception events."),
+ HP_ACC_EH_notify_hook);
eh_notify_hook_addr = 0;
hp_cxx_exception_support = 0;
return 0;
}
/* Next look for the notify callback routine in end.o */
- /* This is always available in the SOM symbol dictionary if end.o is linked in */
+ /* This is always available in the SOM symbol dictionary if end.o is
+ linked in. */
msym = lookup_minimal_symbol (HP_ACC_EH_notify_callback, NULL, NULL);
if (msym)
{
}
else
{
- warning ("Unable to find exception callback routine (%s).", HP_ACC_EH_notify_callback);
- warning ("Suggest linking executable with -g (links in /opt/langtools/lib/end.o).");
- warning ("GDB will be unable to intercept exception events.");
+ warning (_("\
+Unable to find exception callback routine (%s).\n\
+Suggest linking executable with -g (links in /opt/langtools/lib/end.o).\n\
+GDB will be unable to intercept exception events."),
+ HP_ACC_EH_notify_callback);
eh_notify_callback_addr = 0;
return 0;
}
-#ifndef GDB_TARGET_IS_HPPA_20W
- /* Check whether the executable is dynamically linked or archive bound */
- /* With an archive-bound executable we can use the raw addresses we find
- for the callback function, etc. without modification. For an executable
- with shared libraries, we have to do more work to find the plabel, which
- can be the target of a call through $$dyncall from the aCC runtime support
- library (libCsup) which is linked shared by default by aCC. */
- /* This test below was copied from somsolib.c/somread.c. It may not be a very
- reliable one to test that an executable is linked shared. pai/1997-07-18 */
- shlib_info = bfd_get_section_by_name (symfile_objfile->obfd, "$SHLIB_INFO$");
- if (shlib_info && (bfd_section_size (symfile_objfile->obfd, shlib_info) != 0))
+ if (!gdbarch_tdep (current_gdbarch)->is_elf)
{
- /* The minsym we have has the local code address, but that's not the
- plabel that can be used by an inter-load-module call. */
- /* Find solib handle for main image (which has end.o), and use that
- and the min sym as arguments to __d_shl_get() (which does the equivalent
- of shl_findsym()) to find the plabel. */
-
- args_for_find_stub args;
- static char message[] = "Error while finding exception callback hook:\n";
-
- args.solib_handle = som_solib_get_solib_by_pc (eh_notify_callback_addr);
- args.msym = msym;
- args.return_val = 0;
-
- recurse++;
- catch_errors (cover_find_stub_with_shl_get, &args, message,
- RETURN_MASK_ALL);
- eh_notify_callback_addr = args.return_val;
- recurse--;
-
- deprecated_exception_catchpoints_are_fragile = 1;
-
- if (!eh_notify_callback_addr)
- {
- /* We can get here either if there is no plabel in the export list
- for the main image, or if something strange happened (?) */
- warning ("Couldn't find a plabel (indirect function label) for the exception callback.");
- warning ("GDB will not be able to intercept exception events.");
- return 0;
- }
+ /* Check whether the executable is dynamically linked or archive bound */
+ /* With an archive-bound executable we can use the raw addresses we find
+ for the callback function, etc. without modification. For an executable
+ with shared libraries, we have to do more work to find the plabel, which
+ can be the target of a call through $$dyncall from the aCC runtime
+ support library (libCsup) which is linked shared by default by aCC. */
+ /* This test below was copied from somsolib.c/somread.c. It may not be a very
+ reliable one to test that an executable is linked shared.
+ pai/1997-07-18 */
+ shlib_info = bfd_get_section_by_name (symfile_objfile->obfd, "$SHLIB_INFO$");
+ if (shlib_info && (bfd_section_size (symfile_objfile->obfd, shlib_info) != 0))
+ {
+ /* The minsym we have has the local code address, but that's not
+ the plabel that can be used by an inter-load-module call. */
+ /* Find solib handle for main image (which has end.o), and use
+ that and the min sym as arguments to __d_shl_get() (which
+ does the equivalent of shl_findsym()) to find the plabel. */
+
+ args_for_find_stub args;
+
+ args.solib_handle = gdbarch_tdep (current_gdbarch)->solib_get_solib_by_pc (eh_notify_callback_addr);
+ args.msym = msym;
+ args.return_val = 0;
+
+ recurse++;
+ catch_errors (cover_find_stub_with_shl_get, &args,
+ _("Error while finding exception callback hook:\n"),
+ RETURN_MASK_ALL);
+ eh_notify_callback_addr = args.return_val;
+ recurse--;
+
+ deprecated_exception_catchpoints_are_fragile = 1;
+
+ if (!eh_notify_callback_addr)
+ {
+ /* We can get here either if there is no plabel in the export list
+ for the main image, or if something strange happened (?) */
+ warning (_("\
+Couldn't find a plabel (indirect function label) for the exception callback.\n\
+GDB will not be able to intercept exception events."));
+ return 0;
+ }
+ }
+ else
+ deprecated_exception_catchpoints_are_fragile = 0;
}
- else
- deprecated_exception_catchpoints_are_fragile = 0;
-#endif
/* Now, look for the breakpointable routine in end.o */
/* This should also be available in the SOM symbol dict. if end.o linked in */
}
else
{
- warning ("Unable to find exception callback routine to set breakpoint (%s).", HP_ACC_EH_break);
- warning ("Suggest linking executable with -g (link in /opt/langtools/lib/end.o).");
- warning ("GDB will be unable to intercept exception events.");
+ warning (_("\
+Unable to find exception callback routine to set breakpoint (%s).\n\
+Suggest linking executable with -g (link in /opt/langtools/lib/end.o).\n\
+GDB will be unable to intercept exception events."),
+ HP_ACC_EH_break);
eh_break_addr = 0;
return 0;
}
}
else
{
- warning ("Unable to enable interception of exception catches.");
- warning ("Executable may not have been compiled debuggable with HP aCC.");
- warning ("Suggest linking executable with -g (link in /opt/langtools/lib/end.o).");
+ warning (_("\
+Unable to enable interception of exception catches.\n\
+Executable may not have been compiled debuggable with HP aCC.\n\
+Suggest linking executable with -g (link in /opt/langtools/lib/end.o)."));
return 0;
}
}
}
else
{
- warning ("Unable to enable interception of exception throws.");
- warning ("Executable may not have been compiled debuggable with HP aCC.");
- warning ("Suggest linking executable with -g (link in /opt/langtools/lib/end.o).");
+ warning (_("\
+Unable to enable interception of exception throws.\n\
+Executable may not have been compiled debuggable with HP aCC.\n\
+Suggest linking executable with -g (link in /opt/langtools/lib/end.o)."));
return 0;
}
}
/* there may be other cases in the future */
}
- /* Set the EH hook to point to the callback routine */
+ /* Set the EH hook to point to the callback routine. */
store_unsigned_integer (buf, 4, enable ? eh_notify_callback_addr : 0); /* FIXME 32x64 problem */
/* pai: (temp) FIXME should there be a pack operation first? */
if (target_write_memory (eh_notify_hook_addr, buf, 4)) /* FIXME 32x64 problem */
{
- warning ("Could not write to target memory for exception event callback.");
- warning ("Interception of exception events may not work.");
+ warning (_("\
+Could not write to target memory for exception event callback.\n\
+Interception of exception events may not work."));
return (struct symtab_and_line *) -1;
}
if (enable)
}
else
{
- warning ("Internal error: Invalid inferior pid? Cannot intercept exception events.");
+ warning (_("Internal error: Invalid inferior pid? Cannot intercept exception events."));
return (struct symtab_and_line *) -1;
}
}
store_unsigned_integer (buf, 4, enable ? 1 : 0);
if (target_write_memory (eh_catch_throw_addr, buf, 4)) /* FIXME 32x64? */
{
- warning ("Couldn't enable exception throw interception.");
+ warning (_("Couldn't enable exception throw interception."));
return (struct symtab_and_line *) -1;
}
break;
store_unsigned_integer (buf, 4, enable ? 1 : 0);
if (target_write_memory (eh_catch_catch_addr, buf, 4)) /* FIXME 32x64? */
{
- warning ("Couldn't enable exception catch interception.");
+ warning (_("Couldn't enable exception catch interception."));
return (struct symtab_and_line *) -1;
}
break;
default:
- error ("Request to enable unknown or unsupported exception event.");
+ error (_("Request to enable unknown or unsupported exception event."));
}
/* Copy break address into new sal struct, malloc'ing if needed. */
/* Record some information about the current exception event */
static struct exception_event_record current_ex_event;
-/* Convenience struct */
-static struct symtab_and_line null_symtab_and_line =
-{NULL, 0, 0, 0};
/* Report current exception event. Returns a pointer to a record
that describes the kind of the event, where it was thrown from,
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
struct hppa_hpux_sigtramp_unwind_cache *info;
unsigned int flag;
- CORE_ADDR sp, scptr;
- int i, incr, off, szoff;
+ CORE_ADDR sp, scptr, off;
+ int i, incr, szoff;
if (*this_cache)
return *this_cache;
sp = frame_unwind_register_unsigned (next_frame, HPPA_SP_REGNUM);
- scptr = sp - 1352;
+ 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, 4);
-
- if (!(flag & 0x40))
+ flag = read_memory_unsigned_integer(scptr + HPPA_HPUX_SS_FLAGS_OFFSET, 4);
+
+ if (!(flag & HPPA_HPUX_SS_WIDEREGS))
{
/* Narrow registers. */
- off = scptr + offsetof (save_state_t, ss_narrow);
+ off = scptr + HPPA_HPUX_SS_NARROW_OFFSET;
incr = 4;
szoff = 0;
}
else
{
/* Wide registers. */
- off = scptr + offsetof (save_state_t, ss_wide) + 8;
+ off = scptr + HPPA_HPUX_SS_WIDE_OFFSET + 8;
incr = 8;
szoff = (tdep->bytes_per_address == 4 ? 4 : 0);
}
off += incr;
}
- for (i = 0; ARRAY_SIZE (hppa_hpux_tramp_reg); i++)
+ 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;
}
static void
hppa_hpux_sigtramp_frame_prev_register (struct frame_info *next_frame,
- void **this_prologue_cache,
- int regnum, int *optimizedp,
- enum lval_type *lvalp,
- CORE_ADDR *addrp,
- int *realnump, void *valuep)
+ void **this_prologue_cache,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp,
+ CORE_ADDR *addrp,
+ int *realnump, gdb_byte *valuep)
{
struct hppa_hpux_sigtramp_unwind_cache *info
= hppa_hpux_sigtramp_frame_unwind_cache (next_frame, this_prologue_cache);
static const struct frame_unwind *
hppa_hpux_sigtramp_unwind_sniffer (struct frame_info *next_frame)
{
+ struct unwind_table_entry *u;
CORE_ADDR pc = frame_pc_unwind (next_frame);
- char *name;
- find_pc_partial_function (pc, &name, NULL, NULL);
+ 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 (next_frame, u->region_start,
+ buf, sizeof buf))
+ return NULL;
+
+ insn = extract_unsigned_integer (buf, sizeof buf);
+ if ((insn & 0xffe0e000) == 0xe8400000)
+ u = find_unwind_entry(u->region_start + hppa_extract_17 (insn) + 8);
+ }
- if (name && strcmp(name, "_sigreturn") == 0)
+ if (u && u->HP_UX_interrupt_marker)
return &hppa_hpux_sigtramp_frame_unwind;
return NULL;
}
static CORE_ADDR
-hppa_hpux_som_find_global_pointer (struct value *function)
+hppa32_hpux_find_global_pointer (struct value *function)
{
CORE_ADDR faddr;
return extract_unsigned_integer (buf, sizeof (buf));
}
- return som_solib_get_got_by_pc (faddr);
+ return gdbarch_tdep (current_gdbarch)->solib_get_got_by_pc (faddr);
}
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)
+ CORE_ADDR faddr;
+ char buf[32];
+
+ faddr = value_as_address (function);
+
+ if (in_opd_section (faddr))
{
- struct objfile *objfile;
- struct minimal_symbol *funsym, *stubsym;
- CORE_ADDR stubaddr = 0;
+ 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);
+ }
+}
- funsym = lookup_minimal_symbol_by_pc (funcaddr);
- if (!funsym)
- error ("Unable to find symbol for target function.\n");
+static unsigned int ldsid_pattern[] = {
+ 0x000010a0, /* ldsid (rX),rY */
+ 0x00001820, /* mtsp rY,sr0 */
+ 0xe0000000 /* be,n (sr0,rX) */
+};
- ALL_OBJFILES (objfile)
+static CORE_ADDR
+hppa_hpux_search_pattern (CORE_ADDR start, CORE_ADDR end,
+ unsigned int *patterns, int count)
+{
+ 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);
+
+ for (offset = 0; offset <= num_insns - count; offset++)
+ {
+ for (i = 0; i < count; i++)
{
- stubsym = lookup_minimal_symbol_solib_trampoline
- (SYMBOL_LINKAGE_NAME (funsym), objfile);
+ if ((insns[offset + i] & patterns[i]) != patterns[i])
+ break;
+ }
+ if (i == count)
+ break;
+ }
- if (stubsym)
- {
- struct unwind_table_entry *u;
+ 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)
+{
+ 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;
- u = find_unwind_entry (SYMBOL_VALUE (stubsym));
- if (u == NULL
- || (u->stub_unwind.stub_type != IMPORT
- && u->stub_unwind.stub_type != IMPORT_SHLIB))
- continue;
+ sec = find_pc_section (pc);
+ obj = sec->objfile;
+ priv = objfile_data (obj, hppa_objfile_priv_data);
- stubaddr = SYMBOL_VALUE (stubsym);
+ if (!priv)
+ priv = hppa_init_objfile_priv_data (obj);
+ if (!priv)
+ error (_("Internal error creating objfile private data."));
- /* 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;
+ /* 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;
+ }
}
}
+ }
- if (stubaddr != 0)
+ /* 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)
{
- /* 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."));
+
+ /* 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;
+ 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)
{
- /* No import stub found; let's synthesize one. */
-
- /* 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);
+ for (offset = 0; offset < sizeof (buf); offset++)
+ {
+ unsigned int insn;
+
+ insn = extract_unsigned_integer (buf + offset, HPPA_INSN_SIZE);
+ if (insn == 0xe840d002) /* bve,n (rp) */
+ {
+ addr = (end - sizeof (buf)) + offset;
+ goto found_pattern;
+ }
+ }
}
+ }
- /* 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;
- regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr);
+ /* Can't find a suitable sequence. */
+ return 0;
- /* Continue from __gcc_plt_call. */
- *real_pc = SYMBOL_VALUE (sym);
- }
- else
+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)
{
- ULONGEST gp;
+ stubsym = lookup_minimal_symbol_solib_trampoline
+ (SYMBOL_LINKAGE_NAME (funsym), objfile);
- /* Use __d_plt_call as a fallback; __d_plt_call expects to be called
- with a plabel, so we need to build one. */
+ if (stubsym)
+ {
+ struct unwind_table_entry *u;
- 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.");
+ u = find_unwind_entry (SYMBOL_VALUE (stubsym));
+ if (u == NULL
+ || (u->stub_unwind.stub_type != IMPORT
+ && u->stub_unwind.stub_type != IMPORT_SHLIB))
+ continue;
- gp = gdbarch_tdep (gdbarch)->find_global_pointer (funcaddr);
- write_memory_unsigned_integer (sp, 4, funcaddr);
- write_memory_unsigned_integer (sp + 4, 4, gp);
+ stubaddr = SYMBOL_VALUE (stubsym);
- /* plabel is passed in r22 */
- regcache_cooked_write_unsigned (current_regcache, 22, sp);
+ /* 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;
+ }
}
- /* Pushed one stack frame, which has to be 64-byte aligned. */
- sp += 64;
+ return stubaddr;
+}
- return sp;
+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;
}
-\f
-/* 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
+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;
-/* 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
+ 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);
+ }
+ }
-/* The size of `struct save_state. */
-#define HPPA_HPUX_SAVE_STATE_SIZE 1152
+ warning (_("Cannot find suitable address to place dummy breakpoint; nested "
+ "calls may fail."));
+ return addr - 4;
+}
-/* 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
+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 = 0;
+
+ 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."));
+
+ 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."));
+
+ regcache_cooked_write_unsigned (current_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,
/* 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");
+ error (_("Register set contents too small"));
if (flags & HPPA_HPUX_SS_WIDEREGS)
hppa_hpux_supply_ss_wide (regcache, regnum, save_state);
{
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;
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);
}
static void
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);
}
static enum gdb_osabi
{
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;
}
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);