diff options
author | Nick Clifton <nickc@redhat.com> | 2017-03-01 11:09:46 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2017-03-01 11:09:46 +0000 |
commit | 9ef920e933bf2ea228c909cf81636e6d9577e51e (patch) | |
tree | c6118c4b931f1d68528ca12ec38e31af6e3b1d46 /binutils/objcopy.c | |
parent | a7e8b06b8901309632fad842ffd7d90a81447c80 (diff) | |
download | gdb-9ef920e933bf2ea228c909cf81636e6d9577e51e.zip gdb-9ef920e933bf2ea228c909cf81636e6d9577e51e.tar.gz gdb-9ef920e933bf2ea228c909cf81636e6d9577e51e.tar.bz2 |
Add support for displaying and merging GNU_BUILD_NOTEs.
include * elf/common.h (SHF_GNU_BUILD_NOTE): Define.
(NT_GNU_PROPERTY_TYPE_0): Define.
(NT_GNU_BUILD_ATTRIBUTE_OPEN): Define.
(NT_GNU_BUILD_ATTRIBUTE_FUN): Define.
(GNU_BUILD_ATTRIBUTE_TYPE_NUMERIC): Define.
(GNU_BUILD_ATTRIBUTE_TYPE_STRING): Define.
(GNU_BUILD_ATTRIBUTE_TYPE_BOOL_TRUE): Define.
(GNU_BUILD_ATTRIBUTE_TYPE_BOOL_FALSE): Define.
(GNU_BUILD_ATTRIBUTE_VERSION): Define.
(GNU_BUILD_ATTRIBUTE_STACK_PROT): Define.
(GNU_BUILD_ATTRIBUTE_RELRO): Define.
(GNU_BUILD_ATTRIBUTE_STACK_SIZE): Define.
(GNU_BUILD_ATTRIBUTE_TOOL): Define.
(GNU_BUILD_ATTRIBUTE_ABI): Define.
(GNU_BUILD_ATTRIBUTE_PIC): Define.
(NOTE_GNU_PROPERTY_SECTION_NAME): Define.
(GNU_BUILD_ATTRS_SECTION_NAME): Define.
(GNU_PROPERTY_STACK_SIZE): Define.
(GNU_PROPERTY_NO_COPY_ON_PROTECTED): Define.
(GNU_PROPERTY_X86_ISA_1_USED): Define.
(GNU_PROPERTY_X86_ISA_1_NEEDED): Define.
(GNU_PROPERTY_X86_ISA_1_486): Define.
(GNU_PROPERTY_X86_ISA_1_586): Define.
(GNU_PROPERTY_X86_ISA_1_686): Define.
(GNU_PROPERTY_X86_ISA_1_SSE): Define.
(GNU_PROPERTY_X86_ISA_1_SSE2): Define.
(GNU_PROPERTY_X86_ISA_1_SSE3): Define.
(GNU_PROPERTY_X86_ISA_1_SSSE3): Define.
(GNU_PROPERTY_X86_ISA_1_SSE4_1): Define.
(GNU_PROPERTY_X86_ISA_1_SSE4_2): Define.
(GNU_PROPERTY_X86_ISA_1_AVX): Define.
(GNU_PROPERTY_X86_ISA_1_AVX2): Define.
(GNU_PROPERTY_X86_ISA_1_AVX512F): Define.
(GNU_PROPERTY_X86_ISA_1_AVX512CD): Define.
(GNU_PROPERTY_X86_ISA_1_AVX512ER): Define.
(GNU_PROPERTY_X86_ISA_1_AVX512PF): Define.
(GNU_PROPERTY_X86_ISA_1_AVX512VL): Define.
(GNU_PROPERTY_X86_ISA_1_AVX512DQ): Define.
(GNU_PROPERTY_X86_ISA_1_AVX512BW): Define.
binutils* readelf.c (get_note_type): Add support for GNU_BUILD_NOTEs.
(get_gnu_elf_note_type): Add support for GNU_PROPERTY_NOTEs.
(decode_x86_isa): New function.
(print_gnu_property_note): New function.
(print_gnu_note): Handle GNU_PROPERTY_NOTEs.
(print_gnu_build_attribute_description): New function.
(print_gnu_build_attribute_name): New function.
(process_note): Add support for GNU_BUILD_NOTEs.
* objcopy.c (--merge-notes): New command line option.
(copy_options): Add merge-notes.
(copy_usage): Likewise.
(is_merge_note_section): New function.
(merge_gnu_build_notes): New function.
(copy_object): Merge note sections if asked to do so.
(skip_section): Add skip_copy parameter. Add support for skipping
merged note sections.
(copy_relocations_in_section): Update call to skip_section.
(copy_section): Likewise.
(copy_main): Add support for merge-notes option.
* doc/binutils.texi: Document the new option to objcopy.
* NEWS: Mention the new feature.
* testsuite/binutils-all/note-2-32.d: New test. Checks note
merging on 32-bit targets.
* testsuite/binutils-all/note-2-32.s: New test source file.
* testsuite/binutils-all/note-2-64.d: New test. Like note-2-32.d
but for 64-bit targets.
* testsuite/binutils-all/note-2-64.s: New test source file.
* testsuite/binutils-all/objcopy.exp: Run the new test.
Diffstat (limited to 'binutils/objcopy.c')
-rw-r--r-- | binutils/objcopy.c | 393 |
1 files changed, 367 insertions, 26 deletions
diff --git a/binutils/objcopy.c b/binutils/objcopy.c index 9291b3a..baf6990 100644 --- a/binutils/objcopy.c +++ b/binutils/objcopy.c @@ -30,6 +30,7 @@ #include "elf-bfd.h" #include "coff/internal.h" #include "libcoff.h" +#include "safe-ctype.h" /* FIXME: See bfd/peXXigen.c for why we include an architecture specific header in generic PE code. */ @@ -94,7 +95,11 @@ static int copy_width = 1; static bfd_boolean verbose; /* Print file and target names. */ static bfd_boolean preserve_dates; /* Preserve input file timestamp. */ static int deterministic = -1; /* Enable deterministic archives. */ -static int status = 0; /* Exit status. */ +static int status = 0; /* Exit status. */ + +static bfd_boolean merge_notes = FALSE; /* Merge note sections. */ +static bfd_byte * merged_notes = NULL; /* Contents on note section undergoing a merge. */ +static bfd_size_type merged_size = 0; /* New, smaller size of the merged note section. */ enum strip_action { @@ -315,6 +320,7 @@ enum command_line_switch OPTION_LOCALIZE_HIDDEN, OPTION_LOCALIZE_SYMBOLS, OPTION_LONG_SECTION_NAMES, + OPTION_MERGE_NOTES, OPTION_NO_CHANGE_WARNINGS, OPTION_ONLY_KEEP_DEBUG, OPTION_PAD_TO, @@ -436,6 +442,7 @@ static struct option copy_options[] = {"localize-symbol", required_argument, 0, 'L'}, {"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-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}, @@ -634,6 +641,7 @@ copy_usage (FILE *stream, int exit_status) --decompress-debug-sections Decompress DWARF debug sections using zlib\n\ --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\ -v --verbose List all object files modified\n\ @<file> Read options from <file>\n\ -V --version Display this program's version number\n\ @@ -1201,6 +1209,20 @@ is_update_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec) return FALSE; } +static bfd_boolean +is_merged_note_section (bfd * abfd, asection * sec) +{ + if (merge_notes + && bfd_get_flavour (abfd) == bfd_target_elf_flavour + && elf_section_data (sec)->this_hdr.sh_type == SHT_NOTE + /* FIXME: We currently only support merging GNU_BUILD_NOTEs. + We should add support for more note types. */ + && elf_section_data (sec)->this_hdr.sh_flags & SHF_GNU_BUILD_NOTE) + return TRUE; + + return FALSE; +} + /* See if a non-group section is being removed. */ static bfd_boolean @@ -1818,6 +1840,255 @@ copy_unknown_object (bfd *ibfd, bfd *obfd) return TRUE; } +/* Merge the notes on SEC, removing redundant entries. + Returns the new, smaller size of the section upon success. */ + +static bfd_size_type +merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte * contents) +{ + Elf_Internal_Note * pnotes_end; + Elf_Internal_Note * pnotes; + Elf_Internal_Note * pnote; + bfd_size_type remain = size; + unsigned version_notes_seen = 0; + bfd_boolean duplicate_found = FALSE; + const char * err = NULL; + bfd_byte * in = contents; + + /* 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)); + while (remain >= 12) + { + pnote->namesz = (bfd_get_32 (abfd, in ) + 3) & ~3; + pnote->descsz = (bfd_get_32 (abfd, in + 4) + 3) & ~3; + pnote->type = bfd_get_32 (abfd, in + 8); + + if (pnote->type != NT_GNU_BUILD_ATTRIBUTE_OPEN + && pnote->type != NT_GNU_BUILD_ATTRIBUTE_FUNC) + { + err = _("corrupt GNU build attribute note: wrong note type"); + goto done; + } + + if (pnote->namesz + pnote->descsz + 12 > remain) + { + err = _("corrupt GNU build attribute note: note too big"); + goto done; + } + + if (pnote->namesz < 2) + { + err = _("corrupt GNU build attribute note: name too small"); + goto done; + } + + if (pnote->descsz != 0 + && pnote->descsz != 4 + && pnote->descsz != 8) + { + err = _("corrupt GNU build attribute note: bad description size"); + goto done; + } + + pnote->namedata = (char *)(in + 12); + pnote->descdata = (char *)(in + 12 + pnote->namesz); + + remain -= 12 + pnote->namesz + pnote->descsz; + in += 12 + pnote->namesz + pnote->descsz; + + if (pnote->namesz > 1 && pnote->namedata[1] == GNU_BUILD_ATTRIBUTE_VERSION) + ++ version_notes_seen; + pnote ++; + } + + pnotes_end = pnote; + + /* Check that the notes are valid. */ + if (remain != 0) + { + err = _("corrupt GNU build attribute notes: data at end"); + goto done; + } + + if (version_notes_seen == 0) + { + err = _("bad GNU build attribute notes: no version note"); + goto done; + } + + /* Merging is only needed if there is more than one version note... */ + if (version_notes_seen == 1) + goto done; + + /* The first note should be the first version note. */ + if (pnotes[0].namedata[1] != GNU_BUILD_ATTRIBUTE_VERSION) + { + err = _("bad GNU build attribute notes: first note not version note"); + goto done; + } + + if (pnotes[0].namedata[0] != GNU_BUILD_ATTRIBUTE_TYPE_STRING + || strcmp (pnotes[0].namedata + 2, "1") != 0) + { + err = _("bad GNU build attribute notes: version note not v1"); + goto done; + } + + /* Now merge the notes. The rules are: + 1. Preserve the ordering of the notes. + 2. Preserve any NT_GNU_BUILD_ATTRIBUTE_FUNC notes. + 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 + 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 + address to which it refers. */ + for (pnote = pnotes + 1; pnote < pnotes_end; pnote ++) + { + Elf_Internal_Note * back; + Elf_Internal_Note * prev_open = NULL; + + if (pnote->type == NT_GNU_BUILD_ATTRIBUTE_FUNC) + continue; + + /* Scan for duplicates. Clear the type field of any found - but do not + delete them just yet. */ + for (back = pnote - 1; back >= pnotes; back --) + { + if (back->descsz > 0 + && back->type != NT_GNU_BUILD_ATTRIBUTE_FUNC + && prev_open == NULL) + prev_open = back; + + if (back->type == pnote->type + && back->namedata[1] == pnote->namedata[1]) + { + if (back->namesz == pnote->namesz + && memcmp (back->namedata, pnote->namedata, back->namesz) == 0) + { + duplicate_found = TRUE; + pnote->type = 0; + break; + } + + /* If we have found an attribute match then stop searching backwards. */ + if (! ISPRINT (back->namedata[1]) + || 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 + then we must make sure that version note is also preserved. */ + if (pnote->descsz == 0 + && prev_open != NULL + && prev_open->type == 0) + prev_open->type = NT_GNU_BUILD_ATTRIBUTE_FUNC; + + break; + } + } + } + } + + if (duplicate_found) + { + bfd_byte * new_contents; + bfd_byte * old; + bfd_byte * new; + bfd_size_type new_size; + arelent ** relpp = NULL; + long relsize; + long relcount = 0; + + relsize = bfd_get_reloc_upper_bound (abfd, sec); + if (relsize > 0) + { + /* If there are relocs associated with this section then we may + have to adjust them as well, as we remove notes. */ + relpp = (arelent **) xmalloc (relsize); + relcount = bfd_canonicalize_reloc (abfd, sec, relpp, isympp); + if (relcount < 0) + /* Do not bother complaining here - copy_relocations_in_section + will do that for us. */ + relcount = 0; + } + + /* Eliminate the duplicates. */ + new = new_contents = xmalloc (size); + for (pnote = pnotes, old = contents; + pnote < pnotes_end; + pnote ++) + { + bfd_size_type note_size = 12 + pnote->namesz + pnote->descsz; + + if (pnote->type == 0) + { + if (relcount > 0) + { + arelent ** rel; + + /* If there is a reloc at the current offset, delete it. + Adjust the location of any relocs above the current + location downwards by the size of the note being deleted. + FIXME: We could optimize this loop by retaining a pointer to + the last reloc below the current note. */ + for (rel = relpp; rel < relpp + relcount; rel ++) + { + if ((* rel)->howto == NULL) + continue; + if ((* rel)->address < (bfd_vma) (new - new_contents)) + continue; + if ((* rel)->address >= (bfd_vma) ((new + note_size) - new_contents)) + (* rel)->address -= note_size; + else + (* rel)->howto = NULL; + } + } + } + else + { + memcpy (new, old, note_size); + new += note_size; + } + + old += note_size; + } + + new_size = new - new_contents; + memcpy (contents, new_contents, new_size); + size = new_size; + free (new_contents); + + if (relcount > 0) + { + arelent ** rel; + + for (rel = relpp; rel < relpp + relcount; rel ++) + if ((* rel)->howto == NULL) + { + /* Delete eliminated relocs. + FIXME: There are better ways to do this. */ + memmove (rel, rel + 1, ((relcount - (rel - relpp)) - 1) * sizeof (* rel)); + relcount --; + } + bfd_set_reloc (abfd, sec, relpp, relcount); + } + } + + done: + if (err) + { + bfd_set_error (bfd_error_bad_value); + bfd_nonfatal_message (NULL, abfd, sec, err); + status = 1; + } + + free (pnotes); + return size; +} + /* Copy object file IBFD onto OBFD. Returns TRUE upon success, FALSE otherwise. */ @@ -1827,6 +2098,7 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) bfd_vma start; long symcount; asection **osections = NULL; + asection *osec; asection *gnu_debuglink_section = NULL; bfd_size_type *gaps = NULL; bfd_size_type max_gap = 0; @@ -2127,8 +2399,6 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) pupdate != NULL; pupdate = pupdate->next) { - asection *osec; - pupdate->section = bfd_get_section_by_name (ibfd, pupdate->name); if (pupdate->section == NULL) { @@ -2145,16 +2415,63 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) } } + if (merge_notes) + { + /* This palaver is necessary because we must set the output + section size first, before its contents are ready. */ + osec = bfd_get_section_by_name (ibfd, GNU_BUILD_ATTRS_SECTION_NAME); + if (osec && is_merged_note_section (ibfd, osec)) + { + bfd_size_type size; + + size = bfd_get_section_size (osec); + if (size == 0) + { + bfd_nonfatal_message (NULL, ibfd, osec, _("warning: note section is empty")); + merge_notes = FALSE; + } + else if (! bfd_get_full_section_contents (ibfd, osec, & merged_notes)) + { + bfd_nonfatal_message (NULL, ibfd, osec, _("warning: could not load note section")); + free (merged_notes); + merged_notes = NULL; + merge_notes = FALSE; + } + else + { + merged_size = merge_gnu_build_notes (ibfd, osec, size, merged_notes); + if (merged_size == size) + { + /* Merging achieves nothing. */ + free (merged_notes); + merged_notes = NULL; + merge_notes = FALSE; + merged_size = 0; + } + else + { + if (osec->output_section == NULL + || ! bfd_set_section_size (obfd, osec->output_section, merged_size)) + { + bfd_nonfatal_message (NULL, obfd, osec, _("warning: failed to set merged notes size")); + free (merged_notes); + merged_notes = NULL; + merge_notes = FALSE; + merged_size = 0; + } + } + } + } + } + if (dump_sections != NULL) { struct section_add * pdump; for (pdump = dump_sections; pdump != NULL; pdump = pdump->next) { - asection * sec; - - sec = bfd_get_section_by_name (ibfd, pdump->name); - if (sec == NULL) + osec = bfd_get_section_by_name (ibfd, pdump->name); + if (osec == NULL) { bfd_nonfatal_message (NULL, ibfd, NULL, _("can't dump section '%s' - it does not exist"), @@ -2162,17 +2479,17 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) continue; } - if ((bfd_get_section_flags (ibfd, sec) & SEC_HAS_CONTENTS) == 0) + if ((bfd_get_section_flags (ibfd, osec) & SEC_HAS_CONTENTS) == 0) { - bfd_nonfatal_message (NULL, ibfd, sec, + bfd_nonfatal_message (NULL, ibfd, osec, _("can't dump section - it has no contents")); continue; } - bfd_size_type size = bfd_get_section_size (sec); + bfd_size_type size = bfd_get_section_size (osec); if (size == 0) { - bfd_nonfatal_message (NULL, ibfd, sec, + bfd_nonfatal_message (NULL, ibfd, osec, _("can't dump section - it is empty")); continue; } @@ -2187,7 +2504,7 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) } bfd_byte * contents = xmalloc (size); - if (bfd_get_section_contents (ibfd, sec, contents, 0, size)) + if (bfd_get_section_contents (ibfd, osec, contents, 0, size)) { if (fwrite (contents, 1, size, f) != size) { @@ -2198,7 +2515,7 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) } } else - bfd_nonfatal_message (NULL, ibfd, sec, + bfd_nonfatal_message (NULL, ibfd, osec, _("could not retrieve section contents")); fclose (f); @@ -2236,7 +2553,6 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) { bfd_vma debuglink_vma; asection * highest_section; - asection * sec; /* The PE spec requires that all sections be adjacent and sorted in ascending order of VMA. It also specifies that debug @@ -2248,13 +2564,13 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) VMA which makes it contiguous with other debug sections. So walk the current section list, find the section with the highest VMA and start the debuglink section after that one. */ - for (sec = obfd->sections, highest_section = NULL; - sec != NULL; - sec = sec->next) - if (sec->vma > 0 + for (osec = obfd->sections, highest_section = NULL; + osec != NULL; + osec = osec->next) + if (osec->vma > 0 && (highest_section == NULL - || sec->vma > highest_section->vma)) - highest_section = sec; + || osec->vma > highest_section->vma)) + highest_section = osec; if (highest_section) debuglink_vma = BFD_ALIGN (highest_section->vma @@ -2442,8 +2758,6 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) pupdate != NULL; pupdate = pupdate->next) { - asection *osec; - osec = pupdate->section->output_section; if (! bfd_set_section_contents (obfd, osec, pupdate->contents, 0, pupdate->size)) @@ -2454,6 +2768,24 @@ copy_object (bfd *ibfd, bfd *obfd, const bfd_arch_info_type *input_arch) } } + if (merge_notes) + { + osec = bfd_get_section_by_name (obfd, GNU_BUILD_ATTRS_SECTION_NAME); + if (osec && is_merged_note_section (obfd, osec)) + { + if (! bfd_set_section_contents (obfd, osec, merged_notes, 0, merged_size)) + { + bfd_nonfatal_message (NULL, obfd, osec, _("error: failed to copy merged notes into output")); + return FALSE; + } + } + else + bfd_nonfatal_message (NULL, obfd, osec, _("ICE: lost merged note section")); + free (merged_notes); + merged_notes = NULL; + merge_notes = FALSE; + } + if (gnu_debuglink_filename != NULL) { if (! bfd_fill_in_gnu_debuglink_section @@ -3179,7 +3511,7 @@ setup_section (bfd *ibfd, sec_ptr isection, void *obfdarg) /* Return TRUE if input section ISECTION should be skipped. */ static bfd_boolean -skip_section (bfd *ibfd, sec_ptr isection) +skip_section (bfd *ibfd, sec_ptr isection, bfd_boolean skip_copy) { sec_ptr osection; bfd_size_type size; @@ -3199,6 +3531,11 @@ skip_section (bfd *ibfd, sec_ptr isection) if (is_update_section (ibfd, isection)) return TRUE; + /* When merging a note section we skip the copying of the contents, + but not the copying of the relocs associated with the contents. */ + if (skip_copy && is_merged_note_section (ibfd, isection)) + return TRUE; + flags = bfd_get_section_flags (ibfd, isection); if ((flags & SEC_GROUP) != 0) return TRUE; @@ -3265,7 +3602,7 @@ copy_relocations_in_section (bfd *ibfd, sec_ptr isection, void *obfdarg) long relcount; sec_ptr osection; - if (skip_section (ibfd, isection)) + if (skip_section (ibfd, isection, FALSE)) return; osection = isection->output_section; @@ -3354,7 +3691,7 @@ copy_section (bfd *ibfd, sec_ptr isection, void *obfdarg) sec_ptr osection; bfd_size_type size; - if (skip_section (ibfd, isection)) + if (skip_section (ibfd, isection, TRUE)) return; osection = isection->output_section; @@ -4010,7 +4347,7 @@ copy_main (int argc, char *argv[]) struct stat statbuf; const bfd_arch_info_type *input_arch = NULL; - while ((c = getopt_long (argc, argv, "b:B:i:I:j:K:N:s:O:d:F:L:G:R:SpgxXHhVvW:wDU", + while ((c = getopt_long (argc, argv, "b:B:i:I:j:K:MN:s:O:d:F:L:G:R:SpgxXHhVvW:wDU", copy_options, (int *) 0)) != EOF) { switch (c) @@ -4104,6 +4441,10 @@ copy_main (int argc, char *argv[]) add_specific_symbol (optarg, keep_specific_htab); break; + case 'M': + merge_notes = TRUE; + break; + case 'N': add_specific_symbol (optarg, strip_specific_htab); break; |