Update the address and phone number of the FSF organization in the GPL notices
[deliverable/binutils-gdb.git] / bfd / elf-eh-frame.c
index 9ccf561ddbfc0e6392f0123122c141a2143f7c45..d5041d01ba1b1220d44b23704355a6ce0e76c17f 100644 (file)
@@ -16,7 +16,7 @@
 
    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.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
@@ -260,6 +260,102 @@ size_of_output_cie_fde (struct eh_cie_fde *entry, unsigned int alignment)
          + alignment - 1) & -alignment;
 }
 
+/* Assume that the bytes between *ITER and END are CFA instructions.
+   Try to move *ITER past the first instruction and return true on
+   success.  ENCODED_PTR_WIDTH gives the width of pointer entries.  */
+
+static bfd_boolean
+skip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width)
+{
+  bfd_byte op;
+  bfd_vma length;
+
+  if (!read_byte (iter, end, &op))
+    return FALSE;
+
+  switch (op & 0x80 ? op & 0xc0 : op)
+    {
+    case DW_CFA_nop:
+    case DW_CFA_advance_loc:
+    case DW_CFA_restore:
+      /* No arguments.  */
+      return TRUE;
+
+    case DW_CFA_offset:
+    case DW_CFA_restore_extended:
+    case DW_CFA_undefined:
+    case DW_CFA_same_value:
+    case DW_CFA_def_cfa_register:
+    case DW_CFA_def_cfa_offset:
+    case DW_CFA_def_cfa_offset_sf:
+    case DW_CFA_GNU_args_size:
+      /* One leb128 argument.  */
+      return skip_leb128 (iter, end);
+
+    case DW_CFA_offset_extended:
+    case DW_CFA_register:
+    case DW_CFA_def_cfa:
+    case DW_CFA_offset_extended_sf:
+    case DW_CFA_GNU_negative_offset_extended:
+    case DW_CFA_def_cfa_sf:
+      /* Two leb128 arguments.  */
+      return (skip_leb128 (iter, end)
+             && skip_leb128 (iter, end));
+
+    case DW_CFA_def_cfa_expression:
+      /* A variable-length argument.  */
+      return (read_uleb128 (iter, end, &length)
+             && skip_bytes (iter, end, length));
+
+    case DW_CFA_expression:
+      /* A leb128 followed by a variable-length argument.  */
+      return (skip_leb128 (iter, end)
+             && read_uleb128 (iter, end, &length)
+             && skip_bytes (iter, end, length));
+
+    case DW_CFA_set_loc:
+      return skip_bytes (iter, end, encoded_ptr_width);
+
+    case DW_CFA_advance_loc1:
+      return skip_bytes (iter, end, 1);
+
+    case DW_CFA_advance_loc2:
+      return skip_bytes (iter, end, 2);
+
+    case DW_CFA_advance_loc4:
+      return skip_bytes (iter, end, 4);
+
+    case DW_CFA_MIPS_advance_loc8:
+      return skip_bytes (iter, end, 8);
+
+    default:
+      return FALSE;
+    }
+}
+
+/* Try to interpret the bytes between BUF and END as CFA instructions.
+   If every byte makes sense, return a pointer to the first DW_CFA_nop
+   padding byte, or END if there is no padding.  Return null otherwise.
+   ENCODED_PTR_WIDTH is as for skip_cfa_op.  */
+
+static bfd_byte *
+skip_non_nops (bfd_byte *buf, bfd_byte *end, unsigned int encoded_ptr_width)
+{
+  bfd_byte *last;
+
+  last = buf;
+  while (buf < end)
+    if (*buf == DW_CFA_nop)
+      buf++;
+    else
+      {
+       if (!skip_cfa_op (&buf, end, encoded_ptr_width))
+         return 0;
+       last = buf;
+      }
+  return last;
+}
+
 /* 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
@@ -322,8 +418,10 @@ _bfd_elf_discard_section_eh_frame
      it (it would need to use 64-bit .eh_frame format anyway).  */
   REQUIRE (sec->size == (unsigned int) sec->size);
 
-  ptr_size = (elf_elfheader (abfd)->e_ident[EI_CLASS]
-             == ELFCLASS64) ? 8 : 4;
+  ptr_size = (get_elf_backend_data (abfd)
+             ->elf_backend_eh_frame_address_size (abfd, sec));
+  REQUIRE (ptr_size != 0);
+
   buf = ehbuf;
   last_cie = NULL;
   last_cie_inf = NULL;
