Add SHF_COMPRESSED support to gas and objcopy
[deliverable/binutils-gdb.git] / gas / write.c
index cf59d7da883a0d7d716dd938e83dfd86bac56677..bc76962c600efc442d7d180e360320f69808685d 100644 (file)
@@ -1,7 +1,5 @@
 /* write.c - emit .o file
-   Copyright 1986, 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
-   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-   2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1986-2015 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
 #include "libbfd.h"
 #include "compress-debug.h"
 
-#ifndef TC_ADJUST_RELOC_COUNT
-#define TC_ADJUST_RELOC_COUNT(FIX, COUNT)
-#endif
-
 #ifndef TC_FORCE_RELOCATION
 #define TC_FORCE_RELOCATION(FIX)               \
   (generic_force_reloc (FIX))
@@ -126,6 +120,9 @@ symbolS *abs_section_sym;
 /* Remember the value of dot when parsing expressions.  */
 addressT dot_value;
 
+/* The frag that dot_value is based from.  */
+fragS *dot_frag;
+
 /* Relocs generated by ".reloc" pseudo.  */
 struct reloc_list* reloc_list;
 
@@ -151,7 +148,7 @@ fix_new_internal (fragS *frag,              /* Which frag?  */
                  symbolS *sub_symbol,  /* X_op_symbol.  */
                  offsetT offset,       /* X_add_number.  */
                  int pcrel,            /* TRUE if PC-relative relocation.  */
-                 RELOC_ENUM r_type ATTRIBUTE_UNUSED /* Relocation type.  */,
+                 RELOC_ENUM r_type     /* Relocation type.  */,
                  int at_beginning)     /* Add to the start of the list?  */
 {
   fixS *fixP;
@@ -173,6 +170,7 @@ fix_new_internal (fragS *frag,              /* Which frag?  */
   fixP->fx_subsy = sub_symbol;
   fixP->fx_offset = offset;
   fixP->fx_dot_value = dot_value;
+  fixP->fx_dot_frag = dot_frag;
   fixP->fx_pcrel = pcrel;
   fixP->fx_r_type = r_type;
   fixP->fx_im_disp = 0;
@@ -406,8 +404,8 @@ chain_frchains_together_1 (segT section, struct frchain *frchp)
          prev_fix = frchp->fix_tail;
        }
     }
-  gas_assert (prev_frag->fr_type != 0);
-  gas_assert (prev_frag != &dummy);
+  gas_assert (prev_frag != &dummy
+             && prev_frag->fr_type != 0);
   prev_frag->fr_next = 0;
   return prev_frag;
 }
