Add note merging to strip and add code to merge stack size notes.
authorNick Clifton <nickc@redhat.com>
Thu, 13 Apr 2017 13:50:56 +0000 (14:50 +0100)
committerNick Clifton <nickc@redhat.com>
Thu, 13 Apr 2017 13:50:56 +0000 (14:50 +0100)
* objcopy.c: Add --no-merge-notes option to disable note merging.
Add --[no-]merge-notes option to strip, and enable it by default.
(num_bytes): New function.
(merge_gnu_build_notes): Add code to merge stack size notes.
* binutils.texi: Update strip and objcopy documentation.
* readelf.c (print_gnu_build_attribute_name): Use defined
constants for note types.

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

index 1c8d1aa02e537d9d0b9a3b75adf475d27f655ce7..ae734c4bbe5c75f9def109eb7701f9355d4424db 100644 (file)
@@ -1,3 +1,13 @@
+2017-04-13  Nick Clifton  <nickc@redhat.com>
+
+       * objcopy.c: Add --no-merge-notes option to disable note merging.
+       Add --[no-]merge-notes option to strip, and enable it by default.
+       (num_bytes): New function.
+       (merge_gnu_build_notes): Add code to merge stack size notes.
+       * binutils.texi: Update strip and objcopy documentation.
+       * readelf.c (print_gnu_build_attribute_name): Use defined
+       constants for note types.
+
 2017-04-10  John Delsignor  <john.delsignore@roguewave.com>
 
        PR binutils/21319
index da4ed52dc46d491e8c2e17e7c37b23b58a612db8..23d8685b33b0c1685efd7b2bf3d7cddeb9c2228d 100644 (file)
@@ -1191,6 +1191,7 @@ objcopy [@option{-F} @var{bfdname}|@option{--target=}@var{bfdname}]
         [@option{--decompress-debug-sections}]
         [@option{--elf-stt-common=@var{val}}]
         [@option{--merge-notes}]
+        [@option{--no-merge-notes}]
         [@option{-v}|@option{--verbose}]
         [@option{-V}|@option{--version}]
         [@option{--help}] [@option{--info}]
@@ -2008,8 +2009,9 @@ converted to the @code{STT_COMMON} or @code{STT_OBJECT} type.
 type to @code{STT_OBJECT}.
 
 @item --merge-notes
-For ELF files, attempt to reduce the size of any SHT_NOTE type
-sections by removing duplicate notes.
+@itemx --no-merge-notes
+For ELF files, attempt (or do not attempt) to reduce the size of any
+SHT_NOTE type sections by removing duplicate notes.
 
 @item -V
 @itemx --version
@@ -3114,7 +3116,8 @@ strip [@option{-F} @var{bfdname} |@option{--target=}@var{bfdname}]
       [@option{-s}|@option{--strip-all}]
       [@option{-S}|@option{-g}|@option{-d}|@option{--strip-debug}]
       [@option{--strip-dwo}]
-      [@option{-K} @var{symbolname} |@option{--keep-symbol=}@var{symbolname}]
+      [@option{-K} @var{symbolname}|@option{--keep-symbol=}@var{symbolname}]
+      [@option{-M}|@option{--merge-notes}][@option{--no-merge-notes}]
       [@option{-N} @var{symbolname} |@option{--strip-symbol=}@var{symbolname}]
       [@option{-w}|@option{--wildcard}]
       [@option{-x}|@option{--discard-all}] [@option{-X} |@option{--discard-locals}]
@@ -3241,6 +3244,13 @@ Remove all symbols that are not needed for relocation processing.
 When stripping symbols, keep symbol @var{symbolname} even if it would
 normally be stripped.  This option may be given more than once.
 
+@item -M
+@itemx --merge-notes
+@itemx --no-merge-notes
+For ELF files, attempt (or do not attempt) to reduce the size of any
+SHT_NOTE type sections by removing duplicate notes.  The default is to
+attempt this reduction.
+
 @item -N @var{symbolname}
 @itemx --strip-symbol=@var{symbolname}
 Remove symbol @var{symbolname} from the source file. This option may be
