Validate symbol file using build-id
authorJan Kratochvil <jan.kratochvil@redhat.com>
Wed, 15 Jul 2015 15:37:28 +0000 (17:37 +0200)
committerJan Kratochvil <jan.kratochvil@redhat.com>
Wed, 15 Jul 2015 15:42:07 +0000 (17:42 +0200)
Consumer part of the "build-id" attribute.

gdb/ChangeLog
2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
    Jan Kratochvil  <jan.kratochvil@redhat.com>

Validate symbol file using build-id.
* NEWS (Changes since GDB 7.10): Add 'set validate-build-id'
and 'show validate-build-id'.  Add build-id attribute.
* solib-darwin.c (_initialize_darwin_solib): Assign validate value.
* solib-dsbt.c (_initialize_dsbt_solib): Ditto.
* solib-frv.c (_initialize_frv_solib): Ditto.
* solib-spu.c (set_spu_solib_ops): Ditto.
* solib-svr4.c: Include rsp-low.h.
(NOTE_GNU_BUILD_ID_NAME): New define.
(svr4_validate): New function.
(svr4_copy_library_list): Duplicate field build_id.
(library_list_start_library): Parse 'build-id' attribute.
(svr4_library_attributes): Add 'build-id' attribute.
(_initialize_svr4_solib): Assign validate value.
* solib-target.c (solib.h): Include.
(_initialize_solib_target): Assign validate value.
* solib.c (validate_build_id, show_validate_build_id): New.
(solib_map_sections): Use ops->validate.
(clear_so): Free build_id.
(default_solib_validate): New function.
(_initialize_solib): Add "validate-build-id".
* solib.h (default_solib_validate): New declaration.
* solist.h (struct so_list): New fields 'build_idsz' and 'build_id'.
(target_so_ops): New field 'validate'.

gdb/doc/ChangeLog
2015-07-15  Jan Kratochvil  <jan.kratochvil@redhat.com>

* gdb.texinfo (Files): Add 'set validate-build-id'
and 'show validate-build-id'.

13 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/solib-darwin.c
gdb/solib-dsbt.c
gdb/solib-frv.c
gdb/solib-spu.c
gdb/solib-svr4.c
gdb/solib-target.c
gdb/solib.c
gdb/solib.h
gdb/solist.h

index b58b138c98b7b6c7950fce39975e988b25073804..849961c91c2b060d51ec7db061952ad5019ba56a 100644 (file)
@@ -1,3 +1,31 @@
+2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
+           Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+       Validate symbol file using build-id.
+       * NEWS (Changes since GDB 7.10): Add 'set validate-build-id'
+       and 'show validate-build-id'.  Add build-id attribute.
+       * solib-darwin.c (_initialize_darwin_solib): Assign validate value.
+       * solib-dsbt.c (_initialize_dsbt_solib): Ditto.
+       * solib-frv.c (_initialize_frv_solib): Ditto.
+       * solib-spu.c (set_spu_solib_ops): Ditto.
+       * solib-svr4.c: Include rsp-low.h.
+       (NOTE_GNU_BUILD_ID_NAME): New define.
+       (svr4_validate): New function.
+       (svr4_copy_library_list): Duplicate field build_id.
+       (library_list_start_library): Parse 'build-id' attribute.
+       (svr4_library_attributes): Add 'build-id' attribute.
+       (_initialize_svr4_solib): Assign validate value.
+       * solib-target.c (solib.h): Include.
+       (_initialize_solib_target): Assign validate value.
+       * solib.c (validate_build_id, show_validate_build_id): New.
+       (solib_map_sections): Use ops->validate.
+       (clear_so): Free build_id.
+       (default_solib_validate): New function.
+       (_initialize_solib): Add "validate-build-id".
+       * solib.h (default_solib_validate): New declaration.
+       * solist.h (struct so_list): New fields 'build_idsz' and 'build_id'.
+       (target_so_ops): New field 'validate'.
+
 2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
            Jan Kratochvil  <jan.kratochvil@redhat.com>
 
index 7ce97587b83ecc332f37b79255474c93ef92fbd5..ee3c915457d21c87c5faa8a2ee9a78764ed8ee0f 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -5,6 +5,20 @@
 
 * Support for tracepoints on aarch64-linux was added in GDBserver.
 
