gas: Add md_generic_table_relax_frag
[deliverable/binutils-gdb.git] / gas / write.c
index 6ca35f225f106632644aa6d3f7c7b592b58e281d..d2bdb7acdf731aa343fb84f3a1dced524164f406 100644 (file)
@@ -1,13 +1,11 @@
 /* write.c - emit .o file
-   Copyright 1986, 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
-   1998, 1999, 2000, 2001, 2002, 2003, 2004
-   Free Software Foundation, Inc.
+   Copyright (C) 1986-2019 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    GAS 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, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to the Free
-   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 
-/* This thing should be set up to do byteordering correctly.  But...  */
+/* This thing should be set up to do byte ordering correctly.  But...  */
 
 #include "as.h"
 #include "subsegs.h"
 #include "obstack.h"
 #include "output-file.h"
 #include "dwarf2dbg.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)               \
   (TC_FORCE_RELOCATION (FIX))
 #endif
 
-#ifndef TC_FORCE_RELOCATION_LOCAL
-#define TC_FORCE_RELOCATION_LOCAL(FIX)         \
+#define GENERIC_FORCE_RELOCATION_LOCAL(FIX)    \
   (!(FIX)->fx_pcrel                            \
-   || (FIX)->fx_plt                            \
    || TC_FORCE_RELOCATION (FIX))
+#ifndef TC_FORCE_RELOCATION_LOCAL
+#define TC_FORCE_RELOCATION_LOCAL GENERIC_FORCE_RELOCATION_LOCAL
 #endif
 
+#define GENERIC_FORCE_RELOCATION_SUB_SAME(FIX, SEG)    \
+  (!SEG_NORMAL (SEG))
 #ifndef TC_FORCE_RELOCATION_SUB_SAME
-#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \
-  (! SEG_NORMAL (SEG))
+#define TC_FORCE_RELOCATION_SUB_SAME GENERIC_FORCE_RELOCATION_SUB_SAME
+#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
-#ifdef BFD_ASSEMBLER
-#define TC_VALIDATE_FIX_SUB(FIX)               \
-  ((FIX)->fx_r_type == BFD_RELOC_GPREL32       \
-   || (FIX)->fx_r_type == BFD_RELOC_GPREL16)
-#else
-#define TC_VALIDATE_FIX_SUB(FIX) 0
-#endif
+#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
 
 #define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from (FIX)
 #endif
 
+#ifndef TC_FAKE_LABEL
+#define TC_FAKE_LABEL(NAME) (strcmp ((NAME), FAKE_LABEL_NAME) == 0)
+#endif
+
+/* Positive values of TC_FX_SIZE_SLACK allow a target to define
+   fixups that far past the end of a frag.  Having such fixups
+   is of course most most likely a bug in setting fx_size correctly.
+   A negative value disables the fixup check entirely, which is
+   appropriate for something like the Renesas / SuperH SH_COUNT
+   reloc.  */
+#ifndef TC_FX_SIZE_SLACK
+#define TC_FX_SIZE_SLACK(FIX) 0
+#endif
+
 /* Used to control final evaluation of expressions.  */
 int finalize_syms = 0;
 
@@ -107,91 +121,36 @@ symbolS *abs_section_sym;
 /* Remember the value of dot when parsing expressions.  */
 addressT dot_value;
 
-void print_fixup (fixS *);
+/* The frag that dot_value is based from.  */
+fragS *dot_frag;
+
+/* Relocs generated by ".reloc" pseudo.  */
+struct reloc_list* reloc_list;
 
-#ifdef BFD_ASSEMBLER
-static void renumber_sections (bfd *, asection *, PTR);
+void print_fixup (fixS *);
 
 /* We generally attach relocs to frag chains.  However, after we have
    chained these all together into a segment, any relocs we add after
    that must be attached to a segment.  This will include relocs added
    in md_estimate_size_for_relax, for example.  */
 static int frags_chained = 0;
-#endif
-
-#ifndef BFD_ASSEMBLER
-
-#ifndef MANY_SEGMENTS
-struct frag *text_frag_root;
-struct frag *data_frag_root;
-struct frag *bss_frag_root;
-
-struct frag *text_last_frag;   /* Last frag in segment.  */
-struct frag *data_last_frag;   /* Last frag in segment.  */
-static struct frag *bss_last_frag;     /* Last frag in segment.  */
-#endif
-
-#ifndef BFD
-static object_headers headers;
-#endif
-
-long string_byte_count;
-char *next_object_file_charP;  /* Tracks object file bytes.  */
-
-#ifndef OBJ_VMS
-int magic_number_for_object_file = DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE;
-#endif
-
-#endif /* BFD_ASSEMBLER  */
 
 static int n_fixups;
 
-#ifdef BFD_ASSEMBLER
 #define RELOC_ENUM enum bfd_reloc_code_real
