OSDN Git Service

daily update
[pf3gnuchains/pf3gnuchains3x.git] / bfd / elfxx-ia64.c
index 535b1bc..420a0ef 100644 (file)
@@ -1,6 +1,6 @@
 /* IA-64 support for 64-bit ELF
-   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
-   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.
@@ -163,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 */
 
@@ -175,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;
@@ -615,7 +620,7 @@ bfd_elfNN_ia64_after_parse (int itanium)
 static bfd_boolean
 elfNN_ia64_relax_br (bfd_byte *contents, bfd_vma off)
 {
-  unsigned int template, mlx;
+  unsigned int template_val, mlx;
   bfd_vma t0, t1, s0, s1, s2, br_code;
   long br_slot;
   bfd_byte *hit_addr;
@@ -629,7 +634,7 @@ elfNN_ia64_relax_br (bfd_byte *contents, bfd_vma off)
   /* 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 = t0 & 0x1e;
+  template_val = t0 & 0x1e;
   s0 = (t0 >> 5) & 0x1ffffffffffLL;
   s1 = ((t0 >> 46) | (t1 << 18)) & 0x1ffffffffffLL;
   s2 = (t1 >> 23) & 0x1ffffffffffLL;
@@ -645,9 +650,9 @@ elfNN_ia64_relax_br (bfd_byte *contents, bfd_vma off)
     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 == 0x12                          /* MBB */
+      if (!((template_val == 0x12                              /* MBB */
             && IS_NOP_B (s2))
-           || (template == 0x16                        /* BBB */
+           || (template_val == 0x16                    /* BBB */
                && IS_NOP_B (s0)
                && IS_NOP_B (s2))))
        return FALSE;
@@ -656,16 +661,16 @@ elfNN_ia64_relax_br (bfd_byte *contents, bfd_vma off)
     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 == 0x10                          /* MIB */
+      if (!((template_val == 0x10                              /* MIB */
             && IS_NOP_I (s1))
-           || (template == 0x12                        /* MBB */
+           || (template_val == 0x12                    /* MBB */
                && IS_NOP_B (s1))
-           || (template == 0x16                        /* BBB */
+           || (template_val == 0x16                    /* BBB */
                && IS_NOP_B (s0)
                && IS_NOP_B (s1))
-           || (template == 0x18                        /* MMB */
+           || (template_val == 0x18                    /* MMB */
                && IS_NOP_M (s1))
-           || (template == 0x1c                        /* MFB */
+           || (template_val == 0x1c                    /* MFB */
                && IS_NOP_F (s1))))
        return FALSE;
       br_code = s2;
@@ -689,7 +694,7 @@ elfNN_ia64_relax_br (bfd_byte *contents, bfd_vma off)
   else
     mlx = 0x4;
 
-  if (template == 0x16)
+  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.  */
@@ -718,7 +723,7 @@ elfNN_ia64_relax_br (bfd_byte *contents, bfd_vma off)
 static void
 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;
 
@@ -737,10 +742,10 @@ elfNN_ia64_relax_brl (bfd_byte *contents, bfd_vma off)
   /* Turn a MLX bundle into a MBB bundle with the same stop-bit
      variety.  */
   if (t0 & 0x1)
-    template = 0x13;
+    template_val = 0x13;
   else
-    template = 0x12;
-  t0 = (i1 << 46) | (i0 << 5) | template;
+    template_val = 0x12;
+  t0 = (i1 << 46) | (i0 << 5) | template_val;
   t1 = (i2 << 23) | (i1 >> 18);
 
   bfd_putl64 (t0, hit_addr);
@@ -755,6 +760,42 @@ elfNN_ia64_relax_brl (bfd_byte *contents, bfd_vma off)
 \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 (bfd *abfd, asection *sec,
                          struct bfd_link_info *link_info,
@@ -786,6 +827,10 @@ elfNN_ia64_relax_section (bfd *abfd, asection *sec,
      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;
@@ -854,6 +899,9 @@ elfNN_ia64_relax_section (bfd *abfd, asection *sec,
          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 in pass 0 since br relaxations will
@@ -928,7 +976,7 @@ elfNN_ia64_relax_section (bfd *abfd, asection *sec,
              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);
            }
@@ -994,8 +1042,20 @@ elfNN_ia64_relax_section (bfd *abfd, asection *sec,
                     + 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. */
@@ -1060,7 +1120,7 @@ elfNN_ia64_relax_section (bfd *abfd, asection *sec,
 
              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;
@@ -1080,7 +1140,7 @@ elfNN_ia64_relax_section (bfd *abfd, asection *sec,
                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);
 
@@ -1158,7 +1218,11 @@ elfNN_ia64_relax_section (bfd *abfd, asection *sec,
              ||(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);
@@ -1168,6 +1232,10 @@ elfNN_ia64_relax_section (bfd *abfd, asection *sec,
                  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
            {
@@ -1234,16 +1302,16 @@ elfNN_ia64_relax_section (bfd *abfd, asection *sec,
       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;
 
       if (ia64_info->root.dynamic_sections_created
-         && ia64_info->rel_got_sec != NULL)
+         && ia64_info->root.srelgot != NULL)
        {
          /* Resize .rela.got.  */
-         ia64_info->rel_got_sec->size = 0;
+         ia64_info->root.srelgot->size = 0;
          if (link_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 = TRUE;
          elfNN_ia64_dyn_sym_traverse (ia64_info, allocate_dynrel_entries,
                                       &data);
@@ -1796,8 +1864,7 @@ 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.  */
@@ -1987,14 +2054,12 @@ elfNN_ia64_create_dynamic_sections (bfd *abfd,
 
   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))
@@ -2011,17 +2076,6 @@ elfNN_ia64_create_dynamic_sections (bfd *abfd,
     return FALSE;
   ia64_info->rel_pltoff_sec = s;
 
-  s = bfd_make_section_with_flags (abfd, ".rela.got",
-                                  (SEC_ALLOC | SEC_LOAD
-                                   | SEC_HAS_CONTENTS
-                                   | SEC_IN_MEMORY
-                                   | SEC_LINKER_CREATED
-                                   | SEC_READONLY));
-  if (s == NULL
-      || !bfd_set_section_alignment (abfd, s, LOG_SECTION_ALIGN))
-    return FALSE;
-  ia64_info->rel_got_sec = s;
-
   return TRUE;
 }
 
@@ -2033,8 +2087,8 @@ get_local_sym_hash (struct elfNN_ia64_link_hash_table *ia64_info,
 {
   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;
@@ -2402,7 +2456,7 @@ get_got (bfd *abfd, struct bfd_link_info *info,
   asection *got;
   bfd *dynobj;
 
-  got = ia64_info->got_sec;
+  got = ia64_info->root.sgot;
   if (!got)
     {
       flagword flags;
@@ -2413,9 +2467,7 @@ get_got (bfd *abfd, struct bfd_link_info *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))
@@ -3314,14 +3366,14 @@ allocate_dynrel_entries (struct elfNN_ia64_dyn_sym_info *dyn_i,
          || !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;
@@ -3455,13 +3507,13 @@ elfNN_ia64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
   /* 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.  */
@@ -3500,7 +3552,7 @@ elfNN_ia64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       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.  */
@@ -3523,7 +3575,7 @@ elfNN_ia64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
         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);
     }
@@ -3547,12 +3599,12 @@ elfNN_ia64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       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.  */
@@ -3572,10 +3624,10 @@ elfNN_ia64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
               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)
        {
@@ -3944,7 +3996,7 @@ set_got_entry (bfd *abfd, struct bfd_link_info *info,
   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)
     {
@@ -4055,7 +4107,7 @@ set_got_entry (bfd *abfd, struct bfd_link_info *info,
            }
 
          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);
        }
@@ -4188,8 +4240,6 @@ static bfd_vma
 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) ARCH_SIZE / 4,
                                     tls_sec->alignment_power);
 }
@@ -4201,7 +4251,6 @@ elfNN_ia64_tprel_base (struct bfd_link_info *info)
 static bfd_vma
 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;
 }
 
@@ -4262,6 +4311,20 @@ elfNN_ia64_choose_gp (bfd *abfd, struct bfd_link_info *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);
@@ -4279,17 +4342,30 @@ elfNN_ia64_choose_gp (bfd *abfd, struct bfd_link_info *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;
-      else if (max_vma - min_vma < 0x200000)
-       gp_val = min_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 = max_vma - 0x200000 + 8;
+       {
+         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.  */
@@ -4316,6 +4392,7 @@ elfNN_ia64_choose_gp (bfd *abfd, struct bfd_link_info *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),
@@ -4832,8 +4909,8 @@ elfNN_ia64_relocate_section (bfd *output_bfd,
              /* 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
@@ -4896,7 +4973,7 @@ elfNN_ia64_relocate_section (bfd *output_bfd,
            {
              /* Find the segment that contains the output_section.  */
              Elf_Internal_Phdr *p = _bfd_elf_find_segment_containing_section
-               (input_bfd, input_section->output_section);
+               (output_bfd, input_section->output_section);
 
              if (p == NULL)
                {
@@ -4971,6 +5048,8 @@ elfNN_ia64_relocate_section (bfd *output_bfd,
        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 (hit_addr, value, r_type);
          break;
@@ -4982,6 +5061,8 @@ elfNN_ia64_relocate_section (bfd *output_bfd,
        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 (hit_addr, value, r_type);
          break;
@@ -5000,6 +5081,8 @@ elfNN_ia64_relocate_section (bfd *output_bfd,
              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
@@ -5017,7 +5100,11 @@ elfNN_ia64_relocate_section (bfd *output_bfd,
                break;
              case R_IA64_LTOFF_DTPREL22:
                if (!dynamic_symbol_p)
-                 value -= elfNN_ia64_dtprel_base (info);
+                 {
+                   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;
              }
@@ -5068,6 +5155,7 @@ elfNN_ia64_relocate_section (bfd *output_bfd,
        case bfd_reloc_outofrange:
        case bfd_reloc_overflow:
        default:
+missing_tls_sec:
          {
            const char *name;
 
@@ -5079,6 +5167,25 @@ elfNN_ia64_relocate_section (bfd *output_bfd,
 
            switch (r_type)
              {
+             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:
@@ -5141,7 +5248,7 @@ elfNN_ia64_finish_dynamic_symbol (bfd *output_bfd,
       /* 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);
@@ -5270,9 +5377,9 @@ elfNN_ia64_finish_dynamic_sections (bfd *abfd,
        }
 
       /* 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);
@@ -5579,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"
@@ -5711,4 +6079,48 @@ elfNN_hpux_backend_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
 
 #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"