Correct spelling of "relocatable".
[deliverable/binutils-gdb.git] / bfd / elfxx-ia64.c
index 2a32591196cd850b547e4da1314e7afa8d4205a7..2098f6fa9fa341eef68afb900f6005f8189bc4e1 100644 (file)
@@ -102,6 +102,7 @@ struct elfNN_ia64_dyn_sym_info
 
   /* TRUE for the different kinds of linker data we want created.  */
   unsigned want_got : 1;
+  unsigned want_gotx : 1;
   unsigned want_fptr : 1;
   unsigned want_ltoff_fptr : 1;
   unsigned want_plt : 1;
@@ -154,6 +155,12 @@ struct elfNN_ia64_link_hash_table
   struct elfNN_ia64_local_hash_table loc_hash_table;
 };
 
+struct elfNN_ia64_allocate_data
+{
+  struct bfd_link_info *info;
+  bfd_size_type ofs;
+};
+
 #define elfNN_ia64_hash_table(p) \
   ((struct elfNN_ia64_link_hash_table *) ((p)->hash))
 
@@ -169,6 +176,8 @@ static void elfNN_ia64_info_to_howto
 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
@@ -299,6 +308,8 @@ 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
@@ -346,6 +357,10 @@ elfNN_ia64_reloc (abfd, reloc, sym, data, input_section,
       reloc->address += input_section->output_offset;
       return bfd_reloc_ok;
     }
+
+  if (input_section->flags & SEC_DEBUGGING)
+    return bfd_reloc_continue;
+
   *error_message = "Unsupported call to elfNN_ia64_reloc";
   return bfd_reloc_notsupported;
 }
@@ -645,37 +660,14 @@ static const bfd_byte plt_full_entry[PLT_FULL_ENTRY_SIZE] =
 #define DYNAMIC_INTERPRETER(abfd) \
   (elfNN_ia64_aix_vec (abfd->xvec) ? AIX_DYNAMIC_INTERPRETER : ELF_DYNAMIC_INTERPRETER)
 
-/* Select out of range branch fixup type.  Note that Itanium does
-   not support brl, and so it gets emulated by the kernel.  */
-#undef USE_BRL
-
-#ifdef USE_BRL
 static const bfd_byte oor_brl[16] =
 {
   0x05, 0x00, 0x00, 0x00, 0x01, 0x00,  /*  [MLX]        nop.m 0            */
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  /*               brl.sptk.few tgt;; */
   0x00, 0x00, 0x00, 0xc0
 };
-#else
-static const bfd_byte oor_ip[48] =
-{
-  0x04, 0x00, 0x00, 0x00, 0x01, 0x00,  /*  [MLX]        nop.m 0            */
-  0x00, 0x00, 0x00, 0x00, 0x00, 0xe0,  /*               movl r15=0         */
-  0x01, 0x00, 0x00, 0x60,
-  0x03, 0x00, 0x00, 0x00, 0x01, 0x00,  /*  [MII]        nop.m 0            */
-  0x00, 0x01, 0x00, 0x60, 0x00, 0x00,  /*               mov r16=ip;;       */
-  0xf2, 0x80, 0x00, 0x80,              /*               add r16=r15,r16;;  */
-  0x11, 0x00, 0x00, 0x00, 0x01, 0x00,  /*  [MIB]        nop.m 0            */
-  0x60, 0x80, 0x04, 0x80, 0x03, 0x00,  /*               mov b6=r16         */
-  0x60, 0x00, 0x80, 0x00               /*               br b6;;            */
-};
-#endif
 \f
-/* These functions do relaxation for IA-64 ELF.
-
-   This is primarily to support branches to targets out of range;
-   relaxation of R_IA64_LTOFF22X and R_IA64_LDXMOV is handled in
-   relocate_section directly.  */
+/* These functions do relaxation for IA-64 ELF.  */
 
 static bfd_boolean
 elfNN_ia64_relax_section (abfd, sec, link_info, again)
@@ -701,14 +693,23 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
   struct one_fixup *fixups = NULL;
   bfd_boolean changed_contents = FALSE;
   bfd_boolean changed_relocs = FALSE;
+  bfd_boolean changed_got = FALSE;
+  bfd_vma gp = 0;
 
   /* Assume we're not going to change any sizes, and we'll only need
      one pass.  */
   *again = FALSE;
 
-  /* Nothing to do if there are no relocations.  */
+  /* Don't even try to relax for non-ELF outputs.  */
+  if (link_info->hash->creator->flavour != bfd_target_elf_flavour)
+    return FALSE;
+
+  /* Nothing to do if there are no relocations or there is no need for
+     the relax finalize pass.  */
   if ((sec->flags & SEC_RELOC) == 0
-      || sec->reloc_count == 0)
+      || sec->reloc_count == 0
+      || (link_info->relax_finalizing
+         && sec->need_finalize_relax == 0))
     return TRUE;
 
   /* If this is the first time we have been called for this section,
@@ -719,7 +720,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
 
   /* Load the relocations for this section.  */