-#else
-#define RELOC_ENUM int
-#endif
-
-static fixS *fix_new_internal (fragS *, int where, int size,
-                              symbolS *add, symbolS *sub,
-                              offsetT offset, int pcrel,
-                              RELOC_ENUM r_type);
-#if defined (BFD_ASSEMBLER) || (!defined (BFD) && !defined (OBJ_VMS))
-static long fixup_segment (fixS *, segT);
-#endif
-static relax_addressT relax_align (relax_addressT addr, int align);
-#if defined (BFD_ASSEMBLER) || ! defined (BFD)
-static fragS *chain_frchains_together_1 (segT, struct frchain *);
-#endif
-#ifdef BFD_ASSEMBLER
-static void chain_frchains_together (bfd *, segT, PTR);
-static void cvt_frag_to_fill (segT, fragS *);
-static void adjust_reloc_syms (bfd *, asection *, PTR);
-static void fix_segment (bfd *, asection *, PTR);
-static void write_relocs (bfd *, asection *, PTR);
-static void write_contents (bfd *, asection *, PTR);
-static void set_symtab (void);
-#endif
-#if defined (BFD_ASSEMBLER) || (! defined (BFD) && ! defined (OBJ_AOUT))
-static void merge_data_into_text (void);
-#endif
-#if ! defined (BFD_ASSEMBLER) && ! defined (BFD)
-static void cvt_frag_to_fill (object_headers *, segT, fragS *);
-static void remove_subsegs (frchainS *, int, fragS **, fragS **);
-static void relax_and_size_all_segments (void);
-#endif
 
 /* Create a fixS in obstack 'notes'.  */
 
 static fixS *
 fix_new_internal (fragS *frag,         /* Which frag?  */
-                 int where,            /* Where in that frag?  */
-                 int size,             /* 1, 2, or 4 usually.  */
+                 unsigned long where,  /* Where in that frag?  */
+                 unsigned long size,   /* 1, 2, or 4 usually.  */
                  symbolS *add_symbol,  /* X_add_symbol.  */
                  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;
 
@@ -205,23 +164,20 @@ fix_new_internal (fragS *frag,            /* Which frag?  */
   /* We've made fx_size a narrow field; check that it's wide enough.  */
   if (fixP->fx_size != size)
     {
-      as_bad (_("field fx_size too small to hold %d"), size);
+      as_bad (_("field fx_size too small to hold %lu"), size);
       abort ();
     }
   fixP->fx_addsy = add_symbol;
   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_plt = 0;
-#if defined(NEED_FX_R_TYPE) || defined (BFD_ASSEMBLER)
   fixP->fx_r_type = r_type;
-#endif
-  fixP->fx_im_disp = 0;
   fixP->fx_pcrel_adjust = 0;
-  fixP->fx_bit_fixP = 0;
   fixP->fx_addnumber = 0;
   fixP->fx_tcbit = 0;
+  fixP->fx_tcbit2 = 0;
   fixP->fx_done = 0;
   fixP->fx_no_overflow = 0;
   fixP->fx_signed = 0;
@@ -235,39 +191,33 @@ fix_new_internal (fragS *frag,            /* Which frag?  */
   TC_INIT_FIX_DATA (fixP);
 #endif
 
-  as_where (&fixP->fx_file, &fixP->fx_line);
+  fixP->fx_file = as_where (&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.  */
   {
 
-#ifdef BFD_ASSEMBLER
     fixS **seg_fix_rootP = (frags_chained
                            ? &seg_info (now_seg)->fix_root
                            : &frchain_now->fix_root);
     fixS **seg_fix_tailP = (frags_chained
                            ? &seg_info (now_seg)->fix_tail
                            : &frchain_now->fix_tail);
-#endif
-
-#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;
@@ -276,16 +226,16 @@ fix_new_internal (fragS *frag,            /* Which frag?  */
 /* Create a fixup relative to a symbol (plus a constant).  */
 
 fixS *
-fix_new (fragS *frag,          /* Which frag?  */
-        int where,                     /* Where in that frag?  */
-        int size,                      /* 1, 2, or 4 usually.  */
-        symbolS *add_symbol,   /* X_add_symbol.  */
+fix_new (fragS *frag,                  /* Which frag?  */
+        unsigned long where,           /* Where in that frag?  */
+        unsigned long size,            /* 1, 2, or 4 usually.  */
+        symbolS *add_symbol,           /* X_add_symbol.  */
         offsetT offset,                /* X_add_number.  */
         int pcrel,                     /* TRUE if PC-relative relocation.  */
         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
@@ -294,8 +244,8 @@ fix_new (fragS *frag,               /* Which frag?  */
 
 fixS *
 fix_new_exp (fragS *frag,              /* Which frag?  */
-            int where,                 /* Where in that frag?  */
-            int size,                  /* 1, 2, or 4 usually.  */
+            unsigned long where,       /* Where in that frag?  */
+            unsigned long size,        /* 1, 2, or 4 usually.  */
             expressionS *exp,          /* Expression.  */
             int pcrel,                 /* TRUE if PC-relative relocation.  */
             RELOC_ENUM r_type          /* Relocation type.  */)
@@ -330,16 +280,7 @@ fix_new_exp (fragS *frag,          /* Which frag?  */
     case O_symbol_rva:
       add = exp->X_add_symbol;
       off = exp->X_add_number;
-
-#if defined(BFD_ASSEMBLER)
       r_type = BFD_RELOC_RVA;
-#else
-#if defined(TC_RVA_RELOC)
-      r_type = TC_RVA_RELOC;
-#else
-      as_fatal (_("rva not supported"));
-#endif
-#endif
       break;
 
     case O_uminus:
@@ -362,18 +303,32 @@ 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, unsigned long 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.  */
 int
 generic_force_reloc (fixS *fix)
 {
-#ifdef BFD_ASSEMBLER
   if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
       || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     return 1;
-#endif
+
+  if (fix->fx_addsy == NULL)
+    return 0;
+
   return S_FORCE_RELOC (fix->fx_addsy, fix->fx_subsy == NULL);
 }
 
@@ -389,10 +344,6 @@ append (char **charPP, char *fromP, unsigned long length)
   *charPP += length;
 }
 
-#ifndef BFD_ASSEMBLER
-int section_alignment[SEG_MAXIMUM_ORDINAL];
-#endif
-
 /* This routine records the largest alignment seen for each segment.
    If the beginning of the segment is aligned on the worst-case
    boundary, all of the other alignments within it will work.  At
@@ -403,17 +354,13 @@ record_alignment (/* Segment to which alignment pertains.  */
                  segT seg,
                  /* Alignment, as a power of 2 (e.g., 1 => 2-byte
                     boundary, 2 => 4-byte boundary, etc.)  */
-                 int align)
+                 unsigned int align)
 {
   if (seg == absolute_section)
     return;
-#ifdef BFD_ASSEMBLER
-  if ((unsigned int) align > bfd_get_section_alignment (stdoutput, seg))
-    bfd_set_section_alignment (stdoutput, seg, align);
-#else
-  if (align > section_alignment[(int) seg])
-    section_alignment[(int) seg] = align;
-#endif
+
+  if (align > bfd_section_alignment (seg))
+    bfd_set_section_alignment (seg, align);
 }
 
 int
@@ -421,19 +368,14 @@ get_recorded_alignment (segT seg)
 {
   if (seg == absolute_section)
     return 0;
-#ifdef BFD_ASSEMBLER
-  return bfd_get_section_alignment (stdoutput, seg);
-#else
-  return section_alignment[(int) seg];
-#endif
-}
 
-#ifdef BFD_ASSEMBLER
+  return bfd_section_alignment (seg);
+}
 
 /* Reset the section indices after removing the gas created sections.  */
 
 static void
-renumber_sections (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, PTR countparg)
+renumber_sections (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *countparg)
 {
   int *countp = (int *) countparg;
 
@@ -441,24 +383,17 @@ renumber_sections (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, PTR countparg)
   ++*countp;
 }
 
-#endif /* defined (BFD_ASSEMBLER)  */
-
-#if defined (BFD_ASSEMBLER) || ! defined (BFD)
-
 static fragS *
 chain_frchains_together_1 (segT section, struct frchain *frchp)
 {
   fragS dummy, *prev_frag = &dummy;
-#ifdef BFD_ASSEMBLER
   fixS fix_dummy, *prev_fix = &fix_dummy;
-#endif
 
-  for (; frchp && frchp->frch_seg == section; frchp = frchp->frch_next)
+  for (; frchp; frchp = frchp->frch_next)
     {
       prev_frag->fr_next = frchp->frch_root;
       prev_frag = frchp->frch_last;
-      assert (prev_frag->fr_type != 0);
-#ifdef BFD_ASSEMBLER
+      gas_assert (prev_frag->fr_type != 0);
       if (frchp->fix_root != (fixS *) NULL)
        {
          if (seg_info (section)->fix_root == (fixS *) NULL)
@@ -467,21 +402,17 @@ chain_frchains_together_1 (segT section, struct frchain *frchp)
          seg_info (section)->fix_tail = frchp->fix_tail;
          prev_fix = frchp->fix_tail;
        }
-#endif
     }
-  assert (prev_frag->fr_type != 0);
+  gas_assert (prev_frag != &dummy
+             && prev_frag->fr_type != 0);
   prev_frag->fr_next = 0;
   return prev_frag;
 }
 
-#endif
-
-#ifdef BFD_ASSEMBLER
-
 static void
 chain_frchains_together (bfd *abfd ATTRIBUTE_UNUSED,
                         segT section,
-                        PTR xxx ATTRIBUTE_UNUSED)
+                        void *xxx ATTRIBUTE_UNUSED)
 {
   segment_info_type *info;
 
@@ -497,31 +428,13 @@ chain_frchains_together (bfd *abfd ATTRIBUTE_UNUSED,
   frags_chained = 1;
 }
 
-#endif
-
-#if !defined (BFD) && !defined (BFD_ASSEMBLER)
-
-static void
-remove_subsegs (frchainS *head, int seg, fragS **root, fragS **last)
-{
-  *root = head->frch_root;
-  *last = chain_frchains_together_1 (seg, head);
-}
-
-#endif /* BFD  */
-
-#if defined (BFD_ASSEMBLER) || !defined (BFD)
-
-#ifdef BFD_ASSEMBLER
 static void
 cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP)
-#else
-static void
-cvt_frag_to_fill (object_headers *headersP, segT sec, fragS *fragP)
-#endif
 {
   switch (fragP->fr_type)
     {
+    case rs_space_nop:
+      goto skip_align;
     case rs_align:
     case rs_align_code:
     case rs_align_test:
@@ -530,6 +443,7 @@ cvt_frag_to_fill (object_headers *headersP, segT sec, fragS *fragP)
 #ifdef HANDLE_ALIGN
       HANDLE_ALIGN (fragP);
 #endif
+skip_align:
       know (fragP->fr_next != NULL);
       fragP->fr_offset = (fragP->fr_next->fr_address
                          - fragP->fr_address
@@ -537,14 +451,18 @@ cvt_frag_to_fill (object_headers *headersP, segT sec, fragS *fragP)
       if (fragP->fr_offset < 0)
        {
          as_bad_where (fragP->fr_file, fragP->fr_line,
-                       _("attempt to .org/.space backwards? (%ld)"),
+                       _("attempt to .org/.space/.nops backwards? (%ld)"),
                        (long) fragP->fr_offset);
          fragP->fr_offset = 0;
        }
-      fragP->fr_type = rs_fill;
+      if (fragP->fr_type == rs_space_nop)
+       fragP->fr_type = rs_fill_nop;
+      else
+       fragP->fr_type = rs_fill;
       break;
 
     case rs_fill:
+    case rs_fill_nop:
       break;
 
     case rs_leb128:
@@ -552,6 +470,13 @@ cvt_frag_to_fill (object_headers *headersP, segT sec, fragS *fragP)
        valueT value = S_GET_VALUE (fragP->fr_symbol);
        int size;
 
+       if (!S_IS_DEFINED (fragP->fr_symbol))
+         {
+           as_bad_where (fragP->fr_file, fragP->fr_line,
+                         _("leb128 operand is an undefined symbol: %s"),
+                         S_GET_NAME (fragP->fr_symbol));
+         }
+
        size = output_leb128 (fragP->fr_literal + fragP->fr_fix, value,
                              fragP->fr_subtype);
 
@@ -572,15 +497,11 @@ cvt_frag_to_fill (object_headers *headersP, segT sec, fragS *fragP)
       break;
 
     case rs_machine_dependent:
-#ifdef BFD_ASSEMBLER
       md_convert_frag (stdoutput, sec, fragP);
-#else
-      md_convert_frag (headersP, sec, fragP);
-#endif
 
-      assert (fragP->fr_next == NULL
-             || ((offsetT) (fragP->fr_next->fr_address - fragP->fr_address)
-                 == fragP->fr_fix));
+      gas_assert (fragP->fr_next == NULL
+                 || (fragP->fr_next->fr_address - fragP->fr_address
+                     == fragP->fr_fix));
 
       /* After md_convert_frag, we make the frag into a ".space 0".
         md_convert_frag() should set up any fixSs and constants
@@ -616,28 +537,25 @@ cvt_frag_to_fill (object_headers *headersP, segT sec, fragS *fragP)
 #endif
 }
 
-#endif /* defined (BFD_ASSEMBLER) || !defined (BFD)  */
-
-#ifdef BFD_ASSEMBLER
-static void relax_seg (bfd *, asection *, PTR);
+struct relax_seg_info
+{
+  int pass;
+  int changed;
+};
 
 static void
-relax_seg (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, PTR xxx)
+relax_seg (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *xxx)
 {
   segment_info_type *seginfo = seg_info (sec);
+  struct relax_seg_info *info = (struct relax_seg_info *) xxx;
 
   if (seginfo && seginfo->frchainP
-      && relax_segment (seginfo->frchainP->frch_root, sec))
-    {
-      int *result = (int *) xxx;
-      *result = 1;
-    }
+      && relax_segment (seginfo->frchainP->frch_root, sec, info->pass))
+    info->changed = 1;
 }
 
-static void size_seg (bfd *, asection *, PTR);
-
 static void
-size_seg (bfd *abfd, asection *sec, PTR xxx ATTRIBUTE_UNUSED)
+size_seg (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *xxx ATTRIBUTE_UNUSED)
 {
   flagword flags;
   fragS *fragp;
@@ -662,26 +580,30 @@ size_seg (bfd *abfd, asection *sec, PTR xxx ATTRIBUTE_UNUSED)
   else
     size = 0;
 
-  flags = bfd_get_section_flags (abfd, sec);
+  flags = bfd_section_flags (sec);
+  if (size == 0 && bfd_section_size (sec) != 0 &&
+    (flags & SEC_HAS_CONTENTS) != 0)
+    return;
 
   if (size > 0 && ! seginfo->bss)
     flags |= SEC_HAS_CONTENTS;
 
-  /* @@ This is just an approximation.  */
-  if (seginfo && seginfo->fix_root)
-    flags |= SEC_RELOC;
-  else
-    flags &= ~SEC_RELOC;
-  x = bfd_set_section_flags (abfd, sec, flags);
-  assert (x);
+  flags &= ~SEC_RELOC;
+  x = bfd_set_section_flags (sec, flags);
+  gas_assert (x);
 
-  newsize = md_section_align (sec, size);
-  x = bfd_set_section_size (abfd, sec, newsize);
-  assert (x);
+  /* If permitted, allow the backend to pad out the section
+     to some alignment boundary.  */
+  if (do_not_pad_sections_to_alignment)
+    newsize = size;
+  else
+    newsize = md_section_align (sec, size);
+  x = bfd_set_section_size (sec, newsize);
+  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;
@@ -709,12 +631,8 @@ size_seg (bfd *abfd, asection *sec, PTR xxx ATTRIBUTE_UNUSED)
 
 #ifdef DEBUG2
 static void
-dump_section_relocs (abfd, sec, stream_)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     char *stream_;
+dump_section_relocs (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, FILE *stream)
 {
-  FILE *stream = (FILE *) stream_;
   segment_info_type *seginfo = seg_info (sec);
   fixS *fixp = seginfo->fix_root;
 
@@ -746,13 +664,120 @@ dump_section_relocs (abfd, sec, stream_)
 #define EMIT_SECTION_SYMBOLS 1
 #endif
 
+/* Resolve U.A.OFFSET_SYM and U.A.SYM fields of RELOC_LIST entries,
+   and check for validity.  Convert RELOC_LIST from using U.A fields
+   to U.B fields.  */
+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;
+
+      resolve_symbol_value (r->u.a.offset_sym);
+      symval = symbol_get_value_expression (r->u.a.offset_sym);
+
+      offset = 0;
+      sym = NULL;
+      if (symval->X_op == O_constant)
+       sym = r->u.a.offset_sym;
+      else if (symval->X_op == O_symbol)
+       {
+         sym = symval->X_add_symbol;
+         offset = symval->X_add_number;
+         symval = symbol_get_value_expression (symval->X_add_symbol);
+       }
+      if (sym == NULL
+         || symval->X_op != O_constant
+         || (sec = S_GET_SEGMENT (sym)) == NULL
+         || !SEG_NORMAL (sec))
+       {
+         as_bad_where (r->file, r->line, _("invalid offset expression"));
+         sec = NULL;
+       }
+      else
+       offset += S_GET_VALUE (sym);
+
+      sym = NULL;
+      addend = r->u.a.addend;
+      if (r->u.a.sym != NULL)
+       {
+         resolve_symbol_value (r->u.a.sym);
+         symval = symbol_get_value_expression (r->u.a.sym);
+         if (symval->X_op == O_constant)
+           sym = r->u.a.sym;
+         else if (symval->X_op == O_symbol)
+           {
+             sym = symval->X_add_symbol;
+             addend += symval->X_add_number;
+             symval = symbol_get_value_expression (symval->X_add_symbol);
+           }
+         if (symval->X_op != O_constant)
+           {
+             as_bad_where (r->file, r->line, _("invalid reloc expression"));
+             sec = NULL;
+           }
+         else if (sym != NULL && sec != NULL)
+           {
+             /* 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)
+       {
+         if (abs_section_sym == NULL)
+           abs_section_sym = section_symbol (absolute_section);
+         sym = abs_section_sym;
+       }
+
+      r->u.b.sec = sec;
+      r->u.b.s = symbol_get_bfdsym (sym);
+      r->u.b.r.sym_ptr_ptr = &r->u.b.s;
+      r->u.b.r.address = offset;
+      r->u.b.r.addend = addend;
+      r->u.b.r.howto = howto;
+    }
+}
+
 /* This pass over fixups decides whether symbols can be replaced with
    section symbols.  */
 
 static void
 adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED,
                   asection *sec,
-                  PTR xxx ATTRIBUTE_UNUSED)
+                  void *xxx ATTRIBUTE_UNUSED)
 {
   segment_info_type *seginfo = seg_info (sec);
   fixS *fixp;
@@ -787,18 +812,23 @@ adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED,
        if (fixp->fx_subsy != NULL)
          resolve_symbol_value (fixp->fx_subsy);
 
-       /* If this symbol is equated to an undefined symbol, convert
-           the fixup to being against that symbol.  */
-       if (symbol_equated_reloc_p (sym))
+       /* If this symbol is equated to an undefined or common symbol,
+          convert the fixup to being against that symbol.  */
+       while (symbol_equated_reloc_p (sym)
+              || S_IS_WEAKREFR (sym))
          {
+           symbolS *newsym = symbol_get_value_expression (sym)->X_add_symbol;
+           if (sym == newsym)
+             break;
            fixp->fx_offset += symbol_get_value_expression (sym)->X_add_number;
-           sym = symbol_get_value_expression (sym)->X_add_symbol;
-           fixp->fx_addsy = sym;
+           fixp->fx_addsy = newsym;
+           sym = newsym;
          }
 
        if (symbol_mri_common_p (sym))
          {
-           /* These symbols are handled specially in fixup_segment.  */
+           fixp->fx_offset += S_GET_VALUE (sym);
+           fixp->fx_addsy = symbol_get_value_expression (sym)->X_add_symbol;
            continue;
          }
 
@@ -824,17 +854,18 @@ 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.  */
+              symbol in a relocation.  */
            continue;
          }
 
        /* Don't try to reduce relocs which refer to non-local symbols
-           in .linkonce sections.  It can lead to confusion when a
-           debugging section refers to a .linkonce section.  I hope
-           this will always be correct.  */
+          in .linkonce sections.  It can lead to confusion when a
+          debugging section refers to a .linkonce section.  I hope
+          this will always be correct.  */
        if (symsec != sec && ! S_IS_LOCAL (sym))
          {
            if ((symsec->flags & SEC_LINK_ONCE) != 0
@@ -871,192 +902,441 @@ adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED,
   dump_section_relocs (abfd, sec, stderr);
 }
 
-static void
-fix_segment (bfd *abfd ATTRIBUTE_UNUSED,
-            asection *sec,
-            PTR xxx ATTRIBUTE_UNUSED)
-{
-  segment_info_type *seginfo = seg_info (sec);
+/* fixup_segment()
 
-  fixup_segment (seginfo->fix_root, sec);
-}
+   Go through all the fixS's in a segment and see which ones can be
+   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.  */
 
 static void
-write_relocs (bfd *abfd, asection *sec, PTR xxx ATTRIBUTE_UNUSED)
+fixup_segment (fixS *fixP, segT this_segment)
 {
-  segment_info_type *seginfo = seg_info (sec);
-  unsigned int i;
-  unsigned int n;
-  arelent **relocs;
-  fixS *fixp;
-  char *err;
-
-  /* If seginfo is NULL, we did not create this section; don't do
-     anything with it.  */
-  if (seginfo == NULL)
-    return;
+  valueT add_number;
+  fragS *fragP;
+  segT add_symbol_segment = absolute_section;
 
-  n = 0;
-  for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
-    n++;
+  if (fixP != NULL && abs_section_sym == NULL)
+    abs_section_sym = section_symbol (absolute_section);
 
-#ifndef RELOC_EXPANSION_POSSIBLE
-  /* Set up reloc information as well.  */
-  relocs = (arelent **) xcalloc (n, sizeof (arelent *));
+  /* If the linker is doing the relaxing, we must not do any fixups.
 
-  i = 0;
-  for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
+     Well, strictly speaking that's not true -- we could do any that
+     are PC-relative and don't cross regions that could change size.  */
+  if (linkrelax && TC_LINKRELAX_FIXUP (this_segment))
     {
-      arelent *reloc;
-      bfd_reloc_status_type s;
-      symbolS *sym;
-
-      if (fixp->fx_done)
-       {
-         n--;
-         continue;
-       }
-
-      /* If this is an undefined symbol which was equated to another
-         symbol, then generate the reloc against the latter symbol
-         rather than the former.  */
-      sym = fixp->fx_addsy;
-      while (symbol_equated_reloc_p (sym))
-       {
-         symbolS *n;
-
-         /* We must avoid looping, as that can occur with a badly
-            written program.  */
-         n = symbol_get_value_expression (sym)->X_add_symbol;
-         if (n == sym)
-           break;
-         fixp->fx_offset += symbol_get_value_expression (sym)->X_add_number;
-         sym = n;
-       }
-      fixp->fx_addsy = sym;
-
-      reloc = tc_gen_reloc (sec, fixp);
-      if (!reloc)
-       {
-         n--;
-         continue;
-       }
-
-#if 0
-      /* This test is triggered inappropriately for the SH.  */
-      if (fixp->fx_where + fixp->fx_size
-         > fixp->fx_frag->fr_fix + fixp->fx_frag->fr_offset)
-       abort ();
-#endif
-
-      s = bfd_install_relocation (stdoutput, reloc,
-                                 fixp->fx_frag->fr_literal,
-                                 fixp->fx_frag->fr_address,
-                                 sec, &err);
-      switch (s)
-       {
-       case bfd_reloc_ok:
-         break;
-       case bfd_reloc_overflow:
-         as_bad_where (fixp->fx_file, fixp->fx_line,
-                       _("relocation overflow"));
-         break;
-       case bfd_reloc_outofrange:
-         as_bad_where (fixp->fx_file, fixp->fx_line,
-                       _("relocation out of range"));
-         break;
-       default:
-         as_fatal (_("%s:%u: bad return from bfd_install_relocation: %x"),
-                   fixp->fx_file, fixp->fx_line, s);
-       }
-      relocs[i++] = reloc;
+      for (; fixP; fixP = fixP->fx_next)
+       if (!fixP->fx_done)
+         {
+           if (fixP->fx_addsy == NULL)
+             {
+               /* There was no symbol required by this relocation.
+                  However, BFD doesn't really handle relocations
+                  without symbols well. So fake up a local symbol in
+                  the absolute section.  */
+               fixP->fx_addsy = abs_section_sym;
+             }
+           symbol_mark_used_in_reloc (fixP->fx_addsy);
+           if (fixP->fx_subsy != NULL)
+             symbol_mark_used_in_reloc (fixP->fx_subsy);
+         }
+      return;
     }
-#else
-  n = n * MAX_RELOC_EXPANSION;
-  /* Set up reloc information as well.  */
-  relocs = (arelent **) xcalloc (n, sizeof (arelent *));
 
-  i = 0;
-  for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
+  for (; fixP; fixP = fixP->fx_next)
     {
-      arelent **reloc;
-      bfd_reloc_status_type s;
-      symbolS *sym;
-      int j;
-
-      if (fixp->fx_done)
-       {
-         n--;
-         continue;
-       }
+#ifdef DEBUG5
+      fprintf (stderr, "\nprocessing fixup:\n");
+      print_fixup (fixP);
+#endif
 
-      /* If this is an undefined symbol which was equated to another
-         symbol, then generate the reloc against the latter symbol
-         rather than the former.  */
-      sym = fixp->fx_addsy;
-      while (symbol_equated_reloc_p (sym))
-       {
-         symbolS *n;
-
-         /* We must avoid looping, as that can occur with a badly
-            written program.  */
-         n = symbol_get_value_expression (sym)->X_add_symbol;
-         if (n == sym)
-           break;
-         fixp->fx_offset += symbol_get_value_expression (sym)->X_add_number;
-         sym = n;
-       }
-      fixp->fx_addsy = sym;
+      fragP = fixP->fx_frag;
+      know (fragP);
+#ifdef TC_VALIDATE_FIX
+      TC_VALIDATE_FIX (fixP, this_segment, skip);
+#endif
+      add_number = fixP->fx_offset;
 
-      reloc = tc_gen_reloc (sec, fixp);
+      if (fixP->fx_addsy != NULL)
+       add_symbol_segment = S_GET_SEGMENT (fixP->fx_addsy);
 
-      for (j = 0; reloc[j]; j++)
-       {
-         relocs[i++] = reloc[j];
-         assert (i <= n);
-       }
-      if (fixp->fx_where + fixp->fx_size
-         > fixp->fx_frag->fr_fix + fixp->fx_frag->fr_offset)
-       as_bad_where (fixp->fx_file, fixp->fx_line,
-                     _("internal error: fixup not contained within frag"));
-      for (j = 0; reloc[j]; j++)
+      if (fixP->fx_subsy != NULL)
        {
-         s = bfd_install_relocation (stdoutput, reloc[j],
-                                     fixp->fx_frag->fr_literal,
-                                     fixp->fx_frag->fr_address,
-                                     sec, &err);
-         switch (s)
+         segT sub_symbol_segment;
+         resolve_symbol_value (fixP->fx_subsy);
+         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))
            {
-           case bfd_reloc_ok:
-             break;
-           case bfd_reloc_overflow:
-             as_bad_where (fixp->fx_file, fixp->fx_line,
-                           _("relocation overflow"));
-             break;
-           case bfd_reloc_outofrange:
-             as_bad_where (fixp->fx_file, fixp->fx_line,
-                           _("relocation out of range"));
-             break;
-           default:
-             as_fatal (_("%s:%u: bad return from bfd_install_relocation: %x"),
-                       fixp->fx_file, fixp->fx_line, s);
+             add_number += S_GET_VALUE (fixP->fx_addsy);
+             add_number -= S_GET_VALUE (fixP->fx_subsy);
+             fixP->fx_offset = add_number;
+             fixP->fx_addsy = NULL;
+             fixP->fx_subsy = NULL;
+#ifdef TC_M68K
+             /* See the comment below about 68k weirdness.  */
+             fixP->fx_pcrel = 0;
+#endif
            }
+         else if (sub_symbol_segment == absolute_section
+                  && !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
+                  && !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_dot_frag->fr_address);
+
+             /* Make it pc-relative.  If the back-end code has not
+                selected a pc-relative reloc, cancel the adjustment
+                we do later on all pc-relative relocs.  */
+             if (0
+#ifdef TC_M68K
+                 /* Do this for m68k even if it's already described
+                    as pc-relative.  On the m68k, an operand of
+                    "pc@(foo-.-2)" should address "foo" in a
+                    pc-relative mode.  */
+                 || 1
+#endif
+                 || !fixP->fx_pcrel)
+               add_number += MD_PCREL_FROM_SECTION (fixP, this_segment);
+             fixP->fx_subsy = NULL;
+             fixP->fx_pcrel = 1;
+           }
+         else if (!TC_VALIDATE_FIX_SUB (fixP, add_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
+                SEG_UNKNOWN, but it is now in the local segment.
+                So we know how to do the address without relocation.  */
+             add_number += S_GET_VALUE (fixP->fx_addsy);
+             fixP->fx_offset = add_number;
+             if (fixP->fx_pcrel)
+               add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment);
+             fixP->fx_addsy = NULL;
+             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);
+             fixP->fx_offset = add_number;
+             fixP->fx_addsy = NULL;
+           }
+         else if (add_symbol_segment != undefined_section
+                  && ! bfd_is_com_section (add_symbol_segment)
+                  && MD_APPLY_SYM_VALUE (fixP))
+           add_number += S_GET_VALUE (fixP->fx_addsy);
+       }
+
+      if (fixP->fx_pcrel)
+       {
+         add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment);
+         if (!fixP->fx_done && fixP->fx_addsy == NULL)
+           {
+             /* There was no symbol required by this relocation.
+                However, BFD doesn't really handle relocations
+                without symbols well. So fake up a local symbol in
+                the absolute section.  */
+             fixP->fx_addsy = abs_section_sym;
+           }
+       }
+
+      if (!fixP->fx_done)
+       md_apply_fix (fixP, &add_number, this_segment);
+
+      if (!fixP->fx_done)
+       {
+         if (fixP->fx_addsy == NULL)
+           fixP->fx_addsy = abs_section_sym;
+         symbol_mark_used_in_reloc (fixP->fx_addsy);
+         if (fixP->fx_subsy != NULL)
+           symbol_mark_used_in_reloc (fixP->fx_subsy);
+       }
+
+      if (!fixP->fx_no_overflow && fixP->fx_size != 0)
+       {
+         if (fixP->fx_size < sizeof (valueT))
+           {
+             valueT mask;
+
+             mask = 0;
+             mask--;           /* Set all bits to one.  */
+             mask <<= fixP->fx_size * 8 - (fixP->fx_signed ? 1 : 0);
+             if ((add_number & mask) != 0 && (add_number & mask) != mask)
+               {
+                 char buf[50], buf2[50];
+                 sprint_value (buf, fragP->fr_address + fixP->fx_where);
+                 if (add_number > 1000)
+                   sprint_value (buf2, add_number);
+                 else
+                   sprintf (buf2, "%ld", (long) add_number);
+                 as_bad_where (fixP->fx_file, fixP->fx_line,
+                               ngettext ("value of %s too large for field "
+                                         "of %d byte at %s",
+                                         "value of %s too large for field "
+                                         "of %d bytes at %s",
+                                         fixP->fx_size),
+                               buf2, fixP->fx_size, buf);
+               } /* Generic error checking.  */
+           }
+#ifdef WARN_SIGNED_OVERFLOW_WORD
+         /* Warn if a .word value is too large when treated as a signed
+            number.  We already know it is not too negative.  This is to
+            catch over-large switches generated by gcc on the 68k.  */
+         if (!flag_signed_overflow_ok
+             && fixP->fx_size == 2
+             && add_number > 0x7fff)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("signed .word overflow; switch may be too large; %ld at 0x%lx"),
+                         (long) add_number,
+                         (long) (fragP->fr_address + fixP->fx_where));
+#endif
+       }
+
+#ifdef TC_VALIDATE_FIX
+    skip:  ATTRIBUTE_UNUSED_LABEL
+      ;
+#endif
+#ifdef DEBUG5
+      fprintf (stderr, "result:\n");
+      print_fixup (fixP);
+#endif
+    }                          /* For each fixS in this segment.  */
+}
+
+static void
+fix_segment (bfd *abfd ATTRIBUTE_UNUSED,
+            asection *sec,
+            void *xxx ATTRIBUTE_UNUSED)
+{
+  segment_info_type *seginfo = seg_info (sec);
+
+  fixup_segment (seginfo->fix_root, sec);
+}
+
+static void
+install_reloc (asection *sec, arelent *reloc, fragS *fragp,
+              const char *file, unsigned int line)
+{
+  char *err;
+  bfd_reloc_status_type s;
+  asymbol *sym;
+
+  if (reloc->sym_ptr_ptr != NULL
+      && (sym = *reloc->sym_ptr_ptr) != NULL
+      && (sym->flags & BSF_KEEP) == 0
+      && ((sym->flags & BSF_SECTION_SYM) == 0
+         || (EMIT_SECTION_SYMBOLS
+             && !bfd_is_abs_section (sym->section))))
+    as_bad_where (file, line, _("redefined symbol cannot be used on reloc"));
+
+  s = bfd_install_relocation (stdoutput, reloc,
+                             fragp->fr_literal, fragp->fr_address,
+                             sec, &err);
+  switch (s)
+    {
+    case bfd_reloc_ok:
+      break;
+    case bfd_reloc_overflow:
+      as_bad_where (file, line, _("relocation overflow"));
+      break;
+    case bfd_reloc_outofrange:
+      as_bad_where (file, line, _("relocation out of range"));
+      break;
+    default:
+      as_fatal (_("%s:%u: bad return from bfd_install_relocation: %x"),
+               file, line, s);
     }
-  n = i;
+}
+
+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;
+
+  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 ATTRIBUTE_UNUSED, asection *sec,
+             void *xxx ATTRIBUTE_UNUSED)
+{
+  segment_info_type *seginfo = seg_info (sec);
+  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.  */
+  if (seginfo == NULL)
+    return;
+
+  n = 0;
+  for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
+    if (!fixp->fx_done)
+      n++;
+
+#ifdef RELOC_EXPANSION_POSSIBLE
+  n *= MAX_RELOC_EXPANSION;
+#endif
+
+  /* Extract relocs for this section from reloc_list.  */
+  rp = &reloc_list;
+
+  my_reloc_list = NULL;
+  while ((r = *rp) != NULL)
+    {
+      if (r->u.b.sec == sec)
+       {
+         *rp = r->next;
+         r->next = my_reloc_list;
+         my_reloc_list = r;
+         n++;
+       }
+      else
+       rp = &r->next;
+    }
+
+  relocs = XCNEWVEC (arelent *, n);
+
+  n = 0;
+  r = my_reloc_list;
+  last_frag = NULL;
+  for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
+    {
+      int fx_size, slack;
+      valueT loc;
+      arelent **reloc;
+#ifndef RELOC_EXPANSION_POSSIBLE
+      arelent *rel;
+
+      reloc = &rel;
+#endif
+
+      if (fixp->fx_done)
+       continue;
+
+      fx_size = fixp->fx_size;
+      slack = TC_FX_SIZE_SLACK (fixp);
+      if (slack > 0)
+       fx_size = fx_size > slack ? fx_size - slack : 0;
+      loc = fixp->fx_where + fx_size;
+      if (slack >= 0 && loc > fixp->fx_frag->fr_fix)
+       as_bad_where (fixp->fx_file, fixp->fx_line,
+                     _("internal error: fixup not contained within frag"));
+
+#ifndef RELOC_EXPANSION_POSSIBLE
+      *reloc = tc_gen_reloc (sec, fixp);
+#else
+      reloc = tc_gen_reloc (sec, fixp);
 #endif
 
+      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
+       }
+    }
+
+  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;
+    }
+
 #ifdef DEBUG4
   {
-    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 ();
@@ -1065,11 +1345,12 @@ write_relocs (bfd *abfd, asection *sec, PTR xxx ATTRIBUTE_UNUSED)
 #endif
 
   if (n)
-    bfd_set_reloc (stdoutput, sec, relocs, n);
-  else
-    bfd_set_section_flags (abfd, sec,
-                          (bfd_get_section_flags (abfd, sec)
-                           & (flagword) ~SEC_RELOC));
+    {
+      flagword flags = bfd_section_flags (sec);
+      flags |= SEC_RELOC;
+      bfd_set_section_flags (sec, flags);
+      bfd_set_reloc (stdoutput, sec, relocs, n);
+    }
 
 #ifdef SET_SECTION_RELOCS
   SET_SECTION_RELOCS (sec, relocs, n);
@@ -1077,25 +1358,241 @@ write_relocs (bfd *abfd, asection *sec, PTR xxx ATTRIBUTE_UNUSED)
 
 #ifdef DEBUG3
   {
-    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;
-       fprintf (stderr, "  reloc %2d @%08x off %4x : sym %-10s addend %x\n",
-                i, r, r->address, s->name, r->addend);
+       arelent *rel = relocs[k];
+       asymbol *s = *rel->sym_ptr_ptr;
+       fprintf (stderr, "  reloc %2d @%p off %4lx : sym %-10s addend %lx\n",
+                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_section_flags (sec);
+  unsigned int header_size, compression_header_size;
+
+  if (seginfo == NULL
+      || sec->size < 32
+      || (flags & (SEC_ALLOC | SEC_HAS_CONTENTS)) == SEC_ALLOC)
+    return;
+
+  section_name = bfd_section_name (sec);
+  if (strncmp (section_name, ".debug_", 7) != 0)
+    return;
+
+  strm = compress_init ();
+  if (strm == NULL)
+    return;
+
+  if (flag_compress_debug == COMPRESS_DEBUG_GABI_ZLIB)
+    {
+      compression_header_size
+       = bfd_get_compression_header_size (stdoutput, NULL);
+      header_size = compression_header_size;
+    }
+  else
+    {
+      compression_header_size = 0;
+      header_size = 12;
+    }
+
+  /* Create a new frag to contain the compression header.  */
+  first_newf = frag_alloc (ob);
+  if (obstack_room (ob) < header_size)
+    first_newf = frag_alloc (ob);
+  if (obstack_room (ob) < header_size)
+    as_fatal (ngettext ("can't extend frag %lu char",
+                       "can't extend frag %lu chars",
+                       (unsigned long) header_size),
+             (unsigned long) header_size);
+  last_newf = first_newf;
+  obstack_blank_fast (ob, header_size);
+  last_newf->fr_type = rs_fill;
+  last_newf->fr_fix = header_size;
+  header = last_newf->fr_literal;
+  compressed_size = header_size;
+
+  /* Stream the frags through the compression engine, adding new frags
+     as necessary to accommodate 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;
+    }
+
+  /* PR binutils/18087: If compression didn't make the section smaller,
+     just keep it uncompressed.  */
+  if (compressed_size >= uncompressed_size)
+    return;
+
+  /* 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 (sec, compressed_size);
+  gas_assert (x);
+  if (!compression_header_size)
+    {
+      compressed_name = concat (".z", section_name + 1, (char *) NULL);
+      bfd_rename_section (sec, compressed_name);
+    }
+}
+
+#ifndef md_generate_nops
+/* Genenerate COUNT bytes of no-op instructions to WHERE.  A target
+   backend must override this with proper no-op instructions.   */
+
+static void
+md_generate_nops (fragS *f ATTRIBUTE_UNUSED,
+                 char *where ATTRIBUTE_UNUSED,
+                 offsetT count ATTRIBUTE_UNUSED,
+                 int control ATTRIBUTE_UNUSED)
+{
+  as_bad (_("unimplemented .nops directive"));
+}
+#endif
+
 static void
 write_contents (bfd *abfd ATTRIBUTE_UNUSED,
                asection *sec,
-               PTR xxx ATTRIBUTE_UNUSED)
+               void *xxx ATTRIBUTE_UNUSED)
 {
   segment_info_type *seginfo = seg_info (sec);
   addressT offset = 0;
@@ -1103,7 +1600,7 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
 
   /* Write out the frags.  */
   if (seginfo == NULL
-      || !(bfd_get_section_flags (abfd, sec) & SEC_HAS_CONTENTS))
+      || !(bfd_section_flags (sec) & SEC_HAS_CONTENTS))
     return;
 
   for (f = seginfo->frchainP->frch_root;
@@ -1115,24 +1612,53 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
       char *fill_literal;
       offsetT count;
 
-      assert (f->fr_type == rs_fill);
+      gas_assert (f->fr_type == rs_fill || f->fr_type == rs_fill_nop);
       if (f->fr_fix)
        {
          x = bfd_set_section_contents (stdoutput, sec,
                                        f->fr_literal, (file_ptr) offset,
                                        (bfd_size_type) f->fr_fix);
          if (!x)
-           {
-             bfd_perror (stdoutput->filename);
-             as_perror (_("FATAL: Can't write %s"), stdoutput->filename);
-             exit (EXIT_FAILURE);
-           }
+           as_fatal (ngettext ("can't write %ld byte "
+                               "to section %s of %s: '%s'",
+                               "can't write %ld bytes "
+                               "to section %s of %s: '%s'",
+                               (long) f->fr_fix),
+                     (long) f->fr_fix,
+                     sec->name, stdoutput->filename,
+                     bfd_errmsg (bfd_get_error ()));
          offset += f->fr_fix;
        }
-      fill_literal = f->fr_literal + f->fr_fix;
+
       fill_size = f->fr_var;
       count = f->fr_offset;
-      assert (count >= 0);
+      fill_literal = f->fr_literal + f->fr_fix;
+
+      if (f->fr_type == rs_fill_nop)
+       {
+         gas_assert (count >= 0 && fill_size == 1);
+         if (count > 0)
+           {
+             char *buf = xmalloc (count);
+             md_generate_nops (f, buf, count, *fill_literal);
+             x = bfd_set_section_contents
+               (stdoutput, sec, buf, (file_ptr) offset,
+                (bfd_size_type) count);
+             if (!x)
+               as_fatal (ngettext ("can't fill %ld byte "
+                                   "in section %s of %s: '%s'",
+                                   "can't fill %ld bytes "
+                                   "in section %s of %s: '%s'",
+                                   (long) count), (long) count,
+                                   sec->name, stdoutput->filename,
+                                   bfd_errmsg (bfd_get_error ()));
+             offset += count;
+             free (buf);
+           }
+         continue;
+       }
+
+      gas_assert (count >= 0);
       if (fill_size && count)
        {
          char buf[256];
@@ -1146,12 +1672,14 @@ write_contents (bfd *abfd ATTRIBUTE_UNUSED,
                                                (file_ptr) offset,
                                                (bfd_size_type) fill_size);
                  if (!x)
-                   {
-                     bfd_perror (stdoutput->filename);
-                     as_perror (_("FATAL: Can't write %s"),
-                                stdoutput->filename);
-                     exit (EXIT_FAILURE);
-                   }
+                   as_fatal (ngettext ("can't fill %ld byte "
+                                       "in section %s of %s: '%s'",
+                                       "can't fill %ld bytes "
+                                       "in section %s of %s: '%s'",
+                                       (long) fill_size),
+                             (long) fill_size,
+                             sec->name, stdoutput->filename,
+                             bfd_errmsg (bfd_get_error ()));
                  offset += fill_size;
                }
            }
@@ -1180,133 +1708,31 @@ 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 (ngettext ("can't fill %ld byte "
+                                       "in section %s of %s: '%s'",
+                                       "can't fill %ld bytes "
+                                       "in section %s of %s: '%s'",
+                                       (long) (n_per_buf * fill_size)),
+                             (long) (n_per_buf * fill_size),
+                             sec->name, stdoutput->filename,
+                             bfd_errmsg (bfd_get_error ()));
                  offset += n_per_buf * fill_size;
                }
            }
        }
     }
 }
