* configure.tgt (targ_extra_obj) [aarch64*-*]: Define.
[deliverable/binutils-gdb.git] / gas / write.c
index f164e2eeab06e590972eeba811adbb748a3a96e4..263b002e3ce9990f24863cd53f94572c405e467a 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
-   Free Software Foundation, Inc.
+   Copyright (C) 1986-2014 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
 #include "output-file.h"
 #include "dwarf2dbg.h"
 #include "libbfd.h"
-
-#ifndef TC_ADJUST_RELOC_COUNT
-#define TC_ADJUST_RELOC_COUNT(FIX, COUNT)
-#endif
+#include "compress-debug.h"
 
 #ifndef TC_FORCE_RELOCATION
 #define TC_FORCE_RELOCATION(FIX)               \
   (! SEG_NORMAL (SEG))
 #endif
 
+#ifndef md_register_arithmetic
+# define md_register_arithmetic 1
+#endif
+
 #ifndef TC_FORCE_RELOCATION_SUB_ABS
-#define TC_FORCE_RELOCATION_SUB_ABS(FIX)       0
+#define TC_FORCE_RELOCATION_SUB_ABS(FIX, SEG)  \
+  (!md_register_arithmetic && (SEG) == reg_section)
 #endif
 
 #ifndef TC_FORCE_RELOCATION_SUB_LOCAL
 #ifdef DIFF_EXPR_OK
-#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX)     0
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG)        \
+  (!md_register_arithmetic && (SEG) == reg_section)
 #else
-#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX)     1
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG)        1
 #endif
 #endif
 
 #ifndef TC_VALIDATE_FIX_SUB
 #ifdef UNDEFINED_DIFFERENCE_OK
 /* The PA needs this for PIC code generation.  */
-#define TC_VALIDATE_FIX_SUB(FIX) 1
+#define TC_VALIDATE_FIX_SUB(FIX, SEG)                  \
+  (md_register_arithmetic || (SEG) != reg_section)
 #else
-#define TC_VALIDATE_FIX_SUB(FIX)               \
-  ((FIX)->fx_r_type == BFD_RELOC_GPREL32       \
-   || (FIX)->fx_r_type == BFD_RELOC_GPREL16)
+#define TC_VALIDATE_FIX_SUB(FIX, SEG)                  \
+  ((md_register_arithmetic || (SEG) != reg_section)    \
+   && ((FIX)->fx_r_type == BFD_RELOC_GPREL32           \
+       || (FIX)->fx_r_type == BFD_RELOC_GPREL16))
 #endif
 #endif
 
@@ -117,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;
 
@@ -142,13 +148,14 @@ 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;
 
   n_fixups++;
 
-  fixP = obstack_alloc (&notes, sizeof (fixS));
+  fixP = (fixS *) obstack_alloc (&notes, sizeof (fixS));
 
   fixP->fx_frag = frag;
   fixP->fx_where = where;
@@ -163,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;
@@ -186,10 +194,6 @@ fix_new_internal (fragS *frag,             /* Which frag?  */
 
   as_where (&fixP->fx_file, &fixP->fx_line);
 
-  /* Usually, we want relocs sorted numerically, but while
-     comparing to older versions of gas that have relocs
-     reverse sorted, it is convenient to have this compile
-     time option.  xoxorich.  */
   {
 
     fixS **seg_fix_rootP = (frags_chained
@@ -199,22 +203,22 @@ fix_new_internal (fragS *frag,            /* Which frag?  */
                            ? &seg_info (now_seg)->fix_tail
                            : &frchain_now->fix_tail);
 
-#ifdef REVERSE_SORT_RELOCS
-
-    fixP->fx_next = *seg_fix_rootP;
-    *seg_fix_rootP = fixP;
-
-#else /* REVERSE_SORT_RELOCS  */
-
-    fixP->fx_next = NULL;
-
-    if (*seg_fix_tailP)
-      (*seg_fix_tailP)->fx_next = fixP;
+    if (at_beginning)
+      {
+       fixP->fx_next = *seg_fix_rootP;
+       *seg_fix_rootP = fixP;
+       if (fixP->fx_next == NULL)
+         *seg_fix_tailP = fixP;
+      }
     else
-      *seg_fix_rootP = fixP;
-    *seg_fix_tailP = fixP;
-
-#endif /* REVERSE_SORT_RELOCS  */
+      {
+       fixP->fx_next = NULL;
+       if (*seg_fix_tailP)
+         (*seg_fix_tailP)->fx_next = fixP;
+       else
+         *seg_fix_rootP = fixP;
+       *seg_fix_tailP = fixP;
+      }
   }
 
   return fixP;
@@ -232,7 +236,7 @@ fix_new (fragS *frag,               /* Which frag?  */
         RELOC_ENUM r_type              /* Relocation type.  */)
 {
   return fix_new_internal (frag, where, size, add_symbol,
-                          (symbolS *) NULL, offset, pcrel, r_type);
+                          (symbolS *) NULL, offset, pcrel, r_type, FALSE);
 }
 
 /* Create a fixup for an expression.  Currently we only support fixups
@@ -300,7 +304,19 @@ fix_new_exp (fragS *frag,          /* Which frag?  */
       break;
     }
 
