Delete unnecessary code from kill_command
[deliverable/binutils-gdb.git] / gas / config / tc-mmix.c
index e396027c70333950429ad4fa00f6bcbbe1024ff2..a7728fa7937b4e39690f584a989ce483ae1b240c 100644 (file)
@@ -1,11 +1,11 @@
 /* tc-mmix.c -- Assembler for Don Knuth's MMIX.
 /* tc-mmix.c -- Assembler for Don Knuth's MMIX.
-   Copyright (C) 2001, 2002 Free Software Foundation.
+   Copyright (C) 2001-2020 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GAS is distributed in the hope that it will be useful,
    any later version.
 
    GAS is distributed in the hope that it will be useful,
@@ -15,8 +15,8 @@
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
 
 /* Knuth's assembler mmixal does not provide a relocatable format; mmo is
    to be considered a final link-format.  In the final link, we make mmo,
 
 /* Knuth's assembler mmixal does not provide a relocatable format; mmo is
    to be considered a final link-format.  In the final link, we make mmo,
    compatible syntax, but the main purpose is to serve GCC.  */
 
 
    compatible syntax, but the main purpose is to serve GCC.  */
 
 
-#include <stdio.h>
 #include "as.h"
 #include "as.h"
+#include <limits.h>
 #include "subsegs.h"
 #include "subsegs.h"
-#include "bfd.h"
 #include "elf/mmix.h"
 #include "opcode/mmix.h"
 #include "safe-ctype.h"
 #include "elf/mmix.h"
 #include "opcode/mmix.h"
 #include "safe-ctype.h"
    for example assert something of what it became or make a relocation.  */
 
 enum mmix_fixup_action
    for example assert something of what it became or make a relocation.  */
 
 enum mmix_fixup_action
- {
-   mmix_fixup_byte,
-   mmix_fixup_register,
-   mmix_fixup_register_or_adjust_for_byte
- };
+{
+  mmix_fixup_byte,
+  mmix_fixup_register,
+  mmix_fixup_register_or_adjust_for_byte
+};
 
 
-static int get_spec_regno PARAMS ((char *));
-static int get_operands PARAMS ((int, char *, expressionS[]));
-static int get_putget_operands
-  PARAMS ((struct mmix_opcode *, char *, expressionS[]));
-static void s_prefix PARAMS ((int));
-static void s_greg PARAMS ((int));
-static void s_loc PARAMS ((int));
-static void s_bspec PARAMS ((int));
-static void s_espec PARAMS ((int));
-static void mmix_s_local PARAMS ((int));
-static void mmix_greg_internal PARAMS ((char *));
-static void mmix_set_geta_branch_offset PARAMS ((char *, offsetT value));
-static void mmix_set_jmp_offset PARAMS ((char *, offsetT));
-static void mmix_fill_nops PARAMS ((char *, int));
-static int cmp_greg_symbol_fixes PARAMS ((const PTR, const PTR));
-static int cmp_greg_val_greg_symbol_fixes
-  PARAMS ((const PTR p1, const PTR p2));
-static void mmix_handle_rest_of_empty_line PARAMS ((void));
-static void mmix_discard_rest_of_line PARAMS ((void));
-static void mmix_byte PARAMS ((void));
-static void mmix_cons PARAMS ((int));
-static void mmix_frob_local_reloc PARAMS ((bfd *, asection *, PTR));
+static int get_spec_regno (char *);
+static int get_operands (int, char *, expressionS *);
+static int get_putget_operands (struct mmix_opcode *, char *, expressionS *);
+static void s_prefix (int);
+static void s_greg (int);
+static void s_loc (int);
+static void s_bspec (int);
+static void s_espec (int);
+static void mmix_s_local (int);
+static void mmix_greg_internal (char *);
+static void mmix_set_geta_branch_offset (char *, offsetT);
+static void mmix_set_jmp_offset (char *, offsetT);
+static void mmix_fill_nops (char *, int);
+static int cmp_greg_symbol_fixes (const void *, const void *);
+static int cmp_greg_val_greg_symbol_fixes (const void *, const void *);
+static void mmix_handle_rest_of_empty_line (void);
+static void mmix_discard_rest_of_line (void);
+static void mmix_byte (void);
+static void mmix_cons (int);
 
 /* Continue the tradition of symbols.c; use control characters to enforce
    magic.  These are used when replacing e.g. 8F and 8B so we can handle
 
 /* Continue the tradition of symbols.c; use control characters to enforce
    magic.  These are used when replacing e.g. 8F and 8B so we can handle
@@ -112,6 +108,14 @@ static struct
    expressionS exp;
  } mmix_raw_gregs[MAX_GREGS];
 
    expressionS exp;
  } mmix_raw_gregs[MAX_GREGS];
 
+static struct loc_assert_s
+ {
+   segT old_seg;
+   symbolS *loc_sym;
+   fragS *frag;
+   struct loc_assert_s *next;
+ } *loc_asserts = NULL;
+
 /* Fixups for all unique GREG registers.  We store the fixups here in
    md_convert_frag, then we use the array to convert
    BFD_RELOC_MMIX_BASE_PLUS_OFFSET fixups in tc_gen_reloc.  The index is
 /* Fixups for all unique GREG registers.  We store the fixups here in
    md_convert_frag, then we use the array to convert
    BFD_RELOC_MMIX_BASE_PLUS_OFFSET fixups in tc_gen_reloc.  The index is
@@ -144,7 +148,8 @@ struct mmix_symbol_gregs
    this line?  */
 static int label_without_colon_this_line = 1;
 
    this line?  */
 static int label_without_colon_this_line = 1;
 
-/* Should we expand operands for external symbols?  */
+/* Should we automatically expand instructions into multiple insns in
+   order to generate working code?  */
 static int expand_op = 1;
 
 /* Should we warn when expanding operands?  FIXME: test-cases for when -x
 static int expand_op = 1;
 
 /* Should we warn when expanding operands?  FIXME: test-cases for when -x
@@ -171,13 +176,17 @@ int mmix_gnu_syntax = 0;
 /* Do we globalize all symbols?  */
 int mmix_globalize_symbols = 0;
 
 /* Do we globalize all symbols?  */
 int mmix_globalize_symbols = 0;
 
+/* When expanding insns, do we want to expand PUSHJ as a call to a stub
+   (or else as a series of insns)?  */
+int pushj_stubs = 1;
+
 /* Do we know that the next semicolon is at the end of the operands field
 /* Do we know that the next semicolon is at the end of the operands field
-   (in mmixal mode; constant 1 in GNU mode)? */
+   (in mmixal mode; constant 1 in GNU mode)?  */
 int mmix_next_semicolon_is_eoln = 1;
 
 /* Do we have a BSPEC in progress?  */
 static int doing_bspec = 0;
 int mmix_next_semicolon_is_eoln = 1;
 
 /* Do we have a BSPEC in progress?  */
 static int doing_bspec = 0;
-static char *bspec_file;
+static const char *bspec_file;
 static unsigned int bspec_line;
 
 struct option md_longopts[] =
 static unsigned int bspec_line;
 
 struct option md_longopts[] =
@@ -190,6 +199,7 @@ struct option md_longopts[] =
 #define OPTION_GLOBALIZE_SYMBOLS  (OPTION_GNU_SYNTAX + 1)
 #define OPTION_FIXED_SPEC_REGS  (OPTION_GLOBALIZE_SYMBOLS + 1)
 #define OPTION_LINKER_ALLOCATED_GREGS  (OPTION_FIXED_SPEC_REGS + 1)
 #define OPTION_GLOBALIZE_SYMBOLS  (OPTION_GNU_SYNTAX + 1)
 #define OPTION_FIXED_SPEC_REGS  (OPTION_GLOBALIZE_SYMBOLS + 1)
 #define OPTION_LINKER_ALLOCATED_GREGS  (OPTION_FIXED_SPEC_REGS + 1)
+#define OPTION_NOPUSHJSTUBS  (OPTION_LINKER_ALLOCATED_GREGS + 1)
    {"linkrelax", no_argument, NULL, OPTION_RELAX},
    {"no-expand", no_argument, NULL, OPTION_NOEXPAND},
    {"no-merge-gregs", no_argument, NULL, OPTION_NOMERGEGREG},
    {"linkrelax", no_argument, NULL, OPTION_RELAX},
    {"no-expand", no_argument, NULL, OPTION_NOEXPAND},
    {"no-merge-gregs", no_argument, NULL, OPTION_NOMERGEGREG},
@@ -200,6 +210,8 @@ struct option md_longopts[] =
     OPTION_FIXED_SPEC_REGS},
    {"linker-allocated-gregs", no_argument, NULL,
     OPTION_LINKER_ALLOCATED_GREGS},
     OPTION_FIXED_SPEC_REGS},
    {"linker-allocated-gregs", no_argument, NULL,
     OPTION_LINKER_ALLOCATED_GREGS},
+   {"no-pushj-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS},
+   {"no-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS},
    {NULL, no_argument, NULL, 0}
  };
 
    {NULL, no_argument, NULL, 0}
  };
 
@@ -228,15 +240,27 @@ struct obstack mmix_sym_obstack;
 
    3. PUSHJ
       extra length: zero or four insns.
 
    3. PUSHJ
       extra length: zero or four insns.
+      Special handling to deal with transition to PUSHJSTUB.
 
    4. JMP
 
    4. JMP
-      extra length: zero or four insns.  */
+      extra length: zero or four insns.
+
+   5. GREG
+      special handling, allocates a named global register unless another
+      is within reach for all uses.
+
+   6. PUSHJSTUB
+      special handling (mostly) for external references; assumes the
+      linker will generate a stub if target is no longer than 256k from
+      the end of the section plus max size of previous stubs.  Zero or
+      four insns.  */
 
 #define STATE_GETA     (1)
 #define STATE_BCC      (2)
 #define STATE_PUSHJ    (3)
 #define STATE_JMP      (4)
 #define STATE_GREG     (5)
 
 #define STATE_GETA     (1)
 #define STATE_BCC      (2)
 #define STATE_PUSHJ    (3)
 #define STATE_JMP      (4)
 #define STATE_GREG     (5)
+#define STATE_PUSHJSTUB        (6)
 
 /* No fine-grainedness here.  */
 #define STATE_LENGTH_MASK          (1)
 
 /* No fine-grainedness here.  */
 #define STATE_LENGTH_MASK          (1)
@@ -255,35 +279,40 @@ struct obstack mmix_sym_obstack;
 #define STATE_GREG_UNDF ENCODE_RELAX (STATE_GREG, STATE_ZERO)
 #define STATE_GREG_DEF ENCODE_RELAX (STATE_GREG, STATE_MAX)
 
 #define STATE_GREG_UNDF ENCODE_RELAX (STATE_GREG, STATE_ZERO)
 #define STATE_GREG_DEF ENCODE_RELAX (STATE_GREG, STATE_MAX)
 
-/* These displacements are relative to the adress following the opcode
+/* These displacements are relative to the address following the opcode
    word of the instruction.  The catch-all states have zero for "reach"
    and "next" entries.  */
 
 #define GETA_0F (65536 * 4 - 8)
 #define GETA_0B (-65536 * 4 - 4)
 
    word of the instruction.  The catch-all states have zero for "reach"
    and "next" entries.  */
 
 #define GETA_0F (65536 * 4 - 8)
 #define GETA_0B (-65536 * 4 - 4)
 
-#define GETA_MAX_LEN 4*4
+#define GETA_MAX_LEN 4 * 4
 #define GETA_3F 0
 #define GETA_3B 0
 
 #define BCC_0F GETA_0F
 #define BCC_0B GETA_0B
 
 #define GETA_3F 0
 #define GETA_3B 0
 
 #define BCC_0F GETA_0F
 #define BCC_0B GETA_0B
 
-#define BCC_MAX_LEN 6*4
+#define BCC_MAX_LEN 6 * 4
 #define BCC_5F GETA_3F
 #define BCC_5B GETA_3B
 
 #define PUSHJ_0F GETA_0F
 #define PUSHJ_0B GETA_0B
 
 #define BCC_5F GETA_3F
 #define BCC_5B GETA_3B
 
 #define PUSHJ_0F GETA_0F
 #define PUSHJ_0B GETA_0B
 
-#define PUSHJ_MAX_LEN 5*4
+#define PUSHJ_MAX_LEN 5 * 4
 #define PUSHJ_4F GETA_3F
 #define PUSHJ_4B GETA_3B
 
 #define PUSHJ_4F GETA_3F
 #define PUSHJ_4B GETA_3B
 
+/* We'll very rarely have sections longer than LONG_MAX, but we'll make a
+   feeble attempt at getting 64-bit values.  */
+#define PUSHJSTUB_MAX ((offsetT) (((addressT) -1) >> 1))
+#define PUSHJSTUB_MIN (-PUSHJSTUB_MAX - 1)
+
 #define JMP_0F (65536 * 256 * 4 - 8)
 #define JMP_0B (-65536 * 256 * 4 - 4)
 
 #define JMP_0F (65536 * 256 * 4 - 8)
 #define JMP_0B (-65536 * 256 * 4 - 4)
 
-#define JMP_MAX_LEN 5*4
+#define JMP_MAX_LEN 5 * 4
 #define JMP_4F 0
 #define JMP_4B 0
 
 #define JMP_4F 0
 #define JMP_4B 0
 
@@ -312,8 +341,8 @@ const relax_typeS mmix_relax_table[] =
    {BCC_5F,    BCC_5B,
                BCC_MAX_LEN - 4,        0},
 
    {BCC_5F,    BCC_5B,
                BCC_MAX_LEN - 4,        0},
 
-   /* PUSHJ (3, 0).  */
-   {PUSHJ_0F,  PUSHJ_0B,       0,      ENCODE_RELAX (STATE_PUSHJ, STATE_MAX)},
+   /* PUSHJ (3, 0).  Next state is actually PUSHJSTUB (6, 0).  */
+   {PUSHJ_0F,  PUSHJ_0B,       0,      ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO)},
 
    /* PUSHJ (3, 1).  */
    {PUSHJ_4F,  PUSHJ_4B,
 
    /* PUSHJ (3, 1).  */
    {PUSHJ_4F,  PUSHJ_4B,
@@ -327,7 +356,13 @@ const relax_typeS mmix_relax_table[] =
                JMP_MAX_LEN - 4,        0},
 
    /* GREG (5, 0), (5, 1), though the table entry isn't used.  */
                JMP_MAX_LEN - 4,        0},
 
    /* GREG (5, 0), (5, 1), though the table entry isn't used.  */