+* New options
+
+set validate-build-id (on|off)
+show validate-build-id
+  Inferior shared library and symbol file may contain unique build-id.
+  If both build-ids are present but they do not match then this setting
+  enables (off) or disables (on) loading of such symbol file.
+
+* New features in the GDB remote stub, GDBserver
+
+  ** library-list-svr4 contains also optional attribute 'build-id' for
+     each library.  GDB does not load library with build-id that
+     does not match such attribute.
+
 *** Changes in GDB 7.10
 
 * Support for process record-replay and reverse debugging on aarch64*-linux*
index 5b90cb4364d30cbdba03c904954eb36580df9f47..fc66f0cfbd67fbf6352560b6c24d760bd569fce0 100644 (file)
@@ -1,3 +1,8 @@
+2015-07-15  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+       * gdb.texinfo (Files): Add 'set validate-build-id'
+       and 'show validate-build-id'.
+
 2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
            Jan Kratochvil  <jan.kratochvil@redhat.com>
 
index 932c38d318a69ae8b540731acfc24806f667a57a..513c08ee678bd4a425c592d7a302ee2caa17e638 100644 (file)
@@ -17950,6 +17950,44 @@ libraries that were loaded by explicit user requests are not
 discarded.
 @end table
 
+@table @code
+@kindex set validate-build-id
+@cindex override @value{GDBN} build-id check
+@item set validate-build-id @var{mode}
+Setting to override @value{GDBN} build-id check.
+
+Inferior shared libraries and symbol files may contain unique build-id.
+By default @value{GDBN} will ignore symbol files with non-matching build-id
+while printing:
+
+@smallexample
+  warning: Shared object "libfoo.so.1" could not be validated (remote
+  build ID 2bc1745e does not match local build ID a08f8767) and will be
+  ignored; or use 'set validate-build-id off'.
+@end smallexample
+
+Turning off this setting would load such symbol file while still printing:
+
+@smallexample
+  warning: Shared object "libfoo.so.1" could not be validated (remote
+  build ID 2bc1745e does not match local build ID a08f8767) but it is
+  being loaded due to 'set validate-build-id off'.
+@end smallexample
+
+If remote build-id is present but it does not match local build-id (or local
+build-id is not present) then this setting enables (@var{mode} is @code{off}) or
+disables (@var{mode} is @code{on}) loading of such symbol file.  On systems
+where build-id is not present in the remote system this setting has no effect.
+The default value is @code{on}.
+
+Loading non-matching symbol file may confuse debugging including breakage
+of backtrace output.
+
+@kindex show validate-build-id
+@item show validate-build-id
+Display the current mode of build-id check override.
+@end table
+
 Sometimes you may wish that @value{GDBN} stops and gives you control
 when any of shared library events happen.  The best way to do this is
 to use @code{catch load} and @code{catch unload} (@pxref{Set
index f96841f051a6658de1f5ba38c1742bfad41e911f..9309c359c1e2a4b7733df3edb773e172337030d8 100644 (file)
@@ -634,4 +634,5 @@ _initialize_darwin_solib (void)
   darwin_so_ops.in_dynsym_resolve_code = darwin_in_dynsym_resolve_code;
   darwin_so_ops.lookup_lib_global_symbol = darwin_lookup_lib_symbol;
   darwin_so_ops.bfd_open = darwin_bfd_open;
+  darwin_so_ops.validate = default_solib_validate;
 }
index 7da5833897c02e181fb13d1097529b3d6376479a..9fe6533e9a9faff74d39cd5f3c7d74d2f20a4cdf 100644 (file)
@@ -1080,6 +1080,7 @@ _initialize_dsbt_solib (void)
   dsbt_so_ops.open_symbol_file_object = open_symbol_file_object;
   dsbt_so_ops.in_dynsym_resolve_code = dsbt_in_dynsym_resolve_code;
   dsbt_so_ops.bfd_open = solib_bfd_open;
+  dsbt_so_ops.validate = default_solib_validate;
 
   /* Debug this file's internals.  */
   add_setshow_zuinteger_cmd ("solib-dsbt", class_maintenance,
index f7ef38bc320e230d4b29ab5dfabc86585152d520..b76814642878773ace854ec4e90a3b346824c4b9 100644 (file)
@@ -1183,6 +1183,7 @@ _initialize_frv_solib (void)
   frv_so_ops.open_symbol_file_object = open_symbol_file_object;
   frv_so_ops.in_dynsym_resolve_code = frv_in_dynsym_resolve_code;
   frv_so_ops.bfd_open = solib_bfd_open;
+  frv_so_ops.validate = default_solib_validate;
 
   /* Debug this file's internals.  */
   add_setshow_zuinteger_cmd ("solib-frv", class_maintenance,
index 44fbf91506c4eb0758f1e6c4e75f54ada09d4429..d1628846cf42738b361fb741ea555a3b39961769 100644 (file)
@@ -519,6 +519,7 @@ set_spu_solib_ops (struct gdbarch *gdbarch)
       spu_so_ops.current_sos = spu_current_sos;
       spu_so_ops.bfd_open = spu_bfd_open;
       spu_so_ops.lookup_lib_global_symbol = spu_lookup_lib_symbol;
+      spu_so_ops.validate = default_solib_validate;
     }
 
   set_solib_ops (gdbarch, &spu_so_ops);
index 909dfb708d51fe296d75c97e27f7d1b0d05e426c..71522c6122d4f0962a2d0094ef61ece162f3da39 100644 (file)
@@ -45,6 +45,9 @@
 #include "auxv.h"
 #include "gdb_bfd.h"
 #include "probe.h"
+#include "rsp-low.h"
+
+#define NOTE_GNU_BUILD_ID_NAME  ".note.gnu.build-id"
 
 static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
 static int svr4_have_link_map_offsets (void);
@@ -970,6 +973,64 @@ svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size)
   return (name_lm >= vaddr && name_lm < vaddr + size);
 }
 
