+/* For a given OFFSET in SEC, return the delta to the new location
+ after .eh_frame editing. */
+
+static bfd_signed_vma
+offset_adjust (bfd_vma offset, const asection *sec)
+{
+ struct eh_frame_sec_info *sec_info
+ = (struct eh_frame_sec_info *) elf_section_data (sec)->sec_info;
+ unsigned int lo, hi, mid;
+ struct eh_cie_fde *ent = NULL;
+ bfd_signed_vma delta;
+
+ lo = 0;
+ hi = sec_info->count;
+ if (hi == 0)
+ return 0;
+
+ while (lo < hi)
+ {
+ mid = (lo + hi) / 2;
+ ent = &sec_info->entry[mid];
+ if (offset < ent->offset)
+ hi = mid;
+ else if (mid + 1 >= hi)
+ break;
+ else if (offset >= ent[1].offset)
+ lo = mid + 1;
+ else
+ break;
+ }
+
+ if (!ent->removed)
+ delta = (bfd_vma) ent->new_offset - (bfd_vma) ent->offset;
+ else if (ent->cie && ent->u.cie.merged)
+ {
+ struct eh_cie_fde *cie = ent->u.cie.u.merged_with;
+ delta = ((bfd_vma) cie->new_offset + cie->u.cie.u.sec->output_offset
+ - (bfd_vma) ent->offset - sec->output_offset);
+ }
+ else
+ {
+ /* Is putting the symbol on the next entry best for a deleted
+ CIE/FDE? */
+ struct eh_cie_fde *last = sec_info->entry + sec_info->count;
+ delta = ((bfd_vma) next_cie_fde_offset (ent, last, sec)
+ - (bfd_vma) ent->offset);
+ return delta;
+ }
+
+ /* Account for editing within this CIE/FDE. */
+ offset -= ent->offset;
+ if (ent->cie)
+ {
+ unsigned int extra
+ = ent->add_augmentation_size + ent->u.cie.add_fde_encoding;
+ if (extra == 0
+ || offset <= 9u + ent->u.cie.aug_str_len)
+ return delta;
+ delta += extra;
+ if (offset <= 9u + ent->u.cie.aug_str_len + ent->u.cie.aug_data_len)
+ return delta;
+ delta += extra;
+ }
+ else
+ {
+ unsigned int ptr_size, width, extra = ent->add_augmentation_size;
+ if (offset <= 12 || extra == 0)
+ return delta;
+ ptr_size = (get_elf_backend_data (sec->owner)
+ ->elf_backend_eh_frame_address_size (sec->owner, sec));
+ width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+ if (offset <= 8 + 2 * width)
+ return delta;
+ delta += extra;
+ }
+
+ return delta;
+}
+
+/* Adjust a global symbol defined in .eh_frame, so that it stays
+ relative to its original CIE/FDE. It is assumed that a symbol
+ defined at the beginning of a CIE/FDE belongs to that CIE/FDE
+ rather than marking the end of the previous CIE/FDE. This matters
+ when a CIE is merged with a previous CIE, since the symbol is
+ moved to the merged CIE. */
+
+bfd_boolean
+_bfd_elf_adjust_eh_frame_global_symbol (struct elf_link_hash_entry *h,
+ void *arg ATTRIBUTE_UNUSED)
+{
+ asection *sym_sec;
+ bfd_signed_vma delta;
+
+ if (h->root.type != bfd_link_hash_defined
+ && h->root.type != bfd_link_hash_defweak)
+ return TRUE;
+
+ sym_sec = h->root.u.def.section;
+ if (sym_sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME
+ || elf_section_data (sym_sec)->sec_info == NULL)
+ return TRUE;
+
+ delta = offset_adjust (h->root.u.def.value, sym_sec);
+ h->root.u.def.value += delta;
+
+ return TRUE;
+}
+
+/* The same for all local symbols defined in .eh_frame. Returns true
+ if any symbol was changed. */
+
+static int
+adjust_eh_frame_local_symbols (const asection *sec,
+ struct elf_reloc_cookie *cookie)
+{
+ unsigned int shndx;
+ Elf_Internal_Sym *sym;
+ Elf_Internal_Sym *end_sym;
+ int adjusted = 0;
+
+ shndx = elf_section_data (sec)->this_idx;
+ end_sym = cookie->locsyms + cookie->locsymcount;
+ for (sym = cookie->locsyms + 1; sym < end_sym; ++sym)
+ if (sym->st_info <= ELF_ST_INFO (STB_LOCAL, STT_OBJECT)
+ && sym->st_shndx == shndx)
+ {
+ bfd_signed_vma delta = offset_adjust (sym->st_value, sec);
+
+ if (delta != 0)
+ {
+ adjusted = 1;
+ sym->st_value += delta;
+ }
+ }
+ return adjusted;
+}
+