* elf-bfd.h (struct eh_frame_hdr_info): Add offsets_adjusted.
[deliverable/binutils-gdb.git] / bfd / elf-eh-frame.c
index 28e0b55fefdf3cc73b2e143ed816ec48bcfcd0b6..6302cf08110b7f10cedca3c97c12662690ca08c1 100644 (file)
@@ -1,5 +1,5 @@
 /* .eh_frame section optimization.
-   Copyright 2001, 2002, 2003 Free Software Foundation, Inc.
+   Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
    Written by Jakub Jelinek <jakub@redhat.com>.
 
    This file is part of BFD, the Binary File Descriptor library.
@@ -213,18 +213,18 @@ _bfd_elf_discard_section_eh_frame
 {
   bfd_byte *ehbuf = NULL, *buf;
   bfd_byte *last_cie, *last_fde;
+  struct eh_cie_fde *ent, *last_cie_inf, *this_inf;
   struct cie_header hdr;
   struct cie cie;
   struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
   struct eh_frame_sec_info *sec_info = NULL;
   unsigned int leb128_tmp;
-  unsigned int cie_usage_count, last_cie_ndx, i, offset;
-  unsigned int make_relative, make_lsda_relative;
+  unsigned int cie_usage_count, offset;
   bfd_size_type new_size;
   unsigned int ptr_size;
 
-  if (sec->_raw_size == 0)
+  if (sec->size == 0)
     {
       /* This file does not contain .eh_frame information.  */
       return FALSE;
@@ -234,7 +234,7 @@ _bfd_elf_discard_section_eh_frame
        && bfd_is_abs_section (sec->output_section)))
     {
       /* At least one of the sections is being discarded from the
-         link, so we should just ignore them.  */
+        link, so we should just ignore them.  */
       return FALSE;
     }
 
@@ -243,14 +243,10 @@ _bfd_elf_discard_section_eh_frame
 
   /* Read the frame unwind information from abfd.  */
 
-  ehbuf = bfd_malloc (sec->_raw_size);
-  if (ehbuf == NULL)
+  if (!bfd_malloc_and_get_section (abfd, sec, &ehbuf))
     goto free_no_table;
 
-  if (! bfd_get_section_contents (abfd, sec, ehbuf, 0, sec->_raw_size))
-    goto free_no_table;
-
-  if (sec->_raw_size >= 4
+  if (sec->size >= 4
       && bfd_get_32 (abfd, ehbuf) == 0
       && cookie->rel == cookie->relend)
     {
@@ -261,23 +257,22 @@ _bfd_elf_discard_section_eh_frame
 
   /* If .eh_frame section size doesn't fit into int, we cannot handle
      it (it would need to use 64-bit .eh_frame format anyway).  */
-  if (sec->_raw_size != (unsigned int) sec->_raw_size)
+  if (sec->size != (unsigned int) sec->size)
     goto free_no_table;
 
   ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS]
              == ELFCLASS64) ? 8 : 4;
   buf = ehbuf;
   last_cie = NULL;
-  last_cie_ndx = 0;
+  last_cie_inf = NULL;
   memset (&cie, 0, sizeof (cie));
   cie_usage_count = 0;
-  new_size = sec->_raw_size;
-  make_relative = hdr_info->last_cie.make_relative;
-  make_lsda_relative = hdr_info->last_cie.make_lsda_relative;
+  new_size = sec->size;
   sec_info = bfd_zmalloc (sizeof (struct eh_frame_sec_info)
                          + 99 * sizeof (struct eh_cie_fde));
   if (sec_info == NULL)
     goto free_no_table;
+
   sec_info->alloced = 100;
 
 #define ENSURE_NO_RELOCS(buf)                          \
@@ -289,14 +284,14 @@ _bfd_elf_discard_section_eh_frame
 
 #define SKIP_RELOCS(buf)                               \
   while (cookie->rel < cookie->relend                  \
-         && (cookie->rel->r_offset                     \
+        && (cookie->rel->r_offset                      \
             < (bfd_size_type) ((buf) - ehbuf)))        \
     cookie->rel++
 
 #define GET_RELOC(buf)                                 \
   ((cookie->rel < cookie->relend                       \
     && (cookie->rel->r_offset                          \
-        == (bfd_size_type) ((buf) - ehbuf)))           \
+       == (bfd_size_type) ((buf) - ehbuf)))            \
    ? cookie->rel : NULL)
 
   for (;;)