-  return fix_new_internal (frag, where, size, add, sub, off, pcrel, r_type);
+  return fix_new_internal (frag, where, size, add, sub, off, pcrel,
+                          r_type, FALSE);
+}
+
+/* Create a fixup at the beginning of FRAG.  The arguments are the same
+   as for fix_new, except that WHERE is implicitly 0.  */
+
+fixS *
+fix_at_start (fragS *frag, int size, symbolS *add_symbol,
+             offsetT offset, int pcrel, RELOC_ENUM r_type)
+{
+  return fix_new_internal (frag, 0, size, add_symbol,
+                          (symbolS *) NULL, offset, pcrel, r_type, TRUE);
 }
 
 /* Generic function to determine whether a fixup requires a relocation.  */
@@ -378,7 +394,7 @@ chain_frchains_together_1 (segT section, struct frchain *frchp)
     {
       prev_frag->fr_next = frchp->frch_root;
       prev_frag = frchp->frch_last;
-      assert (prev_frag->fr_type != 0);
+      gas_assert (prev_frag->fr_type != 0);
       if (frchp->fix_root != (fixS *) NULL)
        {
          if (seg_info (section)->fix_root == (fixS *) NULL)
@@ -388,8 +404,8 @@ chain_frchains_together_1 (segT section, struct frchain *frchp)
          prev_fix = frchp->fix_tail;
        }
     }
-  assert (prev_frag->fr_type != 0);
-  assert (prev_frag != &dummy);
+  gas_assert (prev_frag != &dummy
+             && prev_frag->fr_type != 0);
   prev_frag->fr_next = 0;
   return prev_frag;
 }
@@ -470,7 +486,7 @@ cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
     case rs_machine_dependent:
       md_convert_frag (stdoutput, sec, fragP);
 
