/* Handle SVR4 shared libraries for GDB, the GNU Debugger.
- Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999,
- 2000, 2001, 2003, 2004, 2005
- Free Software Foundation, Inc.
+ Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000,
+ 2001, 2003, 2004, 2005, 2006, 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 "gdb_assert.h"
#include "solist.h"
+#include "solib.h"
#include "solib-svr4.h"
#include "bfd-target.h"
+#include "elf-bfd.h"
#include "exec.h"
static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
/* Pointer to copy of link map from inferior. The type is char *
rather than void *, so that we may use byte offsets to find the
various fields without the need for a cast. */
- char *lm;
+ gdb_byte *lm;
+
+ /* Amount by which addresses in the binary should be relocated to
+ match the inferior. This could most often be taken directly
+ from lm, but when prelinking is involved and the prelink base
+ address changes, we may need a different offset, we want to
+ warn about the difference and compute it only once. */
+ CORE_ADDR l_addr;
};
/* On SVR4 systems, a list of symbols in the dynamic linker where
/* link map access functions */
static CORE_ADDR
-LM_ADDR (struct so_list *so)
+LM_ADDR_FROM_LINK_MAP (struct so_list *so)
{
struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
- return (CORE_ADDR) extract_signed_integer (so->lm_info->lm + lmo->l_addr_offset,
+ return (CORE_ADDR) extract_signed_integer (so->lm_info->lm
+ + lmo->l_addr_offset,
lmo->l_addr_size);
}
+static int
+HAS_LM_DYNAMIC_FROM_LINK_MAP ()
+{
+ struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
+
+ return (lmo->l_ld_size != 0);
+}
+
+static CORE_ADDR
+LM_DYNAMIC_FROM_LINK_MAP (struct so_list *so)
+{
+ struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
+
+ gdb_assert (lmo->l_ld_size != 0);
+
+ return (CORE_ADDR) extract_signed_integer (so->lm_info->lm
+ + lmo->l_ld_offset,
+ lmo->l_ld_size);
+}
+
+static CORE_ADDR
+LM_ADDR_CHECK (struct so_list *so, bfd *abfd)
+{
+ if (so->lm_info->l_addr == (CORE_ADDR)-1)
+ {
+ struct bfd_section *dyninfo_sect;
+ CORE_ADDR l_addr, l_dynaddr, dynaddr, align = 0x1000;
+
+ l_addr = LM_ADDR_FROM_LINK_MAP (so);
+
+ if (! abfd || ! HAS_LM_DYNAMIC_FROM_LINK_MAP ())
+ goto set_addr;
+
+ l_dynaddr = LM_DYNAMIC_FROM_LINK_MAP (so);
+
+ dyninfo_sect = bfd_get_section_by_name (abfd, ".dynamic");
+ if (dyninfo_sect == NULL)
+ goto set_addr;
+
+ dynaddr = bfd_section_vma (abfd, dyninfo_sect);
+
+ if (dynaddr + l_addr != l_dynaddr)
+ {
+ if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
+ {
+ Elf_Internal_Ehdr *ehdr = elf_tdata (abfd)->elf_header;
+ Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr;
+ int i;
+
+ align = 1;
+
+ for (i = 0; i < ehdr->e_phnum; i++)
+ if (phdr[i].p_type == PT_LOAD && phdr[i].p_align > align)
+ align = phdr[i].p_align;
+ }
+
+ /* Turn it into a mask. */
+ align--;
+
+ /* If the changes match the alignment requirements, we
+ assume we're using a core file that was generated by the
+ same binary, just prelinked with a different base offset.
+ If it doesn't match, we may have a different binary, the
+ same binary with the dynamic table loaded at an unrelated
+ location, or anything, really. To avoid regressions,
+ don't adjust the base offset in the latter case, although
+ odds are that, if things really changed, debugging won't
+ quite work. */
+ if ((l_addr & align) == 0 && ((dynaddr - l_dynaddr) & align) == 0)
+ {
+ l_addr = l_dynaddr - dynaddr;
+
+ warning (_(".dynamic section for \"%s\" "
+ "is not at the expected address"), so->so_name);
+ warning (_("difference appears to be caused by prelink, "
+ "adjusting expectations"));
+ }
+ else
+ warning (_(".dynamic section for \"%s\" "
+ "is not at the expected address "
+ "(wrong library or version mismatch?)"), so->so_name);
+ }
+
+ set_addr:
+ so->lm_info->l_addr = l_addr;
+ }
+
+ return so->lm_info->l_addr;
+}
+
static CORE_ADDR
LM_NEXT (struct so_list *so)
{
static CORE_ADDR debug_base; /* Base of dynamic linker structures */
static CORE_ADDR breakpoint_addr; /* Address where end bkpt is set */
+/* Validity flag for debug_loader_offset. */
+static int debug_loader_offset_p;
+
+/* Load address for the dynamic linker, inferred. */
+static CORE_ADDR debug_loader_offset;
+
+/* Name of the dynamic linker, valid if debug_loader_offset_p. */
+static char *debug_loader_name;
+
/* Local function prototypes */
static int match_main (char *);
struct bfd_section *dyninfo_sect;
int dyninfo_sect_size;
CORE_ADDR dyninfo_addr;
- char *buf;
- char *bufend;
+ gdb_byte *buf;
+ gdb_byte *bufend;
int arch_size;
/* Find the start address of the .dynamic section. */
}
else if (dyn_tag == DT_MIPS_RLD_MAP)
{
- char *pbuf;
+ gdb_byte *pbuf;
int pbuf_size = TARGET_PTR_BIT / HOST_CHAR_BIT;
pbuf = alloca (pbuf_size);
}
else if (dyn_tag == DT_MIPS_RLD_MAP)
{
- char *pbuf;
+ gdb_byte *pbuf;
int pbuf_size = TARGET_PTR_BIT / HOST_CHAR_BIT;
pbuf = alloca (pbuf_size);
return (debug_base);
}
-/*
-
- LOCAL FUNCTION
-
- first_link_map_member -- locate first member in dynamic linker's map
-
- SYNOPSIS
+/* Find the first element in the inferior's dynamic link map, and
+ return its address in the inferior.
- static CORE_ADDR first_link_map_member (void)
-
- DESCRIPTION
-
- Find the first element in the inferior's dynamic link map, and
- return its address in the inferior. This function doesn't copy the
- link map entry itself into our address space; current_sos actually
- does the reading. */
+ FIXME: Perhaps we should validate the info somehow, perhaps by
+ checking r_version for a known version number, or r_state for
+ RT_CONSISTENT. */
static CORE_ADDR
-first_link_map_member (void)
+solib_svr4_r_map (void)
{
- CORE_ADDR lm = 0;
struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
- char *r_map_buf = xmalloc (lmo->r_map_size);
- struct cleanup *cleanups = make_cleanup (xfree, r_map_buf);
- read_memory (debug_base + lmo->r_map_offset, r_map_buf, lmo->r_map_size);
+ return read_memory_typed_address (debug_base + lmo->r_map_offset,
+ builtin_type_void_data_ptr);
+}
- /* Assume that the address is unsigned. */
- lm = extract_unsigned_integer (r_map_buf, lmo->r_map_size);
+/* Find the link map for the dynamic linker (if it is not in the
+ normal list of loaded shared objects). */
- /* FIXME: Perhaps we should validate the info somehow, perhaps by
- checking r_version for a known version number, or r_state for
- RT_CONSISTENT. */
+static CORE_ADDR
+solib_svr4_r_ldsomap (void)
+{
+ struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
+ ULONGEST version;
- do_cleanups (cleanups);
+ /* Check version, and return zero if `struct r_debug' doesn't have
+ the r_ldsomap member. */
+ version = read_memory_unsigned_integer (debug_base + lmo->r_version_offset,
+ lmo->r_version_size);
+ if (version < 2 || lmo->r_ldsomap_offset == -1)
+ return 0;
- return (lm);
+ return read_memory_typed_address (debug_base + lmo->r_ldsomap_offset,
+ builtin_type_void_data_ptr);
}
/*
int errcode;
int from_tty = *(int *)from_ttyp;
struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
- char *l_name_buf = xmalloc (lmo->l_name_size);
+ gdb_byte *l_name_buf = xmalloc (lmo->l_name_size);
struct cleanup *cleanups = make_cleanup (xfree, l_name_buf);
if (symfile_objfile)
return 0; /* failed somehow... */
/* First link map member should be the executable. */
- if ((lm = first_link_map_member ()) == 0)
+ lm = solib_svr4_r_map ();
+ if (lm == 0)
return 0; /* failed somehow... */
/* Read address of name from target memory to GDB. */
return 1;
}
+/* If no shared library information is available from the dynamic
+ linker, build a fallback list from other sources. */
+
+static struct so_list *
+svr4_default_sos (void)
+{
+ struct so_list *head = NULL;
+ struct so_list **link_ptr = &head;
+
+ if (debug_loader_offset_p)
+ {
+ struct so_list *new = XZALLOC (struct so_list);
+
+ new->lm_info = xmalloc (sizeof (struct lm_info));
+
+ /* Nothing will ever check the cached copy of the link
+ map if we set l_addr. */
+ new->lm_info->l_addr = debug_loader_offset;
+ new->lm_info->lm = NULL;
+
+ strncpy (new->so_name, debug_loader_name, SO_NAME_MAX_PATH_SIZE - 1);
+ new->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0';
+ strcpy (new->so_original_name, new->so_name);
+
+ *link_ptr = new;
+ link_ptr = &new->next;
+ }
+
+ return head;
+}
+
/* LOCAL FUNCTION
current_sos -- build a list of currently loaded shared objects
CORE_ADDR lm;
struct so_list *head = 0;
struct so_list **link_ptr = &head;
+ CORE_ADDR ldsomap = 0;
/* Make sure we've looked up the inferior's dynamic linker's base
structure. */
/* If we can't find the dynamic linker's base structure, this
must not be a dynamically linked executable. Hmm. */
if (! debug_base)
- return 0;
+ return svr4_default_sos ();
}
/* Walk the inferior's link map list, and build our list of
`struct so_list' nodes. */
- lm = first_link_map_member ();
+ lm = solib_svr4_r_map ();
+
while (lm)
{
struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
- struct so_list *new
- = (struct so_list *) xmalloc (sizeof (struct so_list));
+ struct so_list *new = XZALLOC (struct so_list);
struct cleanup *old_chain = make_cleanup (xfree, new);
- memset (new, 0, sizeof (*new));
-
new->lm_info = xmalloc (sizeof (struct lm_info));
make_cleanup (xfree, new->lm_info);
- new->lm_info->lm = xmalloc (lmo->link_map_size);
+ new->lm_info->l_addr = (CORE_ADDR)-1;
+ new->lm_info->lm = xzalloc (lmo->link_map_size);
make_cleanup (xfree, new->lm_info->lm);
- memset (new->lm_info->lm, 0, lmo->link_map_size);
read_memory (lm, new->lm_info->lm, lmo->link_map_size);
SVR4, it has no name. For others (Solaris 2.3 for example), it
does have a name, so we can no longer use a missing name to
decide when to ignore it. */
- if (IGNORE_FIRST_LINK_MAP_ENTRY (new))
+ if (IGNORE_FIRST_LINK_MAP_ENTRY (new) && ldsomap == 0)
free_so (new);
else
{
}
}
+ /* On Solaris, the dynamic linker is not in the normal list of
+ shared objects, so make sure we pick it up too. Having
+ symbol information for the dynamic linker is quite crucial
+ for skipping dynamic linker resolver code. */
+ if (lm == 0 && ldsomap == 0)
+ lm = ldsomap = solib_svr4_r_ldsomap ();
+
discard_cleanups (old_chain);
}
+ if (head == NULL)
+ return svr4_default_sos ();
+
return head;
}
return 0; /* failed somehow... */
/* Position ourselves on the first link map. */
- lm = first_link_map_member ();
+ lm = solib_svr4_r_map ();
while (lm)
{
/* Get info on the layout of the r_debug and link_map structures. */
struct lm_info objfile_lm_info;
struct cleanup *old_chain;
CORE_ADDR name_address;
- char *l_name_buf = xmalloc (lmo->l_name_size);
+ gdb_byte *l_name_buf = xmalloc (lmo->l_name_size);
old_chain = make_cleanup (xfree, l_name_buf);
/* Set up the buffer to contain the portion of the link_map
structure that gdb cares about. Note that this is not the
whole link_map structure. */
- objfile_lm_info.lm = xmalloc (lmo->link_map_size);
+ objfile_lm_info.lm = xzalloc (lmo->link_map_size);
make_cleanup (xfree, objfile_lm_info.lm);
- memset (objfile_lm_info.lm, 0, lmo->link_map_size);
/* Read the link map into our internal structure. */
read_memory (lm, objfile_lm_info.lm, lmo->link_map_size);
be trivial on GNU/Linux). Therefore, we have to try an alternate
mechanism to find the dynamic linker's base address. */
- tmp_fd = solib_open (buf, &tmp_pathname);
+ /* TODO drow/2006-09-12: This is somewhat fragile, because it
+ relies on read_pc. On both Solaris and GNU/Linux we can use
+ the AT_BASE auxilliary entry, which GDB now knows how to
+ access, to find the base address. */
+
+ tmp_fd = solib_open (buf, &tmp_pathname);
if (tmp_fd >= 0)
- tmp_bfd = bfd_fdopenr (tmp_pathname, gnutarget, tmp_fd);
+ tmp_bfd = bfd_fopen (tmp_pathname, gnutarget, FOPEN_RB, tmp_fd);
if (tmp_bfd == NULL)
goto bkpt_at_symbol;
if (strcmp (buf, so->so_original_name) == 0)
{
load_addr_found = 1;
- load_addr = LM_ADDR (so);
+ load_addr = LM_ADDR_CHECK (so, tmp_bfd);
break;
}
so = so->next;
the current pc (which should point at the entry point for the
dynamic linker) and subtracting the offset of the entry point. */
if (!load_addr_found)
- load_addr = (read_pc ()
- - exec_entry_point (tmp_bfd, tmp_bfd_target));
+ {
+ load_addr = (read_pc ()
+ - exec_entry_point (tmp_bfd, tmp_bfd_target));
+ debug_loader_name = xstrdup (buf);
+ debug_loader_offset_p = 1;
+ debug_loader_offset = load_addr;
+ solib_add (NULL, 0, NULL, auto_solib_add);
+ }
/* Record the relocated start and end address of the dynamic linker
text and plt section for svr4_in_dynsym_resolve_code. */
/* For whatever reason we couldn't set a breakpoint in the dynamic
linker. Warn and drop into the old code. */
bkpt_at_symbol:
- warning (_("Unable to find dynamic linker breakpoint function.\nGDB will be unable to debug shared library initializers\nand track explicitly loaded dynamic code."));
+ warning (_("Unable to find dynamic linker breakpoint function.\n"
+ "GDB will be unable to debug shared library initializers\n"
+ "and track explicitly loaded dynamic code."));
}
/* Scan through the list of symbols, trying to look up the symbol and
svr4_clear_solib (void)
{
debug_base = 0;
+ debug_loader_offset_p = 0;
+ debug_loader_offset = 0;
+ xfree (debug_loader_name);
+ debug_loader_name = NULL;
}
static void
svr4_relocate_section_addresses (struct so_list *so,
struct section_table *sec)
{
- sec->addr = svr4_truncate_ptr (sec->addr + LM_ADDR (so));
- sec->endaddr = svr4_truncate_ptr (sec->endaddr + LM_ADDR (so));
+ sec->addr = svr4_truncate_ptr (sec->addr + LM_ADDR_CHECK (so,
+ sec->bfd));
+ sec->endaddr = svr4_truncate_ptr (sec->endaddr + LM_ADDR_CHECK (so,
+ sec->bfd));
}
\f
{
lmp = &lmo;
- /* Everything we need is in the first 8 bytes. */
- lmo.r_debug_size = 8;
+ lmo.r_version_offset = 0;
+ lmo.r_version_size = 4;
lmo.r_map_offset = 4;
- lmo.r_map_size = 4;
+ lmo.r_ldsomap_offset = 20;
/* Everything we need is in the first 20 bytes. */
lmo.link_map_size = 20;
lmo.l_addr_offset = 0;
- lmo.l_addr_size = 4;
+ lmo.l_addr_size = 4;
lmo.l_name_offset = 4;
- lmo.l_name_size = 4;
+ lmo.l_name_size = 4;
+ lmo.l_ld_offset = 8;
+ lmo.l_ld_size = 4;
lmo.l_next_offset = 12;
- lmo.l_next_size = 4;
+ lmo.l_next_size = 4;
lmo.l_prev_offset = 16;
- lmo.l_prev_size = 4;
+ lmo.l_prev_size = 4;
}
return lmp;
{
lmp = &lmo;
- /* Everything we need is in the first 16 bytes. */
- lmo.r_debug_size = 16;
+ lmo.r_version_offset = 0;
+ lmo.r_version_size = 4;
lmo.r_map_offset = 8;
- lmo.r_map_size = 8;
+ lmo.r_ldsomap_offset = 40;
/* Everything we need is in the first 40 bytes. */
lmo.link_map_size = 40;
lmo.l_addr_offset = 0;
- lmo.l_addr_size = 8;
+ lmo.l_addr_size = 8;
lmo.l_name_offset = 8;
- lmo.l_name_size = 8;
+ lmo.l_name_size = 8;
+ lmo.l_ld_offset = 16;
+ lmo.l_ld_size = 8;
lmo.l_next_offset = 24;
- lmo.l_next_size = 8;
+ lmo.l_next_size = 8;
lmo.l_prev_offset = 32;
- lmo.l_prev_size = 8;
+ lmo.l_prev_size = 8;
}
return lmp;
{
solib_svr4_data = gdbarch_data_register_pre_init (solib_svr4_init);
- /* FIXME: Eliminate this ASAP. */
-#ifdef SVR4_FETCH_LINK_MAP_OFFSETS
- svr4_legacy_fetch_link_map_offsets = SVR4_FETCH_LINK_MAP_OFFSETS;
-#endif
-
svr4_so_ops.relocate_section_addresses = svr4_relocate_section_addresses;
svr4_so_ops.free_so = svr4_free_so;
svr4_so_ops.clear_solib = svr4_clear_solib;