Fix Solaris gcore / shared libraries interaction

Joseph S. Myers
2009-11-21T00:11:27Z

With some versions of the Solaris 10 dynamic linker, if you create a
core file with GDB's gcore command and then load that core file into
GDB, you get warnings of the form

warning: Can't read pathname for load map: I/O error.

on loading the core file.

The problem is that the l_name pointer for the dynamic linker, in the
process's link map, points into the read-only text segment of ld.so
(specifically, it points to the SONAME in the string table), and GDB
excludes this text segment when creating the core dump. Normally
excluding text segments of shared libraries is fine, but in this case
it causes the above problems reloading the core dump.

This patch fixes the problem by arranging for read-only memory not to
be excluded from core dumps if it contains the l_name value. OK to
commit?

2009-11-21 Joseph Myers <joseph@codesourcery.com>

* gcore.c: Include solib.h.
(gcore_create_callback): Call solib_keep_data_in_core when
considering not saving memory in core file.
* solib-svr4.c (svr4_keep_data_in_core): New.
(_initialize_svr4_solib): Initialize
svr4_so_ops.keep_data_in_core.
* solib.c (solib_keep_data_in_core): New.
* solib.h (solib_keep_data_in_core): Declare.
* solist.h (struct target_so_ops): Add keep_data_in_core.

Index: gdb/gcore.c
===================================================================
RCS file: /cvs/src/src/gdb/gcore.c,v
retrieving revision 1.35
diff -u -p -r1.35 gcore.c
--- gdb/gcore.c 22 Oct 2009 19:36:06 -0000 1.35
+++ gdb/gcore.c 20 Nov 2009 22:14:47 -0000
@@ -24,6 +24,7 @@
#include "inferior.h"
#include "gdbcore.h"
#include "objfiles.h"
+#include "solib.h"
#include "symfile.h"
#include "arch-utils.h"
#include "completer.h"
@@ -389,7 +390,7 @@ gcore_create_callback (CORE_ADDR vaddr,
return 0;
}

- if (write == 0)
+ if (write == 0 && !solib_keep_data_in_core (vaddr, size))
{
/* See if this region of memory lies inside a known file on disk.
If so, we can avoid copying its contents by clearing SEC_LOAD. */
Index: gdb/solib-svr4.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-svr4.c,v
retrieving revision 1.107
diff -u -p -r1.107 solib-svr4.c
--- gdb/solib-svr4.c 10 Nov 2009 21:36:48 -0000 1.107
+++ gdb/solib-svr4.c 20 Nov 2009 22:14:47 -0000
@@ -866,6 +866,50 @@ solib_svr4_r_ldsomap (struct svr4_info *
ptr_type);
}

+/* On Solaris systems with some versions of the dynamic linker,
+ ld.so's l_name pointer points to the SONAME in the string table
+ rather than into writable memory. So that GDB can find shared
+ libraries when loading a core file generated by gcore, ensure that
+ memory areas containing the l_name string are saved in the core
+ file. */
+
+static int
+svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
+{
+ struct svr4_info *info;
+ CORE_ADDR ldsomap;
+ struct so_list *new;
+ struct cleanup *old_chain;
+ struct link_map_offsets *lmo;
+ CORE_ADDR lm_name;
+
+ info = get_svr4_info ();
+
+ info->debug_base = 0;
+ locate_base (info);
+ if (!info->debug_base)
+ return 0;
+
+ ldsomap = solib_svr4_r_ldsomap (info);
+ if (!ldsomap)
+ return 0;
+
+ lmo = svr4_fetch_link_map_offsets ();
+ new = XZALLOC (struct so_list);
+ old_chain = make_cleanup (xfree, new);
+ new->lm_info = xmalloc (sizeof (struct lm_info));
+ make_cleanup (xfree, new->lm_info);
+ new->lm_info->l_addr = (CORE_ADDR)-1;
+ new->lm_info->lm_addr = ldsomap;
+ new->lm_info->lm = xzalloc (lmo->link_map_size);
+ make_cleanup (xfree, new->lm_info->lm);
+ read_memory (ldsomap, new->lm_info->lm, lmo->link_map_size);
+ lm_name = LM_NAME (new);
+ do_cleanups (old_chain);
+
+ return (lm_name >= vaddr && lm_name < vaddr + size);
+}
+
/*

LOCAL FUNCTION
@@ -1918,4 +1962,5 @@ _initialize_svr4_solib (void)
svr4_so_ops.bfd_open = solib_bfd_open;
svr4_so_ops.lookup_lib_global_symbol = elf_lookup_lib_symbol;
svr4_so_ops.same = svr4_same;
+ svr4_so_ops.keep_data_in_core = svr4_keep_data_in_core;
}
Index: gdb/solib.c
===================================================================
RCS file: /cvs/src/src/gdb/solib.c,v
retrieving revision 1.128
diff -u -p -r1.128 solib.c
--- gdb/solib.c 19 Oct 2009 09:51:42 -0000 1.128
+++ gdb/solib.c 20 Nov 2009 22:14:47 -0000
@@ -948,6 +948,23 @@ solib_name_from_address (struct program_
return (0);
}

+/* Return whether the data starting at VADDR, size SIZE, must be kept
+ in a core file for shared libraries loaded before "gcore" is used
+ to be handled correctly when the core file is loaded. This only
+ applies when the section would otherwise not be kept in the core
+ file (in particular, for readonly sections). */
+
+int
+solib_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
+{
+ struct target_so_ops *ops = solib_ops (target_gdbarch);
+
+ if (ops->keep_data_in_core)
+ return ops->keep_data_in_core (vaddr, size);
+ else
+ return 0;
+}
+
/* Called by free_all_symtabs */

void
Index: gdb/solib.h
===================================================================
RCS file: /cvs/src/src/gdb/solib.h,v
retrieving revision 1.25
diff -u -p -r1.25 solib.h
--- gdb/solib.h 19 Oct 2009 09:51:42 -0000 1.25
+++ gdb/solib.h 20 Nov 2009 22:14:47 -0000
@@ -52,6 +52,14 @@ extern char *solib_name_from_address (st

extern int solib_contains_address_p (const struct so_list *, CORE_ADDR);

+/* Return whether the data starting at VADDR, size SIZE, must be kept
+ in a core file for shared libraries loaded before "gcore" is used
+ to be handled correctly when the core file is loaded. This only
+ applies when the section would otherwise not be kept in the core
+ file (in particular, for readonly sections). */
+
+extern int solib_keep_data_in_core (CORE_ADDR vaddr, unsigned long size);
+
/* Return 1 if PC lies in the dynamic symbol resolution code of the
run time loader. */

Index: gdb/solist.h
===================================================================
RCS file: /cvs/src/src/gdb/solist.h,v
retrieving revision 1.26
diff -u -p -r1.26 solist.h
--- gdb/solist.h 19 Oct 2009 09:51:42 -0000 1.26
+++ gdb/solist.h 20 Nov 2009 22:14:47 -0000
@@ -126,6 +126,14 @@ struct target_so_ops
Falls back to using strcmp on so_original_name field when set
to NULL. */
int (*same) (struct so_list *gdb, struct so_list *inferior);
+
+ /* Return whether a region of memory must be kept in a core file
+ for shared libraries loaded before "gcore" is used to be
+ handled correctly when the core file is loaded. This only
+ applies when the section would otherwise not be kept in the
+ core file (in particular, for readonly sections). */
+ int (*keep_data_in_core) (CORE_ADDR vaddr,
+ unsigned long size);
};

/* Free the memory associated with a (so_list *). */