-      assert (fragP->fr_next == NULL
+      gas_assert (fragP->fr_next == NULL
              || ((offsetT) (fragP->fr_next->fr_address - fragP->fr_address)
                  == fragP->fr_fix));
 
@@ -552,21 +568,24 @@ size_seg (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
     size = 0;
 
   flags = bfd_get_section_flags (abfd, sec);
+  if (size == 0 && bfd_get_section_size (sec) != 0 &&
+    (flags & SEC_HAS_CONTENTS) != 0)
+    return;
 
   if (size > 0 && ! seginfo->bss)
     flags |= SEC_HAS_CONTENTS;
 
   flags &= ~SEC_RELOC;
   x = bfd_set_section_flags (abfd, sec, flags);
-  assert (x);
+  gas_assert (x);
 
   newsize = md_section_align (sec, size);
   x = bfd_set_section_size (abfd, sec, newsize);
-  assert (x);
+  gas_assert (x);
 
   /* If the size had to be rounded up, add some padding in the last
      non-empty frag.  */
-  assert (newsize >= size);
+  gas_assert (newsize >= size);
   if (size != newsize)
     {
       fragS *last = seginfo->frchainP->frch_last;
@@ -633,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);
@@ -687,7 +712,30 @@ resolve_reloc_expr_symbols (void)
              sec = NULL;
            }
          else if (sym != NULL)
-           symbol_mark_used_in_reloc (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
+                        && addend != 0)
+                       || (symsec->flags & SEC_THREAD_LOCAL) != 0))
+                   {
+                     addend += S_GET_VALUE (sym);
+                     sym = section_symbol (symsec);
+                   }
+               }
+             symbol_mark_used_in_reloc (sym);
+           }
        }
       if (sym == NULL)
        {
@@ -696,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;
@@ -843,15 +889,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;
@@ -881,10 +924,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)
@@ -911,6 +952,8 @@ fixup_segment (fixS *fixP, segT this_segment)
          sub_symbol_segment = S_GET_SEGMENT (fixP->fx_subsy);
          if (fixP->fx_addsy != NULL
              && sub_symbol_segment == add_symbol_segment
+             && !S_FORCE_RELOC (fixP->fx_addsy, 0)
+             && !S_FORCE_RELOC (fixP->fx_subsy, 0)
              && !TC_FORCE_RELOCATION_SUB_SAME (fixP, add_symbol_segment))
            {
              add_number += S_GET_VALUE (fixP->fx_addsy);
@@ -924,18 +967,20 @@ fixup_segment (fixS *fixP, segT this_segment)
 #endif
            }
          else if (sub_symbol_segment == absolute_section
-                  && !TC_FORCE_RELOCATION_SUB_ABS (fixP))
+                  && !S_FORCE_RELOC (fixP->fx_subsy, 0)
+                  && !TC_FORCE_RELOCATION_SUB_ABS (fixP, add_symbol_segment))
            {
              add_number -= S_GET_VALUE (fixP->fx_subsy);
              fixP->fx_offset = add_number;
              fixP->fx_subsy = NULL;
            }
          else if (sub_symbol_segment == this_segment
-                  && !TC_FORCE_RELOCATION_SUB_LOCAL (fixP))
+                  && !S_FORCE_RELOC (fixP->fx_subsy, 0)
+                  && !TC_FORCE_RELOCATION_SUB_LOCAL (fixP, add_symbol_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
@@ -953,20 +998,31 @@ fixup_segment (fixS *fixP, segT this_segment)
              fixP->fx_subsy = NULL;
              fixP->fx_pcrel = 1;
            }
