RFA: More concise errors for missing shared libraries

Daniel Jacobowitz
2006-05-09T17:44:38Z

I've been working on a new GDB port to SymbianOS for a couple of months now.
One of the quirks of this system, compared to a "typical" embedded target,
is that it supports dynamic loading.

The DLL format used is proprietary. I looked into making GDB read it, but
even if we could, we wouldn't get useful information from it. There's no
usable symbol table. Instead we normally open ELF versions of the DLLs.
I've got various changes in my tree to support this (it's on
gdb-csl-symbian-20060226-branch if you're interested), which I hope I'll be
submitting soon. This patch is one of them.

One of the quirks of SymbianOS compared to a traditional hosted target is
that it's quite likely that GDB will have no symbol file at all for many of
the loaded libraries. A ROM image may contain many DLLs, presented as a
virtual filesystem, which are effectively preloaded into the memory space of
every process. A user probably only has symbol files for his own DLLs, not
the DLLs which provide operating system services. So, the behavior of GDB
when shared libraries can not be found is much more important than on other
targets.

Right now this is really noisy. We get a two-line error message for each
library from update_solib_list when we detect the new library, and another
one from solib_read_symbols later. This patch condenses those into a single
warning:

warning: Could not load shared library symbols for 2 libraries, e.g. /lib/libnss_dns.so.2.
Do you need "set solib-search-path" or "set solib-absolute-prefix"?

It gives the name of the first new library; if you want more information,
you can run "info shared". I toyed with hiding missing shared libraries
in the default "info shared" output and adding "info shared all", but
decided against it - I expect that eventually most of this will be wrapped
up in an IDE, and the IDE can encode that sort of Symbian-specific front-end
knowledge. Or maybe I'll think about it some more later.

Some more detailed examples are at the bottom of this message. While
putting them together I noticed that the current code can also read shared
libraries outside of solib-absolute-prefix unintentionally, because failure
to open the library leaves so_name pointing to the unmodified filename;
that's also fixed as a side effect of this patch.

While I was in this code, I removed the so_list from_tty flag; it's only
used in one place, and really we're interested in the current version of
from_tty at that point, not the version when the library was opened.
The new TRY_CATCH makes this much easier.

Is this OK? Do you see a way to improve the output further?

===

Using an unmodified GDB, with all the libraries and variables in their
normal places, I get:

(gdb) r
Starting program: /usr/bin/telnet
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)

All normal.

===

With solib-absolute-prefix pointing to a directory that has
/lib64/ld-linux-x86-64.so.2 (so that the initial breakpoint can be set),
but no other libraries:

(gdb) set solib-absolute-prefix .
(gdb) r
Starting program: /usr/bin/telnet
Error while mapping shared library sections:
/usr/lib/libncurses.so.5: No such file or directory.
Error while mapping shared library sections:
/usr/lib/libstdc++.so.6: No such file or directory.
Error while mapping shared library sections:
/lib/libm.so.6: No such file or directory.
Error while mapping shared library sections:
/lib/libgcc_s.so.1: No such file or directory.
Error while mapping shared library sections:
/lib/libc.so.6: No such file or directory.
Error while mapping shared library sections:
/lib/libdl.so.2: No such file or directory.
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Error while mapping shared library sections:
/lib/libnss_files.so.2: No such file or directory.
(no debugging symbols found)
(no debugging symbols found)
Error while mapping shared library sections:
/lib/libnss_dns.so.2: No such file or directory.
Error while mapping shared library sections:
/lib/libresolv.so.2: No such file or directory.
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Program received signal SIGINT, Interrupt.
0x00002b19d79a7802 in ?? ()
(gdb) i shared
From To Syms Read Shared Object Library
Yes /usr/lib/libncurses.so.5
Yes /usr/lib/libstdc++.so.6
Yes /lib/libm.so.6
Yes /lib/libgcc_s.so.1
Yes /lib/libc.so.6
Yes /lib/libdl.so.2
0x00002b19d71dfa80 0x00002b19d71f0957 Yes
./lib64/ld-linux-x86-64.so.2
Yes /lib/libnss_files.so.2
Yes /lib/libnss_dns.so.2
Yes /lib/libresolv.so.2

Now, there's two things wrong with this output. One of them is cosmetic,
but annoying: we got a two-line error for every single library. Another
is that we've somehow read symbols from the host libraries despite
solib-absolute-prefix. (I didn't notice that until I was writing this
email, but it also happens to be fixed by this patch.)

If the library weren't present on the host system at all, instead of reading
the syms, we'd issue a second two-line error message for each library.

===

With the patch applied, and solib-absolute-prefix set to the same temporary
directory:

(gdb) set solib-absolute-prefix .
(gdb) r
Starting program: /usr/bin/telnet
warning: Could not load shared library symbols for 6 libraries, e.g. /usr/lib/libncurses.so.5.
Do you need "set solib-search-path" or "set solib-absolute-prefix"?
(no debugging symbols found)
warning: Could not load shared library symbols for /lib/libnss_files.so.2.
Do you need "set solib-search-path" or "set solib-absolute-prefix"?
warning: Could not load shared library symbols for 2 libraries, e.g. /lib/libnss_dns.so.2.
Do you need "set solib-search-path" or "set solib-absolute-prefix"?
telnet>
Program received signal SIGINT, Interrupt.
0x00002b9e84626802 in ?? ()
(gdb) i shared
From To Syms Read Shared Object Library
No /usr/lib/libncurses.so.5
No /usr/lib/libstdc++.so.6
No /lib/libm.so.6
No /lib/libgcc_s.so.1
No /lib/libc.so.6
No /lib/libdl.so.2
0x00002b9e83e5ea80 0x00002b9e83e6f957 Yes ./lib64/ld-linux-x86-64.so.2
No /lib/libnss_files.so.2
No /lib/libnss_dns.so.2
No /lib/libresolv.so.2

We still get multiple warning messages, but that's because libnss_files and
libnss_dns are dlopen'd later. The first group of messages is condensed to
a single line (with suggestion).

--
Daniel Jacobowitz
CodeSourcery

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