@@ -305,26 +300,33 @@ _bfd_elf_discard_section_eh_frame
 
       if (sec_info->count == sec_info->alloced)
        {
+         struct eh_cie_fde *old_entry = sec_info->entry;
          sec_info = bfd_realloc (sec_info,
                                  sizeof (struct eh_frame_sec_info)
-                                 + (sec_info->alloced + 99)
-                                    * sizeof (struct eh_cie_fde));
+                                 + ((sec_info->alloced + 99)
+                                    * sizeof (struct eh_cie_fde)));
          if (sec_info == NULL)
            goto free_no_table;
 
          memset (&sec_info->entry[sec_info->alloced], 0,
                  100 * sizeof (struct eh_cie_fde));
          sec_info->alloced += 100;
+
+         /* Now fix any pointers into the array.  */
+         if (last_cie_inf >= old_entry
+             && last_cie_inf < old_entry + sec_info->count)
+           last_cie_inf = sec_info->entry + (last_cie_inf - old_entry);
        }
 
+      this_inf = sec_info->entry + sec_info->count;
       last_fde = buf;
       /* If we are at the end of the section, we still need to decide
         on whether to output or discard last encountered CIE (if any).  */
-      if ((bfd_size_type) (buf - ehbuf) == sec->_raw_size)
+      if ((bfd_size_type) (buf - ehbuf) == sec->size)
        hdr.id = (unsigned int) -1;
       else
        {
-         if ((bfd_size_type) (buf + 4 - ehbuf) > sec->_raw_size)
+         if ((bfd_size_type) (buf + 4 - ehbuf) > sec->size)
            /* No space for CIE/FDE header length.  */
            goto free_no_table;
 
@@ -333,17 +335,17 @@ _bfd_elf_discard_section_eh_frame
            /* 64-bit .eh_frame is not supported.  */
            goto free_no_table;
          buf += 4;
-         if ((bfd_size_type) (buf - ehbuf) + hdr.length > sec->_raw_size)
+         if ((bfd_size_type) (buf - ehbuf) + hdr.length > sec->size)
            /* CIE/FDE not contained fully in this .eh_frame input section.  */
            goto free_no_table;
 
-         sec_info->entry[sec_info->count].offset = last_fde - ehbuf;
-         sec_info->entry[sec_info->count].size = 4 + hdr.length;
+         this_inf->offset = last_fde - ehbuf;
+         this_inf->size = 4 + hdr.length;
 
          if (hdr.length == 0)
            {
              /* CIE with length 0 must be only the last in the section.  */
-             if ((bfd_size_type) (buf - ehbuf) < sec->_raw_size)
+             if ((bfd_size_type) (buf - ehbuf) < sec->size)
                goto free_no_table;
              ENSURE_NO_RELOCS (buf);
              sec_info->count++;
@@ -379,21 +381,15 @@ _bfd_elf_discard_section_eh_frame
                  || cie_usage_count == 0)
                {
                  new_size -= cie.hdr.length + 4;
-                 sec_info->entry[last_cie_ndx].removed = 1;
-                 sec_info->entry[last_cie_ndx].sec = hdr_info->last_cie_sec;
-                 sec_info->entry[last_cie_ndx].new_offset
-                   = hdr_info->last_cie_offset;
+                 last_cie_inf->removed = 1;
                }
              else
                {
                  hdr_info->last_cie = cie;
                  hdr_info->last_cie_sec = sec;
-                 hdr_info->last_cie_offset = last_cie - ehbuf;
-                 sec_info->entry[last_cie_ndx].make_relative
-                   = cie.make_relative;
-                 sec_info->entry[last_cie_ndx].make_lsda_relative
-                   = cie.make_lsda_relative;
-                 sec_info->entry[last_cie_ndx].per_encoding_relative
+                 last_cie_inf->make_relative = cie.make_relative;
+                 last_cie_inf->make_lsda_relative = cie.make_lsda_relative;
+                 last_cie_inf->per_encoding_relative
                    = (cie.per_encoding & 0x70) == DW_EH_PE_pcrel;
                }
            }
