* config/tc-ia64.c (ia64_handle_align): Remove bogus be_nop.
[deliverable/binutils-gdb.git] / gas / config / tc-ia64.c
index 065a0eefb2ffe6248212eb7a61b990b11148c7d6..647724bed6ca6d05e7f32b3dd17a4230ae7f5cbe 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation.
+   Copyright 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
    Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
 
    This file is part of GAS, the GNU Assembler.
@@ -24,7 +24,6 @@
 
   - optional operands
   - directives:
-       .alias
        .eb
        .estate
        .lb
@@ -43,6 +42,7 @@
  */
 
 #include "as.h"
+#include "safe-ctype.h"
 #include "dwarf2dbg.h"
 #include "subsegs.h"
 
 
 enum special_section
   {
+    /* IA-64 ABI section pseudo-ops.  */
     SPECIAL_SECTION_BSS = 0,
     SPECIAL_SECTION_SBSS,
     SPECIAL_SECTION_SDATA,
     SPECIAL_SECTION_RODATA,
     SPECIAL_SECTION_COMMENT,
     SPECIAL_SECTION_UNWIND,
-    SPECIAL_SECTION_UNWIND_INFO
+    SPECIAL_SECTION_UNWIND_INFO,
+    /* HPUX specific section pseudo-ops.  */
+    SPECIAL_SECTION_INIT_ARRAY,
+    SPECIAL_SECTION_FINI_ARRAY,
   };
 
 enum reloc_func
   {
+    FUNC_DTP_MODULE,
+    FUNC_DTP_RELATIVE,
     FUNC_FPTR_RELATIVE,
     FUNC_GP_RELATIVE,
     FUNC_LT_RELATIVE,
+    FUNC_LT_RELATIVE_X,
     FUNC_PC_RELATIVE,
     FUNC_PLT_RELATIVE,
     FUNC_SEC_RELATIVE,
     FUNC_SEG_RELATIVE,
+    FUNC_TP_RELATIVE,
     FUNC_LTV_RELATIVE,
     FUNC_LT_FPTR_RELATIVE,
+    FUNC_LT_DTP_MODULE,
+    FUNC_LT_DTP_RELATIVE,
+    FUNC_LT_TP_RELATIVE,
+    FUNC_IPLT_RELOC,
   };
 
 enum reg_symbol
@@ -124,6 +136,13 @@ enum dynreg_type
     DYNREG_NUM_TYPES
   };
 
+enum operand_match_result
+  {
+    OPERAND_MATCH,
+    OPERAND_OUT_OF_RANGE,
+    OPERAND_MISMATCH
+  };
+
 /* On the ia64, we can't know the address of a text label until the
    instructions are packed into a bundle.  To handle this, we keep
    track of the list of labels that appear in front of each
@@ -136,6 +155,20 @@ struct label_fix
 
 extern int target_big_endian;
 
+void (*ia64_number_to_chars) PARAMS ((char *, valueT, int));
+
+static void ia64_float_to_chars_bigendian
+  PARAMS ((char *, LITTLENUM_TYPE *, int));
+static void ia64_float_to_chars_littleendian
+  PARAMS ((char *, LITTLENUM_TYPE *, int));
+static void (*ia64_float_to_chars)
+  PARAMS ((char *, LITTLENUM_TYPE *, int));
+
+static struct hash_control *alias_hash;
+static struct hash_control *alias_name_hash;
+static struct hash_control *secalias_hash;
+static struct hash_control *secalias_name_hash;
+
 /* Characters which always start a comment.  */
 const char comment_chars[] = "";
 
@@ -156,7 +189,7 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 /* ia64-specific option processing:  */
 
-const char *md_shortopts = "M:N:x::";
+const char *md_shortopts = "m:N:x::";
 
 struct option md_longopts[] =
   {
@@ -190,7 +223,8 @@ static struct
       explicit_mode : 1,            /* which mode we're in */
       default_explicit_mode : 1,    /* which mode is the default */
       mode_explicitly_set : 1,      /* was the current mode explicitly set? */
-      auto_align : 1;
+      auto_align : 1,
+      keep_pending_output : 1;
 
     /* Each bundle consists of up to three instructions.  We keep
        track of four most recent instructions so we can correctly set
@@ -219,6 +253,7 @@ static struct
        fixup[2];                       /* at most two fixups per insn */
        struct ia64_opcode *idesc;
        struct label_fix *label_fixups;
+       struct label_fix *tag_fixups;
        struct unw_rec_list *unwind_record;     /* Unwind directive.  */
        expressionS opnd[6];
        char *src_file;
@@ -251,6 +286,28 @@ static struct
                                       the current DV-checking block.  */
     int maxpaths;                   /* size currently allocated for
                                       entry_labels */
+    /* Support for hardware errata workarounds.  */
+
+    /* Record data about the last three insn groups.  */
+    struct group
+    {
+      /* B-step workaround.
+        For each predicate register, this is set if the corresponding insn
+        group conditionally sets this register with one of the affected
+        instructions.  */
+      int p_reg_set[64];
+      /* B-step workaround.
+        For each general register, this is set if the corresponding insn
+        a) is conditional one one of the predicate registers for which
+           P_REG_SET is 1 in the corresponding entry of the previous group,
+        b) sets this general register with one of the affected
+           instructions.  */
+      int g_reg_set_conditionally[128];
+    } last_groups[3];
+    int group_idx;
+
+    int pointer_size;       /* size in bytes of a pointer */
+    int pointer_size_shift; /* shift size of a pointer for alignment */
   }
 md;
 
@@ -439,15 +496,23 @@ static struct
 pseudo_func[] =
   {
     /* reloc pseudo functions (these must come first!):  */
-    { "fptr",  PSEUDO_FUNC_RELOC },
-    { "gprel", PSEUDO_FUNC_RELOC },
-    { "ltoff", PSEUDO_FUNC_RELOC },
-    { "pcrel", PSEUDO_FUNC_RELOC },
-    { "pltoff",        PSEUDO_FUNC_RELOC },
-    { "secrel",        PSEUDO_FUNC_RELOC },
-    { "segrel",        PSEUDO_FUNC_RELOC },
-    { "ltv",   PSEUDO_FUNC_RELOC },
-    { 0, },            /* placeholder for FUNC_LT_FPTR_RELATIVE */
+    { "dtpmod",        PSEUDO_FUNC_RELOC, { 0 } },
+    { "dtprel",        PSEUDO_FUNC_RELOC, { 0 } },
+    { "fptr",  PSEUDO_FUNC_RELOC, { 0 } },
+    { "gprel", PSEUDO_FUNC_RELOC, { 0 } },
+    { "ltoff", PSEUDO_FUNC_RELOC, { 0 } },
+    { "ltoffx",        PSEUDO_FUNC_RELOC, { 0 } },
+    { "pcrel", PSEUDO_FUNC_RELOC, { 0 } },
+    { "pltoff",        PSEUDO_FUNC_RELOC, { 0 } },
+    { "secrel",        PSEUDO_FUNC_RELOC, { 0 } },
+    { "segrel",        PSEUDO_FUNC_RELOC, { 0 } },
+    { "tprel", PSEUDO_FUNC_RELOC, { 0 } },
+    { "ltv",   PSEUDO_FUNC_RELOC, { 0 } },
+    { "", 0, { 0 } },  /* placeholder for FUNC_LT_FPTR_RELATIVE */
+    { "", 0, { 0 } },  /* placeholder for FUNC_LT_DTP_MODULE */
+    { "", 0, { 0 } },  /* placeholder for FUNC_LT_DTP_RELATIVE */
+    { "", 0, { 0 } },  /* placeholder for FUNC_LT_TP_RELATIVE */
+    { "iplt",  PSEUDO_FUNC_RELOC, { 0 } },
 
     /* mbtype4 constants:  */
     { "alt",   PSEUDO_FUNC_CONST, { 0xa } },
@@ -469,10 +534,17 @@ pseudo_func[] =
 
     { "natval",        PSEUDO_FUNC_CONST, { 0x100 } }, /* old usage */
 
+    /* hint constants: */
+    { "pause", PSEUDO_FUNC_CONST, { 0x0 } },
+
     /* unwind-related constants:  */
-    { "svr4",  PSEUDO_FUNC_CONST, { 0 } },
-    { "hpux",  PSEUDO_FUNC_CONST, { 1 } },
-    { "nt",    PSEUDO_FUNC_CONST, { 2 } },
+    { "svr4",  PSEUDO_FUNC_CONST,      { ELFOSABI_NONE } },
+    { "hpux",  PSEUDO_FUNC_CONST,      { ELFOSABI_HPUX } },
+    { "nt",    PSEUDO_FUNC_CONST,      { 2 } },                /* conflicts w/ELFOSABI_NETBSD */
+    { "linux", PSEUDO_FUNC_CONST,      { ELFOSABI_LINUX } },
+    { "freebsd", PSEUDO_FUNC_CONST,    { ELFOSABI_FREEBSD } },
+    { "openvms", PSEUDO_FUNC_CONST,    { ELFOSABI_OPENVMS } },
+    { "nsk",   PSEUDO_FUNC_CONST,      { ELFOSABI_NSK } },
 
     /* unwind-related registers:  */
     { "priunat",PSEUDO_FUNC_REG, { REG_PRIUNAT } }
@@ -495,7 +567,13 @@ static const bfd_vma nop[IA64_NUM_UNITS] =
 static char special_section_name[][20] =
   {
     {".bss"}, {".sbss"}, {".sdata"}, {".rodata"}, {".comment"},
-    {".IA_64.unwind"}, {".IA_64.unwind_info"}
+    {".IA_64.unwind"}, {".IA_64.unwind_info"},
+    {".init_array"}, {".fini_array"}
+  };
+
+static char *special_linkonce_name[] =
+  {
+    ".gnu.linkonce.ia64unw.", ".gnu.linkonce.ia64unwi."
   };
 
 /* The best template for a particular sequence of up to three
@@ -519,7 +597,7 @@ static struct rsrc {
   int data_srlz;                    /* current data serialization state */
   int qp_regno;                     /* qualifying predicate for this usage */
   char *file;                       /* what file marked this dependency */
-  int line;                         /* what line marked this dependency */
+  unsigned int line;                /* what line marked this dependency */
   struct mem_offset mem_offset;     /* optional memory offset hint */
   enum { CMP_NONE, CMP_OR, CMP_AND } cmp_type; /* OR or AND compare? */
   int path;                         /* corresponding code entry index */
@@ -556,22 +634,42 @@ static struct gr {
   unsigned known:1;
   int path;
   valueT value;
-} gr_values[128] = {{ 1, 0 }};
+} gr_values[128] = {{ 1, 0, 0 }};
 
 /* These are the routines required to output the various types of
    unwind records.  */
 
+/* A slot_number is a frag address plus the slot index (0-2).  We use the
+   frag address here so that if there is a section switch in the middle of
+   a function, then instructions emitted to a different section are not
+   counted.  Since there may be more than one frag for a function, this
+   means we also need to keep track of which frag this address belongs to
+   so we can compute inter-frag distances.  This also nicely solves the
+   problem with nops emitted for align directives, which can't easily be
+   counted, but can easily be derived from frag sizes.  */
+
 typedef struct unw_rec_list {
   unwind_record r;
   unsigned long slot_number;
+  fragS *slot_frag;
   struct unw_rec_list *next;
 } unw_rec_list;
 
-#define SLOT_NUM_NOT_SET        -1
+#define SLOT_NUM_NOT_SET        (unsigned)-1
+
+/* Linked list of saved prologue counts.  A very poor
+   implementation of a map from label numbers to prologue counts.  */
+typedef struct label_prologue_count
+{
+  struct label_prologue_count *next;
+  unsigned long label_number;
+  unsigned int prologue_count;
+} label_prologue_count;
 
 static struct
 {
   unsigned long next_slot_number;
+  fragS *next_slot_frag;
 
   /* Maintain a list of unwind entries for the current function.  */
   unw_rec_list *list;
@@ -586,10 +684,16 @@ static struct
   symbolS *proc_end;
   symbolS *info;               /* pointer to unwind info */
   symbolS *personality_routine;
+  segT saved_text_seg;
+  subsegT saved_text_subseg;
+  unsigned int force_unwind_entry : 1; /* force generation of unwind entry? */
 
   /* TRUE if processing unwind directives in a prologue region.  */
   int prologue;
   int prologue_mask;
+  unsigned int prologue_count; /* number of .prologues seen so far */
+  /* Prologue counts at previous .label_state directives.  */
+  struct label_prologue_count * saved_prologue_counts;
 } unwind;
 
 typedef void (*vbyte_func) PARAMS ((int, char *, char *));
@@ -655,10 +759,12 @@ static void add_unwind_entry PARAMS((unw_rec_list *ptr));
 static symbolS *declare_register PARAMS ((const char *name, int regnum));
 static void declare_register_set PARAMS ((const char *, int, int));
 static unsigned int operand_width PARAMS ((enum ia64_opnd));
-static int operand_match PARAMS ((const struct ia64_opcode *idesc,
-                                 int index, expressionS *e));
+static enum operand_match_result operand_match PARAMS ((const struct ia64_opcode *idesc,
+                                                       int index,
+                                                       expressionS *e));
 static int parse_operand PARAMS ((expressionS *e));
 static struct ia64_opcode * parse_operands PARAMS ((struct ia64_opcode *));
+static int errata_nop_necessary_p PARAMS ((struct slot *, enum ia64_unit));
 static void build_insn PARAMS ((struct slot *, bfd_vma *));
 static void emit_one_bundle PARAMS ((void));
 static void fix_insn PARAMS ((fixS *, const struct ia64_operand *, valueT));
@@ -672,6 +778,7 @@ static void add_qp_imply PARAMS((int p1, int p2));
 static void clear_qp_branch_flag PARAMS((valueT mask));
 static void clear_qp_mutex PARAMS((valueT mask));
 static void clear_qp_implies PARAMS((valueT p1_mask, valueT p2_mask));
+static int has_suffix_p PARAMS((const char *, const char *));
 static void clear_register_values PARAMS ((void));
 static void print_dependency PARAMS ((const char *action, int depind));
 static void instruction_serialization PARAMS ((void));
@@ -792,12 +899,42 @@ static void process_unw_records PARAMS ((unw_rec_list *, vbyte_func));
 static int calc_record_size PARAMS ((unw_rec_list *));
 static void set_imask PARAMS ((unw_rec_list *, unsigned long, unsigned long, unsigned int));
 static int count_bits PARAMS ((unsigned long));
-static unsigned long slot_index PARAMS ((unsigned long, unsigned long));
+static unsigned long slot_index PARAMS ((unsigned long, fragS *,
+                                        unsigned long, fragS *));
+static unw_rec_list *optimize_unw_records PARAMS ((unw_rec_list *));
 static void fixup_unw_records PARAMS ((unw_rec_list *));
 static int output_unw_records PARAMS ((unw_rec_list *, void **));
 static int convert_expr_to_ab_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
 static int convert_expr_to_xy_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
-static int generate_unwind_image PARAMS ((void));
+static int generate_unwind_image PARAMS ((const char *));
+static unsigned int get_saved_prologue_count PARAMS ((unsigned long));
+static void save_prologue_count PARAMS ((unsigned long, unsigned int));
+static void free_saved_prologue_counts PARAMS ((void));
+
+/* Build the unwind section name by appending the (possibly stripped)
+   text section NAME to the unwind PREFIX.  The resulting string
+   pointer is assigned to RESULT.  The string is allocated on the
+   stack, so this must be a macro...  */
+#define make_unw_section_name(special, text_name, result)                 \
+  {                                                                       \
+    const char *_prefix = special_section_name[special];                  \
+    const char *_suffix = text_name;                                      \
+    size_t _prefix_len, _suffix_len;                                      \
+    char *_result;                                                        \
+    if (strncmp (text_name, ".gnu.linkonce.t.",                                   \
+                sizeof (".gnu.linkonce.t.") - 1) == 0)                    \
+      {                                                                           \
+       _prefix = special_linkonce_name[special - SPECIAL_SECTION_UNWIND]; \
+       _suffix += sizeof (".gnu.linkonce.t.") - 1;                        \
+      }                                                                           \
+    _prefix_len = strlen (_prefix), _suffix_len = strlen (_suffix);       \
+    _result = alloca (_prefix_len + _suffix_len + 1);                     \
+    memcpy (_result, _prefix, _prefix_len);                               \
+    memcpy (_result + _prefix_len, _suffix, _suffix_len);                 \
+    _result[_prefix_len + _suffix_len] = '\0';                            \
+    result = _result;                                                     \
+  }                                                                       \
+while (0)
 
 /* Determine if application register REGNUM resides in the integer
    unit (as opposed to the memory unit).  */
@@ -830,18 +967,66 @@ set_section (name)
   input_line_pointer = saved_input_line_pointer;
 }
 
+/* Map 's' to SHF_IA_64_SHORT.  */
+
+int
+ia64_elf_section_letter (letter, ptr_msg)
+     int letter;
+     char **ptr_msg;
+{
+  if (letter == 's')
+    return SHF_IA_64_SHORT;
+  else if (letter == 'o')
+    return SHF_LINK_ORDER;
+
+  *ptr_msg = _("Bad .section directive: want a,o,s,w,x,M,S,G,T in string");
+  return -1;
+}
+
 /* Map SHF_IA_64_SHORT to SEC_SMALL_DATA.  */
 
 flagword
 ia64_elf_section_flags (flags, attr, type)
      flagword flags;
-     int attr, type;
+     int attr, type ATTRIBUTE_UNUSED;
 {
   if (attr & SHF_IA_64_SHORT)
     flags |= SEC_SMALL_DATA;
   return flags;
 }
 
