* write.c (write_contents): Don't crash if seginfo is NULL.
[deliverable/binutils-gdb.git] / gas / write.c
index 17430b5abf878cfb6fd7cc8cf104368fd9473781..38771b460f53a10ddfcf58167dbed6ad5fdf3c7f 100644 (file)
@@ -47,12 +47,12 @@ 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
@@ -61,23 +61,36 @@ int magic_number_for_object_file = DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE;
 
 #endif /* BFD_ASSEMBLER */
 
+#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 */
@@ -145,6 +158,75 @@ fix_new (frag, where, size, add_symbol, sub_symbol, offset, pcrel, r_type)
   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)
@@ -156,7 +238,7 @@ append (charPP, fromP, length)
   if (length == 0)
     return;
 
-  memcpy (*charPP, fromP, (int) length);
+  memcpy (*charPP, fromP, length);
   *charPP += length;
 }
 
@@ -225,7 +307,7 @@ chain_frchains_together (abfd, section, xxx)
 
 #endif
 
-#ifndef BFD
+#if !defined (BFD) && !defined (BFD_ASSEMBLER)
 
 void 
 remove_subsegs (head, seg, root, last)
@@ -240,23 +322,20 @@ remove_subsegs (head, seg, root, last)
 
 #endif /* BFD */
 
-#ifndef BFD
+#if defined (BFD_ASSEMBLER) || !defined (BFD)
 
-static void
-cvt_frag_to_fill (x, fragP)
 #ifdef BFD_ASSEMBLER
-     segT x;
-#else
-     object_headers *x;
-#endif
+static void
+cvt_frag_to_fill (sec, fragP)
+     segT sec;
      fragS *fragP;
