Additional parsing tests for PA gas.
[deliverable/binutils-gdb.git] / gas / write.c
index 4a754f41ca21a68965d822befc6c89e21206b2c8..30ab74611fe563aec28958af73eb901271ff2171 100644 (file)
@@ -1,6 +1,5 @@
 /* write.c - emit .o file
-
-   Copyright (C) 1986, 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1986, 1987, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
 #include "obstack.h"
 #include "output-file.h"
 
-/* The NOP_OPCODE is for the alignment fill value.
- * fill it a nop instruction so that the disassembler does not choke
- * on it
- */
+/* The NOP_OPCODE is for the alignment fill value.  Fill it with a nop
+   instruction so that the disassembler does not choke on it.  */
 #ifndef NOP_OPCODE
 #define NOP_OPCODE 0x00
 #endif
 
+#ifndef WORKING_DOT_WORD
+extern CONST int md_short_jump_size;
+extern CONST int md_long_jump_size;
+#endif
+
+#ifndef BFD_ASSEMBLER
+
 #ifndef MANY_SEGMENTS
 struct frag *text_frag_root;
 struct frag *data_frag_root;
@@ -43,36 +47,56 @@ struct frag *data_last_frag;        /* Last frag in segment. */
 static struct frag *bss_last_frag;     /* Last frag in segment. */
 #endif
 
+#if ! defined (BFD_ASSEMBLER) && ! defined (BFD)
 static object_headers headers;
-
-long string_byte_count;
-
 static char *the_object_file;
+#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 is_dnrange PARAMS ((struct frag * f1, struct frag * f2));
+#ifdef BFD_ASSEMBLER
+static fixS *fix_new_internal PARAMS ((fragS *, int where, int size,
+                                      symbolS *add, symbolS *sub,
+                                      offsetT offset, int pcrel,
+                                      bfd_reloc_code_real_type r_type));
+#else
+static fixS *fix_new_internal PARAMS ((fragS *, int where, int size,
+                                      symbolS *add, symbolS *sub,
+                                      offsetT offset, int pcrel,
+                                      int r_type));
+#endif
+#if defined (BFD_ASSEMBLER) || !defined (BFD)
 static long fixup_segment PARAMS ((fixS * fixP, segT this_segment_type));
-static relax_addressT relax_align PARAMS ((relax_addressT addr, long align));
-void relax_segment PARAMS ((struct frag * seg_frag_root, segT seg_type));
+#endif
+static relax_addressT relax_align PARAMS ((relax_addressT addr, int align));
 
 /*
  *                     fix_new()
  *
  * Create a fixS in obstack 'notes'.
  */
-fixS *
-fix_new (frag, where, size, add_symbol, sub_symbol, offset, pcrel, r_type)
+static fixS *
+fix_new_internal (frag, where, size, add_symbol, sub_symbol, offset, pcrel,
+                 r_type)
      fragS *frag;              /* Which frag? */
      int where;                        /* Where in that frag? */
-     short int size;           /* 1, 2, or 4 usually. */
+     int size;                 /* 1, 2, or 4 usually. */
      symbolS *add_symbol;      /* X_add_symbol. */
-     symbolS *sub_symbol;      /* X_subtract_symbol. */
-     long offset;              /* X_add_number. */
+     symbolS *sub_symbol;      /* X_op_symbol. */
+     offsetT offset;           /* X_add_number. */
      int pcrel;                        /* TRUE if PC-relative relocation. */
+#ifdef BFD_ASSEMBLER
+     bfd_reloc_code_real_type r_type; /* Relocation type */
+#else
      int r_type;               /* Relocation type */