-   {0, 0, 0, 0}, {0, 0, 0, 0}
+   {0, 0, 0, 0}, {0, 0, 0, 0},
+
+   /* PUSHJSTUB (6, 0).  PUSHJ (3, 0) uses the range, so we set it to infinite.  */
+   {PUSHJSTUB_MAX, PUSHJSTUB_MIN,
+               0,                      ENCODE_RELAX (STATE_PUSHJ, STATE_MAX)},
+   /* PUSHJSTUB (6, 1) isn't used.  */
+   {0, 0,      PUSHJ_MAX_LEN,          0}
 };
 
 const pseudo_typeS md_pseudo_table[] =
 };
 
 const pseudo_typeS md_pseudo_table[] =
@@ -344,10 +379,6 @@ const pseudo_typeS md_pseudo_table[] =
    /* Support " .local $45" syntax.  */
    {"local", mmix_s_local, 1},
 
    /* Support " .local $45" syntax.  */
    {"local", mmix_s_local, 1},
 
-   /* Support DWARF2 debugging info.  */
-   {"file", dwarf2_directive_file, 0},
-   {"loc", dwarf2_directive_loc, 0},
-
    {NULL, 0, 0}
  };
 
    {NULL, 0, 0}
  };
 
@@ -364,17 +395,15 @@ const char line_comment_chars[] = "*#";
 
 const char line_separator_chars[] = ";";
 
 
 const char line_separator_chars[] = ";";
 
-const char mmix_exp_chars[] = "eE";
+const char EXP_CHARS[] = "eE";
 
 
-const char mmix_flt_chars[] = "rf";
+const char FLT_CHARS[] = "rf";
 
 
 /* Fill in the offset-related part of GETA or Bcc.  */
 
 static void
 
 
 /* Fill in the offset-related part of GETA or Bcc.  */
 
 static void
