* elf-bfd.h (struct eh_cie_fde): Add need_relative and
[deliverable/binutils-gdb.git] / bfd / elf-eh-frame.c
index 83ccfe9c54ec67550135f68ef76d993854364dcb..12eef615049419c631ddb26ac72041f7ce49e0c0 100644 (file)
@@ -1,22 +1,22 @@
 /* .eh_frame section optimization.
-   Copyright 2001, 2002 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.
+   This file is part of BFD, the Binary File Descriptor library.
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
@@ -26,129 +26,44 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #define EH_FRAME_HDR_SIZE 8
 
-struct cie_header
-{
-  unsigned int length;
-  unsigned int id;
-};
-
-struct cie
-{
-  struct cie_header hdr;
-  unsigned char version;
-  unsigned char augmentation[20];
-  unsigned int code_align;
-  int data_align;
-  unsigned int ra_column;
-  unsigned int augmentation_size;
-  struct elf_link_hash_entry *personality;
-  unsigned char per_encoding;
-  unsigned char lsda_encoding;
-  unsigned char fde_encoding;
-  unsigned char initial_insn_length;
-  unsigned char make_relative;
-  unsigned char make_lsda_relative;
-  unsigned char initial_instructions[50];
-};
-
-struct eh_cie_fde
-{
-  unsigned int offset;
-  unsigned int size;
-  asection *sec;
-  unsigned int new_offset;
-  unsigned char fde_encoding;
-  unsigned char lsda_encoding;
-  unsigned char lsda_offset;
-  unsigned char cie : 1;
-  unsigned char removed : 1;
-  unsigned char make_relative : 1;
-  unsigned char make_lsda_relative : 1;
-  unsigned char per_encoding_relative : 1;
-};
-
-struct eh_frame_sec_info
-{
-  unsigned int count;
-  unsigned int alloced;
-  struct eh_cie_fde entry[1];
-};
-
-struct eh_frame_array_ent
-{
-  bfd_vma initial_loc;
-  bfd_vma fde;
-};
-
-struct eh_frame_hdr_info
-{
-  struct cie last_cie;
-  asection *last_cie_sec;
-  unsigned int last_cie_offset;
-  unsigned int fde_count, array_count;
-  struct eh_frame_array_ent *array;
-  /* TRUE if .eh_frame_hdr should contain the sorted search table.
-     We build it if we successfully read all .eh_frame input sections
-     and recognize them.  */
-  boolean table;
-  boolean strip;
-};
-
-static bfd_vma read_unsigned_leb128
-  PARAMS ((bfd *, char *, unsigned int *));
-static bfd_signed_vma read_signed_leb128
-  PARAMS ((bfd *, char *, unsigned int *));
-static int get_DW_EH_PE_width
-  PARAMS ((int, int));
-static bfd_vma read_value
-  PARAMS ((bfd *, bfd_byte *, int));
-static void write_value
-  PARAMS ((bfd *, bfd_byte *, bfd_vma, int));
-static int cie_compare
-  PARAMS ((struct cie *, struct cie *));
-static int vma_compare
-  PARAMS ((const PTR a, const PTR b));
-
 /* Helper function for reading uleb128 encoded data.  */
 
 static bfd_vma
-read_unsigned_leb128 (abfd, buf, bytes_read_ptr)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     char *buf;
-     unsigned int *bytes_read_ptr;
+read_unsigned_leb128 (bfd *abfd ATTRIBUTE_UNUSED,
+                     char *buf,
+                     unsigned int *bytes_read_ptr)
 {
-  bfd_vma  result;
-  unsigned int  num_read;
-  int           shift;
+  bfd_vma result;
+  unsigned int num_read;
+  int shift;
   unsigned char byte;
 
-  result   = 0;
-  shift    = 0;
+  result = 0;
+  shift = 0;
   num_read = 0;
   do
     {
       byte = bfd_get_8 (abfd, (bfd_byte *) buf);
-      buf ++;
-      num_read ++;
+      buf++;
+      num_read++;
       result |= (((bfd_vma) byte & 0x7f) << shift);
       shift += 7;
     }
   while (byte & 0x80);
-  * bytes_read_ptr = num_read;
+  *bytes_read_ptr = num_read;
   return result;
 }
 
 /* Helper function for reading sleb128 encoded data.  */
 
 static bfd_signed_vma