+#endif
 {
   fixS *fixP;
 
@@ -85,42 +109,207 @@ fix_new (frag, where, size, add_symbol, sub_symbol, offset, pcrel, r_type)
   fixP->fx_subsy = sub_symbol;
   fixP->fx_offset = offset;
   fixP->fx_pcrel = pcrel;
-#if defined(TC_SPARC) || defined(TC_A29K) || defined( NEED_FX_R_TYPE)
+#if defined(NEED_FX_R_TYPE) || defined (BFD_ASSEMBLER)
   fixP->fx_r_type = r_type;
 #endif
-  /* JF these 'cuz of the NS32K stuff */
   fixP->fx_im_disp = 0;
   fixP->fx_pcrel_adjust = 0;
-  fixP->fx_bsr = 0;
   fixP->fx_bit_fixP = 0;
+  fixP->fx_addnumber = 0;
+
+#ifdef TC_something
+  fixP->fx_bsr = 0;
+#endif
+#ifdef TC_I960
+  fixP->fx_callj = 0;
+#endif
 
-  /* 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. */
+  as_where (&fixP->fx_file, &fixP->fx_line);
+
+  /* Usually, we want relocs sorted numerically, but while
+     comparing to older versions of gas that have relocs
+     reverse sorted, it is convenient to have this compile
+     time option.  xoxorich. */
+
+  {
+
+#ifdef BFD_ASSEMBLER
+    fixS **seg_fix_rootP = & (seg_info (now_seg)->fix_root);
+    fixS **seg_fix_tailP = & (seg_info (now_seg)->fix_tail);
+#endif
 
 #ifdef REVERSE_SORT_RELOCS
 
-  fixP->fx_next = *seg_fix_rootP;
-  *seg_fix_rootP = fixP;
+    fixP->fx_next = *seg_fix_rootP;
+    *seg_fix_rootP = fixP;
 
 #else /* REVERSE_SORT_RELOCS */
 
-  fixP->fx_next = NULL;
+    fixP->fx_next = NULL;
 
-  if (*seg_fix_tailP)
-    (*seg_fix_tailP)->fx_next = fixP;
-  else
-    *seg_fix_rootP = fixP;
-  *seg_fix_tailP = fixP;
+    if (*seg_fix_tailP)
+      (*seg_fix_tailP)->fx_next = fixP;
+    else
+      *seg_fix_rootP = fixP;
+    *seg_fix_tailP = fixP;
 
 #endif /* REVERSE_SORT_RELOCS */
 
-  fixP->fx_callj = 0;
-  return (fixP);
-}                              /* fix_new() */
+  }
+
+  return fixP;
+}
+
+/* Create a fixup relative to a symbol (plus a constant).  */
+
+fixS *
+fix_new (frag, where, size, add_symbol, offset, pcrel, r_type)
+     fragS *frag;              /* Which frag? */
+     int where;                        /* Where in that frag? */
+     short int size;           /* 1, 2, or 4 usually. */
+     symbolS *add_symbol;      /* X_add_symbol. */
+     offsetT offset;           /* X_add_number. */
+     int pcrel;                        /* TRUE if PC-relative relocation. */
+#ifdef BFD_ASSEMBLER
+     bfd_reloc_code_real_type r_type; /* Relocation type */
+#else
+     int r_type;               /* Relocation type */
+#endif
+{
+  return fix_new_internal (frag, where, size, add_symbol,
+                          (symbolS *) NULL, offset, pcrel, r_type);
+}
+
+/* Create a fixup for an expression.  Currently we only support fixups
+   for difference expressions.  That is itself more than most object
+   file formats support anyhow.  */
+
+fixS *
+fix_new_exp (frag, where, size, exp, pcrel, r_type)
+     fragS *frag;              /* Which frag? */
+     int where;                        /* Where in that frag? */
+     short int size;           /* 1, 2, or 4 usually. */
+     expressionS *exp;         /* Expression.  */
+     int pcrel;                        /* TRUE if PC-relative relocation. */
+#ifdef BFD_ASSEMBLER
+     bfd_reloc_code_real_type r_type; /* Relocation type */
+#else
+     int r_type;               /* Relocation type */
+#endif
+{
+  symbolS *add = NULL;
+  symbolS *sub = NULL;
+  offsetT off = 0;
+  
+  switch (exp->X_op)
+    {
+    case O_absent:
+      break;
+
+    case O_uminus:
+      sub = exp->X_add_symbol;
+      off = exp->X_add_number;
+      break;
+
+    case O_subtract:
+      sub = exp->X_op_symbol;
+      /* Fall through.  */
+    case O_symbol:
+      add = exp->X_add_symbol;
+      /* Fall through.   */
+    case O_constant:
+      off = exp->X_add_number;
+      break;
+      
+    default:
+      as_bad ("expression too complex for fixup");
+    }
+
+  return fix_new_internal (frag, where, size, add, sub, off,
+                          pcrel, r_type);
+}
+
+/* Append a string onto another string, bumping the pointer along.  */
+void
+append (charPP, fromP, length)
+     char **charPP;
+     char *fromP;
+     unsigned long length;
+{
+  /* Don't trust memcpy() of 0 chars. */
+  if (length == 0)
+    return;
 
-#ifndef BFD
+  memcpy (*charPP, fromP, 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
+ * least one object format really uses this info.
+ */
+void 
+record_alignment (seg, align)
+     /* 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;
+{
+#ifdef BFD_ASSEMBLER
+  if (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 defined (BFD_ASSEMBLER) || ! defined (BFD)
+
+static fragS *
+chain_frchains_together_1 (section, frchp)
+     segT section;
+     struct frchain *frchp;
+{
+  fragS dummy, *prev_frag = &dummy;
+  for (; frchp && frchp->frch_seg == section; frchp = frchp->frch_next)
+    {
+      prev_frag->fr_next = frchp->frch_root;
+      prev_frag = frchp->frch_last;
+    }
+  prev_frag->fr_next = 0;
+  return prev_frag;
+}
+
+#endif
+
+#ifdef BFD_ASSEMBLER
+
+static void
+chain_frchains_together (abfd, section, xxx)
+     bfd *abfd;                        /* unused */
+     segT section;
+     char *xxx;                        /* unused */
+{
+  segment_info_type *info;
+
+  /* BFD may have introduced its own sections without using
+     subseg_new, so it is possible that seg_info is NULL.  */
+  info = seg_info (section);
+  if (info != (segment_info_type *) NULL)
+    chain_frchains_together_1 (section, info->frchainP);
+}
+
+#endif
+
+#if !defined (BFD) && !defined (BFD_ASSEMBLER)
 
 void 
 remove_subsegs (head, seg, root, last)
@@ -129,129 +318,517 @@ remove_subsegs (head, seg, root, last)
      fragS **root;
      fragS **last;
 {
-  fragS dummy;
-  fragS *prev_frag = &dummy;
   *root = head->frch_root;
-  while (head && head->frch_seg == seg)
+  *last = chain_frchains_together_1 (seg, head);
+}
+
+#endif /* BFD */
+
+#if defined (BFD_ASSEMBLER) || !defined (BFD)
+
+#ifdef BFD_ASSEMBLER
+static void
+cvt_frag_to_fill (sec, fragP)
+     segT sec;
+     fragS *fragP;
+#else
+static void
+cvt_frag_to_fill (headers, fragP)
+     object_headers *headers;
+     fragS *fragP;
+#endif
+{
+  switch (fragP->fr_type)
     {
-      prev_frag->fr_next = head->frch_root;
+    case rs_align:
+    case rs_org:
+#ifdef HANDLE_ALIGN
+      HANDLE_ALIGN (fragP);
+#endif
+      fragP->fr_type = rs_fill;
+      know (fragP->fr_var == 1);
+      know (fragP->fr_next != NULL);
+
+      fragP->fr_offset = (fragP->fr_next->fr_address
+                         - fragP->fr_address
+                         - fragP->fr_fix);
+      break;
+
+    case rs_fill:
+      break;
+
+    case rs_machine_dependent:
+#ifdef BFD_ASSEMBLER
+      md_convert_frag (stdoutput, sec, fragP);
+#else
+      md_convert_frag (headers, fragP);
+#endif
+
+      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
+       * required.
+       */
+      frag_wane (fragP);
+      break;
+
+#ifndef WORKING_DOT_WORD
+    case rs_broken_word:
+      {
+       struct broken_word *lie;
+
+       if (fragP->fr_subtype)
+         {
+           fragP->fr_fix += md_short_jump_size;
+           for (lie = (struct broken_word *) (fragP->fr_symbol);
+                lie && lie->dispfrag == fragP;
+                lie = lie->next_broken_word)
+             if (lie->added == 1)
+               fragP->fr_fix += md_long_jump_size;
+         }
+       frag_wane (fragP);
+      }
+      break;
+#endif
 
-      prev_frag = head->frch_last;
-      head = head->frch_next;
+    default:
+      BAD_CASE (fragP->fr_type);
+      break;
     }
-  *last = prev_frag;
-  prev_frag->fr_next = 0;
 }
 
-void 
-write_object_file ()
+#endif /* defined (BFD_ASSEMBLER) || !defined (BFD) */
+
+#ifdef BFD_ASSEMBLER
+static void
+relax_and_size_seg (abfd, sec, xxx)
+     bfd *abfd;
+     asection *sec;
+     char *xxx;
 {
-  register struct frchain *frchainP;   /* Track along all frchains. */
-  register fragS *fragP;       /* Track along all frags. */
-  register struct frchain *next_frchainP;
-  register fragS **prev_fragPP;
-  unsigned int data_siz;
+  flagword flags;
+  fragS *fragp;
+  segment_info_type *seginfo;
+  int x;
+  valueT size, newsize;
 
-  long object_file_size;
+  flags = bfd_get_section_flags (abfd, sec);
 
-#ifdef 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 /* 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.
-        */
-#define SUB_SEGMENT_ALIGN (2)
-  for (frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next)
+  seginfo = (segment_info_type *) bfd_get_section_userdata (abfd, sec);
+  if (seginfo && seginfo->frchainP)
     {
-#ifdef VMS
-      /*
-                *      Under VAX/VMS, the linker (and PSECT specifications)
-                *      take care of correctly aligning the segments.
-                *      Doing the alignment here (on initialized data) can
-                *      mess up the calculation of global data PSECT sizes.
-                */
-#undef SUB_SEGMENT_ALIGN
-#define        SUB_SEGMENT_ALIGN ((frchainP->frch_seg != SEG_DATA) ? 2 : 0)
-#endif /* VMS */
-      subseg_new (frchainP->frch_seg, frchainP->frch_subseg);
-      frag_align (SUB_SEGMENT_ALIGN, 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);
-      /* know( frags . obstack_c_base == frags . obstack_c_next_free ); */
-      /* Above shows we haven't left a half-completed object on obstack. */
-    }                          /* walk the frag chain */
+      relax_segment (seginfo->frchainP->frch_root, sec);
+      for (fragp = seginfo->frchainP->frch_root; fragp; fragp = fragp->fr_next)
+       cvt_frag_to_fill (sec, fragp);
+      for (fragp = seginfo->frchainP->frch_root;
+          fragp->fr_next;
+          fragp = fragp->fr_next)
+       /* walk to last elt */;
+      size = fragp->fr_address + fragp->fr_fix;
+    }
+  else
+    size = 0;
+  if (size > 0)
+    {
+      flags |= SEC_HAS_CONTENTS;
+      /* @@ This is just an approximation.  */
+      if (seginfo->fix_root)
+       flags |= SEC_RELOC;
+      else
+       flags &= ~SEC_RELOC;
+      x = bfd_set_section_flags (abfd, sec, flags);
+      assert (x == true);
+    }
+  size = md_section_align (sec, size);
+  x = bfd_set_section_size (abfd, sec, size);
+  assert (x == true);
+
+  /* If the size had to be rounded up, add some padding in the last
+     non-empty frag.  */
+  newsize = bfd_get_section_size_before_reloc (sec);
+  assert (newsize >= size);
+  if (size != newsize)
+    {
+      fragS *last = seginfo->frchainP->frch_last;
+      fragp = seginfo->frchainP->frch_root;
+      while (fragp->fr_next != last)
+       fragp = fragp->fr_next;
+      last->fr_address = size;
+      fragp->fr_offset += newsize - size;
+    }
 
-  /*
-        * From now on, we don't care about sub-segments.
-        * Build one frag chain for each segment. Linked thru fr_next.
-        * We know that there is at least 1 text frchain & at least 1 data
-        * frchain.
-        */
+#ifdef tc_frob_section
+  tc_frob_section (sec);
+#endif
+#ifdef obj_frob_section
+  obj_frob_section (sec);
+#endif
+}
 
-  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);
+#ifdef DEBUG2
+static void
+dump_section_relocs (abfd, sec, stream_)
+     bfd *abfd;
+     asection *sec;
+     char *stream_;
+{
+  FILE *stream = (FILE *) stream_;
+  segment_info_type *seginfo = seg_info (sec);
+  fixS *fixp = seginfo->fix_root;
 
-  /*
-        * 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.
-        */
-#ifndef OBJ_AOUT
-  if (flagseen['R'])
+  if (!fixp)
+    return;
+
+  fprintf (stream, "sec %s relocs:\n", sec->name);
+  while (fixp)
     {
-      fixS *tmp;
+      symbolS *s = fixp->fx_addsy;
+      if (s)
+       fprintf (stream, "  %08x: %s(%s+%x)+%x\n", fixp,
+                S_GET_NAME (s), s->bsym->section->name,
+                S_GET_VALUE (s), fixp->fx_offset);
+      else
+       fprintf (stream, "  %08x: type %d no sym\n", fixp, fixp->fx_r_type);
+      fixp = fixp->fx_next;
+    }
+}
+#else
+#define dump_section_relocs(ABFD,SEC,STREAM)   (void)(ABFD,SEC,STREAM)
+#endif
 