-#endif
 
-#if defined(BFD_ASSEMBLER) || (!defined (BFD) && !defined(OBJ_AOUT))
 static void
 merge_data_into_text (void)
 {
-#if defined(BFD_ASSEMBLER) || defined(MANY_SEGMENTS)
   seg_info (text_section)->frchainP->frch_last->fr_next =
     seg_info (data_section)->frchainP->frch_root;
   seg_info (text_section)->frchainP->frch_last =
     seg_info (data_section)->frchainP->frch_last;
   seg_info (data_section)->frchainP = 0;
-#else
-  fixS *tmp;
-
-  text_last_frag->fr_next = data_frag_root;
-  text_last_frag = data_last_frag;
-  data_last_frag = NULL;
-  data_frag_root = NULL;
-  if (text_fix_root)
-    {
-      for (tmp = text_fix_root; tmp->fx_next; tmp = tmp->fx_next);;
-      tmp->fx_next = data_fix_root;
-      text_fix_tail = data_fix_tail;
-    }
-  else
-    text_fix_root = data_fix_root;
-  data_fix_root = NULL;
-#endif
-}
-#endif /* BFD_ASSEMBLER || (! BFD && ! OBJ_AOUT)  */
-
-#if !defined (BFD_ASSEMBLER) && !defined (BFD)
-static void
-relax_and_size_all_segments ()
-{
-  fragS *fragP;
-
-  relax_segment (text_frag_root, SEG_TEXT);
-  relax_segment (data_frag_root, SEG_DATA);
-  relax_segment (bss_frag_root, SEG_BSS);
-
-  /* Now the addresses of frags are correct within the segment.  */
-  know (text_last_frag->fr_type == rs_fill && text_last_frag->fr_offset == 0);
-  H_SET_TEXT_SIZE (&headers, text_last_frag->fr_address);
-  text_last_frag->fr_address = H_GET_TEXT_SIZE (&headers);
-
-  /* Join the 2 segments into 1 huge segment.
-     To do this, re-compute every rn_address in the SEG_DATA frags.
-     Then join the data frags after the text frags.
-
-     Determine a_data [length of data segment].  */
-  if (data_frag_root)
-    {
-      register relax_addressT slide;
-
-      know ((text_last_frag->fr_type == rs_fill)
-           && (text_last_frag->fr_offset == 0));
-
-      H_SET_DATA_SIZE (&headers, data_last_frag->fr_address);
-      data_last_frag->fr_address = H_GET_DATA_SIZE (&headers);
-      slide = H_GET_TEXT_SIZE (&headers);      /* & in file of the data segment.  */
-#ifdef OBJ_BOUT
-#define RoundUp(N,S) (((N)+(S)-1)&-(S))
-      /* For b.out: If the data section has a strict alignment
-        requirement, its load address in the .o file will be
-        rounded up from the size of the text section.  These
-        two values are *not* the same!  Similarly for the bss
-        section....  */
-      slide = RoundUp (slide, 1 << section_alignment[SEG_DATA]);
-#endif
-
-      for (fragP = data_frag_root; fragP; fragP = fragP->fr_next)
-       fragP->fr_address += slide;
-
-      know (text_last_frag != 0);
-      text_last_frag->fr_next = data_frag_root;
-    }
-  else
-    {
-      H_SET_DATA_SIZE (&headers, 0);
-    }
-
-#ifdef OBJ_BOUT
-  /* See above comments on b.out data section address.  */
-  {
-    addressT bss_vma;
-    if (data_last_frag == 0)
-      bss_vma = H_GET_TEXT_SIZE (&headers);
-    else
-      bss_vma = data_last_frag->fr_address;
-    bss_vma = RoundUp (bss_vma, 1 << section_alignment[SEG_BSS]);
-    bss_address_frag.fr_address = bss_vma;
-  }
-#else /* ! OBJ_BOUT  */
-  bss_address_frag.fr_address = (H_GET_TEXT_SIZE (&headers) +
-                                H_GET_DATA_SIZE (&headers));
-
-#endif /* ! OBJ_BOUT  */
-
-  /* Slide all the frags.  */
-  if (bss_frag_root)
-    {
-      relax_addressT slide = bss_address_frag.fr_address;
-
-      for (fragP = bss_frag_root; fragP; fragP = fragP->fr_next)
-       fragP->fr_address += slide;
-    }
-
-  if (bss_last_frag)
-    H_SET_BSS_SIZE (&headers,
-                   bss_last_frag->fr_address - bss_frag_root->fr_address);
-  else
-    H_SET_BSS_SIZE (&headers, 0);
 }