+/* Validate SO by comparing build-id from the associated bfd and
+   corresponding build-id from target memory.  Return NULL for success
+   or a string for error.  Caller must call xfree for the error string.  */
+
+static char *
+svr4_validate (const struct so_list *const so)
+{
+  const bfd_byte *local_id;
+  size_t local_idsz;
+
+  gdb_assert (so != NULL);
+
+  /* Target doesn't support reporting the build ID or the remote shared library
+     does not have build ID.  */
+  if (so->build_id == NULL)
+    return NULL;
+
+  /* Build ID may be present in the local file, just GDB is unable to retrieve
+     it.  As it has been reported by gdbserver it is not FSF gdbserver.  */
+  if (so->abfd == NULL
+      || !bfd_check_format (so->abfd, bfd_object))
+    return NULL;
+
+  /* GDB has verified the local file really does not contain the build ID.  */
+  if (so->abfd->build_id == NULL)
+    {
+      char *remote_hex;
+
+      remote_hex = alloca (so->build_idsz * 2 + 1);
+      bin2hex (so->build_id, remote_hex, so->build_idsz);
+
+      return xstrprintf (_("remote build ID is %s "
+                          "but local file does not have build ID"),
+                        remote_hex);
+    }
+
+  local_id = so->abfd->build_id->data;
+  local_idsz = so->abfd->build_id->size;
+
+  if (so->build_idsz != local_idsz
+      || memcmp (so->build_id, local_id, so->build_idsz) != 0)
+    {
+      char *remote_hex, *local_hex;
+
+      remote_hex = alloca (so->build_idsz * 2 + 1);
+      bin2hex (so->build_id, remote_hex, so->build_idsz);
+      local_hex = alloca (local_idsz * 2 + 1);
+      bin2hex (local_id, local_hex, local_idsz);
+
+      return xstrprintf (_("remote build ID %s "
+                          "does not match local build ID %s"),
+                        remote_hex, local_hex);
+    }
+
+  /* Both build IDs are present and they match.  */
+  return NULL;
+}
+
 /* Implement the "open_symbol_file_object" target_so_ops method.
 
    If no open symbol file, attempt to locate and open the main symbol
@@ -1108,6 +1169,12 @@ svr4_copy_library_list (struct so_list *src)
       newobj->lm_info = xmalloc (sizeof (struct lm_info));
       memcpy (newobj->lm_info, src->lm_info, sizeof (struct lm_info));
 
+      if (newobj->build_id != NULL)
+       {
+         newobj->build_id = xmalloc (src->build_idsz);
+         memcpy (newobj->build_id, src->build_id, src->build_idsz);
+       }
+
       newobj->next = NULL;
       *link = newobj;
       link = &newobj->next;
@@ -1135,6 +1202,9 @@ library_list_start_library (struct gdb_xml_parser *parser,
   ULONGEST *lmp = xml_find_attribute (attributes, "lm")->value;
   ULONGEST *l_addrp = xml_find_attribute (attributes, "l_addr")->value;
   ULONGEST *l_ldp = xml_find_attribute (attributes, "l_ld")->value;
+  const struct gdb_xml_value *const att_build_id
+    = xml_find_attribute (attributes, "build-id");
+  const char *const hex_build_id = att_build_id ? att_build_id->value : NULL;
   struct so_list *new_elem;
 
   new_elem = XCNEW (struct so_list);
@@ -1146,6 +1216,37 @@ library_list_start_library (struct gdb_xml_parser *parser,
   strncpy (new_elem->so_name, name, sizeof (new_elem->so_name) - 1);
   new_elem->so_name[sizeof (new_elem->so_name) - 1] = 0;
   strcpy (new_elem->so_original_name, new_elem->so_name);
+  if (hex_build_id != NULL)
+    {
+      const size_t hex_build_id_len = strlen (hex_build_id);
+
+      if (hex_build_id_len == 0)
+       warning (_("Shared library \"%s\" received empty build-id "
+                  "from gdbserver"), new_elem->so_original_name);
+      else if ((hex_build_id_len & 1U) != 0)
+       warning (_("Shared library \"%s\" received odd number "
+                  "of build-id \"%s\" hex characters from gdbserver"),
+                new_elem->so_original_name, hex_build_id);
+      else
+       {
+         const size_t build_idsz = hex_build_id_len / 2;
+
+         new_elem->build_id = xmalloc (build_idsz);
+         new_elem->build_idsz = hex2bin (hex_build_id, new_elem->build_id,
+                                         build_idsz);
+         if (new_elem->build_idsz != build_idsz)
+           {
+             warning (_("Shared library \"%s\" received invalid "
+                        "build-id \"%s\" hex character at encoded byte "
+                        "position %s (first as 0) from gdbserver"),
+                      new_elem->so_original_name, hex_build_id,
+                      pulongest (new_elem->build_idsz));
+             xfree (new_elem->build_id);
+             new_elem->build_id = NULL;
+             new_elem->build_idsz = 0;
+           }
+       }
+    }
 
   *list->tailp = new_elem;
   list->tailp = &new_elem->next;
@@ -1180,6 +1281,7 @@ static const struct gdb_xml_attribute svr4_library_attributes[] =
   { "lm", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
   { "l_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
   { "l_ld", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "build-id", GDB_XML_AF_OPTIONAL, NULL, NULL },
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
 };
 
@@ -3258,4 +3360,5 @@ _initialize_svr4_solib (void)
   svr4_so_ops.keep_data_in_core = svr4_keep_data_in_core;
   svr4_so_ops.update_breakpoints = svr4_update_solib_event_breakpoints;
   svr4_so_ops.handle_event = svr4_handle_solib_event;
+  svr4_so_ops.validate = svr4_validate;
 }
index f14363a4d9812517f8aedb7dbd3e34315eb77817..13cbd2623094260b7a65126cb85ad52e09ab4e55 100644 (file)
@@ -25,6 +25,7 @@
 #include "target.h"
 #include "vec.h"
 #include "solib-target.h"
+#include "solib.h"
 
 /* Private data for each loaded library.  */
 struct lm_info
