import gdb-1999-06-21 snapshot
[external/binutils.git] / gas / write.c
index ed6f0cb..d0a9ac6 100644 (file)
@@ -1,5 +1,5 @@
 /* write.c - emit .o file
-   Copyright (C) 1986, 87, 90, 91, 92, 93, 94, 1995
+   Copyright (C) 1986, 87, 90, 91, 92, 93, 94, 95, 96, 97, 98, 1999
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
@@ -15,8 +15,9 @@
    GNU General Public License for more details.
 
    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.  */
+   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.  */
 
 /* This thing should be set up to do byteordering correctly.  But... */
 
 #define TC_FORCE_RELOCATION(FIXP) 0
 #endif
 
+#ifndef TC_FORCE_RELOCATION_SECTION
+#define TC_FORCE_RELOCATION_SECTION(FIXP,SEG) TC_FORCE_RELOCATION(FIXP)
+#endif
+
+#ifndef        MD_PCREL_FROM_SECTION
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from(FIXP)
+#endif
+
 #ifndef WORKING_DOT_WORD
 extern CONST int md_short_jump_size;
 extern CONST int md_long_jump_size;
@@ -86,6 +95,8 @@ int magic_number_for_object_file = DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE;
 
 #endif /* BFD_ASSEMBLER */
 