-{
-#ifdef BFD_ASSEMBLER
-  segT sec = x;
 #else
-  object_headers *headers = x;
+static void
+cvt_frag_to_fill (headers, fragP)
+     object_headers *headers;
+     fragS *fragP;
 #endif
-
+{
   switch (fragP->fr_type)
     {
     case rs_align:
@@ -283,9 +362,7 @@ cvt_frag_to_fill (x, fragP)
       md_convert_frag (headers, fragP);
 #endif
 
-      assert (fragP->fr_next == NULL \
-             || (fragP->fr_next->fr_address - fragP->fr_address \
-                 == fragP->fr_fix));
+      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".
@@ -320,6 +397,8 @@ cvt_frag_to_fill (x, fragP)
     }
 }
 
+#endif /* defined (BFD_ASSEMBLER) || !defined (BFD) */
+
 #ifdef BFD_ASSEMBLER
 static void
 relax_and_size_seg (abfd, sec, xxx)
@@ -328,17 +407,16 @@ relax_and_size_seg (abfd, sec, xxx)
      char *xxx;
 {
   flagword flags;
+  fragS *fragp;
+  segment_info_type *seginfo;
+  int x;
+  valueT size, newsize;
 
   flags = bfd_get_section_flags (abfd, sec);
 
-  if (flags & SEC_ALLOC)
+  seginfo = (segment_info_type *) bfd_get_section_userdata (abfd, sec);
+  if (seginfo && seginfo->frchainP)
     {
-      fragS *fragp;
-      segment_info_type *seginfo;
-      int x;
-      unsigned long size, newsize;
-
-      seginfo = (segment_info_type *) bfd_get_section_userdata (abfd, sec);
       relax_segment (seginfo->frchainP->frch_root, sec);
       for (fragp = seginfo->frchainP->frch_root; fragp; fragp = fragp->fr_next)
        cvt_frag_to_fill (sec, fragp);
@@ -346,34 +424,39 @@ relax_and_size_seg (abfd, sec, xxx)
           fragp->fr_next;
           fragp = fragp->fr_next)
        /* walk to last elt */;
-      size = fragp->fr_address;
-      if (size > 0)
-       {
-         flags |= SEC_HAS_CONTENTS;
-         /* @@ This is just an approximation.  */
-         if (seginfo->fix_root)
-           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);
+      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);
-
-      /* 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;
-       }
     }
+  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;
+    }
+
 #ifdef tc_frob_section
   tc_frob_section (sec);
 #endif
@@ -382,63 +465,142 @@ relax_and_size_seg (abfd, sec, xxx)
 #endif
 }
 
+#ifdef DEBUG2
 static void
-write_contents (abfd, sec, xxx)
+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;
+
+  if (!fixp)
+    return;
+
+  fprintf (stream, "sec %s relocs:\n", sec->name);
+  while (fixp)
+    {
+      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
+
+static void
+adjust_reloc_syms (abfd, sec, xxx)
      bfd *abfd;
      asection *sec;
      char *xxx;
 {
   segment_info_type *seginfo = seg_info (sec);
-  unsigned long offset = 0;
-  fragS *frags;
-  int i, n;
-  arelent **relocs;
   fixS *fixp;
 
-  if (! (bfd_get_section_flags (abfd, sec) & SEC_LOAD))
+  if (seginfo == NULL)
     return;
 
-  fixup_segment (seginfo->fix_root, sec);
+  dump_section_relocs (abfd, sec, stderr);
 
-  n = 0;
-  for (i = 0, fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
-    {
-      n++;
-      if (fixp->fx_addsy)
-       {
-         symbolS *sym = fixp->fx_addsy;
-         asection *sec = sym->bsym->section;
-         if (sec == &bfd_und_section
-             || sec == &bfd_abs_section
-             || sec == &bfd_com_section)
+  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;
-         if (sym->bsym == sec->symbol)
+         }
+
+       /* 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;
-         /* 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);
-         fixp->fx_addsy = symbol_find (sec->name);
-         if (!fixp->fx_addsy)
-           {
-             fixp->fx_addsy = symbol_make (sec->name);
-             fixp->fx_addsy->bsym = sec->symbol;
-           }
-       }
-    }
+         }
 
-  /* Force calculations (size, vma) to get done.  */
-  bfd_set_section_contents (stdoutput, sec, "", 0, 0);
+       /* 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
+       /* 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;
-      extern arelent *tc_gen_reloc ();
       char *data;
       bfd_reloc_status_type s;
 
@@ -456,9 +618,13 @@ write_contents (abfd, sec, xxx)
          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.  */
       s = bfd_perform_relocation (stdoutput, reloc, data - reloc->address,
                                  sec, stdoutput);
       switch (s)
@@ -470,38 +636,120 @@ write_contents (abfd, sec, xxx)
        }
       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.  */
-  for (frags = seginfo->frchainP->frch_root;
-       frags;
-       frags = frags->fr_next)
+  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 (frags->fr_type == rs_fill);
-      if (frags->fr_fix)
+      assert (f->fr_type == rs_fill);
+      if (f->fr_fix)
        {
          x = bfd_set_section_contents (stdoutput, sec,
-                                       frags->fr_literal, offset,
-                                       frags->fr_fix);
+                                       f->fr_literal, (file_ptr) offset,
+                                       (bfd_size_type) f->fr_fix);
          assert (x == true);
-         offset += frags->fr_fix;
+         offset += f->fr_fix;
        }
-      fill_literal = frags->fr_literal + frags->fr_fix;
-      fill_size = frags->fr_var;
-      count = frags->fr_offset;
+      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, offset, fill_size);
+                                         fill_literal, (file_ptr) offset,
+                                         (bfd_size_type) fill_size);
            assert (x == true);
            offset += fill_size;
          }
@@ -509,15 +757,141 @@ write_contents (abfd, sec, xxx)
 }
 #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.
