+/* Returns TRUE if section A matches section B.
+ Names, addresses and links may be different, but everything else
+ should be the same. */
+
+static bfd_boolean
+section_match (const Elf_Internal_Shdr * a,
+ const Elf_Internal_Shdr * b)
+{
+ return
+ a->sh_type == b->sh_type
+ && (a->sh_flags & ~ SHF_INFO_LINK)
+ == (b->sh_flags & ~ SHF_INFO_LINK)
+ && a->sh_addralign == b->sh_addralign
+ && a->sh_size == b->sh_size
+ && a->sh_entsize == b->sh_entsize
+ /* FIXME: Check sh_addr ? */
+ ;
+}
+
+/* Find a section in OBFD that has the same characteristics
+ as IHEADER. Return the index of this section or SHN_UNDEF if
+ none can be found. Check's section HINT first, as this is likely
+ to be the correct section. */
+
+static unsigned int
+find_link (const bfd * obfd, const Elf_Internal_Shdr * iheader, const unsigned int hint)
+{
+ Elf_Internal_Shdr ** oheaders = elf_elfsections (obfd);
+ unsigned int i;
+
+ BFD_ASSERT (iheader != NULL);
+
+ /* See PR 20922 for a reproducer of the NULL test. */
+ if (oheaders[hint] != NULL
+ && section_match (oheaders[hint], iheader))
+ return hint;
+
+ for (i = 1; i < elf_numsections (obfd); i++)
+ {
+ Elf_Internal_Shdr * oheader = oheaders[i];
+
+ if (oheader == NULL)
+ continue;
+ if (section_match (oheader, iheader))
+ /* FIXME: Do we care if there is a potential for
+ multiple matches ? */
+ return i;
+ }
+
+ return SHN_UNDEF;
+}
+
+/* PR 19938: Attempt to set the ELF section header fields of an OS or
+ Processor specific section, based upon a matching input section.
+ Returns TRUE upon success, FALSE otherwise. */
+
+static bfd_boolean
+copy_special_section_fields (const bfd *ibfd,
+ bfd *obfd,
+ const Elf_Internal_Shdr *iheader,
+ Elf_Internal_Shdr *oheader,
+ const unsigned int secnum)
+{
+ const struct elf_backend_data *bed = get_elf_backend_data (obfd);
+ const Elf_Internal_Shdr **iheaders = (const Elf_Internal_Shdr **) elf_elfsections (ibfd);
+ bfd_boolean changed = FALSE;
+ unsigned int sh_link;
+
+ if (oheader->sh_type == SHT_NOBITS)
+ {
+ /* This is a feature for objcopy --only-keep-debug:
+ When a section's type is changed to NOBITS, we preserve
+ the sh_link and sh_info fields so that they can be
+ matched up with the original.
+
+ Note: Strictly speaking these assignments are wrong.
+ The sh_link and sh_info fields should point to the
+ relevent sections in the output BFD, which may not be in
+ the same location as they were in the input BFD. But
+ the whole point of this action is to preserve the
+ original values of the sh_link and sh_info fields, so
+ that they can be matched up with the section headers in
+ the original file. So strictly speaking we may be
+ creating an invalid ELF file, but it is only for a file
+ that just contains debug info and only for sections
+ without any contents. */
+ if (oheader->sh_link == 0)
+ oheader->sh_link = iheader->sh_link;
+ if (oheader->sh_info == 0)
+ oheader->sh_info = iheader->sh_info;
+ return TRUE;
+ }
+
+ /* Allow the target a chance to decide how these fields should be set. */
+ if (bed->elf_backend_copy_special_section_fields != NULL
+ && bed->elf_backend_copy_special_section_fields
+ (ibfd, obfd, iheader, oheader))
+ return TRUE;
+
+ /* We have an iheader which might match oheader, and which has non-zero
+ sh_info and/or sh_link fields. Attempt to follow those links and find
+ the section in the output bfd which corresponds to the linked section
+ in the input bfd. */
+ if (iheader->sh_link != SHN_UNDEF)
+ {
+ /* See PR 20931 for a reproducer. */
+ if (iheader->sh_link >= elf_numsections (ibfd))
+ {
+ (* _bfd_error_handler)
+ /* xgettext:c-format */
+ (_("%B: Invalid sh_link field (%d) in section number %d"),
+ ibfd, iheader->sh_link, secnum);
+ return FALSE;
+ }
+
+ sh_link = find_link (obfd, iheaders[iheader->sh_link], iheader->sh_link);
+ if (sh_link != SHN_UNDEF)
+ {
+ oheader->sh_link = sh_link;
+ changed = TRUE;
+ }
+ else
+ /* FIXME: Should we install iheader->sh_link
+ if we could not find a match ? */
+ (* _bfd_error_handler)
+ /* xgettext:c-format */
+ (_("%B: Failed to find link section for section %d"), obfd, secnum);
+ }
+
+ if (iheader->sh_info)
+ {
+ /* The sh_info field can hold arbitrary information, but if the
+ SHF_LINK_INFO flag is set then it should be interpreted as a
+ section index. */
+ if (iheader->sh_flags & SHF_INFO_LINK)
+ {
+ sh_link = find_link (obfd, iheaders[iheader->sh_info],
+ iheader->sh_info);
+ if (sh_link != SHN_UNDEF)
+ oheader->sh_flags |= SHF_INFO_LINK;
+ }
+ else
+ /* No idea what it means - just copy it. */
+ sh_link = iheader->sh_info;
+
+ if (sh_link != SHN_UNDEF)
+ {
+ oheader->sh_info = sh_link;
+ changed = TRUE;
+ }
+ else
+ (* _bfd_error_handler)
+ /* xgettext:c-format */
+ (_("%B: Failed to find info section for section %d"), obfd, secnum);
+ }
+
+ return changed;
+}
+