@@ -401,8 +397,8 @@ _bfd_elf_discard_section_eh_frame
          if (hdr.id == (unsigned int) -1)
            break;
 
-         last_cie_ndx = sec_info->count;
-         sec_info->entry[sec_info->count].cie = 1;
+         last_cie_inf = this_inf;
+         this_inf->cie = 1;
 
          cie_usage_count = 0;
          memset (&cie, 0, sizeof (cie));
@@ -410,7 +406,7 @@ _bfd_elf_discard_section_eh_frame
          cie.version = *buf++;
 
          /* Cannot handle unknown versions.  */
-         if (cie.version != 1)
+         if (cie.version != 1 && cie.version != 3)
            goto free_no_table;
          if (strlen (buf) > sizeof (cie.augmentation) - 1)
            goto free_no_table;
@@ -429,12 +425,10 @@ _bfd_elf_discard_section_eh_frame
            }
          read_uleb128 (cie.code_align, buf);
          read_sleb128 (cie.data_align, buf);
-         /* Note - in DWARF2 the return address column is an unsigned byte.
-            In DWARF3 it is a ULEB128.  We are following DWARF3.  For most
-            ports this will not matter as the value will be less than 128.
-            For the others (eg FRV, SH, MMIX, IA64) they need a fixed GCC
-            which conforms to the DWARF3 standard.  */
-         read_uleb128 (cie.ra_column, buf);
+         if (cie.version == 1)
+           cie.ra_column = *buf++;
+         else
+           read_uleb128 (cie.ra_column, buf);
          ENSURE_NO_RELOCS (buf);
          cie.lsda_encoding = DW_EH_PE_omit;
          cie.fde_encoding = DW_EH_PE_omit;
@@ -517,11 +511,17 @@ _bfd_elf_discard_section_eh_frame
 
          /* For shared libraries, try to get rid of as many RELATIVE relocs
             as possible.  */
-          if (info->shared
+         if (info->shared
+             && (get_elf_backend_data (abfd)
+                 ->elf_backend_can_make_relative_eh_frame
+                 (abfd, info, sec))
              && (cie.fde_encoding & 0xf0) == DW_EH_PE_absptr)
            cie.make_relative = 1;
 
          if (info->shared
+             && (get_elf_backend_data (abfd)
+                 ->elf_backend_can_make_lsda_relative_eh_frame
+                 (abfd, info, sec))
              && (cie.lsda_encoding & 0xf0) == DW_EH_PE_absptr)
            cie.make_lsda_relative = 1;
 
@@ -551,12 +551,13 @@ _bfd_elf_discard_section_eh_frame
          if (GET_RELOC (buf) == NULL)
            /* This should not happen.  */
            goto free_no_table;
+
          if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie))
            {
              /* This is a FDE against a discarded section.  It should
                 be deleted.  */
              new_size -= hdr.length + 4;
-             sec_info->entry[sec_info->count].removed = 1;
+             this_inf->removed = 1;
            }
          else
            {
@@ -584,14 +585,14 @@ _bfd_elf_discard_section_eh_frame
                read_uleb128 (dummy, buf);
              /* If some new augmentation data is added before LSDA
                 in FDE augmentation area, this need to be adjusted.  */
-             sec_info->entry[sec_info->count].lsda_offset = (buf - aug);
+             this_inf->lsda_offset = (buf - aug);
            }
          buf = last_fde + 4 + hdr.length;
          SKIP_RELOCS (buf);
        }
 
-      sec_info->entry[sec_info->count].fde_encoding = cie.fde_encoding;
-      sec_info->entry[sec_info->count].lsda_encoding = cie.lsda_encoding;
+      this_inf->fde_encoding = cie.fde_encoding;
+      this_inf->lsda_encoding = cie.lsda_encoding;
       sec_info->count++;
     }
 
@@ -600,54 +601,27 @@ _bfd_elf_discard_section_eh_frame
 
   /* Ok, now we can assign new offsets.  */
   offset = 0;