+   */
+
+  know (text_last_frag->fr_type == rs_fill && text_last_frag->fr_offset == 0);
+  H_SET_TEXT_SIZE (&headers, text_last_frag->fr_address);
+  text_last_frag->fr_address = H_GET_TEXT_SIZE (&headers);
+
+  /*
+   * Join the 2 segments into 1 huge segment.
+   * To do this, re-compute every rn_address in the SEG_DATA frags.
+   * Then join the data frags after the text frags.
+   *
+   * Determine a_data [length of data segment].
+   */
+  if (data_frag_root)
+    {
+      register relax_addressT slide;
+
+      know ((text_last_frag->fr_type == rs_fill) && (text_last_frag->fr_offset == 0));
+
+      H_SET_DATA_SIZE (&headers, data_last_frag->fr_address);
+      data_last_frag->fr_address = H_GET_DATA_SIZE (&headers);
+      slide = H_GET_TEXT_SIZE (&headers);      /* & in file of the data segment. */
+#ifdef OBJ_BOUT
+#define RoundUp(N,S) (((N)+(S)-1)&-(S))
+      /* For b.out: If the data section has a strict alignment
+        requirement, its load address in the .o file will be
+        rounded up from the size of the text section.  These
+        two values are *not* the same!  Similarly for the bss
+        section....  */
+      slide = RoundUp (slide, 1 << section_alignment[SEG_DATA]);
+#endif
+
+      for (fragP = data_frag_root; fragP; fragP = fragP->fr_next)
+       {
+         fragP->fr_address += slide;
+       }                       /* for each data frag */
+
+      know (text_last_frag != 0);
+      text_last_frag->fr_next = data_frag_root;
+    }
+  else
+    {
+      H_SET_DATA_SIZE (&headers, 0);
+    }
+
+#ifdef OBJ_BOUT
+  /* See above comments on b.out data section address.  */
+  {
+    long bss_vma;
+    if (data_last_frag == 0)
+      bss_vma = H_GET_TEXT_SIZE (&headers);
+    else
+      bss_vma = data_last_frag->fr_address;
+    bss_vma = RoundUp (bss_vma, 1 << section_alignment[SEG_BSS]);
+    bss_address_frag.fr_address = bss_vma;
+  }
+#else /* ! OBJ_BOUT */
+  bss_address_frag.fr_address = (H_GET_TEXT_SIZE (&headers) +
+                                H_GET_DATA_SIZE (&headers));
+
+
+  /* Slide all the frags */
+  if (bss_frag_root)
+    {
+      relax_addressT slide = bss_address_frag.fr_address;
+
+      for (fragP = bss_frag_root; fragP; fragP = fragP->fr_next)
+       {
+         fragP->fr_address += slide;
+       }                       /* for each bss frag */
+    }
+
+#endif /* ! OBJ_BOUT */
+
+  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. */
-  register struct frchain *next_frchainP;
-  register fragS **prev_fragPP;
-
+#endif
+#if !defined (BFD_ASSEMBLER) && !defined (OBJ_VMS)
   long object_file_size;
