include/elf/
authorRichard Henderson <rth@redhat.com>
Thu, 30 May 2002 22:01:38 +0000 (22:01 +0000)
committerRichard Henderson <rth@redhat.com>
Thu, 30 May 2002 22:01:38 +0000 (22:01 +0000)
        * alpha.h (R_ALPHA_TLSGD, R_ALPHA_TLSLDM, R_ALPHA_DTPMOD64,
        R_ALPHA_GOTDTPREL, R_ALPHA_DTPREL64, R_ALPHA_DTPRELHI,
        R_ALPHA_DTPRELLO, R_ALPHA_DTPREL16, R_ALPHA_GOTTPREL, R_ALPHA_TPREL64,
        R_ALPHA_TPRELHI, R_ALPHA_TPRELLO, R_ALPHA_TPREL16): New.

bfd/
        * elf64-alpha.c (ALPHA_ELF_LINK_HASH_LU_TLSGD,
        ALPHA_ELF_LINK_HASH_LU_TLSLDM, ALPHA_ELF_LINK_HASH_LU_FUNC): New.
        (ALPHA_ELF_GOT_ENTRY_RELOCS_DONE): Remove.
        (ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED): Remove.
        (struct alpha_elf_got_entry): Add reloc_type, reloc_done, reloc_xlated.
        (struct alpha_elf_obj_tdata): Rename total_got_entries and
        n_local_got_entries to total_got_size and local_got_size.
        (elf64_alpha_howto, elf64_alpha_reloc_map): Update for TLS relocs.
        (alpha_got_entry_size): New.
        (elf64_alpha_relax_with_lituse): Use it.
        (elf64_alpha_relax_without_lituse): Likewise.
        (MAX_GOT_SIZE): Rename from MAX_GOT_ENTRIES.
        (get_got_entry): New.
        (elf64_alpha_check_relocs): Handle TLS relocs.  Reorganize.
        (elf64_alpha_adjust_dynamic_symbol): Test LU_FUNC as a mask.
        (elf64_alpha_merge_ind_symbols): Check gotent->reloc_type.
        (elf64_alpha_can_merge_gots, elf64_alpha_merge_gots): Likewise.
        (elf64_alpha_calc_got_offsets_for_symbol): Use alpha_got_entry_size.
        (elf64_alpha_calc_got_offsets): Likewise.
        (alpha_dynamic_entries_for_reloc): New.
        (elf64_alpha_calc_dynrel_sizes): Use it.
        (elf64_alpha_size_dynamic_sections): Likewise.
        (elf64_alpha_relocate_section): Handle TLS relocations.
        * reloc.c: Add Alpha TLS relocations.
        * bfd-in2.h, libbfd.h: Rebuild.

gas/
        * expr.h (operatorT): Add O_md17..O_md32.
        * config/tc-alpha.c (O_lituse_tlsgd, O_lituse_tlsldm, O_tlsgd,
        O_tlsldm, O_gotdtprel, O_dtprelhi, O_dtprello, O_dtprel, O_gottprel,
        O_tprelhi, O_tprello, O_tprel): New.
        (USER_RELOC_P, alpha_reloc_op_tag, debug_exp): Include them.
        (DUMMY_RELOC_LITUSE_TLSGD, DUMMY_RELOC_LITUSE_TLSLDM): New.
        (LITUSE_TLSGD, LITUSE_TLSLDM): New.
        (struct alpha_reloc_tag): Add master, saw_tlsgd, saw_tlsld,
        saw_lu_tlsgd, saw_lu_tlsldm.  Make multi_section_p a bit field.
        (md_apply_fix3): Handle TLS relocations.
        (alpha_force_relocation, alpha_fix_adjustable): Likewise.
        (alpha_adjust_symtab_relocs): Sort LITERAL relocs after the
        associated TLS reloc.  Check lituse_tls relocs match up.
        (emit_insn): Handle TLS relocations.
        (ldX_op): Remove.

gas/testsuite/
        * gas/alpha/elf-tls-1.s, gas/alpha/elf-tls-1.d: New.
        * gas/alpha/elf-tls-2.s, gas/alpha/elf-tls-1.l: New.
        * gas/alpha/elf-tls-3.s, gas/alpha/elf-tls-1.l: New.
        * gas/alpha/alpha.exp: Run them.

18 files changed:
bfd/ChangeLog
bfd/bfd-in2.h
bfd/elf64-alpha.c
bfd/libbfd.h
bfd/reloc.c
gas/ChangeLog
gas/config/tc-alpha.c
gas/expr.h
gas/testsuite/ChangeLog
gas/testsuite/gas/alpha/alpha.exp
gas/testsuite/gas/alpha/elf-tls-1.d [new file with mode: 0644]
gas/testsuite/gas/alpha/elf-tls-1.s [new file with mode: 0644]
gas/testsuite/gas/alpha/elf-tls-2.l [new file with mode: 0644]
gas/testsuite/gas/alpha/elf-tls-2.s [new file with mode: 0644]
gas/testsuite/gas/alpha/elf-tls-3.l [new file with mode: 0644]
gas/testsuite/gas/alpha/elf-tls-3.s [new file with mode: 0644]
include/elf/ChangeLog
include/elf/alpha.h

index c1e6aad..9cc7518 100644 (file)
@@ -1,3 +1,31 @@
+2002-05-30  Richard Henderson  <rth@redhat.com>
+
+       * elf64-alpha.c (ALPHA_ELF_LINK_HASH_LU_TLSGD,
+       ALPHA_ELF_LINK_HASH_LU_TLSLDM, ALPHA_ELF_LINK_HASH_LU_FUNC): New.
+       (ALPHA_ELF_GOT_ENTRY_RELOCS_DONE): Remove.
+       (ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED): Remove.
+       (struct alpha_elf_got_entry): Add reloc_type, reloc_done, reloc_xlated.
+       (struct alpha_elf_obj_tdata): Rename total_got_entries and
+       n_local_got_entries to total_got_size and local_got_size.
+       (elf64_alpha_howto, elf64_alpha_reloc_map): Update for TLS relocs.
+       (alpha_got_entry_size): New.
+       (elf64_alpha_relax_with_lituse): Use it.
+       (elf64_alpha_relax_without_lituse): Likewise.
+       (MAX_GOT_SIZE): Rename from MAX_GOT_ENTRIES.
+       (get_got_entry): New.
+       (elf64_alpha_check_relocs): Handle TLS relocs.  Reorganize.
+       (elf64_alpha_adjust_dynamic_symbol): Test LU_FUNC as a mask.
+       (elf64_alpha_merge_ind_symbols): Check gotent->reloc_type.
+       (elf64_alpha_can_merge_gots, elf64_alpha_merge_gots): Likewise.
+       (elf64_alpha_calc_got_offsets_for_symbol): Use alpha_got_entry_size.
+       (elf64_alpha_calc_got_offsets): Likewise.
+       (alpha_dynamic_entries_for_reloc): New.
+       (elf64_alpha_calc_dynrel_sizes): Use it.
+       (elf64_alpha_size_dynamic_sections): Likewise.
+       (elf64_alpha_relocate_section): Handle TLS relocations.
+       * reloc.c: Add Alpha TLS relocations.
+       * bfd-in2.h, libbfd.h: Rebuild.
+
 2002-05-29 Ralf Habacker <ralf.habacker@freenet.de>
 
        * peXXigen.c (pe_print_idata): Remove double printed
index 6d82737..000fee6 100644 (file)
@@ -2147,6 +2147,21 @@ share a common GP, and the target address is adjusted for
 STO_ALPHA_STD_GPLOAD.  */
   BFD_RELOC_ALPHA_BRSGP,
 
+/* Alpha thread-local storage relocations.  */
+  BFD_RELOC_ALPHA_TLSGD,
+  BFD_RELOC_ALPHA_TLSLDM,
+  BFD_RELOC_ALPHA_DTPMOD64,
+  BFD_RELOC_ALPHA_GOTDTPREL16,
+  BFD_RELOC_ALPHA_DTPREL64,
+  BFD_RELOC_ALPHA_DTPREL_HI16,
+  BFD_RELOC_ALPHA_DTPREL_LO16,
+  BFD_RELOC_ALPHA_DTPREL16,
+  BFD_RELOC_ALPHA_GOTTPREL16,
+  BFD_RELOC_ALPHA_TPREL64,
+  BFD_RELOC_ALPHA_TPREL_HI16,
+  BFD_RELOC_ALPHA_TPREL_LO16,
+  BFD_RELOC_ALPHA_TPREL16,
+
 /* Bits 27..2 of the relocation address shifted right 2 bits;
 simple reloc otherwise.  */
   BFD_RELOC_MIPS_JMP,
index 94b4ebe..7099ba5 100644 (file)
@@ -109,11 +109,16 @@ static boolean elf64_alpha_size_got_sections
   PARAMS ((bfd *, struct bfd_link_info *));
 static boolean elf64_alpha_always_size_sections
   PARAMS ((bfd *, struct bfd_link_info *));
+static int alpha_dynamic_entries_for_reloc
+  PARAMS ((int, int, int));
 static boolean elf64_alpha_calc_dynrel_sizes
   PARAMS ((struct alpha_elf_link_hash_entry *, struct bfd_link_info *));
 static boolean elf64_alpha_add_symbol_hook
   PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym *,
           const char **, flagword *, asection **, bfd_vma *));
+static struct alpha_elf_got_entry *get_got_entry
+  PARAMS ((bfd *, struct alpha_elf_link_hash_entry *, unsigned long,
+          unsigned long, bfd_vma));
 static boolean elf64_alpha_check_relocs
   PARAMS((bfd *, struct bfd_link_info *, asection *sec,
          const Elf_Internal_Rela *));
@@ -149,10 +154,13 @@ struct alpha_elf_link_hash_entry
   int flags;
 
   /* Contexts (LITUSE) in which a literal was referenced.  */
-#define ALPHA_ELF_LINK_HASH_LU_ADDR 0x01
-#define ALPHA_ELF_LINK_HASH_LU_MEM  0x02
-#define ALPHA_ELF_LINK_HASH_LU_BYTE 0x04
-#define ALPHA_ELF_LINK_HASH_LU_FUNC 0x08
+#define ALPHA_ELF_LINK_HASH_LU_ADDR    0x01
+#define ALPHA_ELF_LINK_HASH_LU_MEM     0x02
+#define ALPHA_ELF_LINK_HASH_LU_BYTE    0x04
+#define ALPHA_ELF_LINK_HASH_LU_JSR     0x08
+#define ALPHA_ELF_LINK_HASH_LU_TLSGD   0x10
+#define ALPHA_ELF_LINK_HASH_LU_TLSLDM  0x20
+#define ALPHA_ELF_LINK_HASH_LU_FUNC    0x38
 
   /* Used to implement multiple .got subsections.  */
   struct alpha_elf_got_entry
@@ -168,13 +176,20 @@ struct alpha_elf_link_hash_entry
     /* the .got offset for this entry.  */
     int got_offset;
 
-    int flags;
+    /* How many references to this entry?  */
+    int use_count;
 
-    /* Additional flags.  */
-#define ALPHA_ELF_GOT_ENTRY_RELOCS_DONE 0x10
-#define ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED 0x20
+    /* The relocation type of this entry.  */
+    unsigned char reloc_type;
 
-    int use_count;
+    /* How a LITERAL is used.  */
+    unsigned char flags;
+
+    /* Have we initialized the dynamic relocation for this entry?  */
+    unsigned char reloc_done;
+
+    /* Have we adjusted this entry for SEC_MERGE?  */
+    unsigned char reloc_xlated;
   } *got_entries;
 
   /* used to count non-got, non-plt relocations for delayed sizing
@@ -361,12 +376,12 @@ struct alpha_elf_obj_tdata
   /* For every got, this is the section.  */
   asection *got;
 
-  /* For every got, this is it's total number of *entries*.  */
-  int total_got_entries;
+  /* For every got, this is it's total number of words.  */
+  int total_got_size;
 
-  /* For every got, this is the sum of the number of *entries* required
+  /* For every got, this is the sum of the number of words required
      to hold all of the member object's local got.  */
-  int n_local_got_entries;
+  int local_got_size;
 };
 
 #define alpha_elf_tdata(abfd) \
@@ -748,6 +763,203 @@ static reloc_howto_type elf64_alpha_howto_table[] =
         0x1fffff,              /* src_mask */
         0x1fffff,              /* dst_mask */
         true),                 /* pcrel_offset */
