* elf64-alpha.c (ALPHA_ELF_LINK_HASH_PLT_LOC): New.
authorRichard Henderson <rth@redhat.com>
Wed, 22 Jan 2003 00:47:58 +0000 (00:47 +0000)
committerRichard Henderson <rth@redhat.com>
Wed, 22 Jan 2003 00:47:58 +0000 (00:47 +0000)
        (struct alpha_elf_link_hash_entry): Add plt_old_section, plt_old_value.
        (elf64_alpha_adjust_dynamic_symbol): Set them.
        (elf64_alpha_size_plt_section_1): Reset them when plt entry removed.
        (elf64_alpha_relax_tls_get_addr): Handle LDM relocs.  Frob the
        symbol index when relaxing LDM to TPREL.
        (elf64_alpha_relax_section): Likewise.  Allow relaxation of GD
        relocs, even if the target isn't locally defined.
        (elf64_alpha_check_relocs): Frob LDM reloc symndx to zero.
        (elf64_alpha_relocate_section): Likewise.  Force TP-relative
        relocs vs symndx 0 to the tp base.

bfd/ChangeLog
bfd/elf64-alpha.c

index 6e1a3a142ab22e3933dd25a7def9d6621578327a..f292935c02b19fab63a38d25dae3d8ebcb705358 100644 (file)
@@ -1,3 +1,17 @@
+2003-01-21  Richard Henderson  <rth@redhat.com>
+
+       * elf64-alpha.c (ALPHA_ELF_LINK_HASH_PLT_LOC): New.
+       (struct alpha_elf_link_hash_entry): Add plt_old_section, plt_old_value.
+       (elf64_alpha_adjust_dynamic_symbol): Set them.
+       (elf64_alpha_size_plt_section_1): Reset them when plt entry removed.
+       (elf64_alpha_relax_tls_get_addr): Handle LDM relocs.  Frob the
+       symbol index when relaxing LDM to TPREL.
+       (elf64_alpha_relax_section): Likewise.  Allow relaxation of GD
+       relocs, even if the target isn't locally defined.
+       (elf64_alpha_check_relocs): Frob LDM reloc symndx to zero.
+       (elf64_alpha_relocate_section): Likewise.  Force TP-relative
+       relocs vs symndx 0 to the tp base.
+
 2003-01-21  Fabio Alemagna  <falemagn@aros.org>
 
        * config.bfd: Handle i[3456]86-*-aros*.
index 029c1d479068ddb8be2cce61c1b83742d290906c..9569b38eed70652bc1b1ad04beaaff6cf52df748 100644 (file)
@@ -177,6 +177,11 @@ struct alpha_elf_link_hash_entry
 #define ALPHA_ELF_LINK_HASH_LU_TLSLDM  0x20
 #define ALPHA_ELF_LINK_HASH_LU_FUNC    0x38
 #define ALPHA_ELF_LINK_HASH_TLS_IE     0x40
+#define ALPHA_ELF_LINK_HASH_PLT_LOC    0x80
+
+  /* Used to undo the localization of a plt symbol.  */
+  asection *plt_old_section;
+  bfd_vma plt_old_value;
 
   /* Used to implement multiple .got subsections.  */
   struct alpha_elf_got_entry
@@ -1753,16 +1758,10 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd)
   unsigned int insn;
   Elf_Internal_Rela *gpdisp, *hint;
   bfd_boolean dynamic, use_gottprel, pos1_unusable;
+  unsigned long new_symndx;
 
   dynamic = alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info);
 
-  /* ??? For LD relaxation, we need a symbol referencing the beginning
-     of the TLS segment.  */
-  /* ??? The STN_UNDEF symbol (dynindex 0) works fine for this.  Adjust
-     the code below to expect that.  */
-  if (!is_gd)
-    return TRUE;
-
   /* If a TLS symbol is accessed using IE at least once, there is no point
      to use dynamic model for it.  */
   if (is_gd && info->h && (info->h->flags & ALPHA_ELF_LINK_HASH_TLS_IE))
@@ -1868,6 +1867,7 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd)
      as appropriate.  */
 
   use_gottprel = FALSE;