+static int n_fixups;
+
 #ifdef BFD_ASSEMBLER
 static fixS *fix_new_internal PARAMS ((fragS *, int where, int size,
                                       symbolS *add, symbolS *sub,
@@ -101,6 +112,26 @@ static fixS *fix_new_internal PARAMS ((fragS *, int where, int size,
 static long fixup_segment PARAMS ((fixS * fixP, segT this_segment_type));
 #endif
 static relax_addressT relax_align PARAMS ((relax_addressT addr, int align));
+#if defined (BFD_ASSEMBLER) || ! defined (BFD)
+static fragS *chain_frchains_together_1 PARAMS ((segT, struct frchain *));
+#endif
+#ifdef BFD_ASSEMBLER
+static void chain_frchains_together PARAMS ((bfd *, segT, PTR));
+static void cvt_frag_to_fill PARAMS ((segT, fragS *));
+static void relax_and_size_seg PARAMS ((bfd *, asection *, PTR));
+static void adjust_reloc_syms PARAMS ((bfd *, asection *, PTR));
+static void write_relocs PARAMS ((bfd *, asection *, PTR));
+static void write_contents PARAMS ((bfd *, asection *, PTR));
+static void set_symtab PARAMS ((void));
+#endif
+#if defined (BFD_ASSEMBLER) || (! defined (BFD) && ! defined (OBJ_AOUT))
+static void merge_data_into_text PARAMS ((void));
+#endif
+#if ! defined (BFD_ASSEMBLER) && ! defined (BFD)
+static void cvt_frag_to_fill PARAMS ((object_headers *, segT, fragS *));
+static void remove_subsegs PARAMS ((frchainS *, int, fragS **, fragS **));
+static void relax_and_size_all_segments PARAMS ((void));
+#endif
 
 /*
  *                     fix_new()
@@ -125,11 +156,19 @@ fix_new_internal (frag, where, size, add_symbol, sub_symbol, offset, pcrel,
 {
   fixS *fixP;
 
+  n_fixups++;
+
   fixP = (fixS *) obstack_alloc (&notes, sizeof (fixS));
 
   fixP->fx_frag = frag;
   fixP->fx_where = where;
   fixP->fx_size = size;
+  /* 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);
+      abort ();
+    }
   fixP->fx_addsy = add_symbol;
   fixP->fx_subsy = sub_symbol;
   fixP->fx_offset = offset;
@@ -142,12 +181,18 @@ fix_new_internal (frag, where, size, add_symbol, sub_symbol, offset, pcrel,
   fixP->fx_pcrel_adjust = 0;
   fixP->fx_bit_fixP = 0;
   fixP->fx_addnumber = 0;
-  fixP->tc_fix_data = NULL;
   fixP->fx_tcbit = 0;
   fixP->fx_done = 0;
+  fixP->fx_no_overflow = 0;
+  fixP->fx_signed = 0;
 
-#if defined (TC_I960) || defined (TC_NS32K)
-  fixP->fx_bsr = 0;
+#ifdef USING_CGEN
+  fixP->fx_cgen.insn = NULL;
+  fixP->fx_cgen.opinfo = 0;
+#endif
+
+#ifdef TC_FIX_TYPE
+  TC_INIT_FIX_DATA(fixP);
 #endif
 
   as_where (&fixP->fx_file, &fixP->fx_line);
@@ -230,7 +275,7 @@ fix_new_exp (frag, where, size, exp, pcrel, r_type)
   symbolS *add = NULL;
   symbolS *sub = NULL;
   offsetT off = 0;
-  
+
   switch (exp->X_op)
     {
     case O_absent:
@@ -240,7 +285,6 @@ fix_new_exp (frag, where, size, exp, pcrel, r_type)
       /* This comes up when _GLOBAL_OFFSET_TABLE_+(.-L0) is read, if
         the difference expression cannot immediately be reduced.  */
       {
-       extern symbolS *make_expr_symbol ();
        symbolS *stmp = make_expr_symbol (exp);
        exp->X_op = O_symbol;
        exp->X_op_symbol = 0;
@@ -249,6 +293,21 @@ fix_new_exp (frag, where, size, exp, pcrel, r_type)
        return fix_new_exp (frag, where, size, exp, pcrel, r_type);
       }
 
+    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:
       sub = exp->X_add_symbol;
       off = exp->X_add_number;
@@ -263,9 +322,10 @@ fix_new_exp (frag, where, size, exp, pcrel, r_type)
     case O_constant:
       off = exp->X_add_number;
       break;
-      
+
     default:
-      as_bad ("expression too complex for fixup");
+      add = make_expr_symbol (exp);
+      break;
     }
 
   return fix_new_internal (frag, where, size, add, sub, off,
@@ -287,7 +347,7 @@ append (charPP, fromP, length)
   *charPP += length;
 }
 
-#ifndef BFD_ASSEMBLER 
+#ifndef BFD_ASSEMBLER
 int section_alignment[SEG_MAXIMUM_ORDINAL];
 #endif
 
@@ -297,7 +357,7 @@ int section_alignment[SEG_MAXIMUM_ORDINAL];
  * boundary, all of the other alignments within it will work.  At
  * least one object format really uses this info.
  */
-void 
+void
 record_alignment (seg, align)
      /* Segment to which alignment pertains */
      segT seg;
@@ -308,7 +368,7 @@ record_alignment (seg, align)
   if (seg == absolute_section)
     return;
 #ifdef BFD_ASSEMBLER
-  if (align > bfd_get_section_alignment (stdoutput, seg))
+  if ((unsigned int) align > bfd_get_section_alignment (stdoutput, seg))
     bfd_set_section_alignment (stdoutput, seg, align);
 #else
   if (align > section_alignment[(int) seg])
@@ -350,6 +410,7 @@ chain_frchains_together_1 (section, frchp)
     {
       prev_frag->fr_next = frchp->frch_root;
       prev_frag = frchp->frch_last;
+      assert (prev_frag->fr_type != 0);
 #ifdef BFD_ASSEMBLER
       if (frchp->fix_root != (fixS *) NULL)
        {
@@ -361,6 +422,7 @@ chain_frchains_together_1 (section, frchp)
        }
 #endif
     }
+  assert (prev_frag->fr_type != 0);
   prev_frag->fr_next = 0;
   return prev_frag;
 }
@@ -393,7 +455,7 @@ chain_frchains_together (abfd, section, xxx)
 
 #if !defined (BFD) && !defined (BFD_ASSEMBLER)
 
-void 
+static void
 remove_subsegs (head, seg, root, last)
      frchainS *head;
      int seg;
@@ -436,7 +498,7 @@ cvt_frag_to_fill (headersP, sec, fragP)
                          - fragP->fr_fix) / fragP->fr_var;
       if (fragP->fr_offset < 0)
        {
-         as_bad ("attempt to .org/.space backwards? (%ld)",
+         as_bad (_("attempt to .org/.space backwards? (%ld)"),
                  (long) fragP->fr_offset);
        }
       fragP->fr_type = rs_fill;
@@ -445,6 +507,26 @@ cvt_frag_to_fill (headersP, sec, fragP)
     case rs_fill:
       break;
 
+    case rs_leb128:
+      {
+       valueT value = S_GET_VALUE (fragP->fr_symbol);
+       int size;
+
+       size = output_leb128 (fragP->fr_literal + fragP->fr_fix, value,
+                             fragP->fr_subtype);
+
+       fragP->fr_fix += size;
+       fragP->fr_type = rs_fill;
+       fragP->fr_var = 0;
+       fragP->fr_offset = 0;
+       fragP->fr_symbol = NULL;
+      }
+      break;
+
+    case rs_cfa:
+      eh_frame_convert_frag (fragP);
+      break;
+
     case rs_machine_dependent:
 #ifdef BFD_ASSEMBLER
       md_convert_frag (stdoutput, sec, fragP);
@@ -452,7 +534,9 @@ cvt_frag_to_fill (headersP, sec, fragP)
       md_convert_frag (headersP, sec, fragP);
 #endif
 
-      assert (fragP->fr_next == NULL || (fragP->fr_next->fr_address - fragP->fr_address == fragP->fr_fix));
+      assert (fragP->fr_next == NULL
+             || ((offsetT) (fragP->fr_next->fr_address - fragP->fr_address)
+                 == fragP->fr_fix));
 
       /*
        * After md_convert_frag, we make the frag into a ".space 0".
@@ -502,6 +586,8 @@ relax_and_size_seg (abfd, sec, xxx)
   int x;
   valueT size, newsize;
 
+  subseg_change (sec, 0);
+
   flags = bfd_get_section_flags (abfd, sec);
 
   seginfo = seg_info (sec);
@@ -593,7 +679,7 @@ dump_section_relocs (abfd, sec, stream_)
     }
 }
 #else
-#define dump_section_relocs(ABFD,SEC,STREAM)   (void)(ABFD,SEC,STREAM)
+#define dump_section_relocs(ABFD,SEC,STREAM)   ((void) 0)
 #endif
 
 #ifndef EMIT_SECTION_SYMBOLS
@@ -622,98 +708,163 @@ adjust_reloc_syms (abfd, sec, xxx)
        symbolS *sym;
        asection *symsec;
 
-      reduce_fixup:
-
 #ifdef DEBUG5
        fprintf (stderr, "\n\nadjusting fixup:\n");
        print_fixup (fixp);
 #endif
 
        sym = fixp->fx_addsy;
-       symsec = sym->bsym->section;
 
-       if (sym != NULL && sym->sy_mri_common)
+       /* All symbols should have already been resolved at this
+          point.  It is possible to see unresolved expression
+          symbols, though, since they are not in the regular symbol
+          table.  */
+       if (sym != NULL)
+         resolve_symbol_value (sym, 1);
+       
+       if (fixp->fx_subsy != NULL)
+         resolve_symbol_value (fixp->fx_subsy, 1);
+
+       /* If this symbol is equated to an undefined symbol, convert
+           the fixup to being against that symbol.  */
+       if (sym != NULL && symbol_equated_p (sym)
+           && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+         {
+           fixp->fx_offset += symbol_get_value_expression (sym)->X_add_number;
+           sym = symbol_get_value_expression (sym)->X_add_symbol;
+           fixp->fx_addsy = sym;
+         }
+
+       if (sym != NULL && symbol_mri_common_p (sym))
          {
            /* These symbols are handled specially in fixup_segment.  */
            goto done;
          }
 
+       symsec = S_GET_SEGMENT (sym);
+
+       if (symsec == NULL)
+         abort ();
+       
+       if (bfd_is_abs_section (symsec))
+         {
+           /* The fixup_segment routine will not use this symbol in a
+               relocation unless TC_FORCE_RELOCATION returns 1.  */
+           if (TC_FORCE_RELOCATION (fixp))
+             {
+               symbol_mark_used_in_reloc (fixp->fx_addsy);
+#ifdef UNDEFINED_DIFFERENCE_OK
+               if (fixp->fx_subsy != NULL)
+                 symbol_mark_used_in_reloc (fixp->fx_subsy);
+#endif
+             }
+           goto done;
+         }
+
        /* If it's one of these sections, assume the symbol is
           definitely going to be output.  The code in
           md_estimate_size_before_relax in tc-mips.c uses this test
           as well, so if you change this code you should look at that
           code.  */
        if (bfd_is_und_section (symsec)
-           || bfd_is_abs_section (symsec)
            || bfd_is_com_section (symsec))
          {
-           fixp->fx_addsy->sy_used_in_reloc = 1;
+           symbol_mark_used_in_reloc (fixp->fx_addsy);
 #ifdef UNDEFINED_DIFFERENCE_OK
            /* We have the difference of an undefined symbol and some
               other symbol.  Make sure to mark the other symbol as used
               in a relocation so that it will always be output.  */
            if (fixp->fx_subsy)
-             fixp->fx_subsy->sy_used_in_reloc = 1;
+             symbol_mark_used_in_reloc (fixp->fx_subsy);
 #endif
            goto done;
          }
 
+       /* 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.  */
+       if (symsec != sec && ! S_IS_LOCAL (sym))
+         {
+           boolean linkonce;
+
+           linkonce = false;
+#ifdef BFD_ASSEMBLER
+           if ((bfd_get_section_flags (stdoutput, symsec) & SEC_LINK_ONCE)
+               != 0)
+             linkonce = true;
+#endif
+#ifdef OBJ_ELF
+           /* The GNU toolchain uses an extension for ELF: a section
+               beginning with the magic string .gnu.linkonce is a
+               linkonce section.  */
+           if (strncmp (segment_name (symsec), ".gnu.linkonce",
+                        sizeof ".gnu.linkonce" - 1) == 0)
+             linkonce = true;
+#endif
+
+           if (linkonce)
+             {
+               symbol_mark_used_in_reloc (fixp->fx_addsy);
+#ifdef UNDEFINED_DIFFERENCE_OK
+               if (fixp->fx_subsy != NULL)
+                 symbol_mark_used_in_reloc (fixp->fx_subsy);
+#endif
+               goto done;
+             }
+         }
+
        /* Since we're reducing to section symbols, don't attempt to reduce
           anything that's already using one.  */
-       if (sym->bsym->flags & BSF_SECTION_SYM)
+       if (symbol_section_p (sym))
+         {
+           symbol_mark_used_in_reloc (fixp->fx_addsy);
+           goto done;
+         }
+
+#ifdef BFD_ASSEMBLER
+       /* We can never adjust a reloc against a weak symbol.  If we
+           did, and the weak symbol was overridden by a real symbol
+           somewhere else, then our relocation would be pointing at
+           the wrong area of memory.  */
+       if (S_IS_WEAK (sym))
          {
-           fixp->fx_addsy->sy_used_in_reloc = 1;
+           symbol_mark_used_in_reloc (fixp->fx_addsy);
            goto done;
          }
+#endif
 
        /* Is there some other reason we can't adjust this one?  (E.g.,
           call/bal links in i960-bout symbols.)  */
 #ifdef obj_fix_adjustable
        if (! obj_fix_adjustable (fixp))
          {
-           fixp->fx_addsy->sy_used_in_reloc = 1;
+           symbol_mark_used_in_reloc (fixp->fx_addsy);
            goto done;
          }
 #endif
 
        /* Is there some other (target cpu dependent) reason we can't adjust
-          this one?  (E.g. relocations involving function addresses on 
+          this one?  (E.g. relocations involving function addresses on
           the PA.  */
 #ifdef tc_fix_adjustable
        if (! tc_fix_adjustable (fixp))
          {
-           fixp->fx_addsy->sy_used_in_reloc = 1;
+           symbol_mark_used_in_reloc (fixp->fx_addsy);
            goto done;
          }
 #endif
 
-       /* For PIC support: We may get expressions like
-          "_GLOBAL_OFFSET_TABLE_+(.-L5)" where "." and "L5" may not
-          necessarily have had a fixed difference initially.  But now
-          it should be a known constant, so we can reduce it.  Since
-          we can't easily handle a symbol value that looks like
-          someUndefinedSymbol+const, though, we convert the fixup to
-          access the undefined symbol directly, and discard the
-          intermediate symbol.  */
-       if (S_GET_SEGMENT (sym) == expr_section
-           && sym->sy_value.X_op == O_add
-           && (resolve_symbol_value (sym->sy_value.X_add_symbol),
-               S_GET_SEGMENT (sym->sy_value.X_add_symbol) == undefined_section)
-           && (resolve_symbol_value (sym->sy_value.X_op_symbol),
-               S_GET_SEGMENT (sym->sy_value.X_op_symbol) == absolute_section))
-         {
-           fixp->fx_offset += S_GET_VALUE (sym->sy_value.X_op_symbol);
-           fixp->fx_offset += sym->sy_value.X_add_number;
-           fixp->fx_addsy = sym->sy_value.X_add_symbol;
-           goto reduce_fixup;
-         }
-
        /* If the section symbol isn't going to be output, the relocs
           at least should still work.  If not, figure out what to do
-          when we run into that case.  */
+          when we run into that case.
+
+          We refetch the segment when calling section_symbol, rather
+          than using symsec, because S_GET_VALUE may wind up changing
+          the section when it calls resolve_symbol_value. */
        fixp->fx_offset += S_GET_VALUE (sym);
-       fixp->fx_addsy = section_symbol (symsec);
-       fixp->fx_addsy->sy_used_in_reloc = 1;
+       fixp->fx_addsy = section_symbol (S_GET_SEGMENT (sym));
+       symbol_mark_used_in_reloc (fixp->fx_addsy);
 
       done:
        ;
@@ -760,8 +911,7 @@ write_relocs (abfd, sec, xxx)
 
 #ifndef RELOC_EXPANSION_POSSIBLE
   /* Set up reloc information as well.  */
-  relocs = (arelent **) bfd_alloc_by_size_t (stdoutput,
-                                            n * sizeof (arelent *));
+  relocs = (arelent **) xmalloc (n * sizeof (arelent *));
   memset ((char*)relocs, 0, n * sizeof (arelent*));
 
   i = 0;
@@ -769,21 +919,46 @@ write_relocs (abfd, sec, xxx)
     {
       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 use generate the reloc against the latter symbol
+         rather than the former.  */
+      sym = fixp->fx_addsy;
+      while (symbol_equated_p (sym)
+            && (! S_IS_DEFINED (sym) || S_IS_COMMON (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,
@@ -794,19 +969,21 @@ write_relocs (abfd, sec, xxx)
        case bfd_reloc_ok:
          break;
        case bfd_reloc_overflow:
-         as_bad_where (fixp->fx_file, fixp->fx_line, "relocation 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_perform_relocation",
-                   fixp->fx_file, fixp->fx_line);
+         as_fatal (_("%s:%u: bad return from bfd_install_relocation: %x"),
+                   fixp->fx_file, fixp->fx_line, s);
        }
       relocs[i++] = reloc;
     }
 #else
   n = n * MAX_RELOC_EXPANSION;
   /* Set up reloc information as well.  */
-  relocs = (arelent **) bfd_alloc_by_size_t (stdoutput,
-                                            n * sizeof (arelent *));
+  relocs = (arelent **) xmalloc (n * sizeof (arelent *));
 
   i = 0;
   for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
@@ -814,6 +991,7 @@ write_relocs (abfd, sec, xxx)
       arelent **reloc;
       char *data;
       bfd_reloc_status_type s;
+      symbolS *sym;
       int j;
 
       if (fixp->fx_done)
@@ -821,6 +999,16 @@ write_relocs (abfd, sec, xxx)
          n--;
          continue;
        }
+
+      /* If this is an undefined symbol which was equated to another
+         symbol, then use generate the reloc against the latter symbol
+         rather than the former.  */
+      sym = fixp->fx_addsy;
+      while (symbol_equated_p (sym)
+            && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+       sym = symbol_get_value_expression (sym)->X_add_symbol;
+      fixp->fx_addsy = sym;
+
       reloc = tc_gen_reloc (sec, fixp);
 
       for (j = 0; reloc[j]; j++)
@@ -831,7 +1019,8 @@ write_relocs (abfd, sec, xxx)
       data = fixp->fx_frag->fr_literal + fixp->fx_where;
       if (fixp->fx_where + fixp->fx_size
          > fixp->fx_frag->fr_fix + fixp->fx_frag->fr_offset)
-       abort ();
+       as_bad_where (fixp->fx_file, fixp->fx_line,
+                     _("internal error: fixup not contained within frag"));
       for (j = 0; reloc[j]; j++)
         {
          s = bfd_install_relocation (stdoutput, reloc[j],
@@ -844,10 +1033,10 @@ write_relocs (abfd, sec, xxx)
              break;
            case bfd_reloc_overflow:
              as_bad_where (fixp->fx_file, fixp->fx_line,
-                           "relocation overflow");
+                           _("relocation overflow"));
              break;
            default:
-             as_fatal ("%s:%u: bad return from bfd_perform_relocation",
+             as_fatal (_("%s:%u: bad return from bfd_install_relocation"),
                        fixp->fx_file, fixp->fx_line);
            }
         }
@@ -930,7 +1119,7 @@ write_contents (abfd, sec, xxx)
          if (x == false)
            {
              bfd_perror (stdoutput->filename);
-             as_perror ("FATAL: Can't write %s", stdoutput->filename);
+             as_perror (_("FATAL: Can't write %s"), stdoutput->filename);
              exit (EXIT_FAILURE);
            }
          offset += f->fr_fix;
@@ -940,66 +1129,56 @@ write_contents (abfd, sec, xxx)
       count = f->fr_offset;
       assert (count >= 0);
       if (fill_size && count)
-#ifdef BFD_FAST_SECTION_FILL
        {
          char buf[256];
-         if (fill_size > sizeof(buf)) {
-           /* Do it the old way. Can this ever happen? */
-       while (count--)
-         {
-           x = bfd_set_section_contents (stdoutput, sec,
-                                         fill_literal, (file_ptr) offset,
-                                         (bfd_size_type) fill_size);
-           if (x == false)
-             {
-               bfd_perror (stdoutput->filename);
-               as_perror ("FATAL: Can't write %s", stdoutput->filename);
-               exit (EXIT_FAILURE);
-             }
-           offset += fill_size;
-         }
-    }
-         else {
-           /* Build a buffer full of fill objects and output it as
-            * often as necessary. This saves on the overhead of potentially
-            * lots of bfd_set_section_contents calls.
-            */
-           int n_per_buf, i;
-           if (fill_size == 1)
-             {
-               n_per_buf = sizeof (buf);
-               memset (buf, *fill_literal, n_per_buf);
-             }
-           else
-             {
-               char *bufp;
-               n_per_buf = sizeof(buf)/fill_size;
-               for (i = n_per_buf, bufp = buf; i; i--, bufp += fill_size)
-                 memcpy(bufp, fill_literal, fill_size);
-             }
-           for (; count > 0; count -= n_per_buf)
-             {
-               n_per_buf = n_per_buf > count ? count : n_per_buf;
-               x = bfd_set_section_contents (stdoutput, sec,
-                                             buf, (file_ptr) offset,
-                                             (bfd_size_type) n_per_buf * fill_size);
-               if (x != true)
-                 as_fatal ("Cannot write to output file.");
-               offset += n_per_buf * fill_size;
-             }
-         }
+         if (fill_size > sizeof(buf))
+           {
+             /* Do it the old way. Can this ever happen? */
+             while (count--)
+               {
+                 x = bfd_set_section_contents (stdoutput, sec,
+                                               fill_literal,
+                                               (file_ptr) offset,
+                                               (bfd_size_type) fill_size);
+                 if (x == false)
+                   {
+                     bfd_perror (stdoutput->filename);
+                     as_perror (_("FATAL: Can't write %s"), stdoutput->filename);
+                     exit (EXIT_FAILURE);
+                   }
+                 offset += fill_size;
+               }
+           }
+         else
+           {
+             /* Build a buffer full of fill objects and output it as
+                often as necessary. This saves on the overhead of
+                potentially lots of bfd_set_section_contents calls.  */
+             int n_per_buf, i;
+             if (fill_size == 1)
+               {
+                 n_per_buf = sizeof (buf);
+                 memset (buf, *fill_literal, n_per_buf);
+               }
+             else
+               {
+                 char *bufp;
+                 n_per_buf = sizeof(buf)/fill_size;
+                 for (i = n_per_buf, bufp = buf; i; i--, bufp += fill_size)
+                   memcpy(bufp, fill_literal, fill_size);
+               }
+             for (; count > 0; count -= n_per_buf)
+               {
+                 n_per_buf = n_per_buf > count ? count : n_per_buf;
+                 x = bfd_set_section_contents (stdoutput, sec,
+                                               buf, (file_ptr) offset,
+                                               (bfd_size_type) n_per_buf * fill_size);
+                 if (x != true)
+                   as_fatal (_("Cannot write to output file."));
+                 offset += n_per_buf * fill_size;
+               }
+           }
        }
-#else
-       while (count--)
-         {
-           x = bfd_set_section_contents (stdoutput, sec,
-                                         fill_literal, (file_ptr) offset,
-                                         (bfd_size_type) fill_size);
-           if (x != true)
-             as_fatal ("Cannot write to output file.");
-           offset += fill_size;
-         }
-#endif
     }
 }
 #endif
@@ -1154,8 +1333,8 @@ set_symtab ()
       symp = symbol_rootP;
       for (i = 0; i < nsyms; i++, symp = symbol_next (symp))
        {
-         asympp[i] = symp->bsym;
-         symp->written = 1;
+         asympp[i] = symbol_get_bfdsym (symp);
+         symbol_mark_written (symp);
        }
     }
   else
@@ -1166,10 +1345,53 @@ set_symtab ()
 }
 #endif
 
-void 
+/* Finish the subsegments.  After every sub-segment, we fake an
+   ".align ...".  This conforms to BSD4.2 brane-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.  */
+
+#ifndef SUB_SEGMENT_ALIGN
+#ifdef BFD_ASSEMBLER
+#define SUB_SEGMENT_ALIGN(SEG) (0)
+#else
+#define SUB_SEGMENT_ALIGN(SEG) (2)
+#endif
+#endif
+
+void
+subsegs_finish ()
+{
+  struct frchain *frchainP;
+
+  for (frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next)
+    {
+      subseg_set (frchainP->frch_seg, 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.  */
+      frag_align (had_errors () ? 0 : SUB_SEGMENT_ALIGN (now_seg),
+                 subseg_text_p (now_seg) ? NOP_OPCODE : 0,
+                 0);
+
+      /* frag_align will have left a new frag.
+        Use this last frag for an empty ".fill".
+
+        For this segment ...
+        Create a last frag. Do not leave a "being filled in frag".  */
+
+      frag_wane (frag_now);
+      frag_now->fr_fix = 0;
+      know (frag_now->fr_next == NULL);
+    }
+}
+
+/* Write the object file.  */
+
+void
 write_object_file ()
 {
-  struct frchain *frchainP;    /* Track along all frchains. */
 #if ! defined (BFD_ASSEMBLER) || ! defined (WORKING_DOT_WORD)
   fragS *fragP;                        /* Track along all frags. */
 #endif
@@ -1184,14 +1406,14 @@ write_object_file ()
     if (flag_always_generate_output)
       {
        if (n_warns || n_errs)
-         as_warn ("%d error%s, %d warning%s, generating bad object file.\n",
+         as_warn (_("%d error%s, %d warning%s, generating bad object file.\n"),
                   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",
+         as_fatal (_("%d error%s, %d warning%s, no object file generated.\n"),
                    n_errs, n_errs == 1 ? "" : "s",
                    n_warns, n_warns == 1 ? "" : "s");
       }
@@ -1204,34 +1426,6 @@ write_object_file ()
   vms_check_for_main ();
 #endif /* OBJ_VMS */
 
-  /* After every sub-segment, we fake an ".align ...". This conforms to
-     BSD4.2 brane-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.
-
-     @@ Is this really necessary??  */
-#ifndef SUB_SEGMENT_ALIGN
-#ifdef BFD_ASSEMBLER
-#define SUB_SEGMENT_ALIGN(SEG) (0)
-#else
-#define SUB_SEGMENT_ALIGN(SEG) (2)
-#endif
-#endif
-  for (frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next)
-    {
-      subseg_set (frchainP->frch_seg, frchainP->frch_subseg);
-      frag_align (SUB_SEGMENT_ALIGN (now_seg), NOP_OPCODE);
-      /* frag_align will have left a new frag.
-        Use this last frag for an empty ".fill".
-
-        For this segment ...
-        Create a last frag. Do not leave a "being filled in frag".  */
-      frag_wane (frag_now);
-      frag_now->fr_fix = 0;
-      know (frag_now->fr_next == NULL);
-    }
-
   /* From now on, we don't care about sub-segments.  Build one frag chain
      for each segment. Linked thru fr_next.  */
 
@@ -1325,6 +1519,13 @@ write_object_file ()
 
   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.  */
@@ -1351,6 +1552,7 @@ write_object_file ()
        {
          expressionS exp;
 
+         subseg_change (lie->seg, lie->subseg);
          exp.X_op = O_subtract;
          exp.X_add_symbol = lie->add;
          exp.X_op_symbol = lie->sub;
@@ -1363,7 +1565,7 @@ write_object_file ()
 #else
          fix_new_exp (lie->frag,
                       lie->word_goes_here - lie->frag->fr_literal,
-                      2, &exp, 0, BFD_RELOC_NONE);
+                      2, &exp, 0, BFD_RELOC_16);
 #endif
 #else
 #if defined(TC_SPARC) || defined(TC_A29K) || defined(NEED_FX_R_TYPE)
@@ -1395,6 +1597,7 @@ write_object_file ()
        addressT from_addr, to_addr;
        int n, m;
 
+       subseg_change (lie->seg, lie->subseg);
        fragP = lie->dispfrag;
 
        /* Find out how many broken_words go here.  */
@@ -1421,7 +1624,7 @@ write_object_file ()
            /* This is the offset from ??? to table_ptr+0 */
            to_addr = table_addr - S_GET_VALUE (lie->sub);
 #ifdef BFD_ASSEMBLER
-           to_addr -= lie->sub->sy_frag->fr_address;
+           to_addr -= symbol_get_frag (lie->sub)->fr_address;
 #endif
            md_number_to_chars (lie->word_goes_here, to_addr, 2);
            for (untruth = lie->next_broken_word; untruth && untruth->dispfrag == fragP; untruth = untruth->next_broken_word)
@@ -1435,7 +1638,7 @@ write_object_file ()
            from_addr = table_addr;
            to_addr = S_GET_VALUE (lie->add) + lie->addnum;
 #ifdef BFD_ASSEMBLER
-           to_addr += lie->add->sy_frag->fr_address;
+           to_addr += symbol_get_frag (lie->add)->fr_address;
 #endif
            md_create_long_jump (table_ptr, from_addr, to_addr, lie->dispfrag, lie->add);
            table_ptr += md_long_jump_size;
@@ -1568,12 +1771,19 @@ write_object_file ()
       symbolS *symp;
 
       for (symp = symbol_rootP; symp; symp = symbol_next (symp))
-       if (!symp->sy_resolved)
-         resolve_symbol_value (symp);
+       resolve_symbol_value (symp, 1);
     }
+  resolve_local_symbol_values ();
 
   PROGRESS (1);
 
+#ifdef tc_frob_file_before_adjust
+  tc_frob_file_before_adjust ();
+#endif
+#ifdef obj_frob_file_before_adjust
+  obj_frob_file_before_adjust ();
+#endif
+
   bfd_map_over_sections (stdoutput, adjust_reloc_syms, (char *)0);
 
   /* Set up symbol table, and write it out.  */
@@ -1586,10 +1796,10 @@ write_object_file ()
          int punt = 0;
          const char *name;
 
-         if (symp->sy_mri_common)
+         if (symbol_mri_common_p (symp))
            {
              if (S_IS_EXTERNAL (symp))
-               as_bad ("%s: global symbols not supported in common sections",
+               as_bad (_("%s: global symbols not supported in common sections"),
                        S_GET_NAME (symp));
              symbol_remove (symp, &symbol_rootP, &symbol_lastP);
              continue;
@@ -1602,24 +1812,21 @@ write_object_file ()
              /* They only differ if `name' is a fb or dollar local
                 label name.  */
              if (name2 != name && ! S_IS_DEFINED (symp))
-               as_bad ("local label %s is not defined", name2);
+               as_bad (_("local label %s is not defined"), name2);
            }
 
          /* Do it again, because adjust_reloc_syms might introduce
             more symbols.  They'll probably only be section symbols,
             but they'll still need to have the values computed.  */
-         if (! symp->sy_resolved)
+         resolve_symbol_value (symp, 1);
+
+         /* Skip symbols which were equated to undefined or common
+             symbols.  */
+         if (symbol_equated_p (symp)
+             && (! S_IS_DEFINED (symp) || S_IS_COMMON (symp)))
            {
-             if (symp->sy_value.X_op == O_constant)
-               {
-                 /* This is the normal case; skip the call.  */
-                 S_SET_VALUE (symp,
-                              (S_GET_VALUE (symp)
-                               + symp->sy_frag->fr_address));
-                 symp->sy_resolved = 1;
-               }
-             else
-               resolve_symbol_value (symp);
+             symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+             continue;
            }
 
          /* So far, common symbols have been treated like undefined symbols.
@@ -1631,15 +1838,15 @@ write_object_file ()
          printf ("symbol `%s'\n\t@%x: value=%d flags=%x seg=%s\n",
                  S_GET_NAME (symp), symp,
                  S_GET_VALUE (symp),
-                 symp->bsym->flags,
-                 segment_name (symp->bsym->section));
+                 symbol_get_bfdsym (symp)->flags,
+                 segment_name (S_GET_SEGMENT (symp)));
 #endif
 
 #ifdef obj_frob_symbol
          obj_frob_symbol (symp, punt);
 #endif
 #ifdef tc_frob_symbol
-         if (! punt || symp->sy_used_in_reloc)
+         if (! punt || symbol_used_in_reloc_p (symp))
            tc_frob_symbol (symp, punt);
 #endif
 
@@ -1649,13 +1856,13 @@ write_object_file ()
             and symbols that the frob_symbol macros told us to punt,
             but we keep such symbols if they are used in relocs.  */
          if ((! EMIT_SECTION_SYMBOLS
-              && (symp->bsym->flags & BSF_SECTION_SYM) != 0)
+              && symbol_section_p (symp))
              /* Note that S_IS_EXTERN and S_IS_LOCAL are not always
                 opposites.  Sometimes the former checks flags and the
                 latter examines the name...  */
              || (!S_IS_EXTERN (symp)
                  && (S_IS_LOCAL (symp) || punt)
-                 && ! symp->sy_used_in_reloc))
+                 && ! symbol_used_in_reloc_p (symp)))
            {
              symbol_remove (symp, &symbol_rootP, &symbol_lastP);
              /* After symbol_remove, symbol_next(symp) still returns
@@ -1666,16 +1873,16 @@ write_object_file ()
            }
 
          /* Make sure we really got a value for the symbol.  */
-         if (! symp->sy_resolved)
+         if (! symbol_resolved_p (symp))
            {
-             as_bad ("can't resolve value for symbol \"%s\"",
+             as_bad (_("can't resolve value for symbol \"%s\""),
                      S_GET_NAME (symp));
-             symp->sy_resolved = 1;
+             symbol_mark_resolved (symp);
            }
 
          /* Set the value into the BFD symbol.  Up til now the value
             has only been kept in the gas symbolS struct.  */
-         symp->bsym->value = S_GET_VALUE (symp);
+         symbol_get_bfdsym (symp)->value = S_GET_VALUE (symp);
        }
     }
 
@@ -1683,6 +1890,9 @@ write_object_file ()
 
   /* Now do any format-specific adjustments to the symbol table, such
      as adding file symbols.  */
+#ifdef tc_adjust_symtab
+  tc_adjust_symtab ();
+#endif
 #ifdef obj_adjust_symtab
   obj_adjust_symtab ();
 #endif
@@ -1704,6 +1914,13 @@ write_object_file ()
 
   bfd_map_over_sections (stdoutput, write_relocs, (char *) 0);
 
+#ifdef tc_frob_file_after_relocs
+  tc_frob_file_after_relocs ();
+#endif
+#ifdef obj_frob_file_after_relocs
+  obj_frob_file_after_relocs ();
+#endif
+
   bfd_map_over_sections (stdoutput, write_contents, (char *) 0);
 #endif /* BFD_ASSEMBLER */
 }
@@ -1722,13 +1939,15 @@ write_object_file ()
  * these frag addresses may not be the same as final object-file addresses.
  */
 
-#ifndef md_relax_frag
+#ifdef TC_GENERIC_RELAX_TABLE
+
+static int is_dnrange PARAMS ((fragS *, fragS *));
 
 /* Subroutines of relax_segment.  */
-static int 
+static int
 is_dnrange (f1, f2)
-     struct frag *f1;
-     struct frag *f2;
+     fragS *f1;
+     fragS *f2;
 {
   for (; f1; f1 = f1->fr_next)
     if (f1->fr_next == f2)
@@ -1736,7 +1955,109 @@ is_dnrange (f1, f2)
   return 0;
 }
 
-#endif /* ! defined (md_relax_frag) */
+/* Relax a fragment by scanning TC_GENERIC_RELAX_TABLE.  */
+
+long
+relax_frag (fragP, stretch)
+     fragS *fragP;
+     long stretch;
+{
+  const relax_typeS *this_type;
+  const relax_typeS *start_type;
+  relax_substateT next_state;
+  relax_substateT this_state;
+  long aim, target, growth;
+  symbolS *symbolP = fragP->fr_symbol;
+  long offset = fragP->fr_offset;
+  /* Recompute was_address by undoing "+= stretch" done by relax_segment.  */
+  unsigned long was_address = fragP->fr_address - stretch;
+  unsigned long address = fragP->fr_address;
+  const relax_typeS *table = TC_GENERIC_RELAX_TABLE;
+
+  this_state = fragP->fr_subtype;
+  start_type = this_type = table + this_state;
+  target = offset;
+
+  if (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 (symbolP->sy_frag);
+#endif
+      know (!(S_GET_SEGMENT (symbolP) == absolute_section)
+           || symbolP->sy_frag == &zero_address_frag);
+      target +=        S_GET_VALUE (symbolP) + symbol_get_frag (symbolP)->fr_address;
+
+      /* 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.
+
+        Beware zero-length frags.
+
+        There should be a faster way to do this.  */
+
+      if (symbol_get_frag (symbolP)->fr_address >= was_address
+         && is_dnrange (fragP, symbol_get_frag (symbolP)))
+       {
+         target += stretch;
+       }
+    }
+
+  aim = target - address - fragP->fr_fix;
+#ifdef TC_PCREL_ADJUST
+  /* Currently only the ns32k family 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.  */
+/*  assert (fragP->fr_targ.ns32k.pcrel_adjust == 0);*/
+#endif
+#ifdef md_prepare_relax_scan /* formerly called M68K_AIM_KLUDGE */
+  md_prepare_relax_scan (fragP, address, aim, this_state, this_type);
+#endif
+
+  if (aim < 0)
+    {
+      /* Look backwards. */
+      for (next_state = this_type->rlx_more; next_state;)
+       if (aim >= this_type->rlx_backward)
+         next_state = 0;
+       else
+         {
+           /* Grow to next state. */
+           this_state = next_state;
+           this_type = table + this_state;
+           next_state = this_type->rlx_more;
+         }
+    }
+  else
+    {
+      /* Look forwards. */
+      for (next_state = this_type->rlx_more; next_state;)
+       if (aim <= this_type->rlx_forward)
+         next_state = 0;
+       else
+         {
+           /* Grow to next state. */
+           this_state = next_state;
+           this_type = table + this_state;
+           next_state = this_type->rlx_more;
+         }
+    }
+
+  growth = this_type->rlx_length - start_type->rlx_length;
+  if (growth != 0)
+    fragP->fr_subtype = this_state;
+  return growth;
+}
+
+#endif /* defined (TC_GENERIC_RELAX_TABLE) */
 
 /* Relax_align. Advance location counter to next address that has 'alignment'
    lowest order bits all 0s, return size of adjustment made.  */
@@ -1759,7 +2080,7 @@ relax_align (address, alignment)
   return (new_address - address);
 }
 