-  last_cie_ndx = 0;
-  for (i = 0; i < sec_info->count; i++)
-    {
-      if (! sec_info->entry[i].removed)
-       {
-         sec_info->entry[i].new_offset = offset;
-         offset += sec_info->entry[i].size;
-         if (sec_info->entry[i].cie)
-           {
-             last_cie_ndx = i;
-             make_relative = sec_info->entry[i].make_relative;
-             make_lsda_relative = sec_info->entry[i].make_lsda_relative;
-           }
-         else
-           {
-             sec_info->entry[i].make_relative = make_relative;
-             sec_info->entry[i].make_lsda_relative = make_lsda_relative;
-             sec_info->entry[i].per_encoding_relative = 0;
-           }
-       }
-      else if (sec_info->entry[i].cie && sec_info->entry[i].sec == sec)
-       {
-         /* Need to adjust new_offset too.  */
-         BFD_ASSERT (sec_info->entry[last_cie_ndx].offset
-                     == sec_info->entry[i].new_offset);
-         sec_info->entry[i].new_offset
-           = sec_info->entry[last_cie_ndx].new_offset;
-       }
-    }
-  if (hdr_info->last_cie_sec == sec)
-    {
-      BFD_ASSERT (sec_info->entry[last_cie_ndx].offset
-                 == hdr_info->last_cie_offset);
-      hdr_info->last_cie_offset = sec_info->entry[last_cie_ndx].new_offset;
-    }
-
-  /* FIXME: Currently it is not possible to shrink sections to zero size at
-     this point, so build a fake minimal CIE.  */
-  if (new_size == 0)
-    new_size = 16;
+  last_cie_inf = hdr_info->last_cie_inf;
+  for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
+    if (!ent->removed)
+      {
+       ent->new_offset = offset;
+       offset += ent->size;
+       if (ent->cie)
+         last_cie_inf = ent;
+       else
+         ent->cie_inf = last_cie_inf;
+      }
+  hdr_info->last_cie_inf = last_cie_inf;
 
   /* Shrink the sec as needed.  */
-  sec->_cooked_size = new_size;
-  if (sec->_cooked_size == 0)
+  sec->rawsize = sec->size;
+  sec->size = new_size;
+  if (sec->size == 0)
     sec->flags |= SEC_EXCLUDE;
 
   free (ehbuf);
-  return new_size != sec->_raw_size;
+  return new_size != sec->rawsize;
 
 free_no_table:
   if (ehbuf)
@@ -676,9 +650,9 @@ _bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
   if (sec == NULL)
     return FALSE;
 
-  sec->_cooked_size = EH_FRAME_HDR_SIZE;
+  sec->size = EH_FRAME_HDR_SIZE;
   if (hdr_info->table)
-    sec->_cooked_size += 4 + hdr_info->fde_count * 8;
+    sec->size += 4 + hdr_info->fde_count * 8;
 
   /* Request program headers to be recalculated.  */
   elf_tdata (abfd)->program_header_size = 0;
@@ -717,7 +691,7 @@ _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
        /* Count only sections which have at least a single CIE or FDE.
           There cannot be any CIE or FDE <= 8 bytes.  */
        o = bfd_get_section_by_name (abfd, ".eh_frame");
-       if (o && o->_raw_size > 8 && !bfd_is_abs_section (o->output_section))
+       if (o && o->size > 8 && !bfd_is_abs_section (o->output_section))
          break;
       }
 
@@ -749,8 +723,8 @@ _bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
     return offset;
   sec_info = elf_section_data (sec)->sec_info;
 
-  if (offset >= sec->_raw_size)
-    return offset - (sec->_cooked_size - sec->_raw_size);
+  if (offset >= sec->rawsize)
+    return offset - sec->rawsize + sec->size;
 
   lo = 0;
   hi = sec_info->count;
@@ -775,18 +749,24 @@ _bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
 
   /* If converting to DW_EH_PE_pcrel, there will be no need for run-time
      relocation against FDE's initial_location field.  */