@@ -355,8 +453,8 @@ _bfd_elf_discard_section_eh_frame
 
   for (;;)
     {
-      unsigned char *aug;
-      bfd_byte *start, *end;
+      char *aug;
+      bfd_byte *start, *end, *insns;
       bfd_size_type length;
 
       if (sec_info->count == sec_info->alloced)
@@ -384,6 +482,7 @@ _bfd_elf_discard_section_eh_frame
         on whether to output or discard last encountered CIE (if any).  */
       if ((bfd_size_type) (buf - ehbuf) == sec->size)
        {
+         hdr.length = 0;
          hdr.id = (unsigned int) -1;
          end = buf;
        }
@@ -464,10 +563,10 @@ _bfd_elf_discard_section_eh_frame
 
          /* Cannot handle unknown versions.  */
          REQUIRE (cie.version == 1 || cie.version == 3);
-         REQUIRE (strlen (buf) < sizeof (cie.augmentation));
+         REQUIRE (strlen ((char *) buf) < sizeof (cie.augmentation));
 
-         strcpy (cie.augmentation, buf);
-         buf = strchr (buf, '\0') + 1;
+         strcpy (cie.augmentation, (char *) buf);
+         buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
          ENSURE_NO_RELOCS (buf);
          if (buf[0] == 'e' && buf[1] == 'h')
            {
@@ -602,12 +701,13 @@ _bfd_elf_discard_section_eh_frame
          if (cie.fde_encoding == DW_EH_PE_omit)
            cie.fde_encoding = DW_EH_PE_absptr;
 
-         initial_insn_length = cie.hdr.length - (buf - last_fde - 4);
+         initial_insn_length = end - buf;
          if (initial_insn_length <= 50)
            {
              cie.initial_insn_length = initial_insn_length;
              memcpy (cie.initial_instructions, buf, initial_insn_length);
            }
+         insns = buf;
          buf += initial_insn_length;
          ENSURE_NO_RELOCS (buf);
          last_cie = last_fde;
@@ -648,18 +748,38 @@ _bfd_elf_discard_section_eh_frame
 
          /* Skip the augmentation size, if present.  */
          if (cie.augmentation[0] == 'z')
-           REQUIRE (skip_leb128 (&buf, end));
+           REQUIRE (read_uleb128 (&buf, end, &length));
+         else
+           length = 0;
 
          /* Of the supported augmentation characters above, only 'L'
             adds augmentation data to the FDE.  This code would need to
             be adjusted if any future augmentations do the same thing.  */
          if (cie.lsda_encoding != DW_EH_PE_omit)
-           this_inf->lsda_offset = buf - start;
+           {
+             this_inf->lsda_offset = buf - start;
+             /* If there's no 'z' augmentation, we don't know where the
+                CFA insns begin.  Assume no padding.  */
+             if (cie.augmentation[0] != 'z')
+               length = end - buf;
+           }
+
+         /* Skip over the augmentation data.  */
+         REQUIRE (skip_bytes (&buf, end, length));
+         insns = buf;
 
          buf = last_fde + 4 + hdr.length;
          SKIP_RELOCS (buf);
        }
 
+      /* Try to interpret the CFA instructions and find the first
+        padding nop.  Shrink this_inf's size so that it doesn't
+        including the padding.  */
+      length = get_DW_EH_PE_width (cie.fde_encoding, ptr_size);
+      insns = skip_non_nops (insns, end, length);
+      if (insns != 0)
+       this_inf->size -= end - insns;
+
       this_inf->fde_encoding = cie.fde_encoding;
       this_inf->lsda_encoding = cie.lsda_encoding;
       sec_info->count++;
@@ -733,8 +853,8 @@ _bfd_elf_discard_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
 
 /* This function is called from size_dynamic_sections.
    It needs to decide whether .eh_frame_hdr should be output or not,
-   because later on it is too late for calling _bfd_strip_section_from_output,
-   since dynamic symbol table has been sized.  */
+   because when the dynamic symbol table has been sized it is too late
+   to strip sections.  */
 
 bfd_boolean
 _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
@@ -768,7 +888,7 @@ _bfd_elf_maybe_strip_eh_frame_hdr (struct bfd_link_info *info)
 
   if (abfd == NULL)
     {
-      _bfd_strip_section_from_output (info, hdr_info->hdr_sec);
+      hdr_info->hdr_sec->flags |= SEC_EXCLUDE;
       hdr_info->hdr_sec = NULL;
       return TRUE;
     }
@@ -870,12 +990,14 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
   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->size);
+
+  ptr_size = (get_elf_backend_data (abfd)
+             ->elf_backend_eh_frame_address_size (abfd, sec));
+  BFD_ASSERT (ptr_size != 0);
+
   sec_info = elf_section_data (sec)->sec_info;
   htab = elf_hash_table (info);
   hdr_info = &htab->eh_info;
@@ -966,7 +1088,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
              || ent->need_lsda_relative
              || ent->per_encoding_relative)
            {
-             unsigned char *aug;
+             char *aug;
              unsigned int action, extra_string, extra_data;
              unsigned int per_width, per_encoding;
 
@@ -980,8 +1102,8 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
 
              /* Skip length, id and version.  */
              buf += 9;
-             aug = buf;
-             buf = strchr (buf, '\0') + 1;
+             aug = (char *) buf;
+             buf += strlen (aug) + 1;
              skip_leb128 (&buf, end);
              skip_leb128 (&buf, end);
              skip_leb128 (&buf, end);
@@ -995,7 +1117,7 @@ _bfd_elf_write_section_eh_frame (bfd *abfd,
 
              /* Make room for the new augmentation string and data bytes.  */
              memmove (buf + extra_string + extra_data, buf, end - buf);
-             memmove (aug + extra_string, aug, buf - aug);
+             memmove (aug + extra_string, aug, buf - (bfd_byte *) aug);
              buf += extra_string;
              end += extra_string + extra_data;
 
@@ -1290,6 +1412,14 @@ _bfd_elf_write_section_eh_frame_hdr (bfd *abfd, struct bfd_link_info *info)
   return retval;
 }
 
+/* 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)
+{
+  return elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64 ? 8 : 4;
+}
+
 /* Decide whether we can use a PC-relative encoding within the given
    EH frame section.  This is the default implementation.  */
 
This page took 0.027971 seconds and 4 git commands to generate.