[rfa] Add the ELF interpreter to the shared library list early

Daniel Jacobowitz
2006-09-12T22:23:49Z

This patch arose out of an interesting ARM bug. A customer compiled
uClibc in -mthumb mode, which is theoretically possible for any C library
but no one's ever done the work for glibc. Their GDB blows up very early,
by setting an ARM mode breakpoint on _dl_debug_state.

There were a couple of different ways I could have fixed this, but while
I was working on it, this approach occurred to me: when we say "oh, the
interpreter is at this address, set a breakpoint at the magic offset"
we can also record this information for later use. With the following patch
applied, and a breakpoint somewhere before shared libraries are loaded,
"info shared" will now report the dynamic linker. This is especially
handy with gdbserver, where (assuming you have unstripped libraries)
you'll now see _start when you connect instead of ??!

Aside: Actually you won't.

0x00002aaaaaaaba80 in ?? ()
(no debugging symbols found)
(no debugging symbols found)
(gdb) f
#0 0x00002aaaaaaaba80 in _start () from /lib64/ld-linux-x86-64.so.2

But you should and that's a different bug, we print out the position too
early. Anyway, end aside.

I think this is pretty neat. I should have done it ages ago when debugging
ld.so.

Is this OK? Tested x86_64-pc-linux-gnu (local, and again using gdbserver
for good measure).

--
Daniel Jacobowitz
CodeSourcery

2006-09-12 Daniel Jacobowitz <dan@codesourcery.com>

* solib-svr4.c (debug_loader_offset_p, debug_loader_offset)
(debug_loader_name, svr4_default_sos): New.
(svr4_current_sos): Call svr4_default_sos.
(enable_break): Add a comment about AT_BASE. Set the new
variables and retry solib_add.
(svr4_clear_solib): Clear the new variables.

Index: solib-svr4.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-svr4.c,v
retrieving revision 1.58
diff -u -p -r1.58 solib-svr4.c
--- solib-svr4.c 18 May 2006 20:38:56 -0000 1.58
+++ solib-svr4.c 12 Sep 2006 20:46:23 -0000
@@ -263,6 +263,15 @@ IGNORE_FIRST_LINK_MAP_ENTRY (struct so_l
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 *);
@@ -653,6 +662,37 @@ open_symbol_file_object (void *from_ttyp
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
@@ -689,12 +729,13 @@ svr4_current_sos (void)
/* 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 = solib_svr4_r_map ();
+
while (lm)
{
struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
@@ -761,6 +802,9 @@ svr4_current_sos (void)
discard_cleanups (old_chain);
}

+ if (head == NULL)
+ return svr4_default_sos ();
+
return head;
}

@@ -979,7 +1023,12 @@ enable_break (void)
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_fopen (tmp_pathname, gnutarget, FOPEN_RB, tmp_fd);

@@ -1018,8 +1067,14 @@ enable_break (void)
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. */
@@ -1331,6 +1386,10 @@ static void
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