-void 
+void
 relax_segment (segment_frag_root, segment)
      struct frag *segment_frag_root;
      segT segment;
@@ -1789,13 +2110,18 @@ relax_segment (segment_frag_root, segment)
        case rs_align:
        case rs_align_code:
          {
-           int offset = relax_align (address, (int) fragP->fr_offset);
+           addressT offset = relax_align (address, (int) fragP->fr_offset);
+
+           if (fragP->fr_subtype != 0 && offset > fragP->fr_subtype)
+             offset = 0;
+
            if (offset % fragP->fr_var != 0)
              {
-               as_bad ("alignment padding (%d bytes) not a multiple of %ld",
-                       offset, (long) fragP->fr_var);
+               as_bad (_("alignment padding (%lu bytes) not a multiple of %ld"),
+                       (unsigned long) offset, (long) fragP->fr_var);
                offset -= (offset % fragP->fr_var);
              }
+
            address += offset;
          }
          break;
@@ -1815,6 +2141,16 @@ relax_segment (segment_frag_root, segment)
          break;
 #endif
 
+       case rs_leb128:
+         /* Initial guess is always 1; doing otherwise can result in 
+            stable solutions that are larger than the minimum.  */
+         address += fragP->fr_offset = 1;
+         break;
+
+       case rs_cfa:
+         address += eh_frame_estimate_size_before_relax (fragP);
+         break;
+
        default:
          BAD_CASE (fragP->fr_type);
          break;
