OSDN Git Service

daily update
[pf3gnuchains/pf3gnuchains3x.git] / bfd / elfxx-ia64.c
index 37b0d24..420a0ef 100644 (file)
@@ -1,13 +1,13 @@
 /* IA-64 support for 64-bit ELF
-   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004
-   Free Software Foundation, Inc.
+   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+   2008, 2009  Free Software Foundation, Inc.
    Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
 
    This file is part of BFD, the Binary File Descriptor library.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "opcode/ia64.h"
 #include "objalloc.h"
 #include "hashtab.h"
 
+#define ARCH_SIZE      NN
+
+#if ARCH_SIZE == 64
+#define        LOG_SECTION_ALIGN       3
+#endif
+
+#if ARCH_SIZE == 32
+#define        LOG_SECTION_ALIGN       2
+#endif
+
 /* THE RULES for all the stuff the linker creates --
 
   GOT          Entries created in response to LTOFF or LTOFF_FPTR
@@ -70,9 +81,6 @@ struct elfNN_ia64_dyn_sym_info
   /* The addend for which this entry is relevant.  */
   bfd_vma addend;
 
-  /* Next addend in the list.  */
-  struct elfNN_ia64_dyn_sym_info *next;
-
   bfd_vma got_offset;
   bfd_vma fptr_offset;
   bfd_vma pltoff_offset;
@@ -123,6 +131,13 @@ struct elfNN_ia64_local_hash_entry
 {
   int id;
   unsigned int r_sym;
+  /* The number of elements in elfNN_ia64_dyn_sym_info array.  */
+  unsigned int count;
+  /* The number of sorted elements in elfNN_ia64_dyn_sym_info array.  */
+  unsigned int sorted_count;
+  /* The size of elfNN_ia64_dyn_sym_info array.  */
+  unsigned int size;
+  /* The array of elfNN_ia64_dyn_sym_info.  */
   struct elfNN_ia64_dyn_sym_info *info;
 
   /* TRUE if this hash entry's addends was translated for
@@ -133,6 +148,13 @@ struct elfNN_ia64_local_hash_entry
 struct elfNN_ia64_link_hash_entry
 {
   struct elf_link_hash_entry root;
+  /* The number of elements in elfNN_ia64_dyn_sym_info array.  */
+  unsigned int count;
+  /* The number of sorted elements in elfNN_ia64_dyn_sym_info array.  */
+  unsigned int sorted_count;
+  /* The size of elfNN_ia64_dyn_sym_info array.  */
+  unsigned int size;
+  /* The array of elfNN_ia64_dyn_sym_info.  */
   struct elfNN_ia64_dyn_sym_info *info;
 };
 
@@ -141,11 +163,8 @@ struct elfNN_ia64_link_hash_table
   /* The main hash table.  */
   struct elf_link_hash_table root;
 
-  asection *got_sec;           /* the linkage table section (or NULL) */
-  asection *rel_got_sec;       /* dynamic relocation section for same */
   asection *fptr_sec;          /* function descriptor table (or NULL) */
   asection *rel_fptr_sec;      /* dynamic relocation section for same */
-  asection *plt_sec;           /* the primary plt section (or NULL) */
   asection *pltoff_sec;                /* private descriptors for plt (or NULL) */
   asection *rel_pltoff_sec;    /* dynamic relocation section for same */
 
@@ -153,6 +172,14 @@ struct elfNN_ia64_link_hash_table
   unsigned reltext : 1;                /* are there relocs against readonly sections? */
   unsigned self_dtpmod_done : 1;/* has self DTPMOD entry been finished? */
   bfd_vma self_dtpmod_offset;  /* .got offset to self DTPMOD entry */
+  /* There are maybe R_IA64_GPREL22 relocations, including those
+     optimized from R_IA64_LTOFF22X, against non-SHF_IA_64_SHORT
+     sections.  We need to record those sections so that we can choose
+     a proper GP to cover all R_IA64_GPREL22 relocations.  */
+  asection *max_short_sec;     /* maximum short output section */
+  bfd_vma max_short_offset;    /* maximum short offset */
+  asection *min_short_sec;     /* minimum short output section */
+  bfd_vma min_short_offset;    /* minimum short offset */
 
   htab_t loc_hash_table;
   void *loc_hash_memory;
@@ -162,183 +189,51 @@ struct elfNN_ia64_allocate_data
 {
   struct bfd_link_info *info;
   bfd_size_type ofs;
+  bfd_boolean only_got;
 };
 
 #define elfNN_ia64_hash_table(p) \
   ((struct elfNN_ia64_link_hash_table *) ((p)->hash))
 
-static bfd_reloc_status_type elfNN_ia64_reloc
-  PARAMS ((bfd *abfd, arelent *reloc, asymbol *sym, PTR data,
-          asection *input_section, bfd *output_bfd, char **error_message));
-static reloc_howto_type * lookup_howto
-  PARAMS ((unsigned int rtype));
-static reloc_howto_type *elfNN_ia64_reloc_type_lookup
-  PARAMS ((bfd *abfd, bfd_reloc_code_real_type bfd_code));
-static void elfNN_ia64_info_to_howto
-  PARAMS ((bfd *abfd, arelent *bfd_reloc, Elf_Internal_Rela *elf_reloc));
-static bfd_boolean elfNN_ia64_relax_section
-  PARAMS((bfd *abfd, asection *sec, struct bfd_link_info *link_info,
-         bfd_boolean *again));
-static void elfNN_ia64_relax_ldxmov
-  PARAMS((bfd *abfd, bfd_byte *contents, bfd_vma off));
-static bfd_boolean is_unwind_section_name
-  PARAMS ((bfd *abfd, const char *));
-static bfd_boolean elfNN_ia64_section_from_shdr
-  PARAMS ((bfd *, Elf_Internal_Shdr *, const char *));
-static bfd_boolean elfNN_ia64_section_flags
-  PARAMS ((flagword *, const Elf_Internal_Shdr *));
-static bfd_boolean elfNN_ia64_fake_sections
-  PARAMS ((bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec));
-static void elfNN_ia64_final_write_processing
-  PARAMS ((bfd *abfd, bfd_boolean linker));
-static bfd_boolean elfNN_ia64_add_symbol_hook
-  PARAMS ((bfd *abfd, struct bfd_link_info *info, Elf_Internal_Sym *sym,
-          const char **namep, flagword *flagsp, asection **secp,
-          bfd_vma *valp));
-static int elfNN_ia64_additional_program_headers
-  PARAMS ((bfd *abfd));
-static bfd_boolean elfNN_ia64_modify_segment_map
-  PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elfNN_ia64_is_local_label_name
-  PARAMS ((bfd *abfd, const char *name));
+static struct elfNN_ia64_dyn_sym_info * get_dyn_sym_info
+  (struct elfNN_ia64_link_hash_table *ia64_info,
+   struct elf_link_hash_entry *h,
+   bfd *abfd, const Elf_Internal_Rela *rel, bfd_boolean create);
 static bfd_boolean elfNN_ia64_dynamic_symbol_p
-  PARAMS ((struct elf_link_hash_entry *h, struct bfd_link_info *info, int));
-static struct bfd_hash_entry *elfNN_ia64_new_elf_hash_entry
-  PARAMS ((struct bfd_hash_entry *entry, struct bfd_hash_table *table,
-          const char *string));
-static void elfNN_ia64_hash_copy_indirect
-  PARAMS ((const struct elf_backend_data *, struct elf_link_hash_entry *,
-          struct elf_link_hash_entry *));
-static void elfNN_ia64_hash_hide_symbol
-  PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *, bfd_boolean));
-static hashval_t elfNN_ia64_local_htab_hash PARAMS ((const void *));
-static int elfNN_ia64_local_htab_eq PARAMS ((const void *ptr1,
-                                            const void *ptr2));
-static struct bfd_link_hash_table *elfNN_ia64_hash_table_create
-  PARAMS ((bfd *abfd));
-static void elfNN_ia64_hash_table_free
-  PARAMS ((struct bfd_link_hash_table *hash));
-static bfd_boolean elfNN_ia64_global_dyn_sym_thunk
-  PARAMS ((struct bfd_hash_entry *, PTR));
-static int elfNN_ia64_local_dyn_sym_thunk
-  PARAMS ((void **, PTR));
+  (struct elf_link_hash_entry *h, struct bfd_link_info *info, int);
+static bfd_reloc_status_type elfNN_ia64_install_value
+  (bfd_byte *hit_addr, bfd_vma val, unsigned int r_type);
+static bfd_boolean elfNN_ia64_choose_gp
+  (bfd *abfd, struct bfd_link_info *info);
+static void elfNN_ia64_relax_ldxmov
+  (bfd_byte *contents, bfd_vma off);
 static void elfNN_ia64_dyn_sym_traverse
-  PARAMS ((struct elfNN_ia64_link_hash_table *ia64_info,
-          bfd_boolean (*func) (struct elfNN_ia64_dyn_sym_info *, PTR),
-          PTR info));
-static bfd_boolean elfNN_ia64_create_dynamic_sections
-  PARAMS ((bfd *abfd, struct bfd_link_info *info));
-static struct elfNN_ia64_local_hash_entry * get_local_sym_hash
-  PARAMS ((struct elfNN_ia64_link_hash_table *ia64_info,
-          bfd *abfd, const Elf_Internal_Rela *rel, bfd_boolean create));
-static struct elfNN_ia64_dyn_sym_info * get_dyn_sym_info
-  PARAMS ((struct elfNN_ia64_link_hash_table *ia64_info,
-          struct elf_link_hash_entry *h,
-          bfd *abfd, const Elf_Internal_Rela *rel, bfd_boolean create));
-static asection *get_got
-  PARAMS ((bfd *abfd, struct bfd_link_info *info,
-          struct elfNN_ia64_link_hash_table *ia64_info));
-static asection *get_fptr
-  PARAMS ((bfd *abfd, struct bfd_link_info *info,
-          struct elfNN_ia64_link_hash_table *ia64_info));
-static asection *get_pltoff
-  PARAMS ((bfd *abfd, struct bfd_link_info *info,
-          struct elfNN_ia64_link_hash_table *ia64_info));
-static asection *get_reloc_section
-  PARAMS ((bfd *abfd, struct elfNN_ia64_link_hash_table *ia64_info,
-          asection *sec, bfd_boolean create));
-static bfd_boolean elfNN_ia64_check_relocs
-  PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *sec,
-          const Elf_Internal_Rela *relocs));
-static bfd_boolean elfNN_ia64_adjust_dynamic_symbol
-  PARAMS ((struct bfd_link_info *info, struct elf_link_hash_entry *h));
-static long global_sym_index
-  PARAMS ((struct elf_link_hash_entry *h));
-static bfd_boolean allocate_fptr
-  PARAMS ((struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data));
+  (struct elfNN_ia64_link_hash_table *ia64_info,
+   bfd_boolean (*func) (struct elfNN_ia64_dyn_sym_info *, PTR),
+   PTR info);
 static bfd_boolean allocate_global_data_got
-  PARAMS ((struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data));
+  (struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data);
 static bfd_boolean allocate_global_fptr_got
-  PARAMS ((struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data));
+  (struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data);
 static bfd_boolean allocate_local_got
-  PARAMS ((struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data));
-static bfd_boolean allocate_pltoff_entries
-  PARAMS ((struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data));
-static bfd_boolean allocate_plt_entries
-  PARAMS ((struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data));
-static bfd_boolean allocate_plt2_entries
-  PARAMS ((struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data));
-static bfd_boolean allocate_dynrel_entries
-  PARAMS ((struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data));
-static bfd_boolean elfNN_ia64_size_dynamic_sections
-  PARAMS ((bfd *output_bfd, struct bfd_link_info *info));
-static bfd_reloc_status_type elfNN_ia64_install_value
-  PARAMS ((bfd *abfd, bfd_byte *hit_addr, bfd_vma val, unsigned int r_type));
-static void elfNN_ia64_install_dyn_reloc
-  PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *sec,
-          asection *srel, bfd_vma offset, unsigned int type,
-          long dynindx, bfd_vma addend));
-static bfd_vma set_got_entry
-  PARAMS ((bfd *abfd, struct bfd_link_info *info,
-          struct elfNN_ia64_dyn_sym_info *dyn_i, long dynindx,
-          bfd_vma addend, bfd_vma value, unsigned int dyn_r_type));
-static bfd_vma set_fptr_entry
-  PARAMS ((bfd *abfd, struct bfd_link_info *info,
-          struct elfNN_ia64_dyn_sym_info *dyn_i,
-          bfd_vma value));
-static bfd_vma set_pltoff_entry
-  PARAMS ((bfd *abfd, struct bfd_link_info *info,
-          struct elfNN_ia64_dyn_sym_info *dyn_i,
-          bfd_vma value, bfd_boolean));
-static bfd_vma elfNN_ia64_tprel_base
-  PARAMS ((struct bfd_link_info *info));
-static bfd_vma elfNN_ia64_dtprel_base
-  PARAMS ((struct bfd_link_info *info));
-static int elfNN_ia64_unwind_entry_compare
-  PARAMS ((const PTR, const PTR));
-static bfd_boolean elfNN_ia64_choose_gp
-  PARAMS ((bfd *abfd, struct bfd_link_info *info));
-static bfd_boolean elfNN_ia64_final_link
-  PARAMS ((bfd *abfd, struct bfd_link_info *info));
-static bfd_boolean elfNN_ia64_relocate_section
-  PARAMS ((bfd *output_bfd, struct bfd_link_info *info, bfd *input_bfd,
-          asection *input_section, bfd_byte *contents,
-          Elf_Internal_Rela *relocs, Elf_Internal_Sym *local_syms,
-          asection **local_sections));
-static bfd_boolean elfNN_ia64_finish_dynamic_symbol
-  PARAMS ((bfd *output_bfd, struct bfd_link_info *info,
-          struct elf_link_hash_entry *h, Elf_Internal_Sym *sym));
-static bfd_boolean elfNN_ia64_finish_dynamic_sections
-  PARAMS ((bfd *abfd, struct bfd_link_info *info));
-static bfd_boolean elfNN_ia64_set_private_flags
-  PARAMS ((bfd *abfd, flagword flags));
-static bfd_boolean elfNN_ia64_merge_private_bfd_data
-  PARAMS ((bfd *ibfd, bfd *obfd));
-static bfd_boolean elfNN_ia64_print_private_bfd_data
-  PARAMS ((bfd *abfd, PTR ptr));
-static enum elf_reloc_type_class elfNN_ia64_reloc_type_class
-  PARAMS ((const Elf_Internal_Rela *));
+  (struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data);
 static bfd_boolean elfNN_ia64_hpux_vec
-  PARAMS ((const bfd_target *vec));
-static void elfNN_hpux_post_process_headers
-  PARAMS ((bfd *abfd, struct bfd_link_info *info));
-bfd_boolean elfNN_hpux_backend_section_from_bfd_section
-  PARAMS ((bfd *abfd, asection *sec, int *retval));
+  (const bfd_target *vec);
+static bfd_boolean allocate_dynrel_entries
+  (struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data);
+static asection *get_pltoff
+  (bfd *abfd, struct bfd_link_info *info,
+   struct elfNN_ia64_link_hash_table *ia64_info);
 \f
 /* ia64-specific relocation.  */
 
 /* Perform a relocation.  Not much to do here as all the hard work is
    done in elfNN_ia64_final_link_relocate.  */
 static bfd_reloc_status_type
-elfNN_ia64_reloc (abfd, reloc, sym, data, input_section,
-                 output_bfd, error_message)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     arelent *reloc;
-     asymbol *sym ATTRIBUTE_UNUSED;
-     PTR data ATTRIBUTE_UNUSED;
-     asection *input_section;
-     bfd *output_bfd;
-     char **error_message;
+elfNN_ia64_reloc (bfd *abfd ATTRIBUTE_UNUSED, arelent *reloc,
+                 asymbol *sym ATTRIBUTE_UNUSED,
+                 PTR data ATTRIBUTE_UNUSED, asection *input_section,
+                 bfd *output_bfd, char **error_message)
 {
   if (output_bfd)
     {
@@ -445,8 +340,8 @@ static reloc_howto_type ia64_howto_table[] =
     IA64_HOWTO (R_IA64_TPREL64LSB,  "TPREL64LSB",  4, FALSE, FALSE),
     IA64_HOWTO (R_IA64_LTOFF_TPREL22, "LTOFF_TPREL22",  0, FALSE, FALSE),
 
-    IA64_HOWTO (R_IA64_DTPMOD64MSB, "TPREL64MSB",  4, FALSE, FALSE),
-    IA64_HOWTO (R_IA64_DTPMOD64LSB, "TPREL64LSB",  4, FALSE, FALSE),
+    IA64_HOWTO (R_IA64_DTPMOD64MSB, "DTPMOD64MSB",  4, FALSE, FALSE),
+    IA64_HOWTO (R_IA64_DTPMOD64LSB, "DTPMOD64LSB",  4, FALSE, FALSE),
     IA64_HOWTO (R_IA64_LTOFF_DTPMOD22, "LTOFF_DTPMOD22", 0, FALSE, FALSE),
 
     IA64_HOWTO (R_IA64_DTPREL14,    "DTPREL14",           0, FALSE, FALSE),
@@ -464,8 +359,7 @@ static unsigned char elf_code_to_howto_index[R_IA64_MAX_RELOC_CODE + 1];
 /* Given a BFD reloc type, return the matching HOWTO structure.  */
 
 static reloc_howto_type *
-lookup_howto (rtype)
-     unsigned int rtype;
+lookup_howto (unsigned int rtype)
 {
   static int inited = 0;
   int i;
@@ -479,7 +373,8 @@ lookup_howto (rtype)
        elf_code_to_howto_index[ia64_howto_table[i].type] = i;
     }
 
-  BFD_ASSERT (rtype <= R_IA64_MAX_RELOC_CODE);
+  if (rtype > R_IA64_MAX_RELOC_CODE)
+    return 0;
   i = elf_code_to_howto_index[rtype];
   if (i >= NELEMS (ia64_howto_table))
     return 0;
@@ -487,9 +382,8 @@ lookup_howto (rtype)
 }
 
 static reloc_howto_type*
-elfNN_ia64_reloc_type_lookup (abfd, bfd_code)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     bfd_reloc_code_real_type bfd_code;
+elfNN_ia64_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+                             bfd_reloc_code_real_type bfd_code)
 {
   unsigned int rtype;
 
@@ -596,13 +490,28 @@ elfNN_ia64_reloc_type_lookup (abfd, bfd_code)
   return lookup_howto (rtype);
 }
 
+static reloc_howto_type *
+elfNN_ia64_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+                             const char *r_name)
+{
+  unsigned int i;
+
+  for (i = 0;
+       i < sizeof (ia64_howto_table) / sizeof (ia64_howto_table[0]);
+       i++)
+    if (ia64_howto_table[i].name != NULL
+       && strcasecmp (ia64_howto_table[i].name, r_name) == 0)
+      return &ia64_howto_table[i];
+
+  return NULL;
+}
+
 /* Given a ELF reloc, return the matching HOWTO structure.  */
 
 static void
-elfNN_ia64_info_to_howto (abfd, bfd_reloc, elf_reloc)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     arelent *bfd_reloc;
-     Elf_Internal_Rela *elf_reloc;
+elfNN_ia64_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
+                         arelent *bfd_reloc,
+                         Elf_Internal_Rela *elf_reloc)
 {
   bfd_reloc->howto
     = lookup_howto ((unsigned int) ELFNN_R_TYPE (elf_reloc->r_info));
@@ -673,17 +582,155 @@ bfd_elfNN_ia64_after_parse (int itanium)
   oor_branch_size = itanium ? sizeof (oor_ip) : sizeof (oor_brl);
 }
 
