MIPS/GAS: Don't convert RELA JALR relocations on R6
[deliverable/binutils-gdb.git] / gas / config / tc-s390.c
index 4f012778f512757f878ddd7bef5e6bde7a24225d..098debc8d2d88baab1715c3138b958f78dfec18c 100644 (file)
@@ -1,12 +1,12 @@
 /* tc-s390.c -- Assemble for the S390
 /* tc-s390.c -- Assemble for the S390
-   Copyright 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 2000-2016 Free Software Foundation, Inc.
    Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
 
    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
    Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
 
    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,
 
    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
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to the Free
-   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 
 
-#include <stdio.h>
-#include <ctype.h>
 #include "as.h"
 #include "as.h"
+#include "safe-ctype.h"
 #include "subsegs.h"
 #include "struc-symbol.h"
 #include "subsegs.h"
 #include "struc-symbol.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
 
 #include "opcode/s390.h"
 #include "elf/s390.h"
 
 
 #include "opcode/s390.h"
 #include "elf/s390.h"
 
-/* The default architecture */
+/* The default architecture */
 #ifndef DEFAULT_ARCH
 #define DEFAULT_ARCH "s390"
 #endif
 #ifndef DEFAULT_ARCH
 #define DEFAULT_ARCH "s390"
 #endif
-static char *default_arch = DEFAULT_ARCH;
+static const char *default_arch = DEFAULT_ARCH;
 /* Either 32 or 64, selects file format.  */
 /* Either 32 or 64, selects file format.  */
-static int s390_arch_size;
-/* Current architecture. Start with the smallest instruction set */
-static enum s390_opcode_arch_val current_architecture = S390_OPCODE_ESA;
-static int current_arch_mask = 1 << S390_OPCODE_ESA;
-static int current_arch_requested = 0;
+static int s390_arch_size = 0;
 
 
-/* Whether to use user friendly register names. Default is true. */
+/* If no -march option was given default to the highest available CPU.
+   Since with S/390 a newer CPU always supports everything from its
+   predecessors this will accept every valid asm input.  */
+static unsigned int current_cpu = S390_OPCODE_MAXCPU - 1;
+static unsigned int current_mode_mask = 0;
+static unsigned int current_flags = 0;
+
+/* Set to TRUE if the highgprs flag in the ELF header needs to be set
+   for the output file.  */
+static bfd_boolean set_highgprs_p = FALSE;
+
+/* Whether to use user friendly register names. Default is TRUE.  */
 #ifndef TARGET_REG_NAMES_P
 #ifndef TARGET_REG_NAMES_P
-#define TARGET_REG_NAMES_P true
+#define TARGET_REG_NAMES_P TRUE
 #endif
 
 #endif
 
-static boolean reg_names_p = TARGET_REG_NAMES_P;
+static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
+
+/* Set to TRUE if we want to warn about zero base/index registers.  */
+static bfd_boolean warn_areg_zero = FALSE;
 
 /* Generic assembler global variables which must be defined by all
    targets.  */
 
 /* Generic assembler global variables which must be defined by all
    targets.  */
@@ -67,166 +78,67 @@ const char EXP_CHARS[] = "eE";
    as in 0d1.0.  */
 const char FLT_CHARS[] = "dD";
 
    as in 0d1.0.  */
 const char FLT_CHARS[] = "dD";
 
+/* The dwarf2 data alignment, adjusted for 32 or 64 bit.  */
+int s390_cie_data_alignment;
+
 /* The target specific pseudo-ops which we support.  */
 
 /* Define the prototypes for the pseudo-ops */
 /* The target specific pseudo-ops which we support.  */
 
 /* Define the prototypes for the pseudo-ops */
-static void s390_byte PARAMS ((int));
-static void s390_elf_cons PARAMS ((int));
-static void s390_bss PARAMS ((int));
-static void s390_insn PARAMS ((int));
-static void s390_literals PARAMS ((int));
+static void s390_byte (int);
+static void s390_elf_cons (int);
+static void s390_bss (int);
+static void s390_insn (int);
+static void s390_literals (int);
+static void s390_machine (int);
+static void s390_machinemode (int);
 
 const pseudo_typeS md_pseudo_table[] =
 {
 
 const pseudo_typeS md_pseudo_table[] =
 {
-  { "align", s_align_bytes, 0 },
-  /* Pseudo-ops which must be defined. */
-  { "bss",      s390_bss,       0 }, 
-  { "insn",     s390_insn,      0 },
+  { "align",        s_align_bytes,      0 },
+  /* Pseudo-ops which must be defined.  */
+  { "bss",          s390_bss,           0 },
+  { "insn",         s390_insn,          0 },
   /* Pseudo-ops which must be overridden.  */
   /* Pseudo-ops which must be overridden.  */
-  { "byte",    s390_byte,      0 },
-  { "short",    s390_elf_cons,  2 },
-  { "long",    s390_elf_cons,  4 },
-  { "quad",     s390_elf_cons,  8 },
-  { "ltorg",    s390_literals,  0 },
-  { "string",   stringer,       2 },
-  { NULL,      NULL,           0 }
-};
-
-
-/* Structure to hold information about predefined registers.  */
-struct pd_reg
-  {
-    char *name;
-    int value;
-  };
-
-/* List of registers that are pre-defined:
-
-   Each access register has a predefined name of the form:
-     a<reg_num> which has the value <reg_num>.
-
-   Each control register has a predefined name of the form:
-     c<reg_num> which has the value <reg_num>.
-
-   Each general register has a predefined name of the form:
-     r<reg_num> which has the value <reg_num>.
-
-   Each floating point register a has predefined name of the form:
-     f<reg_num> which has the value <reg_num>.
-
-   There are individual registers as well:
-     sp     has the value 15
-     lit    has the value 12
-
-   The table is sorted. Suitable for searching by a binary search. */
-
-static const struct pd_reg pre_defined_registers[] =
-{
-  { "a0", 0 },     /* Access registers */
-  { "a1", 1 },
-  { "a10", 10 },
-  { "a11", 11 },
-  { "a12", 12 },
-  { "a13", 13 },
-  { "a14", 14 },
-  { "a15", 15 },
-  { "a2", 2 },
-  { "a3", 3 },
-  { "a4", 4 },
-  { "a5", 5 },
-  { "a6", 6 },
-  { "a7", 7 },
-  { "a8", 8 },
-  { "a9", 9 },
-
-  { "c0", 0 },     /* Control registers */
-  { "c1", 1 },
-  { "c10", 10 },
-  { "c11", 11 },
-  { "c12", 12 },
-  { "c13", 13 },
-  { "c14", 14 },
-  { "c15", 15 },
-  { "c2", 2 },
-  { "c3", 3 },
-  { "c4", 4 },
-  { "c5", 5 },
-  { "c6", 6 },
-  { "c7", 7 },
-  { "c8", 8 },
-  { "c9", 9 },
-
-  { "f0", 0 },     /* Floating point registers */
-  { "f1", 1 }, 
-  { "f10", 10 }, 
-  { "f11", 11 }, 
-  { "f12", 12 }, 
-  { "f13", 13 }, 
-  { "f14", 14 }, 
-  { "f15", 15 }, 
-  { "f2", 2 }, 
-  { "f3", 3 }, 
-  { "f4", 4 }, 
-  { "f5", 5 }, 
-  { "f6", 6 }, 
-  { "f7", 7 }, 
-  { "f8", 8 }, 
-  { "f9", 9 }, 
-
-  { "lit", 13 },   /* Pointer to literal pool */
-
-  { "r0", 0 },     /* General purpose registers */
-  { "r1", 1 },
-  { "r10", 10 },
-  { "r11", 11 },
-  { "r12", 12 },
-  { "r13", 13 },
-  { "r14", 14 },
-  { "r15", 15 },
-  { "r2", 2 },
-  { "r3", 3 },
-  { "r4", 4 },
-  { "r5", 5 },
-  { "r6", 6 },
-  { "r7", 7 },
-  { "r8", 8 },
-  { "r9", 9 },
-
-  { "sp", 15 },   /* Stack pointer */
-
+  { "byte",        s390_byte,          0 },
+  { "short",        s390_elf_cons,      2 },
+  { "long",        s390_elf_cons,      4 },
+  { "quad",         s390_elf_cons,      8 },
+  { "ltorg",        s390_literals,      0 },
+  { "string",       stringer,           8 + 1 },
+  { "machine",      s390_machine,       0 },
+  { "machinemode",  s390_machinemode,   0 },
+  { NULL,          NULL,               0 }
 };
 
 };
 
-#define REG_NAME_CNT (sizeof(pre_defined_registers) / sizeof(struct pd_reg))
-
 /* Given NAME, find the register number associated with that name, return
    the integer value associated with the given name or -1 on failure.  */
 
 static int
 /* Given NAME, find the register number associated with that name, return
    the integer value associated with the given name or -1 on failure.  */
 
 static int
-reg_name_search (regs, regcount, name)
-     const struct pd_reg *regs;
-     int regcount;
-     const char *name;
+reg_name_search (const char *name)
 {
 {
-  int middle, low, high;
-  int cmp;
+  int val = -1;
 
 
-  low = 0;
-  high = regcount - 1;
+  if (strcasecmp (name, "lit") == 0)
+    return 13;
 
 
-  do
+  if (strcasecmp (name, "sp") == 0)
+    return 15;
+
+  if (name[0] != 'a' && name[0] != 'c' && name[0] != 'f'
+      && name[0] != 'r' && name[0] != 'v')
+    return -1;
+
+  if (ISDIGIT (name[1]))
     {
     {
-      middle = (low + high) / 2;
-      cmp = strcasecmp (name, regs[middle].name);
-      if (cmp < 0)
-       high = middle - 1;
-      else if (cmp > 0)
-       low = middle + 1;
-      else
-       return regs[middle].value;
+      val = name[1] - '0';
+      if (ISDIGIT (name[2]))
+       val = val * 10 + name[2] - '0';
     }
     }
-  while (low <= high);
 
 
-  return -1;
+  if ((name[0] != 'v' && val > 15) || val > 31)
+    val = -1;
+
+  return val;
 }
 
 
 }
 
 
@@ -242,44 +154,42 @@ reg_name_search (regs, regcount, name)
  *      original state.
  */
 
  *      original state.
  */
 
-static boolean
-register_name (expressionP)
-     expressionS *expressionP;
+static bfd_boolean
+register_name (expressionS *expressionP)
 {
   int reg_number;
   char *name;
   char *start;
   char c;
 
 {
   int reg_number;
   char *name;
   char *start;
   char c;
 
-  /* Find the spelling of the operand */
+  /* Find the spelling of the operand */
   start = name = input_line_pointer;
   start = name = input_line_pointer;
-  if (name[0] == '%' && isalpha (name[1]))
+  if (name[0] == '%' && ISALPHA (name[1]))
     name = ++input_line_pointer;
   else
     name = ++input_line_pointer;
   else
-    return false;
+    return FALSE;
+
+  c = get_symbol_name (&name);
+  reg_number = reg_name_search (name);
 
 
-  c = get_symbol_end ();
-  reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
+  /* Put back the delimiting char.  */
+  (void) restore_line_pointer (c);
 
 
-  /* look to see if it's in the register table */
-  if (reg_number >= 0) 
+  /* Look to see if it's in the register table.  */
+  if (reg_number >= 0)
     {
       expressionP->X_op = O_register;
       expressionP->X_add_number = reg_number;
     {
       expressionP->X_op = O_register;
       expressionP->X_add_number = reg_number;
-      
-      /* make the rest nice */
+
+      /* Make the rest nice.  */
       expressionP->X_add_symbol = NULL;
       expressionP->X_op_symbol = NULL;
       expressionP->X_add_symbol = NULL;
       expressionP->X_op_symbol = NULL;
-      *input_line_pointer = c;   /* put back the delimiting char */
-      return true;
-    }
-  else
-    {
-      /* reset the line as if we had not done anything */
-      *input_line_pointer = c;   /* put back the delimiting char */
-      input_line_pointer = start; /* reset input_line pointer */
-      return false;
+      return TRUE;
     }
     }
+
+  /* Reset the line as if we had not done anything.  */
+  input_line_pointer = start;
+  return FALSE;
 }
 
 /* Local variables.  */
 }
 
 /* Local variables.  */
@@ -288,7 +198,7 @@ register_name (expressionP)
 static struct hash_control *s390_opformat_hash;
 
 /* Opcode hash table.  */
 static struct hash_control *s390_opformat_hash;
 
 /* Opcode hash table.  */
-static struct hash_control *s390_opcode_hash;
+static struct hash_control *s390_opcode_hash = NULL;
 
 /* Flags to set in the elf header */
 static flagword s390_flags = 0;
 
 /* Flags to set in the elf header */
 static flagword s390_flags = 0;
@@ -296,55 +206,169 @@ static flagword s390_flags = 0;
 symbolS *GOT_symbol;           /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
 
 #ifndef WORKING_DOT_WORD
 symbolS *GOT_symbol;           /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
 
 #ifndef WORKING_DOT_WORD
-const int md_short_jump_size = 4;
-const int md_long_jump_size = 4;
+int md_short_jump_size = 4;
+int md_long_jump_size = 4;
 #endif
 
 #endif
 
-CONST char *md_shortopts = "A:m:kVQ:";
+const char *md_shortopts = "A:m:kVQ:";
 struct option md_longopts[] = {
   {NULL, no_argument, NULL, 0}
 };
 struct option md_longopts[] = {
   {NULL, no_argument, NULL, 0}
 };
-size_t md_longopts_size = sizeof(md_longopts);
+size_t md_longopts_size = sizeof (md_longopts);
 
 /* Initialize the default opcode arch and word size from the default
 
 /* Initialize the default opcode arch and word size from the default
-   architecture name.  */
+   architecture name if not specified by an option.  */
 static void
 static void
-init_default_arch ()
+init_default_arch (void)
 {
 {
-  if (current_arch_requested)
-    return;
-
-  if (strcmp(default_arch, "s390") == 0)
+  if (strcmp (default_arch, "s390") == 0)
     {
     {
-      s390_arch_size = 32;
-      current_architecture = S390_OPCODE_ESA;
+      if (s390_arch_size == 0)
+       s390_arch_size = 32;
     }
     }
-  else if (strcmp(default_arch, "s390x") == 0)
+  else if (strcmp (default_arch, "s390x") == 0)
     {
     {
-      s390_arch_size = 64;
-      current_architecture = S390_OPCODE_ESAME;
+      if (s390_arch_size == 0)
+       s390_arch_size = 64;
     }
   else
     }
   else
-    as_fatal ("Invalid default architecture, broken assembler.");
-  current_arch_mask = 1 << current_architecture;
+    as_fatal (_("Invalid default architecture, broken assembler."));
+
+  if (current_mode_mask == 0)
+    {
+      /* Default to z/Architecture mode if the CPU supports it.  */
+      if (current_cpu < S390_OPCODE_Z900)
+       current_mode_mask = 1 << S390_OPCODE_ESA;
+      else
+       current_mode_mask = 1 << S390_OPCODE_ZARCH;
+    }
 }
 
 /* Called by TARGET_FORMAT.  */
 const char *
 }
 
 /* Called by TARGET_FORMAT.  */
 const char *
