/* MIPS-specific support for ELF
Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- 2003, 2004 Free Software Foundation, Inc.
+ 2003, 2004, 2005 Free Software Foundation, Inc.
Most of the information added by Ian Lance Taylor, Cygnus Support,
<ian@cygnus.com>.
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. */
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
/* This file handles functionality common to the different MIPS ABI's. */
h->forced_local). */
struct mips_elf_link_hash_entry *h;
} d;
+
+ /* The TLS types included in this GOT entry (specifically, GD and
+ IE). The GD and IE flags can be added as we encounter new
+ relocations. LDM can also be set; it will always be alone, not
+ combined with any GD or IE flags. An LDM GOT entry will be
+ a local symbol entry with r_symndx == 0. */
+ unsigned char tls_type;
+
/* The offset from the beginning of the .got section to the entry
corresponding to this symbol+addend. If it's a global symbol
whose offset is yet to be decided, it's going to be -1. */
struct elf_link_hash_entry *global_gotsym;
/* The number of global .got entries. */
unsigned int global_gotno;
+ /* The number of .got slots used for TLS. */
+ unsigned int tls_gotno;
+ /* The first unused TLS .got entry. Used only during
+ mips_elf_initialize_tls_index. */
+ unsigned int tls_assigned_gotno;
/* The number of local .got entries. */
unsigned int local_gotno;
/* The number of local .got entries we have used. */
/* In multi-got links, a pointer to the next got (err, rather, most
of the time, it points to the previous got). */
struct mips_got_info *next;
+ /* This is the GOT index of the TLS LDM entry for the GOT, MINUS_ONE
+ for none, or MINUS_TWO for not yet assigned. This is needed
+ because a single-GOT link may have multiple hash table entries
+ for the LDM. It does not get initialized in multi-GOT mode. */
+ bfd_vma tls_ldm_offset;
};
/* Map an input bfd to a got in a multi-got link. */
unsigned int primary_count;
/* The number of local and global entries in the current got. */
unsigned int current_count;
+ /* The total number of global entries which will live in the
+ primary got and be automatically relocated. This includes
+ those not referenced by the primary GOT but included in
+ the "master" GOT. */
+ unsigned int global_count;
};
/* Another structure used to pass arguments for got entries traversal. */
struct bfd_link_info *info;
};
+/* A structure used to count TLS relocations or GOT entries, for GOT
+ entry or ELF symbol table traversal. */
+
+struct mips_elf_count_tls_arg
+{
+ struct bfd_link_info *info;
+ unsigned int needed;
+};
+
struct _mips_elf_section_data
{
struct bfd_elf_section_data elf;
/* The symbol in the global GOT with the lowest dynamic symbol table
index. */
struct elf_link_hash_entry *low;
- /* The least dynamic symbol table index corresponding to a symbol
- with a GOT entry. */
+ /* The least dynamic symbol table index corresponding to a non-TLS
+ symbol with a GOT entry. */
long min_got_dynindx;
/* The greatest dynamic symbol table index corresponding to a symbol
with a GOT entry that is not referenced (e.g., a dynamic symbol
being called returns a floating point value. */
asection *call_fp_stub;
- /* Are we forced local? .*/
+ /* Are we forced local? This will only be set if we have converted
+ the initial global GOT entry to a local GOT entry. */
bfd_boolean forced_local;
+
+#define GOT_NORMAL 0
+#define GOT_TLS_GD 1
+#define GOT_TLS_LDM 2
+#define GOT_TLS_IE 4
+#define GOT_TLS_OFFSET_DONE 0x40
+#define GOT_TLS_DONE 0x80
+ unsigned char tls_type;
+ /* This is only used in single-GOT mode; in multi-GOT mode there
+ is one mips_got_entry per GOT entry, so the offset is stored
+ there. In single-GOT mode there may be many mips_got_entry
+ structures all referring to the same GOT slot. It might be
+ possible to use root.got.offset instead, but that field is
+ overloaded already. */
+ bfd_vma tls_got_offset;
};
/* MIPS ELF linker hash table. */
bfd_boolean mips16_stubs_seen;
};
+#define TLS_RELOC_P(r_type) \
+ (r_type == R_MIPS_TLS_DTPMOD32 \
+ || r_type == R_MIPS_TLS_DTPMOD64 \
+ || r_type == R_MIPS_TLS_DTPREL32 \
+ || r_type == R_MIPS_TLS_DTPREL64 \
+ || r_type == R_MIPS_TLS_GD \
+ || r_type == R_MIPS_TLS_LDM \
+ || r_type == R_MIPS_TLS_DTPREL_HI16 \
+ || r_type == R_MIPS_TLS_DTPREL_LO16 \
+ || r_type == R_MIPS_TLS_GOTTPREL \
+ || r_type == R_MIPS_TLS_TPREL32 \
+ || r_type == R_MIPS_TLS_TPREL64 \
+ || r_type == R_MIPS_TLS_TPREL_HI16 \
+ || r_type == R_MIPS_TLS_TPREL_LO16)
+
/* Structure used to pass information to mips_elf_output_extsym. */
struct extsym_info
#define cbRPDR sizeof (RPDR)
#define rpdNil ((pRPDR) 0)
\f
-static struct bfd_hash_entry *mips_elf_link_hash_newfunc
- (struct bfd_hash_entry *, struct bfd_hash_table *, const char *);
-static void ecoff_swap_rpdr_out
- (bfd *, const RPDR *, struct rpdr_ext *);
-static bfd_boolean mips_elf_create_procedure_table
- (void *, bfd *, struct bfd_link_info *, asection *,
- struct ecoff_debug_info *);
-static bfd_boolean mips_elf_check_mips16_stubs
- (struct mips_elf_link_hash_entry *, void *);
-static void bfd_mips_elf32_swap_gptab_in
- (bfd *, const Elf32_External_gptab *, Elf32_gptab *);
-static void bfd_mips_elf32_swap_gptab_out
- (bfd *, const Elf32_gptab *, Elf32_External_gptab *);
-static void bfd_elf32_swap_compact_rel_out
- (bfd *, const Elf32_compact_rel *, Elf32_External_compact_rel *);
-static void bfd_elf32_swap_crinfo_out
- (bfd *, const Elf32_crinfo *, Elf32_External_crinfo *);
-static int sort_dynamic_relocs
- (const void *, const void *);
-static int sort_dynamic_relocs_64
- (const void *, const void *);
-static bfd_boolean mips_elf_output_extsym
- (struct mips_elf_link_hash_entry *, void *);
-static int gptab_compare
- (const void *, const void *);
-static asection *mips_elf_rel_dyn_section
- (bfd *, bfd_boolean);
-static asection *mips_elf_got_section
- (bfd *, bfd_boolean);
-static struct mips_got_info *mips_elf_got_info
- (bfd *, asection **);
-static bfd_vma mips_elf_local_got_index
- (bfd *, bfd *, struct bfd_link_info *, bfd_vma);
-static bfd_vma mips_elf_global_got_index
- (bfd *, bfd *, struct elf_link_hash_entry *);
-static bfd_vma mips_elf_got_page
- (bfd *, bfd *, struct bfd_link_info *, bfd_vma, bfd_vma *);
-static bfd_vma mips_elf_got16_entry
- (bfd *, bfd *, struct bfd_link_info *, bfd_vma, bfd_boolean);
-static bfd_vma mips_elf_got_offset_from_index
- (bfd *, bfd *, bfd *, bfd_vma);
static struct mips_got_entry *mips_elf_create_local_got_entry
- (bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma);
-static bfd_boolean mips_elf_sort_hash_table
- (struct bfd_link_info *, unsigned long);
+ (bfd *, bfd *, struct mips_got_info *, asection *, bfd_vma, unsigned long,
+ struct mips_elf_link_hash_entry *, int);
static bfd_boolean mips_elf_sort_hash_table_f
(struct mips_elf_link_hash_entry *, void *);
-static bfd_boolean mips_elf_record_local_got_symbol
- (bfd *, long, bfd_vma, struct mips_got_info *);
-static bfd_boolean mips_elf_record_global_got_symbol
- (struct elf_link_hash_entry *, bfd *, struct bfd_link_info *,
- struct mips_got_info *);
-static const Elf_Internal_Rela *mips_elf_next_relocation
- (bfd *, unsigned int, const Elf_Internal_Rela *, const Elf_Internal_Rela *);
-static bfd_boolean mips_elf_local_relocation_p
- (bfd *, const Elf_Internal_Rela *, asection **, bfd_boolean);
-static bfd_boolean mips_elf_overflow_p
- (bfd_vma, int);
static bfd_vma mips_elf_high
(bfd_vma);
-static bfd_vma mips_elf_higher
- (bfd_vma);
-static bfd_vma mips_elf_highest
- (bfd_vma);
-static bfd_boolean mips_elf_create_compact_rel_section
- (bfd *, struct bfd_link_info *);
-static bfd_boolean mips_elf_create_got_section
- (bfd *, struct bfd_link_info *, bfd_boolean);
-static bfd_reloc_status_type mips_elf_calculate_relocation
- (bfd *, bfd *, asection *, struct bfd_link_info *,
- const Elf_Internal_Rela *, bfd_vma, reloc_howto_type *,
- Elf_Internal_Sym *, asection **, bfd_vma *, const char **,
- bfd_boolean *, bfd_boolean);
-static bfd_vma mips_elf_obtain_contents
- (reloc_howto_type *, const Elf_Internal_Rela *, bfd *, bfd_byte *);
-static bfd_boolean mips_elf_perform_relocation
- (struct bfd_link_info *, reloc_howto_type *, const Elf_Internal_Rela *,
- bfd_vma, bfd *, asection *, bfd_byte *, bfd_boolean);
static bfd_boolean mips_elf_stub_section_p
(bfd *, asection *);
-static void mips_elf_allocate_dynamic_relocations
- (bfd *, unsigned int);
static bfd_boolean mips_elf_create_dynamic_relocation
(bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
struct mips_elf_link_hash_entry *, asection *, bfd_vma,
bfd_vma *, asection *);
-static void mips_set_isa_flags
- (bfd *);
-static INLINE char *elf_mips_abi_name
- (bfd *);
-static void mips_elf_irix6_finish_dynamic_symbol
- (bfd *, const char *, Elf_Internal_Sym *);
-static bfd_boolean mips_mach_extends_p
- (unsigned long, unsigned long);
-static bfd_boolean mips_32bit_flags_p
- (flagword);
-static INLINE hashval_t mips_elf_hash_bfd_vma
- (bfd_vma);
static hashval_t mips_elf_got_entry_hash
(const void *);
-static int mips_elf_got_entry_eq
- (const void *, const void *);
-
-static bfd_boolean mips_elf_multi_got
- (bfd *, struct bfd_link_info *, struct mips_got_info *,
- asection *, bfd_size_type);
-static hashval_t mips_elf_multi_got_entry_hash
- (const void *);
-static int mips_elf_multi_got_entry_eq
- (const void *, const void *);
-static hashval_t mips_elf_bfd2got_entry_hash
- (const void *);
-static int mips_elf_bfd2got_entry_eq
- (const void *, const void *);
-static int mips_elf_make_got_per_bfd
- (void **, void *);
-static int mips_elf_merge_gots
- (void **, void *);
-static int mips_elf_set_global_got_offset
- (void **, void *);
-static int mips_elf_set_no_stub
- (void **, void *);
-static int mips_elf_resolve_final_got_entry
- (void **, void *);
-static void mips_elf_resolve_final_got_entries
- (struct mips_got_info *);
static bfd_vma mips_elf_adjust_gp
(bfd *, struct mips_got_info *, bfd *);
static struct mips_got_info *mips_elf_got_for_ibfd
#define MIPS_ELF_OPTIONS_SECTION_NAME(abfd) \
(NEWABI_P (abfd) ? ".MIPS.options" : ".options")
+/* True if NAME is the recognized name of any SHT_MIPS_OPTIONS section.
+ Some IRIX system files do not use MIPS_ELF_OPTIONS_SECTION_NAME. */
+#define MIPS_ELF_OPTIONS_SECTION_NAME_P(NAME) \
+ (strcmp (NAME, ".MIPS.options") == 0 || strcmp (NAME, ".options") == 0)
+
/* The name of the stub section. */
#define MIPS_ELF_STUB_SECTION_NAME(abfd) ".MIPS.stubs"
#define mips_elf_hash_table(p) \
((struct mips_elf_link_hash_table *) ((p)->hash))
+/* Find the base offsets for thread-local storage in this object,
+ for GD/LD and IE/LE respectively. */
+
+#define TP_OFFSET 0x7000
+#define DTP_OFFSET 0x8000
+
+static bfd_vma
+dtprel_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma + DTP_OFFSET;
+}
+
+static bfd_vma
+tprel_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma + TP_OFFSET;
+}
+
/* Create an entry in a MIPS ELF linker hash table. */
static struct bfd_hash_entry *
ret->call_stub = NULL;
ret->call_fp_stub = NULL;
ret->forced_local = FALSE;
+ ret->tls_type = GOT_NORMAL;
}
return (struct bfd_hash_entry *) ret;
H_PUT_16 (abfd, in->pcreg, ex->p_pcreg);
H_PUT_32 (abfd, in->irpss, ex->p_irpss);
-#if 0 /* FIXME */
- H_PUT_S32 (abfd, in->exception_info, ex->p_exception_info);
-#endif
}
/* Create a runtime procedure table from the .mdebug section. */
ss = bfd_malloc (count);
if (ss == NULL)
goto error_return;
- if (! _bfd_ecoff_get_accumulated_ss (handle, ss))
+ if (! _bfd_ecoff_get_accumulated_ss (handle, (bfd_byte *) ss))
goto error_return;
count = hdr->ipdMax;
/* Skip this section later on (I don't think this currently
matters, but someday it might). */
- s->link_order_head = NULL;
+ s->map_head.link_order = NULL;
if (epdr != NULL)
free (epdr);
return TRUE;
}
\f
+/* R_MIPS16_26 is used for the mips16 jal and jalx instructions.
+ Most mips16 instructions are 16 bits, but these instructions
+ are 32 bits.
+
+ The format of these instructions is:
+
+ +--------------+--------------------------------+
+ | JALX | X| Imm 20:16 | Imm 25:21 |
+ +--------------+--------------------------------+
+ | Immediate 15:0 |
+ +-----------------------------------------------+
+
+ JALX is the 5-bit value 00011. X is 0 for jal, 1 for jalx.
+ Note that the immediate value in the first word is swapped.
+
+ When producing a relocatable object file, R_MIPS16_26 is
+ handled mostly like R_MIPS_26. In particular, the addend is
+ stored as a straight 26-bit value in a 32-bit instruction.
+ (gas makes life simpler for itself by never adjusting a
+ R_MIPS16_26 reloc to be against a section, so the addend is
+ always zero). However, the 32 bit instruction is stored as 2
+ 16-bit values, rather than a single 32-bit value. In a
+ big-endian file, the result is the same; in a little-endian
+ file, the two 16-bit halves of the 32 bit value are swapped.
+ This is so that a disassembler can recognize the jal
+ instruction.
+
+ When doing a final link, R_MIPS16_26 is treated as a 32 bit
+ instruction stored as two 16-bit values. The addend A is the
+ contents of the targ26 field. The calculation is the same as
+ R_MIPS_26. When storing the calculated value, reorder the
+ immediate value as shown above, and don't forget to store the
+ value as two 16-bit values.
+
+ To put it in MIPS ABI terms, the relocation field is T-targ26-16,
+ defined as
+
+ big-endian:
+ +--------+----------------------+
+ | | |
+ | | targ26-16 |
+ |31 26|25 0|
+ +--------+----------------------+
+
+ little-endian:
+ +----------+------+-------------+
+ | | | |
+ | sub1 | | sub2 |
+ |0 9|10 15|16 31|
+ +----------+--------------------+
+ where targ26-16 is sub1 followed by sub2 (i.e., the addend field A is
+ ((sub1 << 16) | sub2)).
+
+ When producing a relocatable object file, the calculation is
+ (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
+ When producing a fully linked file, the calculation is
+ let R = (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
+ ((R & 0x1f0000) << 5) | ((R & 0x3e00000) >> 5) | (R & 0xffff)
+
+ R_MIPS16_GPREL is used for GP-relative addressing in mips16
+ mode. A typical instruction will have a format like this:
+
+ +--------------+--------------------------------+
+ | EXTEND | Imm 10:5 | Imm 15:11 |
+ +--------------+--------------------------------+
+ | Major | rx | ry | Imm 4:0 |
+ +--------------+--------------------------------+
+
+ EXTEND is the five bit value 11110. Major is the instruction
+ opcode.
+
+ This is handled exactly like R_MIPS_GPREL16, except that the
+ addend is retrieved and stored as shown in this diagram; that
+ is, the Imm fields above replace the V-rel16 field.
+
+ All we need to do here is shuffle the bits appropriately. As
+ above, the two 16-bit halves must be swapped on a
+ little-endian system.
+
+ R_MIPS16_HI16 and R_MIPS16_LO16 are used in mips16 mode to
+ access data when neither GP-relative nor PC-relative addressing
+ can be used. They are handled like R_MIPS_HI16 and R_MIPS_LO16,
+ except that the addend is retrieved and stored as shown above
+ for R_MIPS16_GPREL.
+ */
+void
+_bfd_mips16_elf_reloc_unshuffle (bfd *abfd, int r_type,
+ bfd_boolean jal_shuffle, bfd_byte *data)
+{
+ bfd_vma extend, insn, val;
+
+ if (r_type != R_MIPS16_26 && r_type != R_MIPS16_GPREL
+ && r_type != R_MIPS16_HI16 && r_type != R_MIPS16_LO16)
+ return;
+
+ /* Pick up the mips16 extend instruction and the real instruction. */
+ extend = bfd_get_16 (abfd, data);
+ insn = bfd_get_16 (abfd, data + 2);
+ if (r_type == R_MIPS16_26)
+ {
+ if (jal_shuffle)
+ val = ((extend & 0xfc00) << 16) | ((extend & 0x3e0) << 11)
+ | ((extend & 0x1f) << 21) | insn;
+ else
+ val = extend << 16 | insn;
+ }
+ else
+ val = ((extend & 0xf800) << 16) | ((insn & 0xffe0) << 11)
+ | ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+ bfd_put_32 (abfd, val, data);
+}
+
+void
+_bfd_mips16_elf_reloc_shuffle (bfd *abfd, int r_type,
+ bfd_boolean jal_shuffle, bfd_byte *data)
+{
+ bfd_vma extend, insn, val;
+
+ if (r_type != R_MIPS16_26 && r_type != R_MIPS16_GPREL
+ && r_type != R_MIPS16_HI16 && r_type != R_MIPS16_LO16)
+ return;
+
+ val = bfd_get_32 (abfd, data);
+ if (r_type == R_MIPS16_26)
+ {
+ if (jal_shuffle)
+ {
+ insn = val & 0xffff;
+ extend = ((val >> 16) & 0xfc00) | ((val >> 11) & 0x3e0)
+ | ((val >> 21) & 0x1f);
+ }
+ else
+ {
+ insn = val & 0xffff;
+ extend = val >> 16;
+ }
+ }
+ else
+ {
+ insn = ((val >> 11) & 0xffe0) | (val & 0x1f);
+ extend = ((val >> 16) & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0);
+ }
+ bfd_put_16 (abfd, insn, data + 2);
+ bfd_put_16 (abfd, extend, data);
+}
+
bfd_reloc_status_type
_bfd_mips_elf_gprel16_with_gp (bfd *abfd, asymbol *symbol,
arelent *reloc_entry, asection *input_section,
bfd *output_bfd, char **error_message)
{
bfd_vma vallo;
+ bfd_byte *location = (bfd_byte *) data + reloc_entry->address;
if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
return bfd_reloc_outofrange;
- vallo = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+ _bfd_mips16_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
+ vallo = bfd_get_32 (abfd, location);
+ _bfd_mips16_elf_reloc_shuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
+
while (mips_hi16_list != NULL)
{
bfd_reloc_status_type ret;
reloc_entry->addend += val;
else
{
+ bfd_byte *location = (bfd_byte *) data + reloc_entry->address;
+
/* Add in the separate addend, if any. */
val += reloc_entry->addend;
/* Add VAL to the relocation field. */
+ _bfd_mips16_elf_reloc_unshuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
status = _bfd_relocate_contents (reloc_entry->howto, abfd, val,
- (bfd_byte *) data
- + reloc_entry->address);
+ location);
+ _bfd_mips16_elf_reloc_shuffle (abfd, reloc_entry->howto->type, FALSE,
+ location);
+
if (status != bfd_reloc_ok)
return status;
}
/* Like sort_dynamic_relocs, but used for elf64 relocations. */
static int
-sort_dynamic_relocs_64 (const void *arg1, const void *arg2)
+sort_dynamic_relocs_64 (const void *arg1 ATTRIBUTE_UNUSED,
+ const void *arg2 ATTRIBUTE_UNUSED)
{
+#ifdef BFD64
Elf_Internal_Rela int_reloc1[3];
Elf_Internal_Rela int_reloc2[3];
return (ELF64_R_SYM (int_reloc1[0].r_info)
- ELF64_R_SYM (int_reloc2[0].r_info));
+#else
+ abort ();
+#endif
}
if (h->root.indx == -2)
strip = FALSE;
- else if (((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
- || (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0)
- && (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
- && (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
+ else if ((h->root.def_dynamic
+ || h->root.ref_dynamic
+ || h->root.type == bfd_link_hash_new)
+ && !h->root.def_regular
+ && !h->root.ref_regular)
strip = TRUE;
else if (einfo->info->strip == strip_all
|| (einfo->info->strip == strip_some
else
h->esym.asym.value = 0;
}
- else if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
+ else if (h->root.needs_plt)
{
struct mips_elf_link_hash_entry *hd = h;
bfd_boolean no_fn_stub = h->no_fn_stub;
else
h->esym.asym.value = 0;
}
-#if 0 /* FIXME? */
- h->esym.ifd = 0;
-#endif
}
}
const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
return entry->symndx
+ + ((entry->tls_type & GOT_TLS_LDM) << 17)
+ (! entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address)
: entry->abfd->id
+ (entry->symndx >= 0 ? mips_elf_hash_bfd_vma (entry->d.addend)
const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
+ /* An LDM entry can only match another LDM entry. */
+ if ((e1->tls_type ^ e2->tls_type) & GOT_TLS_LDM)
+ return 0;
+
return e1->abfd == e2->abfd && e1->symndx == e2->symndx
&& (! e1->abfd ? e1->d.address == e2->d.address
: e1->symndx >= 0 ? e1->d.addend == e2->d.addend
+ (! entry->abfd
? mips_elf_hash_bfd_vma (entry->d.address)
: entry->symndx >= 0
- ? (entry->abfd->id
- + mips_elf_hash_bfd_vma (entry->d.addend))
+ ? ((entry->tls_type & GOT_TLS_LDM)
+ ? (GOT_TLS_LDM << 17)
+ : (entry->abfd->id
+ + mips_elf_hash_bfd_vma (entry->d.addend)))
: entry->d.h->root.root.root.hash);
}
const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
+ /* Any two LDM entries match. */
+ if (e1->tls_type & e2->tls_type & GOT_TLS_LDM)
+ return 1;
+
+ /* Nothing else matches an LDM entry. */
+ if ((e1->tls_type ^ e2->tls_type) & GOT_TLS_LDM)
+ return 0;
+
return e1->symndx == e2->symndx
&& (e1->symndx >= 0 ? e1->abfd == e2->abfd && e1->d.addend == e2->d.addend
: e1->abfd == NULL || e2->abfd == NULL
sreloc = bfd_get_section_by_name (dynobj, dname);
if (sreloc == NULL && create_p)
{
- sreloc = bfd_make_section (dynobj, dname);
+ sreloc = bfd_make_section_with_flags (dynobj, dname,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED
+ | SEC_READONLY));
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,
MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
return NULL;
return g;
}
+/* Count the number of relocations needed for a TLS GOT entry, with
+ access types from TLS_TYPE, and symbol H (or a local symbol if H
+ is NULL). */
+
+static int
+mips_tls_got_relocs (struct bfd_link_info *info, unsigned char tls_type,
+ struct elf_link_hash_entry *h)
+{
+ int indx = 0;
+ int ret = 0;
+ bfd_boolean need_relocs = FALSE;
+ bfd_boolean dyn = elf_hash_table (info)->dynamic_sections_created;
+
+ if (h && WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, h)))
+ indx = h->dynindx;
+
+ if ((info->shared || indx != 0)
+ && (h == NULL
+ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ need_relocs = TRUE;
+
+ if (!need_relocs)
+ return FALSE;
+
+ if (tls_type & GOT_TLS_GD)
+ {
+ ret++;
+ if (indx != 0)
+ ret++;
+ }
+
+ if (tls_type & GOT_TLS_IE)
+ ret++;
+
+ if ((tls_type & GOT_TLS_LDM) && info->shared)
+ ret++;
+
+ return ret;
+}
+
+/* Count the number of TLS relocations required for the GOT entry in
+ ARG1, if it describes a local symbol. */
+
+static int
+mips_elf_count_local_tls_relocs (void **arg1, void *arg2)
+{
+ struct mips_got_entry *entry = * (struct mips_got_entry **) arg1;
+ struct mips_elf_count_tls_arg *arg = arg2;
+
+ if (entry->abfd != NULL && entry->symndx != -1)
+ arg->needed += mips_tls_got_relocs (arg->info, entry->tls_type, NULL);
+
+ return 1;
+}
+
+/* Count the number of TLS GOT entries required for the global (or
+ forced-local) symbol in ARG1. */
+
+static int
+mips_elf_count_global_tls_entries (void *arg1, void *arg2)
+{
+ struct mips_elf_link_hash_entry *hm
+ = (struct mips_elf_link_hash_entry *) arg1;
+ struct mips_elf_count_tls_arg *arg = arg2;
+
+ if (hm->tls_type & GOT_TLS_GD)
+ arg->needed += 2;
+ if (hm->tls_type & GOT_TLS_IE)
+ arg->needed += 1;
+
+ return 1;
+}
+
+/* Count the number of TLS relocations required for the global (or
+ forced-local) symbol in ARG1. */
+
+static int
+mips_elf_count_global_tls_relocs (void *arg1, void *arg2)
+{
+ struct mips_elf_link_hash_entry *hm
+ = (struct mips_elf_link_hash_entry *) arg1;
+ struct mips_elf_count_tls_arg *arg = arg2;
+
+ arg->needed += mips_tls_got_relocs (arg->info, hm->tls_type, &hm->root);
+
+ return 1;
+}
+
+/* Output a simple dynamic relocation into SRELOC. */
+
+static void
+mips_elf_output_dynamic_relocation (bfd *output_bfd,
+ asection *sreloc,
+ unsigned long indx,
+ int r_type,
+ bfd_vma offset)
+{
+ Elf_Internal_Rela rel[3];
+
+ memset (rel, 0, sizeof (rel));
+
+ rel[0].r_info = ELF_R_INFO (output_bfd, indx, r_type);
+ rel[0].r_offset = rel[1].r_offset = rel[2].r_offset = offset;
+
+ if (ABI_64_P (output_bfd))
+ {
+ (*get_elf_backend_data (output_bfd)->s->swap_reloc_out)
+ (output_bfd, &rel[0],
+ (sreloc->contents
+ + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
+ }
+ else
+ bfd_elf32_swap_reloc_out
+ (output_bfd, &rel[0],
+ (sreloc->contents
+ + sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+ ++sreloc->reloc_count;
+}
+
+/* Initialize a set of TLS GOT entries for one symbol. */
+
+static void
+mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
+ unsigned char *tls_type_p,
+ struct bfd_link_info *info,
+ struct mips_elf_link_hash_entry *h,
+ bfd_vma value)
+{
+ int indx;
+ asection *sreloc, *sgot;
+ bfd_vma offset, offset2;
+ bfd *dynobj;
+ bfd_boolean need_relocs = FALSE;
+
+ dynobj = elf_hash_table (info)->dynobj;
+ sgot = mips_elf_got_section (dynobj, FALSE);
+
+ indx = 0;
+ if (h != NULL)
+ {
+ bfd_boolean dyn = elf_hash_table (info)->dynamic_sections_created;
+
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, &h->root)
+ && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, &h->root)))
+ indx = h->root.dynindx;
+ }
+
+ if (*tls_type_p & GOT_TLS_DONE)
+ return;
+
+ if ((info->shared || indx != 0)
+ && (h == NULL
+ || ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ need_relocs = TRUE;
+
+ /* MINUS_ONE means the symbol is not defined in this object. It may not
+ be defined at all; assume that the value doesn't matter in that
+ case. Otherwise complain if we would use the value. */
+ BFD_ASSERT (value != MINUS_ONE || (indx != 0 && need_relocs)
+ || h->root.root.type == bfd_link_hash_undefweak);
+
+ /* Emit necessary relocations. */
+ sreloc = mips_elf_rel_dyn_section (dynobj, FALSE);
+
+ /* General Dynamic. */
+ if (*tls_type_p & GOT_TLS_GD)
+ {
+ offset = got_offset;
+ offset2 = offset + MIPS_ELF_GOT_SIZE (abfd);
+
+ if (need_relocs)
+ {
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, indx,
+ ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
+ sgot->output_offset + sgot->output_section->vma + offset);
+
+ if (indx)
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, indx,
+ ABI_64_P (abfd) ? R_MIPS_TLS_DTPREL64 : R_MIPS_TLS_DTPREL32,
+ sgot->output_offset + sgot->output_section->vma + offset2);
+ else
+ MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
+ sgot->contents + offset2);
+ }
+ else
+ {
+ MIPS_ELF_PUT_WORD (abfd, 1,
+ sgot->contents + offset);
+ MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
+ sgot->contents + offset2);
+ }
+
+ got_offset += 2 * MIPS_ELF_GOT_SIZE (abfd);
+ }
+
+ /* Initial Exec model. */
+ if (*tls_type_p & GOT_TLS_IE)
+ {
+ offset = got_offset;
+
+ if (need_relocs)
+ {
+ if (indx == 0)
+ MIPS_ELF_PUT_WORD (abfd, value - elf_hash_table (info)->tls_sec->vma,
+ sgot->contents + offset);
+ else
+ MIPS_ELF_PUT_WORD (abfd, 0,
+ sgot->contents + offset);
+
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, indx,
+ ABI_64_P (abfd) ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32,
+ sgot->output_offset + sgot->output_section->vma + offset);
+ }
+ else
+ MIPS_ELF_PUT_WORD (abfd, value - tprel_base (info),
+ sgot->contents + offset);
+ }
+
+ if (*tls_type_p & GOT_TLS_LDM)
+ {
+ /* The initial offset is zero, and the LD offsets will include the
+ bias by DTP_OFFSET. */
+ MIPS_ELF_PUT_WORD (abfd, 0,
+ sgot->contents + got_offset
+ + MIPS_ELF_GOT_SIZE (abfd));
+
+ if (!info->shared)
+ MIPS_ELF_PUT_WORD (abfd, 1,
+ sgot->contents + got_offset);
+ else
+ mips_elf_output_dynamic_relocation
+ (abfd, sreloc, indx,
+ ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
+ sgot->output_offset + sgot->output_section->vma + got_offset);
+ }
+
+ *tls_type_p |= GOT_TLS_DONE;
+}
+
+/* Return the GOT index to use for a relocation of type R_TYPE against
+ a symbol accessed using TLS_TYPE models. The GOT entries for this
+ symbol in this GOT start at GOT_INDEX. This function initializes the
+ GOT entries and corresponding relocations. */
+
+static bfd_vma
+mips_tls_got_index (bfd *abfd, bfd_vma got_index, unsigned char *tls_type,
+ int r_type, struct bfd_link_info *info,
+ struct mips_elf_link_hash_entry *h, bfd_vma symbol)
+{
+ BFD_ASSERT (r_type == R_MIPS_TLS_GOTTPREL || r_type == R_MIPS_TLS_GD
+ || r_type == R_MIPS_TLS_LDM);
+
+ mips_elf_initialize_tls_slots (abfd, got_index, tls_type, info, h, symbol);
+
+ if (r_type == R_MIPS_TLS_GOTTPREL)
+ {
+ BFD_ASSERT (*tls_type & GOT_TLS_IE);
+ if (*tls_type & GOT_TLS_GD)
+ return got_index + 2 * MIPS_ELF_GOT_SIZE (abfd);
+ else
+ return got_index;
+ }
+
+ if (r_type == R_MIPS_TLS_GD)
+ {
+ BFD_ASSERT (*tls_type & GOT_TLS_GD);
+ return got_index;
+ }
+
+ if (r_type == R_MIPS_TLS_LDM)
+ {
+ BFD_ASSERT (*tls_type & GOT_TLS_LDM);
+ return got_index;
+ }
+
+ return got_index;
+}
+
/* Returns the GOT offset at which the indicated address can be found.
- If there is not yet a GOT entry for this value, create one. Returns
- -1 if no satisfactory GOT offset can be found. */
+ If there is not yet a GOT entry for this value, create one. If
+ R_SYMNDX refers to a TLS symbol, create a TLS GOT entry instead.
+ Returns -1 if no satisfactory GOT offset can be found. */
static bfd_vma
mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info,
- bfd_vma value)
+ bfd_vma value, unsigned long r_symndx,
+ struct mips_elf_link_hash_entry *h, int r_type)
{
asection *sgot;
struct mips_got_info *g;
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
- entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
- if (entry)
- return entry->gotidx;
- else
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value,
+ r_symndx, h, r_type);
+ if (!entry)
return MINUS_ONE;
+
+ if (TLS_RELOC_P (r_type))
+ return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type, r_type,
+ info, h, value);
+ else
+ return entry->gotidx;
}
/* Returns the GOT index for the global symbol indicated by H. */
static bfd_vma
-mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h)
+mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h,
+ int r_type, struct bfd_link_info *info)
{
bfd_vma index;
asection *sgot;
BFD_ASSERT (h->dynindx >= 0);
g = mips_elf_got_for_ibfd (g, ibfd);
- if (g->next != gg)
+ if (g->next != gg || TLS_RELOC_P (r_type))
{
e.abfd = ibfd;
e.symndx = -1;
e.d.h = (struct mips_elf_link_hash_entry *)h;
+ e.tls_type = 0;
p = htab_find (g->got_entries, &e);
BFD_ASSERT (p->gotidx > 0);
- return p->gotidx;
+
+ if (TLS_RELOC_P (r_type))
+ {
+ bfd_vma value = MINUS_ONE;
+ if ((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && h->root.u.def.section->output_section)
+ value = (h->root.u.def.value
+ + h->root.u.def.section->output_offset
+ + h->root.u.def.section->output_section->vma);
+
+ return mips_tls_got_index (abfd, p->gotidx, &p->tls_type, r_type,
+ info, e.d.h, value);
+ }
+ else
+ return p->gotidx;
}
}
if (gg->global_gotsym != NULL)
global_got_dynindx = gg->global_gotsym->dynindx;
- /* Once we determine the global GOT entry with the lowest dynamic
- symbol table index, we must put all dynamic symbols with greater
- indices into the GOT. That makes it easy to calculate the GOT
- offset. */
- BFD_ASSERT (h->dynindx >= global_got_dynindx);
- index = ((h->dynindx - global_got_dynindx + g->local_gotno)
- * MIPS_ELF_GOT_SIZE (abfd));
+ if (TLS_RELOC_P (r_type))
+ {
+ struct mips_elf_link_hash_entry *hm
+ = (struct mips_elf_link_hash_entry *) h;
+ bfd_vma value = MINUS_ONE;
+
+ if ((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && h->root.u.def.section->output_section)
+ value = (h->root.u.def.value
+ + h->root.u.def.section->output_offset
+ + h->root.u.def.section->output_section->vma);
+
+ index = mips_tls_got_index (abfd, hm->tls_got_offset, &hm->tls_type,
+ r_type, info, hm, value);
+ }
+ else
+ {
+ /* Once we determine the global GOT entry with the lowest dynamic
+ symbol table index, we must put all dynamic symbols with greater
+ indices into the GOT. That makes it easy to calculate the GOT
+ offset. */
+ BFD_ASSERT (h->dynindx >= global_got_dynindx);
+ index = ((h->dynindx - global_got_dynindx + g->local_gotno)
+ * MIPS_ELF_GOT_SIZE (abfd));
+ }
BFD_ASSERT (index < sgot->size);
return index;
entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot,
(value + 0x8000)
- & (~(bfd_vma)0xffff));
+ & (~(bfd_vma)0xffff), 0,
+ NULL, R_MIPS_GOT_PAGE);
if (!entry)
return MINUS_ONE;
g = mips_elf_got_info (elf_hash_table (info)->dynobj, &sgot);
- entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value);
+ entry = mips_elf_create_local_got_entry (abfd, ibfd, g, sgot, value, 0, NULL,
+ R_MIPS_GOT16);
if (entry)
return entry->gotidx;
else
}
/* Create a local GOT entry for VALUE. Return the index of the entry,
- or -1 if it could not be created. */
+ or -1 if it could not be created. If R_SYMNDX refers to a TLS symbol,
+ create a TLS entry instead. */
static struct mips_got_entry *
mips_elf_create_local_got_entry (bfd *abfd, bfd *ibfd,
struct mips_got_info *gg,
- asection *sgot, bfd_vma value)
+ asection *sgot, bfd_vma value,
+ unsigned long r_symndx,
+ struct mips_elf_link_hash_entry *h,
+ int r_type)
{
struct mips_got_entry entry, **loc;
struct mips_got_info *g;
entry.abfd = NULL;
entry.symndx = -1;
entry.d.address = value;
+ entry.tls_type = 0;
g = mips_elf_got_for_ibfd (gg, ibfd);
if (g == NULL)
BFD_ASSERT (g != NULL);
}
+ /* We might have a symbol, H, if it has been forced local. Use the
+ global entry then. It doesn't matter whether an entry is local
+ or global for TLS, since the dynamic linker does not
+ automatically relocate TLS GOT entries. */
+ BFD_ASSERT (h == NULL || h->root.forced_local);
+ if (TLS_RELOC_P (r_type))
+ {
+ struct mips_got_entry *p;
+
+ entry.abfd = ibfd;
+ if (r_type == R_MIPS_TLS_LDM)
+ {
+ entry.tls_type = GOT_TLS_LDM;
+ entry.symndx = 0;
+ entry.d.addend = 0;
+ }
+ else if (h == NULL)
+ {
+ entry.symndx = r_symndx;
+ entry.d.addend = 0;
+ }
+ else
+ entry.d.h = h;
+
+ p = (struct mips_got_entry *)
+ htab_find (g->got_entries, &entry);
+
+ BFD_ASSERT (p);
+ return p;
+ }
+
loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
INSERT);
if (*loc)
return *loc;
entry.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
+ entry.tls_type = 0;
*loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
-1. */
if (h->root.got.offset == 2)
{
+ BFD_ASSERT (h->tls_type == GOT_NORMAL);
+
if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx)
hsd->low = (struct elf_link_hash_entry *) h;
h->root.dynindx = hsd->max_unref_got_dynindx++;
h->root.dynindx = hsd->max_non_got_dynindx++;
else
{
+ BFD_ASSERT (h->tls_type == GOT_NORMAL);
+
h->root.dynindx = --hsd->min_got_dynindx;
hsd->low = (struct elf_link_hash_entry *) h;
}
static bfd_boolean
mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
bfd *abfd, struct bfd_link_info *info,
- struct mips_got_info *g)
+ struct mips_got_info *g,
+ unsigned char tls_flag)
{
struct mips_got_entry entry, **loc;
entry.abfd = abfd;
entry.symndx = -1;
entry.d.h = (struct mips_elf_link_hash_entry *) h;
+ entry.tls_type = 0;
loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
INSERT);
/* If we've already marked this entry as needing GOT space, we don't
need to do it again. */
if (*loc)
- return TRUE;
+ {
+ (*loc)->tls_type |= tls_flag;
+ return TRUE;
+ }
*loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
return FALSE;
entry.gotidx = -1;
+ entry.tls_type = tls_flag;
+
memcpy (*loc, &entry, sizeof entry);
if (h->got.offset != MINUS_ONE)
/* By setting this to a value other than -1, we are indicating that
there needs to be a GOT entry for H. Avoid using zero, as the
generic ELF copy_indirect_symbol tests for <= 0. */
- h->got.offset = 1;
+ if (tls_flag == 0)
+ h->got.offset = 1;
return TRUE;
}
static bfd_boolean
mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
- struct mips_got_info *g)
+ struct mips_got_info *g,
+ unsigned char tls_flag)
{
struct mips_got_entry entry, **loc;
entry.abfd = abfd;
entry.symndx = symndx;
entry.d.addend = addend;
+ entry.tls_type = tls_flag;
loc = (struct mips_got_entry **)
htab_find_slot (g->got_entries, &entry, INSERT);
if (*loc)
- return TRUE;
+ {
+ if (tls_flag == GOT_TLS_GD && !((*loc)->tls_type & GOT_TLS_GD))
+ {
+ g->tls_gotno += 2;
+ (*loc)->tls_type |= tls_flag;
+ }
+ else if (tls_flag == GOT_TLS_IE && !((*loc)->tls_type & GOT_TLS_IE))
+ {
+ g->tls_gotno += 1;
+ (*loc)->tls_type |= tls_flag;
+ }
+ return TRUE;
+ }
- entry.gotidx = g->local_gotno++;
+ if (tls_flag != 0)
+ {
+ entry.gotidx = -1;
+ entry.tls_type = tls_flag;
+ if (tls_flag == GOT_TLS_IE)
+ g->tls_gotno += 1;
+ else if (tls_flag == GOT_TLS_GD)
+ g->tls_gotno += 2;
+ else if (g->tls_ldm_offset == MINUS_ONE)
+ {
+ g->tls_ldm_offset = MINUS_TWO;
+ g->tls_gotno += 2;
+ }
+ }
+ else
+ {
+ entry.gotidx = g->local_gotno++;
+ entry.tls_type = 0;
+ }
*loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
g->global_gotno = 0;
g->local_gotno = 0;
g->assigned_gotno = -1;
+ g->tls_gotno = 0;
+ g->tls_assigned_gotno = 0;
+ g->tls_ldm_offset = MINUS_ONE;
g->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
mips_elf_multi_got_entry_eq, NULL);
if (g->got_entries == NULL)
*entryp = entry;
- if (entry->symndx >= 0 || entry->d.h->forced_local)
+ if (entry->tls_type)
+ {
+ if (entry->tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
+ g->tls_gotno += 2;
+ if (entry->tls_type & GOT_TLS_IE)
+ g->tls_gotno += 1;
+ }
+ else if (entry->symndx >= 0 || entry->d.h->forced_local)
++g->local_gotno;
else
++g->global_gotno;
struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
unsigned int lcount = bfd2got->g->local_gotno;
unsigned int gcount = bfd2got->g->global_gotno;
+ unsigned int tcount = bfd2got->g->tls_gotno;
unsigned int maxcnt = arg->max_count;
+ bfd_boolean too_many_for_tls = FALSE;
+
+ /* We place TLS GOT entries after both locals and globals. The globals
+ for the primary GOT may overflow the normal GOT size limit, so be
+ sure not to merge a GOT which requires TLS with the primary GOT in that
+ case. This doesn't affect non-primary GOTs. */
+ if (tcount > 0)
+ {
+ unsigned int primary_total = lcount + tcount + arg->global_count;
+ if (primary_total * MIPS_ELF_GOT_SIZE (bfd2got->bfd)
+ >= MIPS_ELF_GOT_MAX_SIZE (bfd2got->bfd))
+ too_many_for_tls = TRUE;
+ }
/* If we don't have a primary GOT and this is not too big, use it as
a starting point for the primary GOT. */
- if (! arg->primary && lcount + gcount <= maxcnt)
+ if (! arg->primary && lcount + gcount + tcount <= maxcnt
+ && ! too_many_for_tls)
{
arg->primary = bfd2got->g;
arg->primary_count = lcount + gcount;
/* If it looks like we can merge this bfd's entries with those of
the primary, merge them. The heuristics is conservative, but we
don't have to squeeze it too hard. */
- else if (arg->primary
- && (arg->primary_count + lcount + gcount) <= maxcnt)
+ else if (arg->primary && ! too_many_for_tls
+ && (arg->primary_count + lcount + gcount + tcount) <= maxcnt)
{
struct mips_got_info *g = bfd2got->g;
int old_lcount = arg->primary->local_gotno;
int old_gcount = arg->primary->global_gotno;
+ int old_tcount = arg->primary->tls_gotno;
bfd2got->g = arg->primary;
BFD_ASSERT (old_lcount + lcount >= arg->primary->local_gotno);
BFD_ASSERT (old_gcount + gcount >= arg->primary->global_gotno);
+ BFD_ASSERT (old_tcount + tcount >= arg->primary->tls_gotno);
arg->primary_count = arg->primary->local_gotno
- + arg->primary->global_gotno;
+ + arg->primary->global_gotno + arg->primary->tls_gotno;
}
/* If we can merge with the last-created got, do it. */
else if (arg->current
- && arg->current_count + lcount + gcount <= maxcnt)
+ && arg->current_count + lcount + gcount + tcount <= maxcnt)
{
struct mips_got_info *g = bfd2got->g;
int old_lcount = arg->current->local_gotno;
int old_gcount = arg->current->global_gotno;
+ int old_tcount = arg->current->tls_gotno;
bfd2got->g = arg->current;
BFD_ASSERT (old_lcount + lcount >= arg->current->local_gotno);
BFD_ASSERT (old_gcount + gcount >= arg->current->global_gotno);
+ BFD_ASSERT (old_tcount + tcount >= arg->current->tls_gotno);
arg->current_count = arg->current->local_gotno
- + arg->current->global_gotno;
+ + arg->current->global_gotno + arg->current->tls_gotno;
}
/* Well, we couldn't merge, so create a new GOT. Don't check if it
fits; if it turns out that it doesn't, we'll get relocation
bfd2got->g->next = arg->current;
arg->current = bfd2got->g;
- arg->current_count = lcount + gcount;
+ arg->current_count = lcount + gcount + 2 * tcount;
+ }
+
+ return 1;
+}
+
+/* Set the TLS GOT index for the GOT entry in ENTRYP. */
+
+static int
+mips_elf_initialize_tls_index (void **entryp, void *p)
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ struct mips_got_info *g = p;
+
+ /* We're only interested in TLS symbols. */
+ if (entry->tls_type == 0)
+ return 1;
+
+ if (entry->symndx == -1)
+ {
+ /* There may be multiple mips_got_entry structs for a global variable
+ if there is just one GOT. Just do this once. */
+ if (g->next == NULL)
+ {
+ if (entry->d.h->tls_type & GOT_TLS_OFFSET_DONE)
+ return 1;
+ entry->d.h->tls_type |= GOT_TLS_OFFSET_DONE;
+ }
}
+ else if (entry->tls_type & GOT_TLS_LDM)
+ {
+ /* Similarly, there may be multiple structs for the LDM entry. */
+ if (g->tls_ldm_offset != MINUS_TWO && g->tls_ldm_offset != MINUS_ONE)
+ {
+ entry->gotidx = g->tls_ldm_offset;
+ return 1;
+ }
+ }
+
+ /* Initialize the GOT offset. */
+ entry->gotidx = MIPS_ELF_GOT_SIZE (entry->abfd) * (long) g->tls_assigned_gotno;
+ if (g->next == NULL && entry->symndx == -1)
+ entry->d.h->tls_got_offset = entry->gotidx;
+
+ if (entry->tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
+ g->tls_assigned_gotno += 2;
+ if (entry->tls_type & GOT_TLS_IE)
+ g->tls_assigned_gotno += 1;
+
+ if (entry->tls_type & GOT_TLS_LDM)
+ g->tls_ldm_offset = entry->gotidx;
return 1;
}
= (struct mips_elf_set_global_got_offset_arg *)p;
struct mips_got_info *g = arg->g;
+ if (g && entry->tls_type != GOT_NORMAL)
+ arg->needed_relocs +=
+ mips_tls_got_relocs (arg->info, entry->tls_type,
+ entry->symndx == -1 ? &entry->d.h->root : NULL);
+
if (entry->abfd != NULL && entry->symndx == -1
- && entry->d.h->root.dynindx != -1)
+ && entry->d.h->root.dynindx != -1
+ && entry->d.h->tls_type == GOT_NORMAL)
{
if (g)
{
entry->gotidx = arg->value * (long) g->assigned_gotno++;
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)))
+ && entry->d.h->root.def_dynamic
+ && !entry->d.h->root.def_regular))
++arg->needed_relocs;
}
else
g = g->next;
- return (g->local_gotno + g->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+ return (g->local_gotno + g->global_gotno + g->tls_gotno)
+ * MIPS_ELF_GOT_SIZE (abfd);
}
/* Turn a single GOT that is too big for 16-bit addressing into
/* 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.max_count = ((MIPS_ELF_GOT_MAX_SIZE (abfd)
/ MIPS_ELF_GOT_SIZE (abfd))
- MIPS_RESERVED_GOTNO - pages);
+ /* The number of globals that will be included in the primary GOT.
+ See the calls to mips_elf_set_global_got_offset below for more
+ information. */
+ got_per_bfd_arg.global_count = g->global_gotno;
/* Try to merge the GOTs of input bfds together, as long as they
don't seem to exceed the maximum GOT size, choosing one of them
if (got_per_bfd_arg.obfd == NULL)
return FALSE;
- /* If we find any suitable primary GOT, create an empty one. */
+ /* If we do not find any suitable primary GOT, create an empty one. */
if (got_per_bfd_arg.primary == NULL)
{
g->next = (struct mips_got_info *)
g->next->global_gotsym = NULL;
g->next->global_gotno = 0;
g->next->local_gotno = 0;
+ g->next->tls_gotno = 0;
g->next->assigned_gotno = 0;
+ g->next->tls_assigned_gotno = 0;
+ g->next->tls_ldm_offset = MINUS_ONE;
g->next->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
mips_elf_multi_got_entry_eq,
NULL);
points back to the master GOT. */
gg->local_gotno = -g->global_gotno;
gg->global_gotno = g->global_gotno;
+ gg->tls_gotno = 0;
assign = 0;
gg->next = gg;
assign += MIPS_RESERVED_GOTNO;
g->assigned_gotno = assign;
g->local_gotno += assign + pages;
- assign = g->local_gotno + g->global_gotno;
+ assign = g->local_gotno + g->global_gotno + g->tls_gotno;
+
+ /* Set up any TLS entries. We always place the TLS entries after
+ all non-TLS entries. */
+ g->tls_assigned_gotno = g->local_gotno + g->global_gotno;
+ htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
/* Take g out of the direct list, and push it onto the reversed
list that gg points to. */
while (g);
got->size = (gg->next->local_gotno
- + gg->next->global_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+ + gg->next->global_gotno
+ + gg->next->tls_gotno) * MIPS_ELF_GOT_SIZE (abfd);
return TRUE;
}
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 ((h->root.elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
+ if (h->root.forced_local)
return TRUE;
}
flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED
| SEC_READONLY);
- s = bfd_make_section (abfd, ".compact_rel");
+ s = bfd_make_section_with_flags (abfd, ".compact_rel", flags);
if (s == NULL
- || ! bfd_set_section_flags (abfd, s, flags)
|| ! bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return FALSE;
/* We have to use an alignment of 2**4 here because this is hardcoded
in the function stub generation and in the linker script. */
- s = bfd_make_section (abfd, ".got");
+ s = bfd_make_section_with_flags (abfd, ".got", flags);
if (s == NULL
- || ! bfd_set_section_flags (abfd, s, flags)
|| ! bfd_set_section_alignment (abfd, s, 4))
return FALSE;
return FALSE;
h = (struct elf_link_hash_entry *) bh;
- h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
- h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->non_elf = 0;
+ h->def_regular = 1;
h->type = STT_OBJECT;
if (info->shared
return FALSE;
g->global_gotsym = NULL;
g->global_gotno = 0;
+ g->tls_gotno = 0;
g->local_gotno = MIPS_RESERVED_GOTNO;
g->assigned_gotno = MIPS_RESERVED_GOTNO;
g->bfd2got = NULL;
g->next = NULL;
+ g->tls_ldm_offset = MINUS_ONE;
g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
mips_elf_got_entry_eq, NULL);
if (g->got_entries == NULL)
bfd_boolean local_p, was_local_p;
/* TRUE if the symbol referred to by this relocation is "_gp_disp". */
bfd_boolean gp_disp_p = FALSE;
+ /* TRUE if the symbol referred to by this relocation is
+ "__gnu_local_gp". */
+ bfd_boolean gnu_local_gp_p = FALSE;
Elf_Internal_Shdr *symtab_hdr;
size_t extsymoff;
unsigned long r_symndx;
{
/* Relocations against _gp_disp are permitted only with
R_MIPS_HI16 and R_MIPS_LO16 relocations. */
- if (r_type != R_MIPS_HI16 && r_type != R_MIPS_LO16)
+ if (r_type != R_MIPS_HI16 && r_type != R_MIPS_LO16
+ && r_type != R_MIPS16_HI16 && r_type != R_MIPS16_LO16)
return bfd_reloc_notsupported;
gp_disp_p = TRUE;
}
+ /* See if this is the special _gp symbol. Note that such a
+ symbol must always be a global symbol. */
+ else if (strcmp (*namep, "__gnu_local_gp") == 0)
+ gnu_local_gp_p = TRUE;
+
+
/* If this symbol is defined, calculate its address. Note that
_gp_disp is a magic symbol, always implicitly defined by the
linker, so it's inappropriate to check to see whether or not
else if (info->unresolved_syms_in_objects == RM_IGNORE
&& ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT)
symbol = 0;
- else if (strcmp (*namep, "_DYNAMIC_LINK") == 0 ||
- strcmp (*namep, "_DYNAMIC_LINKING") == 0)
+ else if (strcmp (*namep, SGI_COMPAT (input_bfd)
+ ? "_DYNAMIC_LINK" : "_DYNAMIC_LINKING") == 0)
{
/* If this is a dynamic link, we should have created a
_DYNAMIC_LINK symbol or _DYNAMIC_LINKING(for normal mips) symbol
case R_MIPS_CALL_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_CALL_LO16:
+ case R_MIPS_TLS_GD:
+ case R_MIPS_TLS_GOTTPREL:
+ case R_MIPS_TLS_LDM:
/* Find the index into the GOT where this value is located. */
- if (!local_p)
+ if (r_type == R_MIPS_TLS_LDM)
+ {
+ g = mips_elf_local_got_index (abfd, input_bfd, info, 0, 0, NULL,
+ r_type);
+ if (g == MINUS_ONE)
+ return bfd_reloc_outofrange;
+ }
+ else if (!local_p)
{
/* GOT_PAGE may take a non-zero addend, that is ignored in a
GOT_PAGE relocation that decays to GOT_DISP because the
BFD_ASSERT (addend == 0 || r_type == R_MIPS_GOT_PAGE);
g = mips_elf_global_got_index (elf_hash_table (info)->dynobj,
input_bfd,
- (struct elf_link_hash_entry *) h);
- if (! elf_hash_table(info)->dynamic_sections_created
- || (info->shared
- && (info->symbolic || h->root.dynindx == -1)
- && (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
+ (struct elf_link_hash_entry *) h,
+ r_type, info);
+ if (h->tls_type == GOT_NORMAL
+ && (! elf_hash_table(info)->dynamic_sections_created
+ || (info->shared
+ && (info->symbolic || h->root.dynindx == -1)
+ && h->root.def_regular)))
{
/* This is a static link or a -Bsymbolic link. The
symbol is defined locally, or was forced to be local.
else
{
g = mips_elf_local_got_index (abfd, input_bfd,
- info, symbol + addend);
+ info, symbol + addend, r_symndx, h,
+ r_type);
if (g == MINUS_ONE)
return bfd_reloc_outofrange;
}
case R_MIPS_HI16:
case R_MIPS_LO16:
- case R_MIPS16_GPREL:
case R_MIPS_GPREL16:
case R_MIPS_GPREL32:
case R_MIPS_LITERAL:
+ case R_MIPS16_HI16:
+ case R_MIPS16_LO16:
+ case R_MIPS16_GPREL:
gp0 = _bfd_get_gp_value (input_bfd);
gp = _bfd_get_gp_value (abfd);
if (elf_hash_table (info)->dynobj)
break;
}
+ if (gnu_local_gp_p)
+ symbol = gp;
+
/* Figure out what kind of relocation is being performed. */
switch (r_type)
{
if ((info->shared
|| (elf_hash_table (info)->dynamic_sections_created
&& h != NULL
- && ((h->root.elf_link_hash_flags
- & ELF_LINK_HASH_DEF_DYNAMIC) != 0)
- && ((h->root.elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) == 0)))
+ && h->root.def_dynamic
+ && !h->root.def_regular))
&& r_symndx != 0
&& (input_section->flags & SEC_ALLOC) != 0)
{
if (local_p)
value = ((addend | ((p + 4) & 0xf0000000)) + symbol) >> 2;
else
- value = (_bfd_mips_elf_sign_extend (addend, 28) + symbol) >> 2;
+ {
+ value = (_bfd_mips_elf_sign_extend (addend, 28) + symbol) >> 2;
+ if (h->root.root.type != bfd_link_hash_undefweak)
+ overflowed_p = (value >> 26) != ((p + 4) >> 28);
+ }
value &= howto->dst_mask;
break;
+ case R_MIPS_TLS_DTPREL_HI16:
+ value = (mips_elf_high (addend + symbol - dtprel_base (info))
+ & howto->dst_mask);
+ break;
+
+ case R_MIPS_TLS_DTPREL_LO16:
+ value = (symbol + addend - dtprel_base (info)) & howto->dst_mask;
+ break;
+
+ case R_MIPS_TLS_TPREL_HI16:
+ value = (mips_elf_high (addend + symbol - tprel_base (info))
+ & howto->dst_mask);
+ break;
+
+ case R_MIPS_TLS_TPREL_LO16:
+ value = (symbol + addend - tprel_base (info)) & howto->dst_mask;
+ break;
+
case R_MIPS_HI16:
+ case R_MIPS16_HI16:
if (!gp_disp_p)
{
value = mips_elf_high (addend + symbol);
}
else
{
- value = mips_elf_high (addend + gp - p);
+ /* For MIPS16 ABI code we generate this sequence
+ 0: li $v0,%hi(_gp_disp)
+ 4: addiupc $v1,%lo(_gp_disp)
+ 8: sll $v0,16
+ 12: addu $v0,$v1
+ 14: move $gp,$v0
+ So the offsets of hi and lo relocs are the same, but the
+ $pc is four higher than $t9 would be, so reduce
+ both reloc addends by 4. */
+ if (r_type == R_MIPS16_HI16)
+ value = mips_elf_high (addend + gp - p - 4);
+ else
+ value = mips_elf_high (addend + gp - p);
overflowed_p = mips_elf_overflow_p (value, 16);
}
break;
case R_MIPS_LO16:
+ case R_MIPS16_LO16:
if (!gp_disp_p)
value = (symbol + addend) & howto->dst_mask;
else
{
- value = addend + gp - p + 4;
+ /* See the comment for R_MIPS16_HI16 above for the reason
+ for this conditional. */
+ if (r_type == R_MIPS16_LO16)
+ value = addend + gp - p;
+ else
+ value = addend + gp - p + 4;
/* The MIPS ABI requires checking the R_MIPS_LO16 relocation
for overflow. But, on, say, IRIX5, relocations against
_gp_disp are normally generated from the .cpload
/* Fall through. */
+ case R_MIPS_TLS_GD:
+ case R_MIPS_TLS_GOTTPREL:
+ case R_MIPS_TLS_LDM:
case R_MIPS_GOT_DISP:
got_disp:
value = g;
value &= howto->dst_mask;
break;
- case R_MIPS_PJUMP:
case R_MIPS_JALR:
- /* Both of these may be ignored. R_MIPS_JALR is an optimization
- hint; we could improve performance by honoring that hint. */
- return bfd_reloc_continue;
+ /* This relocation is only a hint. In some cases, we optimize
+ it into a bal instruction. But we don't try to optimize
+ branches to the PLT; that will wind up wasting time. */
+ if (h != NULL && h->root.plt.offset != (bfd_vma) -1)
+ return bfd_reloc_continue;
+ value = symbol + addend;
+ break;
+ case R_MIPS_PJUMP:
case R_MIPS_GNU_VTINHERIT:
case R_MIPS_GNU_VTENTRY:
/* We don't do anything with these at present. */
/* Obtain the bytes. */
x = bfd_get ((8 * bfd_get_reloc_size (howto)), input_bfd, location);
- if ((ELF_R_TYPE (input_bfd, relocation->r_info) == R_MIPS16_26
- || ELF_R_TYPE (input_bfd, relocation->r_info) == R_MIPS16_GPREL)
- && bfd_little_endian (input_bfd))
- /* The two 16-bit words will be reversed on a little-endian system.
- See mips_elf_perform_relocation for more details. */
- x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
-
return x;
}
/* Figure out where the relocation is occurring. */
location = contents + relocation->r_offset;
+ _bfd_mips16_elf_reloc_unshuffle (input_bfd, r_type, FALSE, location);
+
/* Obtain the current value. */
x = mips_elf_obtain_contents (howto, relocation, input_bfd, contents);
/* Clear the field we are setting. */
x &= ~howto->dst_mask;
- /* If this is the R_MIPS16_26 relocation, we must store the
- value in a funny way. */
- if (r_type == R_MIPS16_26)
- {
- /* R_MIPS16_26 is used for the mips16 jal and jalx instructions.
- Most mips16 instructions are 16 bits, but these instructions
- are 32 bits.
-
- The format of these instructions is:
-
- +--------------+--------------------------------+
- ! JALX ! X! Imm 20:16 ! Imm 25:21 !
- +--------------+--------------------------------+
- ! Immediate 15:0 !
- +-----------------------------------------------+
-
- JALX is the 5-bit value 00011. X is 0 for jal, 1 for jalx.
- Note that the immediate value in the first word is swapped.
-
- When producing a relocatable object file, R_MIPS16_26 is
- handled mostly like R_MIPS_26. In particular, the addend is
- stored as a straight 26-bit value in a 32-bit instruction.
- (gas makes life simpler for itself by never adjusting a
- R_MIPS16_26 reloc to be against a section, so the addend is
- always zero). However, the 32 bit instruction is stored as 2
- 16-bit values, rather than a single 32-bit value. In a
- big-endian file, the result is the same; in a little-endian
- file, the two 16-bit halves of the 32 bit value are swapped.
- This is so that a disassembler can recognize the jal
- instruction.
-
- When doing a final link, R_MIPS16_26 is treated as a 32 bit
- instruction stored as two 16-bit values. The addend A is the
- contents of the targ26 field. The calculation is the same as
- R_MIPS_26. When storing the calculated value, reorder the
- immediate value as shown above, and don't forget to store the
- value as two 16-bit values.
-
- To put it in MIPS ABI terms, the relocation field is T-targ26-16,
- defined as
-
- big-endian:
- +--------+----------------------+
- | | |
- | | targ26-16 |
- |31 26|25 0|
- +--------+----------------------+
-
- little-endian:
- +----------+------+-------------+
- | | | |
- | sub1 | | sub2 |
- |0 9|10 15|16 31|
- +----------+--------------------+
- where targ26-16 is sub1 followed by sub2 (i.e., the addend field A is
- ((sub1 << 16) | sub2)).
-
- When producing a relocatable object file, the calculation is
- (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
- When producing a fully linked file, the calculation is
- let R = (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
- ((R & 0x1f0000) << 5) | ((R & 0x3e00000) >> 5) | (R & 0xffff) */
-
- if (!info->relocatable)
- /* Shuffle the bits according to the formula above. */
- value = (((value & 0x1f0000) << 5)
- | ((value & 0x3e00000) >> 5)
- | (value & 0xffff));
- }
- else if (r_type == R_MIPS16_GPREL)
- {
- /* R_MIPS16_GPREL is used for GP-relative addressing in mips16
- mode. A typical instruction will have a format like this:
-
- +--------------+--------------------------------+
- ! EXTEND ! Imm 10:5 ! Imm 15:11 !
- +--------------+--------------------------------+
- ! Major ! rx ! ry ! Imm 4:0 !
- +--------------+--------------------------------+
-
- EXTEND is the five bit value 11110. Major is the instruction
- opcode.
-
- This is handled exactly like R_MIPS_GPREL16, except that the
- addend is retrieved and stored as shown in this diagram; that
- is, the Imm fields above replace the V-rel16 field.
-
- All we need to do here is shuffle the bits appropriately. As
- above, the two 16-bit halves must be swapped on a
- little-endian system. */
- value = (((value & 0x7e0) << 16)
- | ((value & 0xf800) << 5)
- | (value & 0x1f));
- }
-
/* Set the field. */
x |= (value & howto->dst_mask);
if (!ok)
{
(*_bfd_error_handler)
- (_("%s: %s+0x%lx: jump to stub routine which is not jal"),
- bfd_archive_filename (input_bfd),
- input_section->name,
+ (_("%B: %A+0x%lx: jump to stub routine which is not jal"),
+ input_bfd,
+ input_section,
(unsigned long) relocation->r_offset);
bfd_set_error (bfd_error_bad_value);
return FALSE;
x = (x & ~(0x3f << 26)) | (jalx_opcode << 26);
}
- /* Swap the high- and low-order 16 bits on little-endian systems
- when doing a MIPS16 relocation. */
- if ((r_type == R_MIPS16_GPREL || r_type == R_MIPS16_26)
- && bfd_little_endian (input_bfd))
- x = (((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16));
+ /* On the RM9000, bal is faster than jal, because bal uses branch
+ prediction hardware. If we are linking for the RM9000, and we
+ see jal, and bal fits, use it instead. Note that this
+ transformation should be safe for all architectures. */
+ if (bfd_get_mach (input_bfd) == bfd_mach_mips9000
+ && !info->relocatable
+ && !require_jalx
+ && ((r_type == R_MIPS_26 && (x >> 26) == 0x3) /* jal addr */
+ || (r_type == R_MIPS_JALR && x == 0x0320f809))) /* jalr t9 */
+ {
+ bfd_vma addr;
+ bfd_vma dest;
+ bfd_signed_vma off;
+
+ addr = (input_section->output_section->vma
+ + input_section->output_offset
+ + relocation->r_offset
+ + 4);
+ if (r_type == R_MIPS_26)
+ dest = (value << 2) | ((addr >> 28) << 28);
+ else
+ dest = value;
+ off = dest - addr;
+ if (off <= 0x1ffff && off >= -0x20000)
+ x = 0x04110000 | (((bfd_vma) off >> 2) & 0xffff); /* bal addr */
+ }
/* Put the value into the output. */
bfd_put (8 * bfd_get_reloc_size (howto), input_bfd, x, location);
+
+ _bfd_mips16_elf_reloc_shuffle(input_bfd, r_type, !info->relocatable,
+ location);
+
return TRUE;
}
bfd_vma *addendp, asection *input_section)
{
Elf_Internal_Rela outrel[3];
- bfd_boolean skip;
asection *sreloc;
bfd *dynobj;
int r_type;
+ long indx;
+ bfd_boolean defined_p;
r_type = ELF_R_TYPE (output_bfd, rel->r_info);
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (sreloc->reloc_count * MIPS_ELF_REL_SIZE (output_bfd)
< sreloc->size);
- skip = FALSE;
outrel[0].r_offset =
_bfd_elf_section_offset (output_bfd, info, input_section, rel[0].r_offset);
outrel[1].r_offset =
outrel[2].r_offset =
_bfd_elf_section_offset (output_bfd, info, input_section, rel[2].r_offset);
-#if 0
- /* We begin by assuming that the offset for the dynamic relocation
- is the same as for the original relocation. We'll adjust this
- later to reflect the correct output offsets. */
- if (input_section->sec_info_type != ELF_INFO_TYPE_STABS)
- {
- outrel[1].r_offset = rel[1].r_offset;
- outrel[2].r_offset = rel[2].r_offset;
- }
- else
- {
- /* Except that in a stab section things are more complex.
- Because we compress stab information, the offset given in the
- relocation may not be the one we want; we must let the stabs
- machinery tell us the offset. */
- outrel[1].r_offset = outrel[0].r_offset;
- outrel[2].r_offset = outrel[0].r_offset;
- /* If we didn't need the relocation at all, this value will be
- -1. */
- if (outrel[0].r_offset == MINUS_ONE)
- skip = TRUE;
- }
-#endif
-
if (outrel[0].r_offset == MINUS_ONE)
/* The relocation field has been deleted. */
- skip = TRUE;
- else if (outrel[0].r_offset == MINUS_TWO)
+ return TRUE;
+
+ if (outrel[0].r_offset == MINUS_TWO)
{
/* The relocation field has been converted into a relative value of
some sort. Functions like _bfd_elf_write_section_eh_frame expect
the field to be fully relocated, so add in the symbol's value. */
- skip = TRUE;
*addendp += symbol;
+ return TRUE;
}
- /* 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
- is a way of setting R_TYPE to R_MIPS_NONE. */
- if (skip)
- memset (outrel, 0, sizeof (Elf_Internal_Rela) * 3);
+ /* We must now calculate the dynamic symbol table index to use
+ in the relocation. */
+ if (h != NULL
+ && (! info->symbolic || !h->root.def_regular)
+ /* h->root.dynindx may be -1 if this symbol was marked to
+ become local. */
+ && h->root.dynindx != -1)
+ {
+ indx = h->root.dynindx;
+ if (SGI_COMPAT (output_bfd))
+ defined_p = h->root.def_regular;
+ else
+ /* ??? glibc's ld.so just adds the final GOT entry to the
+ relocation field. It therefore treats relocs against
+ defined symbols in the same way as relocs against
+ undefined symbols. */
+ defined_p = FALSE;
+ }
else
{
- long indx;
- bfd_boolean defined_p;
-
- /* We must now calculate the dynamic symbol table index to use
- in the relocation. */
- if (h != NULL
- && (! info->symbolic || (h->root.elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) == 0)
- /* h->root.dynindx may be -1 if this symbol was marked to
- become local. */
- && h->root.dynindx != -1)
- {
- indx = h->root.dynindx;
- if (SGI_COMPAT (output_bfd))
- defined_p = ((h->root.elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) != 0);
- else
- /* ??? glibc's ld.so just adds the final GOT entry to the
- relocation field. It therefore treats relocs against
- defined symbols in the same way as relocs against
- undefined symbols. */
- defined_p = FALSE;
+ if (sec != NULL && bfd_is_abs_section (sec))
+ indx = 0;
+ else if (sec == NULL || sec->owner == NULL)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
}
else
{
- if (sec != NULL && bfd_is_abs_section (sec))
- indx = 0;
- else if (sec == NULL || sec->owner == NULL)
- {
- bfd_set_error (bfd_error_bad_value);
- return FALSE;
- }
- else
- {
- indx = elf_section_data (sec->output_section)->dynindx;
- if (indx == 0)
- abort ();
- }
+ indx = elf_section_data (sec->output_section)->dynindx;
+ if (indx == 0)
+ abort ();
+ }
- /* 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. */
- /* ??? Although this behavior is compatible with glibc's ld.so,
- the ABI says that relocations against STN_UNDEF should have
- a symbol value of 0. Irix rld honors this, so relocations
- against STN_UNDEF have no effect. */
- if (!SGI_COMPAT (output_bfd))
- indx = 0;
- defined_p = TRUE;
- }
-
- /* If the relocation was previously an absolute relocation and
- this symbol will not be referred to by the relocation, we must
- adjust it by the value we give it in the dynamic symbol table.
- Otherwise leave the job up to the dynamic linker. */
- if (defined_p && r_type != R_MIPS_REL32)
- *addendp += symbol;
-
- /* The relocation is always an REL32 relocation because we don't
- 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, 0,
- ABI_64_P (output_bfd)
- ? R_MIPS_64
- : R_MIPS_NONE);
- outrel[2].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_NONE);
-
- /* Adjust the output offset of the relocation to reference the
- correct location in the output file. */
- outrel[0].r_offset += (input_section->output_section->vma
- + input_section->output_offset);
- outrel[1].r_offset += (input_section->output_section->vma
- + input_section->output_offset);
- outrel[2].r_offset += (input_section->output_section->vma
- + input_section->output_offset);
- }
+ /* 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. */
+ /* ??? Although this behavior is compatible with glibc's ld.so,
+ the ABI says that relocations against STN_UNDEF should have
+ a symbol value of 0. Irix rld honors this, so relocations
+ against STN_UNDEF have no effect. */
+ if (!SGI_COMPAT (output_bfd))
+ indx = 0;
+ defined_p = TRUE;
+ }
+
+ /* If the relocation was previously an absolute relocation and
+ this symbol will not be referred to by the relocation, we must
+ adjust it by the value we give it in the dynamic symbol table.
+ Otherwise leave the job up to the dynamic linker. */
+ if (defined_p && r_type != R_MIPS_REL32)
+ *addendp += symbol;
+
+ /* The relocation is always an REL32 relocation because we don't
+ 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, 0,
+ ABI_64_P (output_bfd)
+ ? R_MIPS_64
+ : R_MIPS_NONE);
+ outrel[2].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_NONE);
+
+ /* Adjust the output offset of the relocation to reference the
+ correct location in the output file. */
+ outrel[0].r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+ outrel[1].r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+ outrel[2].r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
/* Put the relocation back out. We have to use the special
relocation outputter in the 64-bit case since the 64-bit
|= SHF_WRITE;
/* On IRIX5, make an entry of compact relocation info. */
- if (! skip && IRIX_COMPAT (output_bfd) == ict_irix5)
+ if (IRIX_COMPAT (output_bfd) == ict_irix5)
{
asection *scpt = bfd_get_section_by_name (dynobj, ".compact_rel");
bfd_byte *cr;
cr = (scpt->contents
+ sizeof (Elf32_External_compact_rel));
+ mips_elf_set_cr_relvaddr (cptrel, 0);
bfd_elf32_swap_crinfo_out (output_bfd, &cptrel,
((Elf32_External_crinfo *) cr
+ scpt->reloc_count));
case E_MIPS_MACH_5500:
return bfd_mach_mips5500;
+ case E_MIPS_MACH_9000:
+ return bfd_mach_mips9000;
+
case E_MIPS_MACH_SB1:
return bfd_mach_mips_sb1;
}
}
\f
+/* Implement elf_backend_eh_frame_address_size. This differs from
+ the default in the way it handles EABI64.
+
+ EABI64 was originally specified as an LP64 ABI, and that is what
+ -mabi=eabi normally gives on a 64-bit target. However, gcc has
+ historically accepted the combination of -mabi=eabi and -mlong32,
+ and this ILP32 variation has become semi-official over time.
+ Both forms use elf32 and have pointer-sized FDE addresses.
+
+ If an EABI object was generated by GCC 4.0 or above, it will have
+ an empty .gcc_compiled_longXX section, where XX is the size of longs
+ in bits. Unfortunately, ILP32 objects generated by earlier compilers
+ have no special marking to distinguish them from LP64 objects.
+
+ We don't want users of the official LP64 ABI to be punished for the
+ existence of the ILP32 variant, but at the same time, we don't want
+ to mistakenly interpret pre-4.0 ILP32 objects as being LP64 objects.
+ We therefore take the following approach:
+
+ - If ABFD contains a .gcc_compiled_longXX section, use it to
+ determine the pointer size.
+
+ - Otherwise check the type of the first relocation. Assume that
+ the LP64 ABI is being used if the relocation is of type R_MIPS_64.
+
+ - Otherwise punt.
+
+ The second check is enough to detect LP64 objects generated by pre-4.0
+ compilers because, in the kind of output generated by those compilers,
+ the first relocation will be associated with either a CIE personality
+ routine or an FDE start address. Furthermore, the compilers never
+ used a special (non-pointer) encoding for this ABI.
+
+ Checking the relocation type should also be safe because there is no
+ reason to use R_MIPS_64 in an ILP32 object. Pre-4.0 compilers never
+ did so. */
+
+unsigned int
+_bfd_mips_elf_eh_frame_address_size (bfd *abfd, asection *sec)
+{
+ if (elf_elfheader (abfd)->e_ident[EI_CLASS] == ELFCLASS64)
+ return 8;
+ if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI64)
+ {
+ bfd_boolean long32_p, long64_p;
+
+ long32_p = bfd_get_section_by_name (abfd, ".gcc_compiled_long32") != 0;
+ long64_p = bfd_get_section_by_name (abfd, ".gcc_compiled_long64") != 0;
+ if (long32_p && long64_p)
+ return 0;
+ if (long32_p)
+ return 4;
+ if (long64_p)
+ return 8;
+
+ if (sec->reloc_count > 0
+ && elf_section_data (sec)->relocs != NULL
+ && (ELF32_R_TYPE (elf_section_data (sec)->relocs[0].r_info)
+ == R_MIPS_64))
+ return 8;
+
+ return 0;
+ }
+ return 4;
+}
+\f
/* There appears to be a bug in the MIPSpro linker that causes GOT_DISP
relocations against two unnamed section symbols to resolve to the
same address. For example, if we have code like:
how to. */
bfd_boolean
-_bfd_mips_elf_section_from_shdr (bfd *abfd, Elf_Internal_Shdr *hdr,
- const char *name)
+_bfd_mips_elf_section_from_shdr (bfd *abfd,
+ Elf_Internal_Shdr *hdr,
+ const char *name,
+ int shindex)
{
flagword flags = 0;
return FALSE;
break;
case SHT_MIPS_OPTIONS:
- if (strcmp (name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) != 0)
+ if (!MIPS_ELF_OPTIONS_SECTION_NAME_P (name))
return FALSE;
break;
case SHT_MIPS_DWARF:
return FALSE;
break;
default:
- return FALSE;
+ break;
}
- if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name))
+ if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
return FALSE;
if (flags)
hdr->sh_flags |= SHF_MIPS_NOSTRIP;
/* The sh_info field is set in final_write_processing. */
}
- else if (strcmp (name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) == 0)
+ else if (MIPS_ELF_OPTIONS_SECTION_NAME_P (name))
{
hdr->sh_type = SHT_MIPS_OPTIONS;
hdr->sh_entsize = 1;
return FALSE;
h = (struct elf_link_hash_entry *) bh;
- h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
- h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->non_elf = 0;
+ h->def_regular = 1;
h->type = STT_OBJECT;
if (! bfd_elf_link_record_dynamic_symbol (info, h))
if (bfd_get_section_by_name (abfd,
MIPS_ELF_STUB_SECTION_NAME (abfd)) == NULL)
{
- s = bfd_make_section (abfd, MIPS_ELF_STUB_SECTION_NAME (abfd));
+ s = bfd_make_section_with_flags (abfd,
+ MIPS_ELF_STUB_SECTION_NAME (abfd),
+ flags | SEC_CODE);
if (s == NULL
- || ! bfd_set_section_flags (abfd, s, flags | SEC_CODE)
|| ! bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return FALSE;
&& !info->shared
&& bfd_get_section_by_name (abfd, ".rld_map") == NULL)
{
- s = bfd_make_section (abfd, ".rld_map");
+ s = bfd_make_section_with_flags (abfd, ".rld_map",
+ flags &~ (flagword) SEC_READONLY);
if (s == NULL
- || ! bfd_set_section_flags (abfd, s, flags &~ (flagword) SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return FALSE;
return FALSE;
h = (struct elf_link_hash_entry *) bh;
- h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
- h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->non_elf = 0;
+ h->def_regular = 1;
h->type = STT_SECTION;
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
h = (struct elf_link_hash_entry *) bh;
- h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
- h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->non_elf = 0;
+ h->def_regular = 1;
h->type = STT_SECTION;
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
h = (struct elf_link_hash_entry *) bh;
- h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
- h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+ h->non_elf = 0;
+ h->def_regular = 1;
h->type = STT_OBJECT;
if (! bfd_elf_link_record_dynamic_symbol (info, h))
else if (r_symndx >= extsymoff + NUM_SHDR_ENTRIES (symtab_hdr))
{
(*_bfd_error_handler)
- (_("%s: Malformed reloc detected for section %s"),
- bfd_archive_filename (abfd), name);
+ (_("%B: Malformed reloc detected for section %s"),
+ abfd, name);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
case R_MIPS_GOT_PAGE:
case R_MIPS_GOT_OFST:
case R_MIPS_GOT_DISP:
+ case R_MIPS_TLS_GD:
+ case R_MIPS_TLS_LDM:
if (dynobj == NULL)
elf_hash_table (info)->dynobj = dynobj = abfd;
if (! mips_elf_create_got_section (dynobj, info, FALSE))
R_MIPS_CALL_HI16 because these are always followed by an
R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16. */
if (! mips_elf_record_local_got_symbol (abfd, r_symndx,
- rel->r_addend, g))
+ rel->r_addend, g, 0))
return FALSE;
}
if (h == NULL)
{
(*_bfd_error_handler)
- (_("%s: CALL16 reloc at 0x%lx not against global symbol"),
- bfd_archive_filename (abfd), (unsigned long) rel->r_offset);
+ (_("%B: CALL16 reloc at 0x%lx not against global symbol"),
+ abfd, (unsigned long) rel->r_offset);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
if (h != NULL)
{
/* This symbol requires a global offset table entry. */
- if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ if (! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
return FALSE;
/* We need a stub, not a plt entry for the undefined
function. But we record it as if it needs plt. See
_bfd_elf_adjust_dynamic_symbol. */
- h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+ h->needs_plt = 1;
h->type = STT_FUNC;
}
break;
hmips = (struct mips_elf_link_hash_entry *)
hmips->root.root.u.i.link;
- if ((hmips->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)
+ if (hmips->root.def_regular
&& ! (info->shared && ! info->symbolic
- && ! (hmips->root.elf_link_hash_flags
- & ELF_LINK_FORCED_LOCAL)))
+ && ! hmips->root.forced_local))
break;
}
/* Fall through. */
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_GOT_DISP:
- /* This symbol requires a global offset table entry. */
- if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
return FALSE;
break;
+ case R_MIPS_TLS_GOTTPREL:
+ if (info->shared)
+ info->flags |= DF_STATIC_TLS;
+ /* Fall through */
+
+ case R_MIPS_TLS_LDM:
+ if (r_type == R_MIPS_TLS_LDM)
+ {
+ r_symndx = 0;
+ h = NULL;
+ }
+ /* Fall through */
+
+ case R_MIPS_TLS_GD:
+ /* This symbol requires a global offset table entry, or two
+ for TLS GD relocations. */
+ {
+ unsigned char flag = (r_type == R_MIPS_TLS_GD
+ ? GOT_TLS_GD
+ : r_type == R_MIPS_TLS_LDM
+ ? GOT_TLS_LDM
+ : GOT_TLS_IE);
+ if (h != NULL)
+ {
+ struct mips_elf_link_hash_entry *hmips =
+ (struct mips_elf_link_hash_entry *) h;
+ hmips->tls_type |= flag;
+
+ if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g, flag))
+ return FALSE;
+ }
+ else
+ {
+ BFD_ASSERT (flag == GOT_TLS_LDM || r_symndx != 0);
+
+ if (! mips_elf_record_local_got_symbol (abfd, r_symndx,
+ rel->r_addend, g, flag))
+ return FALSE;
+ }
+ }
+ break;
+
case R_MIPS_32:
case R_MIPS_REL32:
case R_MIPS_64:
if (! mips_elf_create_got_section (dynobj, info, TRUE))
return FALSE;
g = mips_elf_got_info (dynobj, &sgot);
- if (! mips_elf_record_global_got_symbol (h, abfd, info, g))
+ if (! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
return FALSE;
}
}
|| 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)))
+ && !h->root.forced_local))
continue;
sym_sec = h->root.root.u.def.section;
/* Make sure we know what is going on here. */
BFD_ASSERT (dynobj != NULL
- && ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT)
- || h->weakdef != NULL
- || ((h->elf_link_hash_flags
- & ELF_LINK_HASH_DEF_DYNAMIC) != 0
- && (h->elf_link_hash_flags
- & ELF_LINK_HASH_REF_REGULAR) != 0
- && (h->elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) == 0)));
+ && (h->needs_plt
+ || h->u.weakdef != NULL
+ || (h->def_dynamic
+ && h->ref_regular
+ && !h->def_regular)));
/* If this symbol is defined in a dynamic object, we need to copy
any R_MIPS_32 or R_MIPS_REL32 relocs against it into the output
if (! info->relocatable
&& hmips->possibly_dynamic_relocs != 0
&& (h->root.type == bfd_link_hash_defweak
- || (h->elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) == 0))
+ || !h->def_regular))
{
mips_elf_allocate_dynamic_relocations (dynobj,
hmips->possibly_dynamic_relocs);
/* For a function, create a stub, if allowed. */
if (! hmips->no_fn_stub
- && (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
+ && h->needs_plt)
{
if (! elf_hash_table (info)->dynamic_sections_created)
return TRUE;
the symbol to the stub location. This is required to make
function pointers compare as equal between the normal
executable and the shared library. */
- if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+ if (!h->def_regular)
{
/* We need .stub section. */
s = bfd_get_section_by_name (dynobj,
}
}
else if ((h->type == STT_FUNC)
- && (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) == 0)
+ && !h->needs_plt)
{
/* This will set the entry for this symbol in the GOT to 0, and
the dynamic linker will take care of this. */
/* If this is a weak symbol, and there is a real definition, the
processor independent code will have arranged for us to see the
real definition first, and we can just use the same value. */
- if (h->weakdef != NULL)
+ if (h->u.weakdef != NULL)
{
- BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
- || h->weakdef->root.type == bfd_link_hash_defweak);
- h->root.u.def.section = h->weakdef->root.u.def.section;
- h->root.u.def.value = h->weakdef->root.u.def.value;
+ BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
+ || h->u.weakdef->root.type == bfd_link_hash_defweak);
+ h->root.u.def.section = h->u.weakdef->root.u.def.section;
+ h->root.u.def.value = h->u.weakdef->root.u.def.value;
return TRUE;
}
bfd_size_type loadable_size = 0;
bfd_size_type local_gotno;
bfd *sub;
+ struct mips_elf_count_tls_arg count_tls_arg;
/* The .reginfo section has a fixed size. */
ri = bfd_get_section_by_name (output_bfd, ".reginfo");
g->global_gotno = i;
s->size += i * MIPS_ELF_GOT_SIZE (output_bfd);
- if (s->size > MIPS_ELF_GOT_MAX_SIZE (output_bfd)
- && ! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
- return FALSE;
+ /* We need to calculate tls_gotno for global symbols at this point
+ instead of building it up earlier, to avoid doublecounting
+ entries for one global symbol from multiple input files. */
+ count_tls_arg.info = info;
+ count_tls_arg.needed = 0;
+ elf_link_hash_traverse (elf_hash_table (info),
+ mips_elf_count_global_tls_entries,
+ &count_tls_arg);
+ g->tls_gotno += count_tls_arg.needed;
+ s->size += g->tls_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
+
+ mips_elf_resolve_final_got_entries (g);
+
+ if (s->size > MIPS_ELF_GOT_MAX_SIZE (output_bfd))
+ {
+ if (! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
+ return FALSE;
+ }
+ else
+ {
+ /* Set up TLS entries for the first GOT. */
+ g->tls_assigned_gotno = g->global_gotno + g->local_gotno;
+ htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
+ }
return TRUE;
}
set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (output_bfd);
set_got_offset_arg.info = info;
+ /* NOTE 2005-02-03: How can this call, or the next, ever
+ find any indirect entries to resolve? They were all
+ resolved in mips_elf_multi_got. */
mips_elf_resolve_final_got_entries (gg);
for (g = gg->next; g && g->next != gg; g = g->next)
{
needed_relocs += g->local_gotno - g->assigned_gotno;
BFD_ASSERT (g->assigned_gotno == g->next->local_gotno
+ g->next->global_gotno
+ + g->next->tls_gotno
+ MIPS_RESERVED_GOTNO);
}
}
+ }
+ else
+ {
+ struct mips_elf_count_tls_arg arg;
+ arg.info = info;
+ arg.needed = 0;
- if (needed_relocs)
- mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs);
+ htab_traverse (gg->got_entries, mips_elf_count_local_tls_relocs,
+ &arg);
+ elf_link_hash_traverse (elf_hash_table (info),
+ mips_elf_count_global_tls_relocs,
+ &arg);
+
+ needed_relocs += arg.needed;
}
+
+ if (needed_relocs)
+ mips_elf_allocate_dynamic_relocations (dynobj, needed_relocs);
}
else if (strcmp (name, MIPS_ELF_STUB_SECTION_NAME (output_bfd)) == 0)
{
if (strip)
{
- _bfd_strip_section_from_output (info, s);
+ s->flags |= SEC_EXCLUDE;
continue;
}
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_FLAGS, 0))
return FALSE;
-#if 0
- /* Time stamps in executable files are a bad idea. */
- if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_TIME_STAMP, 0))
- return FALSE;
-#endif
-
-#if 0 /* FIXME */
- if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_ICHECKSUM, 0))
- return FALSE;
-#endif
-
-#if 0 /* FIXME */
- if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_IVERSION, 0))
- return FALSE;
-#endif
-
if (! MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_BASE_ADDRESS, 0))
return FALSE;
rel_hdr = elf_section_data (input_section)->rel_hdr2;
if (rel_hdr->sh_entsize == MIPS_ELF_REL_SIZE (input_bfd))
{
+ bfd_byte *location = contents + rel->r_offset;
+
/* Note that this is a REL relocation. */
rela_relocation_p = FALSE;
/* Get the addend, which is stored in the input file. */
+ _bfd_mips16_elf_reloc_unshuffle (input_bfd, r_type, FALSE,
+ location);
addend = mips_elf_obtain_contents (howto, rel, input_bfd,
contents);
+ _bfd_mips16_elf_reloc_shuffle(input_bfd, r_type, FALSE,
+ location);
+
addend &= howto->src_mask;
/* For some kinds of relocations, the ADDEND is a
combination of the addend stored in two different
relocations. */
- if (r_type == R_MIPS_HI16
+ if (r_type == R_MIPS_HI16 || r_type == R_MIPS16_HI16
|| (r_type == R_MIPS_GOT16
&& mips_elf_local_relocation_p (input_bfd, rel,
local_sections, FALSE)))
bfd_vma l;
const Elf_Internal_Rela *lo16_relocation;
reloc_howto_type *lo16_howto;
+ bfd_byte *lo16_location;
+ int lo16_type;
+
+ if (r_type == R_MIPS16_HI16)
+ lo16_type = R_MIPS16_LO16;
+ else
+ lo16_type = R_MIPS_LO16;
/* The combined value is the sum of the HI16 addend,
left-shifted by sixteen bits, and the LO16
as one of these. We permit a similar extension
in general, as that is useful for GCC. */
lo16_relocation = mips_elf_next_relocation (input_bfd,
- R_MIPS_LO16,
+ lo16_type,
rel, relend);
if (lo16_relocation == NULL)
return FALSE;
+ lo16_location = contents + lo16_relocation->r_offset;
+
/* Obtain the addend kept there. */
lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd,
- R_MIPS_LO16, FALSE);
+ lo16_type, FALSE);
+ _bfd_mips16_elf_reloc_unshuffle (input_bfd, lo16_type, FALSE,
+ lo16_location);
l = mips_elf_obtain_contents (lo16_howto, lo16_relocation,
input_bfd, contents);
+ _bfd_mips16_elf_reloc_shuffle (input_bfd, lo16_type, FALSE,
+ lo16_location);
l &= lo16_howto->src_mask;
l <<= lo16_howto->rightshift;
l = _bfd_mips_elf_sign_extend (l, 16);
/* Compute the combined addend. */
addend += l;
}
- else if (r_type == R_MIPS16_GPREL)
- {
- /* The addend is scrambled in the object file. See
- mips_elf_perform_relocation for details on the
- format. */
- addend = (((addend & 0x1f0000) >> 5)
- | ((addend & 0x7e00000) >> 16)
- | (addend & 0x1f));
- }
else
addend <<= howto->rightshift;
}
{
BFD_ASSERT (name != NULL);
if (! ((*info->callbacks->reloc_overflow)
- (info, name, howto->name, 0,
+ (info, NULL, name, howto->name, (bfd_vma) 0,
input_bfd, input_section, rel->r_offset)))
return FALSE;
}
}
BFD_ASSERT (h->dynindx != -1
- || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0);
+ || h->forced_local);
sgot = mips_elf_got_section (dynobj, FALSE);
BFD_ASSERT (sgot != NULL);
bfd_vma value;
value = sym->st_value;
- offset = mips_elf_global_got_index (dynobj, output_bfd, h);
+ offset = mips_elf_global_got_index (dynobj, output_bfd, h, R_MIPS_GOT16, info);
MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
}
- if (g->next && h->dynindx != -1)
+ if (g->next && h->dynindx != -1 && h->type != STT_TLS)
{
struct mips_got_entry e, *p;
bfd_vma entry;
e.abfd = output_bfd;
e.symndx = -1;
e.d.h = (struct mips_elf_link_hash_entry *)h;
+ e.tls_type = 0;
for (g = g->next; g->next != gg; g = g->next)
{
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)))
+ && p->d.h->root.def_dynamic
+ && !p->d.h->root.def_regular))
{
/* Create an R_MIPS_REL32 relocation for this entry. Due to
the various compatibility problems, it's easier to mock
for (g = gg->next; g->next != gg; g = g->next)
{
- bfd_vma index = g->next->local_gotno + g->next->global_gotno;
+ bfd_vma index = g->next->local_gotno + g->next->global_gotno
+ + g->next->tls_gotno;
MIPS_ELF_PUT_WORD (output_bfd, 0, sgot->contents
+ index++ * MIPS_ELF_GOT_SIZE (output_bfd));
val = E_MIPS_ARCH_4 | E_MIPS_MACH_5500;
break;
+ case bfd_mach_mips9000:
+ val = E_MIPS_ARCH_4 | E_MIPS_MACH_9000;
+ break;
+
case bfd_mach_mips5000:
case bfd_mach_mips7000:
case bfd_mach_mips8000:
dirmips->readonly_reloc = TRUE;
if (indmips->no_fn_stub)
dirmips->no_fn_stub = TRUE;
+
+ if (dirmips->tls_type == 0)
+ dirmips->tls_type = indmips->tls_type;
+ else
+ BFD_ASSERT (indmips->tls_type == 0);
}
void
h->forced_local = force_local;
dynobj = elf_hash_table (info)->dynobj;
- if (dynobj != NULL && force_local)
+ if (dynobj != NULL && force_local && h->root.type != STT_TLS)
{
got = mips_elf_got_section (dynobj, FALSE);
g = mips_elf_section_data (got)->u.got_info;
e.abfd = dynobj;
e.symndx = -1;
e.d.h = h;
+ e.tls_type = 0;
for (g = g->next; g != gg; g = g->next)
if (htab_find (g->got_entries, &e))
const void *location,
file_ptr offset, bfd_size_type count)
{
- if (strcmp (section->name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) == 0)
+ if (MIPS_ELF_OPTIONS_SECTION_NAME_P (section->name))
{
bfd_byte *c;
/* Specific to MIPS: Deal with relocation types that require
knowing the gp of the output bfd. */
asymbol *sym = *(*parent)->sym_ptr_ptr;
- if (bfd_is_abs_section (sym->section) && abfd)
- {
- /* The special_function wouldn't get called anyway. */
- }
- else if (!gp_found)
- {
- /* The gp isn't there; let the special function code
- fall over on its own. */
- }
- else if ((*parent)->howto->special_function
- == _bfd_mips_elf32_gprel16_reloc)
- {
- /* bypass special_function call */
- r = _bfd_mips_elf_gprel16_with_gp (input_bfd, sym, *parent,
- input_section, relocatable,
- data, gp);
- goto skip_bfd_perform_relocation;
- }
- /* end mips specific stuff */
- r = bfd_perform_relocation (input_bfd, *parent, data, input_section,
- relocatable ? abfd : NULL,
- &error_message);
- skip_bfd_perform_relocation:
+ /* If we've managed to find the gp and have a special
+ function for the relocation then go ahead, else default
+ to the generic handling. */
+ if (gp_found
+ && (*parent)->howto->special_function
+ == _bfd_mips_elf32_gprel16_reloc)
+ r = _bfd_mips_elf_gprel16_with_gp (input_bfd, sym, *parent,
+ input_section, relocatable,
+ data, gp);
+ else
+ r = bfd_perform_relocation (input_bfd, *parent, data,
+ input_section,
+ relocatable ? abfd : NULL,
+ &error_message);
if (relocatable)
{
break;
case bfd_reloc_overflow:
if (!((*link_info->callbacks->reloc_overflow)
- (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
+ (link_info, NULL,
+ bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
(*parent)->howto->name, (*parent)->addend,
input_bfd, input_section, (*parent)->address)))
goto error_return;
bfd_boolean
_bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info)
{
- asection **secpp;
asection *o;
struct bfd_link_order *p;
asection *reginfo_sec, *mdebug_sec, *gptab_data_sec, *gptab_bss_sec;
<= g->global_gotno);
}
-#if 0
- /* We want to set the GP value for ld -r. */
- /* On IRIX5, we omit the .options section. On IRIX6, however, we
- include it, even though we don't process it quite right. (Some
- entries are supposed to be merged.) Empirically, we seem to be
- better off including it then not. */
- if (IRIX_COMPAT (abfd) == ict_irix5 || IRIX_COMPAT (abfd) == ict_none)
- for (secpp = &abfd->sections; *secpp != NULL; secpp = &(*secpp)->next)
- {
- if (strcmp ((*secpp)->name, MIPS_ELF_OPTIONS_SECTION_NAME (abfd)) == 0)
- {
- for (p = (*secpp)->link_order_head; p != NULL; p = p->next)
- if (p->type == bfd_indirect_link_order)
- p->u.indirect.section->flags &= ~SEC_HAS_CONTENTS;
- (*secpp)->link_order_head = NULL;
- bfd_section_list_remove (abfd, secpp);
- --abfd->section_count;
-
- break;
- }
- }
-
- /* We include .MIPS.options, even though we don't process it quite right.
- (Some entries are supposed to be merged.) At IRIX6 empirically we seem
- to be better off including it than not. */
- for (secpp = &abfd->sections; *secpp != NULL; secpp = &(*secpp)->next)
- {
- if (strcmp ((*secpp)->name, ".MIPS.options") == 0)
- {
- for (p = (*secpp)->link_order_head; p != NULL; p = p->next)
- if (p->type == bfd_indirect_link_order)
- p->u.indirect.section->flags &=~ SEC_HAS_CONTENTS;
- (*secpp)->link_order_head = NULL;
- bfd_section_list_remove (abfd, secpp);
- --abfd->section_count;
-
- break;
- }
- }
-#endif
-
/* Get a value for the GP register. */
if (elf_gp (abfd) == 0)
{
/* We have found the .reginfo section in the output file.
Look through all the link_orders comprising it and merge
the information together. */
- for (p = o->link_order_head; p != NULL; p = p->next)
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
{
asection *input_section;
bfd *input_bfd;
/* Skip this section later on (I don't think this currently
matters, but someday it might). */
- o->link_order_head = NULL;
+ o->map_head.link_order = NULL;
reginfo_sec = o;
}
return FALSE;
}
- for (p = o->link_order_head; p != NULL; p = p->next)
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
{
asection *input_section;
bfd *input_bfd;
flagword flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED | SEC_READONLY);
- rtproc_sec = bfd_make_section (abfd, ".rtproc");
+ rtproc_sec = bfd_make_section_with_flags (abfd,
+ ".rtproc",
+ flags);
if (rtproc_sec == NULL
- || ! bfd_set_section_flags (abfd, rtproc_sec, flags)
|| ! bfd_set_section_alignment (abfd, rtproc_sec, 4))
return FALSE;
}
/* Skip this section later on (I don't think this currently
matters, but someday it might). */
- o->link_order_head = NULL;
+ o->map_head.link_order = NULL;
mdebug_sec = o;
}
not used in executables files. */
if (! info->relocatable)
{
- for (p = o->link_order_head; p != NULL; p = p->next)
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
{
asection *input_section;
/* Skip this section later on (I don't think this
currently matters, but someday it might). */
- o->link_order_head = NULL;
+ o->map_head.link_order = NULL;
/* Really remove the section. */
- for (secpp = &abfd->sections;
- *secpp != o;
- secpp = &(*secpp)->next)
- ;
- bfd_section_list_remove (abfd, secpp);
+ bfd_section_list_remove (abfd, o);
--abfd->section_count;
continue;
tab[0].gt_header.gt_unused = 0;
/* Combine the input sections. */
- for (p = o->link_order_head; p != NULL; p = p->next)
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
{
asection *input_section;
bfd *input_bfd;
/* Skip this section later on (I don't think this currently
matters, but someday it might). */
- o->link_order_head = NULL;
+ o->map_head.link_order = NULL;
}
}
{ bfd_mach_mips10000, bfd_mach_mips8000 },
{ bfd_mach_mips5000, bfd_mach_mips8000 },
{ bfd_mach_mips7000, bfd_mach_mips8000 },
+ { bfd_mach_mips9000, bfd_mach_mips8000 },
/* VR4100 extensions. */
{ bfd_mach_mips4120, bfd_mach_mips4100 },
if (! _bfd_generic_verify_endian_match (ibfd, obfd))
{
(*_bfd_error_handler)
- (_("%s: endianness incompatible with that of the selected emulation"),
- bfd_archive_filename (ibfd));
+ (_("%B: endianness incompatible with that of the selected emulation"),
+ ibfd);
return FALSE;
}
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));
+ (_("%B: ABI is incompatible with that of the selected emulation"),
+ ibfd);
return FALSE;
}
!= ((old_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0))
{
(*_bfd_error_handler)
- (_("%s: warning: linking PIC files with non-PIC files"),
- bfd_archive_filename (ibfd));
+ (_("%B: warning: linking PIC files with non-PIC files"),
+ ibfd);
ok = TRUE;
}
if (mips_32bit_flags_p (old_flags) != mips_32bit_flags_p (new_flags))
{
(*_bfd_error_handler)
- (_("%s: linking 32-bit code with 64-bit code"),
- bfd_archive_filename (ibfd));
+ (_("%B: linking 32-bit code with 64-bit code"),
+ ibfd);
ok = FALSE;
}
else if (!mips_mach_extends_p (bfd_get_mach (ibfd), bfd_get_mach (obfd)))
{
/* The ISAs aren't compatible. */
(*_bfd_error_handler)
- (_("%s: linking %s module with previous %s modules"),
- bfd_archive_filename (ibfd),
+ (_("%B: linking %s module with previous %s modules"),
+ ibfd,
bfd_printable_name (ibfd),
bfd_printable_name (obfd));
ok = FALSE;
!= elf_elfheader (obfd)->e_ident[EI_CLASS]))
{
(*_bfd_error_handler)
- (_("%s: ABI mismatch: linking %s module with previous %s modules"),
- bfd_archive_filename (ibfd),
+ (_("%B: ABI mismatch: linking %s module with previous %s modules"),
+ ibfd,
elf_mips_abi_name (ibfd),
elf_mips_abi_name (obfd));
ok = FALSE;
if (new_flags != old_flags)
{
(*_bfd_error_handler)
- (_("%s: uses different e_flags (0x%lx) fields than previous modules (0x%lx)"),
- bfd_archive_filename (ibfd), (unsigned long) new_flags,
+ (_("%B: uses different e_flags (0x%lx) fields than previous modules (0x%lx)"),
+ ibfd, (unsigned long) new_flags,
(unsigned long) old_flags);
ok = FALSE;
}
return TRUE;
}
-struct bfd_elf_special_section const _bfd_mips_elf_special_sections[]=
+static struct bfd_elf_special_section const
+ mips_special_sections_l[]=
{
- { ".sdata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
- { ".sbss", 5, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
{ ".lit4", 5, 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
{ ".lit8", 5, 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
- { ".ucode", 6, 0, SHT_MIPS_UCODE, 0 },
+ { NULL, 0, 0, 0, 0 }
+};
+
+static struct bfd_elf_special_section const
+ mips_special_sections_m[]=
+{
{ ".mdebug", 7, 0, SHT_MIPS_DEBUG, 0 },
{ NULL, 0, 0, 0, 0 }
};
+
+static struct bfd_elf_special_section const
+ mips_special_sections_s[]=
+{
+ { ".sdata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+ { ".sbss", 5, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL },
+};
+
+static struct bfd_elf_special_section const
+ mips_special_sections_u[]=
+{
+ { ".ucode", 6, 0, SHT_MIPS_UCODE, 0 },
+ { NULL, 0, 0, 0, 0 }
+};
+
+struct bfd_elf_special_section const *
+ _bfd_mips_elf_special_sections[27] =
+{
+ NULL, /* 'a' */
+ NULL, /* 'b' */
+ NULL, /* 'c' */
+ NULL, /* 'd' */
+ NULL, /* 'e' */
+ NULL, /* 'f' */
+ NULL, /* 'g' */
+ NULL, /* 'h' */
+ NULL, /* 'i' */
+ NULL, /* 'j' */
+ NULL, /* 'k' */
+ mips_special_sections_l, /* 'l' */
+ mips_special_sections_m, /* 'm' */
+ NULL, /* 'n' */
+ NULL, /* 'o' */
+ NULL, /* 'p' */
+ NULL, /* 'q' */
+ NULL, /* 'r' */
+ mips_special_sections_s, /* 'm' */
+ NULL, /* 't' */
+ mips_special_sections_u, /* 'u' */
+ NULL, /* 'v' */
+ NULL, /* 'w' */
+ NULL, /* 'x' */
+ NULL, /* 'y' */
+ NULL, /* 'z' */
+ NULL /* other */
+};