-#endif /* ! BFD_ASSEMBLER && ! BFD  */
 
-#if defined (BFD_ASSEMBLER) || !defined (BFD)
-
-#ifdef BFD_ASSEMBLER
 static void
 set_symtab (void)
 {
@@ -1314,7 +1740,6 @@ set_symtab (void)
   asymbol **asympp;
   symbolS *symp;
   bfd_boolean result;
-  extern PTR bfd_alloc (bfd *, bfd_size_type);
 
   /* Count symbols.  We can't rely on a count made by the loop in
      write_object_file, because *_frob_file may add a new symbol or
@@ -1333,19 +1758,22 @@ set_symtab (void)
       for (i = 0; i < nsyms; i++, symp = symbol_next (symp))
        {
          asympp[i] = symbol_get_bfdsym (symp);
+         if (asympp[i]->flags != BSF_SECTION_SYM
+             || !(bfd_is_const_section (asympp[i]->section)
+                  && asympp[i]->section->symbol == asympp[i]))
+           asympp[i]->flags |= BSF_KEEP;
          symbol_mark_written (symp);
        }
     }
   else
     asympp = 0;
   result = bfd_set_symtab (stdoutput, asympp, nsyms);
-  assert (result);
+  gas_assert (result);
   symbol_table_frozen = 1;
 }
-#endif
 
 /* Finish the subsegments.  After every sub-segment, we fake an
-   ".align ...".  This conforms to BSD4.2 brane-damage.  We then fake
+   ".align ...".  This conforms to BSD4.2 brain-damage.  We then fake
    ".fill 0" because that is the kind of frag that requires least
    thought.  ".align" frags like to have a following frag since that
    makes calculating their intended length trivial.  */
@@ -1356,50 +1784,52 @@ 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 || (FRCHAIN)->frch_next->frch_seg != (SEG)    \
-   ? get_recorded_alignment (SEG) : 0)
+  (!(FRCHAIN)->frch_next && subseg_text_p (SEG)                                \
+   && !do_not_pad_sections_to_alignment                                        \
+   ? get_recorded_alignment (SEG)                                      \
+   : 0)
 #else