-s390_target_format ()
+s390_target_format (void)
 {
   /* We don't get a chance to initialize anything before we're called,
      so handle that now.  */
 {
   /* We don't get a chance to initialize anything before we're called,
      so handle that now.  */
-  if (! s390_arch_size)
-    init_default_arch ();
+  init_default_arch ();
 
   return s390_arch_size == 64 ? "elf64-s390" : "elf32-s390";
 }
 
 
   return s390_arch_size == 64 ? "elf64-s390" : "elf32-s390";
 }
 
+/* Map a cpu string ARG as given with -march= or .machine to the respective
+   enum s390_opcode_cpu_val value.  If ALLOW_EXTENSIONS is TRUE, the cpu name
+   can be followed by a list of cpu facility flags each beginning with the
+   character '+'.  The active cpu flags are returned through *RET_FLAGS.
+   In case of an error, S390_OPCODE_MAXCPU is returned.  */
+
+static unsigned int
+s390_parse_cpu (const char *         arg,
+               unsigned int * ret_flags,
+               bfd_boolean    allow_extensions)
+{
+  static struct
+  {
+    const char * name;
+    unsigned int len;
+    unsigned int flags;
+  } cpu_table[S390_OPCODE_MAXCPU] =
+  {
+    { STRING_COMMA_LEN ("g5"), 0 },
+    { STRING_COMMA_LEN ("g6"), 0 },
+    { STRING_COMMA_LEN ("z900"), 0 },
+    { STRING_COMMA_LEN ("z990"), 0 },
+    { STRING_COMMA_LEN ("z9-109"), 0 },
+    { STRING_COMMA_LEN ("z9-ec"), 0 },
+    { STRING_COMMA_LEN ("z10"), 0 },
+    { STRING_COMMA_LEN ("z196"), 0 },
+    { STRING_COMMA_LEN ("zEC12"), S390_INSTR_FLAG_HTM },
+    { STRING_COMMA_LEN ("z13"), S390_INSTR_FLAG_HTM | S390_INSTR_FLAG_VX }
+  };
+  static struct
+  {
+    const char * name;
+    unsigned int mask;
+    bfd_boolean  on;
+  } cpu_flags[] =
+  {
+    { "htm",   S390_INSTR_FLAG_HTM, TRUE },
+    { "nohtm", S390_INSTR_FLAG_HTM, FALSE },
+    { "vx",    S390_INSTR_FLAG_VX, TRUE },
+    { "novx",  S390_INSTR_FLAG_VX, FALSE }
+  };
+  unsigned int icpu;
+  char *ilp_bak;
+
+  icpu = S390_OPCODE_MAXCPU;
+  if (strncmp (arg, "all", 3) == 0 && (arg[3] == 0 || arg[3] == '+'))
+    {
+      icpu = S390_OPCODE_MAXCPU - 1;
+      arg += 3;
+    }
+  else
+    {
+      for (icpu = 0; icpu < S390_OPCODE_MAXCPU; icpu++)
+       {
+         unsigned int l;
+
+         l = cpu_table[icpu].len;
+         if (strncmp (arg, cpu_table[icpu].name, l) == 0
+             && (arg[l] == 0 || arg[l] == '+'))
+           {
+             arg += l;
+             break;
+           }
+       }
+    }
+
+  ilp_bak = input_line_pointer;
+  if (icpu != S390_OPCODE_MAXCPU)
+    {
+      input_line_pointer = (char *) arg;
+      *ret_flags = (cpu_table[icpu].flags & S390_INSTR_FLAG_FACILITY_MASK);
+
+      while (*input_line_pointer == '+' && allow_extensions)
+       {
+         unsigned int iflag;
+         char *sym;
+         char c;
+
+         input_line_pointer++;
+         c = get_symbol_name (&sym);
+         for (iflag = 0; iflag < ARRAY_SIZE (cpu_flags); iflag++)
+           {
+             if (strcmp (sym, cpu_flags[iflag].name) == 0)
+               {
+                 if (cpu_flags[iflag].on)
+                   *ret_flags |= cpu_flags[iflag].mask;
+                 else
+                   *ret_flags &= ~cpu_flags[iflag].mask;
+                 break;
+               }
+           }
+         if (iflag == ARRAY_SIZE (cpu_flags))
+           as_bad (_("no such machine extension `%s'"), sym - 1);
+         *input_line_pointer = c;
+         if (iflag == ARRAY_SIZE (cpu_flags))
+           break;
+       }
+    }
+
+  SKIP_WHITESPACE ();
+
+  if (*input_line_pointer != 0 && *input_line_pointer != '\n')
+    {
+      as_bad (_("junk at end of machine string, first unrecognized character"
+               " is `%c'"), *input_line_pointer);
+      icpu = S390_OPCODE_MAXCPU;
+    }
+  input_line_pointer = ilp_bak;
+
+  return icpu;
+}
+
 int
 int
-md_parse_option (c, arg)
-     int c;
-     char *arg;
+md_parse_option (int c, const char *arg)
 {
   switch (c)
     {
 {
   switch (c)
     {
@@ -353,78 +377,173 @@ md_parse_option (c, arg)
       break;
     case 'm':
       if (arg != NULL && strcmp (arg, "regnames") == 0)
       break;
     case 'm':
       if (arg != NULL && strcmp (arg, "regnames") == 0)
-       reg_names_p = true;
-    
+       reg_names_p = TRUE;
+
       else if (arg != NULL && strcmp (arg, "no-regnames") == 0)
       else if (arg != NULL && strcmp (arg, "no-regnames") == 0)
-       reg_names_p = false;
-    
+       reg_names_p = FALSE;
+
+      else if (arg != NULL && strcmp (arg, "warn-areg-zero") == 0)
+       warn_areg_zero = TRUE;
+
+      else if (arg != NULL && strcmp (arg, "31") == 0)
+       s390_arch_size = 32;
+
+      else if (arg != NULL && strcmp (arg, "64") == 0)
+       s390_arch_size = 64;
+
+      else if (arg != NULL && strcmp (arg, "esa") == 0)
+       current_mode_mask = 1 << S390_OPCODE_ESA;
+
+      else if (arg != NULL && strcmp (arg, "zarch") == 0)
+       {
+         if (s390_arch_size == 32)
+           set_highgprs_p = TRUE;
+         current_mode_mask = 1 << S390_OPCODE_ZARCH;
+       }
+
+      else if (arg != NULL && strncmp (arg, "arch=", 5) == 0)
+       {
+         current_cpu = s390_parse_cpu (arg + 5, &current_flags, FALSE);
+         if (current_cpu == S390_OPCODE_MAXCPU)
+           {
+             as_bad (_("invalid switch -m%s"), arg);
+             return 0;
+           }
+       }
+
       else
        {
          as_bad (_("invalid switch -m%s"), arg);
          return 0;
        }
       break;
       else
        {
          as_bad (_("invalid switch -m%s"), arg);
          return 0;
        }
       break;
-    
+
     case 'A':
     case 'A':
+      /* Option -A is deprecated. Still available for compatibility.  */
       if (arg != NULL && strcmp (arg, "esa") == 0)
       if (arg != NULL && strcmp (arg, "esa") == 0)
-       {
-         current_architecture = S390_OPCODE_ESA;
-         s390_arch_size = 32;
-       }
+       current_cpu = S390_OPCODE_G5;
       else if (arg != NULL && strcmp (arg, "esame") == 0)
       else if (arg != NULL && strcmp (arg, "esame") == 0)
-       {
-         current_architecture = S390_OPCODE_ESAME;
-         s390_arch_size = 64;
-       }
+       current_cpu = S390_OPCODE_Z900;
       else
       else
-       as_bad ("invalid architecture -A%s", arg);
-      current_arch_mask = 1 << current_architecture;
-      current_arch_requested = 1;
+       as_bad (_("invalid architecture -A%s"), arg);
       break;
 
       /* -V: SVR4 argument to print version ID.  */
     case 'V':
       print_version_id ();
       break;
       break;
 
       /* -V: SVR4 argument to print version ID.  */
     case 'V':
       print_version_id ();
       break;
-    
+
       /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
         should be emitted or not.  FIXME: Not implemented.  */
     case 'Q':
       break;
       /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
         should be emitted or not.  FIXME: Not implemented.  */
     case 'Q':
       break;
-      
+
     default:
       return 0;
     }
     default:
       return 0;
     }
-  
+
   return 1;
 }
 
 void
   return 1;
 }
 
 void
-md_show_usage (stream)
-     FILE *stream;
+md_show_usage (FILE *stream)
 {
   fprintf (stream, _("\
 {
   fprintf (stream, _("\
-          S390 options:\n\
-          -mregnames   \tAllow symbolic names for registers\n\
-          -mno-regnames\tDo not allow symbolic names for registers\n"));
+        S390 options:\n\
+        -mregnames        Allow symbolic names for registers\n\
+        -mwarn-areg-zero  Warn about zero base/index registers\n\
+        -mno-regnames     Do not allow symbolic names for registers\n\
+        -m31              Set file format to 31 bit format\n\
+        -m64              Set file format to 64 bit format\n"));
   fprintf (stream, _("\
   fprintf (stream, _("\
-          -V       \tprint assembler version number\n\
-          -Qy, -Qn \tignored\n"));
+        -V                print assembler version number\n\
+        -Qy, -Qn          ignored\n"));
+}
+
+/* Generate the hash table mapping mnemonics to struct s390_opcode.
+   This table is built at startup and whenever the CPU level is
+   changed using .machine.  */
+
+static void
+s390_setup_opcodes (void)
+{
+  const struct s390_opcode *op;
+  const struct s390_opcode *op_end;
+  bfd_boolean dup_insn = FALSE;
+  const char *retval;
+
+  if (s390_opcode_hash != NULL)
+    hash_die (s390_opcode_hash);
+
+  /* Insert the opcodes into a hash table.  */
+  s390_opcode_hash = hash_new ();
+
+  op_end = s390_opcodes + s390_num_opcodes;
+  for (op = s390_opcodes; op < op_end; op++)
+    {
+      int use_opcode;
+
+      while (op < op_end - 1 && strcmp(op->name, op[1].name) == 0)
+       {
+          if (op->min_cpu <= current_cpu && (op->modes & current_mode_mask))
+           break;
+         op++;
+        }
+
+      if ((op->modes & current_mode_mask) == 0)
+       use_opcode = 0;
+      else if ((op->flags & S390_INSTR_FLAG_FACILITY_MASK) == 0)
+       {
+         /* Opcodes that do not belong to a specific facility are enabled if
+            present in the selected cpu.  */
+         use_opcode = (op->min_cpu <= current_cpu);
+       }
+      else
+       {
+         unsigned int f;
+
+         /* Opcodes of a specific facility are enabled if the facility is
+            enabled.  Note: only some facilities are represented as flags.  */
+         f = (op->flags & S390_INSTR_FLAG_FACILITY_MASK);
+         use_opcode = ((f & current_flags) == f);
+       }
+      if (use_opcode)
+       {
+         retval = hash_insert (s390_opcode_hash, op->name, (void *) op);
+         if (retval != (const char *) NULL)
+           {
+             as_bad (_("Internal assembler error for instruction %s"),
+                     op->name);
+             dup_insn = TRUE;
+           }
+       }
+
+      while (op < op_end - 1 && strcmp (op->name, op[1].name) == 0)
+       op++;
+    }
+
+  if (dup_insn)
+    abort ();
 }
 
 /* This function is called when the assembler starts up.  It is called
    after the options have been parsed and the output file has been
 }
 
 /* This function is called when the assembler starts up.  It is called
    after the options have been parsed and the output file has been
-   opened. */
+   opened.  */
 
 void
 
 void
-md_begin ()
+md_begin (void)
 {
 {
-  register const struct s390_opcode *op;
+  const struct s390_opcode *op;
   const struct s390_opcode *op_end;
   const struct s390_opcode *op_end;
-  boolean dup_insn = false;
   const char *retval;
 
   const char *retval;
 
-  /* Set the ELF flags if desired. */
+  /* Give a warning if the combination -m64-bit and -Aesa is used.  */
+  if (s390_arch_size == 64 && current_cpu < S390_OPCODE_Z900)
+    as_warn (_("The 64 bit file format is used without esame instructions."));
+
+  s390_cie_data_alignment = -s390_arch_size / 8;
+
+  /* Set the ELF flags if desired.  */
   if (s390_flags)
     bfd_set_private_flags (stdoutput, s390_flags);
 
   if (s390_flags)
     bfd_set_private_flags (stdoutput, s390_flags);
 
@@ -434,70 +553,37 @@ md_begin ()
   op_end = s390_opformats + s390_num_opformats;
   for (op = s390_opformats; op < op_end; op++)
     {
   op_end = s390_opformats + s390_num_opformats;
   for (op = s390_opformats; op < op_end; op++)
     {
-      retval = hash_insert (s390_opformat_hash, op->name, (PTR) op);
+      retval = hash_insert (s390_opformat_hash, op->name, (void *) op);
       if (retval != (const char *) NULL)
       if (retval != (const char *) NULL)
-       {
-         as_bad (_("Internal assembler error for instruction format %s"),
-                 op->name);
-         dup_insn = true;
-       }
+       as_bad (_("Internal assembler error for instruction format %s"),
+               op->name);
     }
 
     }
 
-  /* Insert the opcodes into a hash table.  */
-  s390_opcode_hash = hash_new ();
-
-  op_end = s390_opcodes + s390_num_opcodes;
-  for (op = s390_opcodes; op < op_end; op++)
-    {
-      retval = hash_insert (s390_opcode_hash, op->name, (PTR) op);
-      if (retval != (const char *) NULL)
-       {
-         as_bad (_("Internal assembler error for instruction %s"), op->name);
-         dup_insn = true;
-       }
-    }
-
-  if (dup_insn)
-    abort ();
+  s390_setup_opcodes ();
 
   record_alignment (text_section, 2);
   record_alignment (data_section, 2);
   record_alignment (bss_section, 2);
 
   record_alignment (text_section, 2);
   record_alignment (data_section, 2);
   record_alignment (bss_section, 2);
-
 }
 
 /* Called after all assembly has been done.  */
 void
 }
 
 /* Called after all assembly has been done.  */
 void
-s390_md_end ()
+s390_md_end (void)
 {
   if (s390_arch_size == 64)
 {
   if (s390_arch_size == 64)
-    bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_esame);
+    bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_64);
   else
   else
-    bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_esa);
-}
-
-void
-s390_align_code (fragP, count)
-     fragS *fragP;
-     int count;
-{
-  /* We use nop pattern 0x0707.  */
-  if (count > 0)
-    {
-      memset(fragP->fr_literal + fragP->fr_fix, 0x07, count);
-      fragP->fr_var = count;
-    }
+    bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_31);
 }
 
 /* Insert an operand value into an instruction.  */
 
 static void
 }
 
 /* Insert an operand value into an instruction.  */
 
 static void