-  internal_relocs = (_bfd_elfNN_link_read_relocs
+  internal_relocs = (_bfd_elf_link_read_relocs
                     (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL,
                      link_info->keep_memory));
   if (internal_relocs == NULL)
@@ -728,24 +729,6 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
   ia64_info = elfNN_ia64_hash_table (link_info);
   irelend = internal_relocs + sec->reloc_count;
 
-  for (irel = internal_relocs; irel < irelend; irel++)
-    {
-      unsigned long r_type = ELFNN_R_TYPE (irel->r_info);
-      if (r_type == R_IA64_PCREL21B
-         || r_type == R_IA64_PCREL21BI
-         || r_type == R_IA64_PCREL21M
-         || r_type == R_IA64_PCREL21F)
-       break;
-    }
-
-  /* No branch-type relocations.  */
-  if (irel == irelend)
-    {
-      if (elf_section_data (sec)->relocs != internal_relocs)
-       free (internal_relocs);
-      return TRUE;
-    }
-
   /* Get the section contents.  */
   if (elf_section_data (sec)->this_hdr.contents != NULL)
     contents = elf_section_data (sec)->this_hdr.contents;
@@ -760,19 +743,40 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
        goto error_return;
     }
 
-  for (; irel < irelend; irel++)
+  for (irel = internal_relocs; irel < irelend; irel++)
     {
       unsigned long r_type = ELFNN_R_TYPE (irel->r_info);
       bfd_vma symaddr, reladdr, trampoff, toff, roff;
       asection *tsec;
       struct one_fixup *f;
       bfd_size_type amt;
+      bfd_boolean is_branch;
+      struct elfNN_ia64_dyn_sym_info *dyn_i;
 
-      if (r_type != R_IA64_PCREL21B
-         && r_type != R_IA64_PCREL21BI
-         && r_type != R_IA64_PCREL21M
-         && r_type != R_IA64_PCREL21F)
-       continue;
+      switch (r_type)
+       {
+       case R_IA64_PCREL21B:
+       case R_IA64_PCREL21BI:
+       case R_IA64_PCREL21M:
+       case R_IA64_PCREL21F:
+         if (link_info->relax_finalizing)
+           continue;
+         is_branch = TRUE;
+         break;
+
+       case R_IA64_LTOFF22X:
+       case R_IA64_LDXMOV:
+         if (!link_info->relax_finalizing)
+           {
+             sec->need_finalize_relax = 1;
+             continue;
+           }
+         is_branch = FALSE;
+         break;
+
+       default:
+         continue;
+       }
 
       /* Get the value of the symbol referred to by the reloc.  */
       if (ELFNN_R_SYM (irel->r_info) < symtab_hdr->sh_info)
@@ -792,7 +796,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
                goto error_return;
            }
 
-         isym = isymbuf + ELF64_R_SYM (irel->r_info);
+         isym = isymbuf + ELFNN_R_SYM (irel->r_info);
          if (isym->st_shndx == SHN_UNDEF)
            continue;   /* We can't do anthing with undefined symbols.  */
          else if (isym->st_shndx == SHN_ABS)
@@ -805,12 +809,12 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
            tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
 
          toff = isym->st_value;
+         dyn_i = get_dyn_sym_info (ia64_info, NULL, abfd, irel, FALSE);
        }
       else
        {
          unsigned long indx;
          struct elf_link_hash_entry *h;
-          struct elfNN_ia64_dyn_sym_info *dyn_i;
 
          indx = ELFNN_R_SYM (irel->r_info) - symtab_hdr->sh_info;
          h = elf_sym_hashes (abfd)[indx];
@@ -824,7 +828,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
 
          /* For branches to dynamic symbols, we're interested instead
             in a branch to the PLT entry.  */
-         if (dyn_i && dyn_i->want_plt2)
+         if (is_branch && dyn_i && dyn_i->want_plt2)
            {
              /* Internal branches shouldn't be sent to the PLT.
                 Leave this for now and we'll give an error later.  */
@@ -833,7 +837,13 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
 
              tsec = ia64_info->plt_sec;
              toff = dyn_i->plt2_offset;
+             BFD_ASSERT (irel->r_addend == 0);
            }
+
+         /* Can't do anything else with dynamic symbols.  */
+         else if (elfNN_ia64_dynamic_symbol_p (h, link_info))
+           continue;
+
          else
            {
              /* We can't do anthing with undefined symbols.  */
@@ -846,109 +856,151 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
            }
        }
 
-      symaddr = (tsec->output_section->vma
-                + tsec->output_offset
-                + toff
-                + irel->r_addend);
+      if (tsec->sec_info_type == ELF_INFO_TYPE_MERGE)
+       toff = _bfd_merged_section_offset (abfd, &tsec,
+                                          elf_section_data (tsec)->sec_info,
+                                          toff + irel->r_addend,
+                                          (bfd_vma) 0);
+      else
+       toff += irel->r_addend;
+
+      symaddr = tsec->output_section->vma + tsec->output_offset + toff;
 
       roff = irel->r_offset;
-      reladdr = (sec->output_section->vma
-                + sec->output_offset
-                + roff) & (bfd_vma) -4;
 
