Automatic date update in version.in
[deliverable/binutils-gdb.git] / bfd / elf-eh-frame.c
index 1743823891541545a5e380536c56cd25ac7ded90..7e0d63f569d04fa19c0d6b9fe7d17d4696f6123b 100644 (file)
@@ -326,9 +326,9 @@ size_of_output_cie_fde (struct eh_cie_fde *entry)
 /* Return the offset of the FDE or CIE after ENT.  */
 
 static unsigned int
-next_cie_fde_offset (struct eh_cie_fde *ent,
-                    struct eh_cie_fde *last,
-                    asection *sec)
+next_cie_fde_offset (const struct eh_cie_fde *ent,
+                    const struct eh_cie_fde *last,
+                    const asection *sec)
 {
   while (++ent < last)
     {
@@ -757,6 +757,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
 
          strcpy (cie->augmentation, (char *) buf);
          buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
+         this_inf->u.cie.aug_str_len = buf - start - 1;
          ENSURE_NO_RELOCS (buf);
          if (buf[0] == 'e' && buf[1] == 'h')
            {
@@ -845,6 +846,8 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
                    goto free_no_table;
                  }
            }
+         this_inf->u.cie.aug_data_len
+           = buf - start - 1 - this_inf->u.cie.aug_str_len;
 
          /* For shared libraries, try to get rid of as many RELATIVE relocs
             as possible.  */
@@ -1327,6 +1330,143 @@ find_merged_cie (bfd *abfd, struct bfd_link_info *info, asection *sec,
   return new_cie->cie_inf;
 }
 
+/* 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;
+  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;
+}
+
 /* 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
@@ -1342,6 +1482,7 @@ _bfd_elf_discard_section_eh_frame
   struct eh_frame_sec_info *sec_info;
   struct eh_frame_hdr_info *hdr_info;
   unsigned int ptr_size, offset, eh_alignment;
+  int changed;
 
   if (sec->sec_info_type != SEC_INFO_TYPE_EH_FRAME)
     return FALSE;
@@ -1428,6 +1569,7 @@ _bfd_elf_discard_section_eh_frame
      last FDE instead.  For other FDEs we align according to their
      encoding, in order to align FDE address range entries naturally.  */
   offset = 0;
+  changed = 0;
   for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
     if (!ent->removed)
       {
@@ -1447,23 +1589,24 @@ _bfd_elf_discard_section_eh_frame
          }
        offset = (offset + eh_alignment - 1) & -eh_alignment;
        ent->new_offset = offset;
+       if (ent->new_offset != ent->offset)
+         changed = 1;
        offset += size_of_output_cie_fde (ent);
       }
 
-  /* Pad the last FDE out to the output section alignment if there are
-     following sections, in order to ensure no padding between this
-     section and the next.  (Relies on the output section alignment
-     being the maximum of all input sections alignments, which is the
-     case unless someone is overriding alignment via scripts.)  */
   eh_alignment = 4;
-  if (sec->map_head.s != NULL
-      && (sec->map_head.s->size != 4
-         || sec->map_head.s->map_head.s != NULL))
-    eh_alignment = 1 << sec->output_section->alignment_power;
   offset = (offset + eh_alignment - 1) & -eh_alignment;
   sec->rawsize = sec->size;
   sec->size = offset;
-  return offset != sec->rawsize;
+  if (sec->size != sec->rawsize)
+    changed = 1;
+
+  if (changed && adjust_eh_frame_local_symbols (sec, cookie))
+    {
+      Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+      symtab_hdr->contents = (unsigned char *) cookie->locsyms;
+    }
+  return changed;
 }
 
 /* This function is called for .eh_frame_hdr section after
@@ -2394,7 +2537,7 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
 /* Return the width of FDE addresses.  This is the default implementation.  */
 
 unsigned int
-_bfd_elf_eh_frame_address_size (bfd *abfd, asection *sec ATTRIBUTE_UNUSED)
+_bfd_elf_eh_frame_address_size (bfd *abfd, const asection *sec ATTRIBUTE_UNUSED)
 {
   return elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 ? 8 : 4;
 }
This page took 0.025226 seconds and 4 git commands to generate.