#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 four types of entry:
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. */
-#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_DONE 0x80
+ /* 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;
/* In multi-got links, a pointer to the next got (err, rather, most
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. */
/* 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. */
/* 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))
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))
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. */
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;
/* 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_entries);
+ htab_delete (tdata->got->got_page_refs);
+ if (tdata->got->got_page_entries)
+ htab_delete (tdata->got->got_page_entries);
}
tdata->got = g;
}
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. Count the number of GOT entries and
- TLS relocations required for the GOT entry in *ENTRYP. DATA points
- to a mips_elf_traverse_got_arg structure. */
-
-static int
-mips_elf_count_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;
- mips_elf_count_got_entry (arg->info, arg->g, entry);
-
- 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;
+ entry->tls_initialized = TRUE;
}
/* Return the offset from _GLOBAL_OFFSET_TABLE_ of the .got.plt entry
return MINUS_ONE;
if (entry->tls_type)
- 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;
}
/* Return the GOT index of global symbol H in the primary GOT. */
+ h->root.u.def.section->output_offset
+ h->root.u.def.section->output_section->vma);
- return mips_tls_got_index (obfd, gotidx, &entry->tls_type,
- info, lookup.d.h, value);
+ mips_elf_initialize_tls_slots (obfd, info, entry, lookup.d.h, value);
}
return gotidx;
}
if (!entry)
return FALSE;
+ lookup->tls_initialized = FALSE;
lookup->gotidx = -1;
*entry = *lookup;
*loc = entry;
}
tls_type = mips_elf_reloc_tls_type (r_type);
- if (tls_type == GOT_NORMAL && hmips->global_got_area > GGA_NORMAL)
+ if (tls_type == GOT_TLS_NONE && hmips->global_got_area > GGA_NORMAL)
hmips->global_got_area = GGA_NORMAL;
entry.abfd = abfd;
return mips_elf_record_got_entry (info, abfd, &entry);
}
-/* Return the maximum number of GOT page entries required for RANGE. */
-
-static bfd_vma
-mips_elf_pages_for_range (const struct mips_got_page_range *range)
-{
- 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 ABFD has a page relocation against SYMNDX + ADDEND.
+ H is the symbol's hash table entry, or null if SYMNDX is local
+ to ABFD. */
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_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_entry lookup, *entry;
- struct mips_got_page_range **range_ptr, *range;
- bfd_vma old_pages, new_pages;
+ struct mips_got_page_ref lookup, *entry;
void **loc, **bfd_loc;
htab = mips_elf_hash_table (info);
g1 = htab->got_info;
BFD_ASSERT (g1 != NULL);
- /* Find the mips_got_page_entry hash table entry for this symbol. */
- lookup.abfd = abfd;
- lookup.symndx = symndx;
- loc = htab_find_slot (g1->got_page_entries, &lookup, INSERT);
+ 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;
- /* Create a mips_got_page_entry if this is the first time we've
- seen the symbol. */
- entry = (struct mips_got_page_entry *) *loc;
+ entry = (struct mips_got_page_ref *) *loc;
if (!entry)
{
entry = bfd_alloc (abfd, sizeof (*entry));
if (!entry)
return FALSE;
- entry->abfd = abfd;
- entry->symndx = symndx;
- entry->ranges = NULL;
- entry->num_pages = 0;
+ *entry = lookup;
*loc = entry;
}
if (!g2)
return FALSE;
- bfd_loc = htab_find_slot (g2->got_page_entries, &lookup, INSERT);
+ bfd_loc = htab_find_slot (g2->got_page_refs, &lookup, INSERT);
if (!bfd_loc)
return FALSE;
if (!*bfd_loc)
*bfd_loc = entry;
- /* Skip over ranges whose maximum extent cannot share a page entry
- with ADDEND. */
- range_ptr = &entry->ranges;
- while (*range_ptr && addend > (*range_ptr)->max_addend + 0xffff)
- range_ptr = &(*range_ptr)->next;
-
- /* If we scanned to the end of the list, or found a range whose
- minimum extent cannot share a page entry with ADDEND, create
- a new singleton range. */
- range = *range_ptr;
- if (!range || addend < range->min_addend - 0xffff)
- {
- range = bfd_alloc (abfd, sizeof (*range));
- if (!range)
- return FALSE;
-
- range->next = *range_ptr;
- range->min_addend = addend;
- range->max_addend = addend;
-
- *range_ptr = range;
- entry->num_pages++;
- g1->page_gotno++;
- g2->page_gotno++;
- return TRUE;
- }
-
- /* Remember how many pages the old range contributed. */
- old_pages = mips_elf_pages_for_range (range);
-
- /* Update the ranges. */
- if (addend < range->min_addend)
- range->min_addend = addend;
- else if (addend > range->max_addend)
- {
- if (range->next && addend >= range->next->min_addend - 0xffff)
- {
- old_pages += mips_elf_pages_for_range (range->next);
- range->max_addend = range->next->max_addend;
- range->next = range->next->next;
- }
- else
- range->max_addend = addend;
- }
-
- /* Record any change in the total estimate. */
- new_pages = mips_elf_pages_for_range (range);
- if (old_pages != new_pages)
- {
- entry->num_pages += new_pages - old_pages;
- g1->page_gotno += new_pages - old_pages;
- g2->page_gotno += new_pages - old_pages;
- }
-
return TRUE;
}
}
}
\f
-/* A htab_traverse callback for GOT entries. Set boolean *DATA to true
- if the GOT entry is for an indirect or warning symbol. */
+/* 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;
- bfd_boolean *must_recreate;
+ struct mips_elf_traverse_got_arg *arg;
entry = (struct mips_got_entry *) *entryp;
- must_recreate = (bfd_boolean *) data;
+ arg = (struct mips_elf_traverse_got_arg *) data;
if (entry->abfd != NULL && entry->symndx == -1)
{
struct mips_elf_link_hash_entry *h;
if (h->root.root.type == bfd_link_hash_indirect
|| h->root.root.type == bfd_link_hash_warning)
{
- *must_recreate = TRUE;
+ arg->value = TRUE;
return 0;
}
}
+ mips_elf_count_got_entry (arg->info, arg->g, entry);
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. */
+/* 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)
{
- htab_t *new_got;
struct mips_got_entry new_entry, *entry;
+ struct mips_elf_traverse_got_arg *arg;
void **slot;
- new_got = (htab_t *) data;
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
|| h->root.root.type == bfd_link_hash_warning);
entry->d.h = h;
}
- slot = htab_find_slot (*new_got, entry, INSERT);
+ slot = htab_find_slot (arg->g->got_entries, entry, INSERT);
if (slot == NULL)
{
- *new_got = NULL;
+ arg->g = NULL;
return 0;
}
if (*slot == NULL)
entry = bfd_alloc (entry->abfd, sizeof (*entry));
if (!entry)
{
- *new_got = NULL;
+ 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
+mips_elf_pages_for_range (const struct mips_got_page_range *range)
+{
+ return (range->max_addend - range->min_addend + 0x1ffff) >> 16;
+}
+
+/* Record that G requires a page entry that can reach SEC + ADDEND. */
+
+static bfd_boolean
+mips_elf_record_got_page_entry (struct mips_got_info *g,
+ asection *sec, bfd_signed_vma addend)
+{
+ struct mips_got_page_entry lookup, *entry;
+ struct mips_got_page_range **range_ptr, *range;
+ bfd_vma old_pages, new_pages;
+ void **loc;
+
+ /* 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 section. */
+ entry = (struct mips_got_page_entry *) *loc;
+ if (!entry)
+ {
+ entry = bfd_zalloc (sec->owner, sizeof (*entry));
+ if (!entry)
+ return FALSE;
+
+ entry->sec = sec;
+ *loc = entry;
+ }
+
+ /* Skip over ranges whose maximum extent cannot share a page entry
+ with ADDEND. */
+ range_ptr = &entry->ranges;
+ while (*range_ptr && addend > (*range_ptr)->max_addend + 0xffff)
+ range_ptr = &(*range_ptr)->next;
+
+ /* If we scanned to the end of the list, or found a range whose
+ minimum extent cannot share a page entry with ADDEND, create
+ a new singleton range. */
+ range = *range_ptr;
+ if (!range || addend < range->min_addend - 0xffff)
+ {
+ range = bfd_zalloc (sec->owner, sizeof (*range));
+ if (!range)
+ return FALSE;
+
+ range->next = *range_ptr;
+ range->min_addend = addend;
+ range->max_addend = addend;
+
+ *range_ptr = range;
+ entry->num_pages++;
+ g->page_gotno++;
+ return TRUE;
+ }
+
+ /* Remember how many pages the old range contributed. */
+ old_pages = mips_elf_pages_for_range (range);
+
+ /* Update the ranges. */
+ if (addend < range->min_addend)
+ range->min_addend = addend;
+ else if (addend > range->max_addend)
+ {
+ if (range->next && addend >= range->next->min_addend - 0xffff)
+ {
+ old_pages += mips_elf_pages_for_range (range->next);
+ range->max_addend = range->next->max_addend;
+ range->next = range->next->next;
+ }
+ else
+ range->max_addend = addend;
+ }
+
+ /* Record any change in the total estimate. */
+ new_pages = mips_elf_pages_for_range (range);
+ if (old_pages != new_pages)
+ {
+ entry->num_pages += new_pages - old_pages;
+ g->page_gotno += new_pages - old_pages;
+ }
+
+ return TRUE;
+}
+
+/* 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 bfd_boolean
+mips_elf_resolve_got_page_ref (void **refp, void *data)
+{
+ 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;
+
+ ref = (struct mips_got_page_ref *) *refp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ htab = mips_elf_hash_table (arg->info);
+
+ if (ref->symndx < 0)
+ {
+ struct mips_elf_link_hash_entry *h;
+
+ /* 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;
+
+ /* 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
+ {
+ Elf_Internal_Sym *isym;
+
+ /* Read in the symbol. */
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache, ref->u.abfd,
+ ref->symndx);
+ if (isym == NULL)
+ {
+ arg->g = NULL;
+ return 0;
+ }
+
+ /* 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;
+ }
+
+ /* 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)
+ {
+ 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;
+ }
+ else
+ addend = isym->st_value + ref->addend;
+ }
+ if (!mips_elf_record_got_page_entry (arg->g, sec, addend))
+ {
+ arg->g = NULL;
+ return 0;
}
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;
+
+ oldg = *g;
- must_recreate = FALSE;
- htab_traverse (g->got_entries, mips_elf_check_recreate_got, &must_recreate);
- if (must_recreate)
+ tga.info = info;
+ tga.g = g;
+ tga.value = FALSE;
+ htab_traverse (g->got_entries, mips_elf_check_recreate_got, &tga);
+ if (tga.value)
{
- 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)
+ *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_delete (g->got_entries);
- g->got_entries = new_got;
+ htab_traverse (oldg.got_entries, mips_elf_recreate_got, &tga);
+ if (!tga.g)
+ return FALSE;
+
+ 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;
}
mips_elf_merge_got (bfd *abfd, struct mips_got_info *g,
struct mips_elf_got_per_bfd_arg *arg)
{
- struct mips_elf_traverse_got_arg tga;
unsigned int estimate;
int result;
- if (!mips_elf_resolve_final_got_entries (g))
+ if (!mips_elf_resolve_final_got_entries (arg->info, g))
return FALSE;
- tga.info = arg->info;
- tga.g = g;
- htab_traverse (g->got_entries, mips_elf_count_got_entries, &tga);
-
/* Work out the number of page, local and TLS entries. */
estimate = arg->max_pages;
if (estimate > g->page_gotno)
{
struct mips_got_entry *entry;
struct mips_elf_traverse_got_arg *arg;
- unsigned char tls_type;
/* We're only interested in TLS symbols. */
entry = (struct mips_got_entry *) *entryp;
- tls_type = (entry->tls_type & GOT_TLS_TYPE);
- if (tls_type == 0)
+ if (entry->tls_type == GOT_TLS_NONE)
return 1;
arg = (struct mips_elf_traverse_got_arg *) data;
}
/* Account for the entries we've just allocated. */
- arg->g->tls_assigned_gotno += mips_tls_got_entries (tls_type);
+ arg->g->tls_assigned_gotno += mips_tls_got_entries (entry->tls_type);
return 1;
}
&& 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
&& (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:
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;
-
/* 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. */
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 GOT entries and TLS relocs. */
- tga.info = info;
- tga.g = g;
- htab_traverse (g->got_entries, mips_elf_count_got_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;
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);
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)
{
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;
}
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)
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