index f9fe06b012892d60bd2d8d7f117f164d55b218e4..9bad4b70f2c07f4fcaa8cfc8925ebd22cce88671 100644 (file)
@@ -321,6 +321,7 @@ enum command_line_switch
   OPTION_LOCALIZE_SYMBOLS,
   OPTION_LONG_SECTION_NAMES,
   OPTION_MERGE_NOTES,
+  OPTION_NO_MERGE_NOTES,
   OPTION_NO_CHANGE_WARNINGS,
   OPTION_ONLY_KEEP_DEBUG,
   OPTION_PAD_TO,
@@ -368,6 +369,8 @@ static struct option strip_options[] =
   {"input-target", required_argument, 0, 'I'},
   {"keep-file-symbols", no_argument, 0, OPTION_KEEP_FILE_SYMBOLS},
   {"keep-symbol", required_argument, 0, 'K'},
+  {"merge-notes", no_argument, 0, 'M'},
+  {"no-merge-notes", no_argument, 0, OPTION_NO_MERGE_NOTES},
   {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG},
   {"output-file", required_argument, 0, 'o'},
   {"output-format", required_argument, 0, 'O'},        /* Obsolete */
@@ -443,6 +446,7 @@ static struct option copy_options[] =
   {"localize-symbols", required_argument, 0, OPTION_LOCALIZE_SYMBOLS},
   {"long-section-names", required_argument, 0, OPTION_LONG_SECTION_NAMES},
   {"merge-notes", no_argument, 0, 'M'},
+  {"no-merge-notes", no_argument, 0, OPTION_NO_MERGE_NOTES},
   {"no-adjust-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS},
   {"no-change-warnings", no_argument, 0, OPTION_NO_CHANGE_WARNINGS},
   {"only-keep-debug", no_argument, 0, OPTION_ONLY_KEEP_DEBUG},
@@ -642,6 +646,7 @@ copy_usage (FILE *stream, int exit_status)
      --elf-stt-common=[yes|no]     Generate ELF common symbols with STT_COMMON\n\
                                      type\n\
   -M  --merge-notes                Remove redundant entries in note sections\n\
+      --no-merge-notes             Do not attempt to remove redundant notes (default)\n\
   -v --verbose                     List all object files modified\n\
   @<file>                          Read options from <file>\n\
   -V --version                     Display this program's version number\n\
@@ -686,6 +691,8 @@ strip_usage (FILE *stream, int exit_status)
      --strip-dwo                   Remove all DWO sections\n\
      --strip-unneeded              Remove all symbols not needed by relocations\n\
      --only-keep-debug             Strip everything but the debug information\n\
+  -M  --merge-notes                Remove redundant entries in note sections (default)\n\
+      --no-merge-notes             Do not attempt to remove redundant notes\n\
   -N --strip-symbol=<name>         Do not copy symbol <name>\n\
   -K --keep-symbol=<name>          Do not strip symbol <name>\n\
      --keep-file-symbols           Do not strip file symbol(s)\n\
@@ -1882,6 +1889,22 @@ copy_unknown_object (bfd *ibfd, bfd *obfd)
   return TRUE;
 }
 
+/* Returns the number of bytes needed to store VAL.  */
+
+static inline unsigned int
+num_bytes (unsigned long val)
+{
+  unsigned int count = 0;
+
+  /* FIXME: There must be a faster way to do this.  */
+  while (val)
+    {
+      count ++;
+      val >>= 8;
+    }
+  return count;
+}
+
 /* Merge the notes on SEC, removing redundant entries.
    Returns the new, smaller size of the section upon success.  */
 
@@ -1899,7 +1922,7 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte
 
   /* Make a copy of the notes.
      Minimum size of a note is 12 bytes.  */
