+ sec->sec_info_type = SEC_INFO_TYPE_EH_FRAME;
+ if (hdr_info->merge_cies)
+ {
+ sec_info->cies = local_cies;
+ local_cies = NULL;
+ }
+ goto success;
+
+ free_no_table:
+ (*info->callbacks->einfo)
+ (_("%P: error in %B(%A); no .eh_frame_hdr table will be created.\n"),
+ abfd, sec);
+ hdr_info->table = FALSE;
+ if (sec_info)
+ free (sec_info);
+ success:
+ if (ehbuf)
+ free (ehbuf);
+ if (local_cies)
+ free (local_cies);
+#undef REQUIRE
+}
+
+/* Finish a pass over all .eh_frame sections. */
+
+void
+_bfd_elf_end_eh_frame_parsing (struct bfd_link_info *info)
+{
+ struct eh_frame_hdr_info *hdr_info;
+
+ hdr_info = &elf_hash_table (info)->eh_info;
+ hdr_info->parsed_eh_frames = TRUE;
+}
+
+/* Mark all relocations against CIE or FDE ENT, which occurs in
+ .eh_frame section SEC. COOKIE describes the relocations in SEC;
+ its "rel" field can be changed freely. */
+
+static bfd_boolean
+mark_entry (struct bfd_link_info *info, asection *sec,
+ struct eh_cie_fde *ent, elf_gc_mark_hook_fn gc_mark_hook,
+ struct elf_reloc_cookie *cookie)
+{
+ /* FIXME: octets_per_byte. */
+ for (cookie->rel = cookie->rels + ent->reloc_index;
+ cookie->rel < cookie->relend
+ && cookie->rel->r_offset < ent->offset + ent->size;
+ cookie->rel++)
+ if (!_bfd_elf_gc_mark_reloc (info, sec, gc_mark_hook, cookie))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Mark all the relocations against FDEs that relate to code in input
+ section SEC. The FDEs belong to .eh_frame section EH_FRAME, whose
+ relocations are described by COOKIE. */
+
+bfd_boolean
+_bfd_elf_gc_mark_fdes (struct bfd_link_info *info, asection *sec,
+ asection *eh_frame, elf_gc_mark_hook_fn gc_mark_hook,
+ struct elf_reloc_cookie *cookie)
+{
+ struct eh_cie_fde *fde, *cie;
+
+ for (fde = elf_fde_list (sec); fde; fde = fde->u.fde.next_for_section)
+ {
+ if (!mark_entry (info, eh_frame, fde, gc_mark_hook, cookie))
+ return FALSE;
+
+ /* At this stage, all cie_inf fields point to local CIEs, so we
+ can use the same cookie to refer to them. */
+ cie = fde->u.fde.cie_inf;
+ if (!cie->u.cie.gc_mark)
+ {
+ cie->u.cie.gc_mark = 1;
+ if (!mark_entry (info, eh_frame, cie, gc_mark_hook, cookie))
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/* Input section SEC of ABFD is an .eh_frame section that contains the
+ CIE described by CIE_INF. Return a version of CIE_INF that is going
+ to be kept in the output, adding CIE_INF to the output if necessary.
+
+ HDR_INFO is the .eh_frame_hdr information and COOKIE describes the
+ relocations in REL. */
+
+static struct eh_cie_fde *
+find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec,
+ struct eh_frame_hdr_info *hdr_info,
+ struct elf_reloc_cookie *cookie,
+ struct eh_cie_fde *cie_inf)
+{
+ unsigned long r_symndx;
+ struct cie *cie, *new_cie;
+ Elf_Internal_Rela *rel;
+ void **loc;
+
+ /* Use CIE_INF if we have already decided to keep it. */
+ if (!cie_inf->removed)
+ return cie_inf;
+
+ /* If we have merged CIE_INF with another CIE, use that CIE instead. */
+ if (cie_inf->u.cie.merged)
+ return cie_inf->u.cie.u.merged_with;
+
+ cie = cie_inf->u.cie.u.full_cie;
+
+ /* Assume we will need to keep CIE_INF. */
+ cie_inf->removed = 0;
+ cie_inf->u.cie.u.sec = sec;
+
+ /* If we are not merging CIEs, use CIE_INF. */
+ if (cie == NULL)
+ return cie_inf;
+
+ if (cie->per_encoding != DW_EH_PE_omit)
+ {
+ bfd_boolean per_binds_local;
+
+ /* Work out the address of personality routine, either as an absolute
+ value or as a symbol. */
+ rel = cookie->rels + cie->personality.reloc_index;
+ memset (&cie->personality, 0, sizeof (cie->personality));
+#ifdef BFD64
+ if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64)
+ r_symndx = ELF64_R_SYM (rel->r_info);
+ else
+#endif
+ r_symndx = ELF32_R_SYM (rel->r_info);
+ if (r_symndx >= cookie->locsymcount
+ || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+ {
+ struct elf_link_hash_entry *h;
+
+ r_symndx -= cookie->extsymoff;
+ h = cookie->sym_hashes[r_symndx];
+
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ cie->personality.h = h;
+ per_binds_local = SYMBOL_REFERENCES_LOCAL (info, h);
+ }
+ else
+ {
+ Elf_Internal_Sym *sym;
+ asection *sym_sec;
+
+ sym = &cookie->locsyms[r_symndx];
+ sym_sec = bfd_section_from_elf_index (abfd, sym->st_shndx);
+ if (sym_sec == NULL)
+ return cie_inf;
+
+ if (sym_sec->kept_section != NULL)
+ sym_sec = sym_sec->kept_section;
+ if (sym_sec->output_section == NULL)
+ return cie_inf;
+
+ cie->local_personality = 1;
+ cie->personality.val = (sym->st_value
+ + sym_sec->output_offset
+ + sym_sec->output_section->vma);
+ per_binds_local = TRUE;
+ }
+
+ if (per_binds_local
+ && info->shared
+ && (cie->per_encoding & 0x70) == DW_EH_PE_absptr
+ && (get_elf_backend_data (abfd)
+ ->elf_backend_can_make_relative_eh_frame (abfd, info, sec)))
+ {
+ cie_inf->u.cie.make_per_encoding_relative = 1;
+ cie_inf->u.cie.per_encoding_relative = 1;
+ }
+ }
+
+ /* See if we can merge this CIE with an earlier one. */
+ cie->output_sec = sec->output_section;
+ cie_compute_hash (cie);
+ if (hdr_info->cies == NULL)
+ {
+ hdr_info->cies = htab_try_create (1, cie_hash, cie_eq, free);
+ if (hdr_info->cies == NULL)
+ return cie_inf;
+ }
+ loc = htab_find_slot_with_hash (hdr_info->cies, cie, cie->hash, INSERT);
+ if (loc == NULL)
+ return cie_inf;
+
+ new_cie = (struct cie *) *loc;
+ if (new_cie == NULL)
+ {
+ /* Keep CIE_INF and record it in the hash table. */
+ new_cie = (struct cie *) malloc (sizeof (struct cie));
+ if (new_cie == NULL)
+ return cie_inf;
+
+ memcpy (new_cie, cie, sizeof (struct cie));
+ *loc = new_cie;
+ }
+ else
+ {
+ /* Merge CIE_INF with NEW_CIE->CIE_INF. */
+ cie_inf->removed = 1;
+ cie_inf->u.cie.merged = 1;
+ cie_inf->u.cie.u.merged_with = new_cie->cie_inf;
+ if (cie_inf->u.cie.make_lsda_relative)
+ new_cie->cie_inf->u.cie.make_lsda_relative = 1;
+ }
+ return new_cie->cie_inf;
+}
+
+/* This function is called for each input file before the .eh_frame
+ section is relocated. It discards duplicate CIEs and FDEs for discarded
+ functions. The function returns TRUE iff any entries have been
+ deleted. */
+
+bfd_boolean
+_bfd_elf_discard_section_eh_frame
+ (bfd *abfd, struct bfd_link_info *info, asection *sec,
+ bfd_boolean (*reloc_symbol_deleted_p) (bfd_vma, void *),
+ struct elf_reloc_cookie *cookie)
+{
+ struct eh_cie_fde *ent;
+ struct eh_frame_sec_info *sec_info;
+ struct eh_frame_hdr_info *hdr_info;
+ unsigned int ptr_size, offset;
+
+ if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME)
+ return FALSE;
+
+ sec_info = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info;
+ if (sec_info == NULL)
+ return FALSE;
+
+ ptr_size = (get_elf_backend_data (sec->owner)
+ ->elf_backend_eh_frame_address_size (sec->owner, sec));
+
+ hdr_info = &elf_hash_table (info)->eh_info;
+ for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
+ if (ent->size == 4)
+ /* There should only be one zero terminator, on the last input
+ file supplying .eh_frame (crtend.o). Remove any others. */
+ ent->removed = sec->map_head.s != NULL;
+ else if (!ent->cie)
+ {
+ bfd_boolean keep;
+ if ((sec->flags & SEC_LINKER_CREATED) != 0 && cookie->rels == NULL)
+ {
+ unsigned int width
+ = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+ bfd_vma value
+ = read_value (abfd, sec->contents + ent->offset + 8 + width,
+ width, get_DW_EH_PE_signed (ent->fde_encoding));
+ keep = value != 0;
+ }
+ else
+ {
+ cookie->rel = cookie->rels + ent->reloc_index;
+ /* FIXME: octets_per_byte. */
+ BFD_ASSERT (cookie->rel < cookie->relend
+ && cookie->rel->r_offset == ent->offset + 8);
+ keep = !(*reloc_symbol_deleted_p) (ent->offset + 8, cookie);
+ }
+ if (keep)
+ {
+ if (info->shared
+ && (((ent->fde_encoding & 0x70) == DW_EH_PE_absptr
+ && ent->make_relative == 0)
+ || (ent->fde_encoding & 0x70) == DW_EH_PE_aligned))
+ {
+ /* If a shared library uses absolute pointers
+ which we cannot turn into PC relative,
+ don't create the binary search table,
+ since it is affected by runtime relocations. */
+ hdr_info->table = FALSE;
+ (*info->callbacks->einfo)
+ (_("%P: fde encoding in %B(%A) prevents .eh_frame_hdr"
+ " table being created.\n"), abfd, sec);
+ }
+ ent->removed = 0;
+ hdr_info->fde_count++;
+ ent->u.fde.cie_inf = find_merged_cie (abfd, info, sec, hdr_info,
+ cookie, ent->u.fde.cie_inf);
+ }
+ }
+
+ if (sec_info->cies)
+ {
+ free (sec_info->cies);
+ sec_info->cies = NULL;
+ }