-  if (sec_info->entry[mid].make_relative
-      && ! sec_info->entry[mid].cie
+  if (!sec_info->entry[mid].cie
+      && sec_info->entry[mid].cie_inf->make_relative
       && offset == sec_info->entry[mid].offset + 8)
-    return (bfd_vma) -2;
+    {
+      sec_info->entry[mid].cie_inf->need_relative = 1;
+      return (bfd_vma) -2;
+    }
 
   /* If converting LSDA pointers to DW_EH_PE_pcrel, there will be no need
      for run-time relocation against LSDA field.  */
-  if (sec_info->entry[mid].make_lsda_relative
-      && ! sec_info->entry[mid].cie
+  if (!sec_info->entry[mid].cie
+      && sec_info->entry[mid].cie_inf->make_lsda_relative
       && (offset == (sec_info->entry[mid].offset + 8
                     + sec_info->entry[mid].lsda_offset)))
-    return (bfd_vma) -2;
+    {
+      sec_info->entry[mid].cie_inf->need_lsda_relative = 1;
+      return (bfd_vma) -2;
+    }
 
   return (offset + sec_info->entry[mid].new_offset
          - sec_info->entry[mid].offset);
@@ -804,21 +784,53 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
   struct eh_frame_sec_info *sec_info;
   struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
-  unsigned int i;
   bfd_byte *p, *buf;
   unsigned int leb128_tmp;
-  unsigned int cie_offset = 0;
   unsigned int ptr_size;
+  struct eh_cie_fde *ent;
 
   ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS]
              == ELFCLASS64) ? 8 : 4;
 
   if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
     return bfd_set_section_contents (abfd, sec->output_section, contents,
-                                    sec->output_offset, sec->_raw_size);
+                                    sec->output_offset, sec->size);
   sec_info = elf_section_data (sec)->sec_info;
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
+
+  /* First convert all offsets to output section offsets, so that a
+     CIE offset is valid if the CIE is used by a FDE from some other
+     section.  This can happen when duplicate CIEs are deleted in
+     _bfd_elf_discard_section_eh_frame.  We do all sections here because
+     this function might not be called on sections in the same order as
+     _bfd_elf_discard_section_eh_frame.  */
+  if (!hdr_info->offsets_adjusted)
+    {
+      bfd *ibfd;
+      asection *eh;
+      struct eh_frame_sec_info *eh_inf;
+
+      for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+       {
+         if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
+             || (ibfd->flags & DYNAMIC) != 0)
+           continue;
+
+         eh = bfd_get_section_by_name (ibfd, ".eh_frame");
+         if (eh == NULL || eh->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
+           continue;
+
+         eh_inf = elf_section_data (eh)->sec_info;
+         for (ent = eh_inf->entry; ent < eh_inf->entry + eh_inf->count; ++ent)
+           {
+             ent->offset += eh->output_offset;
+             ent->new_offset += eh->output_offset;
+           }
+       }
+      hdr_info->offsets_adjusted = TRUE;
+    }
+
   if (hdr_info->table && hdr_info->array == NULL)
     hdr_info->array
       = bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
@@ -826,36 +838,17 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
     hdr_info = NULL;
 
   p = contents;