-read_signed_leb128 (abfd, buf, bytes_read_ptr)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     char *buf;
-     unsigned int * bytes_read_ptr;
+read_signed_leb128 (bfd *abfd ATTRIBUTE_UNUSED,
+                   char *buf,
+                   unsigned int * bytes_read_ptr)
 {
-  bfd_vma      result;
-  int           shift;
-  int           num_read;
+  bfd_vma result;
+  int shift;
+  int num_read;
   unsigned char byte;
 
   result = 0;
@@ -165,7 +80,7 @@ read_signed_leb128 (abfd, buf, bytes_read_ptr)
   while (byte & 0x80);
   if (byte & 0x40)
     result |= (((bfd_vma) -1) << (shift - 7)) << 7;
-  * bytes_read_ptr = num_read;
+  *bytes_read_ptr = num_read;
   return result;
 }
 
@@ -188,8 +103,7 @@ while (0)
 /* Return 0 if either encoding is variable width, or not yet known to bfd.  */
 
 static
-int get_DW_EH_PE_width (encoding, ptr_size)
-     int encoding, ptr_size;
+int get_DW_EH_PE_width (int encoding, int ptr_size)
 {
   /* DW_EH_PE_ values of 0x60 and 0x70 weren't defined at the time .eh_frame
      was added to bfd.  */
@@ -209,35 +123,47 @@ int get_DW_EH_PE_width (encoding, ptr_size)
   return 0;
 }
 
+#define get_DW_EH_PE_signed(encoding) (((encoding) & DW_EH_PE_signed) != 0)
+
 /* Read a width sized value from memory.  */
 
 static bfd_vma
-read_value (abfd, buf, width)
-     bfd *abfd;
-     bfd_byte *buf;
-     int width;
+read_value (bfd *abfd, bfd_byte *buf, int width, int is_signed)
 {
   bfd_vma value;
 
   switch (width)
     {
-    case 2: value = bfd_get_16 (abfd, buf); break;
-    case 4: value = bfd_get_32 (abfd, buf); break;
-    case 8: value = bfd_get_64 (abfd, buf); break;
-    default: BFD_FAIL (); return 0;
+    case 2:
+      if (is_signed)
+       value = bfd_get_signed_16 (abfd, buf);
+      else
+       value = bfd_get_16 (abfd, buf);
+      break;
+    case 4:
+      if (is_signed)
+       value = bfd_get_signed_32 (abfd, buf);
+      else
+       value = bfd_get_32 (abfd, buf);
+      break;
+    case 8:
+      if (is_signed)
+       value = bfd_get_signed_64 (abfd, buf);
+      else
+       value = bfd_get_64 (abfd, buf);
+      break;
+    default:
+      BFD_FAIL ();
+      return 0;
     }
 
   return value;
 }
-    
+
 /* Store a width sized value to memory.  */
 
 static void