-         else if (!TC_VALIDATE_FIX_SUB (fixP))
+         else if (!TC_VALIDATE_FIX_SUB (fixP, add_symbol_segment))
            {
-             as_bad_where (fixP->fx_file, fixP->fx_line,
-                           _("can't resolve `%s' {%s section} - `%s' {%s section}"),
-                           fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0",
-                           segment_name (add_symbol_segment),
-                           S_GET_NAME (fixP->fx_subsy),
-                           segment_name (sub_symbol_segment));
+             if (!md_register_arithmetic
+                 && (add_symbol_segment == reg_section
+                     || sub_symbol_segment == reg_section))
+               as_bad_where (fixP->fx_file, fixP->fx_line,
+                             _("register value used as expression"));
+             else
+               as_bad_where (fixP->fx_file, fixP->fx_line,
+                             _("can't resolve `%s' {%s section} - `%s' {%s section}"),
+                             fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0",
+                             segment_name (add_symbol_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)
        {
          if (add_symbol_segment == this_segment
+             && !S_FORCE_RELOC (fixP->fx_addsy, 0)
              && !TC_FORCE_RELOCATION_LOCAL (fixP))
            {
              /* This fixup was made when the symbol's segment was
@@ -980,6 +1036,7 @@ fixup_segment (fixS *fixP, segT this_segment)
              fixP->fx_pcrel = 0;
            }
          else if (add_symbol_segment == absolute_section
+                  && !S_FORCE_RELOC (fixP->fx_addsy, 0)
                   && !TC_FORCE_RELOCATION_ABS (fixP))
            {
              add_number += S_GET_VALUE (fixP->fx_addsy);
@@ -1010,7 +1067,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);
@@ -1063,9 +1119,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
@@ -1113,15 +1166,37 @@ install_reloc (asection *sec, arelent *reloc, fragS *fragp,
     }
 }
 
+static fragS *
+get_frag_for_reloc (fragS *last_frag,
+                   const segment_info_type *seginfo,
+                   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)
+      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;
+}
+
 static void
 write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
 {
   segment_info_type *seginfo = seg_info (sec);
-  unsigned int i;
   unsigned int n;
   struct reloc_list *my_reloc_list, **rp, *r;
   arelent **relocs;
   fixS *fixp;
+  fragS *last_frag;
 
   /* If seginfo is NULL, we did not create this section; don't do
      anything with it.  */
@@ -1153,14 +1228,21 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
        rp = &r->next;
     }
 
-  relocs = xcalloc (n, sizeof (arelent *));
+  relocs = (arelent **) xcalloc (n, sizeof (arelent *));
 
-  i = 0;
+  n = 0;
+  r = my_reloc_list;
+  last_frag = NULL;
   for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
     {
-      int j;
       int fx_size, slack;
       offsetT loc;
+      arelent **reloc;
+#ifndef RELOC_EXPANSION_POSSIBLE
+      arelent *rel;
+
+      reloc = &rel;
+#endif
 
       if (fixp->fx_done)
        continue;
@@ -1175,40 +1257,58 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
                      _("internal error: fixup not contained within frag"));
 
 #ifndef RELOC_EXPANSION_POSSIBLE
-      {
-       arelent *reloc = tc_gen_reloc (sec, fixp);
-
-       if (!reloc)
-         continue;
-       relocs[i++] = reloc;
-       j = 1;
-      }
+      *reloc = tc_gen_reloc (sec, fixp);
 #else
-      {
-       arelent **reloc = tc_gen_reloc (sec, fixp);
+      reloc = tc_gen_reloc (sec, fixp);
+#endif
 
-       for (j = 0; reloc[j]; j++)
-         relocs[i++] = reloc[j];
-      }
+      while (*reloc)
+       {
+         while (r != NULL && r->u.b.r.address < (*reloc)->address)
+           {
+             fragS *f = get_frag_for_reloc (last_frag, seginfo, r);
+             if (f != NULL)
+               {
+                 last_frag = f;
+                 relocs[n++] = &r->u.b.r;
+                 install_reloc (sec, &r->u.b.r, f, r->file, r->line);
+               }
+             r = r->next;
+           }
+         relocs[n++] = *reloc;
+         install_reloc (sec, *reloc, fixp->fx_frag,
+                        fixp->fx_file, fixp->fx_line);
+#ifndef RELOC_EXPANSION_POSSIBLE
+         break;
+#else
+         reloc++;
 #endif
+       }
+    }
 
-      for ( ; j != 0; --j)
-       install_reloc (sec, relocs[i - j], fixp->fx_frag,
-                      fixp->fx_file, fixp->fx_line);
+  while (r != NULL)
+    {
+      fragS *f = get_frag_for_reloc (last_frag, seginfo, r);
+      if (f != NULL)
+       {
+         last_frag = f;
+         relocs[n++] = &r->u.b.r;
+         install_reloc (sec, &r->u.b.r, f, r->file, r->line);
+       }
+      r = r->next;
     }
-  n = i;
 
 #ifdef DEBUG4
   {
-    unsigned int i, j, nsyms;
+    unsigned int k, j, nsyms;
     asymbol **sympp;
     sympp = bfd_get_outsymbols (stdoutput);
     nsyms = bfd_get_symcount (stdoutput);
-    for (i = 0; i < n; i++)
-      if (((*relocs[i]->sym_ptr_ptr)->flags & BSF_SECTION_SYM) == 0)
+    for (k = 0; k < n; k++)
+      if (((*relocs[k]->sym_ptr_ptr)->flags & BSF_SECTION_SYM) == 0)
        {
          for (j = 0; j < nsyms; j++)
-           if (sympp[j] == *relocs[i]->sym_ptr_ptr)
+           if (sympp[j] == *relocs[k]->sym_ptr_ptr)
              break;
          if (j == nsyms)
            abort ();
@@ -1216,23 +1316,6 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
   }
 #endif
 
-  for (r = my_reloc_list; r != NULL; r = r->next)
-    {
-      fragS *f;
-      for (f = seginfo->frchainP->frch_root; f; f = f->fr_next)
-       if (f->fr_address <= r->u.b.r.address
-           && r->u.b.r.address < f->fr_address + f->fr_fix)
-         break;
-      if (f == NULL)
-       as_bad_where (r->file, r->line,
-                     _("reloc not within (fixed part of) section"));
-      else
-       {
-         relocs[n++] = &r->u.b.r;
-         install_reloc (sec, &r->u.b.r, f, r->file, r->line);
-       }
-    }
-
   if (n)
     {
       flagword flags = bfd_get_section_flags (abfd, sec);
@@ -1247,21 +1330,210 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
 
 #ifdef DEBUG3
   {
-    unsigned int i;
-    arelent *r;
-    asymbol *s;
+    unsigned int k;
+
     fprintf (stderr, "relocs for sec %s\n", sec->name);
-    for (i = 0; i < n; i++)
+    for (k = 0; k < n; k++)
       {
-       r = relocs[i];
-       s = *r->sym_ptr_ptr;
+       arelent *rel = relocs[k];
+       asymbol *s = *rel->sym_ptr_ptr;
        fprintf (stderr, "  reloc %2d @%p off %4lx : sym %-10s addend %lx\n",
-                i, r, (unsigned long)r->address, s->name, (unsigned long)r->addend);
+                k, rel, (unsigned long)rel->address, s->name,
+                (unsigned long)rel->addend);
       }
   }
 #endif
 }
 
+static int
+compress_frag (struct z_stream_s *strm, const char *contents, int in_size,
+              fragS **last_newf, struct obstack *ob)
+{
+  int out_size;
+  int total_out_size = 0;
+  fragS *f = *last_newf;
+  char *next_out;
+  int avail_out;
+
+  /* Call the compression routine repeatedly until it has finished
+     processing the frag.  */
+  while (in_size > 0)
+    {
+      /* Reserve all the space available in the current chunk.
+         If none is available, start a new frag.  */
+      avail_out = obstack_room (ob);
+      if (avail_out <= 0)
+        {
+          obstack_finish (ob);
+          f = frag_alloc (ob);
+         f->fr_type = rs_fill;
+          (*last_newf)->fr_next = f;
+          *last_newf = f;
+          avail_out = obstack_room (ob);
+        }
+      if (avail_out <= 0)
+       as_fatal (_("can't extend frag"));
+      next_out = obstack_next_free (ob);
+      obstack_blank_fast (ob, avail_out);
+      out_size = compress_data (strm, &contents, &in_size,
+                               &next_out, &avail_out);
+      if (out_size < 0)
+        return -1;
+
+      f->fr_fix += out_size;
+      total_out_size += out_size;
+
+      /* Return unused space.  */
+      if (avail_out > 0)
+       obstack_blank_fast (ob, -avail_out);
+    }
+
+  return total_out_size;
+}
+
+static void
+compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
+{
+  segment_info_type *seginfo = seg_info (sec);
+  fragS *f;
+  fragS *first_newf;
+  fragS *last_newf;
+  struct obstack *ob = &seginfo->frchainP->frch_obstack;
+  bfd_size_type uncompressed_size = (bfd_size_type) sec->size;
+  bfd_size_type compressed_size;
+  const char *section_name;
+  char *compressed_name;
+  char *header;
+  struct z_stream_s *strm;
+  int x;
+  flagword flags = bfd_get_section_flags (abfd, sec);
+
+  if (seginfo == NULL
+      || sec->size < 32
+      || (flags & (SEC_ALLOC | SEC_HAS_CONTENTS)) == SEC_ALLOC)
+    return;
+
+  section_name = bfd_get_section_name (stdoutput, sec);
+  if (strncmp (section_name, ".debug_", 7) != 0)
+    return;
+
+  strm = compress_init ();
+  if (strm == NULL)
+    return;
+
+  /* Create a new frag to contain the "ZLIB" header.  */
+  first_newf = frag_alloc (ob);
+  if (obstack_room (ob) < 12)
+    first_newf = frag_alloc (ob);
+  if (obstack_room (ob) < 12)
+    as_fatal (_("can't extend frag %u chars"), 12);
+  last_newf = first_newf;
+  obstack_blank_fast (ob, 12);
+  last_newf->fr_type = rs_fill;
+  last_newf->fr_fix = 12;
+  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;
+
+  /* Stream the frags through the compression engine, adding new frags
+     as necessary to accomodate the compressed output.  */
+  for (f = seginfo->frchainP->frch_root;
+       f;
+       f = f->fr_next)
+    {
+      offsetT fill_size;
+      char *fill_literal;
+      offsetT count;
+      int out_size;
+
+      gas_assert (f->fr_type == rs_fill);
+      if (f->fr_fix)
+       {
+         out_size = compress_frag (strm, f->fr_literal, f->fr_fix,
+                                   &last_newf, ob);
+         if (out_size < 0)
+           return;
+         compressed_size += out_size;
+       }
+      fill_literal = f->fr_literal + f->fr_fix;
+      fill_size = f->fr_var;
+      count = f->fr_offset;
+      gas_assert (count >= 0);
+      if (fill_size && count)
+       {
+         while (count--)
+           {
+             out_size = compress_frag (strm, fill_literal, (int) fill_size,
+                                       &last_newf, ob);
+             if (out_size < 0)
+               return;
+             compressed_size += out_size;
+           }
+       }
+    }
+
+  /* Flush the compression state.  */
+  for (;;)
+    {
+      int avail_out;
+      char *next_out;
+      int out_size;
+
+      /* Reserve all the space available in the current chunk.
+        If none is available, start a new frag.  */
+      avail_out = obstack_room (ob);
+      if (avail_out <= 0)
+       {
+         fragS *newf;
+
+         obstack_finish (ob);
+         newf = frag_alloc (ob);
+         newf->fr_type = rs_fill;
+         last_newf->fr_next = newf;
+         last_newf = newf;
+         avail_out = obstack_room (ob);
+       }
+      if (avail_out <= 0)
+       as_fatal (_("can't extend frag"));
+      next_out = obstack_next_free (ob);
+      obstack_blank_fast (ob, avail_out);
+      x = compress_finish (strm, &next_out, &avail_out, &out_size);
+      if (x < 0)
+       return;
+
+      last_newf->fr_fix += out_size;
+      compressed_size += out_size;
+
+      /* Return unused space.  */
+      if (avail_out > 0)
+       obstack_blank_fast (ob, -avail_out);
+
+      if (x == 0)
+       break;
+    }
+
+  /* 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.  */
+  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;
+}
+
 static void
 write_contents (bfd *abfd ATTRIBUTE_UNUSED,
                asection *sec,
@@ -1285,7 +1557,7 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
       char *fill_literal;
       offsetT count;
 
-      assert (f->fr_type == rs_fill);
+      gas_assert (f->fr_type == rs_fill);
       if (f->fr_fix)
        {
          x = bfd_set_section_contents (stdoutput, sec,
@@ -1299,7 +1571,7 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
       fill_literal = f->fr_literal + f->fr_fix;
       fill_size = f->fr_var;
       count = f->fr_offset;
-      assert (count >= 0);
+      gas_assert (count >= 0);
       if (fill_size && count)
        {
          char buf[256];
@@ -1343,7 +1615,9 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
                    (stdoutput, sec, buf, (file_ptr) offset,
                     (bfd_size_type) n_per_buf * fill_size);
                  if (!x)
-                   as_fatal (_("cannot write to output file"));
+                   as_fatal (_("cannot write to output file '%s': %s"),
+                             stdoutput->filename,
+                             bfd_errmsg (bfd_get_error ()));
                  offset += n_per_buf * fill_size;
                }
            }
@@ -1381,7 +1655,7 @@ set_symtab (void)
       int i;
       bfd_size_type amt = (bfd_size_type) nsyms * sizeof (asymbol *);
 
-      asympp = bfd_alloc (stdoutput, amt);
+      asympp = (asymbol **) bfd_alloc (stdoutput, amt);
       symp = symbol_rootP;
       for (i = 0; i < nsyms; i++, symp = symbol_next (symp))
        {
@@ -1396,7 +1670,7 @@ set_symtab (void)
   else
     asympp = 0;
   result = bfd_set_symtab (stdoutput, asympp, nsyms);
-  assert (result);
+  gas_assert (result);
   symbol_table_frozen = 1;
 }
 
@@ -1418,64 +1692,99 @@ set_symtab (void)
 #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.  */
 
@@ -1487,35 +1796,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 OBJ_VMS
-  /* Under VMS we try to be compatible with VAX-11 "C".  Thus, we call
-     a routine to check for the definition of the procedure "_main",
-     and if so -- fix it up so that it can be program entry point.  */
-  vms_check_for_main ();
-#endif /* OBJ_VMS  */
+#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.  */
@@ -1589,6 +1878,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;
@@ -1608,7 +1902,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,
@@ -1657,18 +1951,22 @@ write_object_file (void)
            if (lie->added == 2)
              continue;
            /* Patch the jump table.  */
-           /* This is the offset from ??? to table_ptr+0.  */
-           to_addr = table_addr - S_GET_VALUE (lie->sub);
-#ifdef TC_CHECK_ADJUSTED_BROKEN_DOT_WORD
-           TC_CHECK_ADJUSTED_BROKEN_DOT_WORD (to_addr, lie);
-#endif
-           md_number_to_chars (lie->word_goes_here, to_addr, 2);
-           for (untruth = lie->next_broken_word;
+           for (untruth = (struct broken_word *) (fragP->fr_symbol);
                 untruth && untruth->dispfrag == fragP;
                 untruth = untruth->next_broken_word)
              {
                if (untruth->use_jump == lie)
-                 md_number_to_chars (untruth->word_goes_here, to_addr, 2);
+                 {
+                   /* This is the offset from ??? to table_ptr+0.
+                      The target is the same for all users of this
+                      md_long_jump, but the "sub" bases (and hence the
+                      offsets) may be different.  */
+                   addressT to_word = table_addr - S_GET_VALUE (untruth->sub);
+#ifdef TC_CHECK_ADJUSTED_BROKEN_DOT_WORD
+                   TC_CHECK_ADJUSTED_BROKEN_DOT_WORD (to_word, untruth);
+#endif
+                   md_number_to_chars (untruth->word_goes_here, to_word, 2);
+                 }
              }
 
            /* Install the long jump.  */
@@ -1766,22 +2064,24 @@ write_object_file (void)
          if (symbol_equated_reloc_p (symp)
              || S_IS_WEAKREFR (symp))
            {
-             const char *name = S_GET_NAME (symp);
+             const char *sname = S_GET_NAME (symp);
+
              if (S_IS_COMMON (symp)
-                 && !TC_FAKE_LABEL (name)
+                 && !TC_FAKE_LABEL (sname)
                  && !S_IS_WEAKREFR (symp)
                  && (!S_IS_EXTERNAL (symp) || S_IS_LOCAL (symp)))
                {
                  expressionS *e = symbol_get_value_expression (symp);
+
                  as_bad (_("Local symbol `%s' can't be equated to common symbol `%s'"),
-                         name, S_GET_NAME (e->X_add_symbol));
+                         sname, S_GET_NAME (e->X_add_symbol));
                }
              if (S_GET_SEGMENT (symp) == reg_section)
                {
                  /* Report error only if we know the symbol name.  */
                  if (S_GET_NAME (symp) != reg_section->name)
                    as_bad (_("can't make global register symbol `%s'"),
-                           name);
+                           sname);
                }
              symbol_remove (symp, &symbol_rootP, &symbol_lastP);
              continue;
@@ -1868,7 +2168,9 @@ write_object_file (void)
 #ifdef obj_frob_file
   obj_frob_file ();
 #endif
-
+#ifdef obj_coff_generate_pdata
+  obj_coff_generate_pdata ();
+#endif
   bfd_map_over_sections (stdoutput, write_relocs, (char *) 0);
 
 #ifdef tc_frob_file_after_relocs
@@ -1878,6 +2180,13 @@ write_object_file (void)
   obj_frob_file_after_relocs ();
 #endif
 
+  /* Once all relocations have been written, we can compress the
+     contents of the debug sections.  This needs to be done before
+     we start writing any sections, because it will affect the file
+     layout, which is fixed once we start writing contents.  */
+  if (flag_compress_debug)
+    bfd_map_over_sections (stdoutput, compress_debug, (char *) 0);
+
   bfd_map_over_sections (stdoutput, write_contents, (char *) 0);
 }
 
@@ -1918,16 +2227,28 @@ relax_frag (segT segment, fragS *fragP, long stretch)
            || sym_frag == &zero_address_frag);
       target += S_GET_VALUE (symbolP);
 
-      /* If frag has yet to be reached on this pass,
-        assume it will move by STRETCH just as we did.
-        If this is not so, it will be because some frag
-        between grows, and that will force another pass.  */
+      /* If SYM_FRAG has yet to be reached on this pass, assume it
+        will move by STRETCH just as we did, unless there is an
+        alignment frag between here and SYM_FRAG.  An alignment may
+        well absorb any STRETCH, and we don't want to choose a larger
+        branch insn by overestimating the needed reach of this
+        branch.  It isn't critical to calculate TARGET exactly;  We
+        know we'll be doing another pass if STRETCH is non-zero.  */
 
       if (stretch != 0
          && sym_frag->relax_marker != fragP->relax_marker
          && S_GET_SEGMENT (symbolP) == segment)
        {
-         target += stretch;
+         if (stretch < 0
+             || sym_frag->region == fragP->region)
+           target += stretch;
+         /* If we get here we know we have a forward branch.  This
+            relax pass may have stretched previous instructions so
+            far that omitting STRETCH would make the branch
+            negative.  Don't allow this in case the negative reach is
+            large enough to require a larger branch instruction.  */
+         else if (target < address)
+           target = fragP->fr_next->fr_address + stretch;
        }
     }
 
@@ -1988,7 +2309,7 @@ relax_align (register relax_addressT address,     /* Address now.  */
   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)
@@ -2015,6 +2336,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
   unsigned long frag_count;
   struct frag *fragP;
   relax_addressT address;
+  int region;
   int ret;
 
   /* In case md_estimate_size_before_relax() wants to make fixSs.  */
@@ -2023,10 +2345,12 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
   /* For each frag in segment: count and store  (a 1st guess of)
      fr_address.  */
   address = 0;
+  region = 0;
   for (frag_count = 0, fragP = segment_frag_root;
        fragP;
        fragP = fragP->fr_next, frag_count ++)
     {
+      fragP->region = region;
       fragP->relax_marker = 0;
       fragP->fr_address = address;
       address += fragP->fr_fix;
@@ -2055,12 +2379,16 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
              }
 
            address += offset;
+           region += 1;
          }
          break;
 
        case rs_org:
-       case rs_space:
          /* Assume .org is nugatory. It will grow with 1st relax.  */
+         region += 1;
+         break;
+
+       case rs_space:
          break;
 
        case rs_machine_dependent:
@@ -2212,13 +2540,17 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
                                             S_GET_NAME (lie->sub),
                                             buf);
                            }
-                         lie->added = 1;
                          if (fragP->fr_subtype == 0)
                            {
                              fragP->fr_subtype++;
                              growth += md_short_jump_size;
                            }
-                         for (untruth = lie->next_broken_word;
+
+                         /* Redirect *all* words of this table with the same
+                            target, lest we have to handle the case where the
+                            same target but with a offset that fits on this
+                            round overflows at the next relaxation round.  */
+                         for (untruth = (struct broken_word *) (fragP->fr_symbol);
                               untruth && untruth->dispfrag == lie->dispfrag;
                               untruth = untruth->next_broken_word)
                            if ((symbol_get_frag (untruth->add)
@@ -2229,6 +2561,8 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
                                untruth->added = 2;
                                untruth->use_jump = lie;
                              }
+
+                         lie->added = 1;
                          growth += md_long_jump_size;
                        }
                    }
@@ -2277,6 +2611,7 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
                              fragP->fr_literal + fragP->fr_fix,
                              fragP->fr_var);
                      newf->fr_type = rs_fill;
+                     newf->fr_address = address + fragP->fr_fix + newoff;
                      newf->fr_fix = 0;
                      newf->fr_offset = (((offsetT) 1 << fragP->fr_offset)
                                         / fragP->fr_var);
@@ -2286,13 +2621,11 @@ relax_segment (struct frag *segment_frag_root, segT segment, int pass)
                          newf->fr_offset = (offsetT) 1 << fragP->fr_offset;
                          newf->fr_var = 1;
                        }
-                     /* Include growth of new frag, because rs_fill
-                        frags don't normally grow.  */
+                     /* Include size of new frag in GROWTH.  */
                      growth += newf->fr_offset * newf->fr_var;
-                     /* The new frag address is newoff.  Adjust this
-                        for the amount we'll add when we process the
-                        new frag.  */
-                     newf->fr_address = newoff - stretch - growth;
+                     /* Adjust the new frag address for the amount
+                        we'll add when we process the new frag.  */
+                     newf->fr_address -= stretch + growth;
                      newf->relax_marker ^= 1;
                      fragP->fr_next = newf;
 #ifdef DEBUG
This page took 0.038887 seconds and 4 git commands to generate.