-      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)
+static void
+adjust_reloc_syms (abfd, sec, xxx)
+     bfd *abfd;
+     asection *sec;
+     char *xxx;
+{
+  segment_info_type *seginfo = seg_info (sec);
+  fixS *fixp;
+
+  if (seginfo == NULL)
+    return;
+
+  dump_section_relocs (abfd, sec, stderr);
+
+  for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
+    if (fixp->fx_addsy)
+      {
+       symbolS *sym = fixp->fx_addsy;
+       asection *symsec = sym->bsym->section;
+       segment_info_type *symseginfo = seg_info (symsec);
+
+       /* If it's one of these sections, assume the symbol is definitely
+          going to be output.  */
+       if (symsec == &bfd_und_section
+           || symsec == &bfd_abs_section
+           || bfd_is_com_section (symsec))
+         {
+           fixp->fx_addsy->sy_used_in_reloc = 1;
+           continue;
+         }
+
+       /* Since we're reducing to section symbols, don't attempt to reduce
+          anything that's already using one.  */
+       if (sym->bsym == symsec->symbol)
+         {
+           fixp->fx_addsy->sy_used_in_reloc = 1;
+           continue;
+         }
+
+       /* 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;
+           continue;
+         }
+#endif
+
+       /* Is there some other (target cpu dependent) reason we can't adjust
+          this one?  (E.g. relocations involving function addresses on 
+          the PA.  */
+#ifdef tc_fix_adjustable
+       if (! tc_fix_adjustable (fixp))
+         continue;
+#endif
+
+       /* 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.  */
+       fixp->fx_offset += S_GET_VALUE (sym);
+       if (sym->sy_frag)
+         fixp->fx_offset += sym->sy_frag->fr_address;
+       if (symseginfo->sym)
+         fixp->fx_addsy = symseginfo->sym;
+       else
+         {
+           fixp->fx_addsy = symbol_find (symsec->name);
+           if (!fixp->fx_addsy)
+             {
+               fixp->fx_addsy = symbol_make (symsec->name);
+               fixp->fx_addsy->bsym = symsec->symbol;
+             }
+           symseginfo->sym = fixp->fx_addsy;
+         }
+       fixp->fx_addsy->sy_used_in_reloc = 1;
+      }
+
+  dump_section_relocs (abfd, sec, stderr);
+}
+
+static void
+write_relocs (abfd, sec, xxx)
+     bfd *abfd;
+     asection *sec;
+     char *xxx;
+{
+  segment_info_type *seginfo = seg_info (sec);
+  int i;
+  unsigned int n;
+  arelent **relocs;
+  fixS *fixp;
+
+  /* If seginfo is NULL, we did not create this section; don't do
+     anything with it.  */
+  if (seginfo == NULL)
+    return;
+
+  fixup_segment (seginfo->fix_root, sec);
+
+  n = 0;
+  for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
+    n++;
+
+#ifndef RELOC_EXPANSION_POSSIBLE
+  /* Set up reloc information as well.  */
+  relocs = (arelent **) bfd_alloc_by_size_t (stdoutput,
+                                            n * sizeof (arelent *));
+  memset ((char*)relocs, 0, n * sizeof (arelent*));
+
+  i = 0;
+  for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
+    {
+      arelent *reloc;
+      char *data;
+      bfd_reloc_status_type s;
+
+      if (fixp->fx_addsy == 0)
        {
-         for (tmp = text_fix_root; tmp->fx_next; tmp = tmp->fx_next);;
-         tmp->fx_next = data_fix_root;
-         text_fix_tail = data_fix_tail;
+         /* @@ Need some other flag to indicate which have already
+            been performed...  */
+         n--;
+         continue;
        }
-      else
-       text_fix_root = data_fix_root;
-      data_fix_root = NULL;
+      reloc = tc_gen_reloc (sec, fixp);
+      if (!reloc)
+       {
+         n--;
+         continue;
+       }
+      data = fixp->fx_frag->fr_literal + fixp->fx_where;
+      /* @@ Assumes max size of reloc is 4. */
+      if (fixp->fx_where + 4
+         > fixp->fx_frag->fr_fix + fixp->fx_frag->fr_offset)
+       abort ();
+      /* Pass bogus address so that when bfd_perform_relocation adds
+        `address' back in, it'll come up with `data', which is where
+        we want it to operate.  */
+      if (reloc->howto->partial_inplace == false
+         && reloc->howto->pcrel_offset == true
+         && reloc->howto->pc_relative == true)
+       {
+         /* bfd_perform_relocation screws this up */
+         reloc->addend += reloc->address;
+       }
+      s = bfd_perform_relocation (stdoutput, reloc, data - reloc->address,
+                                 sec, stdoutput);
+      switch (s)
+       {
+       case bfd_reloc_ok:
+         break;
+       default:
+         as_fatal ("bad return from bfd_perform_relocation");
+       }
+      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 *));
+
+  i = 0;
+  for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next)
+    {
+      arelent **reloc;
+      char *data;
+      bfd_reloc_status_type s;
+      int j;
+
+      if (fixp->fx_addsy == 0)
+       {
+         /* @@ Need some other flag to indicate which have already
+            been performed...  */
+         n--;
+         continue;
+       }
+      reloc = tc_gen_reloc (sec, fixp);
+
+      for (j = 0; reloc[j]; j++)
+       {
+          relocs[i++] = reloc[j];
+          assert(i <= n);
+       }
+      data = fixp->fx_frag->fr_literal + fixp->fx_where;
+      if (fixp->fx_where + 4
+         > fixp->fx_frag->fr_fix + fixp->fx_frag->fr_offset)
+       abort ();
+      for (j = 0; reloc[j]; j++)
+        {
+          s = bfd_perform_relocation (stdoutput, reloc[j], data - reloc[j]->address,
+                                     sec, stdoutput);
+          switch (s)
+           {
+             case bfd_reloc_ok:
+               break;
+             default:
+               as_fatal ("bad return from bfd_perform_relocation");
+           }
+        }
+    }
+  n = i;
+#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));
+#ifdef DEBUG2
+  {
+    int i;
+    arelent *r;
+    asymbol *s;
+    fprintf (stderr, "relocs for sec %s\n", sec->name);
+    for (i = 0; i < n; i++)
+      {
+       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);
+      }
+  }
+#endif
+}
+
+static void
+write_contents (abfd, sec, xxx)
+     bfd *abfd;
+     asection *sec;
+     char *xxx;
+{
+  segment_info_type *seginfo = seg_info (sec);
+  unsigned long offset = 0;
+  fragS *f;
+
+  /* Write out the frags.  */
+  if (seginfo == NULL
+      || ! (bfd_get_section_flags (abfd, sec) & SEC_HAS_CONTENTS))
+    return;
+
+  for (f = seginfo->frchainP->frch_root;
+       f;
+       f = f->fr_next)
+    {
+      int x;
+      unsigned long fill_size;
+      char *fill_literal;
+      long count;
+
+      assert (f->fr_type == rs_fill);
+      if (f->fr_fix)
+       {
+         x = bfd_set_section_contents (stdoutput, sec,
+                                       f->fr_literal, (file_ptr) offset,
+                                       (bfd_size_type) f->fr_fix);
+         assert (x == true);
+         offset += f->fr_fix;
+       }
+      fill_literal = f->fr_literal + f->fr_fix;
+      fill_size = f->fr_var;
+      count = f->fr_offset;
+      assert (count >= 0);
+      if (fill_size && count)
+       while (count--)
+         {
+           x = bfd_set_section_contents (stdoutput, sec,
+                                         fill_literal, (file_ptr) offset,
+                                         (bfd_size_type) fill_size);
+           assert (x == true);
+           offset += fill_size;
+         }
     }
+}
+#endif
+
+#if defined(BFD_ASSEMBLER) || (!defined (BFD) && !defined(OBJ_AOUT))
+static void
+merge_data_into_text ()
+{
+#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.
-        */
+   * 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].
-        */
+   * 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;
@@ -264,10 +841,10 @@ write_object_file ()
 #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....  */