+#define BTYPE_SHIFT    6
+#define Y_SHIFT                26
+#define X6_SHIFT       27
+#define X4_SHIFT       27
+#define X3_SHIFT       33
+#define X2_SHIFT       31
+#define X_SHIFT                33
+#define OPCODE_SHIFT   37
+
+#define OPCODE_BITS    (0xfLL << OPCODE_SHIFT)
+#define X6_BITS                (0x3fLL << X6_SHIFT)
+#define X4_BITS                (0xfLL << X4_SHIFT)
+#define X3_BITS                (0x7LL << X3_SHIFT)
+#define X2_BITS                (0x3LL << X2_SHIFT)
+#define X_BITS         (0x1LL << X_SHIFT)
+#define Y_BITS         (0x1LL << Y_SHIFT)
+#define BTYPE_BITS     (0x7LL << BTYPE_SHIFT)
+#define PREDICATE_BITS (0x3fLL)
+
+#define IS_NOP_B(i) \
+  (((i) & (OPCODE_BITS | X6_BITS)) == (2LL << OPCODE_SHIFT))
+#define IS_NOP_F(i) \
+  (((i) & (OPCODE_BITS | X_BITS | X6_BITS | Y_BITS)) \
+   == (0x1LL << X6_SHIFT))
+#define IS_NOP_I(i) \
+  (((i) & (OPCODE_BITS | X3_BITS | X6_BITS | Y_BITS)) \
+   == (0x1LL << X6_SHIFT))
+#define IS_NOP_M(i) \
+  (((i) & (OPCODE_BITS | X3_BITS | X2_BITS | X4_BITS | Y_BITS)) \
+   == (0x1LL << X4_SHIFT))
+#define IS_BR_COND(i) \
+  (((i) & (OPCODE_BITS | BTYPE_BITS)) == (0x4LL << OPCODE_SHIFT))
+#define IS_BR_CALL(i) \
+  (((i) & OPCODE_BITS) == (0x5LL << OPCODE_SHIFT))
+
+static bfd_boolean
+elfNN_ia64_relax_br (bfd_byte *contents, bfd_vma off)
+{
+  unsigned int template_val, mlx;
+  bfd_vma t0, t1, s0, s1, s2, br_code;
+  long br_slot;
+  bfd_byte *hit_addr;
+
+  hit_addr = (bfd_byte *) (contents + off);
+  br_slot = (long) hit_addr & 0x3;
+  hit_addr -= br_slot;
+  t0 = bfd_getl64 (hit_addr + 0);
+  t1 = bfd_getl64 (hit_addr + 8);
+
+  /* Check if we can turn br into brl.  A label is always at the start
+     of the bundle.  Even if there are predicates on NOPs, we still
+     perform this optimization.  */
+  template_val = t0 & 0x1e;
+  s0 = (t0 >> 5) & 0x1ffffffffffLL;
+  s1 = ((t0 >> 46) | (t1 << 18)) & 0x1ffffffffffLL;
+  s2 = (t1 >> 23) & 0x1ffffffffffLL;
+  switch (br_slot)
+    {
+    case 0:
+      /* Check if slot 1 and slot 2 are NOPs. Possible template is
+         BBB.  We only need to check nop.b.  */
+      if (!(IS_NOP_B (s1) && IS_NOP_B (s2)))
+       return FALSE;
+      br_code = s0;
+      break;
+    case 1:
+      /* Check if slot 2 is NOP. Possible templates are MBB and BBB.
+        For BBB, slot 0 also has to be nop.b.  */
+      if (!((template_val == 0x12                              /* MBB */
+            && IS_NOP_B (s2))
+           || (template_val == 0x16                    /* BBB */
+               && IS_NOP_B (s0)
+               && IS_NOP_B (s2))))
+       return FALSE;
+      br_code = s1;
+      break;
+    case 2:
+      /* Check if slot 1 is NOP. Possible templates are MIB, MBB, BBB,
+        MMB and MFB. For BBB, slot 0 also has to be nop.b.  */
+      if (!((template_val == 0x10                              /* MIB */
+            && IS_NOP_I (s1))
+           || (template_val == 0x12                    /* MBB */
+               && IS_NOP_B (s1))
+           || (template_val == 0x16                    /* BBB */
+               && IS_NOP_B (s0)
+               && IS_NOP_B (s1))
+           || (template_val == 0x18                    /* MMB */
+               && IS_NOP_M (s1))
+           || (template_val == 0x1c                    /* MFB */
+               && IS_NOP_F (s1))))
+       return FALSE;
+      br_code = s2;
+      break;
+    default:
+      /* It should never happen.  */
+      abort ();
+    }
+
+  /* We can turn br.cond/br.call into brl.cond/brl.call.  */
+  if (!(IS_BR_COND (br_code) || IS_BR_CALL (br_code)))
+    return FALSE;
+
+  /* Turn br into brl by setting bit 40.  */
+  br_code |= 0x1LL << 40;
+
+  /* Turn the old bundle into a MLX bundle with the same stop-bit
+     variety.  */
+  if (t0 & 0x1)
+    mlx = 0x5;
+  else
+    mlx = 0x4;
+
+  if (template_val == 0x16)
+    {
+      /* For BBB, we need to put nop.m in slot 0.  We keep the original
+        predicate only if slot 0 isn't br.  */
+      if (br_slot == 0)
+       t0 = 0LL;
+      else
+       t0 &= PREDICATE_BITS << 5;
+      t0 |= 0x1LL << (X4_SHIFT + 5);
+    }
+  else
+    {
+      /* Keep the original instruction in slot 0.  */
+      t0 &= 0x1ffffffffffLL << 5;
+    }
+
+  t0 |= mlx;
+
+  /* Put brl in slot 1.  */
+  t1 = br_code << 23;
+
+  bfd_putl64 (t0, hit_addr);
+  bfd_putl64 (t1, hit_addr + 8);
+  return TRUE;
+}
+
 static void
-elfNN_ia64_relax_brl (bfd *abfd, bfd_byte *contents, bfd_vma off)
+elfNN_ia64_relax_brl (bfd_byte *contents, bfd_vma off)
 {
-  int template;
+  int template_val;
   bfd_byte *hit_addr;
   bfd_vma t0, t1, i0, i1, i2;
 
   hit_addr = (bfd_byte *) (contents + off);
   hit_addr -= (long) hit_addr & 0x3;
-  t0 = bfd_get_64 (abfd, hit_addr);
-  t1 = bfd_get_64 (abfd, hit_addr + 8);
+  t0 = bfd_getl64 (hit_addr);
+  t1 = bfd_getl64 (hit_addr + 8);
 
   /* Keep the instruction in slot 0. */
   i0 = (t0 >> 5) & 0x1ffffffffffLL;
@@ -694,24 +741,65 @@ elfNN_ia64_relax_brl (bfd *abfd, bfd_byte *contents, bfd_vma off)
 
   /* Turn a MLX bundle into a MBB bundle with the same stop-bit
      variety.  */
-  template = 0x12;
-  if ((t0 & 0x1fLL) == 5)
-    template += 1;
-  t0 = (i1 << 46) | (i0 << 5) | template;
+  if (t0 & 0x1)
+    template_val = 0x13;
+  else
+    template_val = 0x12;
+  t0 = (i1 << 46) | (i0 << 5) | template_val;
   t1 = (i2 << 23) | (i1 >> 18);
 
-  bfd_put_64 (abfd, t0, hit_addr);
-  bfd_put_64 (abfd, t1, hit_addr + 8);
+  bfd_putl64 (t0, hit_addr);
+  bfd_putl64 (t1, hit_addr + 8);
 }
+
+/* Rename some of the generic section flags to better document how they
+   are used here.  */
+#define skip_relax_pass_0 need_finalize_relax
+#define skip_relax_pass_1 has_gp_reloc
+
 \f
 /* These functions do relaxation for IA-64 ELF.  */
 
+static void
+elfNN_ia64_update_short_info (asection *sec, bfd_vma offset,
+                             struct elfNN_ia64_link_hash_table *ia64_info)
+{
+  /* Skip ABS and SHF_IA_64_SHORT sections.  */
+  if (sec == bfd_abs_section_ptr
+      || (sec->flags & SEC_SMALL_DATA) != 0)
+    return;
+
+  if (!ia64_info->min_short_sec)
+    {
+      ia64_info->max_short_sec = sec;
+      ia64_info->max_short_offset = offset;
+      ia64_info->min_short_sec = sec;
+      ia64_info->min_short_offset = offset;
+    }
+  else if (sec == ia64_info->max_short_sec
+          && offset > ia64_info->max_short_offset)
+    ia64_info->max_short_offset = offset;
+  else if (sec == ia64_info->min_short_sec
+          && offset < ia64_info->min_short_offset)
+    ia64_info->min_short_offset = offset;
+  else if (sec->output_section->vma
+          > ia64_info->max_short_sec->vma)
+    {
+      ia64_info->max_short_sec = sec;
+      ia64_info->max_short_offset = offset;
+    }
+  else if (sec->output_section->vma
+          < ia64_info->min_short_sec->vma)
+    {
+      ia64_info->min_short_sec = sec;
+      ia64_info->min_short_offset = offset;
+    }
+}
+
 static bfd_boolean
-elfNN_ia64_relax_section (abfd, sec, link_info, again)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
-     bfd_boolean *again;
+elfNN_ia64_relax_section (bfd *abfd, asection *sec,
+                         struct bfd_link_info *link_info,
+                         bfd_boolean *again)
 {
   struct one_fixup
     {
@@ -731,22 +819,28 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
   bfd_boolean changed_contents = FALSE;
   bfd_boolean changed_relocs = FALSE;
   bfd_boolean changed_got = FALSE;
+  bfd_boolean skip_relax_pass_0 = TRUE;
+  bfd_boolean skip_relax_pass_1 = TRUE;
   bfd_vma gp = 0;
 
   /* Assume we're not going to change any sizes, and we'll only need
      one pass.  */
   *again = FALSE;
 
+  if (link_info->relocatable)
+    (*link_info->callbacks->einfo)
+      (_("%P%F: --relax and -r may not be used together\n"));
+
   /* Don't even try to relax for non-ELF outputs.  */
   if (!is_elf_hash_table (link_info->hash))
     return FALSE;
 
   /* Nothing to do if there are no relocations or there is no need for
-     the relax finalize pass.  */
+     the current pass.  */
   if ((sec->flags & SEC_RELOC) == 0
       || sec->reloc_count == 0
-      || (!link_info->need_relax_finalize
-         && sec->need_finalize_relax == 0))
+      || (link_info->relax_pass == 0 && sec->skip_relax_pass_0)
+      || (link_info->relax_pass == 1 && sec->skip_relax_pass_1))
     return TRUE;
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
@@ -787,33 +881,34 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
        case R_IA64_PCREL21BI:
        case R_IA64_PCREL21M:
        case R_IA64_PCREL21F:
-         /* In the finalize pass, all br relaxations are done. We can
-            skip it. */
-         if (!link_info->need_relax_finalize)
+         /* In pass 1, all br relaxations are done. We can skip it. */
+         if (link_info->relax_pass == 1)
            continue;
+         skip_relax_pass_0 = FALSE;
          is_branch = TRUE;
          break;
 
        case R_IA64_PCREL60B:
-         /* We can't optimize brl to br before the finalize pass since
-            br relaxations will increase the code size. Defer it to
-            the finalize pass.  */
-         if (link_info->need_relax_finalize)
+         /* We can't optimize brl to br in pass 0 since br relaxations
+            will increase the code size. Defer it to pass 1.  */
+         if (link_info->relax_pass == 0)
            {
-             sec->need_finalize_relax = 1;
+             skip_relax_pass_1 = FALSE;
              continue;
            }
          is_branch = TRUE;
          break;
 
+       case R_IA64_GPREL22:
+         /* Update max_short_sec/min_short_sec.  */
+
        case R_IA64_LTOFF22X:
        case R_IA64_LDXMOV:
-         /* We can't relax ldx/mov before the finalize pass since
-            br relaxations will increase the code size. Defer it to
-            the finalize pass.  */
-         if (link_info->need_relax_finalize)
+         /* We can't relax ldx/mov in pass 0 since br relaxations will
+            increase the code size. Defer it to pass 1.  */
+         if (link_info->relax_pass == 0)
            {
-             sec->need_finalize_relax = 1;
+             skip_relax_pass_1 = FALSE;
              continue;
            }
          is_branch = FALSE;
@@ -881,7 +976,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
              if (r_type != R_IA64_PCREL21B)
                continue;
 
-             tsec = ia64_info->plt_sec;
+             tsec = ia64_info->root.splt;
              toff = dyn_i->plt2_offset;
              BFD_ASSERT (irel->r_addend == 0);
            }
@@ -924,7 +1019,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
             location of interest is just "sym".  */
           if (symtype == STT_SECTION)
             toff += irel->r_addend;
-          
+
           toff = _bfd_merged_section_offset (abfd, &tsec,
                                              elf_section_data (tsec)->sec_info,
                                              toff);
@@ -943,34 +1038,33 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
        {
          bfd_signed_vma offset;
 
-         /* We can't put a trampoline in a .init/.fini section. Issue
-            an error.  */
-         if (strcmp (sec->output_section->name, ".init") == 0
-             || strcmp (sec->output_section->name, ".fini") == 0)
-           {
-             (*_bfd_error_handler)
-               (_("%s: Can't relax br at 0x%lx in section `%s'. Please use brl or indirect branch."),
-                bfd_archive_filename (sec->owner),
-                (unsigned long) roff, sec->name);
-             bfd_set_error (bfd_error_bad_value);
-             goto error_return;
-           }
-
          reladdr = (sec->output_section->vma
                     + sec->output_offset
                     + roff) & (bfd_vma) -4;
 
+         /* The .plt section is aligned at 32byte and the .text section
+            is aligned at 64byte. The .text section is right after the
+            .plt section.  After the first relaxation pass, linker may
+            increase the gap between the .plt and .text sections up
+            to 32byte.  We assume linker will always insert 32byte
+            between the .plt and .text sections after the the first
+            relaxation pass.  */
+         if (tsec == ia64_info->root.splt)
+           offset = -0x1000000 + 32;
+         else
+           offset = -0x1000000;
+
          /* If the branch is in range, no need to do anything.  */
-         if ((bfd_signed_vma) (symaddr - reladdr) >= -0x1000000
+         if ((bfd_signed_vma) (symaddr - reladdr) >= offset 
              && (bfd_signed_vma) (symaddr - reladdr) <= 0x0FFFFF0)
            {
              /* If the 60-bit branch is in 21-bit range, optimize it. */
              if (r_type == R_IA64_PCREL60B)
                {
-                 elfNN_ia64_relax_brl (abfd, contents, roff);
+                 elfNN_ia64_relax_brl (contents, roff);
 
                  irel->r_info
-                   = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
+                   = ELFNN_R_INFO (ELFNN_R_SYM (irel->r_info),
                                    R_IA64_PCREL21B);
 
                  /* If the original relocation offset points to slot
@@ -983,11 +1077,34 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
            }
          else if (r_type == R_IA64_PCREL60B)
            continue;
+         else if (elfNN_ia64_relax_br (contents, roff))
+           {
+             irel->r_info
+               = ELFNN_R_INFO (ELFNN_R_SYM (irel->r_info),
+                               R_IA64_PCREL60B);
+
+             /* Make the relocation offset point to slot 1.  */
+             irel->r_offset = (irel->r_offset & ~((bfd_vma) 0x3)) + 1;
+             continue;
+           }
+
+         /* We can't put a trampoline in a .init/.fini section. Issue
+            an error.  */
+         if (strcmp (sec->output_section->name, ".init") == 0
+             || strcmp (sec->output_section->name, ".fini") == 0)
+           {
+             (*_bfd_error_handler)
+               (_("%B: Can't relax br at 0x%lx in section `%A'. Please use brl or indirect branch."),
+                sec->owner, sec, (unsigned long) roff);
+             bfd_set_error (bfd_error_bad_value);
+             goto error_return;
+           }
 
          /* If the branch and target are in the same section, you've
-            got one honking big section and we can't help you.  You'll
-            get an error message later.  */
-         if (tsec == sec)
+            got one honking big section and we can't help you unless
+            you are branching backwards.  You'll get an error message
+            later.  */
+         if (tsec == sec && toff > roff)
            continue;
 
          /* Look for an existing fixup to this address.  */
@@ -1003,7 +1120,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
 
              size_t size;
 
-             if (tsec == ia64_info->plt_sec)
+             if (tsec == ia64_info->root.splt)
                size = sizeof (plt_full_entry);
              else
                size = oor_branch_size;
@@ -1023,7 +1140,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
                goto error_return;
              sec->size = amt;
 
-             if (tsec == ia64_info->plt_sec)
+             if (tsec == ia64_info->root.splt)
                {
                  memcpy (contents + trampoff, plt_full_entry, size);
 
@@ -1074,8 +1191,8 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
            }
 
          /* Fix up the existing branch to hit the trampoline.  */
-         if (elfNN_ia64_install_value (abfd, contents + roff, offset,
-                                       r_type) != bfd_reloc_ok)
+         if (elfNN_ia64_install_value (contents + roff, offset, r_type)
+             != bfd_reloc_ok)
            goto error_return;
 
          changed_contents = TRUE;
@@ -1101,7 +1218,11 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
              ||(bfd_signed_vma) (symaddr - gp) < -0x200000)
            continue;
 
-         if (r_type == R_IA64_LTOFF22X)
+         if (r_type == R_IA64_GPREL22)
+           elfNN_ia64_update_short_info (tsec->output_section,
+                                         tsec->output_offset + toff,
+                                         ia64_info);
+         else if (r_type == R_IA64_LTOFF22X)
            {
              irel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (irel->r_info),
                                           R_IA64_GPREL22);
@@ -1111,10 +1232,14 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
                  dyn_i->want_gotx = 0;
                  changed_got |= !dyn_i->want_got;
                }
+
+             elfNN_ia64_update_short_info (tsec->output_section,
+                                           tsec->output_offset + toff,
+                                           ia64_info);
            }
          else
            {
-             elfNN_ia64_relax_ldxmov (abfd, contents, roff);
+             elfNN_ia64_relax_ldxmov (contents, roff);
              irel->r_info = ELFNN_R_INFO (0, R_IA64_NONE);
              changed_contents = TRUE;
              changed_relocs = TRUE;
@@ -1126,7 +1251,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
      enough that the data segment moves, which will change the GP.
      Reset the GP so that we re-calculate next round.  We need to
      do this at the _beginning_ of the next round; now will not do.  */
-      
+
   /* Clean up and go home.  */
   while (fixups)
     {
@@ -1177,13 +1302,28 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
       elfNN_ia64_dyn_sym_traverse (ia64_info, allocate_global_data_got, &data);
       elfNN_ia64_dyn_sym_traverse (ia64_info, allocate_global_fptr_got, &data);
       elfNN_ia64_dyn_sym_traverse (ia64_info, allocate_local_got, &data);
-      ia64_info->got_sec->size = data.ofs;
+      ia64_info->root.sgot->size = data.ofs;
 
-      /* ??? Resize .rela.got too.  */
+      if (ia64_info->root.dynamic_sections_created
+         && ia64_info->root.srelgot != NULL)
+       {
+         /* Resize .rela.got.  */
+         ia64_info->root.srelgot->size = 0;
+         if (link_info->shared
+             && ia64_info->self_dtpmod_offset != (bfd_vma) -1)
+           ia64_info->root.srelgot->size += sizeof (ElfNN_External_Rela);
+         data.only_got = TRUE;
+         elfNN_ia64_dyn_sym_traverse (ia64_info, allocate_dynrel_entries,
+                                      &data);
+       }
     }
 
-  if (!link_info->need_relax_finalize)
-    sec->need_finalize_relax = 0;
+  if (link_info->relax_pass == 0)
+    {
+      /* Pass 0 is only needed to relax br.  */
+      sec->skip_relax_pass_0 = skip_relax_pass_0;
+      sec->skip_relax_pass_1 = skip_relax_pass_1;
+    }
 
   *again = changed_contents || changed_relocs;
   return TRUE;
@@ -1199,12 +1339,11 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
     free (internal_relocs);
   return FALSE;
 }
+#undef skip_relax_pass_0
+#undef skip_relax_pass_1
 
 static void
-elfNN_ia64_relax_ldxmov (abfd, contents, off)
-     bfd *abfd;
-     bfd_byte *contents;
-     bfd_vma off;
+elfNN_ia64_relax_ldxmov (bfd_byte *contents, bfd_vma off)
 {
   int shift, r1, r3;
   bfd_vma dword, insn;
@@ -1218,7 +1357,7 @@ elfNN_ia64_relax_ldxmov (abfd, contents, off)
       abort ();
     }
 
-  dword = bfd_get_64 (abfd, contents + off);
+  dword = bfd_getl64 (contents + off);
   insn = (dword >> shift) & 0x1ffffffffffLL;
 
   r1 = (insn >> 6) & 127;
@@ -1230,38 +1369,32 @@ elfNN_ia64_relax_ldxmov (abfd, contents, off)
 
   dword &= ~(0x1ffffffffffLL << shift);
   dword |= (insn << shift);
-  bfd_put_64 (abfd, dword, contents + off);
+  bfd_putl64 (dword, contents + off);
 }
 \f
 /* Return TRUE if NAME is an unwind table section name.  */
 
 static inline bfd_boolean
-is_unwind_section_name (abfd, name)
-       bfd *abfd;
-       const char *name;
+is_unwind_section_name (bfd *abfd, const char *name)
 {
-  size_t len1, len2, len3;
-
   if (elfNN_ia64_hpux_vec (abfd->xvec)
       && !strcmp (name, ELF_STRING_ia64_unwind_hdr))
     return FALSE;
 
-  len1 = sizeof (ELF_STRING_ia64_unwind) - 1;
-  len2 = sizeof (ELF_STRING_ia64_unwind_info) - 1;
-  len3 = sizeof (ELF_STRING_ia64_unwind_once) - 1;
-  return ((strncmp (name, ELF_STRING_ia64_unwind, len1) == 0
-          && strncmp (name, ELF_STRING_ia64_unwind_info, len2) != 0)
-         || strncmp (name, ELF_STRING_ia64_unwind_once, len3) == 0);
+  return ((CONST_STRNEQ (name, ELF_STRING_ia64_unwind)
+          && ! CONST_STRNEQ (name, ELF_STRING_ia64_unwind_info))
+         || CONST_STRNEQ (name, ELF_STRING_ia64_unwind_once));
 }
 
 /* Handle an IA-64 specific section when reading an object file.  This
-   is called when elfcode.h finds a section with an unknown type.  */
+   is called when bfd_section_from_shdr finds a section with an unknown
+   type.  */
 
 static bfd_boolean
-elfNN_ia64_section_from_shdr (abfd, hdr, name)
-     bfd *abfd;
-     Elf_Internal_Shdr *hdr;
-     const char *name;
+elfNN_ia64_section_from_shdr (bfd *abfd,
+                             Elf_Internal_Shdr *hdr,
+                             const char *name,
+                             int shindex)
 {
   asection *newsect;
 
@@ -1285,7 +1418,7 @@ elfNN_ia64_section_from_shdr (abfd, hdr, name)
       return FALSE;
     }
 
-  if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name))
+  if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
     return FALSE;
   newsect = hdr->bfd_section;
 
@@ -1298,9 +1431,8 @@ elfNN_ia64_section_from_shdr (abfd, hdr, name)
    flag.  */
 
 static bfd_boolean
-elfNN_ia64_section_flags (flags, hdr)
-     flagword *flags;
-     const Elf_Internal_Shdr *hdr;
+elfNN_ia64_section_flags (flagword *flags,
+                         const Elf_Internal_Shdr *hdr)
 {
   if (hdr->sh_flags & SHF_IA_64_SHORT)
     *flags |= SEC_SMALL_DATA;
@@ -1312,10 +1444,8 @@ elfNN_ia64_section_flags (flags, hdr)
    section name, which is a hack, but ought to work.  */
 
 static bfd_boolean
-elfNN_ia64_fake_sections (abfd, hdr, sec)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     Elf_Internal_Shdr *hdr;
-     asection *sec;
+elfNN_ia64_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr,
+                         asection *sec)
 {
   register const char *name;
 
@@ -1354,6 +1484,11 @@ elfNN_ia64_fake_sections (abfd, hdr, sec)
   if (sec->flags & SEC_SMALL_DATA)
     hdr->sh_flags |= SHF_IA_64_SHORT;
 
+  /* Some HP linkers look for the SHF_IA_64_HP_TLS flag instead of SHF_TLS. */
+
+  if (elfNN_ia64_hpux_vec (abfd->xvec) && (sec->flags & SHF_TLS))
+    hdr->sh_flags |= SHF_IA_64_HP_TLS;
+
   return TRUE;
 }
 
@@ -1361,14 +1496,11 @@ elfNN_ia64_fake_sections (abfd, hdr, sec)
    object file.  */
 
 static void