@@ -1838,11 +2174,9 @@ relax_segment (segment_frag_root, segment)
        for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next)
          {
            long growth = 0;
-           unsigned long was_address;
-           long offset;
+           addressT was_address;
+           offsetT offset;
            symbolS *symbolP;
-           long target;
-           long after;
 
            was_address = fragP->fr_address;
            address = fragP->fr_address += stretch;
@@ -1878,10 +2212,10 @@ relax_segment (segment_frag_root, segment)
                      if (lie->added)
                        continue;
 
-                     offset = (lie->add->sy_frag->fr_address
+                     offset = (symbol_get_frag (lie->add)->fr_address
                                + S_GET_VALUE (lie->add)
                                + lie->addnum
-                               - (lie->sub->sy_frag->fr_address
+                               - (symbol_get_frag (lie->sub)->fr_address
                                   + S_GET_VALUE (lie->sub)));
                      if (offset <= -32768 || offset >= 32767)
                        {
@@ -1889,7 +2223,7 @@ relax_segment (segment_frag_root, segment)
                            {
                              char buf[50];
                              sprint_value (buf, (addressT) lie->addnum);
-                             as_warn (".word %s-%s+%s didn't fit",
+                             as_warn (_(".word %s-%s+%s didn't fit"),
                                       S_GET_NAME (lie->add),
                                       S_GET_NAME (lie->sub),
                                       buf);
@@ -1903,8 +2237,10 @@ relax_segment (segment_frag_root, segment)
                          for (untruth = lie->next_broken_word;
                               untruth && untruth->dispfrag == lie->dispfrag;
                               untruth = untruth->next_broken_word)
-                           if ((untruth->add->sy_frag == lie->add->sy_frag)
-                               && S_GET_VALUE (untruth->add) == S_GET_VALUE (lie->add))
+                           if ((symbol_get_frag (untruth->add)
+                                == symbol_get_frag (lie->add))
+                               && (S_GET_VALUE (untruth->add)
+                                   == S_GET_VALUE (lie->add)))
                              {
                                untruth->added = 2;
                                untruth->use_jump = lie;
@@ -1918,57 +2254,75 @@ relax_segment (segment_frag_root, segment)
 #endif
              case rs_align:
              case rs_align_code:
-               growth = (relax_align ((relax_addressT) (address
-                                                        + fragP->fr_fix),
-                                      (int) offset)
-                         - relax_align ((relax_addressT) (was_address
-                                                          + fragP->fr_fix),
-                                        (int) offset));
+               {
+                 addressT oldoff, newoff;
+
+                 oldoff = relax_align (was_address + fragP->fr_fix,
+                                       (int) offset);
+                 newoff = relax_align (address + fragP->fr_fix,
+                                       (int) offset);
+
+                 if (fragP->fr_subtype != 0)
+                   {
+                     if (oldoff > fragP->fr_subtype)
+                       oldoff = 0;
+                     if (newoff > fragP->fr_subtype)
+                       newoff = 0;
+                   }
+
+                 growth = newoff - oldoff;
+               }
                break;
 
              case rs_org:
-               target = offset;
+               {
+                 long target = offset;
+                 long after;
 
-               if (symbolP)
-                 {
+                 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
-                   target += S_GET_VALUE (symbolP)
-                     + symbolP->sy_frag->fr_address;
-                 }             /* if we have a symbol */
-
-               know (fragP->fr_next);
-               after = fragP->fr_next->fr_address;
-               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. */
-                   as_bad ("attempt to .org backwards ignored");
-                   growth = 0;
-                 }
+                     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
+                     target += (S_GET_VALUE (symbolP)
+                                + symbol_get_frag (symbolP)->fr_address);
+                   }           /* if we have a symbol */
 
-               growth -= stretch;      /* This is an absolute growth factor */
-               break;
+                 know (fragP->fr_next);
+                 after = fragP->fr_next->fr_address;
+                 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. */
+                     as_bad (_("attempt to .org backwards ignored"));
+                     growth = 0;
+                   }
+
+                 growth -= stretch;    /* This is an absolute growth factor */
+                 break;
+               }
 
              case rs_space:
                if (symbolP)
                  {
                    growth = S_GET_VALUE (symbolP);
-                   if (symbolP->sy_frag != &zero_address_frag)
-                     as_bad (".space specifies non-absolute value");
+                   if (symbol_get_frag (symbolP) != &zero_address_frag
+                       || S_IS_COMMON (symbolP)
+                       || ! S_IS_DEFINED (symbolP))
+                     as_bad_where (fragP->fr_file, fragP->fr_line,
+                                   _(".space specifies non-absolute value"));
                    fragP->fr_symbol = 0;
                    if (growth < 0)
                      {
-                       as_warn (".space or .fill with negative value, ignored");
+                       as_warn (_(".space or .fill with negative value, ignored"));
                        growth = 0;
                      }
                  }
@@ -1982,100 +2336,26 @@ relax_segment (segment_frag_root, segment)
 #else
 #ifdef TC_GENERIC_RELAX_TABLE
                /* The default way to relax a frag is to look through
-                  md_relax_table.  */
-               {
-                 const relax_typeS *this_type;
-                 const relax_typeS *start_type;
-                 relax_substateT next_state;
-                 relax_substateT this_state;
-                 long aim;
-                 const relax_typeS *table = TC_GENERIC_RELAX_TABLE;
-
-                 this_state = fragP->fr_subtype;
-                 start_type = this_type = table + this_state;
-                 target = offset;
-
-                 if (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 (symbolP->sy_frag);
-#endif
-                     know (!(S_GET_SEGMENT (symbolP) == absolute_section)
-                           || symbolP->sy_frag == &zero_address_frag);
-                     target +=
-                       S_GET_VALUE (symbolP)
-                       + symbolP->sy_frag->fr_address;
-
-                     /* 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.
-
-                        Beware zero-length frags.
-
-                        There should be a faster way to do this.  */
-
-                     if (symbolP->sy_frag->fr_address >= was_address
-                         && is_dnrange (fragP, symbolP->sy_frag))
-                       {
-                         target += stretch;
-                       }
-                   }
-
-                 aim = target - address - fragP->fr_fix;
-#ifdef TC_PCREL_ADJUST
-                 /* Currently only the ns32k family 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.  */
-                 assert (fragP->fr_pcrel_adjust == 0);
+                  TC_GENERIC_RELAX_TABLE.  */
+               growth = relax_frag (fragP, stretch);
+#endif /* TC_GENERIC_RELAX_TABLE */
 #endif
+               break;
 
-                 if (aim < 0)
-                   {
-                     /* Look backwards. */
-                     for (next_state = this_type->rlx_more; next_state;)
-                       if (aim >= this_type->rlx_backward)
-                         next_state = 0;
-                       else
-                         {
-                           /* Grow to next state. */
-                           this_state = next_state;
-                           this_type = table + this_state;
-                           next_state = this_type->rlx_more;
-                         }
-                   }
-                 else
-                   {
-#ifdef M68K_AIM_KLUDGE
-                     M68K_AIM_KLUDGE (aim, this_state, this_type);
-#endif
-                     /* Look forwards. */
-                     for (next_state = this_type->rlx_more; next_state;)
-                       if (aim <= this_type->rlx_forward)
-                         next_state = 0;
-                       else
-                         {
-                           /* Grow to next state. */
-                           this_state = next_state;
-                           this_type = table + this_state;
-                           next_state = this_type->rlx_more;
-                         }
-                   }
+             case rs_leb128:
+               {
+                 valueT value;
+                 int size;
 
-                 growth = this_type->rlx_length - start_type->rlx_length;
-                 if (growth != 0)
-                   fragP->fr_subtype = this_state;
+                 value = resolve_symbol_value (fragP->fr_symbol, 0);
+                 size = sizeof_leb128 (value, fragP->fr_subtype);
+                 growth = size - fragP->fr_offset;
+                 fragP->fr_offset = size;
                }
-#endif /* TC_GENERIC_RELAX_TABLE */
-#endif
+               break;
+
+             case rs_cfa:
+               growth = eh_frame_relax_frag (fragP);
                break;
 
              default:
@@ -2134,7 +2414,7 @@ fixup_segment (fixP, this_segment_type)
   int pcrel, plt;
   fragS *fragP;
   segT add_symbol_segment = absolute_section;
-  
+
   /* 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
@@ -2152,11 +2432,13 @@ fixup_segment (fixP, this_segment_type)
 
   for (; fixP; fixP = fixP->fx_next)
     {
+
 #ifdef DEBUG5
       fprintf (stderr, "\nprocessing fixup:\n");
       print_fixup (fixP);
 #endif
 
+
       fragP = fixP->fx_frag;
       know (fragP);
       where = fixP->fx_where;
@@ -2172,25 +2454,36 @@ fixup_segment (fixP, this_segment_type)
       plt = fixP->fx_plt;
 
       if (add_symbolP != NULL
-         && add_symbolP->sy_mri_common)
+         && symbol_mri_common_p (add_symbolP))
        {
          know (add_symbolP->sy_value.X_op == O_symbol);
          add_number += S_GET_VALUE (add_symbolP);
          fixP->fx_offset = add_number;
-         add_symbolP = fixP->fx_addsy = add_symbolP->sy_value.X_add_symbol;
+         add_symbolP = fixP->fx_addsy =
+           symbol_get_value_expression (add_symbolP)->X_add_symbol;
        }
 
       if (add_symbolP)
        add_symbol_segment = S_GET_SEGMENT (add_symbolP);
-      
+
       if (sub_symbolP)
        {
-         resolve_symbol_value (sub_symbolP);
-         if (!add_symbolP)
+         resolve_symbol_value (sub_symbolP, 1);
+         if (add_symbolP == NULL || add_symbol_segment == absolute_section)
            {
-             /* Its just -sym */
+             if (add_symbolP != NULL)
+               {
+                 add_number += S_GET_VALUE (add_symbolP);
+                 add_symbolP = NULL;
+                 fixP->fx_addsy = NULL;
+               }
+
+             /* It's just -sym */
              if (S_GET_SEGMENT (sub_symbolP) == absolute_section)
-               add_number -= S_GET_VALUE (sub_symbolP);
+               {
+                 add_number -= S_GET_VALUE (sub_symbolP);
+                 fixP->fx_subsy = NULL;
+               }
              else if (pcrel
                       && S_GET_SEGMENT (sub_symbolP) == this_segment_type)
                {
@@ -2200,12 +2493,11 @@ fixup_segment (fixP, this_segment_type)
              else
              bad_sub_reloc:
                as_bad_where (fixP->fx_file, fixP->fx_line,
-                             "Negative of non-absolute symbol %s",
+                             _("Negative of non-absolute symbol %s"),
                              S_GET_NAME (sub_symbolP));
            }
-         else if ((S_GET_SEGMENT (sub_symbolP) == add_symbol_segment)
-                  && (SEG_NORMAL (add_symbol_segment)
-                      || (add_symbol_segment == absolute_section)))
+         else if (S_GET_SEGMENT (sub_symbolP) == add_symbol_segment
+                  && SEG_NORMAL (add_symbol_segment))
            {
              /* Difference of 2 symbols from same segment.
                 Can't make difference of 2 undefineds: 'value' means
@@ -2215,7 +2507,7 @@ fixup_segment (fixP, this_segment_type)
                 as the target of a call instruction.  */
              if (fixP->fx_tcbit)
                as_bad_where (fixP->fx_file, fixP->fx_line,
-                             "callj to difference of 2 symbols");
+                             _("callj to difference of 2 symbols"));
 #endif /* TC_I960 */
              add_number += S_GET_VALUE (add_symbolP) -
                S_GET_VALUE (sub_symbolP);
@@ -2226,10 +2518,11 @@ fixup_segment (fixP, this_segment_type)
              /* Let the target machine make the final determination
                 as to whether or not a relocation will be needed to
                 handle this fixup.  */
-             if (!TC_FORCE_RELOCATION (fixP))
+             if (!TC_FORCE_RELOCATION_SECTION (fixP, this_segment_type))
                {
                  fixP->fx_pcrel = 0;
                  fixP->fx_addsy = NULL;
+                 fixP->fx_subsy = NULL;
                }
            }
          else
@@ -2251,7 +2544,7 @@ fixup_segment (fixP, this_segment_type)
                       )
                {
                  /* Make it pc-relative.  */
-                 add_number += (md_pcrel_from (fixP)
+                 add_number += (MD_PCREL_FROM_SECTION (fixP, this_segment_type)
                                 - S_GET_VALUE (sub_symbolP));
                  pcrel = 1;
                  fixP->fx_pcrel = 1;
@@ -2280,9 +2573,12 @@ fixup_segment (fixP, this_segment_type)
                  char buf[50];
                  sprint_value (buf, fragP->fr_address + where);
                  as_bad_where (fixP->fx_file, fixP->fx_line,
-                               "Can't emit reloc {- %s-seg symbol \"%s\"} @ file address %s.",
+                               _("Subtraction of two symbols in different sections \"%s\" {%s section} - \"%s\" {%s section} at file address %s."),
+                               S_GET_NAME (add_symbolP),
+                               segment_name (S_GET_SEGMENT (add_symbolP)),
+                               S_GET_NAME (sub_symbolP),
                                segment_name (S_GET_SEGMENT (sub_symbolP)),
-                               S_GET_NAME (sub_symbolP), buf);
+                               buf);
                }
            }
        }
@@ -2307,9 +2603,9 @@ fixup_segment (fixP, this_segment_type)
 #endif /* TC_I960 */
 
              add_number += S_GET_VALUE (add_symbolP);
-             add_number -= md_pcrel_from (fixP);
+             add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment_type);
              pcrel = 0;        /* Lie. Don't want further pcrel processing. */
