2004-12-17 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.
This commit is contained in:
parent
e3070fef41
commit
77d18ded6e
4 changed files with 531 additions and 143 deletions
|
@ -1,3 +1,32 @@
|
||||||
|
2004-12-17 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.
|
||||||
|
|
||||||
2004-12-17 Joel Brobecker <brobecker@gnat.com>
|
2004-12-17 Joel Brobecker <brobecker@gnat.com>
|
||||||
|
|
||||||
* hppa-tdep.c (hppa_lookup_stub_minimal_symbol): New function.
|
* hppa-tdep.c (hppa_lookup_stub_minimal_symbol): New function.
|
||||||
|
|
|
@ -45,6 +45,9 @@
|
||||||
#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
|
#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define IS_32BIT_TARGET(_gdbarch) \
|
||||||
|
((gdbarch_tdep (_gdbarch))->bytes_per_address == 4)
|
||||||
|
|
||||||
/* Forward declarations. */
|
/* Forward declarations. */
|
||||||
extern void _initialize_hppa_hpux_tdep (void);
|
extern void _initialize_hppa_hpux_tdep (void);
|
||||||
extern initialize_file_ftype _initialize_hppa_hpux_tdep;
|
extern initialize_file_ftype _initialize_hppa_hpux_tdep;
|
||||||
|
@ -57,6 +60,20 @@ typedef struct
|
||||||
}
|
}
|
||||||
args_for_find_stub;
|
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.
|
/* 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
|
Note we return one for *any* call trampoline (long-call, arg-reloc), not
|
||||||
|
@ -1229,7 +1246,7 @@ hppa_hpux_sigtramp_unwind_sniffer (struct frame_info *next_frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
static CORE_ADDR
|
static CORE_ADDR
|
||||||
hppa_hpux_som_find_global_pointer (struct value *function)
|
hppa32_hpux_find_global_pointer (struct value *function)
|
||||||
{
|
{
|
||||||
CORE_ADDR faddr;
|
CORE_ADDR faddr;
|
||||||
|
|
||||||
|
@ -1251,6 +1268,337 @@ hppa_hpux_som_find_global_pointer (struct value *function)
|
||||||
return gdbarch_tdep (current_gdbarch)->solib_get_got_by_pc (faddr);
|
return gdbarch_tdep (current_gdbarch)->solib_get_got_by_pc (faddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CORE_ADDR
|
||||||
|
hppa64_hpux_find_global_pointer (struct value *function)
|
||||||
|
{
|
||||||
|
CORE_ADDR faddr;
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
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
|
static CORE_ADDR
|
||||||
hppa_hpux_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp,
|
hppa_hpux_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp,
|
||||||
CORE_ADDR funcaddr, int using_gcc,
|
CORE_ADDR funcaddr, int using_gcc,
|
||||||
|
@ -1258,163 +1606,147 @@ hppa_hpux_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp,
|
||||||
struct type *value_type,
|
struct type *value_type,
|
||||||
CORE_ADDR *real_pc, CORE_ADDR *bp_addr)
|
CORE_ADDR *real_pc, CORE_ADDR *bp_addr)
|
||||||
{
|
{
|
||||||
/* FIXME: tausq/2004-06-09: This needs much more testing. It is broken
|
CORE_ADDR pc, stubaddr;
|
||||||
for pa64, but we should be able to get it to work with a little bit
|
int argreg;
|
||||||
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
|
pc = read_pc ();
|
||||||
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
|
/* Note: we don't want to pass a function descriptor here; push_dummy_call
|
||||||
an application is linked with gcc, it will contain a __gcc_plt_call
|
fills in the PIC register for us. */
|
||||||
helper function. __gcc_plt_call, when passed the entry point of an
|
funcaddr = gdbarch_convert_from_func_ptr_addr (gdbarch, funcaddr, NULL);
|
||||||
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
|
/* The simple case is where we call a function in the same space that we are
|
||||||
function called __d_plt_call exists; __d_plt_call expects a PLABEL instead
|
currently in; in that case we don't really need to do anything. */
|
||||||
of an import stub as an argument.
|
if (hppa_hpux_sr_for_addr (pc) == hppa_hpux_sr_for_addr (funcaddr))
|
||||||
|
|
||||||
// *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;
|
/* Intraspace call. */
|
||||||
struct minimal_symbol *funsym, *stubsym;
|
*bp_addr = hppa_hpux_find_dummy_bpaddr (pc);
|
||||||
CORE_ADDR stubaddr = 0;
|
*real_pc = funcaddr;
|
||||||
|
regcache_cooked_write_unsigned (current_regcache, HPPA_RP_REGNUM, *bp_addr);
|
||||||
|
|
||||||
funsym = lookup_minimal_symbol_by_pc (funcaddr);
|
return sp;
|
||||||
if (!funsym)
|
}
|
||||||
error ("Unable to find symbol for target function.\n");
|
|
||||||
|
|
||||||
ALL_OBJFILES (objfile)
|
/* 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
|
||||||
stubsym = lookup_minimal_symbol_solib_trampoline
|
an application is compiled with HP compilers then this stub is not
|
||||||
(SYMBOL_LINKAGE_NAME (funsym), objfile);
|
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.
|
||||||
|
|
||||||
if (stubsym)
|
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
|
||||||
struct unwind_table_entry *u;
|
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:
|
||||||
|
|
||||||
u = find_unwind_entry (SYMBOL_VALUE (stubsym));
|
All we really need to do the branch to the stub is a short instruction
|
||||||
if (u == NULL
|
sequence like this:
|
||||||
|| (u->stub_unwind.stub_type != IMPORT
|
|
||||||
&& u->stub_unwind.stub_type != IMPORT_SHLIB))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
stubaddr = SYMBOL_VALUE (stubsym);
|
PA1.1:
|
||||||
|
ldsid (rX),r1
|
||||||
|
mtsp r1,sr0
|
||||||
|
be,n (sr0,rX)
|
||||||
|
|
||||||
/* If we found an IMPORT stub, then we can stop searching;
|
PA2.0:
|
||||||
if we found an IMPORT_SHLIB, we want to continue the search
|
bve,n (sr0,rX)
|
||||||
in the hopes that we will find an IMPORT stub. */
|
|
||||||
if (u->stub_unwind.stub_type == IMPORT)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stubaddr != 0)
|
Instead of writing these sequences ourselves, we can find it in
|
||||||
{
|
the instruction stream that belongs to the current space. While this
|
||||||
/* Argument to __gcc_plt_call is passed in r22. */
|
seems difficult at first, we are actually guaranteed to find the sequences
|
||||||
regcache_cooked_write_unsigned (current_regcache, 22, stubaddr);
|
in several places:
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* No import stub found; let's synthesize one. */
|
|
||||||
|
|
||||||
/* ldsid %r21, %r1 */
|
For 32-bit code:
|
||||||
write_memory_unsigned_integer (sp, 4, 0x02a010a1);
|
- in export stubs for shared libraries
|
||||||
/* mtsp %r1,%sr0 */
|
- in the "noshlibs" routine in the main module
|
||||||
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);
|
For 64-bit code:
|
||||||
regcache_cooked_write_unsigned (current_regcache, 22, sp);
|
- at the end of each "regular" function
|
||||||
}
|
|
||||||
|
|
||||||
/* We set the breakpoint address and r31 to (close to) where the current
|
We cache the address of these sequences in the objfile's private data
|
||||||
pc is; when __gcc_plt_call returns, it will restore pcsqh to the
|
since these operations can potentially be quite expensive.
|
||||||
current value based on this. The -4 is needed for frame unwinding
|
|
||||||
to work properly -- we need to land in a different function than
|
So, what we do is:
|
||||||
the current function. */
|
- write a stack trampoline
|
||||||
*bp_addr = (read_register (HPPA_PCOQ_HEAD_REGNUM) & ~3) - 4;
|
- 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);
|
regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr);
|
||||||
|
|
||||||
/* Continue from __gcc_plt_call. */
|
*real_pc = hppa32_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg);
|
||||||
*real_pc = SYMBOL_VALUE (sym);
|
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
|
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 */
|
||||||
|
};
|
||||||
|
|
||||||
/* Use __d_plt_call as a fallback; __d_plt_call expects to be called
|
/* for hppa64, we don't need to call through a stub; all functions
|
||||||
with a plabel, so we need to build one. */
|
return via a bve. */
|
||||||
|
regcache_cooked_write_unsigned (current_regcache, 22, funcaddr);
|
||||||
|
write_memory (sp, (char *)&hppa64_tramp, sizeof (hppa64_tramp));
|
||||||
|
|
||||||
sym = lookup_minimal_symbol ("__d_plt_call", NULL, NULL);
|
*bp_addr = pc - 4;
|
||||||
if (sym == NULL)
|
regcache_cooked_write_unsigned (current_regcache, 31, *bp_addr);
|
||||||
error("Can't find an address for __d_plt_call or __gcc_plt_call "
|
|
||||||
"trampoline\nSuggest linking executable with -g or compiling "
|
|
||||||
"with gcc.");
|
|
||||||
|
|
||||||
gp = gdbarch_tdep (gdbarch)->find_global_pointer (funcaddr);
|
*real_pc = hppa64_hpux_search_dummy_call_sequence (gdbarch, pc, &argreg);
|
||||||
write_memory_unsigned_integer (sp, 4, funcaddr);
|
if (*real_pc == 0)
|
||||||
write_memory_unsigned_integer (sp + 4, 4, gp);
|
error ("Cannot make interspace call from here.\n");
|
||||||
|
|
||||||
/* plabel is passed in r22 */
|
regcache_cooked_write_unsigned (current_regcache, argreg, sp);
|
||||||
regcache_cooked_write_unsigned (current_regcache, 22, sp);
|
|
||||||
|
sp += sizeof (hppa64_tramp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pushed one stack frame, which has to be 64-byte aligned. */
|
sp = gdbarch_frame_align (gdbarch, sp);
|
||||||
sp += 64;
|
|
||||||
|
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Bit in the `ss_flag' member of `struct save_state' that indicates
|
/* Bit in the `ss_flag' member of `struct save_state' that indicates
|
||||||
|
@ -1656,7 +1988,7 @@ hppa_hpux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
||||||
{
|
{
|
||||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
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;
|
tdep->in_solib_call_trampoline = hppa32_hpux_in_solib_call_trampoline;
|
||||||
else
|
else
|
||||||
tdep->in_solib_call_trampoline = hppa64_hpux_in_solib_call_trampoline;
|
tdep->in_solib_call_trampoline = hppa64_hpux_in_solib_call_trampoline;
|
||||||
|
@ -1689,7 +2021,8 @@ hppa_hpux_som_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
||||||
|
|
||||||
tdep->is_elf = 0;
|
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);
|
hppa_hpux_init_abi (info, gdbarch);
|
||||||
som_solib_select (tdep);
|
som_solib_select (tdep);
|
||||||
}
|
}
|
||||||
|
@ -1700,6 +2033,8 @@ hppa_hpux_elf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
||||||
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
|
||||||
|
|
||||||
tdep->is_elf = 1;
|
tdep->is_elf = 1;
|
||||||
|
tdep->find_global_pointer = hppa64_hpux_find_global_pointer;
|
||||||
|
|
||||||
hppa_hpux_init_abi (info, gdbarch);
|
hppa_hpux_init_abi (info, gdbarch);
|
||||||
pa64_solib_select (tdep);
|
pa64_solib_select (tdep);
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,6 +273,20 @@ hppa_symbol_address(const char *sym)
|
||||||
else
|
else
|
||||||
return (CORE_ADDR)-1;
|
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
|
/* Compare the start address for two unwind entries returning 1 if
|
||||||
|
@ -529,15 +543,8 @@ read_unwind_info (struct objfile *objfile)
|
||||||
obj_private = (struct hppa_objfile_private *)
|
obj_private = (struct hppa_objfile_private *)
|
||||||
objfile_data (objfile, hppa_objfile_priv_data);
|
objfile_data (objfile, hppa_objfile_priv_data);
|
||||||
if (obj_private == NULL)
|
if (obj_private == NULL)
|
||||||
{
|
obj_private = hppa_init_objfile_priv_data (objfile);
|
||||||
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->unwind_info = ui;
|
obj_private->unwind_info = ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -912,7 +919,8 @@ hppa32_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
|
||||||
write_register (19, gp);
|
write_register (19, gp);
|
||||||
|
|
||||||
/* Set the return address. */
|
/* 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. */
|
/* Update the Stack Pointer. */
|
||||||
regcache_cooked_write_unsigned (regcache, HPPA_SP_REGNUM, param_end);
|
regcache_cooked_write_unsigned (regcache, HPPA_SP_REGNUM, param_end);
|
||||||
|
@ -952,6 +960,10 @@ hppa64_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
|
||||||
been pushed. */
|
been pushed. */
|
||||||
CORE_ADDR new_sp = 0;
|
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,
|
/* Two passes. First pass computes the location of everything,
|
||||||
second pass writes the bytes out. */
|
second pass writes the bytes out. */
|
||||||
int write_pass;
|
int write_pass;
|
||||||
|
@ -1033,8 +1045,14 @@ hppa64_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
|
||||||
if (struct_return)
|
if (struct_return)
|
||||||
write_register (28, struct_addr);
|
write_register (28, struct_addr);
|
||||||
|
|
||||||
|
gp = tdep->find_global_pointer (function);
|
||||||
|
|
||||||
|
if (gp != 0)
|
||||||
|
write_register (27, gp);
|
||||||
|
|
||||||
/* Set the return address. */
|
/* 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. */
|
/* Update the Stack Pointer. */
|
||||||
regcache_cooked_write_unsigned (regcache, HPPA_SP_REGNUM, param_end + 64);
|
regcache_cooked_write_unsigned (regcache, HPPA_SP_REGNUM, param_end + 64);
|
||||||
|
|
|
@ -204,6 +204,9 @@ struct hppa_objfile_private
|
||||||
struct hppa_unwind_info *unwind_info; /* a pointer */
|
struct hppa_unwind_info *unwind_info; /* a pointer */
|
||||||
struct so_list *so_info; /* a pointer */
|
struct so_list *so_info; /* a pointer */
|
||||||
CORE_ADDR dp;
|
CORE_ADDR dp;
|
||||||
|
|
||||||
|
int dummy_call_sequence_reg;
|
||||||
|
CORE_ADDR dummy_call_sequence_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct objfile_data *hppa_objfile_priv_data;
|
extern const struct objfile_data *hppa_objfile_priv_data;
|
||||||
|
@ -235,4 +238,7 @@ extern struct minimal_symbol *
|
||||||
hppa_lookup_stub_minimal_symbol (const char *name,
|
hppa_lookup_stub_minimal_symbol (const char *name,
|
||||||
enum unwind_stub_types stub_type);
|
enum unwind_stub_types stub_type);
|
||||||
|
|
||||||
|
extern struct hppa_objfile_private *
|
||||||
|
hppa_init_objfile_priv_data (struct objfile *objfile);
|
||||||
|
|
||||||
#endif /* HPPA_TDEP_H */
|
#endif /* HPPA_TDEP_H */
|
||||||
|
|
Loading…
Add table
Reference in a new issue