@@ -506,6 +507,7 @@ _initialize_solib_target (void)
   solib_target_so_ops.in_dynsym_resolve_code
     = solib_target_in_dynsym_resolve_code;
   solib_target_so_ops.bfd_open = solib_bfd_open;
+  solib_target_so_ops.validate = default_solib_validate;
 
   /* Set current_target_so_ops to solib_target_so_ops if not already
      set.  */
index eb933c044d55b99f6042ff88ec4eca0ce9d6bf30..01c66dba8e44c61500c5bfdaec904a1463e8adcb 100644 (file)
@@ -518,6 +518,20 @@ solib_bfd_open (char *pathname)
   return abfd;
 }
 
+/* Boolean for command 'set validate-build-id'.  */
+static int validate_build_id = 1;
+
+/* Implement 'show validate-build-id'.  */
+
+static void
+show_validate_build_id (struct ui_file *file, int from_tty,
+                       struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Validation a build-id matches to load a shared "
+                           "library is %s.\n"),
+                   value);
+}
+
 /* Given a pointer to one of the shared objects in our list of mapped
    objects, use the recorded name to open a bfd descriptor for the
    object, build a section table, relocate all the section addresses
@@ -534,7 +548,7 @@ static int
 solib_map_sections (struct so_list *so)
 {
   const struct target_so_ops *ops = solib_ops (target_gdbarch ());
-  char *filename;
+  char *filename, *validate_error;
   struct target_section *p;
   struct cleanup *old_chain;
   bfd *abfd;
@@ -550,6 +564,29 @@ solib_map_sections (struct so_list *so)
   /* Leave bfd open, core_xfer_memory and "info files" need it.  */
   so->abfd = abfd;
 