-      /* If the branch is in range, no need to do anything.  */
-      if ((bfd_signed_vma) (symaddr - reladdr) >= -0x1000000
-         && (bfd_signed_vma) (symaddr - reladdr) <= 0x0FFFFF0)
-       continue;
+      if (is_branch)
+       {
+         reladdr = (sec->output_section->vma
+                    + sec->output_offset
+                    + roff) & (bfd_vma) -4;
 
-      /* 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)
-       continue;
+         /* If the branch is in range, no need to do anything.  */
+         if ((bfd_signed_vma) (symaddr - reladdr) >= -0x1000000
+             && (bfd_signed_vma) (symaddr - reladdr) <= 0x0FFFFF0)
+           continue;
 
-      /* Look for an existing fixup to this address.  */
-      for (f = fixups; f ; f = f->next)
-       if (f->tsec == tsec && f->toff == toff)
-         break;
+         /* 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)
+           continue;
 
-      if (f == NULL)
-       {
-         /* Two alternatives: If it's a branch to a PLT entry, we can
-            make a copy of the FULL_PLT entry.  Otherwise, we'll have
-            to use a `brl' insn to get where we're going.  */
+         /* Look for an existing fixup to this address.  */
+         for (f = fixups; f ; f = f->next)
+           if (f->tsec == tsec && f->toff == toff)
+             break;
+
+         if (f == NULL)
+           {
+             /* Two alternatives: If it's a branch to a PLT entry, we can
+                make a copy of the FULL_PLT entry.  Otherwise, we'll have
+                to use a `brl' insn to get where we're going.  */
+
+             size_t size;
+
+             if (tsec == ia64_info->plt_sec)
+               size = sizeof (plt_full_entry);
+             else
+               {
+                 size = sizeof (oor_brl);
+               }
+
+             /* Resize the current section to make room for the new branch. */
+             trampoff = (sec->_cooked_size + 15) & (bfd_vma) -16;
+             amt = trampoff + size;
+             contents = (bfd_byte *) bfd_realloc (contents, amt);
+             if (contents == NULL)
+               goto error_return;
+             sec->_cooked_size = amt;
 
-         size_t size;
+             if (tsec == ia64_info->plt_sec)
+               {
+                 memcpy (contents + trampoff, plt_full_entry, size);
+
+                 /* Hijack the old relocation for use as the PLTOFF reloc.  */
+                 irel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (irel->r_info),
+                                              R_IA64_PLTOFF22);
+                 irel->r_offset = trampoff;
+               }
+             else
+               {
+                 memcpy (contents + trampoff, oor_brl, size);
+                 irel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (irel->r_info),
+                                              R_IA64_PCREL60B);
+                 irel->r_offset = trampoff + 2;
+               }
 
-         if (tsec == ia64_info->plt_sec)
-           size = sizeof (plt_full_entry);
+             /* Record the fixup so we don't do it again this section.  */
+             f = (struct one_fixup *)
+               bfd_malloc ((bfd_size_type) sizeof (*f));
+             f->next = fixups;
+             f->tsec = tsec;
+             f->toff = toff;
+             f->trampoff = trampoff;
+             fixups = f;
+           }
          else
            {
-#ifdef USE_BRL
-             size = sizeof (oor_brl);
-#else
-             size = sizeof (oor_ip);
-#endif
+             /* Nop out the reloc, since we're finalizing things here.  */
+             irel->r_info = ELFNN_R_INFO (0, R_IA64_NONE);
            }
 
-         /* Resize the current section to make room for the new branch.  */
-         trampoff = (sec->_cooked_size + 15) & (bfd_vma) -16;
-         amt = trampoff + size;
-         contents = (bfd_byte *) bfd_realloc (contents, amt);
-         if (contents == NULL)
+         /* Fix up the existing branch to hit the trampoline.  Hope like
+            hell this doesn't overflow too.  */
+         if (elfNN_ia64_install_value (abfd, contents + roff,
+                                       f->trampoff - (roff & (bfd_vma) -4),
+                                       r_type) != bfd_reloc_ok)
            goto error_return;
-         sec->_cooked_size = amt;
 
-         if (tsec == ia64_info->plt_sec)
+         changed_contents = TRUE;
+         changed_relocs = TRUE;
+       }
+      else
+       {
+         /* Fetch the gp.  */
+         if (gp == 0)
            {
-             memcpy (contents + trampoff, plt_full_entry, size);
+             bfd *obfd = sec->output_section->owner;
+             gp = _bfd_get_gp_value (obfd);
+             if (gp == 0)
+               {
+                 if (!elfNN_ia64_choose_gp (obfd, link_info))
+                   goto error_return;
+                 gp = _bfd_get_gp_value (obfd);
+               }
+           }
 
-             /* Hijack the old relocation for use as the PLTOFF reloc.  */
+         /* If the data is out of range, do nothing.  */
+         if ((bfd_signed_vma) (symaddr - gp) >= 0x200000
+             ||(bfd_signed_vma) (symaddr - gp) < -0x200000)
+           continue;
+
+         if (r_type == R_IA64_LTOFF22X)
+           {
              irel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (irel->r_info),
-                                          R_IA64_PLTOFF22);
-             irel->r_offset = trampoff;
+                                          R_IA64_GPREL22);
+             changed_relocs = TRUE;
+             if (dyn_i->want_gotx)
+               {
+                 dyn_i->want_gotx = 0;
+                 changed_got |= !dyn_i->want_got;
+               }
            }
          else
            {
-#ifdef USE_BRL
-             memcpy (contents + trampoff, oor_brl, size);
-             irel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (irel->r_info),
-                                          R_IA64_PCREL60B);
-             irel->r_offset = trampoff + 2;
-#else
-             memcpy (contents + trampoff, oor_ip, size);
-             irel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (irel->r_info),
-                                          R_IA64_PCREL64I);
-             irel->r_addend -= 16;
-             irel->r_offset = trampoff + 2;
-#endif
+             elfNN_ia64_relax_ldxmov (abfd, contents, roff);
+             irel->r_info = ELFNN_R_INFO (0, R_IA64_NONE);
+             changed_contents = TRUE;
+             changed_relocs = TRUE;
            }