+        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
 
@@ -282,7 +859,6 @@ write_object_file ()
   else
     {
       H_SET_DATA_SIZE (&headers, 0);
-      data_siz = 0;
     }
 
 #ifdef OBJ_BOUT
@@ -296,15 +872,16 @@ write_object_file ()
     bss_vma = RoundUp (bss_vma, 1 << section_alignment[SEG_BSS]);
     bss_address_frag.fr_address = bss_vma;
   }
-#else
+#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 + local_bss_counter;
+      relax_addressT slide = bss_address_frag.fr_address;
 
       for (fragP = bss_frag_root; fragP; fragP = fragP->fr_next)
        {
@@ -312,124 +889,191 @@ write_object_file ()
        }                       /* for each bss frag */
     }
 
+  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)
+
+void 
+write_object_file ()
+{
+  register struct frchain *frchainP;   /* Track along all frchains. */
+#if ! defined (BFD_ASSEMBLER) || ! defined (WORKING_DOT_WORD)
+  register fragS *fragP;       /* Track along all frags. */
+#endif
+#if !defined (BFD_ASSEMBLER) && !defined (OBJ_VMS)
+  long object_file_size;
+#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 (flagseen['Z'])
+      {
+       if (n_warns || n_errs)
+         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",
+                   n_errs, n_errs == 1 ? "" : "s",
+                   n_warns, n_warns == 1 ? "" : "s");
+      }
+  }
+
+#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 /* 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.  */
+
+#ifdef BFD_ASSEMBLER
+  /* Remove the sections created by gas for its own purposes.  */
+  {
+    asection **seclist, *sec;
+    seclist = &stdoutput->sections;
+    while (seclist && *seclist)
+      {
+       sec = *seclist;
+       while (sec == reg_section || sec == expr_section)
+         {
+           sec = sec->next;
+           *seclist = sec;
+           stdoutput->section_count--;
+           if (!sec)
+             break;
+         }
+       if (*seclist)
+         seclist = &(*seclist)->next;
+      }
+  }
+
+  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
-  if (bss_last_frag)
+
+  /* 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 (flagseen['R'])
     {
-      local_bss_counter += bss_last_frag->fr_address - bss_frag_root->fr_address;
+      merge_data_into_text ();
     }
-  H_SET_BSS_SIZE (&headers, local_bss_counter);
+#endif
+
+#ifdef BFD_ASSEMBLER
+  bfd_map_over_sections (stdoutput, relax_and_size_seg, (char *) 0);
+#else
+  relax_and_size_all_segments ();
+#endif /* BFD_ASSEMBLER */
 
+#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.
-        *
-        */
+   *
+   * 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;
-    }                          /* if no strings, then no 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();
-        */
+   * 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)
     {
-      switch (fragP->fr_type)
-       {
-       case rs_align:
-       case rs_org:
-#ifdef HANDLE_ALIGN
-         HANDLE_ALIGN (fragP);
-#endif
-         fragP->fr_type = rs_fill;
-         know (fragP->fr_var == 1);
-         know (fragP->fr_next != NULL);
-
-         fragP->fr_offset = (fragP->fr_next->fr_address
-                             - fragP->fr_address
-                             - fragP->fr_fix);
-         break;
-
-       case rs_fill:
-         break;
-
-       case rs_machine_dependent:
-         md_convert_frag (&headers, fragP);
-
-         know ((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
-                        * required.
-                        */
-         frag_wane (fragP);
-         break;
-
-#ifndef WORKING_DOT_WORD
-       case rs_broken_word:
-         {
-           struct broken_word *lie;
-           extern md_short_jump_size;
-           extern md_long_jump_size;
-
-           if (fragP->fr_subtype)
-             {
-               fragP->fr_fix += md_short_jump_size;
-               for (lie = (struct broken_word *) (fragP->fr_symbol); lie && lie->dispfrag == fragP; lie = lie->next_broken_word)
-                 if (lie->added == 1)
-                   fragP->fr_fix += md_long_jump_size;
-             }
-           frag_wane (fragP);
-         }
-         break;
-#endif
-
-       default:
-         BAD_CASE (fragP->fr_type);
-         break;
-       }                       /* switch (fr_type) */
+      cvt_frag_to_fill (&headers, fragP);
 
-      if (!((fragP->fr_next == NULL)
+      /* 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)
+           || fragP->fr_next == data_frag_root
 #endif
            || ((fragP->fr_next->fr_address - fragP->fr_address)
-               == (fragP->fr_fix + (fragP->fr_offset * fragP->fr_var)))))
-       {
-         fprintf (stderr, "assertion failed: file `%s', line %d\n",
-                  __FILE__, __LINE__ - 4);
-         exit (1);
-       }
-    }                          /* for each frag. */
+               == (fragP->fr_fix + fragP->fr_offset * fragP->fr_var))))
+       abort ();
+#endif
+    }
+#endif /* ! BFD_ASSEMBLER */
 
 #ifndef WORKING_DOT_WORD
   {
@@ -440,33 +1084,33 @@ write_object_file ()
     for (lie = broken_words; lie; lie = lie->next_broken_word)
       if (!lie->added)
        {
+         expressionS exp;
+
+         exp.X_op = O_subtract;
+         exp.X_add_symbol = lie->add;
+         exp.X_op_symbol = lie->sub;
+         exp.X_add_number = lie->addnum;
+#ifdef BFD_ASSEMBLER
+         fix_new_exp (lie->frag,
+                      lie->word_goes_here - lie->frag->fr_literal,
+                      2, &exp, 0, BFD_RELOC_NONE);
+#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 (lie->frag,
-                        lie->word_goes_here - lie->frag->fr_literal,
-                        2,
-                        lie->add,
-                        lie->sub,
-                        lie->addnum,
-                        0, 0, 2, 0, 0);
+         fix_new_ns32k_exp (lie->frag,
+                            lie->word_goes_here - lie->frag->fr_literal,
+                            2, &exp, 0, 0, 2, 0, 0);
 #else
-# if defined(TC_SPARC) || defined(TC_A29K) || defined(NEED_FX_R_TYPE)
-         fix_new (lie->frag, lie->word_goes_here - lie->frag->fr_literal,
-                  2, lie->add,
-                  lie->sub, lie->addnum,
-                  0, NO_RELOC);
-# else
-         fix_new (lie->frag, lie->word_goes_here - lie->frag->fr_literal,
-                  2, lie->add,
-                  lie->sub, lie->addnum,
-                  0, 0);
-
-# endif                                /* tc_sparc|tc_a29k|need_fx_r_type */
+         fix_new_exp (lie->frag,
+                      lie->word_goes_here - lie->frag->fr_literal,
+                      2, &exp, 0, 0);
 #endif /* TC_NS32K */
-         /* md_number_to_chars(lie->word_goes_here,
-                              S_GET_VALUE(lie->add)
-                              + lie->addnum
-                              - S_GET_VALUE(lie->sub),
-                              2); */
+#endif /* TC_SPARC|TC_A29K|NEED_FX_R_TYPE */
+#endif /* BFD_ASSEMBLER */
          *prevP = lie->next_broken_word;
        }
       else
@@ -476,16 +1120,13 @@ write_object_file ()
       {
        struct broken_word *untruth;
        char *table_ptr;
-       long table_addr;
-       long from_addr, to_addr;
+       addressT table_addr;
+       addressT from_addr, to_addr;
        int n, m;
 
-       extern md_short_jump_size;
-       extern md_long_jump_size;
-
        fragP = lie->dispfrag;
 
-       /* Find out how many broken_words go here */
+       /* Find out how many broken_words go here */
        n = 0;
        for (untruth = lie; untruth && untruth->dispfrag == fragP; untruth = untruth->next_broken_word)
          if (untruth->added == 1)
@@ -493,8 +1134,8 @@ write_object_file ()
 
        table_ptr = lie->dispfrag->fr_opcode;
        table_addr = lie->dispfrag->fr_address + (table_ptr - lie->dispfrag->fr_literal);
-       /* Create the jump around the long jumps */
-       /* This is a short jump from table_ptr+0 to table_ptr+n*long_jump_size */
+       /* Create the jump around the long jumps.  This is a short
+          jump from table_ptr+0 to table_ptr+n*long_jump_size.  */
        from_addr = table_addr;
        to_addr = table_addr + md_short_jump_size + n * md_long_jump_size;
        md_create_short_jump (table_ptr, from_addr, to_addr, lie->dispfrag, lie->add);
@@ -507,8 +1148,7 @@ write_object_file ()
              continue;
            /* Patch the jump table */
            /* This is the offset from ??? to table_ptr+0 */
-           to_addr = table_addr
-             - S_GET_VALUE (lie->sub);
+           to_addr = table_addr - S_GET_VALUE (lie->sub);
            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)
              {
@@ -528,20 +1168,19 @@ write_object_file ()
   }
 #endif /* not WORKING_DOT_WORD */
 
-#ifndef        VMS
+#ifndef BFD_ASSEMBLER
+#ifndef        OBJ_VMS
   {                            /* not vms */
     /*
-                * Scan every FixS performing fixups. We had to wait until now to do
-                * this because md_convert_frag() may have made some fixSs.
-                */
+     * 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);
+    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);
+    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 */
@@ -549,22 +1188,6 @@ write_object_file ()
     H_SET_ENTRY_POINT (&headers, 0);
 
     obj_pre_write_hook (&headers);     /* extra coff stuff */