-  for (i = 0; i < sec_info->count; ++i)
+  for (ent = sec_info->entry; ent < sec_info->entry + sec_info->count; ++ent)
     {
-      if (sec_info->entry[i].removed)
-       {
-         if (sec_info->entry[i].cie)
-           {
-             /* If CIE is removed due to no remaining FDEs referencing it
-                and there were no CIEs kept before it, sec_info->entry[i].sec
-                will be zero.  */
-             if (sec_info->entry[i].sec == NULL)
-               cie_offset = 0;
-             else
-               {
-                 cie_offset = sec_info->entry[i].new_offset;
-                 cie_offset += (sec_info->entry[i].sec->output_section->vma
-                                + sec_info->entry[i].sec->output_offset
-                                - sec->output_section->vma
-                                - sec->output_offset);
-               }
-           }
-         continue;
-       }
+      if (ent->removed)
+       continue;
 
-      if (sec_info->entry[i].cie)
+      if (ent->cie)
        {
          /* CIE */
-         cie_offset = sec_info->entry[i].new_offset;
-         if (sec_info->entry[i].make_relative
-             || sec_info->entry[i].make_lsda_relative
-             || sec_info->entry[i].per_encoding_relative)
+         if (ent->need_relative
+             || ent->need_lsda_relative
+             || ent->per_encoding_relative)
            {
              unsigned char *aug;
              unsigned int action;
@@ -863,10 +856,10 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
 
              /* Need to find 'R' or 'L' augmentation's argument and modify
                 DW_EH_PE_* value.  */
-             action = (sec_info->entry[i].make_relative ? 1 : 0)
-                      | (sec_info->entry[i].make_lsda_relative ? 2 : 0)
-                      | (sec_info->entry[i].per_encoding_relative ? 4 : 0);
-             buf = contents + sec_info->entry[i].offset;
+             action = ((ent->need_relative ? 1 : 0)
+                       | (ent->need_lsda_relative ? 2 : 0)
+                       | (ent->per_encoding_relative ? 4 : 0));
+             buf = contents + ent->offset - sec->output_offset;
              /* Skip length, id and version.  */
              buf += 9;
              aug = buf;
@@ -886,7 +879,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
                  case 'L':
                    if (action & 2)
                      {
-                       BFD_ASSERT (*buf == sec_info->entry[i].lsda_encoding);
+                       BFD_ASSERT (*buf == ent->lsda_encoding);
                        *buf |= DW_EH_PE_pcrel;
                        action &= ~2;
                      }
@@ -894,25 +887,22 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
                    break;
                  case 'P':
                    per_encoding = *buf++;
-                    per_width = get_DW_EH_PE_width (per_encoding,
-                                                   ptr_size);
+                   per_width = get_DW_EH_PE_width (per_encoding, ptr_size);
                    BFD_ASSERT (per_width != 0);
                    BFD_ASSERT (((per_encoding & 0x70) == DW_EH_PE_pcrel)
-                               == sec_info->entry[i].per_encoding_relative);
+                               == ent->per_encoding_relative);
                    if ((per_encoding & 0xf0) == DW_EH_PE_aligned)
                      buf = (contents
                             + ((buf - contents + per_width - 1)
                                & ~((bfd_size_type) per_width - 1)));
                    if (action & 4)
                      {
-                       bfd_vma value;
-
-                       value = read_value (abfd, buf, per_width,
-                                           get_DW_EH_PE_signed
-                                           (per_encoding));
-                       value += (sec_info->entry[i].offset
-                                 - sec_info->entry[i].new_offset);
-                       write_value (abfd, buf, value, per_width);
+                       bfd_vma val;
+
+                       val = read_value (abfd, buf, per_width,
+                                         get_DW_EH_PE_signed (per_encoding));
+                       val += ent->offset - ent->new_offset;
+                       write_value (abfd, buf, val, per_width);
                        action &= ~4;
                      }
                    buf += per_width;
@@ -920,7 +910,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
                  case 'R':
                    if (action & 1)
                      {
-                       BFD_ASSERT (*buf == sec_info->entry[i].fde_encoding);
+                       BFD_ASSERT (*buf == ent->fde_encoding);
                        *buf |= DW_EH_PE_pcrel;
                        action &= ~1;
                      }
@@ -931,26 +921,25 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
                  }
            }
        }
-      else if (sec_info->entry[i].size > 4)
+      else if (ent->size > 4)
        {
          /* FDE */
-         bfd_vma value = 0, address;
+         bfd_vma value, address;
          unsigned int width;
 
-         buf = contents + sec_info->entry[i].offset;
+         buf = contents + ent->offset - sec->output_offset;
          /* Skip length.  */
          buf += 4;
-         bfd_put_32 (abfd,
-                     sec_info->entry[i].new_offset + 4 - cie_offset, buf);
+         value = ent->new_offset + 4 - ent->cie_inf->new_offset;
+         bfd_put_32 (abfd, value, buf);
          buf += 4;
-         width = get_DW_EH_PE_width (sec_info->entry[i].fde_encoding,
-                                     ptr_size);
-         address = value = read_value (abfd, buf, width,
-                                       get_DW_EH_PE_signed
-                                       (sec_info->entry[i].fde_encoding));
+         width = get_DW_EH_PE_width (ent->fde_encoding, ptr_size);
+         value = read_value (abfd, buf, width,
+                             get_DW_EH_PE_signed (ent->fde_encoding));
+         address = value;
          if (value)
            {
-             switch (sec_info->entry[i].fde_encoding & 0xf0)
+             switch (ent->fde_encoding & 0xf0)
                {
                case DW_EH_PE_indirect:
                case DW_EH_PE_textrel:
@@ -965,15 +954,12 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
                  }
                  break;
                case DW_EH_PE_pcrel:
-                 value += (sec_info->entry[i].offset
-                           - sec_info->entry[i].new_offset);
-                 address += (sec->output_section->vma + sec->output_offset
-                             + sec_info->entry[i].offset + 8);
+                 value += ent->offset - ent->new_offset;
+                 address += sec->output_section->vma + ent->offset + 8;
                  break;
                }
-             if (sec_info->entry[i].make_relative)
-               value -= (sec->output_section->vma + sec->output_offset
-                         + sec_info->entry[i].new_offset + 8);
+             if (ent->cie_inf->need_relative)
+               value -= sec->output_section->vma + ent->new_offset + 8;
              write_value (abfd, buf, value, width);
            }
 