-#ifdef BFD_ASSEMBLER
 #define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0
-#else
-#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 2
-#endif
 #endif
 #endif
 
-void
-subsegs_finish (void)
+static void
+subsegs_finish_section (asection *s)
 {
   struct frchain *frchainP;
+  segment_info_type *seginfo = seg_info (s);
+  if (!seginfo)
+    return;
 
-  for (frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next)
+  for (frchainP = seginfo->frchainP;
+       frchainP != NULL;
+       frchainP = frchainP->frch_next)
     {
-      int alignment = 0;
+      int alignment;
 
-      subseg_set (frchainP->frch_seg, 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 ())
+        any alignment is meaningless, and, moreover, will look weird
+        if we are generating a listing.  */
+      if (had_errors ())
+       do_not_pad_sections_to_alignment = 1;
+
+      alignment = SUB_SEGMENT_ALIGN (now_seg, frchainP);
+      if ((bfd_section_flags (now_seg) & SEC_MERGE)
+         && now_seg->entsize)
        {
-         alignment = SUB_SEGMENT_ALIGN (now_seg, frchainP);
-#ifdef BFD_ASSEMBLER
-         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;
            }
-#endif
+
+         if (entalign > alignment)
+           alignment = entalign;
        }
 
       if (subseg_text_p (now_seg))
@@ -1418,91 +1848,263 @@ subsegs_finish (void)
     }
 }
 
+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 == 0)
+    return;
+
+  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 (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);
+}
+
+/* Create a relocation against an entry in a GNU Build attribute section.  */
+
+static void
+create_note_reloc (segT           sec,
+                  symbolS *      sym,
+                  bfd_size_type  note_offset,
+                  bfd_size_type  desc2_offset,
+                  int            reloc_type,
+                  bfd_vma        addend,
+                  char *         note)
+{
+  struct reloc_list * reloc;
+
+  reloc = XNEW (struct reloc_list);
+
+  /* We create a .b type reloc as resolve_reloc_expr_symbols() has already been called.  */
+  reloc->u.b.sec           = sec;
+  reloc->u.b.s             = symbol_get_bfdsym (sym);
+  reloc->u.b.r.sym_ptr_ptr = & reloc->u.b.s;
+  reloc->u.b.r.address     = note_offset + desc2_offset;
+  reloc->u.b.r.addend      = addend;
+  reloc->u.b.r.howto       = bfd_reloc_type_lookup (stdoutput, reloc_type);
+
+  if (reloc->u.b.r.howto == NULL)
+    {
+      as_bad (_("unable to create reloc for build note"));
+      return;
+    }
+
+  reloc->file = N_("<gnu build note>");
+  reloc->line = 0;
+
+  reloc->next = reloc_list;
+  reloc_list = reloc;
+
+  /* For REL relocs, store the addend in the section.  */
+  if (! sec->use_rela_p
+      /* The SH target is a special case that uses RELA relocs
+        but still stores the addend in the word being relocated.  */
+      || strstr (bfd_get_target (stdoutput), "-sh") != NULL)
+    {
+      if (target_big_endian)
+       {
+         if (bfd_arch_bits_per_address (stdoutput) <= 32)
+           note[desc2_offset + 3] = addend;
+         else
+           note[desc2_offset + 7] = addend;
+       }
+      else
+       note[desc2_offset] = addend;
+    }
+}
+
+static void
+maybe_generate_build_notes (void)
+{
+  segT      sec;
+  char *    note;
+  offsetT   note_size;
+  offsetT   total_size;
+  offsetT   desc_size;
+  offsetT   desc2_offset;
+  int       desc_reloc;
+  symbolS * sym;
+  asymbol * bsym;
+
+  if (! flag_generate_build_notes
+      || bfd_get_section_by_name (stdoutput,
+                                 GNU_BUILD_ATTRS_SECTION_NAME) != NULL)
+    return;
+
+  /* Create a GNU Build Attribute section.  */
+  sec = subseg_new (GNU_BUILD_ATTRS_SECTION_NAME, FALSE);
+  elf_section_type (sec) = SHT_NOTE;
+  bfd_set_section_flags (sec, (SEC_READONLY | SEC_HAS_CONTENTS | SEC_DATA
+                              | SEC_OCTETS));
+  bfd_set_section_alignment (sec, 2);
+
+  /* Work out the size of the notes that we will create,
+     and the relocation we should use.  */
+  if (bfd_arch_bits_per_address (stdoutput) <= 32)
+    {
+      note_size = 28;
+      desc_size = 8; /* Two 4-byte offsets.  */
+      desc2_offset = 24;
+
+      /* FIXME: The BFD backend for the CRX target does not support the
+        BFD_RELOC_32, even though it really should.  Likewise for the
+        CR16 target.  So we have special case code here...  */
+      if (strstr (bfd_get_target (stdoutput), "-crx") != NULL)
+       desc_reloc = BFD_RELOC_CRX_NUM32;
+      else if (strstr (bfd_get_target (stdoutput), "-cr16") != NULL)
+       desc_reloc = BFD_RELOC_CR16_NUM32;
+      else
+       desc_reloc = BFD_RELOC_32;
+    }
+  else
+    {
+      note_size = 36;
+      desc_size = 16; /* Two  8-byte offsets.  */
+      desc2_offset = 28;
+      /* FIXME: The BFD backend for the IA64 target does not support the
+        BFD_RELOC_64, even though it really should.  The HPPA backend
+        has a similar issue, although it does not support BFD_RELOCs at
+        all!  So we have special case code to handle these targets.  */
+      if (strstr (bfd_get_target (stdoutput), "-ia64") != NULL)
+       desc_reloc = target_big_endian ? BFD_RELOC_IA64_DIR32MSB : BFD_RELOC_IA64_DIR32LSB;
+      else if (strstr (bfd_get_target (stdoutput), "-hppa") != NULL)
+       desc_reloc = 80; /* R_PARISC_DIR64.  */
+      else
+       desc_reloc = BFD_RELOC_64;
+    }
+  
+  /* We have to create a note for *each* code section.
+     Linker garbage collection might discard some.  */
+  total_size = 0;
+  note = NULL;
+
+  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+    if ((bsym = symbol_get_bfdsym (sym)) != NULL
+       && bsym->flags & BSF_SECTION_SYM
+       && bsym->section != NULL
+       /* Skip linkonce sections - we cannot use these section symbols as they may disappear.  */
+       && (bsym->section->flags & (SEC_CODE | SEC_LINK_ONCE)) == SEC_CODE
+       /* Not all linkonce sections are flagged...  */
+       && strncmp (S_GET_NAME (sym), ".gnu.linkonce", sizeof ".gnu.linkonce" - 1) != 0)
+      {
+       /* Create a version note.  */
+       frag_now_fix ();
+       note = frag_more (note_size);
+       memset (note, 0, note_size);
+
+       if (target_big_endian)
+         {
+           note[3] = 8; /* strlen (name) + 1.  */
+           note[7] = desc_size; /* Two 8-byte offsets.  */
+           note[10] = NT_GNU_BUILD_ATTRIBUTE_OPEN >> 8;
+           note[11] = NT_GNU_BUILD_ATTRIBUTE_OPEN & 0xff;
+         }
+       else
+         {
+           note[0] = 8; /* strlen (name) + 1.  */
+           note[4] = desc_size; /* Two 8-byte offsets.  */
+           note[8] = NT_GNU_BUILD_ATTRIBUTE_OPEN & 0xff;
+           note[9] = NT_GNU_BUILD_ATTRIBUTE_OPEN >> 8;
+         }
+
+       /* The a1 version number indicates that this note was
+          generated by the assembler and not the gcc annobin plugin.  */
+       memcpy (note + 12, "GA$\ 13a1", 8);
+
+       /* Create a relocation to install the start address of the note...  */
+       create_note_reloc (sec, sym, total_size, 20, desc_reloc, 0, note);
+
+       /* ...and another one to install the end address.  */
+       create_note_reloc (sec, sym, total_size, desc2_offset, desc_reloc,
+                          bfd_section_size (bsym->section),
+                          note);
+
+       total_size += note_size;
+       /* FIXME: Maybe add a note recording the assembler command line and version ?  */
+      }
+
+  /* Install the note(s) into the section.  */
+  if (total_size)
+    bfd_set_section_contents (stdoutput, sec, (bfd_byte *) note, 0, total_size);
+  subsegs_finish_section (sec);
+  relax_segment (seg_info (sec)->frchainP->frch_root, sec, 0);
+  size_seg (stdoutput, sec, NULL);
+}
+#endif /* OBJ_ELF */
+
 /* Write the object file.  */
 
 void
 write_object_file (void)
 {
-#if ! defined (BFD_ASSEMBLER) || ! defined (WORKING_DOT_WORD)
+  struct relax_seg_info rsi;
+#ifndef WORKING_DOT_WORD
   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 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_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.  */
+     for each segment. Linked through fr_next.  */
 
-#ifdef BFD_ASSEMBLER
   /* Remove the sections created by gas for its own purposes.  */
   {
-    asection **seclist;
     int i;
 
-    seclist = &stdoutput->sections;
-    while (*seclist)
-      {
-       if (*seclist == reg_section || *seclist == expr_section)
-         {
-           bfd_section_list_remove (stdoutput, seclist);
-           stdoutput->section_count--;
-         }
-       else
-         seclist = &(*seclist)->next;
-      }
+    bfd_section_list_remove (stdoutput, reg_section);
+    bfd_section_list_remove (stdoutput, expr_section);
+    stdoutput->section_count -= 2;
     i = 0;
     bfd_map_over_sections (stdoutput, renumber_sections, &i);
   }
 
   bfd_map_over_sections (stdoutput, chain_frchains_together, (char *) 0);
-#else
-  remove_subsegs (frchain_root, SEG_TEXT, &text_frag_root, &text_last_frag);
-  remove_subsegs (data0_frchainP, SEG_DATA, &data_frag_root, &data_last_frag);
-  remove_subsegs (bss0_frchainP, SEG_BSS, &bss_frag_root, &bss_last_frag);
-#endif
 
   /* We have two segments. If user gave -R flag, then we must put the
      data frags into the text segment. Do this before relaxing so
      we know to take advantage of -R and make shorter addresses.  */
-#if !defined (OBJ_AOUT) || defined (BFD_ASSEMBLER)
   if (flag_readonly_data_in_text)
     {
       merge_data_into_text ();
     }
-#endif
 
-#ifdef BFD_ASSEMBLER
+  rsi.pass = 0;
   while (1)
     {
-      int changed;
-
 #ifndef WORKING_DOT_WORD
       /* We need to reset the markers in the broken word list and
         associated frags between calls to relax_segment (via
@@ -1523,9 +2125,10 @@ write_object_file (void)
        }
 #endif
 
-      changed = 0;
-      bfd_map_over_sections (stdoutput, relax_seg, &changed);
-      if (!changed)
+      rsi.changed = 0;
+      bfd_map_over_sections (stdoutput, relax_seg, &rsi);
+      rsi.pass++;
+      if (!rsi.changed)
        break;
     }
 
@@ -1539,74 +2142,20 @@ write_object_file (void)
   finalize_syms = TC_FINALIZE_SYMS_BEFORE_SIZE_SEG;
 
   bfd_map_over_sections (stdoutput, size_seg, (char *) 0);
-#else
-  relax_and_size_all_segments ();
-#endif /* BFD_ASSEMBLER  */
 
   /* Relaxation has completed.  Freeze all syms.  */
   finalize_syms = 1;
 
+  dwarf2dbg_final_check ();
+
 #ifdef md_post_relax_hook
   md_post_relax_hook;
 #endif
 
-#ifndef BFD_ASSEMBLER
-  /* Crawl the symbol chain.
-
-     For each symbol whose value depends on a frag, take the address of
-     that frag and subsume it into the value of the symbol.
-     After this, there is just one way to lookup a symbol value.
-     Values are left in their final state for object file emission.
-     We adjust the values of 'L' local symbols, even if we do
-     not intend to emit them to the object file, because their values
-     are needed for fix-ups.
-
-     Unless we saw a -L flag, remove all symbols that begin with 'L'
-     from the symbol chain.  (They are still pointed to by the fixes.)
-
-     Count the remaining symbols.
-     Assign a symbol number to each symbol.
-     Count the number of string-table chars we will emit.
-     Put this info into the headers as appropriate.  */
-  know (zero_address_frag.fr_address == 0);
-  string_byte_count = sizeof (string_byte_count);
-
-  obj_crawl_symbol_chain (&headers);
-
-  if (string_byte_count == sizeof (string_byte_count))
-    string_byte_count = 0;
-
-  H_SET_STRING_SIZE (&headers, string_byte_count);
-
-  /* Addresses of frags now reflect addresses we use in the object file.
-     Symbol values are correct.
-     Scan the frags, converting any ".org"s and ".align"s to ".fill"s.
-     Also converting any machine-dependent frags using md_convert_frag();  */
-  subseg_change (SEG_TEXT, 0);
-
-  for (fragP = text_frag_root; fragP; fragP = fragP->fr_next)
-    {
-      /* At this point we have linked all the frags into a single
-         chain.  However, cvt_frag_to_fill may call md_convert_frag
-         which may call fix_new.  We need to ensure that fix_new adds
-         the fixup to the right section.  */
-      if (fragP == data_frag_root)
-       subseg_change (SEG_DATA, 0);
-
-      cvt_frag_to_fill (&headers, SEG_TEXT, fragP);
-
-      /* Some assert macros don't work with # directives mixed in.  */
-#ifndef NDEBUG
-      if (!(fragP->fr_next == NULL
-#ifdef OBJ_BOUT
-           || fragP->fr_next == data_frag_root
-#endif
-           || ((offsetT) (fragP->fr_next->fr_address - fragP->fr_address)
-               == (fragP->fr_fix + fragP->fr_offset * fragP->fr_var))))
-       abort ();
+#ifdef OBJ_ELF
+  if (IS_ELF)
+    create_obj_attrs_section ();
 #endif
-    }
-#endif /* ! BFD_ASSEMBLER  */
 
 #ifndef WORKING_DOT_WORD
   {
@@ -1624,33 +2173,15 @@ write_object_file (void)
          exp.X_add_symbol = lie->add;
          exp.X_op_symbol = lie->sub;
          exp.X_add_number = lie->addnum;
-#ifdef BFD_ASSEMBLER
 #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,
                       2, &exp, 0, BFD_RELOC_16);
 #endif
-#else
-#if defined(TC_SPARC) || defined(TC_A29K) || defined(NEED_FX_R_TYPE)
-         fix_new_exp (lie->frag,
-                      lie->word_goes_here - lie->frag->fr_literal,
-                      2, &exp, 0, NO_RELOC);
-#else
-#ifdef TC_NS32K
-         fix_new_ns32k_exp (lie->frag,
-                            lie->word_goes_here - lie->frag->fr_literal,
-                            2, &exp, 0, 0, 2, 0, 0);
-#else
-         fix_new_exp (lie->frag,
-                      lie->word_goes_here - lie->frag->fr_literal,
-                      2, &exp, 0, 0);
-#endif /* TC_NS32K  */
-#endif /* TC_SPARC|TC_A29K|NEED_FX_R_TYPE  */
-#endif /* BFD_ASSEMBLER  */
          *prevP = lie->next_broken_word;
        }
       else
@@ -1694,18 +2225,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.  */
@@ -1721,132 +2256,6 @@ write_object_file (void)
   }
 #endif /* not WORKING_DOT_WORD  */
 