-s390_insert_operand (insn, operand, val, file, line)
-     unsigned char *insn;
-     const struct s390_operand *operand;
-     offsetT val;
-     char *file;
-     unsigned int line;
+s390_insert_operand (unsigned char *insn,
+                    const struct s390_operand *operand,
+                    offsetT val,
+                    const char *file,
+                    unsigned int line)
 {
   addressT uval;
   int offset;
 {
   addressT uval;
   int offset;
@@ -515,7 +601,7 @@ s390_insert_operand (insn, operand, val, file, line)
       if (val < min || val > max)
        {
          const char *err =
       if (val < min || val > max)
        {
          const char *err =
-           "operand out of range (%s not between %ld and %ld)";
+           _("operand out of range (%s not between %ld and %ld)");
          char buf[100];
 
          if (operand->flags & S390_OPERAND_PCREL)
          char buf[100];
 
          if (operand->flags & S390_OPERAND_PCREL)
@@ -533,43 +619,83 @@ s390_insert_operand (insn, operand, val, file, line)
        }
       /* val is ok, now restrict it to operand->bits bits.  */
       uval = (addressT) val & ((((addressT) 1 << (operand->bits-1)) << 1) - 1);
        }
       /* val is ok, now restrict it to operand->bits bits.  */
       uval = (addressT) val & ((((addressT) 1 << (operand->bits-1)) << 1) - 1);
+      /* val is restrict, now check for special case.  */
+      if (operand->bits == 20 && operand->shift == 20)
+        uval = (uval >> 12) | ((uval & 0xfff) << 8);
     }
   else
     {
       addressT min, max;
     }
   else
     {
       addressT min, max;
-      
-      max = (((addressT) 1 << (operand->bits - 1))<<1) - 1;
+
+      max = (((addressT) 1 << (operand->bits - 1)) << 1) - 1;
       min = (offsetT) 0;
       uval = (addressT) val;
       min = (offsetT) 0;
       uval = (addressT) val;
+
+      /* Vector register operands have an additional bit in the RXB
+        field.  */
+      if (operand->flags & S390_OPERAND_VR)
+       max = (max << 1) | 1;
+
       /* Length x in an instructions has real length x+1.  */
       if (operand->flags & S390_OPERAND_LENGTH)
        uval--;
       /* Check for underflow / overflow.  */
       if (uval < min || uval > max)
        {
       /* Length x in an instructions has real length x+1.  */
       if (operand->flags & S390_OPERAND_LENGTH)
        uval--;
       /* Check for underflow / overflow.  */
       if (uval < min || uval > max)
        {
-         const char *err =
-           "operand out of range (%s not between %ld and %ld)";
-         char buf[100];
-    
          if (operand->flags & S390_OPERAND_LENGTH)
            {
              uval++;
              min++;
              max++;
            }
          if (operand->flags & S390_OPERAND_LENGTH)
            {
              uval++;
              min++;
              max++;
            }
-         sprint_value (buf, uval);
-         if (file == (char *) NULL)
-           as_bad (err, buf, (int) min, (int) max);
-         else
-           as_bad_where (file, line, err, buf, (int) min, (int) max);
+
+         as_bad_value_out_of_range (_("operand"), uval, (offsetT) min, (offsetT) max, file, line);
+
          return;
        }
     }
 
          return;
        }
     }
 
+  if (operand->flags & S390_OPERAND_VR)
+    {
+      /* Insert the extra bit into the RXB field.  */
+      switch (operand->shift)
+       {
+       case 8:
+         insn[4] |= (uval & 0x10) >> 1;
+         break;
+       case 12:
+         insn[4] |= (uval & 0x10) >> 2;
+         break;
+       case 16:
+         insn[4] |= (uval & 0x10) >> 3;
+         break;
+       case 32:
+         insn[4] |= (uval & 0x10) >> 4;
+         break;
+       }
+      uval &= 0xf;
+    }
+
+  if (operand->flags & S390_OPERAND_OR1)
+    uval |= 1;
+  if (operand->flags & S390_OPERAND_OR2)
+    uval |= 2;
+  if (operand->flags & S390_OPERAND_OR8)
+    uval |= 8;
+
+  /* Duplicate the operand at bit pos 12 to 16.  */
+  if (operand->flags & S390_OPERAND_CP16)
+    {
+      /* Copy VR operand at bit pos 12 to bit pos 16.  */
+      insn[2] |= uval << 4;
+      /* Copy the flag in the RXB field.  */
+      insn[4] |= (insn[4] & 4) >> 1;
+    }
+
   /* Insert fragments of the operand byte for byte.  */
   offset = operand->shift + operand->bits;
   uval <<= (-offset) & 7;
   /* Insert fragments of the operand byte for byte.  */
   offset = operand->shift + operand->bits;
   uval <<= (-offset) & 7;
-  insn += (offset - 1)/8;
+  insn += (offset - 1) / 8;
   while (uval != 0)
     {
       *insn-- |= uval;
   while (uval != 0)
     {
       *insn-- |= uval;
@@ -577,28 +703,92 @@ s390_insert_operand (insn, operand, val, file, line)
     }
 }
 
     }
 }
 
+struct map_tls
+  {
+    const char *string;
+    int length;
+    bfd_reloc_code_real_type reloc;
+  };
+
+/* Parse tls marker and return the desired relocation.  */
+static bfd_reloc_code_real_type
+s390_tls_suffix (char **str_p, expressionS *exp_p)
+{
+  static struct map_tls mapping[] =
+  {
+    { "tls_load", 8, BFD_RELOC_390_TLS_LOAD },
+    { "tls_gdcall", 10, BFD_RELOC_390_TLS_GDCALL  },
+    { "tls_ldcall", 10, BFD_RELOC_390_TLS_LDCALL  },
+    { NULL,  0, BFD_RELOC_UNUSED }
+  };
+  struct map_tls *ptr;
+  char *orig_line;
+  char *str;
+  char *ident;
+  int len;
+
+  str = *str_p;
+  if (*str++ != ':')
+    return BFD_RELOC_UNUSED;
+
+  ident = str;
+  while (ISIDNUM (*str))
+    str++;
+  len = str - ident;
+  if (*str++ != ':')
+    return BFD_RELOC_UNUSED;
+
+  orig_line = input_line_pointer;
+  input_line_pointer = str;
+  expression (exp_p);
+  str = input_line_pointer;
+  if (&input_line_pointer != str_p)
+    input_line_pointer = orig_line;
+
+  if (exp_p->X_op != O_symbol)
+    return BFD_RELOC_UNUSED;
+
+  for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+    if (len == ptr->length
+       && strncasecmp (ident, ptr->string, ptr->length) == 0)
+      {
+       /* Found a matching tls suffix.  */
+       *str_p = str;
+       return ptr->reloc;
+      }
+  return BFD_RELOC_UNUSED;
+}
+
 /* Structure used to hold suffixes.  */
 typedef enum
   {
     ELF_SUFFIX_NONE = 0,
     ELF_SUFFIX_GOT,
     ELF_SUFFIX_PLT,
 /* Structure used to hold suffixes.  */
 typedef enum
   {
     ELF_SUFFIX_NONE = 0,
     ELF_SUFFIX_GOT,
     ELF_SUFFIX_PLT,
-    ELF_SUFFIX_GOTENT
+    ELF_SUFFIX_GOTENT,
+    ELF_SUFFIX_GOTOFF,
+    ELF_SUFFIX_GOTPLT,
+    ELF_SUFFIX_PLTOFF,
+    ELF_SUFFIX_TLS_GD,
+    ELF_SUFFIX_TLS_GOTIE,
+    ELF_SUFFIX_TLS_IE,
+    ELF_SUFFIX_TLS_LDM,
+    ELF_SUFFIX_TLS_LDO,
+    ELF_SUFFIX_TLS_LE
   }
 elf_suffix_type;
 
 struct map_bfd
   {
   }
 elf_suffix_type;
 
 struct map_bfd
   {
-    char *string;
+    const char *string;
     int length;
     elf_suffix_type suffix;
   };
 
     int length;
     elf_suffix_type suffix;
   };
 
+
 /* Parse @got/@plt/@gotoff. and return the desired relocation.  */
 static elf_suffix_type
 /* Parse @got/@plt/@gotoff. and return the desired relocation.  */
 static elf_suffix_type
-s390_elf_suffix (str_p, exp_p)
-     char **str_p;
-     expressionS *exp_p;
+s390_elf_suffix (char **str_p, expressionS *exp_p)
 {
   static struct map_bfd mapping[] =
   {
 {
   static struct map_bfd mapping[] =
   {
@@ -606,6 +796,15 @@ s390_elf_suffix (str_p, exp_p)
     { "got12", 5, ELF_SUFFIX_GOT  },
     { "plt", 3, ELF_SUFFIX_PLT  },
     { "gotent", 6, ELF_SUFFIX_GOTENT },
     { "got12", 5, ELF_SUFFIX_GOT  },
     { "plt", 3, ELF_SUFFIX_PLT  },
     { "gotent", 6, ELF_SUFFIX_GOTENT },
+    { "gotoff", 6, ELF_SUFFIX_GOTOFF },
+    { "gotplt", 6, ELF_SUFFIX_GOTPLT },
+    { "pltoff", 6, ELF_SUFFIX_PLTOFF },
+    { "tlsgd", 5, ELF_SUFFIX_TLS_GD },
+    { "gotntpoff", 9, ELF_SUFFIX_TLS_GOTIE },
+    { "indntpoff", 9, ELF_SUFFIX_TLS_IE },
+    { "tlsldm", 6, ELF_SUFFIX_TLS_LDM },
+    { "dtpoff", 6, ELF_SUFFIX_TLS_LDO },
+    { "ntpoff", 6, ELF_SUFFIX_TLS_LE },
     { NULL,  0, ELF_SUFFIX_NONE }
   };
 
     { NULL,  0, ELF_SUFFIX_NONE }
   };
 
@@ -618,13 +817,13 @@ s390_elf_suffix (str_p, exp_p)
     return ELF_SUFFIX_NONE;
 
   ident = str;
     return ELF_SUFFIX_NONE;
 
   ident = str;
-  while (isalnum(*str))
+  while (ISALNUM (*str))
     str++;
   len = str - ident;
 
   for (ptr = &mapping[0]; ptr->length > 0; ptr++)
     str++;
   len = str - ident;
 
   for (ptr = &mapping[0]; ptr->length > 0; ptr++)
-    if (len == ptr->length &&
-       strncasecmp(ident, ptr->string, ptr->length) == 0)
+    if (len == ptr->length
+       && strncasecmp (ident, ptr->string, ptr->length) == 0)
       {
        if (exp_p->X_add_number != 0)
          as_warn (_("identifier+constant@%s means identifier@%s+constant"),
       {
        if (exp_p->X_add_number != 0)
          as_warn (_("identifier+constant@%s means identifier@%s+constant"),
@@ -696,9 +895,7 @@ static int lp_count = 0;
 static int lpe_count = 0;
 
 static int
 static int lpe_count = 0;
 
 static int
-s390_exp_compare(exp1, exp2)
-     expressionS *exp1;
-     expressionS *exp2;
+s390_exp_compare (expressionS *exp1, expressionS *exp2)
 {
   if (exp1->X_op != exp2->X_op)
     return 0;
 {
   if (exp1->X_op != exp2->X_op)
     return 0;
@@ -710,15 +907,15 @@ s390_exp_compare(exp1, exp2)
       return exp1->X_add_number == exp2->X_add_number;
 
     case O_big:
       return exp1->X_add_number == exp2->X_add_number;
 
     case O_big:
-      as_bad(_("Can't handle O_big in s390_exp_compare")); 
+      as_bad (_("Can't handle O_big in s390_exp_compare"));
 
     case O_symbol:     /* X_add_symbol & X_add_number must be equal.  */
     case O_symbol_rva:
     case O_uminus:
     case O_bit_not:
     case O_logical_not:
 
     case O_symbol:     /* X_add_symbol & X_add_number must be equal.  */
     case O_symbol_rva:
     case O_uminus:
     case O_bit_not:
     case O_logical_not:
-      return (exp1->X_add_symbol == exp2->X_add_symbol) &&
-       (exp1->X_add_number == exp2->X_add_number);
+      return (exp1->X_add_symbol == exp2->X_add_symbol)
+       &&   (exp1->X_add_number == exp2->X_add_number);
 
     case O_multiply:   /* X_add_symbol,X_op_symbol&X_add_number must be equal.  */
     case O_divide:
 
     case O_multiply:   /* X_add_symbol,X_op_symbol&X_add_number must be equal.  */
     case O_divide:
@@ -739,21 +936,18 @@ s390_exp_compare(exp1, exp2)
     case O_gt:
     case O_logical_and:
     case O_logical_or:
     case O_gt:
     case O_logical_and:
     case O_logical_or:
-      return (exp1->X_add_symbol == exp2->X_add_symbol) &&
-            (exp1->X_op_symbol == exp2->X_op_symbol) &&
-            (exp1->X_add_number == exp2->X_add_number);
+      return (exp1->X_add_symbol == exp2->X_add_symbol)
+       &&   (exp1->X_op_symbol  == exp2->X_op_symbol)
+       &&   (exp1->X_add_number == exp2->X_add_number);
     default:
     default:
-    return 0;
-  }
+      return 0;
+    }
 }
 
 /* Test for @lit and if its present make an entry in the literal pool and
    modify the current expression to be an offset into the literal pool.  */
 static elf_suffix_type
 }
 
 /* Test for @lit and if its present make an entry in the literal pool and
    modify the current expression to be an offset into the literal pool.  */
 static elf_suffix_type
-s390_lit_suffix (str_p, exp_p, suffix)
-     char **str_p;
-     expressionS *exp_p;
-     elf_suffix_type suffix;
+s390_lit_suffix (char **str_p, expressionS *exp_p, elf_suffix_type suffix)
 {
   bfd_reloc_code_real_type reloc;
   char tmp_name[64];
 {
   bfd_reloc_code_real_type reloc;
   char tmp_name[64];
@@ -764,14 +958,14 @@ s390_lit_suffix (str_p, exp_p, suffix)
 
   if (*str++ != ':')
     return suffix;       /* No modification.  */
 
   if (*str++ != ':')
     return suffix;       /* No modification.  */
+
   /* We look for a suffix of the form "@lit1", "@lit2", "@lit4" or "@lit8".  */
   ident = str;
   /* We look for a suffix of the form "@lit1", "@lit2", "@lit4" or "@lit8".  */
   ident = str;
-  while (isalnum(*str))
+  while (ISALNUM (*str))
     str++;
   len = str - ident;
     str++;
   len = str - ident;
-  if (len != 4 || strncasecmp(ident, "lit", 3) != 0 ||
-      (ident[3]!='1' && ident[3]!='2' && ident[3]!='4' && ident[3]!='8'))
+  if (len != 4 || strncasecmp (ident, "lit", 3) != 0
+      || (ident[3]!='1' && ident[3]!='2' && ident[3]!='4' && ident[3]!='8'))
     return suffix;      /* no modification */
   nbytes = ident[3] - '0';
 
     return suffix;      /* no modification */
   nbytes = ident[3] - '0';
 
@@ -823,8 +1017,8 @@ s390_lit_suffix (str_p, exp_p, suffix)
     {
       /* Processing for 'normal' data types.  */
       for (lpe = lpe_list; lpe != NULL; lpe = lpe->next)
     {
       /* Processing for 'normal' data types.  */
       for (lpe = lpe_list; lpe != NULL; lpe = lpe->next)
-       if (lpe->nbytes == nbytes && lpe->reloc == reloc &&
-           s390_exp_compare(exp_p, &lpe->ex) != 0)
+       if (lpe->nbytes == nbytes && lpe->reloc == reloc
+           && s390_exp_compare (exp_p, &lpe->ex) != 0)
          break;
     }
 
          break;
     }
 
@@ -838,7 +1032,7 @@ s390_lit_suffix (str_p, exp_p, suffix)
        }
       else
        {
        }
       else
        {
-         lpe = (struct s390_lpe *) xmalloc(sizeof(struct s390_lpe));
+         lpe = XNEW (struct s390_lpe);
        }
 
       lpe->ex = *exp_p;
        }
 
       lpe->ex = *exp_p;
@@ -849,7 +1043,7 @@ s390_lit_suffix (str_p, exp_p, suffix)
            lpe->floatnum = generic_floating_point_number;
          else if (exp_p->X_add_number <= 4)
            memcpy (lpe->bignum, generic_bignum,
            lpe->floatnum = generic_floating_point_number;
          else if (exp_p->X_add_number <= 4)
            memcpy (lpe->bignum, generic_bignum,
-                   exp_p->X_add_number*sizeof(LITTLENUM_TYPE));
+                   exp_p->X_add_number * sizeof (LITTLENUM_TYPE));
          else
            as_bad (_("Big number is too big"));
        }
          else
            as_bad (_("Big number is too big"));
        }
@@ -859,14 +1053,14 @@ s390_lit_suffix (str_p, exp_p, suffix)
       /* Literal pool name defined ?  */
       if (lp_sym == NULL)
        {
       /* Literal pool name defined ?  */
       if (lp_sym == NULL)
        {
-         sprintf(tmp_name, ".L\001%i", lp_count);
-         lp_sym = symbol_make(tmp_name);
+         sprintf (tmp_name, ".L\001%i", lp_count);
+         lp_sym = symbol_make (tmp_name);
        }
 
       /* Make name for literal pool entry.  */
        }
 
       /* Make name for literal pool entry.  */
-      sprintf(tmp_name, ".L\001%i\002%i", lp_count, lpe_count);
+      sprintf (tmp_name, ".L\001%i\002%i", lp_count, lpe_count);
       lpe_count++;
       lpe_count++;
-      lpe->sym = symbol_make(tmp_name);
+      lpe->sym = symbol_make (tmp_name);
 
       /* Add to literal pool list.  */
       lpe->next = NULL;
 
       /* Add to literal pool list.  */
       lpe->next = NULL;
@@ -878,7 +1072,7 @@ s390_lit_suffix (str_p, exp_p, suffix)
       else
        lpe_list = lpe_list_tail = lpe;
     }
       else
        lpe_list = lpe_list_tail = lpe;
     }
