/* MIPS-specific support for ELF
Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
Free Software Foundation, Inc.
Most of the information added by Ian Lance Taylor, Cygnus Support,
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. */
+ /* The TLS type of this GOT entry: GOT_NORMAL, GOT_TLS_IE, GOT_TLS_GD
+ or GOT_TLS_LDM. 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
struct mips_got_info
{
- /* The global symbol in the GOT with the lowest index in the dynamic
- symbol table. */
- struct elf_link_hash_entry *global_gotsym;
/* The number of global .got entries. */
unsigned int global_gotno;
/* The number of global .got entries that are in the GGA_RELOC_ONLY area. */
#define GOT_TLS_GD 1
#define GOT_TLS_LDM 2
#define GOT_TLS_IE 4
+#define GOT_TLS_TYPE 7
#define GOT_TLS_OFFSET_DONE 0x40
#define GOT_TLS_DONE 0x80
- unsigned char tls_type;
+ unsigned char tls_ie_type;
+ unsigned char tls_gd_type;
- /* This is only used in single-GOT mode; in multi-GOT mode there
+ /* These fields are 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;
+ structures all referring to the same GOT slot. */
+ bfd_vma tls_ie_got_offset;
+ bfd_vma tls_gd_got_offset;
/* The highest GGA_* value that satisfies all references to this symbol. */
unsigned int global_got_area : 2;
struct mips_elf_link_hash_table
{
struct elf_link_hash_table root;
-#if 0
- /* We no longer use this. */
- /* String section indices for the dynamic section symbols. */
- bfd_size_type dynsym_sec_strindex[SIZEOF_MIPS_DYNSYM_SECNAMES];
-#endif
/* The number of .rtproc entries. */
bfd_size_type procedure_count;
/* The master GOT information. */
struct mips_got_info *got_info;
+ /* The global symbol in the GOT with the lowest index in the dynamic
+ symbol table. */
+ struct elf_link_hash_entry *global_gotsym;
+
/* The size of the PLT header in bytes. */
bfd_vma plt_header_size;
(bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
struct mips_elf_link_hash_entry *, asection *, bfd_vma,
bfd_vma *, asection *);
-static hashval_t mips_elf_got_entry_hash
- (const void *);
static bfd_vma mips_elf_adjust_gp
(bfd *, struct mips_got_info *, bfd *);
static struct mips_got_info *mips_elf_got_for_ibfd
ret->fn_stub = NULL;
ret->call_stub = NULL;
ret->call_fp_stub = NULL;
- ret->tls_type = GOT_NORMAL;
+ ret->tls_ie_type = GOT_NORMAL;
+ ret->tls_gd_type = GOT_NORMAL;
ret->global_got_area = GGA_NONE;
ret->got_only_for_calls = TRUE;
ret->readonly_reloc = FALSE;
use all fields to compute the hash, and compare the appropriate
union members. */
-static hashval_t
-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)
- : entry->d.h->root.root.root.hash));
-}
-
static int
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
- : e1->d.h == e2->d.h);
+ return (e1->abfd == e2->abfd
+ && e1->symndx == e2->symndx
+ && (e1->tls_type & GOT_TLS_TYPE) == (e2->tls_type & GOT_TLS_TYPE)
+ && (!e1->abfd ? e1->d.address == e2->d.address
+ : e1->symndx >= 0 ? e1->d.addend == e2->d.addend
+ : e1->d.h == e2->d.h));
}
/* multi_got_entries are still a match in the case of global objects,
accordingly. */
static hashval_t
-mips_elf_multi_got_entry_hash (const void *entry_)
+mips_elf_got_entry_hash (const void *entry_)
{
const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
- return entry->symndx
- + (! entry->abfd
- ? mips_elf_hash_bfd_vma (entry->d.address)
- : entry->symndx >= 0
- ? ((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);
+ return (entry->symndx
+ + (((entry->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM) << 18)
+ + ((entry->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM ? 0
+ : !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->d.h->root.root.root.hash));
}
static int
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
- ? e1->abfd == e2->abfd && e1->d.address == e2->d.address
- : e1->d.h == e2->d.h);
+ return (e1->symndx == e2->symndx
+ && (e1->tls_type & GOT_TLS_TYPE) == (e2->tls_type & GOT_TLS_TYPE)
+ && ((e1->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM ? TRUE
+ : !e1->abfd ? !e2->abfd && e1->d.address == e2->d.address
+ : e1->symndx >= 0 ? (e1->abfd == e2->abfd
+ && e1->d.addend == e2->d.addend)
+ : e2->abfd && e1->d.h == e2->d.h));
}
static hashval_t
return entry1->abfd == entry2->abfd && entry1->symndx == entry2->symndx;
}
\f
+/* Create and return a new mips_got_info structure. MASTER_GOT_P
+ is true if this is the master GOT rather than a multigot. */
+
+static struct mips_got_info *
+mips_elf_create_got_info (bfd *abfd, bfd_boolean master_got_p)
+{
+ struct mips_got_info *g;
+
+ g = bfd_zalloc (abfd, sizeof (struct mips_got_info));
+ if (g == NULL)
+ return NULL;
+
+ g->tls_ldm_offset = MINUS_ONE;
+ if (master_got_p)
+ g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
+ mips_elf_got_entry_eq, NULL);
+ else
+ g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
+ mips_elf_multi_got_entry_eq, NULL);
+ if (g->got_entries == NULL)
+ return NULL;
+
+ g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+ mips_got_page_entry_eq, NULL);
+ if (g->got_page_entries == NULL)
+ return NULL;
+
+ return g;
+}
+
/* Return the dynamic relocation section. If it doesn't exist, try to
create a new it if CREATE_P, otherwise return NULL. Also return NULL
if creation fails. */
return sreloc;
}
+/* Return the GOT_TLS_* type required by relocation type R_TYPE. */
+
+static int
+mips_elf_reloc_tls_type (unsigned int r_type)
+{
+ if (tls_gd_reloc_p (r_type))
+ return GOT_TLS_GD;
+
+ if (tls_ldm_reloc_p (r_type))
+ return GOT_TLS_LDM;
+
+ if (tls_gottprel_reloc_p (r_type))
+ return GOT_TLS_IE;
+
+ return GOT_NORMAL;
+}
+
+/* Return the number of GOT slots needed for GOT TLS type TYPE. */
+
+static int
+mips_tls_got_entries (unsigned int type)
+{
+ switch (type)
+ {
+ case GOT_TLS_GD:
+ case GOT_TLS_LDM:
+ return 2;
+
+ case GOT_TLS_IE:
+ return 1;
+
+ case GOT_NORMAL:
+ return 0;
+ }
+ abort ();
+}
+
/* 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). */
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;
need_relocs = TRUE;
if (!need_relocs)
- return FALSE;
+ return 0;
- if (tls_type & GOT_TLS_GD)
+ switch (tls_type & GOT_TLS_TYPE)
{
- ret++;
- if (indx != 0)
- ret++;
- }
+ case GOT_TLS_GD:
+ return indx != 0 ? 2 : 1;
- if (tls_type & GOT_TLS_IE)
- ret++;
+ case GOT_TLS_IE:
+ return 1;
- if ((tls_type & GOT_TLS_LDM) && info->shared)
- ret++;
+ case GOT_TLS_LDM:
+ return info->shared ? 1 : 0;
- return ret;
+ default:
+ return 0;
+ }
}
/* Count the number of TLS relocations required for the GOT entry in
= (struct mips_elf_link_hash_entry *) arg1;
struct mips_elf_count_tls_arg *arg = arg2;
- if (hm->tls_type & GOT_TLS_GD)
+ if (hm->root.root.type == bfd_link_hash_indirect
+ || hm->root.root.type == bfd_link_hash_warning)
+ return 1;
+
+ if (hm->tls_gd_type)
arg->needed += 2;
- if (hm->tls_type & GOT_TLS_IE)
+ if (hm->tls_ie_type)
arg->needed += 1;
return 1;
= (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);
+ if (hm->root.root.type == bfd_link_hash_indirect
+ || hm->root.root.type == bfd_link_hash_warning)
+ return 1;
+
+ arg->needed += mips_tls_got_relocs (arg->info, hm->tls_ie_type, &hm->root);
+ arg->needed += mips_tls_got_relocs (arg->info, hm->tls_gd_type, &hm->root);
return 1;
}
struct mips_elf_link_hash_table *htab;
int indx;
asection *sreloc, *sgot;
- bfd_vma offset, offset2;
+ bfd_vma got_offset2;
bfd_boolean need_relocs = FALSE;
htab = mips_elf_hash_table (info);
/* Emit necessary relocations. */
sreloc = mips_elf_rel_dyn_section (info, FALSE);
- /* General Dynamic. */
- if (*tls_type_p & GOT_TLS_GD)
+ switch (*tls_type_p & GOT_TLS_TYPE)
{
- offset = got_offset;
- offset2 = offset + MIPS_ELF_GOT_SIZE (abfd);
+ case GOT_TLS_GD:
+ /* General Dynamic. */
+ got_offset2 = got_offset + MIPS_ELF_GOT_SIZE (abfd);
if (need_relocs)
{
mips_elf_output_dynamic_relocation
(abfd, sreloc, sreloc->reloc_count++, indx,
ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
- sgot->output_offset + sgot->output_section->vma + offset);
+ sgot->output_offset + sgot->output_section->vma + got_offset);
if (indx)
mips_elf_output_dynamic_relocation
(abfd, sreloc, sreloc->reloc_count++, indx,
ABI_64_P (abfd) ? R_MIPS_TLS_DTPREL64 : R_MIPS_TLS_DTPREL32,
- sgot->output_offset + sgot->output_section->vma + offset2);
+ sgot->output_offset + sgot->output_section->vma + got_offset2);
else
MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
- sgot->contents + offset2);
+ sgot->contents + got_offset2);
}
else
{
MIPS_ELF_PUT_WORD (abfd, 1,
- sgot->contents + offset);
+ sgot->contents + got_offset);
MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
- sgot->contents + offset2);
+ sgot->contents + got_offset2);
}
+ break;
- got_offset += 2 * MIPS_ELF_GOT_SIZE (abfd);
- }
-
- /* Initial Exec model. */
- if (*tls_type_p & GOT_TLS_IE)
- {
- offset = got_offset;
-
+ case GOT_TLS_IE:
+ /* Initial Exec model. */
if (need_relocs)
{
if (indx == 0)
MIPS_ELF_PUT_WORD (abfd, value - elf_hash_table (info)->tls_sec->vma,
- sgot->contents + offset);
+ sgot->contents + got_offset);
else
MIPS_ELF_PUT_WORD (abfd, 0,
- sgot->contents + offset);
+ sgot->contents + got_offset);
mips_elf_output_dynamic_relocation
(abfd, sreloc, sreloc->reloc_count++, indx,
ABI_64_P (abfd) ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32,
- sgot->output_offset + sgot->output_section->vma + offset);
+ sgot->output_offset + sgot->output_section->vma + got_offset);
}
else
MIPS_ELF_PUT_WORD (abfd, value - tprel_base (info),
- sgot->contents + offset);
- }
+ sgot->contents + got_offset);
+ break;
- if (*tls_type_p & GOT_TLS_LDM)
- {
+ case 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,
(abfd, sreloc, sreloc->reloc_count++, indx,
ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
sgot->output_offset + sgot->output_section->vma + got_offset);
+ break;
+
+ default:
+ abort ();
}
*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. */
+/* Return the GOT index to use for a relocation against H using the
+ TLS model in *TLS_TYPE. The GOT entries for this symbol/model
+ combination start at GOT_INDEX into ABFD's GOT. 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 bfd_link_info *info,
struct mips_elf_link_hash_entry *h, bfd_vma symbol)
{
- BFD_ASSERT (tls_gottprel_reloc_p (r_type)
- || tls_gd_reloc_p (r_type)
- || tls_ldm_reloc_p (r_type));
-
mips_elf_initialize_tls_slots (abfd, got_index, tls_type, info, h, symbol);
+ return got_index;
+}
- if (tls_gottprel_reloc_p (r_type))
- {
- 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;
- }
+/* Return the GOT index to use for a relocation of type R_TYPE against H
+ in ABFD. */
+static bfd_vma
+mips_tls_single_got_index (bfd *abfd, int r_type, struct bfd_link_info *info,
+ struct mips_elf_link_hash_entry *h, bfd_vma symbol)
+{
+ if (tls_gottprel_reloc_p (r_type))
+ return mips_tls_got_index (abfd, h->tls_ie_got_offset, &h->tls_ie_type,
+ info, h, symbol);
if (tls_gd_reloc_p (r_type))
- {
- BFD_ASSERT (*tls_type & GOT_TLS_GD);
- return got_index;
- }
-
- if (tls_ldm_reloc_p (r_type))
- {
- BFD_ASSERT (*tls_type & GOT_TLS_LDM);
- return got_index;
- }
-
- return got_index;
+ return mips_tls_got_index (abfd, h->tls_gd_got_offset, &h->tls_gd_type,
+ info, h, symbol);
+ abort ();
}
/* Return the offset from _GLOBAL_OFFSET_TABLE_ of the .got.plt entry
if (!entry)
return MINUS_ONE;
- if (TLS_RELOC_P (r_type))
+ if (entry->tls_type)
{
if (entry->symndx == -1 && htab->got_info->next == NULL)
/* A type (3) entry in the single-GOT case. We use the symbol's
hash table entry to track the index. */
- return mips_tls_got_index (abfd, h->tls_got_offset, &h->tls_type,
- r_type, info, h, value);
+ return mips_tls_single_got_index (abfd, r_type, info, h, value);
else
return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type,
- r_type, info, h, value);
+ info, h, value);
}
else
return entry->gotidx;
e.abfd = ibfd;
e.symndx = -1;
e.d.h = (struct mips_elf_link_hash_entry *)h;
- e.tls_type = 0;
+ e.tls_type = mips_elf_reloc_tls_type (r_type);
p = htab_find (g->got_entries, &e);
- BFD_ASSERT (p->gotidx > 0);
+ BFD_ASSERT (p && p->gotidx > 0);
- if (TLS_RELOC_P (r_type))
+ if (p->tls_type)
{
bfd_vma value = MINUS_ONE;
if ((h->root.type == bfd_link_hash_defined
+ 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,
+ return mips_tls_got_index (abfd, p->gotidx, &p->tls_type,
info, e.d.h, value);
}
else
}
}
- if (gg->global_gotsym != NULL)
- global_got_dynindx = gg->global_gotsym->dynindx;
+ if (htab->global_gotsym != NULL)
+ global_got_dynindx = htab->global_gotsym->dynindx;
if (TLS_RELOC_P (r_type))
{
+ h->root.u.def.section->output_offset
+ h->root.u.def.section->output_section->vma);
- got_index = mips_tls_got_index (abfd, hm->tls_got_offset, &hm->tls_type,
- r_type, info, hm, value);
+ got_index = mips_tls_single_got_index (abfd, r_type, info, hm, value);
}
else
{
entry.abfd = NULL;
entry.symndx = -1;
entry.d.address = value;
- entry.tls_type = 0;
+ entry.tls_type = mips_elf_reloc_tls_type (r_type);
g = mips_elf_got_for_ibfd (htab->got_info, ibfd);
if (g == NULL)
/* This function shouldn't be called for symbols that live in the global
area of the GOT. */
BFD_ASSERT (h == NULL || h->global_got_area == GGA_NONE);
- if (TLS_RELOC_P (r_type))
+ if (entry.tls_type)
{
struct mips_got_entry *p;
entry.abfd = ibfd;
if (tls_ldm_reloc_p (r_type))
{
- entry.tls_type = GOT_TLS_LDM;
entry.symndx = 0;
entry.d.addend = 0;
}
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);
/* Now we know which dynamic symbol has the lowest dynamic symbol
table index in the GOT. */
- g->global_gotsym = hsd.low;
+ htab->global_gotsym = hsd.low;
return TRUE;
}
break;
case GGA_NORMAL:
- BFD_ASSERT (h->tls_type == GOT_NORMAL);
-
h->root.dynindx = --hsd->min_got_dynindx;
hsd->low = (struct elf_link_hash_entry *) h;
break;
case GGA_RELOC_ONLY:
- 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++;
return TRUE;
}
-/* If H is a symbol that needs a global GOT entry, but has a dynamic
- symbol table index lower than any we've seen to date, record it for
- posterity. FOR_CALL is true if the caller is only interested in
+/* ABFD has a GOT relocation of type R_TYPE against H. Reserve a GOT
+ entry for it. FOR_CALL is true if the caller is only interested in
using the GOT entry for calls. */
static bfd_boolean
mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
bfd *abfd, struct bfd_link_info *info,
- bfd_boolean for_call,
- unsigned char tls_flag)
+ bfd_boolean for_call, int r_type)
{
struct mips_elf_link_hash_table *htab;
struct mips_elf_link_hash_entry *hmips;
entry.abfd = abfd;
entry.symndx = -1;
entry.d.h = (struct mips_elf_link_hash_entry *) h;
- entry.tls_type = 0;
+ entry.tls_type = mips_elf_reloc_tls_type (r_type);
loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
INSERT);
/* If we've already marked this entry as needing GOT space, we don't
need to do it again. */
if (*loc)
- {
- (*loc)->tls_type |= tls_flag;
- return TRUE;
- }
+ return TRUE;
*loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
return FALSE;
entry.gotidx = -1;
- entry.tls_type = tls_flag;
memcpy (*loc, &entry, sizeof entry);
- if (tls_flag == 0)
+ if (entry.tls_type == GOT_NORMAL)
hmips->global_got_area = GGA_NORMAL;
+ else if (entry.tls_type == GOT_TLS_IE)
+ hmips->tls_ie_type = entry.tls_type;
+ else if (entry.tls_type == GOT_TLS_GD)
+ hmips->tls_gd_type = entry.tls_type;
return TRUE;
}
-/* Reserve space in G for a GOT entry containing the value of symbol
- SYMNDX in input bfd ABDF, plus ADDEND. */
+/* ABFD has a GOT relocation of type R_TYPE against symbol SYMNDX + ADDEND,
+ where SYMNDX is a local symbol. Reserve a GOT entry for it. */
static bfd_boolean
mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
- struct bfd_link_info *info,
- unsigned char tls_flag)
+ struct bfd_link_info *info, int r_type)
{
struct mips_elf_link_hash_table *htab;
struct mips_got_info *g;
entry.abfd = abfd;
entry.symndx = symndx;
entry.d.addend = addend;
- entry.tls_type = tls_flag;
+ entry.tls_type = mips_elf_reloc_tls_type (r_type);
loc = (struct mips_got_entry **)
htab_find_slot (g->got_entries, &entry, INSERT);
if (*loc)
- {
- 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;
- }
+ return TRUE;
- if (tls_flag != 0)
+ entry.gotidx = -1;
+ if (entry.tls_type)
{
- 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;
+ if (entry.tls_type != GOT_TLS_LDM)
+ g->tls_gotno += mips_tls_got_entries (entry.tls_type);
else if (g->tls_ldm_offset == MINUS_ONE)
{
g->tls_ldm_offset = MINUS_TWO;
- g->tls_gotno += 2;
+ g->tls_gotno += mips_tls_got_entries (entry.tls_type);
}
}
else
- {
- entry.gotidx = g->local_gotno++;
- entry.tls_type = 0;
- }
+ g->local_gotno += 1;
*loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
}
if (*slot == NULL)
*slot = entry;
- else
- free (entry);
return 1;
}
if (new_got == NULL)
return FALSE;
- /* Each entry in g->got_entries has either been copied to new_got
- or freed. Now delete the hash table itself. */
htab_delete (g->got_entries);
g->got_entries = new_got;
}
bfd *input_bfd)
{
struct mips_elf_bfd2got_hash bfdgot_entry, *bfdgot;
- struct mips_got_info *g;
void **bfdgotp;
bfdgot_entry.bfd = input_bfd;
*bfdgotp = bfdgot;
- g = ((struct mips_got_info *)
- bfd_alloc (output_bfd, sizeof (struct mips_got_info)));
- if (g == NULL)
- return NULL;
-
bfdgot->bfd = input_bfd;
- bfdgot->g = g;
-
- g->global_gotsym = NULL;
- g->global_gotno = 0;
- g->reloc_only_gotno = 0;
- g->local_gotno = 0;
- g->page_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)
- return NULL;
-
- g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
- mips_got_page_entry_eq, NULL);
- if (g->got_page_entries == NULL)
+ bfdgot->g = mips_elf_create_got_info (input_bfd, FALSE);
+ if (bfdgot->g == NULL)
return NULL;
-
- g->bfd2got = NULL;
- g->next = NULL;
}
return bfdgot->g;
*entryp = entry;
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;
- }
+ g->tls_gotno += mips_tls_got_entries (entry->tls_type & GOT_TLS_TYPE);
else if (entry->symndx >= 0 || entry->d.h->global_got_area == GGA_NONE)
- ++g->local_gotno;
+ g->local_gotno += 1;
else
- ++g->global_gotno;
+ g->global_gotno += 1;
return 1;
}
estimate += from->local_gotno + to->local_gotno;
estimate += from->tls_gotno + to->tls_gotno;
- /* If we're merging with the primary got, we will always have
- the full set of global entries. Otherwise estimate those
+ /* If we're merging with the primary got, any TLS relocations will
+ come after the full set of global entries. Otherwise estimate those
conservatively as well. */
- if (to == arg->primary)
+ if (to == arg->primary && from->tls_gotno + to->tls_gotno)
estimate += arg->global_count;
else
estimate += from->global_gotno + to->global_gotno;
unsigned char tls_type;
/* We're only interested in TLS symbols. */
- if (entry->tls_type == 0)
+ tls_type = (entry->tls_type & GOT_TLS_TYPE);
+ if (tls_type == 0)
return 1;
next_index = MIPS_ELF_GOT_SIZE (entry->abfd) * (long) g->tls_assigned_gotno;
{
/* A type (3) got entry in the single-GOT case. We use the symbol's
hash table entry to track its index. */
- if (entry->d.h->tls_type & GOT_TLS_OFFSET_DONE)
- return 1;
- entry->d.h->tls_type |= GOT_TLS_OFFSET_DONE;
- entry->d.h->tls_got_offset = next_index;
- tls_type = entry->d.h->tls_type;
+ if (tls_type == GOT_TLS_IE)
+ {
+ if (entry->d.h->tls_ie_type & GOT_TLS_OFFSET_DONE)
+ return 1;
+ entry->d.h->tls_ie_type |= GOT_TLS_OFFSET_DONE;
+ entry->d.h->tls_ie_got_offset = next_index;
+ }
+ else
+ {
+ BFD_ASSERT (tls_type == GOT_TLS_GD);
+ if (entry->d.h->tls_gd_type & GOT_TLS_OFFSET_DONE)
+ return 1;
+ entry->d.h->tls_gd_type |= GOT_TLS_OFFSET_DONE;
+ entry->d.h->tls_gd_got_offset = next_index;
+ }
}
else
{
- if (entry->tls_type & GOT_TLS_LDM)
+ if (tls_type == GOT_TLS_LDM)
{
/* There are separate mips_got_entry objects for each input bfd
that requires an LDM entry. Make sure that all LDM entries in
g->tls_ldm_offset = next_index;
}
entry->gotidx = next_index;
- tls_type = entry->tls_type;
}
/* Account for the entries we've just allocated. */
- if (tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
- g->tls_assigned_gotno += 2;
- if (tls_type & GOT_TLS_IE)
- g->tls_assigned_gotno += 1;
-
+ g->tls_assigned_gotno += mips_tls_got_entries (tls_type);
return 1;
}
{
if (g)
{
- BFD_ASSERT (g->global_gotsym == NULL);
-
entry->gotidx = arg->value * (long) g->assigned_gotno++;
if (arg->info->shared
|| (elf_hash_table (arg->info)->dynamic_sections_created
/* 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 *)
- bfd_alloc (abfd, sizeof (struct mips_got_info));
- if (g->next == NULL)
- return FALSE;
-
- g->next->global_gotsym = NULL;
- g->next->global_gotno = 0;
- g->next->reloc_only_gotno = 0;
- g->next->local_gotno = 0;
- g->next->page_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);
- if (g->next->got_entries == NULL)
- return FALSE;
- g->next->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
- mips_got_page_entry_eq,
- NULL);
- if (g->next->got_page_entries == NULL)
- return FALSE;
- g->next->bfd2got = NULL;
- }
+ g->next = mips_elf_create_got_info (abfd, FALSE);
else
g->next = got_per_bfd_arg.primary;
g->next->next = got_per_bfd_arg.current;
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);
+ BFD_ASSERT (g->tls_assigned_gotno == assign);
/* Move onto the next GOT. It will be a secondary GOT if nonull. */
g = gn;
}
while (g);
- got->size = (gg->next->local_gotno
- + gg->next->global_gotno
- + gg->next->tls_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+ got->size = assign * MIPS_ELF_GOT_SIZE (abfd);
needed_relocs = 0;
set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (abfd);
register asection *s;
struct elf_link_hash_entry *h;
struct bfd_link_hash_entry *bh;
- struct mips_got_info *g;
- bfd_size_type amt;
struct mips_elf_link_hash_table *htab;
htab = mips_elf_hash_table (info);
&& ! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
- amt = sizeof (struct mips_got_info);
- g = bfd_alloc (abfd, amt);
- if (g == NULL)
- return FALSE;
- g->global_gotsym = NULL;
- g->global_gotno = 0;
- g->reloc_only_gotno = 0;
- g->tls_gotno = 0;
- g->local_gotno = 0;
- g->page_gotno = 0;
- g->assigned_gotno = 0;
- 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)
- return FALSE;
- g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
- mips_got_page_entry_eq, NULL);
- if (g->got_page_entries == NULL)
- return FALSE;
- htab->got_info = g;
+ htab->got_info = mips_elf_create_got_info (abfd, TRUE);
mips_elf_section_data (s)->elf.this_hdr.sh_flags
|= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
if (h->call_stub != NULL && h->call_fp_stub != NULL)
{
asection *o;
-
+
sec = NULL;
for (o = input_bfd->sections; o != NULL; o = o->next)
{
BFD_ASSERT (addend == 0);
g = mips_elf_global_got_index (dynobj, input_bfd,
&h->root, r_type, info);
- if (h->tls_type == GOT_NORMAL
+ if (!TLS_RELOC_P (r_type)
&& !elf_hash_table (info)->dynamic_sections_created)
/* This is a static link. We must initialize the GOT entry. */
MIPS_ELF_PUT_WORD (dynobj, symbol, htab->sgot->contents + g);
/* It has been determined that the result of the RELOCATION is the
VALUE. Use HOWTO to place VALUE into the output file at the
appropriate position. The SECTION is the section to which the
- relocation applies.
+ relocation applies.
CROSS_MODE_JUMP_P is true if the relocation field
is a MIPS16 or microMIPS jump to standard MIPS code, or vice versa.
case E_MIPS_MACH_5500:
return bfd_mach_mips5500;
+ case E_MIPS_MACH_5900:
+ return bfd_mach_mips5900;
+
case E_MIPS_MACH_9000:
return bfd_mach_mips9000;
{
h = ((struct mips_elf_link_hash_entry *)
sym_hashes[r_symndx - extsymoff]);
-
+
/* H is the symbol this stub is for. */
-
+
if (CALL_FP_STUB_P (name))
loc = &h->call_fp_stub;
else
loc = &h->call_stub;
-
+
/* If we already have an appropriate stub for this function, we
don't need another one, so we can discard this one. Since
this function is called before the linker maps input sections
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, info, 0))
+ rel->r_addend, info, r_type))
return FALSE;
}
/* Make sure there is room in the regular GOT to hold the
function's address. We may eliminate it in favour of
a .got.plt entry later; see mips_elf_count_got_symbols. */
- if (!mips_elf_record_global_got_symbol (h, abfd, info, TRUE, 0))
+ if (!mips_elf_record_global_got_symbol (h, abfd, info, TRUE,
+ r_type))
return FALSE;
/* We need a stub, not a plt entry for the undefined
case R_MIPS_GOT_DISP:
case R_MICROMIPS_GOT_DISP:
if (h && !mips_elf_record_global_got_symbol (h, abfd, info,
- FALSE, 0))
+ FALSE, r_type))
return FALSE;
break;
case R_MICROMIPS_TLS_GD:
/* This symbol requires a global offset table entry, or two
for TLS GD relocations. */
- {
- unsigned char flag;
-
- flag = (tls_gd_reloc_p (r_type)
- ? GOT_TLS_GD
- : tls_ldm_reloc_p (r_type) ? 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,
- FALSE, flag))
- return FALSE;
- }
- else
- {
- BFD_ASSERT (flag == GOT_TLS_LDM || r_symndx != STN_UNDEF);
-
- if (!mips_elf_record_local_got_symbol (abfd, r_symndx,
- rel->r_addend,
- info, flag))
- return FALSE;
- }
- }
+ if (h != NULL)
+ {
+ if (!mips_elf_record_global_got_symbol (h, abfd, info,
+ FALSE, r_type))
+ return FALSE;
+ }
+ else
+ {
+ if (!mips_elf_record_local_got_symbol (abfd, r_symndx,
+ rel->r_addend,
+ info, r_type))
+ return FALSE;
+ }
break;
case R_MIPS_32:
/* Set up TLS entries. */
g->tls_assigned_gotno = g->global_gotno + g->local_gotno;
htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
+ BFD_ASSERT (g->tls_assigned_gotno
+ == g->global_gotno + g->local_gotno + g->tls_gotno);
/* Allocate room for the TLS relocations. */
arg.info = info;
MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
}
- if (hmips->global_got_area != GGA_NONE && g->next && h->type != STT_TLS)
+ if (hmips->global_got_area != GGA_NONE && g->next)
{
struct mips_got_entry e, *p;
bfd_vma entry;
break;
case DT_MIPS_GOTSYM:
- if (gg->global_gotsym)
+ if (htab->global_gotsym)
{
- dyn.d_un.d_val = gg->global_gotsym->dynindx;
+ dyn.d_un.d_val = htab->global_gotsym->dynindx;
break;
}
/* In case if we don't have global got symbols we default
val = E_MIPS_ARCH_4 | E_MIPS_MACH_5500;
break;
+ case bfd_mach_mips5900:
+ val = E_MIPS_ARCH_3 | E_MIPS_MACH_5900;
+ break;
+
case bfd_mach_mips9000:
val = E_MIPS_ARCH_4 | E_MIPS_MACH_9000;
break;
if (indmips->has_nonpic_branches)
dirmips->has_nonpic_branches = TRUE;
- if (dirmips->tls_type == 0)
- dirmips->tls_type = indmips->tls_type;
+ if (dirmips->tls_ie_type == 0)
+ dirmips->tls_ie_type = indmips->tls_ie_type;
+ if (dirmips->tls_gd_type == 0)
+ dirmips->tls_gd_type = indmips->tls_gd_type;
}
\f
#define PDR_SIZE 32
struct mips_elf_link_hash_table *ret;
bfd_size_type amt = sizeof (struct mips_elf_link_hash_table);
- ret = bfd_malloc (amt);
+ ret = bfd_zmalloc (amt);
if (ret == NULL)
return NULL;
return NULL;
}
-#if 0
- /* We no longer use this. */
- for (i = 0; i < SIZEOF_MIPS_DYNSYM_SECNAMES; i++)
- ret->dynsym_sec_strindex[i] = (bfd_size_type) -1;
-#endif
- ret->procedure_count = 0;
- ret->compact_rel_size = 0;
- ret->use_rld_obj_head = FALSE;
- ret->rld_symbol = NULL;
- ret->mips16_stubs_seen = FALSE;
- ret->use_plts_and_copy_relocs = FALSE;
- ret->is_vxworks = FALSE;
- ret->small_data_overflow_reported = FALSE;
- ret->srelbss = NULL;
- ret->sdynbss = NULL;
- ret->srelplt = NULL;
- ret->srelplt2 = NULL;
- ret->sgotplt = NULL;
- ret->splt = NULL;
- ret->sstubs = NULL;
- ret->sgot = NULL;
- ret->got_info = NULL;
- ret->plt_header_size = 0;
- ret->plt_entry_size = 0;
- ret->lazy_stub_count = 0;
- ret->function_stub_size = 0;
- ret->strampoline = NULL;
- ret->la25_stubs = NULL;
- ret->add_stub_section = NULL;
-
return &ret->root.root;
}
{ bfd_mach_mips4300, bfd_mach_mips4000 },
{ bfd_mach_mips4100, bfd_mach_mips4000 },
{ bfd_mach_mips4010, bfd_mach_mips4000 },
+ { bfd_mach_mips5900, bfd_mach_mips4000 },
/* MIPS32 extensions. */
{ bfd_mach_mipsisa32r2, bfd_mach_mipsisa32 },
if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
&& (bfd_get_arch_info (obfd)->the_default
- || mips_mach_extends_p (bfd_get_mach (obfd),
+ || mips_mach_extends_p (bfd_get_mach (obfd),
bfd_get_mach (ibfd))))
{
if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),