-elfNN_ia64_final_write_processing (abfd, linker)
-     bfd *abfd;
-     bfd_boolean linker ATTRIBUTE_UNUSED;
+elfNN_ia64_final_write_processing (bfd *abfd,
+                                  bfd_boolean linker ATTRIBUTE_UNUSED)
 {
   Elf_Internal_Shdr *hdr;
-  const char *sname;
-  asection *text_sect, *s;
-  size_t len;
+  asection *s;
 
   for (s = abfd->sections; s; s = s->next)
     {
@@ -1376,64 +1508,11 @@ elfNN_ia64_final_write_processing (abfd, linker)
       switch (hdr->sh_type)
        {
        case SHT_IA_64_UNWIND:
-         /* See comments in gas/config/tc-ia64.c:dot_endp on why we
-            have to do this.  */
-         sname = bfd_get_section_name (abfd, s);
-         len = sizeof (ELF_STRING_ia64_unwind) - 1;
-         if (sname && strncmp (sname, ELF_STRING_ia64_unwind, len) == 0)
-           {
-             sname += len;
-
-             if (sname[0] == '\0')
-               /* .IA_64.unwind -> .text */
-               text_sect = bfd_get_section_by_name (abfd, ".text");
-             else
-               /* .IA_64.unwindFOO -> FOO */
-               text_sect = bfd_get_section_by_name (abfd, sname);
-           }
-         else if (sname
-                  && (len = sizeof (ELF_STRING_ia64_unwind_once) - 1,
-                      strncmp (sname, ELF_STRING_ia64_unwind_once, len)) == 0)
-           {
-             /* .gnu.linkonce.ia64unw.FOO -> .gnu.linkonce.t.FOO */
-             size_t len2 = sizeof (".gnu.linkonce.t.") - 1;
-             char *once_name = bfd_malloc (len2 + strlen (sname + len) + 1);
-
-             if (once_name != NULL)
-               {
-                 memcpy (once_name, ".gnu.linkonce.t.", len2);
-                 strcpy (once_name + len2, sname + len);
-                 text_sect = bfd_get_section_by_name (abfd, once_name);
-                 free (once_name);
-               }
-             else
-               /* Should only happen if we run out of memory, in
-                  which case we're probably toast anyway.  Try to
-                  cope by finding the section the slow way.  */
-               for (text_sect = abfd->sections;
-                    text_sect != NULL;
-                    text_sect = text_sect->next)
-                 {
-                   if (strncmp (bfd_section_name (abfd, text_sect),
-                                ".gnu.linkonce.t.", len2) == 0
-                       && strcmp (bfd_section_name (abfd, text_sect) + len2,
-                                  sname + len) == 0)
-                     break;
-                 }
-           }
-         else
-           /* last resort: fall back on .text */
-           text_sect = bfd_get_section_by_name (abfd, ".text");
-
-         if (text_sect)
-           {
-             /* The IA-64 processor-specific ABI requires setting
-                sh_link to the unwind section, whereas HP-UX requires
-                sh_info to do so.  For maximum compatibility, we'll
-                set both for now... */
-             hdr->sh_link = elf_section_data (text_sect)->this_idx;
-             hdr->sh_info = elf_section_data (text_sect)->this_idx;
-           }
+         /* The IA-64 processor-specific ABI requires setting sh_link
+            to the unwind section, whereas HP-UX requires sh_info to
+            do so.  For maximum compatibility, we'll set both for
+            now... */
+         hdr->sh_info = hdr->sh_link;
          break;
        }
     }
@@ -1456,14 +1535,13 @@ elfNN_ia64_final_write_processing (abfd, linker)
    file.  We use it to put .comm items in .sbss, and not .bss.  */
 
 static bfd_boolean
-elfNN_ia64_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     Elf_Internal_Sym *sym;
-     const char **namep ATTRIBUTE_UNUSED;
-     flagword *flagsp ATTRIBUTE_UNUSED;
-     asection **secp;
-     bfd_vma *valp;
+elfNN_ia64_add_symbol_hook (bfd *abfd,
+                           struct bfd_link_info *info,
+                           Elf_Internal_Sym *sym,
+                           const char **namep ATTRIBUTE_UNUSED,
+                           flagword *flagsp ATTRIBUTE_UNUSED,
+                           asection **secp,
+                           bfd_vma *valp)
 {
   if (sym->st_shndx == SHN_COMMON
       && !info->relocatable
@@ -1476,11 +1554,11 @@ elfNN_ia64_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
 
       if (scomm == NULL)
        {
-         scomm = bfd_make_section (abfd, ".scommon");
-         if (scomm == NULL
-             || !bfd_set_section_flags (abfd, scomm, (SEC_ALLOC
-                                                      | SEC_IS_COMMON
-                                                      | SEC_LINKER_CREATED)))
+         scomm = bfd_make_section_with_flags (abfd, ".scommon",
+                                              (SEC_ALLOC
+                                               | SEC_IS_COMMON
+                                               | SEC_LINKER_CREATED));
+         if (scomm == NULL)
            return FALSE;
        }
 
@@ -1494,8 +1572,8 @@ elfNN_ia64_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
 /* Return the number of additional phdrs we will need.  */
 
 static int
-elfNN_ia64_additional_program_headers (abfd)
-     bfd *abfd;
+elfNN_ia64_additional_program_headers (bfd *abfd,
+                                      struct bfd_link_info *info ATTRIBUTE_UNUSED)
 {
   asection *s;
   int ret = 0;
@@ -1514,9 +1592,8 @@ elfNN_ia64_additional_program_headers (abfd)
 }
 
 static bfd_boolean
-elfNN_ia64_modify_segment_map (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
+elfNN_ia64_modify_segment_map (bfd *abfd,
+                              struct bfd_link_info *info ATTRIBUTE_UNUSED)
 {
   struct elf_segment_map *m, **pm;
   Elf_Internal_Shdr *hdr;
@@ -1599,17 +1676,30 @@ elfNN_ia64_modify_segment_map (abfd, info)
        }
     }
 
-  /* Turn on PF_IA_64_NORECOV if needed.  This involves traversing all of
-     the input sections for each output section in the segment and testing
-     for SHF_IA_64_NORECOV on each.  */
-  for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+  return TRUE;
+}
+
+/* Turn on PF_IA_64_NORECOV if needed.  This involves traversing all of
+   the input sections for each output section in the segment and testing
+   for SHF_IA_64_NORECOV on each.  */
+
+static bfd_boolean
+elfNN_ia64_modify_program_headers (bfd *abfd,
+                                  struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+  struct elf_obj_tdata *tdata = elf_tdata (abfd);
+  struct elf_segment_map *m;
+  Elf_Internal_Phdr *p;
+
+  for (p = tdata->phdr, m = tdata->segment_map; m != NULL; m = m->next, p++)
     if (m->p_type == PT_LOAD)
       {
        int i;
        for (i = m->count - 1; i >= 0; --i)
          {
-           struct bfd_link_order *order = m->sections[i]->link_order_head;
-           while (order)
+           struct bfd_link_order *order = m->sections[i]->map_head.link_order;
+
+           while (order != NULL)
              {
                if (order->type == bfd_indirect_link_order)
                  {
@@ -1617,7 +1707,7 @@ elfNN_ia64_modify_segment_map (abfd, info)
                    bfd_vma flags = elf_section_data(is)->this_hdr.sh_flags;
                    if (flags & SHF_IA_64_NORECOV)
                      {
-                       m->p_flags |= PF_IA_64_NORECOV;
+                       p->p_flags |= PF_IA_64_NORECOV;
                        goto found;
                      }
                  }
@@ -1634,9 +1724,8 @@ elfNN_ia64_modify_segment_map (abfd, info)
    '.' are local.  */
 
 static bfd_boolean
-elfNN_ia64_is_local_label_name (abfd, name)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     const char *name;
+elfNN_ia64_is_local_label_name (bfd *abfd ATTRIBUTE_UNUSED,
+                               const char *name)
 {
   return name[0] == '.';
 }
@@ -1644,10 +1733,8 @@ elfNN_ia64_is_local_label_name (abfd, name)
 /* Should we do dynamic things to this symbol?  */
 
 static bfd_boolean
-elfNN_ia64_dynamic_symbol_p (h, info, r_type)
-     struct elf_link_hash_entry *h;
-     struct bfd_link_info *info;
-     int r_type;
+elfNN_ia64_dynamic_symbol_p (struct elf_link_hash_entry *h,
+                            struct bfd_link_info *info, int r_type)
 {
   bfd_boolean ignore_protected
     = ((r_type & 0xf8) == 0x40         /* FPTR relocs */
@@ -1657,10 +1744,9 @@ elfNN_ia64_dynamic_symbol_p (h, info, r_type)
 }
 \f
 static struct bfd_hash_entry*
-elfNN_ia64_new_elf_hash_entry (entry, table, string)
-     struct bfd_hash_entry *entry;
-     struct bfd_hash_table *table;
-     const char *string;
+elfNN_ia64_new_elf_hash_entry (struct bfd_hash_entry *entry,
+                              struct bfd_hash_table *table,
+                              const char *string)
 {
   struct elfNN_ia64_link_hash_entry *ret;
   ret = (struct elfNN_ia64_link_hash_entry *) entry;
@@ -1673,22 +1759,22 @@ elfNN_ia64_new_elf_hash_entry (entry, table, string)
   if (!ret)
     return 0;
 
-  /* Initialize our local data.  All zeros, and definitely easier
-     than setting a handful of bit fields.  */
-  memset (ret, 0, sizeof (*ret));
-
   /* Call the allocation method of the superclass.  */
   ret = ((struct elfNN_ia64_link_hash_entry *)
         _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
                                     table, string));
 
+  ret->info = NULL;
+  ret->count = 0;
+  ret->sorted_count = 0;
+  ret->size = 0;
   return (struct bfd_hash_entry *) ret;
 }
 
 static void
-elfNN_ia64_hash_copy_indirect (bed, xdir, xind)
-     const struct elf_backend_data *bed ATTRIBUTE_UNUSED;
-     struct elf_link_hash_entry *xdir, *xind;
+elfNN_ia64_hash_copy_indirect (struct bfd_link_info *info,
+                              struct elf_link_hash_entry *xdir,
+                              struct elf_link_hash_entry *xind)
 {
   struct elfNN_ia64_link_hash_entry *dir, *ind;
 
@@ -1698,12 +1784,10 @@ elfNN_ia64_hash_copy_indirect (bed, xdir, xind)
   /* Copy down any references that we may have already seen to the
      symbol which just became indirect.  */
 
-  dir->root.elf_link_hash_flags |=
-    (ind->root.elf_link_hash_flags
-     & (ELF_LINK_HASH_REF_DYNAMIC
-        | ELF_LINK_HASH_REF_REGULAR
-        | ELF_LINK_HASH_REF_REGULAR_NONWEAK
-        | ELF_LINK_HASH_NEEDS_PLT));
+  dir->root.ref_dynamic |= ind->root.ref_dynamic;
+  dir->root.ref_regular |= ind->root.ref_regular;
+  dir->root.ref_regular_nonweak |= ind->root.ref_regular_nonweak;
+  dir->root.needs_plt |= ind->root.needs_plt;
 
   if (ind->root.root.type != bfd_link_hash_indirect)
     return;
@@ -1711,45 +1795,61 @@ elfNN_ia64_hash_copy_indirect (bed, xdir, xind)
   /* Copy over the got and plt data.  This would have been done
      by check_relocs.  */
 
-  if (dir->info == NULL)
+  if (ind->info != NULL)
     {
       struct elfNN_ia64_dyn_sym_info *dyn_i;
+      unsigned int count;
+
+      if (dir->info)
+       free (dir->info);
+
+      dir->info = ind->info;
+      dir->count = ind->count;
+      dir->sorted_count = ind->sorted_count;
+      dir->size = ind->size;
 
-      dir->info = dyn_i = ind->info;
       ind->info = NULL;
+      ind->count = 0;
+      ind->sorted_count = 0;
+      ind->size = 0;
 
       /* Fix up the dyn_sym_info pointers to the global symbol.  */
-      for (; dyn_i; dyn_i = dyn_i->next)
+      for (count = dir->count, dyn_i = dir->info;
+          count != 0;
+          count--, dyn_i++)
        dyn_i->h = &dir->root;
     }
-  BFD_ASSERT (ind->info == NULL);
 
   /* Copy over the dynindx.  */
 
-  if (dir->root.dynindx == -1)
+  if (ind->root.dynindx != -1)
     {
+      if (dir->root.dynindx != -1)
+       _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
+                               dir->root.dynstr_index);
       dir->root.dynindx = ind->root.dynindx;
       dir->root.dynstr_index = ind->root.dynstr_index;
       ind->root.dynindx = -1;
       ind->root.dynstr_index = 0;
     }
-  BFD_ASSERT (ind->root.dynindx == -1);
 }
 
 static void
-elfNN_ia64_hash_hide_symbol (info, xh, force_local)
-     struct bfd_link_info *info;
-     struct elf_link_hash_entry *xh;
-     bfd_boolean force_local;
+elfNN_ia64_hash_hide_symbol (struct bfd_link_info *info,
+                            struct elf_link_hash_entry *xh,
+                            bfd_boolean force_local)
 {
   struct elfNN_ia64_link_hash_entry *h;
   struct elfNN_ia64_dyn_sym_info *dyn_i;
+  unsigned int count;
 
   h = (struct elfNN_ia64_link_hash_entry *)xh;
 
   _bfd_elf_link_hash_hide_symbol (info, &h->root, force_local);
 
-  for (dyn_i = h->info; dyn_i; dyn_i = dyn_i->next)
+  for (count = h->count, dyn_i = h->info;
+       count != 0;
+       count--, dyn_i++)
     {
       dyn_i->want_plt2 = 0;
       dyn_i->want_plt = 0;
@@ -1759,21 +1859,18 @@ elfNN_ia64_hash_hide_symbol (info, xh, force_local)
 /* Compute a hash of a local hash entry.  */
 
 static hashval_t
-elfNN_ia64_local_htab_hash (ptr)
-     const void *ptr;
+elfNN_ia64_local_htab_hash (const void *ptr)
 {
   struct elfNN_ia64_local_hash_entry *entry
     = (struct elfNN_ia64_local_hash_entry *) ptr;
 
-  return (((entry->id & 0xff) << 24) | ((entry->id & 0xff00) << 8))
-         ^ entry->r_sym ^ (entry->id >> 16);
+  return ELF_LOCAL_SYMBOL_HASH (entry->id, entry->r_sym);
 }
 
 /* Compare local hash entries.  */
 
 static int
-elfNN_ia64_local_htab_eq (ptr1, ptr2)
-     const void *ptr1, *ptr2;
+elfNN_ia64_local_htab_eq (const void *ptr1, const void *ptr2)
 {
   struct elfNN_ia64_local_hash_entry *entry1
     = (struct elfNN_ia64_local_hash_entry *) ptr1;
@@ -1788,8 +1885,7 @@ elfNN_ia64_local_htab_eq (ptr1, ptr2)
    linker (without using static variables).  */
 
 static struct bfd_link_hash_table*
-elfNN_ia64_hash_table_create (abfd)
-     bfd *abfd;
+elfNN_ia64_hash_table_create (bfd *abfd)
 {
   struct elfNN_ia64_link_hash_table *ret;
 
@@ -1798,7 +1894,8 @@ elfNN_ia64_hash_table_create (abfd)
     return 0;
 
   if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
-                                     elfNN_ia64_new_elf_hash_entry))
+                                     elfNN_ia64_new_elf_hash_entry,
+                                     sizeof (struct elfNN_ia64_link_hash_entry)))
     {
       free (ret);
       return 0;
@@ -1816,18 +1913,68 @@ elfNN_ia64_hash_table_create (abfd)
   return &ret->root.root;
 }
 
+/* Free the global elfNN_ia64_dyn_sym_info array.  */
+
+static bfd_boolean
+elfNN_ia64_global_dyn_info_free (void **xentry,
+                               PTR unused ATTRIBUTE_UNUSED)
+{
+  struct elfNN_ia64_link_hash_entry *entry
+    = (struct elfNN_ia64_link_hash_entry *) xentry;
+
+  if (entry->root.root.type == bfd_link_hash_warning)
+    entry = (struct elfNN_ia64_link_hash_entry *) entry->root.root.u.i.link;
+
+  if (entry->info)
+    {
+      free (entry->info);
+      entry->info = NULL;
+      entry->count = 0;
+      entry->sorted_count = 0;
+      entry->size = 0;
+    }
+
+  return TRUE;
+}
+
+/* Free the local elfNN_ia64_dyn_sym_info array.  */
+
+static bfd_boolean
+elfNN_ia64_local_dyn_info_free (void **slot,
+                               PTR unused ATTRIBUTE_UNUSED)
+{
+  struct elfNN_ia64_local_hash_entry *entry
+    = (struct elfNN_ia64_local_hash_entry *) *slot;
+
+  if (entry->info)
+    {
+      free (entry->info);
+      entry->info = NULL;
+      entry->count = 0;
+      entry->sorted_count = 0;
+      entry->size = 0;
+    }
+
+  return TRUE;
+}
+
 /* Destroy IA-64 linker hash table.  */
 
 static void
-elfNN_ia64_hash_table_free (hash)
-     struct bfd_link_hash_table *hash;
+elfNN_ia64_hash_table_free (struct bfd_link_hash_table *hash)
 {
   struct elfNN_ia64_link_hash_table *ia64_info
     = (struct elfNN_ia64_link_hash_table *) hash;
   if (ia64_info->loc_hash_table)
-    htab_delete (ia64_info->loc_hash_table);
+    {
+      htab_traverse (ia64_info->loc_hash_table,
+                    elfNN_ia64_local_dyn_info_free, NULL);
+      htab_delete (ia64_info->loc_hash_table);
+    }
   if (ia64_info->loc_hash_memory)
     objalloc_free ((struct objalloc *) ia64_info->loc_hash_memory);
+  elf_link_hash_traverse (&ia64_info->root,
+                         elfNN_ia64_global_dyn_info_free, NULL);
   _bfd_generic_link_hash_table_free (hash);
 }
 
@@ -1835,52 +1982,54 @@ elfNN_ia64_hash_table_free (hash)
 
 struct elfNN_ia64_dyn_sym_traverse_data
 {
-  bfd_boolean (*func) PARAMS ((struct elfNN_ia64_dyn_sym_info *, PTR));
+  bfd_boolean (*func) (struct elfNN_ia64_dyn_sym_info *, PTR);
   PTR data;
 };
 
 static bfd_boolean
-elfNN_ia64_global_dyn_sym_thunk (xentry, xdata)
-     struct bfd_hash_entry *xentry;
-     PTR xdata;
+elfNN_ia64_global_dyn_sym_thunk (struct bfd_hash_entry *xentry,
+                                PTR xdata)
 {
   struct elfNN_ia64_link_hash_entry *entry
     = (struct elfNN_ia64_link_hash_entry *) xentry;
   struct elfNN_ia64_dyn_sym_traverse_data *data
     = (struct elfNN_ia64_dyn_sym_traverse_data *) xdata;
   struct elfNN_ia64_dyn_sym_info *dyn_i;
+  unsigned int count;
 
   if (entry->root.root.type == bfd_link_hash_warning)
     entry = (struct elfNN_ia64_link_hash_entry *) entry->root.root.u.i.link;
 
-  for (dyn_i = entry->info; dyn_i; dyn_i = dyn_i->next)
+  for (count = entry->count, dyn_i = entry->info;
+       count != 0;
+       count--, dyn_i++)
     if (! (*data->func) (dyn_i, data->data))
       return FALSE;
   return TRUE;
 }
 
 static bfd_boolean
-elfNN_ia64_local_dyn_sym_thunk (slot, xdata)
-     void **slot;
-     PTR xdata;
+elfNN_ia64_local_dyn_sym_thunk (void **slot, PTR xdata)
 {
   struct elfNN_ia64_local_hash_entry *entry
     = (struct elfNN_ia64_local_hash_entry *) *slot;
   struct elfNN_ia64_dyn_sym_traverse_data *data
     = (struct elfNN_ia64_dyn_sym_traverse_data *) xdata;
   struct elfNN_ia64_dyn_sym_info *dyn_i;
+  unsigned int count;
 
-  for (dyn_i = entry->info; dyn_i; dyn_i = dyn_i->next)
+  for (count = entry->count, dyn_i = entry->info;
+       count != 0;
+       count--, dyn_i++)
     if (! (*data->func) (dyn_i, data->data))
-      return 0;
-  return 1;
+      return FALSE;
+  return TRUE;
 }
 
 static void
-elfNN_ia64_dyn_sym_traverse (ia64_info, func, data)
-     struct elfNN_ia64_link_hash_table *ia64_info;
-     bfd_boolean (*func) PARAMS ((struct elfNN_ia64_dyn_sym_info *, PTR));
-     PTR data;
+elfNN_ia64_dyn_sym_traverse (struct elfNN_ia64_link_hash_table *ia64_info,
+                            bfd_boolean (*func) (struct elfNN_ia64_dyn_sym_info *, PTR),
+                            PTR data)
 {
   struct elfNN_ia64_dyn_sym_traverse_data xdata;
 
@@ -1894,9 +2043,8 @@ elfNN_ia64_dyn_sym_traverse (ia64_info, func, data)
 }
 \f
 static bfd_boolean
-elfNN_ia64_create_dynamic_sections (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info;
+elfNN_ia64_create_dynamic_sections (bfd *abfd,
+                                   struct bfd_link_info *info)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   asection *s;
@@ -1906,56 +2054,41 @@ elfNN_ia64_create_dynamic_sections (abfd, info)
 
   ia64_info = elfNN_ia64_hash_table (info);
 
-  ia64_info->plt_sec = bfd_get_section_by_name (abfd, ".plt");
-  ia64_info->got_sec = bfd_get_section_by_name (abfd, ".got");
-
   {
-    flagword flags = bfd_get_section_flags (abfd, ia64_info->got_sec);
-    bfd_set_section_flags (abfd, ia64_info->got_sec, SEC_SMALL_DATA | flags);
+    flagword flags = bfd_get_section_flags (abfd, ia64_info->root.sgot);
+    bfd_set_section_flags (abfd, ia64_info->root.sgot,
+                          SEC_SMALL_DATA | flags);
     /* The .got section is always aligned at 8 bytes.  */
-    bfd_set_section_alignment (abfd, ia64_info->got_sec, 3);
+    bfd_set_section_alignment (abfd, ia64_info->root.sgot, 3);
   }
 
   if (!get_pltoff (abfd, info, ia64_info))
     return FALSE;
 
-  s = bfd_make_section(abfd, ".rela.IA_64.pltoff");
+  s = bfd_make_section_with_flags (abfd, ".rela.IA_64.pltoff",
+                                  (SEC_ALLOC | SEC_LOAD
+                                   | SEC_HAS_CONTENTS
+                                   | SEC_IN_MEMORY
+                                   | SEC_LINKER_CREATED
+                                   | SEC_READONLY));
   if (s == NULL
-      || !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD
-                                          | SEC_HAS_CONTENTS
-                                          | SEC_IN_MEMORY
-                                          | SEC_LINKER_CREATED
-                                          | SEC_READONLY))
-      || !bfd_set_section_alignment (abfd, s, 3))
+      || !bfd_set_section_alignment (abfd, s, LOG_SECTION_ALIGN))
     return FALSE;
   ia64_info->rel_pltoff_sec = s;
 
-  s = bfd_make_section(abfd, ".rela.got");
-  if (s == NULL
-      || !bfd_set_section_flags (abfd, s, (SEC_ALLOC | SEC_LOAD
-                                          | SEC_HAS_CONTENTS
-                                          | SEC_IN_MEMORY
-                                          | SEC_LINKER_CREATED
-                                          | SEC_READONLY))
-      || !bfd_set_section_alignment (abfd, s, 3))
-    return FALSE;
-  ia64_info->rel_got_sec = s;
-
   return TRUE;
 }
 
 /* Find and/or create a hash entry for local symbol.  */
 static struct elfNN_ia64_local_hash_entry *
-get_local_sym_hash (ia64_info, abfd, rel, create)
-     struct elfNN_ia64_link_hash_table *ia64_info;
-     bfd *abfd;
-     const Elf_Internal_Rela *rel;
-     bfd_boolean create;
+get_local_sym_hash (struct elfNN_ia64_link_hash_table *ia64_info,
+                   bfd *abfd, const Elf_Internal_Rela *rel,
+                   bfd_boolean create)
 {
   struct elfNN_ia64_local_hash_entry e, *ret;
   asection *sec = abfd->sections;
-  hashval_t h = (((sec->id & 0xff) << 24) | ((sec->id & 0xff00) << 8))
-               ^ ELFNN_R_SYM (rel->r_info) ^ (sec->id >> 16);
+  hashval_t h = ELF_LOCAL_SYMBOL_HASH (sec->id,
+                                      ELFNN_R_SYM (rel->r_info));
   void **slot;
 
   e.id = sec->id;
@@ -1982,23 +2115,222 @@ get_local_sym_hash (ia64_info, abfd, rel, create)
   return ret;
 }
 
+/* Used to sort elfNN_ia64_dyn_sym_info array.  */
+
+static int
+addend_compare (const void *xp, const void *yp)
+{
+  const struct elfNN_ia64_dyn_sym_info *x
+    = (const struct elfNN_ia64_dyn_sym_info *) xp;
+  const struct elfNN_ia64_dyn_sym_info *y
+    = (const struct elfNN_ia64_dyn_sym_info *) yp;
+
+  return x->addend < y->addend ? -1 : x->addend > y->addend ? 1 : 0;
+}
+
+/* Sort elfNN_ia64_dyn_sym_info array and remove duplicates.  */
+
+static unsigned int
+sort_dyn_sym_info (struct elfNN_ia64_dyn_sym_info *info,
+                  unsigned int count)
+{
+  bfd_vma curr, prev, got_offset;
+  unsigned int i, kept, dup, diff, dest, src, len;
+
+  qsort (info, count, sizeof (*info), addend_compare);
+
+  /* Find the first duplicate.  */
+  prev = info [0].addend;
+  got_offset = info [0].got_offset;
+  for (i = 1; i < count; i++)
+    {
+      curr = info [i].addend;
+      if (curr == prev)
+       {
+         /* For duplicates, make sure that GOT_OFFSET is valid.  */
+         if (got_offset == (bfd_vma) -1)
+           got_offset = info [i].got_offset;
+         break;
+       }
+      got_offset = info [i].got_offset;
+      prev = curr;
+    }
+
+  /* We may move a block of elements to here.  */
+  dest = i++;
+
+  /* Remove duplicates.  */
+  if (i < count)
+    {
+      while (i < count)
+       {
+         /* For duplicates, make sure that the kept one has a valid
+            got_offset.  */
+         kept = dest - 1;
+         if (got_offset != (bfd_vma) -1)
+           info [kept].got_offset = got_offset;
+
+         curr = info [i].addend;
+         got_offset = info [i].got_offset;
+
+         /* Move a block of elements whose first one is different from
+            the previous.  */
+         if (curr == prev)
+           {
+             for (src = i + 1; src < count; src++)
+               {
+                 if (info [src].addend != curr)
+                   break;
+                 /* For duplicates, make sure that GOT_OFFSET is
+                    valid.  */
+                 if (got_offset == (bfd_vma) -1)
+                   got_offset = info [src].got_offset;
+               }
+
+             /* Make sure that the kept one has a valid got_offset.  */
+             if (got_offset != (bfd_vma) -1)
+               info [kept].got_offset = got_offset;
+           }
+         else
+           src = i;
+
+         if (src >= count)
+           break;
+
+         /* Find the next duplicate.  SRC will be kept.  */
+         prev = info [src].addend;
+         got_offset = info [src].got_offset;
+         for (dup = src + 1; dup < count; dup++)
+           {
+             curr = info [dup].addend;
+             if (curr == prev)
+               {
+                 /* Make sure that got_offset is valid.  */
+                 if (got_offset == (bfd_vma) -1)
+                   got_offset = info [dup].got_offset;
+
+                 /* For duplicates, make sure that the kept one has
+                    a valid got_offset.  */
+                 if (got_offset != (bfd_vma) -1)
+                   info [dup - 1].got_offset = got_offset;
+                 break;
+               }
+             got_offset = info [dup].got_offset;
+             prev = curr;
+           }
+
+         /* How much to move.  */
+         len = dup - src;
+         i = dup + 1;
+
+         if (len == 1 && dup < count)
+           {
+             /* If we only move 1 element, we combine it with the next
+                one.  There must be at least a duplicate.  Find the
+                next different one.  */
+             for (diff = dup + 1, src++; diff < count; diff++, src++)
+               {
+                 if (info [diff].addend != curr)
+                   break;
+                 /* Make sure that got_offset is valid.  */
+                 if (got_offset == (bfd_vma) -1)
+                   got_offset = info [diff].got_offset;
+               }
+
+             /* Makre sure that the last duplicated one has an valid
+                offset.  */
+             BFD_ASSERT (curr == prev);
+             if (got_offset != (bfd_vma) -1)
+               info [diff - 1].got_offset = got_offset;
+
+             if (diff < count)
+               {
+                 /* Find the next duplicate.  Track the current valid
+                    offset.  */
+                 prev = info [diff].addend;
+                 got_offset = info [diff].got_offset;
+                 for (dup = diff + 1; dup < count; dup++)
+                   {
+                     curr = info [dup].addend;
+                     if (curr == prev)
+                       {
+                         /* For duplicates, make sure that GOT_OFFSET
+                            is valid.  */
+                         if (got_offset == (bfd_vma) -1)
+                           got_offset = info [dup].got_offset;
+                         break;
+                       }
+                     got_offset = info [dup].got_offset;
+                     prev = curr;
+                     diff++;
+                   }
+
+                 len = diff - src + 1;
+                 i = diff + 1;
+               }
+           }
+
+         memmove (&info [dest], &info [src], len * sizeof (*info));
+
+         dest += len;
+       }
+
+      count = dest;
+    }
+  else
+    {
+      /* When we get here, either there is no duplicate at all or
+        the only duplicate is the last element.  */
+      if (dest < count)
+       {
+         /* If the last element is a duplicate, make sure that the
+            kept one has a valid got_offset.  We also update count.  */
+         if (got_offset != (bfd_vma) -1)
+           info [dest - 1].got_offset = got_offset;
+         count = dest;
+       }
+    }
+
+  return count;
+}
+
 /* Find and/or create a descriptor for dynamic symbol info.  This will
-   vary based on global or local symbol, and the addend to the reloc.  */
+   vary based on global or local symbol, and the addend to the reloc.
+
+   We don't sort when inserting.  Also, we sort and eliminate
+   duplicates if there is an unsorted section.  Typically, this will
+   only happen once, because we do all insertions before lookups.  We
+   then use bsearch to do a lookup.  This also allows lookups to be
+   fast.  So we have fast insertion (O(log N) due to duplicate check),
+   fast lookup (O(log N)) and one sort (O(N log N) expected time).
+   Previously, all lookups were O(N) because of the use of the linked
+   list and also all insertions were O(N) because of the check for
+   duplicates.  There are some complications here because the array
+   size grows occasionally, which may add an O(N) factor, but this
+   should be rare.  Also,  we free the excess array allocation, which
+   requires a copy which is O(N), but this only happens once.  */
 
 static struct elfNN_ia64_dyn_sym_info *
-get_dyn_sym_info (ia64_info, h, abfd, rel, create)
-     struct elfNN_ia64_link_hash_table *ia64_info;
-     struct elf_link_hash_entry *h;
-     bfd *abfd;
-     const Elf_Internal_Rela *rel;
-     bfd_boolean create;
+get_dyn_sym_info (struct elfNN_ia64_link_hash_table *ia64_info,
+                 struct elf_link_hash_entry *h, bfd *abfd,
+                 const Elf_Internal_Rela *rel, bfd_boolean create)
 {
-  struct elfNN_ia64_dyn_sym_info **pp;
-  struct elfNN_ia64_dyn_sym_info *dyn_i;
+  struct elfNN_ia64_dyn_sym_info **info_p, *info, *dyn_i, key;
+  unsigned int *count_p, *sorted_count_p, *size_p;
+  unsigned int count, sorted_count, size;
   bfd_vma addend = rel ? rel->r_addend : 0;
+  bfd_size_type amt;
 
   if (h)
-    pp = &((struct elfNN_ia64_link_hash_entry *)h)->info;
+    {
+      struct elfNN_ia64_link_hash_entry *global_h;
+
+      global_h = (struct elfNN_ia64_link_hash_entry *) h;
+      info_p = &global_h->info;
+      count_p = &global_h->count;
+      sorted_count_p = &global_h->sorted_count;
+      size_p = &global_h->size;
+    }
   else
     {
       struct elfNN_ia64_local_hash_entry *loc_h;
@@ -2010,33 +2342,121 @@ get_dyn_sym_info (ia64_info, h, abfd, rel, create)
          return NULL;
        }
 
-      pp = &loc_h->info;
+      info_p = &loc_h->info;
+      count_p = &loc_h->count;
+      sorted_count_p = &loc_h->sorted_count;
+      size_p = &loc_h->size;
     }
 
-  for (dyn_i = *pp; dyn_i && dyn_i->addend != addend; dyn_i = *pp)
-    pp = &dyn_i->next;
-
-  if (dyn_i == NULL && create)
+  count = *count_p;
+  sorted_count = *sorted_count_p;
+  size = *size_p;
+  info = *info_p;
+  if (create)
     {
-      dyn_i = ((struct elfNN_ia64_dyn_sym_info *)
-              bfd_zalloc (abfd, (bfd_size_type) sizeof *dyn_i));
-      *pp = dyn_i;
-      dyn_i->addend = addend;
-    }
-
+      /* When we create the array, we don't check for duplicates,
+         except in the previously sorted section if one exists, and
+        against the last inserted entry.  This allows insertions to
+        be fast.  */
+      if (info)
+       {
+         if (sorted_count)
+           {
+             /* Try bsearch first on the sorted section.  */
+             key.addend = addend;
+             dyn_i = bsearch (&key, info, sorted_count,
+                              sizeof (*info), addend_compare);
+
+             if (dyn_i)
+               {
+                 return dyn_i;
+               }
+           }
+
+         /* Do a quick check for the last inserted entry.  */
+         dyn_i = info + count - 1;
+         if (dyn_i->addend == addend)
+           {
+             return dyn_i;
+           }
+       }
+
+      if (size == 0)
+       {
+         /* It is the very first element. We create the array of size
+            1.  */
+         size = 1;
+         amt = size * sizeof (*info);
+         info = bfd_malloc (amt);
+       }
+      else if (size <= count)
+       {
+         /* We double the array size every time when we reach the
+            size limit.  */
+         size += size;
+         amt = size * sizeof (*info);
+         info = bfd_realloc (info, amt);
+       }
+      else
+       goto has_space;
+
+      if (info == NULL)
+       return NULL;
+      *size_p = size;
+      *info_p = info;
+
+has_space:
+      /* Append the new one to the array.  */
+      dyn_i = info + count;
+      memset (dyn_i, 0, sizeof (*dyn_i));
+      dyn_i->got_offset = (bfd_vma) -1;
+      dyn_i->addend = addend;
+
+      /* We increment count only since the new ones are unsorted and
+        may have duplicate.  */
+      (*count_p)++;
+    }
+  else
+    {
+      /* It is a lookup without insertion.  Sort array if part of the
+        array isn't sorted.  */
+      if (count != sorted_count)
+       {
+         count = sort_dyn_sym_info (info, count);
+         *count_p = count;
+         *sorted_count_p = count;
+       }
+
+      /* Free unused memory.  */
+      if (size != count)
+       {
+         amt = count * sizeof (*info);
+         info = bfd_malloc (amt);
+         if (info != NULL)
+           {
+             memcpy (info, *info_p, amt);
+             free (*info_p);
+             *size_p = count;
+             *info_p = info;
+           }
+       }
+
+      key.addend = addend;
+      dyn_i = bsearch (&key, info, count,
+                      sizeof (*info), addend_compare);
+    }
+
   return dyn_i;
 }
 
 static asection *
