/* MIPS-specific support for ELF
- Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
- Free Software Foundation, Inc.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003 Free Software Foundation, Inc.
Most of the information added by Ian Lance Taylor, Cygnus Support,
<ian@cygnus.com>.
Traditional MIPS targets support added by Koundinya.K, Dansk Data
Elektronik & Operations Research Group. <kk@ddeorg.soft.net>
-This file is part of BFD, the Binary File Descriptor library.
+ This file is part of BFD, the Binary File Descriptor library.
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* This file handles functionality common to the different MIPS ABI's. */
#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
+#include "libiberty.h"
#include "elf-bfd.h"
#include "elfxx-mips.h"
#include "elf/mips.h"
{
/* The input bfd in which the symbol is defined. */
bfd *abfd;
- /* The index of the symbol, as stored in the relocation r_info. If
- it's -1, the addend is a complete address into the
- executable/shared library. */
- unsigned long symndx;
- /* The addend of the relocation that should be added to the symbol
- value. */
- bfd_vma addend;
+ /* The index of the symbol, as stored in the relocation r_info, if
+ we have a local symbol; -1 otherwise. */
+ long symndx;
+ union
+ {
+ /* If abfd == NULL, an address that must be stored in the got. */
+ bfd_vma address;
+ /* If abfd != NULL && symndx != -1, the addend of the relocation
+ that should be added to the symbol value. */
+ bfd_vma addend;
+ /* If abfd != NULL && symndx == -1, the hash table entry
+ corresponding to a global symbol in the got (or, local, if
+ h->forced_local). */
+ struct mips_elf_link_hash_entry *h;
+ } d;
/* The offset from the beginning of the .got section to the entry
- corresponding to this symbol+addend. */
- unsigned long gotidx;
+ 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 is used to hold .got information when linking. It
- is stored in the tdata field of the bfd_elf_section_data structure. */
+/* This structure is used to hold .got information when linking. */
struct mips_got_info
{
unsigned int assigned_gotno;
/* A hash table holding members of the got. */
struct htab *got_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;
};
+/* 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. */
+
+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. */
+ struct bfd_link_info *info;
+ /* A pointer to the primary got, i.e., the one that's going to get
+ the implicit relocations from DT_MIPS_LOCAL_GOTNO and
+ DT_MIPS_GOTSYM. */
+ struct mips_got_info *primary;
+ /* A non-primary got we're trying to merge with other input bfd's
+ gots. */
+ struct mips_got_info *current;
+ /* The maximum number of got entries that can be addressed with a
+ 16-bit offset. */
+ unsigned int max_count;
+ /* The number of local and global entries in the primary got. */
+ unsigned int primary_count;
+ /* The number of local and global entries in the current got. */
+ unsigned int current_count;
+};
+
+/* Another structure used to pass arguments for got entries traversal. */
+
+struct mips_elf_set_global_got_offset_arg
+{
+ struct mips_got_info *g;
+ int value;
+ unsigned int needed_relocs;
+ struct bfd_link_info *info;
+};
+
+struct _mips_elf_section_data
+{
+ struct bfd_elf_section_data elf;
+ union
+ {
+ struct mips_got_info *got_info;
+ bfd_byte *tdata;
+ } u;
+};
+
+#define mips_elf_section_data(sec) \
+ ((struct _mips_elf_section_data *) elf_section_data (sec))
+
/* This structure is passed to mips_elf_sort_hash_table_f when sorting
the dynamic symbols. */
/* The least dynamic symbol table index corresponding to a symbol
with a GOT entry. */
long min_got_dynindx;
+ /* The greatest dynamic symbol table index corresponding to a symbol
+ with a GOT entry that is not referenced (e.g., a dynamic symbol
+ with dynamic relocations pointing to it from non-primary GOTs). */
+ long max_unref_got_dynindx;
/* The greatest dynamic symbol table index not corresponding to a
symbol without a GOT entry. */
long max_non_got_dynindx;
loader for use by the static exception system. */
typedef struct runtime_pdr {
- bfd_vma adr; /* memory address of start of procedure */
- long regmask; /* save register mask */
- long regoffset; /* save register offset */
- long fregmask; /* save floating point register mask */
- long fregoffset; /* save floating point register offset */
- long frameoffset; /* frame size */
- short framereg; /* frame pointer register */
- short pcreg; /* offset or reg of return pc */
- long irpss; /* index into the runtime string table */
+ bfd_vma adr; /* Memory address of start of procedure. */
+ long regmask; /* Save register mask. */
+ long regoffset; /* Save register offset. */
+ long fregmask; /* Save floating point register mask. */
+ long fregoffset; /* Save floating point register offset. */
+ long frameoffset; /* Frame size. */
+ short framereg; /* Frame pointer register. */
+ short pcreg; /* Offset or reg of return pc. */
+ long irpss; /* Index into the runtime string table. */
long reserved;
- struct exception_info *exception_info;/* pointer to exception array */
+ struct exception_info *exception_info;/* Pointer to exception array. */
} RPDR, *pRPDR;
#define cbRPDR sizeof (RPDR)
#define rpdNil ((pRPDR) 0)
PARAMS ((bfd *, const Elf32_Internal_Msym *, Elf32_External_Msym *));
static int sort_dynamic_relocs
PARAMS ((const void *, const void *));
+static int sort_dynamic_relocs_64
+ PARAMS ((const void *, const void *));
static bfd_boolean mips_elf_output_extsym
PARAMS ((struct mips_elf_link_hash_entry *, PTR));
static int gptab_compare PARAMS ((const void *, const void *));
-static asection * mips_elf_got_section PARAMS ((bfd *));
+static asection * mips_elf_rel_dyn_section PARAMS ((bfd *, bfd_boolean));
+static asection * mips_elf_got_section PARAMS ((bfd *, bfd_boolean));
static struct mips_got_info *mips_elf_got_info
PARAMS ((bfd *, asection **));
+static long mips_elf_get_global_gotsym_index PARAMS ((bfd *abfd));
static bfd_vma mips_elf_local_got_index
- PARAMS ((bfd *, struct bfd_link_info *, bfd_vma));
+ PARAMS ((bfd *, bfd *, struct bfd_link_info *, bfd_vma));
static bfd_vma mips_elf_global_got_index
- PARAMS ((bfd *, struct elf_link_hash_entry *));
+ PARAMS ((bfd *, bfd *, struct elf_link_hash_entry *));
static bfd_vma mips_elf_got_page
- PARAMS ((bfd *, struct bfd_link_info *, bfd_vma, bfd_vma *));
+ PARAMS ((bfd *, bfd *, struct bfd_link_info *, bfd_vma, bfd_vma *));
static bfd_vma mips_elf_got16_entry
- PARAMS ((bfd *, struct bfd_link_info *, bfd_vma, bfd_boolean));
+ PARAMS ((bfd *, bfd *, struct bfd_link_info *, bfd_vma, bfd_boolean));
static bfd_vma mips_elf_got_offset_from_index
- PARAMS ((bfd *, bfd *, bfd_vma));
+ PARAMS ((bfd *, bfd *, bfd *, bfd_vma));
static struct mips_got_entry *mips_elf_create_local_got_entry
- PARAMS ((bfd *, struct mips_got_info *, asection *, bfd_vma));
+ PARAMS ((bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma));
static bfd_boolean mips_elf_sort_hash_table
PARAMS ((struct bfd_link_info *, unsigned long));
static bfd_boolean mips_elf_sort_hash_table_f
PARAMS ((struct mips_elf_link_hash_entry *, PTR));
+static bfd_boolean mips_elf_record_local_got_symbol
+ PARAMS ((bfd *, long, bfd_vma, struct mips_got_info *));
static bfd_boolean mips_elf_record_global_got_symbol
- PARAMS ((struct elf_link_hash_entry *, struct bfd_link_info *,
+ PARAMS ((struct elf_link_hash_entry *, bfd *, struct bfd_link_info *,
struct mips_got_info *));
static const Elf_Internal_Rela *mips_elf_next_relocation
PARAMS ((bfd *, unsigned int, const Elf_Internal_Rela *,
static bfd_boolean mips_elf_create_compact_rel_section
PARAMS ((bfd *, struct bfd_link_info *));
static bfd_boolean mips_elf_create_got_section
- PARAMS ((bfd *, struct bfd_link_info *));
+ PARAMS ((bfd *, struct bfd_link_info *, bfd_boolean));
static asection *mips_elf_create_msym_section
PARAMS ((bfd *));
static bfd_reloc_status_type mips_elf_calculate_relocation
PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
struct mips_elf_link_hash_entry *, asection *,
bfd_vma, bfd_vma *, asection *));
-static INLINE int elf_mips_isa PARAMS ((flagword));
+static void mips_set_isa_flags PARAMS ((bfd *));
static INLINE char* elf_mips_abi_name PARAMS ((bfd *));
static void mips_elf_irix6_finish_dynamic_symbol
PARAMS ((bfd *, const char *, Elf_Internal_Sym *));
-static bfd_boolean _bfd_mips_elf_mach_extends_p PARAMS ((flagword, flagword));
+static bfd_boolean mips_mach_extends_p PARAMS ((unsigned long, unsigned long));
+static bfd_boolean mips_32bit_flags_p PARAMS ((flagword));
+static INLINE hashval_t mips_elf_hash_bfd_vma PARAMS ((bfd_vma));
static hashval_t mips_elf_got_entry_hash PARAMS ((const PTR));
static int mips_elf_got_entry_eq PARAMS ((const PTR, const PTR));
+static bfd_boolean mips_elf_multi_got
+ PARAMS ((bfd *, struct bfd_link_info *, struct mips_got_info *,
+ asection *, bfd_size_type));
+static hashval_t mips_elf_multi_got_entry_hash PARAMS ((const PTR));
+static int mips_elf_multi_got_entry_eq PARAMS ((const PTR, const PTR));
+static hashval_t mips_elf_bfd2got_entry_hash PARAMS ((const PTR));
+static int mips_elf_bfd2got_entry_eq PARAMS ((const PTR, const PTR));
+static int mips_elf_make_got_per_bfd PARAMS ((void **, void *));
+static int mips_elf_merge_gots PARAMS ((void **, void *));
+static int mips_elf_set_global_got_offset PARAMS ((void**, void *));
+static int mips_elf_resolve_final_got_entry PARAMS ((void**, void *));
+static void mips_elf_resolve_final_got_entries
+ PARAMS ((struct mips_got_info *));
+static bfd_vma mips_elf_adjust_gp
+ PARAMS ((bfd *, struct mips_got_info *, bfd *));
+static struct mips_got_info *mips_elf_got_for_ibfd
+ PARAMS ((struct mips_got_info *, bfd *));
+
/* This will be used when we sort the dynamic relocation records. */
static bfd *reldyn_sorting_bfd;
/* The default alignment for sections, as a power of two. */
#define MIPS_ELF_LOG_FILE_ALIGN(abfd) \
- (get_elf_backend_data (abfd)->s->file_align == 8 ? 3 : 2)
+ (get_elf_backend_data (abfd)->s->log_file_align)
/* Get word-sized data. */
#define MIPS_ELF_GET_WORD(abfd, ptr) \
/* The number of local .got entries we reserve. */
#define MIPS_RESERVED_GOTNO (2)
+/* The offset of $gp from the beginning of the .got section. */
+#define ELF_MIPS_GP_OFFSET(abfd) (0x7ff0)
+
+/* The maximum size of the GOT for it to be addressable using 16-bit
+ offsets from $gp. */
+#define MIPS_ELF_GOT_MAX_SIZE(abfd) (ELF_MIPS_GP_OFFSET(abfd) + 0x7fff)
+
/* Instructions which appear in a stub. For some reason the stub is
slightly different on an SGI system. */
-#define ELF_MIPS_GP_OFFSET(abfd) (SGI_COMPAT (abfd) ? 0x7ff0 : 0x8000)
#define STUB_LW(abfd) \
- (SGI_COMPAT (abfd) \
- ? (ABI_64_P (abfd) \
- ? 0xdf998010 /* ld t9,0x8010(gp) */ \
- : 0x8f998010) /* lw t9,0x8010(gp) */ \
- : 0x8f998010) /* lw t9,0x8000(gp) */
+ ((ABI_64_P (abfd) \
+ ? 0xdf998010 /* ld t9,0x8010(gp) */ \
+ : 0x8f998010)) /* lw t9,0x8010(gp) */
#define STUB_MOVE(abfd) \
(SGI_COMPAT (abfd) ? 0x03e07825 : 0x03e07821) /* move t7,ra */
#define STUB_JALR 0x0320f809 /* jal t9 */
return (struct bfd_hash_entry *) ret;
}
+
+bfd_boolean
+_bfd_mips_elf_new_section_hook (abfd, sec)
+ bfd *abfd;
+ asection *sec;
+{
+ struct _mips_elf_section_data *sdata;
+ bfd_size_type amt = sizeof (*sdata);
+
+ sdata = (struct _mips_elf_section_data *) bfd_zalloc (abfd, amt);
+ if (sdata == NULL)
+ return FALSE;
+ sec->used_by_bfd = (PTR) sdata;
+
+ return _bfd_elf_new_section_hook (abfd, sec);
+}
\f
/* Read ECOFF debugging information from a .mdebug section into a
ecoff_debug_info structure. */
return ELF32_R_SYM (int_reloc1.r_info) - ELF32_R_SYM (int_reloc2.r_info);
}
+/* Like sort_dynamic_relocs, but used for elf64 relocations. */
+
+static int
+sort_dynamic_relocs_64 (arg1, arg2)
+ const PTR arg1;
+ const PTR arg2;
+{
+ Elf_Internal_Rela int_reloc1[3];
+ Elf_Internal_Rela int_reloc2[3];
+
+ (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in)
+ (reldyn_sorting_bfd, arg1, int_reloc1);
+ (*get_elf_backend_data (reldyn_sorting_bfd)->s->swap_reloc_in)
+ (reldyn_sorting_bfd, arg2, int_reloc2);
+
+ return (ELF64_R_SYM (int_reloc1[0].r_info)
+ - ELF64_R_SYM (int_reloc2[0].r_info));
+}
+
+
/* This routine is used to write out ECOFF debugging external symbol
information. It is called via mips_elf_link_hash_traverse. The
ECOFF external symbol information must match the ELF external
}
\f
/* Functions to manage the got entry hash table. */
+
+/* Use all 64 bits of a bfd_vma for the computation of a 32-bit
+ hash number. */
+
+static INLINE hashval_t
+mips_elf_hash_bfd_vma (addr)
+ bfd_vma addr;
+{
+#ifdef BFD64
+ return addr + (addr >> 32);
+#else
+ return addr;
+#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 hashval_t
mips_elf_got_entry_hash (entry_)
const PTR entry_;
{
const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
- return htab_hash_pointer (entry->abfd) + entry->symndx
-#ifdef BFD64
- + (entry->addend >> 32)
-#endif
- + entry->addend;
+ return entry->symndx
+ + (! entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address)
+ : entry->abfd->id
+ + (entry->symndx >= 0 ? mips_elf_hash_bfd_vma (entry->d.addend)
+ : entry->d.h->root.root.root.hash));
}
static int
const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
return e1->abfd == e2->abfd && e1->symndx == e2->symndx
- && e1->addend == e2->addend;
+ && (! 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_multi_got_entry_hash (entry_)
+ const PTR entry_;
+{
+ const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
+
+ return entry->symndx
+ + (! entry->abfd
+ ? mips_elf_hash_bfd_vma (entry->d.address)
+ : entry->symndx >= 0
+ ? (entry->abfd->id
+ + mips_elf_hash_bfd_vma (entry->d.addend))
+ : entry->d.h->root.root.root.hash);
+}
+
+static int
+mips_elf_multi_got_entry_eq (entry1, entry2)
+ const PTR entry1;
+ const PTR 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->symndx >= 0 ? e1->abfd == e2->abfd && e1->d.addend == e2->d.addend
+ : e1->abfd == NULL || e2->abfd == NULL
+ ? e1->abfd == e2->abfd && e1->d.address == e2->d.address
+ : e1->d.h == e2->d.h);
}
\f
+/* Returns the dynamic relocation section for DYNOBJ. */
+
+static asection *
+mips_elf_rel_dyn_section (dynobj, create_p)
+ bfd *dynobj;
+ bfd_boolean create_p;
+{
+ static const char dname[] = ".rel.dyn";
+ asection *sreloc;
+
+ sreloc = bfd_get_section_by_name (dynobj, dname);
+ if (sreloc == NULL && create_p)
+ {
+ sreloc = bfd_make_section (dynobj, dname);
+ if (sreloc == NULL
+ || ! bfd_set_section_flags (dynobj, sreloc,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED
+ | SEC_READONLY))
+ || ! bfd_set_section_alignment (dynobj, sreloc,
+ 4))
+ return NULL;
+ }
+ return sreloc;
+}
+
/* Returns the GOT section for ABFD. */
static asection *
-mips_elf_got_section (abfd)
+mips_elf_got_section (abfd, maybe_excluded)
bfd *abfd;
+ bfd_boolean maybe_excluded;
{
- return bfd_get_section_by_name (abfd, ".got");
+ asection *sgot = bfd_get_section_by_name (abfd, ".got");
+ if (sgot == NULL
+ || (! maybe_excluded && (sgot->flags & SEC_EXCLUDE) != 0))
+ return NULL;
+ return sgot;
}
/* Returns the GOT information associated with the link indicated by
asection *sgot;
struct mips_got_info *g;
- sgot = mips_elf_got_section (abfd);
+ sgot = mips_elf_got_section (abfd, TRUE);
BFD_ASSERT (sgot != NULL);
- BFD_ASSERT (elf_section_data (sgot) != NULL);
- g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
+ BFD_ASSERT (mips_elf_section_data (sgot) != NULL);
+ g = mips_elf_section_data (sgot)->u.got_info;
BFD_ASSERT (g != NULL);
if (sgotp)
- *sgotp = sgot;
+ *sgotp = (sgot->flags & SEC_EXCLUDE) == 0 ? sgot : NULL;
+
return g;
}
+/* Obtain the lowest dynamic index of a symbol that was assigned a
+ global GOT entry. */
+static long
+mips_elf_get_global_gotsym_index (abfd)
+ bfd *abfd;
+{
+ asection *sgot;
+ struct mips_got_info *g;
+
+ if (abfd == NULL)
+ return 0;
+
+ sgot = mips_elf_got_section (abfd, TRUE);
+ if (sgot == NULL || mips_elf_section_data (sgot) == NULL)
+ return 0;
+
+ g = mips_elf_section_data (sgot)->u.got_info;
+ if (g == NULL || g->global_gotsym == NULL)
+ return 0;
+
+ return g->global_gotsym->dynindx;
+}
+
/* Returns the GOT offset at which the indicated address can be found.
If there is not yet a GOT entry for this value, create one. Returns
-1 if no satisfactory GOT offset can be found. */
static bfd_vma
-mips_elf_local_got_index (abfd, info, value)
- bfd *abfd;
+mips_elf_local_got_index (abfd, ibfd, info, value)
+ bfd *abfd, *ibfd;
struct bfd_link_info *info;
bfd_vma value;
{
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
- entry = mips_elf_create_local_got_entry (abfd, g, sgot, value);
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
if (entry)
return entry->gotidx;
else
/* Returns the GOT index for the global symbol indicated by H. */
static bfd_vma
-mips_elf_global_got_index (abfd, h)
- bfd *abfd;
+mips_elf_global_got_index (abfd, ibfd, h)
+ bfd *abfd, *ibfd;
struct elf_link_hash_entry *h;
{
bfd_vma index;
asection *sgot;
- struct mips_got_info *g;
+ struct mips_got_info *g, *gg;
long global_got_dynindx = 0;
- g = mips_elf_got_info (abfd, &sgot);
- if (g->global_gotsym != NULL)
- global_got_dynindx = g->global_gotsym->dynindx;
+ gg = g = mips_elf_got_info (abfd, &sgot);
+ if (g->bfd2got && ibfd)
+ {
+ struct mips_got_entry e, *p;
+
+ BFD_ASSERT (h->dynindx >= 0);
+
+ g = mips_elf_got_for_ibfd (g, ibfd);
+ if (g->next != gg)
+ {
+ e.abfd = ibfd;
+ e.symndx = -1;
+ e.d.h = (struct mips_elf_link_hash_entry *)h;
+
+ p = (struct mips_got_entry *) htab_find (g->got_entries, &e);
+
+ BFD_ASSERT (p->gotidx > 0);
+ return p->gotidx;
+ }
+ }
+
+ if (gg->global_gotsym != NULL)
+ global_got_dynindx = gg->global_gotsym->dynindx;
/* Once we determine the global GOT entry with the lowest dynamic
symbol table index, we must put all dynamic symbols with greater
OFFSETP, if it is non-NULL. */
static bfd_vma
-mips_elf_got_page (abfd, info, value, offsetp)
- bfd *abfd;
+mips_elf_got_page (abfd, ibfd, info, value, offsetp)
+ bfd *abfd, *ibfd;
struct bfd_link_info *info;
bfd_vma value;
bfd_vma *offsetp;
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
- entry = mips_elf_create_local_got_entry (abfd, g, sgot,
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot,
(value + 0x8000)
& (~(bfd_vma)0xffff));
if (!entry)
return MINUS_ONE;
-
+
index = entry->gotidx;
if (offsetp)
- *offsetp = value - entry->addend;
+ *offsetp = value - entry->d.address;
return index;
}
for value. Return the index into the GOT for this entry. */
static bfd_vma
-mips_elf_got16_entry (abfd, info, value, external)
- bfd *abfd;
+mips_elf_got16_entry (abfd, ibfd, info, value, external)
+ bfd *abfd, *ibfd;
struct bfd_link_info *info;
bfd_vma value;
bfd_boolean external;
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
- entry = mips_elf_create_local_got_entry (abfd, g, sgot, value);
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
if (entry)
return entry->gotidx;
else
in the GOT. */
static bfd_vma
-mips_elf_got_offset_from_index (dynobj, output_bfd, index)
+mips_elf_got_offset_from_index (dynobj, output_bfd, input_bfd, index)
bfd *dynobj;
bfd *output_bfd;
+ bfd *input_bfd;
bfd_vma index;
{
asection *sgot;
bfd_vma gp;
+ struct mips_got_info *g;
+
+ g = mips_elf_got_info (dynobj, &sgot);
+ gp = _bfd_get_gp_value (output_bfd)
+ + mips_elf_adjust_gp (output_bfd, g, input_bfd);
- sgot = mips_elf_got_section (dynobj);
- gp = _bfd_get_gp_value (output_bfd);
- return (sgot->output_section->vma + sgot->output_offset + index -
- gp);
+ return sgot->output_section->vma + sgot->output_offset + index - gp;
}
/* Create a local GOT entry for VALUE. Return the index of the entry,
or -1 if it could not be created. */
static struct mips_got_entry *
-mips_elf_create_local_got_entry (abfd, g, sgot, value)
- bfd *abfd;
- struct mips_got_info *g;
+mips_elf_create_local_got_entry (abfd, ibfd, gg, sgot, value)
+ bfd *abfd, *ibfd;
+ struct mips_got_info *gg;
asection *sgot;
bfd_vma value;
{
struct mips_got_entry entry, **loc;
+ struct mips_got_info *g;
- entry.abfd = abfd;
- entry.symndx = (unsigned long)-1;
- entry.addend = value;
+ entry.abfd = NULL;
+ entry.symndx = -1;
+ entry.d.address = value;
+
+ g = mips_elf_got_for_ibfd (gg, ibfd);
+ if (g == NULL)
+ {
+ g = mips_elf_got_for_ibfd (gg, abfd);
+ BFD_ASSERT (g != NULL);
+ }
loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
INSERT);
if (*loc)
return *loc;
-
+
entry.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
*loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
if (! *loc)
return NULL;
-
+
memcpy (*loc, &entry, sizeof entry);
if (g->assigned_gotno >= g->local_gotno)
{
- (*loc)->gotidx = (unsigned long)-1;
+ (*loc)->gotidx = -1;
/* We didn't allocate enough space in the GOT. */
(*_bfd_error_handler)
(_("not enough GOT space for local GOT entries"));
dynobj = elf_hash_table (info)->dynobj;
+ g = mips_elf_got_info (dynobj, NULL);
+
hsd.low = NULL;
- hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount;
+ hsd.max_unref_got_dynindx =
+ hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount
+ /* In the multi-got case, assigned_gotno of the master got_info
+ indicate the number of entries that aren't referenced in the
+ primary GOT, but that must have entries because there are
+ dynamic relocations that reference it. Since they aren't
+ referenced, we move them to the end of the GOT, so that they
+ don't prevent other entries that are referenced from getting
+ too large offsets. */
+ - (g->next ? g->assigned_gotno : 0);
hsd.max_non_got_dynindx = max_local;
mips_elf_link_hash_traverse (((struct mips_elf_link_hash_table *)
elf_hash_table (info)),
/* There should have been enough room in the symbol table to
accommodate both the GOT and non-GOT symbols. */
BFD_ASSERT (hsd.max_non_got_dynindx <= hsd.min_got_dynindx);
+ BFD_ASSERT ((unsigned long)hsd.max_unref_got_dynindx
+ <= elf_hash_table (info)->dynsymcount);
/* Now we know which dynamic symbol has the lowest dynamic symbol
table index in the GOT. */
- g = mips_elf_got_info (dynobj, NULL);
g->global_gotsym = hsd.low;
return TRUE;
if (h->root.dynindx == -1)
return TRUE;
- if (h->root.got.offset != 1)
+ /* Global symbols that need GOT entries that are not explicitly
+ referenced are marked with got offset 2. Those that are
+ referenced get a 1, and those that don't need GOT entries get
+ -1. */
+ if (h->root.got.offset == 2)
+ {
+ if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx)
+ hsd->low = (struct elf_link_hash_entry *) h;
+ h->root.dynindx = hsd->max_unref_got_dynindx++;
+ }
+ else if (h->root.got.offset != 1)
h->root.dynindx = hsd->max_non_got_dynindx++;
else
{
posterity. */
static bfd_boolean
-mips_elf_record_global_got_symbol (h, info, g)
+mips_elf_record_global_got_symbol (h, abfd, info, g)
struct elf_link_hash_entry *h;
+ bfd *abfd;
struct bfd_link_info *info;
- struct mips_got_info *g ATTRIBUTE_UNUSED;
+ struct mips_got_info *g;
{
+ struct mips_got_entry entry, **loc;
+
/* A global symbol in the GOT must also be in the dynamic symbol
table. */
if (h->dynindx == -1)
return FALSE;
}
+ entry.abfd = abfd;
+ entry.symndx = -1;
+ entry.d.h = (struct mips_elf_link_hash_entry *) h;
+
+ 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 (h->got.offset != MINUS_ONE)
return TRUE;
return TRUE;
}
+
+/* Reserve space in G for a GOT entry containing the value of symbol
+ SYMNDX in input bfd ABDF, plus ADDEND. */
+
+static bfd_boolean
+mips_elf_record_local_got_symbol (abfd, symndx, addend, g)
+ bfd *abfd;
+ long symndx;
+ bfd_vma addend;
+ struct mips_got_info *g;
+{
+ struct mips_got_entry entry, **loc;
+
+ entry.abfd = abfd;
+ entry.symndx = symndx;
+ entry.d.addend = addend;
+ loc = (struct mips_got_entry **)
+ htab_find_slot (g->got_entries, &entry, INSERT);
+
+ if (*loc)
+ return TRUE;
+
+ entry.gotidx = g->local_gotno++;
+
+ *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+
+ if (! *loc)
+ return FALSE;
+
+ memcpy (*loc, &entry, sizeof entry);
+
+ return TRUE;
+}
+\f
+/* Compute the hash value of the bfd in a bfd2got hash entry. */
+
+static hashval_t
+mips_elf_bfd2got_entry_hash (entry_)
+ const PTR 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. */
+
+static int
+mips_elf_bfd2got_entry_eq (entry1, entry2)
+ const PTR entry1;
+ const PTR 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 IBDF. G must
+ be the master GOT data. */
+
+static struct mips_got_info *
+mips_elf_got_for_ibfd (g, ibfd)
+ struct mips_got_info *g;
+ bfd *ibfd;
+{
+ struct mips_elf_bfd2got_hash e, *p;
+
+ if (! g->bfd2got)
+ return g;
+
+ e.bfd = ibfd;
+ p = (struct mips_elf_bfd2got_hash *) htab_find (g->bfd2got, &e);
+ return p ? p->g : NULL;
+}
+
+/* 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 (entryp, p)
+ 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;
+ htab_t bfd2got = arg->bfd2got;
+ struct mips_got_info *g;
+ struct mips_elf_bfd2got_hash bfdgot_entry, *bfdgot;
+ void **bfdgotp;
+
+ /* Find the got_info for this GOT entry's input bfd. Create one if
+ none exists. */
+ bfdgot_entry.bfd = entry->abfd;
+ bfdgotp = htab_find_slot (bfd2got, &bfdgot_entry, INSERT);
+ bfdgot = (struct mips_elf_bfd2got_hash *)*bfdgotp;
+
+ if (bfdgot != NULL)
+ g = bfdgot->g;
+ else
+ {
+ bfdgot = (struct mips_elf_bfd2got_hash *)bfd_alloc
+ (arg->obfd, sizeof (struct mips_elf_bfd2got_hash));
+
+ if (bfdgot == NULL)
+ {
+ arg->obfd = 0;
+ return 0;
+ }
+
+ *bfdgotp = bfdgot;
+
+ bfdgot->bfd = entry->abfd;
+ bfdgot->g = g = (struct mips_got_info *)
+ bfd_alloc (arg->obfd, sizeof (struct mips_got_info));
+ if (g == NULL)
+ {
+ arg->obfd = 0;
+ return 0;
+ }
+
+ g->global_gotsym = NULL;
+ g->global_gotno = 0;
+ g->local_gotno = 0;
+ g->assigned_gotno = -1;
+ g->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
+ mips_elf_multi_got_entry_eq,
+ (htab_del) NULL);
+ if (g->got_entries == NULL)
+ {
+ arg->obfd = 0;
+ return 0;
+ }
+
+ g->bfd2got = NULL;
+ g->next = NULL;
+ }
+
+ /* 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;
+
+ if (entry->symndx >= 0 || entry->d.h->forced_local)
+ ++g->local_gotno;
+ else
+ ++g->global_gotno;
+
+ return 1;
+}
+
+/* Attempt to merge gots of different input bfds. 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 (bfd2got_, p)
+ void **bfd2got_;
+ void *p;
+{
+ 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;
+ unsigned int lcount = bfd2got->g->local_gotno;
+ unsigned int gcount = bfd2got->g->global_gotno;
+ unsigned int maxcnt = arg->max_count;
+
+ /* If we don't have a primary GOT and this is not too big, use it as
+ a starting point for the primary GOT. */
+ if (! arg->primary && lcount + gcount <= maxcnt)
+ {
+ arg->primary = bfd2got->g;
+ arg->primary_count = lcount + gcount;
+ }
+ /* If it looks like we can merge this bfd's entries with those of
+ the primary, merge them. The heuristics is conservative, but we
+ don't have to squeeze it too hard. */
+ else if (arg->primary
+ && (arg->primary_count + lcount + gcount) <= maxcnt)
+ {
+ struct mips_got_info *g = bfd2got->g;
+ int old_lcount = arg->primary->local_gotno;
+ int old_gcount = arg->primary->global_gotno;
+
+ bfd2got->g = arg->primary;
+
+ htab_traverse (g->got_entries,
+ mips_elf_make_got_per_bfd,
+ arg);
+ if (arg->obfd == NULL)
+ return 0;
+
+ htab_delete (g->got_entries);
+ /* 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. */
+
+ BFD_ASSERT (old_lcount + lcount == arg->primary->local_gotno);
+ BFD_ASSERT (old_gcount + gcount >= arg->primary->global_gotno);
+
+ arg->primary_count = arg->primary->local_gotno
+ + arg->primary->global_gotno;
+ }
+ /* If we can merge with the last-created got, do it. */
+ else if (arg->current
+ && arg->current_count + lcount + gcount <= maxcnt)
+ {
+ struct mips_got_info *g = bfd2got->g;
+ int old_lcount = arg->current->local_gotno;
+ int old_gcount = arg->current->global_gotno;
+
+ bfd2got->g = arg->current;
+
+ htab_traverse (g->got_entries,
+ mips_elf_make_got_per_bfd,
+ arg);
+ if (arg->obfd == NULL)
+ return 0;
+
+ htab_delete (g->got_entries);
+
+ BFD_ASSERT (old_lcount + lcount == arg->current->local_gotno);
+ BFD_ASSERT (old_gcount + gcount >= arg->current->global_gotno);
+
+ arg->current_count = arg->current->local_gotno
+ + arg->current->global_gotno;
+ }
+ /* Well, we couldn't merge, so create a new GOT. Don't check if it
+ fits; if it turns out that it doesn't, we'll get relocation
+ overflows anyway. */
+ else
+ {
+ bfd2got->g->next = arg->current;
+ arg->current = bfd2got->g;
+
+ arg->current_count = lcount + gcount;
+ }
+
+ return 1;
+}
+
+/* If passed a NULL mips_got_info in the argument, set the marker used
+ to tell whether a global symbol needs a got entry (in the primary
+ got) to the given VALUE.
+
+ If passed a pointer G to a mips_got_info in the argument (it must
+ not be the primary GOT), compute the offset from the beginning of
+ the (primary) GOT section to the entry in G corresponding to the
+ global symbol. G's assigned_gotno must contain the index of the
+ first available global GOT entry in G. VALUE must contain the size
+ of a GOT entry in bytes. For each global GOT entry that requires a
+ dynamic relocation, NEEDED_RELOCS is incremented, and the symbol is
+ marked as not elligible for lazy resolution through a function
+ stub. */
+static int
+mips_elf_set_global_got_offset (entryp, p)
+ void **entryp;
+ void *p;
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ struct mips_elf_set_global_got_offset_arg *arg
+ = (struct mips_elf_set_global_got_offset_arg *)p;
+ struct mips_got_info *g = arg->g;
+
+ if (entry->abfd != NULL && entry->symndx == -1
+ && entry->d.h->root.dynindx != -1)
+ {
+ if (g)
+ {
+ BFD_ASSERT (g->global_gotsym == NULL);
+
+ entry->gotidx = arg->value * (long) g->assigned_gotno++;
+ /* We can't do lazy update of GOT entries for
+ non-primary GOTs since the PLT entries don't use the
+ right offsets, so punt at it for now. */
+ entry->d.h->no_fn_stub = TRUE;
+ if (arg->info->shared
+ || (elf_hash_table (arg->info)->dynamic_sections_created
+ && ((entry->d.h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_DYNAMIC) != 0)
+ && ((entry->d.h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_REGULAR) == 0)))
+ ++arg->needed_relocs;
+ }
+ else
+ entry->d.h->root.got.offset = arg->value;
+ }
+
+ return 1;
+}
+
+/* Follow indirect and warning hash entries so that each got entry
+ points to the final symbol definition. P must point to a pointer
+ to the hash table we're traversing. Since this traversal may
+ modify the hash table, we set this pointer to NULL to indicate
+ we've made a potentially-destructive change to the hash table, so
+ the traversal must be restarted. */
+static int
+mips_elf_resolve_final_got_entry (entryp, p)
+ void **entryp;
+ void *p;
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ htab_t got_entries = *(htab_t *)p;
+
+ if (entry->abfd != NULL && entry->symndx == -1)
+ {
+ struct mips_elf_link_hash_entry *h = entry->d.h;
+
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ if (entry->d.h == h)
+ return 1;
+
+ entry->d.h = h;
+
+ /* If we can't find this entry with the new bfd hash, re-insert
+ it, and get the traversal restarted. */
+ if (! htab_find (got_entries, entry))
+ {
+ htab_clear_slot (got_entries, entryp);
+ entryp = htab_find_slot (got_entries, entry, INSERT);
+ if (! *entryp)
+ *entryp = entry;
+ /* Abort the traversal, since the whole table may have
+ moved, and leave it up to the parent to restart the
+ process. */
+ *(htab_t *)p = NULL;
+ return 0;
+ }
+ /* We might want to decrement the global_gotno count, but it's
+ either too early or too late for that at this point. */
+ }
+
+ return 1;
+}
+
+/* Turn indirect got entries in a got_entries table into their final
+ locations. */
+static void
+mips_elf_resolve_final_got_entries (g)
+ struct mips_got_info *g;
+{
+ htab_t got_entries;
+
+ do
+ {
+ got_entries = g->got_entries;
+
+ htab_traverse (got_entries,
+ mips_elf_resolve_final_got_entry,
+ &got_entries);
+ }
+ while (got_entries == NULL);
+}
+
+/* Return the offset of an input bfd IBFD's GOT from the beginning of
+ the primary GOT. */
+static bfd_vma
+mips_elf_adjust_gp (abfd, g, ibfd)
+ bfd *abfd;
+ struct mips_got_info *g;
+ bfd *ibfd;
+{
+ if (g->bfd2got == NULL)
+ return 0;
+
+ g = mips_elf_got_for_ibfd (g, ibfd);
+ if (! g)
+ return 0;
+
+ BFD_ASSERT (g->next);
+
+ g = g->next;
+
+ return (g->local_gotno + g->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+}
+
+/* Turn a single GOT that is too big for 16-bit addressing into
+ a sequence of GOTs, each one 16-bit addressable. */
+
+static bfd_boolean
+mips_elf_multi_got (abfd, info, g, got, pages)
+ bfd *abfd;
+ struct bfd_link_info *info;
+ struct mips_got_info *g;
+ asection *got;
+ bfd_size_type pages;
+{
+ struct mips_elf_got_per_bfd_arg got_per_bfd_arg;
+ struct mips_elf_set_global_got_offset_arg set_got_offset_arg;
+ struct mips_got_info *gg;
+ unsigned int assign;
+
+ g->bfd2got = htab_try_create (1, mips_elf_bfd2got_entry_hash,
+ mips_elf_bfd2got_entry_eq,
+ (htab_del) 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. */
+ mips_elf_resolve_final_got_entries (g);
+ htab_traverse (g->got_entries, mips_elf_make_got_per_bfd, &got_per_bfd_arg);
+ if (got_per_bfd_arg.obfd == NULL)
+ return FALSE;
+
+ got_per_bfd_arg.current = NULL;
+ got_per_bfd_arg.primary = NULL;
+ /* Taking out PAGES entries is a worst-case estimate. We could
+ compute the maximum number of pages that each separate input bfd
+ uses, but it's probably not worth it. */
+ got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (abfd)
+ / MIPS_ELF_GOT_SIZE (abfd))
+ - MIPS_RESERVED_GOTNO - pages);
+
+ /* 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;
+
+ /* If we find any suitable primary GOT, create an empty one. */
+ if (got_per_bfd_arg.primary == NULL)
+ {
+ g->next = (struct mips_got_info *)
+ bfd_alloc (abfd, sizeof (struct mips_got_info));
+ if (g->next == NULL)
+ return FALSE;
+
+ g->next->global_gotsym = NULL;
+ g->next->global_gotno = 0;
+ g->next->local_gotno = 0;
+ g->next->assigned_gotno = 0;
+ g->next->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
+ mips_elf_multi_got_entry_eq,
+ (htab_del) NULL);
+ if (g->next->got_entries == NULL)
+ return FALSE;
+ g->next->bfd2got = NULL;
+ }
+ else
+ g->next = got_per_bfd_arg.primary;
+ g->next->next = got_per_bfd_arg.current;
+
+ /* GG is now the master GOT, and G is the primary GOT. */
+ gg = g;
+ g = g->next;
+
+ /* Map the output bfd to the primary got. That's what we're going
+ to use for bfds that use GOT16 or GOT_PAGE relocations that we
+ 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;
+ }
+
+ /* The IRIX dynamic linker requires every symbol that is referenced
+ in a dynamic relocation to be present in the primary GOT, so
+ arrange for them to appear after those that are actually
+ referenced.
+
+ GNU/Linux could very well do without it, but it would slow down
+ the dynamic linker, since it would have to resolve every dynamic
+ symbol referenced in other GOTs more than once, without help from
+ the cache. Also, knowing that every external symbol has a GOT
+ helps speed up the resolution of local symbols too, so GNU/Linux
+ follows IRIX's practice.
+
+ The number 2 is used by mips_elf_sort_hash_table_f to count
+ global GOT symbols that are unreferenced in the primary GOT, with
+ an initial dynamic index computed from gg->assigned_gotno, where
+ the number of unreferenced global entries in the primary GOT is
+ preserved. */
+ if (1)
+ {
+ gg->assigned_gotno = gg->global_gotno - g->global_gotno;
+ g->global_gotno = gg->global_gotno;
+ set_got_offset_arg.value = 2;
+ }
+ else
+ {
+ /* This could be used for dynamic linkers that don't optimize
+ symbol resolution while applying relocations so as to use
+ primary GOT entries or assuming the symbol is locally-defined.
+ With this code, we assign lower dynamic indices to global
+ symbols that are not referenced in the primary GOT, so that
+ their entries can be omitted. */
+ gg->assigned_gotno = 0;
+ set_got_offset_arg.value = -1;
+ }
+
+ /* Reorder dynamic symbols as described above (which behavior
+ depends on the setting of VALUE). */
+ set_got_offset_arg.g = NULL;
+ htab_traverse (gg->got_entries, mips_elf_set_global_got_offset,
+ &set_got_offset_arg);
+ set_got_offset_arg.value = 1;
+ htab_traverse (g->got_entries, mips_elf_set_global_got_offset,
+ &set_got_offset_arg);
+ if (! mips_elf_sort_hash_table (info, 1))
+ return FALSE;
+
+ /* Now go through the GOTs assigning them offset ranges.
+ [assigned_gotno, local_gotno[ will be set to the range of local
+ entries in each GOT. We can then compute the end of a GOT by
+ adding local_gotno to global_gotno. We reverse the list and make
+ it circular since then we'll be able to quickly compute the
+ beginning of a GOT, by computing the end of its predecessor. To
+ avoid special cases for the primary GOT, while still preserving
+ assertions that are valid for both single- and multi-got links,
+ we arrange for the main got struct to have the right number of
+ global entries, but set its local_gotno such that the initial
+ offset of the primary GOT is zero. Remember that the primary GOT
+ will become the last item in the circular linked list, so it
+ points back to the master GOT. */
+ gg->local_gotno = -g->global_gotno;
+ gg->global_gotno = g->global_gotno;
+ assign = 0;
+ gg->next = gg;
+
+ do
+ {
+ struct mips_got_info *gn;
+
+ assign += MIPS_RESERVED_GOTNO;
+ g->assigned_gotno = assign;
+ g->local_gotno += assign + pages;
+ assign = g->local_gotno + g->global_gotno;
+
+ /* Take g out of the direct list, and push it onto the reversed
+ list that gg points to. */
+ gn = g->next;
+ g->next = gg->next;
+ gg->next = g;
+ g = gn;
+ }
+ while (g);
+
+ got->_raw_size = (gg->next->local_gotno
+ + gg->next->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+
+ return TRUE;
+}
+
\f
/* Returns the first relocation of type r_type found, beginning with
RELOCATION. RELEND is one-past-the-end of the relocation table. */
/* Create the .got section to hold the global offset table. */
static bfd_boolean
-mips_elf_create_got_section (abfd, info)
+mips_elf_create_got_section (abfd, info, maybe_exclude)
bfd *abfd;
struct bfd_link_info *info;
+ bfd_boolean maybe_exclude;
{
flagword flags;
register asection *s;
bfd_size_type amt;
/* This function may be called more than once. */
- if (mips_elf_got_section (abfd))
- return TRUE;
+ s = mips_elf_got_section (abfd, TRUE);
+ if (s)
+ {
+ if (! maybe_exclude)
+ s->flags &= ~SEC_EXCLUDE;
+ return TRUE;
+ }
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED);
+ if (maybe_exclude)
+ flags |= SEC_EXCLUDE;
+
s = bfd_make_section (abfd, ".got");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags)
&& ! bfd_elf32_link_record_dynamic_symbol (info, h))
return FALSE;
- /* The first several global offset table entries are reserved. */
- s->_raw_size = MIPS_RESERVED_GOTNO * MIPS_ELF_GOT_SIZE (abfd);
-
amt = sizeof (struct mips_got_info);
g = (struct mips_got_info *) bfd_alloc (abfd, amt);
if (g == NULL)
g->global_gotsym = NULL;
g->local_gotno = MIPS_RESERVED_GOTNO;
g->assigned_gotno = MIPS_RESERVED_GOTNO;
+ g->bfd2got = NULL;
+ g->next = NULL;
g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
mips_elf_got_entry_eq,
(htab_del) NULL);
if (g->got_entries == NULL)
return FALSE;
- if (elf_section_data (s) == NULL)
- {
- amt = sizeof (struct bfd_elf_section_data);
- s->used_by_bfd = (PTR) bfd_zalloc (abfd, amt);
- if (elf_section_data (s) == NULL)
- return FALSE;
- }
- elf_section_data (s)->tdata = (PTR) g;
- elf_section_data (s)->this_hdr.sh_flags
+ mips_elf_section_data (s)->u.got_info = g;
+ mips_elf_section_data (s)->elf.this_hdr.sh_flags
|= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
return TRUE;
addresses. */
symbol = 0;
else if (info->shared
- && (!info->symbolic || info->allow_shlib_undefined)
&& !info->no_undefined
&& ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT)
symbol = 0;
and we're going to need it, get it now. */
switch (r_type)
{
+ case R_MIPS_GOT_PAGE:
+ case R_MIPS_GOT_OFST:
+ /* If this symbol got a global GOT entry, we have to decay
+ GOT_PAGE/GOT_OFST to GOT_DISP/addend. */
+ local_p = local_p || ! h
+ || (h->root.dynindx
+ < mips_elf_get_global_gotsym_index (elf_hash_table (info)
+ ->dynobj));
+ if (local_p || r_type == R_MIPS_GOT_OFST)
+ break;
+ /* Fall through. */
+
case R_MIPS_CALL16:
case R_MIPS_GOT16:
case R_MIPS_GOT_DISP:
/* Find the index into the GOT where this value is located. */
if (!local_p)
{
- BFD_ASSERT (addend == 0);
+ /* GOT_PAGE may take a non-zero addend, that is ignored in a
+ GOT_PAGE relocation that decays to GOT_DISP because the
+ symbol turns out to be global. The addend is then added
+ as GOT_OFST. */
+ BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE);
g = mips_elf_global_got_index (elf_hash_table (info)->dynobj,
+ input_bfd,
(struct elf_link_hash_entry *) h);
if (! elf_hash_table(info)->dynamic_sections_created
|| (info->shared
symbol is defined locally, or was forced to be local.
We must initialize this entry in the GOT. */
bfd *tmpbfd = elf_hash_table (info)->dynobj;
- asection *sgot = mips_elf_got_section(tmpbfd);
- MIPS_ELF_PUT_WORD (tmpbfd, symbol + addend, sgot->contents + g);
+ asection *sgot = mips_elf_got_section (tmpbfd, FALSE);
+ MIPS_ELF_PUT_WORD (tmpbfd, symbol, sgot->contents + g);
}
}
else if (r_type == R_MIPS_GOT16 || r_type == R_MIPS_CALL16)
break;
else
{
- g = mips_elf_local_got_index (abfd, info, symbol + addend);
+ g = mips_elf_local_got_index (abfd, input_bfd,
+ info, symbol + addend);
if (g == MINUS_ONE)
return bfd_reloc_outofrange;
}
/* Convert GOT indices to actual offsets. */
g = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
- abfd, g);
+ abfd, input_bfd, g);
break;
case R_MIPS_HI16:
case R_MIPS_LITERAL:
gp0 = _bfd_get_gp_value (input_bfd);
gp = _bfd_get_gp_value (abfd);
+ if (elf_hash_table (info)->dynobj)
+ gp += mips_elf_adjust_gp (abfd,
+ mips_elf_got_info
+ (elf_hash_table (info)->dynobj, NULL),
+ input_bfd);
break;
default:
follows. */
forced = ! mips_elf_local_relocation_p (input_bfd, relocation,
local_sections, FALSE);
- value = mips_elf_got16_entry (abfd, info, symbol + addend, forced);
+ value = mips_elf_got16_entry (abfd, input_bfd, info,
+ symbol + addend, forced);
if (value == MINUS_ONE)
return bfd_reloc_outofrange;
value
= mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
- abfd, value);
+ abfd, input_bfd, value);
overflowed_p = mips_elf_overflow_p (value, 16);
break;
}
/* Fall through. */
case R_MIPS_GOT_DISP:
+ got_disp:
value = g;
overflowed_p = mips_elf_overflow_p (value, 16);
break;
case R_MIPS_PC16:
value = mips_elf_sign_extend (addend, 16) + symbol - p;
overflowed_p = mips_elf_overflow_p (value, 16);
- value = (bfd_vma) ((bfd_signed_vma) value / 4);
break;
case R_MIPS_GOT_HI16:
break;
case R_MIPS_GOT_PAGE:
- value = mips_elf_got_page (abfd, info, symbol + addend, NULL);
+ /* GOT_PAGE relocations that reference non-local symbols decay
+ to GOT_DISP. The corresponding GOT_OFST relocation decays to
+ 0. */
+ if (! local_p)
+ goto got_disp;
+ value = mips_elf_got_page (abfd, input_bfd, info, symbol + addend, NULL);
if (value == MINUS_ONE)
return bfd_reloc_outofrange;
value = mips_elf_got_offset_from_index (elf_hash_table (info)->dynobj,
- abfd, value);
+ abfd, input_bfd, value);
overflowed_p = mips_elf_overflow_p (value, 16);
break;
case R_MIPS_GOT_OFST:
- mips_elf_got_page (abfd, info, symbol + addend, &value);
+ if (local_p)
+ mips_elf_got_page (abfd, input_bfd, info, symbol + addend, &value);
+ else
+ value = addend;
overflowed_p = mips_elf_overflow_p (value, 16);
break;
{
asection *s;
- s = bfd_get_section_by_name (abfd, ".rel.dyn");
+ s = mips_elf_rel_dyn_section (abfd, FALSE);
BFD_ASSERT (s != NULL);
if (s->_raw_size == 0)
r_type = ELF_R_TYPE (output_bfd, rel->r_info);
dynobj = elf_hash_table (info)->dynobj;
- sreloc = bfd_get_section_by_name (dynobj, ".rel.dyn");
+ sreloc = mips_elf_rel_dyn_section (dynobj, FALSE);
BFD_ASSERT (sreloc != NULL);
BFD_ASSERT (sreloc->contents != NULL);
BFD_ASSERT (sreloc->reloc_count * MIPS_ELF_REL_SIZE (output_bfd)
}
#endif
- if (outrel[0].r_offset == (bfd_vma) -1)
+ if (outrel[0].r_offset == (bfd_vma) -1
+ || outrel[0].r_offset == (bfd_vma) -2)
skip = TRUE;
- /* FIXME: For -2 runtime relocation needs to be skipped, but
- properly resolved statically and installed. */
- BFD_ASSERT (outrel[0].r_offset != (bfd_vma) -2);
/* If we've decided to skip this relocation, just output an empty
record. Note that R_MIPS_NONE == 0, so that this call to memset
else
{
long indx;
- bfd_vma section_offset;
/* We must now calculate the dynamic symbol table index to use
in the relocation. */
abort ();
}
- /* Figure out how far the target of the relocation is from
- the beginning of its section. */
- section_offset = symbol - sec->output_section->vma;
- /* The relocation we're building is section-relative.
- Therefore, the original addend must be adjusted by the
- section offset. */
- *addendp += section_offset;
- /* Now, the relocation is just against the section. */
- symbol = sec->output_section->vma;
+ /* Instead of generating a relocation using the section
+ symbol, we may as well make it a fully relative
+ relocation. We want to avoid generating relocations to
+ local symbols because we used to generate them
+ incorrectly, without adding the original symbol value,
+ which is mandated by the ABI for section symbols. In
+ order to give dynamic loaders and applications time to
+ phase out the incorrect use, we refrain from emitting
+ section-relative relocations. It's not like they're
+ useful, after all. This should be a bit more efficient
+ as well. */
+ indx = 0;
}
/* If the relocation was previously an absolute relocation and
know where the shared library will wind up at load-time. */
outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
R_MIPS_REL32);
+ /* For strict adherence to the ABI specification, we should
+ generate a R_MIPS_64 relocation record by itself before the
+ _REL32/_64 record as well, such that the addend is read in as
+ a 64-bit value (REL32 is a 32-bit relocation, after all).
+ However, since none of the existing ELF64 MIPS dynamic
+ loaders seems to care, we don't waste space with these
+ artificial relocations. If this turns out to not be true,
+ mips_elf_allocate_dynamic_relocation() should be tweaked so
+ as to make room for a pair of dynamic relocations per
+ invocation if ABI_64_P, and here we should generate an
+ additional relocation record with R_MIPS_64 by itself for a
+ NULL symbol before this relocation record. */
outrel[1].r_info = ELF_R_INFO (output_bfd, (unsigned long) 0,
ABI_64_P (output_bfd)
? R_MIPS_64
return TRUE;
}
\f
-/* Return the ISA for a MIPS e_flags value. */
-
-static INLINE int
-elf_mips_isa (flags)
- flagword flags;
-{
- switch (flags & EF_MIPS_ARCH)
- {
- case E_MIPS_ARCH_1:
- return 1;
- case E_MIPS_ARCH_2:
- return 2;
- case E_MIPS_ARCH_3:
- return 3;
- case E_MIPS_ARCH_4:
- return 4;
- case E_MIPS_ARCH_5:
- return 5;
- case E_MIPS_ARCH_32:
- return 32;
- case E_MIPS_ARCH_64:
- return 64;
- }
- return 4;
-}
-
/* Return the MACH for a MIPS e_flags value. */
unsigned long
case E_MIPS_ARCH_64:
return bfd_mach_mipsisa64;
break;
+
+ case E_MIPS_ARCH_32R2:
+ return bfd_mach_mipsisa32r2;
+ break;
}
}
if (hdr->sh_type == SHT_MIPS_OPTIONS
&& hdr->bfd_section != NULL
- && elf_section_data (hdr->bfd_section) != NULL
- && elf_section_data (hdr->bfd_section)->tdata != NULL)
+ && mips_elf_section_data (hdr->bfd_section) != NULL
+ && mips_elf_section_data (hdr->bfd_section)->u.tdata != NULL)
{
bfd_byte *contents, *l, *lend;
- /* We stored the section contents in the elf_section_data tdata
- field in the set_section_contents routine. We save the
- section contents so that we don't have to read them again.
+ /* We stored the section contents in the tdata field in the
+ set_section_contents routine. We save the section contents
+ so that we don't have to read them again.
At this point we know that elf_gp is set, so we can look
through the section contents to see if there is an
ODK_REGINFO structure. */
- contents = (bfd_byte *) elf_section_data (hdr->bfd_section)->tdata;
+ contents = mips_elf_section_data (hdr->bfd_section)->u.tdata;
l = contents;
lend = contents + hdr->sh_size;
while (l + sizeof (Elf_External_Options) <= lend)
esd->rel_hdr2 = (Elf_Internal_Shdr *) bfd_zalloc (abfd, amt);
if (!esd->rel_hdr2)
return FALSE;
- _bfd_elf_init_reloc_shdr (abfd, esd->rel_hdr2, sec,
- !elf_section_data (sec)->use_rela_p);
+ _bfd_elf_init_reloc_shdr (abfd, esd->rel_hdr2, sec, !sec->use_rela_p);
}
return TRUE;
}
/* We need to create .got section. */
- if (! mips_elf_create_got_section (abfd, info))
+ if (! mips_elf_create_got_section (abfd, info, FALSE))
+ return FALSE;
+
+ if (! mips_elf_rel_dyn_section (elf_hash_table (info)->dynobj, TRUE))
return FALSE;
/* Create the .msym section on IRIX6. It is used by the dynamic
sizeof CALL_FP_STUB - 1) == 0)
continue;
- sec_relocs = (MNAME(abfd,_bfd_elf,link_read_relocs)
- (abfd, o, (PTR) NULL,
- (Elf_Internal_Rela *) NULL,
- info->keep_memory));
+ sec_relocs
+ = _bfd_elf_link_read_relocs (abfd, o, (PTR) NULL,
+ (Elf_Internal_Rela *) NULL,
+ info->keep_memory);
if (sec_relocs == NULL)
return FALSE;
}
else
{
- sgot = mips_elf_got_section (dynobj);
+ sgot = mips_elf_got_section (dynobj, FALSE);
if (sgot == NULL)
g = NULL;
else
{
- BFD_ASSERT (elf_section_data (sgot) != NULL);
- g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
+ BFD_ASSERT (mips_elf_section_data (sgot) != NULL);
+ g = mips_elf_section_data (sgot)->u.got_info;
BFD_ASSERT (g != NULL);
}
}
case R_MIPS_GOT_DISP:
if (dynobj == NULL)
elf_hash_table (info)->dynobj = dynobj = abfd;
- if (! mips_elf_create_got_section (dynobj, info))
+ if (! mips_elf_create_got_section (dynobj, info, FALSE))
return FALSE;
g = mips_elf_got_info (dynobj, &sgot);
break;
|| r_type == R_MIPS_GOT_LO16
|| r_type == R_MIPS_GOT_DISP))
{
- struct mips_got_entry entry, **loc;
-
/* We may need a local GOT entry for this relocation. We
don't count R_MIPS_GOT_PAGE because we can estimate the
maximum number of pages needed by looking at the size of
R_MIPS_CALL16. We don't count R_MIPS_GOT_HI16, or
R_MIPS_CALL_HI16 because these are always followed by an
R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16. */
-
- entry.abfd = abfd;
- entry.symndx = r_symndx;
- entry.addend = rel->r_addend;
- loc = (struct mips_got_entry **)
- htab_find_slot (g->got_entries, &entry, INSERT);
-
- if (*loc == NULL)
- {
- entry.gotidx = g->local_gotno++;
-
- *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
-
- if (! *loc)
- return FALSE;
-
- memcpy (*loc, &entry, sizeof entry);
-
- sgot->_raw_size += MIPS_ELF_GOT_SIZE (dynobj);
- }
+ if (! mips_elf_record_local_got_symbol (abfd, r_symndx,
+ rel->r_addend, g))
+ return FALSE;
}
switch (r_type)
if (h != NULL)
{
/* This symbol requires a global offset table entry. */
- if (! mips_elf_record_global_got_symbol (h, info, g))
+ if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
return FALSE;
/* We need a stub, not a plt entry for the undefined
}
break;
+ case R_MIPS_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 == NULL)
+ break;
+ else
+ {
+ struct mips_elf_link_hash_entry *hmips =
+ (struct mips_elf_link_hash_entry *) h;
+
+ while (hmips->root.root.type == bfd_link_hash_indirect
+ || hmips->root.root.type == bfd_link_hash_warning)
+ hmips = (struct mips_elf_link_hash_entry *)
+ hmips->root.root.u.i.link;
+
+ if ((hmips->root.root.type == bfd_link_hash_defined
+ || hmips->root.root.type == bfd_link_hash_defweak)
+ && hmips->root.root.u.def.section
+ && ! (info->shared && ! info->symbolic
+ && ! (hmips->root.elf_link_hash_flags
+ & ELF_LINK_FORCED_LOCAL))
+ /* If we've encountered any other relocation
+ referencing the symbol, we'll have marked it as
+ dynamic, and, even though we might be able to get
+ rid of the GOT entry should we know for sure all
+ previous relocations were GOT_PAGE ones, at this
+ point we can't tell, so just keep using the
+ symbol as dynamic. This is very important in the
+ multi-got case, since we don't decide whether to
+ decay GOT_PAGE to GOT_DISP on a per-GOT basis: if
+ the symbol is dynamic, we'll need a GOT entry for
+ every GOT in which the symbol is referenced with
+ a GOT_PAGE relocation. */
+ && hmips->root.dynindx == -1)
+ break;
+ }
+ /* Fall through. */
+
case R_MIPS_GOT16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_GOT_DISP:
/* This symbol requires a global offset table entry. */
- if (h && ! mips_elf_record_global_got_symbol (h, info, g))
+ if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g))
return FALSE;
break;
{
if (sreloc == NULL)
{
- const char *dname = ".rel.dyn";
-
- sreloc = bfd_get_section_by_name (dynobj, dname);
+ sreloc = mips_elf_rel_dyn_section (dynobj, TRUE);
if (sreloc == NULL)
- {
- sreloc = bfd_make_section (dynobj, dname);
- if (sreloc == NULL
- || ! bfd_set_section_flags (dynobj, sreloc,
- (SEC_ALLOC
- | SEC_LOAD
- | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY
- | SEC_LINKER_CREATED
- | SEC_READONLY))
- || ! bfd_set_section_alignment (dynobj, sreloc,
- 4))
- return FALSE;
- }
+ return FALSE;
}
#define MIPS_READONLY_SECTION (SEC_ALLOC | SEC_LOAD | SEC_READONLY)
if (info->shared)
this symbol, a symbol must have a dynamic symbol
table index greater that DT_MIPS_GOTSYM if there are
dynamic relocations against it. */
- if (h != NULL
- && ! mips_elf_record_global_got_symbol (h, info, g))
- return FALSE;
+ if (h != NULL)
+ {
+ if (dynobj == NULL)
+ elf_hash_table (info)->dynobj = dynobj = abfd;
+ if (! mips_elf_create_got_section (dynobj, info, TRUE))
+ return FALSE;
+ g = mips_elf_got_info (dynobj, &sgot);
+ if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ return FALSE;
+ }
}
if (SGI_COMPAT (abfd))
return TRUE;
}
\f
+bfd_boolean
+_bfd_mips_relax_section (abfd, sec, link_info, again)
+ bfd *abfd;
+ asection *sec;
+ struct bfd_link_info *link_info;
+ bfd_boolean *again;
+{
+ Elf_Internal_Rela *internal_relocs;
+ Elf_Internal_Rela *irel, *irelend;
+ Elf_Internal_Shdr *symtab_hdr;
+ bfd_byte *contents = NULL;
+ bfd_byte *free_contents = NULL;
+ size_t extsymoff;
+ bfd_boolean changed_contents = FALSE;
+ bfd_vma sec_start = sec->output_section->vma + sec->output_offset;
+ Elf_Internal_Sym *isymbuf = NULL;
+
+ /* We are not currently changing any sizes, so only one pass. */
+ *again = FALSE;
+
+ if (link_info->relocateable)
+ return TRUE;
+
+ internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, (PTR) NULL,
+ (Elf_Internal_Rela *) NULL,
+ link_info->keep_memory);
+ if (internal_relocs == NULL)
+ return TRUE;
+
+ irelend = internal_relocs + sec->reloc_count
+ * get_elf_backend_data (abfd)->s->int_rels_per_ext_rel;
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ extsymoff = (elf_bad_symtab (abfd)) ? 0 : symtab_hdr->sh_info;
+
+ for (irel = internal_relocs; irel < irelend; irel++)
+ {
+ bfd_vma symval;
+ bfd_signed_vma sym_offset;
+ unsigned int r_type;
+ unsigned long r_symndx;
+ asection *sym_sec;
+ unsigned long instruction;
+
+ /* Turn jalr into bgezal, and jr into beq, if they're marked
+ with a JALR relocation, that indicate where they jump to.
+ This saves some pipeline bubbles. */
+ r_type = ELF_R_TYPE (abfd, irel->r_info);
+ if (r_type != R_MIPS_JALR)
+ continue;
+
+ r_symndx = ELF_R_SYM (abfd, irel->r_info);
+ /* Compute the address of the jump target. */
+ if (r_symndx >= extsymoff)
+ {
+ struct mips_elf_link_hash_entry *h
+ = ((struct mips_elf_link_hash_entry *)
+ elf_sym_hashes (abfd) [r_symndx - extsymoff]);
+
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+
+ /* If a symbol is undefined, or if it may be overridden,
+ skip it. */
+ if (! ((h->root.root.type == bfd_link_hash_defined
+ || h->root.root.type == bfd_link_hash_defweak)
+ && h->root.root.u.def.section)
+ || (link_info->shared && ! link_info->symbolic
+ && ! (h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL)))
+ continue;
+
+ sym_sec = h->root.root.u.def.section;
+ if (sym_sec->output_section)
+ symval = (h->root.root.u.def.value
+ + sym_sec->output_section->vma
+ + sym_sec->output_offset);
+ else
+ symval = h->root.root.u.def.value;
+ }
+ else
+ {
+ Elf_Internal_Sym *isym;
+
+ /* Read this BFD's symbols if we haven't done so already. */
+ if (isymbuf == NULL && symtab_hdr->sh_info != 0)
+ {
+ isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (isymbuf == NULL)
+ isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+ symtab_hdr->sh_info, 0,
+ NULL, NULL, NULL);
+ if (isymbuf == NULL)
+ goto relax_return;
+ }
+
+ isym = isymbuf + r_symndx;
+ if (isym->st_shndx == SHN_UNDEF)
+ continue;
+ else if (isym->st_shndx == SHN_ABS)
+ sym_sec = bfd_abs_section_ptr;
+ else if (isym->st_shndx == SHN_COMMON)
+ sym_sec = bfd_com_section_ptr;
+ else
+ sym_sec
+ = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ symval = isym->st_value
+ + sym_sec->output_section->vma
+ + sym_sec->output_offset;
+ }
+
+ /* Compute branch offset, from delay slot of the jump to the
+ branch target. */
+ sym_offset = (symval + irel->r_addend)
+ - (sec_start + irel->r_offset + 4);
+
+ /* Branch offset must be properly aligned. */
+ if ((sym_offset & 3) != 0)
+ continue;
+
+ sym_offset >>= 2;
+
+ /* Check that it's in range. */
+ if (sym_offset < -0x8000 || sym_offset >= 0x8000)
+ continue;
+
+ /* Get the section contents if we haven't done so already. */
+ if (contents == NULL)
+ {
+ /* Get cached copy if it exists. */
+ if (elf_section_data (sec)->this_hdr.contents != NULL)
+ contents = elf_section_data (sec)->this_hdr.contents;
+ else
+ {
+ contents = (bfd_byte *) bfd_malloc (sec->_raw_size);
+ if (contents == NULL)
+ goto relax_return;
+
+ free_contents = contents;
+ if (! bfd_get_section_contents (abfd, sec, contents,
+ (file_ptr) 0, sec->_raw_size))
+ goto relax_return;
+ }
+ }
+
+ instruction = bfd_get_32 (abfd, contents + irel->r_offset);
+
+ /* If it was jalr <reg>, turn it into bgezal $zero, <target>. */
+ if ((instruction & 0xfc1fffff) == 0x0000f809)
+ instruction = 0x04110000;
+ /* If it was jr <reg>, turn it into b <target>. */
+ else if ((instruction & 0xfc1fffff) == 0x00000008)
+ instruction = 0x10000000;
+ else
+ continue;
+
+ instruction |= (sym_offset & 0xffff);
+ bfd_put_32 (abfd, instruction, contents + irel->r_offset);
+ changed_contents = TRUE;
+ }
+
+ if (contents != NULL
+ && elf_section_data (sec)->this_hdr.contents != contents)
+ {
+ if (!changed_contents && !link_info->keep_memory)
+ free (contents);
+ else
+ {
+ /* Cache the section contents for elf_link_input_bfd. */
+ elf_section_data (sec)->this_hdr.contents = contents;
+ }
+ }
+ return TRUE;
+
+ relax_return:
+ if (free_contents != NULL)
+ free (free_contents);
+ return FALSE;
+}
+\f
/* Adjust a symbol defined by a dynamic object and referenced by a
regular object. The current definition is in some section of the
dynamic object, but we're not including those sections. We have to
{
asection *ri;
+ bfd *dynobj;
+ asection *s;
+ struct mips_got_info *g;
+ int i;
+ bfd_size_type loadable_size = 0;
+ bfd_size_type local_gotno;
+ bfd *sub;
+
/* The .reginfo section has a fixed size. */
ri = bfd_get_section_by_name (output_bfd, ".reginfo");
if (ri != NULL)
bfd_set_section_size (output_bfd, ri,
(bfd_size_type) sizeof (Elf32_External_RegInfo));
- if (info->relocateable
- || ! mips_elf_hash_table (info)->mips16_stubs_seen)
+ if (! (info->relocateable
+ || ! mips_elf_hash_table (info)->mips16_stubs_seen))
+ mips_elf_link_hash_traverse (mips_elf_hash_table (info),
+ mips_elf_check_mips16_stubs,
+ (PTR) NULL);
+
+ dynobj = elf_hash_table (info)->dynobj;
+ if (dynobj == NULL)
+ /* Relocatable links don't have it. */
+ return TRUE;
+
+ g = mips_elf_got_info (dynobj, &s);
+ if (s == NULL)
return TRUE;
- mips_elf_link_hash_traverse (mips_elf_hash_table (info),
- mips_elf_check_mips16_stubs,
- (PTR) NULL);
+ /* 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)
+ {
+ asection *subsection;
+
+ for (subsection = sub->sections;
+ subsection;
+ subsection = subsection->next)
+ {
+ if ((subsection->flags & SEC_ALLOC) == 0)
+ continue;
+ loadable_size += ((subsection->_raw_size + 0xf)
+ &~ (bfd_size_type) 0xf);
+ }
+ }
+
+ /* There has to be a global GOT entry for every symbol with
+ a dynamic symbol table index of DT_MIPS_GOTSYM or
+ higher. Therefore, it make sense to put those symbols
+ that need GOT entries at the end of the symbol table. We
+ do that here. */
+ if (! mips_elf_sort_hash_table (info, 1))
+ return FALSE;
+
+ if (g->global_gotsym != NULL)
+ i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx;
+ else
+ /* If there are no global symbols, or none requiring
+ relocations, then GLOBAL_GOTSYM will be NULL. */
+ i = 0;
+
+ /* In the worst case, we'll get one stub per dynamic symbol, plus
+ one to account for the dummy entry at the end required by IRIX
+ rld. */
+ loadable_size += MIPS_FUNCTION_STUB_SIZE * (i + 1);
+
+ /* Assume there are two loadable segments consisting of
+ contiguous sections. Is 5 enough? */
+ local_gotno = (loadable_size >> 16) + 5;
+
+ g->local_gotno += local_gotno;
+ s->_raw_size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
+
+ g->global_gotno = i;
+ s->_raw_size += i * MIPS_ELF_GOT_SIZE (output_bfd);
+
+ if (s->_raw_size > MIPS_ELF_GOT_MAX_SIZE (output_bfd)
+ && ! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
+ return FALSE;
return TRUE;
}
bfd *dynobj;
asection *s;
bfd_boolean reltext;
- struct mips_got_info *g = NULL;
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL);
to copy relocs into the output file. */
if (strcmp (name, ".rel.dyn") != 0)
s->reloc_count = 0;
+
+ /* If combreloc is enabled, elf_link_sort_relocs() will
+ sort relocations, but in a different way than we do,
+ and before we're done creating relocations. Also, it
+ will move them around between input sections'
+ relocation's contents, so our sorting would be
+ broken, so don't let it run. */
+ info->combreloc = 0;
}
}
else if (strncmp (name, ".got", 4) == 0)
{
- int i;
- bfd_size_type loadable_size = 0;
- bfd_size_type local_gotno;
- bfd *sub;
-
- BFD_ASSERT (elf_section_data (s) != NULL);
- g = (struct mips_got_info *) elf_section_data (s)->tdata;
- BFD_ASSERT (g != NULL);
-
- /* 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)
+ /* _bfd_mips_elf_always_size_sections() has already done
+ most of the work, but some symbols may have been mapped
+ to versions that we must now resolve in the got_entries
+ hash tables. */
+ struct mips_got_info *gg = mips_elf_got_info (dynobj, NULL);
+ struct mips_got_info *g = gg;
+ struct mips_elf_set_global_got_offset_arg set_got_offset_arg;
+ unsigned int needed_relocs = 0;
+
+ if (gg->next)
{
- asection *subsection;
+ set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (output_bfd);
+ set_got_offset_arg.info = info;
- for (subsection = sub->sections;
- subsection;
- subsection = subsection->next)
+ mips_elf_resolve_final_got_entries (gg);
+ for (g = gg->next; g && g->next != gg; g = g->next)
{
- if ((subsection->flags & SEC_ALLOC) == 0)
- continue;
- loadable_size += ((subsection->_raw_size + 0xf)
- &~ (bfd_size_type) 0xf);
+ unsigned int save_assign;
+
+ mips_elf_resolve_final_got_entries (g);
+
+ /* Assign offsets to global GOT entries. */
+ save_assign = g->assigned_gotno;
+ g->assigned_gotno = g->local_gotno;
+ set_got_offset_arg.g = g;
+ set_got_offset_arg.needed_relocs = 0;
+ htab_traverse (g->got_entries,
+ mips_elf_set_global_got_offset,
+ &set_got_offset_arg);
+ needed_relocs += set_got_offset_arg.needed_relocs;
+ BFD_ASSERT (g->assigned_gotno - g->local_gotno
+ <= g->global_gotno);
+
+ g->assigned_gotno = save_assign;
+ if (info->shared)
+ {
+ needed_relocs += g->local_gotno - g->assigned_gotno;
+ BFD_ASSERT (g->assigned_gotno == g->next->local_gotno
+ + g->next->global_gotno
+ + MIPS_RESERVED_GOTNO);
+ }
}
- }
- loadable_size += MIPS_FUNCTION_STUB_SIZE;
-
- /* Assume there are two loadable segments consisting of
- contiguous sections. Is 5 enough? */
- local_gotno = (loadable_size >> 16) + 5;
- g->local_gotno += local_gotno;
- s->_raw_size += local_gotno * MIPS_ELF_GOT_SIZE (dynobj);
-
- /* There has to be a global GOT entry for every symbol with
- a dynamic symbol table index of DT_MIPS_GOTSYM or
- higher. Therefore, it make sense to put those symbols
- that need GOT entries at the end of the symbol table. We
- do that here. */
- if (! mips_elf_sort_hash_table (info, 1))
- return FALSE;
-
- if (g->global_gotsym != NULL)
- i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx;
- else
- /* If there are no global symbols, or none requiring
- relocations, then GLOBAL_GOTSYM will be NULL. */
- i = 0;
- g->global_gotno = i;
- s->_raw_size += i * MIPS_ELF_GOT_SIZE (dynobj);
+ if (needed_relocs)
+ mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs);
+ }
}
else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0)
{
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_PLTGOT, 0))
return FALSE;
- if (bfd_get_section_by_name (dynobj, ".rel.dyn"))
+ if (mips_elf_rel_dyn_section (dynobj, FALSE))
{
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_REL, 0))
return FALSE;
bfd_vma gval;
asection *sgot;
asection *smsym;
- struct mips_got_info *g;
+ struct mips_got_info *g, *gg;
const char *name;
struct mips_elf_link_hash_entry *mh;
BFD_ASSERT (h->dynindx != -1
|| (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0);
- sgot = mips_elf_got_section (dynobj);
+ sgot = mips_elf_got_section (dynobj, FALSE);
BFD_ASSERT (sgot != NULL);
- BFD_ASSERT (elf_section_data (sgot) != NULL);
- g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
+ BFD_ASSERT (mips_elf_section_data (sgot) != NULL);
+ g = mips_elf_section_data (sgot)->u.got_info;
BFD_ASSERT (g != NULL);
/* Run through the global symbol table, creating GOT entries for all
That's because such the functions are now no longer defined
in a shared object.) */
- if (info->shared && h->root.type == bfd_link_hash_undefined)
+ if ((info->shared && h->root.type == bfd_link_hash_undefined)
+ || h->root.type == bfd_link_hash_undefweak)
value = 0;
else
value = h->root.u.def.value;
}
- offset = mips_elf_global_got_index (dynobj, h);
+ offset = mips_elf_global_got_index (dynobj, output_bfd, h);
MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
}
+ if (g->next && h->dynindx != -1)
+ {
+ struct mips_got_entry e, *p;
+ bfd_vma offset;
+ bfd_vma value;
+ Elf_Internal_Rela rel[3];
+ bfd_vma addend = 0;
+
+ gg = g;
+
+ e.abfd = output_bfd;
+ e.symndx = -1;
+ e.d.h = (struct mips_elf_link_hash_entry *)h;
+
+ if (info->shared
+ || h->root.type == bfd_link_hash_undefined
+ || h->root.type == bfd_link_hash_undefweak)
+ value = 0;
+ else if (sym->st_value)
+ value = sym->st_value;
+ else
+ value = h->root.u.def.value;
+
+ memset (rel, 0, sizeof (rel));
+ rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_REL32);
+
+ for (g = g->next; g->next != gg; g = g->next)
+ {
+ if (g->got_entries
+ && (p = (struct mips_got_entry *) htab_find (g->got_entries,
+ &e)))
+ {
+ offset = p->gotidx;
+ rel[0].r_offset = rel[1].r_offset = rel[2].r_offset = offset;
+
+ MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
+
+ if ((info->shared
+ || (elf_hash_table (info)->dynamic_sections_created
+ && p->d.h != NULL
+ && ((p->d.h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_DYNAMIC) != 0)
+ && ((p->d.h->root.elf_link_hash_flags
+ & ELF_LINK_HASH_DEF_REGULAR) == 0)))
+ && ! (mips_elf_create_dynamic_relocation
+ (output_bfd, info, rel,
+ e.d.h, NULL, value, &addend, sgot)))
+ return FALSE;
+ BFD_ASSERT (addend == 0);
+ }
+ }
+ }
+
/* Create a .msym entry, if appropriate. */
smsym = bfd_get_section_by_name (dynobj, ".msym");
if (smsym)
bfd *dynobj;
asection *sdyn;
asection *sgot;
- struct mips_got_info *g;
+ struct mips_got_info *gg, *g;
dynobj = elf_hash_table (info)->dynobj;
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
- sgot = bfd_get_section_by_name (dynobj, ".got");
+ sgot = mips_elf_got_section (dynobj, FALSE);
if (sgot == NULL)
- g = NULL;
+ gg = g = NULL;
else
{
- BFD_ASSERT (elf_section_data (sgot) != NULL);
- g = (struct mips_got_info *) elf_section_data (sgot)->tdata;
+ BFD_ASSERT (mips_elf_section_data (sgot) != NULL);
+ gg = mips_elf_section_data (sgot)->u.got_info;
+ BFD_ASSERT (gg != NULL);
+ g = mips_elf_got_for_ibfd (gg, output_bfd);
BFD_ASSERT (g != NULL);
}
switch (dyn.d_tag)
{
case DT_RELENT:
- s = (bfd_get_section_by_name (dynobj, ".rel.dyn"));
+ s = mips_elf_rel_dyn_section (dynobj, FALSE);
BFD_ASSERT (s != NULL);
dyn.d_un.d_val = MIPS_ELF_REL_SIZE (dynobj);
break;
break;
case DT_MIPS_GOTSYM:
- if (g->global_gotsym)
+ if (gg->global_gotsym)
{
- dyn.d_un.d_val = g->global_gotsym->dynindx;
+ dyn.d_un.d_val = gg->global_gotsym->dynindx;
break;
}
/* In case if we don't have global got symbols we default
elf_section_data (sgot->output_section)->this_hdr.sh_entsize
= MIPS_ELF_GOT_SIZE (output_bfd);
+ /* Generate dynamic relocations for the non-primary gots. */
+ if (gg != NULL && gg->next)
+ {
+ Elf_Internal_Rela rel[3];
+ bfd_vma addend = 0;
+
+ memset (rel, 0, sizeof (rel));
+ rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_REL32);
+
+ for (g = gg->next; g->next != gg; g = g->next)
+ {
+ bfd_vma index = g->next->local_gotno + g->next->global_gotno;
+
+ MIPS_ELF_PUT_WORD (output_bfd, (bfd_vma) 0, sgot->contents
+ + index++ * MIPS_ELF_GOT_SIZE (output_bfd));
+ MIPS_ELF_PUT_WORD (output_bfd, (bfd_vma) 0x80000000, sgot->contents
+ + index++ * MIPS_ELF_GOT_SIZE (output_bfd));
+
+ if (! info->shared)
+ continue;
+
+ while (index < g->assigned_gotno)
+ {
+ rel[0].r_offset = rel[1].r_offset = rel[2].r_offset
+ = index++ * MIPS_ELF_GOT_SIZE (output_bfd);
+ if (!(mips_elf_create_dynamic_relocation
+ (output_bfd, info, rel, NULL,
+ bfd_abs_section_ptr,
+ 0, &addend, sgot)))
+ return FALSE;
+ BFD_ASSERT (addend == 0);
+ }
+ }
+ }
+
{
asection *smsym;
asection *s;
/* We need to sort the entries of the dynamic relocation section. */
- if (!ABI_64_P (output_bfd))
+ s = mips_elf_rel_dyn_section (dynobj, FALSE);
+
+ if (s != NULL
+ && s->_raw_size > (bfd_vma)2 * MIPS_ELF_REL_SIZE (output_bfd))
{
- asection *reldyn;
+ reldyn_sorting_bfd = output_bfd;
- reldyn = bfd_get_section_by_name (dynobj, ".rel.dyn");
- if (reldyn != NULL && reldyn->reloc_count > 2)
- {
- reldyn_sorting_bfd = output_bfd;
- qsort ((Elf32_External_Rel *) reldyn->contents + 1,
- (size_t) reldyn->reloc_count - 1,
- sizeof (Elf32_External_Rel), sort_dynamic_relocs);
- }
+ if (ABI_64_P (output_bfd))
+ qsort ((Elf64_External_Rel *) s->contents + 1,
+ (size_t) s->reloc_count - 1,
+ sizeof (Elf64_Mips_External_Rel), sort_dynamic_relocs_64);
+ else
+ qsort ((Elf32_External_Rel *) s->contents + 1,
+ (size_t) s->reloc_count - 1,
+ sizeof (Elf32_External_Rel), sort_dynamic_relocs);
}
-
- /* Clean up a first relocation in .rel.dyn. */
- s = bfd_get_section_by_name (dynobj, ".rel.dyn");
- if (s != NULL && s->_raw_size > 0)
- memset (s->contents, 0, MIPS_ELF_REL_SIZE (dynobj));
}
return TRUE;
}
-/* The final processing done just before writing out a MIPS ELF object
- file. This gets the MIPS architecture right based on the machine
- number. This is used by both the 32-bit and the 64-bit ABI. */
-void
-_bfd_mips_elf_final_write_processing (abfd, linker)
+/* Set ABFD's EF_MIPS_ARCH and EF_MIPS_MACH flags. */
+
+static void
+mips_set_isa_flags (abfd)
bfd *abfd;
- bfd_boolean linker ATTRIBUTE_UNUSED;
{
- unsigned long val;
- unsigned int i;
- Elf_Internal_Shdr **hdrpp;
- const char *name;
- asection *sec;
+ flagword val;
switch (bfd_get_mach (abfd))
{
case bfd_mach_mipsisa64:
val = E_MIPS_ARCH_64;
- }
+ break;
+ case bfd_mach_mipsisa32r2:
+ val = E_MIPS_ARCH_32R2;
+ break;
+ }
elf_elfheader (abfd)->e_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
elf_elfheader (abfd)->e_flags |= val;
+}
+
+
+/* The final processing done just before writing out a MIPS ELF object
+ file. This gets the MIPS architecture right based on the machine
+ number. This is used by both the 32-bit and the 64-bit ABI. */
+
+void
+_bfd_mips_elf_final_write_processing (abfd, linker)
+ bfd *abfd;
+ bfd_boolean linker ATTRIBUTE_UNUSED;
+{
+ unsigned int i;
+ Elf_Internal_Shdr **hdrpp;
+ const char *name;
+ asection *sec;
+
+ /* Keep the existing EF_MIPS_MACH and EF_MIPS_ARCH flags if the former
+ is nonzero. This is for compatibility with old objects, which used
+ a combination of a 32-bit EF_MIPS_ARCH and a 64-bit EF_MIPS_MACH. */
+ if ((elf_elfheader (abfd)->e_flags & EF_MIPS_MACH) == 0)
+ mips_set_isa_flags (abfd);
+
/* Set the sh_info field for .gptab sections and other appropriate
info for each special section. */
for (i = 1, hdrpp = elf_elfsections (abfd) + 1;
h = (struct mips_elf_link_hash_entry *) entry;
if (h->forced_local)
return;
- h->forced_local = TRUE;
+ h->forced_local = force_local;
dynobj = elf_hash_table (info)->dynobj;
- got = bfd_get_section_by_name (dynobj, ".got");
- g = (struct mips_got_info *) elf_section_data (got)->tdata;
+ if (dynobj != NULL && force_local)
+ {
+ got = mips_elf_got_section (dynobj, FALSE);
+ g = mips_elf_section_data (got)->u.got_info;
- _bfd_elf_link_hash_hide_symbol (info, &h->root, force_local);
+ if (g->next)
+ {
+ struct mips_got_entry e;
+ struct mips_got_info *gg = g;
+
+ /* Since we're turning what used to be a global symbol into a
+ local one, bump up the number of local entries of each GOT
+ that had an entry for it. This will automatically decrease
+ the number of global entries, since global_gotno is actually
+ the upper limit of global entries. */
+ e.abfd = dynobj;
+ e.symndx = -1;
+ e.d.h = h;
+
+ for (g = g->next; g != gg; g = g->next)
+ if (htab_find (g->got_entries, &e))
+ {
+ BFD_ASSERT (g->global_gotno > 0);
+ g->local_gotno++;
+ g->global_gotno--;
+ }
+
+ /* If this was a global symbol forced into the primary GOT, we
+ no longer need an entry for it. We can't release the entry
+ at this point, but we must at least stop counting it as one
+ of the symbols that required a forced got entry. */
+ if (h->root.got.offset == 2)
+ {
+ BFD_ASSERT (gg->assigned_gotno > 0);
+ gg->assigned_gotno--;
+ }
+ }
+ else if (g->global_gotno == 0 && g->global_gotsym == NULL)
+ /* If we haven't got through GOT allocation yet, just bump up the
+ number of local entries, as this symbol won't be counted as
+ global. */
+ g->local_gotno++;
+ else if (h->root.got.offset == 1)
+ {
+ /* If we're past non-multi-GOT allocation and this symbol had
+ been marked for a global got entry, give it a local entry
+ instead. */
+ BFD_ASSERT (g->global_gotno > 0);
+ g->local_gotno++;
+ g->global_gotno--;
+ }
+ }
- /* FIXME: Do we allocate too much GOT space here? */
- g->local_gotno++;
- got->_raw_size += MIPS_ELF_GOT_SIZE (dynobj);
+ _bfd_elf_link_hash_hide_symbol (info, &h->root, force_local);
}
\f
#define PDR_SIZE 32
if (! tdata)
return FALSE;
- cookie->rels = (MNAME(abfd,_bfd_elf,link_read_relocs)
- (abfd, o, (PTR) NULL,
- (Elf_Internal_Rela *) NULL,
- info->keep_memory));
+ cookie->rels = _bfd_elf_link_read_relocs (abfd, o, (PTR) NULL,
+ (Elf_Internal_Rela *) NULL,
+ info->keep_memory);
if (!cookie->rels)
{
free (tdata);
cookie->rel = cookie->rels;
cookie->relend = cookie->rels + o->reloc_count;
- for (i = 0, skip = 0; i < o->_raw_size; i ++)
+ for (i = 0, skip = 0; i < o->_raw_size / PDR_SIZE; i ++)
{
if (MNAME(abfd,_bfd_elf,reloc_symbol_deleted_p) (i * PDR_SIZE, cookie))
{
if (skip != 0)
{
- elf_section_data (o)->tdata = tdata;
+ mips_elf_section_data (o)->u.tdata = tdata;
o->_cooked_size = o->_raw_size - skip * PDR_SIZE;
ret = TRUE;
}
if (strcmp (sec->name, ".pdr") != 0)
return FALSE;
- if (elf_section_data (sec)->tdata == NULL)
+ if (mips_elf_section_data (sec)->u.tdata == NULL)
return FALSE;
to = contents;
from < end;
from += PDR_SIZE, i++)
{
- if (((unsigned char *) elf_section_data (sec)->tdata)[i] == 1)
+ if ((mips_elf_section_data (sec)->u.tdata)[i] == 1)
continue;
if (to != from)
memcpy (to, from, PDR_SIZE);
if (elf_section_data (section) == NULL)
return FALSE;
}
- c = (bfd_byte *) elf_section_data (section)->tdata;
+ c = mips_elf_section_data (section)->u.tdata;
if (c == NULL)
{
bfd_size_type size;
c = (bfd_byte *) bfd_zalloc (abfd, size);
if (c == NULL)
return FALSE;
- elf_section_data (section)->tdata = (PTR) c;
+ mips_elf_section_data (section)->u.tdata = c;
}
memcpy (c + offset, location, (size_t) count);
scRData, scSData, scSBss, scBss
};
- /* If all the things we linked together were PIC, but we're
- producing an executable (rather than a shared object), then the
- resulting file is CPIC (i.e., it calls PIC code.) */
- if (!info->shared
- && !info->relocateable
- && elf_elfheader (abfd)->e_flags & EF_MIPS_PIC)
- {
- elf_elfheader (abfd)->e_flags &= ~EF_MIPS_PIC;
- elf_elfheader (abfd)->e_flags |= EF_MIPS_CPIC;
- }
-
/* We'd carefully arranged the dynamic symbol indices, and then the
generic size_dynamic_sections renumbered them out from under us.
Rather than trying somehow to prevent the renumbering, just do
/* Make sure we didn't grow the global .got region. */
dynobj = elf_hash_table (info)->dynobj;
- got = bfd_get_section_by_name (dynobj, ".got");
- g = (struct mips_got_info *) elf_section_data (got)->tdata;
+ got = mips_elf_got_section (dynobj, FALSE);
+ g = mips_elf_section_data (got)->u.got_info;
if (g->global_gotsym != NULL)
BFD_ASSERT ((elf_hash_table (info)->dynsymcount
return TRUE;
}
\f
-/* Return TRUE if machine EXTENSION is an extension of machine BASE,
- meaning that it should be safe to link code for the two machines
- and set the output machine to EXTENSION. EXTENSION and BASE are
- both submasks of EF_MIPS_MACH. */
+/* Structure for saying that BFD machine EXTENSION extends BASE. */
+
+struct mips_mach_extension {
+ unsigned long extension, base;
+};
+
+
+/* An array describing how BFD machines relate to one another. The entries
+ are ordered topologically with MIPS I extensions listed last. */
+
+static const struct mips_mach_extension mips_mach_extensions[] = {
+ /* MIPS64 extensions. */
+ { bfd_mach_mips_sb1, bfd_mach_mipsisa64 },
+
+ /* MIPS V extensions. */
+ { bfd_mach_mipsisa64, bfd_mach_mips5 },
+
+ /* R10000 extensions. */
+ { bfd_mach_mips12000, bfd_mach_mips10000 },
+
+ /* R5000 extensions. Note: the vr5500 ISA is an extension of the core
+ vr5400 ISA, but doesn't include the multimedia stuff. It seems
+ better to allow vr5400 and vr5500 code to be merged anyway, since
+ many libraries will just use the core ISA. Perhaps we could add
+ some sort of ASE flag if this ever proves a problem. */
+ { bfd_mach_mips5500, bfd_mach_mips5400 },
+ { bfd_mach_mips5400, bfd_mach_mips5000 },
+
+ /* MIPS IV extensions. */
+ { bfd_mach_mips5, bfd_mach_mips8000 },
+ { bfd_mach_mips10000, bfd_mach_mips8000 },
+ { bfd_mach_mips5000, bfd_mach_mips8000 },
+
+ /* VR4100 extensions. */
+ { bfd_mach_mips4120, bfd_mach_mips4100 },
+ { bfd_mach_mips4111, bfd_mach_mips4100 },
+
+ /* MIPS III extensions. */
+ { bfd_mach_mips8000, bfd_mach_mips4000 },
+ { bfd_mach_mips4650, bfd_mach_mips4000 },
+ { bfd_mach_mips4600, bfd_mach_mips4000 },
+ { bfd_mach_mips4400, bfd_mach_mips4000 },
+ { bfd_mach_mips4300, bfd_mach_mips4000 },
+ { bfd_mach_mips4100, bfd_mach_mips4000 },
+ { bfd_mach_mips4010, bfd_mach_mips4000 },
+
+ /* MIPS32 extensions. */
+ { bfd_mach_mipsisa32r2, bfd_mach_mipsisa32 },
+
+ /* MIPS II extensions. */
+ { bfd_mach_mips4000, bfd_mach_mips6000 },
+ { bfd_mach_mipsisa32, bfd_mach_mips6000 },
+
+ /* MIPS I extensions. */
+ { bfd_mach_mips6000, bfd_mach_mips3000 },
+ { bfd_mach_mips3900, bfd_mach_mips3000 }
+};
+
+
+/* Return true if bfd machine EXTENSION is an extension of machine BASE. */
+
+static bfd_boolean
+mips_mach_extends_p (base, extension)
+ unsigned long base, extension;
+{
+ size_t i;
+
+ for (i = 0; extension != base && i < ARRAY_SIZE (mips_mach_extensions); i++)
+ if (extension == mips_mach_extensions[i].extension)
+ extension = mips_mach_extensions[i].base;
+
+ return extension == base;
+}
+
+
+/* Return true if the given ELF header flags describe a 32-bit binary. */
static bfd_boolean
-_bfd_mips_elf_mach_extends_p (base, extension)
- flagword base, extension;
+mips_32bit_flags_p (flags)
+ flagword flags;
{
- /* The vr5500 ISA is an extension of the core vr5400 ISA, but doesn't
- include the multimedia stuff. It seems better to allow vr5400
- and vr5500 code to be merged anyway, since many libraries will
- just use the core ISA. Perhaps we could add some sort of ASE
- flag if this ever proves a problem. */
- return (base == 0
- || (base == E_MIPS_MACH_5400 && extension == E_MIPS_MACH_5500)
- || (base == E_MIPS_MACH_4100 && extension == E_MIPS_MACH_4111)
- || (base == E_MIPS_MACH_4100 && extension == E_MIPS_MACH_4120));
+ return ((flags & EF_MIPS_32BITMODE) != 0
+ || (flags & EF_MIPS_ABI) == E_MIPS_ABI_O32
+ || (flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2);
}
+
/* Merge backend specific data from an object file to the output
object file when linking. */
/* Check if we have the same endianess */
if (! _bfd_generic_verify_endian_match (ibfd, obfd))
- return FALSE;
+ {
+ (*_bfd_error_handler)
+ (_("%s: endianness incompatible with that of the selected emulation"),
+ bfd_archive_filename (ibfd));
+ return FALSE;
+ }
if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return TRUE;
+ if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0)
+ {
+ (*_bfd_error_handler)
+ (_("%s: ABI is incompatible with that of the selected emulation"),
+ bfd_archive_filename (ibfd));
+ return FALSE;
+ }
+
new_flags = elf_elfheader (ibfd)->e_flags;
elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
old_flags = elf_elfheader (obfd)->e_flags;
new_flags &= ~EF_MIPS_NOREORDER;
old_flags &= ~EF_MIPS_NOREORDER;
+ /* Some IRIX 6 BSD-compatibility objects have this bit set. It
+ doesn't seem to matter. */
+ new_flags &= ~EF_MIPS_XGOT;
+ old_flags &= ~EF_MIPS_XGOT;
+
if (new_flags == old_flags)
return TRUE;
ok = TRUE;
- if ((new_flags & EF_MIPS_PIC) != (old_flags & EF_MIPS_PIC))
+ if (((new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0)
+ != ((old_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0))
{
- new_flags &= ~EF_MIPS_PIC;
- old_flags &= ~EF_MIPS_PIC;
(*_bfd_error_handler)
- (_("%s: linking PIC files with non-PIC files"),
+ (_("%s: warning: linking PIC files with non-PIC files"),
bfd_archive_filename (ibfd));
- ok = FALSE;
+ ok = TRUE;
}
- if ((new_flags & EF_MIPS_CPIC) != (old_flags & EF_MIPS_CPIC))
+ if (new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC))
+ elf_elfheader (obfd)->e_flags |= EF_MIPS_CPIC;
+ if (! (new_flags & EF_MIPS_PIC))
+ elf_elfheader (obfd)->e_flags &= ~EF_MIPS_PIC;
+
+ new_flags &= ~ (EF_MIPS_PIC | EF_MIPS_CPIC);
+ old_flags &= ~ (EF_MIPS_PIC | EF_MIPS_CPIC);
+
+ /* Compare the ISAs. */
+ if (mips_32bit_flags_p (old_flags) != mips_32bit_flags_p (new_flags))
{
- new_flags &= ~EF_MIPS_CPIC;
- old_flags &= ~EF_MIPS_CPIC;
(*_bfd_error_handler)
- (_("%s: linking abicalls files with non-abicalls files"),
+ (_("%s: linking 32-bit code with 64-bit code"),
bfd_archive_filename (ibfd));
ok = FALSE;
}
-
- /* Compare the ISA's. */
- if ((new_flags & (EF_MIPS_ARCH | EF_MIPS_MACH))
- != (old_flags & (EF_MIPS_ARCH | EF_MIPS_MACH)))
+ else if (!mips_mach_extends_p (bfd_get_mach (ibfd), bfd_get_mach (obfd)))
{
- int new_mach = new_flags & EF_MIPS_MACH;
- int old_mach = old_flags & EF_MIPS_MACH;
- int new_isa = elf_mips_isa (new_flags);
- int old_isa = elf_mips_isa (old_flags);
-
- /* If either has no machine specified, just compare the general isa's.
- Some combinations of machines are ok, if the isa's match. */
- if (new_mach == old_mach
- || _bfd_mips_elf_mach_extends_p (new_mach, old_mach)
- || _bfd_mips_elf_mach_extends_p (old_mach, new_mach))
+ /* OBFD's ISA isn't the same as, or an extension of, IBFD's. */
+ if (mips_mach_extends_p (bfd_get_mach (obfd), bfd_get_mach (ibfd)))
{
- /* Don't warn about mixing code using 32-bit ISAs, or mixing code
- using 64-bit ISAs. They will normally use the same data sizes
- and calling conventions. */
-
- if (( (new_isa == 1 || new_isa == 2 || new_isa == 32)
- ^ (old_isa == 1 || old_isa == 2 || old_isa == 32)) != 0)
- {
- (*_bfd_error_handler)
- (_("%s: ISA mismatch (-mips%d) with previous modules (-mips%d)"),
- bfd_archive_filename (ibfd), new_isa, old_isa);
- ok = FALSE;
- }
- else
- {
- /* Do we need to update the mach field? */
- if (_bfd_mips_elf_mach_extends_p (old_mach, new_mach))
- {
- elf_elfheader (obfd)->e_flags &= ~EF_MIPS_MACH;
- elf_elfheader (obfd)->e_flags |= new_mach;
- }
-
- /* Do we need to update the ISA field? */
- if (new_isa > old_isa)
- {
- elf_elfheader (obfd)->e_flags &= ~EF_MIPS_ARCH;
- elf_elfheader (obfd)->e_flags
- |= new_flags & EF_MIPS_ARCH;
- }
- }
+ /* Copy the architecture info from IBFD to OBFD. Also copy
+ the 32-bit flag (if set) so that we continue to recognise
+ OBFD as a 32-bit binary. */
+ bfd_set_arch_info (obfd, bfd_get_arch_info (ibfd));
+ elf_elfheader (obfd)->e_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
+ elf_elfheader (obfd)->e_flags
+ |= new_flags & (EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+
+ /* Copy across the ABI flags if OBFD doesn't use them
+ and if that was what caused us to treat IBFD as 32-bit. */
+ if ((old_flags & EF_MIPS_ABI) == 0
+ && mips_32bit_flags_p (new_flags)
+ && !mips_32bit_flags_p (new_flags & ~EF_MIPS_ABI))
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_ABI;
}
else
{
+ /* The ISAs aren't compatible. */
(*_bfd_error_handler)
- (_("%s: ISA mismatch (%d) with previous modules (%d)"),
+ (_("%s: linking %s module with previous %s modules"),
bfd_archive_filename (ibfd),
- _bfd_elf_mips_mach (new_flags),
- _bfd_elf_mips_mach (old_flags));
+ bfd_printable_name (ibfd),
+ bfd_printable_name (obfd));
ok = FALSE;
}
-
- new_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
- old_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
}
- /* Compare ABI's. The 64-bit ABI does not use EF_MIPS_ABI. But, it
+ new_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+ old_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+
+ /* Compare ABIs. The 64-bit ABI does not use EF_MIPS_ABI. But, it
does set EI_CLASS differently from any 32-bit ABI. */
if ((new_flags & EF_MIPS_ABI) != (old_flags & EF_MIPS_ABI)
|| (elf_elfheader (ibfd)->e_ident[EI_CLASS]
fprintf (file, _(" [mips32]"));
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64)
fprintf (file, _(" [mips64]"));
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2)
+ fprintf (file, _(" [mips32r2]"));
else
fprintf (file, _(" [unknown ISA]"));