-#ifndef BFD_ASSEMBLER
-#ifndef        OBJ_VMS
-  {                            /* not vms  */
-    char *the_object_file;
-    long object_file_size;
-    /* Scan every FixS performing fixups. We had to wait until now to
-       do this because md_convert_frag() may have made some fixSs.  */
-    int trsize, drsize;
-
-    subseg_change (SEG_TEXT, 0);
-    trsize = md_reloc_size * fixup_segment (text_fix_root, SEG_TEXT);
-    subseg_change (SEG_DATA, 0);
-    drsize = md_reloc_size * fixup_segment (data_fix_root, SEG_DATA);
-    H_SET_RELOCATION_SIZE (&headers, trsize, drsize);
-
-    /* FIXME: Move this stuff into the pre-write-hook.  */
-    H_SET_MAGIC_NUMBER (&headers, magic_number_for_object_file);
-    H_SET_ENTRY_POINT (&headers, 0);
-
-    obj_pre_write_hook (&headers);     /* Extra coff stuff.  */
-
-    object_file_size = H_GET_FILE_SIZE (&headers);
-    next_object_file_charP = the_object_file = xmalloc (object_file_size);
-
-    output_file_create (out_file_name);
-
-    obj_header_append (&next_object_file_charP, &headers);
-
-    know ((next_object_file_charP - the_object_file)
-         == H_GET_HEADER_SIZE (&headers));
-
-    /* Emit code.  */
-    for (fragP = text_frag_root; fragP; fragP = fragP->fr_next)
-      {
-       register long count;
-       register char *fill_literal;
-       register long fill_size;
-
-       PROGRESS (1);
-       know (fragP->fr_type == rs_fill);
-       append (&next_object_file_charP, fragP->fr_literal,
-               (unsigned long) fragP->fr_fix);
-       fill_literal = fragP->fr_literal + fragP->fr_fix;
-       fill_size = fragP->fr_var;
-       know (fragP->fr_offset >= 0);
-
-       for (count = fragP->fr_offset; count; count--)
-         append (&next_object_file_charP, fill_literal,
-                 (unsigned long) fill_size);
-      }
-
-    know ((next_object_file_charP - the_object_file)
-         == (H_GET_HEADER_SIZE (&headers)
-             + H_GET_TEXT_SIZE (&headers)
-             + H_GET_DATA_SIZE (&headers)));
-
-    /* Emit relocations.  */
-    obj_emit_relocations (&next_object_file_charP, text_fix_root,
-                         (relax_addressT) 0);
-    know ((next_object_file_charP - the_object_file)
-         == (H_GET_HEADER_SIZE (&headers)
-             + H_GET_TEXT_SIZE (&headers)
-             + H_GET_DATA_SIZE (&headers)
-             + H_GET_TEXT_RELOCATION_SIZE (&headers)));
-#ifdef TC_I960
-    /* Make addresses in data relocation directives relative to beginning of
-       first data fragment, not end of last text fragment:  alignment of the
-       start of the data segment may place a gap between the segments.  */
-    obj_emit_relocations (&next_object_file_charP, data_fix_root,
-                         data0_frchainP->frch_root->fr_address);
-#else /* TC_I960  */
-    obj_emit_relocations (&next_object_file_charP, data_fix_root,
-                         text_last_frag->fr_address);
-#endif /* TC_I960  */
-
-    know ((next_object_file_charP - the_object_file)
-         == (H_GET_HEADER_SIZE (&headers)
-             + H_GET_TEXT_SIZE (&headers)
-             + H_GET_DATA_SIZE (&headers)
-             + H_GET_TEXT_RELOCATION_SIZE (&headers)
-             + H_GET_DATA_RELOCATION_SIZE (&headers)));
-
-    /* Emit line number entries.  */
-    OBJ_EMIT_LINENO (&next_object_file_charP, lineno_rootP, the_object_file);
-    know ((next_object_file_charP - the_object_file)
-         == (H_GET_HEADER_SIZE (&headers)
-             + H_GET_TEXT_SIZE (&headers)
-             + H_GET_DATA_SIZE (&headers)
-             + H_GET_TEXT_RELOCATION_SIZE (&headers)
-             + H_GET_DATA_RELOCATION_SIZE (&headers)
-             + H_GET_LINENO_SIZE (&headers)));
-
-    /* Emit symbols.  */
-    obj_emit_symbols (&next_object_file_charP, symbol_rootP);
-    know ((next_object_file_charP - the_object_file)
-         == (H_GET_HEADER_SIZE (&headers)
-             + H_GET_TEXT_SIZE (&headers)
-             + H_GET_DATA_SIZE (&headers)
-             + H_GET_TEXT_RELOCATION_SIZE (&headers)
-             + H_GET_DATA_RELOCATION_SIZE (&headers)
-             + H_GET_LINENO_SIZE (&headers)
-             + H_GET_SYMBOL_TABLE_SIZE (&headers)));
-
-    /* Emit strings.  */
-    if (string_byte_count > 0)
-      obj_emit_strings (&next_object_file_charP);
-
-#ifdef BFD_HEADERS
-    bfd_seek (stdoutput, (file_ptr) 0, 0);
-    bfd_bwrite (the_object_file, (bfd_size_type) object_file_size, stdoutput);
-#else
-
-    /* Write the data to the file.  */
-    output_file_append (the_object_file, object_file_size, out_file_name);
-    free (the_object_file);
-#endif
-  }
-#else /* OBJ_VMS  */
-  /* Now do the VMS-dependent part of writing the object file.  */
-  vms_write_object_file (H_GET_TEXT_SIZE (&headers),
-                        H_GET_DATA_SIZE (&headers),
-                        H_GET_BSS_SIZE (&headers),
-                        text_frag_root, data_frag_root);
-#endif /* OBJ_VMS  */
-#else /* BFD_ASSEMBLER  */
-
   /* Resolve symbol values.  This needs to be done before processing
      the relocations.  */
   if (symbol_rootP)
@@ -1857,6 +2266,12 @@ write_object_file (void)
        resolve_symbol_value (symp);
     }
   resolve_local_symbol_values ();
+  resolve_reloc_expr_symbols ();
+
+#ifdef OBJ_ELF
+  if (IS_ELF)
+    maybe_generate_build_notes ();
+#endif
 
   PROGRESS (1);
 