-
-         /* Record the fixup so we don't do it again this section.  */
-         f = (struct one_fixup *) bfd_malloc ((bfd_size_type) sizeof (*f));
-         f->next = fixups;
-         f->tsec = tsec;
-         f->toff = toff;
-         f->trampoff = trampoff;
-         fixups = f;
        }
-      else
-       {
-         /* Nop out the reloc, since we're finalizing things here.  */
-         irel->r_info = ELFNN_R_INFO (0, R_IA64_NONE);
-       }
-
-      /* Fix up the existing branch to hit the trampoline.  Hope like
-        hell this doesn't overflow too.  */
-      if (elfNN_ia64_install_value (abfd, contents + roff,
-                                   f->trampoff - (roff & (bfd_vma) -4),
-                                   r_type) != bfd_reloc_ok)
-       goto error_return;
-
-      changed_contents = TRUE;
-      changed_relocs = TRUE;
     }
 
+  /* ??? If we created fixups, this may push the code segment large
+     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)
     {
@@ -989,6 +1041,25 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
        elf_section_data (sec)->relocs = internal_relocs;
     }
 
+  if (changed_got)
+    {
+      struct elfNN_ia64_allocate_data data;
+      data.info = link_info;
+      data.ofs = 0;
+      ia64_info->self_dtpmod_offset = (bfd_vma) -1;
+
+      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->_raw_size = data.ofs;
+      ia64_info->got_sec->_cooked_size = data.ofs;
+
+      /* ??? Resize .rela.got too.  */
+    }
+
+  if (link_info->relax_finalizing)
+    sec->need_finalize_relax = 0;
+
   *again = changed_contents || changed_relocs;
   return TRUE;
 
@@ -1003,6 +1074,39 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
     free (internal_relocs);
   return FALSE;
 }
+
+static void
+elfNN_ia64_relax_ldxmov (abfd, contents, off)
+     bfd *abfd;
+     bfd_byte *contents;
+     bfd_vma off;
+{
+  int shift, r1, r3;
+  bfd_vma dword, insn;
+
+  switch ((int)off & 0x3)
+    {
+    case 0: shift =  5; break;
+    case 1: shift = 14; off += 3; break;
+    case 2: shift = 23; off += 6; break;
+    default:
+      abort ();
+    }
+
+  dword = bfd_get_64 (abfd, contents + off);
+  insn = (dword >> shift) & 0x1ffffffffffLL;
+
+  r1 = (insn >> 6) & 127;
+  r3 = (insn >> 20) & 127;
+  if (r1 == r3)
+    insn = 0x8000000;                             /* nop */
+  else
+    insn = (insn & 0x7f01fff) | 0x10800000000LL;   /* (qp) mov r1 = r3 */
+
+  dword &= ~(0x1ffffffffffLL << shift);
+  dword |= (insn << shift);
+  bfd_put_64 (abfd, dword, contents + off);
+}
 \f
 /* Return TRUE if NAME is an unwind table section name.  */
 
