#include "hashtab.h"
+/* Types of TLS GOT entry. */
+enum mips_got_tls_type {
+ GOT_TLS_NONE,
+ GOT_TLS_GD,
+ GOT_TLS_LDM,
+ GOT_TLS_IE
+};
+
/* This structure is used to hold information about one GOT entry.
- There are three types of entry:
-
- (1) absolute addresses
- (abfd == NULL)
- (2) SYMBOL + OFFSET addresses, where SYMBOL is local to an input bfd
- (abfd != NULL, symndx >= 0)
- (3) SYMBOL addresses, where SYMBOL is not local to an input bfd
- (abfd != NULL, symndx == -1)
-
- Type (3) entries are treated differently for different types of GOT.
- In the "master" GOT -- i.e. the one that describes every GOT
- reference needed in the link -- the mips_got_entry is keyed on both
- the symbol and the input bfd that references it. If it turns out
- that we need multiple GOTs, we can then use this information to
- create separate GOTs for each input bfd.
-
- However, we want each of these separate GOTs to have at most one
- entry for a given symbol, so their type (3) entries are keyed only
- on the symbol. The input bfd given by the "abfd" field is somewhat
- arbitrary in this case.
-
- This means that when there are multiple GOTs, each GOT has a unique
- mips_got_entry for every symbol within it. We can therefore use the
- mips_got_entry fields (tls_type and gotidx) to track the symbol's
- GOT index.
-
- However, if it turns out that we need only a single GOT, we continue
- to use the master GOT to describe it. There may therefore be several
- mips_got_entries for the same symbol, each with a different input bfd.
- We want to make sure that each symbol gets a unique GOT entry, so when
- there's a single GOT, we use the symbol's hash entry, not the
- mips_got_entry fields, to track a symbol's GOT index. */
+ There are four types of entry:
+
+ (1) an absolute address
+ requires: abfd == NULL
+ fields: d.address
+
+ (2) a SYMBOL + OFFSET address, where SYMBOL is local to an input bfd
+ requires: abfd != NULL, symndx >= 0, tls_type != GOT_TLS_LDM
+ fields: abfd, symndx, d.addend, tls_type
+
+ (3) a SYMBOL address, where SYMBOL is not local to an input bfd
+ requires: abfd != NULL, symndx == -1
+ fields: d.h, tls_type
+
+ (4) a TLS LDM slot
+ requires: abfd != NULL, symndx == 0, tls_type == GOT_TLS_LDM
+ fields: none; there's only one of these per GOT. */
struct mips_got_entry
{
- /* The input bfd in which the symbol is defined. */
+ /* One input bfd that needs the GOT entry. */
bfd *abfd;
/* The index of the symbol, as stored in the relocation r_info, if
we have a local symbol; -1 otherwise. */
that should be added to the symbol value. */
bfd_vma addend;
/* If abfd != NULL && symndx == -1, the hash table entry
- corresponding to symbol in the GOT. The symbol's entry
+ corresponding to a symbol in the GOT. The symbol's entry
is in the local area if h->global_got_area is GGA_NONE,
otherwise it is in the global area. */
struct mips_elf_link_hash_entry *h;
} d;
- /* 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. */
+ /* The TLS type of this GOT entry. An LDM GOT entry will be a local
+ symbol entry with r_symndx == 0. */
unsigned char tls_type;
+ /* True if we have filled in the GOT contents for a TLS entry,
+ and created the associated relocations. */
+ unsigned char tls_initialized;
+
/* 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. */
long gotidx;
};
+/* This structure represents a GOT page reference from an input bfd.
+ Each instance represents a symbol + ADDEND, where the representation
+ of the symbol depends on whether it is local to the input bfd.
+ If it is, then SYMNDX >= 0, and the symbol has index SYMNDX in U.ABFD.
+ Otherwise, SYMNDX < 0 and U.H points to the symbol's hash table entry.
+
+ Page references with SYMNDX >= 0 always become page references
+ in the output. Page references with SYMNDX < 0 only become page
+ references if the symbol binds locally; in other cases, the page
+ reference decays to a global GOT reference. */
+struct mips_got_page_ref
+{
+ long symndx;
+ union
+ {
+ struct mips_elf_link_hash_entry *h;
+ bfd *abfd;
+ } u;
+ bfd_vma addend;
+};
+
/* This structure describes a range of addends: [MIN_ADDEND, MAX_ADDEND].
The structures form a non-overlapping list that is sorted by increasing
MIN_ADDEND. */
};
/* This structure describes the range of addends that are applied to page
- relocations against a given symbol. */
+ relocations against a given section. */
struct mips_got_page_entry
{
- /* The input bfd in which the symbol is defined. */
- bfd *abfd;
- /* The index of the symbol, as stored in the relocation r_info. */
- long symndx;
+ /* The section that these entries are based on. */
+ asection *sec;
/* The ranges for this page entry. */
struct mips_got_page_range *ranges;
/* The maximum number of page entries needed for RANGES. */
unsigned int assigned_gotno;
/* A hash table holding members of the got. */
struct htab *got_entries;
+ /* A hash table holding mips_got_page_ref structures. */
+ struct htab *got_page_refs;
/* A hash table of mips_got_page_entry structures. */
struct htab *got_page_entries;
- /* A hash table mapping input bfds to other mips_got_info. NULL
- unless multi-got was necessary. */
- struct htab *bfd2got;
/* 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. */
-
-struct mips_elf_bfd2got_hash
-{
- bfd *bfd;
- struct mips_got_info *g;
};
-/* Structure passed when traversing the bfd2got hash table, used to
- create and merge bfd's gots. */
+/* Structure passed when merging bfds' gots. */
struct mips_elf_got_per_bfd_arg
{
- /* A hashtable that maps bfds to gots. */
- htab_t bfd2got;
/* The output bfd. */
bfd *obfd;
/* The link information. */
being called returns a floating point value. */
asection *call_fp_stub;
-#define GOT_NORMAL 0
-#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_ie_type;
- unsigned char tls_gd_type;
-
- /* 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. */
- 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;
The function returns the new section on success, otherwise it
returns null. */
asection *(*add_stub_section) (const char *, asection *, asection *);
+
+ /* Small local sym cache. */
+ struct sym_cache sym_cache;
};
/* Get the MIPS ELF linker hash table from a link_info structure. */
/* Input BFD providing Tag_GNU_MIPS_ABI_FP attribute for output. */
bfd *abi_fp_bfd;
+
+ /* The GOT requirements of input bfds. */
+ struct mips_got_info *got;
+
+ /* Used by _bfd_mips_elf_find_nearest_line. The structure could be
+ included directly in this one, but there's no point to wasting
+ the memory just for the infrequently called find_nearest_line. */
+ struct mips_elf_find_line *find_line_info;
+
+ /* An array of stub sections indexed by symbol number. */
+ asection **local_stubs;
+ asection **local_call_stubs;
+
+ /* The Irix 5 support uses two virtual sections, which represent
+ text/data symbols defined in dynamic objects. */
+ asymbol *elf_data_symbol;
+ asymbol *elf_text_symbol;
+ asection *elf_data_section;
+ asection *elf_text_section;
};
/* Get MIPS ELF private object data from BFD's tdata. */
bfd_vma *, asection *);
static bfd_vma mips_elf_adjust_gp
(bfd *, struct mips_got_info *, bfd *);
-static struct mips_got_info *mips_elf_got_for_ibfd
- (struct mips_got_info *, bfd *);
/* This will be used when we sort the dynamic relocation records. */
static bfd *reldyn_sorting_bfd;
/* Nonzero if ABFD is using NewABI conventions. */
#define NEWABI_P(abfd) (ABI_N32_P (abfd) || ABI_64_P (abfd))
+/* Nonzero if ABFD has microMIPS code. */
+#define MICROMIPS_P(abfd) \
+ ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS) != 0)
+
/* The IRIX compatibility level we are striving for. */
#define IRIX_COMPAT(abfd) \
(get_elf_backend_data (abfd)->elf_backend_mips_irix_compat (abfd))
ret->fn_stub = NULL;
ret->call_stub = NULL;
ret->call_fp_stub = NULL;
- 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;
#endif
}
-/* got_entries only match if they're identical, except for gotidx, so
- use all fields to compute the hash, and compare the appropriate
- union members. */
-
-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;
-
- 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,
- even if the input bfd in which they're referenced differs, so the
- hash computation and compare functions are adjusted
- accordingly. */
-
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_TYPE) == GOT_TLS_LDM) << 18)
- + ((entry->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM ? 0
+ + ((entry->tls_type == GOT_TLS_LDM) << 18)
+ + (entry->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))
}
static int
-mips_elf_multi_got_entry_eq (const void *entry1, const void *entry2)
+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;
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->tls_type == e2->tls_type
+ && (e1->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
+mips_got_page_ref_hash (const void *ref_)
+{
+ const struct mips_got_page_ref *ref;
+
+ ref = (const struct mips_got_page_ref *) ref_;
+ return ((ref->symndx >= 0
+ ? (hashval_t) (ref->u.abfd->id + ref->symndx)
+ : ref->u.h->root.root.root.hash)
+ + mips_elf_hash_bfd_vma (ref->addend));
+}
+
+static int
+mips_got_page_ref_eq (const void *ref1_, const void *ref2_)
+{
+ const struct mips_got_page_ref *ref1, *ref2;
+
+ ref1 = (const struct mips_got_page_ref *) ref1_;
+ ref2 = (const struct mips_got_page_ref *) ref2_;
+ return (ref1->symndx == ref2->symndx
+ && (ref1->symndx < 0
+ ? ref1->u.h == ref2->u.h
+ : ref1->u.abfd == ref2->u.abfd)
+ && ref1->addend == ref2->addend);
+}
+
static hashval_t
mips_got_page_entry_hash (const void *entry_)
{
const struct mips_got_page_entry *entry;
entry = (const struct mips_got_page_entry *) entry_;
- return entry->abfd->id + entry->symndx;
+ return entry->sec->id;
}
static int
entry1 = (const struct mips_got_page_entry *) entry1_;
entry2 = (const struct mips_got_page_entry *) entry2_;
- return entry1->abfd == entry2->abfd && entry1->symndx == entry2->symndx;
+ return entry1->sec == entry2->sec;
}
\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. */
+/* Create and return a new mips_got_info structure. */
static struct mips_got_info *
-mips_elf_create_got_info (bfd *abfd, bfd_boolean master_got_p)
+mips_elf_create_got_info (bfd *abfd)
{
struct mips_got_info *g;
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);
+ g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
+ mips_elf_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)
+ g->got_page_refs = htab_try_create (1, mips_got_page_ref_hash,
+ mips_got_page_ref_eq, NULL);
+ if (g->got_page_refs == NULL)
return NULL;
return g;
}
+/* Return the GOT info for input bfd ABFD, trying to create a new one if
+ CREATE_P and if ABFD doesn't already have a GOT. */
+
+static struct mips_got_info *
+mips_elf_bfd_got (bfd *abfd, bfd_boolean create_p)
+{
+ struct mips_elf_obj_tdata *tdata;
+
+ if (!is_mips_elf (abfd))
+ return NULL;
+
+ tdata = mips_elf_tdata (abfd);
+ if (!tdata->got && create_p)
+ tdata->got = mips_elf_create_got_info (abfd);
+ return tdata->got;
+}
+
+/* Record that ABFD should use output GOT G. */
+
+static void
+mips_elf_replace_bfd_got (bfd *abfd, struct mips_got_info *g)
+{
+ struct mips_elf_obj_tdata *tdata;
+
+ BFD_ASSERT (is_mips_elf (abfd));
+ tdata = mips_elf_tdata (abfd);
+ if (tdata->got)
+ {
+ /* The GOT structure itself and the hash table entries are
+ allocated to a bfd, but the hash tables aren't. */
+ htab_delete (tdata->got->got_entries);
+ htab_delete (tdata->got->got_page_refs);
+ if (tdata->got->got_page_entries)
+ htab_delete (tdata->got->got_page_entries);
+ }
+ tdata->got = 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. */
if (tls_gottprel_reloc_p (r_type))
return GOT_TLS_IE;
- return GOT_NORMAL;
+ return GOT_TLS_NONE;
}
/* Return the number of GOT slots needed for GOT TLS type TYPE. */
case GOT_TLS_IE:
return 1;
- case GOT_NORMAL:
+ case GOT_TLS_NONE:
return 0;
}
abort ();
if (!need_relocs)
return 0;
- switch (tls_type & GOT_TLS_TYPE)
+ switch (tls_type)
{
case GOT_TLS_GD:
return indx != 0 ? 2 : 1;
struct mips_got_info *g,
struct mips_got_entry *entry)
{
- unsigned char tls_type;
-
- tls_type = entry->tls_type & GOT_TLS_TYPE;
- if (tls_type)
+ if (entry->tls_type)
{
- g->tls_gotno += mips_tls_got_entries (tls_type);
- g->relocs += mips_tls_got_relocs (info, tls_type,
+ g->tls_gotno += mips_tls_got_entries (entry->tls_type);
+ g->relocs += mips_tls_got_relocs (info, entry->tls_type,
entry->symndx < 0
? &entry->d.h->root : NULL);
}
g->global_gotno += 1;
}
-/* A htab_traverse callback. If *SLOT describes a GOT entry for a local
- symbol, count the number of GOT entries and TLS relocations that it
- requires. DATA points to a mips_elf_traverse_got_arg structure. */
-
-static int
-mips_elf_count_local_got_entries (void **entryp, void *data)
-{
- struct mips_got_entry *entry;
- struct mips_elf_traverse_got_arg *arg;
-
- entry = (struct mips_got_entry *) *entryp;
- arg = (struct mips_elf_traverse_got_arg *) data;
- if (entry->abfd != NULL && entry->symndx != -1)
- {
- if ((entry->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM)
- {
- if (arg->g->tls_ldm_offset == MINUS_TWO)
- return 1;
- arg->g->tls_ldm_offset = MINUS_TWO;
- }
- mips_elf_count_got_entry (arg->info, arg->g, entry);
- }
-
- return 1;
-}
-
-/* Count the number of TLS GOT entries and relocationss required for the
- global (or forced-local) symbol in ARG1. */
-
-static int
-mips_elf_count_global_tls_entries (void *entry, void *data)
-{
- struct mips_elf_link_hash_entry *hm;
- struct mips_elf_traverse_got_arg *arg;
-
- hm = (struct mips_elf_link_hash_entry *) entry;
- if (hm->root.root.type == bfd_link_hash_indirect
- || hm->root.root.type == bfd_link_hash_warning)
- return 1;
-
- arg = (struct mips_elf_traverse_got_arg *) data;
- if (hm->tls_gd_type)
- {
- arg->g->tls_gotno += 2;
- arg->g->relocs += mips_tls_got_relocs (arg->info, hm->tls_gd_type,
- &hm->root);
- }
- if (hm->tls_ie_type)
- {
- arg->g->tls_gotno += 1;
- arg->g->relocs += mips_tls_got_relocs (arg->info, hm->tls_ie_type,
- &hm->root);
- }
-
- return 1;
-}
-
/* Output a simple dynamic relocation into SRELOC. */
static void
/* 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,
+mips_elf_initialize_tls_slots (bfd *abfd, struct bfd_link_info *info,
+ struct mips_got_entry *entry,
struct mips_elf_link_hash_entry *h,
bfd_vma value)
{
struct mips_elf_link_hash_table *htab;
int indx;
asection *sreloc, *sgot;
- bfd_vma got_offset2;
+ bfd_vma got_offset, got_offset2;
bfd_boolean need_relocs = FALSE;
htab = mips_elf_hash_table (info);
indx = h->root.dynindx;
}
- if (*tls_type_p & GOT_TLS_DONE)
+ if (entry->tls_initialized)
return;
if ((info->shared || indx != 0)
/* Emit necessary relocations. */
sreloc = mips_elf_rel_dyn_section (info, FALSE);
+ got_offset = entry->gotidx;
- switch (*tls_type_p & GOT_TLS_TYPE)
+ switch (entry->tls_type)
{
case GOT_TLS_GD:
/* General Dynamic. */
abort ();
}
- *tls_type_p |= GOT_TLS_DONE;
-}
-
-/* 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,
- struct bfd_link_info *info,
- struct mips_elf_link_hash_entry *h, bfd_vma symbol)
-{
- mips_elf_initialize_tls_slots (abfd, got_index, tls_type, info, h, symbol);
- 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))
- return mips_tls_got_index (abfd, h->tls_gd_got_offset, &h->tls_gd_type,
- info, h, symbol);
- abort ();
+ entry->tls_initialized = TRUE;
}
/* Return the offset from _GLOBAL_OFFSET_TABLE_ of the .got.plt entry
return MINUS_ONE;
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_single_got_index (abfd, r_type, info, h, value);
- else
- return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type,
- info, h, value);
- }
- else
- return entry->gotidx;
+ mips_elf_initialize_tls_slots (abfd, info, entry, h, value);
+ return entry->gotidx;
}
-/* Returns the GOT index for the global symbol indicated by H. */
+/* Return the GOT index of global symbol H in the primary GOT. */
static bfd_vma
-mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h,
- int r_type, struct bfd_link_info *info)
+mips_elf_primary_global_got_index (bfd *obfd, struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
{
struct mips_elf_link_hash_table *htab;
+ long global_got_dynindx;
+ struct mips_got_info *g;
bfd_vma got_index;
- struct mips_got_info *g, *gg;
- long global_got_dynindx = 0;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
- gg = g = htab->got_info;
- if (g->bfd2got && ibfd)
- {
- struct mips_got_entry e, *p;
+ global_got_dynindx = 0;
+ if (htab->global_gotsym != NULL)
+ global_got_dynindx = htab->global_gotsym->dynindx;
- BFD_ASSERT (h->dynindx >= 0);
+ /* 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 primary GOT. That makes it easy to calculate the
+ GOT offset. */
+ BFD_ASSERT (h->dynindx >= global_got_dynindx);
+ g = mips_elf_bfd_got (obfd, FALSE);
+ got_index = ((h->dynindx - global_got_dynindx + g->local_gotno)
+ * MIPS_ELF_GOT_SIZE (obfd));
+ BFD_ASSERT (got_index < htab->sgot->size);
- g = mips_elf_got_for_ibfd (g, ibfd);
- 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 = mips_elf_reloc_tls_type (r_type);
+ return got_index;
+}
+
+/* Return the GOT index for the global symbol indicated by H, which is
+ referenced by a relocation of type R_TYPE in IBFD. */
+
+static bfd_vma
+mips_elf_global_got_index (bfd *obfd, struct bfd_link_info *info, bfd *ibfd,
+ struct elf_link_hash_entry *h, int r_type)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_info *g;
+ struct mips_got_entry lookup, *entry;
+ bfd_vma gotidx;
- p = htab_find (g->got_entries, &e);
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
- BFD_ASSERT (p && p->gotidx > 0);
+ g = mips_elf_bfd_got (ibfd, FALSE);
+ BFD_ASSERT (g);
- if (p->tls_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,
- info, e.d.h, value);
- }
- else
- return p->gotidx;
- }
- }
+ lookup.tls_type = mips_elf_reloc_tls_type (r_type);
+ if (!lookup.tls_type && g == mips_elf_bfd_got (obfd, FALSE))
+ return mips_elf_primary_global_got_index (obfd, info, h);
- if (htab->global_gotsym != NULL)
- global_got_dynindx = htab->global_gotsym->dynindx;
+ lookup.abfd = ibfd;
+ lookup.symndx = -1;
+ lookup.d.h = (struct mips_elf_link_hash_entry *) h;
+ entry = htab_find (g->got_entries, &lookup);
+ BFD_ASSERT (entry);
- if (TLS_RELOC_P (r_type))
+ gotidx = entry->gotidx;
+ BFD_ASSERT (gotidx > 0 && gotidx < htab->sgot->size);
+
+ if (lookup.tls_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.u.def.section->output_offset
+ h->root.u.def.section->output_section->vma);
- got_index = mips_tls_single_got_index (abfd, 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);
- got_index = ((h->dynindx - global_got_dynindx + g->local_gotno)
- * MIPS_ELF_GOT_SIZE (abfd));
+ mips_elf_initialize_tls_slots (obfd, info, entry, lookup.d.h, value);
}
- BFD_ASSERT (got_index < htab->sgot->size);
-
- return got_index;
+ return gotidx;
}
/* Find a GOT page entry that points to within 32KB of VALUE. These
struct mips_elf_link_hash_entry *h,
int r_type)
{
- struct mips_got_entry entry, **loc;
+ struct mips_got_entry lookup, *entry;
+ void **loc;
struct mips_got_info *g;
struct mips_elf_link_hash_table *htab;
+ bfd_vma gotidx;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
- entry.abfd = NULL;
- entry.symndx = -1;
- entry.d.address = value;
- entry.tls_type = mips_elf_reloc_tls_type (r_type);
-
- g = mips_elf_got_for_ibfd (htab->got_info, ibfd);
+ g = mips_elf_bfd_got (ibfd, FALSE);
if (g == NULL)
{
- g = mips_elf_got_for_ibfd (htab->got_info, abfd);
+ g = mips_elf_bfd_got (abfd, FALSE);
BFD_ASSERT (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 (entry.tls_type)
- {
- struct mips_got_entry *p;
- entry.abfd = ibfd;
+ lookup.tls_type = mips_elf_reloc_tls_type (r_type);
+ if (lookup.tls_type)
+ {
+ lookup.abfd = ibfd;
if (tls_ldm_reloc_p (r_type))
{
- entry.symndx = 0;
- entry.d.addend = 0;
+ lookup.symndx = 0;
+ lookup.d.addend = 0;
}
else if (h == NULL)
{
- entry.symndx = r_symndx;
- entry.d.addend = 0;
+ lookup.symndx = r_symndx;
+ lookup.d.addend = 0;
}
else
- entry.d.h = h;
-
- p = (struct mips_got_entry *)
- htab_find (g->got_entries, &entry);
-
- BFD_ASSERT (p);
- return p;
- }
+ {
+ lookup.symndx = -1;
+ lookup.d.h = h;
+ }
- loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
- INSERT);
- if (*loc)
- return *loc;
+ entry = (struct mips_got_entry *) htab_find (g->got_entries, &lookup);
+ BFD_ASSERT (entry);
- entry.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
+ gotidx = entry->gotidx;
+ BFD_ASSERT (gotidx > 0 && gotidx < htab->sgot->size);
- *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+ return entry;
+ }
- if (! *loc)
+ lookup.abfd = NULL;
+ lookup.symndx = -1;
+ lookup.d.address = value;
+ loc = htab_find_slot (g->got_entries, &lookup, INSERT);
+ if (!loc)
return NULL;
- memcpy (*loc, &entry, sizeof entry);
+ entry = (struct mips_got_entry *) *loc;
+ if (entry)
+ return entry;
- if (g->assigned_gotno > g->local_gotno)
+ if (g->assigned_gotno >= g->local_gotno)
{
- (*loc)->gotidx = -1;
/* We didn't allocate enough space in the GOT. */
(*_bfd_error_handler)
(_("not enough GOT space for local GOT entries"));
return NULL;
}
- MIPS_ELF_PUT_WORD (abfd, value,
- (htab->sgot->contents + entry.gotidx));
+ entry = (struct mips_got_entry *) bfd_alloc (abfd, sizeof (*entry));
+ if (!entry)
+ return NULL;
+
+ lookup.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
+ *entry = lookup;
+ *loc = entry;
+
+ MIPS_ELF_PUT_WORD (abfd, value, htab->sgot->contents + entry->gotidx);
/* These GOT entries need a dynamic relocation on VxWorks. */
if (htab->is_vxworks)
s = mips_elf_rel_dyn_section (info, FALSE);
got_address = (htab->sgot->output_section->vma
+ htab->sgot->output_offset
- + entry.gotidx);
+ + entry->gotidx);
rloc = s->contents + (s->reloc_count++ * sizeof (Elf32_External_Rela));
outrel.r_offset = got_address;
bfd_elf32_swap_reloca_out (abfd, &outrel, rloc);
}
- return *loc;
+ return entry;
}
/* Return the number of dynamic section symbols required by OUTPUT_BFD.
return TRUE;
}
+/* Record that input bfd ABFD requires a GOT entry like *LOOKUP
+ (which is owned by the caller and shouldn't be added to the
+ hash table directly). */
+
+static bfd_boolean
+mips_elf_record_got_entry (struct bfd_link_info *info, bfd *abfd,
+ struct mips_got_entry *lookup)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_entry *entry;
+ struct mips_got_info *g;
+ void **loc, **bfd_loc;
+
+ /* Make sure there's a slot for this entry in the master GOT. */
+ htab = mips_elf_hash_table (info);
+ g = htab->got_info;
+ loc = htab_find_slot (g->got_entries, lookup, INSERT);
+ if (!loc)
+ return FALSE;
+
+ /* Populate the entry if it isn't already. */
+ entry = (struct mips_got_entry *) *loc;
+ if (!entry)
+ {
+ entry = (struct mips_got_entry *) bfd_alloc (abfd, sizeof (*entry));
+ if (!entry)
+ return FALSE;
+
+ lookup->tls_initialized = FALSE;
+ lookup->gotidx = -1;
+ *entry = *lookup;
+ *loc = entry;
+ }
+
+ /* Reuse the same GOT entry for the BFD's GOT. */
+ g = mips_elf_bfd_got (abfd, TRUE);
+ if (!g)
+ return FALSE;
+
+ bfd_loc = htab_find_slot (g->got_entries, lookup, INSERT);
+ if (!bfd_loc)
+ return FALSE;
+
+ if (!*bfd_loc)
+ *bfd_loc = entry;
+ return TRUE;
+}
+
/* 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. */
{
struct mips_elf_link_hash_table *htab;
struct mips_elf_link_hash_entry *hmips;
- struct mips_got_entry entry, **loc;
- struct mips_got_info *g;
+ struct mips_got_entry entry;
+ unsigned char tls_type;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
return FALSE;
}
- /* Make sure we have a GOT to put this entry into. */
- g = htab->got_info;
- BFD_ASSERT (g != NULL);
+ tls_type = mips_elf_reloc_tls_type (r_type);
+ if (tls_type == GOT_TLS_NONE && hmips->global_got_area > GGA_NORMAL)
+ hmips->global_got_area = GGA_NORMAL;
entry.abfd = abfd;
entry.symndx = -1;
entry.d.h = (struct mips_elf_link_hash_entry *) h;
- 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)
- return TRUE;
-
- *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
-
- if (! *loc)
- return FALSE;
-
- entry.gotidx = -1;
-
- memcpy (*loc, &entry, sizeof entry);
-
- 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;
+ entry.tls_type = tls_type;
+ return mips_elf_record_got_entry (info, abfd, &entry);
}
/* ABFD has a GOT relocation of type R_TYPE against symbol SYMNDX + ADDEND,
{
struct mips_elf_link_hash_table *htab;
struct mips_got_info *g;
- struct mips_got_entry entry, **loc;
+ struct mips_got_entry entry;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
entry.symndx = symndx;
entry.d.addend = addend;
entry.tls_type = mips_elf_reloc_tls_type (r_type);
- loc = (struct mips_got_entry **)
- htab_find_slot (g->got_entries, &entry, INSERT);
+ return mips_elf_record_got_entry (info, abfd, &entry);
+}
- if (*loc)
- return TRUE;
+/* Record that ABFD has a page relocation against SYMNDX + ADDEND.
+ H is the symbol's hash table entry, or null if SYMNDX is local
+ to ABFD. */
- entry.gotidx = -1;
+static bfd_boolean
+mips_elf_record_got_page_ref (struct bfd_link_info *info, bfd *abfd,
+ long symndx, struct elf_link_hash_entry *h,
+ bfd_signed_vma addend)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_info *g1, *g2;
+ struct mips_got_page_ref lookup, *entry;
+ void **loc, **bfd_loc;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
- *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+ g1 = htab->got_info;
+ BFD_ASSERT (g1 != NULL);
- if (! *loc)
+ if (h)
+ {
+ lookup.symndx = -1;
+ lookup.u.h = (struct mips_elf_link_hash_entry *) h;
+ }
+ else
+ {
+ lookup.symndx = symndx;
+ lookup.u.abfd = abfd;
+ }
+ lookup.addend = addend;
+ loc = htab_find_slot (g1->got_page_refs, &lookup, INSERT);
+ if (loc == NULL)
return FALSE;
- memcpy (*loc, &entry, sizeof entry);
+ entry = (struct mips_got_page_ref *) *loc;
+ if (!entry)
+ {
+ entry = bfd_alloc (abfd, sizeof (*entry));
+ if (!entry)
+ return FALSE;
+
+ *entry = lookup;
+ *loc = entry;
+ }
+
+ /* Add the same entry to the BFD's GOT. */
+ g2 = mips_elf_bfd_got (abfd, TRUE);
+ if (!g2)
+ return FALSE;
+
+ bfd_loc = htab_find_slot (g2->got_page_refs, &lookup, INSERT);
+ if (!bfd_loc)
+ return FALSE;
+
+ if (!*bfd_loc)
+ *bfd_loc = entry;
return TRUE;
}
+/* Add room for N relocations to the .rel(a).dyn section in ABFD. */
+
+static void
+mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info,
+ unsigned int n)
+{
+ asection *s;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ s = mips_elf_rel_dyn_section (info, FALSE);
+ BFD_ASSERT (s != NULL);
+
+ if (htab->is_vxworks)
+ s->size += n * MIPS_ELF_RELA_SIZE (abfd);
+ else
+ {
+ if (s->size == 0)
+ {
+ /* Make room for a null element. */
+ s->size += MIPS_ELF_REL_SIZE (abfd);
+ ++s->reloc_count;
+ }
+ s->size += n * MIPS_ELF_REL_SIZE (abfd);
+ }
+}
+\f
+/* A htab_traverse callback for GOT entries, with DATA pointing to a
+ mips_elf_traverse_got_arg structure. Count the number of GOT
+ entries and TLS relocs. Set DATA->value to true if we need
+ to resolve indirect or warning symbols and then recreate the GOT. */
+
+static int
+mips_elf_check_recreate_got (void **entryp, void *data)
+{
+ struct mips_got_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
+
+ entry = (struct mips_got_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ if (entry->abfd != NULL && entry->symndx == -1)
+ {
+ struct mips_elf_link_hash_entry *h;
+
+ h = entry->d.h;
+ if (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ {
+ arg->value = TRUE;
+ return 0;
+ }
+ }
+ mips_elf_count_got_entry (arg->info, arg->g, entry);
+ return 1;
+}
+
+/* A htab_traverse callback for GOT entries, with DATA pointing to a
+ mips_elf_traverse_got_arg structure. Add all entries to DATA->g,
+ converting entries for indirect and warning symbols into entries
+ for the target symbol. Set DATA->g to null on error. */
+
+static int
+mips_elf_recreate_got (void **entryp, void *data)
+{
+ struct mips_got_entry new_entry, *entry;
+ struct mips_elf_traverse_got_arg *arg;
+ void **slot;
+
+ entry = (struct mips_got_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ if (entry->abfd != NULL
+ && entry->symndx == -1
+ && (entry->d.h->root.root.type == bfd_link_hash_indirect
+ || entry->d.h->root.root.type == bfd_link_hash_warning))
+ {
+ struct mips_elf_link_hash_entry *h;
+
+ new_entry = *entry;
+ entry = &new_entry;
+ h = entry->d.h;
+ do
+ {
+ BFD_ASSERT (h->global_got_area == GGA_NONE);
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+ }
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning);
+ entry->d.h = h;
+ }
+ slot = htab_find_slot (arg->g->got_entries, entry, INSERT);
+ if (slot == NULL)
+ {
+ arg->g = NULL;
+ return 0;
+ }
+ if (*slot == NULL)
+ {
+ if (entry == &new_entry)
+ {
+ entry = bfd_alloc (entry->abfd, sizeof (*entry));
+ if (!entry)
+ {
+ arg->g = NULL;
+ return 0;
+ }
+ *entry = new_entry;
+ }
+ *slot = entry;
+ mips_elf_count_got_entry (arg->info, arg->g, entry);
+ }
+ return 1;
+}
+
/* Return the maximum number of GOT page entries required for RANGE. */
static bfd_vma
return (range->max_addend - range->min_addend + 0x1ffff) >> 16;
}
-/* Record that ABFD has a page relocation against symbol SYMNDX and
- that ADDEND is the addend for that relocation.
-
- This function creates an upper bound on the number of GOT slots
- required; no attempt is made to combine references to non-overridable
- global symbols across multiple input files. */
+/* Record that G requires a page entry that can reach SEC + ADDEND. */
static bfd_boolean
-mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
- long symndx, bfd_signed_vma addend)
+mips_elf_record_got_page_entry (struct mips_got_info *g,
+ asection *sec, bfd_signed_vma addend)
{
- struct mips_elf_link_hash_table *htab;
- struct mips_got_info *g;
struct mips_got_page_entry lookup, *entry;
struct mips_got_page_range **range_ptr, *range;
- bfd_vma old_pages, new_pages;
- void **loc;
-
- htab = mips_elf_hash_table (info);
- BFD_ASSERT (htab != NULL);
-
- g = htab->got_info;
- BFD_ASSERT (g != NULL);
+ bfd_vma old_pages, new_pages;
+ void **loc;
- /* Find the mips_got_page_entry hash table entry for this symbol. */
- lookup.abfd = abfd;
- lookup.symndx = symndx;
+ /* Find the mips_got_page_entry hash table entry for this section. */
+ lookup.sec = sec;
loc = htab_find_slot (g->got_page_entries, &lookup, INSERT);
if (loc == NULL)
return FALSE;
/* Create a mips_got_page_entry if this is the first time we've
- seen the symbol. */
+ seen the section. */
entry = (struct mips_got_page_entry *) *loc;
if (!entry)
{
- entry = bfd_alloc (abfd, sizeof (*entry));
+ entry = bfd_zalloc (sec->owner, sizeof (*entry));
if (!entry)
return FALSE;
- entry->abfd = abfd;
- entry->symndx = symndx;
- entry->ranges = NULL;
- entry->num_pages = 0;
+ entry->sec = sec;
*loc = entry;
}
range = *range_ptr;
if (!range || addend < range->min_addend - 0xffff)
{
- range = bfd_alloc (abfd, sizeof (*range));
+ range = bfd_zalloc (sec->owner, sizeof (*range));
if (!range)
return FALSE;
return TRUE;
}
-/* Add room for N relocations to the .rel(a).dyn section in ABFD. */
+/* A htab_traverse callback for which *REFP points to a mips_got_page_ref
+ and for which DATA points to a mips_elf_traverse_got_arg. Work out
+ whether the page reference described by *REFP needs a GOT page entry,
+ and record that entry in DATA->g if so. Set DATA->g to null on failure. */
-static void
-mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info,
- unsigned int n)
+static bfd_boolean
+mips_elf_resolve_got_page_ref (void **refp, void *data)
{
- asection *s;
+ struct mips_got_page_ref *ref;
+ struct mips_elf_traverse_got_arg *arg;
struct mips_elf_link_hash_table *htab;
+ asection *sec;
+ bfd_vma addend;
- htab = mips_elf_hash_table (info);
- BFD_ASSERT (htab != NULL);
-
- s = mips_elf_rel_dyn_section (info, FALSE);
- BFD_ASSERT (s != NULL);
+ ref = (struct mips_got_page_ref *) *refp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ htab = mips_elf_hash_table (arg->info);
- if (htab->is_vxworks)
- s->size += n * MIPS_ELF_RELA_SIZE (abfd);
- else
+ if (ref->symndx < 0)
{
- if (s->size == 0)
- {
- /* Make room for a null element. */
- s->size += MIPS_ELF_REL_SIZE (abfd);
- ++s->reloc_count;
- }
- s->size += n * MIPS_ELF_REL_SIZE (abfd);
- }
-}
-\f
-/* A htab_traverse callback for GOT entries. Set boolean *DATA to true
- if the GOT entry is for an indirect or warning symbol. */
+ struct mips_elf_link_hash_entry *h;
-static int
-mips_elf_check_recreate_got (void **entryp, void *data)
-{
- struct mips_got_entry *entry;
- bfd_boolean *must_recreate;
+ /* Global GOT_PAGEs decay to GOT_DISP and so don't need page entries. */
+ h = ref->u.h;
+ if (!SYMBOL_REFERENCES_LOCAL (arg->info, &h->root))
+ return 1;
- entry = (struct mips_got_entry *) *entryp;
- must_recreate = (bfd_boolean *) data;
- if (entry->abfd != NULL && entry->symndx == -1)
+ /* Ignore undefined symbols; we'll issue an error later if
+ appropriate. */
+ if (!((h->root.root.type == bfd_link_hash_defined
+ || h->root.root.type == bfd_link_hash_defweak)
+ && h->root.root.u.def.section))
+ return 1;
+
+ sec = h->root.root.u.def.section;
+ addend = h->root.root.u.def.value + ref->addend;
+ }
+ else
{
- struct mips_elf_link_hash_entry *h;
+ Elf_Internal_Sym *isym;
- h = entry->d.h;
- if (h->root.root.type == bfd_link_hash_indirect
- || h->root.root.type == bfd_link_hash_warning)
+ /* Read in the symbol. */
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache, ref->u.abfd,
+ ref->symndx);
+ if (isym == NULL)
{
- *must_recreate = TRUE;
+ arg->g = NULL;
return 0;
}
- }
- return 1;
-}
-
-/* A htab_traverse callback for GOT entries. Add all entries to
- hash table *DATA, converting entries for indirect and warning
- symbols into entries for the target symbol. Set *DATA to null
- on error. */
-
-static int
-mips_elf_recreate_got (void **entryp, void *data)
-{
- htab_t *new_got;
- struct mips_got_entry *entry;
- void **slot;
- new_got = (htab_t *) data;
- entry = (struct mips_got_entry *) *entryp;
- if (entry->abfd != NULL && entry->symndx == -1)
- {
- struct mips_elf_link_hash_entry *h;
+ /* Get the associated input section. */
+ sec = bfd_section_from_elf_index (ref->u.abfd, isym->st_shndx);
+ if (sec == NULL)
+ {
+ arg->g = NULL;
+ return 0;
+ }
- h = entry->d.h;
- while (h->root.root.type == bfd_link_hash_indirect
- || h->root.root.type == bfd_link_hash_warning)
+ /* If this is a mergable section, work out the section and offset
+ of the merged data. For section symbols, the addend specifies
+ of the offset _of_ the first byte in the data, otherwise it
+ specifies the offset _from_ the first byte. */
+ if (sec->flags & SEC_MERGE)
{
- BFD_ASSERT (h->global_got_area == GGA_NONE);
- h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+ void *secinfo;
+
+ secinfo = elf_section_data (sec)->sec_info;
+ if (ELF_ST_TYPE (isym->st_info) == STT_SECTION)
+ addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo,
+ isym->st_value + ref->addend);
+ else
+ addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo,
+ isym->st_value) + ref->addend;
}
- entry->d.h = h;
+ else
+ addend = isym->st_value + ref->addend;
}
- slot = htab_find_slot (*new_got, entry, INSERT);
- if (slot == NULL)
+ if (!mips_elf_record_got_page_entry (arg->g, sec, addend))
{
- *new_got = NULL;
+ arg->g = NULL;
return 0;
}
- if (*slot == NULL)
- *slot = entry;
return 1;
}
/* If any entries in G->got_entries are for indirect or warning symbols,
- replace them with entries for the target symbol. */
+ replace them with entries for the target symbol. Convert g->got_page_refs
+ into got_page_entry structures and estimate the number of page entries
+ that they require. */
static bfd_boolean
-mips_elf_resolve_final_got_entries (struct mips_got_info *g)
+mips_elf_resolve_final_got_entries (struct bfd_link_info *info,
+ struct mips_got_info *g)
{
- bfd_boolean must_recreate;
- htab_t new_got;
+ struct mips_elf_traverse_got_arg tga;
+ struct mips_got_info oldg;
- must_recreate = FALSE;
- htab_traverse (g->got_entries, mips_elf_check_recreate_got, &must_recreate);
- if (must_recreate)
- {
- new_got = htab_create (htab_size (g->got_entries),
- mips_elf_got_entry_hash,
- mips_elf_got_entry_eq, NULL);
- htab_traverse (g->got_entries, mips_elf_recreate_got, &new_got);
- if (new_got == NULL)
+ oldg = *g;
+
+ tga.info = info;
+ tga.g = g;
+ tga.value = FALSE;
+ htab_traverse (g->got_entries, mips_elf_check_recreate_got, &tga);
+ if (tga.value)
+ {
+ *g = oldg;
+ g->got_entries = htab_create (htab_size (oldg.got_entries),
+ mips_elf_got_entry_hash,
+ mips_elf_got_entry_eq, NULL);
+ if (!g->got_entries)
+ return FALSE;
+
+ htab_traverse (oldg.got_entries, mips_elf_recreate_got, &tga);
+ if (!tga.g)
return FALSE;
- htab_delete (g->got_entries);
- g->got_entries = new_got;
+ htab_delete (oldg.got_entries);
}
+
+ 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;
+
+ tga.info = info;
+ tga.g = g;
+ htab_traverse (g->got_page_refs, mips_elf_resolve_got_page_ref, &tga);
+
return TRUE;
}
-/* A mips_elf_link_hash_traverse callback for which DATA points
- to the link_info structure. Count the number of type (3) entries
- in the master GOT. */
+/* A mips_elf_link_hash_traverse callback for which DATA points to the
+ link_info structure. Decide whether the hash entry needs an entry in
+ the global part of the primary GOT, setting global_got_area accordingly.
+ Count the number of global symbols that are in the primary GOT only
+ because they have relocations against them (reloc_only_gotno). */
static int
mips_elf_count_got_symbols (struct mips_elf_link_hash_entry *h, void *data)
|| (h->got_only_for_calls
? SYMBOL_CALLS_LOCAL (info, &h->root)
: SYMBOL_REFERENCES_LOCAL (info, &h->root)))
- {
- /* The symbol belongs in the local GOT. We no longer need this
- entry if it was only used for relocations; those relocations
- will be against the null or section symbol instead of H. */
- if (h->global_got_area != GGA_RELOC_ONLY)
- g->local_gotno++;
- h->global_got_area = GGA_NONE;
- }
+ /* The symbol belongs in the local GOT. We no longer need this
+ entry if it was only used for relocations; those relocations
+ will be against the null or section symbol instead of H. */
+ h->global_got_area = GGA_NONE;
else if (htab->is_vxworks
&& h->got_only_for_calls
&& h->root.plt.offset != MINUS_ONE)
they don't need entries in the regular GOT. .got.plt entries
will be allocated by _bfd_mips_elf_adjust_dynamic_symbol. */
h->global_got_area = GGA_NONE;
- else
+ else if (h->global_got_area == GGA_RELOC_ONLY)
{
+ g->reloc_only_gotno++;
g->global_gotno++;
- if (h->global_got_area == GGA_RELOC_ONLY)
- g->reloc_only_gotno++;
}
}
return 1;
}
\f
-/* Compute the hash value of the bfd in a bfd2got hash entry. */
-
-static hashval_t
-mips_elf_bfd2got_entry_hash (const void *entry_)
-{
- const struct mips_elf_bfd2got_hash *entry
- = (struct mips_elf_bfd2got_hash *)entry_;
-
- return entry->bfd->id;
-}
-
-/* Check whether two hash entries have the same bfd. */
+/* A htab_traverse callback for GOT entries. Add each one to the GOT
+ given in mips_elf_traverse_got_arg DATA. Clear DATA->G on error. */
static int
-mips_elf_bfd2got_entry_eq (const void *entry1, const void *entry2)
-{
- const struct mips_elf_bfd2got_hash *e1
- = (const struct mips_elf_bfd2got_hash *)entry1;
- const struct mips_elf_bfd2got_hash *e2
- = (const struct mips_elf_bfd2got_hash *)entry2;
-
- return e1->bfd == e2->bfd;
-}
-
-/* In a multi-got link, determine the GOT to be used for IBFD. G must
- be the master GOT data. */
-
-static struct mips_got_info *
-mips_elf_got_for_ibfd (struct mips_got_info *g, bfd *ibfd)
-{
- struct mips_elf_bfd2got_hash e, *p;
-
- if (! g->bfd2got)
- return g;
-
- e.bfd = ibfd;
- p = htab_find (g->bfd2got, &e);
- return p ? p->g : NULL;
-}
-
-/* Use BFD2GOT to find ABFD's got entry, creating one if none exists.
- Return NULL if an error occured. */
-
-static struct mips_got_info *
-mips_elf_get_got_for_bfd (struct htab *bfd2got, bfd *output_bfd,
- bfd *input_bfd)
+mips_elf_add_got_entry (void **entryp, void *data)
{
- struct mips_elf_bfd2got_hash bfdgot_entry, *bfdgot;
- void **bfdgotp;
-
- bfdgot_entry.bfd = input_bfd;
- bfdgotp = htab_find_slot (bfd2got, &bfdgot_entry, INSERT);
- bfdgot = (struct mips_elf_bfd2got_hash *) *bfdgotp;
+ struct mips_got_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
+ void **slot;
- if (bfdgot == NULL)
+ entry = (struct mips_got_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ slot = htab_find_slot (arg->g->got_entries, entry, INSERT);
+ if (!slot)
{
- bfdgot = ((struct mips_elf_bfd2got_hash *)
- bfd_alloc (output_bfd, sizeof (struct mips_elf_bfd2got_hash)));
- if (bfdgot == NULL)
- return NULL;
-
- *bfdgotp = bfdgot;
-
- bfdgot->bfd = input_bfd;
- bfdgot->g = mips_elf_create_got_info (input_bfd, FALSE);
- if (bfdgot->g == NULL)
- return NULL;
+ arg->g = NULL;
+ return 0;
}
-
- return bfdgot->g;
-}
-
-/* A htab_traverse callback for the entries in the master got.
- Create one separate got for each bfd that has entries in the global
- got, such that we can tell how many local and global entries each
- bfd requires. */
-
-static int
-mips_elf_make_got_per_bfd (void **entryp, void *p)
-{
- struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
- struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
- struct mips_got_info *g;
-
- g = mips_elf_get_got_for_bfd (arg->bfd2got, arg->obfd, entry->abfd);
- if (g == NULL)
+ if (!*slot)
{
- arg->obfd = NULL;
- return 0;
+ *slot = entry;
+ mips_elf_count_got_entry (arg->info, arg->g, entry);
}
-
- /* Insert the GOT entry in the bfd's got entry hash table. */
- entryp = htab_find_slot (g->got_entries, entry, INSERT);
- if (*entryp != NULL)
- return 1;
-
- *entryp = entry;
- mips_elf_count_got_entry (arg->info, g, entry);
-
return 1;
}
-/* A htab_traverse callback for the page entries in the master got.
- Associate each page entry with the bfd's got. */
+/* A htab_traverse callback for GOT page entries. Add each one to the GOT
+ given in mips_elf_traverse_got_arg DATA. Clear DATA->G on error. */
static int
-mips_elf_make_got_pages_per_bfd (void **entryp, void *p)
+mips_elf_add_got_page_entry (void **entryp, void *data)
{
- struct mips_got_page_entry *entry = (struct mips_got_page_entry *) *entryp;
- struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *) p;
- struct mips_got_info *g;
+ struct mips_got_page_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
+ void **slot;
- g = mips_elf_get_got_for_bfd (arg->bfd2got, arg->obfd, entry->abfd);
- if (g == NULL)
+ entry = (struct mips_got_page_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ slot = htab_find_slot (arg->g->got_page_entries, entry, INSERT);
+ if (!slot)
{
- arg->obfd = NULL;
+ arg->g = NULL;
return 0;
}
-
- /* Insert the GOT entry in the bfd's got entry hash table. */
- entryp = htab_find_slot (g->got_page_entries, entry, INSERT);
- if (*entryp != NULL)
- return 1;
-
- *entryp = entry;
- g->page_gotno += entry->num_pages;
+ if (!*slot)
+ {
+ *slot = entry;
+ arg->g->page_gotno += entry->num_pages;
+ }
return 1;
}
-/* Consider merging the got described by BFD2GOT with TO, using the
- information given by ARG. Return -1 if this would lead to overflow,
- 1 if they were merged successfully, and 0 if a merge failed due to
- lack of memory. (These values are chosen so that nonnegative return
- values can be returned by a htab_traverse callback.) */
+/* Consider merging FROM, which is ABFD's GOT, into TO. Return -1 if
+ this would lead to overflow, 1 if they were merged successfully,
+ and 0 if a merge failed due to lack of memory. (These values are chosen
+ so that nonnegative return values can be returned by a htab_traverse
+ callback.) */
static int
-mips_elf_merge_got_with (struct mips_elf_bfd2got_hash *bfd2got,
+mips_elf_merge_got_with (bfd *abfd, struct mips_got_info *from,
struct mips_got_info *to,
struct mips_elf_got_per_bfd_arg *arg)
{
- struct mips_got_info *from = bfd2got->g;
+ struct mips_elf_traverse_got_arg tga;
unsigned int estimate;
/* Work out how many page entries we would need for the combined GOT. */
if (estimate > arg->max_count)
return -1;
- /* Commit to the merge. Record that TO is now the bfd for this got. */
- bfd2got->g = to;
-
/* Transfer the bfd's got information from FROM to TO. */
- htab_traverse (from->got_entries, mips_elf_make_got_per_bfd, arg);
- if (arg->obfd == NULL)
+ tga.info = arg->info;
+ tga.g = to;
+ htab_traverse (from->got_entries, mips_elf_add_got_entry, &tga);
+ if (!tga.g)
return 0;
- htab_traverse (from->got_page_entries, mips_elf_make_got_pages_per_bfd, arg);
- if (arg->obfd == NULL)
+ htab_traverse (from->got_page_entries, mips_elf_add_got_page_entry, &tga);
+ if (!tga.g)
return 0;
- /* We don't have to worry about releasing memory of the actual
- got entries, since they're all in the master got_entries hash
- table anyway. */
- htab_delete (from->got_entries);
- htab_delete (from->got_page_entries);
+ mips_elf_replace_bfd_got (abfd, to);
return 1;
}
-/* Attempt to merge gots of different input bfds. Try to use as much
+/* Attempt to merge GOT G, which belongs to ABFD. Try to use as much
as possible of the primary got, since it doesn't require explicit
dynamic relocations, but don't use bfds that would reference global
symbols out of the addressable range. Failing the primary got,
attempt to merge with the current got, or finish the current got
and then make make the new got current. */
-static int
-mips_elf_merge_gots (void **bfd2got_, void *p)
+static bfd_boolean
+mips_elf_merge_got (bfd *abfd, struct mips_got_info *g,
+ struct mips_elf_got_per_bfd_arg *arg)
{
- struct mips_elf_bfd2got_hash *bfd2got
- = (struct mips_elf_bfd2got_hash *)*bfd2got_;
- struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
- struct mips_got_info *g;
unsigned int estimate;
int result;
- g = bfd2got->g;
+ if (!mips_elf_resolve_final_got_entries (arg->info, g))
+ return FALSE;
/* Work out the number of page, local and TLS entries. */
estimate = arg->max_pages;
a starting point for the primary GOT. */
if (!arg->primary)
{
- arg->primary = bfd2got->g;
- return 1;
+ arg->primary = g;
+ return TRUE;
}
/* Try merging with the primary GOT. */
- result = mips_elf_merge_got_with (bfd2got, arg->primary, arg);
+ result = mips_elf_merge_got_with (abfd, g, arg->primary, arg);
if (result >= 0)
return result;
}
/* If we can merge with the last-created got, do it. */
if (arg->current)
{
- result = mips_elf_merge_got_with (bfd2got, arg->current, arg);
+ result = mips_elf_merge_got_with (abfd, g, arg->current, arg);
if (result >= 0)
return result;
}
g->next = arg->current;
arg->current = g;
- return 1;
+ return TRUE;
+}
+
+/* ENTRYP is a hash table entry for a mips_got_entry. Set its gotidx
+ to GOTIDX, duplicating the entry if it has already been assigned
+ an index in a different GOT. */
+
+static bfd_boolean
+mips_elf_set_gotidx (void **entryp, long gotidx)
+{
+ struct mips_got_entry *entry;
+
+ entry = (struct mips_got_entry *) *entryp;
+ if (entry->gotidx > 0)
+ {
+ struct mips_got_entry *new_entry;
+
+ new_entry = bfd_alloc (entry->abfd, sizeof (*entry));
+ if (!new_entry)
+ return FALSE;
+
+ *new_entry = *entry;
+ *entryp = new_entry;
+ entry = new_entry;
+ }
+ entry->gotidx = gotidx;
+ return TRUE;
}
-/* Set the TLS GOT index for the GOT entry in ENTRYP. ENTRYP's NEXT field
- is null iff there is just a single GOT. */
+/* Set the TLS GOT index for the GOT entry in ENTRYP. DATA points to a
+ mips_elf_traverse_got_arg in which DATA->value is the size of one
+ GOT entry. Set DATA->g to null on failure. */
static int
-mips_elf_initialize_tls_index (void **entryp, void *p)
+mips_elf_initialize_tls_index (void **entryp, void *data)
{
- struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
- struct mips_got_info *g = p;
- bfd_vma next_index;
- unsigned char tls_type;
+ struct mips_got_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
/* We're only interested in TLS symbols. */
- tls_type = (entry->tls_type & GOT_TLS_TYPE);
- if (tls_type == 0)
+ entry = (struct mips_got_entry *) *entryp;
+ if (entry->tls_type == GOT_TLS_NONE)
return 1;
- next_index = MIPS_ELF_GOT_SIZE (entry->abfd) * (long) g->tls_assigned_gotno;
-
- if (entry->symndx == -1 && g->next == NULL)
- {
- /* A type (3) got entry in the single-GOT case. We use the symbol's
- hash table entry to track its index. */
- 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
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ if (!mips_elf_set_gotidx (entryp, arg->value * arg->g->tls_assigned_gotno))
{
- 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
- a GOT resolve to the same index. */
- if (g->tls_ldm_offset != MINUS_TWO && g->tls_ldm_offset != MINUS_ONE)
- {
- entry->gotidx = g->tls_ldm_offset;
- return 1;
- }
- g->tls_ldm_offset = next_index;
- }
- entry->gotidx = next_index;
+ arg->g = NULL;
+ return 0;
}
/* Account for the entries we've just allocated. */
- g->tls_assigned_gotno += mips_tls_got_entries (tls_type);
+ arg->g->tls_assigned_gotno += mips_tls_got_entries (entry->tls_type);
return 1;
}
/* A htab_traverse callback for secondary GOT entries, where DATA points
to a mips_elf_traverse_got_arg. Assign GOT indices to global entries
and record the number of relocations they require. DATA->value is
- the size of one GOT entry. */
+ the size of one GOT entry. Set DATA->g to null on failure. */
static int
mips_elf_set_global_gotidx (void **entryp, void *data)
&& entry->symndx == -1
&& entry->d.h->global_got_area != GGA_NONE)
{
- entry->gotidx = arg->value * (long) arg->g->assigned_gotno++;
+ if (!mips_elf_set_gotidx (entryp, arg->value * arg->g->assigned_gotno))
+ {
+ arg->g = NULL;
+ return 0;
+ }
+ arg->g->assigned_gotno += 1;
+
if (arg->info->shared
|| (elf_hash_table (arg->info)->dynamic_sections_created
&& entry->d.h->root.def_dynamic
static bfd_vma
mips_elf_adjust_gp (bfd *abfd, struct mips_got_info *g, bfd *ibfd)
{
- if (g->bfd2got == NULL)
+ if (!g->next)
return 0;
- g = mips_elf_got_for_ibfd (g, ibfd);
+ g = mips_elf_bfd_got (ibfd, FALSE);
if (! g)
return 0;
struct mips_elf_traverse_got_arg tga;
struct mips_got_info *g, *gg;
unsigned int assign, needed_relocs;
- bfd *dynobj;
+ bfd *dynobj, *ibfd;
dynobj = elf_hash_table (info)->dynobj;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
g = htab->got_info;
- g->bfd2got = htab_try_create (1, mips_elf_bfd2got_entry_hash,
- mips_elf_bfd2got_entry_eq, NULL);
- if (g->bfd2got == NULL)
- return FALSE;
- got_per_bfd_arg.bfd2got = g->bfd2got;
got_per_bfd_arg.obfd = abfd;
got_per_bfd_arg.info = info;
-
- /* Count how many GOT entries each input bfd requires, creating a
- map from bfd to got info while at that. */
- htab_traverse (g->got_entries, mips_elf_make_got_per_bfd, &got_per_bfd_arg);
- if (got_per_bfd_arg.obfd == NULL)
- return FALSE;
-
- /* Also count how many page entries each input bfd requires. */
- htab_traverse (g->got_page_entries, mips_elf_make_got_pages_per_bfd,
- &got_per_bfd_arg);
- if (got_per_bfd_arg.obfd == NULL)
- return FALSE;
-
got_per_bfd_arg.current = NULL;
got_per_bfd_arg.primary = NULL;
got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (info)
/* 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
to be the primary GOT. */
- htab_traverse (g->bfd2got, mips_elf_merge_gots, &got_per_bfd_arg);
- if (got_per_bfd_arg.obfd == NULL)
- return FALSE;
+ for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link_next)
+ {
+ gg = mips_elf_bfd_got (ibfd, FALSE);
+ if (gg && !mips_elf_merge_got (ibfd, gg, &got_per_bfd_arg))
+ return FALSE;
+ }
/* If we do not find any suitable primary GOT, create an empty one. */
if (got_per_bfd_arg.primary == NULL)
- g->next = mips_elf_create_got_info (abfd, FALSE);
+ g->next = mips_elf_create_got_info (abfd);
else
g->next = got_per_bfd_arg.primary;
g->next->next = got_per_bfd_arg.current;
didn't mark in check_relocs, and we want a quick way to find it.
We can't just use gg->next because we're going to reverse the
list. */
- {
- struct mips_elf_bfd2got_hash *bfdgot;
- void **bfdgotp;
-
- bfdgot = (struct mips_elf_bfd2got_hash *)bfd_alloc
- (abfd, sizeof (struct mips_elf_bfd2got_hash));
-
- if (bfdgot == NULL)
- return FALSE;
-
- bfdgot->bfd = abfd;
- bfdgot->g = g;
- bfdgotp = htab_find_slot (gg->bfd2got, bfdgot, INSERT);
-
- BFD_ASSERT (*bfdgotp == NULL);
- *bfdgotp = bfdgot;
- }
+ mips_elf_replace_bfd_got (abfd, g);
/* Every symbol that is referenced in a dynamic relocation must be
present in the primary GOT, so arrange for them to appear after
/* 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);
+ tga.g = g;
+ tga.value = MIPS_ELF_GOT_SIZE (abfd);
+ htab_traverse (g->got_entries, mips_elf_initialize_tls_index, &tga);
+ if (!tga.g)
+ return FALSE;
BFD_ASSERT (g->tls_assigned_gotno == assign);
/* Move onto the next GOT. It will be a secondary GOT if nonull. */
tga.value = MIPS_ELF_GOT_SIZE (abfd);
tga.g = g;
htab_traverse (g->got_entries, mips_elf_set_global_gotidx, &tga);
- BFD_ASSERT (g->assigned_gotno - g->local_gotno <= g->global_gotno);
-
+ if (!tga.g)
+ return FALSE;
+ BFD_ASSERT (g->assigned_gotno == g->local_gotno + g->global_gotno);
g->assigned_gotno = save_assign;
+
if (info->shared)
{
g->relocs += g->local_gotno - g->assigned_gotno;
&& ! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
- htab->got_info = mips_elf_create_got_info (abfd, TRUE);
+ htab->got_info = mips_elf_create_got_info (abfd);
mips_elf_section_data (s)->elf.this_hdr.sh_flags
|= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
&& h->fn_stub != NULL
&& (r_type != R_MIPS16_CALL16 || h->need_fn_stub))
|| (local_p
- && elf_tdata (input_bfd)->local_stubs != NULL
- && elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
+ && mips_elf_tdata (input_bfd)->local_stubs != NULL
+ && mips_elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
&& !section_allows_mips16_refs_p (input_section))
{
/* This is a 32- or 64-bit call to a 16-bit function. We should
stub. */
if (local_p)
{
- sec = elf_tdata (input_bfd)->local_stubs[r_symndx];
+ sec = mips_elf_tdata (input_bfd)->local_stubs[r_symndx];
value = 0;
}
else
else if (r_type == R_MIPS16_26 && !info->relocatable
&& ((h != NULL && (h->call_stub != NULL || h->call_fp_stub != NULL))
|| (local_p
- && elf_tdata (input_bfd)->local_call_stubs != NULL
- && elf_tdata (input_bfd)->local_call_stubs[r_symndx] != NULL))
+ && mips_elf_tdata (input_bfd)->local_call_stubs != NULL
+ && mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx] != NULL))
&& !target_is_16_bit_code_p)
{
if (local_p)
- sec = elf_tdata (input_bfd)->local_call_stubs[r_symndx];
+ sec = mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx];
else
{
/* If both call_stub and call_fp_stub are defined, we can figure
else
{
BFD_ASSERT (addend == 0);
- g = mips_elf_global_got_index (dynobj, input_bfd,
- &h->root, r_type, info);
+ g = mips_elf_global_got_index (abfd, info, input_bfd,
+ &h->root, r_type);
if (!TLS_RELOC_P (r_type)
&& !elf_hash_table (info)->dynamic_sections_created)
/* This is a static link. We must initialize the GOT entry. */
&& (asym->value & 1) != 0)
{
asym->value--;
- if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS)
+ if (MICROMIPS_P (abfd))
elfsym->internal_elf_sym.st_other
= ELF_ST_SET_MICROMIPS (elfsym->internal_elf_sym.st_other);
else
case SHN_MIPS_TEXT:
/* This section is used in a shared object. */
- if (elf_tdata (abfd)->elf_text_section == NULL)
+ if (mips_elf_tdata (abfd)->elf_text_section == NULL)
{
asymbol *elf_text_symbol;
asection *elf_text_section;
/* Initialize the section. */
- elf_tdata (abfd)->elf_text_section = elf_text_section;
- elf_tdata (abfd)->elf_text_symbol = elf_text_symbol;
+ mips_elf_tdata (abfd)->elf_text_section = elf_text_section;
+ mips_elf_tdata (abfd)->elf_text_symbol = elf_text_symbol;
elf_text_section->symbol = elf_text_symbol;
- elf_text_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_text_symbol;
+ elf_text_section->symbol_ptr_ptr = &mips_elf_tdata (abfd)->elf_text_symbol;
elf_text_section->name = ".text";
elf_text_section->flags = SEC_NO_FLAGS;
/* This code used to do *secp = bfd_und_section_ptr if
info->shared. I don't know why, and that doesn't make sense,
so I took it out. */
- *secp = elf_tdata (abfd)->elf_text_section;
+ *secp = mips_elf_tdata (abfd)->elf_text_section;
break;
case SHN_MIPS_ACOMMON:
/* Fall through. XXX Can we treat this as allocated data? */
case SHN_MIPS_DATA:
/* This section is used in a shared object. */
- if (elf_tdata (abfd)->elf_data_section == NULL)
+ if (mips_elf_tdata (abfd)->elf_data_section == NULL)
{
asymbol *elf_data_symbol;
asection *elf_data_section;
/* Initialize the section. */
- elf_tdata (abfd)->elf_data_section = elf_data_section;
- elf_tdata (abfd)->elf_data_symbol = elf_data_symbol;
+ mips_elf_tdata (abfd)->elf_data_section = elf_data_section;
+ mips_elf_tdata (abfd)->elf_data_symbol = elf_data_symbol;
elf_data_section->symbol = elf_data_symbol;
- elf_data_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_data_symbol;
+ elf_data_section->symbol_ptr_ptr = &mips_elf_tdata (abfd)->elf_data_symbol;
elf_data_section->name = ".data";
elf_data_section->flags = SEC_NO_FLAGS;
/* This code used to do *secp = bfd_und_section_ptr if
info->shared. I don't know why, and that doesn't make sense,
so I took it out. */
- *secp = elf_tdata (abfd)->elf_data_section;
+ *secp = mips_elf_tdata (abfd)->elf_data_section;
break;
case SHN_MIPS_SUNDEFINED:
}
/* Create the .plt, .rel(a).plt, .dynbss and .rel(a).bss sections.
- Also create the _PROCEDURE_LINKAGE_TABLE symbol. */
+ Also, on VxWorks, create the _PROCEDURE_LINKAGE_TABLE_ symbol. */
if (!_bfd_elf_create_dynamic_sections (abfd, info))
return FALSE;
/* Record this stub in an array of local symbol stubs for
this BFD. */
- if (elf_tdata (abfd)->local_stubs == NULL)
+ if (mips_elf_tdata (abfd)->local_stubs == NULL)
{
unsigned long symcount;
asection **n;
n = bfd_zalloc (abfd, amt);
if (n == NULL)
return FALSE;
- elf_tdata (abfd)->local_stubs = n;
+ mips_elf_tdata (abfd)->local_stubs = n;
}
sec->flags |= SEC_KEEP;
- elf_tdata (abfd)->local_stubs[r_symndx] = sec;
+ mips_elf_tdata (abfd)->local_stubs[r_symndx] = sec;
/* We don't need to set mips16_stubs_seen in this case.
That flag is used to see whether we need to look through
/* Record this stub in an array of local symbol call_stubs for
this BFD. */
- if (elf_tdata (abfd)->local_call_stubs == NULL)
+ if (mips_elf_tdata (abfd)->local_call_stubs == NULL)
{
unsigned long symcount;
asection **n;
n = bfd_zalloc (abfd, amt);
if (n == NULL)
return FALSE;
- elf_tdata (abfd)->local_call_stubs = n;
+ mips_elf_tdata (abfd)->local_call_stubs = n;
}
sec->flags |= SEC_KEEP;
- elf_tdata (abfd)->local_call_stubs[r_symndx] = sec;
+ mips_elf_tdata (abfd)->local_call_stubs[r_symndx] = sec;
/* We don't need to set mips16_stubs_seen in this case.
That flag is used to see whether we need to look through
case R_MIPS_GOT_PAGE:
case R_MICROMIPS_GOT_PAGE:
- /* If this is a global, overridable symbol, GOT_PAGE will
- decay to GOT_DISP, so we'll need a GOT entry for it. */
- if (h)
- {
- struct mips_elf_link_hash_entry *hmips =
- (struct mips_elf_link_hash_entry *) h;
-
- /* This symbol is definitely not overridable. */
- if (hmips->root.def_regular
- && ! (info->shared && ! info->symbolic
- && ! hmips->root.forced_local))
- h = NULL;
- }
- /* Fall through. */
-
case R_MIPS16_GOT16:
case R_MIPS_GOT16:
case R_MIPS_GOT_HI16:
}
else
addend = rel->r_addend;
- if (!mips_elf_record_got_page_entry (info, abfd, r_symndx,
- addend))
+ if (!mips_elf_record_got_page_ref (info, abfd, r_symndx,
+ h, addend))
return FALSE;
+
+ if (h)
+ {
+ struct mips_elf_link_hash_entry *hmips =
+ (struct mips_elf_link_hash_entry *) h;
+
+ /* This symbol is definitely not overridable. */
+ if (hmips->root.def_regular
+ && ! (info->shared && ! info->symbolic
+ && ! hmips->root.forced_local))
+ h = NULL;
+ }
}
+ /* If this is a global, overridable symbol, GOT_PAGE will
+ decay to GOT_DISP, so we'll need a GOT entry for it. */
/* Fall through. */
case R_MIPS_GOT_DISP:
struct mips_got_info *g;
bfd_size_type loadable_size = 0;
bfd_size_type page_gotno;
- bfd *sub;
+ bfd *ibfd;
struct mips_elf_traverse_got_arg tga;
struct mips_elf_link_hash_table *htab;
g->local_gotno += htab->reserved_gotno;
g->assigned_gotno = htab->reserved_gotno;
- /* Replace entries for indirect and warning symbols with entries for
- the target symbol. */
- if (!mips_elf_resolve_final_got_entries (g))
- return FALSE;
-
- /* Count the number of GOT symbols. */
+ /* Decide which symbols need to go in the global part of the GOT and
+ count the number of reloc-only GOT symbols. */
mips_elf_link_hash_traverse (htab, mips_elf_count_got_symbols, info);
+ if (!mips_elf_resolve_final_got_entries (info, g))
+ return FALSE;
+
/* Calculate the total loadable size of the output. That
will give us the maximum number of GOT_PAGE entries
required. */
- for (sub = info->input_bfds; sub; sub = sub->link_next)
+ for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link_next)
{
asection *subsection;
- for (subsection = sub->sections;
+ for (subsection = ibfd->sections;
subsection;
subsection = subsection->next)
{
sections. Is 5 enough? */
page_gotno = (loadable_size >> 16) + 5;
- /* Choose the smaller of the two estimates; both are intended to be
+ /* Choose the smaller of the two page estimates; both are intended to be
conservative. */
if (page_gotno > g->page_gotno)
page_gotno = g->page_gotno;
g->local_gotno += page_gotno;
- /* Count the number of local GOT entries and TLS relocs. */
- tga.info = info;
- tga.g = g;
- htab_traverse (g->got_entries, mips_elf_count_local_got_entries, &tga);
-
- /* 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. */
- elf_link_hash_traverse (elf_hash_table (info),
- mips_elf_count_global_tls_entries,
- &tga);
-
s->size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
s->size += g->global_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
s->size += g->tls_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
/* VxWorks does not support multiple GOTs. It initializes $gp to
__GOTT_BASE__[__GOTT_INDEX__], the value of which is set by the
dynamic loader. */
- if (htab->is_vxworks)
- {
- /* VxWorks executables do not need a GOT. */
- if (info->shared)
- {
- /* Each VxWorks GOT entry needs an explicit relocation. */
- unsigned int count;
-
- count = g->global_gotno + g->local_gotno - htab->reserved_gotno;
- if (count)
- mips_elf_allocate_dynamic_relocations (dynobj, info, count);
- }
- }
- else if (s->size > MIPS_ELF_GOT_MAX_SIZE (info))
+ if (!htab->is_vxworks && s->size > MIPS_ELF_GOT_MAX_SIZE (info))
{
if (!mips_elf_multi_got (output_bfd, info, s, page_gotno))
return FALSE;
}
else
{
+ /* Record that all bfds use G. This also has the effect of freeing
+ the per-bfd GOTs, which we no longer need. */
+ for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link_next)
+ if (mips_elf_bfd_got (ibfd, FALSE))
+ mips_elf_replace_bfd_got (ibfd, g);
+ mips_elf_replace_bfd_got (output_bfd, g);
+
/* 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);
+ tga.info = info;
+ tga.g = g;
+ tga.value = MIPS_ELF_GOT_SIZE (output_bfd);
+ htab_traverse (g->got_entries, mips_elf_initialize_tls_index, &tga);
+ if (!tga.g)
+ return FALSE;
BFD_ASSERT (g->tls_assigned_gotno
== g->global_gotno + g->local_gotno + g->tls_gotno);
+ /* Each VxWorks GOT entry needs an explicit relocation. */
+ if (htab->is_vxworks && info->shared)
+ g->relocs += g->global_gotno + g->local_gotno - htab->reserved_gotno;
+
/* Allocate room for the TLS relocations. */
if (g->relocs)
mips_elf_allocate_dynamic_relocations (dynobj, info, g->relocs);
bfd_vma value;
value = sym->st_value;
- offset = mips_elf_global_got_index (dynobj, output_bfd, h,
- R_MIPS_GOT16, info);
+ offset = mips_elf_primary_global_got_index (output_bfd, info, h);
MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
}
e.abfd = output_bfd;
e.symndx = -1;
e.d.h = hmips;
- e.tls_type = 0;
+ e.tls_type = GOT_TLS_NONE;
for (g = g->next; g->next != gg; g = g->next)
{
&e)))
{
offset = p->gotidx;
+ BFD_ASSERT (offset > 0 && offset < htab->sgot->size);
if (info->shared
|| (elf_hash_table (info)->dynamic_sections_created
&& p->d.h != NULL
if (IRIX_COMPAT (output_bfd) == ict_irix6)
mips_elf_irix6_finish_dynamic_symbol (output_bfd, name, sym);
- /* Keep dynamic MIPS16 symbols odd. This allows the dynamic linker to
- treat MIPS16 symbols like any other. */
+ /* Keep dynamic compressed symbols odd. This allows the dynamic linker
+ to treat compressed symbols like any other. */
if (ELF_ST_IS_MIPS16 (sym->st_other))
{
BFD_ASSERT (sym->st_value & 1);
sym->st_other -= STO_MIPS16;
}
+ else if (ELF_ST_IS_MICROMIPS (sym->st_other))
+ {
+ BFD_ASSERT (sym->st_value & 1);
+ sym->st_other -= STO_MICROMIPS;
+ }
return TRUE;
}
asection *s;
/* Install the symbol value in the GOT. */
- offset = mips_elf_global_got_index (dynobj, output_bfd, h,
- R_MIPS_GOT16, info);
+ offset = mips_elf_primary_global_got_index (output_bfd, info, h);
MIPS_ELF_PUT_WORD (output_bfd, sym->st_value, sgot->contents + offset);
/* Add a dynamic relocation for it. */
BFD_ASSERT (sdyn != NULL);
BFD_ASSERT (gg != NULL);
- g = mips_elf_got_for_ibfd (gg, output_bfd);
+ g = mips_elf_bfd_got (output_bfd, FALSE);
BFD_ASSERT (g != NULL);
for (b = sdyn->contents;
s = bfd_get_section_by_name (abfd, ".reginfo");
if (s != NULL && (s->flags & SEC_LOAD) != 0)
{
- for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+ for (m = elf_seg_map (abfd); m != NULL; m = m->next)
if (m->p_type == PT_MIPS_REGINFO)
break;
if (m == NULL)
m->sections[0] = s;
/* We want to put it after the PHDR and INTERP segments. */
- pm = &elf_tdata (abfd)->segment_map;
+ pm = &elf_seg_map (abfd);
while (*pm != NULL
&& ((*pm)->p_type == PT_PHDR
|| (*pm)->p_type == PT_INTERP))
{
struct elf_segment_map *options_segment;
- pm = &elf_tdata (abfd)->segment_map;
+ pm = &elf_seg_map (abfd);
while (*pm != NULL
&& ((*pm)->p_type == PT_PHDR
|| (*pm)->p_type == PT_INTERP))
&& bfd_get_section_by_name (abfd, ".dynamic") != NULL
&& bfd_get_section_by_name (abfd, ".mdebug") != NULL)
{
- for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+ for (m = elf_seg_map (abfd); m != NULL; m = m->next)
if (m->p_type == PT_MIPS_RTPROC)
break;
if (m == NULL)
}
/* We want to put it after the DYNAMIC segment. */
- pm = &elf_tdata (abfd)->segment_map;
+ pm = &elf_seg_map (abfd);
while (*pm != NULL && (*pm)->p_type != PT_DYNAMIC)
pm = &(*pm)->next;
if (*pm != NULL)
/* On IRIX5, the PT_DYNAMIC segment includes the .dynamic,
.dynstr, .dynsym, and .hash sections, and everything in
between. */
- for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL;
+ for (pm = &elf_seg_map (abfd); *pm != NULL;
pm = &(*pm)->next)
if ((*pm)->p_type == PT_DYNAMIC)
break;
&& !SGI_COMPAT (abfd)
&& bfd_get_section_by_name (abfd, ".dynamic"))
{
- for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL; pm = &(*pm)->next)
+ for (pm = &elf_seg_map (abfd); *pm != NULL; pm = &(*pm)->next)
if ((*pm)->p_type == PT_NULL)
break;
if (*pm == NULL)
indmips->global_got_area = GGA_NONE;
if (indmips->has_nonpic_branches)
dirmips->has_nonpic_branches = TRUE;
-
- 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
if (elf_section_data (msec)->this_hdr.sh_type != SHT_NOBITS)
msec->flags |= SEC_HAS_CONTENTS;
- fi = elf_tdata (abfd)->find_line_info;
+ fi = mips_elf_tdata (abfd)->find_line_info;
if (fi == NULL)
{
bfd_size_type external_fdr_size;
for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++)
(*swap->swap_fdr_in) (abfd, fraw_src, fdr_ptr);
- elf_tdata (abfd)->find_line_info = fi;
+ mips_elf_tdata (abfd)->find_line_info = fi;
/* Note that we don't bother to ever free this information.
find_nearest_line is either called all the time, as in