-             
+
              /* Let the target machine make the final determination
                 as to whether or not a relocation will be needed to
                 handle this fixup.  */
@@ -2321,7 +2617,8 @@ fixup_segment (fixP, this_segment_type)
            }
          else
            {
-             if (add_symbol_segment == absolute_section)
+             if (add_symbol_segment == absolute_section
+                 && ! pcrel)
                {
 #ifdef TC_I960
                  /* See comment about reloc_callj() above.  */
@@ -2332,6 +2629,7 @@ fixup_segment (fixP, this_segment_type)
                  /* Let the target machine make the final determination
                     as to whether or not a relocation will be needed to
                     handle this fixup.  */
+
                  if (!TC_FORCE_RELOCATION (fixP))
                    {
                      fixP->fx_addsy = NULL;
@@ -2353,13 +2651,13 @@ fixup_segment (fixP, this_segment_type)
                       * relocation.
                       */
                      as_bad_where (fixP->fx_file, fixP->fx_line,
-                                   "can't use COBR format with external label");
+                                   _("can't use COBR format with external label"));
                      fixP->fx_addsy = NULL;
                      fixP->fx_done = 1;
                      continue;
                    }           /* COBR */
 #endif /* TC_I960 */
-                 
+
 #ifdef OBJ_COFF
 #ifdef TE_I386AIX
                  if (S_IS_COMMON (add_symbolP))
@@ -2371,16 +2669,24 @@ fixup_segment (fixP, this_segment_type)
              else
                {
                  seg_reloc_count++;
-#if !defined (TC_I386) || !(defined (OBJ_ELF) || defined (OBJ_COFF))
+#if !(defined (TC_V850) && defined (OBJ_ELF))
+#if !(defined (TC_M68K) && defined (OBJ_ELF))
+#if !(defined (TC_ARM)  && defined (OBJ_ELF))
+#if !(defined (TC_I960) && defined (OBJ_ELF))
+#if !defined (TC_I386) || !(defined (OBJ_ELF) || defined (OBJ_COFF)) || defined (TE_PE)
                  add_number += S_GET_VALUE (add_symbolP);
 #endif
+#endif
+#endif
+#endif
+#endif
                }
            }
        }
 
       if (pcrel)
        {
-         add_number -= md_pcrel_from (fixP);
+         add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment_type);
          if (add_symbolP == 0)
            {
 #ifndef BFD_ASSEMBLER
@@ -2388,17 +2694,40 @@ fixup_segment (fixP, this_segment_type)
 #else
              fixP->fx_addsy = section_symbol (absolute_section);
 #endif
-             fixP->fx_addsy->sy_used_in_reloc = 1;
+             symbol_mark_used_in_reloc (fixP->fx_addsy);
              ++seg_reloc_count;
            }
        }
 
