Readelf: Add --lto-syms option to display LTO symbol tables.
authorNick Clifton <nickc@redhat.com>
Mon, 19 Oct 2020 14:27:58 +0000 (15:27 +0100)
committerNick Clifton <nickc@redhat.com>
Mon, 19 Oct 2020 14:27:58 +0000 (15:27 +0100)
* readelf.c (do_lto_syms): New local.
(long_option_values): Add OPTION_LTO_SYMS.
(options): Add --lto-syms.
(usage): Mention the new option.
(parse_args): Parse the new option.
(get_lto_kind): New function.
(get_lto_visibility): New function.
(get_lto_sym_type): New function.
(display_lto_symtab): New function - displays the contents of an
LTo symbol table section.
(process_lto_symbol_tables): New functions.  Calls
dipslay_lto_symtab on any LTO symbol table section.
(process_object_file): Call process_lto_symbol_tables.
* doc/binutils.texi: Document the new option.
* NEWS: Mention the new feature.

binutils/ChangeLog
binutils/NEWS
binutils/doc/binutils.texi
binutils/readelf.c

index ed05b24acfd6fb0b21a4623729db6a7c963f5aee..786d3f429ebe65982880c765e3b813a47c3375f3 100644 (file)
@@ -1,3 +1,21 @@
+2020-10-19  Nick Clifton  <nickc@redhat.com>
+
+       * readelf.c (do_lto_syms): New local.
+       (long_option_values): Add OPTION_LTO_SYMS.
+       (options): Add --lto-syms.
+       (usage): Mention the new option.
+       (parse_args): Parse the new option.
+       (get_lto_kind): New function.
+       (get_lto_visibility): New function.
+       (get_lto_sym_type): New function.
+       (display_lto_symtab): New function - displays the contents of an
+       LTo symbol table section.
+       (process_lto_symbol_tables): New functions.  Calls
+       dipslay_lto_symtab on any LTO symbol table section.
+       (process_object_file): Call process_lto_symbol_tables.
+       * doc/binutils.texi: Document the new option.
+       * NEWS: Mention the new feature.
+
 2020-10-09  H.J. Lu  <hongjiu.lu@intel.com>
 
        PR gas/26703
index c0dc73d7d8f5e239f1c7077275803ab3e915a2f2..35e4e303e15e172ade188993a2b6d875d2d0336c 100644 (file)
@@ -1,5 +1,8 @@
 -*- text -*-
 
+* Readelf can now display the contents of LTO symbol table sections when asked
+  to do so via the --lto-syms command line option.
+
 * Readelf now accepts the -C command line option to enable the demangling of
   symbol names.  In addition the --demangle=<style>, --no-demangle,
   --recurse-limit and --no-recurse-limit options are also now availale.
index ef2d665e34db56ee892c9a26992d36334328a9a5..6203fde8877b8e7c5ffd38d80c94133c076a1960 100644 (file)
@@ -4707,7 +4707,7 @@ readelf [@option{-a}|@option{--all}]
         [@option{-t}|@option{--section-details}]
         [@option{-e}|@option{--headers}]
         [@option{-s}|@option{--syms}|@option{--symbols}]
-        [@option{--dyn-syms}]
+        [@option{--dyn-syms}|@option{--lto-syms}]
         [@option{--demangle@var{=style}}|@option{--no-demangle}]
         [@option{--recurse-limit}|@option{--no-recurse-limit}]
         [@option{-n}|@option{--notes}]
@@ -4825,6 +4825,10 @@ Displays the entries in dynamic symbol table section of the file, if it
 has one.  The output format is the same as the format used by the
 @option{--syms} option.
 
+@item --lto-syms
+@cindex LTO symbol table
+Displays the contents of any LTO symbol tables in the file.
+
 @item -C
 @itemx --demangle[=@var{style}]
 @cindex demangling in nm
index 33ae4e527f6d2ae1b4c6e0d0d2b5acc2c13540f3..d2fd249a71f487ff7aede1c533b072756bfdcacf 100644 (file)
@@ -216,6 +216,7 @@ static bfd_boolean show_name = FALSE;
 static bfd_boolean do_dynamic = FALSE;
 static bfd_boolean do_syms = FALSE;
 static bfd_boolean do_dyn_syms = FALSE;