+
+  /* Creates a tls_index for the symbol in the got.  */
+  HOWTO (R_ALPHA_TLSGD,                /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TLSGD",               /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Creates a tls_index for the (current) module in the got.  */
+  HOWTO (R_ALPHA_TLSLDM,       /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TLSLDM",              /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A dynamic relocation for a DTP module entry.  */
+  HOWTO (R_ALPHA_DTPMOD64,     /* type */
+        0,                     /* rightshift */
+        4,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPMOD64",            /* name */
+        false,                 /* partial_inplace */
+        MINUS_ONE,             /* src_mask */
+        MINUS_ONE,             /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Creates a 64-bit offset in the got for the displacement
+     from DTP to the target.  */
+  HOWTO (R_ALPHA_GOTDTPREL,    /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "GOTDTPREL",           /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A dynamic relocation for a displacement from DTP to the target.  */
+  HOWTO (R_ALPHA_DTPREL64,     /* type */
+        0,                     /* rightshift */
+        4,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPREL64",            /* name */
+        false,                 /* partial_inplace */
+        MINUS_ONE,             /* src_mask */
+        MINUS_ONE,             /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* The high 16 bits of the displacement from DTP to the target.  */
+  HOWTO (R_ALPHA_DTPRELHI,     /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPRELHI",            /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* The low 16 bits of the displacement from DTP to the target.  */
+  HOWTO (R_ALPHA_DTPRELLO,     /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPRELLO",            /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A 16-bit displacement from DTP to the target.  */
+  HOWTO (R_ALPHA_DTPREL16,     /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPREL16",            /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Creates a 64-bit offset in the got for the displacement
+     from TP to the target.  */
+  HOWTO (R_ALPHA_GOTTPREL,     /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "GOTTPREL",            /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A dynamic relocation for a displacement from TP to the target.  */
+  HOWTO (R_ALPHA_TPREL64,      /* type */
+        0,                     /* rightshift */
+        4,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TPREL64",             /* name */
+        false,                 /* partial_inplace */
+        MINUS_ONE,             /* src_mask */
+        MINUS_ONE,             /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* The high 16 bits of the displacement from TP to the target.  */
+  HOWTO (R_ALPHA_TPRELHI,      /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TPRELHI",             /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* The low 16 bits of the displacement from TP to the target.  */
+  HOWTO (R_ALPHA_TPRELLO,      /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TPRELLO",             /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A 16-bit displacement from TP to the target.  */
+  HOWTO (R_ALPHA_TPREL16,      /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TPREL16",             /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
 };
 
 /* A relocation function which doesn't do anything.  */
@@ -902,6 +1114,19 @@ static const struct elf_reloc_map elf64_alpha_reloc_map[] =
   {BFD_RELOC_ALPHA_GPREL_LO16,         R_ALPHA_GPRELLOW},
   {BFD_RELOC_GPREL16,                  R_ALPHA_GPREL16},
   {BFD_RELOC_ALPHA_BRSGP,              R_ALPHA_BRSGP},
+  {BFD_RELOC_ALPHA_TLSGD,              R_ALPHA_TLSGD},
+  {BFD_RELOC_ALPHA_TLSLDM,             R_ALPHA_TLSLDM},
+  {BFD_RELOC_ALPHA_DTPMOD64,           R_ALPHA_DTPMOD64},
+  {BFD_RELOC_ALPHA_GOTDTPREL16,                R_ALPHA_GOTDTPREL},
+  {BFD_RELOC_ALPHA_DTPREL64,           R_ALPHA_DTPREL64},
+  {BFD_RELOC_ALPHA_DTPREL_HI16,                R_ALPHA_DTPRELHI},
+  {BFD_RELOC_ALPHA_DTPREL_LO16,                R_ALPHA_DTPRELLO},
+  {BFD_RELOC_ALPHA_DTPREL16,           R_ALPHA_DTPREL16},
+  {BFD_RELOC_ALPHA_GOTTPREL16,         R_ALPHA_GOTTPREL},
+  {BFD_RELOC_ALPHA_TPREL64,            R_ALPHA_TPREL64},
+  {BFD_RELOC_ALPHA_TPREL_HI16,         R_ALPHA_TPRELHI},
+  {BFD_RELOC_ALPHA_TPREL_LO16,         R_ALPHA_TPRELLO},
+  {BFD_RELOC_ALPHA_TPREL16,            R_ALPHA_TPREL16},
 };
 
 /* Given a BFD reloc type, return a HOWTO structure.  */
@@ -936,6 +1161,10 @@ elf64_alpha_info_to_howto (abfd, cache_ptr, dst)
   BFD_ASSERT (r_type < (unsigned int) R_ALPHA_max);
   cache_ptr->howto = &elf64_alpha_howto_table[r_type];
 }
+
+/* These two relocations create a two-word entry in the got.  */
+#define alpha_got_entry_size(r_type) \
+  (r_type == R_ALPHA_TLSGD || r_type == R_ALPHA_TLSLDM ? 16 : 8)
 \f
 /* These functions do relaxation for Alpha ELF.
 
@@ -1213,14 +1442,17 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
      got entry by one, possibly eliminating it.  */
   if (all_optimized)
     {
-      info->gotent->use_count -= 1;
-      alpha_elf_tdata (info->gotent->gotobj)->total_got_entries -= 1;
-      if (!info->h)
-       alpha_elf_tdata (info->gotent->gotobj)->n_local_got_entries -= 1;
+      if (--info->gotent->use_count == 0)
+       {
+         int sz = alpha_got_entry_size (info->gotent->reloc_type);
+         alpha_elf_tdata (info->gotent->gotobj)->total_got_size -= sz;
+         if (!info->h)
+           alpha_elf_tdata (info->gotent->gotobj)->local_got_size -= sz;
+       }
 
       /* If the literal instruction is no longer needed (it may have been
-        reused.  We can eliminate it.
-        ??? For now, I don't want to deal with compacting the section,
+        reused.  We can eliminate it.  */
+      /* ??? For now, I don't want to deal with compacting the section,
         so just nop it out.  */
       if (!lit_reused)
        {
@@ -1349,10 +1581,13 @@ elf64_alpha_relax_without_lituse (info, symval, irel)
 
   /* Reduce the use count on this got entry by one, possibly
      eliminating it.  */
-  info->gotent->use_count -= 1;
-  alpha_elf_tdata (info->gotent->gotobj)->total_got_entries -= 1;
-  if (!info->h)
-    alpha_elf_tdata (info->gotent->gotobj)->n_local_got_entries -= 1;
+  if (--info->gotent->use_count == 0)
+    {
+      int sz = alpha_got_entry_size (info->gotent->reloc_type);
+      alpha_elf_tdata (info->gotent->gotobj)->total_got_size -= sz;
+      if (!info->h)
+       alpha_elf_tdata (info->gotent->gotobj)->local_got_size -= sz;
+    }
 
   /* ??? Search forward through this basic block looking for insns
      that use the target register.  Stop after an insn modifying the
@@ -1643,7 +1878,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
 #define PLT_ENTRY_WORD2                0
 #define PLT_ENTRY_WORD3                0
 
-#define MAX_GOT_ENTRIES                (64*1024 / 8)
+#define MAX_GOT_SIZE           (64*1024)
 
 #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so"
 \f
@@ -2256,6 +2491,86 @@ mips_elf_create_procedure_table (handle, abfd, info, s, debug)
      struct ecoff_debug_info *debug;
 */
 \f
+/* Search for and possibly create a got entry.  */
+
+static struct alpha_elf_got_entry *
+get_got_entry (abfd, h, r_type, r_symndx, r_addend)
+     bfd *abfd;
+     struct alpha_elf_link_hash_entry *h;
+     unsigned long r_type, r_symndx;
+     bfd_vma r_addend;
+{
+  struct alpha_elf_got_entry *gotent;
+  struct alpha_elf_got_entry **slot;
+
+  if (h)
+    slot = &h->got_entries;
+  else
+    {
+      /* This is a local .got entry -- record for merge.  */
+
+      struct alpha_elf_got_entry **local_got_entries;
+
+      local_got_entries = alpha_elf_tdata(abfd)->local_got_entries;
+      if (!local_got_entries)
+       {
+         bfd_size_type size;
+         Elf_Internal_Shdr *symtab_hdr;
+
+         symtab_hdr = &elf_tdata(abfd)->symtab_hdr;
+         size = symtab_hdr->sh_info;
+         size *= sizeof (struct alpha_elf_got_entry *);
+
+         local_got_entries
+           = (struct alpha_elf_got_entry **) bfd_alloc (abfd, size);
+         if (!local_got_entries)
+           return NULL;
+
+         memset (local_got_entries, 0, (size_t) size);
+         alpha_elf_tdata (abfd)->local_got_entries = local_got_entries;
+       }
+
+      slot = &local_got_entries[r_symndx];
+    }
+
+  for (gotent = *slot; gotent ; gotent = gotent->next)
+    if (gotent->gotobj == abfd
+       && gotent->reloc_type == r_type
+       && gotent->addend == r_addend)
+      break;
+
+  if (!gotent)
+    {
+      int entry_size;
+      bfd_size_type amt;
+
+      amt = sizeof (struct alpha_elf_got_entry);
+      gotent = (struct alpha_elf_got_entry *) bfd_alloc (abfd, amt);
+      if (!gotent)
+       return NULL;
+
+      gotent->gotobj = abfd;
+      gotent->addend = r_addend;
+      gotent->got_offset = -1;
+      gotent->use_count = 1;
+      gotent->reloc_type = r_type;
+      gotent->reloc_done = 0;
+      gotent->reloc_xlated = 0;
+
+      gotent->next = *slot;
+      *slot = gotent;
+
+      entry_size = alpha_got_entry_size (r_type);
+      alpha_elf_tdata (abfd)->total_got_size += entry_size;
+      if (!h)
+       alpha_elf_tdata(abfd)->local_got_size += entry_size;
+    }
+  else
+    gotent->use_count += 1;
+
+  return gotent;
+}
+
 /* Handle dynamic relocations when doing an Alpha ELF link.  */
 
 static boolean
@@ -2270,9 +2585,8 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
   const char *rel_sec_name;
   Elf_Internal_Shdr *symtab_hdr;
   struct alpha_elf_link_hash_entry **sym_hashes;
-  struct alpha_elf_got_entry **local_got_entries;
   const Elf_Internal_Rela *rel, *relend;
-  int got_created;
+  boolean got_created;
   bfd_size_type amt;
 
   if (info->relocateable)
@@ -2286,14 +2600,23 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
   rel_sec_name = NULL;
   symtab_hdr = &elf_tdata(abfd)->symtab_hdr;
   sym_hashes = alpha_elf_sym_hashes(abfd);
-  local_got_entries = alpha_elf_tdata(abfd)->local_got_entries;
-  got_created = 0;
+  got_created = false;
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; ++rel)
     {
+      enum {
+       NEED_GOT = 1,
+       NEED_GOT_ENTRY = 2,
+       NEED_DYNREL = 4
+      };
+
       unsigned long r_symndx, r_type;
       struct alpha_elf_link_hash_entry *h;
+      unsigned int gotent_flags;
+      boolean maybe_dynamic;
+      unsigned int need;
+      bfd_vma addend;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -2308,125 +2631,40 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
 
          h->root.elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR;
        }
+
+      /* 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->shared
+                && (!info->symbolic || info->allow_shlib_undefined))
+               || ! (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)
+               || h->root.type == bfd_link_hash_defweak))
+        maybe_dynamic = true;
+
+      need = 0;
+      gotent_flags = 0;
       r_type = ELF64_R_TYPE (rel->r_info);
+      addend = rel->r_addend;
 
       switch (r_type)
        {
        case R_ALPHA_LITERAL:
-         {
-           struct alpha_elf_got_entry *gotent;
-           int flags = 0;
-
-           if (h)
-             {
-               /* Search for and possibly create a got entry.  */
-               for (gotent = h->got_entries; gotent ; gotent = gotent->next)
-                 if (gotent->gotobj == abfd &&
-                     gotent->addend == rel->r_addend)
-                   break;
-
-               if (!gotent)
-                 {
-                   amt = sizeof (struct alpha_elf_got_entry);
-                   gotent = ((struct alpha_elf_got_entry *)
-                             bfd_alloc (abfd, amt));
-                   if (!gotent)
-                     return false;
-
-                   gotent->gotobj = abfd;
-                   gotent->addend = rel->r_addend;
-                   gotent->got_offset = -1;
-                   gotent->flags = 0;
-                   gotent->use_count = 1;
-
-                   gotent->next = h->got_entries;
-                   h->got_entries = gotent;
-
-                   alpha_elf_tdata (abfd)->total_got_entries++;
-                 }
-               else
-                 gotent->use_count += 1;
-             }
-           else
-             {
-               /* This is a local .got entry -- record for merge.  */
-               if (!local_got_entries)
-                 {
-                   bfd_size_type size;
-                   size = symtab_hdr->sh_info;
-                   size *= sizeof (struct alpha_elf_got_entry *);
-
-                   local_got_entries = ((struct alpha_elf_got_entry **)
-                                        bfd_alloc (abfd, size));
-                   if (!local_got_entries)
-                     return false;
-
-                   memset (local_got_entries, 0, (size_t) size);
-                   alpha_elf_tdata (abfd)->local_got_entries =
-                     local_got_entries;
-                 }
-
-               for (gotent = local_got_entries[ELF64_R_SYM(rel->r_info)];
-                    gotent != NULL && gotent->addend != rel->r_addend;
-                    gotent = gotent->next)
-                 continue;
-               if (!gotent)
-                 {
-                   amt = sizeof (struct alpha_elf_got_entry);
-                   gotent = ((struct alpha_elf_got_entry *)
-                             bfd_alloc (abfd, amt));
-                   if (!gotent)
-                     return false;
-
-                   gotent->gotobj = abfd;
-                   gotent->addend = rel->r_addend;
-                   gotent->got_offset = -1;
-                   gotent->flags = 0;
-                   gotent->use_count = 1;
-
-                   gotent->next = local_got_entries[ELF64_R_SYM(rel->r_info)];
-                   local_got_entries[ELF64_R_SYM(rel->r_info)] = gotent;
-
-                   alpha_elf_tdata(abfd)->total_got_entries++;
-                   alpha_elf_tdata(abfd)->n_local_got_entries++;
-                 }
-               else
-                 gotent->use_count += 1;
-             }
-
-           /* Remember how this literal is used from its LITUSEs.
-              This will be important when it comes to decide if we can
-              create a .plt entry for a function symbol.  */
-           if (rel+1 < relend
-               && ELF64_R_TYPE (rel[1].r_info) == R_ALPHA_LITUSE)
-             {
-               do
-                 {
-                   ++rel;
-                   if (rel->r_addend >= 1 && rel->r_addend <= 3)
-                     flags |= 1 << rel->r_addend;
-                 }
-               while (rel+1 < relend &&
-                      ELF64_R_TYPE (rel[1].r_info) == R_ALPHA_LITUSE);
-             }
-           else
-             {
-               /* No LITUSEs -- presumably the address is not being
-                  loaded for nothing.  */
-               flags = ALPHA_ELF_LINK_HASH_LU_ADDR;
-             }
-
-           gotent->flags |= flags;
-           if (h)
-             {
-               /* Make a guess as to whether a .plt entry will be needed.  */
-               if ((h->flags |= flags) == ALPHA_ELF_LINK_HASH_LU_FUNC)
-                 h->root.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
-               else
-                 h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
-             }
-         }
-         /* FALLTHRU */
+         need = NEED_GOT | NEED_GOT_ENTRY;
+
+         /* Remember how this literal is used from its LITUSEs.
+            This will be important when it comes to decide if we can
+            create a .plt entry for a function symbol.  */
+         while (++rel < relend && ELF64_R_TYPE (rel->r_info) == R_ALPHA_LITUSE)
+           if (rel->r_addend >= 1 && rel->r_addend <= 5)
+             gotent_flags |= 1 << rel->r_addend;
+         --rel;
+
+         /* No LITUSEs -- presumably the address is used somehow.  */
+         if (gotent_flags == 0)
+           gotent_flags = ALPHA_ELF_LINK_HASH_LU_ADDR;
+         break;
 
        case R_ALPHA_GPDISP:
        case R_ALPHA_GPREL16:
@@ -2434,9 +2672,37 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
        case R_ALPHA_GPRELHIGH:
        case R_ALPHA_GPRELLOW:
        case R_ALPHA_BRSGP:
-         /* We don't actually use the .got here, but the sections must
-            be created before the linker maps input sections to output
-            sections.  */
+         need = NEED_GOT;
+         break;
+
+       case R_ALPHA_REFLONG:
+       case R_ALPHA_REFQUAD:
+         if (info->shared || maybe_dynamic)
+           need = NEED_DYNREL;
+         break;
+
+       case R_ALPHA_TLSGD:
+       case R_ALPHA_TLSLDM:
+       case R_ALPHA_GOTDTPREL:
+         need = NEED_GOT | NEED_GOT_ENTRY;
+         break;
+
+       case R_ALPHA_GOTTPREL:
+         need = NEED_GOT | NEED_GOT_ENTRY;
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         break;
+
+       case R_ALPHA_TPREL64:
+         if (info->shared || maybe_dynamic)
+           need = NEED_DYNREL;
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         break;
+       }
+
+      if (need & NEED_GOT)
+       {
          if (!got_created)
            {
              if (!elf64_alpha_create_got_section (abfd, info))
@@ -2450,17 +2716,36 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
 
              got_created = 1;
            }
-         break;
+       }
 
-       case R_ALPHA_SREL16:
-       case R_ALPHA_SREL32:
-       case R_ALPHA_SREL64:
-         if (h == NULL)
-           break;
-         /* FALLTHRU */
+      if (need & NEED_GOT_ENTRY)
+       {
+         struct alpha_elf_got_entry *gotent;
 
-       case R_ALPHA_REFLONG:
-       case R_ALPHA_REFQUAD:
+         gotent = get_got_entry (abfd, h, r_type, r_symndx, addend);
+         if (!gotent)
+           return false;
+
+         if (gotent_flags)
+           {
+             gotent->flags |= gotent_flags;
+             if (h)
+               {
+                 gotent_flags |= h->flags;
+                 h->flags = gotent_flags;
+
+                 /* Make a guess as to whether a .plt entry is needed.  */
+                 if ((gotent_flags & ALPHA_ELF_LINK_HASH_LU_FUNC)
+                     && !(gotent_flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC))
+                   h->root.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+                 else
+                   h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+               }
+           }
+       }
+
+      if (need & NEED_DYNREL)
+       {
          if (rel_sec_name == NULL)
            {
              rel_sec_name = (bfd_elf_string_from_elf_section
@@ -2538,7 +2823,6 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
              if (sec->flags & SEC_READONLY)
                info->flags |= DF_TEXTREL;
            }
-         break;
        }
     }
 
@@ -2570,7 +2854,8 @@ elf64_alpha_adjust_dynamic_symbol (info, h)
       && ((h->type == STT_FUNC
           && !(ah->flags & ALPHA_ELF_LINK_HASH_LU_ADDR))
          || (h->type == STT_NOTYPE
-             && ah->flags == ALPHA_ELF_LINK_HASH_LU_FUNC))
+             && (ah->flags & ALPHA_ELF_LINK_HASH_LU_FUNC)
+             && !(ah->flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC)))
       /* Don't prevent otherwise valid programs from linking by attempting
         to create a new .got entry somewhere.  A Correct Solution would be
         to add a new .got section to a new object file and let it be merged
@@ -2667,8 +2952,13 @@ elf64_alpha_merge_ind_symbols (hi, dummy)
        {
          gin = gi->next;
          for (gs = gsh; gs ; gs = gs->next)
-           if (gi->gotobj == gs->gotobj && gi->addend == gs->addend)
-             goto got_found;
+           if (gi->gotobj == gs->gotobj
+               && gi->reloc_type == gs->reloc_type
+               && gi->addend == gs->addend)
+             {
+               gi->use_count += gs->use_count;
+               goto got_found;
+             }
          gi->next = hs->got_entries;
          hs->got_entries = gi;
        got_found:;
@@ -2710,15 +3000,15 @@ static boolean
 elf64_alpha_can_merge_gots (a, b)
      bfd *a, *b;
 {
-  int total = alpha_elf_tdata (a)->total_got_entries;
+  int total = alpha_elf_tdata (a)->total_got_size;
   bfd *bsub;
 
   /* Trivial quick fallout test.  */
-  if (total + alpha_elf_tdata (b)->total_got_entries <= MAX_GOT_ENTRIES)
+  if (total + alpha_elf_tdata (b)->total_got_size <= MAX_GOT_SIZE)
     return true;
 
   /* By their nature, local .got entries cannot be merged.  */
-  if ((total += alpha_elf_tdata (b)->n_local_got_entries) > MAX_GOT_ENTRIES)
+  if ((total += alpha_elf_tdata (b)->local_got_size) > MAX_GOT_SIZE)
     return false;
 
   /* Failing the common trivial comparison, we must effectively
@@ -2749,10 +3039,13 @@ elf64_alpha_can_merge_gots (a, b)
                continue;
 
              for (ae = h->got_entries; ae ; ae = ae->next)
-               if (ae->gotobj == a && ae->addend == be->addend)
+               if (ae->gotobj == a
+                   && ae->reloc_type == be->reloc_type
+                   && ae->addend == be->addend)
                  goto global_found;
 
-             if (++total > MAX_GOT_ENTRIES)
+             total += alpha_got_entry_size (be->reloc_type);
+             if (total > MAX_GOT_SIZE)
                return false;
            global_found:;
            }
@@ -2768,14 +3061,14 @@ static void
 elf64_alpha_merge_gots (a, b)
      bfd *a, *b;
 {
-  int total = alpha_elf_tdata (a)->total_got_entries;
+  int total = alpha_elf_tdata (a)->total_got_size;
   bfd *bsub;
 
   /* Remember local expansion.  */
   {
-    int e = alpha_elf_tdata (b)->n_local_got_entries;
+    int e = alpha_elf_tdata (b)->local_got_size;
     total += e;
-    alpha_elf_tdata (a)->n_local_got_entries += e;
+    alpha_elf_tdata (a)->local_got_size += e;
   }
 
   for (bsub = b; bsub ; bsub = alpha_elf_tdata (bsub)->in_got_link_next)
@@ -2825,7 +3118,9 @@ elf64_alpha_merge_gots (a, b)
                continue;
 
              for (ae = *start; ae ; ae = ae->next)
-               if (ae->gotobj == a && ae->addend == be->addend)
+               if (ae->gotobj == a
+                   && ae->reloc_type == be->reloc_type
+                   && ae->addend == be->addend)
                  {
                    ae->flags |= be->flags;
                    ae->use_count += be->use_count;
@@ -2833,7 +3128,7 @@ elf64_alpha_merge_gots (a, b)
                    goto global_found;
                  }
              be->gotobj = a;
-             total += 1;
+             total += alpha_got_entry_size (be->reloc_type);
 
            global_found:;
            }
@@ -2841,7 +3136,7 @@ elf64_alpha_merge_gots (a, b)
 
       alpha_elf_tdata (bsub)->gotobj = a;
     }
-  alpha_elf_tdata (a)->total_got_entries = total;
+  alpha_elf_tdata (a)->total_got_size = total;
 
   /* Merge the two in_got chains.  */
   {
@@ -2874,7 +3169,7 @@ elf64_alpha_calc_got_offsets_for_symbol (h, arg)
          = &alpha_elf_tdata (gotent->gotobj)->got->_raw_size;
 
        gotent->got_offset = *plge;
-       *plge += 8;
+       *plge += alpha_got_entry_size (gotent->reloc_type);
       }
 
   return true;
@@ -2916,7 +3211,7 @@ elf64_alpha_calc_got_offsets (info)
              if (gotent->use_count > 0)
                {
                  gotent->got_offset = got_offset;
-                 got_offset += 8;
+                 got_offset += alpha_got_entry_size (gotent->reloc_type);
                }
        }
 
@@ -2950,13 +3245,13 @@ elf64_alpha_size_got_sections (output_bfd, info)
          /* We are assuming no merging has yet ocurred.  */
          BFD_ASSERT (this_got == i);
 
-          if (alpha_elf_tdata (this_got)->total_got_entries > MAX_GOT_ENTRIES)
+          if (alpha_elf_tdata (this_got)->total_got_size > MAX_GOT_SIZE)
            {
              /* Yikes! A single object file has too many entries.  */
              (*_bfd_error_handler)
                (_("%s: .got subsegment exceeds 64K (size %d)"),
                 bfd_archive_filename (i),
-                alpha_elf_tdata (this_got)->total_got_entries * 8);
+                alpha_elf_tdata (this_got)->total_got_size);
              return false;
            }
 
@@ -3037,6 +3332,40 @@ elf64_alpha_always_size_sections (output_bfd, info)
   return true;
 }
 
+/* The number of dynamic relocations required by a static relocation.  */
+
+static int
+alpha_dynamic_entries_for_reloc (r_type, dynamic, shared)
+     int r_type, dynamic, shared;
+{
+  switch (r_type)
+    {
+    /* May appear in GOT entries.  */
+    case R_ALPHA_TLSGD:
+      return (dynamic ? 2 : shared ? 1 : 0);
+    case R_ALPHA_TLSLDM:
+      return shared;
+    case R_ALPHA_LITERAL:
+      return dynamic || shared;
+    case R_ALPHA_GOTDTPREL:
+    case R_ALPHA_GOTTPREL:
+      return dynamic;
+
+    /* May appear in data sections.  */
+    case R_ALPHA_REFLONG:
+    case R_ALPHA_REFQUAD:
+      return dynamic || shared;
+    case R_ALPHA_SREL64:
+    case R_ALPHA_TPREL64:
+      return dynamic;
+
+    /* Everything else is illegal.  We'll issue an error during
+       relocate_section.  */
+    default:
+      return 0;
+    }
+}
+
 /* Work out the sizes of the dynamic relocation entries.  */
 
 static boolean
@@ -3044,6 +3373,11 @@ elf64_alpha_calc_dynrel_sizes (h, info)
      struct alpha_elf_link_hash_entry *h;
      struct bfd_link_info *info;
 {
+  boolean dynamic;
+  struct alpha_elf_reloc_entry *relent;
+  struct alpha_elf_got_entry *gotent;
+  int entries;
+
   if (h->root.root.type == bfd_link_hash_warning)
     h = (struct alpha_elf_link_hash_entry *) h->root.root.u.i.link;
 
@@ -3070,41 +3404,37 @@ elf64_alpha_calc_dynrel_sizes (h, info)
      natural form.  If this is a shared object, and it has been forced
      local, we'll need the same number of RELATIVE relocations.  */
 
-  if (alpha_elf_dynamic_symbol_p (&h->root, info) || info->shared)
-    {
-      struct alpha_elf_reloc_entry *relent;
-      bfd *dynobj;
-      struct alpha_elf_got_entry *gotent;
-      bfd_size_type count;
-      asection *srel;
-
-      for (relent = h->reloc_entries; relent; relent = relent->next)
-       if (relent->rtype == R_ALPHA_REFLONG
-           || relent->rtype == R_ALPHA_REFQUAD)
-         {
-           relent->srel->_raw_size +=
-             sizeof (Elf64_External_Rela) * relent->count;
-           if (relent->reltext)
-             info->flags |= DT_TEXTREL;
-         }
+  dynamic = alpha_elf_dynamic_symbol_p (&h->root, info);
 
-      dynobj = elf_hash_table(info)->dynobj;
-      count = 0;
+  for (relent = h->reloc_entries; relent; relent = relent->next)
+    {
+      entries = alpha_dynamic_entries_for_reloc (relent->rtype, dynamic,
+                                                info->shared);
+      if (entries)
+       {
+         relent->srel->_raw_size +=
+           entries * sizeof (Elf64_External_Rela) * relent->count;
+         if (relent->reltext)
+           info->flags |= DT_TEXTREL;
+       }
+    }
 
-      for (gotent = h->got_entries; gotent ; gotent = gotent->next)
-       count++;
+  entries = 0;
+  for (gotent = h->got_entries; gotent ; gotent = gotent->next)
+    entries += alpha_dynamic_entries_for_reloc (gotent->reloc_type,
+                                               dynamic, info->shared);
 
-      /* If we are using a .plt entry, subtract one, as the first
-        reference uses a .rela.plt entry instead.  */
-      if (h->root.plt.offset != MINUS_ONE)
-       count--;
+  /* If we are using a .plt entry, subtract one, as the first
+     reference uses a .rela.plt entry instead.  */
+  if (h->root.plt.offset != MINUS_ONE)
+    entries--;
 
-      if (count > 0)
-       {
-         srel = bfd_get_section_by_name (dynobj, ".rela.got");
-         BFD_ASSERT (srel != NULL);
-         srel->_raw_size += sizeof (Elf64_External_Rela) * count;
-       }
+  if (entries > 0)
+    {
+      bfd *dynobj = elf_hash_table(info)->dynobj;
+      asection *srel = bfd_get_section_by_name (dynobj, ".rela.got");
+      BFD_ASSERT (srel != NULL);
+      srel->_raw_size += sizeof (Elf64_External_Rela) * entries;
     }
 
   return true;
@@ -3126,6 +3456,9 @@ elf64_alpha_size_dynamic_sections (output_bfd, info)
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
+      int entries;
+      bfd *i;
+
       /* Set the contents of the .interp section to the interpreter.  */
       if (!info->shared)
        {
@@ -3143,23 +3476,38 @@ elf64_alpha_size_dynamic_sections (output_bfd, info)
                                    elf64_alpha_calc_dynrel_sizes,
                                    info);
 
-      /* When building shared libraries, each local .got entry needs a
-        RELATIVE reloc.  */
-      if (info->shared)
+      /* Shared libraries often require RELATIVE relocs, and some relocs
+        require attention for the main application as well.  */
+        
+      entries = 0;
+      for (i = alpha_elf_hash_table(info)->got_list;
+          i ; i = alpha_elf_tdata(i)->got_link_next)
        {
-         bfd *i;
-         asection *srel;
-         bfd_size_type count;
-
-         srel = bfd_get_section_by_name (dynobj, ".rela.got");
-         BFD_ASSERT (srel != NULL);
+         bfd *j;
 
-         for (i = alpha_elf_hash_table(info)->got_list, count = 0;
-              i != NULL;
-              i = alpha_elf_tdata(i)->got_link_next)
-           count += alpha_elf_tdata(i)->n_local_got_entries;
+         for (j = i; j ; j = alpha_elf_tdata(j)->in_got_link_next)
+           {
+             struct alpha_elf_got_entry **local_got_entries, *gotent;
+             int k, n;
+
+             local_got_entries = alpha_elf_tdata(j)->local_got_entries;
+             if (!local_got_entries)
+               continue;
+
+             for (k = 0, n = elf_tdata(j)->symtab_hdr.sh_info; k < n; ++k)
+               for (gotent = local_got_entries[k];
+                    gotent ; gotent = gotent->next)
+                 if (gotent->use_count > 0)
+                   entries += (alpha_dynamic_entries_for_reloc
+                               (gotent->reloc_type, 0, info->shared));
+           }
+       }
 
-         srel->_raw_size += count * sizeof (Elf64_External_Rela);
+      if (entries > 0)
+       {
+         s = bfd_get_section_by_name (dynobj, ".rela.got");
+         BFD_ASSERT (s != NULL);
+         s->_raw_size += sizeof (Elf64_External_Rela) * entries;
        }
     }
   /* else we're not dynamic and by definition we don't need such things.  */
@@ -3278,60 +3626,85 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
      Elf_Internal_Sym *local_syms;
      asection **local_sections;
 {
-  Elf_Internal_Shdr *symtab_hdr;
+  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
-  asection *sec, *sgot, *srel, *srelgot;
-  bfd *dynobj, *gotobj;
-  bfd_vma gp;
+  struct elf_link_tls_segment *tls_segment = NULL;
+  asection *sgot = NULL, *srel = NULL, *srelgot = NULL;
+  bfd *dynobj = NULL, *gotobj = NULL;
+  bfd_vma gp = 0, tp_base = 0, dtp_base = 0;
   boolean ret_val = true;
 
-  srelgot = srel = NULL;
-  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
-  dynobj = elf_hash_table (info)->dynobj;
-  if (dynobj)
+  if (!info->relocateable)
     {
-      srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
-    }
+      const char *name;
 
-  /* Find the gp value for this input bfd.  */
-  sgot = NULL;
-  gp = 0;
-  gotobj = alpha_elf_tdata (input_bfd)->gotobj;
-  if (gotobj)
-    {
-      sgot = alpha_elf_tdata (gotobj)->got;
-      gp = _bfd_get_gp_value (gotobj);
-      if (gp == 0)
+      dynobj = elf_hash_table (info)->dynobj;
+      if (dynobj)
+        srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+
+      name = (bfd_elf_string_from_elf_section
+             (input_bfd, elf_elfheader(input_bfd)->e_shstrndx,
+              elf_section_data(input_section)->rel_hdr.sh_name));
+      BFD_ASSERT(name != NULL);
+      srel = bfd_get_section_by_name (dynobj, name);
+
+      /* Find the gp value for this input bfd.  */
+      gotobj = alpha_elf_tdata (input_bfd)->gotobj;
+      if (gotobj)
        {
-         gp = (sgot->output_section->vma
-               + sgot->output_offset
-               + 0x8000);
-         _bfd_set_gp_value (gotobj, gp);
-       }
+         sgot = alpha_elf_tdata (gotobj)->got;
+         gp = _bfd_get_gp_value (gotobj);
+         if (gp == 0)
+           {
+             gp = (sgot->output_section->vma
+                   + sgot->output_offset
+                   + 0x8000);
+             _bfd_set_gp_value (gotobj, gp);
+           }
+        }
+
+      tls_segment = elf_hash_table (info)->tls_segment;
+      if (tls_segment)
+        {
+          /* This is PT_TLS segment p_vaddr.  */
+          dtp_base = tls_segment->start;
+
+          /* Main program TLS (whose template starts at PT_TLS p_vaddr)
+            is assigned offset round(16, PT_TLS p_align).  */
+          tp_base = dtp_base - align_power (16, tls_segment->align);
+        }
     }
 
   rel = relocs;
   relend = relocs + input_section->reloc_count;
   for (; rel < relend; rel++)
     {
-      int r_type;
+      struct alpha_elf_link_hash_entry *h;
+      struct alpha_elf_got_entry *gotent;
+      bfd_reloc_status_type r;
       reloc_howto_type *howto;
       unsigned long r_symndx;
-      struct alpha_elf_link_hash_entry *h;
       Elf_Internal_Sym *sym;
-      bfd_vma relocation;
+      asection *sec;
+      bfd_vma value;
       bfd_vma addend;
-      bfd_reloc_status_type r;
+      boolean dynamic_symbol_p;
+      boolean undef_weak_ref;
+      unsigned long r_type;
 
       r_type = ELF64_R_TYPE(rel->r_info);
-      if (r_type < 0 || r_type >= (int) R_ALPHA_max)
+      if (r_type >= R_ALPHA_max)
        {
+         (*_bfd_error_handler)
+           (_("%s: unknown relocation type %d"),
+            bfd_archive_filename (input_bfd), (int)r_type);
          bfd_set_error (bfd_error_bad_value);
-         return false;
+         ret_val = false;
+         continue;
        }
-      howto = elf64_alpha_howto_table + r_type;
 
+      howto = elf64_alpha_howto_table + r_type;
       r_symndx = ELF64_R_SYM(rel->r_info);
 
       if (info->relocateable)
@@ -3364,12 +3737,48 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
       h = NULL;
       sym = NULL;
       sec = NULL;
+      undef_weak_ref = false;
 
       if (r_symndx < symtab_hdr->sh_info)
        {
          sym = local_syms + r_symndx;
          sec = local_sections[r_symndx];
-         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
+         value = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
+
+         gotent = alpha_elf_tdata(input_bfd)->local_got_entries[r_symndx];
+
+         /* Need to adjust local GOT entries' addends for SEC_MERGE
+            unless it has been done already.  */
+         if ((sec->flags & SEC_MERGE)
+              && ELF_ST_TYPE (sym->st_info) == STT_SECTION
+              && (elf_section_data (sec)->sec_info_type
+                  == ELF_INFO_TYPE_MERGE)
+              && !gotent->reloc_xlated)
+           {
+             struct alpha_elf_got_entry *ent;
+             asection *msec;
+
+             for (ent = gotent; ent; ent = ent->next)
+               {
+                 ent->reloc_xlated = 1;
+                 if (ent->use_count == 0)
+                   continue;
+                 msec = sec;
+                 ent->addend =
+                   _bfd_merged_section_offset (output_bfd, &msec,
+                                               elf_section_data (sec)->
+                                                 sec_info,
+                                               sym->st_value + ent->addend,
+                                               (bfd_vma) 0);
+                 ent->addend -= sym->st_value;
+                 ent->addend += msec->output_section->vma
+                                + msec->output_offset
+                                - sec->output_section->vma
+                                - sec->output_offset;
+               }
+           }
+
+         dynamic_symbol_p = false;
        }
       else
        {
@@ -3379,27 +3788,32 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
                 || h->root.root.type == bfd_link_hash_warning)
            h = (struct alpha_elf_link_hash_entry *)h->root.root.u.i.link;
 
+         value = 0;
          if (h->root.root.type == bfd_link_hash_defined
              || h->root.root.type == bfd_link_hash_defweak)
            {
              sec = h->root.root.u.def.section;
 
-             if (sec->output_section == NULL)
-               relocation = 0;
-             else
-               {
-                 relocation = (h->root.root.u.def.value
-                               + sec->output_section->vma
-                               + sec->output_offset);
-               }
+             /* Detect the cases that sym_sec->output_section is
+                expected to be NULL -- all cases in which the symbol
+                is defined in another shared module.  This includes
+                PLT relocs for which we've created a PLT entry and
+                other relocs for which we're prepared to create
+                dynamic relocations.  */
+             /* ??? Just accept it NULL and continue.  */
+
+             if (sec->output_section != NULL)
+               value = (h->root.root.u.def.value
+                        + sec->output_section->vma
+                             + sec->output_offset);
            }
          else if (h->root.root.type == bfd_link_hash_undefweak)
-           relocation = 0;
+           undef_weak_ref = true;
          else if (info->shared
                   && (!info->symbolic || info->allow_shlib_undefined)
                   && !info->no_undefined
                   && ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT)
-           relocation = 0;
+           ;
          else
            {
              if (!((*info->callbacks->undefined_symbol)
@@ -3407,11 +3821,24 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
                     input_section, rel->r_offset,
                     (!info->shared || info->no_undefined
                      || ELF_ST_VISIBILITY (h->root.other)))))
-               ret_val = false;
-             relocation = 0;
+               return false;
+             ret_val = false;
+             continue;
            }
+
+          dynamic_symbol_p = alpha_elf_dynamic_symbol_p (&h->root, info);
+         gotent = h->got_entries;
        }
+
       addend = rel->r_addend;
+      value += addend;
+
+      /* Search for the proper got entry.  */
+      for (; gotent ; gotent = gotent->next)
+       if (gotent->gotobj == gotobj
+           && gotent->reloc_type == r_type
+           && gotent->addend == addend)
+         break;
 
       switch (r_type)
        {
@@ -3421,124 +3848,66 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
 
            BFD_ASSERT(gp != 0);
 
-           relocation = (input_section->output_section->vma
-                         + input_section->output_offset
-                         + rel->r_offset);
+           value = (input_section->output_section->vma
+                    + input_section->output_offset
+                    + rel->r_offset);
 
-           p_ldah = contents + rel->r_offset - input_section->vma;
+           p_ldah = contents + rel->r_offset;
            p_lda = p_ldah + rel->r_addend;
 
-           r = elf64_alpha_do_reloc_gpdisp (input_bfd, gp - relocation,
+           r = elf64_alpha_do_reloc_gpdisp (input_bfd, gp - value,
                                             p_ldah, p_lda);
          }
          break;
 
        case R_ALPHA_LITERAL:
-         {
-           struct alpha_elf_got_entry *gotent;
-           boolean dynamic_symbol;
-
-           BFD_ASSERT(sgot != NULL);
-           BFD_ASSERT(gp != 0);
-
-           if (h != NULL)
-             {
-               gotent = h->got_entries;
-               dynamic_symbol = alpha_elf_dynamic_symbol_p (&h->root, info);
-             }
-           else
-             {
-               gotent = (alpha_elf_tdata(input_bfd)->
-                         local_got_entries[r_symndx]);
-               dynamic_symbol = false;
-
-               /* Need to adjust local GOT entries' addends for SEC_MERGE
-                  unless it has been done already.  */
-               if ((sec->flags & SEC_MERGE)
-                   && ELF_ST_TYPE (sym->st_info) == STT_SECTION
-                   && (elf_section_data (sec)->sec_info_type
-                       == ELF_INFO_TYPE_MERGE)
-                   && (gotent->flags & ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED) == 0)
-                 {
-                   struct alpha_elf_got_entry *ent;
-                   asection *msec;
-
-                   for (ent = gotent; ent; ent = ent->next)
-                     {
-                       ent->flags |= ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED;
-                       if (ent->use_count == 0)
-                         continue;
-                       msec = sec;
-                       ent->addend =
-                         _bfd_merged_section_offset (output_bfd, &msec,
-                                                     elf_section_data (sec)->
-                                                     sec_info,
-                                                     sym->st_value
-                                                     + ent->addend,
-                                                     (bfd_vma) 0);
-                       ent->addend -= sym->st_value;
-                       ent->addend += msec->output_section->vma
-                                      + msec->output_offset
-                                      - sec->output_section->vma
-                                      - sec->output_offset;
-                     }
-                 }
-             }
+         BFD_ASSERT(sgot != NULL);
+         BFD_ASSERT(gp != 0);
+         BFD_ASSERT(gotent != NULL);
+         BFD_ASSERT(gotent->use_count >= 1);
 
-           BFD_ASSERT(gotent != NULL);
+         if (!gotent->reloc_done)
+           {
+             gotent->reloc_done = 1;
 
-           while (gotent->gotobj != gotobj || gotent->addend != addend)
-             gotent = gotent->next;
+             bfd_put_64 (output_bfd, value,
+                         sgot->contents + gotent->got_offset);
 
-           BFD_ASSERT(gotent->use_count >= 1);
+             /* If the symbol has been forced local, output a
+                RELATIVE reloc, otherwise it will be handled in
+                finish_dynamic_symbol.  */
+             if (info->shared && !dynamic_symbol_p)
+               {
+                 Elf_Internal_Rela outrel;
 
-           /* Initialize the .got entry's value.  */
-           if (!(gotent->flags & ALPHA_ELF_GOT_ENTRY_RELOCS_DONE))
-             {
-               bfd_put_64 (output_bfd, relocation + addend,
-                           sgot->contents + gotent->got_offset);
+                 BFD_ASSERT(srelgot != NULL);
 
-               /* If the symbol has been forced local, output a
-                  RELATIVE reloc, otherwise it will be handled in
-                  finish_dynamic_symbol.  */
-               if (info->shared && !dynamic_symbol)
-                 {
-                   Elf_Internal_Rela outrel;
-
-                   BFD_ASSERT(srelgot != NULL);
-
-                   outrel.r_offset = (sgot->output_section->vma
-                                      + sgot->output_offset
-                                      + gotent->got_offset);
-                   outrel.r_info = ELF64_R_INFO(0, R_ALPHA_RELATIVE);
-                   outrel.r_addend = relocation + addend;
-
-                   bfd_elf64_swap_reloca_out (output_bfd, &outrel,
-                                              ((Elf64_External_Rela *)
-                                               srelgot->contents)
-                                              + srelgot->reloc_count++);
-                   BFD_ASSERT (sizeof (Elf64_External_Rela)
-                               * srelgot->reloc_count
-                               <= srelgot->_cooked_size);
-                 }
+                 outrel.r_offset = (sgot->output_section->vma
+                                    + sgot->output_offset
+                                    + gotent->got_offset);
+                 outrel.r_info = ELF64_R_INFO (0, R_ALPHA_RELATIVE);
+                 outrel.r_addend = value;
 
-               gotent->flags |= ALPHA_ELF_GOT_ENTRY_RELOCS_DONE;
-             }
+                 bfd_elf64_swap_reloca_out (output_bfd, &outrel,
+                                            ((Elf64_External_Rela *)
+                                             srelgot->contents)
+                                            + srelgot->reloc_count++);
+                 BFD_ASSERT (sizeof (Elf64_External_Rela)
+                             * srelgot->reloc_count
+                             <= srelgot->_cooked_size);
+               }
+           }
 
-           /* Figure the gprel relocation.  */
-           addend = 0;
-           relocation = (sgot->output_section->vma
-                         + sgot->output_offset
-                         + gotent->got_offset);
-           relocation -= gp;
-         }
-         /* overflow handled by _bfd_final_link_relocate */
+         value = (sgot->output_section->vma
+                  + sgot->output_offset
+                  + gotent->got_offset);
+         value -= gp;
          goto default_reloc;
 
        case R_ALPHA_GPREL16:
        case R_ALPHA_GPREL32:
        case R_ALPHA_GPRELLOW:
-         if (h && alpha_elf_dynamic_symbol_p (&h->root, info))
+         if (dynamic_symbol_p)
             {
               (*_bfd_error_handler)
                 (_("%s: gp-relative relocation against dynamic symbol %s"),
@@ -3546,11 +3915,11 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
               ret_val = false;
             }
          BFD_ASSERT(gp != 0);
-         relocation -= gp;
+         value -= gp;
          goto default_reloc;
 
        case R_ALPHA_GPRELHIGH:
-         if (h && alpha_elf_dynamic_symbol_p (&h->root, info))
+         if (dynamic_symbol_p)
             {
               (*_bfd_error_handler)
                 (_("%s: gp-relative relocation against dynamic symbol %s"),
@@ -3558,27 +3927,34 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
               ret_val = false;
             }
          BFD_ASSERT(gp != 0);
-         relocation -= gp;
-         relocation += addend;
-         addend = 0;
-         relocation = (((bfd_signed_vma) relocation >> 16)
-                       + ((relocation >> 15) & 1));
+         value -= gp;
+         value = ((bfd_signed_vma) value >> 16) + ((value >> 15) & 1);
          goto default_reloc;
 
        case R_ALPHA_HINT:
          /* A call to a dynamic symbol is definitely out of range of
             the 16-bit displacement.  Don't bother writing anything.  */
-         if (h && alpha_elf_dynamic_symbol_p (&h->root, info))
+         if (dynamic_symbol_p)
            {
              r = bfd_reloc_ok;
              break;
            }
-         /* FALLTHRU */
+         /* The regular PC-relative stuff measures from the start of
+            the instruction rather than the end.  */
+         value -= 4;
+         goto default_reloc;
 
        case R_ALPHA_BRADDR:
+         if (dynamic_symbol_p)
+            {
+              (*_bfd_error_handler)
+                (_("%s: pc-relative relocation against dynamic symbol %s"),
+                 bfd_archive_filename (input_bfd), h->root.root.root.string);
+              ret_val = false;
+            }
          /* The regular PC-relative stuff measures from the start of
             the instruction rather than the end.  */
-         addend -= 4;
+         value -= 4;
          goto default_reloc;
 
        case R_ALPHA_BRSGP:
@@ -3588,7 +3964,7 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
 
            /* The regular PC-relative stuff measures from the start of
               the instruction rather than the end.  */
-           addend -= 4;
+           value -= 4;
 
            /* The source and destination gp must be the same.  Note that
               the source will always have an assigned gp, since we forced
@@ -3641,41 +4017,52 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
 
        case R_ALPHA_REFLONG:
        case R_ALPHA_REFQUAD:
+       case R_ALPHA_DTPREL64:
+       case R_ALPHA_TPREL64:
          {
            Elf_Internal_Rela outrel;
 
            /* Careful here to remember RELATIVE relocations for global
               variables for symbolic shared objects.  */
 
-           if (h && alpha_elf_dynamic_symbol_p (&h->root, info))
+           if (dynamic_symbol_p)
              {
                BFD_ASSERT(h->root.dynindx != -1);
-               outrel.r_info = ELF64_R_INFO(h->root.dynindx, r_type);
+               outrel.r_info = ELF64_R_INFO (h->root.dynindx, r_type);
                outrel.r_addend = addend;
-               addend = 0, relocation = 0;
+               addend = 0, value = 0;
+             }
+           else if (r_type == R_ALPHA_DTPREL64)
+             {
+               BFD_ASSERT(tls_segment != NULL);
+               value -= dtp_base;
+               goto default_reloc;
+             }
+           else if (r_type == R_ALPHA_TPREL64)
+             {
+               BFD_ASSERT(tls_segment != NULL);
+               value -= dtp_base;
+               goto default_reloc;
              }
            else if (info->shared
                     && r_symndx != 0
                     && (input_section->flags & SEC_ALLOC))
              {
-               outrel.r_info = ELF64_R_INFO(0, R_ALPHA_RELATIVE);
-               outrel.r_addend = relocation + addend;
+               if (r_type == R_ALPHA_REFLONG)
+                 {
+                   (*_bfd_error_handler)
+                     (_("%s: unhandled dynamic relocation against %s"),
+                      bfd_archive_filename (input_bfd),
+                      h->root.root.root.string);
+                   ret_val = false;
+                 }
+               outrel.r_info = ELF64_R_INFO (0, R_ALPHA_RELATIVE);
+               outrel.r_addend = value;
              }
            else
              goto default_reloc;
 
-           if (!srel)
-             {
-               const char *name;
-
-               name = (bfd_elf_string_from_elf_section
-                       (input_bfd, elf_elfheader(input_bfd)->e_shstrndx,
-                        elf_section_data(input_section)->rel_hdr.sh_name));
-               BFD_ASSERT(name != NULL);
-
-               srel = bfd_get_section_by_name (dynobj, name);
-               BFD_ASSERT(srel != NULL);
-             }
+           BFD_ASSERT(srel != NULL);
 
            outrel.r_offset =
              _bfd_elf_section_offset (output_bfd, info, input_section,
@@ -3695,8 +4082,17 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
          }
          goto default_reloc;
 
+       case R_ALPHA_SREL16:
        case R_ALPHA_SREL32:
        case R_ALPHA_SREL64:
+         if (dynamic_symbol_p)
+            {
+              (*_bfd_error_handler)
+                (_("%s: pc-relative relocation against dynamic symbol %s"),
+                 bfd_archive_filename (input_bfd), h->root.root.root.string);
+              ret_val = false;
+            }
+
          /* ??? .eh_frame references to discarded sections will be smashed
             to relocations against SHN_UNDEF.  The .eh_frame format allows
             NULL to be encoded as 0 in any format, so this works here.  */
@@ -3705,11 +4101,123 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
                     + (r_type - R_ALPHA_SREL32 + R_ALPHA_REFLONG));
          goto default_reloc;
 
+       case R_ALPHA_TLSLDM:
+         /* Ignore the symbol for the relocation.  The result is always
+            the current module.  */
+         dynamic_symbol_p = 0;
+         /* FALLTHRU */
+
+       case R_ALPHA_TLSGD:
+         if (!gotent->reloc_done)
+           {
+             gotent->reloc_done = 1;
+
+             /* Note that the module index for the main program is 1.  */
+             bfd_put_64 (output_bfd, !info->shared && !dynamic_symbol_p,
+                         sgot->contents + gotent->got_offset);
+
+             /* If the symbol has been forced local, output a
+                DTPMOD64 reloc, otherwise it will be handled in
+                finish_dynamic_symbol.  */
+             if (info->shared && !dynamic_symbol_p)
+               {
+                 Elf_Internal_Rela outrel;
+
+                 BFD_ASSERT(srelgot != NULL);
+
+                 outrel.r_offset = (sgot->output_section->vma
+                                    + sgot->output_offset
+                                    + gotent->got_offset);
+                 /* ??? Proper dynindx here.  */
+                 outrel.r_info = ELF64_R_INFO (0, R_ALPHA_DTPMOD64);
+                 outrel.r_addend = 0;
+
+                 bfd_elf64_swap_reloca_out (output_bfd, &outrel,
+                                            ((Elf64_External_Rela *)
+                                             srelgot->contents)
+                                            + srelgot->reloc_count++);
+                 BFD_ASSERT (sizeof (Elf64_External_Rela)
+                             * srelgot->reloc_count
+                             <= srelgot->_cooked_size);
+               }
+
+             if (dynamic_symbol_p || r_type == R_ALPHA_TLSLDM)
+               value = 0;
+             else
+               {
+                 BFD_ASSERT(tls_segment != NULL);
+                 value -= dtp_base;
+               }
+             bfd_put_64 (output_bfd, value,
+                         sgot->contents + gotent->got_offset + 8);
+           }
+
+         value = (sgot->output_section->vma
+                  + sgot->output_offset
+                  + gotent->got_offset);
+         value -= gp;
+         goto default_reloc;
+
+       case R_ALPHA_DTPRELHI:
+       case R_ALPHA_DTPRELLO:
+       case R_ALPHA_DTPREL16:
+         if (dynamic_symbol_p)
+            {
+              (*_bfd_error_handler)
+                (_("%s: dtp-relative relocation against dynamic symbol %s"),
+                 bfd_archive_filename (input_bfd), h->root.root.root.string);
+              ret_val = false;
+            }
+         BFD_ASSERT(tls_segment != NULL);
+         value -= dtp_base;
+         goto default_reloc;
+
+       case R_ALPHA_TPRELHI:
+       case R_ALPHA_TPRELLO:
+       case R_ALPHA_TPREL16:
+         if (dynamic_symbol_p)
+            {
+              (*_bfd_error_handler)
+                (_("%s: tp-relative relocation against dynamic symbol %s"),
+                 bfd_archive_filename (input_bfd), h->root.root.root.string);
+              ret_val = false;
+            }
+         BFD_ASSERT(tls_segment != NULL);
+         value -= tp_base;
+         goto default_reloc;
+
+       case R_ALPHA_GOTDTPREL:
+       case R_ALPHA_GOTTPREL:
+         BFD_ASSERT(sgot != NULL);
+         BFD_ASSERT(gp != 0);
+         BFD_ASSERT(gotent != NULL);
+         BFD_ASSERT(gotent->use_count >= 1);
+
+         if (!gotent->reloc_done)
+           {
+             gotent->reloc_done = 1;
+
+             if (dynamic_symbol_p)
+               value = 0;
+             else
+               {
+                 BFD_ASSERT(tls_segment != NULL);
+                 value -= (r_type == R_ALPHA_GOTDTPREL ? dtp_base : tp_base);
+               }
+             bfd_put_64 (output_bfd, value,
+                         sgot->contents + gotent->got_offset);
+           }
+
+         value = (sgot->output_section->vma
+                  + sgot->output_offset
+                  + gotent->got_offset);
+         value -= gp;
+         goto default_reloc;
+
        default:
        default_reloc:
          r = _bfd_final_link_relocate (howto, input_bfd, input_section,
-                                       contents, rel->r_offset, relocation,
-                                       addend);
+                                       contents, rel->r_offset, value, 0);
          break;
        }
 
@@ -3881,20 +4389,54 @@ elf64_alpha_finish_dynamic_symbol (output_bfd, info, h, sym)
       srel = bfd_get_section_by_name (dynobj, ".rela.got");
       BFD_ASSERT (srel != NULL);
 
-      outrel.r_info = ELF64_R_INFO (h->dynindx, R_ALPHA_GLOB_DAT);
       for (gotent = ((struct alpha_elf_link_hash_entry *) h)->got_entries;
           gotent != NULL;
           gotent = gotent->next)
        {
          asection *sgot = alpha_elf_tdata (gotent->gotobj)->got;
+         int r_type;
+
          outrel.r_offset = (sgot->output_section->vma
                             + sgot->output_offset
                             + gotent->got_offset);
+
+         r_type = gotent->reloc_type;
+         switch (r_type)
+           {
+           case R_ALPHA_LITERAL:
+             r_type = R_ALPHA_GLOB_DAT;
+             break;
+           case R_ALPHA_TLSGD:
+             r_type = R_ALPHA_DTPMOD64;
+             break;
+           case R_ALPHA_GOTDTPREL:
+             r_type = R_ALPHA_DTPREL64;
+             break;
+           case R_ALPHA_GOTTPREL:
+             r_type = R_ALPHA_TPREL64;
+             break;
+           case R_ALPHA_TLSLDM:
+           default:
+             abort ();
+           }
+
+         outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
          outrel.r_addend = gotent->addend;
 
          bfd_elf64_swap_reloca_out (output_bfd, &outrel,
                                     ((Elf64_External_Rela *)srel->contents
                                      + srel->reloc_count++));
+
+         if (gotent->reloc_type == R_ALPHA_TLSGD)
+           {
+             outrel.r_offset += 8;
+             outrel.r_info = ELF64_R_INFO (h->dynindx, R_ALPHA_DTPREL64);
+
+             bfd_elf64_swap_reloca_out (output_bfd, &outrel,
+                                        ((Elf64_External_Rela *)srel->contents
+                                         + srel->reloc_count++));
+           }
+
          BFD_ASSERT (sizeof (Elf64_External_Rela) * srel->reloc_count
                      <= srel->_cooked_size);
        }
index cbb6390..6cba829 100644 (file)
@@ -732,6 +732,19 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_ALPHA_GPREL_HI16",
   "BFD_RELOC_ALPHA_GPREL_LO16",
   "BFD_RELOC_ALPHA_BRSGP",
+  "BFD_RELOC_ALPHA_TLSGD",
+  "BFD_RELOC_ALPHA_TLSLDM",
+  "BFD_RELOC_ALPHA_DTPMOD64",
+  "BFD_RELOC_ALPHA_GOTDTPREL16",
+  "BFD_RELOC_ALPHA_DTPREL64",
+  "BFD_RELOC_ALPHA_DTPREL_HI16",
+  "BFD_RELOC_ALPHA_DTPREL_LO16",
+  "BFD_RELOC_ALPHA_DTPREL16",
+  "BFD_RELOC_ALPHA_GOTTPREL16",
+  "BFD_RELOC_ALPHA_TPREL64",
+  "BFD_RELOC_ALPHA_TPREL_HI16",
+  "BFD_RELOC_ALPHA_TPREL_LO16",
+  "BFD_RELOC_ALPHA_TPREL16",
   "BFD_RELOC_MIPS_JMP",
   "BFD_RELOC_MIPS16_JMP",
   "BFD_RELOC_MIPS16_GPREL",
index 60d40aa..8af90fc 100644 (file)
@@ -1963,6 +1963,35 @@ ENUMDOC
   STO_ALPHA_STD_GPLOAD.
 
 ENUM
+  BFD_RELOC_ALPHA_TLSGD
+ENUMX
+  BFD_RELOC_ALPHA_TLSLDM
+ENUMX
+  BFD_RELOC_ALPHA_DTPMOD64
+ENUMX
+  BFD_RELOC_ALPHA_GOTDTPREL16
+ENUMX
+  BFD_RELOC_ALPHA_DTPREL64
+ENUMX
+  BFD_RELOC_ALPHA_DTPREL_HI16
+ENUMX
+  BFD_RELOC_ALPHA_DTPREL_LO16
+ENUMX
+  BFD_RELOC_ALPHA_DTPREL16
+ENUMX
+  BFD_RELOC_ALPHA_GOTTPREL16
+ENUMX
+  BFD_RELOC_ALPHA_TPREL64
+ENUMX
+  BFD_RELOC_ALPHA_TPREL_HI16
+ENUMX
+  BFD_RELOC_ALPHA_TPREL_LO16
+ENUMX
+  BFD_RELOC_ALPHA_TPREL16
+ENUMDOC
+  Alpha thread-local storage relocations.
+
+ENUM
   BFD_RELOC_MIPS_JMP
 ENUMDOC
   Bits 27..2 of the relocation address shifted right 2 bits;
index 979573b..84fac45 100644 (file)
@@ -1,3 +1,21 @@
+2002-05-30  Richard Henderson  <rth@redhat.com>
+
+       * expr.h (operatorT): Add O_md17..O_md32.
+       * config/tc-alpha.c (O_lituse_tlsgd, O_lituse_tlsldm, O_tlsgd,
+       O_tlsldm, O_gotdtprel, O_dtprelhi, O_dtprello, O_dtprel, O_gottprel,
+       O_tprelhi, O_tprello, O_tprel): New.
+       (USER_RELOC_P, alpha_reloc_op_tag, debug_exp): Include them.
+       (DUMMY_RELOC_LITUSE_TLSGD, DUMMY_RELOC_LITUSE_TLSLDM): New.
+       (LITUSE_TLSGD, LITUSE_TLSLDM): New.
+       (struct alpha_reloc_tag): Add master, saw_tlsgd, saw_tlsld,
+       saw_lu_tlsgd, saw_lu_tlsldm.  Make multi_section_p a bit field.
+       (md_apply_fix3): Handle TLS relocations.
+       (alpha_force_relocation, alpha_fix_adjustable): Likewise.
+       (alpha_adjust_symtab_relocs): Sort LITERAL relocs after the
+       associated TLS reloc.  Check lituse_tls relocs match up.
+       (emit_insn): Handle TLS relocations.
+       (ldX_op): Remove.
+
 2002-05-30  Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
 
        * config/tc-mips.c (mips_gprel_offset): New variable.
index 7746f99..1ba7aca 100644 (file)
@@ -106,30 +106,45 @@ struct alpha_macro {
 #define O_pregister    O_md1   /* O_register, in parentheses */
 #define O_cpregister   O_md2   /* + a leading comma */
 
-/* Note, the alpha_reloc_op table below depends on the ordering
-   of O_literal .. O_gpre16.  */
+/* The alpha_reloc_op table below depends on the ordering of these.  */
 #define O_literal      O_md3   /* !literal relocation */
 #define O_lituse_addr  O_md4   /* !lituse_addr relocation */
 #define O_lituse_base  O_md5   /* !lituse_base relocation */
 #define O_lituse_bytoff        O_md6   /* !lituse_bytoff relocation */
 #define O_lituse_jsr   O_md7   /* !lituse_jsr relocation */
-#define O_gpdisp       O_md8   /* !gpdisp relocation */
-#define O_gprelhigh    O_md9   /* !gprelhigh relocation */
-#define O_gprellow     O_md10  /* !gprellow relocation */
-#define O_gprel                O_md11  /* !gprel relocation */
-#define O_samegp       O_md12  /* !samegp relocation */
+#define O_lituse_tlsgd O_md8   /* !lituse_tlsgd relocation */
+#define O_lituse_tlsldm        O_md9   /* !lituse_tlsldm relocation */
+#define O_gpdisp       O_md10  /* !gpdisp relocation */
+#define O_gprelhigh    O_md11  /* !gprelhigh relocation */
+#define O_gprellow     O_md12  /* !gprellow relocation */
+#define O_gprel                O_md13  /* !gprel relocation */
+#define O_samegp       O_md14  /* !samegp relocation */
+#define O_tlsgd                O_md15  /* !tlsgd relocation */
+#define O_tlsldm       O_md16  /* !tlsldm relocation */
+#define O_gotdtprel    O_md17  /* !gotdtprel relocation */
+#define O_dtprelhi     O_md18  /* !dtprelhi relocation */
+#define O_dtprello     O_md19  /* !dtprello relocation */
+#define O_dtprel       O_md20  /* !dtprel relocation */
+#define O_gottprel     O_md21  /* !gottprel relocation */
+#define O_tprelhi      O_md22  /* !tprelhi relocation */
+#define O_tprello      O_md23  /* !tprello relocation */
+#define O_tprel                O_md24  /* !tprel relocation */
 
 #define DUMMY_RELOC_LITUSE_ADDR                (BFD_RELOC_UNUSED + 1)
 #define DUMMY_RELOC_LITUSE_BASE                (BFD_RELOC_UNUSED + 2)
 #define DUMMY_RELOC_LITUSE_BYTOFF      (BFD_RELOC_UNUSED + 3)
 #define DUMMY_RELOC_LITUSE_JSR         (BFD_RELOC_UNUSED + 4)
+#define DUMMY_RELOC_LITUSE_TLSGD       (BFD_RELOC_UNUSED + 5)
+#define DUMMY_RELOC_LITUSE_TLSLDM      (BFD_RELOC_UNUSED + 6)
 
 #define LITUSE_ADDR    0
 #define LITUSE_BASE    1
 #define LITUSE_BYTOFF  2
 #define LITUSE_JSR     3
+#define LITUSE_TLSGD   4
+#define LITUSE_TLSLDM  5
 
-#define USER_RELOC_P(R) ((R) >= O_literal && (R) <= O_samegp)
+#define USER_RELOC_P(R) ((R) >= O_literal && (R) <= O_tprel)
 
 /* Macros for extracting the type and number of encoded register tokens */
 
@@ -496,11 +511,23 @@ static const struct alpha_reloc_op_tag {
   DEF(lituse_base, DUMMY_RELOC_LITUSE_BASE, 1, 1),
   DEF(lituse_bytoff, DUMMY_RELOC_LITUSE_BYTOFF, 1, 1),
   DEF(lituse_jsr, DUMMY_RELOC_LITUSE_JSR, 1, 1),
+  DEF(lituse_tlsgd, DUMMY_RELOC_LITUSE_TLSGD, 1, 1),
+  DEF(lituse_tlsldm, DUMMY_RELOC_LITUSE_TLSLDM, 1, 1),
   DEF(gpdisp, BFD_RELOC_ALPHA_GPDISP, 1, 1),
   DEF(gprelhigh, BFD_RELOC_ALPHA_GPREL_HI16, 0, 0),
   DEF(gprellow, BFD_RELOC_ALPHA_GPREL_LO16, 0, 0),
   DEF(gprel, BFD_RELOC_GPREL16, 0, 0),
-  DEF(samegp, BFD_RELOC_ALPHA_BRSGP, 0, 0)
+  DEF(samegp, BFD_RELOC_ALPHA_BRSGP, 0, 0),
+  DEF(tlsgd, BFD_RELOC_ALPHA_TLSGD, 0, 1),
+  DEF(tlsldm, BFD_RELOC_ALPHA_TLSLDM, 0, 1),
+  DEF(gotdtprel, BFD_RELOC_ALPHA_GOTDTPREL16, 0, 0),
+  DEF(dtprelhi, BFD_RELOC_ALPHA_DTPREL_HI16, 0, 0),
+  DEF(dtprello, BFD_RELOC_ALPHA_DTPREL_LO16, 0, 0),
+  DEF(dtprel, BFD_RELOC_ALPHA_DTPREL16, 0, 0),
+  DEF(gottprel, BFD_RELOC_ALPHA_GOTTPREL16, 0, 0),
+  DEF(tprelhi, BFD_RELOC_ALPHA_TPREL_HI16, 0, 0),
+  DEF(tprello, BFD_RELOC_ALPHA_TPREL_LO16, 0, 0),
+  DEF(tprel, BFD_RELOC_ALPHA_TPREL16, 0, 0),
 };
 
 #undef DEF
@@ -515,12 +542,17 @@ static const int alpha_num_reloc_op
 /* Structure to hold explict sequence information.  */
 struct alpha_reloc_tag
 {
-  fixS *slaves;                        /* head of linked list of !literals */
+  fixS *master;                        /* the literal reloc */
+  fixS *slaves;                        /* head of linked list of lituses */
   segT segment;                        /* segment relocs are in or undefined_section*/
   long sequence;               /* sequence # */
   unsigned n_master;           /* # of literals */
   unsigned n_slaves;           /* # of lituses */
-  char multi_section_p;                /* True if more than one section was used */
+  unsigned saw_tlsgd : 1;      /* true if ... */
+  unsigned saw_tlsldm : 1;
+  unsigned saw_lu_tlsgd : 1;
+  unsigned saw_lu_tlsldm : 1;
+  unsigned multi_section_p : 1;        /* true if more than one section was used */
   char string[1];              /* printable form of sequence to hash with */
 };
 
@@ -1223,6 +1255,16 @@ md_apply_fix3 (fixP, valP, seg)
 
 #ifdef OBJ_ELF
     case BFD_RELOC_ALPHA_BRSGP:
+    case BFD_RELOC_ALPHA_TLSGD:
+    case BFD_RELOC_ALPHA_TLSLDM:
+    case BFD_RELOC_ALPHA_GOTDTPREL16:
+    case BFD_RELOC_ALPHA_DTPREL_HI16:
+    case BFD_RELOC_ALPHA_DTPREL_LO16:
+    case BFD_RELOC_ALPHA_DTPREL16:
+    case BFD_RELOC_ALPHA_GOTTPREL16:
+    case BFD_RELOC_ALPHA_TPREL_HI16:
+    case BFD_RELOC_ALPHA_TPREL_LO16:
+    case BFD_RELOC_ALPHA_TPREL16:
       return;
 #endif
 
@@ -1441,6 +1483,16 @@ alpha_force_relocation (f)
     case BFD_RELOC_ALPHA_BRSGP:
     case BFD_RELOC_VTABLE_INHERIT:
     case BFD_RELOC_VTABLE_ENTRY:
+    case BFD_RELOC_ALPHA_TLSGD:
+    case BFD_RELOC_ALPHA_TLSLDM:
+    case BFD_RELOC_ALPHA_GOTDTPREL16:
+    case BFD_RELOC_ALPHA_DTPREL_HI16:
+    case BFD_RELOC_ALPHA_DTPREL_LO16:
+    case BFD_RELOC_ALPHA_DTPREL16:
+    case BFD_RELOC_ALPHA_GOTTPREL16:
+    case BFD_RELOC_ALPHA_TPREL_HI16:
+    case BFD_RELOC_ALPHA_TPREL_LO16:
+    case BFD_RELOC_ALPHA_TPREL16:
       return 1;
 
     case BFD_RELOC_23_PCREL_S2:
@@ -1497,6 +1549,20 @@ alpha_fix_adjustable (f)
     case BFD_RELOC_ALPHA_HINT:
       return 1;
 
+    case BFD_RELOC_ALPHA_TLSGD:
+    case BFD_RELOC_ALPHA_TLSLDM:
+    case BFD_RELOC_ALPHA_GOTDTPREL16:
+    case BFD_RELOC_ALPHA_DTPREL_HI16:
+    case BFD_RELOC_ALPHA_DTPREL_LO16:
+    case BFD_RELOC_ALPHA_DTPREL16:
+    case BFD_RELOC_ALPHA_GOTTPREL16:
+    case BFD_RELOC_ALPHA_TPREL_HI16:
+    case BFD_RELOC_ALPHA_TPREL_LO16:
+    case BFD_RELOC_ALPHA_TPREL16:
+      /* ??? No idea why we can't return a reference to .tbss+10, but
+        we're preventing this in the other assemblers.  Follow for now.  */
+      return 0;
+
     default:
       return 1;
     }
@@ -1666,7 +1732,6 @@ alpha_adjust_symtab_relocs (abfd, sec, ptr)
   fixS *fixp;
   fixS *next;
   fixS *slave;
-  unsigned long n_slaves = 0;
 
   /* If seginfo is NULL, we did not create this section; don't do
      anything with it.  By using a pointer to a pointer, we can update
@@ -1689,21 +1754,39 @@ alpha_adjust_symtab_relocs (abfd, sec, ptr)
       switch (fixp->fx_r_type)
        {
        case BFD_RELOC_ALPHA_LITUSE:
-         n_slaves++;
          if (fixp->tc_fix_data.info->n_master == 0)
            as_bad_where (fixp->fx_file, fixp->fx_line,
                          _("No !literal!%ld was found"),
                          fixp->tc_fix_data.info->sequence);
+         if (fixp->fx_offset == LITUSE_TLSGD)
+           {
+             if (! fixp->tc_fix_data.info->saw_tlsgd)
+               as_bad_where (fixp->fx_file, fixp->fx_line,
+                             _("No !tlsgd!%ld was found"),
+                             fixp->tc_fix_data.info->sequence);
+           }
+         else if (fixp->fx_offset == LITUSE_TLSLDM)
+           {
+             if (! fixp->tc_fix_data.info->saw_tlsldm)
+               as_bad_where (fixp->fx_file, fixp->fx_line,
+                             _("No !tlsldm!%ld was found"),
+                             fixp->tc_fix_data.info->sequence);
+           }
          break;
 
        case BFD_RELOC_ALPHA_GPDISP_LO16:
-         n_slaves++;
          if (fixp->tc_fix_data.info->n_master == 0)
            as_bad_where (fixp->fx_file, fixp->fx_line,
                          _("No ldah !gpdisp!%ld was found"),
                          fixp->tc_fix_data.info->sequence);
          break;
 
+       case BFD_RELOC_ALPHA_ELF_LITERAL:
+         if (fixp->tc_fix_data.info->saw_tlsgd
+             || fixp->tc_fix_data.info->saw_tlsldm)
+           break;
+         /* FALLTHRU */
+
        default:
          *prevP = fixp;
          prevP = &fixp->fx_next;
@@ -1711,10 +1794,10 @@ alpha_adjust_symtab_relocs (abfd, sec, ptr)
        }
     }
 