-mmix_set_geta_branch_offset (opcodep, value)
-     char *opcodep;
-     offsetT value;
+mmix_set_geta_branch_offset (char *opcodep, offsetT value)
 {
   if (value < 0)
     {
 {
   if (value < 0)
     {
@@ -389,9 +418,7 @@ mmix_set_geta_branch_offset (opcodep, value)
 /* Fill in the offset-related part of JMP.  */
 
 static void
 /* Fill in the offset-related part of JMP.  */
 
 static void
-mmix_set_jmp_offset (opcodep, value)
-     char *opcodep;
-     offsetT value;
+mmix_set_jmp_offset (char *opcodep, offsetT value)
 {
   if (value < 0)
     {
 {
   if (value < 0)
     {
@@ -406,22 +433,18 @@ mmix_set_jmp_offset (opcodep, value)
 /* Fill in NOP:s for the expanded part of GETA/JMP/Bcc/PUSHJ.  */
 
 static void
 /* Fill in NOP:s for the expanded part of GETA/JMP/Bcc/PUSHJ.  */
 
 static void
-mmix_fill_nops (opcodep, n)
-     char *opcodep;
-     int n;
+mmix_fill_nops (char *opcodep, int n)
 {
   int i;
 
   for (i = 0; i < n; i++)
 {
   int i;
 
   for (i = 0; i < n; i++)
-    md_number_to_chars (opcodep + i*4, SWYM_INSN_BYTE << 24, 4);
+    md_number_to_chars (opcodep + i * 4, SWYM_INSN_BYTE << 24, 4);
 }
 
 /* See macro md_parse_name in tc-mmix.h.  */
 
 int
 }
 
 /* See macro md_parse_name in tc-mmix.h.  */
 
 int
-mmix_current_location (fn, exp)
-     void (*fn) PARAMS ((expressionS *));
-     expressionS *exp;
+mmix_current_location (void (*fn) (expressionS *), expressionS *exp)
 {
   (*fn) (exp);
 
 {
   (*fn) (exp);
 
@@ -432,10 +455,7 @@ mmix_current_location (fn, exp)
    General idea and code stolen from the tic80 port.  */
 
 static int
    General idea and code stolen from the tic80 port.  */
 
 static int
-get_operands (max_operands, s, exp)
-     int max_operands;
-     char *s;
-     expressionS exp[];
+get_operands (int max_operands, char *s, expressionS *exp)
 {
   char *p = s;
   int numexp = 0;
 {
   char *p = s;
   int numexp = 0;
@@ -461,7 +481,7 @@ get_operands (max_operands, s, exp)
          return 0;
        }
 
          return 0;
        }
 
-      /* Begin operand parsing at the current scan point. */
+      /* Begin operand parsing at the current scan point.  */
 
       input_line_pointer = p;
       expression (&exp[numexp]);
 
       input_line_pointer = p;
       expression (&exp[numexp]);
@@ -491,7 +511,7 @@ get_operands (max_operands, s, exp)
       input_line_pointer--;
     }
 
       input_line_pointer--;
     }
 
-  /* Mark the end of the valid operands with an illegal expression. */
+  /* Mark the end of the valid operands with an illegal expression.  */
   exp[numexp].X_op = O_illegal;
 
   return (numexp);
   exp[numexp].X_op = O_illegal;
 
   return (numexp);
@@ -501,8 +521,7 @@ get_operands (max_operands, s, exp)
    one.  NAME is a null-terminated string.  */
 
 static int
    one.  NAME is a null-terminated string.  */
 
 static int
-get_spec_regno (name)
-     char *name;
+get_spec_regno (char *name)
 {
   int i;
 
 {
   int i;
 
@@ -524,10 +543,8 @@ get_spec_regno (name)
 /* For GET and PUT, parse the register names "manually", so we don't use
    user labels.  */
 static int
 /* For GET and PUT, parse the register names "manually", so we don't use
    user labels.  */
 static int
-get_putget_operands (insn, operands, exp)
-     struct mmix_opcode *insn;
-     char *operands;
-     expressionS exp[];
+get_putget_operands (struct mmix_opcode *insn, char *operands,
+                    expressionS *exp)
 {
   expressionS *expp_reg;
   expressionS *expp_sreg;
 {
   expressionS *expp_reg;
   expressionS *expp_sreg;
@@ -543,6 +560,11 @@ get_putget_operands (insn, operands, exp)
 
   input_line_pointer = p;
 
 
   input_line_pointer = p;
 
+  /* Initialize both possible operands to error state, in case we never
+     get further.  */
+  exp[0].X_op = O_illegal;
+  exp[1].X_op = O_illegal;
+
   if (insn->operands == mmix_operands_get)
     {
       expp_reg = &exp[0];
   if (insn->operands == mmix_operands_get)
     {
       expp_reg = &exp[0];
@@ -565,8 +587,10 @@ get_putget_operands (insn, operands, exp)
            p++;
          sregp = p;
          input_line_pointer = sregp;
            p++;
          sregp = p;
          input_line_pointer = sregp;
-         c = get_symbol_end ();
+         c = get_symbol_name (&sregp);
          sregend = input_line_pointer;
          sregend = input_line_pointer;
+         if (c == '"')
+           ++ input_line_pointer;
        }
     }
   else
        }
     }
   else
@@ -574,14 +598,10 @@ get_putget_operands (insn, operands, exp)
       expp_sreg = &exp[0];
       expp_reg = &exp[1];
 
       expp_sreg = &exp[0];
       expp_reg = &exp[1];
 
-      /* Initialize to error state in case we'll never call expression on
-         this operand.  */
-      expp_reg->X_op = O_illegal;
-
-      sregp = p;
-      c = get_symbol_end ();
-      sregend = p = input_line_pointer;
-      *p = c;
+      c = get_symbol_name (&sregp);
+      sregend = input_line_pointer;
+      restore_line_pointer (c);
+      p = input_line_pointer;
 
       /* Skip whitespace */
       while (*p == ' ' || *p == '\t')
 
       /* Skip whitespace */
       while (*p == ' ' || *p == '\t')
@@ -620,9 +640,7 @@ get_putget_operands (insn, operands, exp)
 /* Handle MMIX-specific option.  */
 
 int
 /* Handle MMIX-specific option.  */
 
 int
-md_parse_option (c, arg)
-     int c;
-     char *arg ATTRIBUTE_UNUSED;
+md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
 {
   switch (c)
     {
 {
   switch (c)
     {
@@ -665,6 +683,10 @@ md_parse_option (c, arg)
       allocate_undefined_gregs_in_linker = 1;
       break;
 
       allocate_undefined_gregs_in_linker = 1;
       break;
 
+    case OPTION_NOPUSHJSTUBS:
+      pushj_stubs = 0;
+      break;
+
     default:
       return 0;
     }
     default:
       return 0;
     }
@@ -675,8 +697,7 @@ md_parse_option (c, arg)
 /* Display MMIX-specific help text.  */
 
 void
 /* Display MMIX-specific help text.  */
 
 void
-md_show_usage (stream)
-     FILE * stream;
+md_show_usage (FILE * stream)
 {
   fprintf (stream, _(" MMIX-specific command line options:\n"));
   fprintf (stream, _("\
 {
   fprintf (stream, _(" MMIX-specific command line options:\n"));
   fprintf (stream, _("\
@@ -709,10 +730,10 @@ md_show_usage (stream)
 /* Step to end of line, but don't step over the end of the line.  */
 
 static void
 /* Step to end of line, but don't step over the end of the line.  */
 
 static void
-mmix_discard_rest_of_line ()
+mmix_discard_rest_of_line (void)
 {
   while (*input_line_pointer
 {
   while (*input_line_pointer
-        && (! is_end_of_line [(unsigned char) *input_line_pointer]
+        && (! is_end_of_line[(unsigned char) *input_line_pointer]
             || TC_EOL_IN_INSN (input_line_pointer)))
     input_line_pointer++;
 }
             || TC_EOL_IN_INSN (input_line_pointer)))
     input_line_pointer++;
 }
@@ -722,7 +743,7 @@ mmix_discard_rest_of_line ()
    delimiter).  */
 
 static void
    delimiter).  */
 
 static void
-mmix_handle_rest_of_empty_line ()
+mmix_handle_rest_of_empty_line (void)
 {
   if (mmix_gnu_syntax)
     demand_empty_rest_of_line ();
 {
   if (mmix_gnu_syntax)
     demand_empty_rest_of_line ();
@@ -736,7 +757,7 @@ mmix_handle_rest_of_empty_line ()
 /* Initialize GAS MMIX specifics.  */
 
 void
 /* Initialize GAS MMIX specifics.  */
 
 void
-mmix_md_begin ()
+mmix_md_begin (void)
 {
   int i;
   const struct mmix_opcode *opcode;
 {
   int i;
   const struct mmix_opcode *opcode;
@@ -746,7 +767,7 @@ mmix_md_begin ()
 
   /* This will break the day the "lex" thingy changes.  For now, it's the
      only way to make ':' part of a name, and a name beginner.  */
 
   /* This will break the day the "lex" thingy changes.  For now, it's the
      only way to make ':' part of a name, and a name beginner.  */
-  lex_type [':'] = (LEX_NAME | LEX_BEGIN_NAME);
+  lex_type[':'] = (LEX_NAME | LEX_BEGIN_NAME);
 
   mmix_opcode_hash = hash_new ();
 
 
   mmix_opcode_hash = hash_new ();
 
@@ -795,8 +816,7 @@ mmix_md_begin ()
 /* Assemble one insn in STR.  */
 
 void
 /* Assemble one insn in STR.  */
 
 void
-md_assemble (str)
-     char *str;
+md_assemble (char *str)
 {
   char *operands = str;
   char modified_char = 0;
 {
   char *operands = str;
   char modified_char = 0;
@@ -1081,7 +1101,7 @@ md_assemble (str)
        }
 
       if (expand_op)
        }
 
       if (expand_op)
-       frag_var (rs_machine_dependent, 4*4, 0,
+       frag_var (rs_machine_dependent, 4 * 4, 0,
                  ENCODE_RELAX (STATE_JMP, STATE_UNDF),
                  exp[0].X_add_symbol,
                  exp[0].X_add_number,
                  ENCODE_RELAX (STATE_JMP, STATE_UNDF),
                  exp[0].X_add_symbol,
                  exp[0].X_add_number,
@@ -1281,7 +1301,7 @@ md_assemble (str)
       /* SYNCD: "X,$Y,$Z|Z".  */
       /* FALLTHROUGH.  */
     case mmix_operands_regs:
       /* SYNCD: "X,$Y,$Z|Z".  */
       /* FALLTHROUGH.  */
     case mmix_operands_regs:
-      /* Three registers, $X,$Y,$Z. */
+      /* Three registers, $X,$Y,$Z.  */
       /* FALLTHROUGH.  */
     case mmix_operands_regs_z:
       /* Operands "$X,$Y,$Z|Z", number of arguments checked above.  */
       /* FALLTHROUGH.  */
     case mmix_operands_regs_z:
       /* Operands "$X,$Y,$Z|Z", number of arguments checked above.  */
@@ -1354,6 +1374,9 @@ md_assemble (str)
             pass expressions as symbols and use fix_new, not fix_new_exp.  */
          sym = make_expr_symbol (exp + 1);
 
             pass expressions as symbols and use fix_new, not fix_new_exp.  */
          sym = make_expr_symbol (exp + 1);
 
+         /* Mark the symbol as being OK for a reloc.  */
+         symbol_get_bfdsym (sym)->flags |= BSF_KEEP;
+
          /* Now we know it can be a "base address plus offset".  Add
             proper fixup types so we can handle this later, when we've
             parsed everything.  */
          /* Now we know it can be a "base address plus offset".  Add
             proper fixup types so we can handle this later, when we've
             parsed everything.  */
@@ -1419,7 +1442,7 @@ md_assemble (str)
       break;
 
     case mmix_operands_jmp:
       break;
 
     case mmix_operands_jmp:
-      /* A JMP.  Everyhing is already done.  */
+      /* A JMP.  Everything is already done.  */
       break;
 
     case mmix_operands_roundregs:
       break;
 
     case mmix_operands_roundregs:
@@ -1454,8 +1477,8 @@ md_assemble (str)
                && ((exp[1].X_op == O_register
                     && exp[1].X_add_number < 512)
                    || (exp[1].X_op == O_constant
                && ((exp[1].X_op == O_register
                     && exp[1].X_add_number < 512)
                    || (exp[1].X_op == O_constant
-                       && exp[1].X_add_number < 0
-                       && exp[1].X_add_number > 4)
+                       && (exp[1].X_add_number < 0
+                           || exp[1].X_add_number > 4))
                    || (exp[1].X_op != O_register
                        && exp[1].X_op != O_constant))))
          {
                    || (exp[1].X_op != O_register
                        && exp[1].X_op != O_constant))))
          {
@@ -1486,27 +1509,27 @@ md_assemble (str)
 
     case mmix_operands_sync:
     a_single_24_bit_number_operand:
 
     case mmix_operands_sync:
     a_single_24_bit_number_operand:
-    if (n_operands != 1
-       || exp[0].X_op == O_register
-       || (exp[0].X_op == O_constant
-           && (exp[0].X_add_number > 0xffffff || exp[0].X_add_number < 0)))
-      {
-       as_bad (_("invalid operands to opcode %s: `%s'"),
-               instruction->name, operands);
-       return;
-      }
+      if (n_operands != 1
+         || exp[0].X_op == O_register
+         || (exp[0].X_op == O_constant
+             && (exp[0].X_add_number > 0xffffff || exp[0].X_add_number < 0)))
+       {
+         as_bad (_("invalid operands to opcode %s: `%s'"),
+                 instruction->name, operands);
+         return;
+       }
 
 
-    if (exp[0].X_op == O_constant)
-      {
-       opcodep[1] = (exp[0].X_add_number >> 16) & 255;
-       opcodep[2] = (exp[0].X_add_number >> 8) & 255;
-       opcodep[3] = exp[0].X_add_number & 255;
-      }
-    else
-      /* FIXME: This doesn't bring us unsignedness checking.  */
-      fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
-                  3, exp + 0, 0, BFD_RELOC_24);
-    break;
+      if (exp[0].X_op == O_constant)
+       {
+         opcodep[1] = (exp[0].X_add_number >> 16) & 255;
+         opcodep[2] = (exp[0].X_add_number >> 8) & 255;
+         opcodep[3] = exp[0].X_add_number & 255;
+       }
+      else
+       /* FIXME: This doesn't bring us unsignedness checking.  */
+       fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+                    3, exp + 0, 0, BFD_RELOC_24);
+      break;
 
     case mmix_operands_neg:
       /* Operands "$X,Y,$Z|Z"; NEG or NEGU.  Y is optional, 0 is default.  */
 
     case mmix_operands_neg:
       /* Operands "$X,Y,$Z|Z"; NEG or NEGU.  Y is optional, 0 is default.  */
@@ -1560,7 +1583,7 @@ md_assemble (str)
       break;
 
     case mmix_operands_regaddr:
       break;
 
     case mmix_operands_regaddr:
-      /* A GETA/branch-type. */
+      /* A GETA/branch-type.  */
       break;
 
     case mmix_operands_get:
       break;
 
     case mmix_operands_get:
@@ -1638,7 +1661,7 @@ md_assemble (str)
          break;
        }
 
          break;
        }
 
-      /* "0,$Z"; UNSAVE. */
+      /* "0,$Z"; UNSAVE.  */
       if (n_operands != 2
          || exp[0].X_op != O_constant
          || exp[0].X_add_number != 0
       if (n_operands != 2
          || exp[0].X_op != O_constant
          || exp[0].X_add_number != 0
@@ -1659,7 +1682,10 @@ md_assemble (str)
       break;
 
     case mmix_operands_xyz_opt:
       break;
 
     case mmix_operands_xyz_opt:
-      /* SWYM, TRIP, TRAP: zero, one, two or three operands.  */
+      /* SWYM, TRIP, TRAP: zero, one, two or three operands.  It's
+        unspecified whether operands are registers or constants, but
+        when we find register syntax, we require operands to be literal and
+        within 0..255.  */
       if (n_operands == 0 && ! mmix_gnu_syntax)
        /* Zeros are in place - nothing needs to be done for zero
           operands.  We don't allow this in GNU syntax mode, because it
       if (n_operands == 0 && ! mmix_gnu_syntax)
        /* Zeros are in place - nothing needs to be done for zero
           operands.  We don't allow this in GNU syntax mode, because it
@@ -1670,7 +1696,7 @@ md_assemble (str)
        {
          if (exp[0].X_op == O_constant)
            {
        {
          if (exp[0].X_op == O_constant)
            {
-             if (exp[0].X_add_number > 255*255*255
+             if (exp[0].X_add_number > 255*256*256
                  || exp[0].X_add_number < 0)
                {
                  as_bad (_("invalid operands to opcode %s: `%s'"),
                  || exp[0].X_add_number < 0)
                {
                  as_bad (_("invalid operands to opcode %s: `%s'"),
@@ -1712,7 +1738,7 @@ md_assemble (str)
 
          if (exp[1].X_op == O_constant)
            {
 
          if (exp[1].X_op == O_constant)
            {
-             if (exp[1].X_add_number > 255*255
+             if (exp[1].X_add_number > 255*256
                  || exp[1].X_add_number < 0)
                {
                  as_bad (_("invalid operands to opcode %s: `%s'"),
                  || exp[1].X_add_number < 0)
                {
                  as_bad (_("invalid operands to opcode %s: `%s'"),
@@ -1784,62 +1810,64 @@ md_assemble (str)
            fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
                         1, exp + 2, 0, BFD_RELOC_8);
        }
            fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
                         1, exp + 2, 0, BFD_RELOC_8);
        }
-      else if (n_operands <= 3
-              && (strcmp (instruction->name, "trip") == 0
-                  || strcmp (instruction->name, "trap") == 0))
+      else
        {
        {
-         /* The meaning of operands to TRIP and TRAP are not defined, so
-            we add combinations not handled above here as we find them.  */
+         /* We can't get here for other cases.  */
+         gas_assert (n_operands <= 3);
+
+         /* The meaning of operands to TRIP and TRAP is not defined (and
+            SWYM operands aren't enforced in mmixal, so let's avoid
+            that).  We add combinations not handled above here as we find
+            them and as they're reported.  */
          if (n_operands == 3)
            {
              /* Don't require non-register operands.  Always generate
                 fixups, so we don't have to copy lots of code and create
          if (n_operands == 3)
            {
              /* Don't require non-register operands.  Always generate
                 fixups, so we don't have to copy lots of code and create
-                maintanance problems.  TRIP is supposed to be a rare
+                maintenance problems.  TRIP is supposed to be a rare
                 instruction, so the overhead should not matter.  We
                 aren't allowed to fix_new_exp for an expression which is
                 instruction, so the overhead should not matter.  We
                 aren't allowed to fix_new_exp for an expression which is
-                an  O_register at this point, however.  */
+                an O_register at this point, however.
+
+                Don't use BFD_RELOC_MMIX_REG_OR_BYTE as that modifies
+                the insn for a register in the Z field and we want
+                consistency.  */
              if (exp[0].X_op == O_register)
                opcodep[1] = exp[0].X_add_number;
              else
                fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
              if (exp[0].X_op == O_register)
                opcodep[1] = exp[0].X_add_number;
              else
                fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
-                            1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+                            1, exp, 0, BFD_RELOC_8);
              if (exp[1].X_op == O_register)
                opcodep[2] = exp[1].X_add_number;
              else
                fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
              if (exp[1].X_op == O_register)
                opcodep[2] = exp[1].X_add_number;
              else
                fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
-                            1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+                            1, exp + 1, 0, BFD_RELOC_8);
              if (exp[2].X_op == O_register)
                opcodep[3] = exp[2].X_add_number;
              else
                fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
              if (exp[2].X_op == O_register)
                opcodep[3] = exp[2].X_add_number;
              else
                fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
-                            1, exp + 2, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+                            1, exp + 2, 0, BFD_RELOC_8);
            }
          else if (n_operands == 2)
            {
              if (exp[0].X_op == O_register)
            }
          else if (n_operands == 2)
            {
              if (exp[0].X_op == O_register)
-               opcodep[2] = exp[0].X_add_number;
+               opcodep[1] = exp[0].X_add_number;
              else
              else
-               fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
-                            1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+               fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+                            1, exp, 0, BFD_RELOC_8);
              if (exp[1].X_op == O_register)
                opcodep[3] = exp[1].X_add_number;
              else
              if (exp[1].X_op == O_register)
                opcodep[3] = exp[1].X_add_number;
              else
-               fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
-                            1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+               fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+                            2, exp + 1, 0, BFD_RELOC_16);
            }
          else
            {
            }
          else
            {
-             as_bad (_("unsupported operands to %s: `%s'"),
-                     instruction->name, operands);
-             return;
+             /* We can't get here for other cases.  */
+             gas_assert (n_operands == 1 && exp[0].X_op == O_register);
+
+             opcodep[3] = exp[0].X_add_number;
            }
        }
            }
        }
-      else
-       {
-         as_bad (_("invalid operands to opcode %s: `%s'"),
-                 instruction->name, operands);
-         return;
-       }
       break;
 
     case mmix_operands_resume:
       break;
 
     case mmix_operands_resume:
@@ -1877,8 +1905,7 @@ md_assemble (str)
    tc_unrecognized_line too, through this function.  */
 
 int
    tc_unrecognized_line too, through this function.  */
 
 int
-mmix_assemble_return_nonzero (str)
-     char  *str;
+mmix_assemble_return_nonzero (char *str)
 {
   int last_error_count = had_errors ();
   char *s2 = str;
 {
   int last_error_count = had_errors ();
   char *s2 = str;
@@ -1907,19 +1934,16 @@ mmix_assemble_return_nonzero (str)
 /* The PREFIX pseudo.  */
 
 static void
 /* The PREFIX pseudo.  */
 
 static void
-s_prefix (unused)
-     int unused ATTRIBUTE_UNUSED;
+s_prefix (int unused ATTRIBUTE_UNUSED)
 {
   char *p;
   int c;
 
   SKIP_WHITESPACE ();
 
 {
   char *p;
   int c;
 
   SKIP_WHITESPACE ();
 
-  p = input_line_pointer;
-
-  c = get_symbol_end ();
-
-  /* Reseting prefix?  */
+  c = get_symbol_name (&p);
+  
+  /* Resetting prefix?  */
   if (*p == ':' && p[1] == 0)
     mmix_current_prefix = NULL;
   else
   if (*p == ':' && p[1] == 0)
     mmix_current_prefix = NULL;
   else
@@ -1937,7 +1961,7 @@ s_prefix (unused)
       mmix_current_prefix = p;
     }
 
       mmix_current_prefix = p;
     }
 
-  *input_line_pointer = c;
+  (void) restore_line_pointer (c);
 
   mmix_handle_rest_of_empty_line ();
 }
 
   mmix_handle_rest_of_empty_line ();
 }
@@ -1950,8 +1974,7 @@ s_prefix (unused)
    that.  (It might be worth a rewrite for other reasons, though).  */
 
 char *
    that.  (It might be worth a rewrite for other reasons, though).  */
 
 char *
-mmix_prefix_name (shortname)
-     char *shortname;
+mmix_prefix_name (char *shortname)
 {
   if (*shortname == ':')
     return shortname + 1;
 {
   if (*shortname == ':')
     return shortname + 1;
@@ -1975,14 +1998,14 @@ mmix_prefix_name (shortname)
    be persistent, perhaps allocated on an obstack.  */
 
 static void
    be persistent, perhaps allocated on an obstack.  */
 
 static void
-mmix_greg_internal (label)
-     char *label;
+mmix_greg_internal (char *label)
 {
   expressionS *expP = &mmix_raw_gregs[n_of_raw_gregs].exp;
 {
   expressionS *expP = &mmix_raw_gregs[n_of_raw_gregs].exp;
+  segT section;
 
   /* Don't set the section to register contents section before the
      expression has been parsed; it may refer to the current position.  */
 
   /* Don't set the section to register contents section before the
      expression has been parsed; it may refer to the current position.  */
-  expression (expP);
+  section = expression (expP);
 
   /* FIXME: Check that no expression refers to the register contents
      section.  May need to be done in elf64-mmix.c.  */
 
   /* FIXME: Check that no expression refers to the register contents
      section.  May need to be done in elf64-mmix.c.  */
@@ -1996,6 +2019,24 @@ mmix_greg_internal (label)
       expP->X_op_symbol = NULL;
     }
 
       expP->X_op_symbol = NULL;
     }
 
+  if (section == undefined_section)
+    {
+      /* This is an error or a LOC with an expression involving
+        forward references.  For the expression to be correctly
+        evaluated, we need to force a proper symbol; gas loses track
+        of the segment for "local symbols".  */
+      if (expP->X_op == O_add)
+       {
+         symbol_get_value_expression (expP->X_op_symbol);
+         symbol_get_value_expression (expP->X_add_symbol);
+       }
+      else
+       {
+         gas_assert (expP->X_op == O_symbol);
+         symbol_get_value_expression (expP->X_add_symbol);
+       }
+    }
+
   /* We must handle prefixes here, as we save the labels and expressions
      to be output later.  */
   mmix_raw_gregs[n_of_raw_gregs].label
   /* We must handle prefixes here, as we save the labels and expressions
      to be output later.  */
   mmix_raw_gregs[n_of_raw_gregs].label
@@ -2012,20 +2053,21 @@ mmix_greg_internal (label)
 /* The ".greg label,expr" worker.  */
 
 static void
 /* The ".greg label,expr" worker.  */
 
 static void
-s_greg (unused)
-     int unused ATTRIBUTE_UNUSED;
+s_greg (int unused ATTRIBUTE_UNUSED)
 {
   char *p;
   char c;
 {
   char *p;
   char c;
-  p = input_line_pointer;
 
   /* This will skip over what can be a symbol and zero out the next
      character, which we assume is a ',' or other meaningful delimiter.
      What comes after that is the initializer expression for the
      register.  */
 
   /* This will skip over what can be a symbol and zero out the next
      character, which we assume is a ',' or other meaningful delimiter.
      What comes after that is the initializer expression for the
      register.  */
-  c = get_symbol_end ();
+  c = get_symbol_name (&p);
 
 
-  if (! is_end_of_line [(unsigned char) c])
+  if (c == '"')
+    c = * ++ input_line_pointer;
+
+  if (! is_end_of_line[(unsigned char) c])
     input_line_pointer++;
 
   if (*p)
     input_line_pointer++;
 
   if (*p)
@@ -2042,8 +2084,7 @@ s_greg (unused)
 /* The "BSPEC expr" worker.  */
 
 static void
 /* The "BSPEC expr" worker.  */
 
 static void
-s_bspec (unused)
-     int unused ATTRIBUTE_UNUSED;
+s_bspec (int unused ATTRIBUTE_UNUSED)
 {
   asection *expsec;
   asection *sec;
 {
   asection *expsec;
   asection *sec;
@@ -2089,9 +2130,8 @@ s_bspec (unused)
       if (sec == NULL)
        as_fatal (_("can't create section %s"), newsecname);
 
       if (sec == NULL)
        as_fatal (_("can't create section %s"), newsecname);
 
-      if (!bfd_set_section_flags (stdoutput, sec,
-                                 bfd_get_section_flags (stdoutput, sec)
-                                 | SEC_READONLY))
+      if (!bfd_set_section_flags (sec,
+                                 bfd_section_flags (sec) | SEC_READONLY))
        as_fatal (_("can't set section flags for section %s"), newsecname);
     }
 
        as_fatal (_("can't set section flags for section %s"), newsecname);
     }
 
@@ -2100,7 +2140,7 @@ s_bspec (unused)
   subseg_set (sec, 0);
 
   /* Save position for missing ESPEC.  */
   subseg_set (sec, 0);
 
   /* Save position for missing ESPEC.  */
-  as_where (&bspec_file, &bspec_line);
+  bspec_file = as_where (&bspec_line);
 
   doing_bspec = 1;
 }
 
   doing_bspec = 1;
 }
@@ -2108,8 +2148,7 @@ s_bspec (unused)
 /* The "ESPEC" worker.  */
 
 static void
 /* The "ESPEC" worker.  */
 
 static void
-s_espec (unused)
-     int unused ATTRIBUTE_UNUSED;
+s_espec (int unused ATTRIBUTE_UNUSED)
 {
   /* First, check that we *do* have a BSPEC in progress.  */
   if (! doing_bspec)
 {
   /* First, check that we *do* have a BSPEC in progress.  */
   if (! doing_bspec)
@@ -2132,8 +2171,7 @@ s_espec (unused)
    Implementing this by means of contents in a section lost.  */
 
 static void
    Implementing this by means of contents in a section lost.  */
 
 static void
-mmix_s_local (unused)
-     int unused ATTRIBUTE_UNUSED;
+mmix_s_local (int unused ATTRIBUTE_UNUSED)
 {
   expressionS exp;
 
 {
   expressionS exp;
 
@@ -2163,16 +2201,15 @@ mmix_s_local (unused)
    function may be called multiple times.  */
 
 int
    function may be called multiple times.  */
 
 int
-md_estimate_size_before_relax (fragP, segment)
-     fragS *fragP;
-     segT    segment;
+md_estimate_size_before_relax (fragS *fragP, segT segment)
 {
   int length;
 
 #define HANDLE_RELAXABLE(state)                                                \
  case ENCODE_RELAX (state, STATE_UNDF):                                        \
    if (fragP->fr_symbol != NULL                                                \
 {
   int length;
 
 #define HANDLE_RELAXABLE(state)                                                \
  case ENCODE_RELAX (state, STATE_UNDF):                                        \
    if (fragP->fr_symbol != NULL                                                \
-       && S_GET_SEGMENT (fragP->fr_symbol) == segment)                 \
+       && S_GET_SEGMENT (fragP->fr_symbol) == segment                  \
+       && !S_IS_WEAK (fragP->fr_symbol))                               \
      {                                                                 \
        /* The symbol lies in the same segment - a relaxable case.  */  \
        fragP->fr_subtype                                               \
      {                                                                 \
        /* The symbol lies in the same segment - a relaxable case.  */  \
        fragP->fr_subtype                                               \
@@ -2184,12 +2221,27 @@ md_estimate_size_before_relax (fragP, segment)
     {
       HANDLE_RELAXABLE (STATE_GETA);
       HANDLE_RELAXABLE (STATE_BCC);
     {
       HANDLE_RELAXABLE (STATE_GETA);
       HANDLE_RELAXABLE (STATE_BCC);
-      HANDLE_RELAXABLE (STATE_PUSHJ);
       HANDLE_RELAXABLE (STATE_JMP);
 
       HANDLE_RELAXABLE (STATE_JMP);
 
+    case ENCODE_RELAX (STATE_PUSHJ, STATE_UNDF):
+      if (fragP->fr_symbol != NULL
+         && S_GET_SEGMENT (fragP->fr_symbol) == segment
+         && !S_IS_WEAK (fragP->fr_symbol))
+       /* The symbol lies in the same segment - a relaxable case.  */
+       fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO);
+      else if (pushj_stubs)
+       /* If we're to generate stubs, assume we can reach a stub after
+           the section.  */
+       fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
+      /* FALLTHROUGH.  */
+    case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
+    case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
+      /* We need to distinguish different relaxation rounds.  */
+      seg_info (segment)->tc_segment_info_data.last_stubfrag = fragP;
+      break;
+
     case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
     case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
     case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
     case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
-    case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
     case ENCODE_RELAX (STATE_JMP, STATE_ZERO):
       /* When relaxing a section for the second time, we don't need to do
         anything except making sure that fr_var is set right.  */
     case ENCODE_RELAX (STATE_JMP, STATE_ZERO):
       /* When relaxing a section for the second time, we don't need to do
         anything except making sure that fr_var is set right.  */
@@ -2219,59 +2271,26 @@ md_estimate_size_before_relax (fragP, segment)
    emitted is stored in *sizeP .  An error message is returned, or NULL on
    OK.  */
 
    emitted is stored in *sizeP .  An error message is returned, or NULL on
    OK.  */
 
-char *
-md_atof (type, litP, sizeP)
-     int type;
-     char *litP;
-     int *sizeP;
+const char *
+md_atof (int type, char *litP, int *sizeP)
 {
 {
-  int prec;
-  LITTLENUM_TYPE words[4];
-  char *t;
-  int i;
-
-  switch (type)
-    {
-      /* FIXME: Having 'f' in mmix_flt_chars (and here) makes it
-        problematic to also have a forward reference in an expression.
-        The testsuite wants it, and it's customary.
-        We'll deal with the real problems when they come; we share the
-        problem with most other ports.  */
-    case 'f':
-    case 'r':
-      prec = 2;
-      break;
-    case 'd':
-      prec = 4;
-      break;
-    default:
-      *sizeP = 0;
-      return _("bad call to md_atof");
-    }
-
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
-
-  *sizeP = prec * 2;
-
-  for (i = 0; i < prec; i++)
-    {
-      md_number_to_chars (litP, (valueT) words[i], 2);
-         litP += 2;
-    }
-  return NULL;
+  if (type == 'r')
+    type = 'f';
+  /* FIXME: Having 'f' in FLT_CHARS (and here) makes it
+     problematic to also have a forward reference in an expression.
+     The testsuite wants it, and it's customary.
+     We'll deal with the real problems when they come; we share the
+     problem with most other ports.  */
+  return ieee_md_atof (type, litP, sizeP, TRUE);
 }
 
 /* Convert variable-sized frags into one or more fixups.  */
 
 void
 }
 
 /* Convert variable-sized frags into one or more fixups.  */
 
 void
-md_convert_frag (abfd, sec, fragP)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     segT sec ATTRIBUTE_UNUSED;
-     fragS *fragP;
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
+                fragS *fragP)
 {
 {
-  /* Pointer to first byte in variable-sized part of the frag. */
+  /* Pointer to first byte in variable-sized part of the frag.  */
   char *var_partp;
 
   /* Pointer to first opcode byte in frag.  */
   char *var_partp;
 
   /* Pointer to first opcode byte in frag.  */
@@ -2309,48 +2328,62 @@ md_convert_frag (abfd, sec, fragP)
   opcode_address = fragP->fr_address + fragP->fr_fix - 4;
 
   switch (fragP->fr_subtype)
   opcode_address = fragP->fr_address + fragP->fr_fix - 4;
 
   switch (fragP->fr_subtype)
-  {
-  case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
-  case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
-  case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
-    mmix_set_geta_branch_offset (opcodep, target_address - opcode_address);
-    if (linkrelax)
-      {
-       tmpfixP
-         = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
-                    fragP->fr_symbol, fragP->fr_offset, 1,
-                    BFD_RELOC_MMIX_ADDR19);
-       COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
-      }
-    var_part_size = 0;
-    break;
+    {
+    case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
+      /* Setting the unknown bits to 0 seems the most appropriate.  */
+      mmix_set_geta_branch_offset (opcodep, 0);
+      tmpfixP = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 8,
+                        fragP->fr_symbol, fragP->fr_offset, 1,
+                        BFD_RELOC_MMIX_PUSHJ_STUBBABLE);
+      COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
+      var_part_size = 0;
+      break;
 
 
-  case ENCODE_RELAX (STATE_JMP, STATE_ZERO):
-    mmix_set_jmp_offset (opcodep, target_address - opcode_address);
-    if (linkrelax)
-      {
-       tmpfixP
-         = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
-                    fragP->fr_symbol, fragP->fr_offset, 1,
-                    BFD_RELOC_MMIX_ADDR27);
-       COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
-      }
-    var_part_size = 0;
-    break;
+    case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
+    case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
+    case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
+      mmix_set_geta_branch_offset (opcodep, target_address - opcode_address);
+      if (linkrelax)
+       {
+         tmpfixP
+           = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+                      fragP->fr_symbol, fragP->fr_offset, 1,
+                      BFD_RELOC_MMIX_ADDR19);
+         COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
+       }
+      var_part_size = 0;
+      break;
 
 
-  case STATE_GREG_DEF:
-    if (fragP->tc_frag_data == NULL)
-      {
-       tmpfixP
-         = fix_new (fragP, var_partp - fragP->fr_literal, 8,
-                    fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_64);
-       COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
-       mmix_gregs[n_of_cooked_gregs++] = tmpfixP;
-       var_part_size = 8;
-      }
-    else
+    case ENCODE_RELAX (STATE_JMP, STATE_ZERO):
+      mmix_set_jmp_offset (opcodep, target_address - opcode_address);
+      if (linkrelax)
+       {
+         tmpfixP
+           = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+                      fragP->fr_symbol, fragP->fr_offset, 1,
+                      BFD_RELOC_MMIX_ADDR27);
+         COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
+       }
       var_part_size = 0;
       var_part_size = 0;
-    break;
+      break;
+
+    case STATE_GREG_DEF:
+      if (fragP->tc_frag_data == NULL)
+       {
+         /* We must initialize data that's supposed to be "fixed up" to
+            avoid emitting garbage, because md_apply_fix won't do
+            anything for undefined symbols.  */
+         md_number_to_chars (var_partp, 0, 8);
+         tmpfixP
+           = fix_new (fragP, var_partp - fragP->fr_literal, 8,
+                      fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_64);
+         COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
+         mmix_gregs[n_of_cooked_gregs++] = tmpfixP;
+         var_part_size = 8;
+       }
+      else
+       var_part_size = 0;
+      break;
 
 #define HANDLE_MAX_RELOC(state, reloc)                                 \
   case ENCODE_RELAX (state, STATE_MAX):                                        \
 
 #define HANDLE_MAX_RELOC(state, reloc)                                 \
   case ENCODE_RELAX (state, STATE_MAX):                                        \
@@ -2365,15 +2398,15 @@ md_convert_frag (abfd, sec, fragP)
     COPY_FR_WHERE_TO_FX (fragP, tmpfixP);                              \
     break
 
     COPY_FR_WHERE_TO_FX (fragP, tmpfixP);                              \
     break
 
-  HANDLE_MAX_RELOC (STATE_GETA, BFD_RELOC_MMIX_GETA);
-  HANDLE_MAX_RELOC (STATE_BCC, BFD_RELOC_MMIX_CBRANCH);
-  HANDLE_MAX_RELOC (STATE_PUSHJ, BFD_RELOC_MMIX_PUSHJ);
-  HANDLE_MAX_RELOC (STATE_JMP, BFD_RELOC_MMIX_JMP);
+      HANDLE_MAX_RELOC (STATE_GETA, BFD_RELOC_MMIX_GETA);
+      HANDLE_MAX_RELOC (STATE_BCC, BFD_RELOC_MMIX_CBRANCH);
+      HANDLE_MAX_RELOC (STATE_PUSHJ, BFD_RELOC_MMIX_PUSHJ);
+      HANDLE_MAX_RELOC (STATE_JMP, BFD_RELOC_MMIX_JMP);
 
 
-  default:
-    BAD_CASE (fragP->fr_subtype);
-    break;
-  }
+    default:
+      BAD_CASE (fragP->fr_subtype);
+      break;
+    }
 
   fragP->fr_fix += var_part_size;
   fragP->fr_var = 0;
 
   fragP->fr_fix += var_part_size;
   fragP->fr_var = 0;
@@ -2386,10 +2419,7 @@ md_convert_frag (abfd, sec, fragP)
    Note that this function isn't called when linkrelax != 0.  */
 
 void
    Note that this function isn't called when linkrelax != 0.  */
 
 void
-md_apply_fix3 (fixP, valP, segment)
-     fixS *   fixP;
-     valueT * valP;
-     segT     segment;
+md_apply_fix (fixS *fixP, valueT *valP, segT segment)
 {
   char *buf  = fixP->fx_where + fixP->fx_frag->fr_literal;
   /* Note: use offsetT because it is signed, valueT is unsigned.  */
 {
   char *buf  = fixP->fx_where + fixP->fx_frag->fr_literal;
   /* Note: use offsetT because it is signed, valueT is unsigned.  */
@@ -2409,8 +2439,7 @@ md_apply_fix3 (fixP, valP, segment)
              && symsec != absolute_section
              && ((fixP->fx_r_type != BFD_RELOC_MMIX_REG
                   && fixP->fx_r_type != BFD_RELOC_MMIX_REG_OR_BYTE)
              && symsec != absolute_section
              && ((fixP->fx_r_type != BFD_RELOC_MMIX_REG
                   && fixP->fx_r_type != BFD_RELOC_MMIX_REG_OR_BYTE)
-                 || (symsec != reg_section
-                     && symsec != real_reg_section)))))
+                 || symsec != reg_section))))
     {
       fixP->fx_done = 0;
       return;
     {
       fixP->fx_done = 0;
       return;
@@ -2453,6 +2482,7 @@ md_apply_fix3 (fixP, valP, segment)
     case BFD_RELOC_MMIX_GETA:
     case BFD_RELOC_MMIX_CBRANCH:
     case BFD_RELOC_MMIX_PUSHJ:
     case BFD_RELOC_MMIX_GETA:
     case BFD_RELOC_MMIX_CBRANCH:
     case BFD_RELOC_MMIX_PUSHJ:
+    case BFD_RELOC_MMIX_PUSHJ_STUBBABLE:
       /* If this fixup is out of range, punt to the linker to emit an
         error.  This should only happen with -no-expand.  */
       if (val < -(((offsetT) 1 << 19)/2)
       /* If this fixup is out of range, punt to the linker to emit an
         error.  This should only happen with -no-expand.  */
       if (val < -(((offsetT) 1 << 19)/2)
@@ -2494,11 +2524,17 @@ md_apply_fix3 (fixP, valP, segment)
 
     case BFD_RELOC_MMIX_REG_OR_BYTE:
       if (fixP->fx_addsy != NULL
 
     case BFD_RELOC_MMIX_REG_OR_BYTE:
       if (fixP->fx_addsy != NULL
-         && (S_GET_SEGMENT (fixP->fx_addsy) != real_reg_section
+         && (S_GET_SEGMENT (fixP->fx_addsy) != reg_section
              || S_GET_VALUE (fixP->fx_addsy) > 255)
          && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
              || S_GET_VALUE (fixP->fx_addsy) > 255)
          && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
-       as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("invalid operands"));
+       {
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("invalid operands"));
+         /* We don't want this "symbol" appearing in output, because
+            that will fail.  */
+         fixP->fx_done = 1;
+       }
+
       buf[0] = val;
 
       /* If this reloc is for a Z field, we need to adjust
       buf[0] = val;
 
       /* If this reloc is for a Z field, we need to adjust
@@ -2509,25 +2545,19 @@ md_apply_fix3 (fixP, valP, segment)
          && (fixP->fx_addsy == NULL
              || S_GET_SEGMENT (fixP->fx_addsy) == absolute_section))
        buf[-3] |= IMM_OFFSET_BIT;
          && (fixP->fx_addsy == NULL
              || S_GET_SEGMENT (fixP->fx_addsy) == absolute_section))
        buf[-3] |= IMM_OFFSET_BIT;
-
-      /* We don't want this "symbol" appearing in output, because that
-        will fail.  */
-      if (fixP->fx_addsy
-         && S_GET_SEGMENT (fixP->fx_addsy) == real_reg_section)
-       symbol_clear_used_in_reloc (fixP->fx_addsy);
       break;
 
     case BFD_RELOC_MMIX_REG:
       if (fixP->fx_addsy == NULL
       break;
 
     case BFD_RELOC_MMIX_REG:
       if (fixP->fx_addsy == NULL
-         || S_GET_SEGMENT (fixP->fx_addsy) != real_reg_section
+         || S_GET_SEGMENT (fixP->fx_addsy) != reg_section
          || S_GET_VALUE (fixP->fx_addsy) > 255)
          || S_GET_VALUE (fixP->fx_addsy) > 255)
-       as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("invalid operands"));
-      *buf = val;
+       {
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("invalid operands"));
+         fixP->fx_done = 1;
+       }
 
 
-      if (fixP->fx_addsy
-         && S_GET_SEGMENT (fixP->fx_addsy) == real_reg_section)
-       symbol_clear_used_in_reloc (fixP->fx_addsy);
+      *buf = val;
       break;
 
     case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
       break;
 
     case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
@@ -2563,9 +2593,7 @@ md_apply_fix3 (fixP, valP, segment)
    definitions.  */
 
 static int
    definitions.  */
 
 static int
-cmp_greg_val_greg_symbol_fixes (p1, p2)
-     const PTR p1;
-     const PTR p2;
+cmp_greg_val_greg_symbol_fixes (const void *p1, const void *p2)
 {
   offsetT val1 = *(offsetT *) p1;
   offsetT val2 = ((struct mmix_symbol_greg_fixes *) p2)->offs;
 {
   offsetT val1 = *(offsetT *) p1;
   offsetT val2 = ((struct mmix_symbol_greg_fixes *) p2)->offs;
@@ -2582,19 +2610,23 @@ cmp_greg_val_greg_symbol_fixes (p1, p2)
 /* Generate a machine-dependent relocation.  */
 
 arelent *
 /* Generate a machine-dependent relocation.  */
 
 arelent *
-tc_gen_reloc (section, fixP)
-     asection *section ATTRIBUTE_UNUSED;
-     fixS *fixP;
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
 {
   bfd_signed_vma val
 {
   bfd_signed_vma val
-    = fixP->fx_offset + (fixP->fx_addsy ? S_GET_VALUE (fixP->fx_addsy) : 0);
+    = fixP->fx_offset
+    + (fixP->fx_addsy != NULL
+       && !S_IS_WEAK (fixP->fx_addsy)
+       && !S_IS_COMMON (fixP->fx_addsy)
+       ? S_GET_VALUE (fixP->fx_addsy) : 0);
   arelent *relP;
   bfd_reloc_code_real_type code = BFD_RELOC_NONE;
   char *buf  = fixP->fx_where + fixP->fx_frag->fr_literal;
   symbolS *addsy = fixP->fx_addsy;
   asection *addsec = addsy == NULL ? NULL : S_GET_SEGMENT (addsy);
   asymbol *baddsy = addsy != NULL ? symbol_get_bfdsym (addsy) : NULL;
   arelent *relP;
   bfd_reloc_code_real_type code = BFD_RELOC_NONE;
   char *buf  = fixP->fx_where + fixP->fx_frag->fr_literal;
   symbolS *addsy = fixP->fx_addsy;
   asection *addsec = addsy == NULL ? NULL : S_GET_SEGMENT (addsy);
   asymbol *baddsy = addsy != NULL ? symbol_get_bfdsym (addsy) : NULL;
-  bfd_vma addend = val - (baddsy == NULL ? 0 : bfd_asymbol_value (baddsy));
+  bfd_vma addend
+    = val - (baddsy == NULL || S_IS_COMMON (addsy) || S_IS_WEAK (addsy)
+            ? 0 : bfd_asymbol_value (baddsy));
 
   /* A single " LOCAL expression" in the wrong section will not work when
      linking to MMO; relocations for zero-content sections are then
 
   /* A single " LOCAL expression" in the wrong section will not work when
      linking to MMO; relocations for zero-content sections are then
@@ -2605,7 +2637,7 @@ tc_gen_reloc (section, fixP)
      than just helping the user around this limitation here; hopefully the
      code using the local expression is around.  Putting the LOCAL
      semantics in a relocation still seems right; a section didn't do.  */
      than just helping the user around this limitation here; hopefully the
      code using the local expression is around.  Putting the LOCAL
      semantics in a relocation still seems right; a section didn't do.  */
-  if (bfd_section_size (section->owner, section) == 0)
+  if (bfd_section_size (section) == 0)
     as_bad_where
       (fixP->fx_file, fixP->fx_line,
        fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
     as_bad_where
       (fixP->fx_file, fixP->fx_line,
        fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
@@ -2626,7 +2658,7 @@ tc_gen_reloc (section, fixP)
 
       if (addsy == NULL || bfd_is_abs_section (addsec))
        {
 
       if (addsy == NULL || bfd_is_abs_section (addsec))
        {
-         /* Resolve this reloc now, as md_apply_fix3 would have done (not
+         /* Resolve this reloc now, as md_apply_fix would have done (not
             called if -linkrelax).  There is no point in keeping a reloc
             to an absolute symbol.  No reloc that is subject to
             relaxation must be to an absolute symbol; difference
             called if -linkrelax).  There is no point in keeping a reloc
             to an absolute symbol.  No reloc that is subject to
             relaxation must be to an absolute symbol; difference
@@ -2659,6 +2691,7 @@ tc_gen_reloc (section, fixP)
     case BFD_RELOC_MMIX_PUSHJ_1:
     case BFD_RELOC_MMIX_PUSHJ_2:
     case BFD_RELOC_MMIX_PUSHJ_3:
     case BFD_RELOC_MMIX_PUSHJ_1:
     case BFD_RELOC_MMIX_PUSHJ_2:
     case BFD_RELOC_MMIX_PUSHJ_3:
+    case BFD_RELOC_MMIX_PUSHJ_STUBBABLE:
     case BFD_RELOC_MMIX_JMP:
     case BFD_RELOC_MMIX_JMP_1:
     case BFD_RELOC_MMIX_JMP_2:
     case BFD_RELOC_MMIX_JMP:
     case BFD_RELOC_MMIX_JMP_1:
     case BFD_RELOC_MMIX_JMP_2:
@@ -2674,7 +2707,7 @@ tc_gen_reloc (section, fixP)
         resolve the relocation here.  */
       if (addsy != NULL
          && (bfd_is_und_section (addsec)
         resolve the relocation here.  */
       if (addsy != NULL
          && (bfd_is_und_section (addsec)
-             || strcmp (bfd_get_section_name (addsec->owner, addsec),
+             || strcmp (bfd_section_name (addsec),
                         MMIX_REG_CONTENTS_SECTION_NAME) == 0))
        {
          code = fixP->fx_r_type;
                         MMIX_REG_CONTENTS_SECTION_NAME) == 0))
        {
          code = fixP->fx_r_type;
@@ -2701,7 +2734,7 @@ tc_gen_reloc (section, fixP)
 
     case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
       if (addsy != NULL
 
     case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
       if (addsy != NULL
-         && strcmp (bfd_get_section_name (addsec->owner, addsec),
+         && strcmp (bfd_section_name (addsec),
                     MMIX_REG_CONTENTS_SECTION_NAME) == 0)
        {
          /* This changed into a register; the relocation is for the
                     MMIX_REG_CONTENTS_SECTION_NAME) == 0)
        {
          /* This changed into a register; the relocation is for the
@@ -2720,7 +2753,9 @@ tc_gen_reloc (section, fixP)
          struct mmix_symbol_gregs *gregs;
          struct mmix_symbol_greg_fixes *fix;
 
          struct mmix_symbol_gregs *gregs;
          struct mmix_symbol_greg_fixes *fix;
 
-         if (S_IS_DEFINED (addsy))
+         if (S_IS_DEFINED (addsy)
+             && !bfd_is_com_section (addsec)
+             && !S_IS_WEAK (addsy))
            {
              if (! symbol_section_p (addsy) && ! bfd_is_abs_section (addsec))
                as_fatal (_("internal: BFD_RELOC_MMIX_BASE_PLUS_OFFSET not resolved to section"));
            {
              if (! symbol_section_p (addsy) && ! bfd_is_abs_section (addsec))
                as_fatal (_("internal: BFD_RELOC_MMIX_BASE_PLUS_OFFSET not resolved to section"));
@@ -2802,7 +2837,7 @@ tc_gen_reloc (section, fixP)
     case BFD_RELOC_MMIX_REG:
       if (addsy != NULL
          && (bfd_is_und_section (addsec)
     case BFD_RELOC_MMIX_REG:
       if (addsy != NULL
          && (bfd_is_und_section (addsec)
-             || strcmp (bfd_get_section_name (addsec->owner, addsec),
+             || strcmp (bfd_section_name (addsec),
                         MMIX_REG_CONTENTS_SECTION_NAME) == 0))
        {
          code = fixP->fx_r_type;
                         MMIX_REG_CONTENTS_SECTION_NAME) == 0))
        {
          code = fixP->fx_r_type;
@@ -2821,11 +2856,11 @@ tc_gen_reloc (section, fixP)
          buf[0] = val;
          return NULL;
        }
          buf[0] = val;
          return NULL;
        }
-      /* FALLTHROUGH. */
+      /* FALLTHROUGH.  */
 
 
-      /* The others are supposed to be handled by md_apply_fix3.
+      /* The others are supposed to be handled by md_apply_fix.
         FIXME: ... which isn't called when -linkrelax.  Move over
         FIXME: ... which isn't called when -linkrelax.  Move over
-        md_apply_fix3 code here for everything reasonable.  */
+        md_apply_fix code here for everything reasonable.  */
     badop:
     default:
       as_bad_where
     badop:
     default:
       as_bad_where
@@ -2834,14 +2869,13 @@ tc_gen_reloc (section, fixP)
 
       /* Unmark this symbol as used in a reloc, so we don't bump into a BFD
         assert when trying to output reg_section.  FIXME: A gas bug.  */
 
       /* Unmark this symbol as used in a reloc, so we don't bump into a BFD
         assert when trying to output reg_section.  FIXME: A gas bug.  */
-      if (addsy)
-       symbol_clear_used_in_reloc (addsy);
+      fixP->fx_addsy = NULL;
       return NULL;
     }
 
       return NULL;
     }
 
-  relP = (arelent *) xmalloc (sizeof (arelent));
-  assert (relP != 0);
-  relP->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  relP = XNEW (arelent);
+  gas_assert (relP != 0);
+  relP->sym_ptr_ptr = XNEW (asymbol *);
   *relP->sym_ptr_ptr = baddsy;
   relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
 
   *relP->sym_ptr_ptr = baddsy;
   relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
 
@@ -2870,10 +2904,10 @@ tc_gen_reloc (section, fixP)
    ugly labels_without_colons etc.  */
 
 void
    ugly labels_without_colons etc.  */
 
 void
-mmix_handle_mmixal ()
+mmix_handle_mmixal (void)
 {
 {
-  char *s0 = input_line_pointer;
-  char *s;
+  char *insn;
+  char *s = input_line_pointer;
   char *label = NULL;
   char c;
 
   char *label = NULL;
   char c;
 
@@ -2883,93 +2917,96 @@ mmix_handle_mmixal ()
   if (mmix_gnu_syntax)
     return;
 
   if (mmix_gnu_syntax)
     return;
 
-  /* If the first character is a '.', then it's a pseudodirective, not a
-     label.  Make GAS not handle label-without-colon on this line.  We
-     also don't do mmixal-specific stuff on this line.  */
-  if (input_line_pointer[0] == '.')
-    {
-      label_without_colon_this_line = 0;
-      return;
-    }
-
-  /* Don't handle empty lines here.  */
-  while (1)
-    {
-      if (*s0 == 0 || is_end_of_line [(unsigned int) *s0])
-       return;
-
-      if (! ISSPACE (*s0))
-       break;
-
-      s0++;
-    }
-
   /* If we're on a line with a label, check if it's a mmixal fb-label.
      Save an indicator and skip the label; it must be set only after all
      fb-labels of expressions are evaluated.  */
   /* If we're on a line with a label, check if it's a mmixal fb-label.
      Save an indicator and skip the label; it must be set only after all
      fb-labels of expressions are evaluated.  */
-  if (ISDIGIT (input_line_pointer[0])
-      && input_line_pointer[1] == 'H'
-      && ISSPACE (input_line_pointer[2]))
+  if (ISDIGIT (s[0]) && s[1] == 'H' && ISSPACE (s[2]))
     {
     {
-      char *s;
-      current_fb_label = input_line_pointer[0] - '0';
+      current_fb_label = s[0] - '0';
 
       /* We have to skip the label, but also preserve the newlineness of
         the previous character, since the caller checks that.  It's a
         mess we blame on the caller.  */
 
       /* We have to skip the label, but also preserve the newlineness of
         the previous character, since the caller checks that.  It's a
         mess we blame on the caller.  */
-      input_line_pointer[1] = input_line_pointer[-1];
-      input_line_pointer += 2;
+      s[1] = s[-1];
+      s += 2;
+      input_line_pointer = s;
 
 
-      s = input_line_pointer;
       while (*s && ISSPACE (*s) && ! is_end_of_line[(unsigned int) *s])
        s++;
 
       /* For errors emitted here, the book-keeping is off by one; the
         caller is about to bump the counters.  Adjust the error messages.  */
       while (*s && ISSPACE (*s) && ! is_end_of_line[(unsigned int) *s])
        s++;
 
       /* For errors emitted here, the book-keeping is off by one; the
         caller is about to bump the counters.  Adjust the error messages.  */
-      if (is_end_of_line [(unsigned int) *s])
+      if (is_end_of_line[(unsigned int) *s])
        {
        {
-         char *name;
          unsigned int line;
          unsigned int line;
-         as_where (&name, &line);
+         const char * name = as_where (&line);
          as_bad_where (name, line + 1,
                        _("[0-9]H labels may not appear alone on a line"));
          current_fb_label = -1;
        }
       if (*s == '.')
        {
          as_bad_where (name, line + 1,
                        _("[0-9]H labels may not appear alone on a line"));
          current_fb_label = -1;
        }
       if (*s == '.')
        {
-         char *name;
          unsigned int line;
          unsigned int line;
-         as_where (&name, &line);
+         const char * name  = as_where (&line);
          as_bad_where (name, line + 1,
                        _("[0-9]H labels do not mix with dot-pseudos"));
          current_fb_label = -1;
        }
          as_bad_where (name, line + 1,
                        _("[0-9]H labels do not mix with dot-pseudos"));
          current_fb_label = -1;
        }
+
+      /* Back off to the last space before the opcode so we don't handle
+        the opcode as a label.  */
+      s--;
     }
   else
     }
   else
+    current_fb_label = -1;
+
+  if (*s == '.')
     {
     {
-      current_fb_label = -1;
-      if (is_name_beginner (input_line_pointer[0]))
-       label = input_line_pointer;
+      /* If the first character is a '.', then it's a pseudodirective, not a
+        label.  Make GAS not handle label-without-colon on this line.  We
+        also don't do mmixal-specific stuff on this line.  */
+      label_without_colon_this_line = 0;
+      return;
     }
 
     }
 
-  s0 = input_line_pointer;
-  /* Skip over label. */
-  while (*s0 && is_part_of_name (*s0))
-    s0++;
-
-  /* Remove trailing ":" off labels, as they'd otherwise be considered
-     part of the name.  But don't do it for local labels.  */
-  if (s0 != input_line_pointer && s0[-1] == ':'
-      && (s0 - 2 != input_line_pointer
-         || ! ISDIGIT (s0[-2])))
-    s0[-1] = ' ';
-  else if (label != NULL)
+  if (*s == 0 || is_end_of_line[(unsigned int) *s])
+    /* We avoid handling empty lines here.  */
+    return;
+
+  if (is_name_beginner (*s))
+    label = s;
+
+  /* If there is a label, skip over it.  */
+  while (*s && is_part_of_name (*s))
+    s++;
+
+  /* Find the start of the instruction or pseudo following the label,
+     if there is one.  */
+  for (insn = s;
+       *insn && ISSPACE (*insn) && ! is_end_of_line[(unsigned int) *insn];
+       insn++)
+    /* Empty */
+    ;
+
+  /* Remove a trailing ":" off labels, as they'd otherwise be considered
+     part of the name.  But don't do this for local labels.  */
+  if (s != input_line_pointer && s[-1] == ':'
+      && (s - 2 != input_line_pointer
+         || ! ISDIGIT (s[-2])))
+    s[-1] = ' ';
+  else if (label != NULL
+          /* For a lone label on a line, we don't attach it to the next
+             instruction or MMIXAL-pseudo (getting its alignment).  Thus
+             is acts like a "normal" :-ended label.  Ditto if it's
+             followed by a non-MMIXAL pseudo.  */
+          && !is_end_of_line[(unsigned int) *insn]
+          && *insn != '.')
     {
       /* For labels that don't end in ":", we save it so we can later give
         it the same alignment and address as the associated instruction.  */
 
       /* Make room for the label including the ending nul.  */
     {
       /* For labels that don't end in ":", we save it so we can later give
         it the same alignment and address as the associated instruction.  */
 
       /* Make room for the label including the ending nul.  */
-      int len_0 = s0 - label + 1;
+      size_t len_0 = s - label + 1;
 
       /* Save this label on the MMIX symbol obstack.  Saving it on an
         obstack is needless for "IS"-pseudos, but it's harmless and we
 
       /* Save this label on the MMIX symbol obstack.  Saving it on an
         obstack is needless for "IS"-pseudos, but it's harmless and we
@@ -2979,14 +3016,10 @@ mmix_handle_mmixal ()
       pending_label[len_0 - 1] = 0;
     }
 
       pending_label[len_0 - 1] = 0;
     }
 
-  while (*s0 && ISSPACE (*s0) && ! is_end_of_line [(unsigned int) *s0])
-    s0++;
-
-  if (pending_label != NULL && is_end_of_line [(unsigned int) *s0])
-    /* Whoops, this was actually a lone label on a line.  Like :-ended
-       labels, we don't attach such labels to the next instruction or
-       pseudo.  */
-    pending_label = NULL;
+  /* If we have a non-MMIXAL pseudo, we have not business with the rest of
+     the line.  */
+  if (*insn == '.')
+    return;
 
   /* Find local labels of operands.  Look for "[0-9][FB]" where the
      characters before and after are not part of words.  Break if a single
 
   /* Find local labels of operands.  Look for "[0-9][FB]" where the
      characters before and after are not part of words.  Break if a single
@@ -2998,18 +3031,17 @@ mmix_handle_mmixal ()
 
   /* First make sure we don't have any of the magic characters on the line
      appearing as input.  */
 
   /* First make sure we don't have any of the magic characters on the line
      appearing as input.  */
-  s = s0;
   while (*s)
     {
       c = *s++;
   while (*s)
     {
       c = *s++;
-      if (is_end_of_line [(unsigned int) c])
+      if (is_end_of_line[(unsigned int) c])
        break;
       if (c == MAGIC_FB_BACKWARD_CHAR || c == MAGIC_FB_FORWARD_CHAR)
        as_bad (_("invalid characters in input"));
     }
 
   /* Scan again, this time looking for ';' after operands.  */
        break;
       if (c == MAGIC_FB_BACKWARD_CHAR || c == MAGIC_FB_FORWARD_CHAR)
        as_bad (_("invalid characters in input"));
     }
 
   /* Scan again, this time looking for ';' after operands.  */
-  s = s0;
+  s = insn;
 
   /* Skip the insn.  */
   while (*s
 
   /* Skip the insn.  */
   while (*s
@@ -3039,7 +3071,7 @@ mmix_handle_mmixal ()
          /* FIXME: Test-case for semi-colon in string.  */
          while (*s
                 && *s != '"'
          /* FIXME: Test-case for semi-colon in string.  */
          while (*s
                 && *s != '"'
-                && (! is_end_of_line [(unsigned int) *s] || *s == ';'))
+                && (! is_end_of_line[(unsigned int) *s] || *s == ';'))
            s++;
 
          if (*s == '"')
            s++;
 
          if (*s == '"')
@@ -3049,7 +3081,9 @@ mmix_handle_mmixal ()
        {
          if ((s[1] != 'B' && s[1] != 'F')
              || is_part_of_name (s[-1])
        {
          if ((s[1] != 'B' && s[1] != 'F')
              || is_part_of_name (s[-1])
-             || is_part_of_name (s[2]))
+             || is_part_of_name (s[2])
+             /* Don't treat e.g. #1F as a local-label reference.  */
+             || (s != input_line_pointer && s[-1] == '#'))
            s++;
          else
            {
            s++;
          else
            {
@@ -3075,7 +3109,7 @@ mmix_handle_mmixal ()
 
   /* Make IS into an EQU by replacing it with "= ".  Only match upper-case
      though; let lower-case be a syntax error.  */
 
   /* Make IS into an EQU by replacing it with "= ".  Only match upper-case
      though; let lower-case be a syntax error.  */
-  s = s0;
+  s = insn;
   if (s[0] == 'I' && s[1] == 'S' && ISSPACE (s[2]))
     {
       *s = '=';
   if (s[0] == 'I' && s[1] == 'S' && ISSPACE (s[2]))
     {
       *s = '=';
@@ -3181,8 +3215,7 @@ mmix_handle_mmixal ()
    We fill in the label as an expression.  */
 
 void
    We fill in the label as an expression.  */
 
 void
-mmix_fb_label (expP)
-     expressionS *expP;
+mmix_fb_label (expressionS *expP)
 {
   symbolS *sym;
   char *fb_internal_name;
 {
   symbolS *sym;
   char *fb_internal_name;
@@ -3231,40 +3264,30 @@ mmix_fb_label (expP)
    relaxing.  */
 
 int
    relaxing.  */
 
 int
-mmix_force_relocation (fixP)
-     fixS * fixP;
+mmix_force_relocation (fixS *fixP)
 {
   if (fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
 {
   if (fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
-      || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
-      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
       || fixP->fx_r_type == BFD_RELOC_MMIX_BASE_PLUS_OFFSET)
     return 1;
 
       || fixP->fx_r_type == BFD_RELOC_MMIX_BASE_PLUS_OFFSET)
     return 1;
 
-  /* FIXME: This is dubious.  Handling of weak symbols should have been
-     caught before we get here.  */
-  if ((fixP->fx_addsy && S_IS_WEAK (fixP->fx_addsy)))
-    return 1;
-
   if (linkrelax)
     return 1;
 
   if (linkrelax)
     return 1;
 
-  /* All our pcrel relocations are must-keep.  Note that md_apply_fix3 is
+  /* All our pcrel relocations are must-keep.  Note that md_apply_fix is
      called *after* this, and will handle getting rid of the presumed
      reloc; a relocation isn't *forced* other than to be handled by
      called *after* this, and will handle getting rid of the presumed
      reloc; a relocation isn't *forced* other than to be handled by
-     md_apply_fix3 (or tc_gen_reloc if linkrelax).  */
+     md_apply_fix (or tc_gen_reloc if linkrelax).  */
   if (fixP->fx_pcrel)
     return 1;
 
   if (fixP->fx_pcrel)
     return 1;
 
-  return 0;
+  return generic_force_reloc (fixP);
 }
 
 /* The location from which a PC relative jump should be calculated,
    given a PC relative reloc.  */
 
 long
 }
 
 /* The location from which a PC relative jump should be calculated,
    given a PC relative reloc.  */
 
 long
-md_pcrel_from_section (fixP, sec)
-     fixS * fixP;
-     segT   sec;
+md_pcrel_from_section (fixS *fixP, segT sec)
 {
   if (fixP->fx_addsy != (symbolS *) NULL
       && (! S_IS_DEFINED (fixP->fx_addsy)
 {
   if (fixP->fx_addsy != (symbolS *) NULL
       && (! S_IS_DEFINED (fixP->fx_addsy)
@@ -3279,47 +3302,29 @@ md_pcrel_from_section (fixP, sec)
 }
 
 /* Adjust the symbol table.  We make reg_section relative to the real
 }
 
 /* Adjust the symbol table.  We make reg_section relative to the real
-   register section.
-
-   FIXME: There's a gas bug; should be fixed when the reg_section symbol
-   is "accidentally" saved for relocs which are really fixups that will be
-   fixed up.  */
+   register section.  */
 
 void
 
 void
-mmix_adjust_symtab ()
+mmix_adjust_symtab (void)
 {
   symbolS *sym;
 {
   symbolS *sym;
-  symbolS *prevsym;
   symbolS *regsec = section_symbol (reg_section);
   symbolS *regsec = section_symbol (reg_section);
-  segT realregsec = NULL;
 
 
-  for (prevsym = sym = symbol_rootP;
-       sym != NULL;
-       prevsym = sym, sym = symbol_next (sym))
+  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
     if (S_GET_SEGMENT (sym) == reg_section)
       {
     if (S_GET_SEGMENT (sym) == reg_section)
       {
-       if (sym == regsec
-           || (!S_IS_EXTERN (sym) && !symbol_used_in_reloc_p (sym)))
+       if (sym == regsec)
          {
          {
+           if (S_IS_EXTERNAL (sym) || symbol_used_in_reloc_p (sym))
+             abort ();
            symbol_remove (sym, &symbol_rootP, &symbol_lastP);
            symbol_remove (sym, &symbol_rootP, &symbol_lastP);
-
-           /* We make one extra turn, or we'll lose the next symbol.  We
-              assume that the symbol we remove is not the symbol root
-              (.text normally is).  */
-           sym = prevsym;
          }
        else
          }
        else
-         {
-           /* Change section to the *real* register section, so it gets
-              proper treatment when writing it out.  Only do this for
-              global symbols.  This also means we don't have to check for
-              $0..$255.  */
-           if (realregsec == NULL)
-             realregsec
-               = bfd_make_section_old_way (stdoutput, MMIX_REG_SECTION_NAME);
-
-           S_SET_SEGMENT (sym, realregsec);
-         }
+         /* Change section to the *real* register section, so it gets
+            proper treatment when writing it out.  Only do this for
+            global symbols.  This also means we don't have to check for
+            $0..$255.  */
+         S_SET_SEGMENT (sym, real_reg_section);
       }
 }
 
       }
 }
 
@@ -3330,7 +3335,7 @@ mmix_adjust_symtab ()
    thought at the time I first wrote this.  */
 
 int
    thought at the time I first wrote this.  */
 
 int
-mmix_label_without_colon_this_line ()
+mmix_label_without_colon_this_line (void)
 {
   int retval = label_without_colon_this_line;
 
 {
   int retval = label_without_colon_this_line;
 
@@ -3346,18 +3351,123 @@ mmix_label_without_colon_this_line ()
    join with.  */
 
 long
    join with.  */
 
 long
-mmix_md_relax_frag (seg, fragP, stretch)
-     segT seg;
-     fragS *fragP;
-     long stretch;
+mmix_md_relax_frag (segT seg, fragS *fragP, long stretch)
 {
 {
-  if (fragP->fr_subtype != STATE_GREG_DEF
-      && fragP->fr_subtype != STATE_GREG_UNDF)
-    return relax_frag (seg, fragP, stretch);
+  switch (fragP->fr_subtype)
+    {
+      /* Growth for this type has been handled by mmix_md_end and
+        correctly estimated, so there's nothing more to do here.  */
+    case STATE_GREG_DEF:
+      return 0;
 
 
-  /* If we're defined, we don't grow.  */
-  if (fragP->fr_subtype == STATE_GREG_DEF)
-    return 0;
+    case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
+      {
+       /* We need to handle relaxation type ourselves, since relax_frag
+          doesn't update fr_subtype if there's no size increase in the
+          current section; when going from plain PUSHJ to a stub.  This
+          is otherwise functionally the same as relax_frag in write.c,
+          simplified for this case.  */
+       offsetT aim;
+       addressT target;
+       addressT address;
+       symbolS *symbolP;
+       target = fragP->fr_offset;
+       address = fragP->fr_address;
+       symbolP = fragP->fr_symbol;
+
+       if (symbolP)
+         {
+           fragS *sym_frag;
+
+           sym_frag = symbol_get_frag (symbolP);
+           know (S_GET_SEGMENT (symbolP) != absolute_section
+                 || sym_frag == &zero_address_frag);
+           target += S_GET_VALUE (symbolP);
+
+           /* If frag has yet to be reached on this pass, assume it will
+              move by STRETCH just as we did.  If this is not so, it will
+              be because some frag between grows, and that will force
+              another pass.  */
+
+           if (stretch != 0
+               && sym_frag->relax_marker != fragP->relax_marker
+               && S_GET_SEGMENT (symbolP) == seg)
+             target += stretch;
+         }
+
+       aim = target - address - fragP->fr_fix;
+       if (aim >= PUSHJ_0B && aim <= PUSHJ_0F)
+         {
+           /* Target is reachable with a PUSHJ.  */
+           segment_info_type *seginfo = seg_info (seg);
+
+           /* If we're at the end of a relaxation round, clear the stub
+              counter as initialization for the next round.  */
+           if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
+             seginfo->tc_segment_info_data.nstubs = 0;
+           return 0;
+         }
+
+       /* Not reachable.  Try a stub.  */
+       fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
+      }
+      /* FALLTHROUGH.  */
+
+      /* See if this PUSHJ is redirectable to a stub.  */
+    case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
+      {
+       segment_info_type *seginfo = seg_info (seg);
+       fragS *lastfrag = seginfo->frchainP->frch_last;
+       relax_substateT prev_type = fragP->fr_subtype;
+
+       /* The last frag is always an empty frag, so it suffices to look
+          at its address to know the ending address of this section.  */
+       know (lastfrag->fr_type == rs_fill
+             && lastfrag->fr_fix == 0
+             && lastfrag->fr_var == 0);
+
+       /* For this PUSHJ to be relaxable into a call to a stub, the
+          distance must be no longer than 256k bytes from the PUSHJ to
+          the end of the section plus the maximum size of stubs so far.  */
+       if ((lastfrag->fr_address
+            + stretch
+            + PUSHJ_MAX_LEN * seginfo->tc_segment_info_data.nstubs)
+           - (fragP->fr_address + fragP->fr_fix)
+           > GETA_0F
+           || !pushj_stubs)
+         fragP->fr_subtype = mmix_relax_table[prev_type].rlx_more;
+       else
+         seginfo->tc_segment_info_data.nstubs++;
+
+       /* If we're at the end of a relaxation round, clear the stub
+          counter as initialization for the next round.  */
+       if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
+         seginfo->tc_segment_info_data.nstubs = 0;
+
+       return
+          (mmix_relax_table[fragP->fr_subtype].rlx_length
+           - mmix_relax_table[prev_type].rlx_length);
+      }
+
+    case ENCODE_RELAX (STATE_PUSHJ, STATE_MAX):
+      {
+       segment_info_type *seginfo = seg_info (seg);
+
+       /* Need to cover all STATE_PUSHJ states to act on the last stub
+          frag (the end of this relax round; initialization for the
+          next).  */
+       if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
+         seginfo->tc_segment_info_data.nstubs = 0;
+
+       return 0;
+      }
+
+    default:
+      return relax_frag (seg, fragP, stretch);
+
+    case STATE_GREG_UNDF:
+      BAD_CASE (fragP->fr_subtype);
+    }
 
   as_fatal (_("internal: unexpected relax type %d:%d"),
            fragP->fr_type, fragP->fr_subtype);
 
   as_fatal (_("internal: unexpected relax type %d:%d"),
            fragP->fr_type, fragP->fr_subtype);
@@ -3367,10 +3477,12 @@ mmix_md_relax_frag (seg, fragP, stretch)
 /* Various things we punt until all input is seen.  */
 
 void
 /* Various things we punt until all input is seen.  */
 
 void
-mmix_md_end ()
+mmix_md_end (void)
 {
   fragS *fragP;
   symbolS *mainsym;
 {
   fragS *fragP;
   symbolS *mainsym;
+  asection *regsec;
+  struct loc_assert_s *loc_assert;
   int i;
 
   /* The first frag of GREG:s going into the register contents section.  */
   int i;
 
   /* The first frag of GREG:s going into the register contents section.  */
@@ -3405,8 +3517,8 @@ mmix_md_end ()
     {
       symbolS *symbolP;
       char locsymbol[sizeof (":") - 1
     {
       symbolS *symbolP;
       char locsymbol[sizeof (":") - 1
-                   + sizeof (MMIX_LOC_SECTION_START_SYMBOL_PREFIX) - 1
-                   + sizeof (".data")];
+                    + sizeof (MMIX_LOC_SECTION_START_SYMBOL_PREFIX) - 1
+                    + sizeof (".data")];
 
       sprintf (locsymbol, ":%s%s", MMIX_LOC_SECTION_START_SYMBOL_PREFIX,
               ".data");
 
       sprintf (locsymbol, ":%s%s", MMIX_LOC_SECTION_START_SYMBOL_PREFIX,
               ".data");
@@ -3428,6 +3540,38 @@ mmix_md_end ()
       S_SET_EXTERNAL (mainsym);
     }
 
       S_SET_EXTERNAL (mainsym);
     }
 
+  /* Check that we didn't LOC into the unknown, or rather that when it
+     was unknown, we actually change sections.  */
+  for (loc_assert = loc_asserts;
+       loc_assert != NULL;
+       loc_assert = loc_assert->next)
+    {
+      segT actual_seg;
+
+      resolve_symbol_value (loc_assert->loc_sym);
+      actual_seg = S_GET_SEGMENT (loc_assert->loc_sym);
+      if (actual_seg != loc_assert->old_seg)
+       {
+         const char *fnam;
+         unsigned int line;
+         int e_valid = expr_symbol_where (loc_assert->loc_sym, &fnam, &line);
+
+         gas_assert (e_valid == 1);
+         as_bad_where (fnam, line,
+                       _("LOC to section unknown or indeterminable "
+                         "at first pass"));
+
+         /* Patch up the generic location data to avoid cascading
+            error messages from later passes.  (See original in
+            write.c:relax_segment.)  */
+         fragP = loc_assert->frag;
+         fragP->fr_type = rs_align;
+         fragP->fr_subtype = 0;
+         fragP->fr_offset = 0;
+         fragP->fr_fix = 0;
+       }
+    }
+
   if (n_of_raw_gregs != 0)
     {
       /* Emit GREGs.  They are collected in order of appearance, but must
   if (n_of_raw_gregs != 0)
     {
       /* Emit GREGs.  They are collected in order of appearance, but must
@@ -3435,9 +3579,9 @@ mmix_md_end ()
         and the same allocation order (within a file) as mmixal.  */
       segT this_segment = now_seg;
       subsegT this_subsegment = now_subseg;
         and the same allocation order (within a file) as mmixal.  */
       segT this_segment = now_seg;
       subsegT this_subsegment = now_subseg;
-      asection *regsec
-       = bfd_make_section_old_way (stdoutput,
-                                   MMIX_REG_CONTENTS_SECTION_NAME);
+
+      regsec = bfd_make_section_old_way (stdoutput,
+                                        MMIX_REG_CONTENTS_SECTION_NAME);
       subseg_set (regsec, 0);
 
       /* Finally emit the initialization-value.  Emit a variable frag, which
       subseg_set (regsec, 0);
 
       /* Finally emit the initialization-value.  Emit a variable frag, which
@@ -3464,6 +3608,11 @@ mmix_md_end ()
       subseg_set (this_segment, this_subsegment);
     }
 
       subseg_set (this_segment, this_subsegment);
     }
 
+  regsec = bfd_get_section_by_name (stdoutput, MMIX_REG_CONTENTS_SECTION_NAME);
+  /* Mark the section symbol as being OK for a reloc.  */
+  if (regsec != NULL)
+    regsec->symbol->flags |= BSF_KEEP;
+
   /* Iterate over frags resulting from GREGs and move those that evidently
      have the same value together and point one to another.
 
   /* Iterate over frags resulting from GREGs and move those that evidently
      have the same value together and point one to another.
 
@@ -3542,9 +3691,7 @@ mmix_md_end ()
 /* qsort function for mmix_symbol_gregs.  */
 
 static int
 /* qsort function for mmix_symbol_gregs.  */
 
 static int
-cmp_greg_symbol_fixes (parg, qarg)
-     const PTR parg;
-     const PTR qarg;
+cmp_greg_symbol_fixes (const void *parg, const void *qarg)
 {
   const struct mmix_symbol_greg_fixes *p
     = (const struct mmix_symbol_greg_fixes *) parg;
 {
   const struct mmix_symbol_greg_fixes *p
     = (const struct mmix_symbol_greg_fixes *) parg;
@@ -3561,7 +3708,7 @@ cmp_greg_symbol_fixes (parg, qarg)
    as an ELF section.  */
 
 void
    as an ELF section.  */
 
 void
-mmix_frob_file ()
+mmix_frob_file (void)
 {
   int i;
   struct mmix_symbol_gregs *all_greg_symbols[MAX_GREGS];
 {
   int i;
   struct mmix_symbol_gregs *all_greg_symbols[MAX_GREGS];
@@ -3592,7 +3739,9 @@ mmix_frob_file ()
 
       /* If the symbol is defined, then it must be resolved to a section
         symbol at this time, or else we don't know how to handle it.  */
 
       /* If the symbol is defined, then it must be resolved to a section
         symbol at this time, or else we don't know how to handle it.  */
-      if (S_IS_DEFINED (sym))
+      if (S_IS_DEFINED (sym)
+         && !bfd_is_com_section (S_GET_SEGMENT (sym))
+         && !S_IS_WEAK (sym))
        {
          if (! symbol_section_p (sym)
              && ! bfd_is_abs_section (S_GET_SEGMENT (sym)))
        {
          if (! symbol_section_p (sym)
              && ! bfd_is_abs_section (S_GET_SEGMENT (sym)))
@@ -3625,7 +3774,7 @@ mmix_frob_file ()
 
       if (gregs == NULL)
        {
 
       if (gregs == NULL)
        {
-         gregs = xmalloc (sizeof (*gregs));
+         gregs = XNEW (struct mmix_symbol_gregs);
          gregs->n_gregs = 0;
          symbol_set_tc (sym, &gregs);
          all_greg_symbols[n_greg_symbols++] = gregs;
          gregs->n_gregs = 0;
          symbol_set_tc (sym, &gregs);
          all_greg_symbols[n_greg_symbols++] = gregs;
@@ -3643,18 +3792,11 @@ mmix_frob_file ()
 
   if (real_reg_section != NULL)
     {
 
   if (real_reg_section != NULL)
     {
-      asection **secpp;
-
       /* FIXME: Pass error state gracefully.  */
       /* FIXME: Pass error state gracefully.  */
-      if (bfd_get_section_flags (stdoutput, real_reg_section) & SEC_HAS_CONTENTS)
+      if (bfd_section_flags (real_reg_section) & SEC_HAS_CONTENTS)
        as_fatal (_("register section has contents\n"));
 
        as_fatal (_("register section has contents\n"));
 
-      /* Really remove the section.  */
-      for (secpp = &stdoutput->sections;
-          *secpp != real_reg_section;
-          secpp = &(*secpp)->next)
-       ;
-      bfd_section_list_remove (stdoutput, secpp);
+      bfd_section_list_remove (stdoutput, real_reg_section);
       --stdoutput->section_count;
     }
 
       --stdoutput->section_count;
     }
 
@@ -3667,12 +3809,10 @@ mmix_frob_file ()
    If the name isn't a built-in name and parsed into *EXPP, return zero.  */
 
 int
    If the name isn't a built-in name and parsed into *EXPP, return zero.  */
 
 int
-mmix_parse_predefined_name (name, expP)
-     char *name;
-     expressionS *expP;
+mmix_parse_predefined_name (char *name, expressionS *expP)
 {
   char *canon_name;
 {
   char *canon_name;
-  char *handler_charp;
+  const char *handler_charp;
   const char handler_chars[] = "DVWIOUZX";
   symbolS *symp;
 
   const char handler_chars[] = "DVWIOUZX";
   symbolS *symp;
 
@@ -3747,7 +3887,7 @@ mmix_parse_predefined_name (name, expP)
        return 0;
 
       for (i = 0;
        return 0;
 
       for (i = 0;
-          i < sizeof (predefined_abs_syms)/sizeof (predefined_abs_syms[0]);
+          i < sizeof (predefined_abs_syms) / sizeof (predefined_abs_syms[0]);
           i++)
        if (strcmp (canon_name, predefined_abs_syms[i].name) == 0)
          {
           i++)
        if (strcmp (canon_name, predefined_abs_syms[i].name) == 0)
          {
@@ -3773,61 +3913,17 @@ mmix_parse_predefined_name (name, expP)
   return 1;
 }
 
   return 1;
 }
 
-/* Worker for mmix_frob_file_before_adjust.  */
-
-static void
-mmix_frob_local_reloc (abfd, sec, xxx)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec;
-     PTR xxx ATTRIBUTE_UNUSED;
-{
-  segment_info_type *seginfo = seg_info (sec);
-  fixS *fixp;
-
-  if (seginfo == NULL)
-    return;
-
-  for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
-    if (! fixp->fx_done && fixp->fx_addsy != NULL)
-      {
-       symbolS *sym = fixp->fx_addsy;
-       asection *section = S_GET_SEGMENT (sym);
-
-       if (section == reg_section
-           && fixp->fx_r_type == BFD_RELOC_MMIX_LOCAL)
-         {
-           /* If the register is marked global, we don't need to replace
-              with the *real* register section since that will be done
-              when the symbol is changed.  */
-           if (! S_IS_EXTERNAL (sym))
-             /* If it's a local symbol, we replace it with an anonymous
-                one with the same constant value.  */
-             fixp->fx_addsy = expr_build_uconstant (S_GET_VALUE (sym));
-         }
-      }
-}
-
-/* Change fixups for register symbols for BFD_MMIX_LOCAL to be for an
-   absolute symbol.  */
-
-void
-mmix_frob_file_before_adjust ()
-{
-  return;
-  bfd_map_over_sections (stdoutput, mmix_frob_local_reloc, (char *) 0);
-}
-
 /* Just check that we don't have a BSPEC/ESPEC pair active when changing
    sections "normally", and get knowledge about alignment from the new
    section.  */
 
 void
 /* Just check that we don't have a BSPEC/ESPEC pair active when changing
    sections "normally", and get knowledge about alignment from the new
    section.  */
 
 void
-mmix_md_elf_section_change_hook ()
+mmix_md_elf_section_change_hook (void)
 {
   if (doing_bspec)
     as_bad (_("section change from within a BSPEC/ESPEC pair is not supported"));
 
 {
   if (doing_bspec)
     as_bad (_("section change from within a BSPEC/ESPEC pair is not supported"));
 
-  last_alignment = bfd_get_section_alignment (now_seg->owner, now_seg);
+  last_alignment = bfd_section_alignment (now_seg);
   want_unaligned = 0;
 }
 
   want_unaligned = 0;
 }
 
@@ -3835,8 +3931,7 @@ mmix_md_elf_section_change_hook ()
    section too.   */
 
 static void
    section too.   */
 
 static void
-s_loc (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s_loc (int ignore ATTRIBUTE_UNUSED)
 {
   segT section;
   expressionS exp;
 {
   segT section;
   expressionS exp;
@@ -3844,7 +3939,7 @@ s_loc (ignore)
   symbolS *sym;
   offsetT off;
 
   symbolS *sym;
   offsetT off;
 
-  /* Must not have a BSPEC in progress. */
+  /* Must not have a BSPEC in progress.  */
   if (doing_bspec)
     {
       as_bad (_("directive LOC from within a BSPEC/ESPEC pair is not supported"));
   if (doing_bspec)
     {
       as_bad (_("directive LOC from within a BSPEC/ESPEC pair is not supported"));
@@ -3855,20 +3950,39 @@ s_loc (ignore)
 
   if (exp.X_op == O_illegal
       || exp.X_op == O_absent
 
   if (exp.X_op == O_illegal
       || exp.X_op == O_absent
-      || exp.X_op == O_big
-      || section == undefined_section)
+      || exp.X_op == O_big)
     {
       as_bad (_("invalid LOC expression"));
       return;
     }
 
     {
       as_bad (_("invalid LOC expression"));
       return;
     }
 
+  if (section == undefined_section)
+    {
+      /* This is an error or a LOC with an expression involving
+        forward references.  For the expression to be correctly
+        evaluated, we need to force a proper symbol; gas loses track
+        of the segment for "local symbols".  */
+      if (exp.X_op == O_add)
+       {
+         symbol_get_value_expression (exp.X_op_symbol);
+         symbol_get_value_expression (exp.X_add_symbol);
+       }
+      else
+       {
+         gas_assert (exp.X_op == O_symbol);
+         symbol_get_value_expression (exp.X_add_symbol);
+       }
+    }
+
   if (section == absolute_section)
     {
       /* Translate a constant into a suitable section.  */
 
       if (exp.X_add_number < ((offsetT) 0x20 << 56))
        {
   if (section == absolute_section)
     {
       /* Translate a constant into a suitable section.  */
 
       if (exp.X_add_number < ((offsetT) 0x20 << 56))
        {
-         /* Lower than Data_Segment - assume it's .text.  */
+         /* Lower than Data_Segment or in the reserved area (the
+            segment number is >= 0x80, appearing negative) - assume
+            it's .text.  */
          section = text_section;
 
          /* Save the lowest seen location, so we can pass on this
          section = text_section;
 
          /* Save the lowest seen location, so we can pass on this
@@ -3880,8 +3994,8 @@ s_loc (ignore)
             this one), we org at (this - lower).  There's an implicit
             "LOC 0" before any entered code.  FIXME: handled by spurious
             settings of text_has_contents.  */
             this one), we org at (this - lower).  There's an implicit
             "LOC 0" before any entered code.  FIXME: handled by spurious
             settings of text_has_contents.  */
-         if (exp.X_add_number < 0
-             || exp.X_add_number < (offsetT) lowest_text_loc)
+         if (lowest_text_loc != (bfd_vma) -1
+             && (bfd_vma) exp.X_add_number < lowest_text_loc)
            {
              as_bad (_("LOC expression stepping backwards is not supported"));
              exp.X_op = O_absent;
            {
              as_bad (_("LOC expression stepping backwards is not supported"));
              exp.X_op = O_absent;
@@ -3904,7 +4018,8 @@ s_loc (ignore)
        }
       else
        {
        }
       else
        {
-         /* Do the same for the .data section.  */
+         /* Do the same for the .data section, except we don't have
+            to worry about exp.X_add_number carrying a sign.  */
          section = data_section;
 
          if (exp.X_add_number < (offsetT) lowest_data_loc)
          section = data_section;
 
          if (exp.X_add_number < (offsetT) lowest_data_loc)
@@ -3930,7 +4045,9 @@ s_loc (ignore)
        }
     }
 
        }
     }
 
-  if (section != now_seg)
+  /* If we can't deduce the section, it must be the current one.
+     Below, we arrange to assert this.  */
+  if (section != now_seg && section != undefined_section)
     {
       obj_elf_section_change_hook ();
       subseg_set (section, 0);
     {
       obj_elf_section_change_hook ();
       subseg_set (section, 0);
@@ -3941,16 +4058,41 @@ s_loc (ignore)
 
   if (exp.X_op != O_absent)
     {
 
   if (exp.X_op != O_absent)
     {
+      symbolS *esym = NULL;
+
       if (exp.X_op != O_constant && exp.X_op != O_symbol)
        {
          /* Handle complex expressions.  */
       if (exp.X_op != O_constant && exp.X_op != O_symbol)
        {
          /* Handle complex expressions.  */
-         sym = make_expr_symbol (&exp);
+         esym = sym = make_expr_symbol (&exp);
          off = 0;
        }
       else
        {
          sym = exp.X_add_symbol;
          off = exp.X_add_number;
          off = 0;
        }
       else
        {
          sym = exp.X_add_symbol;
          off = exp.X_add_number;
+
+         if (section == undefined_section)
+           {
+             /* We need an expr_symbol when tracking sections.  In
+                order to make this an expr_symbol with file and line
+                tracked, we have to make the exp non-trivial; not an
+                O_symbol with .X_add_number == 0.  The constant part
+                is unused.  */
+             exp.X_add_number = 1;
+             esym = make_expr_symbol (&exp);
+           }
+       }
+
+      /* Track the LOC's where we couldn't deduce the section: assert
+        that we weren't supposed to change section.  */
+      if (section == undefined_section)
+       {
+         struct loc_assert_s *next = loc_asserts;
+         loc_asserts = XNEW (struct loc_assert_s);
+         loc_asserts->next = next;
+         loc_asserts->old_seg = now_seg;
+         loc_asserts->loc_sym = esym;
+         loc_asserts->frag = frag_now;
        }
 
       p = frag_var (rs_org, 1, 1, (relax_substateT) 0, sym, off, (char *) 0);
        }
 
       p = frag_var (rs_org, 1, 1, (relax_substateT) 0, sym, off, (char *) 0);
@@ -3965,10 +4107,9 @@ s_loc (ignore)
    by comma.  */
 
 static void
    by comma.  */
 
 static void
-mmix_byte ()
+mmix_byte (void)
 {
   unsigned int c;
 {
   unsigned int c;
-  char *start;
 
   if (now_seg == text_section)
     text_has_contents = 1;
 
   if (now_seg == text_section)
     text_has_contents = 1;
@@ -3982,7 +4123,6 @@ mmix_byte ()
        {
        case '\"':
          ++input_line_pointer;
        {
        case '\"':
          ++input_line_pointer;
-         start = input_line_pointer;
          while (is_a_char (c = next_char_of_string ()))
            {
              FRAG_APPEND_1_CHAR (c);
          while (is_a_char (c = next_char_of_string ()))
            {
              FRAG_APPEND_1_CHAR (c);
@@ -4055,11 +4195,9 @@ mmix_byte ()
    lenient than mmix_byte but FIXME: they should eventually merge.  */
 
 static void
    lenient than mmix_byte but FIXME: they should eventually merge.  */
 
 static void
-mmix_cons (nbytes)
-     int nbytes;
+mmix_cons (int nbytes)
 {
   expressionS exp;
 {
   expressionS exp;
-  char *start;
 
   /* If we don't have any contents, then it's ok to have a specified start
      address that is not a multiple of the max data size.  We will then
 
   /* If we don't have any contents, then it's ok to have a specified start
      address that is not a multiple of the max data size.  We will then
@@ -4118,7 +4256,7 @@ mmix_cons (nbytes)
 
   SKIP_WHITESPACE ();
 
 
   SKIP_WHITESPACE ();
 
-  if (is_end_of_line [(unsigned int) *input_line_pointer])
+  if (is_end_of_line[(unsigned int) *input_line_pointer])
     {
       /* Default to zero if the expression was absent.  */
 
     {
       /* Default to zero if the expression was absent.  */
 
@@ -4140,7 +4278,6 @@ mmix_cons (nbytes)
               bytes.  */
          case '\"':
            ++input_line_pointer;
               bytes.  */
          case '\"':
            ++input_line_pointer;
-           start = input_line_pointer;
            while (is_a_char (c = next_char_of_string ()))
              {
                exp.X_op = O_constant;
            while (is_a_char (c = next_char_of_string ()))
              {
                exp.X_op = O_constant;
@@ -4185,11 +4322,8 @@ mmix_cons (nbytes)
    Arguably this is a GCC bug.  */
 
 void
    Arguably this is a GCC bug.  */
 
 void
-mmix_md_do_align (n, fill, len, max)
-     int n;
-     char *fill ATTRIBUTE_UNUSED;
-     int len ATTRIBUTE_UNUSED;
-     int max ATTRIBUTE_UNUSED;
+mmix_md_do_align (int n, char *fill ATTRIBUTE_UNUSED,
+                 int len ATTRIBUTE_UNUSED, int max ATTRIBUTE_UNUSED)
 {
   last_alignment = n;
   want_unaligned = n == 0;
 {
   last_alignment = n;
   want_unaligned = n == 0;
This page took 0.053715 seconds and 4 git commands to generate.