+int
+ia64_elf_section_type (str, len)
+     const char *str;
+     size_t len;
+{
+#define STREQ(s) ((len == sizeof (s) - 1) && (strncmp (str, s, sizeof (s) - 1) == 0))
+
+  if (STREQ (ELF_STRING_ia64_unwind_info))
+    return SHT_PROGBITS;
+
+  if (STREQ (ELF_STRING_ia64_unwind_info_once))
+    return SHT_PROGBITS;
+
+  if (STREQ (ELF_STRING_ia64_unwind))
+    return SHT_IA_64_UNWIND;
+
+  if (STREQ (ELF_STRING_ia64_unwind_once))
+    return SHT_IA_64_UNWIND;
+
+  if (STREQ ("unwind"))
+    return SHT_IA_64_UNWIND;
+
+  if (STREQ ("init_array"))
+    return SHT_INIT_ARRAY;
+
+  if (STREQ ("fini_array"))
+    return SHT_FINI_ARRAY;
+
+  return -1;
+#undef STREQ
+}
+
 static unsigned int
 set_regstack (ins, locs, outs, rots)
      unsigned int ins, locs, outs, rots;
@@ -877,6 +1062,7 @@ ia64_flush_insns ()
   struct label_fix *lfix;
   segT saved_seg;
   subsegT saved_subseg;
+  unw_rec_list *ptr;
 
   if (!md.last_text_seg)
     return;
@@ -897,8 +1083,33 @@ ia64_flush_insns ()
       symbol_set_frag (lfix->sym, frag_now);
     }
   CURR_SLOT.label_fixups = 0;
+  for (lfix = CURR_SLOT.tag_fixups; lfix; lfix = lfix->next)
+    {
+      S_SET_VALUE (lfix->sym, frag_now_fix ());
+      symbol_set_frag (lfix->sym, frag_now);
+    }
+  CURR_SLOT.tag_fixups = 0;
+
+  /* In case there are unwind directives following the last instruction,
+     resolve those now.  We only handle body and prologue directives here.
+     Give an error for others.  */
+  for (ptr = unwind.current_entry; ptr; ptr = ptr->next)
+    {
+      if (ptr->r.type == prologue || ptr->r.type == prologue_gr
+         || ptr->r.type == body)
+       {
+         ptr->slot_number = (unsigned long) frag_more (0);
+         ptr->slot_frag = frag_now;
+       }
+      else
+       as_bad (_("Unwind directive not followed by an instruction."));
+    }
+  unwind.current_entry = NULL;
 
   subseg_set (saved_seg, saved_subseg);
+
+  if (md.qp.X_op == O_register)
+    as_bad ("qualifying predicate not followed by instruction");
 }
 
 void
@@ -932,7 +1143,7 @@ void
 output_vbyte_mem (count, ptr, comment)
      int count;
      char *ptr;
-     char *comment;
+     char *comment ATTRIBUTE_UNUSED;
 {
   int x;
   if (vbyte_mem_ptr == NULL)
@@ -949,8 +1160,8 @@ static int vbyte_count = 0;
 void
 count_output (count, ptr, comment)
      int count;
-     char *ptr;
-     char *comment;
+     char *ptr ATTRIBUTE_UNUSED;
+     char *comment ATTRIBUTE_UNUSED;
 {
   vbyte_count += count;
 }
@@ -2396,11 +2607,64 @@ count_bits (unsigned long mask)
   return n;
 }
 
+/* Return the number of instruction slots from FIRST_ADDR to SLOT_ADDR.
+   SLOT_FRAG is the frag containing SLOT_ADDR, and FIRST_FRAG is the frag
+   containing FIRST_ADDR.  */
+
 unsigned long
-slot_index (unsigned long slot_addr, unsigned long first_addr)
+slot_index (slot_addr, slot_frag, first_addr, first_frag)
+     unsigned long slot_addr;
+     fragS *slot_frag;
+     unsigned long first_addr;
+     fragS *first_frag;
 {
-  return (3 * ((slot_addr >> 4) - (first_addr >> 4))
-         + ((slot_addr & 0x3) - (first_addr & 0x3)));
+  unsigned long index = 0;
+
+  /* First time we are called, the initial address and frag are invalid.  */
+  if (first_addr == 0)
+    return 0;
+
+  /* If the two addresses are in different frags, then we need to add in
+     the remaining size of this frag, and then the entire size of intermediate
+     frags.  */
+  while (slot_frag != first_frag)
+    {
+      unsigned long start_addr = (unsigned long) &first_frag->fr_literal;
+
+      /* Add in the full size of the frag converted to instruction slots.  */
+      index += 3 * (first_frag->fr_fix >> 4);
+      /* Subtract away the initial part before first_addr.  */
+      index -= (3 * ((first_addr >> 4) - (start_addr >> 4))
+               + ((first_addr & 0x3) - (start_addr & 0x3)));
+
+      /* Move to the beginning of the next frag.  */
+      first_frag = first_frag->fr_next;
+      first_addr = (unsigned long) &first_frag->fr_literal;
+    }
+
+  /* Add in the used part of the last frag.  */
+  index += (3 * ((slot_addr >> 4) - (first_addr >> 4))
+           + ((slot_addr & 0x3) - (first_addr & 0x3)));
+  return index;
+}
+
+/* Optimize unwind record directives.  */
+
+static unw_rec_list *
+optimize_unw_records (list)
+     unw_rec_list *list;
+{
+  if (!list)
+    return NULL;
+
+  /* If the only unwind record is ".prologue" or ".prologue" followed
+     by ".body", then we can optimize the unwind directives away.  */
+  if (list->r.type == prologue
+      && (list->next == NULL
+         || (list->next->r.type == body && list->next->next == NULL)))
+    return NULL;
+
+  return list;
 }
 
 /* Given a complete record list, process any records which have
@@ -2414,12 +2678,14 @@ fixup_unw_records (list)
 {
   unw_rec_list *ptr, *region = 0;
   unsigned long first_addr = 0, rlen = 0, t;
+  fragS *first_frag = 0;
 
   for (ptr = list; ptr; ptr = ptr->next)
     {
       if (ptr->slot_number == SLOT_NUM_NOT_SET)
        as_bad (" Insn slot not set in unwind record.");
-      t = slot_index (ptr->slot_number, first_addr);
+      t = slot_index (ptr->slot_number, ptr->slot_frag,
+                     first_addr, first_frag);
       switch (ptr->r.type)
        {
        case prologue:
@@ -2429,17 +2695,21 @@ fixup_unw_records (list)
            unw_rec_list *last;
            int size, dir_len = 0;
            unsigned long last_addr;
+           fragS *last_frag;
 
            first_addr = ptr->slot_number;
+           first_frag = ptr->slot_frag;
            ptr->slot_number = 0;
            /* Find either the next body/prologue start, or the end of
               the list, and determine the size of the region.  */
            last_addr = unwind.next_slot_number;
+           last_frag = unwind.next_slot_frag;
            for (last = ptr->next; last != NULL; last = last->next)
              if (last->r.type == prologue || last->r.type == prologue_gr
                  || last->r.type == body)
                {
                  last_addr = last->slot_number;
+                 last_frag = last->slot_frag;
                  break;
                }
              else if (!last->next)
@@ -2450,6 +2720,7 @@ fixup_unw_records (list)
                  if (ptr->r.type != body)
                    {
                      last_addr = last->slot_number;
+                     last_frag = last->slot_frag;
                      switch (last->r.type)
                        {
                        case frgr_mem:
@@ -2474,9 +2745,14 @@ fixup_unw_records (list)
                    }
                  break;
                }
-           size = slot_index (last_addr, first_addr) + dir_len;
+           size = (slot_index (last_addr, last_frag, first_addr, first_frag)
+                   + dir_len);
            rlen = ptr->r.record.r.rlen = size;
-           region = ptr;
+           if (ptr->r.type == body)
+             /* End of region.  */
+             region = 0;
+           else
+             region = ptr;
            break;
          }
        case epilogue:
@@ -2570,6 +2846,47 @@ fixup_unw_records (list)
     }
 }
 
+/* Helper routine for output_unw_records.  Emits the header for the unwind
+   info.  */
+
+static int
+setup_unwind_header (int size, unsigned char **mem)
+{
+  int x, extra = 0;
+  valueT flag_value;
+
+  /* pad to pointer-size boundry.  */
+  x = size % md.pointer_size;
+  if (x != 0)
+    extra = md.pointer_size - x;
+
+  /* Add 8 for the header + a pointer for the
+     personality offset.  */
+  *mem = xmalloc (size + extra + 8 + md.pointer_size);
+
+  /* Clear the padding area and personality.  */
+  memset (*mem + 8 + size, 0, extra + md.pointer_size);
+
+  /* Initialize the header area.  */
+  if (unwind.personality_routine)
+    {
+      if (md.flags & EF_IA_64_ABI64)
+       flag_value = (bfd_vma) 3 << 32;
+      else
+       /* 32-bit unwind info block.  */
+       flag_value = (bfd_vma) 0x1003 << 32;
+    }
+  else
+    flag_value = 0;
+
+  md_number_to_chars (*mem, (((bfd_vma) 1 << 48)     /* Version.  */
+                            | flag_value            /* U & E handler flags.  */
+                            | ((size + extra) / md.pointer_size)), /* Length.  */
+                     8);
+
+  return extra;
+}
+
 /* Generate an unwind image from a record list.  Returns the number of
    bytes in the resulting image. The memory image itselof is returned
    in the 'ptr' parameter.  */
@@ -2578,34 +2895,28 @@ output_unw_records (list, ptr)
      unw_rec_list *list;
      void **ptr;
 {
-  int size, x, extra = 0;
+  int size, extra;
   unsigned char *mem;
 
+  *ptr = NULL;
+
+  list = optimize_unw_records (list);
   fixup_unw_records (list);
   size = calc_record_size (list);
 
-  /* pad to 8 byte boundry.  */
-  x = size % 8;
-  if (x != 0)
-    extra = 8 - x;
-  /* Add 8 for the header + 8 more bytes for the personality offset.  */
-  mem = xmalloc (size + extra + 16);
+  if (size > 0 || unwind.force_unwind_entry)
+    {
+      unwind.force_unwind_entry = 0;
+      extra = setup_unwind_header (size, &mem);
 
-  vbyte_mem_ptr = mem + 8;
-  /* Clear the padding area and personality.  */
-  memset (mem + 8 + size, 0 , extra + 8);
-  /* Initialize the header area.  */
-  md_number_to_chars (mem, (((bfd_vma) 1 << 48)     /* version */
-                           | (unwind.personality_routine
-                              ? ((bfd_vma) 3 << 32) /* U & E handler flags */
-                              : 0)
-                           | ((size + extra) / 8)),  /* length (dwords) */
-                     8);
+      vbyte_mem_ptr = mem + 8;
+      process_unw_records (list, output_vbyte_mem);
 
-  process_unw_records (list, output_vbyte_mem);
+      *ptr = mem;
 
-  *ptr = mem;
-  return size + extra + 16;
+      size += extra + 8 + md.pointer_size;
+    }
+  return size;
 }
 
 static int
@@ -2620,18 +2931,18 @@ convert_expr_to_ab_reg (e, ab, regp)
     return 0;
 
   reg = e->X_add_number;
-  if (reg >= REG_GR + 4 && reg <= REG_GR + 7)
+  if (reg >= (REG_GR + 4) && reg <= (REG_GR + 7))
     {
       *ab = 0;
       *regp = reg - REG_GR;
     }
-  else if ((reg >= REG_FR + 2 && reg <= REG_FR + 5)
-          || (reg >= REG_FR + 16 && reg <= REG_FR + 31))
+  else if ((reg >= (REG_FR + 2) && reg <= (REG_FR + 5))
+          || (reg >= (REG_FR + 16) && reg <= (REG_FR + 31)))
     {
       *ab = 1;
       *regp = reg - REG_FR;
     }