+  gdb_assert (ops->validate != NULL);
+
+  validate_error = ops->validate (so);
+  if (validate_error != NULL)
+    {
+      if (validate_build_id)
+       {
+         warning (_("Shared object \"%s\" could not be validated (%s) and "
+                    "will be ignored; "
+                    "or use 'set validate-build-id off'."),
+                  so->so_name, validate_error);
+         xfree (validate_error);
+         gdb_bfd_unref (so->abfd);
+         so->abfd = NULL;
+         return 0;
+       }
+      warning (_("Shared object \"%s\" could not be validated (%s) "
+                "but it is being loaded due to "
+                "'set validate-build-id off'."),
+              so->so_name, validate_error);
+      xfree (validate_error);
+    }
+
   /* Copy the full path name into so_name, allowing symbol_file_add
      to find it later.  This also affects the =library-loaded GDB/MI
      event, and in particular the part of that notification providing
@@ -626,6 +663,9 @@ clear_so (struct so_list *so)
      of the symbol file.  */
   strcpy (so->so_name, so->so_original_name);
 
+  xfree (so->build_id);
+  so->build_id = NULL;
+
   /* Do the same for target-specific data.  */
   if (ops->clear_so != NULL)
     ops->clear_so (so);
@@ -1657,6 +1697,14 @@ remove_user_added_objfile (struct objfile *objfile)
     }
 }
 
+/* Default implementation does not perform any validation.  */
+
+char *
+default_solib_validate (const struct so_list *const so)
+{
+  return NULL; /* No validation.  */
+}
+
 extern initialize_file_ftype _initialize_solib; /* -Wmissing-prototypes */
 
 void
@@ -1714,4 +1762,18 @@ PATH and LD_LIBRARY_PATH."),
                                     reload_shared_libraries,
                                     show_solib_search_path,
                                     &setlist, &showlist);
+
+  add_setshow_boolean_cmd ("validate-build-id", class_support,
+                          &validate_build_id, _("\
+Set validation a build-id matches to load a shared library."), _("\
+SHow validation a build-id matches to load a shared library."), _("\
+Inferior shared library and symbol file may contain unique build-id.\n\
+If both build-ids are present but they do not match then this setting\n\
+enables (off) or disables (on) loading of such symbol file.\n\
+Loading non-matching symbol file may confuse debugging including breakage\n\
+of backtrace output."),
+                          NULL,
+                          show_validate_build_id,
+                          &setlist, &showlist);
+
 }
index 336971d0870c149a0fa8cb7b5d166ffe4c4ffc20..c3bf529027174cc6c66917dd9666d5d73b7bbd9d 100644 (file)
@@ -98,4 +98,8 @@ extern void update_solib_breakpoints (void);
 
 extern void handle_solib_event (void);
 
+/* Default validation always returns 1.  */
+
+extern char *default_solib_validate (const struct so_list *so);
+
 #endif /* SOLIB_H */
index 7021f5cde87618b20b023ff2560d0e634ece8965..e6e74afdbe1a5bf58752526141905b15281f9037 100644 (file)
@@ -75,6 +75,19 @@ struct so_list
        There may not be just one (e.g. if two segments are relocated
        differently); but this is only used for "info sharedlibrary".  */
     CORE_ADDR addr_low, addr_high;
+
+    /* Build id decoded from .note.gnu.build-id without note header.  This is
+       actual BUILD_ID which comes either from the remote target via qXfer
+       packet or via reading target memory.  Note that if there's a
+       mismatch with the associated bfd then so->abfd will be cleared.
+       Reading target memory should be done by following execution view
+       of the binary (following program headers in the case of ELF).
+       Computing address from the linking view (following ELF section
+       headers) may give incorrect build-id memory address despite the
+       symbols still match.
+       Such an example is a prelinked vs. unprelinked i386 ELF file.  */
+    size_t build_idsz;
+    gdb_byte *build_id;
   };
 
 struct target_so_ops
@@ -168,6 +181,11 @@ struct target_so_ops
        NULL, in which case no specific preprocessing is necessary
        for this target.  */
     void (*handle_event) (void);
+
+    /* Return NULL if SO does match target SO it is supposed to
+       represent.  Otherwise return string describing why it does not match.
+       Caller has to free the string.  */
+    char *(*validate) (const struct so_list *so);
   };
 
 /* Free the memory associated with a (so_list *).  */