-get_got (abfd, info, ia64_info)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     struct elfNN_ia64_link_hash_table *ia64_info;
+get_got (bfd *abfd, struct bfd_link_info *info,
+        struct elfNN_ia64_link_hash_table *ia64_info)
 {
   asection *got;
   bfd *dynobj;
 
-  got = ia64_info->got_sec;
+  got = ia64_info->root.sgot;
   if (!got)
     {
       flagword flags;
@@ -2047,9 +2467,7 @@ get_got (abfd, info, ia64_info)
       if (!_bfd_elf_create_got_section (dynobj, info))
        return 0;
 
-      got = bfd_get_section_by_name (dynobj, ".got");
-      BFD_ASSERT (got);
-      ia64_info->got_sec = got;
+      got = ia64_info->root.sgot;
 
       /* The .got section is always aligned at 8 bytes.  */
       if (!bfd_set_section_alignment (abfd, got, 3))
@@ -2068,10 +2486,8 @@ get_got (abfd, info, ia64_info)
    of a procedure, thus ensuring a unique address for each procedure.  */
 
 static asection *
-get_fptr (abfd, info, ia64_info)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     struct elfNN_ia64_link_hash_table *ia64_info;
+get_fptr (bfd *abfd, struct bfd_link_info *info,
+         struct elfNN_ia64_link_hash_table *ia64_info)
 {
   asection *fptr;
   bfd *dynobj;
@@ -2083,15 +2499,14 @@ get_fptr (abfd, info, ia64_info)
       if (!dynobj)
        ia64_info->root.dynobj = dynobj = abfd;
 
-      fptr = bfd_make_section (dynobj, ".opd");
+      fptr = bfd_make_section_with_flags (dynobj, ".opd",
+                                         (SEC_ALLOC
+                                          | SEC_LOAD
+                                          | SEC_HAS_CONTENTS
+                                          | SEC_IN_MEMORY
+                                          | (info->pie ? 0 : SEC_READONLY)
+                                          | SEC_LINKER_CREATED));
       if (!fptr
-         || !bfd_set_section_flags (dynobj, fptr,
-                                    (SEC_ALLOC
-                                     | SEC_LOAD
-                                     | SEC_HAS_CONTENTS
-                                     | SEC_IN_MEMORY
-                                     | (info->pie ? 0 : SEC_READONLY)
-                                     | SEC_LINKER_CREATED))
          || !bfd_set_section_alignment (abfd, fptr, 4))
        {
          BFD_ASSERT (0);
@@ -2103,15 +2518,15 @@ get_fptr (abfd, info, ia64_info)
       if (info->pie)
        {
          asection *fptr_rel;
-         fptr_rel = bfd_make_section(dynobj, ".rela.opd");
+         fptr_rel = bfd_make_section_with_flags (dynobj, ".rela.opd",
+                                                 (SEC_ALLOC | SEC_LOAD
+                                                  | SEC_HAS_CONTENTS
+                                                  | SEC_IN_MEMORY
+                                                  | SEC_LINKER_CREATED
+                                                  | SEC_READONLY));
          if (fptr_rel == NULL
-             || !bfd_set_section_flags (dynobj, fptr_rel,
-                                        (SEC_ALLOC | SEC_LOAD
-                                         | SEC_HAS_CONTENTS
-                                         | SEC_IN_MEMORY
-                                         | SEC_LINKER_CREATED
-                                         | SEC_READONLY))
-             || !bfd_set_section_alignment (abfd, fptr_rel, 3))
+             || !bfd_set_section_alignment (abfd, fptr_rel,
+                                            LOG_SECTION_ALIGN))
            {
              BFD_ASSERT (0);
              return NULL;
@@ -2125,10 +2540,8 @@ get_fptr (abfd, info, ia64_info)
 }
 
 static asection *
-get_pltoff (abfd, info, ia64_info)
-     bfd *abfd;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     struct elfNN_ia64_link_hash_table *ia64_info;
+get_pltoff (bfd *abfd, struct bfd_link_info *info ATTRIBUTE_UNUSED,
+           struct elfNN_ia64_link_hash_table *ia64_info)
 {
   asection *pltoff;
   bfd *dynobj;
@@ -2140,15 +2553,15 @@ get_pltoff (abfd, info, ia64_info)
       if (!dynobj)
        ia64_info->root.dynobj = dynobj = abfd;
 
-      pltoff = bfd_make_section (dynobj, ELF_STRING_ia64_pltoff);
+      pltoff = bfd_make_section_with_flags (dynobj,
+                                           ELF_STRING_ia64_pltoff,
+                                           (SEC_ALLOC
+                                            | SEC_LOAD
+                                            | SEC_HAS_CONTENTS
+                                            | SEC_IN_MEMORY
+                                            | SEC_SMALL_DATA
+                                            | SEC_LINKER_CREATED));
       if (!pltoff
-         || !bfd_set_section_flags (dynobj, pltoff,
-                                    (SEC_ALLOC
-                                     | SEC_LOAD
-                                     | SEC_HAS_CONTENTS
-                                     | SEC_IN_MEMORY
-                                     | SEC_SMALL_DATA
-                                     | SEC_LINKER_CREATED))
          || !bfd_set_section_alignment (abfd, pltoff, 4))
        {
          BFD_ASSERT (0);
@@ -2162,11 +2575,9 @@ get_pltoff (abfd, info, ia64_info)
 }
 
 static asection *
-get_reloc_section (abfd, ia64_info, sec, create)
-     bfd *abfd;
-     struct elfNN_ia64_link_hash_table *ia64_info;
-     asection *sec;
-     bfd_boolean create;
+get_reloc_section (bfd *abfd,
+                  struct elfNN_ia64_link_hash_table *ia64_info,
+                  asection *sec, bfd_boolean create)
 {
   const char *srel_name;
   asection *srel;
@@ -2178,10 +2589,10 @@ get_reloc_section (abfd, ia64_info, sec, create)
   if (srel_name == NULL)
     return NULL;
 
-  BFD_ASSERT ((strncmp (srel_name, ".rela", 5) == 0
+  BFD_ASSERT ((CONST_STRNEQ (srel_name, ".rela")
               && strcmp (bfd_get_section_name (abfd, sec),
                          srel_name+5) == 0)
-             || (strncmp (srel_name, ".rel", 4) == 0
+             || (CONST_STRNEQ (srel_name, ".rel")
                  && strcmp (bfd_get_section_name (abfd, sec),
                             srel_name+4) == 0));
 
@@ -2192,16 +2603,15 @@ get_reloc_section (abfd, ia64_info, sec, create)
   srel = bfd_get_section_by_name (dynobj, srel_name);
   if (srel == NULL && create)
     {
-      srel = bfd_make_section (dynobj, srel_name);
+      srel = bfd_make_section_with_flags (dynobj, srel_name,
+                                         (SEC_ALLOC | SEC_LOAD
+                                          | SEC_HAS_CONTENTS
+                                          | SEC_IN_MEMORY
+                                          | SEC_LINKER_CREATED
+                                          | SEC_READONLY));
       if (srel == NULL
-         || !bfd_set_section_flags (dynobj, srel,
-                                    (SEC_ALLOC
-                                     | SEC_LOAD
-                                     | SEC_HAS_CONTENTS
-                                     | SEC_IN_MEMORY
-                                     | SEC_LINKER_CREATED
-                                     | SEC_READONLY))
-         || !bfd_set_section_alignment (dynobj, srel, 3))
+         || !bfd_set_section_alignment (dynobj, srel,
+                                        LOG_SECTION_ALIGN))
        return NULL;
     }
 
@@ -2238,17 +2648,32 @@ count_dyn_reloc (bfd *abfd, struct elfNN_ia64_dyn_sym_info *dyn_i,
 }
 
 static bfd_boolean
-elfNN_ia64_check_relocs (abfd, info, sec, relocs)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     asection *sec;
-     const Elf_Internal_Rela *relocs;
+elfNN_ia64_check_relocs (bfd *abfd, struct bfd_link_info *info,
+                        asection *sec,
+                        const Elf_Internal_Rela *relocs)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   const Elf_Internal_Rela *relend;
   Elf_Internal_Shdr *symtab_hdr;
   const Elf_Internal_Rela *rel;
   asection *got, *fptr, *srel, *pltoff;
+  enum {
+    NEED_GOT = 1,
+    NEED_GOTX = 2,
+    NEED_FPTR = 4,
+    NEED_PLTOFF = 8,
+    NEED_MIN_PLT = 16,
+    NEED_FULL_PLT = 32,
+    NEED_DYNREL = 64,
+    NEED_LTOFF_FPTR = 128,
+    NEED_TPREL = 256,
+    NEED_DTPMOD = 512,
+    NEED_DTPREL = 1024
+  };
+  int need_entry;
+  struct elf_link_hash_entry *h;
+  unsigned long r_symndx;
+  bfd_boolean maybe_dynamic;
 
   if (info->relocatable)
     return TRUE;
@@ -2259,52 +2684,33 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
   got = fptr = srel = pltoff = NULL;
 
   relend = relocs + sec->reloc_count;
+
+  /* We scan relocations first to create dynamic relocation arrays.  We
+     modified get_dyn_sym_info to allow fast insertion and support fast
+     lookup in the next loop.  */
   for (rel = relocs; rel < relend; ++rel)
     {
-      enum {
-       NEED_GOT = 1,
-       NEED_GOTX = 2,
-       NEED_FPTR = 4,
-       NEED_PLTOFF = 8,
-       NEED_MIN_PLT = 16,
-       NEED_FULL_PLT = 32,
-       NEED_DYNREL = 64,
-       NEED_LTOFF_FPTR = 128,
-       NEED_TPREL = 256,
-       NEED_DTPMOD = 512,
-       NEED_DTPREL = 1024
-      };
-
-      struct elf_link_hash_entry *h = NULL;
-      unsigned long r_symndx = ELFNN_R_SYM (rel->r_info);
-      struct elfNN_ia64_dyn_sym_info *dyn_i;
-      int need_entry;
-      bfd_boolean maybe_dynamic;
-      int dynrel_type = R_IA64_NONE;
-
+      r_symndx = ELFNN_R_SYM (rel->r_info);
       if (r_symndx >= symtab_hdr->sh_info)
        {
-         /* We're dealing with a global symbol -- find its hash entry
-            and mark it as being referenced.  */
          long indx = r_symndx - symtab_hdr->sh_info;
          h = elf_sym_hashes (abfd)[indx];
          while (h->root.type == bfd_link_hash_indirect
                 || h->root.type == bfd_link_hash_warning)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
-         h->elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR;
        }
+      else
+       h = NULL;
 
       /* We can only get preliminary data on whether a symbol is
         locally or externally defined, as not all of the input files
         have yet been processed.  Do something with what we know, as
         this may help reduce memory usage and processing time later.  */
-      maybe_dynamic = FALSE;
-      if (h && ((!info->executable
-                && (!info->symbolic || info->unresolved_syms_in_shared_libs == RM_IGNORE))
-               || ! (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)
-               || h->root.type == bfd_link_hash_defweak))
-       maybe_dynamic = TRUE;
+      maybe_dynamic = (h && ((!info->executable
+                             && (!SYMBOLIC_BIND (info, h)
+                                 || info->unresolved_syms_in_shared_libs == RM_IGNORE))
+                            || !h->def_regular
+                            || h->root.type == bfd_link_hash_defweak));
 
       need_entry = 0;
       switch (ELFNN_R_TYPE (rel->r_info))
@@ -2313,9 +2719,6 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
        case R_IA64_TPREL64LSB:
          if (info->shared || maybe_dynamic)
            need_entry = NEED_DYNREL;
-         dynrel_type = R_IA64_TPREL64LSB;
-         if (info->shared)
-           info->flags |= DF_STATIC_TLS;
          break;
 
        case R_IA64_LTOFF_TPREL22:
@@ -2324,11 +2727,12 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
            info->flags |= DF_STATIC_TLS;
          break;
 
+       case R_IA64_DTPREL32MSB:
+       case R_IA64_DTPREL32LSB:
        case R_IA64_DTPREL64MSB:
        case R_IA64_DTPREL64LSB:
          if (info->shared || maybe_dynamic)
            need_entry = NEED_DYNREL;
-         dynrel_type = R_IA64_DTPREL64LSB;
          break;
 
        case R_IA64_LTOFF_DTPREL22:
@@ -2339,7 +2743,6 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
        case R_IA64_DTPMOD64LSB:
          if (info->shared || maybe_dynamic)
            need_entry = NEED_DYNREL;
-         dynrel_type = R_IA64_DTPMOD64LSB;
          break;
 
        case R_IA64_LTOFF_DTPMOD22:
@@ -2364,7 +2767,6 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
            need_entry = NEED_FPTR | NEED_DYNREL;
          else
            need_entry = NEED_FPTR;
-         dynrel_type = R_IA64_FPTR64LSB;
          break;
 
        case R_IA64_LTOFF22:
@@ -2414,7 +2816,6 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
          /* Shared objects will always need at least a REL relocation.  */
          if (info->shared || maybe_dynamic)
            need_entry = NEED_DYNREL;
-         dynrel_type = R_IA64_DIR64LSB;
          break;
 
        case R_IA64_IPLTMSB:
@@ -2422,7 +2823,6 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
          /* Shared objects will always need at least a REL relocation.  */
          if (info->shared || maybe_dynamic)
            need_entry = NEED_DYNREL;
-         dynrel_type = R_IA64_IPLTLSB;
          break;
 
        case R_IA64_PCREL22:
@@ -2433,7 +2833,6 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
        case R_IA64_PCREL64LSB:
          if (maybe_dynamic)
            need_entry = NEED_DYNREL;
-         dynrel_type = R_IA64_PCREL64LSB;
          break;
        }
 
@@ -2448,7 +2847,174 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
             abfd, 0, (bfd_vma) 0);
        }
 
-      dyn_i = get_dyn_sym_info (ia64_info, h, abfd, rel, TRUE);
+      if (get_dyn_sym_info (ia64_info, h, abfd, rel, TRUE) == NULL)
+       return FALSE;
+    }
+
+  /* Now, we only do lookup without insertion, which is very fast
+     with the modified get_dyn_sym_info.  */
+  for (rel = relocs; rel < relend; ++rel)
+    {
+      struct elfNN_ia64_dyn_sym_info *dyn_i;
+      int dynrel_type = R_IA64_NONE;
+
+      r_symndx = ELFNN_R_SYM (rel->r_info);
+      if (r_symndx >= symtab_hdr->sh_info)
+       {
+         /* We're dealing with a global symbol -- find its hash entry
+            and mark it as being referenced.  */
+         long indx = r_symndx - symtab_hdr->sh_info;
+         h = elf_sym_hashes (abfd)[indx];
+         while (h->root.type == bfd_link_hash_indirect
+                || h->root.type == bfd_link_hash_warning)
+           h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+         h->ref_regular = 1;
+       }
+      else
+       h = NULL;
+
+      /* We can only get preliminary data on whether a symbol is
+        locally or externally defined, as not all of the input files
+        have yet been processed.  Do something with what we know, as
+        this may help reduce memory usage and processing time later.  */
+      maybe_dynamic = (h && ((!info->executable
+                             && (!SYMBOLIC_BIND (info, h)
+                                 || info->unresolved_syms_in_shared_libs == RM_IGNORE))
+                            || !h->def_regular
+                            || h->root.type == bfd_link_hash_defweak));
+
+      need_entry = 0;
+      switch (ELFNN_R_TYPE (rel->r_info))
+       {
+       case R_IA64_TPREL64MSB:
+       case R_IA64_TPREL64LSB:
+         if (info->shared || maybe_dynamic)
+           need_entry = NEED_DYNREL;
+         dynrel_type = R_IA64_TPREL64LSB;
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         break;
+
+       case R_IA64_LTOFF_TPREL22:
+         need_entry = NEED_TPREL;
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         break;
+
+       case R_IA64_DTPREL32MSB:
+       case R_IA64_DTPREL32LSB:
+       case R_IA64_DTPREL64MSB:
+       case R_IA64_DTPREL64LSB:
+         if (info->shared || maybe_dynamic)
+           need_entry = NEED_DYNREL;
+         dynrel_type = R_IA64_DTPRELNNLSB;
+         break;
+
+       case R_IA64_LTOFF_DTPREL22:
+         need_entry = NEED_DTPREL;
+         break;
+
+       case R_IA64_DTPMOD64MSB:
+       case R_IA64_DTPMOD64LSB:
+         if (info->shared || maybe_dynamic)
+           need_entry = NEED_DYNREL;
+         dynrel_type = R_IA64_DTPMOD64LSB;
+         break;
+
+       case R_IA64_LTOFF_DTPMOD22:
+         need_entry = NEED_DTPMOD;
+         break;
+
+       case R_IA64_LTOFF_FPTR22:
+       case R_IA64_LTOFF_FPTR64I:
+       case R_IA64_LTOFF_FPTR32MSB:
+       case R_IA64_LTOFF_FPTR32LSB:
+       case R_IA64_LTOFF_FPTR64MSB:
+       case R_IA64_LTOFF_FPTR64LSB:
+         need_entry = NEED_FPTR | NEED_GOT | NEED_LTOFF_FPTR;
+         break;
+
+       case R_IA64_FPTR64I:
+       case R_IA64_FPTR32MSB:
+       case R_IA64_FPTR32LSB:
+       case R_IA64_FPTR64MSB:
+       case R_IA64_FPTR64LSB:
+         if (info->shared || h)
+           need_entry = NEED_FPTR | NEED_DYNREL;
+         else
+           need_entry = NEED_FPTR;
+         dynrel_type = R_IA64_FPTRNNLSB;
+         break;
+
+       case R_IA64_LTOFF22:
+       case R_IA64_LTOFF64I:
+         need_entry = NEED_GOT;
+         break;
+
+       case R_IA64_LTOFF22X:
+         need_entry = NEED_GOTX;
+         break;
+
+       case R_IA64_PLTOFF22:
+       case R_IA64_PLTOFF64I:
+       case R_IA64_PLTOFF64MSB:
+       case R_IA64_PLTOFF64LSB:
+         need_entry = NEED_PLTOFF;
+         if (h)
+           {
+             if (maybe_dynamic)
+               need_entry |= NEED_MIN_PLT;
+           }
+         break;
+
+       case R_IA64_PCREL21B:
+        case R_IA64_PCREL60B:
+         /* Depending on where this symbol is defined, we may or may not
+            need a full plt entry.  Only skip if we know we'll not need
+            the entry -- static or symbolic, and the symbol definition
+            has already been seen.  */
+         if (maybe_dynamic && rel->r_addend == 0)
+           need_entry = NEED_FULL_PLT;
+         break;
+
+       case R_IA64_IMM14:
+       case R_IA64_IMM22:
+       case R_IA64_IMM64:
+       case R_IA64_DIR32MSB:
+       case R_IA64_DIR32LSB:
+       case R_IA64_DIR64MSB:
+       case R_IA64_DIR64LSB:
+         /* Shared objects will always need at least a REL relocation.  */
+         if (info->shared || maybe_dynamic)
+           need_entry = NEED_DYNREL;
+         dynrel_type = R_IA64_DIRNNLSB;
+         break;
+
+       case R_IA64_IPLTMSB:
+       case R_IA64_IPLTLSB:
+         /* Shared objects will always need at least a REL relocation.  */
+         if (info->shared || maybe_dynamic)
+           need_entry = NEED_DYNREL;
+         dynrel_type = R_IA64_IPLTLSB;
+         break;
+
+       case R_IA64_PCREL22:
+       case R_IA64_PCREL64I:
+       case R_IA64_PCREL32MSB:
+       case R_IA64_PCREL32LSB:
+       case R_IA64_PCREL64MSB:
+       case R_IA64_PCREL64LSB:
+         if (maybe_dynamic)
+           need_entry = NEED_DYNREL;
+         dynrel_type = R_IA64_PCRELNNLSB;
+         break;
+       }
+
+      if (!need_entry)
+       continue;
+
+      dyn_i = get_dyn_sym_info (ia64_info, h, abfd, rel, FALSE);
 
       /* Record whether or not this is a local symbol.  */
       dyn_i->h = h;
@@ -2501,7 +3067,7 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
        {
           if (!ia64_info->root.dynobj)
            ia64_info->root.dynobj = abfd;
-         h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+         h->needs_plt = 1;
          dyn_i->want_plt = 1;
        }
       if (need_entry & NEED_FULL_PLT)
@@ -2516,7 +3082,7 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
              if (!pltoff)
                return FALSE;
            }
-         
+
          dyn_i->want_pltoff = 1;
        }
       if ((need_entry & NEED_DYNREL) && (sec->flags & SEC_ALLOC))
@@ -2540,9 +3106,8 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
    external GOT entries first.  */
 
 static bfd_boolean
-allocate_global_data_got (dyn_i, data)
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     PTR data;
+allocate_global_data_got (struct elfNN_ia64_dyn_sym_info *dyn_i,
+                         PTR data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
 
@@ -2589,15 +3154,14 @@ allocate_global_data_got (dyn_i, data)
 /* Next, allocate all the GOT entries used by LTOFF_FPTR relocs.  */
 
 static bfd_boolean
-allocate_global_fptr_got (dyn_i, data)
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     PTR data;
+allocate_global_fptr_got (struct elfNN_ia64_dyn_sym_info *dyn_i,
+                         PTR data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
 
   if (dyn_i->want_got
       && dyn_i->want_fptr
-      && elfNN_ia64_dynamic_symbol_p (dyn_i->h, x->info, R_IA64_FPTR64LSB))
+      && elfNN_ia64_dynamic_symbol_p (dyn_i->h, x->info, R_IA64_FPTRNNLSB))
     {
       dyn_i->got_offset = x->ofs;
       x->ofs += 8;
@@ -2608,9 +3172,8 @@ allocate_global_fptr_got (dyn_i, data)
 /* Lastly, allocate all the GOT entries for local data.  */
 
 static bfd_boolean
-allocate_local_got (dyn_i, data)
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     PTR data;
+allocate_local_got (struct elfNN_ia64_dyn_sym_info *dyn_i,
+                   PTR data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
 
@@ -2626,8 +3189,7 @@ allocate_local_got (dyn_i, data)
 /* Search for the index of a global symbol in it's defining object file.  */
 
 static long
-global_sym_index (h)
-     struct elf_link_hash_entry *h;
+global_sym_index (struct elf_link_hash_entry *h)
 {
   struct elf_link_hash_entry **p;
   bfd *obj;
@@ -2646,9 +3208,7 @@ global_sym_index (h)
    in a main executable that is not exported.  */
 
 static bfd_boolean
-allocate_fptr (dyn_i, data)
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     PTR data;
+allocate_fptr (struct elfNN_ia64_dyn_sym_info *dyn_i, PTR data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
 
@@ -2664,7 +3224,8 @@ allocate_fptr (dyn_i, data)
       if (!x->info->executable
          && (!h
              || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
-             || h->root.type != bfd_link_hash_undefweak))
+             || (h->root.type != bfd_link_hash_undefweak
+                 && h->root.type != bfd_link_hash_undefined)))
        {
          if (h && h->dynindx == -1)
            {
@@ -2693,9 +3254,8 @@ allocate_fptr (dyn_i, data)
 /* Allocate all the minimal PLT entries.  */
 
 static bfd_boolean
-allocate_plt_entries (dyn_i, data)
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     PTR data;
+allocate_plt_entries (struct elfNN_ia64_dyn_sym_info *dyn_i,
+                     PTR data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
 
@@ -2708,7 +3268,7 @@ allocate_plt_entries (dyn_i, data)
               || h->root.type == bfd_link_hash_warning)
          h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
-      /* ??? Versioned symbols seem to lose ELF_LINK_HASH_NEEDS_PLT.  */
+      /* ??? Versioned symbols seem to lose NEEDS_PLT.  */
       if (elfNN_ia64_dynamic_symbol_p (h, x->info, 0))
        {
          bfd_size_type offset = x->ofs;
@@ -2731,9 +3291,8 @@ allocate_plt_entries (dyn_i, data)
 /* Allocate all the full PLT entries.  */
 
 static bfd_boolean
-allocate_plt2_entries (dyn_i, data)
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     PTR data;
+allocate_plt2_entries (struct elfNN_ia64_dyn_sym_info *dyn_i,
+                      PTR data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
 
@@ -2759,9 +3318,8 @@ allocate_plt2_entries (dyn_i, data)
    ??? Relaxation might be able to determine that they are.  */
 
 static bfd_boolean
-allocate_pltoff_entries (dyn_i, data)
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     PTR data;
+allocate_pltoff_entries (struct elfNN_ia64_dyn_sym_info *dyn_i,
+                        PTR data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
 
@@ -2777,9 +3335,8 @@ allocate_pltoff_entries (dyn_i, data)
    to be dynamic.  */
 
 static bfd_boolean
-allocate_dynrel_entries (dyn_i, data)
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     PTR data;
+allocate_dynrel_entries (struct elfNN_ia64_dyn_sym_info *dyn_i,
+                        PTR data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
   struct elfNN_ia64_link_hash_table *ia64_info;
@@ -2796,50 +3353,6 @@ allocate_dynrel_entries (dyn_i, data)
                   && ELF_ST_VISIBILITY (dyn_i->h->other)
                   && dyn_i->h->root.type == bfd_link_hash_undefweak);
 
-  /* Take care of the normal data relocations.  */
-
-  for (rent = dyn_i->reloc_entries; rent; rent = rent->next)
-    {
-      int count = rent->count;
-
-      switch (rent->type)
-       {
-       case R_IA64_FPTR64LSB:
-         /* Allocate one iff !want_fptr and not PIE, which by this point
-            will be true only if we're actually allocating one statically
-            in the main executable.  Position independent executables
-            need a relative reloc.  */
-         if (dyn_i->want_fptr && !x->info->pie)
-           continue;
-         break;
-       case R_IA64_PCREL64LSB:
-         if (!dynamic_symbol)
-           continue;
-         break;
-       case R_IA64_DIR64LSB:
-         if (!dynamic_symbol && !shared)
-           continue;
-         break;
-       case R_IA64_IPLTLSB:
-         if (!dynamic_symbol && !shared)
-           continue;
-         /* Use two REL relocations for IPLT relocations
-            against local symbols.  */
-         if (!dynamic_symbol)
-           count *= 2;
-         break;
-       case R_IA64_TPREL64LSB:
-       case R_IA64_DTPREL64LSB:
-       case R_IA64_DTPMOD64LSB:
-         break;
-       default:
-         abort ();
-       }
-      if (rent->reltext)
-       ia64_info->reltext = 1;
-      rent->srel->size += sizeof (ElfNN_External_Rela) * count;
-    }
-
   /* Take care of the GOT and PLT relocations.  */
 
   if ((!resolved_zero
@@ -2853,14 +3366,18 @@ allocate_dynrel_entries (dyn_i, data)
          || !x->info->pie
          || dyn_i->h == NULL
          || dyn_i->h->root.type != bfd_link_hash_undefweak)
-       ia64_info->rel_got_sec->size += sizeof (ElfNN_External_Rela);
+       ia64_info->root.srelgot->size += sizeof (ElfNN_External_Rela);
     }
   if ((dynamic_symbol || shared) && dyn_i->want_tprel)
-    ia64_info->rel_got_sec->size += sizeof (ElfNN_External_Rela);
+    ia64_info->root.srelgot->size += sizeof (ElfNN_External_Rela);
   if (dynamic_symbol && dyn_i->want_dtpmod)
-    ia64_info->rel_got_sec->size += sizeof (ElfNN_External_Rela);
+    ia64_info->root.srelgot->size += sizeof (ElfNN_External_Rela);
   if (dynamic_symbol && dyn_i->want_dtprel)
-    ia64_info->rel_got_sec->size += sizeof (ElfNN_External_Rela);
+    ia64_info->root.srelgot->size += sizeof (ElfNN_External_Rela);
+
+  if (x->only_got)
+    return TRUE;
+
   if (ia64_info->rel_fptr_sec && dyn_i->want_fptr)
     {
       if (dyn_i->h == NULL || dyn_i->h->root.type != bfd_link_hash_undefweak)
@@ -2882,13 +3399,60 @@ allocate_dynrel_entries (dyn_i, data)
       ia64_info->rel_pltoff_sec->size += t;
     }
 
+  /* Take care of the normal data relocations.  */
+
+  for (rent = dyn_i->reloc_entries; rent; rent = rent->next)
+    {
+      int count = rent->count;
+
+      switch (rent->type)
+       {
+       case R_IA64_FPTR32LSB:
+       case R_IA64_FPTR64LSB:
+         /* Allocate one iff !want_fptr and not PIE, which by this point
+            will be true only if we're actually allocating one statically
+            in the main executable.  Position independent executables
+            need a relative reloc.  */
+         if (dyn_i->want_fptr && !x->info->pie)
+           continue;
+         break;
+       case R_IA64_PCREL32LSB:
+       case R_IA64_PCREL64LSB:
+         if (!dynamic_symbol)
+           continue;
+         break;
+       case R_IA64_DIR32LSB:
+       case R_IA64_DIR64LSB:
+         if (!dynamic_symbol && !shared)
+           continue;
+         break;
+       case R_IA64_IPLTLSB:
+         if (!dynamic_symbol && !shared)
+           continue;
+         /* Use two REL relocations for IPLT relocations
+            against local symbols.  */
+         if (!dynamic_symbol)
+           count *= 2;
+         break;
+       case R_IA64_DTPREL32LSB:
+       case R_IA64_TPREL64LSB:
+       case R_IA64_DTPREL64LSB:
+       case R_IA64_DTPMOD64LSB:
+         break;
+       default:
+         abort ();
+       }
+      if (rent->reltext)
+       ia64_info->reltext = 1;
+      rent->srel->size += sizeof (ElfNN_External_Rela) * count;
+    }
+
   return TRUE;
 }
 
 static bfd_boolean
-elfNN_ia64_adjust_dynamic_symbol (info, h)
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     struct elf_link_hash_entry *h;
+elfNN_ia64_adjust_dynamic_symbol (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                                 struct elf_link_hash_entry *h)
 {
   /* ??? Undefined symbols with PLT entries should be re-defined
      to be the PLT entry.  */
@@ -2896,12 +3460,12 @@ elfNN_ia64_adjust_dynamic_symbol (info, h)
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
      real definition first, and we can just use the same value.  */
-  if (h->weakdef != NULL)
+  if (h->u.weakdef != NULL)
     {
-      BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
-                  || h->weakdef->root.type == bfd_link_hash_defweak);
-      h->root.u.def.section = h->weakdef->root.u.def.section;
-      h->root.u.def.value = h->weakdef->root.u.def.value;
+      BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
+                  || h->u.weakdef->root.type == bfd_link_hash_defweak);
+      h->root.u.def.section = h->u.weakdef->root.u.def.section;
+      h->root.u.def.value = h->u.weakdef->root.u.def.value;
       return TRUE;
     }
 
@@ -2916,9 +3480,8 @@ elfNN_ia64_adjust_dynamic_symbol (info, h)
 }
 
 static bfd_boolean
-elfNN_ia64_size_dynamic_sections (output_bfd, info)
-     bfd *output_bfd ATTRIBUTE_UNUSED;
-     struct bfd_link_info *info;
+elfNN_ia64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
+                                 struct bfd_link_info *info)
 {
   struct elfNN_ia64_allocate_data data;
   struct elfNN_ia64_link_hash_table *ia64_info;
@@ -2944,13 +3507,13 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
 
   /* Allocate the GOT entries.  */
 
-  if (ia64_info->got_sec)
+  if (ia64_info->root.sgot)
     {
       data.ofs = 0;
       elfNN_ia64_dyn_sym_traverse (ia64_info, allocate_global_data_got, &data);
       elfNN_ia64_dyn_sym_traverse (ia64_info, allocate_global_fptr_got, &data);
       elfNN_ia64_dyn_sym_traverse (ia64_info, allocate_local_got, &data);
-      ia64_info->got_sec->size = data.ofs;
+      ia64_info->root.sgot->size = data.ofs;
     }
 
   /* Allocate the FPTR entries.  */
@@ -2989,7 +3552,7 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
 
       BFD_ASSERT (ia64_info->root.dynamic_sections_created);
 
-      ia64_info->plt_sec->size = data.ofs;
+      ia64_info->root.splt->size = data.ofs;
 
       /* If we've got a .plt, we need some extra memory for the dynamic
         linker.  We stuff these in .got.plt.  */
@@ -3012,7 +3575,8 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
         required.  */
 
       if (info->shared && ia64_info->self_dtpmod_offset != (bfd_vma) -1)
-       ia64_info->rel_got_sec->size += sizeof (ElfNN_External_Rela);
+       ia64_info->root.srelgot->size += sizeof (ElfNN_External_Rela);
+      data.only_got = FALSE;
       elfNN_ia64_dyn_sym_traverse (ia64_info, allocate_dynrel_entries, &data);
     }
 
@@ -3035,12 +3599,12 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
 
       strip = (sec->size == 0);
 
-      if (sec == ia64_info->got_sec)
+      if (sec == ia64_info->root.sgot)
        strip = FALSE;
-      else if (sec == ia64_info->rel_got_sec)
+      else if (sec == ia64_info->root.srelgot)
        {
          if (strip)
-           ia64_info->rel_got_sec = NULL;
+           ia64_info->root.srelgot = NULL;
          else
            /* We use the reloc_count field as a counter if we need to
               copy relocs into the output file.  */
@@ -3060,10 +3624,10 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
               copy relocs into the output file.  */
            sec->reloc_count = 0;
        }
-      else if (sec == ia64_info->plt_sec)
+      else if (sec == ia64_info->root.splt)
        {
          if (strip)
-           ia64_info->plt_sec = NULL;
+           ia64_info->root.splt = NULL;
        }
       else if (sec == ia64_info->pltoff_sec)
        {
@@ -3092,7 +3656,7 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
 
          if (strcmp (name, ".got.plt") == 0)
            strip = FALSE;
-         else if (strncmp (name, ".rel", 4) == 0)
+         else if (CONST_STRNEQ (name, ".rel"))
            {
              if (!strip)
                {
@@ -3106,7 +3670,7 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
        }
 
       if (strip)
-       _bfd_strip_section_from_output (info, sec);
+       sec->flags |= SEC_EXCLUDE;
       else
        {
          /* Allocate memory for the section contents.  */
@@ -3165,15 +3729,13 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
 }
 
 static bfd_reloc_status_type
-elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
-     bfd *abfd;
-     bfd_byte *hit_addr;
-     bfd_vma v;
-     unsigned int r_type;
+elfNN_ia64_install_value (bfd_byte *hit_addr, bfd_vma v,
+                         unsigned int r_type)
 {
   const struct ia64_operand *op;
   int bigendian = 0, shift = 0;
-  bfd_vma t0, t1, insn, dword;
+  bfd_vma t0, t1, dword;
+  ia64_insn insn;
   enum ia64_opnd opnd;
   const char *err;
   size_t size = 8;
@@ -3298,8 +3860,8 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
     {
     case IA64_OPND_IMMU64:
       hit_addr -= (long) hit_addr & 0x3;
-      t0 = bfd_get_64 (abfd, hit_addr);
-      t1 = bfd_get_64 (abfd, hit_addr + 8);
+      t0 = bfd_getl64 (hit_addr);
+      t1 = bfd_getl64 (hit_addr + 8);
 
       /* tmpl/s: bits  0.. 5 in t0
         slot 0: bits  5..45 in t0
@@ -3321,14 +3883,14 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
               | (((val >> 21) & 0x001) << 21)          /* ic */
               | (((val >> 63) & 0x001) << 36)) << 23;  /* i */
 
-      bfd_put_64 (abfd, t0, hit_addr);
-      bfd_put_64 (abfd, t1, hit_addr + 8);
+      bfd_putl64 (t0, hit_addr);
+      bfd_putl64 (t1, hit_addr + 8);
       break;
 
     case IA64_OPND_TGT64:
       hit_addr -= (long) hit_addr & 0x3;
-      t0 = bfd_get_64 (abfd, hit_addr);
-      t1 = bfd_get_64 (abfd, hit_addr + 8);
+      t0 = bfd_getl64 (hit_addr);
+      t1 = bfd_getl64 (hit_addr + 8);
 
       /* tmpl/s: bits  0.. 5 in t0
         slot 0: bits  5..45 in t0
@@ -3346,8 +3908,8 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
       t1 |= ((((val >> 0) & 0xfffffLL) << 13)          /* imm20b */
              | (((val >> 59) & 0x1LL) << 36)) << 23;   /* i */
 
-      bfd_put_64 (abfd, t0, hit_addr);
-      bfd_put_64 (abfd, t1, hit_addr + 8);
+      bfd_putl64 (t0, hit_addr);
+      bfd_putl64 (t1, hit_addr + 8);
       break;
 
     default:
@@ -3358,17 +3920,17 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
        case 2: shift = 23; hit_addr += 6; break;
        case 3: return bfd_reloc_notsupported; /* shouldn't happen...  */
        }
-      dword = bfd_get_64 (abfd, hit_addr);
+      dword = bfd_getl64 (hit_addr);
       insn = (dword >> shift) & 0x1ffffffffffLL;
 
       op = elf64_ia64_operands + opnd;
-      err = (*op->insert) (op, val, (ia64_insn *)& insn);
+      err = (*op->insert) (op, val, &insn);
       if (err)
        return bfd_reloc_overflow;
 
       dword &= ~(0x1ffffffffffLL << shift);
       dword |= (insn << shift);
-      bfd_put_64 (abfd, dword, hit_addr);
+      bfd_putl64 (dword, hit_addr);
       break;
 
     case IA64_OPND_NIL:
@@ -3390,16 +3952,10 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
 }
 
 static void
-elfNN_ia64_install_dyn_reloc (abfd, info, sec, srel, offset, type,
-                             dynindx, addend)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     asection *sec;
-     asection *srel;
-     bfd_vma offset;
-     unsigned int type;
-     long dynindx;
-     bfd_vma addend;
+elfNN_ia64_install_dyn_reloc (bfd *abfd, struct bfd_link_info *info,
+                             asection *sec, asection *srel,
+                             bfd_vma offset, unsigned int type,
+                             long dynindx, bfd_vma addend)
 {
   Elf_Internal_Rela outrel;
   bfd_byte *loc;
@@ -3429,14 +3985,10 @@ elfNN_ia64_install_dyn_reloc (abfd, info, sec, srel, offset, type,
    and return the gp-relative address of the linkage table entry.  */
 
 static bfd_vma
-set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     long dynindx;
-     bfd_vma addend;
-     bfd_vma value;
-     unsigned int dyn_r_type;
+set_got_entry (bfd *abfd, struct bfd_link_info *info,
+              struct elfNN_ia64_dyn_sym_info *dyn_i,
+              long dynindx, bfd_vma addend, bfd_vma value,
+              unsigned int dyn_r_type)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   asection *got_sec;
@@ -3444,7 +3996,7 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
   bfd_vma got_offset;
 
   ia64_info = elfNN_ia64_hash_table (info);
-  got_sec = ia64_info->got_sec;
+  got_sec = ia64_info->root.sgot;
 
   switch (dyn_r_type)
     {
@@ -3467,6 +4019,7 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
        }
       got_offset = dyn_i->dtpmod_offset;
       break;
+    case R_IA64_DTPREL32LSB:
     case R_IA64_DTPREL64LSB:
       done = dyn_i->dtprel_done;
       dyn_i->dtprel_done = TRUE;
@@ -3491,9 +4044,12 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
            && (!dyn_i->h
                || ELF_ST_VISIBILITY (dyn_i->h->other) == STV_DEFAULT
                || dyn_i->h->root.type != bfd_link_hash_undefweak)
+           && dyn_r_type != R_IA64_DTPREL32LSB
            && dyn_r_type != R_IA64_DTPREL64LSB)
            || elfNN_ia64_dynamic_symbol_p (dyn_i->h, info, dyn_r_type)
-          || (dynindx != -1 && dyn_r_type == R_IA64_FPTR64LSB))
+          || (dynindx != -1
+              && (dyn_r_type == R_IA64_FPTR32LSB
+                  || dyn_r_type == R_IA64_FPTR64LSB)))
          && (!dyn_i->want_ltoff_fptr
              || !info->pie
              || !dyn_i->h
@@ -3502,9 +4058,10 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
          if (dynindx == -1
              && dyn_r_type != R_IA64_TPREL64LSB
              && dyn_r_type != R_IA64_DTPMOD64LSB
+             && dyn_r_type != R_IA64_DTPREL32LSB
              && dyn_r_type != R_IA64_DTPREL64LSB)
            {
-             dyn_r_type = R_IA64_REL64LSB;
+             dyn_r_type = R_IA64_RELNNLSB;
              dynindx = 0;
              addend = value;
            }
@@ -3513,6 +4070,18 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
            {
              switch (dyn_r_type)
                {
+               case R_IA64_REL32LSB:
+                 dyn_r_type = R_IA64_REL32MSB;
+                 break;
+               case R_IA64_DIR32LSB:
+                 dyn_r_type = R_IA64_DIR32MSB;
+                 break;
+               case R_IA64_FPTR32LSB:
+                 dyn_r_type = R_IA64_FPTR32MSB;
+                 break;
+               case R_IA64_DTPREL32LSB:
+                 dyn_r_type = R_IA64_DTPREL32MSB;
+                 break;
                case R_IA64_REL64LSB:
                  dyn_r_type = R_IA64_REL64MSB;
                  break;
@@ -3538,7 +4107,7 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
            }
 
          elfNN_ia64_install_dyn_reloc (abfd, NULL, got_sec,
-                                       ia64_info->rel_got_sec,
+                                       ia64_info->root.srelgot,
                                        got_offset, dyn_r_type,
                                        dynindx, addend);
        }
@@ -3556,11 +4125,9 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
    address and its global pointer.  Return the descriptor's address.  */
 
 static bfd_vma
-set_fptr_entry (abfd, info, dyn_i, value)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     bfd_vma value;
+set_fptr_entry (bfd *abfd, struct bfd_link_info *info,
+               struct elfNN_ia64_dyn_sym_info *dyn_i,
+               bfd_vma value)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   asection *fptr_sec;
@@ -3608,12 +4175,9 @@ set_fptr_entry (abfd, info, dyn_i, value)
    and its global pointer.  Return the descriptor's address.  */
 
 static bfd_vma
-set_pltoff_entry (abfd, info, dyn_i, value, is_plt)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     bfd_vma value;
-     bfd_boolean is_plt;
+set_pltoff_entry (bfd *abfd, struct bfd_link_info *info,
+                 struct elfNN_ia64_dyn_sym_info *dyn_i,
+                 bfd_vma value, bfd_boolean is_plt)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   asection *pltoff_sec;
@@ -3642,9 +4206,9 @@ set_pltoff_entry (abfd, info, dyn_i, value, is_plt)
          unsigned int dyn_r_type;
 
          if (bfd_big_endian (abfd))
-           dyn_r_type = R_IA64_REL64MSB;
+           dyn_r_type = R_IA64_RELNNMSB;
          else
-           dyn_r_type = R_IA64_REL64LSB;
+           dyn_r_type = R_IA64_RELNNLSB;
 
          elfNN_ia64_install_dyn_reloc (abfd, NULL, pltoff_sec,
                                        ia64_info->rel_pltoff_sec,
@@ -3652,7 +4216,7 @@ set_pltoff_entry (abfd, info, dyn_i, value, is_plt)
                                        dyn_r_type, 0, value);
          elfNN_ia64_install_dyn_reloc (abfd, NULL, pltoff_sec,
                                        ia64_info->rel_pltoff_sec,
-                                       dyn_i->pltoff_offset + 8,
+                                       dyn_i->pltoff_offset + ARCH_SIZE / 8,
                                        dyn_r_type, 0, gp);
        }
 
@@ -3670,16 +4234,14 @@ set_pltoff_entry (abfd, info, dyn_i, value, is_plt)
 /* Return the base VMA address which should be subtracted from real addresses
    when resolving @tprel() relocation.
    Main program TLS (whose template starts at PT_TLS p_vaddr)
-   is assigned offset round(16, PT_TLS p_align).  */
+   is assigned offset round(2 * size of pointer, PT_TLS p_align).  */
 
 static bfd_vma
-elfNN_ia64_tprel_base (info)
-     struct bfd_link_info *info;
+elfNN_ia64_tprel_base (struct bfd_link_info *info)
 {
   asection *tls_sec = elf_hash_table (info)->tls_sec;
-
-  BFD_ASSERT (tls_sec != NULL);
-  return tls_sec->vma - align_power ((bfd_vma) 16, tls_sec->alignment_power);
+  return tls_sec->vma - align_power ((bfd_vma) ARCH_SIZE / 4,
+                                    tls_sec->alignment_power);
 }
 
 /* Return the base VMA address which should be subtracted from real addresses
@@ -3687,10 +4249,8 @@ elfNN_ia64_tprel_base (info)
    This is PT_TLS segment p_vaddr.  */
 
 static bfd_vma
-elfNN_ia64_dtprel_base (info)
-     struct bfd_link_info *info;
+elfNN_ia64_dtprel_base (struct bfd_link_info *info)
 {
-  BFD_ASSERT (elf_hash_table (info)->tls_sec != NULL);
   return elf_hash_table (info)->tls_sec->vma;
 }
 
@@ -3701,9 +4261,7 @@ elfNN_ia64_dtprel_base (info)
 static bfd *elfNN_ia64_unwind_entry_compare_bfd;
 
 static int
-elfNN_ia64_unwind_entry_compare (a, b)
-     const PTR a;
-     const PTR b;
+elfNN_ia64_unwind_entry_compare (const PTR a, const PTR b)
 {
   bfd_vma av, bv;
 
@@ -3715,9 +4273,7 @@ elfNN_ia64_unwind_entry_compare (a, b)
 
 /* Make sure we've got ourselves a nice fat __gp value.  */
 static bfd_boolean
-elfNN_ia64_choose_gp (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info;
+elfNN_ia64_choose_gp (bfd *abfd, struct bfd_link_info *info)
 {
   bfd_vma min_vma = (bfd_vma) -1, max_vma = 0;
   bfd_vma min_short_vma = min_vma, max_short_vma = 0;
@@ -3738,7 +4294,7 @@ elfNN_ia64_choose_gp (abfd, info)
        continue;
 
       lo = os->vma;
-      hi = os->vma + os->size;
+      hi = os->vma + (os->rawsize ? os->rawsize : os->size);
       if (hi < lo)
        hi = (bfd_vma) -1;
 
@@ -3755,6 +4311,20 @@ elfNN_ia64_choose_gp (abfd, info)
        }
     }
 
+  if (ia64_info->min_short_sec)
+    {
+      if (min_short_vma 
+         > (ia64_info->min_short_sec->vma
+            + ia64_info->min_short_offset))
+       min_short_vma = (ia64_info->min_short_sec->vma
+                        + ia64_info->min_short_offset);
+      if (max_short_vma
+         < (ia64_info->max_short_sec->vma
+            + ia64_info->max_short_offset))
+       max_short_vma = (ia64_info->max_short_sec->vma
+                        + ia64_info->max_short_offset);
+    }
+
   /* See if the user wants to force a value.  */
   gp = elf_link_hash_lookup (elf_hash_table (info), "__gp", FALSE,
                             FALSE, FALSE);
@@ -3772,21 +4342,36 @@ elfNN_ia64_choose_gp (abfd, info)
     {
       /* Pick a sensible value.  */
 
-      asection *got_sec = ia64_info->got_sec;
+      if (ia64_info->min_short_sec)
+       {
+         bfd_vma short_range = max_short_vma - min_short_vma;
 
-      /* Start with just the address of the .got.  */
-      if (got_sec)
-       gp_val = got_sec->output_section->vma;
-      else if (max_short_vma != 0)
-       gp_val = min_short_vma;
+         /* If min_short_sec is set, pick one in the middle bewteen
+            min_short_vma and max_short_vma.  */
+         if (short_range >= 0x400000)
+           goto overflow;
+         gp_val = min_short_vma + short_range / 2;
+       }
       else
-       gp_val = min_vma;
+       {
+         asection *got_sec = ia64_info->root.sgot;
+
+         /* Start with just the address of the .got.  */
+         if (got_sec)
+           gp_val = got_sec->output_section->vma;
+         else if (max_short_vma != 0)
+           gp_val = min_short_vma;
+         else if (max_vma - min_vma < 0x200000)
+           gp_val = min_vma;
+         else
+           gp_val = max_vma - 0x200000 + 8;
+       }
 
       /* If it is possible to address the entire image, but we
         don't with the choice above, adjust.  */
       if (max_vma - min_vma < 0x400000
-         && max_vma - gp_val <= 0x200000
-         && gp_val - min_vma > 0x200000)
+         && (max_vma - gp_val >= 0x200000
+             || gp_val - min_vma > 0x200000))
        gp_val = min_vma + 0x200000;
       else if (max_short_vma != 0)
        {
@@ -3807,6 +4392,7 @@ elfNN_ia64_choose_gp (abfd, info)
     {
       if (max_short_vma - min_short_vma >= 0x400000)
        {
+overflow:
          (*_bfd_error_handler)
            (_("%s: short data segment overflowed (0x%lx >= 0x400000)"),
             bfd_get_filename (abfd),
@@ -3831,9 +4417,7 @@ elfNN_ia64_choose_gp (abfd, info)
 }
 
 static bfd_boolean
-elfNN_ia64_final_link (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info;
+elfNN_ia64_final_link (bfd *abfd, struct bfd_link_info *info)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   asection *unwind_output_sec;
@@ -3843,15 +4427,15 @@ elfNN_ia64_final_link (abfd, info)
   /* Make sure we've got ourselves a nice fat __gp value.  */
   if (!info->relocatable)
     {
-      bfd_vma gp_val = _bfd_get_gp_value (abfd);
+      bfd_vma gp_val;
       struct elf_link_hash_entry *gp;
 
-      if (gp_val == 0)
-       {
-         if (! elfNN_ia64_choose_gp (abfd, info))
-           return FALSE;
-         gp_val = _bfd_get_gp_value (abfd);
-       }
+      /* We assume after gp is set, section size will only decrease. We
+        need to adjust gp for it.  */
+      _bfd_set_gp_value (abfd, 0);
+      if (! elfNN_ia64_choose_gp (abfd, info))
+       return FALSE;
+      gp_val = _bfd_get_gp_value (abfd);
 
       gp = elf_link_hash_lookup (elf_hash_table (info), "__gp", FALSE,
                                 FALSE, FALSE);
@@ -3902,16 +4486,14 @@ elfNN_ia64_final_link (abfd, info)
 }
 
 static bfd_boolean
-elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
-                            contents, relocs, local_syms, local_sections)
-     bfd *output_bfd;
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     bfd_byte *contents;
-     Elf_Internal_Rela *relocs;
-     Elf_Internal_Sym *local_syms;
-     asection **local_sections;
+elfNN_ia64_relocate_section (bfd *output_bfd,
+                            struct bfd_link_info *info,
+                            bfd *input_bfd,
+                            asection *input_section,
+                            bfd_byte *contents,
+                            Elf_Internal_Rela *relocs,
+                            Elf_Internal_Sym *local_syms,
+                            asection **local_sections)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   Elf_Internal_Shdr *symtab_hdr;
@@ -3934,7 +4516,6 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
 
       elf_section_data(input_section->output_section)
        ->this_hdr.sh_flags |= flags;
-      return TRUE;
     }
 
   gp_val = _bfd_get_gp_value (output_bfd);
@@ -3961,8 +4542,8 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
       if (r_type > R_IA64_MAX_RELOC_CODE)
        {
          (*_bfd_error_handler)
-           (_("%s: unknown relocation type %d"),
-            bfd_archive_filename (input_bfd), (int)r_type);
+           (_("%B: unknown relocation type %d"),
+            input_bfd, (int) r_type);
          bfd_set_error (bfd_error_bad_value);
          ret_val = FALSE;
          continue;
@@ -3983,7 +4564,8 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
          sym_sec = local_sections[r_symndx];
          msec = sym_sec;
          value = _bfd_elf_rela_local_sym (output_bfd, sym, &msec, rel);
-         if ((sym_sec->flags & SEC_MERGE)
+         if (!info->relocatable
+             && (sym_sec->flags & SEC_MERGE) != 0
              && ELF_ST_TYPE (sym->st_info) == STT_SECTION
              && sym_sec->sec_info_type == ELF_INFO_TYPE_MERGE)
            {
@@ -3993,8 +4575,11 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
              if (loc_h && ! loc_h->sec_merge_done)
                {
                  struct elfNN_ia64_dyn_sym_info *dynent;
+                 unsigned int count;
 
-                 for (dynent = loc_h->info; dynent; dynent = dynent->next)
+                 for (count = loc_h->count, dynent = loc_h->info;
+                      count != 0;
+                      count--, dynent++)
                    {
                      msec = sym_sec;
                      dynent->addend =
@@ -4009,6 +4594,16 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                                        - sym_sec->output_section->vma
                                        - sym_sec->output_offset;
                    }
+
+                 /* We may have introduced duplicated entries. We need
+                    to remove them properly.  */
+                 count = sort_dyn_sym_info (loc_h->info, loc_h->count);
+                 if (count != loc_h->count)
+                   {
+                     loc_h->count = count;
+                     loc_h->sorted_count = count;
+                   }
+
                  loc_h->sec_merge_done = 1;
                }
            }
@@ -4030,6 +4625,20 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
            continue;
        }
 
+      /* For relocs against symbols from removed linkonce sections,
+        or sections discarded by a linker script, we just want the
+        section contents zeroed.  Avoid any special processing.  */
+      if (sym_sec != NULL && elf_discarded_section (sym_sec))
+       {
+         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
+         rel->r_info = 0;
+         rel->r_addend = 0;
+         continue;
+       }
+
+      if (info->relocatable)
+       continue;
+
       hit_addr = contents + rel->r_offset;
       value += rel->r_addend;
       dynamic_symbol_p = elfNN_ia64_dynamic_symbol_p (h, info, r_type);
@@ -4066,9 +4675,11 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                  /* ??? People shouldn't be doing non-pic code in
                     shared libraries nor dynamic executables.  */
                  (*_bfd_error_handler)
-                   (_("%s: non-pic code with imm relocation against dynamic symbol `%s'"),
-                    bfd_archive_filename (input_bfd),
-                    h->root.root.string);
+                   (_("%B: non-pic code with imm relocation against dynamic symbol `%s'"),
+                    input_bfd,
+                    h ? h->root.root.string
+                      : bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+                                          sym_sec));
                  ret_val = FALSE;
                  continue;
 
@@ -4119,7 +4730,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_IA64_LTV32LSB:
        case R_IA64_LTV64MSB:
        case R_IA64_LTV64LSB:
-         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         r = elfNN_ia64_install_value (hit_addr, value, r_type);
          break;
 
        case R_IA64_GPREL22:
@@ -4131,13 +4742,16 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
          if (dynamic_symbol_p)
            {
              (*_bfd_error_handler)
-               (_("%s: @gprel relocation against dynamic symbol %s"),
-                bfd_archive_filename (input_bfd), h->root.root.string);
+               (_("%B: @gprel relocation against dynamic symbol %s"),
+                input_bfd,
+                h ? h->root.root.string
+                  : bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+                                      sym_sec));
              ret_val = FALSE;
              continue;
            }
          value -= gp_val;
-         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         r = elfNN_ia64_install_value (hit_addr, value, r_type);
          break;
 
        case R_IA64_LTOFF22:
@@ -4145,9 +4759,9 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_IA64_LTOFF64I:
           dyn_i = get_dyn_sym_info (ia64_info, h, input_bfd, rel, FALSE);
          value = set_got_entry (input_bfd, info, dyn_i, (h ? h->dynindx : -1),
-                                rel->r_addend, value, R_IA64_DIR64LSB);
+                                rel->r_addend, value, R_IA64_DIRNNLSB);
          value -= gp_val;
-         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         r = elfNN_ia64_install_value (hit_addr, value, r_type);
          break;
 
        case R_IA64_PLTOFF22:
@@ -4157,7 +4771,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
           dyn_i = get_dyn_sym_info (ia64_info, h, input_bfd, rel, FALSE);
          value = set_pltoff_entry (output_bfd, info, dyn_i, value, FALSE);
          value -= gp_val;
-         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         r = elfNN_ia64_install_value (hit_addr, value, r_type);
          break;
 
        case R_IA64_FPTR64I:
@@ -4191,14 +4805,14 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                      /* ??? People shouldn't be doing non-pic code in
                         shared libraries.  Hork.  */
                      (*_bfd_error_handler)
-                       (_("%s: linking non-pic code in a position independent executable"),
-                        bfd_archive_filename (input_bfd));
+                       (_("%B: linking non-pic code in a position independent executable"),
+                        input_bfd);
                      ret_val = FALSE;
                      continue;
                    }
                  dynindx = 0;
                  addend = value;
-                 dyn_r_type = r_type + R_IA64_REL64LSB - R_IA64_FPTR64LSB;
+                 dyn_r_type = r_type + R_IA64_RELNNLSB - R_IA64_FPTRNNLSB;
                }
              else if (h)
                {
@@ -4222,7 +4836,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                                            dynindx, addend);
            }
 
-         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         r = elfNN_ia64_install_value (hit_addr, value, r_type);
          break;
 
        case R_IA64_LTOFF_FPTR22:
@@ -4237,7 +4851,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
            dyn_i = get_dyn_sym_info (ia64_info, h, input_bfd, rel, FALSE);
            if (dyn_i->want_fptr)
              {
-               BFD_ASSERT (h == NULL || h->dynindx == -1)
+               BFD_ASSERT (h == NULL || h->dynindx == -1);
                if (!undef_weak_ref)
                  value = set_fptr_entry (output_bfd, info, dyn_i, value);
                dynindx = -1;
@@ -4262,9 +4876,9 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
              }
 
            value = set_got_entry (output_bfd, info, dyn_i, dynindx,
-                                  rel->r_addend, value, R_IA64_FPTR64LSB);
+                                  rel->r_addend, value, R_IA64_FPTRNNLSB);
            value -= gp_val;
-           r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+           r = elfNN_ia64_install_value (hit_addr, value, r_type);
          }
          break;
 
@@ -4295,8 +4909,8 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
              /* Should have caught this earlier.  */
              BFD_ASSERT (rel->r_addend == 0);
 
-             value = (ia64_info->plt_sec->output_section->vma
-                      + ia64_info->plt_sec->output_offset
+             value = (ia64_info->root.splt->output_section->vma
+                      + ia64_info->root.splt->output_offset
                       + dyn_i->plt2_offset);
            }
          else
@@ -4321,20 +4935,24 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_IA64_PCREL64I:
          /* The PCREL21BI reloc is specifically not intended for use with
             dynamic relocs.  PCREL21F and PCREL21M are used for speculation
-            fixup code, and thus probably ought not be dynamic.  The 
+            fixup code, and thus probably ought not be dynamic.  The
             PCREL22 and PCREL64I relocs aren't emitted as dynamic relocs.  */
          if (dynamic_symbol_p)
            {
              const char *msg;
 
              if (r_type == R_IA64_PCREL21BI)
-               msg = _("%s: @internal branch to dynamic symbol %s");
+               msg = _("%B: @internal branch to dynamic symbol %s");
              else if (r_type == R_IA64_PCREL21F || r_type == R_IA64_PCREL21M)
-               msg = _("%s: speculation fixup to dynamic symbol %s");
+               msg = _("%B: speculation fixup to dynamic symbol %s");
              else
-               msg = _("%s: @pcrel relocation against dynamic symbol %s");
-             (*_bfd_error_handler) (msg, bfd_archive_filename (input_bfd),
-                                    h->root.root.string);
+               msg = _("%B: @pcrel relocation against dynamic symbol %s");
+             (*_bfd_error_handler) (msg, input_bfd,
+                                    h ? h->root.root.string
+                                      : bfd_elf_sym_name (input_bfd,
+                                                          symtab_hdr,
+                                                          sym,
+                                                          sym_sec));
              ret_val = FALSE;
              continue;
            }
@@ -4345,39 +4963,19 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
          value -= (input_section->output_section->vma
                    + input_section->output_offset
                    + rel->r_offset) & ~ (bfd_vma) 0x3;
-         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         r = elfNN_ia64_install_value (hit_addr, value, r_type);
          break;
 
        case R_IA64_SEGREL32MSB:
        case R_IA64_SEGREL32LSB:
        case R_IA64_SEGREL64MSB:
        case R_IA64_SEGREL64LSB:
-         if (r_symndx == 0)
-           {
-             /* If the input section was discarded from the output, then
-                do nothing.  */
-             r = bfd_reloc_ok;
-           }
-         else
            {
-             struct elf_segment_map *m;
-             Elf_Internal_Phdr *p;
-
              /* Find the segment that contains the output_section.  */
-             for (m = elf_tdata (output_bfd)->segment_map,
-                    p = elf_tdata (output_bfd)->phdr;
-                  m != NULL;
-                  m = m->next, p++)
-               {
-                 int i;
-                 for (i = m->count - 1; i >= 0; i--)
-                   if (m->sections[i] == input_section->output_section)
-                     break;
-                 if (i >= 0)
-                   break;
-               }
+             Elf_Internal_Phdr *p = _bfd_elf_find_segment_containing_section
+               (output_bfd, input_section->output_section);
 
-             if (m == NULL)
+             if (p == NULL)
                {
                  r = bfd_reloc_notsupported;
                }
@@ -4389,8 +4987,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                    value -= p->p_vaddr;
                  else
                    value = 0;
-                 r = elfNN_ia64_install_value (output_bfd, hit_addr, value,
-                                               r_type);
+                 r = elfNN_ia64_install_value (hit_addr, value, r_type);
                }
              break;
            }
@@ -4399,12 +4996,11 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_IA64_SECREL32LSB:
        case R_IA64_SECREL64MSB:
        case R_IA64_SECREL64LSB:
-         /* Make output-section relative.  */
-         if (value > input_section->output_section->vma)
-           value -= input_section->output_section->vma;
-         else
-           value = 0;
-         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         /* Make output-section relative to section where the symbol
+            is defined. PR 475  */
+         if (sym_sec)
+           value -= sym_sec->output_section->vma;
+         r = elfNN_ia64_install_value (hit_addr, value, r_type);
          break;
 
        case R_IA64_IPLTMSB:
@@ -4445,25 +5041,30 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
            r_type = R_IA64_DIR64MSB;
          else
            r_type = R_IA64_DIR64LSB;
-         elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
-         r = elfNN_ia64_install_value (output_bfd, hit_addr + 8, gp_val,
-                                       r_type);
+         elfNN_ia64_install_value (hit_addr, value, r_type);
+         r = elfNN_ia64_install_value (hit_addr + 8, gp_val, r_type);
          break;
 
        case R_IA64_TPREL14:
        case R_IA64_TPREL22:
        case R_IA64_TPREL64I:
+         if (elf_hash_table (info)->tls_sec == NULL)
+           goto missing_tls_sec;
          value -= elfNN_ia64_tprel_base (info);
-         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         r = elfNN_ia64_install_value (hit_addr, value, r_type);
          break;
 
        case R_IA64_DTPREL14:
        case R_IA64_DTPREL22:
        case R_IA64_DTPREL64I:
+       case R_IA64_DTPREL32LSB:
+       case R_IA64_DTPREL32MSB:
        case R_IA64_DTPREL64LSB:
        case R_IA64_DTPREL64MSB:
+         if (elf_hash_table (info)->tls_sec == NULL)
+           goto missing_tls_sec;
          value -= elfNN_ia64_dtprel_base (info);
-         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         r = elfNN_ia64_install_value (hit_addr, value, r_type);
          break;
 
        case R_IA64_LTOFF_TPREL22:
@@ -4480,6 +5081,8 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
              case R_IA64_LTOFF_TPREL22:
                if (!dynamic_symbol_p)
                  {
+                   if (elf_hash_table (info)->tls_sec == NULL)
+                     goto missing_tls_sec;
                    if (!info->shared)
                      value -= elfNN_ia64_tprel_base (info);
                    else
@@ -4497,16 +5100,19 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                break;
              case R_IA64_LTOFF_DTPREL22:
                if (!dynamic_symbol_p)
-                 value -= elfNN_ia64_dtprel_base (info);
-               got_r_type = R_IA64_DTPREL64LSB;
+                 {
+                   if (elf_hash_table (info)->tls_sec == NULL)
+                     goto missing_tls_sec;
+                   value -= elfNN_ia64_dtprel_base (info);
+                 }
+               got_r_type = R_IA64_DTPRELNNLSB;
                break;
              }
            dyn_i = get_dyn_sym_info (ia64_info, h, input_bfd, rel, FALSE);
            value = set_got_entry (input_bfd, info, dyn_i, dynindx, r_addend,
                                   value, got_r_type);
            value -= gp_val;
-           r = elfNN_ia64_install_value (output_bfd, hit_addr, value,
-                                         r_type);
+           r = elfNN_ia64_install_value (hit_addr, value, r_type);
          }
          break;
 
@@ -4535,15 +5141,8 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
            if (h)
              name = h->root.root.string;
            else
-             {
-               name = bfd_elf_string_from_elf_section (input_bfd,
-                                                       symtab_hdr->sh_link,
-                                                       sym->st_name);
-               if (name == NULL)
-                 return FALSE;
-               if (*name == '\0')
-                 name = bfd_section_name (input_bfd, input_section);
-             }
+             name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+                                      sym_sec);
            if (!(*info->callbacks->warning) (info, _("unsupported reloc"),
                                              name, input_bfd,
                                              input_section, rel->r_offset))