-  else if (reg >= REG_BR + 1 && reg <= REG_BR + 5)
+  else if (reg >= (REG_BR + 1) && reg <= (REG_BR + 5))
     {
       *ab = 2;
       *regp = reg - REG_BR;
@@ -2673,17 +2984,17 @@ convert_expr_to_xy_reg (e, xy, regp)
 
   reg = e->X_add_number;
 
-  if (reg >= REG_GR && reg <= REG_GR + 127)
+  if (/* reg >= REG_GR && */ reg <= (REG_GR + 127))
     {
       *xy = 0;
       *regp = reg - REG_GR;
     }
-  else if (reg >= REG_FR && reg <= REG_FR + 127)
+  else if (reg >= REG_FR && reg <= (REG_FR + 127))
     {
       *xy = 1;
       *regp = reg - REG_FR;
     }
-  else if (reg >= REG_BR && reg <= REG_BR + 7)
+  else if (reg >= REG_BR && reg <= (REG_BR + 7))
     {
       *xy = 2;
       *regp = reg - REG_BR;
@@ -2695,7 +3006,7 @@ convert_expr_to_xy_reg (e, xy, regp)
 
 static void
 dot_radix (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   int radix;
 
@@ -2735,7 +3046,7 @@ add_unwind_entry (ptr)
 
 static void
 dot_fframe (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
 
@@ -2749,7 +3060,7 @@ dot_fframe (dummy)
 
 static void
 dot_vframe (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
   unsigned reg;
@@ -2768,7 +3079,7 @@ dot_vframe (dummy)
 
 static void
 dot_vframesp (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
 
@@ -2779,12 +3090,12 @@ dot_vframesp (dummy)
       add_unwind_entry (output_psp_sprel (e.X_add_number));
     }
   else
-    as_bad ("First operand to .vframesp must be a general register");
+    as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
 }
 
 static void
 dot_vframepsp (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
 
@@ -2795,12 +3106,12 @@ dot_vframepsp (dummy)
       add_unwind_entry (output_psp_sprel (e.X_add_number));
     }
   else
-    as_bad ("First operand to .vframepsp must be a general register");
+    as_bad ("Operand to .vframepsp must be a constant (psp-relative offset)");
 }
 
 static void
 dot_save (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
   int sep;
@@ -2877,10 +3188,10 @@ dot_save (dummy)
 
 static void
 dot_restore (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
-  unsigned long ecount = 0;
+  unsigned long ecount;        /* # of _additional_ regions to pop */
   int sep;
 
   sep = parse_operand (&e1);
@@ -2893,19 +3204,34 @@ dot_restore (dummy)
   if (sep == ',')
     {
       parse_operand (&e2);
-      if (e1.X_op != O_constant)
+      if (e2.X_op != O_constant || e2.X_add_number < 0)
        {
-         as_bad ("Second operand to .restore must be constant");
+         as_bad ("Second operand to .restore must be a constant >= 0");
          return;
        }
-      ecount = e1.X_op;
+      ecount = e2.X_add_number;
+    }
+  else
+    ecount = unwind.prologue_count - 1;
+
+  if (ecount >= unwind.prologue_count)
+    {
+      as_bad ("Epilogue count of %lu exceeds number of nested prologues (%u)",
+             ecount + 1, unwind.prologue_count);
+      return;
     }
+
   add_unwind_entry (output_epilogue (ecount));
+
+  if (ecount < unwind.prologue_count)
+    unwind.prologue_count -= ecount + 1;
+  else
+    unwind.prologue_count = 0;
 }
 
 static void
 dot_restorereg (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   unsigned int ab, reg;
   expressionS e;
@@ -2922,7 +3248,7 @@ dot_restorereg (dummy)
 
 static void
 dot_restorereg_p (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   unsigned int qp, ab, reg;
   expressionS e1, e2;
@@ -2953,26 +3279,38 @@ dot_restorereg_p (dummy)
 }
 
 static int
-generate_unwind_image ()
+generate_unwind_image (text_name)
+     const char *text_name;
 {
   int size;
-  unsigned char *unw_rec;
+  void *unw_rec;
 
   /* Force out pending instructions, to make sure all unwind records have
      a valid slot_number field.  */
   ia64_flush_insns ();
 
   /* Generate the unwind record.  */
-  size = output_unw_records (unwind.list, (void **) &unw_rec);
-  if (size % 8 != 0)
-    as_bad ("Unwind record is not a multiple of 8 bytes.");
+  size = output_unw_records (unwind.list, &unw_rec);
+  if (size % md.pointer_size != 0)
+    as_bad ("Unwind record is not a multiple of %d bytes.", md.pointer_size);
 
   /* If there are unwind records, switch sections, and output the info.  */
   if (size != 0)
     {
       unsigned char *where;
+      char *sec_name;
       expressionS exp;
-      set_section ((char *) special_section_name[SPECIAL_SECTION_UNWIND_INFO]);
+      bfd_reloc_code_real_type reloc;
+
+      make_unw_section_name (SPECIAL_SECTION_UNWIND_INFO, text_name, sec_name);
+      set_section (sec_name);
+      bfd_set_section_flags (stdoutput, now_seg,
+                            SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+
+      /* Make sure the section has 4 byte alignment for ILP32 and
+        8 byte alignment for LP64.  */
+      frag_align (md.pointer_size_shift, 0, 0);
+      record_alignment (now_seg, md.pointer_size_shift);
 
       /* Set expression which points to start of unwind descriptor area.  */
       unwind.info = expr_build_dot ();
@@ -2985,20 +3323,37 @@ generate_unwind_image ()
       /* Copy the information from the unwind record into this section. The
         data is already in the correct byte order.  */
       memcpy (where, unw_rec, size);
+
       /* Add the personality address to the image.  */
       if (unwind.personality_routine != 0)
        {
-         exp.X_op  = O_symbol;
+         exp.X_op = O_symbol;
          exp.X_add_symbol = unwind.personality_routine;
          exp.X_add_number = 0;
-         fix_new_exp (frag_now, frag_now_fix () - 8, 8,
-                            &exp, 0, BFD_RELOC_IA64_LTOFF_FPTR64LSB);
+
+         if (md.flags & EF_IA_64_BE)
+           {
+             if (md.flags & EF_IA_64_ABI64)
+               reloc = BFD_RELOC_IA64_LTOFF_FPTR64MSB;
+             else
+               reloc = BFD_RELOC_IA64_LTOFF_FPTR32MSB;
+           }
+         else
+           {
+             if (md.flags & EF_IA_64_ABI64)
+               reloc = BFD_RELOC_IA64_LTOFF_FPTR64LSB;
+             else
+               reloc = BFD_RELOC_IA64_LTOFF_FPTR32LSB;
+           }
+
+         fix_new_exp (frag_now, frag_now_fix () - md.pointer_size,
+                      md.pointer_size, &exp, 0, reloc);
          unwind.personality_routine = 0;
        }
-      obj_elf_previous (0);
     }
 
   free_list_records (unwind.list);
+  free_saved_prologue_counts ();
   unwind.list = unwind.tail = unwind.current_entry = NULL;
 
   return size;
@@ -3006,22 +3361,39 @@ generate_unwind_image ()
 
 static void
 dot_handlerdata (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
-  generate_unwind_image ();
+  const char *text_name = segment_name (now_seg);
+
+  /* If text section name starts with ".text" (which it should),
+     strip this prefix off.  */
+  if (strcmp (text_name, ".text") == 0)
+    text_name = "";
+
+  unwind.force_unwind_entry = 1;
+
+  /* Remember which segment we're in so we can switch back after .endp */
+  unwind.saved_text_seg = now_seg;
+  unwind.saved_text_subseg = now_subseg;
+
+  /* Generate unwind info into unwind-info section and then leave that
+     section as the currently active one so dataXX directives go into
+     the language specific data area of the unwind info block.  */
+  generate_unwind_image (text_name);
   demand_empty_rest_of_line ();
 }
 
 static void
 dot_unwentry (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
+  unwind.force_unwind_entry = 1;
   demand_empty_rest_of_line ();
 }
 
 static void
 dot_altrp (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
   unsigned reg;
@@ -3130,7 +3502,7 @@ dot_savemem (psprel)
 
 static void
 dot_saveg (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
   int sep;
@@ -3158,7 +3530,7 @@ dot_saveg (dummy)
 
 static void
 dot_savef (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1;
   int sep;
@@ -3172,7 +3544,7 @@ dot_savef (dummy)
 
 static void
 dot_saveb (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
   unsigned int reg;
@@ -3207,7 +3579,7 @@ dot_saveb (dummy)
 
 static void
 dot_savegf (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
   int sep;
@@ -3227,7 +3599,7 @@ dot_savegf (dummy)
 
 static void
 dot_spill (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
   unsigned char sep;
@@ -3244,7 +3616,7 @@ dot_spill (dummy)
 
 static void
 dot_spillreg (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   int sep, ab, xy, reg, treg;
   expressionS e1, e2;
@@ -3311,7 +3683,7 @@ dot_spillmem (psprel)
 
 static void
 dot_spillreg_p (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   int sep, ab, xy, reg, treg;
   expressionS e1, e2, e3;
@@ -3403,14 +3775,69 @@ dot_spillmem_p (psprel)
     }
 
   if (psprel)
-    add_unwind_entry (output_spill_psprel_p (qp, ab, reg, e3.X_add_number));
+    add_unwind_entry (output_spill_psprel_p (ab, reg, e3.X_add_number, qp));
+  else
+    add_unwind_entry (output_spill_sprel_p (ab, reg, e3.X_add_number, qp));
+}
+
+static unsigned int
+get_saved_prologue_count (lbl)
+     unsigned long lbl;
+{
+  label_prologue_count *lpc = unwind.saved_prologue_counts;
+
+  while (lpc != NULL && lpc->label_number != lbl)
+    lpc = lpc->next;
+
+  if (lpc != NULL)
+    return lpc->prologue_count;
+
+  as_bad ("Missing .label_state %ld", lbl);
+  return 1;
+}
+
+static void
+save_prologue_count (lbl, count)
+     unsigned long lbl;
+     unsigned int count;
+{
+  label_prologue_count *lpc = unwind.saved_prologue_counts;
+
+  while (lpc != NULL && lpc->label_number != lbl)
+    lpc = lpc->next;
+
+  if (lpc != NULL)
+    lpc->prologue_count = count;
   else
-    add_unwind_entry (output_spill_sprel_p (qp, ab, reg, e3.X_add_number));
+    {
+      label_prologue_count *new_lpc = xmalloc (sizeof (* new_lpc));
+
+      new_lpc->next = unwind.saved_prologue_counts;
+      new_lpc->label_number = lbl;
+      new_lpc->prologue_count = count;
+      unwind.saved_prologue_counts = new_lpc;
+    }
+}
+
+static void
+free_saved_prologue_counts ()
+{
+  label_prologue_count *lpc = unwind.saved_prologue_counts;
+  label_prologue_count *next;
+
+  while (lpc != NULL)
+    {
+      next = lpc->next;
+      free (lpc);
+      lpc = next;
+    }
+
+  unwind.saved_prologue_counts = NULL;
 }
 
 static void
 dot_label_state (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
 
@@ -3421,11 +3848,12 @@ dot_label_state (dummy)
       return;
     }
   add_unwind_entry (output_label_state (e.X_add_number));
+  save_prologue_count (e.X_add_number, unwind.prologue_count);
 }
 
 static void
 dot_copy_state (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
 
@@ -3436,11 +3864,12 @@ dot_copy_state (dummy)
       return;
     }
   add_unwind_entry (output_copy_state (e.X_add_number));
+  unwind.prologue_count = get_saved_prologue_count (e.X_add_number);
 }
 
 static void
 dot_unwabi (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e1, e2;
   unsigned char sep;
@@ -3472,7 +3901,7 @@ dot_unwabi (dummy)
 
 static void
 dot_personality (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   char *name, *p, c;
   SKIP_WHITESPACE ();
@@ -3480,6 +3909,7 @@ dot_personality (dummy)
   c = get_symbol_end ();
   p = input_line_pointer;
   unwind.personality_routine = symbol_find_or_make (name);
+  unwind.force_unwind_entry = 1;
   *p = c;
   SKIP_WHITESPACE ();
   demand_empty_rest_of_line ();
@@ -3487,7 +3917,7 @@ dot_personality (dummy)
 
 static void
 dot_proc (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   char *name, *p, c;
   symbolS *sym;
@@ -3516,13 +3946,14 @@ dot_proc (dummy)
   demand_empty_rest_of_line ();
   ia64_do_align (16);
 
+  unwind.prologue_count = 0;
   unwind.list = unwind.tail = unwind.current_entry = NULL;
   unwind.personality_routine = 0;
 }
 
 static void
 dot_body (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   unwind.prologue = 0;
   unwind.prologue_mask = 0;
@@ -3533,10 +3964,10 @@ dot_body (dummy)
 
 static void
 dot_prologue (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   unsigned char sep;
-  int mask = 0, grsave;
+  int mask = 0, grsave = 0;
 
   if (!is_it_end_of_statement ())
     {
@@ -3570,64 +4001,177 @@ dot_prologue (dummy)
 
   unwind.prologue = 1;
   unwind.prologue_mask = mask;
+  ++unwind.prologue_count;
 }
 
 static void
 dot_endp (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS e;
   unsigned char *ptr;
+  int bytes_per_address;
   long where;
   segT saved_seg;
   subsegT saved_subseg;
+  const char *sec_name, *text_name;
+  char *name, *p, c;
+  symbolS *sym;
 
-  saved_seg = now_seg;
-  saved_subseg = now_subseg;
-
-  expression (&e);
-  demand_empty_rest_of_line ();
+  if (unwind.saved_text_seg)
+    {
+      saved_seg = unwind.saved_text_seg;
+      saved_subseg = unwind.saved_text_subseg;
+      unwind.saved_text_seg = NULL;
+    }
+  else
+    {
+      saved_seg = now_seg;
+      saved_subseg = now_subseg;
+    }
+
+  /*
+    Use a slightly ugly scheme to derive the unwind section names from
+    the text section name:
+
+    text sect.  unwind table sect.
+    name:       name:                      comments:
+    ----------  -----------------          --------------------------------
+    .text       .IA_64.unwind
+    .text.foo   .IA_64.unwind.text.foo
+    .foo        .IA_64.unwind.foo
+    .gnu.linkonce.t.foo
+               .gnu.linkonce.ia64unw.foo
+    _info       .IA_64.unwind_info         gas issues error message (ditto)
+    _infoFOO    .IA_64.unwind_infoFOO      gas issues error message (ditto)
+
+    This mapping is done so that:
+
+       (a) An object file with unwind info only in .text will use
+           unwind section names .IA_64.unwind and .IA_64.unwind_info.
+           This follows the letter of the ABI and also ensures backwards
+           compatibility with older toolchains.
+
+       (b) An object file with unwind info in multiple text sections
+           will use separate unwind sections for each text section.
+           This allows us to properly set the "sh_info" and "sh_link"
+           fields in SHT_IA_64_UNWIND as required by the ABI and also
+           lets GNU ld support programs with multiple segments
+           containing unwind info (as might be the case for certain
+           embedded applications).
+
+       (c) An error is issued if there would be a name clash.
+  */
+  text_name = segment_name (saved_seg);
+  if (strncmp (text_name, "_info", 5) == 0)
+    {
+      as_bad ("Illegal section name `%s' (causes unwind section name clash)",
+             text_name);
+      ignore_rest_of_line ();
+      return;
+    }
+  if (strcmp (text_name, ".text") == 0)
+    text_name = "";
 
   insn_group_break (1, 0, 0);
 
-  /* If there was a .handlerdata, we haven't generated an image yet.  */
-  if (unwind.info == 0)
+  /* If there wasn't a .handlerdata, we haven't generated an image yet.  */
+  if (!unwind.info)
+    generate_unwind_image (text_name);
+
+  if (unwind.info || unwind.force_unwind_entry)
     {
-      generate_unwind_image ();
-    }
+      subseg_set (md.last_text_seg, 0);
+      unwind.proc_end = expr_build_dot ();
 
-  subseg_set (md.last_text_seg, 0);
-  unwind.proc_end = expr_build_dot ();
+      make_unw_section_name (SPECIAL_SECTION_UNWIND, text_name, sec_name);
+      set_section ((char *) sec_name);
+      bfd_set_section_flags (stdoutput, now_seg,
+                            SEC_LOAD | SEC_ALLOC | SEC_READONLY);
 
-  set_section ((char *) special_section_name[SPECIAL_SECTION_UNWIND]);
-  ptr = frag_more (24);
-  where = frag_now_fix () - 24;
+      /* Make sure that section has 4 byte alignment for ILP32 and
+         8 byte alignment for LP64.  */
+      record_alignment (now_seg, md.pointer_size_shift);
 
-  /* Issue the values of  a) Proc Begin,  b) Proc End,  c) Unwind Record.  */
-  e.X_op = O_pseudo_fixup;
-  e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
-  e.X_add_number = 0;
-  e.X_add_symbol = unwind.proc_start;
-  ia64_cons_fix_new (frag_now, where, 8, &e);
+      /* Need space for 3 pointers for procedure start, procedure end,
+        and unwind info.  */
+      ptr = frag_more (3 * md.pointer_size);
+      where = frag_now_fix () - (3 * md.pointer_size);
+      bytes_per_address = bfd_arch_bits_per_address (stdoutput) / 8;
 
-  e.X_op = O_pseudo_fixup;
-  e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
-  e.X_add_number = 0;
-  e.X_add_symbol = unwind.proc_end;
-  ia64_cons_fix_new (frag_now, where + 8, 8, &e);
+      /* Issue the values of  a) Proc Begin, b) Proc End, c) Unwind Record.  */
+      e.X_op = O_pseudo_fixup;
+      e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
+      e.X_add_number = 0;
+      e.X_add_symbol = unwind.proc_start;
+      ia64_cons_fix_new (frag_now, where, bytes_per_address, &e);
 
-  if (unwind.info != 0)
-    {
       e.X_op = O_pseudo_fixup;
       e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
       e.X_add_number = 0;
-      e.X_add_symbol = unwind.info;
-      ia64_cons_fix_new (frag_now, where + 16, 8, &e);
-    }
-  else
-    md_number_to_chars (ptr + 16, 0, 8);
+      e.X_add_symbol = unwind.proc_end;
+      ia64_cons_fix_new (frag_now, where + bytes_per_address,
+                        bytes_per_address, &e);
+
+      if (unwind.info)
+       {
+         e.X_op = O_pseudo_fixup;
+         e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
+         e.X_add_number = 0;
+         e.X_add_symbol = unwind.info;
+         ia64_cons_fix_new (frag_now, where + (bytes_per_address * 2),
+                            bytes_per_address, &e);
+       }
+      else
+       md_number_to_chars (ptr + (bytes_per_address * 2), 0,
+                           bytes_per_address);
 
+    }
   subseg_set (saved_seg, saved_subseg);
+
+  /* Parse names of main and alternate entry points and set symbol sizes.  */
+  while (1)
+    {
+      SKIP_WHITESPACE ();
+      name = input_line_pointer;
+      c = get_symbol_end ();
+      p = input_line_pointer;
+      sym = symbol_find (name);
+      if (sym && unwind.proc_start
+         && (symbol_get_bfdsym (sym)->flags & BSF_FUNCTION)
+         && S_GET_SIZE (sym) == 0 && symbol_get_obj (sym)->size == NULL)
+       {
+         fragS *fr = symbol_get_frag (unwind.proc_start);
+         fragS *frag = symbol_get_frag (sym);
+
+         /* Check whether the function label is at or beyond last
+            .proc directive.  */
+         while (fr && fr != frag)
+           fr = fr->fr_next;
+         if (fr)
+           {
+             if (frag == frag_now && SEG_NORMAL (now_seg))
+               S_SET_SIZE (sym, frag_now_fix () - S_GET_VALUE (sym));
+             else
+               {
+                 symbol_get_obj (sym)->size =
+                   (expressionS *) xmalloc (sizeof (expressionS));
+                 symbol_get_obj (sym)->size->X_op = O_subtract;
+                 symbol_get_obj (sym)->size->X_add_symbol
+                   = symbol_new (FAKE_LABEL_NAME, now_seg,
+                                 frag_now_fix (), frag_now);
+                 symbol_get_obj (sym)->size->X_op_symbol = sym;
+                 symbol_get_obj (sym)->size->X_add_number = 0;
+               }
+           }
+       }
+      *p = c;
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer != ',')
+       break;
+      ++input_line_pointer;
+    }
+  demand_empty_rest_of_line ();
   unwind.proc_start = unwind.proc_end = unwind.info = 0;
 }
 
@@ -3640,7 +4184,7 @@ dot_template (template)
 
 static void
 dot_regstk (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   int ins, locs, outs, rots;
 
@@ -3786,12 +4330,37 @@ static void
 dot_byteorder (byteorder)
      int byteorder;
 {
-  target_big_endian = byteorder;
+  segment_info_type *seginfo = seg_info (now_seg);
+
+  if (byteorder == -1)
+    {
+      if (seginfo->tc_segment_info_data.endian == 0)
+       seginfo->tc_segment_info_data.endian
+         = TARGET_BYTES_BIG_ENDIAN ? 1 : 2;
+      byteorder = seginfo->tc_segment_info_data.endian == 1;
+    }
+  else
+    seginfo->tc_segment_info_data.endian = byteorder ? 1 : 2;
+
+  if (target_big_endian != byteorder)
+    {
+      target_big_endian = byteorder;
+      if (target_big_endian)
+       {
+         ia64_number_to_chars = number_to_chars_bigendian;
+         ia64_float_to_chars = ia64_float_to_chars_bigendian;
+       }
+      else
+       {
+         ia64_number_to_chars = number_to_chars_littleendian;
+         ia64_float_to_chars = ia64_float_to_chars_littleendian;
+       }
+    }
 }
 
 static void
 dot_psr (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   char *option;
   int ch;
@@ -3822,16 +4391,9 @@ dot_psr (dummy)
   demand_empty_rest_of_line ();
 }
 
-static void
-dot_alias (dummy)
-     int dummy;
-{
-  as_bad (".alias not implemented yet");
-}
-
 static void
 dot_ln (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   new_logical_line (0, get_absolute_expression ());
   demand_empty_rest_of_line ();
@@ -3875,9 +4437,11 @@ dot_xdata (size)
   if (!name)
     return;
 
+  md.keep_pending_output = 1;
   set_section (name);
   cons (size);
   obj_elf_previous (0);
+  md.keep_pending_output = 0;
 }
 
 /* Why doesn't float_cons() call md_cons_align() the way cons() does?  */
@@ -3886,19 +4450,25 @@ static void
 stmt_float_cons (kind)
      int kind;
 {
-  size_t size;
+  size_t alignment;
 
   switch (kind)
     {
-    case 'd': size = 8; break;
-    case 'x': size = 10; break;
+    case 'd':
+      alignment = 8;
+      break;
+
+    case 'x':
+    case 'X':
+      alignment = 16;
+      break;
 
     case 'f':
     default:
-      size = 4;
+      alignment = 4;
       break;
     }
-  ia64_do_align (size);
+  ia64_do_align (alignment);
   float_cons (kind);
 }
 
@@ -3921,9 +4491,11 @@ dot_xfloat_cons (kind)
   if (!name)
     return;
 
+  md.keep_pending_output = 1;
   set_section (name);
   stmt_float_cons (kind);
   obj_elf_previous (0);
+  md.keep_pending_output = 0;
 }
 
 static void
@@ -3934,9 +4506,11 @@ dot_xstringer (zero)
   if (!name)
     return;
 
+  md.keep_pending_output = 1;
   set_section (name);
   stringer (zero);
   obj_elf_previous (0);
+  md.keep_pending_output = 0;
 }
 
 static void
@@ -3948,11 +4522,13 @@ dot_xdata_ua (size)
   if (!name)
     return;
 
+  md.keep_pending_output = 1;
   set_section (name);
   md.auto_align = 0;
   cons (size);
   md.auto_align = saved_auto_align;
   obj_elf_previous (0);
+  md.keep_pending_output = 0;
 }
 
 static void
@@ -3964,18 +4540,20 @@ dot_xfloat_cons_ua (kind)
   if (!name)
     return;
 
+  md.keep_pending_output = 1;
   set_section (name);
   md.auto_align = 0;
   stmt_float_cons (kind);
   md.auto_align = saved_auto_align;
   obj_elf_previous (0);
+  md.keep_pending_output = 0;
 }
 
 /* .reg.val <regname>,value */
 
 static void
 dot_reg_val (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   expressionS reg;
 
@@ -4118,7 +4696,7 @@ dot_pred_rel (type)
       valueT bit = 1;
       int regno;
 
-      if (toupper (*input_line_pointer) != 'P'
+      if (TOUPPER (*input_line_pointer) != 'P'
          || (regno = atoi (++input_line_pointer)) < 0
          || regno > 63)
        {
@@ -4126,7 +4704,7 @@ dot_pred_rel (type)
          ignore_rest_of_line ();
          return;
        }
-      while (isdigit (*input_line_pointer))
+      while (ISDIGIT (*input_line_pointer))
        ++input_line_pointer;
       if (p1 == -1)
        p1 = regno;
@@ -4143,7 +4721,7 @@ dot_pred_rel (type)
          valueT stop = 1;
          ++input_line_pointer;
 
-         if (toupper (*input_line_pointer) != 'P'
+         if (TOUPPER (*input_line_pointer) != 'P'
              || (regno = atoi (++input_line_pointer)) < 0
              || regno > 63)
            {
@@ -4151,7 +4729,7 @@ dot_pred_rel (type)
              ignore_rest_of_line ();
              return;
            }
-         while (isdigit (*input_line_pointer))
+         while (ISDIGIT (*input_line_pointer))
            ++input_line_pointer;
          stop <<= regno;
          if (bit >= stop)
@@ -4228,7 +4806,7 @@ dot_pred_rel (type)
 
 static void
 dot_entry (dummy)
-     int dummy;
+     int dummy ATTRIBUTE_UNUSED;
 {
   const char *err;
   char *name;
@@ -4267,7 +4845,7 @@ dot_entry (dummy)
 
 static void
 dot_mem_offset (dummy)
-  int dummy;
+  int dummy ATTRIBUTE_UNUSED;
 {
   md.mem_offset.hint = 1;
   md.mem_offset.offset = get_absolute_expression ();
@@ -4294,43 +4872,43 @@ const pseudo_typeS md_pseudo_table[] =
     { "comment", dot_special_section, SPECIAL_SECTION_COMMENT },
     { "ia_64.unwind", dot_special_section, SPECIAL_SECTION_UNWIND },
     { "ia_64.unwind_info", dot_special_section, SPECIAL_SECTION_UNWIND_INFO },
+    { "init_array", dot_special_section, SPECIAL_SECTION_INIT_ARRAY },
+    { "fini_array", dot_special_section, SPECIAL_SECTION_FINI_ARRAY },
     { "proc", dot_proc, 0 },
     { "body", dot_body, 0 },
     { "prologue", dot_prologue, 0 },
-    { "endp", dot_endp },
-    { "file", dwarf2_directive_file },
-    { "loc", dwarf2_directive_loc },
-
-    { "fframe", dot_fframe },
-    { "vframe", dot_vframe },
-    { "vframesp", dot_vframesp },
-    { "vframepsp", dot_vframepsp },
-    { "save", dot_save },
-    { "restore", dot_restore },
-    { "restorereg", dot_restorereg },
-    { "restorereg.p", dot_restorereg_p },
-    { "handlerdata", dot_handlerdata },
-    { "unwentry", dot_unwentry },
-    { "altrp", dot_altrp },
+    { "endp", dot_endp, 0 },
+
+    { "fframe", dot_fframe, 0 },
+    { "vframe", dot_vframe, 0 },
+    { "vframesp", dot_vframesp, 0 },
+    { "vframepsp", dot_vframepsp, 0 },
+    { "save", dot_save, 0 },
+    { "restore", dot_restore, 0 },
+    { "restorereg", dot_restorereg, 0 },
+    { "restorereg.p", dot_restorereg_p, 0 },
+    { "handlerdata", dot_handlerdata, 0 },
+    { "unwentry", dot_unwentry, 0 },
+    { "altrp", dot_altrp, 0 },
     { "savesp", dot_savemem, 0 },
     { "savepsp", dot_savemem, 1 },
-    { "save.g", dot_saveg },
-    { "save.f", dot_savef },
-    { "save.b", dot_saveb },
-    { "save.gf", dot_savegf },
-    { "spill", dot_spill },
-    { "spillreg", dot_spillreg },
+    { "save.g", dot_saveg, 0 },
+    { "save.f", dot_savef, 0 },
+    { "save.b", dot_saveb, 0 },
+    { "save.gf", dot_savegf, 0 },
+    { "spill", dot_spill, 0 },
+    { "spillreg", dot_spillreg, 0 },
     { "spillsp", dot_spillmem, 0 },
     { "spillpsp", dot_spillmem, 1 },
-    { "spillreg.p", dot_spillreg_p },
+    { "spillreg.p", dot_spillreg_p, 0 },
     { "spillsp.p", dot_spillmem_p, 0 },
     { "spillpsp.p", dot_spillmem_p, 1 },
-    { "label_state", dot_label_state },
-    { "copy_state", dot_copy_state },
-    { "unwabi", dot_unwabi },
-    { "personality", dot_personality },
+    { "label_state", dot_label_state, 0 },
+    { "copy_state", dot_copy_state, 0 },
+    { "unwabi", dot_unwabi, 0 },
+    { "personality", dot_personality, 0 },
 #if 0
-    { "estate", dot_estate },
+    { "estate", dot_estate, 0 },
 #endif
     { "mii", dot_template, 0x0 },
     { "mli", dot_template, 0x2 }, /* old format, for compatibility */
@@ -4356,6 +4934,7 @@ const pseudo_typeS md_pseudo_table[] =
     { "msb", dot_byteorder, 1 },
     { "psr", dot_psr, 0 },
     { "alias", dot_alias, 0 },
+    { "secalias", dot_alias, 1 },
     { "ln", dot_ln, 0 },               /* source line info (for debugging) */
 
     { "xdata1", dot_xdata, 1 },
@@ -4365,6 +4944,7 @@ const pseudo_typeS md_pseudo_table[] =
     { "xreal4", dot_xfloat_cons, 'f' },
     { "xreal8", dot_xfloat_cons, 'd' },
     { "xreal10", dot_xfloat_cons, 'x' },
+    { "xreal16", dot_xfloat_cons, 'X' },
     { "xstring", dot_xstringer, 0 },
     { "xstringz", dot_xstringer, 1 },
 
@@ -4375,20 +4955,30 @@ const pseudo_typeS md_pseudo_table[] =
     { "xreal4.ua", dot_xfloat_cons_ua, 'f' },
     { "xreal8.ua", dot_xfloat_cons_ua, 'd' },
     { "xreal10.ua", dot_xfloat_cons_ua, 'x' },
+    { "xreal16.ua", dot_xfloat_cons_ua, 'X' },
 
     /* annotations/DV checking support */
     { "entry", dot_entry, 0 },
-    { "mem.offset", dot_mem_offset },
+    { "mem.offset", dot_mem_offset, 0 },
     { "pred.rel", dot_pred_rel, 0 },
     { "pred.rel.clear", dot_pred_rel, 'c' },
     { "pred.rel.imply", dot_pred_rel, 'i' },
     { "pred.rel.mutex", dot_pred_rel, 'm' },
     { "pred.safe_across_calls", dot_pred_rel, 's' },
-    { "reg.val", dot_reg_val },
+    { "reg.val", dot_reg_val, 0 },
     { "auto", dot_dv_mode, 'a' },
     { "explicit", dot_dv_mode, 'e' },
     { "default", dot_dv_mode, 'd' },
 
+    /* ??? These are needed to make gas/testsuite/gas/elf/ehopt.s work.
+       IA-64 aligns data allocation pseudo-ops by default, so we have to
+       tell it that these ones are supposed to be unaligned.  Long term,
+       should rewrite so that only IA-64 specific data allocation pseudo-ops
+       are aligned by default.  */
+    {"2byte", stmt_cons_ua, 2},
+    {"4byte", stmt_cons_ua, 4},
+    {"8byte", stmt_cons_ua, 8},
+
     { NULL, 0, 0 }
   };
 
@@ -4405,9 +4995,11 @@ pseudo_opcode[] =
     { "data2", cons, 2 },
     { "data4", cons, 4 },
     { "data8", cons, 8 },
+    { "data16", cons, 16 },
     { "real4", stmt_float_cons, 'f' },
     { "real8", stmt_float_cons, 'd' },
     { "real10", stmt_float_cons, 'x' },
+    { "real16", stmt_float_cons, 'X' },
     { "string", stringer, 0 },
     { "stringz", stringer, 1 },
 
@@ -4415,9 +5007,11 @@ pseudo_opcode[] =
     { "data2.ua", stmt_cons_ua, 2 },
     { "data4.ua", stmt_cons_ua, 4 },
     { "data8.ua", stmt_cons_ua, 8 },
+    { "data16.ua", stmt_cons_ua, 16 },
     { "real4.ua", float_cons, 'f' },
     { "real8.ua", float_cons, 'd' },
     { "real10.ua", float_cons, 'x' },
+    { "real16.ua", float_cons, 'X' },
   };
 
 /* Declare a register by creating a symbol for it and entering it in
@@ -4472,7 +5066,7 @@ operand_width (opnd)
   return bits;
 }
 
-static int
+static enum operand_match_result
 operand_match (idesc, index, e)
      const struct ia64_opcode *idesc;
      int index;
@@ -4489,62 +5083,82 @@ operand_match (idesc, index, e)
 
     case IA64_OPND_AR_CCV:
       if (e->X_op == O_register && e->X_add_number == REG_AR + 32)
-       return 1;
+       return OPERAND_MATCH;
+      break;
+
+    case IA64_OPND_AR_CSD:
+      if (e->X_op == O_register && e->X_add_number == REG_AR + 25)
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_AR_PFS:
       if (e->X_op == O_register && e->X_add_number == REG_AR + 64)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_GR0:
       if (e->X_op == O_register && e->X_add_number == REG_GR + 0)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_IP:
       if (e->X_op == O_register && e->X_add_number == REG_IP)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_PR:
       if (e->X_op == O_register && e->X_add_number == REG_PR)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_PR_ROT:
       if (e->X_op == O_register && e->X_add_number == REG_PR_ROT)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_PSR:
       if (e->X_op == O_register && e->X_add_number == REG_PSR)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_PSR_L:
       if (e->X_op == O_register && e->X_add_number == REG_PSR_L)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_PSR_UM:
       if (e->X_op == O_register && e->X_add_number == REG_PSR_UM)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_C1:
-      if (e->X_op == O_constant && e->X_add_number == 1)
-       return 1;
+      if (e->X_op == O_constant)
+       {
+         if (e->X_add_number == 1)
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
     case IA64_OPND_C8:
-      if (e->X_op == O_constant && e->X_add_number == 8)
-       return 1;
+      if (e->X_op == O_constant)
+       {
+         if (e->X_add_number == 8)
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
     case IA64_OPND_C16:
-      if (e->X_op == O_constant && e->X_add_number == 16)
-       return 1;
+      if (e->X_op == O_constant)
+       {
+         if (e->X_add_number == 16)
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
       /* register operands:  */
@@ -4552,20 +5166,20 @@ operand_match (idesc, index, e)
     case IA64_OPND_AR3:
       if (e->X_op == O_register && e->X_add_number >= REG_AR
          && e->X_add_number < REG_AR + 128)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_B1:
     case IA64_OPND_B2:
       if (e->X_op == O_register && e->X_add_number >= REG_BR
          && e->X_add_number < REG_BR + 8)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_CR3:
       if (e->X_op == O_register && e->X_add_number >= REG_CR
          && e->X_add_number < REG_CR + 128)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_F1:
@@ -4574,14 +5188,14 @@ operand_match (idesc, index, e)
     case IA64_OPND_F4:
       if (e->X_op == O_register && e->X_add_number >= REG_FR
          && e->X_add_number < REG_FR + 128)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_P1:
     case IA64_OPND_P2:
       if (e->X_op == O_register && e->X_add_number >= REG_P
          && e->X_add_number < REG_P + 64)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_R1:
@@ -4589,13 +5203,17 @@ operand_match (idesc, index, e)
     case IA64_OPND_R3:
       if (e->X_op == O_register && e->X_add_number >= REG_GR
          && e->X_add_number < REG_GR + 128)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_R3_2:
-      if (e->X_op == O_register && e->X_add_number >= REG_GR
-         && e->X_add_number < REG_GR + 4)
-       return 1;
+      if (e->X_op == O_register && e->X_add_number >= REG_GR)
+       {
+         if (e->X_add_number < REG_GR + 4)
+           return OPERAND_MATCH;
+         else if (e->X_add_number < REG_GR + 128)
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
       /* indirect operands:  */
@@ -4612,12 +5230,12 @@ operand_match (idesc, index, e)
       if (e->X_op == O_index && e->X_op_symbol
          && (S_GET_VALUE (e->X_op_symbol) - IND_CPUID
              == opnd - IA64_OPND_CPUID_R3))
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_MR3:
       if (e->X_op == O_index && !e->X_op_symbol)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
       /* immediate operands:  */
@@ -4625,40 +5243,58 @@ operand_match (idesc, index, e)
     case IA64_OPND_LEN4:
     case IA64_OPND_LEN6:
       bits = operand_width (idesc->operands[index]);
-      if (e->X_op == O_constant
-         && (bfd_vma) (e->X_add_number - 1) < ((bfd_vma) 1 << bits))
-       return 1;
+      if (e->X_op == O_constant)
+       {
+         if ((bfd_vma) (e->X_add_number - 1) < ((bfd_vma) 1 << bits))
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
     case IA64_OPND_CNT2b:
-      if (e->X_op == O_constant
-         && (bfd_vma) (e->X_add_number - 1) < 3)
-       return 1;
+      if (e->X_op == O_constant)
+       {
+         if ((bfd_vma) (e->X_add_number - 1) < 3)
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
     case IA64_OPND_CNT2c:
       val = e->X_add_number;
-      if (e->X_op == O_constant
-         && (val == 0 || val == 7 || val == 15 || val == 16))
-       return 1;
+      if (e->X_op == O_constant)
+       {
+         if ((val == 0 || val == 7 || val == 15 || val == 16))
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
     case IA64_OPND_SOR:
       /* SOR must be an integer multiple of 8 */
-      if (e->X_add_number & 0x7)
-       break;
+      if (e->X_op == O_constant && e->X_add_number & 0x7)
+       return OPERAND_OUT_OF_RANGE;
     case IA64_OPND_SOF:
     case IA64_OPND_SOL:
-      if (e->X_op == O_constant &&
-         (bfd_vma) e->X_add_number <= 96)
-       return 1;
+      if (e->X_op == O_constant)
+       {
+         if ((bfd_vma) e->X_add_number <= 96)
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
     case IA64_OPND_IMMU62:
       if (e->X_op == O_constant)
        {
          if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << 62))
-           return 1;
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
        }
       else
        {
@@ -4684,10 +5320,10 @@ operand_match (idesc, index, e)
          fix->expr = *e;
          fix->is_pcrel = 0;
          ++CURR_SLOT.num_fixups;
-         return 1;
+         return OPERAND_MATCH;
        }
       else if (e->X_op == O_constant)
-       return 1;
+       return OPERAND_MATCH;
       break;
 
     case IA64_OPND_CCNT5:
@@ -4705,59 +5341,78 @@ operand_match (idesc, index, e)
     case IA64_OPND_MHTYPE8:
     case IA64_OPND_POS6:
       bits = operand_width (idesc->operands[index]);
-      if (e->X_op == O_constant
-         && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
-       return 1;
+      if (e->X_op == O_constant)
+       {
+         if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
     case IA64_OPND_IMMU9:
       bits = operand_width (idesc->operands[index]);
-      if (e->X_op == O_constant
-         && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
+      if (e->X_op == O_constant)
        {
-         int lobits = e->X_add_number & 0x3;
-         if (((bfd_vma) e->X_add_number & 0x3C) != 0 && lobits == 0)
-           e->X_add_number |= (bfd_vma) 0x3;
-         return 1;
+         if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
+           {
+             int lobits = e->X_add_number & 0x3;
+             if (((bfd_vma) e->X_add_number & 0x3C) != 0 && lobits == 0)
+               e->X_add_number |= (bfd_vma) 0x3;
+             return OPERAND_MATCH;
+           }
+         else
+           return OPERAND_OUT_OF_RANGE;
        }
       break;
 
     case IA64_OPND_IMM44:
       /* least 16 bits must be zero */
       if ((e->X_add_number & 0xffff) != 0)
+       /* XXX technically, this is wrong: we should not be issuing warning
+          messages until we're sure this instruction pattern is going to
+          be used! */
        as_warn (_("lower 16 bits of mask ignored"));
 
-      if (e->X_op == O_constant
-         && ((e->X_add_number >= 0
-              && e->X_add_number < ((bfd_vma) 1 << 44))
-             || (e->X_add_number < 0
-                 && -e->X_add_number <= ((bfd_vma) 1 << 44))))
+      if (e->X_op == O_constant)
        {
-         /* sign-extend */
-         if (e->X_add_number >= 0
-             && (e->X_add_number & ((bfd_vma) 1 << 43)) != 0)
+         if (((e->X_add_number >= 0
+               && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 44))
+              || (e->X_add_number < 0
+                  && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 44))))
            {
-             e->X_add_number |= ~(((bfd_vma) 1 << 44) - 1);
+             /* sign-extend */
+             if (e->X_add_number >= 0
+                 && (e->X_add_number & ((bfd_vma) 1 << 43)) != 0)
+               {
+                 e->X_add_number |= ~(((bfd_vma) 1 << 44) - 1);
+               }
+             return OPERAND_MATCH;
            }
-         return 1;
+         else
+           return OPERAND_OUT_OF_RANGE;
        }
       break;
 
     case IA64_OPND_IMM17:
       /* bit 0 is a don't care (pr0 is hardwired to 1) */
-      if (e->X_op == O_constant
-         && ((e->X_add_number >= 0
-              && e->X_add_number < ((bfd_vma) 1 << 17))
-             || (e->X_add_number < 0
-                 && -e->X_add_number <= ((bfd_vma) 1 << 17))))
-       {
-         /* sign-extend */
-         if (e->X_add_number >= 0
-             && (e->X_add_number & ((bfd_vma) 1 << 16)) != 0)
+      if (e->X_op == O_constant)
+       {
+         if (((e->X_add_number >= 0
+               && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 17))
+              || (e->X_add_number < 0
+                  && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 17))))
            {
-             e->X_add_number |= ~(((bfd_vma) 1 << 17) - 1);
+             /* sign-extend */
+             if (e->X_add_number >= 0
+                 && (e->X_add_number & ((bfd_vma) 1 << 16)) != 0)
+               {
+                 e->X_add_number |= ~(((bfd_vma) 1 << 17) - 1);
+               }
+             return OPERAND_MATCH;
            }
-         return 1;
+         else
+           return OPERAND_OUT_OF_RANGE;
        }
       break;
 
@@ -4795,18 +5450,18 @@ operand_match (idesc, index, e)
          fix->expr = *e;
          fix->is_pcrel = 0;
          ++CURR_SLOT.num_fixups;
-         return 1;
+         return OPERAND_MATCH;
        }
       else if (e->X_op != O_constant
               && ! (e->X_op == O_big && opnd == IA64_OPND_IMM8M1U8))
-       return 0;
+       return OPERAND_MISMATCH;
 
       if (opnd == IA64_OPND_IMM8M1U4)
        {
          /* Zero is not valid for unsigned compares that take an adjusted
             constant immediate range.  */
          if (e->X_add_number == 0)
-           return 0;
+           return OPERAND_OUT_OF_RANGE;
 
          /* Sign-extend 32-bit unsigned numbers, so that the following range
             checks will work.  */
@@ -4818,7 +5473,7 @@ operand_match (idesc, index, e)
          /* Check for 0x100000000.  This is valid because
             0x100000000-1 is the same as ((uint32_t) -1).  */
          if (val == ((bfd_signed_vma) 1 << 32))
-           return 1;
+           return OPERAND_MATCH;
 
          val = val - 1;
        }
@@ -4827,7 +5482,7 @@ operand_match (idesc, index, e)
          /* Zero is not valid for unsigned compares that take an adjusted
             constant immediate range.  */
          if (e->X_add_number == 0)
-           return 0;
+           return OPERAND_OUT_OF_RANGE;
 
          /* Check for 0x10000000000000000.  */
          if (e->X_op == O_big)
@@ -4837,9 +5492,9 @@ operand_match (idesc, index, e)
                  && generic_bignum[2] == 0
                  && generic_bignum[3] == 0
                  && generic_bignum[4] == 1)
-               return 1;
+               return OPERAND_MATCH;
              else
-               return 0;
+               return OPERAND_OUT_OF_RANGE;
            }
          else
            val = e->X_add_number - 1;
@@ -4858,19 +5513,24 @@ operand_match (idesc, index, e)
       else
        val = e->X_add_number;
 
-      if ((val >= 0 && val < ((bfd_vma) 1 << (bits - 1)))
-         || (val < 0 && -val <= ((bfd_vma) 1 << (bits - 1))))
-       return 1;
-      break;
+      if ((val >= 0 && (bfd_vma) val < ((bfd_vma) 1 << (bits - 1)))
+         || (val < 0 && (bfd_vma) -val <= ((bfd_vma) 1 << (bits - 1))))
+       return OPERAND_MATCH;
+      else
+       return OPERAND_OUT_OF_RANGE;
 
     case IA64_OPND_INC3:
       /* +/- 1, 4, 8, 16 */
       val = e->X_add_number;
       if (val < 0)
        val = -val;
-      if (e->X_op == O_constant
-         && (val == 1 || val == 4 || val == 8 || val == 16))
-       return 1;
+      if (e->X_op == O_constant)
+       {
+         if ((val == 1 || val == 4 || val == 8 || val == 16))
+           return OPERAND_MATCH;
+         else
+           return OPERAND_OUT_OF_RANGE;
+       }
       break;
 
     case IA64_OPND_TGT25:
@@ -4896,33 +5556,45 @@ operand_match (idesc, index, e)
          fix->expr = *e;
          fix->is_pcrel = 1;
          ++CURR_SLOT.num_fixups;
-         return 1;
+         return OPERAND_MATCH;
        }
     case IA64_OPND_TAG13:
     case IA64_OPND_TAG13b:
       switch (e->X_op)
        {
        case O_constant:
-         return 1;
+         return OPERAND_MATCH;
 
        case O_symbol:
          fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
-         fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, 0);
+         /* There are no external relocs for TAG13/TAG13b fields, so we
+            create a dummy reloc.  This will not live past md_apply_fix3.  */
+         fix->code = BFD_RELOC_UNUSED;
+         fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, fix->code);
          fix->opnd = idesc->operands[index];
          fix->expr = *e;
          fix->is_pcrel = 1;
          ++CURR_SLOT.num_fixups;
-         return 1;
+         return OPERAND_MATCH;
 
        default:
          break;
        }
       break;
 
+    case IA64_OPND_LDXMOV:
+      fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
+      fix->code = BFD_RELOC_IA64_LDXMOV;
+      fix->opnd = idesc->operands[index];
+      fix->expr = *e;
+      fix->is_pcrel = 0;
+      ++CURR_SLOT.num_fixups;
+      return OPERAND_MATCH;
+
     default:
       break;
     }
-  return 0;
+  return OPERAND_MISMATCH;
 }
 
 static int
@@ -4970,8 +5642,9 @@ parse_operands (idesc)
      struct ia64_opcode *idesc;
 {
   int i = 0, highest_unmatched_operand, num_operands = 0, num_outputs = 0;
-  int sep = 0;
+  int error_pos, out_of_range_pos, curr_out_of_range_pos, sep = 0;
   enum ia64_opnd expected_operand = IA64_OPND_NIL;
+  enum operand_match_result result;
   char mnemonic[129];
   char *first_arg = 0, *end, *saved_input_pointer;
   unsigned int sof;
@@ -5053,6 +5726,8 @@ parse_operands (idesc)
     }
 
   highest_unmatched_operand = 0;
+  curr_out_of_range_pos = -1;
+  error_pos = 0;
   expected_operand = idesc->operands[0];
   for (; idesc; idesc = get_next_opcode (idesc))
     {
@@ -5060,16 +5735,52 @@ parse_operands (idesc)
        continue;               /* mismatch in # of outputs */
 
       CURR_SLOT.num_fixups = 0;
+
+      /* Try to match all operands.  If we see an out-of-range operand,
+        then continue trying to match the rest of the operands, since if
+        the rest match, then this idesc will give the best error message.  */
+
+      out_of_range_pos = -1;
       for (i = 0; i < num_operands && idesc->operands[i]; ++i)
-       if (!operand_match (idesc, i, CURR_SLOT.opnd + i))
-         break;
+       {
+         result = operand_match (idesc, i, CURR_SLOT.opnd + i);
+         if (result != OPERAND_MATCH)
+           {
+             if (result != OPERAND_OUT_OF_RANGE)
+               break;
+             if (out_of_range_pos < 0)
+               /* remember position of the first out-of-range operand: */
+               out_of_range_pos = i;
+           }
+       }
 
-      if (i != num_operands)
+      /* If we did not match all operands, or if at least one operand was
+        out-of-range, then this idesc does not match.  Keep track of which
+        idesc matched the most operands before failing.  If we have two
+        idescs that failed at the same position, and one had an out-of-range
+        operand, then prefer the out-of-range operand.  Thus if we have
+        "add r0=0x1000000,r1" we get an error saying the constant is out
+        of range instead of an error saying that the constant should have been
+        a register.  */
+
+      if (i != num_operands || out_of_range_pos >= 0)
        {
-         if (i > highest_unmatched_operand)
+         if (i > highest_unmatched_operand
+             || (i == highest_unmatched_operand
+                 && out_of_range_pos > curr_out_of_range_pos))
            {
              highest_unmatched_operand = i;
-             expected_operand = idesc->operands[i];
+             if (out_of_range_pos >= 0)
+               {
+                 expected_operand = idesc->operands[out_of_range_pos];
+                 error_pos = out_of_range_pos;
+               }
+             else
+               {
+                 expected_operand = idesc->operands[i];
+                 error_pos = i;
+               }
+             curr_out_of_range_pos = out_of_range_pos;
            }
          continue;
        }
@@ -5084,7 +5795,7 @@ parse_operands (idesc)
     {
       if (expected_operand)
        as_bad ("Operand %u of `%s' should be %s",
-               highest_unmatched_operand + 1, mnemonic,
+               error_pos + 1, mnemonic,
                elf64_ia64_operands[expected_operand].desc);
       else
        as_bad ("Operand mismatch");
@@ -5093,6 +5804,98 @@ parse_operands (idesc)
   return idesc;
 }
 
+/* Keep track of state necessary to determine whether a NOP is necessary
+   to avoid an erratum in A and B step Itanium chips, and return 1 if we
+   detect a case where additional NOPs may be necessary.  */
+static int
+errata_nop_necessary_p (slot, insn_unit)
+     struct slot *slot;
+     enum ia64_unit insn_unit;
+{
+  int i;
+  struct group *this_group = md.last_groups + md.group_idx;
+  struct group *prev_group = md.last_groups + (md.group_idx + 2) % 3;
+  struct ia64_opcode *idesc = slot->idesc;
+
+  /* Test whether this could be the first insn in a problematic sequence.  */
+  if (insn_unit == IA64_UNIT_F)
+    {
+      for (i = 0; i < idesc->num_outputs; i++)
+       if (idesc->operands[i] == IA64_OPND_P1
+           || idesc->operands[i] == IA64_OPND_P2)
+         {
+           int regno = slot->opnd[i].X_add_number - REG_P;
+           /* Ignore invalid operands; they generate errors elsewhere.  */
+           if (regno >= 64)
+             return 0;
+           this_group->p_reg_set[regno] = 1;
+         }
+    }
+
+  /* Test whether this could be the second insn in a problematic sequence.  */
+  if (insn_unit == IA64_UNIT_M && slot->qp_regno > 0
+      && prev_group->p_reg_set[slot->qp_regno])
+    {
+      for (i = 0; i < idesc->num_outputs; i++)
+       if (idesc->operands[i] == IA64_OPND_R1
+           || idesc->operands[i] == IA64_OPND_R2
+           || idesc->operands[i] == IA64_OPND_R3)
+         {
+           int regno = slot->opnd[i].X_add_number - REG_GR;
+           /* Ignore invalid operands; they generate errors elsewhere.  */
+           if (regno >= 128)
+             return 0;
+           if (strncmp (idesc->name, "add", 3) != 0
+               && strncmp (idesc->name, "sub", 3) != 0
+               && strncmp (idesc->name, "shladd", 6) != 0
+               && (idesc->flags & IA64_OPCODE_POSTINC) == 0)
+             this_group->g_reg_set_conditionally[regno] = 1;
+         }
+    }
+
+  /* Test whether this could be the third insn in a problematic sequence.  */
+  for (i = 0; i < NELEMS (idesc->operands) && idesc->operands[i]; i++)
+    {
+      if (/* For fc, ptc, ptr, tak, thash, tpa, ttag, probe, ptr, ptc.  */
+         idesc->operands[i] == IA64_OPND_R3
+         /* For mov indirect.  */
+         || idesc->operands[i] == IA64_OPND_RR_R3
+         || idesc->operands[i] == IA64_OPND_DBR_R3
+         || idesc->operands[i] == IA64_OPND_IBR_R3
+         || idesc->operands[i] == IA64_OPND_PKR_R3
+         || idesc->operands[i] == IA64_OPND_PMC_R3
+         || idesc->operands[i] == IA64_OPND_PMD_R3
+         || idesc->operands[i] == IA64_OPND_MSR_R3
+         || idesc->operands[i] == IA64_OPND_CPUID_R3
+         /* For itr.  */
+         || idesc->operands[i] == IA64_OPND_ITR_R3
+         || idesc->operands[i] == IA64_OPND_DTR_R3
+         /* Normal memory addresses (load, store, xchg, cmpxchg, etc.).  */
+         || idesc->operands[i] == IA64_OPND_MR3)
+       {
+         int regno = slot->opnd[i].X_add_number - REG_GR;
+         /* Ignore invalid operands; they generate errors elsewhere.  */
+         if (regno >= 128)
+           return 0;
+         if (idesc->operands[i] == IA64_OPND_R3)
+           {
+             if (strcmp (idesc->name, "fc") != 0
+                 && strcmp (idesc->name, "tak") != 0
+                 && strcmp (idesc->name, "thash") != 0
+                 && strcmp (idesc->name, "tpa") != 0
+                 && strcmp (idesc->name, "ttag") != 0
+                 && strncmp (idesc->name, "ptr", 3) != 0
+                 && strncmp (idesc->name, "ptc", 3) != 0
+                 && strncmp (idesc->name, "probe", 5) != 0)
+               return 0;
+           }
+         if (prev_group->g_reg_set_conditionally[regno])
+           return 1;
+       }
+    }
+  return 0;
+}
+
 static void
 build_insn (slot, insnp)
      struct slot *slot;
@@ -5229,7 +6032,7 @@ emit_one_bundle ()
   struct ia64_opcode *idesc;
   int end_of_insn_group = 0, user_template = -1;
   int n, i, j, first, curr;
-  unw_rec_list *ptr, *prev;
+  unw_rec_list *ptr;
   bfd_vma t0 = 0, t1 = 0;
   struct label_fix *lfix;
   struct insn_fix *ifix;
@@ -5281,7 +6084,10 @@ emit_one_bundle ()
       for (ptr = md.slot[curr].unwind_record; ptr; ptr = ptr->next)
        if (ptr->r.type == prologue || ptr->r.type == prologue_gr
            || ptr->r.type == body)
-         ptr->slot_number = (unsigned long) f + i;
+         {
+           ptr->slot_number = (unsigned long) f + i;
+           ptr->slot_frag = frag_now;
+         }
 
       if (idesc->flags & IA64_OPCODE_SLOT2)
        {
@@ -5293,7 +6099,8 @@ emit_one_bundle ()
        }
       if (idesc->flags & IA64_OPCODE_LAST)
        {
-         int required_slot, required_template;
+         int required_slot;
+         unsigned int required_template;
 
          /* If we need a stop bit after an M slot, our only choice is
             template 5 (M;;MI).  If we need a stop bit after a B
@@ -5408,10 +6215,11 @@ emit_one_bundle ()
        }
       required_unit = ia64_templ_desc[template].exec_unit[i];
 
-      /* resolve dynamic opcodes such as "break" and "nop":  */
+      /* resolve dynamic opcodes such as "break", "hint", and "nop":  */
       if (idesc->type == IA64_TYPE_DYN)
        {
          if ((strcmp (idesc->name, "nop") == 0)
+             || (strcmp (idesc->name, "hint") == 0)
              || (strcmp (idesc->name, "break") == 0))
            insn_unit = required_unit;
          else if (strcmp (idesc->name, "chk.s") == 0)
@@ -5472,13 +6280,15 @@ emit_one_bundle ()
          continue;             /* try next slot */
        }
 
-      if (debug_type == DEBUG_DWARF2)
-       {
-         bfd_vma addr;
+      {
+       bfd_vma addr;
 
-         addr = frag_now->fr_address + frag_now_fix () - 16 + 1 * i;
-         dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
-       }
+       addr = frag_now->fr_address + frag_now_fix () - 16 + i;
+       dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
+      }
+
+      if (errata_nop_necessary_p (md.slot + curr, insn_unit))
+       as_warn (_("Additional NOP may be necessary to workaround Itanium processor A/B step errata"));
 
       build_insn (md.slot + curr, insn + i);
 
@@ -5486,9 +6296,11 @@ emit_one_bundle ()
       for (ptr = md.slot[curr].unwind_record; ptr; ptr = ptr->next)
        if (ptr->r.type != prologue && ptr->r.type != prologue_gr
            && ptr->r.type != body)
-         ptr->slot_number = (unsigned long) f + i;
+         {
+           ptr->slot_number = (unsigned long) f + i;
+           ptr->slot_frag = frag_now;
+         }
       md.slot[curr].unwind_record = NULL;
-      unwind.next_slot_number = (unsigned long) f + i + ((i == 2)?(0x10-2):1);
 
       if (required_unit == IA64_UNIT_L)
        {
@@ -5504,11 +6316,17 @@ emit_one_bundle ()
          S_SET_VALUE (lfix->sym, frag_now_fix () - 16);
          symbol_set_frag (lfix->sym, frag_now);
        }
+      /* and fix up the tags also.  */
+      for (lfix = md.slot[curr].tag_fixups; lfix; lfix = lfix->next)
+       {
+         S_SET_VALUE (lfix->sym, frag_now_fix () - 16 + i);
+         symbol_set_frag (lfix->sym, frag_now);
+       }
 
       for (j = 0; j < md.slot[curr].num_fixups; ++j)
        {
          ifix = md.slot[curr].fixup + j;
-         fix = fix_new_exp (frag_now, frag_now_fix () - 16 + i, 4,
+         fix = fix_new_exp (frag_now, frag_now_fix () - 16 + i, 8,
                             &ifix->expr, ifix->is_pcrel, ifix->code);
          fix->tc_fix_data.opnd = ifix->opnd;
          fix->fx_plt = (fix->fx_r_type == BFD_RELOC_IA64_PLTOFF22);
@@ -5518,6 +6336,12 @@ emit_one_bundle ()
 
       end_of_insn_group = md.slot[curr].end_of_insn_group;
 
+      if (end_of_insn_group)
+       {
+         md.group_idx = (md.group_idx + 1) % 3;
+         memset (md.last_groups + md.group_idx, 0, sizeof md.last_groups[0]);
+       }
+
       /* clear slot:  */
       ia64_free_opcode (md.slot[curr].idesc);
       memset (md.slot + curr, 0, sizeof (md.slot[curr]));
@@ -5546,8 +6370,11 @@ emit_one_bundle ()
   t0 = end_of_insn_group | (template << 1) | (insn[0] << 5) | (insn[1] << 46);
   t1 = ((insn[1] >> 18) & 0x7fffff) | (insn[2] << 23);
 
-  md_number_to_chars (f + 0, t0, 8);
-  md_number_to_chars (f + 8, t1, 8);
+  number_to_chars_littleendian (f + 0, t0, 8);
+  number_to_chars_littleendian (f + 8, t1, 8);
+
+  unwind.next_slot_number = (unsigned long) f + 16;
+  unwind.next_slot_frag = frag_now;
 }
 
 int
@@ -5555,10 +6382,11 @@ md_parse_option (c, arg)
      int c;
      char *arg;
 {
+
   switch (c)
     {
     /* Switches from the Intel assembler.  */
-    case 'M':
+    case 'm':
       if (strcmp (arg, "ilp64") == 0
          || strcmp (arg, "lp64") == 0
          || strcmp (arg, "p64") == 0)
@@ -5610,12 +6438,13 @@ md_parse_option (c, arg)
       break;
 
     case 'a':
-      /* ??? Conflicts with gas' listing option.  */
       /* indirect=<tgt>        Assume unannotated indirect branches behavior
                        according to <tgt> --
                        exit:   branch out from the current context (default)
                        labels: all labels in context may be branch targets
        */
+      if (strncmp (arg, "indirect=", 9) != 0)
+        return 0;
       break;
 
     case 'x':
@@ -5672,14 +6501,26 @@ md_show_usage (stream)
 {
   fputs (_("\
 IA-64 options:\n\
-  -Milp32|-Milp64|-Mlp64|-Mp64 select data model (default -Mlp64)\n\
-  -Mle | -Mbe            select little- or big-endian byte order (default -Mle)\n\
+  --mconstant-gp         mark output file as using the constant-GP model\n\
+                         (sets ELF header flag EF_IA_64_CONS_GP)\n\
+  --mauto-pic            mark output file as using the constant-GP model\n\
+                         without function descriptors (sets ELF header flag\n\
+                         EF_IA_64_NOFUNCDESC_CONS_GP)\n\
+  -milp32|-milp64|-mlp64|-mp64 select data model (default -mlp64)\n\
+  -mle | -mbe            select little- or big-endian byte order (default -mle)\n\
   -x | -xexplicit        turn on dependency violation checking (default)\n\
   -xauto                 automagically remove dependency violations\n\
   -xdebug                debug dependency violation checker\n"),
        stream);
 }
 
+void
+ia64_after_parse_args ()
+{
+  if (debug_type == DEBUG_STABS)
+    as_fatal (_("--gstabs is not supported for ia64"));
+}
+
 /* Return true if TYPE fits in TEMPL at SLOT.  */
 
 static int
@@ -5711,9 +6552,9 @@ match (int templ, int type, int slot)
 static inline int
 extra_goodness (int templ, int slot)
 {
-  if (match (templ, IA64_TYPE_F, slot))
+  if (slot == 1 && match (templ, IA64_TYPE_F, slot))
     return 2;
-  if (match (templ, IA64_TYPE_B, slot))
+  if (slot == 2 && match (templ, IA64_TYPE_B, slot))
     return 1;
   return 0;
 }
@@ -5724,7 +6565,7 @@ extra_goodness (int templ, int slot)
 void
 md_begin ()
 {
-  int i, j, k, t, total, ar_base, cr_base, goodness, best, regnum;
+  int i, j, k, t, total, ar_base, cr_base, goodness, best, regnum, ok;
   const char *err;
   char name[8];
 
@@ -5733,7 +6574,23 @@ md_begin ()
 
   bfd_set_section_alignment (stdoutput, text_section, 4);
 
-  target_big_endian = 0;
+  /* Make sure fucntion pointers get initialized.  */
+  target_big_endian = -1;
+  dot_byteorder (TARGET_BYTES_BIG_ENDIAN);
+
+  alias_hash = hash_new ();
+  alias_name_hash = hash_new ();
+  secalias_hash = hash_new ();
+  secalias_name_hash = hash_new ();
+
+  pseudo_func[FUNC_DTP_MODULE].u.sym =
+    symbol_new (".<dtpmod>", undefined_section, FUNC_DTP_MODULE,
+               &zero_address_frag);
+
+  pseudo_func[FUNC_DTP_RELATIVE].u.sym =
+    symbol_new (".<dtprel>", undefined_section, FUNC_DTP_RELATIVE,
+               &zero_address_frag);
+
   pseudo_func[FUNC_FPTR_RELATIVE].u.sym =
     symbol_new (".<fptr>", undefined_section, FUNC_FPTR_RELATIVE,
                &zero_address_frag);
@@ -5746,6 +6603,10 @@ md_begin ()
     symbol_new (".<ltoff>", undefined_section, FUNC_LT_RELATIVE,
                &zero_address_frag);
 
+  pseudo_func[FUNC_LT_RELATIVE_X].u.sym =
+    symbol_new (".<ltoffx>", undefined_section, FUNC_LT_RELATIVE_X,
+               &zero_address_frag);
+
   pseudo_func[FUNC_PC_RELATIVE].u.sym =
     symbol_new (".<pcrel>", undefined_section, FUNC_PC_RELATIVE,
                &zero_address_frag);
@@ -5762,6 +6623,10 @@ md_begin ()
     symbol_new (".<segrel>", undefined_section, FUNC_SEG_RELATIVE,
                &zero_address_frag);
 
+  pseudo_func[FUNC_TP_RELATIVE].u.sym =
+    symbol_new (".<tprel>", undefined_section, FUNC_TP_RELATIVE,
+               &zero_address_frag);
+
   pseudo_func[FUNC_LTV_RELATIVE].u.sym =
     symbol_new (".<ltv>", undefined_section, FUNC_LTV_RELATIVE,
                &zero_address_frag);
@@ -5770,6 +6635,22 @@ md_begin ()
     symbol_new (".<ltoff.fptr>", undefined_section, FUNC_LT_FPTR_RELATIVE,
                &zero_address_frag);
 
+  pseudo_func[FUNC_LT_DTP_MODULE].u.sym =
+    symbol_new (".<ltoff.dtpmod>", undefined_section, FUNC_LT_DTP_MODULE,
+               &zero_address_frag);
+
+  pseudo_func[FUNC_LT_DTP_RELATIVE].u.sym =
+    symbol_new (".<ltoff.dptrel>", undefined_section, FUNC_LT_DTP_RELATIVE,
+               &zero_address_frag);
+
+  pseudo_func[FUNC_LT_TP_RELATIVE].u.sym =
+    symbol_new (".<ltoff.tprel>", undefined_section, FUNC_LT_TP_RELATIVE,
+               &zero_address_frag);
+
+  pseudo_func[FUNC_IPLT_RELOC].u.sym =
+    symbol_new (".<iplt>", undefined_section, FUNC_IPLT_RELOC,
+               &zero_address_frag);
+
   /* Compute the table of best templates.  We compute goodness as a
      base 4 value, in which each match counts for 3, each F counts
      for 2, each B counts for 1.  This should maximize the number of
@@ -5932,9 +6813,28 @@ md_begin ()
                  name, err);
     }
 
-  /* Default to 64-bit mode.  */
-  /* ??? This overrides the -M options, but they aren't working anyways.  */
-  md.flags |= EF_IA_64_ABI64;
+  /* Set the architecture and machine depending on defaults and command line
+     options.  */
+  if (md.flags & EF_IA_64_ABI64)
+    ok = bfd_set_arch_mach (stdoutput, bfd_arch_ia64, bfd_mach_ia64_elf64);
+  else
+    ok = bfd_set_arch_mach (stdoutput, bfd_arch_ia64, bfd_mach_ia64_elf32);
+
+  if (! ok)
+     as_warn (_("Could not set architecture and machine"));
+
+  /* Set the pointer size and pointer shift size depending on md.flags */
+
+  if (md.flags & EF_IA_64_ABI64)
+    {
+      md.pointer_size = 8;         /* pointers are 8 bytes */
+      md.pointer_size_shift = 3;   /* alignment is 8 bytes = 2^2 */
+    }
+  else
+    {
+      md.pointer_size = 4;         /* pointers are 4 bytes */
+      md.pointer_size_shift = 2;   /* alignment is 4 bytes = 2^2 */
+    }
 
   md.mem_offset.hint = 0;
   md.path = 0;
@@ -5942,6 +6842,65 @@ md_begin ()
   md.entry_labels = NULL;
 }
 
+/* Set the elf type to 64 bit ABI by default.  Cannot do this in md_begin
+   because that is called after md_parse_option which is where we do the
+   dynamic changing of md.flags based on -mlp64 or -milp32.  Also, set the
+   default endianness.  */
+
+void
+ia64_init (argc, argv)
+     int argc ATTRIBUTE_UNUSED;
+     char **argv ATTRIBUTE_UNUSED;
+{
+  md.flags = MD_FLAGS_DEFAULT;
+}
+
+/* Return a string for the target object file format.  */
+
+const char *
+ia64_target_format ()
+{
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+    {
+      if (md.flags & EF_IA_64_BE)
+       {
+         if (md.flags & EF_IA_64_ABI64)
+#if defined(TE_AIX50)
+           return "elf64-ia64-aix-big";
+#elif defined(TE_HPUX)
+           return "elf64-ia64-hpux-big";
+#else
+           return "elf64-ia64-big";
+#endif
+         else
+#if defined(TE_AIX50)
+           return "elf32-ia64-aix-big";
+#elif defined(TE_HPUX)
+           return "elf32-ia64-hpux-big";
+#else
+           return "elf32-ia64-big";
+#endif
+       }
+      else
+       {
+         if (md.flags & EF_IA_64_ABI64)
+#ifdef TE_AIX50
+           return "elf64-ia64-aix-little";
+#else
+           return "elf64-ia64-little";
+#endif
+         else
+#ifdef TE_AIX50
+           return "elf32-ia64-aix-little";
+#else
+           return "elf32-ia64-little";
+#endif
+       }
+    }
+  else
+    return "unknown-format";
+}
+
 void
 ia64_end_of_source ()
 {
@@ -5953,15 +6912,14 @@ ia64_end_of_source ()
 
   bfd_set_private_flags (stdoutput, md.flags);
 
-  if (debug_type == DEBUG_DWARF2)
-    dwarf2_finish ();
-
   md.mem_offset.hint = 0;
 }
 
 void
 ia64_start_line ()
 {
+  if (md.qp.X_op == O_register)
+    as_bad ("qualifying predicate not followed by instruction");
   md.qp.X_op = O_absent;
 
   if (ignore_input ())
@@ -5976,6 +6934,10 @@ ia64_start_line ()
     }
 }
 
+/* This is a hook for ia64_frob_label, so that it can distinguish tags from
+   labels.  */
+static int defining_tag = 0;
+
 int
 ia64_unrecognized_line (ch)
      int ch;
@@ -6046,6 +7008,67 @@ ia64_unrecognized_line (ch)
       demand_empty_rest_of_line ();
       return 1;
 
+    case '[':
+      {
+       char *s;
+       char c;
+       symbolS *tag;
+       int temp;
+
+       if (md.qp.X_op == O_register)
+         {
+           as_bad ("Tag must come before qualifying predicate.");
+           return 0;
+         }
+
+       /* This implements just enough of read_a_source_file in read.c to
+          recognize labels.  */
+       if (is_name_beginner (*input_line_pointer))
+         {
+           s = input_line_pointer;
+           c = get_symbol_end ();
+         }
+       else if (LOCAL_LABELS_FB
+                && ISDIGIT (*input_line_pointer))
+         {
+           temp = 0;
+           while (ISDIGIT (*input_line_pointer))
+             temp = (temp * 10) + *input_line_pointer++ - '0';
+           fb_label_instance_inc (temp);
+           s = fb_label_name (temp, 0);
+           c = *input_line_pointer;
+         }
+       else
+         {
+           s = NULL;
+           c = '\0';
+         }
+       if (c != ':')
+         {
+           /* Put ':' back for error messages' sake.  */
+           *input_line_pointer++ = ':';
+           as_bad ("Expected ':'");
+           return 0;
+         }
+
+       defining_tag = 1;
+       tag = colon (s);
+       defining_tag = 0;
+       /* Put ':' back for error messages' sake.  */
+       *input_line_pointer++ = ':';
+       if (*input_line_pointer++ != ']')
+         {
+           as_bad ("Expected ']'");
+           return 0;
+         }
+       if (! tag)
+         {
+           as_bad ("Tag name expected");
+           return 0;
+         }
+       return 1;
+      }
+
     default:
       break;
     }
@@ -6060,6 +7083,18 @@ ia64_frob_label (sym)
 {
   struct label_fix *fix;
 
+  /* Tags need special handling since they are not bundle breaks like
+     labels.  */
+  if (defining_tag)
+    {
+      fix = obstack_alloc (&notes, sizeof (*fix));
+      fix->sym = sym;
+      fix->next = CURR_SLOT.tag_fixups;
+      CURR_SLOT.tag_fixups = fix;
+
+      return;
+    }
+
   if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
     {
       md.last_text_seg = now_seg;
@@ -6083,7 +7118,8 @@ ia64_frob_label (sym)
 void
 ia64_flush_pending_output ()
 {
-  if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
+  if (!md.keep_pending_output
+      && bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
     {
       /* ??? This causes many unnecessary stop bits to be emitted.
         Unfortunately, it isn't clear if it is safe to remove this.  */
@@ -6169,7 +7205,7 @@ ia64_parse_name (name, e)
   switch (name[0])
     {
     case 'i':
-      if (name[1] == 'n' && isdigit (name[2]))
+      if (name[1] == 'n' && ISDIGIT (name[2]))
        {
          dr = &md.in;
          name += 2;
@@ -6177,7 +7213,7 @@ ia64_parse_name (name, e)
       break;
 
     case 'l':
-      if (name[1] == 'o' && name[2] == 'c' && isdigit (name[3]))
+      if (name[1] == 'o' && name[2] == 'c' && ISDIGIT (name[3]))
        {
          dr = &md.loc;
          name += 3;
@@ -6185,7 +7221,7 @@ ia64_parse_name (name, e)
       break;
 
     case 'o':
-      if (name[1] == 'u' && name[2] == 't' && isdigit (name[3]))
+      if (name[1] == 'u' && name[2] == 't' && ISDIGIT (name[3]))
        {
          dr = &md.out;
          name += 3;
@@ -6242,19 +7278,29 @@ ia64_canonicalize_symbol_name (name)
   return name;
 }
 
+/* Return true if idesc is a conditional branch instruction.  This excludes
+   the modulo scheduled branches, and br.ia.  Mod-sched branches are excluded
+   because they always read/write resources regardless of the value of the
+   qualifying predicate.  br.ia must always use p0, and hence is always
+   taken.  Thus this function returns true for branches which can fall
+   through, and which use no resources if they do fall through.  */
+
 static int
 is_conditional_branch (idesc)
      struct ia64_opcode *idesc;
 {
-  return (strncmp (idesc->name, "br", 2) == 0
-         && (strcmp (idesc->name, "br") == 0
-             || strncmp (idesc->name, "br.cond", 7) == 0
-             || strncmp (idesc->name, "br.call", 7) == 0
-             || strncmp (idesc->name, "br.ret", 6) == 0
-             || strcmp (idesc->name, "brl") == 0
-             || strncmp (idesc->name, "brl.cond", 7) == 0
-             || strncmp (idesc->name, "brl.call", 7) == 0
-             || strncmp (idesc->name, "brl.ret", 6) == 0));
+  /* br is a conditional branch.  Everything that starts with br. except
+     br.ia, br.c{loop,top,exit}, and br.w{top,exit} is a conditional branch.
+     Everything that starts with brl is a conditional branch.  */
+  return (idesc->name[0] == 'b' && idesc->name[1] == 'r'
+         && (idesc->name[2] == '\0'
+             || (idesc->name[2] == '.' && idesc->name[3] != 'i'
+                 && idesc->name[3] != 'c' && idesc->name[3] != 'w')
+             || idesc->name[2] == 'l'
+             /* br.cond, br.call, br.clr  */
+             || (idesc->name[2] == '.' && idesc->name[3] == 'c'
+                 && (idesc->name[4] == 'a' || idesc->name[4] == 'o'
+                     || (idesc->name[4] == 'l' && idesc->name[5] == 'r')))));
 }
 
 /* Return whether the given opcode is a taken branch.  If there's any doubt,
@@ -6530,7 +7576,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
            }
          else
            {
-             for (i = idesc->num_outputs;i < NELEMS (idesc->operands); i++)
+             for (i = idesc->num_outputs; i < NELEMS (idesc->operands); i++)
                if (idesc->operands[i] == IA64_OPND_B1
                    || idesc->operands[i] == IA64_OPND_B2)
                  {
@@ -6654,7 +7700,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
                    for (i = 0; i < NELEMS (gr_values); i++)
                      {
                        /* Uses all registers *except* the one in R3.  */
-                       if (i != (gr_values[regno].value & 0xFF))
+                       if ((unsigned)i != (gr_values[regno].value & 0xFF))
                          {
                            specs[count] = tmpl;
                            specs[count++].index = i;
@@ -6988,8 +8034,8 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
            {
              int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
              int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
-             int or_andcm = strstr(idesc->name, "or.andcm") != NULL;
-             int and_orcm = strstr(idesc->name, "and.orcm") != NULL;
+             int or_andcm = strstr (idesc->name, "or.andcm") != NULL;
+             int and_orcm = strstr (idesc->name, "and.orcm") != NULL;
 
              if ((idesc->operands[0] == IA64_OPND_P1
                   || idesc->operands[0] == IA64_OPND_P2)
@@ -7052,7 +8098,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
              && idesc->operands[0] == IA64_OPND_PR)
            {
              mask = CURR_SLOT.opnd[2].X_add_number;
-             if (mask & ((valueT) 1<<16))
+             if (mask & ((valueT) 1 << 16))
                for (i = 16; i < 63; i++)
                  {
                    specs[count] = tmpl;
@@ -7110,8 +8156,8 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
            {
              int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
              int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
-             int or_andcm = strstr(idesc->name, "or.andcm") != NULL;
-             int and_orcm = strstr(idesc->name, "and.orcm") != NULL;
+             int or_andcm = strstr (idesc->name, "or.andcm") != NULL;
+             int and_orcm = strstr (idesc->name, "and.orcm") != NULL;
 
              if ((idesc->operands[0] == IA64_OPND_P1
                   || idesc->operands[0] == IA64_OPND_P2)
@@ -7543,13 +8589,13 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
          specs[count++] = tmpl;
        }
       else if (note == 7)
-        {
-          valueT mask = 0;
-          if (idesc->operands[2] == IA64_OPND_IMM17)
-            mask = CURR_SLOT.opnd[2].X_add_number;
-          if (mask & ((valueT) 1 << 63))
+       {
+         valueT mask = 0;
+         if (idesc->operands[2] == IA64_OPND_IMM17)
+           mask = CURR_SLOT.opnd[2].X_add_number;
+         if (mask & ((valueT) 1 << 63))
            specs[count++] = tmpl;
-        }
+       }
       else if (note == 11)
        {
          if ((idesc->operands[0] == IA64_OPND_P1
@@ -7571,16 +8617,16 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
        {
          if (rsrc_write)
            {
-              int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
-              int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
-             int or_andcm = strstr(idesc->name, "or.andcm") != NULL;
-             int and_orcm = strstr(idesc->name, "and.orcm") != NULL;
+             int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
+             int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
+             int or_andcm = strstr (idesc->name, "or.andcm") != NULL;
+             int and_orcm = strstr (idesc->name, "and.orcm") != NULL;
 
              if (p1 == 63
                  && (idesc->operands[0] == IA64_OPND_P1
                      || idesc->operands[0] == IA64_OPND_P2))
                {
-                  specs[count] = tmpl;
+                 specs[count] = tmpl;
                  specs[count++].cmp_type =
                    (or_andcm ? CMP_OR : (and_orcm ? CMP_AND : CMP_NONE));
                }
@@ -7588,7 +8634,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note)
                  && (idesc->operands[1] == IA64_OPND_P1
                      || idesc->operands[1] == IA64_OPND_P2))
                {
-                  specs[count] = tmpl;
+                 specs[count] = tmpl;
                  specs[count++].cmp_type =
                    (or_andcm ? CMP_AND : (and_orcm ? CMP_OR : CMP_NONE));
                }
@@ -7680,6 +8726,77 @@ clear_qp_branch_flag (mask)
     }
 }
 
+/* MASK contains 2 and only 2 PRs which are mutually exclusive.  Remove
+   any mutexes which contain one of the PRs and create new ones when
+   needed.  */
+
+static int
+update_qp_mutex (valueT mask)
+{
+  int i;
+  int add = 0;
+
+  i = 0;
+  while (i < qp_mutexeslen)
+    {
+      if ((qp_mutexes[i].prmask & mask) != 0)
+       {
+         /* If it destroys and creates the same mutex, do nothing.  */
+         if (qp_mutexes[i].prmask == mask
+             && qp_mutexes[i].path == md.path)
+           {
+             i++;
+             add = -1;
+           }
+         else
+           {
+             int keep = 0;
+
+             if (md.debug_dv)
+               {
+                 fprintf (stderr, "  Clearing mutex relation");
+                 print_prmask (qp_mutexes[i].prmask);
+                 fprintf (stderr, "\n");
+               }
+             
+             /* Deal with the old mutex with more than 3+ PRs only if
+                the new mutex on the same execution path with it.
+
+                FIXME: The 3+ mutex support is incomplete.
+                dot_pred_rel () may be a better place to fix it.  */
+             if (qp_mutexes[i].path == md.path)
+               {
+                 /* If it is a proper subset of the mutex, create a
+                    new mutex.  */
+                 if (add == 0
+                     && (qp_mutexes[i].prmask & mask) == mask)
+                   add = 1;
+                 
+                 qp_mutexes[i].prmask &= ~mask;
+                 if (qp_mutexes[i].prmask & (qp_mutexes[i].prmask - 1))
+                   {
+                     /* Modify the mutex if there are more than one
+                        PR left.  */
+                     keep = 1;
+                     i++;
+                   }
+               }
+             
+             if (keep == 0)
+               /* Remove the mutex.  */
+               qp_mutexes[i] = qp_mutexes[--qp_mutexeslen];
+           }
+       }
+      else
+       ++i;
+    }
+
+  if (add == 1)
+    add_qp_mutex (mask);
+
+  return add;
+}
+
 /* Remove any mutexes which contain any of the PRs indicated in the mask.
 
    Any changes to a PR clears the mutex relations which include that PR.  */
@@ -7825,6 +8942,19 @@ add_qp_mutex (mask)
   qp_mutexes[qp_mutexeslen++].prmask = mask;
 }
 
+static int
+has_suffix_p (name, suffix)
+     const char *name;
+     const char *suffix;
+{
+  size_t namelen = strlen (name);
+  size_t sufflen = strlen (suffix);
+
+  if (namelen <= sufflen)
+    return 0;
+  return strcmp (name + namelen - sufflen, suffix) == 0;
+}
+
 static void
 clear_register_values ()
 {
@@ -7881,7 +9011,7 @@ note_register_values (idesc)
       else if (idesc->operands[i] == IA64_OPND_PR_ROT)
        {
          if (idesc->operands[1] & ((valueT) 1 << 43))
-           qp_changemask = ~(valueT) 0xFFFFFFFFFFF | idesc->operands[1];
+           qp_changemask = -((valueT) 1 << 44) | idesc->operands[1];
          else
            qp_changemask = idesc->operands[1];
          qp_changemask &= ~(valueT) 0xFFFF;
@@ -7931,58 +9061,58 @@ note_register_values (idesc)
     {
       int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
       int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
-      valueT p1mask = (valueT) 1 << p1;
-      valueT p2mask = (valueT) 1 << p2;
+      valueT p1mask = (p1 != 0) ? (valueT) 1 << p1 : 0;
+      valueT p2mask = (p2 != 0) ? (valueT) 1 << p2 : 0;
 
-      /* If one of the PRs is PR0, we can't really do anything.  */
-      if (p1 == 0 || p2 == 0)
+      /* If both PRs are PR0, we can't really do anything.  */
+      if (p1 == 0 && p2 == 0)
        {
          if (md.debug_dv)
            fprintf (stderr, "  Ignoring PRs due to inclusion of p0\n");
        }
       /* In general, clear mutexes and implies which include P1 or P2,
         with the following exceptions.  */
-      else if (strstr (idesc->name, ".or.andcm") != NULL)
+      else if (has_suffix_p (idesc->name, ".or.andcm")
+              || has_suffix_p (idesc->name, ".and.orcm"))
        {
-         add_qp_mutex (p1mask | p2mask);
          clear_qp_implies (p2mask, p1mask);
        }
-      else if (strstr (idesc->name, ".and.orcm") != NULL)
-       {
-         add_qp_mutex (p1mask | p2mask);
-         clear_qp_implies (p1mask, p2mask);
-       }
-      else if (strstr (idesc->name, ".and") != NULL)
+      else if (has_suffix_p (idesc->name, ".andcm")
+              || has_suffix_p (idesc->name, ".and"))
        {
          clear_qp_implies (0, p1mask | p2mask);
        }
-      else if (strstr (idesc->name, ".or") != NULL)
+      else if (has_suffix_p (idesc->name, ".orcm")
+              || has_suffix_p (idesc->name, ".or"))
        {
          clear_qp_mutex (p1mask | p2mask);
          clear_qp_implies (p1mask | p2mask, 0);
        }
       else
        {
+         int added = 0;
+
          clear_qp_implies (p1mask | p2mask, p1mask | p2mask);
-         if (strstr (idesc->name, ".unc") != NULL)
+
+         /* If one of the PRs is PR0, we call clear_qp_mutex.  */
+         if (p1 == 0 || p2 == 0)
+           clear_qp_mutex (p1mask | p2mask);
+         else
+           added = update_qp_mutex (p1mask | p2mask);
+
+         if (CURR_SLOT.qp_regno == 0
+             || has_suffix_p (idesc->name, ".unc"))
            {
-             add_qp_mutex (p1mask | p2mask);
+             if (added == 0 && p1 && p2)
+               add_qp_mutex (p1mask | p2mask);
              if (CURR_SLOT.qp_regno != 0)
                {
-                 add_qp_imply (CURR_SLOT.opnd[0].X_add_number - REG_P,
-                               CURR_SLOT.qp_regno);
-                 add_qp_imply (CURR_SLOT.opnd[1].X_add_number - REG_P,
-                               CURR_SLOT.qp_regno);
+                 if (p1)
+                   add_qp_imply (p1, CURR_SLOT.qp_regno);
+                 if (p2)
+                   add_qp_imply (p2, CURR_SLOT.qp_regno);
                }
            }
-         else if (CURR_SLOT.qp_regno == 0)
-           {
-             add_qp_mutex (p1mask | p2mask);
-           }
-         else
-           {
-             clear_qp_mutex (p1mask | p2mask);
-           }
        }
     }
   /* Look for mov imm insns into GRs.  */
@@ -7999,8 +9129,11 @@ note_register_values (idesc)
          gr_values[regno].value = CURR_SLOT.opnd[1].X_add_number;
          gr_values[regno].path = md.path;
          if (md.debug_dv)
-           fprintf (stderr, "  Know gr%d = 0x%llx\n",
-                    regno, gr_values[regno].value);
+           {
+             fprintf (stderr, "  Know gr%d = ", regno);
+             fprintf_vma (stderr, gr_values[regno].value);
+             fputs ("\n", stderr);
+           }
        }
     }
   else
@@ -8192,8 +9325,8 @@ insn_group_break (insert_stop, qp_regno, save_current)
 
 static void
 mark_resource (idesc, dep, spec, depind, path)
-     struct ia64_opcode *idesc;
-     const struct ia64_dependency *dep;
+     struct ia64_opcode *idesc ATTRIBUTE_UNUSED;
+     const struct ia64_dependency *dep ATTRIBUTE_UNUSED;
      struct rsrc *spec;
      int depind;
      int path;
@@ -8203,7 +9336,7 @@ mark_resource (idesc, dep, spec, depind, path)
       regdepstotlen += 20;
       regdeps = (struct rsrc *)
        xrealloc ((void *) regdeps,
-                 regdepstotlen * sizeof(struct rsrc));
+                 regdepstotlen * sizeof (struct rsrc));
     }
 
   regdeps[regdepslen] = *spec;
@@ -8230,9 +9363,12 @@ print_dependency (action, depind)
       if (regdeps[depind].specific && regdeps[depind].index != 0)
        fprintf (stderr, " (%d)", regdeps[depind].index);
       if (regdeps[depind].mem_offset.hint)
-       fprintf (stderr, " 0x%llx+0x%llx",
-                regdeps[depind].mem_offset.base,
-                regdeps[depind].mem_offset.offset);
+       {
+         fputs (" ", stderr);
+         fprintf_vma (stderr, regdeps[depind].mem_offset.base);
+         fputs ("+", stderr);
+         fprintf_vma (stderr, regdeps[depind].mem_offset.offset);
+       }
       fprintf (stderr, "\n");
     }
 }
@@ -8768,7 +9904,10 @@ md_assemble (str)
 
   qp_regno = 0;
   if (md.qp.X_op == O_register)
-    qp_regno = md.qp.X_add_number - REG_P;
+    {
+      qp_regno = md.qp.X_add_number - REG_P;
+      md.qp.X_op = O_absent;
+    }
 
   flags = idesc->flags;
 
@@ -8785,8 +9924,7 @@ md_assemble (str)
   CURR_SLOT.qp_regno = qp_regno;
   CURR_SLOT.idesc = idesc;
   as_where (&CURR_SLOT.src_file, &CURR_SLOT.src_line);
-  if (debug_type == DEBUG_DWARF2)
-    dwarf2_where (&CURR_SLOT.debug_line);
+  dwarf2_where (&CURR_SLOT.debug_line);
 
   /* Add unwind entry, if there is one.  */
   if (unwind.current_entry)
@@ -8817,7 +9955,7 @@ md_assemble (str)
 
 symbolS *
 md_undefined_symbol (name)
-     char *name;
+     char *name ATTRIBUTE_UNUSED;
 {
   return 0;
 }
@@ -8878,11 +10016,22 @@ md_operand (e)
                  as_bad ("Not a symbolic expression");
                  goto err;
                }
-             if (S_GET_VALUE (e->X_op_symbol) == FUNC_FPTR_RELATIVE
-                 && i == FUNC_LT_RELATIVE)
-               i = FUNC_LT_FPTR_RELATIVE;
-             else
+             if (i != FUNC_LT_RELATIVE)
+               {
+                 as_bad ("Illegal combination of relocation functions");
+                 goto err;
+               }
+             switch (S_GET_VALUE (e->X_op_symbol))
                {
+               case FUNC_FPTR_RELATIVE:
+                 i = FUNC_LT_FPTR_RELATIVE; break;
+               case FUNC_DTP_MODULE:
+                 i = FUNC_LT_DTP_MODULE; break;
+               case FUNC_DTP_RELATIVE:
+                 i = FUNC_LT_DTP_RELATIVE; break;
+               case FUNC_TP_RELATIVE:
+                 i = FUNC_LT_TP_RELATIVE; break;
+               default:
                  as_bad ("Illegal combination of relocation functions");
                  goto err;
                }
@@ -8987,12 +10136,16 @@ ia64_force_relocation (fix)
     case BFD_RELOC_IA64_PLTOFF64I:
     case BFD_RELOC_IA64_PLTOFF64MSB:
     case BFD_RELOC_IA64_PLTOFF64LSB:
+
+    case BFD_RELOC_IA64_LTOFF22X:
+    case BFD_RELOC_IA64_LDXMOV:
       return 1;
 
     default:
-      return 0;
+      break;
     }
-  return 0;
+
+  return generic_force_reloc (fix);
 }
 
 /* Decide from what point a pc-relative relocation is relative to,
@@ -9010,6 +10163,20 @@ ia64_pcrel_from_section (fix, sec)
   return off;
 }
 
+
+/* Used to emit section-relative relocs for the dwarf2 debug data.  */
+void
+ia64_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
+{
+  expressionS expr;
+
+  expr.X_op = O_pseudo_fixup;
+  expr.X_op_symbol = pseudo_func[FUNC_SEC_RELATIVE].u.sym;
+  expr.X_add_number = 0;
+  expr.X_add_symbol = symbol;
+  emit_expr (&expr, size);
+}
+
 /* This is called whenever some data item (not an instruction) needs a
    fixup.  We pick the right reloc code depending on the byteorder
    currently in effect.  */
@@ -9038,27 +10205,59 @@ ia64_cons_fix_new (f, where, nbytes, exp)
       break;
 
     case 8:
-      if (target_big_endian)
-       code = BFD_RELOC_IA64_DIR64MSB;
+      /* In 32-bit mode, data8 could mean function descriptors too.  */
+      if (exp->X_op == O_pseudo_fixup
+         && exp->X_op_symbol
+         && S_GET_VALUE (exp->X_op_symbol) == FUNC_IPLT_RELOC
+         && !(md.flags & EF_IA_64_ABI64))
+       {
+         if (target_big_endian)
+           code = BFD_RELOC_IA64_IPLTMSB;
+         else
+           code = BFD_RELOC_IA64_IPLTLSB;
+         exp->X_op = O_symbol;
+         break;
+       }
       else
-       code = BFD_RELOC_IA64_DIR64LSB;
-      break;
+       {
+         if (target_big_endian)
+           code = BFD_RELOC_IA64_DIR64MSB;
+         else
+           code = BFD_RELOC_IA64_DIR64LSB;
+         break;
+       }
+
+    case 16:
+      if (exp->X_op == O_pseudo_fixup
+         && exp->X_op_symbol
+         && S_GET_VALUE (exp->X_op_symbol) == FUNC_IPLT_RELOC)
+       {
+         if (target_big_endian)
+           code = BFD_RELOC_IA64_IPLTMSB;
+         else
+           code = BFD_RELOC_IA64_IPLTLSB;
+         exp->X_op = O_symbol;
+         break;
+       }
+      /* FALLTHRU */
 
     default:
       as_bad ("Unsupported fixup size %d", nbytes);
       ignore_rest_of_line ();
       return;
     }
+
   if (exp->X_op == O_pseudo_fixup)
     {
-      /* ??? */
       exp->X_op = O_symbol;
       code = ia64_gen_real_reloc_type (exp->X_op_symbol, code);
+      /* ??? If code unchanged, unsupported.  */
     }
+
   fix = fix_new_exp (f, where, nbytes, exp, 0, code);
   /* We need to store the byte order in effect in case we're going
      to fix an 8 or 16 bit relocation (for which there no real
-     relocs available).  See md_apply_fix().  */
+     relocs available).  See md_apply_fix3().  */
   fix->tc_fix_data.bigendian = target_big_endian;
 }
 
@@ -9114,6 +10313,14 @@ ia64_gen_real_reloc_type (sym, r_type)
        }
       break;
 
+    case FUNC_LT_RELATIVE_X:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM22:      new = BFD_RELOC_IA64_LTOFF22X; break;
+       default:                        break;
+       }
+      break;
+
     case FUNC_PC_RELATIVE:
       switch (r_type)
        {
@@ -9182,9 +10389,76 @@ ia64_gen_real_reloc_type (sym, r_type)
          break;
        }
       break;
+
+    case FUNC_TP_RELATIVE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM14:
+         new = BFD_RELOC_IA64_TPREL14; break;
+       case BFD_RELOC_IA64_IMM22:
+         new = BFD_RELOC_IA64_TPREL22; break;
+       case BFD_RELOC_IA64_IMM64:
+         new = BFD_RELOC_IA64_TPREL64I; break;
+       default:
+         break;
+       }
+      break;
+
+    case FUNC_LT_TP_RELATIVE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM22:
+         new = BFD_RELOC_IA64_LTOFF_TPREL22; break;
+       default:
+         break;
+       }
+      break;
+
+    case FUNC_LT_DTP_MODULE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM22:
+         new = BFD_RELOC_IA64_LTOFF_DTPMOD22; break;
+       default:
+         break;
+       }
+      break;
+
+    case FUNC_DTP_RELATIVE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_DIR64MSB:
+         new = BFD_RELOC_IA64_DTPREL64MSB; break;
+       case BFD_RELOC_IA64_DIR64LSB:
+         new = BFD_RELOC_IA64_DTPREL64LSB; break;
+       case BFD_RELOC_IA64_IMM14:
+         new = BFD_RELOC_IA64_DTPREL14; break;
+       case BFD_RELOC_IA64_IMM22:
+         new = BFD_RELOC_IA64_DTPREL22; break;
+       case BFD_RELOC_IA64_IMM64:
+         new = BFD_RELOC_IA64_DTPREL64I; break;
+       default:
+         break;
+       }
+      break;
+
+    case FUNC_LT_DTP_RELATIVE:
+      switch (r_type)
+       {
+       case BFD_RELOC_IA64_IMM22:
+         new = BFD_RELOC_IA64_LTOFF_DTPREL22; break;
+       default:
+         break;
+       }
+      break;
+
+    case FUNC_IPLT_RELOC:
+      break;
+
     default:
       abort ();
     }
+
   /* Hmmmm.  Should this ever occur?  */
   if (new)
     return new;
@@ -9270,8 +10544,8 @@ fix_insn (fix, odesc, value)
 
   t0 = control_bits | (insn[0] << 5) | (insn[1] << 46);
   t1 = ((insn[1] >> 18) & 0x7fffff) | (insn[2] << 23);
-  md_number_to_chars (fixpos + 0, t0, 8);
-  md_number_to_chars (fixpos + 8, t1, 8);
+  number_to_chars_littleendian (fixpos + 0, t0, 8);
+  number_to_chars_littleendian (fixpos + 8, t1, 8);
 }
 
 /* Attempt to simplify or even eliminate a fixup.  The return value is
@@ -9280,15 +10554,15 @@ fix_insn (fix, odesc, value)
 
    If fixp->fx_addsy is non-NULL, we'll have to generate a reloc entry
    (if possible).  */
-int
-md_apply_fix3 (fix, valuep, seg)
+
+void
+md_apply_fix3 (fix, valP, seg)
      fixS *fix;
-     valueT *valuep;
-     segT seg;
+     valueT *valP;
+     segT seg ATTRIBUTE_UNUSED;
 {
   char *fixpos;
-  valueT value = *valuep;
-  int adjust = 0;
+  valueT value = *valP;
 
   fixpos = fix->fx_frag->fr_literal + fix->fx_where;
 
@@ -9298,22 +10572,18 @@ md_apply_fix3 (fix, valuep, seg)
        {
        case BFD_RELOC_IA64_DIR32MSB:
          fix->fx_r_type = BFD_RELOC_IA64_PCREL32MSB;
-         adjust = 1;
          break;
 
        case BFD_RELOC_IA64_DIR32LSB:
          fix->fx_r_type = BFD_RELOC_IA64_PCREL32LSB;
-         adjust = 1;
          break;
 
        case BFD_RELOC_IA64_DIR64MSB:
          fix->fx_r_type = BFD_RELOC_IA64_PCREL64MSB;
-         adjust = 1;
          break;
 
        case BFD_RELOC_IA64_DIR64LSB:
          fix->fx_r_type = BFD_RELOC_IA64_PCREL64LSB;
-         adjust = 1;
          break;
 
        default:
@@ -9324,20 +10594,30 @@ md_apply_fix3 (fix, valuep, seg)
     {
       switch (fix->fx_r_type)
        {
-       case 0:
+       case BFD_RELOC_UNUSED:
+         /* This must be a TAG13 or TAG13b operand.  There are no external
+            relocs defined for them, so we must give an error.  */
          as_bad_where (fix->fx_file, fix->fx_line,
                        "%s must have a constant value",
                        elf64_ia64_operands[fix->tc_fix_data.opnd].desc);
+         fix->fx_done = 1;
+         return;
+
+       case BFD_RELOC_IA64_TPREL14:
+       case BFD_RELOC_IA64_TPREL22:
+       case BFD_RELOC_IA64_TPREL64I:
+       case BFD_RELOC_IA64_LTOFF_TPREL22:
+       case BFD_RELOC_IA64_LTOFF_DTPMOD22:
+       case BFD_RELOC_IA64_DTPREL14:
+       case BFD_RELOC_IA64_DTPREL22:
+       case BFD_RELOC_IA64_DTPREL64I:
+       case BFD_RELOC_IA64_LTOFF_DTPREL22:
+         S_SET_THREAD_LOCAL (fix->fx_addsy);
          break;
 
        default:
          break;
        }
-
-      /* ??? This is a hack copied from tc-i386.c to make PCREL relocs
-        work.  There should be a better way to handle this.  */
-      if (adjust)
-       fix->fx_offset += fix->fx_where + fix->fx_frag->fr_address;
     }
   else if (fix->tc_fix_data.opnd == IA64_OPND_NIL)
     {
@@ -9346,15 +10626,12 @@ md_apply_fix3 (fix, valuep, seg)
       else
        number_to_chars_littleendian (fixpos, value, fix->fx_size);
       fix->fx_done = 1;
-      return 1;
     }
   else
     {
       fix_insn (fix, elf64_ia64_operands + fix->tc_fix_data.opnd, value);
       fix->fx_done = 1;
-      return 1;
     }
-  return 1;
 }
 
 /* Generate the BFD reloc to be stuck in the object file from the
@@ -9362,7 +10639,7 @@ md_apply_fix3 (fix, valuep, seg)
 
 arelent *
 tc_gen_reloc (sec, fixp)
-     asection *sec;
+     asection *sec ATTRIBUTE_UNUSED;
      fixS *fixp;
 {
   arelent *reloc;
@@ -9397,7 +10674,6 @@ md_atof (type, lit, size)
      int *size;
 {
   LITTLENUM_TYPE words[MAX_LITTLENUMS];
-  LITTLENUM_TYPE *word;
   char *t;
   int prec;
 
@@ -9432,66 +10708,274 @@ md_atof (type, lit, size)
   t = atof_ieee (input_line_pointer, type, words);
   if (t)
     input_line_pointer = t;
-  *size = prec * sizeof (LITTLENUM_TYPE);
 
-  for (word = words + prec - 1; prec--;)
+  (*ia64_float_to_chars) (lit, words, prec);
+
+  if (type == 'X')
     {
-      md_number_to_chars (lit, (long) (*word--), sizeof (LITTLENUM_TYPE));
-      lit += sizeof (LITTLENUM_TYPE);
+      /* It is 10 byte floating point with 6 byte padding.  */
+      memset (&lit [10], 0, 6);
+      *size = 8 * sizeof (LITTLENUM_TYPE);
     }
+  else
+    *size = prec * sizeof (LITTLENUM_TYPE);
+
   return 0;
 }
 
-/* Round up a section's size to the appropriate boundary.  */
-valueT
-md_section_align (seg, size)
-     segT seg;
-     valueT size;
+/* Handle ia64 specific semantics of the align directive.  */
+
+void
+ia64_md_do_align (n, fill, len, max)
+     int n ATTRIBUTE_UNUSED;
+     const char *fill ATTRIBUTE_UNUSED;
+     int len ATTRIBUTE_UNUSED;
+     int max ATTRIBUTE_UNUSED;
 {
-  int align = bfd_get_section_alignment (stdoutput, seg);
-  valueT mask = ((valueT) 1 << align) - 1;
+  if (subseg_text_p (now_seg))
+    ia64_flush_insns ();
+}
+
+/* This is called from HANDLE_ALIGN in write.c.  Fill in the contents
+   of an rs_align_code fragment.  */
+
+void
+ia64_handle_align (fragp)
+     fragS *fragp;
+{
+  /* Use mfi bundle of nops with no stop bits.  */
+  static const unsigned char le_nop[]
+    = { 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+       0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
+
+  int bytes;
+  char *p;
+
+  if (fragp->fr_type != rs_align_code)
+    return;
 
-  return (size + mask) & ~mask;
+  bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+  p = fragp->fr_literal + fragp->fr_fix;
+
+  /* Make sure we are on a 16-byte boundary, in case someone has been
+     putting data into a text section.  */
+  if (bytes & 15)
+    {
+      int fix = bytes & 15;
+      memset (p, 0, fix);
+      p += fix;
+      bytes -= fix;
+      fragp->fr_fix += fix;
+    }
+
+  /* Instruction bundles are always little-endian.  */
+  memcpy (p, le_nop, 16);
+  fragp->fr_var = 16;
 }
 
-/* Handle ia64 specific semantics of the align directive.  */
+static void
+ia64_float_to_chars_bigendian (char *lit, LITTLENUM_TYPE *words,
+                              int prec)
+{
+  while (prec--)
+    {
+      number_to_chars_bigendian (lit, (long) (*words++),
+                                sizeof (LITTLENUM_TYPE));
+      lit += sizeof (LITTLENUM_TYPE);
+    }
+}
 
-int
-ia64_md_do_align (n, fill, len, max)
-     int n;
-     const char *fill;
-     int len;
-     int max;
+static void
+ia64_float_to_chars_littleendian (char *lit, LITTLENUM_TYPE *words,
+                                 int prec)
 {
-  /* Fill any pending bundle with nops.  */
-  if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
-    ia64_flush_insns ();
+  while (prec--)
+    {
+      number_to_chars_littleendian (lit, (long) (words[prec]),
+                                   sizeof (LITTLENUM_TYPE));
+      lit += sizeof (LITTLENUM_TYPE);
+    }
+}
 
-  /* When we align code in a text section, emit a bundle of 3 nops instead of
-     zero bytes.  We can only do this if a multiple of 16 bytes was requested.
-     N is log base 2 of the requested alignment.  */
-  if (fill == NULL
-      && bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE
-      && n > 4)
-    {
-      /* Use mfi bundle of nops with no stop bits.  */
-      static const unsigned char be_nop[]
-       = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
-           0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c};
-      static const unsigned char le_nop[]
-       = { 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-           0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
-
-      /* Make sure we are on a 16-byte boundary, in case someone has been
-        putting data into a text section.  */
-      frag_align (4, 0, 0);
+void
+ia64_elf_section_change_hook  (void)
+{
+  dot_byteorder (-1);
+}
 
-      if (target_big_endian)
-       frag_align_pattern (n, be_nop, 16, max);
-      else
-       frag_align_pattern (n, le_nop, 16, max);
-      return 1;
+/* Check if a label should be made global.  */
+void
+ia64_check_label (symbolS *label)
+{
+  if (*input_line_pointer == ':')
+    {
+      S_SET_EXTERNAL (label);
+      input_line_pointer++;
     }
+}
 
-  return 0;
+/* Used to remember where .alias and .secalias directives are seen. We
+   will rename symbol and section names when we are about to output
+   the relocatable file.  */
+struct alias
+{
+  char *file;          /* The file where the directive is seen.  */
+  unsigned int line;   /* The line number the directive is at.  */
+  const char *name;    /* The orignale name of the symbol.  */
+};
+
+/* Called for .alias and .secalias directives. If SECTION is 1, it is
+   .secalias. Otherwise, it is .alias.  */
+static void
+dot_alias (int section)
+{
+  char *name, *alias;
+  char delim;
+  char *end_name;
+  int len;
+  const char *error_string;
+  struct alias *h;
+  const char *a;
+  struct hash_control *ahash, *nhash;
+  const char *kind;
+
+  name = input_line_pointer;
+  delim = get_symbol_end ();
+  end_name = input_line_pointer;
+  *end_name = delim;
+
+  if (name == end_name)
+    {
+      as_bad (_("expected symbol name"));
+      discard_rest_of_line ();
+      return;
+    }
+
+  SKIP_WHITESPACE ();
+
+  if (*input_line_pointer != ',')
+    {
+      *end_name = 0;
+      as_bad (_("expected comma after \"%s\""), name);
+      *end_name = delim;
+      ignore_rest_of_line ();
+      return;
+    }
+
+  input_line_pointer++;
+  *end_name = 0;
+
+  /* We call demand_copy_C_string to check if alias string is valid.
+     There should be a closing `"' and no `\0' in the string.  */
+  alias = demand_copy_C_string (&len);
+  if (alias == NULL)
+    {
+      ignore_rest_of_line ();
+      return;
+    }
+
+  /* Make a copy of name string.  */
+  len = strlen (name) + 1;
+  obstack_grow (&notes, name, len);
+  name = obstack_finish (&notes);
+
+  if (section)
+    {
+      kind = "section";
+      ahash = secalias_hash;
+      nhash = secalias_name_hash;
+    }
+  else
+    {
+      kind = "symbol";
+      ahash = alias_hash;
+      nhash = alias_name_hash;
+    }
+
+  /* Check if alias has been used before.  */
+  h = (struct alias *) hash_find (ahash, alias);
+  if (h)
+    {
+      if (strcmp (h->name, name))
+       as_bad (_("`%s' is already the alias of %s `%s'"),
+               alias, kind, h->name);
+      goto out;
+    }
+
+  /* Check if name already has an alias.  */
+  a = (const char *) hash_find (nhash, name);
+  if (a)
+    {
+      if (strcmp (a, alias))
+       as_bad (_("%s `%s' already has an alias `%s'"), kind, name, a);
+      goto out;
+    }
+
+  h = (struct alias *) xmalloc (sizeof (struct alias));
+  as_where (&h->file, &h->line);
+  h->name = name;
+  
+  error_string = hash_jam (ahash, alias, (PTR) h);
+  if (error_string)
+    {
+      as_fatal (_("inserting \"%s\" into %s alias hash table failed: %s"),
+               alias, kind, error_string);
+      goto out;
+    }
+
+  error_string = hash_jam (nhash, name, (PTR) alias);
+  if (error_string)
+    {
+      as_fatal (_("inserting \"%s\" into %s name hash table failed: %s"),
+               alias, kind, error_string);
+out:
+      obstack_free (&notes, name);
+      obstack_free (&notes, alias);
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+/* It renames the original symbol name to its alias.  */
+static void
+do_alias (const char *alias, PTR value)
+{
+  struct alias *h = (struct alias *) value;
+  symbolS *sym = symbol_find (h->name);
+
+  if (sym == NULL)
+    as_warn_where (h->file, h->line,
+                  _("symbol `%s' aliased to `%s' is not used"),
+                  h->name, alias);
+    else
+      S_SET_NAME (sym, (char *) alias);
+}
+
+/* Called from write_object_file.  */
+void
+ia64_adjust_symtab (void)
+{
+  hash_traverse (alias_hash, do_alias);
+}
+
+/* It renames the original section name to its alias.  */
+static void
+do_secalias (const char *alias, PTR value)
+{
+  struct alias *h = (struct alias *) value;
+  segT sec = bfd_get_section_by_name (stdoutput, h->name);
+
+  if (sec == NULL)
+    as_warn_where (h->file, h->line,
+                  _("section `%s' aliased to `%s' is not used"),
+                  h->name, alias);
+  else
+    sec->name = alias;
+}
+
+/* Called from write_object_file.  */
+void
+ia64_frob_file (void)
+{
+  hash_traverse (secalias_hash, do_secalias);
 }
This page took 0.072622 seconds and 4 git commands to generate.