+ if (bfd_bread (cbuf, (bfd_size_type) tocopy, ibfd)
+ != (bfd_size_type) tocopy)
+ {
+ bfd_nonfatal_message (NULL, ibfd, NULL, NULL);
+ free (cbuf);
+ return FALSE;
+ }
+
+ if (bfd_bwrite (cbuf, (bfd_size_type) tocopy, obfd)
+ != (bfd_size_type) tocopy)
+ {
+ bfd_nonfatal_message (NULL, obfd, NULL, NULL);
+ free (cbuf);
+ return FALSE;
+ }
+
+ ncopied += tocopy;
+ }
+
+ /* We should at least to be able to read it back when copying an
+ unknown object in an archive. */
+ chmod (bfd_get_filename (obfd), buf.st_mode | S_IRUSR);
+ free (cbuf);
+ 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. */
+
+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 *) xcalloc ((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->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 ++;
+ }
+
+ 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
+ || 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.
+ 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. 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
+ 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->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)
+ {
+ duplicate_found = TRUE;
+ pnote->type = 0;
+ break;
+ }
+
+ /* 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 version 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;