-  /* If there were any dependent relocations, go and add them back to
-     the chain.  They are linked through the next_reloc field in
-     reverse order, so as we go through the next_reloc chain, we
-     effectively reverse the chain once again.
+  /* Go back and re-chain dependent relocations.  They are currently
+     linked through the next_reloc field in reverse order, so as we
+     go through the next_reloc chain, we effectively reverse the chain
+     once again.
 
      Except if there is more than one !literal for a given sequence
      number.  In that case, the programmer and/or compiler is not sure
@@ -1734,6 +1817,27 @@ alpha_adjust_symtab_relocs (abfd, sec, ptr)
       next = fixp->fx_next;
       switch (fixp->fx_r_type)
        {
+       case BFD_RELOC_ALPHA_TLSGD:
+       case BFD_RELOC_ALPHA_TLSLDM:
+         if (!fixp->tc_fix_data.info)
+           break;
+         if (fixp->tc_fix_data.info->n_master == 0)
+           break;
+         else if (fixp->tc_fix_data.info->n_master > 1)
+           {
+             as_bad_where (fixp->fx_file, fixp->fx_line,
+                           _("too many !literal!%ld for %s"),
+                           fixp->tc_fix_data.info->sequence,
+                           (fixp->fx_r_type == BFD_RELOC_ALPHA_TLSGD
+                            ? "!tlsgd" : "!tlsldm"));
+             break;
+           }
+
+         fixp->tc_fix_data.info->master->fx_next = fixp->fx_next;
+         fixp->fx_next = fixp->tc_fix_data.info->master;
+         fixp = fixp->fx_next;
+         /* FALLTHRU */
+
        case BFD_RELOC_ALPHA_ELF_LITERAL:
          if (fixp->tc_fix_data.info->n_master == 1
              && ! fixp->tc_fix_data.info->multi_section_p)