-  
+
   /* Now change exp_p to the offset into the literal pool.
      Thats the expression: .L^Ax^By-.L^Ax   */
   exp_p->X_add_symbol = lpe->sym;
   /* Now change exp_p to the offset into the literal pool.
      Thats the expression: .L^Ax^By-.L^Ax   */
   exp_p->X_add_symbol = lpe->sym;
@@ -896,8 +1090,7 @@ s390_lit_suffix (str_p, exp_p, suffix)
 /* Like normal .long/.short/.word, except support @got, etc.
    clobbers input_line_pointer, checks end-of-line.  */
 static void
 /* Like normal .long/.short/.word, except support @got, etc.
    clobbers input_line_pointer, checks end-of-line.  */
 static void
-s390_elf_cons (nbytes)
-     register int nbytes;      /* 1=.byte, 2=.word, 4=.long */
+s390_elf_cons (int nbytes /* 1=.byte, 2=.word, 4=.long */)
 {
   expressionS exp;
   elf_suffix_type suffix;
 {
   expressionS exp;
   elf_suffix_type suffix;
@@ -921,30 +1114,82 @@ s390_elf_cons (nbytes)
          int size;
          char *where;
 
          int size;
          char *where;
 
-         if (nbytes == 2 && suffix == ELF_SUFFIX_GOT)
-           reloc = BFD_RELOC_390_GOT16;
-         else if (nbytes == 4 && suffix == ELF_SUFFIX_GOT)
-           reloc = BFD_RELOC_32_GOT_PCREL;
-         else if (nbytes == 8 && suffix == ELF_SUFFIX_GOT)
-           reloc = BFD_RELOC_390_GOT64;
-         else if (nbytes == 4 && suffix == ELF_SUFFIX_PLT)
-           reloc = BFD_RELOC_390_PLT32;
-         else if (nbytes == 8 && suffix == ELF_SUFFIX_PLT)
-           reloc = BFD_RELOC_390_PLT64;
+         if (nbytes == 2)
+           {
+             static bfd_reloc_code_real_type tab2[] =
+               {
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_NONE  */
+                 BFD_RELOC_390_GOT16,          /* ELF_SUFFIX_GOT  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_PLT  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_GOTENT  */
+                 BFD_RELOC_16_GOTOFF,          /* ELF_SUFFIX_GOTOFF  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_GOTPLT  */
+                 BFD_RELOC_390_PLTOFF16,       /* ELF_SUFFIX_PLTOFF  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_TLS_GD  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_TLS_GOTIE  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_TLS_IE  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_TLS_LDM  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_TLS_LDO  */
+                 BFD_RELOC_UNUSED              /* ELF_SUFFIX_TLS_LE  */
+               };
+             reloc = tab2[suffix];
+           }
+         else if (nbytes == 4)
+           {
+             static bfd_reloc_code_real_type tab4[] =
+               {
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_NONE  */
+                 BFD_RELOC_32_GOT_PCREL,       /* ELF_SUFFIX_GOT  */
+                 BFD_RELOC_390_PLT32,          /* ELF_SUFFIX_PLT  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_GOTENT  */
+                 BFD_RELOC_32_GOTOFF,          /* ELF_SUFFIX_GOTOFF  */
+                 BFD_RELOC_390_GOTPLT32,       /* ELF_SUFFIX_GOTPLT  */
+                 BFD_RELOC_390_PLTOFF32,       /* ELF_SUFFIX_PLTOFF  */
+                 BFD_RELOC_390_TLS_GD32,       /* ELF_SUFFIX_TLS_GD  */
+                 BFD_RELOC_390_TLS_GOTIE32,    /* ELF_SUFFIX_TLS_GOTIE  */
+                 BFD_RELOC_390_TLS_IE32,       /* ELF_SUFFIX_TLS_IE  */
+                 BFD_RELOC_390_TLS_LDM32,      /* ELF_SUFFIX_TLS_LDM  */
+                 BFD_RELOC_390_TLS_LDO32,      /* ELF_SUFFIX_TLS_LDO  */
+                 BFD_RELOC_390_TLS_LE32        /* ELF_SUFFIX_TLS_LE  */
+               };
+             reloc = tab4[suffix];
+           }
+         else if (nbytes == 8)
+           {
+             static bfd_reloc_code_real_type tab8[] =
+               {
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_NONE  */
+                 BFD_RELOC_390_GOT64,          /* ELF_SUFFIX_GOT  */
+                 BFD_RELOC_390_PLT64,          /* ELF_SUFFIX_PLT  */
+                 BFD_RELOC_UNUSED,             /* ELF_SUFFIX_GOTENT  */
+                 BFD_RELOC_390_GOTOFF64,       /* ELF_SUFFIX_GOTOFF  */
+                 BFD_RELOC_390_GOTPLT64,       /* ELF_SUFFIX_GOTPLT  */
+                 BFD_RELOC_390_PLTOFF64,       /* ELF_SUFFIX_PLTOFF  */
+                 BFD_RELOC_390_TLS_GD64,       /* ELF_SUFFIX_TLS_GD  */
+                 BFD_RELOC_390_TLS_GOTIE64,    /* ELF_SUFFIX_TLS_GOTIE  */
+                 BFD_RELOC_390_TLS_IE64,       /* ELF_SUFFIX_TLS_IE  */
+                 BFD_RELOC_390_TLS_LDM64,      /* ELF_SUFFIX_TLS_LDM  */
+                 BFD_RELOC_390_TLS_LDO64,      /* ELF_SUFFIX_TLS_LDO  */
+                 BFD_RELOC_390_TLS_LE64        /* ELF_SUFFIX_TLS_LE  */
+               };
+             reloc = tab8[suffix];
+           }
          else
            reloc = BFD_RELOC_UNUSED;
 
          else
            reloc = BFD_RELOC_UNUSED;
 
-         if (reloc != BFD_RELOC_UNUSED)
+         if (reloc != BFD_RELOC_UNUSED
+             && (reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc)))
            {
            {
-             reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc);
              size = bfd_get_reloc_size (reloc_howto);
              if (size > nbytes)
                as_bad (_("%s relocations do not fit in %d bytes"),
                        reloc_howto->name, nbytes);
              size = bfd_get_reloc_size (reloc_howto);
              if (size > nbytes)
                as_bad (_("%s relocations do not fit in %d bytes"),
                        reloc_howto->name, nbytes);
-             where = frag_more(nbytes);
+             where = frag_more (nbytes);
              md_number_to_chars (where, 0, size);
              md_number_to_chars (where, 0, size);
-             fix_new_exp (frag_now, where - frag_now->fr_literal, 
-                          size, &exp, reloc_howto->pc_relative, reloc);
+             /* To make fixup_segment do the pc relative conversion the
+                pcrel parameter on the fix_new_exp call needs to be FALSE.  */
+             fix_new_exp (frag_now, where - frag_now->fr_literal,
+                          size, &exp, FALSE, reloc);
            }
          else
            as_bad (_("relocation not applicable"));
            }
          else
            as_bad (_("relocation not applicable"));
@@ -954,7 +1199,7 @@ s390_elf_cons (nbytes)
     }
   while (*input_line_pointer++ == ',');
 
     }
   while (*input_line_pointer++ == ',');
 
-  input_line_pointer--;                /* Put terminator back into stream. */
+  input_line_pointer--;                /* Put terminator back into stream.  */
   demand_empty_rest_of_line ();
 }
 
   demand_empty_rest_of_line ();
 }
 
@@ -973,61 +1218,85 @@ struct s390_fixup
 
 /* This routine is called for each instruction to be assembled.  */
 
 
 /* This routine is called for each instruction to be assembled.  */
 
-char *
-md_gather_operands (str, insn, opcode)
-     char *str;
-     unsigned char *insn;
-     const struct s390_opcode *opcode;
+static char *
+md_gather_operands (char *str,
+                   unsigned char *insn,
+                   const struct s390_opcode *opcode)
 {
   struct s390_fixup fixups[MAX_INSN_FIXUPS];
   const struct s390_operand *operand;
   const unsigned char *opindex_ptr;
 {
   struct s390_fixup fixups[MAX_INSN_FIXUPS];
   const struct s390_operand *operand;
   const unsigned char *opindex_ptr;
+  expressionS ex;
   elf_suffix_type suffix;
   bfd_reloc_code_real_type reloc;
   int skip_optional;
   elf_suffix_type suffix;
   bfd_reloc_code_real_type reloc;
   int skip_optional;
-  int parentheses;
   char *f;
   int fc, i;
 
   char *f;
   int fc, i;
 
-  while (isspace(*str)) str++;
+  while (ISSPACE (*str))
+    str++;
 
 
-  parentheses = 0;
   skip_optional = 0;
 
   /* Gather the operands.  */
   fc = 0;
   for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
     {
   skip_optional = 0;
 
   /* Gather the operands.  */
   fc = 0;
   for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
     {
-      expressionS ex;
       char *hold;
 
       operand = s390_operands + *opindex_ptr;
       char *hold;
 
       operand = s390_operands + *opindex_ptr;
-    
+
+      if ((opcode->flags & S390_INSTR_FLAG_OPTPARM) && *str == '\0')
+       {
+         /* Optional parameters might need to be ORed with a
+            value so calling s390_insert_operand is needed.  */
+         s390_insert_operand (insn, operand, 0, NULL, 0);
+         break;
+       }
+
       if (skip_optional && (operand->flags & S390_OPERAND_INDEX))
        {
          /* We do an early skip. For D(X,B) constructions the index
       if (skip_optional && (operand->flags & S390_OPERAND_INDEX))
        {
          /* We do an early skip. For D(X,B) constructions the index
-            register is skipped (X is optional). For D(L,B) the base  
+            register is skipped (X is optional). For D(L,B) the base
             register will be the skipped operand, because L is NOT
             optional.  */
          skip_optional = 0;
          continue;
        }
             register will be the skipped operand, because L is NOT
             optional.  */
          skip_optional = 0;
          continue;
        }
-    
+
       /* Gather the operand.  */
       hold = input_line_pointer;
       input_line_pointer = str;
 
       /* Gather the operand.  */
       hold = input_line_pointer;
       input_line_pointer = str;
 
-      if (! register_name (&ex))    /* parse the operand */
+      /* Parse the operand.  */
+      if (! register_name (&ex))
        expression (&ex);
        expression (&ex);
-    
+
       str = input_line_pointer;
       input_line_pointer = hold;
       str = input_line_pointer;
       input_line_pointer = hold;
-    
+
       /* Write the operand to the insn.  */
       if (ex.X_op == O_illegal)
        as_bad (_("illegal operand"));
       else if (ex.X_op == O_absent)
       /* Write the operand to the insn.  */
       if (ex.X_op == O_illegal)
        as_bad (_("illegal operand"));
       else if (ex.X_op == O_absent)
-       as_bad (_("missing operand"));
+       {
+         /* No operands, check if all operands can be skipped.  */
+         while (*opindex_ptr != 0 && operand->flags & S390_OPERAND_OPTIONAL)
+           {
+             if (operand->flags & S390_OPERAND_DISP)
+               {
+                 /* An optional displacement makes the whole D(X,B)
+                    D(L,B) or D(B) block optional.  */
+                 do {
+                   operand = s390_operands + *(++opindex_ptr);
+                 } while (!(operand->flags & S390_OPERAND_BASE));
+               }
+             operand = s390_operands + *(++opindex_ptr);
+           }
+         if (opindex_ptr[0] == '\0')
+           break;
+         as_bad (_("missing operand"));
+       }
       else if (ex.X_op == O_register || ex.X_op == O_constant)
        {
          s390_lit_suffix (&str, &ex, ELF_SUFFIX_NONE);
       else if (ex.X_op == O_register || ex.X_op == O_constant)
        {
          s390_lit_suffix (&str, &ex, ELF_SUFFIX_NONE);
@@ -1045,10 +1314,31 @@ md_gather_operands (str, insn, opcode)
            }
          else
            {
            }
          else
            {
-             if ((operand->flags & S390_OPERAND_INDEX) && ex.X_add_number == 0)
-               as_warn("index register specified but zero");
-             if ((operand->flags & S390_OPERAND_BASE) && ex.X_add_number == 0)
-               as_warn("base register specified but zero");
+             if ((operand->flags & S390_OPERAND_LENGTH)
+                 && ex.X_op != O_constant)
+               as_fatal (_("invalid length field specified"));
+             if ((operand->flags & S390_OPERAND_INDEX)
+                 && ex.X_add_number == 0
+                 && warn_areg_zero)
+               as_warn (_("index register specified but zero"));
+             if ((operand->flags & S390_OPERAND_BASE)
+                 && ex.X_add_number == 0
+                 && warn_areg_zero)
+               as_warn (_("base register specified but zero"));
+             if ((operand->flags & S390_OPERAND_GPR)
+                 && (operand->flags & S390_OPERAND_REG_PAIR)
+                 && (ex.X_add_number & 1))
+               as_fatal (_("odd numbered general purpose register specified as "
+                           "register pair"));
+             if ((operand->flags & S390_OPERAND_FPR)
+                 && (operand->flags & S390_OPERAND_REG_PAIR)
+                 && ex.X_add_number != 0 && ex.X_add_number != 1
+                 && ex.X_add_number != 4 && ex.X_add_number != 5
+                 && ex.X_add_number != 8 && ex.X_add_number != 9
+                 && ex.X_add_number != 12 && ex.X_add_number != 13)
+               as_fatal (_("invalid floating point register pair.  Valid fp "
+                           "register pair operands are 0, 1, 4, 5, 8, 9, "
+                           "12 or 13."));
              s390_insert_operand (insn, operand, ex.X_add_number, NULL, 0);
            }
        }
              s390_insert_operand (insn, operand, ex.X_add_number, NULL, 0);
            }
        }
@@ -1060,30 +1350,79 @@ md_gather_operands (str, insn, opcode)
 
          if (suffix == ELF_SUFFIX_GOT)
            {
 
          if (suffix == ELF_SUFFIX_GOT)
            {
-             if (operand->flags & S390_OPERAND_DISP)
+             if ((operand->flags & S390_OPERAND_DISP) &&
+                 (operand->bits == 12))
                reloc = BFD_RELOC_390_GOT12;
                reloc = BFD_RELOC_390_GOT12;
-             else if ((operand->flags & S390_OPERAND_SIGNED) &&
-                      (operand->bits == 16))
+             else if ((operand->flags & S390_OPERAND_DISP) &&
+                      (operand->bits == 20))
+               reloc = BFD_RELOC_390_GOT20;
+             else if ((operand->flags & S390_OPERAND_SIGNED)
+                      && (operand->bits == 16))
                reloc = BFD_RELOC_390_GOT16;
                reloc = BFD_RELOC_390_GOT16;
-             else if ((operand->flags & S390_OPERAND_PCREL) &&
-                      (operand->bits == 32))
+             else if ((operand->flags & S390_OPERAND_PCREL)
+                      && (operand->bits == 32))
                reloc = BFD_RELOC_390_GOTENT;
            }
          else if (suffix == ELF_SUFFIX_PLT)
            {
                reloc = BFD_RELOC_390_GOTENT;
            }
          else if (suffix == ELF_SUFFIX_PLT)
            {
-             if ((operand->flags & S390_OPERAND_PCREL) &&
-                 (operand->bits == 16))
+             if ((operand->flags & S390_OPERAND_PCREL)
+                 && (operand->bits == 12))
+               reloc = BFD_RELOC_390_PLT12DBL;
+             else if ((operand->flags & S390_OPERAND_PCREL)
+                      && (operand->bits == 16))
                reloc = BFD_RELOC_390_PLT16DBL;
                reloc = BFD_RELOC_390_PLT16DBL;
-             else if ((operand->flags & S390_OPERAND_PCREL) &&
-                      (operand->bits == 32))
+             else if ((operand->flags & S390_OPERAND_PCREL)
+                      && (operand->bits == 24))
+               reloc = BFD_RELOC_390_PLT24DBL;
+             else if ((operand->flags & S390_OPERAND_PCREL)
+                      && (operand->bits == 32))
                reloc = BFD_RELOC_390_PLT32DBL;
            }
          else if (suffix == ELF_SUFFIX_GOTENT)
            {
                reloc = BFD_RELOC_390_PLT32DBL;
            }
          else if (suffix == ELF_SUFFIX_GOTENT)
            {
-             if ((operand->flags & S390_OPERAND_PCREL) &&
-                 (operand->bits == 32))
+             if ((operand->flags & S390_OPERAND_PCREL)
+                 && (operand->bits == 32))
                reloc = BFD_RELOC_390_GOTENT;
            }
                reloc = BFD_RELOC_390_GOTENT;
            }
+         else if (suffix == ELF_SUFFIX_GOTOFF)
+           {
+             if ((operand->flags & S390_OPERAND_SIGNED)
+                 && (operand->bits == 16))
+               reloc = BFD_RELOC_16_GOTOFF;
+           }
+         else if (suffix == ELF_SUFFIX_PLTOFF)
+           {
+             if ((operand->flags & S390_OPERAND_SIGNED)
+                 && (operand->bits == 16))
+               reloc = BFD_RELOC_390_PLTOFF16;
+           }
+         else if (suffix == ELF_SUFFIX_GOTPLT)
+           {
+             if ((operand->flags & S390_OPERAND_DISP)
+                 && (operand->bits == 12))
+               reloc = BFD_RELOC_390_GOTPLT12;
+             else if ((operand->flags & S390_OPERAND_SIGNED)
+                      && (operand->bits == 16))
+               reloc = BFD_RELOC_390_GOTPLT16;
+             else if ((operand->flags & S390_OPERAND_PCREL)
+                      && (operand->bits == 32))
+               reloc = BFD_RELOC_390_GOTPLTENT;
+           }
+         else if (suffix == ELF_SUFFIX_TLS_GOTIE)
+           {
+             if ((operand->flags & S390_OPERAND_DISP)
+                 && (operand->bits == 12))
+               reloc = BFD_RELOC_390_TLS_GOTIE12;
+             else if ((operand->flags & S390_OPERAND_DISP)
+                      && (operand->bits == 20))
+               reloc = BFD_RELOC_390_TLS_GOTIE20;
+           }
+         else if (suffix == ELF_SUFFIX_TLS_IE)
+           {
+             if ((operand->flags & S390_OPERAND_PCREL)
+                      && (operand->bits == 32))
+               reloc = BFD_RELOC_390_TLS_IEENT;
+           }
 
          if (suffix != ELF_SUFFIX_NONE && reloc == BFD_RELOC_UNUSED)
            as_bad (_("invalid operand suffix"));
 
          if (suffix != ELF_SUFFIX_NONE && reloc == BFD_RELOC_UNUSED)
            as_bad (_("invalid operand suffix"));
@@ -1096,7 +1435,7 @@ md_gather_operands (str, insn, opcode)
          fixups[fc].reloc = reloc;
          ++fc;
        }
          fixups[fc].reloc = reloc;
          ++fc;
        }