+  new_symndx = is_gd ? ELF64_R_SYM (irel->r_info) : 0;
   switch (!dynamic && !info->link_info->shared)
     {
     case 1:
@@ -1886,8 +1886,7 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd)
            bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos[1]);
 
            irel[0].r_offset = pos[0] - info->contents;
-           irel[0].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
-                                          R_ALPHA_TPREL16);
+           irel[0].r_info = ELF64_R_INFO (new_symndx, R_ALPHA_TPREL16);
            irel[1].r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
            break;
          }
@@ -1901,11 +1900,9 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd)
            bfd_put_32 (info->abfd, (bfd_vma) insn, pos[1]);
 
            irel[0].r_offset = pos[0] - info->contents;
-           irel[0].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
-                                          R_ALPHA_TPRELHI);
+           irel[0].r_info = ELF64_R_INFO (new_symndx, R_ALPHA_TPRELHI);
            irel[1].r_offset = pos[1] - info->contents;
-           irel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
-                                          R_ALPHA_TPRELLO);
+           irel[1].r_info = ELF64_R_INFO (new_symndx, R_ALPHA_TPRELLO);
            break;
          }
       }
@@ -1919,8 +1916,7 @@ elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd)
       bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos[1]);
 
       irel[0].r_offset = pos[0] - info->contents;
-      irel[0].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
-                                    R_ALPHA_GOTTPREL);
+      irel[0].r_info = ELF64_R_INFO (new_symndx, R_ALPHA_GOTTPREL);
       irel[1].r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
       break;
     }
@@ -2123,6 +2119,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
       bfd_vma symval;
       struct alpha_elf_got_entry *gotent;
       unsigned long r_type = ELF64_R_TYPE (irel->r_info);
+      unsigned long r_symndx = ELF64_R_SYM (irel->r_info);
 
       /* Early exit for unhandled or unrelaxable relocations.  */
       switch (r_type)
@@ -2133,14 +2130,20 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
        case R_ALPHA_GOTDTPREL:
        case R_ALPHA_GOTTPREL:
        case R_ALPHA_TLSGD:
+         break;
+
        case R_ALPHA_TLSLDM:
+         /* The symbol for a TLSLDM reloc is ignored.  Collapse the
+             reloc to the 0 symbol so that they all match.  */
+         r_symndx = 0;
          break;
+
        default:
          continue;
        }
 
       /* Get the value of the symbol referred to by the reloc.  */
-      if (ELF64_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+      if (r_symndx < symtab_hdr->sh_info)
        {
          /* A local symbol.  */
          Elf_Internal_Sym *isym;
@@ -2157,27 +2160,38 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
                goto error_return;
            }
 
-         isym = isymbuf + ELF64_R_SYM (irel->r_info);
-         if (isym->st_shndx == SHN_UNDEF)
-           continue;
-         else if (isym->st_shndx == SHN_ABS)
-           info.tsec = bfd_abs_section_ptr;
-         else if (isym->st_shndx == SHN_COMMON)
-           info.tsec = bfd_com_section_ptr;
+         isym = isymbuf + r_symndx;
+
+         /* Given the symbol for a TLSLDM reloc is ignored, this also
+            means forcing the symbol value to the tp base.  */
+         if (r_type == R_ALPHA_TLSLDM)
+           {
+             info.tsec = bfd_abs_section_ptr;
+             symval = alpha_get_tprel_base (info.tls_segment);
+           }
          else
-           info.tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+           {
+             symval = isym->st_value;
+             if (isym->st_shndx == SHN_UNDEF)
+               continue;
+             else if (isym->st_shndx == SHN_ABS)
+               info.tsec = bfd_abs_section_ptr;
+             else if (isym->st_shndx == SHN_COMMON)
+               info.tsec = bfd_com_section_ptr;
+             else
+               info.tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+           }
 
          info.h = NULL;
          info.other = isym->st_other;
-         info.first_gotent = &local_got_entries[ELF64_R_SYM(irel->r_info)];
-         symval = isym->st_value;
+         info.first_gotent = &local_got_entries[r_symndx];
        }
       else
        {
          unsigned long indx;
          struct alpha_elf_link_hash_entry *h;
 
-         indx = ELF64_R_SYM (irel->r_info) - symtab_hdr->sh_info;
+         indx = r_symndx - symtab_hdr->sh_info;
          h = alpha_elf_sym_hashes (abfd)[indx];
          BFD_ASSERT (h != NULL);
 
@@ -2193,13 +2207,23 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
          /* If the symbol isn't defined in the current module, again
             we can't do anything.  */
          if (!(h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))
-           continue;
+           {
+             /* Except for TLSGD relocs, which can sometimes be
+                relaxed to GOTTPREL relocs.  */
+             if (r_type != R_ALPHA_TLSGD)
+               continue;
+             info.tsec = bfd_abs_section_ptr;
+             symval = 0;
+           }
+         else
+           {
+             info.tsec = h->root.root.u.def.section;
+             symval = h->root.root.u.def.value;
+           }
 
          info.h = h;
-         info.tsec = h->root.root.u.def.section;
          info.other = h->root.other;
          info.first_gotent = &h->got_entries;
-         symval = h->root.root.u.def.value;
        }
 
       /* Search for the got entry to be used by this relocation.  */