-write_value (abfd, buf, value, width)
-     bfd *abfd;
-     bfd_byte *buf;
-     bfd_vma value;
-     int width;
+write_value (bfd *abfd, bfd_byte *buf, bfd_vma value, int width)
 {
   switch (width)
     {
@@ -251,8 +177,7 @@ write_value (abfd, buf, value, width)
 /* Return zero if C1 and C2 CIEs can be merged.  */
 
 static
-int cie_compare (c1, c2)
-     struct cie *c1, *c2;
+int cie_compare (struct cie *c1, struct cie *c2)
 {
   if (c1->hdr.length == c2->hdr.length
       && c1->version == c2->version
@@ -266,8 +191,7 @@ int cie_compare (c1, c2)
       && c1->per_encoding == c2->per_encoding
       && c1->lsda_encoding == c2->lsda_encoding
       && c1->fde_encoding == c2->fde_encoding
-      && (c1->initial_insn_length
-         == c2->initial_insn_length)
+      && c1->initial_insn_length == c2->initial_insn_length
       && memcmp (c1->initial_instructions,
                 c2->initial_instructions,
                 c1->initial_insn_length) == 0)
@@ -278,35 +202,32 @@ int cie_compare (c1, c2)
 
 /* 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
+   functions.  The function returns TRUE iff any entries have been
    deleted.  */
 
-boolean
-_bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
-                                  reloc_symbol_deleted_p, cookie)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     asection *sec, *ehdrsec;
-     boolean (*reloc_symbol_deleted_p) (bfd_vma, PTR);
-     struct elf_reloc_cookie *cookie;
+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)
 {
   bfd_byte *ehbuf = NULL, *buf;
   bfd_byte *last_cie, *last_fde;
   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;
-  Elf_Internal_Rela *rel;
   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;
+      return FALSE;
     }
 
   if ((sec->output_section != NULL
@@ -314,36 +235,29 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
     {
       /* At least one of the sections is being discarded from the
          link, so we should just ignore them.  */
-      return false;
+      return FALSE;
     }
 
-  BFD_ASSERT (elf_section_data (ehdrsec)->sec_info_type
-             == ELF_INFO_TYPE_EH_FRAME_HDR);
-  hdr_info = (struct eh_frame_hdr_info *)
-            elf_section_data (ehdrsec)->sec_info;
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
 
   /* Read the frame unwind information from abfd.  */
 
-  ehbuf = (bfd_byte *) 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, (bfd_vma) 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)
     {
       /* Empty .eh_frame section.  */
       free (ehbuf);
-      return false;
+      return FALSE;
     }
 
   /* 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]
@@ -353,19 +267,21 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
   last_cie_ndx = 0;
   memset (&cie, 0, sizeof (cie));
   cie_usage_count = 0;
-  new_size = sec->_raw_size;
+  new_size = sec->size;
   make_relative = hdr_info->last_cie.make_relative;
   make_lsda_relative = hdr_info->last_cie.make_lsda_relative;
   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)                          \
   if (cookie->rel < cookie->relend                     \
       && (cookie->rel->r_offset                                \
-         < (bfd_size_type) ((buf) - ehbuf)))           \
+         < (bfd_size_type) ((buf) - ehbuf))            \
+      && cookie->rel->r_info != 0)                     \
     goto free_no_table
 
 #define SKIP_RELOCS(buf)                               \
@@ -401,11 +317,11 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
       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;
 
@@ -414,7 +330,7 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
            /* 64-bit .eh_frame is not supported.  */
            goto free_no_table;
          buf += 4;
-         if ((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;
 
@@ -424,7 +340,7 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
          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++;
@@ -448,11 +364,15 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
          /* CIE  */
          if (last_cie != NULL)
            {
-             /* Now check if this CIE is identical to last CIE, in which case
-                we can remove it, provided we adjust all FDEs.
-                Also, it can be removed if we have removed all FDEs using
-                that. */
-             if (cie_compare (&cie, &hdr_info->last_cie) == 0
+             /* Now check if this CIE is identical to the last CIE,
+                in which case we can remove it provided we adjust
+                all FDEs.  Also, it can be removed if we have removed
+                all FDEs using it.  */
+             if ((!info->relocatable
+                  && hdr_info->last_cie_sec
+                  && (sec->output_section
+                      == hdr_info->last_cie_sec->output_section)
+                  && cie_compare (&cie, &hdr_info->last_cie) == 0)
                  || cie_usage_count == 0)
                {
                  new_size -= cie.hdr.length + 4;
@@ -487,7 +407,7 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
          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;
@@ -506,7 +426,10 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
            }
          read_uleb128 (cie.code_align, buf);
          read_sleb128 (cie.data_align, buf);
-         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;
@@ -550,10 +473,9 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
                               + ((buf - ehbuf + per_width - 1)
                                  & ~((bfd_size_type) per_width - 1)));
                      ENSURE_NO_RELOCS (buf);
-                     rel = GET_RELOC (buf);
                      /* Ensure we have a reloc here, against
                         a global symbol.  */
-                     if (rel != NULL)
+                     if (GET_RELOC (buf) != NULL)
                        {
                          unsigned long r_symndx;
 
@@ -591,10 +513,16 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
          /* For shared libraries, try to get rid of as many RELATIVE relocs
             as possible.  */
           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;
 
@@ -621,14 +549,12 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
            goto free_no_table;
 
          ENSURE_NO_RELOCS (buf);
-         rel = GET_RELOC (buf);
-         if (rel == NULL)
+         if (GET_RELOC (buf) == NULL)
            /* This should not happen.  */
            goto free_no_table;
          if ((*reloc_symbol_deleted_p) (buf - ehbuf, cookie))
            {
-             cookie->rel = rel;
-             /* This is a FDE against discarded section, it should
+             /* 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;
@@ -636,19 +562,19 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
          else
            {
              if (info->shared
-                 && (cie.fde_encoding & 0xf0) == DW_EH_PE_absptr
-                 && cie.make_relative == 0)
+                 && (((cie.fde_encoding & 0xf0) == DW_EH_PE_absptr
+                      && cie.make_relative == 0)
+                     || (cie.fde_encoding & 0xf0) == DW_EH_PE_aligned))
                {
-                 /* If shared library uses absolute pointers
+                 /* If 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;
+                 hdr_info->table = FALSE;
                }
              cie_usage_count++;
              hdr_info->fde_count++;
            }
-         cookie->rel = rel;
          if (cie.lsda_encoding != DW_EH_PE_omit)
            {
              unsigned int dummy;
@@ -671,7 +597,7 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
     }
 
   elf_section_data (sec)->sec_info = sec_info;
-  elf_section_data (sec)->sec_info_type = ELF_INFO_TYPE_EH_FRAME;
+  sec->sec_info_type = ELF_INFO_TYPE_EH_FRAME;
 
   /* Ok, now we can assign new offsets.  */
   offset = 0;
@@ -711,65 +637,50 @@ _bfd_elf_discard_section_eh_frame (abfd, info, sec, ehdrsec,
       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;
-
   /* 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)
     free (ehbuf);
   if (sec_info)
     free (sec_info);
-  hdr_info->table = false;
+  hdr_info->table = FALSE;
   hdr_info->last_cie.hdr.length = 0;
-  return false;
+  return FALSE;
 }
 
 /* This function is called for .eh_frame_hdr section after
    _bfd_elf_discard_section_eh_frame has been called on all .eh_frame
    input sections.  It finalizes the size of .eh_frame_hdr section.  */
 
-boolean
-_bfd_elf_discard_section_eh_frame_hdr (abfd, info, sec)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     asection *sec;
+bfd_boolean
+_bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
 {
+  struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
-  unsigned int ptr_size;
-
-  ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS]
-             == ELFCLASS64) ? 8 : 4;
+  asection *sec;
 
-  if ((elf_section_data (sec)->sec_info_type
-       != ELF_INFO_TYPE_EH_FRAME_HDR)
-      || ! info->eh_frame_hdr)
-    {
-      _bfd_strip_section_from_output (info, sec);
-      return false;
-    }
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+  sec = hdr_info->hdr_sec;
+  if (sec == NULL)
+    return FALSE;
 
-  hdr_info = (struct eh_frame_hdr_info *)
-            elf_section_data (sec)->sec_info;
-  if (hdr_info->strip)
-    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;
-  elf_tdata (abfd)->eh_frame_hdr = true;
-  return true;
+  elf_tdata (abfd)->eh_frame_hdr = sec;
+  return TRUE;
 }
 
 /* This function is called from size_dynamic_sections.
@@ -777,25 +688,24 @@ _bfd_elf_discard_section_eh_frame_hdr (abfd, info, sec)
    because later on it is too late for calling _bfd_strip_section_from_output,
    since dynamic symbol table has been sized.  */
 
-boolean
-_bfd_elf_maybe_strip_eh_frame_hdr (info)
-     struct bfd_link_info *info;
+bfd_boolean
+_bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
 {
-  asection *sec, *o;
+  asection *o;
   bfd *abfd;
+  struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
 
-  sec = bfd_get_section_by_name (elf_hash_table (info)->dynobj, ".eh_frame_hdr");
-  if (sec == NULL)
-    return true;
-
-  hdr_info
-    = bfd_zmalloc (sizeof (struct eh_frame_hdr_info));
-  if (hdr_info == NULL)
-    return false;
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+  if (hdr_info->hdr_sec == NULL)
+    return TRUE;
 
-  elf_section_data (sec)->sec_info = hdr_info;
-  elf_section_data (sec)->sec_info_type = ELF_INFO_TYPE_EH_FRAME_HDR;
+  if (bfd_is_abs_section (hdr_info->hdr_sec->output_section))
+    {
+      hdr_info->hdr_sec = NULL;
+      return TRUE;
+    }
 
   abfd = NULL;
   if (info->eh_frame_hdr)
@@ -804,18 +714,19 @@ _bfd_elf_maybe_strip_eh_frame_hdr (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)
+       if (o && o->size > 8 && !bfd_is_abs_section (o->output_section))
          break;
       }
 
   if (abfd == NULL)
     {
-      _bfd_strip_section_from_output (info, sec);
-      hdr_info->strip = true;
+      _bfd_strip_section_from_output (info, hdr_info->hdr_sec);
+      hdr_info->hdr_sec = NULL;
+      return TRUE;
     }
-  else
-    hdr_info->table = true;
-  return true;
+
+  hdr_info->table = TRUE;
+  return TRUE;
 }
 
 /* Adjust an address in the .eh_frame section.  Given OFFSET within
@@ -824,21 +735,19 @@ _bfd_elf_maybe_strip_eh_frame_hdr (info)
    or to offset with dynamic relocation which is no longer needed.  */
 
 bfd_vma
-_bfd_elf_eh_frame_section_offset (output_bfd, sec, offset)
-     bfd *output_bfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     bfd_vma offset;
+_bfd_elf_eh_frame_section_offset (bfd *output_bfd ATTRIBUTE_UNUSED,
+                                 asection *sec,
+                                 bfd_vma offset)
 {
   struct eh_frame_sec_info *sec_info;
   unsigned int lo, hi, mid;
 
-  if (elf_section_data (sec)->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
+  if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
     return offset;
-  sec_info = (struct eh_frame_sec_info *)
-            elf_section_data (sec)->sec_info;
+  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;
@@ -866,16 +775,21 @@ _bfd_elf_eh_frame_section_offset (output_bfd, sec, offset)
   if (sec_info->entry[mid].make_relative
       && ! sec_info->entry[mid].cie
       && offset == sec_info->entry[mid].offset + 8)
-    return (bfd_vma) -2;
+    {
+      sec_info->entry[mid].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
-      && (offset
-         == (sec_info->entry[mid].offset + 8
-             + sec_info->entry[mid].lsda_offset)))
-    return (bfd_vma) -2;
+      && (offset == (sec_info->entry[mid].offset + 8
+                    + sec_info->entry[mid].lsda_offset)))
+    {
+      sec_info->entry[mid].need_lsda_relative = 1;
+      return (bfd_vma) -2;
+    }
 
   return (offset + sec_info->entry[mid].new_offset
          - sec_info->entry[mid].offset);
@@ -884,13 +798,14 @@ _bfd_elf_eh_frame_section_offset (output_bfd, sec, offset)
 /* Write out .eh_frame section.  This is called with the relocated
    contents.  */
 
-boolean
-_bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
-     bfd *abfd;
-     asection *sec, *ehdrsec;
-     bfd_byte *contents;
+bfd_boolean
+_bfd_elf_write_section_eh_frame (bfd *abfd,
+                                struct bfd_link_info *info,
+                                asection *sec,
+                                bfd_byte *contents)
 {
   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;
@@ -901,26 +816,17 @@ _bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
   ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS]
              == ELFCLASS64) ? 8 : 4;
 
-  if (elf_section_data (sec)->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
-    return bfd_set_section_contents (abfd, sec->output_section,
-                                    contents,
-                                    (file_ptr) sec->output_offset,
-                                    sec->_raw_size);
-  sec_info = (struct eh_frame_sec_info *)
-            elf_section_data (sec)->sec_info;
-  hdr_info = NULL;
-  if (ehdrsec
-      && (elf_section_data (ehdrsec)->sec_info_type
-         == ELF_INFO_TYPE_EH_FRAME_HDR))
-    {
-      hdr_info = (struct eh_frame_hdr_info *)
-                elf_section_data (ehdrsec)->sec_info;
-      if (hdr_info->table && hdr_info->array == NULL)
-       hdr_info->array
-         = bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
-      if (hdr_info->array == NULL)
-        hdr_info = NULL;
-    }
+  if (sec->sec_info_type != ELF_INFO_TYPE_EH_FRAME)
+    return bfd_set_section_contents (abfd, sec->output_section, contents,
+                                    sec->output_offset, sec->size);
+  sec_info = elf_section_data (sec)->sec_info;
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+  if (hdr_info->table && hdr_info->array == NULL)
+    hdr_info->array
+      = bfd_malloc (hdr_info->fde_count * sizeof(*hdr_info->array));
+  if (hdr_info->array == NULL)
+    hdr_info = NULL;
 
   p = contents;
   for (i = 0; i < sec_info->count; ++i)
@@ -950,8 +856,8 @@ _bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
        {
          /* CIE */
          cie_offset = sec_info->entry[i].new_offset;
-         if (sec_info->entry[i].make_relative
-             || sec_info->entry[i].make_lsda_relative
+         if (sec_info->entry[i].need_relative
+             || sec_info->entry[i].need_lsda_relative
              || sec_info->entry[i].per_encoding_relative)
            {
              unsigned char *aug;
@@ -960,8 +866,8 @@ _bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
 
              /* 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)
+             action = (sec_info->entry[i].need_relative ? 1 : 0)
+                      | (sec_info->entry[i].need_lsda_relative ? 2 : 0)
                       | (sec_info->entry[i].per_encoding_relative ? 4 : 0);
              buf = contents + sec_info->entry[i].offset;
              /* Skip length, id and version.  */
@@ -1004,7 +910,9 @@ _bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
                      {
                        bfd_vma value;
 
-                       value = read_value (abfd, buf, per_width);
+                       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);
@@ -1026,21 +934,23 @@ _bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
                  }
            }
        }
-      else
+      else if (sec_info->entry[i].size > 4)
        {
          /* FDE */
          bfd_vma value = 0, address;
          unsigned int width;
 
          buf = contents + sec_info->entry[i].offset;
-         /* Skip length.  */   
+         /* Skip length.  */
          buf += 4;
          bfd_put_32 (abfd,
                      sec_info->entry[i].new_offset + 4 - cie_offset, buf);
          buf += 4;
          width = get_DW_EH_PE_width (sec_info->entry[i].fde_encoding,
                                      ptr_size);
-         address = value = read_value (abfd, buf, width);
+         address = value = read_value (abfd, buf, width,
+                                       get_DW_EH_PE_signed
+                                       (sec_info->entry[i].fde_encoding));
          if (value)
            {
              switch (sec_info->entry[i].fde_encoding & 0xf0)
@@ -1064,7 +974,7 @@ _bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
                              + sec_info->entry[i].offset + 8);
                  break;
                }
-             if (sec_info->entry[i].make_relative)
+             if (sec_info->entry[i].need_relative)
                value -= (sec->output_section->vma + sec->output_offset
                          + sec_info->entry[i].new_offset + 8);
              write_value (abfd, buf, value, width);
@@ -1079,19 +989,21 @@ _bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
            }
 
          if ((sec_info->entry[i].lsda_encoding & 0xf0) == DW_EH_PE_pcrel
-             || sec_info->entry[i].make_lsda_relative)
+             || sec_info->entry[i].need_lsda_relative)
            {
              buf += sec_info->entry[i].lsda_offset;
              width = get_DW_EH_PE_width (sec_info->entry[i].lsda_encoding,
                                          ptr_size);
-             value = read_value (abfd, buf, width);
+             value = read_value (abfd, buf, width,
+                                 get_DW_EH_PE_signed
+                                 (sec_info->entry[i].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)
+                 else if (sec_info->entry[i].need_lsda_relative)
                    value -= (sec->output_section->vma + sec->output_offset
                              + sec_info->entry[i].new_offset + 8
                              + sec_info->entry[i].lsda_offset);
@@ -1099,6 +1011,9 @@ _bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
                }
            }
        }
+      else
+       /* Terminating FDE must be at the end of .eh_frame section only.  */
+       BFD_ASSERT (i == sec_info->count - 1);
 
       BFD_ASSERT (p == contents + sec_info->entry[i].new_offset);
       memmove (p, contents + sec_info->entry[i].offset,
@@ -1106,35 +1021,57 @@ _bfd_elf_write_section_eh_frame (abfd, sec, ehdrsec, contents)
       p += sec_info->entry[i].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;
+      unsigned int alignment = 1 << sec->alignment_power;
+      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->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)
+             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)
+           abort ();
+
+         pad = alignment - pad;
+
+         buf = contents + sec_info->entry[i].new_offset;
+
+         /* Update length.  */
+         sec_info->entry[i].size += pad;
+         bfd_put_32 (abfd, sec_info->entry[i].size - 4, buf);
+
+         /* Pad it with DW_CFA_nop  */
+         memset (p, 0, pad);
+         p += 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);
+                                   sec->size);
 }
 
 /* Helper function used to sort .eh_frame_hdr search table by increasing
    VMA of FDE initial location.  */
 
 static int