-    if ((had_warnings () && flagseen['Z'])
-       || had_errors () > 0)
-      {
-       if (flagseen['Z'])
-         {
-           as_warn ("%d error%s, %d warning%s, generating bad object file.\n",
-                    had_errors (), had_errors () == 1 ? "" : "s",
-                    had_warnings (), had_warnings () == 1 ? "" : "s");
-         }
-       else
-         {
-           as_fatal ("%d error%s, %d warning%s, no object file generated.\n",
-                     had_errors (), had_errors () == 1 ? "" : "s",
-                     had_warnings (), had_warnings () == 1 ? "" : "s");
-         }                     /* on want output */
-      }                                /* on error condition */
 
     object_file_size = H_GET_FILE_SIZE (&headers);
     next_object_file_charP = the_object_file = xmalloc (object_file_size);
@@ -576,8 +1199,8 @@ write_object_file ()
     know ((next_object_file_charP - the_object_file) == H_GET_HEADER_SIZE (&headers));
 
     /*
-                * Emit code.
-                */
+     * Emit code.
+     */
     for (fragP = text_frag_root; fragP; fragP = fragP->fr_next)
       {
        register long count;
@@ -600,15 +1223,15 @@ write_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)));
 
     /*
-                * Emit relocations.
-                */
+     * 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.
-                */
+     * 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);
@@ -617,30 +1240,26 @@ write_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)));
 
     /*
-                * Emit line number entries.
-                */
+     * 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.
-                */
+     * 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.
-                */
+     * Emit strings.
+     */
 
     if (string_byte_count > 0)
       {
        obj_emit_strings (&next_object_file_charP);
       }                                /* only if we have a string table */
 
-    /*   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) + H_GET_STRING_SIZE(&headers)));
-                */
-    /*   know(next_object_file_charP == the_object_file + object_file_size);*/
-
 #ifdef BFD_HEADERS
     bfd_seek (stdoutput, 0, 0);
     bfd_write (the_object_file, 1, object_file_size, stdoutput);
@@ -654,14 +1273,165 @@ write_object_file ()
   }                            /* non vms output */
 #else /* VMS */
   /*
-        *      Now do the VMS-dependent part of writing the object file
-        */
-  VMS_write_object_file (text_siz, data_siz, text_frag_root, data_frag_root);
+   *   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 /* VMS */
-}                              /* write_object_file() */
+#else /* BFD_ASSEMBLER */
 
-#else
+#ifdef obj_check_file_symbols
+  obj_check_file_symbols ();
+#endif
+
+  bfd_map_over_sections (stdoutput, adjust_reloc_syms, (char *)0);
+
+  /* Set up symbol table, and write it out.  */
+  if (symbol_rootP)
+    {
+      unsigned int i = 0;
+      unsigned int n;
+      symbolS *symp;
+
+      for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+       {
+         if (! symp->sy_resolved)
+           {
+             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);
+           }
+
+         /* 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);
+#if 0
+         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));
+#endif
+         if (! symp->sy_used_in_reloc)
+           {
+#ifdef obj_frob_symbol
+             {
+               int punt = 0;
+               obj_frob_symbol (symp, punt);
+               if (punt)
+                 goto punt_it;
+             }
+#endif
+#ifdef tc_frob_symbol
+             {
+               int punt = 0;
+               tc_frob_symbol (symp, punt);
+               if (punt)
+                 goto punt_it;
+             }
 #endif
+           }
+
+         /* If we don't want to keep this symbol, splice it out of the
+            chain now.  */
+         if (! symp->sy_used_in_reloc
+             && S_IS_LOCAL (symp))
+           {
+             symbolS *prev, *next;
+#if defined (obj_frob_symbol) || defined (tc_frob_symbol)
+           punt_it:
+#endif
+             prev = symbol_previous (symp);
+             next = symbol_next (symp);
+#ifdef DEBUG_SYMS
+             verify_symbol_chain_2 (symp);
+#endif
+             if (prev)
+               {
+                 symbol_next (prev) = next;
+                 symp = prev;
+               }
+             else if (symp == symbol_rootP)
+               symbol_rootP = next;
+             else
+               abort ();
+             if (next)
+               symbol_previous (next) = prev;
+             else
+               symbol_lastP = prev;
+#ifdef DEBUG_SYMS
+             if (prev)
+               verify_symbol_chain_2 (prev);
+             else if (next)
+               verify_symbol_chain_2 (next);
+#endif
+             continue;
+           }
+
+         /* Make sure we really got a value for the symbol.  */
+         if (! symp->sy_resolved)
+           {
+             as_bad ("can't resolve value for symbol \"%s\"",
+                     S_GET_NAME (symp));
+             symp->sy_resolved = 1;
+           }
+
+         /* 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);
+
+         i++;
+       }
+      n = i;
+      if (n)
+       {
+         asymbol **asympp;
+         boolean result;
+         extern PTR bfd_alloc PARAMS ((bfd *, size_t));
+
+         asympp = (asymbol **) bfd_alloc (stdoutput,
+                                          n * sizeof (asymbol *));
+         symp = symbol_rootP;
+         for (i = 0; i < n; i++, symp = symbol_next (symp))
+           {
+             asympp[i] = symp->bsym;
+             symp->written = 1;
+           }
+         result = bfd_set_symtab (stdoutput, asympp, n);
+         assert (result == true);
+       }
+    }
+
+
+#ifdef obj_frob_file
+  /* If obj_frob_file changes the symbol value at this point, it is
+     responsible for moving the changed value into symp->bsym->value
+     as well.  Hopefully all symbol value changing can be done in
+     {obj,tc}_frob_symbol.  */
+  obj_frob_file ();
+#endif
+
+  /* Now that all the sizes are known, and contents correct, we can
+     start writing the file.  */
+  bfd_map_over_sections (stdoutput, write_relocs, (char *) 0);
+
+  bfd_map_over_sections (stdoutput, write_contents, (char *) 0);
+
+  output_file_close (out_file_name);
+#endif /* BFD_ASSEMBLER */
+}
+#endif /* ! BFD */
 
 /*
  *                     relax_segment()
@@ -676,25 +1446,52 @@ write_object_file ()
  * these frag addresses may not be the same as final object-file addresses.
  */
 
+/* Subroutines of relax_segment.  */
+static int 
+is_dnrange (f1, f2)
+     struct frag *f1;
+     struct frag *f2;
+{
+  for (; f1; f1 = f1->fr_next)
+    if (f1->fr_next == f2)
+      return 1;
+  return 0;
+}
+
+/* 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 (address, alignment)
+     register relax_addressT address;  /* Address now. */
+     register int alignment;   /* Alignment (binary). */
+{
+  relax_addressT mask;
+  relax_addressT new_address;
 
+  mask = ~((~0) << alignment);
+  new_address = (address + mask) & (~mask);
+  if (linkrelax)
+    /* We must provide lots of padding, so the linker can discard it
+       when needed.  The linker will not add extra space, ever.  */
+    new_address += (1 << alignment);
+  return (new_address - address);
+}
 
 void 
 relax_segment (segment_frag_root, segment)
      struct frag *segment_frag_root;