@@ -3117,8 +3141,15 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
            need = NEED_DYNREL;
          break;
 
-       case R_ALPHA_TLSGD:
        case R_ALPHA_TLSLDM:
+         /* The symbol for a TLSLDM reloc is ignored.  Collapse the
+            reloc to the 0 symbol so that they all match.  */
+         r_symndx = 0;
+         h = 0;
+         maybe_dynamic = FALSE;
+         /* FALLTHRU */
+
+       case R_ALPHA_TLSGD:
        case R_ALPHA_GOTDTPREL:
          need = NEED_GOT | NEED_GOT_ENTRY;
          break;
@@ -3320,6 +3351,9 @@ elf64_alpha_adjust_dynamic_symbol (info, h)
       if (! info->shared
          && h->root.type != bfd_link_hash_defweak)
        {
+         ah->plt_old_section = h->root.u.def.section;
+         ah->plt_old_value = h->root.u.def.value;
+         ah->flags |= ALPHA_ELF_LINK_HASH_PLT_LOC;
          h->root.u.def.section = s;
          h->root.u.def.value = h->plt.offset;
        }
@@ -3801,6 +3835,14 @@ elf64_alpha_size_plt_section_1 (h, data)
     {
       h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
       h->root.plt.offset = -1;
+
+      /* Undo the definition frobbing begun in adjust_dynamic_symbol.  */
+      if (h->flags & ALPHA_ELF_LINK_HASH_PLT_LOC)
+       {
+         h->root.root.u.def.section = h->plt_old_section;
+         h->root.root.u.def.value = h->plt_old_value;
+         h->flags &= ~ALPHA_ELF_LINK_HASH_PLT_LOC;
+       }
     }
 
   return TRUE;
@@ -4364,12 +4406,29 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
       howto = elf64_alpha_howto_table + r_type;
       r_symndx = ELF64_R_SYM(rel->r_info);
 
+      /* The symbol for a TLSLDM reloc is ignored.  Collapse the
+        reloc to the 0 symbol so that they all match.  */
+      if (r_type == R_ALPHA_TLSLDM)
+       r_symndx = 0;
+
       if (r_symndx < symtab_hdr->sh_info)
        {
          sym = local_syms + r_symndx;
          sec = local_sections[r_symndx];
          value = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
 
+         /* If this is a tp-relative relocation against sym 0,
+            this is hackery from relax_section.  Force the value to
+            be the tls base.  */
+         if (r_symndx == 0
+             && (r_type == R_ALPHA_TLSLDM
+                 || r_type == R_ALPHA_GOTTPREL
+                 || r_type == R_ALPHA_TPREL64
+                 || r_type == R_ALPHA_TPRELHI
+                 || r_type == R_ALPHA_TPRELLO
+                 || r_type == R_ALPHA_TPREL16))
+           value = tp_base;
+
          if (local_got_entries)
            gotent = local_got_entries[r_symndx];
          else