-vma_compare (a, b)
-     const PTR a;
-     const PTR b;
+vma_compare (const void *a, const void *b)
 {
-  struct eh_frame_array_ent *p = (struct eh_frame_array_ent *) a;
-  struct eh_frame_array_ent *q = (struct eh_frame_array_ent *) b;
+  const struct eh_frame_array_ent *p = a;
+  const struct eh_frame_array_ent *q = b;
   if (p->initial_loc > q->initial_loc)
     return 1;
   if (p->initial_loc < q->initial_loc)
@@ -1162,55 +1099,58 @@ vma_compare (a, b)
    fde_count x [encoded] initial_loc, fde
                                (array of encoded pairs containing
                                 FDE initial_location field and FDE address,
-                                sorted by increasing initial_loc)  */
+                                sorted by increasing initial_loc).  */
 
-boolean
-_bfd_elf_write_section_eh_frame_hdr (abfd, sec)
-     bfd *abfd;
-     asection *sec;
+bfd_boolean
+_bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
 {
+  struct elf_link_hash_table *htab;
   struct eh_frame_hdr_info *hdr_info;
-  unsigned int ptr_size;
+  asection *sec;
   bfd_byte *contents;
   asection *eh_frame_sec;
   bfd_size_type size;
+  bfd_boolean retval;
+  bfd_vma encoded_eh_frame;
 
-  ptr_size = (elf_elfheader (sec->owner)->e_ident[EI_CLASS]
-             == ELFCLASS64) ? 8 : 4;
-
-  BFD_ASSERT (elf_section_data (sec)->sec_info_type
-             == ELF_INFO_TYPE_EH_FRAME_HDR);
-  hdr_info = (struct eh_frame_hdr_info *)
-            elf_section_data (sec)->sec_info;
-  if (hdr_info->strip)
-    return true;
+  htab = elf_hash_table (info);
+  hdr_info = &htab->eh_info;
+  sec = hdr_info->hdr_sec;
+  if (sec == NULL)
+    return TRUE;
 
   size = EH_FRAME_HDR_SIZE;
   if (hdr_info->array && hdr_info->array_count == hdr_info->fde_count)
     size += 4 + hdr_info->fde_count * 8;
   contents = bfd_malloc (size);
   if (contents == NULL)
-    return false;
+    return FALSE;
 
   eh_frame_sec = bfd_get_section_by_name (abfd, ".eh_frame");
   if (eh_frame_sec == NULL)
-    return false;
+    {
+      free (contents);
+      return FALSE;
+    }
 
   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[0] = 1;                             /* Version.  */
+  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  */
-      contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; /* search table enc  */
+      contents[2] = DW_EH_PE_udata4;           /* FDE count encoding.  */
+      contents[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4; /* Search table enc.  */
     }
   else
     {
       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;
@@ -1230,7 +1170,35 @@ _bfd_elf_write_section_eh_frame_hdr (abfd, sec)
        }
     }
 
-  return bfd_set_section_contents (abfd, sec->output_section,
-                                  contents, (file_ptr) sec->output_offset,
-                                   sec->_cooked_size);
+  retval = bfd_set_section_contents (abfd, sec->output_section,
+                                    contents, (file_ptr) sec->output_offset,
+                                    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.037053 seconds and 4 git commands to generate.