+
       /* Check the next character. The call to expression has advanced
         str past any whitespace.  */
       if (operand->flags & S390_OPERAND_DISP)
       /* Check the next character. The call to expression has advanced
         str past any whitespace.  */
       if (operand->flags & S390_OPERAND_DISP)
@@ -1104,7 +1443,7 @@ md_gather_operands (str, insn, opcode)
          /* After a displacement a block in parentheses can start.  */
          if (*str != '(')
            {
          /* After a displacement a block in parentheses can start.  */
          if (*str != '(')
            {
-             /* Check if parethesed block can be skipped. If the next
+             /* Check if parenthesized block can be skipped. If the next
                 operand is neiter an optional operand nor a base register
                 then we have a syntax error.  */
              operand = s390_operands + *(++opindex_ptr);
                 operand is neiter an optional operand nor a base register
                 then we have a syntax error.  */
              operand = s390_operands + *(++opindex_ptr);
@@ -1114,12 +1453,23 @@ md_gather_operands (str, insn, opcode)
              /* Ok, skip all operands until S390_OPERAND_BASE.  */
              while (!(operand->flags & S390_OPERAND_BASE))
                operand = s390_operands + *(++opindex_ptr);
              /* Ok, skip all operands until S390_OPERAND_BASE.  */
              while (!(operand->flags & S390_OPERAND_BASE))
                operand = s390_operands + *(++opindex_ptr);
-       
-             /* If there is a next operand it must be seperated by a comma.  */
+
+             /* If there is a next operand it must be separated by a comma.  */
              if (opindex_ptr[1] != '\0')
                {
              if (opindex_ptr[1] != '\0')
                {
-                 if (*str++ != ',')
-                   as_bad(_("syntax error; expected ,"));
+                 if (*str != ',')
+                   {
+                     while (opindex_ptr[1] != '\0')
+                       {
+                         operand = s390_operands + *(++opindex_ptr);
+                         if (operand->flags & S390_OPERAND_OPTIONAL)
+                           continue;
+                         as_bad (_("syntax error; expected ,"));
+                         break;
+                       }
+                   }
+                 else
+                   str++;
                }
            }
          else
                }
            }
          else
@@ -1148,11 +1498,22 @@ md_gather_operands (str, insn, opcode)
          if (*str++ != ')')
            as_bad (_("syntax error; missing ')' after base register"));
          skip_optional = 0;
          if (*str++ != ')')
            as_bad (_("syntax error; missing ')' after base register"));
          skip_optional = 0;
-         /* If there is a next operand it must be seperated by a comma.  */
+         /* If there is a next operand it must be separated by a comma.  */
          if (opindex_ptr[1] != '\0')
            {
          if (opindex_ptr[1] != '\0')
            {
-             if (*str++ != ',')
-               as_bad (_("syntax error; expected ,"));
+             if (*str != ',')
+               {
+                 while (opindex_ptr[1] != '\0')
+                   {
+                     operand = s390_operands + *(++opindex_ptr);
+                     if (operand->flags & S390_OPERAND_OPTIONAL)
+                       continue;
+                     as_bad (_("syntax error; expected ,"));
+                     break;
+                   }
+               }
+             else
+               str++;
            }
        }
       else
            }
        }
       else
@@ -1167,23 +1528,52 @@ md_gather_operands (str, insn, opcode)
                as_bad (_("syntax error; ')' not allowed here"));
              str++;
            }
                as_bad (_("syntax error; ')' not allowed here"));
              str++;
            }
-         /* If there is a next operand it must be seperated by a comma.  */
+
+         if ((opcode->flags & S390_INSTR_FLAG_OPTPARM) && *str == '\0')
+           continue;
+
+         /* If there is a next operand it must be separated by a comma.  */
          if (opindex_ptr[1] != '\0')
            {
          if (opindex_ptr[1] != '\0')
            {
-             if (*str++ != ',')
-               as_bad(_("syntax error; expected ,"));
+             if (*str != ',')
+               {
+                 while (opindex_ptr[1] != '\0')
+                   {
+                     operand = s390_operands + *(++opindex_ptr);
+                     if (operand->flags & S390_OPERAND_OPTIONAL)
+                       continue;
+                     as_bad (_("syntax error; expected ,"));
+                     break;
+                   }
+               }
+             else
+               str++;
            }
        }
     }
 
            }
        }
     }
 
-  while (isspace (*str))
+  while (ISSPACE (*str))
     ++str;
 
     ++str;
 
+  /* Check for tls instruction marker.  */
+  reloc = s390_tls_suffix (&str, &ex);
+  if (reloc != BFD_RELOC_UNUSED)
+    {
+      /* We need to generate a fixup of type 'reloc' for this
+        instruction.  */
+      if (fc >= MAX_INSN_FIXUPS)
+       as_fatal (_("too many fixups"));
+      fixups[fc].exp = ex;
+      fixups[fc].opindex = -1;
+      fixups[fc].reloc = reloc;
+      ++fc;
+    }
+
   if (*str != '\0')
     {
       char *linefeed;
 
   if (*str != '\0')
     {
       char *linefeed;
 
-      if ((linefeed = strchr(str, '\n')) != NULL)
+      if ((linefeed = strchr (str, '\n')) != NULL)
        *linefeed = '\0';
       as_bad (_("junk at end of line: `%s'"), str);
       if (linefeed != NULL)
        *linefeed = '\0';
       as_bad (_("junk at end of line: `%s'"), str);
       if (linefeed != NULL)
@@ -1193,15 +1583,25 @@ md_gather_operands (str, insn, opcode)
   /* Write out the instruction.  */
   f = frag_more (opcode->oplen);
   memcpy (f, insn, opcode->oplen);
   /* Write out the instruction.  */
   f = frag_more (opcode->oplen);
   memcpy (f, insn, opcode->oplen);
+  dwarf2_emit_insn (opcode->oplen);
 
   /* Create any fixups.  At this point we do not use a
      bfd_reloc_code_real_type, but instead just use the
      BFD_RELOC_UNUSED plus the operand index.  This lets us easily
      handle fixups for any operand type, although that is admittedly
      not a very exciting feature.  We pick a BFD reloc type in
 
   /* Create any fixups.  At this point we do not use a
      bfd_reloc_code_real_type, but instead just use the
      BFD_RELOC_UNUSED plus the operand index.  This lets us easily
      handle fixups for any operand type, although that is admittedly
      not a very exciting feature.  We pick a BFD reloc type in
-     md_apply_fix3.  */
+     md_apply_fix.  */
   for (i = 0; i < fc; i++)
     {
   for (i = 0; i < fc; i++)
     {
+
+      if (fixups[i].opindex < 0)
+       {
+         /* Create tls instruction marker relocation.  */
+         fix_new_exp (frag_now, f - frag_now->fr_literal, opcode->oplen,
+                      &fixups[i].exp, 0, fixups[i].reloc);
+         continue;
+       }
+
       operand = s390_operands + fixups[i].opindex;
 
       if (fixups[i].reloc != BFD_RELOC_UNUSED)
       operand = s390_operands + fixups[i].opindex;
 
       if (fixups[i].reloc != BFD_RELOC_UNUSED)
@@ -1209,25 +1609,26 @@ md_gather_operands (str, insn, opcode)
          reloc_howto_type *reloc_howto;
          fixS *fixP;
          int size;
          reloc_howto_type *reloc_howto;
          fixS *fixP;
          int size;
-      
+
          reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
          if (!reloc_howto)
            abort ();
          reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
          if (!reloc_howto)
            abort ();
-      
-         size = bfd_get_reloc_size (reloc_howto);
+
+         size = ((reloc_howto->bitsize - 1) / 8) + 1;
 
          if (size < 1 || size > 4)
            abort ();
 
          if (size < 1 || size > 4)
            abort ();
-      
-         fixP = fix_new_exp (frag_now, 
-                             f - frag_now->fr_literal + (operand->shift/8), 
+
+         fixP = fix_new_exp (frag_now,
+                             f - frag_now->fr_literal + (operand->shift/8),
                              size, &fixups[i].exp, reloc_howto->pc_relative,
                              fixups[i].reloc);
          /* Turn off overflow checking in fixup_segment. This is necessary
             because fixup_segment will signal an overflow for large 4 byte
             quantities for GOT12 relocations.  */
                              size, &fixups[i].exp, reloc_howto->pc_relative,
                              fixups[i].reloc);
          /* Turn off overflow checking in fixup_segment. This is necessary
             because fixup_segment will signal an overflow for large 4 byte
             quantities for GOT12 relocations.  */
-         if (fixups[i].reloc == BFD_RELOC_390_GOT12 ||
-             fixups[i].reloc == BFD_RELOC_390_GOT16)
+         if (   fixups[i].reloc == BFD_RELOC_390_GOT12
+             || fixups[i].reloc == BFD_RELOC_390_GOT20
+             || fixups[i].reloc == BFD_RELOC_390_GOT16)
            fixP->fx_no_overflow = 1;
        }
       else
            fixP->fx_no_overflow = 1;
        }
       else
@@ -1242,15 +1643,14 @@ md_gather_operands (str, insn, opcode)
 /* This routine is called for each instruction to be assembled.  */
 
 void
 /* This routine is called for each instruction to be assembled.  */
 
 void
-md_assemble (str)
-     char *str;
+md_assemble (char *str)
 {
   const struct s390_opcode *opcode;
   unsigned char insn[6];
   char *s;
 
   /* Get the opcode.  */
 {
   const struct s390_opcode *opcode;
   unsigned char insn[6];
   char *s;
 
   /* Get the opcode.  */
-  for (s = str; *s != '\0' && ! isspace (*s); s++)
+  for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
     ;
   if (*s != '\0')
     *s++ = '\0';
     ;
   if (*s != '\0')
     *s++ = '\0';
@@ -1262,13 +1662,12 @@ md_assemble (str)
       as_bad (_("Unrecognized opcode: `%s'"), str);
       return;
     }
       as_bad (_("Unrecognized opcode: `%s'"), str);
       return;
     }
-  else if (!(opcode->architecture & current_arch_mask))
+  else if (!(opcode->modes & current_mode_mask))
     {
     {
-      as_bad("Opcode %s not available in this architecture", str);
+      as_bad (_("Opcode %s not available in this mode"), str);
       return;
     }
       return;
     }
-
-  memcpy (insn, opcode->opcode, sizeof(insn));
+  memcpy (insn, opcode->opcode, sizeof (insn));
   md_gather_operands (s, insn, opcode);
 }
 
   md_gather_operands (s, insn, opcode);
 }
 