-     segT segment;             /* SEG_DATA or SEG_TEXT */
+     segT segment;
 {
   register struct frag *fragP;
   register relax_addressT address;
-#ifndef MANY_SEGMENTS
+#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.
-        */
+  /* 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)
     {
@@ -708,14 +1505,11 @@ relax_segment (segment_frag_root, segment)
          break;
 
        case rs_align:
-         address += relax_align (address, fragP->fr_offset);
+         address += relax_align (address, (int) fragP->fr_offset);
          break;
 
        case rs_org:
-         /*
-                        * Assume .org is nugatory. It will grow with 1st
-                        * relax.
-                        */
+         /* Assume .org is nugatory. It will grow with 1st relax.  */
          break;
 
        case rs_machine_dependent:
@@ -734,39 +1528,33 @@ relax_segment (segment_frag_root, segment)
        }                       /* switch(fr_type) */
     }                          /* for each frag in the segment */
 
-  /*
-        * Do relax().
-        */
+  /* Do relax().  */
   {
-    register long stretch;     /* May be any size, 0 or negative. */
+    long 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. */
-    register long 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 */
+    long 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.  */
 
     do
       {
        stretch = stretched = 0;
        for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next)
          {
-           register long growth = 0;
-           register unsigned long was_address;
-           /* register long var; */
-           register long offset;
-           register symbolS *symbolP;
-           register long target;
-           register long after;
-           register long aim;
+           long growth = 0;
+           unsigned long was_address;
+           long offset;
+           symbolS *symbolP;
+           long target;
+           long after;
 
            was_address = fragP->fr_address;
            address = fragP->fr_address += stretch;
            symbolP = fragP->fr_symbol;
            offset = fragP->fr_offset;
-           /* var = fragP->fr_var; */
 
            switch (fragP->fr_type)
              {
@@ -776,20 +1564,18 @@ relax_segment (segment_frag_root, segment)
 
 #ifndef WORKING_DOT_WORD
                /* JF:  This is RMS's idea.  I do *NOT* want to be blamed
-                                          for it I do not want to write it.  I do not want to have
-                                          anything to do with it.  This is not the proper way to
-                                          implement this misfeature. */
+                  for it I do not want to write it.  I do not want to have
+                  anything to do with it.  This is not the proper way to
+                  implement this misfeature.  */
              case rs_broken_word:
                {
                  struct broken_word *lie;
                  struct broken_word *untruth;
-                 extern int md_short_jump_size;
-                 extern int md_long_jump_size;
 
                  /* Yes this is ugly (storing the broken_word pointer
-                                          in the symbol slot).  Still, this whole chunk of
-                                          code is ugly, and I don't feel like doing anything
-                                          about it.  Think of it as stubbornness in action */
+                    in the symbol slot).  Still, this whole chunk of
+                    code is ugly, and I don't feel like doing anything
+                    about it.  Think of it as stubbornness in action.  */
                  growth = 0;
                  for (lie = (struct broken_word *) (fragP->fr_symbol);
                       lie && lie->dispfrag == fragP;
@@ -799,22 +1585,31 @@ relax_segment (segment_frag_root, segment)
                      if (lie->added)
                        continue;
 
-                     offset = lie->add->sy_frag->fr_address + S_GET_VALUE (lie->add) + lie->addnum -
-                       (lie->sub->sy_frag->fr_address + S_GET_VALUE (lie->sub));
+                     offset = (lie->add->sy_frag->fr_address
+                               + S_GET_VALUE (lie->add)
+                               + lie->addnum
+                               - (lie->sub->sy_frag->fr_address
+                                  + S_GET_VALUE (lie->sub)));
                      if (offset <= -32768 || offset >= 32767)
                        {
                          if (flagseen['K'])
-                           as_warn (".word %s-%s+%ld didn't fit",
-                                    S_GET_NAME (lie->add),
-                                    S_GET_NAME (lie->sub),
-                                    lie->addnum);
+                           {
+                             char buf[50];
+                             sprint_value (buf, (addressT) lie->addnum);
+                             as_warn (".word %s-%s+%s didn't fit",
+                                      S_GET_NAME (lie->add),
+                                      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; untruth && untruth->dispfrag == lie->dispfrag; untruth = untruth->next_broken_word)
+                         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))
                              {
@@ -829,8 +1624,12 @@ relax_segment (segment_frag_root, segment)
                }               /* case rs_broken_word */
 #endif
              case rs_align:
-               growth = relax_align ((relax_addressT) (address + fragP->fr_fix), offset)
-                 - relax_align ((relax_addressT) (was_address + fragP->fr_fix), offset);
+               growth = (relax_align ((relax_addressT) (address
+                                                        + fragP->fr_fix),
+                                      (int) offset)
+                         - relax_align ((relax_addressT) (was_address
+                                                          + fragP->fr_fix),
+                                        (int) offset));
                break;
 
              case rs_org:
@@ -838,11 +1637,14 @@ relax_segment (segment_frag_root, segment)
 
                if (symbolP)
                  {
-#ifdef MANY_SEGMENTS
-#else
-                   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);
+#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));
+                   know (!(S_GET_SEGMENT (symbolP) == SEG_ABSOLUTE)
+                         || (symbolP->sy_frag == &zero_address_frag));
 #endif
                    target += S_GET_VALUE (symbolP)
                      + symbolP->sy_frag->fr_address;
@@ -851,130 +1653,107 @@ relax_segment (segment_frag_root, segment)
                know (fragP->fr_next);
                after = fragP->fr_next->fr_address;
                growth = ((target - after) > 0) ? (target - after) : 0;
-               /* Growth may be -ve, but variable part */
-               /* of frag cannot have < 0 chars. */
-               /* That is, we can't .org backwards. */
+               /* Growth may be negative, but variable part of frag
+                  cannot have fewer than 0 chars.  That is, we can't
+                  .org backwards. */
 
                growth -= stretch;      /* This is an absolute growth factor */
                break;
 
              case rs_machine_dependent:
                {
-                 register const relax_typeS *this_type;
-                 register const relax_typeS *start_type;
-                 register relax_substateT next_state;
-                 register relax_substateT this_state;
-
-                 start_type = this_type = md_relax_table + (this_state = fragP->fr_subtype);
+                 const relax_typeS *this_type;
+                 const relax_typeS *start_type;
+                 relax_substateT next_state;
+                 relax_substateT this_state;
+                 long aim;
+
+                 this_state = fragP->fr_subtype;
+                 start_type = this_type = md_relax_table + this_state;
                  target = offset;
 
                  if (symbolP)
                    {
-#ifndef MANY_SEGMENTS
-                     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));
+#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);
-                     know (!(S_GET_SEGMENT (symbolP) == SEG_ABSOLUTE) || symbolP->sy_frag == &zero_address_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.  */
-
-                     /* JF was just address */
-                     /* JF also added is_dnrange hack */
-                     /* There's gotta be a better/faster/etc way
-                                                  to do this. . . */
-                     /* gnu@cygnus.com:  I changed this from > to >=
-                                                  because I ran into a zero-length frag (fr_fix=0)
-                                                  which was created when the obstack needed a new
-                                                  chunk JUST AFTER the opcode of a branch.  Since
-                                                  fr_fix is zero, fr_address of this frag is the same
-                                                  as fr_address of the next frag.  This
-                                                  zero-length frag was variable and jumped to .+2
-                                                  (in the next frag), but since the > comparison
-                                                  below failed (the two were =, not >), "stretch"
-                                                  was not added to the target.  Stretch was 178, so
-                                                  the offset appeared to be .-176 instead, which did
-                                                  not fit into a byte branch, so the assembler
-                                                  relaxed the branch to a word.  This didn't compare
-                                                  with what happened when the same source file was
-                                                  assembled on other machines, which is how I found it.
-                                                  You might want to think about what other places have
-                                                  trouble with zero length frags... */
+                        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;
-                       }       /*  */
-
-                   }           /* if there's a symbol attached */
+                       }
+                   }
 
                  aim = target - address - fragP->fr_fix;
                  /* The displacement is affected by the instruction size
-                                        * for the 32k architecture. I think we ought to be able
-                                        * to add fragP->fr_pcrel_adjust in all cases (it should be
-                                        * zero if not used), but just in case it breaks something
-                                        * else we'll put this inside #ifdef NS32K ... #endif
-                                        */
-#ifdef TC_NS32K
+                    for the 32k architecture. I think we ought to be able
+                    to add fragP->fr_pcrel_adjust in all cases (it should be
+                    zero if not used), but just in case it breaks something
+                    else we'll put this inside #ifdef NS32K ... #endif  */
+#ifndef TC_NS32K
+                 if (fragP->fr_pcrel_adjust)
+                   abort ();
+#endif
                  aim += fragP->fr_pcrel_adjust;
-#endif /* TC_NS32K */
 
                  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_type = md_relax_table + (this_state = next_state);
-                             next_state = this_type->rlx_more;
-                           }
-                       }
+                       if (aim >= this_type->rlx_backward)
+                         next_state = 0;
+                       else
+                         {
+                           /* Grow to next state. */
+                           this_state = next_state;
+                           this_type = md_relax_table + this_state;
+                           next_state = this_type->rlx_more;
+                         }
                    }
                  else
                    {
-#ifdef DONTDEF
-                     /* JF these next few lines of code are for the mc68020 which can't handle short
-                                                  offsets of zero in branch instructions.  What a kludge! */
-                     if (aim == 0 && this_state == (1 << 2 + 0))
-                       {       /* FOO hard encoded from m.c */
-                         aim = this_type->rlx_forward + 1;     /* Force relaxation into word mode */
-                       }
-#endif
 #ifdef M68K_AIM_KLUDGE
                      M68K_AIM_KLUDGE (aim, this_state, this_type);
 #endif
-                     /* JF end of 68020 code */
                      /* 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_type = md_relax_table + (this_state = next_state);
-                             next_state = this_type->rlx_more;
-                           }
-                       }
+                       if (aim <= this_type->rlx_forward)
+                         next_state = 0;
+                       else
+                         {
+                           /* Grow to next state. */
+                           this_state = next_state;
+                           this_type = md_relax_table + this_state;
+                           next_state = this_type->rlx_more;
+                         }
                    }
 