-  pnote = pnotes = (Elf_Internal_Note *) xmalloc ((size / 12) * sizeof (Elf_Internal_Note));
+  pnote = pnotes = (Elf_Internal_Note *) xcalloc ((size / 12), sizeof (Elf_Internal_Note));
   while (remain >= 12)
     {
       pnote->namesz = (bfd_get_32 (abfd, in    ) + 3) & ~3;
@@ -1939,6 +1962,12 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte
       remain -= 12 + pnote->namesz + pnote->descsz;
       in     += 12 + pnote->namesz + pnote->descsz;
 
+      if (pnote->namedata[pnote->namesz - 1] != 0)
+       {
+         err = _("corrupt GNU build attribute note: name not NUL terminated");
+         goto done;
+       }
+      
       if (pnote->namesz > 1 && pnote->namedata[1] == GNU_BUILD_ATTRIBUTE_VERSION)
        ++ version_notes_seen;
       pnote ++;
@@ -1983,7 +2012,9 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte
      3. Eliminate any NT_GNU_BUILD_ATTRIBUTE_OPEN notes that have the same
         full name field as the immediately preceeding note with the same type
        of name.
-     4. If an NT_GNU_BUILD_ATTRIBUTE_OPEN note is going to be preserved and
+     4. Combine the numeric value of any NT_GNU_BUILD_ATTRIBUTE_OPEN notes
+        of type GNU_BUILD_ATTRIBUTE_STACK_SIZE.
+     5. If an NT_GNU_BUILD_ATTRIBUTE_OPEN note is going to be preserved and
         its description field is empty then the nearest preceeding OPEN note
        with a non-empty description field must also be preserved *OR* the
        description field of the note must be changed to contain the starting
@@ -2008,6 +2039,54 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte
          if (back->type == pnote->type
              && back->namedata[1] == pnote->namedata[1])
            {
+             if (back->namedata[1] == GNU_BUILD_ATTRIBUTE_STACK_SIZE)
+               {
+                 unsigned char * name;
+                 unsigned long   note_val;
+                 unsigned long   back_val;
+                 unsigned int    shift;
+                 unsigned int    bytes;
+                 unsigned long   byte;
+
+                 for (shift = 0, note_val = 0, bytes = pnote->namesz - 2, name = (unsigned char *) pnote->namedata + 2;
+                      bytes--;)
+                   {
+                     byte = (* name ++) & 0xff;
+                     note_val |= byte << shift;
+                     shift += 8;
+                   }
+                 for (shift = 0, back_val = 0, bytes = back->namesz - 2, name = (unsigned char *) back->namedata + 2;
+                      bytes--;)
+                   {
+                     byte = (* name ++) & 0xff;
+                     back_val |= byte << shift;
+                     shift += 8;
+                   }
+                 back_val += note_val;
+                 if (num_bytes (back_val) >= back->namesz - 2)
+                   {
+                     /* We have a problem - the new value requires more bytes of
+                        storage in the name field than are available.  Currently
+                        we have no way of fixing this, so we just preserve both
+                        notes.  */
+                     continue;
+                   }
+                 /* Write the new val into back.  */
+                 name = (unsigned char *) back->namedata + 2;
+                 while (name < (unsigned char *) back->namedata + back->namesz)
+                   {
+                     byte = back_val & 0xff;
+                     * name ++ = byte;
+                     if (back_val == 0)
+                       break;
+                     back_val >>= 8;
+                   }
+
+                 duplicate_found = TRUE;
+                 pnote->type = 0;
+                 break;
+               }
+                 
              if (back->namesz == pnote->namesz
                  && memcmp (back->namedata, pnote->namedata, back->namesz) == 0)
                {
@@ -2018,10 +2097,11 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte
 
              /* If we have found an attribute match then stop searching backwards.  */
              if (! ISPRINT (back->namedata[1])
+                 /* Names are NUL terminated, so this is safe.  */
                  || strcmp (back->namedata + 2, pnote->namedata + 2) == 0)
                {
                  /* Since we are keeping this note we must check to see if its
-                    description refers back to an earlier OPEN note.  If so
+                    description refers back to an earlier OPEN version note.  If so
                     then we must make sure that version note is also preserved.  */
                  if (pnote->descsz == 0
                      && prev_open != NULL
@@ -2821,8 +2901,8 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch)
              return FALSE;
            }
        }
-      else
-       bfd_nonfatal_message (NULL, obfd, osec, _("ICE: lost merged note section"));
+      else if (! is_strip)
+       bfd_nonfatal_message (NULL, obfd, osec, _("could not find any mergeable note sections"));
       free (merged_notes);
       merged_notes = NULL;
       merge_notes = FALSE;
@@ -4023,6 +4103,8 @@ strip_main (int argc, char *argv[])
   int i;
   char *output_file = NULL;
 
+  merge_notes = TRUE;
+
   while ((c = getopt_long (argc, argv, "I:O:F:K:N:R:o:sSpdgxXHhVvwDU",
                           strip_options, (int *) 0)) != EOF)
     {
@@ -4060,6 +4142,12 @@ strip_main (int argc, char *argv[])
        case 'K':
          add_specific_symbol (optarg, keep_specific_htab);
          break;
+       case 'M':
+         merge_notes = TRUE;
+         break;
+       case OPTION_NO_MERGE_NOTES:
+         merge_notes = FALSE;
+         break;
        case 'N':
          add_specific_symbol (optarg, strip_specific_htab);
          break;
@@ -4486,6 +4574,9 @@ copy_main (int argc, char *argv[])
        case 'M':
          merge_notes = TRUE;
          break;
+       case OPTION_NO_MERGE_NOTES:
+         merge_notes = FALSE;
+         break;
 
        case 'N':
          add_specific_symbol (optarg, strip_specific_htab);
index d2b8dd4e790554ac0f7b6cd4393d65fbabf6b5c1..93b94023659512a3d60a0d0210a0b23a81eae8e3 100644 (file)
@@ -16809,9 +16809,12 @@ print_gnu_build_attribute_description (Elf_Internal_Note * pnote,
 static bfd_boolean
 print_gnu_build_attribute_name (Elf_Internal_Note * pnote)
 {
+  static const char string_expected [2] = { GNU_BUILD_ATTRIBUTE_TYPE_STRING, 0 };
+  static const char number_expected [2] = { GNU_BUILD_ATTRIBUTE_TYPE_NUMERIC, 0 };
+  static const char bool_expected [3] = { GNU_BUILD_ATTRIBUTE_TYPE_BOOL_TRUE, GNU_BUILD_ATTRIBUTE_TYPE_BOOL_FALSE, 0 };
   char         name_type;
   char         name_attribute;
-  char *       expected_types;
+  const char * expected_types;
   const char * name = pnote->namedata;
   const char * text;
   int          left;
@@ -16845,7 +16848,7 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote)
     {
     case GNU_BUILD_ATTRIBUTE_VERSION:
       text = _("<version>");
-      expected_types = "$";
+      expected_types = string_expected;
       ++ name;
       break;
     case GNU_BUILD_ATTRIBUTE_STACK_PROT:
@@ -16855,17 +16858,17 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote)
       break;
     case GNU_BUILD_ATTRIBUTE_RELRO:
       text = _("<relro>");
-      expected_types = "!+";
+      expected_types = bool_expected;
       ++ name;
       break;
     case GNU_BUILD_ATTRIBUTE_STACK_SIZE:
       text = _("<stack size>");
-      expected_types = "*";
+      expected_types = number_expected;
       ++ name;
       break;
     case GNU_BUILD_ATTRIBUTE_TOOL:
       text = _("<tool>");
-      expected_types = "$";
+      expected_types = string_expected;
       ++ name;
       break;
     case GNU_BUILD_ATTRIBUTE_ABI:
@@ -16875,12 +16878,12 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote)
       break;
     case GNU_BUILD_ATTRIBUTE_PIC:
       text = _("<PIC>");
-      expected_types = "*";
+      expected_types = number_expected;
       ++ name;
       break;
     case GNU_BUILD_ATTRIBUTE_SHORT_ENUM:
       text = _("<short enum>");
-      expected_types = "!+";
+      expected_types = bool_expected;
       ++ name;
       break;