From 88305e1b9f4592a2ab129015ce409fcc16613ebb Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Wed, 28 Jun 2017 14:52:12 +0100 Subject: [PATCH] Add support for version 2 of the GNU Build Attribute note specification. * objcopy.c (merge_gnu_build_notes): Add support for version 2 notes. * readelf.c (print_gnu_build_attribute_name): Likewise. --- binutils/ChangeLog | 5 ++++ binutils/objcopy.c | 68 ++++++++++++++++++++++++++++++---------------- binutils/readelf.c | 22 ++++++++++----- 3 files changed, 65 insertions(+), 30 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index da3cf67a95..c5b8f19b31 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,8 @@ +2017-06-28 Nick Clifton + + * objcopy.c (merge_gnu_build_notes): Add support for version 2 notes. + * readelf.c (print_gnu_build_attribute_name): Likewise. + 2017-06-28 Maciej W. Rozycki Matthew Fortune diff --git a/binutils/objcopy.c b/binutils/objcopy.c index 42c7775234..4f481905ee 100644 --- a/binutils/objcopy.c +++ b/binutils/objcopy.c @@ -1915,10 +1915,13 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte Elf_Internal_Note * pnotes; Elf_Internal_Note * pnote; bfd_size_type remain = size; - unsigned version_notes_seen = 0; + unsigned version_1_seen = 0; + unsigned version_2_seen = 0; bfd_boolean duplicate_found = FALSE; const char * err = NULL; bfd_byte * in = contents; + int attribute_type_byte; + int val_start; /* Make a copy of the notes. Minimum size of a note is 12 bytes. */ @@ -1968,8 +1971,18 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte goto done; } - if (pnote->namesz > 1 && pnote->namedata[1] == GNU_BUILD_ATTRIBUTE_VERSION) - ++ version_notes_seen; + if (pnote->namesz > 2 + && pnote->namedata[0] == '$' + && pnote->namedata[1] == GNU_BUILD_ATTRIBUTE_VERSION + && pnote->namedata[2] == '1') + ++ version_1_seen; + else if (pnote->namesz > 4 + && pnote->namedata[0] == 'G' + && pnote->namedata[1] == 'A' + && pnote->namedata[2] == '$' + && pnote->namedata[3] == GNU_BUILD_ATTRIBUTE_VERSION + && pnote->namedata[4] == '2') + ++ version_2_seen; pnote ++; } @@ -1978,34 +1991,36 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte /* Check that the notes are valid. */ if (remain != 0) { - err = _("corrupt GNU build attribute notes: data at end"); + err = _("corrupt GNU build attribute notes: excess data at end"); goto done; } - if (version_notes_seen == 0) + if (version_1_seen == 0 && version_2_seen == 0) { - err = _("bad GNU build attribute notes: no version note"); + err = _("bad GNU build attribute notes: no known versions detected"); + goto done; + } + + if (version_1_seen > 0 && version_2_seen > 0) + { + err = _("bad GNU build attribute notes: multiple different versions"); goto done; } /* Merging is only needed if there is more than one version note... */ - if (version_notes_seen == 1) + if (version_1_seen == 1 || version_2_seen == 1) goto done; + attribute_type_byte = version_1_seen ? 1 : 3; + val_start = attribute_type_byte + 1; + /* The first note should be the first version note. */ - if (pnotes[0].namedata[1] != GNU_BUILD_ATTRIBUTE_VERSION) + if (pnotes[0].namedata[attribute_type_byte] != 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 - || pnotes[0].namedata[2] != '1') - { - 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. @@ -2037,9 +2052,9 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte prev_open = back; if (back->type == pnote->type - && back->namedata[1] == pnote->namedata[1]) + && back->namedata[attribute_type_byte] == pnote->namedata[attribute_type_byte]) { - if (back->namedata[1] == GNU_BUILD_ATTRIBUTE_STACK_SIZE) + if (back->namedata[attribute_type_byte] == GNU_BUILD_ATTRIBUTE_STACK_SIZE) { unsigned char * name; unsigned long note_val; @@ -2048,22 +2063,28 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte unsigned int bytes; unsigned long byte; - for (shift = 0, note_val = 0, bytes = pnote->namesz - 2, name = (unsigned char *) pnote->namedata + 2; + for (shift = 0, note_val = 0, + bytes = pnote->namesz - val_start, + name = (unsigned char *) pnote->namedata + val_start; 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; + + for (shift = 0, back_val = 0, + bytes = back->namesz - val_start, + name = (unsigned char *) back->namedata + val_start; bytes--;) { byte = (* name ++) & 0xff; back_val |= byte << shift; shift += 8; } + back_val += note_val; - if (num_bytes (back_val) >= back->namesz - 2) + if (num_bytes (back_val) >= back->namesz - val_start) { /* We have a problem - the new value requires more bytes of storage in the name field than are available. Currently @@ -2071,8 +2092,9 @@ merge_gnu_build_notes (bfd * abfd, asection * sec, bfd_size_type size, bfd_byte notes. */ continue; } + /* Write the new val into back. */ - name = (unsigned char *) back->namedata + 2; + name = (unsigned char *) back->namedata + val_start; while (name < (unsigned char *) back->namedata + back->namesz) { byte = back_val & 0xff; @@ -2096,9 +2118,9 @@ 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]) + if (! ISPRINT (back->namedata[attribute_type_byte]) /* Names are NUL terminated, so this is safe. */ - || strcmp (back->namedata + 2, pnote->namedata + 2) == 0) + || strcmp (back->namedata + val_start, pnote->namedata + val_start) == 0) { /* Since we are keeping this note we must check to see if its description refers back to an earlier OPEN version note. If so diff --git a/binutils/readelf.c b/binutils/readelf.c index edfe652a7a..df7e8c3406 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -481,7 +481,7 @@ print_symbol (signed int width, const char *symbol) if (width < 0) { - /* Keep the width positive. This also helps. */ + /* Keep the width positive. This helps the code below. */ width = - width; extra_padding = TRUE; } @@ -17185,7 +17185,7 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote) const char * expected_types; const char * name = pnote->namedata; const char * text; - int left; + signed int left; if (name == NULL || pnote->namesz < 2) { @@ -17194,6 +17194,16 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote) return FALSE; } + left = 20; + + /* Version 2 of the spec adds a "GA" prefix to the name field. */ + if (name[0] == 'G' && name[1] == 'A') + { + printf ("GA"); + name += 2; + left -= 2; + } + switch ((name_type = * name)) { case GNU_BUILD_ATTRIBUTE_TYPE_NUMERIC: @@ -17201,6 +17211,7 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote) case GNU_BUILD_ATTRIBUTE_TYPE_BOOL_TRUE: case GNU_BUILD_ATTRIBUTE_TYPE_BOOL_FALSE: printf ("%c", * name); + left --; break; default: error (_("unrecognised attribute type in name field: %d\n"), name_type); @@ -17208,7 +17219,6 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote) return FALSE; } - left = 19; ++ name; text = NULL; @@ -17268,6 +17278,7 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote) else { static char tmpbuf [128]; + error (_("unrecognised byte in name field: %d\n"), * name); sprintf (tmpbuf, _(""), * name); text = tmpbuf; @@ -17278,10 +17289,7 @@ print_gnu_build_attribute_name (Elf_Internal_Note * pnote) } if (text) - { - printf ("%s", text); - left -= strlen (text); - } + left -= printf ("%s", text); if (strchr (expected_types, name_type) == NULL) warn (_("attribute does not have an expected type (%c)\n"), name_type);