+      if (!fixP->fx_done)
+       {
+#ifdef MD_APPLY_FIX3
+         md_apply_fix3 (fixP, &add_number, this_segment_type);
+#else
+#ifdef BFD_ASSEMBLER
+         md_apply_fix (fixP, &add_number);
+#else
+         md_apply_fix (fixP, add_number);
+#endif
+#endif
+
+#ifndef TC_HANDLES_FX_DONE
+         /* If the tc-* files haven't been converted, assume it's handling
+            it the old way, where a null fx_addsy means that the fix has
+            been applied completely, and no further work is needed.  */
+         if (fixP->fx_addsy == 0 && fixP->fx_pcrel == 0)
+           fixP->fx_done = 1;
+#endif
+       }
+
       if (!fixP->fx_bit_fixP && !fixP->fx_no_overflow && size > 0)
        {
-         valueT mask = 0;
-         if (size < sizeof (mask))
+         if ((size_t) size < sizeof (valueT))
            {
+             valueT mask, hibit;
+
              /* set all bits to one */
+             mask = 0;
              mask--;
              /* Technically, combining these produces an undefined result
                 if size is sizeof (valueT), though I think these two
@@ -2407,8 +2736,12 @@ fixup_segment (fixP, this_segment_type)
                 the host architecture.  */
              mask <<= size * 4;
              mask <<= size * 4;
-             if ((add_number & mask) != 0
-                 && (add_number & mask) != mask)
+             hibit = (valueT) 1 << (size * 8 - 1);
+             if (((add_number & mask) != 0
+                  || (fixP->fx_signed
+                      && (add_number & hibit) != 0))
+                 && ((add_number & mask) != mask
+                     || (add_number & hibit) == 0))
                {
                  char buf[50], buf2[50];
                  sprint_value (buf, fragP->fr_address + where);
@@ -2417,7 +2750,7 @@ fixup_segment (fixP, this_segment_type)
                  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",
+                               _("Value of %s too large for field of %d bytes at %s"),
                                buf2, size, buf);
                } /* generic error checking */
            }