@@ -1820,15 +1924,23 @@ debug_exp (tok, ntok)
        case O_lituse_base:             name = "O_lituse_base";         break;
        case O_lituse_bytoff:           name = "O_lituse_bytoff";       break;
        case O_lituse_jsr:              name = "O_lituse_jsr";          break;
+       case O_lituse_tlsgd:            name = "O_lituse_tlsgd";        break;
+       case O_lituse_tlsldm:           name = "O_lituse_tlsldm";       break;
        case O_gpdisp:                  name = "O_gpdisp";              break;
        case O_gprelhigh:               name = "O_gprelhigh";           break;
        case O_gprellow:                name = "O_gprellow";            break;
        case O_gprel:                   name = "O_gprel";               break;
        case O_samegp:                  name = "O_samegp";              break;
-       case O_md13:                    name = "O_md13";                break;
-       case O_md14:                    name = "O_md14";                break;
-       case O_md15:                    name = "O_md15";                break;
-       case O_md16:                    name = "O_md16";                break;
+       case O_tlsgd:                   name = "O_tlsgd";               break;
+       case O_tlsldm:                  name = "O_tlsldm";              break;
+       case O_gotdtprel:               name = "O_gotdtprel";           break;
+       case O_dtprelhi:                name = "O_dtprelhi";            break;
+       case O_dtprello:                name = "O_dtprello";            break;
+       case O_dtprel:                  name = "O_dtprel";              break;
+       case O_gottprel:                name = "O_gottprel";            break;
+       case O_tprelhi:                 name = "O_tprelhi";             break;
+       case O_tprello:                 name = "O_tprello";             break;
+       case O_tprel:                   name = "O_tprel";               break;
        }
 
       fprintf (stderr, ", %s(%s, %s, %d)", name,
@@ -2479,7 +2591,7 @@ emit_insn (insn)
     {
       const struct alpha_operand *operand = (const struct alpha_operand *) 0;
       struct alpha_fixup *fixup = &insn->fixups[i];
-      struct alpha_reloc_tag *info;
+      struct alpha_reloc_tag *info = NULL;
       int size, pcrel;
       fixS *fixP;
 
@@ -2521,6 +2633,14 @@ emit_insn (insn)
        case BFD_RELOC_GPREL16:
        case BFD_RELOC_ALPHA_GPREL_HI16:
        case BFD_RELOC_ALPHA_GPREL_LO16:
+       case BFD_RELOC_ALPHA_GOTDTPREL16:
+       case BFD_RELOC_ALPHA_DTPREL_HI16:
+       case BFD_RELOC_ALPHA_DTPREL_LO16:
+       case BFD_RELOC_ALPHA_DTPREL16:
+       case BFD_RELOC_ALPHA_GOTTPREL16:
+       case BFD_RELOC_ALPHA_TPREL_HI16:
+       case BFD_RELOC_ALPHA_TPREL_LO16:
+       case BFD_RELOC_ALPHA_TPREL16:
          fixP->fx_no_overflow = 1;
          break;
 
@@ -2555,7 +2675,10 @@ emit_insn (insn)
        case BFD_RELOC_ALPHA_ELF_LITERAL:
          fixP->fx_no_overflow = 1;
 
+         if (insn->sequence == 0)
+           break;
          info = get_alpha_reloc_tag (insn->sequence);
+         info->master = fixP;
          info->n_master++;
          if (info->segment != now_seg)
            info->multi_section_p = 1;
@@ -2573,12 +2696,31 @@ emit_insn (insn)
          goto do_lituse;
        case DUMMY_RELOC_LITUSE_JSR:
          fixP->fx_offset = LITUSE_JSR;
+         goto do_lituse;
+       case DUMMY_RELOC_LITUSE_TLSGD:
+         fixP->fx_offset = LITUSE_TLSGD;
+         goto do_lituse;
+       case DUMMY_RELOC_LITUSE_TLSLDM:
+         fixP->fx_offset = LITUSE_TLSLDM;
+         goto do_lituse;
        do_lituse:
          fixP->fx_addsy = section_symbol (now_seg);
          fixP->fx_r_type = BFD_RELOC_ALPHA_LITUSE;
 
          info = get_alpha_reloc_tag (insn->sequence);
-         info->n_slaves++;
+         if (fixup->reloc == DUMMY_RELOC_LITUSE_TLSGD)
+           info->saw_lu_tlsgd = 1;
+         else if (fixup->reloc == DUMMY_RELOC_LITUSE_TLSLDM)
+           info->saw_lu_tlsldm = 1;
+         if (++info->n_slaves > 1)
+           {
+             if (info->saw_lu_tlsgd)
+               as_bad (_("too many lituse insns for !lituse_tlsgd!%ld"),
+                       insn->sequence);
+             else if (info->saw_lu_tlsldm)
+               as_bad (_("too many lituse insns for !lituse_tlsldm!%ld"),
+                       insn->sequence);
+           }
          fixP->tc_fix_data.info = info;
          fixP->tc_fix_data.next_reloc = info->slaves;
          info->slaves = fixP;
@@ -2586,6 +2728,38 @@ emit_insn (insn)
            info->multi_section_p = 1;
          break;
 
+       case BFD_RELOC_ALPHA_TLSGD:
+         fixP->fx_no_overflow = 1;
+
+         if (insn->sequence == 0)
+           break;
+         info = get_alpha_reloc_tag (insn->sequence);
+         if (info->saw_tlsgd)
+           as_bad (_("duplicate !tlsgd!%ld"), insn->sequence);
+         else if (info->saw_tlsldm)
+           as_bad (_("sequence number in use for !tlsldm!%ld"),
+                   insn->sequence);
+         else
+           info->saw_tlsgd = 1;
+         fixP->tc_fix_data.info = info;
+         break;
+
+       case BFD_RELOC_ALPHA_TLSLDM:
+         fixP->fx_no_overflow = 1;
+
+         if (insn->sequence == 0)
+           break;
+         info = get_alpha_reloc_tag (insn->sequence);
+         if (info->saw_tlsldm)
+           as_bad (_("duplicate !tlsldm!%ld"), insn->sequence);
+         else if (info->saw_tlsgd)
+           as_bad (_("sequence number in use for !tlsgd!%ld"),
+                   insn->sequence);
+         else
+           info->saw_tlsldm = 1;
+         fixP->tc_fix_data.info = info;
+         break;
+
        default:
          if ((int) fixup->reloc < 0)
            {
@@ -2715,7 +2889,6 @@ static const char * const extXh_op[] = { NULL,    "extwh", "extlh", "extqh" };
 static const char * const mskXl_op[] = { "mskbl", "mskwl", "mskll", "mskql" };
 static const char * const mskXh_op[] = { NULL,    "mskwh", "msklh", "mskqh" };
 static const char * const stX_op[] = { "stb", "stw", "stl", "stq" };
-static const char * const ldX_op[] = { "ldb", "ldw", "ldll", "ldq" };
 static const char * const ldXu_op[] = { "ldbu", "ldwu", NULL, NULL };
 
 /* Implement the ldgp macro.  */
index 9483caf..3a4c931 100644 (file)
@@ -107,6 +107,8 @@ typedef enum {
   /* machine dependent operators */
   O_md1,  O_md2,  O_md3,  O_md4,  O_md5,  O_md6,  O_md7,  O_md8,
   O_md9,  O_md10, O_md11, O_md12, O_md13, O_md14, O_md15, O_md16,
+  O_md17, O_md18, O_md19, O_md20, O_md21, O_md22, O_md23, O_md24,
+  O_md25, O_md26, O_md27, O_md28, O_md29, O_md30, O_md31, O_md32,
   /* this must be the largest value */
   O_max
 } operatorT;
index ff91d8d..9554e69 100644 (file)
@@ -1,3 +1,10 @@
+2002-05-30  Richard Henderson  <rth@redhat.com>
+
+       * gas/alpha/elf-tls-1.s, gas/alpha/elf-tls-1.d: New.
+       * gas/alpha/elf-tls-2.s, gas/alpha/elf-tls-1.l: New.
+       * gas/alpha/elf-tls-3.s, gas/alpha/elf-tls-1.l: New.
+       * gas/alpha/alpha.exp: Run them.
+
 2002-05-30  Tom Rix  <trix@redhat.com>
 
        * gas/d10v/d10v.exp: Add -gstabs packing, sequence control
index 89a6334..6564cf9 100644 (file)
@@ -29,6 +29,9 @@ if { [istarget alpha*-*-*] } then {
        run_dump_test "elf-reloc-4"
        run_dump_test "elf-reloc-5"
        run_list_test "elf-reloc-6" ""
+       run_dump_test "elf-tls-1"
+       run_list_test "elf-tls-2" ""
+       run_list_test "elf-tls-3" ""
     }
 
     run_dump_test "fp"
diff --git a/gas/testsuite/gas/alpha/elf-tls-1.d b/gas/testsuite/gas/alpha/elf-tls-1.d
new file mode 100644 (file)
index 0000000..7f80de7
--- /dev/null
@@ -0,0 +1,29 @@
+#objdump: -r
+#name: alpha elf-tls-1
+
+.*:     file format elf64-alpha
+
+RELOCATION RECORDS FOR \[\.text\]:
+OFFSET           TYPE              VALUE 
+0*0000004 TLSGD             a
+0*0000000 ELF_LITERAL       __tls_get_addr
+0*0000008 LITUSE            \.text\+0x0*0000004
+0*0000008 HINT              __tls_get_addr
+0*000000c HINT              __tls_get_addr
+0*0000014 TLSLDM            b
+0*0000010 ELF_LITERAL       __tls_get_addr
+0*000000c LITUSE            \.text\+0x0*0000005
+0*0000018 TLSGD             c
+0*000001c TLSLDM            d
+0*0000020 TLSGD             e
+0*0000024 TLSLDM            f
+0*0000028 GOTDTPREL         g
+0*000002c DTPRELHI          h
+0*0000030 DTPRELLO          i
+0*0000034 DTPREL16          j
+0*0000038 GOTTPREL          k
+0*000003c TPRELHI           l
+0*0000040 TPRELLO           m
+0*0000044 TPREL16           n
+
+
diff --git a/gas/testsuite/gas/alpha/elf-tls-1.s b/gas/testsuite/gas/alpha/elf-tls-1.s
new file mode 100644 (file)
index 0000000..42385d7
--- /dev/null
@@ -0,0 +1,24 @@
+       .set nomacro
+       ldq     $27, __tls_get_addr($29)        !literal!1
+       ldq     $16, a($29)                     !tlsgd!1
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsgd!1
+
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsldm!2
+       ldq     $27, __tls_get_addr($29)        !literal!2
+       ldq     $16, b($29)                     !tlsldm!2
+
+       ldq     $16, c($29)                     !tlsgd
+       ldq     $16, d($29)                     !tlsldm
+
+       ldq     $16, e($29)                     !tlsgd!3
+       ldq     $16, f($29)                     !tlsldm!4
+
+       ldq     $16, g($29)                     !gotdtprel
+       ldah    $16, h($31)                     !dtprelhi
+       lda     $16, i($16)                     !dtprello
+       lda     $16, j($31)                     !dtprel
+
+       ldq     $16, k($29)                     !gottprel
+       ldah    $16, l($31)                     !tprelhi
+       lda     $16, m($16)                     !tprello
+       lda     $16, n($31)                     !tprel
diff --git a/gas/testsuite/gas/alpha/elf-tls-2.l b/gas/testsuite/gas/alpha/elf-tls-2.l
new file mode 100644 (file)
index 0000000..4fcee79
--- /dev/null
@@ -0,0 +1,9 @@
+.*: Assembler messages:
+.*:5: Error: too many lituse insns for !lituse_tlsgd!1
+.*:10: Error: too many lituse insns for !lituse_tlsldm!2
+.*:15: Error: too many lituse insns for !lituse_tlsgd!3
+.*:20: Error: too many lituse insns for !lituse_tlsldm!4
+.*:23: Error: duplicate !tlsgd!5
+.*:26: Error: duplicate !tlsldm!6
+.*:29: Error: sequence number in use for !tlsgd!7
+.*:32: Error: sequence number in use for !tlsldm!8
diff --git a/gas/testsuite/gas/alpha/elf-tls-2.s b/gas/testsuite/gas/alpha/elf-tls-2.s
new file mode 100644 (file)
index 0000000..214fe3a
--- /dev/null
@@ -0,0 +1,32 @@
+       .set nomacro
+       ldq     $16, c($29)                     !tlsgd!1
+       ldq     $27, __tls_get_addr($29)        !literal!1
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsgd!1
+       jsr     $26, ($27), __tls_get_addr      !lituse_jsr!1
+
+       ldq     $16, d($29)                     !tlsldm!2
+       ldq     $27, __tls_get_addr($29)        !literal!2
+       jsr     $26, ($27), __tls_get_addr      !lituse_jsr!2
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsldm!2
+
+       ldq     $16, g($29)                     !tlsgd!3
+       ldq     $27, __tls_get_addr($29)        !literal!3
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsgd!3
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsgd!3
+       
+       ldq     $16, h($29)                     !tlsldm!4
+       ldq     $27, __tls_get_addr($29)        !literal!4
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsldm!4
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsldm!4
+
+       ldq     $16, i($29)                     !tlsgd!5
+       ldq     $16, i($29)                     !tlsgd!5
+
+       ldq     $16, j($29)                     !tlsldm!6
+       ldq     $16, j($29)                     !tlsldm!6
+
+       ldq     $16, k($29)                     !tlsgd!7
+       ldq     $16, k($29)                     !tlsldm!7
+
+       ldq     $16, l($29)                     !tlsldm!8
+       ldq     $16, l($29)                     !tlsgd!8
diff --git a/gas/testsuite/gas/alpha/elf-tls-3.l b/gas/testsuite/gas/alpha/elf-tls-3.l
new file mode 100644 (file)
index 0000000..51d93e7
--- /dev/null
@@ -0,0 +1,7 @@
+.*: Assembler messages:
+.*:3: Error: No !tlsgd!1 was found
+.*:6: Error: No !tlsldm!2 was found
+.*:18: Error: No !tlsldm!5 was found
+.*:22: Error: No !tlsgd!6 was found
+.*:8: Error: too many !literal!3 for !tlsgd
+.*:12: Error: too many !literal!4 for !tlsldm
diff --git a/gas/testsuite/gas/alpha/elf-tls-3.s b/gas/testsuite/gas/alpha/elf-tls-3.s
new file mode 100644 (file)
index 0000000..31480da
--- /dev/null
@@ -0,0 +1,22 @@
+       .set nomacro
+       ldq     $27, __tls_get_addr($29)        !literal!1
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsgd!1
+
+       ldq     $27, __tls_get_addr($29)        !literal!2
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsldm!2
+
+       ldq     $16, a($29)                     !tlsgd!3
+       ldq     $27, __tls_get_addr($29)        !literal!3
+       ldq     $27, __tls_get_addr($29)        !literal!3
+       
+       ldq     $16, b($29)                     !tlsldm!4
+       ldq     $27, __tls_get_addr($29)        !literal!4
+       ldq     $27, __tls_get_addr($29)        !literal!4
+
+       ldq     $16, e($29)                     !tlsgd!5
+       ldq     $27, __tls_get_addr($29)        !literal!5
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsldm!5
+       
+       ldq     $16, f($29)                     !tlsldm!6
+       ldq     $27, __tls_get_addr($29)        !literal!6
+       jsr     $26, ($27), __tls_get_addr      !lituse_tlsgd!6
index 3be8a53..905bad1 100644 (file)
@@ -1,3 +1,10 @@
+2002-05-30  Richard Henderson  <rth@redhat.com>
+
+       * alpha.h (R_ALPHA_TLSGD, R_ALPHA_TLSLDM, R_ALPHA_DTPMOD64,
+       R_ALPHA_GOTDTPREL, R_ALPHA_DTPREL64, R_ALPHA_DTPRELHI,
+       R_ALPHA_DTPRELLO, R_ALPHA_DTPREL16, R_ALPHA_GOTTPREL, R_ALPHA_TPREL64,
+       R_ALPHA_TPRELHI, R_ALPHA_TPRELLO, R_ALPHA_TPREL16): New.
+
 2002-05-29  Matt Thomas  <matt@3am-software.com>
 
        * vax.h: New file
index e937b81..70168d6 100644 (file)
@@ -99,6 +99,21 @@ START_RELOC_NUMBERS (elf_alpha_reloc_type)
      STO_ALPHA_STD_GPLOAD.  */
   RELOC_NUMBER (R_ALPHA_BRSGP, 28)
 
+  /* Thread-Local Storage.  */
+  RELOC_NUMBER (R_ALPHA_TLSGD, 29)
+  RELOC_NUMBER (R_ALPHA_TLSLDM, 30)
+  RELOC_NUMBER (R_ALPHA_DTPMOD64, 31)
+  RELOC_NUMBER (R_ALPHA_GOTDTPREL, 32)
+  RELOC_NUMBER (R_ALPHA_DTPREL64, 33)
+  RELOC_NUMBER (R_ALPHA_DTPRELHI, 34)
+  RELOC_NUMBER (R_ALPHA_DTPRELLO, 35)
+  RELOC_NUMBER (R_ALPHA_DTPREL16, 36)
+  RELOC_NUMBER (R_ALPHA_GOTTPREL, 37)
+  RELOC_NUMBER (R_ALPHA_TPREL64, 38)
+  RELOC_NUMBER (R_ALPHA_TPRELHI, 39)
+  RELOC_NUMBER (R_ALPHA_TPRELLO, 40)
+  RELOC_NUMBER (R_ALPHA_TPREL16, 41)
+
 END_RELOC_NUMBERS (R_ALPHA_max)
 
 #endif /* _ELF_ALPHA_H */