+static bfd_boolean do_lto_syms = FALSE;
 static bfd_boolean do_reloc = FALSE;
 static bfd_boolean do_sections = FALSE;
 static bfd_boolean do_section_groups = FALSE;
@@ -4483,6 +4484,7 @@ enum long_option_values
 {
   OPTION_DEBUG_DUMP = 512,
   OPTION_DYN_SYMS,
+  OPTION_LTO_SYMS,
   OPTION_DWARF_DEPTH,
   OPTION_DWARF_START,
   OPTION_DWARF_CHECK,
@@ -4538,6 +4540,7 @@ static struct option options[] =
   {"no-recurse-limit", no_argument, NULL, OPTION_NO_RECURSE_LIMIT},
   {"no-recursion-limit", no_argument, NULL, OPTION_NO_RECURSE_LIMIT},
   {"dyn-syms",        no_argument, 0, OPTION_DYN_SYMS},
+  {"lto-syms",         no_argument, 0, OPTION_LTO_SYMS},
   {"debug-dump",       optional_argument, 0, OPTION_DEBUG_DUMP},
   {"dwarf-depth",      required_argument, 0, OPTION_DWARF_DEPTH},
   {"dwarf-start",      required_argument, 0, OPTION_DWARF_START},
@@ -4570,6 +4573,7 @@ usage (FILE * stream)
   -s --syms              Display the symbol table\n\
      --symbols           An alias for --syms\n\
      --dyn-syms          Display the dynamic symbol table\n\
+     --lto-syms          Display LTO symbol tables\n\
   -C --demangle[=STYLE]  Decode low-level symbol names into user-level names\n\
                           The STYLE, if specified, can be `auto' (the default),\n\
                           `gnu', `lucid', `arm', `hp', `edg', `gnu-v3', `java'\n\
@@ -4809,7 +4813,7 @@ parse_args (struct dump_data *dumpdata, int argc, char ** argv)
          break;
        case 'w':
          do_dump = TRUE;
-         if (optarg == 0)
+         if (optarg == NULL)
            {
              do_debugging = TRUE;
              dwarf_select_sections_all ();
@@ -4822,7 +4826,7 @@ parse_args (struct dump_data *dumpdata, int argc, char ** argv)
          break;
        case OPTION_DEBUG_DUMP:
          do_dump = TRUE;
-         if (optarg == 0)
+         if (optarg == NULL)
            do_debugging = TRUE;
          else
            {
@@ -4866,6 +4870,9 @@ parse_args (struct dump_data *dumpdata, int argc, char ** argv)
        case OPTION_DYN_SYMS:
          do_dyn_syms = TRUE;
          break;
+       case OPTION_LTO_SYMS:
+         do_lto_syms = TRUE;
+         break;
 #ifdef SUPPORT_DISASSEMBLY
        case 'i':
          request_dump (dumpdata, DISASS_DUMP);
@@ -4922,7 +4929,7 @@ parse_args (struct dump_data *dumpdata, int argc, char ** argv)
       && !do_segments && !do_header && !do_dump && !do_version
       && !do_histogram && !do_debugging && !do_arch && !do_notes
       && !do_section_groups && !do_archive_index
-      && !do_dyn_syms)
+      && !do_dyn_syms && !do_lto_syms)
     {
       if (do_checks)
        {
@@ -4931,6 +4938,7 @@ parse_args (struct dump_data *dumpdata, int argc, char ** argv)
          do_segments = do_header = do_dump = do_version = TRUE;
          do_histogram = do_debugging = do_arch = do_notes = TRUE;
          do_section_groups = do_archive_index = do_dyn_syms = TRUE;
+         do_lto_syms = TRUE;
        }
       else
        usage (stderr);
@@ -12179,7 +12187,256 @@ print_dynamic_symbol (Filedata *filedata, unsigned long si,
          si, printable_section_name (filedata, section), section->sh_info);
 }
 
+static const char *
+get_lto_kind (unsigned int kind)
+{
+  switch (kind)
+    {
+    case 0: return "DEF";
+    case 1: return "WEAKDEF";
+    case 2: return "UNDEF";
+    case 3: return "WEAKUNDEF";
+    case 4: return "COMMON";
+    default:
+      break;
+    }
+
+  static char buffer[30];
+  error (_("Unknown LTO symbol definition encountered: %u\n"), kind);
+  sprintf (buffer, "<unknown: %u>", kind);
+  return buffer;
+}
+
+static const char *
+get_lto_visibility (unsigned int visibility)
+{
+  switch (visibility)
+    {
+    case 0: return "DEFAULT";
+    case 1: return "PROTECTED";
+    case 2: return "INTERNAL";
+    case 3: return "HIDDEN";
+    default:
+      break;
+    }
+
+  static char buffer[30];
+  error (_("Unknown LTO symbol visibility encountered: %u\n"), visibility);
+  sprintf (buffer, "<unknown: %u>", visibility);
+  return buffer;
+}
+
+static const char *
+get_lto_sym_type (unsigned int sym_type)
+{
+  switch (sym_type)
+    {
+    case 0: return "UNKNOWN";
+    case 1: return "FUNCTION";
+    case 2: return "VARIABLE";
+    default:
+      break;
+    }
+
+  static char buffer[30];
+  error (_("Unknown LTO symbol type encountered: %u\n"), sym_type);
+  sprintf (buffer, "<unknown: %u>", sym_type);
+  return buffer;
+}
+
+/* Display an LTO format symbol table.
+   FIXME: The format of LTO symbol tables is not formalized.
+   So this code could need changing in the future.  */
+
+static bfd_boolean
+display_lto_symtab (Filedata *           filedata,
+                   Elf_Internal_Shdr *  section)
+{
+  if (section->sh_size == 0)
+    {
+      printf (_("\nLTO Symbol table '%s' is empty!\n"),
+             printable_section_name (filedata, section));
+      return TRUE;
+    }
+
+  if (section->sh_size > filedata->file_size)
+    {
+      error (_("Section %s has an invalid sh_size of 0x%lx\n"),
+            printable_section_name (filedata, section),
+            (unsigned long) section->sh_size);
+      return FALSE;
+    }
+
+  void * alloced_data = get_data (NULL, filedata, section->sh_offset,
+                                 section->sh_size, 1, _("LTO symbols"));
+  if (alloced_data == NULL)
+    return FALSE;
+
+  /* Look for extended data for the symbol table.  */
+  Elf_Internal_Shdr * ext;
+  void * ext_data_orig = NULL;
+  char * ext_data = NULL;
+  char * ext_data_end = NULL;
+  char * ext_name = NULL;
+
+  if (asprintf (& ext_name, ".gnu.lto_.ext_symtab.%s",
+               SECTION_NAME (section) + strlen (".gnu.lto_.symtab.")) > 0
+      && ext_name != NULL /* Paranoia.  */
+      && (ext = find_section (filedata, ext_name)) != NULL)
+    {
+      if (ext->sh_size < 3)
+       error (_("LTO Symbol extension table '%s' is empty!\n"),
+              printable_section_name (filedata, ext));
+      else
+       {
+         ext_data_orig = ext_data = get_data (NULL, filedata, ext->sh_offset,
+                                              ext->sh_size, 1,
+                                              _("LTO ext symbol data"));
+         if (ext_data != NULL)
+           {
+             ext_data_end = ext_data + ext->sh_size;
+             if (* ext_data++ != 1)
+               error (_("Unexpected version number in symbol extension table\n"));
+           }
+       }
+    }
+  
+  const unsigned char * data = (const unsigned char *) alloced_data;
+  const unsigned char * end = data + section->sh_size;
+
+  if (ext_data_orig != NULL)
+    {
+      if (do_wide)
+       printf (_("\nLTO Symbol table '%s' and extension table '%s' contain:\n"),
+               printable_section_name (filedata, section),
+               printable_section_name (filedata, ext));
+      else
+       {
+         printf (_("\nLTO Symbol table '%s'\n"),
+                 printable_section_name (filedata, section));
+         printf (_(" and extension table '%s' contain:\n"),
+                 printable_section_name (filedata, ext));
+       }
+    }
+  else
+    printf (_("\nLTO Symbol table '%s' contains:\n"),
+           printable_section_name (filedata, section));
+    
+
+  /* FIXME: Add a wide version.  */
+  if (ext_data_orig != NULL) 
+    printf (_("  Comdat_Key       Kind  Visibility     Size      Slot      Type  Section Name\n"));
+  else
+    printf (_("  Comdat_Key       Kind  Visibility     Size      Slot Name\n"));
+
+  /* FIXME: We do not handle style prefixes.  */
+
+  while (data < end)
+    {
+      const unsigned char * sym_name = data;
+      data += strnlen ((const char *) sym_name, end - data) + 1;
+      if (data >= end)
+       goto fail;
+
+      const unsigned char * comdat_key = data;
+      data += strnlen ((const char *) comdat_key, end - data) + 1;
+      if (data >= end)
+       goto fail;
+
+      if (data + 2 + 8 + 4 > end)
+       goto fail;
+
+      unsigned int kind = *data++;
+      unsigned int visibility = *data++;
+
+      elf_vma size = byte_get (data, 8);
+      data += 8;
+
+      elf_vma slot = byte_get (data, 4);
+      data += 4;
+
+      if (ext_data != NULL)
+       {
+         if (ext_data < (ext_data_end - 1))
+           {
+             unsigned int sym_type = * ext_data ++;
+             unsigned int sec_kind = * ext_data ++;
+
+             printf ("  %10s %10s %11s %08lx  %08lx %9s %08lx _",
+                     * comdat_key == 0 ? "-" : (char *) comdat_key,
+                     get_lto_kind (kind),
+                     get_lto_visibility (visibility),
+                     (long) size,
+                     (long) slot,
+                     get_lto_sym_type (sym_type),
+                     (long) sec_kind);
+             print_symbol (6, (const char *) sym_name);
+           }
+         else
+           {
+             error (_("Ran out of LTO symbol extension data\n"));
+             ext_data = NULL;
+             /* FIXME: return FAIL result ?  */
+           }
+       }
+      else
+       {
+         printf ("  %10s %10s %11s %08lx  %08lx _",
+                 * comdat_key == 0 ? "-" : (char *) comdat_key,
+                 get_lto_kind (kind),
+                 get_lto_visibility (visibility),
+                 (long) size,
+                 (long) slot);
+         print_symbol (21, (const char *) sym_name);
+       }
+      putchar ('\n');
+    }
+
+  if (ext_data != NULL && ext_data < ext_data_end)
+    {
+      error (_("Data remains in the LTO symbol extension table\n"));
+      goto fail;
+    }
+
+  free (alloced_data);
+  free (ext_data_orig);
+  free (ext_name);
+  return TRUE;
+  
+ fail:
+  error (_("Buffer overrun encountered whilst decoding LTO symbol table\n"));
+  free (alloced_data);
+  free (ext_data_orig);
+  free (ext_name);
+  return FALSE;
+}
+
+/* Display LTO symbol tables.  */
+
+static bfd_boolean
+process_lto_symbol_tables (Filedata * filedata)
+{
+  Elf_Internal_Shdr * section;
+  unsigned int i;
+  bfd_boolean res = TRUE;
+
+  if (!do_lto_syms)
+    return TRUE;
+
+  if (filedata->section_headers == NULL)
+    return TRUE;
+
+  for (i = 0, section = filedata->section_headers;
+       i < filedata->file_header.e_shnum;
+       i++, section++)
+    if (CONST_STRNEQ (SECTION_NAME (section), ".gnu.lto_.symtab"))
+      res &= display_lto_symtab (filedata, section);
+
+  return res; 
+}
+
 /* Dump the symbol table.  */
+
 static bfd_boolean
 process_symbol_table (Filedata * filedata)
 {
@@ -20604,6 +20861,9 @@ process_object (Filedata * filedata)
   if (! process_symbol_table (filedata))
     res = FALSE;
 
+  if (! process_lto_symbol_tables (filedata))
+    res = FALSE;
+  
   if (! process_syminfo (filedata))
     res = FALSE;