@@ -981,96 +967,77 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
            {
              hdr_info->array[hdr_info->array_count].initial_loc = address;
              hdr_info->array[hdr_info->array_count++].fde
-               = (sec->output_section->vma + sec->output_offset
-                  + sec_info->entry[i].new_offset);
+               = sec->output_section->vma + ent->new_offset;
            }
 
-         if ((sec_info->entry[i].lsda_encoding & 0xf0) == DW_EH_PE_pcrel
-             || sec_info->entry[i].make_lsda_relative)
+         if ((ent->lsda_encoding & 0xf0) == DW_EH_PE_pcrel
+             || ent->cie_inf->need_lsda_relative)
            {
-             buf += sec_info->entry[i].lsda_offset;
-             width = get_DW_EH_PE_width (sec_info->entry[i].lsda_encoding,
-                                         ptr_size);
+             buf += ent->lsda_offset;
+             width = get_DW_EH_PE_width (ent->lsda_encoding, ptr_size);
              value = read_value (abfd, buf, width,
-                                 get_DW_EH_PE_signed
-                                 (sec_info->entry[i].lsda_encoding));
+                                 get_DW_EH_PE_signed (ent->lsda_encoding));
              if (value)
                {
-                 if ((sec_info->entry[i].lsda_encoding & 0xf0)
-                     == DW_EH_PE_pcrel)
-                   value += (sec_info->entry[i].offset
-                             - sec_info->entry[i].new_offset);
-                 else if (sec_info->entry[i].make_lsda_relative)
-                   value -= (sec->output_section->vma + sec->output_offset
-                             + sec_info->entry[i].new_offset + 8
-                             + sec_info->entry[i].lsda_offset);
+                 if ((ent->lsda_encoding & 0xf0) == DW_EH_PE_pcrel)
+                   value += ent->offset - ent->new_offset;
+                 else if (ent->cie_inf->need_lsda_relative)
+                   value -= (sec->output_section->vma + ent->new_offset + 8
+                             + ent->lsda_offset);
                  write_value (abfd, buf, value, width);
                }
            }
        }
       else
        /* Terminating FDE must be at the end of .eh_frame section only.  */
-       BFD_ASSERT (i == sec_info->count - 1);
+       BFD_ASSERT (ent == sec_info->entry + sec_info->count - 1);
 
-      BFD_ASSERT (p == contents + sec_info->entry[i].new_offset);
-      memmove (p, contents + sec_info->entry[i].offset,
-              sec_info->entry[i].size);
-      p += sec_info->entry[i].size;
+      BFD_ASSERT (p == contents + ent->new_offset - sec->output_offset);
+      memmove (p, contents + ent->offset - sec->output_offset, ent->size);
+      p += ent->size;
     }
 