-                 if ((growth = this_type->rlx_length - start_type->rlx_length) != 0)
+                 growth = this_type->rlx_length - start_type->rlx_length;
+                 if (growth != 0)
                    fragP->fr_subtype = this_state;
-
-                 break;
-               }               /* case rs_machine_dependent */
+               }
+               break;
 
              default:
                BAD_CASE (fragP->fr_type);
@@ -991,37 +1770,16 @@ relax_segment (segment_frag_root, segment)
   }                            /* do_relax */
 
   /*
-        * We now have valid fr_address'es for each frag.
-        */
+   * We now have valid fr_address'es for each frag.
+   */
 
   /*
-        * All fr_address's are correct, relative to their own segment.
-        * We have made all the fixS we will ever make.
-        */
+   * All fr_address's are correct, relative to their own segment.
+   * We have made all the fixS we will ever make.
+   */
 }                              /* relax_segment() */
 
-/*
- * Relax_align. Advance location counter to next address that has 'alignment'
- * lowest order bits all 0s.
- */
-
-/* How many addresses does the .align take? */
-static relax_addressT
-relax_align (address, alignment)
-     register relax_addressT address;  /* Address now. */
-     register long alignment;  /* Alignment (binary). */
-{
-  relax_addressT mask;
-  relax_addressT new_address;
-
-  mask = ~((~0) << alignment);
-  new_address = (address + mask) & (~mask);
-  if (linkrelax)
-    /* We must provide lots of padding, so the linker can discard it
-       when needed.  The linker will not add extra space, ever.  */
-    new_address += (1 << alignment);
-  return (new_address - address);
-}                              /* relax_align() */
+#if defined (BFD_ASSEMBLER) || !defined (BFD)
 
 /* fixup_segment()
 
@@ -1042,27 +1800,25 @@ fixup_segment (fixP, this_segment_type)
   register long seg_reloc_count;
   register symbolS *add_symbolP;
   register symbolS *sub_symbolP;
-  register long add_number;
+  valueT add_number;
   register int size;
   register char *place;
   register long where;
   register char pcrel;
   register fragS *fragP;
-  register segT add_symbol_segment = SEG_ABSOLUTE;
+  register segT add_symbol_segment = absolute_section;
 
-  /* FIXME: remove this line *//*      fixS *orig = fixP; */
   seg_reloc_count = 0;
-#ifdef TC_I960
-  /* If the linker is doing the relaxing, we must not do any fixups */
+  /* 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 (the only machine for which we've got a relaxing
+     linker right now), we might be able to turn callx/callj into bal
+     in cases where we know the maximum displacement.  */
   if (linkrelax)
-    {
-      for (; fixP; fixP = fixP->fx_next)
-       {
-         seg_reloc_count++;
-       }
-    }
+    for (; fixP; fixP = fixP->fx_next)
+      seg_reloc_count++;
   else
-#endif
     for (; fixP; fixP = fixP->fx_next)
       {
        fragP = fixP->fx_frag;
@@ -1074,9 +1830,8 @@ fixup_segment (fixP, this_segment_type)
 #ifdef TC_I960
        if (fixP->fx_callj && TC_S_IS_CALLNAME (add_symbolP))
          {
-           /* Relocation should be done via the
-                          associated 'bal' entry point
-                          symbol. */
+           /* Relocation should be done via the associated 'bal'
+              entry point symbol. */
 
            if (!TC_S_IS_BALNAME (tc_get_bal_of_call (add_symbolP)))
              {
@@ -1085,43 +1840,37 @@ fixup_segment (fixP, this_segment_type)
                continue;
              }
            fixP->fx_addsy = add_symbolP = tc_get_bal_of_call (add_symbolP);
-         }                     /* callj relocation */
+         }
 #endif
        sub_symbolP = fixP->fx_subsy;
        add_number = fixP->fx_offset;
        pcrel = fixP->fx_pcrel;
 
        if (add_symbolP)
-         {
-           add_symbol_segment = S_GET_SEGMENT (add_symbolP);
-         }                     /* if there is an addend */
+         add_symbol_segment = S_GET_SEGMENT (add_symbolP);
 
        if (sub_symbolP)
          {
            if (!add_symbolP)
              {
                /* Its just -sym */
-               if (S_GET_SEGMENT (sub_symbolP) != SEG_ABSOLUTE)
-                 {
-                   as_bad ("Negative of non-absolute symbol %s", S_GET_NAME (sub_symbolP));
-                 }             /* not absolute */
+               /* @@ Should try converting to pcrel ref to fixed addr.  */
+               if (S_GET_SEGMENT (sub_symbolP) != absolute_section)
+                 as_bad ("Negative of non-absolute symbol %s",
+                         S_GET_NAME (sub_symbolP));
 
                add_number -= S_GET_VALUE (sub_symbolP);
-
-               /* if sub_symbol is in the same segment that add_symbol
-                                  and add_symbol is either in DATA, TEXT, BSS or ABSOLUTE */
              }
            else if ((S_GET_SEGMENT (sub_symbolP) == add_symbol_segment)
                     && (SEG_NORMAL (add_symbol_segment)
-                        || (add_symbol_segment == SEG_ABSOLUTE)))
+                        || (add_symbol_segment == absolute_section)))
              {
-               /* Difference of 2 symbols from same segment. */
-               /* Can't make difference of 2 undefineds: 'value' means */
-               /* something different for N_UNDF. */
+               /* Difference of 2 symbols from same segment.
+                  Can't make difference of 2 undefineds: 'value' means
+                  something different for N_UNDF. */
 #ifdef TC_I960
                /* Makes no sense to use the difference of 2 arbitrary symbols
-                                * as the target of a call instruction.
-                                */
+                  as the target of a call instruction.  */
                if (fixP->fx_callj)
                  {
                    as_bad ("callj to difference of 2 symbols");
@@ -1133,73 +1882,100 @@ fixup_segment (fixP, this_segment_type)
                add_symbolP = NULL;
                fixP->fx_addsy = NULL;
              }
+#if !defined(SEG_DIFF_ALLOWED) && !defined (GLOBAL_DIFF_ALLOWED)
            else
              {
                /* Different segments in subtraction. */
-               know (!(S_IS_EXTERNAL (sub_symbolP) && (S_GET_SEGMENT (sub_symbolP) == SEG_ABSOLUTE)));
+               know (!(S_IS_EXTERNAL (sub_symbolP)
+                       && (S_GET_SEGMENT (sub_symbolP) == absolute_section)));
 
-               if ((S_GET_SEGMENT (sub_symbolP) == SEG_ABSOLUTE))
+               if ((S_GET_SEGMENT (sub_symbolP) == absolute_section))
                  {
                    add_number -= S_GET_VALUE (sub_symbolP);
                  }
+#ifdef DIFF_EXPR_OK
+               else if (!pcrel
+                        && S_GET_SEGMENT (sub_symbolP) == this_segment_type)
+                 {
+                   /* Make it pc-relative.  */
+                   add_number += (md_pcrel_from (fixP)
+                                  - S_GET_VALUE (sub_symbolP));
+                   pcrel = 1;
+                   fixP->fx_pcrel = 1;
+                   sub_symbolP = 0;
+                   fixP->fx_subsy = 0;
+                 }
+#endif
                else
                  {
-                   as_bad ("Can't emit reloc {- %s-seg symbol \"%s\"} @ file address %d.",
+                   char buf[50];
+                   sprint_value (buf, fragP->fr_address + where);
+                   as_bad ("Can't emit reloc {- %s-seg symbol \"%s\"} @ file address %s.",
                            segment_name (S_GET_SEGMENT (sub_symbolP)),
-                      S_GET_NAME (sub_symbolP), fragP->fr_address + where);
-                 }             /* if absolute */
+                           S_GET_NAME (sub_symbolP), buf);
+                 }
+#else
+                else
+                  {
+                    seg_reloc_count++;
+                   fixP->fx_addnumber = add_number;    /* Remember value for emit_reloc */
+                    continue;
+                  }            /* if absolute */
+#endif
              }
