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.
This commit is contained in:
Nick Clifton 2017-04-13 14:50:56 +01:00
parent 4274208406
commit 1d15e434f4
4 changed files with 129 additions and 15 deletions

View 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

View 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

View 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);

View 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;