@@ -4556,28 +5155,65 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
        case bfd_reloc_outofrange:
        case bfd_reloc_overflow:
        default:
+missing_tls_sec:
          {
            const char *name;
 
            if (h)
              name = h->root.root.string;
            else
+             name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+                                      sym_sec);
+
+           switch (r_type)
              {
-               name = bfd_elf_string_from_elf_section (input_bfd,
-                                                       symtab_hdr->sh_link,
-                                                       sym->st_name);
-               if (name == NULL)
+             case R_IA64_TPREL14:
+             case R_IA64_TPREL22:
+             case R_IA64_TPREL64I:
+             case R_IA64_DTPREL14:
+             case R_IA64_DTPREL22:
+             case R_IA64_DTPREL64I:
+             case R_IA64_DTPREL32LSB:
+             case R_IA64_DTPREL32MSB:
+             case R_IA64_DTPREL64LSB:
+             case R_IA64_DTPREL64MSB:
+             case R_IA64_LTOFF_TPREL22:
+             case R_IA64_LTOFF_DTPMOD22:
+             case R_IA64_LTOFF_DTPREL22:
+               (*_bfd_error_handler)
+                 (_("%B: missing TLS section for relocation %s against `%s' at 0x%lx in section `%A'."),
+                  input_bfd, input_section, howto->name, name,
+                  rel->r_offset);
+               break;
+
+             case R_IA64_PCREL21B:
+             case R_IA64_PCREL21BI:
+             case R_IA64_PCREL21M:
+             case R_IA64_PCREL21F:
+               if (is_elf_hash_table (info->hash))
+                 {
+                   /* Relaxtion is always performed for ELF output.
+                      Overflow failures for those relocations mean
+                      that the section is too big to relax.  */
+                   (*_bfd_error_handler)
+                     (_("%B: Can't relax br (%s) to `%s' at 0x%lx in section `%A' with size 0x%lx (> 0x1000000)."),
+                      input_bfd, input_section, howto->name, name,
+                      rel->r_offset, input_section->size);
+                   break;
+                 }
+             default:
+               if (!(*info->callbacks->reloc_overflow) (info,
+                                                        &h->root,
+                                                        name,
+                                                        howto->name,
+                                                        (bfd_vma) 0,
+                                                        input_bfd,
+                                                        input_section,
+                                                        rel->r_offset))
                  return FALSE;
-               if (*name == '\0')
-                 name = bfd_section_name (input_bfd, input_section);
+               break;
              }
