From 1d15e434f43bc41a07bc7b0648fcb7e6ccbe8dcc Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Thu, 13 Apr 2017 14:50:56 +0100 Subject: [PATCH] Add note merging to strip and add code to merge stack size notes. * 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 | 10 ++++ binutils/doc/binutils.texi | 16 ++++-- binutils/objcopy.c | 101 +++++++++++++++++++++++++++++++++++-- binutils/readelf.c | 17 ++++--- 4 files changed, 129 insertions(+), 15 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 1c8d1aa02e5..ae734c4bbe5 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,13 @@ +2017-04-13 Nick Clifton + + * 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 PR binutils/21319 diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi index da4ed52dc46..23d8685b33b 100644 --- a/binutils/doc/binutils.texi +++ b/binutils/doc/binutils.texi @@ -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 diff --git a/binutils/objcopy.c b/binutils/objcopy.c index f9fe06b0128..9bad4b70f2c 100644 --- a/binutils/objcopy.c +++ b/binutils/objcopy.c @@ -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\ @ Read options from \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= Do not copy symbol \n\ -K --keep-symbol= Do not strip symbol \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); diff --git a/binutils/readelf.c b/binutils/readelf.c index d2b8dd4e790..93b94023659 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -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 = _(""); - 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 = _(""); - expected_types = "!+"; + expected_types = bool_expected; ++ name; break; case GNU_BUILD_ATTRIBUTE_STACK_SIZE: text = _(""); - expected_types = "*"; + expected_types = number_expected; ++ name; break; case GNU_BUILD_ATTRIBUTE_TOOL: text = _(""); - 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 = _(""); - expected_types = "*"; + expected_types = number_expected; ++ name; break; case GNU_BUILD_ATTRIBUTE_SHORT_ENUM: text = _(""); - expected_types = "!+"; + expected_types = bool_expected; ++ name; break; -- 2.30.2