+#endif
 
   /* Do we really want to write it?  */
   {
@@ -568,11 +942,7 @@ write_object_file ()
 #endif
   for (frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next)
     {
-#ifdef BFD_ASSEMBLER
       subseg_set (frchainP->frch_seg, frchainP->frch_subseg);
-#else
-      subseg_new (frchainP->frch_seg, frchainP->frch_subseg);
-#endif
       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".
@@ -582,8 +952,6 @@ write_object_file ()
       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. */
     }
 
   /* From now on, we don't care about sub-segments.  Build one frag chain
@@ -597,11 +965,7 @@ write_object_file ()
     while (seclist && *seclist)
       {
        sec = *seclist;
-       while (sec == big_section
-              || sec == reg_section
-              || sec == pass1_section
-              || sec == diff_section
-              || sec == absent_section)
+       while (sec == reg_section || sec == expr_section)
          {
            sec = sec->next;
            *seclist = sec;
@@ -627,119 +991,14 @@ write_object_file ()
 #if !defined (OBJ_AOUT) || defined (BFD_ASSEMBLER)
   if (flagseen['R'])
     {
-#ifdef BFD_ASSEMBLER
-      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
+      merge_data_into_text ();
     }
 #endif
 
 #ifdef BFD_ASSEMBLER
   bfd_map_over_sections (stdoutput, relax_and_size_seg, (char *) 0);
 #else
-  relax_segment (text_frag_root, SEG_TEXT);
-  relax_segment (data_frag_root, SEG_DATA);
-  relax_segment (bss_frag_root, SEG_BSS);
-  /*
-   * Now the addresses of frags are correct within the segment.
-   */
-
-  know (text_last_frag->fr_type == rs_fill && text_last_frag->fr_offset == 0);
-  H_SET_TEXT_SIZE (&headers, text_last_frag->fr_address);
-  text_last_frag->fr_address = H_GET_TEXT_SIZE (&headers);
-
-  /*
-   * Join the 2 segments into 1 huge segment.
-   * To do this, re-compute every rn_address in the SEG_DATA frags.
-   * Then join the data frags after the text frags.
-   *
-   * Determine a_data [length of data segment].
-   */
-  if (data_frag_root)
-    {
-      register relax_addressT slide;
-
-      know ((text_last_frag->fr_type == rs_fill) && (text_last_frag->fr_offset == 0));
-
-      H_SET_DATA_SIZE (&headers, data_last_frag->fr_address);
-      data_last_frag->fr_address = H_GET_DATA_SIZE (&headers);
-      slide = H_GET_TEXT_SIZE (&headers);      /* & in file of the data segment. */
-#ifdef OBJ_BOUT
-#define RoundUp(N,S) (((N)+(S)-1)&-(S))
-      /* For b.out: If the data section has a strict alignment
-        requirement, its load address in the .o file will be
-        rounded up from the size of the text section.  These
-        two values are *not* the same!  Similarly for the bss
-        section....  */
-      slide = RoundUp (slide, 1 << section_alignment[SEG_DATA]);
-#endif
-
-      for (fragP = data_frag_root; fragP; fragP = fragP->fr_next)
-       {
-         fragP->fr_address += slide;
-       }                       /* for each data frag */
-
-      know (text_last_frag != 0);
-      text_last_frag->fr_next = data_frag_root;
-    }
-  else
-    {
-      H_SET_DATA_SIZE (&headers, 0);
-    }
-
-#ifdef OBJ_BOUT
-  /* See above comments on b.out data section address.  */
-  {
-    long bss_vma;
-    if (data_last_frag == 0)
-      bss_vma = H_GET_TEXT_SIZE (&headers);
-    else
-      bss_vma = data_last_frag->fr_address;
-    bss_vma = RoundUp (bss_vma, 1 << section_alignment[SEG_BSS]);
-    bss_address_frag.fr_address = bss_vma;
-  }
-#else /* ! OBJ_BOUT */
-  bss_address_frag.fr_address = (H_GET_TEXT_SIZE (&headers) +
-                                H_GET_DATA_SIZE (&headers));
-
-
-  /* Slide all the frags */
-  if (bss_frag_root)
-    {
-      relax_addressT slide = bss_address_frag.fr_address;
-
-      for (fragP = bss_frag_root; fragP; fragP = fragP->fr_next)
-       {
-         fragP->fr_address += slide;
-       }                       /* for each bss frag */
-    }
-
-#endif /* ! OBJ_BOUT */
-
-  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);
+  relax_and_size_all_segments ();
 #endif /* BFD_ASSEMBLER */
 
 #ifndef BFD_ASSEMBLER
@@ -808,29 +1067,30 @@ 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 (lie->frag, lie->word_goes_here - lie->frag->fr_literal,
-                  2, lie->add, lie->sub, lie->addnum, 0, BFD_RELOC_NONE);
+         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 (lie->frag, lie->word_goes_here - lie->frag->fr_literal,
-                  2, lie->add,
-                  lie->sub, lie->addnum,
-                  0, NO_RELOC);
+         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
-         fix_new (lie->frag, lie->word_goes_here - lie->frag->fr_literal,
-                  2, lie->add,
-                  lie->sub, lie->addnum,
-                  0, 0);
+         fix_new_exp (lie->frag,
+                      lie->word_goes_here - lie->frag->fr_literal,
+                      2, &exp, 0, 0);
 #endif /* TC_NS32K */
 #endif /* TC_SPARC|TC_A29K|NEED_FX_R_TYPE */
 #endif /* BFD_ASSEMBLER */