@@ -1237,7 +1341,7 @@ elfNN_ia64_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
      bfd_vma *valp;
 {
   if (sym->st_shndx == SHN_COMMON
-      && !info->relocateable
+      && !info->relocatable
       && sym->st_size <= elf_gp_size (abfd))
     {
       /* Common symbols less than or equal to -G nn bytes are
@@ -1362,7 +1466,7 @@ elfNN_ia64_aix_link_add_symbols (abfd, info)
   if (! elf_hash_table (info)->dynamic_sections_created
       && abfd->xvec == info->hash->creator)
     {
-      if (! bfd_elfNN_link_create_dynamic_sections (abfd, info))
+      if (! _bfd_elf_link_create_dynamic_sections (abfd, info))
        return FALSE;
     }
 
@@ -1688,7 +1792,10 @@ elfNN_ia64_hash_hide_symbol (info, xh, force_local)
   _bfd_elf_link_hash_hide_symbol (info, &h->root, force_local);
 
   for (dyn_i = h->info; dyn_i; dyn_i = dyn_i->next)
-    dyn_i->want_plt2 = 0;
+    {
+      dyn_i->want_plt2 = 0;
+      dyn_i->want_plt = 0;
+    }
 }
 
 /* Create the derived linker hash table.  The IA-64 ELF port uses this
@@ -1701,19 +1808,24 @@ elfNN_ia64_hash_table_create (abfd)
 {
   struct elfNN_ia64_link_hash_table *ret;
 
-  ret = bfd_zalloc (abfd, (bfd_size_type) sizeof (*ret));
+  ret = bfd_zmalloc ((bfd_size_type) sizeof (*ret));
   if (!ret)
     return 0;
+
   if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
                                      elfNN_ia64_new_elf_hash_entry))
     {
-      bfd_release (abfd, ret);
+      free (ret);
       return 0;
     }
 
   if (!elfNN_ia64_local_hash_table_init (&ret->loc_hash_table, abfd,
                                         elfNN_ia64_new_loc_hash_entry))
-    return 0;
+    {
+      free (ret);
+      return 0;
+    }
+
   return &ret->root.root;
 }
 
@@ -1889,7 +2001,11 @@ get_dyn_sym_info (ia64_info, h, abfd, rel, create)
       struct elfNN_ia64_local_hash_entry *loc_h;
 
       loc_h = get_local_sym_hash (ia64_info, abfd, rel, create);
-      BFD_ASSERT (loc_h);
+      if (!loc_h)
+       {
+         BFD_ASSERT (!create);
+         return NULL;
+       }
 
       pp = &loc_h->info;
     }
@@ -2112,7 +2228,7 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
   const Elf_Internal_Rela *rel;
   asection *got, *fptr, *srel;
 
-  if (info->relocateable)
+  if (info->relocatable)
     return TRUE;
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
@@ -2125,15 +2241,16 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
     {
       enum {
        NEED_GOT = 1,
-       NEED_FPTR = 2,
-       NEED_PLTOFF = 4,
-       NEED_MIN_PLT = 8,
-       NEED_FULL_PLT = 16,
-       NEED_DYNREL = 32,
-       NEED_LTOFF_FPTR = 64,
-       NEED_TPREL = 128,
-       NEED_DTPMOD = 256,
-       NEED_DTPREL = 512
+       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;
@@ -2230,11 +2347,14 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
          break;
 
        case R_IA64_LTOFF22:
-       case R_IA64_LTOFF22X:
        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:
@@ -2316,7 +2436,8 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
       dyn_i->h = h;
 
       /* Create what's needed.  */
-      if (need_entry & (NEED_GOT | NEED_TPREL | NEED_DTPMOD | NEED_DTPREL))
+      if (need_entry & (NEED_GOT | NEED_GOTX | NEED_TPREL
+                       | NEED_DTPMOD | NEED_DTPREL))
        {
          if (!got)
            {
@@ -2326,6 +2447,8 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
            }
          if (need_entry & NEED_GOT)
            dyn_i->want_got = 1;
+         if (need_entry & NEED_GOTX)
+           dyn_i->want_gotx = 1;
          if (need_entry & NEED_TPREL)
            dyn_i->want_tprel = 1;
          if (need_entry & NEED_DTPMOD)
@@ -2385,12 +2508,6 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
   return TRUE;
 }
 
-struct elfNN_ia64_allocate_data
-{
-  struct bfd_link_info *info;
-  bfd_size_type ofs;
-};
-
 /* For cleanliness, and potentially faster dynamic loading, allocate
    external GOT entries first.  */
 
@@ -2401,7 +2518,7 @@ allocate_global_data_got (dyn_i, data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
 
-  if (dyn_i->want_got
+  if ((dyn_i->want_got || dyn_i->want_gotx)
       && ! dyn_i->want_fptr
       && (elfNN_ia64_dynamic_symbol_p (dyn_i->h, x->info)
          || (elfNN_ia64_aix_vec (x->info->hash->creator)
@@ -2473,7 +2590,7 @@ allocate_local_got (dyn_i, data)
 {
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
 
-  if (dyn_i->want_got
+  if ((dyn_i->want_got || dyn_i->want_gotx)
       && ! (elfNN_ia64_dynamic_symbol_p (dyn_i->h, x->info)
            || elfNN_ia64_aix_vec (x->info->hash->creator)))
     {
@@ -2521,7 +2638,10 @@ allocate_fptr (dyn_i, data)
               || h->root.type == bfd_link_hash_warning)
          h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
-      if (x->info->shared
+      if ((x->info->shared
+          && (!h
+              || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+              || h->root.type != bfd_link_hash_undefweak))
          /* AIX needs an FPTR in this case. */
          || (elfNN_ia64_aix_vec (x->info->hash->creator)
              && (!h
@@ -2646,15 +2766,18 @@ allocate_dynrel_entries (dyn_i, data)
   struct elfNN_ia64_allocate_data *x = (struct elfNN_ia64_allocate_data *)data;
   struct elfNN_ia64_link_hash_table *ia64_info;
   struct elfNN_ia64_dyn_reloc_entry *rent;
-  bfd_boolean dynamic_symbol, shared;
+  bfd_boolean dynamic_symbol, shared, resolved_zero;
 
   ia64_info = elfNN_ia64_hash_table (x->info);
   dynamic_symbol = elfNN_ia64_dynamic_symbol_p (dyn_i->h, x->info)
     || (elfNN_ia64_aix_vec (x->info->hash->creator)
        /* Don't allocate an entry for __GLOB_DATA_PTR */
        && (!dyn_i->h || strcmp (dyn_i->h->root.root.string,
-         "__GLOB_DATA_PTR") != 0));
+                                "__GLOB_DATA_PTR") != 0));
   shared = x->info->shared;
+  resolved_zero = (dyn_i->h
+                  && ELF_ST_VISIBILITY (dyn_i->h->other)
+                  && dyn_i->h->root.type == bfd_link_hash_undefweak);
 
   /* Take care of the normal data relocations.  */
 
@@ -2699,8 +2822,12 @@ allocate_dynrel_entries (dyn_i, data)
 
   /* Take care of the GOT and PLT relocations.  */
 
-  if (((dynamic_symbol || shared) && dyn_i->want_got)
-      || (dyn_i->want_ltoff_fptr && dyn_i->h && dyn_i->h->dynindx != -1))
+  if ((!resolved_zero
+       && (dynamic_symbol || shared)
+       && (dyn_i->want_got || dyn_i->want_gotx))
+      || (dyn_i->want_ltoff_fptr
+         && dyn_i->h
+         && dyn_i->h->dynindx != -1))
     ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
   if ((dynamic_symbol || shared) && dyn_i->want_tprel)
     ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
@@ -2709,7 +2836,7 @@ allocate_dynrel_entries (dyn_i, data)
   if (dynamic_symbol && dyn_i->want_dtprel)
     ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
 
-  if (dyn_i->want_pltoff)
+  if (!resolved_zero && dyn_i->want_pltoff)
     {
       bfd_size_type t = 0;
 
@@ -2776,7 +2903,7 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
 
   /* Set the contents of the .interp section to the interpreter.  */
   if (ia64_info->root.dynamic_sections_created
-      && !info->shared)
+      && info->executable)
     {
       sec = bfd_get_section_by_name (dynobj, ".interp");
       BFD_ASSERT (sec != NULL);
@@ -2951,7 +3078,7 @@ elfNN_ia64_size_dynamic_sections (output_bfd, info)
         later (in finish_dynamic_sections) but we must add the entries now
         so that we get the correct size for the .dynamic section.  */
 
-      if (!info->shared)
+      if (info->executable)
        {
          /* The DT_DEBUG entry is filled in by the dynamic linker and used
             by the debugger.  */
@@ -3317,7 +3444,11 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
       bfd_put_64 (abfd, value, got_sec->contents + got_offset);
 
       /* Install a dynamic relocation if needed.  */
-      if ((info->shared && dyn_r_type != R_IA64_DTPREL64LSB)
+      if ((info->shared
+          && (!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_DTPREL64LSB)
           || elfNN_ia64_dynamic_symbol_p (dyn_i->h, info)
          || elfNN_ia64_aix_vec (abfd->xvec)
          || (dynindx != -1 && dyn_r_type == R_IA64_FPTR64LSB))
@@ -3438,7 +3569,11 @@ set_pltoff_entry (abfd, info, dyn_i, value, is_plt)
       bfd_put_64 (abfd, gp, pltoff_sec->contents + dyn_i->pltoff_offset + 8);
 
       /* Install dynamic relocations if needed.  */
-      if (!is_plt && info->shared)
+      if (!is_plt
+         && info->shared
+         && (!dyn_i->h
+             || ELF_ST_VISIBILITY (dyn_i->h->other) == STV_DEFAULT
+             || dyn_i->h->root.type != bfd_link_hash_undefweak))
        {
          unsigned int dyn_r_type;
 
@@ -3516,125 +3651,148 @@ elfNN_ia64_unwind_entry_compare (a, b)
   return (av < bv ? -1 : av > bv ? 1 : 0);
 }
 
+/* Make sure we've got ourselves a nice fat __gp value.  */
 static bfd_boolean
-elfNN_ia64_final_link (abfd, info)
+elfNN_ia64_choose_gp (abfd, info)
      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;
+  struct elf_link_hash_entry *gp;
+  bfd_vma gp_val;
+  asection *os;
   struct elfNN_ia64_link_hash_table *ia64_info;
-  asection *unwind_output_sec;
 
   ia64_info = elfNN_ia64_hash_table (info);
 
-  /* Make sure we've got ourselves a nice fat __gp value.  */
-  if (!info->relocateable)
+  /* Find the min and max vma of all sections marked short.  Also collect
+     min and max vma of any type, for use in selecting a nice gp.  */
+  for (os = abfd->sections; os ; os = os->next)
     {
-      bfd_vma min_vma = (bfd_vma) -1, max_vma = 0;
-      bfd_vma min_short_vma = min_vma, max_short_vma = 0;
-      struct elf_link_hash_entry *gp;
-      bfd_vma gp_val;
-      asection *os;
+      bfd_vma lo, hi;
+
+      if ((os->flags & SEC_ALLOC) == 0)
+       continue;
+
+      lo = os->vma;
+      hi = os->vma + os->_raw_size;
+      if (hi < lo)
+       hi = (bfd_vma) -1;
 
-      /* Find the min and max vma of all sections marked short.  Also
-        collect min and max vma of any type, for use in selecting a
-        nice gp.  */
-      for (os = abfd->sections; os ; os = os->next)
+      if (min_vma > lo)
+       min_vma = lo;
+      if (max_vma < hi)
+       max_vma = hi;
+      if (os->flags & SEC_SMALL_DATA)
        {
-         bfd_vma lo, hi;
+         if (min_short_vma > lo)
+           min_short_vma = lo;
+         if (max_short_vma < hi)
+           max_short_vma = hi;
+       }
+    }
 
-         if ((os->flags & SEC_ALLOC) == 0)
-           continue;
+  /* See if the user wants to force a value.  */
+  gp = elf_link_hash_lookup (elf_hash_table (info), "__gp", FALSE,
+                            FALSE, FALSE);
 
-         lo = os->vma;
-         hi = os->vma + os->_raw_size;
-         if (hi < lo)
-           hi = (bfd_vma) -1;
+  if (gp
+      && (gp->root.type == bfd_link_hash_defined
+         || gp->root.type == bfd_link_hash_defweak))
+    {
+      asection *gp_sec = gp->root.u.def.section;
+      gp_val = (gp->root.u.def.value
+               + gp_sec->output_section->vma
+               + gp_sec->output_offset);
+    }
+  else
+    {
+      /* Pick a sensible value.  */
 
-         if (min_vma > lo)
-           min_vma = lo;
-         if (max_vma < hi)
-           max_vma = hi;
-         if (os->flags & SEC_SMALL_DATA)
-           {
-             if (min_short_vma > lo)
-               min_short_vma = lo;
-             if (max_short_vma < hi)
-               max_short_vma = hi;
-           }
+      asection *got_sec = ia64_info->got_sec;
+
+      /* 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
+       gp_val = min_vma;
+
+      /* 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)
+       gp_val = min_vma + 0x200000;
+      else if (max_short_vma != 0)
+       {
+         /* If we don't cover all the short data, adjust.  */
+         if (max_short_vma - gp_val >= 0x200000)
+           gp_val = min_short_vma + 0x200000;
+
+         /* If we're addressing stuff past the end, adjust back.  */
+         if (gp_val > max_vma)
+           gp_val = max_vma - 0x200000 + 8;
        }
+    }
 
-      /* See if the user wants to force a value.  */
-      gp = elf_link_hash_lookup (elf_hash_table (info), "__gp", FALSE,
-                                FALSE, FALSE);
+  /* Validate whether all SHF_IA_64_SHORT sections are within
+     range of the chosen GP.  */
 
-      if (gp
-         && (gp->root.type == bfd_link_hash_defined
-             || gp->root.type == bfd_link_hash_defweak))
+  if (max_short_vma != 0)
+    {
+      if (max_short_vma - min_short_vma >= 0x400000)
        {
-         asection *gp_sec = gp->root.u.def.section;
-         gp_val = (gp->root.u.def.value
-                   + gp_sec->output_section->vma
-                   + gp_sec->output_offset);
+         (*_bfd_error_handler)
+           (_("%s: short data segment overflowed (0x%lx >= 0x400000)"),
+            bfd_get_filename (abfd),
+            (unsigned long) (max_short_vma - min_short_vma));
+         return FALSE;
        }
-      else
+      else if ((gp_val > min_short_vma
+               && gp_val - min_short_vma > 0x200000)
+              || (gp_val < max_short_vma
+                  && max_short_vma - gp_val >= 0x200000))
        {
-         /* Pick a sensible value.  */
+         (*_bfd_error_handler)
+           (_("%s: __gp does not cover short data segment"),
+            bfd_get_filename (abfd));
+         return FALSE;
+       }
+    }
 
-         asection *got_sec = ia64_info->got_sec;
+  _bfd_set_gp_value (abfd, gp_val);
 
-         /* 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
-           gp_val = min_vma;
-
-         /* 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)
-           gp_val = min_vma + 0x200000;
-         else if (max_short_vma != 0)
-           {
-             /* If we don't cover all the short data, adjust.  */
-             if (max_short_vma - gp_val >= 0x200000)
-               gp_val = min_short_vma + 0x200000;
+  return TRUE;
+}
 
-             /* If we're addressing stuff past the end, adjust back.  */
-             if (gp_val > max_vma)
-               gp_val = max_vma - 0x200000 + 8;
-           }
-       }
+static bfd_boolean
+elfNN_ia64_final_link (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  struct elfNN_ia64_link_hash_table *ia64_info;
+  asection *unwind_output_sec;
 
-      /* Validate whether all SHF_IA_64_SHORT sections are within
-        range of the chosen GP.  */
+  ia64_info = elfNN_ia64_hash_table (info);
+
+  /* Make sure we've got ourselves a nice fat __gp value.  */
+  if (!info->relocatable)
+    {
+      bfd_vma gp_val = _bfd_get_gp_value (abfd);
+      struct elf_link_hash_entry *gp;
 
-      if (max_short_vma != 0)
+      if (gp_val == 0)
        {
-         if (max_short_vma - min_short_vma >= 0x400000)
-           {
-             (*_bfd_error_handler)
-               (_("%s: short data segment overflowed (0x%lx >= 0x400000)"),
-                bfd_get_filename (abfd),
-                (unsigned long) (max_short_vma - min_short_vma));
-             return FALSE;
-           }
-         else if ((gp_val > min_short_vma
-                   && gp_val - min_short_vma > 0x200000)
-                  || (gp_val < max_short_vma
-                      && max_short_vma - gp_val >= 0x200000))
-           {
-             (*_bfd_error_handler)
-               (_("%s: __gp does not cover short data segment"),
-                bfd_get_filename (abfd));
-             return FALSE;
-           }
+         if (! elfNN_ia64_choose_gp (abfd, info))
+           return FALSE;
+         gp_val = _bfd_get_gp_value (abfd);
        }
 
-      _bfd_set_gp_value (abfd, gp_val);
-
+      gp = elf_link_hash_lookup (elf_hash_table (info), "__gp", FALSE,
+                                FALSE, FALSE);
       if (gp)
        {
          gp->root.type = bfd_link_hash_defined;
@@ -3647,7 +3805,7 @@ elfNN_ia64_final_link (abfd, info)
      of the .IA_64.unwind section.  Force this section to be relocated
      into memory rather than written immediately to the output file.  */
   unwind_output_sec = NULL;
-  if (!info->relocateable)
+  if (!info->relocatable)
     {
       asection *s = bfd_get_section_by_name (abfd, ELF_STRING_ia64_unwind);
       if (s)
@@ -3705,7 +3863,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
   ia64_info = elfNN_ia64_hash_table (info);
 
   /* Infect various flags from the input section to the output section.  */
-  if (info->relocateable)
+  if (info->relocatable)
     {
       bfd_vma flags;
 
@@ -3735,6 +3893,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
       asection *sym_sec;
       bfd_byte *hit_addr;
       bfd_boolean dynamic_symbol_p;
+      bfd_boolean local_symbol_p;
       bfd_boolean undef_weak_ref;
 
       r_type = ELFNN_R_TYPE (rel->r_info);
@@ -3763,8 +3922,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
          value = _bfd_elf_rela_local_sym (output_bfd, sym, sym_sec, rel);
          if ((sym_sec->flags & SEC_MERGE)
              && ELF_ST_TYPE (sym->st_info) == STT_SECTION
-             && (elf_section_data (sym_sec)->sec_info_type
-                 == ELF_INFO_TYPE_MERGE))
+             && sym_sec->sec_info_type == ELF_INFO_TYPE_MERGE)
            {
              struct elfNN_ia64_local_hash_entry *loc_h;
 
@@ -3829,7 +3987,6 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
          else if (h->root.type == bfd_link_hash_undefweak)
            undef_weak_ref = TRUE;
          else if (info->shared
-                  && (!info->symbolic || info->allow_shlib_undefined)
                   && !info->no_undefined
                   && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
            ;
@@ -3841,7 +3998,6 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                      (!info->shared || info->no_undefined
                       || ELF_ST_VISIBILITY (h->other)))))
                return FALSE;
-             ret_val = FALSE;
              continue;
            }
        }
@@ -3849,6 +4005,11 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
       hit_addr = contents + rel->r_offset;
       value += rel->r_addend;
       dynamic_symbol_p = elfNN_ia64_dynamic_symbol_p (h, info);
+      /* Is this symbol locally defined? A protected symbol is locallly
+        defined. But its function descriptor may not. Use it with
+        caution.  */
+      local_symbol_p = (! dynamic_symbol_p
+                       || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT);
 
       switch (r_type)
        {
@@ -3881,7 +4042,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
              /* If we don't need dynamic symbol lookup, find a
                 matching RELATIVE relocation.  */
              dyn_r_type = r_type;
-             if (dynamic_symbol_p)
+             if (! local_symbol_p)
                {
                  dynindx = h->dynindx;
                  addend = rel->r_addend;
@@ -3927,7 +4088,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                                            srel, rel->r_offset, dyn_r_type,
                                            dynindx, addend);
            }
-         /* FALLTHRU */
+         /* Fall through.  */
 
        case R_IA64_LTV32MSB:
        case R_IA64_LTV32LSB:
@@ -4164,7 +4325,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                {
                  int i;
                  for (i = m->count - 1; i >= 0; i--)
-                   if (m->sections[i] == sym_sec->output_section)
+                   if (m->sections[i] == input_section->output_section)
                      break;
                  if (i >= 0)
                    break;
@@ -4210,7 +4371,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
 
              /* If we don't need dynamic symbol lookup, install two
                 RELATIVE relocations.  */
-             if (! dynamic_symbol_p)
+             if (local_symbol_p)
                {
                  unsigned int dyn_r_type;
 
@@ -4264,13 +4425,23 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_IA64_LTOFF_DTPREL22:
          {
            int got_r_type;
+           long dynindx = h ? h->dynindx : -1;
+           bfd_vma r_addend = rel->r_addend;
 
            switch (r_type)
              {
              default:
              case R_IA64_LTOFF_TPREL22:
-               if (!dynamic_symbol_p && !info->shared)
-                 value -= elfNN_ia64_tprel_base (info);
+               if (!dynamic_symbol_p)
+                 {
+                   if (!info->shared)
+                     value -= elfNN_ia64_tprel_base (info);
+                   else
+                     {
+                       r_addend += value - elfNN_ia64_dtprel_base (info);
+                       dynindx = 0;
+                     }
+                 }
                got_r_type = R_IA64_TPREL64LSB;
                break;
              case R_IA64_LTOFF_DTPMOD22:
@@ -4285,8 +4456,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                break;
              }
            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 = 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,
@@ -4528,7 +4698,7 @@ elfNN_ia64_finish_dynamic_sections (abfd, info)
          bfd_elfNN_swap_dyn_out (abfd, &dyn, dyncon);
        }
 
-      /* Initialize the PLT0 entry */
+      /* Initialize the PLT0 entry */
       if (ia64_info->plt_sec)
        {
          bfd_byte *loc = ia64_info->plt_sec->contents;
@@ -4547,7 +4717,7 @@ elfNN_ia64_finish_dynamic_sections (abfd, info)
   return TRUE;
 }
 \f
-/* ELF file flag handling: */
+/* ELF file flag handling:  */
 
 /* Function to keep IA-64 specific file flags.  */
 static bfd_boolean
This page took 0.038677 seconds and 4 git commands to generate.