This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
[patch/rfc/hppa] handle setting gp for calling shlib functions
- From: Randolph Chung <randolph at tausq dot org>
- To: gdb-patches at sources dot redhat dot com
- Date: Wed, 28 Apr 2004 23:23:24 -0700
- Subject: [patch/rfc/hppa] handle setting gp for calling shlib functions
- Reply-to: Randolph Chung <randolph at tausq dot org>
We need to be careful about setting the gp when doing a function call in
several cases:
(1) when calling a function through a function descriptor
(2) when calling a shared library function (e.g. malloc)
This patch tries to fix these problems --
(1) is relatively easy
On the 32-bit ABI, function descriptors (called plabels) have a bit set
in their address. We implement the convert_from_func_ptr_addr method to
convert a plabel to a function address, and set the gp.
(2) is a bit more tricky. For hppa-linux we can use almost the same
method as ia64-linux -- by looking through the elf .dynamic section we
can determine the appropriate PLTGOT offset to use. Just have to be
careful that, if we get a pointer that is inside the plt we don't try to
do this, since the function pointer is not yet fixed up.
For SOM and hppa64-hpux, I am not quite sure how to do this. pa64solib.c
has some code that reads the dp of a shlib into object private data.
Possibly we can use this. For now both of these targets have a dummy
find_global_pointer method that returns 0, so they retain the previous
broken behavior.
On hppa-linux this fixes many failures related to calling inferior
shlib functions from gdb. It also fixes the C++ virtfunc tests.
Is this ok? Or can somebody suggest a better method?
thanks
randolph
2004-04-28 Randolph Chung <tausq@debian.org>
* hppa-tdep.h (gdbarch_tdep): Add find_global_pointer method.
(FIND_GLOBAL_POINTER): Define.
* hppa-tdep.c (hppa32_push_dummy_call): Try to find and set the gp
if we are calling a function in a shared library.
(hppa32_convert_from_func_ptr_addr): New.
(hppa_find_global_pointer): New.
(hppa_gdbarch_init): Set find_global_pointer and
convert_from_func_ptr_addr.
* hppa-linux-tdep.c (hppa_linux_find_global_pointer): New function to
find the gp in an elf object.
(hppa_linux_init_abi): Set find_global_pointer
Index: hppa-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/hppa-tdep.h,v
retrieving revision 1.4
diff -u -p -r1.4 hppa-tdep.h
--- hppa-tdep.h 23 Apr 2004 02:54:21 -0000 1.4
+++ hppa-tdep.h 29 Apr 2004 05:50:00 -0000
@@ -33,8 +33,15 @@ struct gdbarch_tdep
/* Is this an ELF target? This can be 64-bit HP-UX, or a 32/64-bit GNU/Linux
system. */
int is_elf;
+
+ /* Given a function address, try to find the global pointer for the
+ corresponding shared object. */
+ CORE_ADDR (*find_global_pointer) (CORE_ADDR);
};
+#define FIND_GLOBAL_POINTER \
+ (gdbarch_tdep (current_gdbarch)->find_global_pointer)
+
/*
* Unwind table and descriptor.
*/
Index: hppa-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/hppa-tdep.c,v
retrieving revision 1.153
diff -u -p -r1.153 hppa-tdep.c
--- hppa-tdep.c 29 Apr 2004 03:36:49 -0000 1.153
+++ hppa-tdep.c 29 Apr 2004 05:49:59 -0000
@@ -768,6 +768,10 @@ hppa32_push_dummy_call (struct gdbarch *
/* Two passes. First pass computes the location of everything,
second pass writes the bytes out. */
int write_pass;
+
+ /* Global pointer (r19) of the function we are trying to call. */
+ CORE_ADDR gp;
+
for (write_pass = 0; write_pass < 2; write_pass++)
{
CORE_ADDR struct_ptr = 0;
@@ -888,6 +892,11 @@ hppa32_push_dummy_call (struct gdbarch *
if (struct_return)
write_register (28, struct_addr);
+ gp = FIND_GLOBAL_POINTER (func_addr);
+
+ if (gp != 0)
+ write_register (19, gp);
+
/* Set the return address. */
regcache_cooked_write_unsigned (regcache, RP_REGNUM, bp_addr);
@@ -1021,6 +1030,25 @@ hppa64_push_dummy_call (struct gdbarch *
}
static CORE_ADDR
+hppa32_convert_from_func_ptr_addr (struct gdbarch *gdbarch,
+ CORE_ADDR addr,
+ struct target_ops *targ)
+{
+ if (addr & 2)
+ {
+ ULONGEST gp;
+
+ addr &= ~3;
+
+ gp = read_memory_unsigned_integer (addr + 4, 4);
+ write_register (19, gp);
+ addr = read_memory_unsigned_integer (addr, 4);
+ }
+
+ return addr;
+}
+
+static CORE_ADDR
hppa32_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
{
/* HP frames are 64-byte (or cache line) aligned (yes that's _byte_
@@ -2230,6 +2258,12 @@ hppa_pseudo_register_read (struct gdbarc
store_unsigned_integer (buf, sizeof(tmp), tmp);
}
+static CORE_ADDR
+hppa_find_global_pointer (CORE_ADDR faddr)
+{
+ return 0;
+}
+
/* Here is a table of C type sizes on hppa with various compiles
and options. I measured this on PA 9000/800 with HP-UX 11.11
and these compilers:
@@ -2297,6 +2331,8 @@ hppa_gdbarch_init (struct gdbarch_info i
else
tdep->bytes_per_address = 4;
+ tdep->find_global_pointer = hppa_find_global_pointer;
+
/* Some parts of the gdbarch vector depend on whether we are running
on a 32 bits or 64 bits target. */
switch (tdep->bytes_per_address)
@@ -2356,6 +2392,8 @@ hppa_gdbarch_init (struct gdbarch_info i
case 4:
set_gdbarch_push_dummy_call (gdbarch, hppa32_push_dummy_call);
set_gdbarch_frame_align (gdbarch, hppa32_frame_align);
+ set_gdbarch_convert_from_func_ptr_addr
+ (gdbarch, hppa32_convert_from_func_ptr_addr);
break;
case 8:
set_gdbarch_push_dummy_call (gdbarch, hppa64_push_dummy_call);
Index: hppa-linux-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/hppa-linux-tdep.c,v
retrieving revision 1.1
diff -u -p -r1.1 hppa-linux-tdep.c
--- hppa-linux-tdep.c 29 Apr 2004 03:36:49 -0000 1.1
+++ hppa-linux-tdep.c 29 Apr 2004 05:49:59 -0000
@@ -30,6 +30,8 @@ Foundation, Inc., 59 Temple Place - Suit
#include "dwarf2-frame.h"
#include "hppa-tdep.h"
+#include "elf/common.h"
+
#if 0
/* Convert DWARF register number REG to the appropriate register
number used by GDB. */
@@ -456,6 +458,77 @@ hppa_linux_sigtramp_unwind_sniffer (stru
return NULL;
}
+/* Attempt to find (and return) the global pointer for the given
+ function.
+
+ This is a rather nasty bit of code searchs for the .dynamic section
+ in the objfile corresponding to the pc of the function we're trying
+ to call. Once it finds the addresses at which the .dynamic section
+ lives in the child process, it scans the Elf32_Dyn entries for a
+ DT_PLTGOT tag. If it finds one of these, the corresponding
+ d_un.d_ptr value is the global pointer. */
+
+static CORE_ADDR
+hppa_linux_find_global_pointer (CORE_ADDR faddr)
+{
+ struct obj_section *faddr_sect;
+
+ /* If the address is in the plt section, then the real function hasn't
+ yet been fixed up by the linker so we cannot determine the gp of
+ that function. */
+ if (in_plt_section (faddr, NULL))
+ return 0;
+
+ faddr_sect = find_pc_section (faddr);
+ if (faddr_sect != NULL)
+ {
+ struct obj_section *osect;
+
+ ALL_OBJFILE_OSECTIONS (faddr_sect->objfile, osect)
+ {
+ if (strcmp (osect->the_bfd_section->name, ".dynamic") == 0)
+ break;
+ }
+
+ if (osect < faddr_sect->objfile->sections_end)
+ {
+ CORE_ADDR addr;
+
+ addr = osect->addr;
+ while (addr < osect->endaddr)
+ {
+ int status;
+ LONGEST tag;
+ char buf[4];
+
+ status = target_read_memory (addr, buf, sizeof (buf));
+ if (status != 0)
+ break;
+ tag = extract_signed_integer (buf, sizeof (buf));
+
+ if (tag == DT_PLTGOT)
+ {
+ CORE_ADDR global_pointer;
+
+ status = target_read_memory (addr + 4, buf, sizeof (buf));
+ if (status != 0)
+ break;
+ global_pointer = extract_unsigned_integer (buf, sizeof (buf));
+
+ /* The payoff... */
+ return global_pointer;
+ }
+
+ if (tag == DT_NULL)
+ break;
+
+ addr += 8;
+ }
+ }
+ }
+ return 0;
+}
+
/* Forward declarations. */
extern initialize_file_ftype _initialize_hppa_linux_tdep;
@@ -466,6 +539,8 @@ hppa_linux_init_abi (struct gdbarch_info
/* Linux is always ELF. */
tdep->is_elf = 1;
+
+ tdep->find_global_pointer = hppa_linux_find_global_pointer;
set_gdbarch_write_pc (gdbarch, hppa_linux_target_write_pc);
--
Randolph Chung
Debian GNU/Linux Developer, hppa/ia64 ports
http://www.tausq.org/