-         }                     /* if sub_symbolP */
+         }
 
        if (add_symbolP)
          {
            if (add_symbol_segment == this_segment_type && pcrel)
              {
                /*
-                                * 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.
-                                */
+                * 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.
+                */
 #ifdef TC_I960
-               /* reloc_callj() may replace a 'call' with a 'calls' or a 'bal',
-                                * in which cases it modifies *fixP as appropriate.  In the case
-                                * of a 'calls', no further work is required, and *fixP has been
-                                * set up to make the rest of the code below a no-op.
-                                */
+               /* reloc_callj() may replace a 'call' with a 'calls' or a
+                  'bal', in which cases it modifies *fixP as appropriate.
+                  In the case of a 'calls', no further work is required,
+                  and *fixP has been set up to make the rest of the code
+                  below a no-op. */
                reloc_callj (fixP);
 #endif /* TC_I960 */
 
                add_number += S_GET_VALUE (add_symbolP);
                add_number -= md_pcrel_from (fixP);
                pcrel = 0;      /* Lie. Don't want further pcrel processing. */
+#ifndef TC_HPPA
                fixP->fx_addsy = NULL;  /* No relocations please. */
+#endif
              }
            else
              {
-               switch (add_symbol_segment)
+               if (add_symbol_segment == absolute_section)
                  {
-                 case SEG_ABSOLUTE:
 #ifdef TC_I960
-                   reloc_callj (fixP); /* See comment about reloc_callj() above*/
+                   /* See comment about reloc_callj() above.  */
+                   reloc_callj (fixP);
 #endif /* TC_I960 */
                    add_number += S_GET_VALUE (add_symbolP);
                    fixP->fx_addsy = NULL;
                    add_symbolP = NULL;
-                   break;
-                 default:
-                   seg_reloc_count++;
-                   add_number += S_GET_VALUE (add_symbolP);
-                   break;
-
-                 case SEG_UNKNOWN:
+                 }
+               else if (add_symbol_segment == undefined_section
+#ifdef BFD_ASSEMBLER
+                        || bfd_is_com_section (add_symbol_segment)
+#endif
+                        )
+                 {
 #ifdef TC_I960
                    if ((int) fixP->fx_bit_fixP == 13)
                      {
                        /* This is a COBR instruction.  They have only a
-                                                * 13-bit displacement and are only to be used
-                                                * for local branches: flag as error, don't generate
-                                                * relocation.
-                                                */
+                        * 13-bit displacement and are only to be used
+                        * for local branches: flag as error, don't generate
+                        * relocation.
+                        */
                        as_bad ("can't use COBR format with external label");
                        fixP->fx_addsy = NULL;  /* No relocations please. */
                        continue;
@@ -1213,13 +1989,14 @@ fixup_segment (fixP, this_segment_type)
 #endif /* TE_I386AIX */
 #endif /* OBJ_COFF */
                    ++seg_reloc_count;
-
-                   break;
-
-
-                 }             /* switch on symbol seg */
-             }                 /* if not in local seg */
-         }                     /* if there was a + symbol */
+                 }
+               else
+                 {
+                   seg_reloc_count++;
+                   add_number += S_GET_VALUE (add_symbolP);
+                 }
+             }
+         }
 
        if (pcrel)
          {
@@ -1233,109 +2010,63 @@ fixup_segment (fixP, this_segment_type)
 
        if (!fixP->fx_bit_fixP)
          {
-           if ((size == 1 &&
-                (add_number & ~0xFF) && ((add_number & ~0xFF) != (-1 & ~0xFF))) ||
-               (size == 2 &&
-                (add_number & ~0xFFFF) && ((add_number & ~0xFFFF) != (-1 & ~0xFFFF))))
+           valueT mask = 0;
+           /* set all bits to one */
+           mask--;
+           /* Technically speaking, combining these produces an
+              undefined result if size is sizeof (valueT), though I
+              think these two half-way operations should both be
+              defined.  */
+           mask <<= size * 4;
+           mask <<= size * 4;
+           if ((add_number & mask) != 0
+               && (add_number & mask) != mask)
              {
-               as_bad ("Value of %d too large for field of %d bytes at 0x%x",
-                       add_number, size, fragP->fr_address + where);
+               char buf[50], buf2[50];
+               sprint_value (buf, fragP->fr_address + 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, 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.  */
+           /* 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 (!flagseen['J']
                && size == 2
                && add_number > 0x7fff)
-             as_bad ("Signed .word overflow; switch may be too large; %d at 0x%x",
-                     add_number, fragP->fr_address + where);
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           "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 */
 
+#ifdef BFD_ASSEMBLER
+       md_apply_fix (fixP, &add_number);
+#else
        md_apply_fix (fixP, add_number);
+#endif
       }                                /* For each fixS in this segment. */
 
-#ifdef OBJ_COFF
-#ifdef TC_I960
+#if defined (OBJ_COFF) && defined (TC_I960)
   {
     fixS *topP = fixP;
 
     /* two relocs per callj under coff. */
     for (fixP = topP; fixP; fixP = fixP->fx_next)
-      {
-       if (fixP->fx_callj && fixP->fx_addsy != 0)
-         {
-           ++seg_reloc_count;
-         }                     /* if callj and not already fixed. */
-      }                                /* for each fix */
+      if (fixP->fx_callj && fixP->fx_addsy != 0)
+       ++seg_reloc_count;
   }
-#endif /* TC_I960 */
+#endif /* OBJ_COFF && TC_I960 */
 
-#endif /* OBJ_COFF */
   return (seg_reloc_count);
-}                              /* fixup_segment() */
-
-
-static int 
-is_dnrange (f1, f2)
-     struct frag *f1;
-     struct frag *f2;
-{
-  while (f1)
-    {
-      if (f1->fr_next == f2)
-       return 1;
-      f1 = f1->fr_next;
-    }
-  return 0;
-}                              /* is_dnrange() */
-
-/* Append a string onto another string, bumping the pointer along.  */
-void
-append (charPP, fromP, length)
-     char **charPP;
-     char *fromP;
-     unsigned long length;
-{
-  if (length)
-    {                          /* Don't trust memcpy() of 0 chars. */
-      memcpy (*charPP, fromP, (int) length);
-      *charPP += length;
-    }
 }
 
-int section_alignment[SEG_MAXIMUM_ORDINAL];
-
-/*
- * 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
- * least one object format really uses this info.
- */
-void 
-record_alignment (seg, align)
-     segT seg;                 /* Segment to which alignment pertains */
-     int align;                        /* Alignment, as a power of 2
-                *      (e.g., 1 => 2-byte boundary, 2 => 4-byte boundary, etc.)
-                */
-{
-
-  if (align > section_alignment[(int) seg])
-    {
-      section_alignment[(int) seg] = align;
-    }                          /* if highest yet */
-
-  return;
-}                              /* record_alignment() */
-
-/*
- * Local Variables:
- * comment-column: 0
- * fill-column: 131
- * End:
- */
+#endif /* defined (BFD_ASSEMBLER) || !defined (BFD) */
 
 /* end of write.c */
This page took 0.051381 seconds and 4 git commands to generate.