@@ -654,15 +652,21 @@ dump_section_relocs (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, FILE *stream)
 static void
 resolve_reloc_expr_symbols (void)
 {
+  bfd_vma addr_mask = 1;
   struct reloc_list *r;
 
+  /* Avoid a shift by the width of type.  */
+  addr_mask <<= bfd_arch_bits_per_address (stdoutput) - 1;
+  addr_mask <<= 1;
+  addr_mask -= 1;
+
   for (r = reloc_list; r; r = r->next)
     {
+      reloc_howto_type *howto = r->u.a.howto;
       expressionS *symval;
       symbolS *sym;
       bfd_vma offset, addend;
       asection *sec;
-      reloc_howto_type *howto;
 
       resolve_symbol_value (r->u.a.offset_sym);
       symval = symbol_get_value_expression (r->u.a.offset_sym);
@@ -709,7 +713,17 @@ resolve_reloc_expr_symbols (void)
            }
          else if (sym != NULL)
            {
-             if (S_IS_LOCAL (sym) && !symbol_section_p (sym))
+             /* Convert relocs against local symbols to refer to the
+                corresponding section symbol plus offset instead.  Keep
+                PC-relative relocs of the REL variety intact though to
+                prevent the offset from overflowing the relocated field,
+                unless it has enough bits to cover the whole address
+                space.  */
+             if (S_IS_LOCAL (sym) && !symbol_section_p (sym)
+                 && (sec->use_rela_p
+                     || (howto->partial_inplace
+                         && (!howto->pc_relative
+                             || howto->src_mask == addr_mask))))
                {
                  asection *symsec = S_GET_SEGMENT (sym);
                  if (!(((symsec->flags & SEC_MERGE) != 0
@@ -730,8 +744,6 @@ resolve_reloc_expr_symbols (void)
          sym = abs_section_sym;
        }
 
-      howto = r->u.a.howto;
-
       r->u.b.sec = sec;
       r->u.b.s = symbol_get_bfdsym (sym);
       r->u.b.r.sym_ptr_ptr = &r->u.b.s;
@@ -824,7 +836,8 @@ adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED,
        if (symsec == NULL)
          abort ();
 
-       if (bfd_is_abs_section (symsec))
+       if (bfd_is_abs_section (symsec)
+           || symsec == reg_section)
          {
            /* The fixup_segment routine normally will not use this
               symbol in a relocation.  */
@@ -877,15 +890,12 @@ adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED,
    handled now.  (These consist of fixS where we have since discovered
    the value of a symbol, or the address of the frag involved.)
    For each one, call md_apply_fix to put the fix into the frag data.
+   Ones that we couldn't completely handle here will be output later
+   by emit_relocations.  */
 
-   Result is a count of how many relocation structs will be needed to
-   handle the remaining fixS's that we couldn't completely handle here.
-   These will be output later by emit_relocations().  */
-
-static long
+static void
 fixup_segment (fixS *fixP, segT this_segment)
 {
-  long seg_reloc_count = 0;
   valueT add_number;
   fragS *fragP;
   segT add_symbol_segment = absolute_section;
@@ -915,10 +925,8 @@ fixup_segment (fixS *fixP, segT this_segment)
            symbol_mark_used_in_reloc (fixP->fx_addsy);
            if (fixP->fx_subsy != NULL)
              symbol_mark_used_in_reloc (fixP->fx_subsy);
-           seg_reloc_count++;
          }
-      TC_ADJUST_RELOC_COUNT (fixP, seg_reloc_count);
-      return seg_reloc_count;
+      return;
     }
 
   for (; fixP; fixP = fixP->fx_next)
@@ -973,7 +981,7 @@ fixup_segment (fixS *fixP, segT this_segment)
            {
              add_number -= S_GET_VALUE (fixP->fx_subsy);
              fixP->fx_offset = (add_number + fixP->fx_dot_value
-                                + fixP->fx_frag->fr_address);
+                                + fixP->fx_dot_frag->fr_address);
 
              /* Make it pc-relative.  If the back-end code has not
                 selected a pc-relative reloc, cancel the adjustment
@@ -1006,6 +1014,10 @@ fixup_segment (fixS *fixP, segT this_segment)
                              S_GET_NAME (fixP->fx_subsy),
                              segment_name (sub_symbol_segment));
            }
+         else if (sub_symbol_segment != undefined_section
+                  && ! bfd_is_com_section (sub_symbol_segment)
+                  && MD_APPLY_SYM_VALUE (fixP))
+           add_number -= S_GET_VALUE (fixP->fx_subsy);
        }
 
       if (fixP->fx_addsy)
@@ -1056,7 +1068,6 @@ fixup_segment (fixS *fixP, segT this_segment)
 
       if (!fixP->fx_done)
        {
-         ++seg_reloc_count;
          if (fixP->fx_addsy == NULL)
            fixP->fx_addsy = abs_section_sym;
          symbol_mark_used_in_reloc (fixP->fx_addsy);
@@ -1109,9 +1120,6 @@ fixup_segment (fixS *fixP, segT this_segment)
       print_fixup (fixP);
 #endif
     }                          /* For each fixS in this segment.  */
-
-  TC_ADJUST_RELOC_COUNT (fixP, seg_reloc_count);
-  return seg_reloc_count;
 }
 
 static void
@@ -1165,7 +1173,7 @@ get_frag_for_reloc (fragS *last_frag,
                    const struct reloc_list *r)
 {
   fragS *f;
-  
+
   for (f = last_frag; f != NULL; f = f->fr_next)
     if (f->fr_address <= r->u.b.r.address
        && r->u.b.r.address < f->fr_address + f->fr_fix)
@@ -1176,6 +1184,11 @@ get_frag_for_reloc (fragS *last_frag,
        && r->u.b.r.address < f->fr_address + f->fr_fix)
       return f;
 
+  for (f = seginfo->frchainP->frch_root; f != NULL; f = f->fr_next)
+    if (f->fr_address <= r->u.b.r.address
+       && r->u.b.r.address <= f->fr_address + f->fr_fix)
+      return f;
+
   as_bad_where (r->file, r->line,
                _("reloc not within (fixed part of) section"));
   return NULL;
@@ -1400,6 +1413,9 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   struct z_stream_s *strm;
   int x;
   flagword flags = bfd_get_section_flags (abfd, sec);
+  unsigned int header_size, compression_header_size;
+  /* Maximimum compression header is 24 bytes.  */
+  bfd_byte compression_header[24];
 
   if (seginfo == NULL
       || sec->size < 32
@@ -1414,27 +1430,26 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   if (strm == NULL)
     return;
 
+  if (flag_compress_debug == COMPRESS_DEBUG_GABI_ZLIB)
+    stdoutput->flags |= BFD_COMPRESS | BFD_COMPRESS_GABI;
+  else
+    stdoutput->flags |= BFD_COMPRESS;
+  compression_header_size
+    = bfd_get_compression_header_size (stdoutput, NULL);
+
   /* Create a new frag to contain the "ZLIB" header.  */
+  header_size = 12 + compression_header_size;
   first_newf = frag_alloc (ob);
-  if (obstack_room (ob) < 12)
+  if (obstack_room (ob) < header_size)
     first_newf = frag_alloc (ob);
-  if (obstack_room (ob) < 12)
-    as_fatal (_("can't extend frag %u chars"), 12);
+  if (obstack_room (ob) < header_size)
+    as_fatal (_("can't extend frag %u chars"), header_size);
   last_newf = first_newf;
-  obstack_blank_fast (ob, 12);
+  obstack_blank_fast (ob, header_size);
   last_newf->fr_type = rs_fill;
-  last_newf->fr_fix = 12;
+  last_newf->fr_fix = header_size;
   header = last_newf->fr_literal;
-  memcpy (header, "ZLIB", 4);
-  header[11] = uncompressed_size; uncompressed_size >>= 8;
-  header[10] = uncompressed_size; uncompressed_size >>= 8;
-  header[9] = uncompressed_size; uncompressed_size >>= 8;
-  header[8] = uncompressed_size; uncompressed_size >>= 8;
-  header[7] = uncompressed_size; uncompressed_size >>= 8;
-  header[6] = uncompressed_size; uncompressed_size >>= 8;
-  header[5] = uncompressed_size; uncompressed_size >>= 8;
-  header[4] = uncompressed_size;
-  compressed_size = 12;
+  compressed_size = header_size;
 
   /* Stream the frags through the compression engine, adding new frags
      as necessary to accomodate the compressed output.  */
@@ -1513,18 +1528,32 @@ compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
        break;
     }
 
+  /* PR binutils/18087: If compression didn't make the section smaller,
+     just keep it uncompressed.  */
+  if (compressed_size >= uncompressed_size)
+    return;
+
+  if (compression_header_size)
+    memcpy (header, compression_header, compression_header_size);
+  memcpy (header + compression_header_size, "ZLIB", 4);
+  bfd_putb64 (uncompressed_size, header + compression_header_size + 4);
+
   /* Replace the uncompressed frag list with the compressed frag list.  */
   seginfo->frchainP->frch_root = first_newf;
   seginfo->frchainP->frch_last = last_newf;
 
   /* Update the section size and its name.  */
+  bfd_update_compression_header (abfd, (bfd_byte *) header, sec);
   x = bfd_set_section_size (abfd, sec, compressed_size);
   gas_assert (x);
-  compressed_name = (char *) xmalloc (strlen (section_name) + 2);
-  compressed_name[0] = '.';
-  compressed_name[1] = 'z';
-  strcpy (compressed_name + 2, section_name + 1);
-  bfd_section_name (stdoutput, sec) = compressed_name;
+  if (!compression_header_size)
+    {
+      compressed_name = (char *) xmalloc (strlen (section_name) + 2);
+      compressed_name[0] = '.';
+      compressed_name[1] = 'z';
+      strcpy (compressed_name + 2, section_name + 1);
+      bfd_section_name (stdoutput, sec) = compressed_name;
+    }
 }
 
 static void
@@ -1679,70 +1708,107 @@ set_symtab (void)
    of the section.  This allows proper nop-filling at the end of
    code-bearing sections.  */
 #define SUB_SEGMENT_ALIGN(SEG, FRCHAIN)                                        \
-  (!(FRCHAIN)->frch_next ? get_recorded_alignment (SEG) : 0)
+  (!(FRCHAIN)->frch_next && subseg_text_p (SEG)                                \
+   ? get_recorded_alignment (SEG)                                      \
+   : 0)
 #else
 #define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0
 #endif
 #endif
 
-void
-subsegs_finish (void)
+static void
+subsegs_finish_section (asection *s)
 {
   struct frchain *frchainP;
-  asection *s;
+  segment_info_type *seginfo = seg_info (s);
+  if (!seginfo)
+    return;
 
-  for (s = stdoutput->sections; s; s = s->next)
+  for (frchainP = seginfo->frchainP;
+       frchainP != NULL;
+       frchainP = frchainP->frch_next)
     {
-      segment_info_type *seginfo = seg_info (s);
-      if (!seginfo)
-       continue;
-
-      for (frchainP = seginfo->frchainP;
-          frchainP != NULL;
-          frchainP = frchainP->frch_next)
-       {
-         int alignment = 0;
+      int alignment = 0;
 
-         subseg_set (s, frchainP->frch_subseg);
+      subseg_set (s, frchainP->frch_subseg);
 
-         /* This now gets called even if we had errors.  In that case,
-            any alignment is meaningless, and, moreover, will look weird
-            if we are generating a listing.  */
-         if (!had_errors ())
+      /* This now gets called even if we had errors.  In that case,
+        any alignment is meaningless, and, moreover, will look weird
+        if we are generating a listing.  */
+      if (!had_errors ())
+       {
+         alignment = SUB_SEGMENT_ALIGN (now_seg, frchainP);
+         if ((bfd_get_section_flags (now_seg->owner, now_seg) & SEC_MERGE)
+             && now_seg->entsize)
            {
-             alignment = SUB_SEGMENT_ALIGN (now_seg, frchainP);
-             if ((bfd_get_section_flags (now_seg->owner, now_seg) & SEC_MERGE)
-                 && now_seg->entsize)
-               {
-                 unsigned int entsize = now_seg->entsize;
-                 int entalign = 0;
+             unsigned int entsize = now_seg->entsize;
+             int entalign = 0;
 
-                 while ((entsize & 1) == 0)
-                   {
-                     ++entalign;
-                     entsize >>= 1;
-                   }
-                 if (entalign > alignment)
-                   alignment = entalign;
+             while ((entsize & 1) == 0)
+               {
+                 ++entalign;
+                 entsize >>= 1;
                }
+
+             if (entalign > alignment)
+               alignment = entalign;
            }
+       }
 
-         if (subseg_text_p (now_seg))
-           frag_align_code (alignment, 0);
-         else
-           frag_align (alignment, 0, 0);
+      if (subseg_text_p (now_seg))
+       frag_align_code (alignment, 0);
+      else
+       frag_align (alignment, 0, 0);
 
-         /* frag_align will have left a new frag.
-            Use this last frag for an empty ".fill".
+      /* frag_align will have left a new frag.
+        Use this last frag for an empty ".fill".
 
-            For this segment ...
-            Create a last frag. Do not leave a "being filled in frag".  */
-         frag_wane (frag_now);
-         frag_now->fr_fix = 0;
-         know (frag_now->fr_next == NULL);
-       }
+        For this segment ...
+        Create a last frag. Do not leave a "being filled in frag".  */
+      frag_wane (frag_now);
+      frag_now->fr_fix = 0;
+      know (frag_now->fr_next == NULL);
+    }
+}
+
+static void
+subsegs_finish (void)
+{
+  asection *s;
+
+  for (s = stdoutput->sections; s; s = s->next)
+    subsegs_finish_section (s);
+}
+
+#ifdef OBJ_ELF
+static void
+create_obj_attrs_section (void)
+{
+  segT s;
+  char *p;
+  offsetT size;
+  const char *name;
+
+  size = bfd_elf_obj_attr_size (stdoutput);
+  if (size)
+    {
+      name = get_elf_backend_data (stdoutput)->obj_attrs_section;
+      if (!name)
+       name = ".gnu.attributes";
+      s = subseg_new (name, 0);
+      elf_section_type (s)
+       = get_elf_backend_data (stdoutput)->obj_attrs_section_type;
+      bfd_set_section_flags (stdoutput, s, SEC_READONLY | SEC_DATA);
+      frag_now_fix ();
+      p = frag_more (size);
+      bfd_elf_set_obj_attr_contents (stdoutput, (bfd_byte *)p, size);
+
+      subsegs_finish_section (s);
+      relax_segment (seg_info (s)->frchainP->frch_root, s, 0);
+      size_seg (stdoutput, s, NULL);
     }
 }
+#endif
 
 /* Write the object file.  */
 
@@ -1754,28 +1820,15 @@ write_object_file (void)
   fragS *fragP;                        /* Track along all frags.  */
 #endif
 
-  /* Do we really want to write it?  */
-  {
-    int n_warns, n_errs;
-    n_warns = had_warnings ();
-    n_errs = had_errors ();
-    /* The -Z flag indicates that an object file should be generated,
-       regardless of warnings and errors.  */
-    if (flag_always_generate_output)
-      {
-       if (n_warns || n_errs)
-         as_warn (_("%d error%s, %d warning%s, generating bad object file"),
-                  n_errs, n_errs == 1 ? "" : "s",
-                  n_warns, n_warns == 1 ? "" : "s");
-      }
-    else
-      {
-       if (n_errs)
-         as_fatal (_("%d error%s, %d warning%s, no object file generated"),
-                   n_errs, n_errs == 1 ? "" : "s",
-                   n_warns, n_warns == 1 ? "" : "s");
-      }
-  }
+  subsegs_finish ();
+
+#ifdef md_pre_output_hook
+  md_pre_output_hook;
+#endif
+
+#ifdef md_pre_relax_hook
+  md_pre_relax_hook;
+#endif
 
   /* From now on, we don't care about sub-segments.  Build one frag chain
      for each segment. Linked thru fr_next.  */
@@ -1849,6 +1902,11 @@ write_object_file (void)
   md_post_relax_hook;
 #endif
 
+#ifdef OBJ_ELF
+  if (IS_ELF)
+    create_obj_attrs_section ();
+#endif
+
 #ifndef WORKING_DOT_WORD
   {
     struct broken_word *lie;
@@ -1868,7 +1926,7 @@ write_object_file (void)
 #ifdef TC_CONS_FIX_NEW
          TC_CONS_FIX_NEW (lie->frag,
                           lie->word_goes_here - lie->frag->fr_literal,
-                          2, &exp);
+                          2, &exp, TC_PARSE_CONS_RETURN_NONE);
 #else
          fix_new_exp (lie->frag,
                       lie->word_goes_here - lie->frag->fr_literal,
@@ -2269,13 +2327,13 @@ relax_frag (segT segment, fragS *fragP, long stretch)
 /* Relax_align. Advance location counter to next address that has 'alignment'
    lowest order bits all 0s, return size of adjustment made.  */
 static relax_addressT
-relax_align (register relax_addressT address,  /* Address now.  */
-            register int alignment     /* Alignment (binary).  */)
+relax_align (relax_addressT address,   /* Address now.  */
+            int alignment      /* Alignment (binary).  */)
 {
   relax_addressT mask;
   relax_addressT new_address;
 
-  mask = ~((~0) << alignment);
+  mask = ~((relax_addressT) ~0 << alignment);
   new_address = (address + mask) & (~mask);
 #ifdef LINKER_RELAXING_SHRINKS_ONLY
   if (linkrelax)
This page took 0.033245 seconds and 4 git commands to generate.