-           if (!(*info->callbacks->reloc_overflow) (info, name,
-                                                    howto->name,
-                                                    (bfd_vma) 0,
-                                                    input_bfd,
-                                                    input_section,
-                                                    rel->r_offset))
-             return FALSE;
+
            ret_val = FALSE;
          }
          break;
@@ -4588,11 +5224,10 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
 }
 
 static bfd_boolean
-elfNN_ia64_finish_dynamic_symbol (output_bfd, info, h, sym)
-     bfd *output_bfd;
-     struct bfd_link_info *info;
-     struct elf_link_hash_entry *h;
-     Elf_Internal_Sym *sym;
+elfNN_ia64_finish_dynamic_symbol (bfd *output_bfd,
+                                 struct bfd_link_info *info,
+                                 struct elf_link_hash_entry *h,
+                                 Elf_Internal_Sym *sym)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   struct elfNN_ia64_dyn_sym_info *dyn_i;
@@ -4613,13 +5248,12 @@ elfNN_ia64_finish_dynamic_symbol (output_bfd, info, h, sym)
       /* Initialize the minimal PLT entry.  */
 
       index = (dyn_i->plt_offset - PLT_HEADER_SIZE) / PLT_MIN_ENTRY_SIZE;
-      plt_sec = ia64_info->plt_sec;
+      plt_sec = ia64_info->root.splt;
       loc = plt_sec->contents + dyn_i->plt_offset;
 
       memcpy (loc, plt_min_entry, PLT_MIN_ENTRY_SIZE);