@@ -2429,32 +2762,12 @@ fixup_segment (fixP, this_segment_type)
              && 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",
+                         _("Signed .word overflow; switch may be too large; %ld at 0x%lx"),
                          (long) add_number,
                          (unsigned long) (fragP->fr_address + where));
 #endif
        }                       /* not a bit fix */
 
-      if (!fixP->fx_done)
-       {
-#ifdef MD_APPLY_FIX3
-         md_apply_fix3 (fixP, &add_number, this_segment_type);
-#else
-#ifdef BFD_ASSEMBLER
-         md_apply_fix (fixP, &add_number);
-#else
-         md_apply_fix (fixP, add_number);
-#endif
-#endif
-
-#ifndef TC_HANDLES_FX_DONE
-         /* If the tc-* files haven't been converted, assume it's handling
-            it the old way, where a null fx_addsy means that the fix has
-            been applied completely, and no further work is needed.  */
-         if (fixP->fx_addsy == 0 && fixP->fx_pcrel == 0)
-           fixP->fx_done = 1;
-#endif
-       }
 #ifdef TC_VALIDATE_FIX
     skip: ;
 #endif
@@ -2476,7 +2789,7 @@ number_to_chars_bigendian (buf, val, n)
      valueT val;
      int n;
 {
-  if (n > sizeof (val)|| n <= 0)
+  if ((size_t) n > sizeof (val) || n <= 0)
     abort ();
   while (n--)
     {
@@ -2491,7 +2804,7 @@ number_to_chars_littleendian (buf, val, n)
      valueT val;
      int n;
 {
-  if (n > sizeof (val) || n <= 0)
+  if ((size_t) n > sizeof (val) || n <= 0)
     abort ();
   while (n--)
     {
@@ -2500,9 +2813,15 @@ number_to_chars_littleendian (buf, val, n)
     }
 }
 
+void
+write_print_statistics (file)
+     FILE *file;
+{
+  fprintf (file, "fixups: %d\n", n_fixups);
+}
+
 /* for debugging */
 extern int indent_level;
-extern void print_symbol_value_1 ();
 
 void
 print_fixup (fixp)
@@ -2550,6 +2869,9 @@ print_fixup (fixp)
       fprintf (stderr, ">");
     }
   fprintf (stderr, "\n");
+#ifdef TC_FIX_DATA_PRINT
+  TC_FIX_DATA_PRINT (stderr, fixp);
+#endif
 }
 
 /* end of write.c */