@@ -1882,12 +2297,22 @@ write_object_file (void)
   if (symbol_rootP)
     {
       symbolS *symp;
+      bfd_boolean skip_next_symbol = FALSE;
 
       for (symp = symbol_rootP; symp; symp = symbol_next (symp))
        {
          int punt = 0;
          const char *name;
 
+         if (skip_next_symbol)
+           {
+             /* Don't do anything besides moving the value of the
+                symbol from the GAS value-field to the BFD value-field.  */
+             symbol_get_bfdsym (symp)->value = S_GET_VALUE (symp);
+             skip_next_symbol = FALSE;
+             continue;
+           }
+
          if (symbol_mri_common_p (symp))
            {
              if (S_IS_EXTERNAL (symp))
@@ -1914,26 +2339,32 @@ write_object_file (void)
          resolve_symbol_value (symp);
 
          /* Skip symbols which were equated to undefined or common
-             symbols.  */
-         if (symbol_equated_reloc_p (symp))
+            symbols.  */
+         if (symbol_equated_reloc_p (symp)
+             || S_IS_WEAKREFR (symp))
            {
+             const char *sname = S_GET_NAME (symp);
+
+             if (S_IS_COMMON (symp)
+                 && !TC_FAKE_LABEL (sname)
+                 && !S_IS_WEAKREFR (symp))
+               {
+                 expressionS *e = symbol_get_value_expression (symp);
+
+                 as_bad (_("`%s' can't be equated to common symbol `%s'"),
+                         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'"),
+                           sname);
+               }
              symbol_remove (symp, &symbol_rootP, &symbol_lastP);
              continue;
            }
 
-         /* So far, common symbols have been treated like undefined symbols.
-            Put them in the common section now.  */
-         if (S_IS_DEFINED (symp) == 0
-             && S_GET_VALUE (symp) != 0)
-           S_SET_SEGMENT (symp, bfd_com_section_ptr);
-#if 0
-         printf ("symbol `%s'\n\t@%x: value=%d flags=%x seg=%s\n",
-                 S_GET_NAME (symp), symp,
-                 S_GET_VALUE (symp),
-                 symbol_get_bfdsym (symp)->flags,
-                 segment_name (S_GET_SEGMENT (symp)));
-#endif
-
 #ifdef obj_frob_symbol
          obj_frob_symbol (symp, punt);
 #endif
@@ -1950,11 +2381,12 @@ write_object_file (void)
          if (symp == abs_section_sym
              || (! EMIT_SECTION_SYMBOLS
                  && symbol_section_p (symp))
-             /* Note that S_IS_EXTERN and S_IS_LOCAL are not always
+             /* Note that S_IS_EXTERNAL and S_IS_LOCAL are not always
                 opposites.  Sometimes the former checks flags and the
                 latter examines the name...  */
-             || (!S_IS_EXTERN (symp)
-                 && (punt || S_IS_LOCAL (symp))
+             || (!S_IS_EXTERNAL (symp)
+                 && (punt || S_IS_LOCAL (symp) ||
+                     (S_IS_WEAKREFD (symp) && ! symbol_used_p (symp)))
                  && ! symbol_used_in_reloc_p (symp)))
            {
              symbol_remove (symp, &symbol_rootP, &symbol_lastP);
@@ -1976,6 +2408,12 @@ write_object_file (void)
          /* Set the value into the BFD symbol.  Up til now the value
             has only been kept in the gas symbolS struct.  */
          symbol_get_bfdsym (symp)->value = S_GET_VALUE (symp);
+
+         /* A warning construct is a warning symbol followed by the
+            symbol warned about.  Don't let anything object-format or
+            target-specific muck with it; it's ready for output.  */
+         if (symbol_get_bfdsym (symp)->flags & BSF_WARNING)
+           skip_next_symbol = TRUE;
        }
     }
 
@@ -1990,6 +2428,10 @@ write_object_file (void)
   obj_adjust_symtab ();
 #endif
 
+  /* Stop if there is an error.  */
+  if (had_errors ())
+    return;
+
   /* Now that all the sizes are known, and contents correct, we can
      start writing to the file.  */
   set_symtab ();
@@ -2004,6 +2446,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);
 
@@ -2014,12 +2459,31 @@ write_object_file (void)
   obj_frob_file_after_relocs ();
 #endif
 
+#if defined OBJ_ELF || defined OBJ_MAYBE_ELF
+  if (IS_ELF && flag_use_elf_stt_common)
+    stdoutput->flags |= BFD_CONVERT_ELF_COMMON | BFD_USE_ELF_STT_COMMON;
+#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)
+    {
+      if (flag_compress_debug == COMPRESS_DEBUG_GABI_ZLIB)
+       stdoutput->flags |= BFD_COMPRESS | BFD_COMPRESS_GABI;
+      else
+       stdoutput->flags |= BFD_COMPRESS;
+      bfd_map_over_sections (stdoutput, compress_debug, (char *) 0);
+    }
+
   bfd_map_over_sections (stdoutput, write_contents, (char *) 0);
-#endif /* BFD_ASSEMBLER  */
 }
-#endif /* ! BFD  */
 
 #ifdef TC_GENERIC_RELAX_TABLE
+#ifndef md_generic_table_relax_frag
+#define md_generic_table_relax_frag relax_frag
+#endif
 
 /* Relax a fragment by scanning TC_GENERIC_RELAX_TABLE.  */
 
@@ -2038,7 +2502,7 @@ relax_frag (segT segment, fragS *fragP, long stretch)
   const relax_typeS *table;
 
   target = fragP->fr_offset;
-  address = fragP->fr_address;
+  address = fragP->fr_address + fragP->fr_fix;
   table = TC_GENERIC_RELAX_TABLE;
   this_state = fragP->fr_subtype;
   start_type = this_type = table + this_state;
@@ -2051,43 +2515,45 @@ relax_frag (segT segment, fragS *fragP, long stretch)
       sym_frag = symbol_get_frag (symbolP);
 
 #ifndef DIFF_EXPR_OK
-#if !defined (MANY_SEGMENTS) && !defined (BFD_ASSEMBLER)
-      know ((S_GET_SEGMENT (symbolP) == SEG_ABSOLUTE)
-           || (S_GET_SEGMENT (symbolP) == SEG_DATA)
-           || (S_GET_SEGMENT (symbolP) == SEG_BSS)
-           || (S_GET_SEGMENT (symbolP) == SEG_TEXT));
-#endif
       know (sym_frag != NULL);
 #endif
       know (S_GET_SEGMENT (symbolP) != absolute_section
            || 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)
+           return 0;
        }
     }
 
-  aim = target - address - fragP->fr_fix;
+  aim = target - address;
 #ifdef TC_PCREL_ADJUST
-  /* Currently only the ns32k family needs this.  */
+  /* Currently only the ns32k and arc needs this.  */
   aim += TC_PCREL_ADJUST (fragP);
-/* #else */
-  /* This machine doesn't want to use pcrel_adjust.
-     In that case, pcrel_adjust should be zero.  */
-#if 0
-  assert (fragP->fr_targ.ns32k.pcrel_adjust == 0);
 #endif
-#endif
-#ifdef md_prepare_relax_scan /* formerly called M68K_AIM_KLUDGE  */
+
+#ifdef md_prepare_relax_scan
+  /* Formerly called M68K_AIM_KLUDGE.  */
   md_prepare_relax_scan (fragP, address, aim, this_state, this_type);
 #endif
 
@@ -2131,13 +2597,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)
@@ -2159,23 +2625,26 @@ relax_align (register relax_addressT address,   /* Address now.  */
    addresses.  */
 
 int
-relax_segment (struct frag *segment_frag_root, segT segment)
+relax_segment (struct frag *segment_frag_root, segT segment, int pass)
 {
-  register struct frag *fragP;
-  register relax_addressT address;
+  unsigned long frag_count;
+  struct frag *fragP;
+  relax_addressT address;
+  int region;
   int ret;
 
-#if !defined (MANY_SEGMENTS) && !defined (BFD_ASSEMBLER)
-  know (segment == SEG_DATA || segment == SEG_TEXT || segment == SEG_BSS);
-#endif
   /* In case md_estimate_size_before_relax() wants to make fixSs.  */
   subseg_change (segment, 0);
 
   /* For each frag in segment: count and store  (a 1st guess of)
      fr_address.  */
   address = 0;
-  for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next)
+  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;
@@ -2198,18 +2667,27 @@ relax_segment (struct frag *segment_frag_root, segT segment)
            if (offset % fragP->fr_var != 0)
              {
                as_bad_where (fragP->fr_file, fragP->fr_line,
-                             _("alignment padding (%lu bytes) not a multiple of %ld"),
+                             ngettext ("alignment padding (%lu byte) "
+                                       "not a multiple of %ld",
+                                       "alignment padding (%lu bytes) "
+                                       "not a multiple of %ld",
+                                       (unsigned long) offset),
                              (unsigned long) offset, (long) fragP->fr_var);
                offset -= (offset % fragP->fr_var);
              }
 
            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:
+       case rs_space_nop:
          break;
 
        case rs_machine_dependent:
@@ -2250,14 +2728,55 @@ relax_segment (struct frag *segment_frag_root, segT segment)
 
   /* Do relax().  */
   {
-    offsetT stretch;   /* May be any size, 0 or negative.  */
-    /* Cumulative number of addresses we have relaxed this pass.
-       We may have relaxed more than one address.  */
-    int stretched;     /* Have we stretched on this pass?  */
-    /* This is 'cuz stretch may be zero, when, in fact some piece of code
-       grew, and another shrank.  If a branch instruction doesn't fit anymore,
-       we could be scrod.  */
-
+    unsigned long max_iterations;
+
+    /* Cumulative address adjustment.  */
+    offsetT stretch;
+
+    /* Have we made any adjustment this pass?  We can't just test
+       stretch because one piece of code may have grown and another
+       shrank.  */
+    int stretched;
+
+    /* Most horrible, but gcc may give us some exception data that
+       is impossible to assemble, of the form
+
+       .align 4
+       .byte 0, 0
+       .uleb128 end - start
+       start:
+       .space 128*128 - 1
+       .align 4
+       end:
+
+       If the leb128 is two bytes in size, then end-start is 128*128,
+       which requires a three byte leb128.  If the leb128 is three
+       bytes in size, then end-start is 128*128-1, which requires a
+       two byte leb128.  We work around this dilemma by inserting
+       an extra 4 bytes of alignment just after the .align.  This
+       works because the data after the align is accessed relative to
+       the end label.
+
+       This counter is used in a tiny state machine to detect
+       whether a leb128 followed by an align is impossible to
+       relax.  */
+    int rs_leb128_fudge = 0;
+
+    /* We want to prevent going into an infinite loop where one frag grows
+       depending upon the location of a symbol which is in turn moved by
+       the growing frag.  eg:
+
+        foo = .
+        .org foo+16
+        foo = .
+
+       So we dictate that this algorithm can be at most O2.  */
+    max_iterations = frag_count * frag_count;
+    /* Check for overflow.  */
+    if (max_iterations < frag_count)
+      max_iterations = frag_count;
+
+    ret = 0;
     do
       {
        stretch = 0;
@@ -2320,13 +2839,17 @@ relax_segment (struct frag *segment_frag_root, segT segment)
                                             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)
@@ -2337,6 +2860,8 @@ relax_segment (struct frag *segment_frag_root, segT segment)
                                untruth->added = 2;
                                untruth->use_jump = lie;
                              }
+
+                         lie->added = 1;
                          growth += md_long_jump_size;
                        }
                    }
@@ -2364,6 +2889,48 @@ relax_segment (struct frag *segment_frag_root, segT segment)
                    }
 
                  growth = newoff - oldoff;
+
+                 /* If this align happens to follow a leb128 and
+                    we have determined that the leb128 is bouncing
+                    in size, then break the cycle by inserting an
+                    extra alignment.  */
+                 if (growth < 0
+                     && (rs_leb128_fudge & 16) != 0
+                     && (rs_leb128_fudge & 15) >= 2)
+                   {
+                     segment_info_type *seginfo = seg_info (segment);
+                     struct obstack *ob = &seginfo->frchainP->frch_obstack;
+                     struct frag *newf;
+
+                     newf = frag_alloc (ob);
+                     obstack_blank_fast (ob, fragP->fr_var);
+                     obstack_finish (ob);
+                     memcpy (newf, fragP, SIZEOF_STRUCT_FRAG);
+                     memcpy (newf->fr_literal,
+                             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);
+                     if (newf->fr_offset * newf->fr_var
+                         != (offsetT) 1 << fragP->fr_offset)
+                       {
+                         newf->fr_offset = (offsetT) 1 << fragP->fr_offset;
+                         newf->fr_var = 1;
+                       }
+                     /* Include size of new frag in GROWTH.  */
+                     growth += newf->fr_offset * newf->fr_var;
+                     /* 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
+                     as_warn (_("padding added"));
+#endif
+                   }
                }
                break;
 
@@ -2374,49 +2941,58 @@ relax_segment (struct frag *segment_frag_root, segT segment)
 
                  if (symbolP)
                    {
-#if !defined (MANY_SEGMENTS) && !defined (BFD_ASSEMBLER)
-                     know ((S_GET_SEGMENT (symbolP) == SEG_ABSOLUTE)
-                           || (S_GET_SEGMENT (symbolP) == SEG_DATA)
-                           || (S_GET_SEGMENT (symbolP) == SEG_TEXT)
-                           || S_GET_SEGMENT (symbolP) == SEG_BSS);
-                     know (symbolP->sy_frag);
-                     know (!(S_GET_SEGMENT (symbolP) == SEG_ABSOLUTE)
-                           || (symbolP->sy_frag == &zero_address_frag));
-#endif
-                      /* Convert from an actual address to an octet offset
-                         into the section.  Here it is assumed that the
-                         section's VMA is zero, and can omit subtracting it
-                         from the symbol's value to get the address offset.  */
-                      know (S_GET_SECTION (symbolP)->vma == 0);
+                     /* Convert from an actual address to an octet offset
+                        into the section.  Here it is assumed that the
+                        section's VMA is zero, and can omit subtracting it
+                        from the symbol's value to get the address offset.  */
+                     know (S_GET_SEGMENT (symbolP)->vma == 0);
                      target += S_GET_VALUE (symbolP) * OCTETS_PER_BYTE;
                    }
 
                  know (fragP->fr_next);
-                 after = fragP->fr_next->fr_address;
+                 after = fragP->fr_next->fr_address + stretch;
                  growth = target - after;
-                 if (growth < 0)
+
+                 /* Growth may be negative, but variable part of frag
+                    cannot have fewer than 0 chars.  That is, we can't
+                    .org backwards.  */
+                 if (address + fragP->fr_fix > target)
                    {
-                     /* Growth may be negative, but variable part of frag
-                        cannot have fewer than 0 chars.  That is, we can't
-                        .org backwards.  */
+                     growth = 0;
+
+                     /* Don't error on first few frag relax passes.
+                        The symbol might be an expression involving
+                        symbol values from other sections.  If those
+                        sections have not yet been processed their
+                        frags will all have zero addresses, so we
+                        will calculate incorrect values for them.  The
+                        number of passes we allow before giving an
+                        error is somewhat arbitrary.  It should be at
+                        least one, with larger values requiring
+                        increasingly contrived dependencies between
+                        frags to trigger a false error.  */
+                     if (pass < 2)
+                       {
+                         /* Force another pass.  */
+                         ret = 1;
+                         break;
+                       }
+
                      as_bad_where (fragP->fr_file, fragP->fr_line,
                                    _("attempt to move .org backwards"));
 
                      /* We've issued an error message.  Change the
-                         frag to avoid cascading errors.  */
+                        frag to avoid cascading errors.  */
                      fragP->fr_type = rs_align;
                      fragP->fr_subtype = 0;
                      fragP->fr_offset = 0;
-                     fragP->fr_fix = after - was_address;
-                     growth = stretch;
+                     fragP->fr_fix = after - address;
                    }
-
-                 /* This is an absolute growth factor  */
-                 growth -= stretch;
-                 break;
                }
+               break;
 
              case rs_space:
+             case rs_space_nop:
                growth = 0;
                if (symbolP)
                  {
@@ -2434,8 +3010,16 @@ relax_segment (struct frag *segment_frag_root, segT segment)
                      }
                    else if (amount < 0)
                      {
+                       /* Don't error on first few frag relax passes.
+                          See rs_org comment for a longer explanation.  */
+                       if (pass < 2)
+                         {
+                           ret = 1;
+                           break;
+                         }
+
                        as_warn_where (fragP->fr_file, fragP->fr_line,
-                                      _(".space or .fill with negative value, ignored"));
+                                      _(".space, .nops or .fill with negative value, ignored"));
                        fragP->fr_symbol = 0;
                      }
                    else
@@ -2451,7 +3035,8 @@ relax_segment (struct frag *segment_frag_root, segT segment)
 #ifdef TC_GENERIC_RELAX_TABLE
                /* The default way to relax a frag is to look through
                   TC_GENERIC_RELAX_TABLE.  */
-               growth = relax_frag (segment, fragP, stretch);
+               growth = md_generic_table_relax_frag (segment, fragP,
+                                                     stretch);
 #endif /* TC_GENERIC_RELAX_TABLE  */
 #endif
                break;
@@ -2484,13 +3069,32 @@ relax_segment (struct frag *segment_frag_root, segT segment)
              {
                stretch += growth;
                stretched = 1;
+               if (fragP->fr_type == rs_leb128)
+                 rs_leb128_fudge += 16;
+               else if (fragP->fr_type == rs_align
+                        && (rs_leb128_fudge & 16) != 0
+                        && stretch == 0)
+                 rs_leb128_fudge += 16;
+               else
+                 rs_leb128_fudge = 0;
              }
-         }                     /* For each frag in the segment.  */
+         }
+
+       if (stretch == 0
+           && (rs_leb128_fudge & 16) == 0
+           && (rs_leb128_fudge & -16) != 0)
+         rs_leb128_fudge += 1;
+       else
+         rs_leb128_fudge = 0;
       }
-    while (stretched);         /* Until nothing further to relax.  */
-  }                            /* do_relax  */
+    /* Until nothing further to relax.  */
+    while (stretched && -- max_iterations);
+
+    if (stretched)
+      as_fatal (_("Infinite loop encountered whilst attempting to compute the addresses of symbols in section %s"),
+               segment_name (segment));
+  }
 
-  ret = 0;
   for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next)
     if (fragP->last_fr_address != fragP->fr_address)
       {
@@ -2500,259 +3104,6 @@ relax_segment (struct frag *segment_frag_root, segT segment)
   return ret;
 }
 
-#if defined (BFD_ASSEMBLER) || (!defined (BFD) && !defined (OBJ_VMS))
-
-/* fixup_segment()
-
-   Go through all the fixS's in a segment and see which ones can be
-   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_fix3 to put the fix into the frag data.
-
-   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
-fixup_segment (fixS *fixP, segT this_segment)
-{
-  long seg_reloc_count = 0;
-  valueT add_number;
-  fragS *fragP;
-  segT add_symbol_segment = absolute_section;
-
-  if (fixP != NULL && abs_section_sym == NULL)
-    {
-#ifndef BFD_ASSEMBLER
-      abs_section_sym = &abs_symbol;
-#else
-      abs_section_sym = section_symbol (absolute_section);
-#endif
-    }
-
-  /* If the linker is doing the relaxing, we must not do any fixups.
-
-     Well, strictly speaking that's not true -- we could do any that
-     are PC-relative and don't cross regions that could change size.
-     And for the i960 we might be able to turn callx/callj into bal
-     anyways in cases where we know the maximum displacement.  */
-  if (linkrelax && TC_LINKRELAX_FIXUP (this_segment))
-    {
-      for (; fixP; fixP = fixP->fx_next)
-       if (!fixP->fx_done)
-         {
-           if (fixP->fx_addsy == NULL)
-             {
-               /* There was no symbol required by this relocation.
-                  However, BFD doesn't really handle relocations
-                  without symbols well. So fake up a local symbol in
-                  the absolute section.  */
-               fixP->fx_addsy = abs_section_sym;
-             }
-           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;
-    }
-
-  for (; fixP; fixP = fixP->fx_next)
-    {
-#ifdef DEBUG5
-      fprintf (stderr, "\nprocessing fixup:\n");
-      print_fixup (fixP);
-#endif
-
-      fragP = fixP->fx_frag;
-      know (fragP);
-#ifdef TC_VALIDATE_FIX
-      TC_VALIDATE_FIX (fixP, this_segment, skip);
-#endif
-      add_number = fixP->fx_offset;
-
-      if (fixP->fx_addsy != NULL
-         && symbol_mri_common_p (fixP->fx_addsy))
-       {
-         know (fixP->fx_addsy->sy_value.X_op == O_symbol);
-         add_number += S_GET_VALUE (fixP->fx_addsy);
-         fixP->fx_offset = add_number;
-         fixP->fx_addsy
-           = symbol_get_value_expression (fixP->fx_addsy)->X_add_symbol;
-       }
-
-      if (fixP->fx_addsy != NULL)
-       add_symbol_segment = S_GET_SEGMENT (fixP->fx_addsy);
-
-      if (fixP->fx_subsy != NULL)
-       {
-         segT sub_symbol_segment;
-         resolve_symbol_value (fixP->fx_subsy);
-         sub_symbol_segment = S_GET_SEGMENT (fixP->fx_subsy);
-         if (fixP->fx_addsy != NULL
-             && sub_symbol_segment == add_symbol_segment
-             && !TC_FORCE_RELOCATION_SUB_SAME (fixP, add_symbol_segment))
-           {
-             add_number += S_GET_VALUE (fixP->fx_addsy);
-             add_number -= S_GET_VALUE (fixP->fx_subsy);
-             fixP->fx_offset = add_number;
-             fixP->fx_addsy = NULL;
-             fixP->fx_subsy = NULL;
-#ifdef TC_M68K
-             /* See the comment below about 68k weirdness.  */
-             fixP->fx_pcrel = 0;
-#endif
-           }
-         else if (sub_symbol_segment == absolute_section
-                  && !TC_FORCE_RELOCATION_SUB_ABS (fixP))
-           {
-             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))
-           {
-             add_number -= S_GET_VALUE (fixP->fx_subsy);
-             fixP->fx_offset = (add_number + fixP->fx_dot_value
-                                + fixP->fx_frag->fr_address);
-
-             /* Make it pc-relative.  If the back-end code has not
-                selected a pc-relative reloc, cancel the adjustment
-                we do later on all pc-relative relocs.  */
-             if (0
-#ifdef TC_M68K
-                 /* Do this for m68k even if it's already described
-                    as pc-relative.  On the m68k, an operand of
-                    "pc@(foo-.-2)" should address "foo" in a
-                    pc-relative mode.  */
-                 || 1
-#endif
-                 || !fixP->fx_pcrel)
-               add_number += MD_PCREL_FROM_SECTION (fixP, this_segment);
-             fixP->fx_subsy = NULL;
-             fixP->fx_pcrel = 1;
-           }
-         else if (!TC_VALIDATE_FIX_SUB (fixP))
-           {
-             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 (fixP->fx_addsy)
-       {
-         if (add_symbol_segment == this_segment
-             && !TC_FORCE_RELOCATION_LOCAL (fixP))
-           {
-             /* This fixup was made when the symbol's segment was
-                SEG_UNKNOWN, but it is now in the local segment.
-                So we know how to do the address without relocation.  */
-             add_number += S_GET_VALUE (fixP->fx_addsy);
-             fixP->fx_offset = add_number;
-             if (fixP->fx_pcrel)
-               add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment);
-             fixP->fx_addsy = NULL;
-             fixP->fx_pcrel = 0;
-           }
-         else if (add_symbol_segment == absolute_section
-                  && !TC_FORCE_RELOCATION_ABS (fixP))
-           {
-             add_number += S_GET_VALUE (fixP->fx_addsy);
-             fixP->fx_offset = add_number;
-             fixP->fx_addsy = NULL;
-           }
-         else if (add_symbol_segment != undefined_section
-#ifdef BFD_ASSEMBLER
-                  && ! bfd_is_com_section (add_symbol_segment)
-#endif
-                  && MD_APPLY_SYM_VALUE (fixP))
-           add_number += S_GET_VALUE (fixP->fx_addsy);
-       }
-
-      if (fixP->fx_pcrel)
-       {
-         add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment);
-         if (!fixP->fx_done && fixP->fx_addsy == NULL)
-           {
-             /* There was no symbol required by this relocation.
-                However, BFD doesn't really handle relocations
-                without symbols well. So fake up a local symbol in
-                the absolute section.  */
-             fixP->fx_addsy = abs_section_sym;
-           }
-       }
-
-      if (!fixP->fx_done)
-       md_apply_fix3 (fixP, &add_number, 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);
-         if (fixP->fx_subsy != NULL)
-           symbol_mark_used_in_reloc (fixP->fx_subsy);
-       }
-
-      if (!fixP->fx_bit_fixP && !fixP->fx_no_overflow && fixP->fx_size != 0)
-       {
-         if (fixP->fx_size < sizeof (valueT))
-           {
-             valueT mask;
-
-             mask = 0;
-             mask--;           /* Set all bits to one.  */
-             mask <<= fixP->fx_size * 8 - (fixP->fx_signed ? 1 : 0);
-             if ((add_number & mask) != 0 && (add_number & mask) != mask)
-               {
-                 char buf[50], buf2[50];
-                 sprint_value (buf, fragP->fr_address + fixP->fx_where);
-                 if (add_number > 1000)
-                   sprint_value (buf2, add_number);
-                 else
-                   sprintf (buf2, "%ld", (long) add_number);
-                 as_bad_where (fixP->fx_file, fixP->fx_line,
-                               _("value of %s too large for field of %d bytes at %s"),
-                               buf2, fixP->fx_size, buf);
-               } /* Generic error checking.  */
-           }
-#ifdef WARN_SIGNED_OVERFLOW_WORD
-         /* Warn if a .word value is too large when treated as a signed
-            number.  We already know it is not too negative.  This is to
-            catch over-large switches generated by gcc on the 68k.  */
-         if (!flag_signed_overflow_ok
-             && fixP->fx_size == 2
-             && add_number > 0x7fff)
-           as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("signed .word overflow; switch may be too large; %ld at 0x%lx"),
-                         (long) add_number,
-                         (long) (fragP->fr_address + fixP->fx_where));
-#endif
-       }                       /* Not a bit fix.  */
-
-#ifdef TC_VALIDATE_FIX
-    skip:  ATTRIBUTE_UNUSED_LABEL
-      ;
-#endif
-#ifdef DEBUG5
-      fprintf (stderr, "result:\n");
-      print_fixup (fixP);
-#endif
-    }                          /* For each fixS in this segment.  */
-
-  TC_ADJUST_RELOC_COUNT (fixP, seg_reloc_count);
-  return seg_reloc_count;
-}
-
-#endif /* defined (BFD_ASSEMBLER) || (!defined (BFD) && !defined (OBJ_VMS)) */
-
 void
 number_to_chars_bigendian (char *buf, valueT val, int n)
 {
@@ -2790,34 +3141,25 @@ void
 print_fixup (fixS *fixp)
 {
   indent_level = 1;
-  fprintf (stderr, "fix %lx %s:%d", (long) fixp, fixp->fx_file, fixp->fx_line);
+  fprintf (stderr, "fix ");
+  fprintf_vma (stderr, (bfd_vma)((bfd_hostptr_t) fixp));
+  fprintf (stderr, " %s:%d",fixp->fx_file, fixp->fx_line);
   if (fixp->fx_pcrel)
     fprintf (stderr, " pcrel");
   if (fixp->fx_pcrel_adjust)
     fprintf (stderr, " pcrel_adjust=%d", fixp->fx_pcrel_adjust);
-  if (fixp->fx_im_disp)
-    {
-#ifdef TC_NS32K
-      fprintf (stderr, " im_disp=%d", fixp->fx_im_disp);
-#else
-      fprintf (stderr, " im_disp");
-#endif
-    }
   if (fixp->fx_tcbit)
     fprintf (stderr, " tcbit");
   if (fixp->fx_done)
     fprintf (stderr, " done");
-  fprintf (stderr, "\n    size=%d frag=%lx where=%ld offset=%lx addnumber=%lx",
-          fixp->fx_size, (long) fixp->fx_frag, (long) fixp->fx_where,
-          (long) fixp->fx_offset, (long) fixp->fx_addnumber);
-#ifdef BFD_ASSEMBLER
+  fprintf (stderr, "\n    size=%d frag=", fixp->fx_size);
+  fprintf_vma (stderr, (bfd_vma) ((bfd_hostptr_t) fixp->fx_frag));
+  fprintf (stderr, " where=%ld offset=%lx addnumber=%lx",
+          (long) fixp->fx_where,
+          (unsigned long) fixp->fx_offset,
+          (unsigned long) fixp->fx_addnumber);
   fprintf (stderr, "\n    %s (%d)", bfd_get_reloc_code_name (fixp->fx_r_type),
           fixp->fx_r_type);
-#else
-#ifdef NEED_FX_R_TYPE
-  fprintf (stderr, " r_type=%d", fixp->fx_r_type);
-#endif
-#endif
   if (fixp->fx_addsy)
     {
       fprintf (stderr, "\n   +<");
This page took 0.064001 seconds and 4 git commands to generate.