-  /* FIXME: Once _bfd_elf_discard_section_eh_frame will be able to
-     shrink sections to zero size, this won't be needed any more.  */
-  if (p == contents && sec->_cooked_size == 16)
-    {
-      bfd_put_32 (abfd, 12, p);                /* Fake CIE length */
-      bfd_put_32 (abfd, 0, p + 4);     /* Fake CIE id */
-      p[8] = 1;                                /* Fake CIE version */
-      memset (p + 9, 0, 7);            /* Fake CIE augmentation, 3xleb128
-                                          and 3xDW_CFA_nop as pad  */
-      p += 16;
-    }
-  else
     {
       unsigned int alignment = 1 << sec->alignment_power;
-      unsigned int pad = sec->_cooked_size % alignment;
+      unsigned int pad = sec->size % alignment;
 
       /* Don't pad beyond the raw size of the output section. It
         can happen at the last input section.  */
       if (pad
-         && ((sec->output_offset + sec->_cooked_size + pad)
-             <= sec->output_section->_raw_size))
+         && ((sec->output_offset + sec->size + pad)
+             <= sec->output_section->size))
        {
          /* Find the last CIE/FDE.  */
-         for (i = sec_info->count - 1; i > 0; i--)
-           if (! sec_info->entry[i].removed)
+         ent = sec_info->entry + sec_info->count;
+         while (--ent != sec_info->entry)
+           if (!ent->removed)
              break;
 
          /* The size of the last CIE/FDE must be at least 4.  */
-         if (sec_info->entry[i].removed
-             || sec_info->entry[i].size < 4)
+         if (ent->removed || ent->size < 4)
            abort ();
 
          pad = alignment - pad;
 
-         buf = contents + sec_info->entry[i].new_offset;
+         buf = contents + ent->new_offset - sec->output_offset;
 
          /* Update length.  */
-         sec_info->entry[i].size += pad;
-         bfd_put_32 (abfd, sec_info->entry[i].size - 4, buf);
+         ent->size += pad;
+         bfd_put_32 (abfd, ent->size - 4, buf);
 
          /* Pad it with DW_CFA_nop  */
          memset (p, 0, pad);
          p += pad;
 
-         sec->_cooked_size += pad;
+         sec->size += pad;
        }
     }
 
-  BFD_ASSERT ((bfd_size_type) (p - contents) == sec->_cooked_size);
+  BFD_ASSERT ((bfd_size_type) (p - contents) == sec->size);
 
   return bfd_set_section_contents (abfd, sec->output_section,
-                                   contents, (file_ptr) sec->output_offset,
-                                   sec->_cooked_size);
+                                  contents, (file_ptr) sec->output_offset,
+                                  sec->size);
 }
 
 /* Helper function used to sort .eh_frame_hdr search table by increasing
@@ -1120,6 +1087,7 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
   asection *eh_frame_sec;
   bfd_size_type size;
   bfd_boolean retval;
+  bfd_vma encoded_eh_frame;
 
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
@@ -1143,7 +1111,10 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
 
   memset (contents, 0, EH_FRAME_HDR_SIZE);
   contents[0] = 1;                             /* Version.  */
-  contents[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; /* .eh_frame offset.  */
+  contents[1] = get_elf_backend_data (abfd)->elf_backend_encode_eh_address
+    (abfd, info, eh_frame_sec, 0, sec, 4,
+     &encoded_eh_frame);                       /* .eh_frame offset.  */
+
   if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
     {
       contents[2] = DW_EH_PE_udata4;           /* FDE count encoding.  */
@@ -1154,8 +1125,8 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
       contents[2] = DW_EH_PE_omit;
       contents[3] = DW_EH_PE_omit;
     }
-  bfd_put_32 (abfd, eh_frame_sec->vma - sec->output_section->vma - 4,
-             contents + 4);
+  bfd_put_32 (abfd, encoded_eh_frame, contents + 4);
+
   if (contents[2] != DW_EH_PE_omit)
     {
       unsigned int i;
@@ -1177,7 +1148,33 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
 
   retval = bfd_set_section_contents (abfd, sec->output_section,
                                     contents, (file_ptr) sec->output_offset,
-                                    sec->_cooked_size);
+                                    sec->size);
   free (contents);
   return retval;
 }
+
+/* Decide whether we can use a PC-relative encoding within the given
+   EH frame section.  This is the default implementation.  */
+
+bfd_boolean
+_bfd_elf_can_make_relative (bfd *input_bfd ATTRIBUTE_UNUSED,
+                           struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                           asection *eh_frame_section ATTRIBUTE_UNUSED)
+{
+  return TRUE;
+}
+
+/* Select an encoding for the given address.  Preference is given to
+   PC-relative addressing modes.  */
+
+bfd_byte
+_bfd_elf_encode_eh_address (bfd *abfd ATTRIBUTE_UNUSED,
+                           struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                           asection *osec, bfd_vma offset,
+                           asection *loc_sec, bfd_vma loc_offset,
+                           bfd_vma *encoded)
+{
+  *encoded = osec->vma + offset -
+    (loc_sec->output_section->vma + loc_sec->output_offset + loc_offset);
+  return DW_EH_PE_pcrel | DW_EH_PE_sdata4;
+}
This page took 0.034258 seconds and 4 git commands to generate.