@@ -843,8 +1103,8 @@ 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;
 
        fragP = lie->dispfrag;
@@ -871,8 +1131,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)
              {
@@ -902,11 +1161,9 @@ write_object_file ()
     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 */
@@ -1008,84 +1265,115 @@ write_object_file ()
 #endif /* VMS */
 #else /* BFD_ASSEMBLER */
 
+#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)
     {
-      int i = 0, n;
+      unsigned int i = 0;
+      unsigned int n;
       symbolS *symp;
 
       for (symp = symbol_rootP; symp; symp = symbol_next (symp))
        {
-         S_SET_VALUE (symp, S_GET_VALUE (symp) + symp->sy_frag->fr_address);
+         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 type=%d forward=%x seg=%s\n",
+         printf ("symbol `%s'\n\t@%x: value=%d flags=%x seg=%s\n",
                  S_GET_NAME (symp), symp,
                  S_GET_VALUE (symp),
-                 S_GET_DATA_TYPE (symp),
-                 symp->sy_forward,
+                 symp->bsym->flags,
                  segment_name (symp->bsym->section));
 #endif
-         {
-           int punt = 0;
+         if (! symp->sy_used_in_reloc)
+           {
 #ifdef obj_frob_symbol
-           obj_frob_symbol (symp, punt);
-           if (punt)
-             goto punt_it;
+             {
+               int punt = 0;
+               obj_frob_symbol (symp, punt);
+               if (punt)
+                 goto punt_it;
+             }
 #endif
 #ifdef tc_frob_symbol
-           tc_frob_symbol (symp, punt);
-           if (punt)
-             goto punt_it;
+             {
+               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 (S_IS_LOCAL (symp))
+         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
-             /* debugging: verify consistency */
-             {
-               symbolS *p = symp, *n = symp;
-               while (symbol_previous (p))
-                 p = symbol_previous (p);
-               while (symbol_next (n))
-                 n = symbol_next (n);
-               verify_symbol_chain (p, n);
-             }
+#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
-             /* debugging: verify consistency */
-             {
-               symbolS *p = symp, *n = symp;
-               while (symbol_previous (p))
-                 p = symbol_previous (p);
-               while (symbol_next (n))
-                 n = symbol_next (n);
-               verify_symbol_chain (p, n);
-             }
+#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;
@@ -1093,8 +1381,10 @@ write_object_file ()
        {
          asymbol **asympp;
          boolean result;
+         extern PTR bfd_alloc PARAMS ((bfd *, size_t));
 
-         asympp = (asymbol **) bfd_alloc (stdoutput, n * sizeof (asymbol *));
+         asympp = (asymbol **) bfd_alloc (stdoutput,
+                                          n * sizeof (asymbol *));
          symp = symbol_rootP;
          for (i = 0; i < n; i++, symp = symbol_next (symp))
            {
@@ -1106,18 +1396,25 @@ write_object_file ()
        }
     }
 
+
 #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 */
+#endif /* BFD */
 
 /*
  *                     relax_segment()
@@ -1132,7 +1429,7 @@ 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;
@@ -1145,13 +1442,11 @@ is_dnrange (f1, f2)
 }
 
 /* Relax_align. Advance location counter to next address that has 'alignment'
-   lowest order bits all 0s.  */
-
-/* How many addresses does the .align take? */
+   lowest order bits all 0s, return size of adjustment made.  */
 static relax_addressT
 relax_align (address, alignment)
      register relax_addressT address;  /* Address now. */
-     register long alignment;  /* Alignment (binary). */
+     register int alignment;   /* Alignment (binary). */
 {
   relax_addressT mask;
   relax_addressT new_address;
@@ -1193,7 +1488,7 @@ 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:
@@ -1238,7 +1533,6 @@ relax_segment (segment_frag_root, segment)
            symbolS *symbolP;
            long target;
            long after;
-           long aim;
 
            was_address = fragP->fr_address;
            address = fragP->fr_address += stretch;
@@ -1282,10 +1576,14 @@ relax_segment (segment_frag_root, segment)
                      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)
                            {
@@ -1311,10 +1609,10 @@ relax_segment (segment_frag_root, segment)
              case rs_align:
                growth = (relax_align ((relax_addressT) (address
                                                         + fragP->fr_fix),
-                                      offset)
+                                      (int) offset)
                          - relax_align ((relax_addressT) (was_address
                                                           + fragP->fr_fix),
-                                        offset));
+                                        (int) offset));
                break;
 
              case rs_org:
@@ -1351,6 +1649,7 @@ relax_segment (segment_frag_root, segment)
                  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;
@@ -1358,6 +1657,7 @@ relax_segment (segment_frag_root, segment)
 
                  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)
@@ -1365,6 +1665,7 @@ relax_segment (segment_frag_root, segment)
                            || (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 +=
@@ -1374,36 +1675,18 @@ relax_segment (segment_frag_root, segment)
                      /* 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... */
+                        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
@@ -1411,9 +1694,11 @@ relax_segment (segment_frag_root, segment)
                     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
+#ifndef TC_NS32K
+                 if (fragP->fr_pcrel_adjust)
+                   abort ();
+#endif
                  aim += fragP->fr_pcrel_adjust;
-#endif /* TC_NS32K */
 
                  if (aim < 0)
                    {
@@ -1477,6 +1762,8 @@ relax_segment (segment_frag_root, segment)
    */
 }                              /* relax_segment() */
 
+#if defined (BFD_ASSEMBLER) || !defined (BFD)
+
 /* fixup_segment()
 
    Go through all the fixS's in a segment and see which ones can be
@@ -1496,7 +1783,7 @@ fixup_segment (fixP, this_segment_type)
   register long seg_reloc_count;
   register symbolS *add_symbolP;
   register symbolS *sub_symbolP;
-  long add_number;
+  valueT add_number;
   register int size;
   register char *place;
   register long where;
@@ -1505,7 +1792,12 @@ fixup_segment (fixP, this_segment_type)
   register segT add_symbol_segment = absolute_section;
 
   seg_reloc_count = 0;
-  /* 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++;
@@ -1545,6 +1837,7 @@ fixup_segment (fixP, this_segment_type)
            if (!add_symbolP)
              {
                /* Its just -sym */
+               /* @@ 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));
@@ -1582,14 +1875,29 @@ fixup_segment (fixP, this_segment_type)
                  {
                    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);
+                 }
              }
-         }                     /* if sub_symbolP */
+         }
 
        if (add_symbolP)
          {
@@ -1628,7 +1936,7 @@ fixup_segment (fixP, this_segment_type)
                  }
                else if (add_symbol_segment == undefined_section
 #ifdef BFD_ASSEMBLER
-                        || add_symbol_segment == &bfd_com_section
+                        || bfd_is_com_section (add_symbol_segment)
 #endif
                         )
                  {
@@ -1674,15 +1982,22 @@ 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];
+               sprint_value (buf, fragP->fr_address + where);
+               as_bad ("Value of %ld too large for field of %d bytes at %s",
+                       (long) add_number, size, buf);
              }                 /* generic error checking */
 #ifdef WARN_SIGNED_OVERFLOW_WORD
            /* Warn if a .word value is too large when treated as a signed
@@ -1691,8 +2006,9 @@ fixup_segment (fixP, this_segment_type)
            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 ("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 */
 
@@ -1703,24 +2019,20 @@ fixup_segment (fixP, this_segment_type)
 #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() */
+}
+
+#endif /* defined (BFD_ASSEMBLER) || !defined (BFD) */
 
 /* end of write.c */
This page took 0.040657 seconds and 4 git commands to generate.