@@ -1296,8 +1695,7 @@ md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
 #endif
 
 void
 #endif
 
 void
-s390_bss (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s390_bss (int ignore ATTRIBUTE_UNUSED)
 {
   /* We don't support putting frags in the BSS segment, we fake it
      by marking in_bss, then looking at s_skip for clues.  */
 {
   /* We don't support putting frags in the BSS segment, we fake it
      by marking in_bss, then looking at s_skip for clues.  */
@@ -1309,8 +1707,7 @@ s390_bss (ignore)
 /* Pseudo-op handling.  */
 
 void
 /* Pseudo-op handling.  */
 
 void
-s390_insn(ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s390_insn (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS exp;
   const struct s390_opcode *opformat;
 {
   expressionS exp;
   const struct s390_opcode *opformat;
@@ -1319,7 +1716,7 @@ s390_insn(ignore)
 
   /* Get the opcode format.  */
   s = input_line_pointer;
 
   /* Get the opcode format.  */
   s = input_line_pointer;
-  while (*s != '\0' && *s != ',' && ! isspace (*s))
+  while (*s != '\0' && *s != ',' && ! ISSPACE (*s))
     s++;
   if (*s != ',')
     as_bad (_("Invalid .insn format\n"));
     s++;
   if (*s != ',')
     as_bad (_("Invalid .insn format\n"));
@@ -1337,33 +1734,39 @@ s390_insn(ignore)
   expression (&exp);
   if (exp.X_op == O_constant)
     {
   expression (&exp);
   if (exp.X_op == O_constant)
     {
-      if (opformat->oplen == 4 ||
-         (opformat->oplen == 2 && exp.X_op < 0x10000))
-       md_number_to_chars (insn, exp.X_add_number, opformat->oplen);
+      if (   (   opformat->oplen == 6
+             && (addressT) exp.X_add_number < (1ULL << 48))
+         || (   opformat->oplen == 4
+             && (addressT) exp.X_add_number < (1ULL << 32))
+         || (   opformat->oplen == 2
+             && (addressT) exp.X_add_number < (1ULL << 16)))
+       md_number_to_chars ((char *) insn, exp.X_add_number, opformat->oplen);
       else
       else
-       as_bad(_("Invalid .insn format\n"));
+       as_bad (_("Invalid .insn format\n"));
     }
   else if (exp.X_op == O_big)
     {
     }
   else if (exp.X_op == O_big)
     {
-      if (exp.X_add_number > 0 && 
-         opformat->oplen == 6 &&
-         generic_bignum[3] == 0)
+      if (exp.X_add_number > 0
+         && opformat->oplen == 6
+         && generic_bignum[3] == 0)
        {
        {
-         md_number_to_chars (insn, generic_bignum[2], 2);
-         md_number_to_chars (&insn[2], generic_bignum[1], 2);
-         md_number_to_chars (&insn[4], generic_bignum[0], 2);
+         md_number_to_chars ((char *) insn, generic_bignum[2], 2);
+         md_number_to_chars ((char *) &insn[2], generic_bignum[1], 2);
+         md_number_to_chars ((char *) &insn[4], generic_bignum[0], 2);
        }
       else
        }
       else
-       as_bad(_("Invalid .insn format\n"));
+       as_bad (_("Invalid .insn format\n"));
     }
   else
     as_bad (_("second operand of .insn not a constant\n"));
     }
   else
     as_bad (_("second operand of .insn not a constant\n"));
-  if (*input_line_pointer++ != ',')
+
+  if (strcmp (opformat->name, "e") != 0 && *input_line_pointer++ != ',')
     as_bad (_("missing comma after insn constant\n"));
 
     as_bad (_("missing comma after insn constant\n"));
 
-  if ((s = strchr(input_line_pointer, '\n')) != NULL)
+  if ((s = strchr (input_line_pointer, '\n')) != NULL)
     *s = '\0';
     *s = '\0';
-  input_line_pointer = md_gather_operands (input_line_pointer, insn, opformat);
+  input_line_pointer = md_gather_operands (input_line_pointer, insn,
+                                          opformat);
   if (s != NULL)
     *s = '\n';
   demand_empty_rest_of_line ();
   if (s != NULL)
     *s = '\n';
   demand_empty_rest_of_line ();
@@ -1373,8 +1776,7 @@ s390_insn(ignore)
    pseudo-op, but it can also take a single ASCII string.  */
 
 static void
    pseudo-op, but it can also take a single ASCII string.  */
 
 static void
-s390_byte (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s390_byte (int ignore ATTRIBUTE_UNUSED)
 {
   if (*input_line_pointer != '\"')
     {
 {
   if (*input_line_pointer != '\"')
     {
@@ -1405,17 +1807,16 @@ s390_byte (ignore)
 }
 
 /* The .ltorg pseudo-op.This emits all literals defined since the last
 }
 
 /* The .ltorg pseudo-op.This emits all literals defined since the last
-   .ltorg or the invocation of gas. Literals are defined with the 
+   .ltorg or the invocation of gas. Literals are defined with the
    @lit suffix.  */
 
 static void
    @lit suffix.  */
 
 static void
-s390_literals (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+s390_literals (int ignore ATTRIBUTE_UNUSED)
 {
   struct s390_lpe *lpe;
 
   if (lp_sym == NULL || lpe_count == 0)
 {
   struct s390_lpe *lpe;
 
   if (lp_sym == NULL || lpe_count == 0)
-    return;     /* nothing to be done */
+    return;     /* Nothing to be done.  */
 
   /* Emit symbol for start of literal pool.  */
   S_SET_SEGMENT (lp_sym, now_seg);
 
   /* Emit symbol for start of literal pool.  */
   S_SET_SEGMENT (lp_sym, now_seg);
@@ -1433,7 +1834,7 @@ s390_literals (ignore)
       /* Emit literal pool entry.  */
       if (lpe->reloc != BFD_RELOC_UNUSED)
        {
       /* Emit literal pool entry.  */
       if (lpe->reloc != BFD_RELOC_UNUSED)
        {
-         reloc_howto_type *reloc_howto = 
+         reloc_howto_type *reloc_howto =
            bfd_reloc_type_lookup (stdoutput, lpe->reloc);
          int size = bfd_get_reloc_size (reloc_howto);
          char *where;
            bfd_reloc_type_lookup (stdoutput, lpe->reloc);
          int size = bfd_get_reloc_size (reloc_howto);
          char *where;
@@ -1441,7 +1842,7 @@ s390_literals (ignore)
          if (size > lpe->nbytes)
            as_bad (_("%s relocations do not fit in %d bytes"),
                    reloc_howto->name, lpe->nbytes);
          if (size > lpe->nbytes)
            as_bad (_("%s relocations do not fit in %d bytes"),
                    reloc_howto->name, lpe->nbytes);
-         where = frag_more(lpe->nbytes);
+         where = frag_more (lpe->nbytes);
          md_number_to_chars (where, 0, size);
          fix_new_exp (frag_now, where - frag_now->fr_literal,
                       size, &lpe->ex, reloc_howto->pc_relative, lpe->reloc);
          md_number_to_chars (where, 0, size);
          fix_new_exp (frag_now, where - frag_now->fr_literal,
                       size, &lpe->ex, reloc_howto->pc_relative, lpe->reloc);
@@ -1454,7 +1855,7 @@ s390_literals (ignore)
                generic_floating_point_number = lpe->floatnum;
              else
                memcpy (generic_bignum, lpe->bignum,
                generic_floating_point_number = lpe->floatnum;
              else
                memcpy (generic_bignum, lpe->bignum,
-                       lpe->ex.X_add_number*sizeof(LITTLENUM_TYPE));
+                       lpe->ex.X_add_number * sizeof (LITTLENUM_TYPE));
            }
          emit_expr (&lpe->ex, lpe->nbytes);
        }
            }
          emit_expr (&lpe->ex, lpe->nbytes);
        }
@@ -1468,70 +1869,192 @@ s390_literals (ignore)
   lpe_count = 0;
 }
 
   lpe_count = 0;
 }
 
-/* Turn a string in input_line_pointer into a floating point constant
-   of type type, and store the appropriate bytes in *litp.  The number
-   of LITTLENUMS emitted is stored in *sizep .  An error message is
-   returned, or NULL on OK.  */
+#define MAX_HISTORY 100
+
+/* The .machine pseudo op allows to switch to a different CPU level in
+   the asm listing.  The current CPU setting can be stored on a stack
+   with .machine push and restored with .machine pop.  */
 
 
-char *
-md_atof (type, litp, sizep)
-     int type;
-     char *litp;
-     int *sizep;
+static void
+s390_machine (int ignore ATTRIBUTE_UNUSED)
 {
 {
-  int prec;
-  LITTLENUM_TYPE words[4];
-  char *t;
-  int i;
+  char *cpu_string;
+  static struct cpu_history
+  {
+    unsigned int cpu;
+    unsigned int flags;
+  } *cpu_history;
+  static int curr_hist;
+
+  SKIP_WHITESPACE ();
 
 
-  switch (type)
+  if (*input_line_pointer == '"')
     {
     {
-    case 'f':
-      prec = 2;
-      break;
+      int len;
+      cpu_string = demand_copy_C_string (&len);
+    }
+  else
+    {
+      char c;
 
 
-    case 'd':
-      prec = 4;
-      break;
+      cpu_string = input_line_pointer;
+      do
+       {
+         char * str;
 
 
-    default:
-      *sizep = 0;
-      return "bad call to md_atof";
+         c = get_symbol_name (&str);
+         c = restore_line_pointer (c);
+         if (c == '+')
+           ++ input_line_pointer;
+       }
+      while (c == '+');
+
+      c = *input_line_pointer;
+      *input_line_pointer = 0;
+      cpu_string = xstrdup (cpu_string);
+      (void) restore_line_pointer (c);
+    }
+
+  if (cpu_string != NULL)
+    {
+      unsigned int new_cpu = current_cpu;
+      unsigned int new_flags = current_flags;
+
+      if (strcmp (cpu_string, "push") == 0)
+       {
+         if (cpu_history == NULL)
+           cpu_history = XNEWVEC (struct cpu_history, MAX_HISTORY);
+
+         if (curr_hist >= MAX_HISTORY)
+           as_bad (_(".machine stack overflow"));
+         else
+           {
+             cpu_history[curr_hist].cpu = current_cpu;
+             cpu_history[curr_hist].flags = current_flags;
+             curr_hist++;
+           }
+       }
+      else if (strcmp (cpu_string, "pop") == 0)
+       {
+         if (curr_hist <= 0)
+           as_bad (_(".machine stack underflow"));
+         else
+           {
+             curr_hist--;
+             new_cpu = cpu_history[curr_hist].cpu;
+             new_flags = cpu_history[curr_hist].flags;
+           }
+       }
+      else
+       new_cpu = s390_parse_cpu (cpu_string, &new_flags, TRUE);
+
+      if (new_cpu == S390_OPCODE_MAXCPU)
+       as_bad (_("invalid machine `%s'"), cpu_string);
+
+      if (new_cpu != current_cpu || new_flags != current_flags)
+       {
+         current_cpu = new_cpu;
+         current_flags = new_flags;
+         s390_setup_opcodes ();
+       }
     }
 
     }
 
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
+  demand_empty_rest_of_line ();
+}
+
+/* The .machinemode pseudo op allows to switch to a different
+   architecture mode in the asm listing.  The current architecture
+   mode setting can be stored on a stack with .machinemode push and
+   restored with .machinemode pop.  */
+
+static void
+s390_machinemode (int ignore ATTRIBUTE_UNUSED)
+{
+  char *mode_string;
+  static unsigned int *mode_history;
+  static int curr_hist;
 
 
-  *sizep = prec * 2;
+  SKIP_WHITESPACE ();
 
 
-  for (i = 0; i < prec; i++)
+  {
+    char c;
+
+    c = get_symbol_name (&mode_string);
+    mode_string = xstrdup (mode_string);
+    (void) restore_line_pointer (c);
+  }
+
+  if (mode_string != NULL)
     {
     {
-      md_number_to_chars (litp, (valueT) words[i], 2);
-      litp += 2;
+      unsigned int old_mode_mask = current_mode_mask;
+      char *p;
+
+      for (p = mode_string; *p != 0; p++)
+       *p = TOLOWER (*p);
+
+      if (strcmp (mode_string, "push") == 0)
+       {
+         if (mode_history == NULL)
+           mode_history = XNEWVEC (unsigned int, MAX_HISTORY);
+
+         if (curr_hist >= MAX_HISTORY)
+           as_bad (_(".machinemode stack overflow"));
+         else
+           mode_history[curr_hist++] = current_mode_mask;
+       }
+      else if (strcmp (mode_string, "pop") == 0)
+       {
+         if (curr_hist <= 0)
+           as_bad (_(".machinemode stack underflow"));
+         else
+           current_mode_mask = mode_history[--curr_hist];
+       }
+      else
+       {
+         if (strcmp (mode_string, "esa") == 0)
+           current_mode_mask = 1 << S390_OPCODE_ESA;
+         else if (strcmp (mode_string, "zarch") == 0)
+           {
+             if (s390_arch_size == 32)
+               set_highgprs_p = TRUE;
+             current_mode_mask = 1 << S390_OPCODE_ZARCH;
+           }
+         else if (strcmp (mode_string, "zarch_nohighgprs") == 0)
+           current_mode_mask = 1 << S390_OPCODE_ZARCH;
+         else
+           as_bad (_("invalid machine mode `%s'"), mode_string);
+       }
+
+      if (current_mode_mask != old_mode_mask)
+       s390_setup_opcodes ();
     }
     }
-     
-  return NULL;
+
+  demand_empty_rest_of_line ();
+}
+
+#undef MAX_HISTORY
+
+const char *
+md_atof (int type, char *litp, int *sizep)
+{
+  return ieee_md_atof (type, litp, sizep, TRUE);
 }
 
 /* Align a section (I don't know why this is machine dependent).  */
 
 valueT
 }
 
 /* Align a section (I don't know why this is machine dependent).  */
 
 valueT
-md_section_align (seg, addr)
-     asection *seg;
-     valueT addr;
+md_section_align (asection *seg, valueT addr)
 {
   int align = bfd_get_section_alignment (stdoutput, seg);
 
 {
   int align = bfd_get_section_alignment (stdoutput, seg);
 
-  return ((addr + (1 << align) - 1) & (-1 << align));
+  return ((addr + (1 << align) - 1) & -(1 << align));
 }
 
 /* We don't have any form of relaxing.  */
 
 int
 }
 
 /* We don't have any form of relaxing.  */
 
 int
-md_estimate_size_before_relax (fragp, seg)
-     fragS *fragp ATTRIBUTE_UNUSED;
-     asection *seg ATTRIBUTE_UNUSED;
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+                              asection *seg ATTRIBUTE_UNUSED)
 {
   abort ();
   return 0;
 {
   abort ();
   return 0;
@@ -1540,30 +2063,28 @@ md_estimate_size_before_relax (fragp, seg)
 /* Convert a machine dependent frag.  We never generate these.  */
 
 void
 /* Convert a machine dependent frag.  We never generate these.  */
 
 void
-md_convert_frag (abfd, sec, fragp)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     asection *sec ATTRIBUTE_UNUSED;
-     fragS *fragp ATTRIBUTE_UNUSED;
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+                asection *sec ATTRIBUTE_UNUSED,
+                fragS *fragp ATTRIBUTE_UNUSED)
 {
   abort ();
 }
 
 symbolS *
 {
   abort ();
 }
 
 symbolS *
-md_undefined_symbol (name)
-     char *name;
+md_undefined_symbol (char *name)
 {
 {
-  if (*name == '_' && *(name+1) == 'G'
-      && strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0)
-   {
-     if(!GOT_symbol)
-      {
-        if(symbol_find(name))
-          as_bad(_("GOT already in symbol table"));
-        GOT_symbol = symbol_new (name, undefined_section,
-                                 (valueT) 0, &zero_address_frag);
-      }
-     return GOT_symbol;
-   }
+  if (*name == '_' && *(name + 1) == 'G'
+      && strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
+    {
+      if (!GOT_symbol)
+       {
+         if (symbol_find (name))
+           as_bad (_("GOT already in symbol table"));
+         GOT_symbol = symbol_new (name, undefined_section,
+                                  (valueT) 0, &zero_address_frag);
+       }
+      return GOT_symbol;
+    }
   return 0;
 }
 
   return 0;
 }
 
@@ -1573,9 +2094,7 @@ md_undefined_symbol (name)
    given a PC relative reloc.  */
 
 long
    given a PC relative reloc.  */
 
 long
-md_pcrel_from_section (fixp, sec)
-     fixS *fixp;
-     segT sec ATTRIBUTE_UNUSED;
+md_pcrel_from_section (fixS *fixp, segT sec ATTRIBUTE_UNUSED)
 {
   return fixp->fx_frag->fr_address + fixp->fx_where;
 }
 {
   return fixp->fx_frag->fr_address + fixp->fx_where;
 }
@@ -1585,31 +2104,105 @@ md_pcrel_from_section (fixp, sec)
    to make sure that the dynamic relocations are done correctly, so in
    some cases we force the original symbol to be used.  */
 int
    to make sure that the dynamic relocations are done correctly, so in
    some cases we force the original symbol to be used.  */
 int
-tc_s390_fix_adjustable(fixP)
-     fixS * fixP;
+tc_s390_fix_adjustable (fixS *fixP)
 {
 {
-  /* Prevent all adjustments to global symbols.  */
-  if (S_IS_EXTERN (fixP->fx_addsy))
-    return 0;
-  if (S_IS_WEAK (fixP->fx_addsy))
+  /* Don't adjust references to merge sections.  */
+  if ((S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) != 0)
     return 0;
   /* adjust_reloc_syms doesn't know about the GOT.  */
     return 0;
   /* adjust_reloc_syms doesn't know about the GOT.  */
-  if (fixP->fx_r_type == BFD_RELOC_32_GOTOFF
+  if (   fixP->fx_r_type == BFD_RELOC_16_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_32_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_390_GOTOFF64
+      || fixP->fx_r_type == BFD_RELOC_390_PLTOFF16
+      || fixP->fx_r_type == BFD_RELOC_390_PLTOFF32
+      || fixP->fx_r_type == BFD_RELOC_390_PLTOFF64
+      || fixP->fx_r_type == BFD_RELOC_390_PLT12DBL
       || fixP->fx_r_type == BFD_RELOC_390_PLT16DBL
       || fixP->fx_r_type == BFD_RELOC_390_PLT16DBL
+      || fixP->fx_r_type == BFD_RELOC_390_PLT24DBL
       || fixP->fx_r_type == BFD_RELOC_390_PLT32
       || fixP->fx_r_type == BFD_RELOC_390_PLT32DBL
       || fixP->fx_r_type == BFD_RELOC_390_PLT64
       || fixP->fx_r_type == BFD_RELOC_390_GOT12
       || fixP->fx_r_type == BFD_RELOC_390_PLT32
       || fixP->fx_r_type == BFD_RELOC_390_PLT32DBL
       || fixP->fx_r_type == BFD_RELOC_390_PLT64
       || fixP->fx_r_type == BFD_RELOC_390_GOT12
+      || fixP->fx_r_type == BFD_RELOC_390_GOT20
       || fixP->fx_r_type == BFD_RELOC_390_GOT16
       || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
       || fixP->fx_r_type == BFD_RELOC_390_GOT64
       || fixP->fx_r_type == BFD_RELOC_390_GOTENT
       || fixP->fx_r_type == BFD_RELOC_390_GOT16
       || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
       || fixP->fx_r_type == BFD_RELOC_390_GOT64
       || fixP->fx_r_type == BFD_RELOC_390_GOTENT
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLT12
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLT16
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLT20
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLT32
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLT64
+      || fixP->fx_r_type == BFD_RELOC_390_GOTPLTENT
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_LOAD
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_GDCALL
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_LDCALL
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_GD32
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_GD64
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE12
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE20
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE32
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE64
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_LDM32
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_LDM64
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_IE32
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_IE64
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_IEENT
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_LE32
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_LE64
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_LDO32
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_LDO64
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_DTPMOD
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_DTPOFF
+      || fixP->fx_r_type == BFD_RELOC_390_TLS_TPOFF
       || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
       || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     return 0;
   return 1;
 }
 
       || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
       || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     return 0;
   return 1;
 }
 
+/* Return true if we must always emit a reloc for a type and false if
+   there is some hope of resolving it at assembly time.  */
+int
+tc_s390_force_relocation (struct fix *fixp)
+{
+  /* Ensure we emit a relocation for every reference to the global
+     offset table or to the procedure link table.  */
+  switch (fixp->fx_r_type)
+    {
+    case BFD_RELOC_390_GOT12:
+    case BFD_RELOC_390_GOT20:
+    case BFD_RELOC_32_GOT_PCREL:
+    case BFD_RELOC_32_GOTOFF:
+    case BFD_RELOC_390_GOTOFF64:
+    case BFD_RELOC_390_PLTOFF16:
+    case BFD_RELOC_390_PLTOFF32:
+    case BFD_RELOC_390_PLTOFF64:
+    case BFD_RELOC_390_GOTPC:
+    case BFD_RELOC_390_GOT16:
+    case BFD_RELOC_390_GOTPCDBL:
+    case BFD_RELOC_390_GOT64:
+    case BFD_RELOC_390_GOTENT:
+    case BFD_RELOC_390_PLT32:
+    case BFD_RELOC_390_PLT12DBL:
+    case BFD_RELOC_390_PLT16DBL:
+    case BFD_RELOC_390_PLT24DBL:
+    case BFD_RELOC_390_PLT32DBL:
+    case BFD_RELOC_390_PLT64:
+    case BFD_RELOC_390_GOTPLT12:
+    case BFD_RELOC_390_GOTPLT16:
+    case BFD_RELOC_390_GOTPLT20:
+    case BFD_RELOC_390_GOTPLT32:
+    case BFD_RELOC_390_GOTPLT64:
+    case BFD_RELOC_390_GOTPLTENT:
+      return 1;
+    default:
+      break;
+    }
+
+  return generic_force_reloc (fixp);
+}
+
 /* Apply a fixup to the object code.  This is called for all the
    fixups we generated by the call to fix_new_exp, above.  In the call
    above we used a reloc code which was the largest legal reloc code
 /* Apply a fixup to the object code.  This is called for all the
    fixups we generated by the call to fix_new_exp, above.  In the call
    above we used a reloc code which was the largest legal reloc code
@@ -1619,250 +2212,328 @@ tc_s390_fix_adjustable(fixP)
    that, we determine the correct reloc code and put it back in the
    fixup.  */
 
    that, we determine the correct reloc code and put it back in the
    fixup.  */
 
-int
-md_apply_fix3 (fixp, valuep, seg)
-     fixS *fixp;
-     valueT *valuep;
-     segT seg ATTRIBUTE_UNUSED;
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
   char *where;
 {
   char *where;
-  valueT value;
+  valueT value = *valP;
 
 
-  value = *valuep;
-  where = fixp->fx_frag->fr_literal + fixp->fx_where;
+  where = fixP->fx_frag->fr_literal + fixP->fx_where;
 
 
-  if (fixp->fx_subsy != NULL)
-    {
-      if (!S_IS_DEFINED (fixp->fx_subsy))
-       as_bad_where (fixp->fx_file, fixp->fx_line,
-                     _("unresolved fx_subsy symbol that must be resolved"));
-      value -= S_GET_VALUE(fixp->fx_subsy);
-    }
+  if (fixP->fx_subsy != NULL)
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("cannot emit relocation %s against subsy symbol %s"),
+                 bfd_get_reloc_code_name (fixP->fx_r_type),
+                 S_GET_NAME (fixP->fx_subsy));
 
 
-  if (fixp->fx_addsy != NULL)
+  if (fixP->fx_addsy != NULL)
     {
     {
-      /* `*valuep' may contain the value of the symbol on which the reloc
-        will be based; we have to remove it.  */
-      if (fixp->fx_addsy->sy_used_in_reloc
-         && S_GET_SEGMENT (fixp->fx_addsy) != absolute_section
-         && S_GET_SEGMENT (fixp->fx_addsy) != undefined_section
-         && ! bfd_is_com_section (S_GET_SEGMENT (fixp->fx_addsy)))
-       value -= S_GET_VALUE (fixp->fx_addsy);
-      
-      if (fixp->fx_pcrel)
-       value += fixp->fx_frag->fr_address + fixp->fx_where;
+      if (fixP->fx_pcrel)
+       value += fixP->fx_frag->fr_address + fixP->fx_where;
     }
   else
     }
   else
-    fixp->fx_done = 1;
+    fixP->fx_done = 1;
 
 
-  if ((int) fixp->fx_r_type >= (int) BFD_RELOC_UNUSED)
+  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
     {
       const struct s390_operand *operand;
       int opindex;
     {
       const struct s390_operand *operand;
       int opindex;
-    
-      opindex = (int) fixp->fx_r_type - (int) BFD_RELOC_UNUSED;
+
+      opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
       operand = &s390_operands[opindex];
       operand = &s390_operands[opindex];
-      
-      if (fixp->fx_done)
+
+      if (fixP->fx_done)
        {
          /* Insert the fully resolved operand value.  */
        {
          /* Insert the fully resolved operand value.  */
-         s390_insert_operand (where, operand, (offsetT) value,
-                              fixp->fx_file, fixp->fx_line);
-
-         return 1;
+         s390_insert_operand ((unsigned char *) where, operand,
+                              (offsetT) value, fixP->fx_file, fixP->fx_line);
+         return;
        }
        }
-    
+
       /* Determine a BFD reloc value based on the operand information.
         We are only prepared to turn a few of the operands into
         relocs.  */
       /* Determine a BFD reloc value based on the operand information.
         We are only prepared to turn a few of the operands into
         relocs.  */
-      fixp->fx_offset = value;
+      fixP->fx_offset = value;
       if (operand->bits == 12 && operand->shift == 20)
        {
       if (operand->bits == 12 && operand->shift == 20)
        {
-         fixp->fx_size = 2;
-         fixp->fx_where += 2;
-         fixp->fx_r_type = BFD_RELOC_390_12;
+         fixP->fx_size = 2;
+         fixP->fx_where += 2;
+         fixP->fx_r_type = BFD_RELOC_390_12;
        }
       else if (operand->bits == 12 && operand->shift == 36)
        {
        }
       else if (operand->bits == 12 && operand->shift == 36)
        {
-         fixp->fx_size = 2;
-         fixp->fx_where += 4;
-         fixp->fx_r_type = BFD_RELOC_390_12;
+         fixP->fx_size = 2;
+         fixP->fx_where += 4;
+         fixP->fx_r_type = BFD_RELOC_390_12;
+       }
+      else if (operand->bits == 20 && operand->shift == 20)
+       {
+         fixP->fx_size = 2;
+         fixP->fx_where += 2;
+         fixP->fx_r_type = BFD_RELOC_390_20;
        }
       else if (operand->bits == 8 && operand->shift == 8)
        {
        }
       else if (operand->bits == 8 && operand->shift == 8)
        {
-         fixp->fx_size = 1;
-         fixp->fx_where += 1;
-         fixp->fx_r_type = BFD_RELOC_8;
+         fixP->fx_size = 1;
+         fixP->fx_where += 1;
+         fixP->fx_r_type = BFD_RELOC_8;
+       }
+      else if (operand->bits == 12 && operand->shift == 12
+              && (operand->flags & S390_OPERAND_PCREL))
+       {
+         fixP->fx_size = 2;
+         fixP->fx_where += 1;
+         fixP->fx_offset += 1;
+         fixP->fx_r_type = BFD_RELOC_390_PC12DBL;
        }
       else if (operand->bits == 16 && operand->shift == 16)
        {
        }
       else if (operand->bits == 16 && operand->shift == 16)
        {
-         fixp->fx_size = 2;
-         fixp->fx_where += 2;
+         fixP->fx_size = 2;
+         fixP->fx_where += 2;
          if (operand->flags & S390_OPERAND_PCREL)
            {
          if (operand->flags & S390_OPERAND_PCREL)
            {
-             fixp->fx_r_type = BFD_RELOC_390_PC16DBL;
-             fixp->fx_offset += 2;
+             fixP->fx_r_type = BFD_RELOC_390_PC16DBL;
+             fixP->fx_offset += 2;
            }
          else
            }
          else
-           fixp->fx_r_type = BFD_RELOC_16;
+           fixP->fx_r_type = BFD_RELOC_16;
+       }
+      else if (operand->bits == 24 && operand->shift == 24
+              && (operand->flags & S390_OPERAND_PCREL))
+       {
+         fixP->fx_size = 3;
+         fixP->fx_where += 3;
+         fixP->fx_offset += 3;
+         fixP->fx_r_type = BFD_RELOC_390_PC24DBL;
        }
        }
-      else if (operand->bits == 32 && operand->shift == 16 &&
-              (operand->flags & S390_OPERAND_PCREL))
+      else if (operand->bits == 32 && operand->shift == 16
+              && (operand->flags & S390_OPERAND_PCREL))
        {
        {
-         fixp->fx_size = 4;
-         fixp->fx_where += 2;
-         fixp->fx_offset += 2;
-         fixp->fx_r_type = BFD_RELOC_390_PC32DBL;
+         fixP->fx_size = 4;
+         fixP->fx_where += 2;
+         fixP->fx_offset += 2;
+         fixP->fx_r_type = BFD_RELOC_390_PC32DBL;
        }
       else
        {
        }
       else
        {
-         char *sfile;
+         const char *sfile;
          unsigned int sline;
          unsigned int sline;
-      
+
          /* Use expr_symbol_where to see if this is an expression
             symbol.  */
          /* Use expr_symbol_where to see if this is an expression
             symbol.  */
-         if (expr_symbol_where (fixp->fx_addsy, &sfile, &sline))
-           as_bad_where (fixp->fx_file, fixp->fx_line,
+         if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("unresolved expression that must be resolved"));
          else
                          _("unresolved expression that must be resolved"));
          else
-           as_bad_where (fixp->fx_file, fixp->fx_line,
+           as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("unsupported relocation type"));
                          _("unsupported relocation type"));
-         fixp->fx_done = 1;
-         return 1;
+         fixP->fx_done = 1;
+         return;
        }
     }
   else
     {
        }
     }
   else
     {
-      switch (fixp->fx_r_type)
+      switch (fixP->fx_r_type)
        {
        case BFD_RELOC_8:
        {
        case BFD_RELOC_8:
-         if (fixp->fx_pcrel)
+         if (fixP->fx_pcrel)
            abort ();
            abort ();
-         if (fixp->fx_done)
+         if (fixP->fx_done)
            md_number_to_chars (where, value, 1);
          break;
        case BFD_RELOC_390_12:
        case BFD_RELOC_390_GOT12:
            md_number_to_chars (where, value, 1);
          break;
        case BFD_RELOC_390_12:
        case BFD_RELOC_390_GOT12:
-         if (fixp->fx_done)
+       case BFD_RELOC_390_GOTPLT12:
+       case BFD_RELOC_390_PC12DBL:
+       case BFD_RELOC_390_PLT12DBL:
+         if (fixP->fx_pcrel)
+           value++;
+
+         if (fixP->fx_done)
            {
              unsigned short mop;
 
            {
              unsigned short mop;
 
+             if (fixP->fx_pcrel)
+               value >>= 1;
+
              mop = bfd_getb16 ((unsigned char *) where);
              mop |= (unsigned short) (value & 0xfff);
              bfd_putb16 ((bfd_vma) mop, (unsigned char *) where);
              mop = bfd_getb16 ((unsigned char *) where);
              mop |= (unsigned short) (value & 0xfff);
              bfd_putb16 ((bfd_vma) mop, (unsigned char *) where);
-           } 
+           }
          break;
          break;
-    
+
+       case BFD_RELOC_390_20:
+       case BFD_RELOC_390_GOT20:
+       case BFD_RELOC_390_GOTPLT20:
+         if (fixP->fx_done)
+           {
+             unsigned int mop;
+             mop = bfd_getb32 ((unsigned char *) where);
+             mop |= (unsigned int) ((value & 0xfff) << 8 |
+                                    (value & 0xff000) >> 12);
+             bfd_putb32 ((bfd_vma) mop, (unsigned char *) where);
+           }
+         break;
+
        case BFD_RELOC_16:
        case BFD_RELOC_GPREL16:
        case BFD_RELOC_16_GOT_PCREL:
        case BFD_RELOC_16_GOTOFF:
        case BFD_RELOC_16:
        case BFD_RELOC_GPREL16:
        case BFD_RELOC_16_GOT_PCREL:
        case BFD_RELOC_16_GOTOFF:
-         if (fixp->fx_pcrel)
-           as_bad_where (fixp->fx_file, fixp->fx_line,
-                         "cannot emit PC relative %s relocation%s%s",
-                         bfd_get_reloc_code_name (fixp->fx_r_type),
-                         fixp->fx_addsy != NULL ? " against " : "",
-                         (fixp->fx_addsy != NULL
-                          ? S_GET_NAME (fixp->fx_addsy)
+         if (fixP->fx_pcrel)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("cannot emit PC relative %s relocation%s%s"),
+                         bfd_get_reloc_code_name (fixP->fx_r_type),
+                         fixP->fx_addsy != NULL ? " against " : "",
+                         (fixP->fx_addsy != NULL
+                          ? S_GET_NAME (fixP->fx_addsy)
                           : ""));
                           : ""));
-         if (fixp->fx_done)
+         if (fixP->fx_done)
            md_number_to_chars (where, value, 2);
          break;
        case BFD_RELOC_390_GOT16:
            md_number_to_chars (where, value, 2);
          break;
        case BFD_RELOC_390_GOT16:
-         if (fixp->fx_done)
+       case BFD_RELOC_390_PLTOFF16:
+       case BFD_RELOC_390_GOTPLT16:
+         if (fixP->fx_done)
            md_number_to_chars (where, value, 2);
          break;
        case BFD_RELOC_390_PC16DBL:
        case BFD_RELOC_390_PLT16DBL:
          value += 2;
            md_number_to_chars (where, value, 2);
          break;
        case BFD_RELOC_390_PC16DBL:
        case BFD_RELOC_390_PLT16DBL:
          value += 2;
-         if (fixp->fx_done)
+         if (fixP->fx_done)
            md_number_to_chars (where, (offsetT) value >> 1, 2);
          break;
 
            md_number_to_chars (where, (offsetT) value >> 1, 2);
          break;
 
+       case BFD_RELOC_390_PC24DBL:
+       case BFD_RELOC_390_PLT24DBL:
+         value += 3;
+         if (fixP->fx_done)
+           {
+             unsigned int mop;
+             value >>= 1;
+
+             mop = bfd_getb32 ((unsigned char *) where - 1);
+             mop |= (unsigned int) (value & 0xffffff);
+             bfd_putb32 ((bfd_vma) mop, (unsigned char *) where - 1);
+           }
+         break;
+
        case BFD_RELOC_32:
        case BFD_RELOC_32:
-         if (fixp->fx_pcrel)
-           fixp->fx_r_type = BFD_RELOC_32_PCREL;
+         if (fixP->fx_pcrel)
+           fixP->fx_r_type = BFD_RELOC_32_PCREL;
          else
          else
-           fixp->fx_r_type = BFD_RELOC_32;
-         if (fixp->fx_done)
+           fixP->fx_r_type = BFD_RELOC_32;
+         if (fixP->fx_done)
            md_number_to_chars (where, value, 4);
          break;
        case BFD_RELOC_32_PCREL:
        case BFD_RELOC_32_BASEREL:
            md_number_to_chars (where, value, 4);
          break;
        case BFD_RELOC_32_PCREL:
        case BFD_RELOC_32_BASEREL:
-         fixp->fx_r_type = BFD_RELOC_32_PCREL;
-         if (fixp->fx_done)
+         fixP->fx_r_type = BFD_RELOC_32_PCREL;
+         if (fixP->fx_done)
            md_number_to_chars (where, value, 4);
          break;
        case BFD_RELOC_32_GOT_PCREL:
            md_number_to_chars (where, value, 4);
          break;
        case BFD_RELOC_32_GOT_PCREL:
+       case BFD_RELOC_390_PLTOFF32:
        case BFD_RELOC_390_PLT32:
        case BFD_RELOC_390_PLT32:
-         if (fixp->fx_done)
+       case BFD_RELOC_390_GOTPLT32:
+         if (fixP->fx_done)
            md_number_to_chars (where, value, 4);
          break;
        case BFD_RELOC_390_PC32DBL:
        case BFD_RELOC_390_PLT32DBL:
        case BFD_RELOC_390_GOTPCDBL:
        case BFD_RELOC_390_GOTENT:
            md_number_to_chars (where, value, 4);
          break;
        case BFD_RELOC_390_PC32DBL:
        case BFD_RELOC_390_PLT32DBL:
        case BFD_RELOC_390_GOTPCDBL:
        case BFD_RELOC_390_GOTENT:
+       case BFD_RELOC_390_GOTPLTENT:
          value += 2;
          value += 2;
-         if (fixp->fx_done)
+         if (fixP->fx_done)
            md_number_to_chars (where, (offsetT) value >> 1, 4);
          break;
 
        case BFD_RELOC_32_GOTOFF:
            md_number_to_chars (where, (offsetT) value >> 1, 4);
          break;
 
        case BFD_RELOC_32_GOTOFF:
-         if (fixp->fx_done)
-           md_number_to_chars (where, value, sizeof(int));
+         if (fixP->fx_done)
+           md_number_to_chars (where, value, sizeof (int));
+         break;
+
+       case BFD_RELOC_390_GOTOFF64:
+         if (fixP->fx_done)
+           md_number_to_chars (where, value, 8);
          break;
 
        case BFD_RELOC_390_GOT64:
          break;
 
        case BFD_RELOC_390_GOT64:
+       case BFD_RELOC_390_PLTOFF64:
        case BFD_RELOC_390_PLT64:
        case BFD_RELOC_390_PLT64:
-         if (fixp->fx_done)
+       case BFD_RELOC_390_GOTPLT64:
+         if (fixP->fx_done)
            md_number_to_chars (where, value, 8);
          break;
 
        case BFD_RELOC_64:
            md_number_to_chars (where, value, 8);
          break;
 
        case BFD_RELOC_64:
-         if (fixp->fx_pcrel)
-           fixp->fx_r_type = BFD_RELOC_64_PCREL;
+         if (fixP->fx_pcrel)
+           fixP->fx_r_type = BFD_RELOC_64_PCREL;
          else
          else
-           fixp->fx_r_type = BFD_RELOC_64;
-         if (fixp->fx_done)
+           fixP->fx_r_type = BFD_RELOC_64;
+         if (fixP->fx_done)
            md_number_to_chars (where, value, 8);
          break;
 
        case BFD_RELOC_64_PCREL:
            md_number_to_chars (where, value, 8);
          break;
 
        case BFD_RELOC_64_PCREL:
-         fixp->fx_r_type = BFD_RELOC_64_PCREL;
-         if (fixp->fx_done)
+         fixP->fx_r_type = BFD_RELOC_64_PCREL;
+         if (fixP->fx_done)
            md_number_to_chars (where, value, 8);
          break;
 
        case BFD_RELOC_VTABLE_INHERIT:
        case BFD_RELOC_VTABLE_ENTRY:
            md_number_to_chars (where, value, 8);
          break;
 
        case BFD_RELOC_VTABLE_INHERIT:
        case BFD_RELOC_VTABLE_ENTRY:
-         fixp->fx_done = 0;
-         return 1;
+         fixP->fx_done = 0;
+         return;
+
+       case BFD_RELOC_390_TLS_LOAD:
+       case BFD_RELOC_390_TLS_GDCALL:
+       case BFD_RELOC_390_TLS_LDCALL:
+       case BFD_RELOC_390_TLS_GD32:
+       case BFD_RELOC_390_TLS_GD64:
+       case BFD_RELOC_390_TLS_GOTIE12:
+       case BFD_RELOC_390_TLS_GOTIE20:
+       case BFD_RELOC_390_TLS_GOTIE32:
+       case BFD_RELOC_390_TLS_GOTIE64:
+       case BFD_RELOC_390_TLS_LDM32:
+       case BFD_RELOC_390_TLS_LDM64:
+       case BFD_RELOC_390_TLS_IE32:
+       case BFD_RELOC_390_TLS_IE64:
+       case BFD_RELOC_390_TLS_LE32:
+       case BFD_RELOC_390_TLS_LE64:
+       case BFD_RELOC_390_TLS_LDO32:
+       case BFD_RELOC_390_TLS_LDO64:
+       case BFD_RELOC_390_TLS_DTPMOD:
+       case BFD_RELOC_390_TLS_DTPOFF:
+       case BFD_RELOC_390_TLS_TPOFF:
+         S_SET_THREAD_LOCAL (fixP->fx_addsy);
+         /* Fully resolved at link time.  */
+         break;
+       case BFD_RELOC_390_TLS_IEENT:
+         /* Fully resolved at link time.  */
+         S_SET_THREAD_LOCAL (fixP->fx_addsy);
+         value += 2;
+         break;
 
        default:
          {
 
        default:
          {
-           const char *reloc_name = bfd_get_reloc_code_name (fixp->fx_r_type);
-           
+           const char *reloc_name = bfd_get_reloc_code_name (fixP->fx_r_type);
+
            if (reloc_name != NULL)
            if (reloc_name != NULL)
-             fprintf (stderr, "Gas failure, reloc type %s\n", reloc_name);
+             as_fatal (_("Gas failure, reloc type %s\n"), reloc_name);
            else
            else
-             fprintf (stderr, "Gas failure, reloc type #%i\n", fixp->fx_r_type);
-           fflush (stderr);
-           abort ();
+             as_fatal (_("Gas failure, reloc type #%i\n"), fixP->fx_r_type);
          }
        }
 
          }
        }
 
-      fixp->fx_offset = value;
+      fixP->fx_offset = value;
     }
     }
-
-  return 1;
 }
 
 /* Generate a reloc for a fixup.  */
 
 arelent *
 }
 
 /* Generate a reloc for a fixup.  */
 
 arelent *
-tc_gen_reloc (seg, fixp)
-     asection *seg ATTRIBUTE_UNUSED;
-     fixS *fixp;
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
 {
   bfd_reloc_code_real_type code;
   arelent *reloc;
 {
   bfd_reloc_code_real_type code;
   arelent *reloc;
@@ -1870,40 +2541,59 @@ tc_gen_reloc (seg, fixp)
   code = fixp->fx_r_type;
   if (GOT_symbol && fixp->fx_addsy == GOT_symbol)
     {
   code = fixp->fx_r_type;
   if (GOT_symbol && fixp->fx_addsy == GOT_symbol)
     {
-      if ((s390_arch_size == 32 && code == BFD_RELOC_32_PCREL) ||
-         (s390_arch_size == 64 && code == BFD_RELOC_64_PCREL))
+      if (   (s390_arch_size == 32 && code == BFD_RELOC_32_PCREL)
+         || (s390_arch_size == 64 && code == BFD_RELOC_64_PCREL))
        code = BFD_RELOC_390_GOTPC;
       if (code == BFD_RELOC_390_PC32DBL)
        code = BFD_RELOC_390_GOTPCDBL;
     }
 
        code = BFD_RELOC_390_GOTPC;
       if (code == BFD_RELOC_390_PC32DBL)
        code = BFD_RELOC_390_GOTPCDBL;
     }
 
-  reloc = (arelent *) xmalloc (sizeof (arelent));
-  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  reloc = XNEW (arelent);
+  reloc->sym_ptr_ptr = XNEW (asymbol *);
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
   reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
   if (reloc->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
   reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
   if (reloc->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
-                    _("cannot represent relocation type %s"),
-                    bfd_get_reloc_code_name (code));
+                   _("cannot represent relocation type %s"),
+                   bfd_get_reloc_code_name (code));
       /* Set howto to a garbage value so that we can keep going.  */
       reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
       /* Set howto to a garbage value so that we can keep going.  */
       reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
-      assert (reloc->howto != NULL);
+      gas_assert (reloc->howto != NULL);
     }
   reloc->addend = fixp->fx_offset;
 
   return reloc;
 }
 
     }
   reloc->addend = fixp->fx_offset;
 
   return reloc;
 }
 
+void
+s390_cfi_frame_initial_instructions (void)
+{
+  cfi_add_CFA_def_cfa (15, s390_arch_size == 64 ? 160 : 96);
+}
+
 int
 int
-s390_force_relocation (fixp)
-     struct fix * fixp;
+tc_s390_regname_to_dw2regnum (char *regname)
 {
 {
-  if (   fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
-      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
-    return 1;
+  int regnum = -1;
 
 
-  return 0;
+  if (regname[0] != 'c' && regname[0] != 'a')
+    {
+      regnum = reg_name_search (regname);
+      if (regname[0] == 'f' && regnum != -1)
+        regnum += 16;
+    }
+  else if (strcmp (regname, "ap") == 0)
+    regnum = 32;
+  else if (strcmp (regname, "cc") == 0)
+    regnum = 33;
+  return regnum;
 }
 
 }
 
+void
+s390_elf_final_processing (void)
+{
+  if (set_highgprs_p)
+    elf_elfheader (stdoutput)->e_flags |= EF_S390_HIGH_GPRS;
+}
This page took 0.05821 seconds and 4 git commands to generate.