summaryrefslogtreecommitdiff
path: root/bfd/elfxx-mips.c
diff options
context:
space:
mode:
authorDaniel Jacobowitz <dan@debian.org>2005-03-02 21:23:21 +0000
committerDaniel Jacobowitz <dan@debian.org>2005-03-02 21:23:21 +0000
commit61f6f9f4779e32c93e5813f0609a158a05ecee1d (patch)
tree29e33de79cab0538d2014230ebddb1ddefecf5ac /bfd/elfxx-mips.c
parentb45b4052c1cfe365d7a03914e566441e6508e9f7 (diff)
downloadbinutils-redhat-61f6f9f4779e32c93e5813f0609a158a05ecee1d.tar.gz
* elfxx-mips.c (struct mips_got_entry): Add tls_type.
(struct mips_got_info): Add tls_gotno, tls_assigned_gotno, and tls_ldm_offset. (struct mips_elf_got_per_bfd_arg): Add global_count. (struct mips_elf_count_tls_arg): New. (struct mips_elf_hash_sort_data): Update comment for min_got_dynindx. (struct mips_elf_link_hash_entry): Add tls_type and tls_got_offset. (GOT_NORMAL, GOT_TLS_GD, GOT_TLS_LDM, GOT_TLS_IE) (GOT_TLS_OFFSET_DONE, GOT_TLS_DONE): Define. (TLS_RELOC_P): Define. (TP_OFFSET, DTP_OFFSET): Define. (dtprel_base, tprel_base): New functions. (mips_elf_link_hash_newfunc): Initialize tls_type. (mips_elf_got_entry_hash, mips_elf_got_entry_eq) (mips_elf_multi_got_entry_hash, mips_elf_multi_got_entry_eq): Handle TLS entries. (mips_tls_got_relocs, mips_elf_count_local_tls_relocs) (mips_elf_count_global_tls_entries, mips_elf_count_global_tls_relocs) (mips_elf_output_dynamic_relocation, mips_elf_initialize_tls_slots) (mips_tls_got_index): New functions. (mips_elf_local_got_index): Add new R_SYMNDX, H, and R_TYPE arguments. Pass them to mips_elf_create_local_got_entry. Use mips_tls_got_index. (mips_elf_global_got_index): Add new R_TYPE and INFO arguments. Handle TLS entries. (mips_elf_got_page, mips_elf_got16_entry): Update calls to mips_elf_create_local_got_entry. (mips_elf_create_local_got_entry): Add new R_SYMNDX, H, and R_TYPE arguments. Handle TLS entries. (mips_elf_sort_hash_table_f): Add non-TLS assertions. (mips_elf_record_local_got_symbol): Add new TLS_FLAG argument. Handle TLS entries. (mips_elf_record_global_got_symbol): Likewise. (mips_elf_make_got_per_bfd): Initialize new mips_got_info members. Count TLS entries. (mips_elf_merge_gots): Handle TLS entries when merging. (mips_elf_initialize_tls_index): New function. (mips_elf_set_global_got_offset): Handle TLS entries. (mips_elf_adjust_gp): Handle TLS. (mips_elf_multi_got): Remove redundant call to mips_elf_resolve_final_got_entries. Initialize global_count. Correct a comment. Initialize new TLS members of mips_got_info. Assign TLS GOT indexes for new GOTs. (mips_elf_create_got_section): Initialize new TLS members of mips_got_info. (mips_elf_calculate_relocation): Handle TLS relocs. (_bfd_mips_elf_check_relocs): Likewise. Update calls to changed functions. (_bfd_mips_elf_always_size_sections): Handle TLS. (_bfd_mips_elf_size_dynamic_sections): Likewise. (_bfd_mips_elf_finish_dynamic_symbol): Likewise. Update calls to changed functions. (_bfd_mips_elf_copy_indirect_symbol): Copy tls_type. (_bfd_mips_elf_hide_symbol): Handle TLS. * elfn32-mips.c (elf_mips_howto_table_rel, elf_mips_howto_table_rela) (mips_reloc_map): Add TLS relocs. * elf32-mips.c (elf_mips_howto_table_rel, mips_reloc_map): Likewise. * elf64-mips.c (mips_elf64_howto_table_rel) (mips_elf64_howto_table_rela, mips_reloc_map): Likewise. * reloc.c: Define new MIPS TLS relocations. * libbfd.h, bfd-in2.h: Regenerated.
Diffstat (limited to 'bfd/elfxx-mips.c')
-rw-r--r--bfd/elfxx-mips.c872
1 files changed, 805 insertions, 67 deletions
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index ccd1c23e5c..5c2e5b4f25 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -64,6 +64,14 @@ struct mips_got_entry
h->forced_local). */
struct mips_elf_link_hash_entry *h;
} d;
+
+ /* The TLS types included in this GOT entry (specifically, GD and
+ IE). The GD and IE flags can be added as we encounter new
+ relocations. LDM can also be set; it will always be alone, not
+ combined with any GD or IE flags. An LDM GOT entry will be
+ a local symbol entry with r_symndx == 0. */
+ unsigned char tls_type;
+
/* The offset from the beginning of the .got section to the entry
corresponding to this symbol+addend. If it's a global symbol
whose offset is yet to be decided, it's going to be -1. */
@@ -79,6 +87,11 @@ struct mips_got_info
struct elf_link_hash_entry *global_gotsym;
/* The number of global .got entries. */
unsigned int global_gotno;
+ /* The number of .got slots used for TLS. */
+ unsigned int tls_gotno;
+ /* The first unused TLS .got entry. Used only during
+ mips_elf_initialize_tls_index. */
+ unsigned int tls_assigned_gotno;
/* The number of local .got entries. */
unsigned int local_gotno;
/* The number of local .got entries we have used. */
@@ -91,6 +104,11 @@ struct mips_got_info
/* In multi-got links, a pointer to the next got (err, rather, most
of the time, it points to the previous got). */
struct mips_got_info *next;
+ /* This is the GOT index of the TLS LDM entry for the GOT, MINUS_ONE
+ for none, or MINUS_TWO for not yet assigned. This is needed
+ because a single-GOT link may have multiple hash table entries
+ for the LDM. It does not get initialized in multi-GOT mode. */
+ bfd_vma tls_ldm_offset;
};
/* Map an input bfd to a got in a multi-got link. */
@@ -125,6 +143,11 @@ struct mips_elf_got_per_bfd_arg
unsigned int primary_count;
/* The number of local and global entries in the current got. */
unsigned int current_count;
+ /* The total number of global entries which will live in the
+ primary got and be automatically relocated. This includes
+ those not referenced by the primary GOT but included in
+ the "master" GOT. */
+ unsigned int global_count;
};
/* Another structure used to pass arguments for got entries traversal. */
@@ -137,6 +160,15 @@ struct mips_elf_set_global_got_offset_arg
struct bfd_link_info *info;
};
+/* A structure used to count TLS relocations or GOT entries, for GOT
+ entry or ELF symbol table traversal. */
+
+struct mips_elf_count_tls_arg
+{
+ struct bfd_link_info *info;
+ unsigned int needed;
+};
+
struct _mips_elf_section_data
{
struct bfd_elf_section_data elf;
@@ -158,8 +190,8 @@ struct mips_elf_hash_sort_data
/* The symbol in the global GOT with the lowest dynamic symbol table
index. */
struct elf_link_hash_entry *low;
- /* The least dynamic symbol table index corresponding to a symbol
- with a GOT entry. */
+ /* The least dynamic symbol table index corresponding to a non-TLS
+ symbol with a GOT entry. */
long min_got_dynindx;
/* The greatest dynamic symbol table index corresponding to a symbol
with a GOT entry that is not referenced (e.g., a dynamic symbol
@@ -212,6 +244,21 @@ struct mips_elf_link_hash_entry
/* Are we forced local? .*/
bfd_boolean forced_local;
+
+#define GOT_NORMAL 0
+#define GOT_TLS_GD 1
+#define GOT_TLS_LDM 2
+#define GOT_TLS_IE 4
+#define GOT_TLS_OFFSET_DONE 0x40
+#define GOT_TLS_DONE 0x80
+ unsigned char tls_type;
+ /* This is only used in single-GOT mode; in multi-GOT mode there
+ is one mips_got_entry per GOT entry, so the offset is stored
+ there. In single-GOT mode there may be many mips_got_entry
+ structures all referring to the same GOT slot. It might be
+ possible to use root.got.offset instead, but that field is
+ overloaded already. */
+ bfd_vma tls_got_offset;
};
/* MIPS ELF linker hash table. */
@@ -237,6 +284,21 @@ struct mips_elf_link_hash_table
bfd_boolean mips16_stubs_seen;
};
+#define TLS_RELOC_P(r_type) \
+ (r_type == R_MIPS_TLS_DTPMOD32 \
+ || r_type == R_MIPS_TLS_DTPMOD64 \
+ || r_type == R_MIPS_TLS_DTPREL32 \
+ || r_type == R_MIPS_TLS_DTPREL64 \
+ || r_type == R_MIPS_TLS_GD \
+ || r_type == R_MIPS_TLS_LDM \
+ || r_type == R_MIPS_TLS_DTPREL_HI16 \
+ || r_type == R_MIPS_TLS_DTPREL_LO16 \
+ || r_type == R_MIPS_TLS_GOTTPREL \
+ || r_type == R_MIPS_TLS_TPREL32 \
+ || r_type == R_MIPS_TLS_TPREL64 \
+ || r_type == R_MIPS_TLS_TPREL_HI16 \
+ || r_type == R_MIPS_TLS_TPREL_LO16)
+
/* Structure used to pass information to mips_elf_output_extsym. */
struct extsym_info
@@ -370,7 +432,8 @@ typedef struct runtime_pdr {
#define rpdNil ((pRPDR) 0)
static struct mips_got_entry *mips_elf_create_local_got_entry
- (bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma);
+ (bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma, unsigned long,
+ struct mips_elf_link_hash_entry *, int);
static bfd_boolean mips_elf_sort_hash_table_f
(struct mips_elf_link_hash_entry *, void *);
static bfd_vma mips_elf_high
@@ -588,6 +651,30 @@ static bfd *reldyn_sorting_bfd;
#define mips_elf_hash_table(p) \
((struct mips_elf_link_hash_table *) ((p)->hash))
+/* Find the base offsets for thread-local storage in this object,
+ for GD/LD and IE/LE respectively. */
+
+#define TP_OFFSET 0x7000
+#define DTP_OFFSET 0x8000
+
+static bfd_vma
+dtprel_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma + DTP_OFFSET;
+}
+
+static bfd_vma
+tprel_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma + TP_OFFSET;
+}
+
/* Create an entry in a MIPS ELF linker hash table. */
static struct bfd_hash_entry *
@@ -623,6 +710,7 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
ret->call_stub = NULL;
ret->call_fp_stub = NULL;
ret->forced_local = FALSE;
+ ret->tls_type = GOT_NORMAL;
}
return (struct bfd_hash_entry *) ret;
@@ -1738,6 +1826,7 @@ mips_elf_got_entry_hash (const void *entry_)
const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
return entry->symndx
+ + ((entry->tls_type & GOT_TLS_LDM) << 17)
+ (! entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address)
: entry->abfd->id
+ (entry->symndx >= 0 ? mips_elf_hash_bfd_vma (entry->d.addend)
@@ -1750,6 +1839,10 @@ mips_elf_got_entry_eq (const void *entry1, const void *entry2)
const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
+ /* An LDM entry can only match another LDM entry. */
+ if ((e1->tls_type ^ e2->tls_type) & GOT_TLS_LDM)
+ return 0;
+
return e1->abfd == e2->abfd && e1->symndx == e2->symndx
&& (! e1->abfd ? e1->d.address == e2->d.address
: e1->symndx >= 0 ? e1->d.addend == e2->d.addend
@@ -1770,8 +1863,10 @@ mips_elf_multi_got_entry_hash (const void *entry_)
+ (! entry->abfd
? mips_elf_hash_bfd_vma (entry->d.address)
: entry->symndx >= 0
- ? (entry->abfd->id
- + mips_elf_hash_bfd_vma (entry->d.addend))
+ ? ((entry->tls_type & GOT_TLS_LDM)
+ ? (GOT_TLS_LDM << 17)
+ : (entry->abfd->id
+ + mips_elf_hash_bfd_vma (entry->d.addend)))
: entry->d.h->root.root.root.hash);
}
@@ -1781,6 +1876,14 @@ mips_elf_multi_got_entry_eq (const void *entry1, const void *entry2)
const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
+ /* Any two LDM entries match. */
+ if (e1->tls_type & e2->tls_type & GOT_TLS_LDM)
+ return 1;
+
+ /* Nothing else matches an LDM entry. */
+ if ((e1->tls_type ^ e2->tls_type) & GOT_TLS_LDM)
+ return 0;
+
return e1->symndx == e2->symndx
&& (e1->symndx >= 0 ? e1->abfd == e2->abfd && e1->d.addend == e2->d.addend
: e1->abfd == NULL || e2->abfd == NULL
@@ -1849,13 +1952,299 @@ mips_elf_got_info (bfd *abfd, asection **sgotp)
return g;
}
+/* Count the number of relocations needed for a TLS GOT entry, with
+ access types from TLS_TYPE, and symbol H (or a local symbol if H
+ is NULL). */
+
+static int
+mips_tls_got_relocs (struct bfd_link_info *info, unsigned char tls_type,
+ struct elf_link_hash_entry *h)
+{
+ int indx = 0;
+ int ret = 0;
+ bfd_boolean need_relocs = FALSE;
+ bfd_boolean dyn = elf_hash_table (info)->dynamic_sections_created;
+
+ if (h && WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, h)))
+ indx = h->dynindx;
+
+ if ((info->shared || indx != 0)
+ && (h == NULL
+ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ need_relocs = TRUE;
+
+ if (!need_relocs)
+ return FALSE;
+
+ if (tls_type & GOT_TLS_GD)
+ {
+ ret++;
+ if (indx != 0)
+ ret++;
+ }
+
+ if (tls_type & GOT_TLS_IE)
+ ret++;
+
+ if ((tls_type & GOT_TLS_LDM) && info->shared)
+ ret++;
+
+ return ret;
+}
+
+/* Count the number of TLS relocations required for the GOT entry in
+ ARG1, if it describes a local symbol. */
+
+static int
+mips_elf_count_local_tls_relocs (void **arg1, void *arg2)
+{
+ struct mips_got_entry *entry = * (struct mips_got_entry **) arg1;
+ struct mips_elf_count_tls_arg *arg = arg2;
+
+ if (entry->abfd != NULL && entry->symndx != -1)
+ arg->needed += mips_tls_got_relocs (arg->info, entry->tls_type, NULL);
+
+ return 1;
+}
+
+/* Count the number of TLS GOT entries required for the global (or
+ forced-local) symbol in ARG1. */
+
+static int
+mips_elf_count_global_tls_entries (void *arg1, void *arg2)
+{
+ struct mips_elf_link_hash_entry *hm
+ = (struct mips_elf_link_hash_entry *) arg1;
+ struct mips_elf_count_tls_arg *arg = arg2;
+
+ if (hm->tls_type & GOT_TLS_GD)
+ arg->needed += 2;
+ if (hm->tls_type & GOT_TLS_IE)
+ arg->needed += 1;
+
+ return 1;
+}
+
+/* Count the number of TLS relocations required for the global (or
+ forced-local) symbol in ARG1. */
+
+static int
+mips_elf_count_global_tls_relocs (void *arg1, void *arg2)
+{
+ struct mips_elf_link_hash_entry *hm
+ = (struct mips_elf_link_hash_entry *) arg1;
+ struct mips_elf_count_tls_arg *arg = arg2;
+
+ arg->needed += mips_tls_got_relocs (arg->info, hm->tls_type, &hm->root);
+
+ return 1;
+}
+
+/* Output a simple dynamic relocation into SRELOC. */
+
+static void
+mips_elf_output_dynamic_relocation (bfd *output_bfd,
+ asection *sreloc,
+ unsigned long indx,
+ int r_type,
+ bfd_vma offset)
+{
+ Elf_Internal_Rela rel[3];
+
+ memset (rel, 0, sizeof (rel));
+
+ rel[0].r_info = ELF_R_INFO (output_bfd, indx, r_type);
+ rel[0].r_offset = rel[1].r_offset = rel[2].r_offset = offset;
+
+ if (ABI_64_P (output_bfd))
+ {
+ (*get_elf_backend_data (output_bfd)->s->swap_reloc_out)
+ (output_bfd, &rel[0],
+ (sreloc->contents
+ + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
+ }
+ else
+ bfd_elf32_swap_reloc_out
+ (output_bfd, &rel[0],
+ (sreloc->contents
+ + sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+ ++sreloc->reloc_count;
+}
+
+/* Initialize a set of TLS GOT entries for one symbol. */
+
+static void
+mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
+ unsigned char *tls_type_p,
+ struct bfd_link_info *info,
+ struct mips_elf_link_hash_entry *h,
+ bfd_vma value)
+{
+ int indx;
+ asection *sreloc, *sgot;
+ bfd_vma offset, offset2;
+ bfd *dynobj;
+ bfd_boolean need_relocs = FALSE;
+
+ dynobj = elf_hash_table (info)->dynobj;
+ sgot = mips_elf_got_section (dynobj, FALSE);
+
+ indx = 0;
+ if (h != NULL)
+ {
+ bfd_boolean dyn = elf_hash_table (info)->dynamic_sections_created;
+
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, &h->root)
+ && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, &h->root)))
+ indx = h->root.dynindx;
+ }
+
+ if (*tls_type_p & GOT_TLS_DONE)
+ return;
+
+ if ((info->shared || indx != 0)
+ && (h == NULL
+ || ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ need_relocs = TRUE;
+
+ /* MINUS_ONE means the symbol is not defined in this object. It may not
+ be defined at all; assume that the value doesn't matter in that
+ case. Otherwise complain if we would use the value. */
+ BFD_ASSERT (value != MINUS_ONE || (indx != 0 && need_relocs)
+ || h->root.root.type == bfd_link_hash_undefweak);
+
+ /* Emit necessary relocations. */
+ sreloc = mips_elf_rel_dyn_section (dynobj, FALSE);
+
+ /* General Dynamic. */
+ if (*tls_type_p & GOT_TLS_GD)
+ {
+ offset = got_offset;
+ offset2 = offset + MIPS_ELF_GOT_SIZE (abfd);
+
+ if (need_relocs)
+ {
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, indx,
+ ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
+ sgot->output_offset + sgot->output_section->vma + offset);
+
+ if (indx)
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, indx,
+ ABI_64_P (abfd) ? R_MIPS_TLS_DTPREL64 : R_MIPS_TLS_DTPREL32,
+ sgot->output_offset + sgot->output_section->vma + offset2);
+ else
+ MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
+ sgot->contents + offset2);
+ }
+ else
+ {
+ MIPS_ELF_PUT_WORD (abfd, 1,
+ sgot->contents + offset);
+ MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
+ sgot->contents + offset2);
+ }
+
+ got_offset += 2 * MIPS_ELF_GOT_SIZE (abfd);
+ }
+
+ /* Initial Exec model. */
+ if (*tls_type_p & GOT_TLS_IE)
+ {
+ offset = got_offset;
+
+ if (need_relocs)
+ {
+ if (indx == 0)
+ MIPS_ELF_PUT_WORD (abfd, value - elf_hash_table (info)->tls_sec->vma,
+ sgot->contents + offset);
+ else
+ MIPS_ELF_PUT_WORD (abfd, 0,
+ sgot->contents + offset);
+
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, indx,
+ ABI_64_P (abfd) ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32,
+ sgot->output_offset + sgot->output_section->vma + offset);
+ }
+ else
+ MIPS_ELF_PUT_WORD (abfd, value - tprel_base (info),
+ sgot->contents + offset);
+ }
+
+ if (*tls_type_p & GOT_TLS_LDM)
+ {
+ /* The initial offset is zero, and the LD offsets will include the
+ bias by DTP_OFFSET. */
+ MIPS_ELF_PUT_WORD (abfd, 0,
+ sgot->contents + got_offset
+ + MIPS_ELF_GOT_SIZE (abfd));
+
+ if (!info->shared)
+ MIPS_ELF_PUT_WORD (abfd, 1,
+ sgot->contents + got_offset);
+ else
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, indx,
+ ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
+ sgot->output_offset + sgot->output_section->vma + got_offset);
+ }
+
+ *tls_type_p |= GOT_TLS_DONE;
+}
+
+/* Return the GOT index to use for a relocation of type R_TYPE against
+ a symbol accessed using TLS_TYPE models. The GOT entries for this
+ symbol in this GOT start at GOT_INDEX. This function initializes the
+ GOT entries and corresponding relocations. */
+
+static bfd_vma
+mips_tls_got_index (bfd *abfd, bfd_vma got_index, unsigned char *tls_type,
+ int r_type, struct bfd_link_info *info,
+ struct mips_elf_link_hash_entry *h, bfd_vma symbol)
+{
+ BFD_ASSERT (r_type == R_MIPS_TLS_GOTTPREL || r_type == R_MIPS_TLS_GD
+ || r_type == R_MIPS_TLS_LDM);
+
+ mips_elf_initialize_tls_slots (abfd, got_index, tls_type, info, h, symbol);
+
+ if (r_type == R_MIPS_TLS_GOTTPREL)
+ {
+ BFD_ASSERT (*tls_type & GOT_TLS_IE);
+ if (*tls_type & GOT_TLS_GD)
+ return got_index + 2 * MIPS_ELF_GOT_SIZE (abfd);
+ else
+ return got_index;
+ }
+
+ if (r_type == R_MIPS_TLS_GD)
+ {
+ BFD_ASSERT (*tls_type & GOT_TLS_GD);
+ return got_index;
+ }
+
+ if (r_type == R_MIPS_TLS_LDM)
+ {
+ BFD_ASSERT (*tls_type & GOT_TLS_LDM);
+ return got_index;
+ }
+
+ return got_index;
+}
+
/* Returns the GOT offset at which the indicated address can be found.
- If there is not yet a GOT entry for this value, create one. Returns
- -1 if no satisfactory GOT offset can be found. */
+ If there is not yet a GOT entry for this value, create one. If
+ R_SYMNDX refers to a TLS symbol, create a TLS GOT entry instead.
+ Returns -1 if no satisfactory GOT offset can be found. */
static bfd_vma
mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
- bfd_vma value)
+ bfd_vma value, unsigned long r_symndx,
+ struct mips_elf_link_hash_entry *h, int r_type)
{
asection *sgot;
struct mips_got_info *g;
@@ -1863,17 +2252,23 @@ mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
- entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
- if (entry)
- return entry->gotidx;
- else
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value,
+ r_symndx, h, r_type);
+ if (!entry)
return MINUS_ONE;
+
+ if (TLS_RELOC_P (r_type))
+ return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type, r_type,
+ info, h, value);
+ else
+ return entry->gotidx;
}
/* Returns the GOT index for the global symbol indicated by H. */
static bfd_vma
-mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h)
+mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h,
+ int r_type, struct bfd_link_info *info)
{
bfd_vma index;
asection *sgot;
@@ -1888,29 +2283,64 @@ mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h)
BFD_ASSERT (h->dynindx >= 0);
g = mips_elf_got_for_ibfd (g, ibfd);
- if (g->next != gg)
+ if (g->next != gg || TLS_RELOC_P (r_type))
{
e.abfd = ibfd;
e.symndx = -1;
e.d.h = (struct mips_elf_link_hash_entry *)h;
+ e.tls_type = 0;
p = htab_find (g->got_entries, &e);
BFD_ASSERT (p->gotidx > 0);
- return p->gotidx;
+
+ if (TLS_RELOC_P (r_type))
+ {
+ bfd_vma value = MINUS_ONE;
+ if ((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && h->root.u.def.section->output_section)
+ value = (h->root.u.def.value
+ + h->root.u.def.section->output_offset
+ + h->root.u.def.section->output_section->vma);
+
+ return mips_tls_got_index (abfd, p->gotidx, &p->tls_type, r_type,
+ info, e.d.h, value);
+ }
+ else
+ return p->gotidx;
}
}
if (gg->global_gotsym != NULL)
global_got_dynindx = gg->global_gotsym->dynindx;
- /* Once we determine the global GOT entry with the lowest dynamic
- symbol table index, we must put all dynamic symbols with greater
- indices into the GOT. That makes it easy to calculate the GOT
- offset. */
- BFD_ASSERT (h->dynindx >= global_got_dynindx);
- index = ((h->dynindx - global_got_dynindx + g->local_gotno)
- * MIPS_ELF_GOT_SIZE (abfd));
+ if (TLS_RELOC_P (r_type))
+ {
+ struct mips_elf_link_hash_entry *hm
+ = (struct mips_elf_link_hash_entry *) h;
+ bfd_vma value = MINUS_ONE;
+
+ if ((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && h->root.u.def.section->output_section)
+ value = (h->root.u.def.value
+ + h->root.u.def.section->output_offset
+ + h->root.u.def.section->output_section->vma);
+
+ index = mips_tls_got_index (abfd, hm->tls_got_offset, &hm->tls_type,
+ r_type, info, hm, value);
+ }
+ else
+ {
+ /* Once we determine the global GOT entry with the lowest dynamic
+ symbol table index, we must put all dynamic symbols with greater
+ indices into the GOT. That makes it easy to calculate the GOT
+ offset. */
+ BFD_ASSERT (h->dynindx >= global_got_dynindx);
+ index = ((h->dynindx - global_got_dynindx + g->local_gotno)
+ * MIPS_ELF_GOT_SIZE (abfd));
+ }
BFD_ASSERT (index < sgot->size);
return index;
@@ -1935,7 +2365,8 @@ mips_elf_got_page (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot,
(value + 0x8000)
- & (~(bfd_vma)0xffff));
+ & (~(bfd_vma)0xffff), 0,
+ NULL, R_MIPS_GOT_PAGE);
if (!entry)
return MINUS_ONE;
@@ -1970,7 +2401,8 @@ mips_elf_got16_entry (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
- entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value, 0, NULL,
+ R_MIPS_GOT16);
if (entry)
return entry->gotidx;
else
@@ -1996,12 +2428,16 @@ mips_elf_got_offset_from_index (bfd *dynobj, bfd *output_bfd,
}
/* Create a local GOT entry for VALUE. Return the index of the entry,
- or -1 if it could not be created. */
+ or -1 if it could not be created. If R_SYMNDX refers to a TLS symbol,
+ create a TLS entry instead. */
static struct mips_got_entry *
mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd,
struct mips_got_info *gg,
- asection *sgot, bfd_vma value)
+ asection *sgot, bfd_vma value,
+ unsigned long r_symndx,
+ struct mips_elf_link_hash_entry *h,
+ int r_type)
{
struct mips_got_entry entry, **loc;
struct mips_got_info *g;
@@ -2009,6 +2445,7 @@ mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd,
entry.abfd = NULL;
entry.symndx = -1;
entry.d.address = value;
+ entry.tls_type = 0;
g = mips_elf_got_for_ibfd (gg, ibfd);
if (g == NULL)
@@ -2017,12 +2454,44 @@ mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd,
BFD_ASSERT (g != NULL);
}
+ /* We might have a symbol, H, if it has been forced local. Use the
+ global entry then. It doesn't matter whether an entry is local
+ or global for TLS, since the dynamic linker does not
+ automatically relocate TLS GOT entries. */
+ BFD_ASSERT (h == NULL || h->forced_local);
+ if (TLS_RELOC_P (r_type))
+ {
+ struct mips_got_entry *p;
+
+ entry.abfd = ibfd;
+ if (r_type == R_MIPS_TLS_LDM)
+ {
+ entry.tls_type = GOT_TLS_LDM;
+ entry.symndx = 0;
+ entry.d.addend = 0;
+ }
+ else if (h == NULL)
+ {
+ entry.symndx = r_symndx;
+ entry.d.addend = 0;
+ }
+ else
+ entry.d.h = h;
+
+ p = (struct mips_got_entry *)
+ htab_find (g->got_entries, &entry);
+
+ BFD_ASSERT (p);
+ return p;
+ }
+
loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
INSERT);
if (*loc)
return *loc;
entry.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
+ entry.tls_type = 0;
*loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
@@ -2118,6 +2587,8 @@ mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data)
-1. */
if (h->root.got.offset == 2)
{
+ BFD_ASSERT (h->tls_type == GOT_NORMAL);
+
if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx)
hsd->low = (struct elf_link_hash_entry *) h;
h->root.dynindx = hsd->max_unref_got_dynindx++;
@@ -2126,6 +2597,8 @@ mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data)
h->root.dynindx = hsd->max_non_got_dynindx++;
else
{
+ BFD_ASSERT (h->tls_type == GOT_NORMAL);
+
h->root.dynindx = --hsd->min_got_dynindx;
hsd->low = (struct elf_link_hash_entry *) h;
}
@@ -2140,7 +2613,8 @@ mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data)
static bfd_boolean
mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
bfd *abfd, struct bfd_link_info *info,
- struct mips_got_info *g)
+ struct mips_got_info *g,
+ unsigned char tls_flag)
{
struct mips_got_entry entry, **loc;
@@ -2162,6 +2636,7 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
entry.abfd = abfd;
entry.symndx = -1;
entry.d.h = (struct mips_elf_link_hash_entry *) h;
+ entry.tls_type = 0;
loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
INSERT);
@@ -2169,7 +2644,10 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
/* If we've already marked this entry as needing GOT space, we don't
need to do it again. */
if (*loc)
- return TRUE;
+ {
+ (*loc)->tls_type |= tls_flag;
+ return TRUE;
+ }
*loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
@@ -2177,6 +2655,8 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
return FALSE;
entry.gotidx = -1;
+ entry.tls_type = tls_flag;
+
memcpy (*loc, &entry, sizeof entry);
if (h->got.offset != MINUS_ONE)
@@ -2185,7 +2665,8 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
/* By setting this to a value other than -1, we are indicating that
there needs to be a GOT entry for H. Avoid using zero, as the
generic ELF copy_indirect_symbol tests for <= 0. */
- h->got.offset = 1;
+ if (tls_flag == 0)
+ h->got.offset = 1;
return TRUE;
}
@@ -2195,20 +2676,52 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
static bfd_boolean
mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
- struct mips_got_info *g)
+ struct mips_got_info *g,
+ unsigned char tls_flag)
{
struct mips_got_entry entry, **loc;
entry.abfd = abfd;
entry.symndx = symndx;
entry.d.addend = addend;
+ entry.tls_type = tls_flag;
loc = (struct mips_got_entry **)
htab_find_slot (g->got_entries, &entry, INSERT);
if (*loc)
- return TRUE;
+ {
+ if (tls_flag == GOT_TLS_GD && !((*loc)->tls_type & GOT_TLS_GD))
+ {
+ g->tls_gotno += 2;
+ (*loc)->tls_type |= tls_flag;
+ }
+ else if (tls_flag == GOT_TLS_IE && !((*loc)->tls_type & GOT_TLS_IE))
+ {
+ g->tls_gotno += 1;
+ (*loc)->tls_type |= tls_flag;
+ }
+ return TRUE;
+ }
- entry.gotidx = g->local_gotno++;
+ if (tls_flag != 0)
+ {
+ entry.gotidx = -1;
+ entry.tls_type = tls_flag;
+ if (tls_flag == GOT_TLS_IE)
+ g->tls_gotno += 1;
+ else if (tls_flag == GOT_TLS_GD)
+ g->tls_gotno += 2;
+ else if (g->tls_ldm_offset == MINUS_ONE)
+ {
+ g->tls_ldm_offset = MINUS_TWO;
+ g->tls_gotno += 2;
+ }
+ }
+ else
+ {
+ entry.gotidx = g->local_gotno++;
+ entry.tls_type = 0;
+ }
*loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
@@ -2308,6 +2821,9 @@ mips_elf_make_got_per_bfd (void **entryp, void *p)
g->global_gotno = 0;
g->local_gotno = 0;
g->assigned_gotno = -1;
+ g->tls_gotno = 0;
+ g->tls_assigned_gotno = 0;
+ g->tls_ldm_offset = MINUS_ONE;
g->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
mips_elf_multi_got_entry_eq, NULL);
if (g->got_entries == NULL)
@@ -2327,7 +2843,14 @@ mips_elf_make_got_per_bfd (void **entryp, void *p)
*entryp = entry;
- if (entry->symndx >= 0 || entry->d.h->forced_local)
+ if (entry->tls_type)
+ {
+ if (entry->tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
+ g->tls_gotno += 2;
+ if (entry->tls_type & GOT_TLS_IE)
+ g->tls_gotno += 1;
+ }
+ else if (entry->symndx >= 0 || entry->d.h->forced_local)
++g->local_gotno;
else
++g->global_gotno;
@@ -2350,11 +2873,26 @@ mips_elf_merge_gots (void **bfd2got_, void *p)
struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
unsigned int lcount = bfd2got->g->local_gotno;
unsigned int gcount = bfd2got->g->global_gotno;
+ unsigned int tcount = bfd2got->g->tls_gotno;
unsigned int maxcnt = arg->max_count;
+ bfd_boolean too_many_for_tls = FALSE;
+
+ /* We place TLS GOT entries after both locals and globals. The globals
+ for the primary GOT may overflow the normal GOT size limit, so be
+ sure not to merge a GOT which requires TLS with the primary GOT in that
+ case. This doesn't affect non-primary GOTs. */
+ if (tcount > 0)
+ {
+ unsigned int primary_total = lcount + tcount + arg->global_count;
+ if (primary_total * MIPS_ELF_GOT_SIZE (bfd2got->bfd)
+ >= MIPS_ELF_GOT_MAX_SIZE (bfd2got->bfd))
+ too_many_for_tls = TRUE;
+ }
/* If we don't have a primary GOT and this is not too big, use it as
a starting point for the primary GOT. */
- if (! arg->primary && lcount + gcount <= maxcnt)
+ if (! arg->primary && lcount + gcount + tcount <= maxcnt
+ && ! too_many_for_tls)
{
arg->primary = bfd2got->g;
arg->primary_count = lcount + gcount;
@@ -2362,12 +2900,13 @@ mips_elf_merge_gots (void **bfd2got_, void *p)
/* If it looks like we can merge this bfd's entries with those of
the primary, merge them. The heuristics is conservative, but we
don't have to squeeze it too hard. */
- else if (arg->primary
- && (arg->primary_count + lcount + gcount) <= maxcnt)
+ else if (arg->primary && ! too_many_for_tls
+ && (arg->primary_count + lcount + gcount + tcount) <= maxcnt)
{
struct mips_got_info *g = bfd2got->g;
int old_lcount = arg->primary->local_gotno;
int old_gcount = arg->primary->global_gotno;
+ int old_tcount = arg->primary->tls_gotno;
bfd2got->g = arg->primary;
@@ -2384,17 +2923,19 @@ mips_elf_merge_gots (void **bfd2got_, void *p)
BFD_ASSERT (old_lcount + lcount >= arg->primary->local_gotno);
BFD_ASSERT (old_gcount + gcount >= arg->primary->global_gotno);
+ BFD_ASSERT (old_tcount + tcount >= arg->primary->tls_gotno);
arg->primary_count = arg->primary->local_gotno
- + arg->primary->global_gotno;
+ + arg->primary->global_gotno + arg->primary->tls_gotno;
}
/* If we can merge with the last-created got, do it. */
else if (arg->current
- && arg->current_count + lcount + gcount <= maxcnt)
+ && arg->current_count + lcount + gcount + tcount <= maxcnt)
{
struct mips_got_info *g = bfd2got->g;
int old_lcount = arg->current->local_gotno;
int old_gcount = arg->current->global_gotno;
+ int old_tcount = arg->current->tls_gotno;
bfd2got->g = arg->current;
@@ -2408,9 +2949,10 @@ mips_elf_merge_gots (void **bfd2got_, void *p)
BFD_ASSERT (old_lcount + lcount >= arg->current->local_gotno);
BFD_ASSERT (old_gcount + gcount >= arg->current->global_gotno);
+ BFD_ASSERT (old_tcount + tcount >= arg->current->tls_gotno);
arg->current_count = arg->current->local_gotno
- + arg->current->global_gotno;
+ + arg->current->global_gotno + arg->current->tls_gotno;
}
/* Well, we couldn't merge, so create a new GOT. Don't check if it
fits; if it turns out that it doesn't, we'll get relocation
@@ -2420,9 +2962,58 @@ mips_elf_merge_gots (void **bfd2got_, void *p)
bfd2got->g->next = arg->current;
arg->current = bfd2got->g;
- arg->current_count = lcount + gcount;
+ arg->current_count = lcount + gcount + 2 * tcount;
+ }
+
+ return 1;
+}
+
+/* Set the TLS GOT index for the GOT entry in ENTRYP. */
+
+static int
+mips_elf_initialize_tls_index (void **entryp, void *p)
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ struct mips_got_info *g = p;
+
+ /* We're only interested in TLS symbols. */
+ if (entry->tls_type == 0)
+ return 1;
+
+ if (entry->symndx == -1)
+ {
+ /* There may be multiple mips_got_entry structs for a global variable
+ if there is just one GOT. Just do this once. */
+ if (g->next == NULL)
+ {
+ if (entry->d.h->tls_type & GOT_TLS_OFFSET_DONE)
+ return 1;
+ entry->d.h->tls_type |= GOT_TLS_OFFSET_DONE;
+ }
+ }
+ else if (entry->tls_type & GOT_TLS_LDM)
+ {
+ /* Similarly, there may be multiple structs for the LDM entry. */
+ if (g->tls_ldm_offset != MINUS_TWO && g->tls_ldm_offset != MINUS_ONE)
+ {
+ entry->gotidx = g->tls_ldm_offset;
+ return 1;
+ }
}
+ /* Initialize the GOT offset. */
+ entry->gotidx = MIPS_ELF_GOT_SIZE (entry->abfd) * (long) g->tls_assigned_gotno;
+ if (g->next == NULL && entry->symndx == -1)
+ entry->d.h->tls_got_offset = entry->gotidx;
+
+ if (entry->tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
+ g->tls_assigned_gotno += 2;
+ if (entry->tls_type & GOT_TLS_IE)
+ g->tls_assigned_gotno += 1;
+
+ if (entry->tls_type & GOT_TLS_LDM)
+ g->tls_ldm_offset = entry->gotidx;
+
return 1;
}
@@ -2447,8 +3038,14 @@ mips_elf_set_global_got_offset (void **entryp, void *p)
= (struct mips_elf_set_global_got_offset_arg *)p;
struct mips_got_info *g = arg->g;
+ if (g && entry->tls_type != GOT_NORMAL)
+ arg->needed_relocs +=
+ mips_tls_got_relocs (arg->info, entry->tls_type,
+ entry->symndx == -1 ? &entry->d.h->root : NULL);
+
if (entry->abfd != NULL && entry->symndx == -1
- && entry->d.h->root.dynindx != -1)
+ && entry->d.h->root.dynindx != -1
+ && entry->d.h->tls_type == GOT_NORMAL)
{
if (g)
{
@@ -2563,7 +3160,8 @@ mips_elf_adjust_gp (bfd *abfd, struct mips_got_info *g, bfd *ibfd)
g = g->next;
- return (g->local_gotno + g->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+ return (g->local_gotno + g->global_gotno + g->tls_gotno)
+ * MIPS_ELF_GOT_SIZE (abfd);
}
/* Turn a single GOT that is too big for 16-bit addressing into
@@ -2590,7 +3188,6 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
/* Count how many GOT entries each input bfd requires, creating a
map from bfd to got info while at that. */
- mips_elf_resolve_final_got_entries (g);
htab_traverse (g->got_entries, mips_elf_make_got_per_bfd, &got_per_bfd_arg);
if (got_per_bfd_arg.obfd == NULL)
return FALSE;
@@ -2603,6 +3200,10 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (abfd)
/ MIPS_ELF_GOT_SIZE (abfd))
- MIPS_RESERVED_GOTNO - pages);
+ /* The number of globals that will be included in the primary GOT.
+ See the calls to mips_elf_set_global_got_offset below for more
+ information. */
+ got_per_bfd_arg.global_count = g->global_gotno;
/* Try to merge the GOTs of input bfds together, as long as they
don't seem to exceed the maximum GOT size, choosing one of them
@@ -2611,7 +3212,7 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
if (got_per_bfd_arg.obfd == NULL)
return FALSE;
- /* If we find any suitable primary GOT, create an empty one. */
+ /* If we do not find any suitable primary GOT, create an empty one. */
if (got_per_bfd_arg.primary == NULL)
{
g->next = (struct mips_got_info *)
@@ -2622,7 +3223,10 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
g->next->global_gotsym = NULL;
g->next->global_gotno = 0;
g->next->local_gotno = 0;
+ g->next->tls_gotno = 0;
g->next->assigned_gotno = 0;
+ g->next->tls_assigned_gotno = 0;
+ g->next->tls_ldm_offset = MINUS_ONE;
g->next->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
mips_elf_multi_got_entry_eq,
NULL);
@@ -2722,6 +3326,7 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
points back to the master GOT. */
gg->local_gotno = -g->global_gotno;
gg->global_gotno = g->global_gotno;
+ gg->tls_gotno = 0;
assign = 0;
gg->next = gg;
@@ -2732,7 +3337,12 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
assign += MIPS_RESERVED_GOTNO;
g->assigned_gotno = assign;
g->local_gotno += assign + pages;
- assign = g->local_gotno + g->global_gotno;
+ assign = g->local_gotno + g->global_gotno + g->tls_gotno;
+
+ /* Set up any TLS entries. We always place the TLS entries after
+ all non-TLS entries. */
+ g->tls_assigned_gotno = g->local_gotno + g->global_gotno;
+ htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
/* Take g out of the direct list, and push it onto the reversed
list that gg points to. */
@@ -2749,7 +3359,8 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
while (g);
got->size = (gg->next->local_gotno
- + gg->next->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+ + gg->next->global_gotno
+ + gg->next->tls_gotno) * MIPS_ELF_GOT_SIZE (abfd);
return TRUE;
}
@@ -2968,10 +3579,12 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info,
return FALSE;
g->global_gotsym = NULL;
g->global_gotno = 0;
+ g->tls_gotno = 0;
g->local_gotno = MIPS_RESERVED_GOTNO;
g->assigned_gotno = MIPS_RESERVED_GOTNO;
g->bfd2got = NULL;
g->next = NULL;
+ g->tls_ldm_offset = MINUS_ONE;
g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
mips_elf_got_entry_eq, NULL);
if (g->got_entries == NULL)
@@ -3268,8 +3881,18 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
case R_MIPS_CALL_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_CALL_LO16:
+ case R_MIPS_TLS_GD:
+ case R_MIPS_TLS_GOTTPREL:
+ case R_MIPS_TLS_LDM:
/* Find the index into the GOT where this value is located. */
- if (!local_p)
+ if (r_type == R_MIPS_TLS_LDM)
+ {
+ g = mips_elf_local_got_index (abfd, input_bfd, info, 0, 0, NULL,
+ r_type);
+ if (g == MINUS_ONE)
+ return bfd_reloc_outofrange;
+ }
+ else if (!local_p)
{
/* GOT_PAGE may take a non-zero addend, that is ignored in a
GOT_PAGE relocation that decays to GOT_DISP because the
@@ -3278,11 +3901,13 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE);
g = mips_elf_global_got_index (elf_hash_table (info)->dynobj,
input_bfd,
- (struct elf_link_hash_entry *) h);
- if (! elf_hash_table(info)->dynamic_sections_created
- || (info->shared
- && (info->symbolic || h->root.dynindx == -1)
- && h->root.def_regular))
+ (struct elf_link_hash_entry *) h,
+ r_type, info);
+ if (h->tls_type == GOT_NORMAL
+ && (! elf_hash_table(info)->dynamic_sections_created
+ || (info->shared
+ && (info->symbolic || h->root.dynindx == -1)
+ && h->root.def_regular)))
{
/* This is a static link or a -Bsymbolic link. The
symbol is defined locally, or was forced to be local.
@@ -3299,7 +3924,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
else
{
g = mips_elf_local_got_index (abfd, input_bfd,
- info, symbol + addend);
+ info, symbol + addend, r_symndx, h,
+ r_type);
if (g == MINUS_ONE)
return bfd_reloc_outofrange;
}
@@ -3407,6 +4033,24 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
value &= howto->dst_mask;
break;
+ case R_MIPS_TLS_DTPREL_HI16:
+ value = (mips_elf_high (addend + symbol - dtprel_base (info))
+ & howto->dst_mask);
+ break;
+
+ case R_MIPS_TLS_DTPREL_LO16:
+ value = (symbol + addend - dtprel_base (info)) & howto->dst_mask;
+ break;
+
+ case R_MIPS_TLS_TPREL_HI16:
+ value = (mips_elf_high (addend + symbol - tprel_base (info))
+ & howto->dst_mask);
+ break;
+
+ case R_MIPS_TLS_TPREL_LO16:
+ value = (symbol + addend - tprel_base (info)) & howto->dst_mask;
+ break;
+
case R_MIPS_HI16:
case R_MIPS16_HI16:
if (!gp_disp_p)
@@ -3518,6 +4162,9 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
/* Fall through. */
+ case R_MIPS_TLS_GD:
+ case R_MIPS_TLS_GOTTPREL:
+ case R_MIPS_TLS_LDM:
case R_MIPS_GOT_DISP:
got_disp:
value = g;
@@ -5288,6 +5935,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
case R_MIPS_GOT_PAGE:
case R_MIPS_GOT_OFST:
case R_MIPS_GOT_DISP:
+ case R_MIPS_TLS_GD:
+ case R_MIPS_TLS_LDM:
if (dynobj == NULL)
elf_hash_table (info)->dynobj = dynobj = abfd;
if (! mips_elf_create_got_section (dynobj, info, FALSE))
@@ -5321,7 +5970,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
R_MIPS_CALL_HI16 because these are always followed by an
R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16. */
if (! mips_elf_record_local_got_symbol (abfd, r_symndx,
- rel->r_addend, g))
+ rel->r_addend, g, 0))
return FALSE;
}
@@ -5343,7 +5992,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
if (h != NULL)
{
/* This symbol requires a global offset table entry. */
- if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ if (! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
return FALSE;
/* We need a stub, not a plt entry for the undefined
@@ -5380,11 +6029,52 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_GOT_DISP:
- /* This symbol requires a global offset table entry. */
- if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
return FALSE;
break;
+ case R_MIPS_TLS_GOTTPREL:
+ if (info->shared)
+ info->flags |= DF_STATIC_TLS;
+ /* Fall through */
+
+ case R_MIPS_TLS_LDM:
+ if (r_type == R_MIPS_TLS_LDM)
+ {
+ r_symndx = 0;
+ h = NULL;
+ }
+ /* Fall through */
+
+ case R_MIPS_TLS_GD:
+ /* This symbol requires a global offset table entry, or two
+ for TLS GD relocations. */
+ {
+ unsigned char flag = (r_type == R_MIPS_TLS_GD
+ ? GOT_TLS_GD
+ : r_type == R_MIPS_TLS_LDM
+ ? GOT_TLS_LDM
+ : GOT_TLS_IE);
+ if (h != NULL)
+ {
+ struct mips_elf_link_hash_entry *hmips =
+ (struct mips_elf_link_hash_entry *) h;
+ hmips->tls_type |= flag;
+
+ if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g, flag))
+ return FALSE;
+ }
+ else
+ {
+ BFD_ASSERT (flag == GOT_TLS_LDM || r_symndx != 0);
+
+ if (! mips_elf_record_local_got_symbol (abfd, r_symndx,
+ rel->r_addend, g, flag))
+ return FALSE;
+ }
+ }
+ break;
+
case R_MIPS_32:
case R_MIPS_REL32:
case R_MIPS_64:
@@ -5437,7 +6127,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
if (! mips_elf_create_got_section (dynobj, info, TRUE))
return FALSE;
g = mips_elf_got_info (dynobj, &sgot);
- if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ if (! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
return FALSE;
}
}
@@ -5803,6 +6493,7 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
bfd_size_type loadable_size = 0;
bfd_size_type local_gotno;
bfd *sub;
+ struct mips_elf_count_tls_arg count_tls_arg;
/* The .reginfo section has a fixed size. */
ri = bfd_get_section_by_name (output_bfd, ".reginfo");
@@ -5871,9 +6562,30 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
g->global_gotno = i;
s->size += i * MIPS_ELF_GOT_SIZE (output_bfd);
- if (s->size > MIPS_ELF_GOT_MAX_SIZE (output_bfd)
- && ! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
- return FALSE;
+ /* We need to calculate tls_gotno for global symbols at this point
+ instead of building it up earlier, to avoid doublecounting
+ entries for one global symbol from multiple input files. */
+ count_tls_arg.info = info;
+ count_tls_arg.needed = 0;
+ elf_link_hash_traverse (elf_hash_table (info),
+ mips_elf_count_global_tls_entries,
+ &count_tls_arg);
+ g->tls_gotno += count_tls_arg.needed;
+ s->size += g->tls_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
+
+ mips_elf_resolve_final_got_entries (g);
+
+ if (s->size > MIPS_ELF_GOT_MAX_SIZE (output_bfd))
+ {
+ if (! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
+ return FALSE;
+ }
+ else
+ {
+ /* Set up TLS entries for the first GOT. */
+ g->tls_assigned_gotno = g->global_gotno + g->local_gotno;
+ htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
+ }
return TRUE;
}
@@ -5988,6 +6700,9 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (output_bfd);
set_got_offset_arg.info = info;
+ /* NOTE 2005-02-03: How can this call, or the next, ever
+ find any indirect entries to resolve? They were all
+ resolved in mips_elf_multi_got. */
mips_elf_resolve_final_got_entries (gg);
for (g = gg->next; g && g->next != gg; g = g->next)
{
@@ -6013,13 +6728,28 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd,
needed_relocs += g->local_gotno - g->assigned_gotno;
BFD_ASSERT (g->assigned_gotno == g->next->local_gotno
+ g->next->global_gotno
+ + g->next->tls_gotno
+ MIPS_RESERVED_GOTNO);
}
}
+ }
+ else
+ {
+ struct mips_elf_count_tls_arg arg;
+ arg.info = info;
+ arg.needed = 0;
- if (needed_relocs)
- mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs);
+ htab_traverse (gg->got_entries, mips_elf_count_local_tls_relocs,
+ &arg);
+ elf_link_hash_traverse (elf_hash_table (info),
+ mips_elf_count_global_tls_relocs,
+ &arg);
+
+ needed_relocs += arg.needed;
}
+
+ if (needed_relocs)
+ mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs);
}
else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0)
{
@@ -6646,11 +7376,11 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
bfd_vma value;
value = sym->st_value;
- offset = mips_elf_global_got_index (dynobj, output_bfd, h);
+ offset = mips_elf_global_got_index (dynobj, output_bfd, h, R_MIPS_GOT16, info);
MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
}
- if (g->next && h->dynindx != -1)
+ if (g->next && h->dynindx != -1 && h->type != STT_TLS)
{
struct mips_got_entry e, *p;
bfd_vma entry;
@@ -6661,6 +7391,7 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
e.abfd = output_bfd;
e.symndx = -1;
e.d.h = (struct mips_elf_link_hash_entry *)h;
+ e.tls_type = 0;
for (g = g->next; g->next != gg; g = g->next)
{
@@ -6973,7 +7704,8 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
for (g = gg->next; g->next != gg; g = g->next)
{
- bfd_vma index = g->next->local_gotno + g->next->global_gotno;
+ bfd_vma index = g->next->local_gotno + g->next->global_gotno
+ + g->next->tls_gotno;
MIPS_ELF_PUT_WORD (output_bfd, 0, sgot->contents
+ index++ * MIPS_ELF_GOT_SIZE (output_bfd));
@@ -7587,6 +8319,11 @@ _bfd_mips_elf_copy_indirect_symbol (const struct elf_backend_data *bed,
dirmips->readonly_reloc = TRUE;
if (indmips->no_fn_stub)
dirmips->no_fn_stub = TRUE;
+
+ if (dirmips->tls_type == 0)
+ dirmips->tls_type = indmips->tls_type;
+ else
+ BFD_ASSERT (indmips->tls_type == 0);
}
void
@@ -7605,7 +8342,7 @@ _bfd_mips_elf_hide_symbol (struct bfd_link_info *info,
h->forced_local = force_local;
dynobj = elf_hash_table (info)->dynobj;
- if (dynobj != NULL && force_local)
+ if (dynobj != NULL && force_local && h->root.type != STT_TLS)
{
got = mips_elf_got_section (dynobj, FALSE);
g = mips_elf_section_data (got)->u.got_info;
@@ -7623,6 +8360,7 @@ _bfd_mips_elf_hide_symbol (struct bfd_link_info *info,
e.abfd = dynobj;
e.symndx = -1;
e.d.h = h;
+ e.tls_type = 0;
for (g = g->next; g != gg; g = g->next)
if (htab_find (g->got_entries, &e))