2004-07-10 Randolph Chung <tausq@debian.org>

* hppa-hpux-tdep.c (hppa_hpux_som_find_global_pointer): New
	function.
	(hppa_hpux_push_dummy_code): New function.
	(hppa_hpux_init_abi): Set push_dummy_code and call_dummy_location.
	Set find_global_pointer method.
This commit is contained in:
Randolph Chung 2004-07-11 06:51:41 +00:00
parent bbb268c36c
commit c268433a1a
2 changed files with 201 additions and 0 deletions

View file

@ -1,3 +1,11 @@
2004-07-10 Randolph Chung <tausq@debian.org>
* hppa-hpux-tdep.c (hppa_hpux_som_find_global_pointer): New
function.
(hppa_hpux_push_dummy_code): New function.
(hppa_hpux_init_abi): Set push_dummy_code and call_dummy_location.
Set find_global_pointer method.
2004-07-10 Mark Kettenis <kettenis@gnu.org>
* NEWS: Mention BSD libkvm interface.

View file

@ -1204,6 +1204,194 @@ hppa_hpux_sigtramp_unwind_sniffer (struct frame_info *next_frame)
return NULL;
}
static CORE_ADDR
hppa_hpux_som_find_global_pointer (struct value *function)
{
CORE_ADDR faddr;
faddr = value_as_address (function);
/* Is this a plabel? If so, dereference it to get the gp value. */
if (faddr & 2)
{
int status;
char buf[4];
faddr &= ~3;
status = target_read_memory (faddr + 4, buf, sizeof (buf));
if (status == 0)
return extract_unsigned_integer (buf, sizeof (buf));
}
return som_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)
{
/* 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");
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;
}
}
if (stubaddr != 0)
{
/* Argument to __gcc_plt_call is passed in r22. */
regcache_cooked_write_unsigned (current_regcache, 22, stubaddr);
}
else
{
/* 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);
}
/* 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);
/* Continue from __gcc_plt_call. */
*real_pc = SYMBOL_VALUE (sym);
}
else
{
unsigned int gp;
/* Use __d_plt_call as a fallback; __d_plt_call expects to be called
with a plabel, so we need to build one. */
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.");
gp = gdbarch_tdep (gdbarch)->find_global_pointer (funcaddr);
write_memory_unsigned_integer (sp, 4, funcaddr);
write_memory_unsigned_integer (sp + 4, 4, gp);
/* plabel is passed in r22 */
regcache_cooked_write_unsigned (current_regcache, 22, sp);
}
/* Pushed one stack frame, which has to be 64-byte aligned. */
sp += 64;
return sp;
}
static void
hppa_hpux_inferior_created (struct target_ops *objfile, int from_tty)
{
@ -1229,6 +1417,9 @@ hppa_hpux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
hppa_hpux_in_solib_return_trampoline);
set_gdbarch_skip_trampoline_code (gdbarch, hppa_hpux_skip_trampoline_code);
set_gdbarch_push_dummy_code (gdbarch, hppa_hpux_push_dummy_code);
set_gdbarch_call_dummy_location (gdbarch, ON_STACK);
frame_unwind_append_sniffer (gdbarch, hppa_hpux_sigtramp_unwind_sniffer);
observer_attach_inferior_created (hppa_hpux_inferior_created);
@ -1240,6 +1431,8 @@ hppa_hpux_som_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
tdep->is_elf = 0;
tdep->find_global_pointer = hppa_hpux_som_find_global_pointer;
hppa_hpux_init_abi (info, gdbarch);
}