* bsd-uthread.c (bsd_uthread_solib_loaded): Always pass 0 for
from_tty.
* solib.c: Include "exceptions.h".
(solib_map_sections): Throw NOT_FOUND_ERROR if appropriate.
(symbol_add_stub): Delete.
(solib_read_symbols): Use TRY_CATCH. Inline symbol_add_stub.
Use current from_tty, not a copy from the so_list. Don't warn
a second time for a missing library.
(update_solib_list): Don't save from_tty. Use TRY_CATCH. Print
out a single warning for all missing libraries.
* solist.h (struct so_list): Remove from_tty.

Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.812
diff -u -p -r1.812 Makefile.in
--- Makefile.in 5 May 2006 22:39:12 -0000 1.812
+++ Makefile.in 9 May 2006 15:52:47 -0000
@@ -2579,7 +2579,7 @@ solib.o: solib.c $(defs_h) $(gdb_string_
$(objfiles_h) $(exceptions_h) $(gdbcore_h) $(command_h) $(target_h) \
$(frame_h) $(gdb_regex_h) $(inferior_h) $(environ_h) $(language_h) \
$(gdbcmd_h) $(completer_h) $(filenames_h) $(exec_h) $(solist_h) \
- $(observer_h) $(readline_h)
+ $(observer_h) $(readline_h) $(exceptions_h)
solib-frv.o: solib-frv.c $(defs_h) $(gdb_string_h) $(inferior_h) \
$(gdbcore_h) $(solist_h) $(frv_tdep_h) $(objfiles_h) $(symtab_h) \
$(language_h) $(command_h) $(gdbcmd_h) $(elf_frv_h)
Index: bsd-uthread.c
===================================================================
RCS file: /cvs/src/src/gdb/bsd-uthread.c,v
retrieving revision 1.7
diff -u -p -r1.7 bsd-uthread.c
--- bsd-uthread.c 17 Dec 2005 22:33:59 -0000 1.7
+++ bsd-uthread.c 9 May 2006 15:52:47 -0000
@@ -237,7 +237,7 @@ bsd_uthread_solib_loaded (struct so_list
{
if (strncmp (so->so_original_name, *names, strlen (*names)) == 0)
{
- solib_read_symbols (so, so->from_tty);
+ solib_read_symbols (so, 0);

if (bsd_uthread_activate (so->objfile))
{
Index: solib.c
===================================================================
RCS file: /cvs/src/src/gdb/solib.c,v
retrieving revision 1.84
diff -u -p -r1.84 solib.c
--- solib.c 25 Feb 2006 04:36:39 -0000 1.84
+++ solib.c 9 May 2006 15:52:48 -0000
@@ -45,6 +45,7 @@
#include "exec.h"
#include "solist.h"
#include "observer.h"
+#include "exceptions.h"
#include "readline/readline.h"

/* Architecture-specific operations. */
@@ -273,6 +274,11 @@ solib_map_sections (void *arg)

if (scratch_chan < 0)
{
+ /* Throw a more specific error if the file could not be found, so
+ that we can accumulate messages about missing libraries. */
+ if (errno == ENOENT)
+ throw_error (NOT_FOUND_ERROR, "%s", safe_strerror (errno));
+
perror_with_name (filename);
}

@@ -381,31 +387,6 @@ master_so_list (void)
}

-/* A small stub to get us past the arg-passing pinhole of catch_errors. */
-
-static int
-symbol_add_stub (void *arg)
-{
- struct so_list *so = (struct so_list *) arg; /* catch_errs bogon */
- struct section_addr_info *sap;
-
- /* Have we already loaded this shared object? */
- ALL_OBJFILES (so->objfile)
- {
- if (strcmp (so->objfile->name, so->so_name) == 0)
- return 1;
- }
-
- sap = build_section_addr_info_from_section_table (so->sections,
- so->sections_end);
-
- so->objfile = symbol_file_add (so->so_name, so->from_tty,
- sap, 0, OBJF_SHARED);
- free_section_addr_info (sap);
-
- return (1);
-}
-
/* Read in symbols for shared object SO. If FROM_TTY is non-zero, be
chatty about it. Return non-zero if any symbols were actually
loaded. */
@@ -420,9 +401,44 @@ solib_read_symbols (struct so_list *so,
}
else
{
- if (catch_errors (symbol_add_stub, so,
- "Error while reading shared library symbols:\n",
- RETURN_MASK_ALL))
+ volatile struct gdb_exception e;
+
+ TRY_CATCH (e, RETURN_MASK_ERROR)
+ {
+ struct section_addr_info *sap;
+
+ /* Could we find a file for this shared library? If we
+ couldn't, don't try to open it again. */
+ if (so->abfd == NULL)
+ throw_error (NOT_FOUND_ERROR, "%s", safe_strerror (errno));
+
+ /* Have we already loaded this shared object? */
+ ALL_OBJFILES (so->objfile)
+ {
+ if (strcmp (so->objfile->name, so->so_name) == 0)
+ break;
+ }
+ if (so->objfile != NULL)
+ break;
+
+ sap = build_section_addr_info_from_section_table (so->sections,
+ so->sections_end);
+ so->objfile = symbol_file_add (so->so_name, from_tty,
+ sap, 0, OBJF_SHARED);
+ free_section_addr_info (sap);
+ }
+
+ if (e.reason == RETURN_ERROR && e.error == NOT_FOUND_ERROR)
+ /* We've already warned about this library, when trying to
+ open it. */
+ return 0;
+ else if (e.reason < 0)
+ {
+ if (from_tty)
+ exception_fprintf
+ (gdb_stderr, e, _("Error while reading shared library symbols:\n"));
+ }
+ else
{
if (from_tty)
printf_unfiltered (_("Loaded symbols for %s\n"), so->so_name);
@@ -568,6 +584,9 @@ update_solib_list (int from_tty, struct
to GDB's shared object list. */
if (inferior)
{
+ int not_found = 0;
+ const char *not_found_filename = NULL;
+
struct so_list *i;

/* Add the new shared objects to GDB's list. */
@@ -576,12 +595,25 @@ update_solib_list (int from_tty, struct
/* Fill in the rest of each of the `struct so_list' nodes. */
for (i = inferior; i; i = i->next)
{
- i->from_tty = from_tty;
+ volatile struct gdb_exception e;

- /* Fill in the rest of the `struct so_list' node. */
- catch_errors (solib_map_sections, i,
- "Error while mapping shared library sections:\n",
- RETURN_MASK_ALL);
+ TRY_CATCH (e, RETURN_MASK_ERROR)
+ {
+ /* Fill in the rest of the `struct so_list' node. */
+ solib_map_sections (i);
+ }
+
+ if (e.reason == RETURN_ERROR && e.error == NOT_FOUND_ERROR)
+ {
+ not_found++;
+ if (not_found_filename == NULL)
+ not_found_filename = i->so_original_name;
+ }
+ else if (e.reason < 0)
+ {
+ exception_fprintf (gdb_stderr, e,
+ _("Error while mapping shared library sections:\n"));
+ }

/* If requested, add the shared object's sections to the TARGET's
section table. Do this immediately after mapping the object so
@@ -603,6 +635,24 @@ update_solib_list (int from_tty, struct
loaded now that we've added it to GDB's tables. */
observer_notify_solib_loaded (i);
}
+
+ /* If a library was not found, issue an appropriate warning
+ message. We have to use a single call to warning () in case
+ the front end does something special with warnings, e.g. pop
+ up a dialog box. It Would Be Nice if we could get a
+ "warning: " prefix on each line in the CLI front end,
+ though - it doesn't stand out well. */
+
+ if (not_found == 1)
+ warning (_("\
+Could not load shared library symbols for %s.\n\
+Do you need \"set solib-search-path\" or \"set solib-absolute-prefix\"?"),
+ not_found_filename);
+ else if (not_found > 1)
+ warning (_("\
+Could not load shared library symbols for %d libraries, e.g. %s.\n\
+Do you need \"set solib-search-path\" or \"set solib-absolute-prefix\"?"),
+ not_found, not_found_filename);
}
}

Index: solist.h
===================================================================
RCS file: /cvs/src/src/gdb/solist.h,v
retrieving revision 1.12
diff -u -p -r1.12 solist.h
--- solist.h 17 Dec 2005 22:34:02 -0000 1.12
+++ solist.h 9 May 2006 15:52:48 -0000
@@ -61,7 +61,6 @@ struct so_list

bfd *abfd;
char symbols_loaded; /* flag: symbols read in yet? */
- char from_tty; /* flag: print msgs? */
struct objfile *objfile; /* objfile for loaded lib */
struct section_table *sections;
struct section_table *sections_end;