-      elfNN_ia64_install_value (output_bfd, loc, index, R_IA64_IMM22);
-      elfNN_ia64_install_value (output_bfd, loc+2, -dyn_i->plt_offset,
-                               R_IA64_PCREL21B);
+      elfNN_ia64_install_value (loc, index, R_IA64_IMM22);
+      elfNN_ia64_install_value (loc+2, -dyn_i->plt_offset, R_IA64_PCREL21B);
 
       plt_addr = (plt_sec->output_section->vma
                  + plt_sec->output_offset
@@ -4632,14 +5266,13 @@ elfNN_ia64_finish_dynamic_symbol (output_bfd, info, h, sym)
          loc = plt_sec->contents + dyn_i->plt2_offset;
 
          memcpy (loc, plt_full_entry, PLT_FULL_ENTRY_SIZE);
-         elfNN_ia64_install_value (output_bfd, loc, pltoff_addr - gp_val,
-                                   R_IA64_IMM22);
+         elfNN_ia64_install_value (loc, pltoff_addr - gp_val, R_IA64_IMM22);
 
          /* Mark the symbol as undefined, rather than as defined in the
             plt section.  Leave the value alone.  */
          /* ??? We didn't redefine it in adjust_dynamic_symbol in the
             first place.  But perhaps elflink.c did some for us.  */
-         if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+         if (!h->def_regular)
            sym->st_shndx = SHN_UNDEF;
        }
 
@@ -4671,17 +5304,16 @@ elfNN_ia64_finish_dynamic_symbol (output_bfd, info, h, sym)
 
   /* Mark some specially defined symbols as absolute.  */
   if (strcmp (h->root.root.string, "_DYNAMIC") == 0
-      || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0
-      || strcmp (h->root.root.string, "_PROCEDURE_LINKAGE_TABLE_") == 0)
+      || h == ia64_info->root.hgot
+      || h == ia64_info->root.hplt)
     sym->st_shndx = SHN_ABS;
 
   return TRUE;
 }
 
 static bfd_boolean
-elfNN_ia64_finish_dynamic_sections (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info;
+elfNN_ia64_finish_dynamic_sections (bfd *abfd,
+                                   struct bfd_link_info *info)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   bfd *dynobj;
@@ -4745,9 +5377,9 @@ elfNN_ia64_finish_dynamic_sections (abfd, info)
        }
 
       /* Initialize the PLT0 entry.  */
-      if (ia64_info->plt_sec)
+      if (ia64_info->root.splt)
        {
-         bfd_byte *loc = ia64_info->plt_sec->contents;
+         bfd_byte *loc = ia64_info->root.splt->contents;
          bfd_vma pltres;
 
          memcpy (loc, plt_header, PLT_HEADER_SIZE);
@@ -4756,7 +5388,7 @@ elfNN_ia64_finish_dynamic_sections (abfd, info)
                    + sgotplt->output_offset
                    - gp_val);
 
-         elfNN_ia64_install_value (abfd, loc+1, pltres, R_IA64_GPREL22);
+         elfNN_ia64_install_value (loc+1, pltres, R_IA64_GPREL22);
        }
     }
 
@@ -4767,9 +5399,7 @@ elfNN_ia64_finish_dynamic_sections (abfd, info)
 
 /* Function to keep IA-64 specific file flags.  */
 static bfd_boolean
-elfNN_ia64_set_private_flags (abfd, flags)
-     bfd *abfd;
-     flagword flags;
+elfNN_ia64_set_private_flags (bfd *abfd, flagword flags)
 {
   BFD_ASSERT (!elf_flags_init (abfd)
              || elf_elfheader (abfd)->e_flags == flags);
@@ -4782,8 +5412,7 @@ elfNN_ia64_set_private_flags (abfd, flags)
 /* Merge backend specific data from an object file to the output
    object file when linking.  */
 static bfd_boolean
-elfNN_ia64_merge_private_bfd_data (ibfd, obfd)
-     bfd *ibfd, *obfd;
+elfNN_ia64_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
 {
   flagword out_flags;
   flagword in_flags;
@@ -4823,8 +5452,8 @@ elfNN_ia64_merge_private_bfd_data (ibfd, obfd)
   if ((in_flags & EF_IA_64_TRAPNIL) != (out_flags & EF_IA_64_TRAPNIL))
     {
       (*_bfd_error_handler)
-       (_("%s: linking trap-on-NULL-dereference with non-trapping files"),
-        bfd_archive_filename (ibfd));
+       (_("%B: linking trap-on-NULL-dereference with non-trapping files"),
+        ibfd);
 
       bfd_set_error (bfd_error_bad_value);
       ok = FALSE;
@@ -4832,8 +5461,8 @@ elfNN_ia64_merge_private_bfd_data (ibfd, obfd)
   if ((in_flags & EF_IA_64_BE) != (out_flags & EF_IA_64_BE))
     {
       (*_bfd_error_handler)
-       (_("%s: linking big-endian files with little-endian files"),
-        bfd_archive_filename (ibfd));
+       (_("%B: linking big-endian files with little-endian files"),
+        ibfd);
 
       bfd_set_error (bfd_error_bad_value);
       ok = FALSE;
@@ -4841,8 +5470,8 @@ elfNN_ia64_merge_private_bfd_data (ibfd, obfd)
   if ((in_flags & EF_IA_64_ABI64) != (out_flags & EF_IA_64_ABI64))
     {
       (*_bfd_error_handler)
-       (_("%s: linking 64-bit files with 32-bit files"),
-        bfd_archive_filename (ibfd));
+       (_("%B: linking 64-bit files with 32-bit files"),
+        ibfd);
 
       bfd_set_error (bfd_error_bad_value);
       ok = FALSE;
@@ -4850,8 +5479,8 @@ elfNN_ia64_merge_private_bfd_data (ibfd, obfd)
   if ((in_flags & EF_IA_64_CONS_GP) != (out_flags & EF_IA_64_CONS_GP))
     {
       (*_bfd_error_handler)
-       (_("%s: linking constant-gp files with non-constant-gp files"),
-        bfd_archive_filename (ibfd));
+       (_("%B: linking constant-gp files with non-constant-gp files"),
+        ibfd);
 
       bfd_set_error (bfd_error_bad_value);
       ok = FALSE;
@@ -4860,8 +5489,8 @@ elfNN_ia64_merge_private_bfd_data (ibfd, obfd)
       != (out_flags & EF_IA_64_NOFUNCDESC_CONS_GP))
     {
       (*_bfd_error_handler)
-       (_("%s: linking auto-pic files with non-auto-pic files"),
-        bfd_archive_filename (ibfd));
+       (_("%B: linking auto-pic files with non-auto-pic files"),
+        ibfd);
 
       bfd_set_error (bfd_error_bad_value);
       ok = FALSE;
@@ -4871,9 +5500,7 @@ elfNN_ia64_merge_private_bfd_data (ibfd, obfd)
 }
 
 static bfd_boolean
-elfNN_ia64_print_private_bfd_data (abfd, ptr)
-     bfd *abfd;
-     PTR ptr;
+elfNN_ia64_print_private_bfd_data (bfd *abfd, PTR ptr)
 {
   FILE *file = (FILE *) ptr;
   flagword flags = elf_elfheader (abfd)->e_flags;
@@ -4895,8 +5522,7 @@ elfNN_ia64_print_private_bfd_data (abfd, ptr)
 }
 
 static enum elf_reloc_type_class
-elfNN_ia64_reloc_type_class (rela)
-     const Elf_Internal_Rela *rela;
+elfNN_ia64_reloc_type_class (const Elf_Internal_Rela *rela)
 {
   switch ((int) ELFNN_R_TYPE (rela->r_info))
     {
@@ -4915,14 +5541,107 @@ elfNN_ia64_reloc_type_class (rela)
     }
 }
 
-static struct bfd_elf_special_section const elfNN_ia64_special_sections[]=
+static const struct bfd_elf_special_section elfNN_ia64_special_sections[] =
 {
-  { ".sbss",  5, -1, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE + SHF_IA_64_SHORT },
-  { ".sdata", 6, -1, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_IA_64_SHORT },
-  { NULL,     0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".sbss"),  -1, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE + SHF_IA_64_SHORT },
+  { STRING_COMMA_LEN (".sdata"), -1, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_IA_64_SHORT },
+  { NULL,                    0,   0, 0,            0 }
 };
 
 static bfd_boolean
+elfNN_ia64_object_p (bfd *abfd)
+{
+  asection *sec;
+  asection *group, *unwi, *unw;
+  flagword flags;
+  const char *name;
+  char *unwi_name, *unw_name;
+  bfd_size_type amt;
+
+  if (abfd->flags & DYNAMIC)
+    return TRUE;
+
+  /* Flags for fake group section.  */
+  flags = (SEC_LINKER_CREATED | SEC_GROUP | SEC_LINK_ONCE
+          | SEC_EXCLUDE);
+
+  /* We add a fake section group for each .gnu.linkonce.t.* section,
+     which isn't in a section group, and its unwind sections.  */
+  for (sec = abfd->sections; sec != NULL; sec = sec->next)
+    {
+      if (elf_sec_group (sec) == NULL
+         && ((sec->flags & (SEC_LINK_ONCE | SEC_CODE | SEC_GROUP))
+             == (SEC_LINK_ONCE | SEC_CODE))
+         && CONST_STRNEQ (sec->name, ".gnu.linkonce.t."))
+       {
+         name = sec->name + 16;
+
+         amt = strlen (name) + sizeof (".gnu.linkonce.ia64unwi.");
+         unwi_name = bfd_alloc (abfd, amt);
+         if (!unwi_name)
+           return FALSE;
+
+         strcpy (stpcpy (unwi_name, ".gnu.linkonce.ia64unwi."), name);
+         unwi = bfd_get_section_by_name (abfd, unwi_name);
+
+         amt = strlen (name) + sizeof (".gnu.linkonce.ia64unw.");
+         unw_name = bfd_alloc (abfd, amt);
+         if (!unw_name)
+           return FALSE;
+
+         strcpy (stpcpy (unw_name, ".gnu.linkonce.ia64unw."), name);
+         unw = bfd_get_section_by_name (abfd, unw_name);
+
+         /* We need to create a fake group section for it and its
+            unwind sections.  */
+         group = bfd_make_section_anyway_with_flags (abfd, name,
+                                                     flags);
+         if (group == NULL)
+           return FALSE;
+
+         /* Move the fake group section to the beginning.  */
+         bfd_section_list_remove (abfd, group);
+         bfd_section_list_prepend (abfd, group);
+
+         elf_next_in_group (group) = sec;
+
+         elf_group_name (sec) = name;
+         elf_next_in_group (sec) = sec;
+         elf_sec_group (sec) = group;
+
+         if (unwi)
+           {
+             elf_group_name (unwi) = name;
+             elf_next_in_group (unwi) = sec;
+             elf_next_in_group (sec) = unwi;
+             elf_sec_group (unwi) = group;
+           }
+
+          if (unw)
+            {
+              elf_group_name (unw) = name;
+              if (unwi)
+                {
+                  elf_next_in_group (unw) = elf_next_in_group (unwi);
+                  elf_next_in_group (unwi) = unw;
+                }
+              else
+                {
+                  elf_next_in_group (unw) = sec;
+                  elf_next_in_group (sec) = unw;
+                }
+              elf_sec_group (unw) = group;
+            }
+
+          /* Fake SHT_GROUP section header.  */
+         elf_section_data (group)->this_hdr.bfd_section = group;
+         elf_section_data (group)->this_hdr.sh_type = SHT_GROUP;
+       }
+    }
+  return TRUE;
+}
+
+static bfd_boolean
 elfNN_ia64_hpux_vec (const bfd_target *vec)
 {
   extern const bfd_target bfd_elfNN_ia64_hpux_big_vec;
@@ -4930,21 +5649,18 @@ elfNN_ia64_hpux_vec (const bfd_target *vec)
 }
 
 static void
-elfNN_hpux_post_process_headers (abfd, info)
-       bfd *abfd;
-       struct bfd_link_info *info ATTRIBUTE_UNUSED;
+elfNN_hpux_post_process_headers (bfd *abfd,
+                                struct bfd_link_info *info ATTRIBUTE_UNUSED)
 {
   Elf_Internal_Ehdr *i_ehdrp = elf_elfheader (abfd);
 
-  i_ehdrp->e_ident[EI_OSABI] = ELFOSABI_HPUX;
+  i_ehdrp->e_ident[EI_OSABI] = get_elf_backend_data (abfd)->elf_osabi;
   i_ehdrp->e_ident[EI_ABIVERSION] = 1;
 }
 
-bfd_boolean
-elfNN_hpux_backend_section_from_bfd_section (abfd, sec, retval)
-       bfd *abfd ATTRIBUTE_UNUSED;
-       asection *sec;
-       int *retval;
+static bfd_boolean
+elfNN_hpux_backend_section_from_bfd_section (bfd *abfd ATTRIBUTE_UNUSED,
+                                            asection *sec, int *retval)
 {
   if (bfd_is_com_section (sec))
     {
@@ -4958,7 +5674,7 @@ static void
 elfNN_hpux_backend_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
                                      asymbol *asym)
 {
-  elf_symbol_type *elfsym = (elf_symbol_type *) asym;;
+  elf_symbol_type *elfsym = (elf_symbol_type *) asym;
 
   switch (elfsym->internal_elf_sym.st_shndx)
     {
@@ -4970,6 +5686,267 @@ elfNN_hpux_backend_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
     }
 }
 
+static bfd_boolean
+elfNN_vms_section_from_shdr (bfd *abfd,
+                            Elf_Internal_Shdr *hdr,
+                            const char *name,
+                            int shindex)
+{
+  asection *newsect;
+
+  switch (hdr->sh_type)
+    {
+    case SHT_IA_64_VMS_TRACE:
+    case SHT_IA_64_VMS_DEBUG:
+    case SHT_IA_64_VMS_DEBUG_STR:
+      break;
+
+    default:
+      return elfNN_ia64_section_from_shdr (abfd, hdr, name, shindex);
+    }
+
+  if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
+    return FALSE;
+  newsect = hdr->bfd_section;
+
+  return TRUE;
+}
+
+static bfd_boolean
+elfNN_vms_object_p (bfd *abfd)
+{
+  Elf_Internal_Ehdr *i_ehdrp = elf_elfheader (abfd);
+  Elf_Internal_Phdr *i_phdr = elf_tdata (abfd)->phdr;
+  unsigned int i;
+  unsigned int num_text = 0;
+  unsigned int num_data = 0;
+  unsigned int num_rodata = 0;
+  char name[16];
+
+  if (!elfNN_ia64_object_p (abfd))
+    return FALSE;
+
+  for (i = 0; i < i_ehdrp->e_phnum; i++, i_phdr++)
+    {
+      /* Is there a section for this segment?  */
+      bfd_vma base_vma = i_phdr->p_vaddr;
+      bfd_vma limit_vma = base_vma + i_phdr->p_filesz;
+
+      if (i_phdr->p_type != PT_LOAD)
+       continue;
+
+    again:
+      while (base_vma < limit_vma)
+       {
+         bfd_vma next_vma = limit_vma;
+         asection *nsec;
+         asection *sec;
+         flagword flags;
+         char *nname = NULL;
+
+         /* Find a section covering base_vma.  */
+         for (sec = abfd->sections; sec != NULL; sec = sec->next)
+           {
+             if ((sec->flags & (SEC_ALLOC | SEC_LOAD)) == 0)
+               continue;
+             if (sec->vma <= base_vma && sec->vma + sec->size > base_vma)
+               {
+                 base_vma = sec->vma + sec->size;
+                 goto again;
+               }
+             if (sec->vma < next_vma && sec->vma + sec->size >= base_vma)
+               next_vma = sec->vma;
+           }
+
+         /* No section covering [base_vma; next_vma).  Create a fake one.  */
+         flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS;
+         if (i_phdr->p_flags & PF_X)
+           {
+             flags |= SEC_CODE;
+             if (num_text++ == 0)
+               nname = ".text";
+             else
+               sprintf (name, ".text$%u", num_text);
+           }
+         else if ((i_phdr->p_flags & (PF_R | PF_W)) == PF_R)
+           {
+             flags |= SEC_READONLY;
+             sprintf (name, ".rodata$%u", num_rodata++);
+           }
+         else
+           {
+             flags |= SEC_DATA;
+             sprintf (name, ".data$%u", num_data++);
+           }
+
+         /* Allocate name.  */
+         if (nname == NULL)
+           {
+             size_t name_len = strlen (name) + 1;
+             nname = bfd_alloc (abfd, name_len);
+             if (nname == NULL)
+               return FALSE;
+             memcpy (nname, name, name_len);
+           }
+
+         /* Create and fill new section.  */
+         nsec = bfd_make_section_anyway_with_flags (abfd, nname, flags);
+         if (nsec == NULL)
+           return FALSE;
+         nsec->vma = base_vma;
+         nsec->size = next_vma - base_vma;
+         nsec->filepos = i_phdr->p_offset + (base_vma - i_phdr->p_vaddr);
+         
+         base_vma = next_vma;
+       }
+    }
+  return TRUE;
+}
+
+static void
+elfNN_vms_post_process_headers (bfd *abfd,
+                               struct bfd_link_info *info ATTRIBUTE_UNUSED)
+{
+  Elf_Internal_Ehdr *i_ehdrp = elf_elfheader (abfd);
+
+  i_ehdrp->e_ident[EI_OSABI] = ELFOSABI_OPENVMS;
+  i_ehdrp->e_ident[EI_ABIVERSION] = 2;
+}
+
+static bfd_boolean
+elfNN_vms_section_processing (bfd *abfd ATTRIBUTE_UNUSED,
+                             Elf_Internal_Shdr *hdr)
+{
+  if (hdr->bfd_section != NULL)
+    {
+      const char *name = bfd_get_section_name (abfd, hdr->bfd_section);
+
+      if (strcmp (name, ".text") == 0)
+       hdr->sh_flags |= SHF_IA_64_VMS_SHARED;
+      else if ((strcmp (name, ".debug") == 0)
+           || (strcmp (name, ".debug_abbrev") == 0)
+           || (strcmp (name, ".debug_aranges") == 0)
+           || (strcmp (name, ".debug_frame") == 0)
+           || (strcmp (name, ".debug_info") == 0)
+           || (strcmp (name, ".debug_loc") == 0)
+           || (strcmp (name, ".debug_macinfo") == 0)
+           || (strcmp (name, ".debug_pubnames") == 0)
+           || (strcmp (name, ".debug_pubtypes") == 0))
+       hdr->sh_type = SHT_IA_64_VMS_DEBUG;
+      else if ((strcmp (name, ".debug_line") == 0)
+           || (strcmp (name, ".debug_ranges") == 0))
+       hdr->sh_type = SHT_IA_64_VMS_TRACE;
+      else if (strcmp (name, ".debug_str") == 0)
+       hdr->sh_type = SHT_IA_64_VMS_DEBUG_STR;
+      else if (strcmp (name, ".vms_display_name_info") == 0)
+       {
+         int idx, symcount;
+         asymbol **syms;
+         struct elf_obj_tdata *t = elf_tdata (abfd);
+         int buf[2];
+         int demangler_sym_idx = -1;
+
+         symcount = bfd_get_symcount (abfd);
+         syms = bfd_get_outsymbols (abfd);
+         for (idx = 0; idx < symcount; idx++)
+           {
+             asymbol *sym;
+             sym = syms[idx];
+             if ((sym->flags & (BSF_DEBUGGING | BSF_DYNAMIC))
+                 && strchr (sym->name, '@')
+                 && (strcmp (sym->section->name, BFD_ABS_SECTION_NAME) == 0))
+               {
+                 demangler_sym_idx = sym->udata.i;
+                 break;
+               }
+           }
+
+         hdr->sh_type = SHT_IA_64_VMS_DISPLAY_NAME_INFO;
+         hdr->sh_entsize = 4;
+         hdr->sh_addralign = 0;
+         hdr->sh_link = t->symtab_section;
+
+         /* Find symtab index of demangler routine and stuff it in
+            the second long word of section data.  */
+
+         if (demangler_sym_idx > -1)
+           {
+             bfd_seek (abfd, hdr->sh_offset, SEEK_SET);
+             bfd_bread (buf, hdr->sh_size, abfd);
+             buf [1] = demangler_sym_idx;
+             bfd_seek (abfd, hdr->sh_offset, SEEK_SET);
+             bfd_bwrite (buf, hdr->sh_size, abfd);
+           }
+       }
+    }
+
+  return TRUE;
+}
+
+/* The final processing done just before writing out a VMS IA-64 ELF
+   object file.  */
+
+static void
+elfNN_vms_final_write_processing (bfd *abfd,
+                                 bfd_boolean linker ATTRIBUTE_UNUSED)
+{
+  Elf_Internal_Shdr *hdr;
+  asection *s;
+  int unwind_info_sect_idx = 0;
+
+  for (s = abfd->sections; s; s = s->next)
+    {
+      hdr = &elf_section_data (s)->this_hdr;
+
+      if (strcmp (bfd_get_section_name (abfd, hdr->bfd_section),
+                 ".IA_64.unwind_info") == 0)
+       unwind_info_sect_idx = elf_section_data (s)->this_idx;
+
+      switch (hdr->sh_type)
+       {
+       case SHT_IA_64_UNWIND:
+         /* VMS requires sh_info to point to the unwind info section.  */
+          hdr->sh_info = unwind_info_sect_idx;
+         break;
+       }
+    }
+
+  if (! elf_flags_init (abfd))
+    {
+      unsigned long flags = 0;
+
+      if (abfd->xvec->byteorder == BFD_ENDIAN_BIG)
+       flags |= EF_IA_64_BE;
+      if (bfd_get_mach (abfd) == bfd_mach_ia64_elf64)
+       flags |= EF_IA_64_ABI64;
+
+      elf_elfheader(abfd)->e_flags = flags;
+      elf_flags_init (abfd) = TRUE;
+    }
+}
+
+static bfd_boolean
+elfNN_vms_close_and_cleanup (bfd *abfd)
+{
+  if (bfd_get_format (abfd) == bfd_object)
+    {
+      long isize, irsize;
+
+      if (elf_shstrtab (abfd) != NULL)
+       _bfd_elf_strtab_free (elf_shstrtab (abfd));
+
+      /* Pad to 8 byte boundary for IPF/VMS.  */
+      isize = bfd_get_size (abfd);
+      if ((irsize = isize/8*8) < isize)
+       {
+         int ishort = (irsize + 8) - isize;
+         bfd_seek (abfd, isize, SEEK_SET);
+         bfd_bwrite (bfd_zmalloc (ishort), ishort, abfd);
+       }
+    }
+
+  return _bfd_generic_close_and_cleanup (abfd);
+}
 \f
 #define TARGET_LITTLE_SYM              bfd_elfNN_ia64_little_vec
 #define TARGET_LITTLE_NAME             "elfNN-ia64-little"
@@ -4980,6 +5957,7 @@ elfNN_hpux_backend_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
 #define ELF_MACHINE_ALT1               1999    /* EAS2.3 */
 #define ELF_MACHINE_ALT2               1998    /* EAS2.2 */
 #define ELF_MAXPAGESIZE                        0x10000 /* 64KB */
+#define ELF_COMMONPAGESIZE             0x4000  /* 16KB */
 
 #define elf_backend_section_from_shdr \
        elfNN_ia64_section_from_shdr
@@ -4995,16 +5973,23 @@ elfNN_hpux_backend_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
        elfNN_ia64_additional_program_headers
 #define elf_backend_modify_segment_map \
        elfNN_ia64_modify_segment_map
+#define elf_backend_modify_program_headers \
+       elfNN_ia64_modify_program_headers
 #define elf_info_to_howto \
        elfNN_ia64_info_to_howto
 
 #define bfd_elfNN_bfd_reloc_type_lookup \
        elfNN_ia64_reloc_type_lookup
+#define bfd_elfNN_bfd_reloc_name_lookup \
+       elfNN_ia64_reloc_name_lookup
 #define bfd_elfNN_bfd_is_local_label_name \
        elfNN_ia64_is_local_label_name
 #define bfd_elfNN_bfd_relax_section \
        elfNN_ia64_relax_section
 
+#define elf_backend_object_p \
+       elfNN_ia64_object_p
+
 /* Stuff for the BFD linker: */
 #define bfd_elfNN_bfd_link_hash_table_create \
        elfNN_ia64_hash_table_create
@@ -5018,6 +6003,8 @@ elfNN_hpux_backend_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
        elfNN_ia64_adjust_dynamic_symbol
 #define elf_backend_size_dynamic_sections \
        elfNN_ia64_size_dynamic_sections
+#define elf_backend_omit_section_dynsym \
+  ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
 #define elf_backend_relocate_section \
        elfNN_ia64_relocate_section
 #define elf_backend_finish_dynamic_symbol \
@@ -5045,9 +6032,18 @@ elfNN_hpux_backend_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
 #define elf_backend_want_dynbss                0
 #define elf_backend_copy_indirect_symbol elfNN_ia64_hash_copy_indirect
 #define elf_backend_hide_symbol                elfNN_ia64_hash_hide_symbol
+#define elf_backend_fixup_symbol       _bfd_elf_link_hash_fixup_symbol
 #define elf_backend_reloc_type_class   elfNN_ia64_reloc_type_class
 #define elf_backend_rela_normal                1
 #define elf_backend_special_sections   elfNN_ia64_special_sections
+#define elf_backend_default_execstack  0
+
+/* FIXME: PR 290: The Intel C compiler generates SHT_IA_64_UNWIND with
+   SHF_LINK_ORDER. But it doesn't set the sh_link or sh_info fields.
+   We don't want to flood users with so many error messages. We turn
+   off the warning for now. It will be turned on later when the Intel
+   compiler is fixed.   */
+#define elf_backend_link_order_error_handler NULL
 
 #include "elfNN-target.h"
 
@@ -5074,12 +6070,57 @@ elfNN_hpux_backend_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
 #undef  elf_backend_want_p_paddr_set_to_zero
 #define elf_backend_want_p_paddr_set_to_zero 1
 
-#undef  ELF_MAXPAGESIZE
-#define ELF_MAXPAGESIZE                 0x1000  /* 1K */
+#undef ELF_COMMONPAGESIZE
+#undef ELF_OSABI
+#define ELF_OSABI                      ELFOSABI_HPUX
 
 #undef  elfNN_bed
 #define elfNN_bed elfNN_ia64_hpux_bed
 
 #include "elfNN-target.h"
 
+/* VMS-specific vectors.  */
+
+#undef  TARGET_LITTLE_SYM
+#define TARGET_LITTLE_SYM              bfd_elfNN_ia64_vms_vec
+#undef  TARGET_LITTLE_NAME
+#define TARGET_LITTLE_NAME             "elfNN-ia64-vms"
+#undef  TARGET_BIG_SYM
+#undef  TARGET_BIG_NAME
+
+/* These are VMS specific functions.  */
+
+#undef  elf_backend_object_p
+#define elf_backend_object_p elfNN_vms_object_p
+
+#undef  elf_backend_section_from_shdr
+#define elf_backend_section_from_shdr elfNN_vms_section_from_shdr
+
+#undef  elf_backend_post_process_headers
+#define elf_backend_post_process_headers elfNN_vms_post_process_headers
+
+#undef  elf_backend_section_processing
+#define elf_backend_section_processing elfNN_vms_section_processing
+
+#undef  elf_backend_final_write_processing
+#define elf_backend_final_write_processing elfNN_vms_final_write_processing
+
+#undef  bfd_elfNN_close_and_cleanup
+#define bfd_elfNN_close_and_cleanup elfNN_vms_close_and_cleanup
+
+#undef  elf_backend_section_from_bfd_section
+
+#undef  elf_backend_symbol_processing
+
 #undef  elf_backend_want_p_paddr_set_to_zero
+
+#undef ELF_OSABI
+#define ELF_OSABI                      ELFOSABI_OPENVMS
+
+#undef  ELF_MAXPAGESIZE
+#define ELF_MAXPAGESIZE                        0x10000 /* 64KB */
+
+#undef  elfNN_bed
+#define elfNN_bed elfNN_ia64_vms_bed
+
+#include "elfNN-target.h"