/* MIPS-specific support for ELF
- Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
Most of the information added by Ian Lance Taylor, Cygnus Support,
<ian@cygnus.com>.
#include "hashtab.h"
+/* Types of TLS GOT entry. */
+enum mips_got_tls_type {
+ GOT_TLS_NONE,
+ GOT_TLS_GD,
+ GOT_TLS_LDM,
+ GOT_TLS_IE
+};
+
/* This structure is used to hold information about one GOT entry.
- There are three types of entry:
-
- (1) absolute addresses
- (abfd == NULL)
- (2) SYMBOL + OFFSET addresses, where SYMBOL is local to an input bfd
- (abfd != NULL, symndx >= 0)
- (3) SYMBOL addresses, where SYMBOL is not local to an input bfd
- (abfd != NULL, symndx == -1)
-
- Type (3) entries are treated differently for different types of GOT.
- In the "master" GOT -- i.e. the one that describes every GOT
- reference needed in the link -- the mips_got_entry is keyed on both
- the symbol and the input bfd that references it. If it turns out
- that we need multiple GOTs, we can then use this information to
- create separate GOTs for each input bfd.
-
- However, we want each of these separate GOTs to have at most one
- entry for a given symbol, so their type (3) entries are keyed only
- on the symbol. The input bfd given by the "abfd" field is somewhat
- arbitrary in this case.
-
- This means that when there are multiple GOTs, each GOT has a unique
- mips_got_entry for every symbol within it. We can therefore use the
- mips_got_entry fields (tls_type and gotidx) to track the symbol's
- GOT index.
-
- However, if it turns out that we need only a single GOT, we continue
- to use the master GOT to describe it. There may therefore be several
- mips_got_entries for the same symbol, each with a different input bfd.
- We want to make sure that each symbol gets a unique GOT entry, so when
- there's a single GOT, we use the symbol's hash entry, not the
- mips_got_entry fields, to track a symbol's GOT index. */
+ There are four types of entry:
+
+ (1) an absolute address
+ requires: abfd == NULL
+ fields: d.address
+
+ (2) a SYMBOL + OFFSET address, where SYMBOL is local to an input bfd
+ requires: abfd != NULL, symndx >= 0, tls_type != GOT_TLS_LDM
+ fields: abfd, symndx, d.addend, tls_type
+
+ (3) a SYMBOL address, where SYMBOL is not local to an input bfd
+ requires: abfd != NULL, symndx == -1
+ fields: d.h, tls_type
+
+ (4) a TLS LDM slot
+ requires: abfd != NULL, symndx == 0, tls_type == GOT_TLS_LDM
+ fields: none; there's only one of these per GOT. */
struct mips_got_entry
{
- /* The input bfd in which the symbol is defined. */
+ /* One input bfd that needs the GOT entry. */
bfd *abfd;
/* The index of the symbol, as stored in the relocation r_info, if
we have a local symbol; -1 otherwise. */
that should be added to the symbol value. */
bfd_vma addend;
/* If abfd != NULL && symndx == -1, the hash table entry
- corresponding to symbol in the GOT. The symbol's entry
+ corresponding to a symbol in the GOT. The symbol's entry
is in the local area if h->global_got_area is GGA_NONE,
otherwise it is in the global area. */
struct mips_elf_link_hash_entry *h;
} d;
- /* The TLS types included in this GOT entry (specifically, GD and
- IE). The GD and IE flags can be added as we encounter new
- relocations. LDM can also be set; it will always be alone, not
- combined with any GD or IE flags. An LDM GOT entry will be
- a local symbol entry with r_symndx == 0. */
+ /* The TLS type of this GOT entry. An LDM GOT entry will be a local
+ symbol entry with r_symndx == 0. */
unsigned char tls_type;
+ /* True if we have filled in the GOT contents for a TLS entry,
+ and created the associated relocations. */
+ unsigned char tls_initialized;
+
/* The offset from the beginning of the .got section to the entry
corresponding to this symbol+addend. If it's a global symbol
whose offset is yet to be decided, it's going to be -1. */
long gotidx;
};
+/* This structure represents a GOT page reference from an input bfd.
+ Each instance represents a symbol + ADDEND, where the representation
+ of the symbol depends on whether it is local to the input bfd.
+ If it is, then SYMNDX >= 0, and the symbol has index SYMNDX in U.ABFD.
+ Otherwise, SYMNDX < 0 and U.H points to the symbol's hash table entry.
+
+ Page references with SYMNDX >= 0 always become page references
+ in the output. Page references with SYMNDX < 0 only become page
+ references if the symbol binds locally; in other cases, the page
+ reference decays to a global GOT reference. */
+struct mips_got_page_ref
+{
+ long symndx;
+ union
+ {
+ struct mips_elf_link_hash_entry *h;
+ bfd *abfd;
+ } u;
+ bfd_vma addend;
+};
+
/* This structure describes a range of addends: [MIN_ADDEND, MAX_ADDEND].
The structures form a non-overlapping list that is sorted by increasing
MIN_ADDEND. */
};
/* This structure describes the range of addends that are applied to page
- relocations against a given symbol. */
+ relocations against a given section. */
struct mips_got_page_entry
{
- /* The input bfd in which the symbol is defined. */
- bfd *abfd;
- /* The index of the symbol, as stored in the relocation r_info. */
- long symndx;
+ /* The section that these entries are based on. */
+ asection *sec;
/* The ranges for this page entry. */
struct mips_got_page_range *ranges;
/* The maximum number of page entries needed for RANGES. */
struct mips_got_info
{
- /* The global symbol in the GOT with the lowest index in the dynamic
- symbol table. */
- struct elf_link_hash_entry *global_gotsym;
/* The number of global .got entries. */
unsigned int global_gotno;
/* The number of global .got entries that are in the GGA_RELOC_ONLY area. */
unsigned int local_gotno;
/* The maximum number of page entries needed. */
unsigned int page_gotno;
- /* The number of local .got entries we have used. */
- unsigned int assigned_gotno;
+ /* The number of relocations needed for the GOT entries. */
+ unsigned int relocs;
+ /* The first unused local .got entry. */
+ unsigned int assigned_low_gotno;
+ /* The last unused local .got entry. */
+ unsigned int assigned_high_gotno;
/* A hash table holding members of the got. */
struct htab *got_entries;
+ /* A hash table holding mips_got_page_ref structures. */
+ struct htab *got_page_refs;
/* A hash table of mips_got_page_entry structures. */
struct htab *got_page_entries;
- /* A hash table mapping input bfds to other mips_got_info. NULL
- unless multi-got was necessary. */
- struct htab *bfd2got;
/* In multi-got links, a pointer to the next got (err, rather, most
of the time, it points to the previous got). */
struct mips_got_info *next;
- /* This is the GOT index of the TLS LDM entry for the GOT, MINUS_ONE
- for none, or MINUS_TWO for not yet assigned. This is needed
- because a single-GOT link may have multiple hash table entries
- for the LDM. It does not get initialized in multi-GOT mode. */
- bfd_vma tls_ldm_offset;
-};
-
-/* Map an input bfd to a got in a multi-got link. */
-
-struct mips_elf_bfd2got_hash
-{
- bfd *bfd;
- struct mips_got_info *g;
};
-/* Structure passed when traversing the bfd2got hash table, used to
- create and merge bfd's gots. */
+/* Structure passed when merging bfds' gots. */
struct mips_elf_got_per_bfd_arg
{
- /* A hashtable that maps bfds to gots. */
- htab_t bfd2got;
/* The output bfd. */
bfd *obfd;
/* The link information. */
unsigned int global_count;
};
-/* Another structure used to pass arguments for got entries traversal. */
+/* A structure used to pass information to htab_traverse callbacks
+ when laying out the GOT. */
-struct mips_elf_set_global_got_offset_arg
+struct mips_elf_traverse_got_arg
{
+ struct bfd_link_info *info;
struct mips_got_info *g;
int value;
- unsigned int needed_relocs;
- 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
#define LA25_LUI(VAL) (0x3c190000 | (VAL)) /* lui t9,VAL */
#define LA25_J(VAL) (0x08000000 | (((VAL) >> 2) & 0x3ffffff)) /* j VAL */
#define LA25_ADDIU(VAL) (0x27390000 | (VAL)) /* addiu t9,t9,VAL */
-#define LA25_LUI_MICROMIPS_1(VAL) (0x41b9) /* lui t9,VAL */
-#define LA25_LUI_MICROMIPS_2(VAL) (VAL)
-#define LA25_J_MICROMIPS_1(VAL) (0xd400 | (((VAL) >> 17) & 0x3ff)) /* j VAL */
-#define LA25_J_MICROMIPS_2(VAL) ((VAL) >> 1)
-#define LA25_ADDIU_MICROMIPS_1(VAL) (0x3339) /* addiu t9,t9,VAL */
-#define LA25_ADDIU_MICROMIPS_2(VAL) (VAL)
+#define LA25_LUI_MICROMIPS(VAL) \
+ (0x41b90000 | (VAL)) /* lui t9,VAL */
+#define LA25_J_MICROMIPS(VAL) \
+ (0xd4000000 | (((VAL) >> 1) & 0x3ffffff)) /* j VAL */
+#define LA25_ADDIU_MICROMIPS(VAL) \
+ (0x33390000 | (VAL)) /* addiu t9,t9,VAL */
/* This structure is passed to mips_elf_sort_hash_table_f when sorting
the dynamic symbols. */
long max_non_got_dynindx;
};
+/* We make up to two PLT entries if needed, one for standard MIPS code
+ and one for compressed code, either a MIPS16 or microMIPS one. We
+ keep a separate record of traditional lazy-binding stubs, for easier
+ processing. */
+
+struct plt_entry
+{
+ /* Traditional SVR4 stub offset, or -1 if none. */
+ bfd_vma stub_offset;
+
+ /* Standard PLT entry offset, or -1 if none. */
+ bfd_vma mips_offset;
+
+ /* Compressed PLT entry offset, or -1 if none. */
+ bfd_vma comp_offset;
+
+ /* The corresponding .got.plt index, or -1 if none. */
+ bfd_vma gotplt_index;
+
+ /* Whether we need a standard PLT entry. */
+ unsigned int need_mips : 1;
+
+ /* Whether we need a compressed PLT entry. */
+ unsigned int need_comp : 1;
+};
+
/* The MIPS ELF linker needs additional information for each symbol in
the global hash table. */
being called returns a floating point value. */
asection *call_fp_stub;
-#define GOT_NORMAL 0
-#define GOT_TLS_GD 1
-#define GOT_TLS_LDM 2
-#define GOT_TLS_IE 4
-#define GOT_TLS_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;
-
/* The highest GGA_* value that satisfies all references to this symbol. */
unsigned int global_got_area : 2;
/* Does this symbol need a traditional MIPS lazy-binding stub
(as opposed to a PLT entry)? */
unsigned int needs_lazy_stub : 1;
+
+ /* Does this symbol resolve to a PLT entry? */
+ unsigned int use_plt_entry : 1;
};
/* MIPS ELF linker hash table. */
struct mips_elf_link_hash_table
{
struct elf_link_hash_table root;
-#if 0
- /* We no longer use this. */
- /* String section indices for the dynamic section symbols. */
- bfd_size_type dynsym_sec_strindex[SIZEOF_MIPS_DYNSYM_SECNAMES];
-#endif
/* The number of .rtproc entries. */
bfd_size_type procedure_count;
/* The size of the .compact_rel section (if SGI_COMPAT). */
bfd_size_type compact_rel_size;
- /* This flag indicates that the value of DT_MIPS_RLD_MAP dynamic
- entry is set to the address of __rld_obj_head as in IRIX5. */
+ /* This flag indicates that the value of DT_MIPS_RLD_MAP dynamic entry
+ is set to the address of __rld_obj_head as in IRIX5 and IRIX6. */
bfd_boolean use_rld_obj_head;
- /* This is the value of the __rld_map or __rld_obj_head symbol. */
- bfd_vma rld_value;
+ /* The __rld_map or __rld_obj_head symbol. */
+ struct elf_link_hash_entry *rld_symbol;
/* This is set if we see any mips16 stub sections. */
bfd_boolean mips16_stubs_seen;
/* True if we can generate copy relocs and PLTs. */
bfd_boolean use_plts_and_copy_relocs;
+ /* True if we can only use 32-bit microMIPS instructions. */
+ bfd_boolean insn32;
+
/* True if we're generating code for VxWorks. */
bfd_boolean is_vxworks;
/* The master GOT information. */
struct mips_got_info *got_info;
+ /* The global symbol in the GOT with the lowest index in the dynamic
+ symbol table. */
+ struct elf_link_hash_entry *global_gotsym;
+
/* The size of the PLT header in bytes. */
bfd_vma plt_header_size;
- /* The size of a PLT entry in bytes. */
- bfd_vma plt_entry_size;
+ /* The size of a standard PLT entry in bytes. */
+ bfd_vma plt_mips_entry_size;
+
+ /* The size of a compressed PLT entry in bytes. */
+ bfd_vma plt_comp_entry_size;
+
+ /* The offset of the next standard PLT entry to create. */
+ bfd_vma plt_mips_offset;
+
+ /* The offset of the next compressed PLT entry to create. */
+ bfd_vma plt_comp_offset;
+
+ /* The index of the next .got.plt entry to create. */
+ bfd_vma plt_got_index;
/* The number of functions that need a lazy-binding stub. */
bfd_vma lazy_stub_count;
The function returns the new section on success, otherwise it
returns null. */
asection *(*add_stub_section) (const char *, asection *, asection *);
+
+ /* Small local sym cache. */
+ struct sym_cache sym_cache;
+
+ /* Is the PLT header compressed? */
+ unsigned int plt_header_is_comp : 1;
};
/* Get the MIPS ELF linker hash table from a link_info structure. */
bfd_boolean error;
};
+/* MIPS ELF private object data. */
+
+struct mips_elf_obj_tdata
+{
+ /* Generic ELF private object data. */
+ struct elf_obj_tdata root;
+
+ /* Input BFD providing Tag_GNU_MIPS_ABI_FP attribute for output. */
+ bfd *abi_fp_bfd;
+
+ /* Input BFD providing Tag_GNU_MIPS_ABI_MSA attribute for output. */
+ bfd *abi_msa_bfd;
+
+ /* The abiflags for this object. */
+ Elf_Internal_ABIFlags_v0 abiflags;
+ bfd_boolean abiflags_valid;
+
+ /* The GOT requirements of input bfds. */
+ struct mips_got_info *got;
+
+ /* Used by _bfd_mips_elf_find_nearest_line. The structure could be
+ included directly in this one, but there's no point to wasting
+ the memory just for the infrequently called find_nearest_line. */
+ struct mips_elf_find_line *find_line_info;
+
+ /* An array of stub sections indexed by symbol number. */
+ asection **local_stubs;
+ asection **local_call_stubs;
+
+ /* The Irix 5 support uses two virtual sections, which represent
+ text/data symbols defined in dynamic objects. */
+ asymbol *elf_data_symbol;
+ asymbol *elf_text_symbol;
+ asection *elf_data_section;
+ asection *elf_text_section;
+};
+
+/* Get MIPS ELF private object data from BFD's tdata. */
+
+#define mips_elf_tdata(bfd) \
+ ((struct mips_elf_obj_tdata *) (bfd)->tdata.any)
+
#define TLS_RELOC_P(r_type) \
(r_type == R_MIPS_TLS_DTPMOD32 \
|| r_type == R_MIPS_TLS_DTPMOD64 \
|| r_type == R_MIPS_TLS_TPREL64 \
|| r_type == R_MIPS_TLS_TPREL_HI16 \
|| r_type == R_MIPS_TLS_TPREL_LO16 \
+ || r_type == R_MIPS16_TLS_GD \
+ || r_type == R_MIPS16_TLS_LDM \
+ || r_type == R_MIPS16_TLS_DTPREL_HI16 \
+ || r_type == R_MIPS16_TLS_DTPREL_LO16 \
+ || r_type == R_MIPS16_TLS_GOTTPREL \
+ || r_type == R_MIPS16_TLS_TPREL_HI16 \
+ || r_type == R_MIPS16_TLS_TPREL_LO16 \
|| r_type == R_MICROMIPS_TLS_GD \
|| r_type == R_MICROMIPS_TLS_LDM \
|| r_type == R_MICROMIPS_TLS_DTPREL_HI16 \
(bfd *, struct bfd_link_info *, const Elf_Internal_Rela *,
struct mips_elf_link_hash_entry *, asection *, bfd_vma,
bfd_vma *, asection *);
-static hashval_t mips_elf_got_entry_hash
- (const void *);
static bfd_vma mips_elf_adjust_gp
(bfd *, struct mips_got_info *, bfd *);
-static struct mips_got_info *mips_elf_got_for_ibfd
- (struct mips_got_info *, bfd *);
/* This will be used when we sort the dynamic relocation records. */
static bfd *reldyn_sorting_bfd;
#define PIC_OBJECT_P(abfd) \
((elf_elfheader (abfd)->e_flags & EF_MIPS_PIC) != 0)
+/* Nonzero if ABFD is using the O32 ABI. */
+#define ABI_O32_P(abfd) \
+ ((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI) == E_MIPS_ABI_O32)
+
/* Nonzero if ABFD is using the N32 ABI. */
#define ABI_N32_P(abfd) \
((elf_elfheader (abfd)->e_flags & EF_MIPS_ABI2) != 0)
/* Nonzero if ABFD is using NewABI conventions. */
#define NEWABI_P(abfd) (ABI_N32_P (abfd) || ABI_64_P (abfd))
+/* Nonzero if ABFD has microMIPS code. */
+#define MICROMIPS_P(abfd) \
+ ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS) != 0)
+
+/* Nonzero if ABFD is MIPS R6. */
+#define MIPSR6_P(abfd) \
+ ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R6 \
+ || (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64R6)
+
/* The IRIX compatibility level we are striving for. */
#define IRIX_COMPAT(abfd) \
(get_elf_backend_data (abfd)->elf_backend_mips_irix_compat (abfd))
#define MIPS_ELF_OPTIONS_SECTION_NAME_P(NAME) \
(strcmp (NAME, ".MIPS.options") == 0 || strcmp (NAME, ".options") == 0)
+/* True if NAME is the recognized name of any SHT_MIPS_ABIFLAGS section. */
+#define MIPS_ELF_ABIFLAGS_SECTION_NAME_P(NAME) \
+ (strcmp (NAME, ".MIPS.abiflags") == 0)
+
/* Whether the section is readonly. */
#define MIPS_ELF_READONLY_SECTION(sec) \
((sec->flags & (SEC_ALLOC | SEC_LOAD | SEC_READONLY)) \
#define MIPS_ELF_GOT_SIZE(abfd) \
(get_elf_backend_data (abfd)->s->arch_size / 8)
+/* The size of the .rld_map section. */
+#define MIPS_ELF_RLD_MAP_SIZE(abfd) \
+ (get_elf_backend_data (abfd)->s->arch_size / 8)
+
/* The size of a symbol-table entry. */
#define MIPS_ELF_SYM_SIZE(abfd) \
(get_elf_backend_data (abfd)->s->sizeof_sym)
? (0x64180000 + (VAL)) /* daddiu t8,zero,VAL sign extended */ \
: (0x24180000 + (VAL)))) /* addiu t8,zero,VAL sign extended */
+/* Likewise for the microMIPS ASE. */
+#define STUB_LW_MICROMIPS(abfd) \
+ (ABI_64_P (abfd) \
+ ? 0xdf3c8010 /* ld t9,0x8010(gp) */ \
+ : 0xff3c8010) /* lw t9,0x8010(gp) */
+#define STUB_MOVE_MICROMIPS 0x0dff /* move t7,ra */
+#define STUB_MOVE32_MICROMIPS(abfd) \
+ (ABI_64_P (abfd) \
+ ? 0x581f7950 /* daddu t7,ra,zero */ \
+ : 0x001f7950) /* addu t7,ra,zero */
+#define STUB_LUI_MICROMIPS(VAL) \
+ (0x41b80000 + (VAL)) /* lui t8,VAL */
+#define STUB_JALR_MICROMIPS 0x45d9 /* jalr t9 */
+#define STUB_JALR32_MICROMIPS 0x03f90f3c /* jalr ra,t9 */
+#define STUB_ORI_MICROMIPS(VAL) \
+ (0x53180000 + (VAL)) /* ori t8,t8,VAL */
+#define STUB_LI16U_MICROMIPS(VAL) \
+ (0x53000000 + (VAL)) /* ori t8,zero,VAL unsigned */
+#define STUB_LI16S_MICROMIPS(abfd, VAL) \
+ (ABI_64_P (abfd) \
+ ? 0x5f000000 + (VAL) /* daddiu t8,zero,VAL sign extended */ \
+ : 0x33000000 + (VAL)) /* addiu t8,zero,VAL sign extended */
+
#define MIPS_FUNCTION_STUB_NORMAL_SIZE 16
#define MIPS_FUNCTION_STUB_BIG_SIZE 20
+#define MICROMIPS_FUNCTION_STUB_NORMAL_SIZE 12
+#define MICROMIPS_FUNCTION_STUB_BIG_SIZE 16
+#define MICROMIPS_INSN32_FUNCTION_STUB_NORMAL_SIZE 16
+#define MICROMIPS_INSN32_FUNCTION_STUB_BIG_SIZE 20
/* The name of the dynamic interpreter. This is put in the .interp
section. */
0x8f990000, /* lw $25, %lo(&GOTPLT[0])($28) */
0x279c0000, /* addiu $28, $28, %lo(&GOTPLT[0]) */
0x031cc023, /* subu $24, $24, $28 */
- 0x03e07821, /* move $15, $31 */
+ 0x03e07821, /* move $15, $31 # 32-bit move (addu) */
0x0018c082, /* srl $24, $24, 2 */
0x0320f809, /* jalr $25 */
0x2718fffe /* subu $24, $24, 2 */
0x8dd90000, /* lw $25, %lo(&GOTPLT[0])($14) */
0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */
0x030ec023, /* subu $24, $24, $14 */
- 0x03e07821, /* move $15, $31 */
+ 0x03e07821, /* move $15, $31 # 32-bit move (addu) */
0x0018c082, /* srl $24, $24, 2 */
0x0320f809, /* jalr $25 */
0x2718fffe /* subu $24, $24, 2 */
0xddd90000, /* ld $25, %lo(&GOTPLT[0])($14) */
0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */
0x030ec023, /* subu $24, $24, $14 */
- 0x03e07821, /* move $15, $31 */
+ 0x03e0782d, /* move $15, $31 # 64-bit move (daddu) */
0x0018c0c2, /* srl $24, $24, 3 */
0x0320f809, /* jalr $25 */
0x2718fffe /* subu $24, $24, 2 */
};
-/* The format of subsequent PLT entries. */
+/* The format of the microMIPS first PLT entry in an O32 executable.
+ We rely on v0 ($2) rather than t8 ($24) to contain the address
+ of the GOTPLT entry handled, so this stub may only be used when
+ all the subsequent PLT entries are microMIPS code too.
+
+ The trailing NOP is for alignment and correct disassembly only. */
+static const bfd_vma micromips_o32_exec_plt0_entry[] =
+{
+ 0x7980, 0x0000, /* addiupc $3, (&GOTPLT[0]) - . */
+ 0xff23, 0x0000, /* lw $25, 0($3) */
+ 0x0535, /* subu $2, $2, $3 */
+ 0x2525, /* srl $2, $2, 2 */
+ 0x3302, 0xfffe, /* subu $24, $2, 2 */
+ 0x0dff, /* move $15, $31 */
+ 0x45f9, /* jalrs $25 */
+ 0x0f83, /* move $28, $3 */
+ 0x0c00 /* nop */
+};
+
+/* The format of the microMIPS first PLT entry in an O32 executable
+ in the insn32 mode. */
+static const bfd_vma micromips_insn32_o32_exec_plt0_entry[] =
+{
+ 0x41bc, 0x0000, /* lui $28, %hi(&GOTPLT[0]) */
+ 0xff3c, 0x0000, /* lw $25, %lo(&GOTPLT[0])($28) */
+ 0x339c, 0x0000, /* addiu $28, $28, %lo(&GOTPLT[0]) */
+ 0x0398, 0xc1d0, /* subu $24, $24, $28 */
+ 0x001f, 0x7950, /* move $15, $31 */
+ 0x0318, 0x1040, /* srl $24, $24, 2 */
+ 0x03f9, 0x0f3c, /* jalr $25 */
+ 0x3318, 0xfffe /* subu $24, $24, 2 */
+};
+
+/* The format of subsequent standard PLT entries. */
static const bfd_vma mips_exec_plt_entry[] =
{
0x3c0f0000, /* lui $15, %hi(.got.plt entry) */
0x03200008 /* jr $25 */
};
+/* In the following PLT entry the JR and ADDIU instructions will
+ be swapped in _bfd_mips_elf_finish_dynamic_symbol because
+ LOAD_INTERLOCKS_P will be true for MIPS R6. */
+static const bfd_vma mipsr6_exec_plt_entry[] =
+{
+ 0x3c0f0000, /* lui $15, %hi(.got.plt entry) */
+ 0x01f90000, /* l[wd] $25, %lo(.got.plt entry)($15) */
+ 0x25f80000, /* addiu $24, $15, %lo(.got.plt entry) */
+ 0x03200009 /* jr $25 */
+};
+
+/* The format of subsequent MIPS16 o32 PLT entries. We use v0 ($2)
+ and v1 ($3) as temporaries because t8 ($24) and t9 ($25) are not
+ directly addressable. */
+static const bfd_vma mips16_o32_exec_plt_entry[] =
+{
+ 0xb203, /* lw $2, 12($pc) */
+ 0x9a60, /* lw $3, 0($2) */
+ 0x651a, /* move $24, $2 */
+ 0xeb00, /* jr $3 */
+ 0x653b, /* move $25, $3 */
+ 0x6500, /* nop */
+ 0x0000, 0x0000 /* .word (.got.plt entry) */
+};
+
+/* The format of subsequent microMIPS o32 PLT entries. We use v0 ($2)
+ as a temporary because t8 ($24) is not addressable with ADDIUPC. */
+static const bfd_vma micromips_o32_exec_plt_entry[] =
+{
+ 0x7900, 0x0000, /* addiupc $2, (.got.plt entry) - . */
+ 0xff22, 0x0000, /* lw $25, 0($2) */
+ 0x4599, /* jr $25 */
+ 0x0f02 /* move $24, $2 */
+};
+
+/* The format of subsequent microMIPS o32 PLT entries in the insn32 mode. */
+static const bfd_vma micromips_insn32_o32_exec_plt_entry[] =
+{
+ 0x41af, 0x0000, /* lui $15, %hi(.got.plt entry) */
+ 0xff2f, 0x0000, /* lw $25, %lo(.got.plt entry)($15) */
+ 0x0019, 0x0f3c, /* jr $25 */
+ 0x330f, 0x0000 /* addiu $24, $15, %lo(.got.plt entry) */
+};
+
/* The format of the first PLT entry in a VxWorks executable. */
static const bfd_vma mips_vxworks_exec_plt0_entry[] =
{
0x24180000 /* li t8, <pltindex> */
};
\f
+/* microMIPS 32-bit opcode helper installer. */
+
+static void
+bfd_put_micromips_32 (const bfd *abfd, bfd_vma opcode, bfd_byte *ptr)
+{
+ bfd_put_16 (abfd, (opcode >> 16) & 0xffff, ptr);
+ bfd_put_16 (abfd, opcode & 0xffff, ptr + 2);
+}
+
+/* microMIPS 32-bit opcode helper retriever. */
+
+static bfd_vma
+bfd_get_micromips_32 (const bfd *abfd, const bfd_byte *ptr)
+{
+ return (bfd_get_16 (abfd, ptr) << 16) | bfd_get_16 (abfd, ptr + 2);
+}
+\f
/* Look up an entry in a MIPS ELF linker hash table. */
#define mips_elf_link_hash_lookup(table, string, create, copy, follow) \
ret->fn_stub = NULL;
ret->call_stub = NULL;
ret->call_fp_stub = NULL;
- ret->tls_type = GOT_NORMAL;
ret->global_got_area = GGA_NONE;
ret->got_only_for_calls = TRUE;
ret->readonly_reloc = FALSE;
ret->need_fn_stub = FALSE;
ret->has_nonpic_branches = FALSE;
ret->needs_lazy_stub = FALSE;
+ ret->use_plt_entry = FALSE;
}
return (struct bfd_hash_entry *) ret;
}
+/* Allocate MIPS ELF private object data. */
+
+bfd_boolean
+_bfd_mips_elf_mkobject (bfd *abfd)
+{
+ return bfd_elf_allocate_object (abfd, sizeof (struct mips_elf_obj_tdata),
+ MIPS_ELF_DATA);
+}
+
bfd_boolean
_bfd_mips_elf_new_section_hook (bfd *abfd, asection *sec)
{
function, or 0 if we can't decide which function that is. */
static unsigned long
-mips16_stub_symndx (asection *sec ATTRIBUTE_UNUSED,
+mips16_stub_symndx (const struct elf_backend_data *bed,
+ asection *sec ATTRIBUTE_UNUSED,
const Elf_Internal_Rela *relocs,
const Elf_Internal_Rela *relend)
{
+ int int_rels_per_ext_rel = bed->s->int_rels_per_ext_rel;
const Elf_Internal_Rela *rel;
- /* Trust the first R_MIPS_NONE relocation, if any. */
- for (rel = relocs; rel < relend; rel++)
+ /* Trust the first R_MIPS_NONE relocation, if any, but not a subsequent
+ one in a compound relocation. */
+ for (rel = relocs; rel < relend; rel += int_rels_per_ext_rel)
if (ELF_R_TYPE (sec->owner, rel->r_info) == R_MIPS_NONE)
return ELF_R_SYM (sec->owner, rel->r_info);
}
/* Return true if H is a locally-defined PIC function, in the sense
- that it might need $25 to be valid on entry. Note that MIPS16
- functions never need $25 to be valid on entry; they set up $gp
- using PC-relative instructions instead. */
+ that it or its fn_stub might need $25 to be valid on entry.
+ Note that MIPS16 functions set up $gp using PC-relative instructions,
+ so they themselves never need $25 to be valid. Only non-MIPS16
+ entry points are of interest here. */
static bfd_boolean
mips_elf_local_pic_function_p (struct mips_elf_link_hash_entry *h)
|| h->root.root.type == bfd_link_hash_defweak)
&& h->root.def_regular
&& !bfd_is_abs_section (h->root.root.u.def.section)
- && !ELF_ST_IS_MIPS16 (h->root.other)
+ && (!ELF_ST_IS_MIPS16 (h->root.other)
+ || (h->fn_stub && h->need_fn_stub))
&& (PIC_OBJECT_P (h->root.root.u.def.section->owner)
|| ELF_ST_IS_MIPS_PIC (h->root.other)));
}
+/* Set *SEC to the input section that contains the target of STUB.
+ Return the offset of the target from the start of that section. */
+
+static bfd_vma
+mips_elf_get_la25_target (struct mips_elf_la25_stub *stub,
+ asection **sec)
+{
+ if (ELF_ST_IS_MIPS16 (stub->h->root.other))
+ {
+ BFD_ASSERT (stub->h->need_fn_stub);
+ *sec = stub->h->fn_stub;
+ return 0;
+ }
+ else
+ {
+ *sec = stub->h->root.root.u.def.section;
+ return stub->h->root.root.u.def.value;
+ }
+}
+
/* STUB describes an la25 stub that we have decided to implement
by inserting an LUI/ADDIU pair before the target function.
Create the section and redirect the function symbol to it. */
sprintf (name, ".text.stub.%d", (int) htab_elements (htab->la25_stubs));
/* Create the section. */
- input_section = stub->h->root.root.u.def.section;
+ mips_elf_get_la25_target (stub, &input_section);
s = htab->add_stub_section (name, input_section,
input_section->output_section);
if (s == NULL)
bfd_vma value;
void **slot;
- /* Prefer to use LUI/ADDIU stubs if the function is at the beginning
- of the section and if we would need no more than 2 nops. */
- s = h->root.root.u.def.section;
- value = h->root.root.u.def.value;
- use_trampoline_p = (value != 0 || s->alignment_power > 4);
-
/* Describe the stub we want. */
search.stub_section = NULL;
search.offset = 0;
*stub = search;
*slot = stub;
+ /* Prefer to use LUI/ADDIU stubs if the function is at the beginning
+ of the section and if we would need no more than 2 nops. */
+ value = mips_elf_get_la25_target (stub, &s);
+ use_trampoline_p = (value != 0 || s->alignment_power > 4);
+
h->la25_stub = stub;
return (use_trampoline_p
? mips_elf_add_la25_trampoline (stub, info)
case R_MIPS16_CALL16:
case R_MIPS16_HI16:
case R_MIPS16_LO16:
+ case R_MIPS16_TLS_GD:
+ case R_MIPS16_TLS_LDM:
+ case R_MIPS16_TLS_DTPREL_HI16:
+ case R_MIPS16_TLS_DTPREL_LO16:
+ case R_MIPS16_TLS_GOTTPREL:
+ case R_MIPS16_TLS_TPREL_HI16:
+ case R_MIPS16_TLS_TPREL_LO16:
return TRUE;
default:
{
return (r_type == R_MIPS_HI16
|| r_type == R_MIPS16_HI16
- || r_type == R_MICROMIPS_HI16);
+ || r_type == R_MICROMIPS_HI16
+ || r_type == R_MIPS_PCHI16);
}
static inline bfd_boolean
{
return (r_type == R_MIPS_LO16
|| r_type == R_MIPS16_LO16
- || r_type == R_MICROMIPS_LO16);
+ || r_type == R_MICROMIPS_LO16
+ || r_type == R_MIPS_PCLO16);
}
static inline bfd_boolean
|| r_type == R_MICROMIPS_26_S1);
}
+static inline bfd_boolean
+aligned_pcrel_reloc_p (int r_type)
+{
+ return (r_type == R_MIPS_PC18_S3
+ || r_type == R_MIPS_PC19_S2);
+}
+
static inline bfd_boolean
micromips_branch_reloc_p (int r_type)
{
static inline bfd_boolean
tls_gd_reloc_p (unsigned int r_type)
{
- return r_type == R_MIPS_TLS_GD || r_type == R_MICROMIPS_TLS_GD;
+ return (r_type == R_MIPS_TLS_GD
+ || r_type == R_MIPS16_TLS_GD
+ || r_type == R_MICROMIPS_TLS_GD);
}
static inline bfd_boolean
tls_ldm_reloc_p (unsigned int r_type)
{
- return r_type == R_MIPS_TLS_LDM || r_type == R_MICROMIPS_TLS_LDM;
+ return (r_type == R_MIPS_TLS_LDM
+ || r_type == R_MIPS16_TLS_LDM
+ || r_type == R_MICROMIPS_TLS_LDM);
}
static inline bfd_boolean
tls_gottprel_reloc_p (unsigned int r_type)
{
- return r_type == R_MIPS_TLS_GOTTPREL || r_type == R_MICROMIPS_TLS_GOTTPREL;
+ return (r_type == R_MIPS_TLS_GOTTPREL
+ || r_type == R_MIPS16_TLS_GOTTPREL
+ || r_type == R_MICROMIPS_TLS_GOTTPREL);
}
void
H_PUT_16 (abfd, in->section, ex->section);
H_PUT_32 (abfd, in->info, ex->info);
}
+
+/* Swap in an abiflags structure. */
+
+void
+bfd_mips_elf_swap_abiflags_v0_in (bfd *abfd,
+ const Elf_External_ABIFlags_v0 *ex,
+ Elf_Internal_ABIFlags_v0 *in)
+{
+ in->version = H_GET_16 (abfd, ex->version);
+ in->isa_level = H_GET_8 (abfd, ex->isa_level);
+ in->isa_rev = H_GET_8 (abfd, ex->isa_rev);
+ in->gpr_size = H_GET_8 (abfd, ex->gpr_size);
+ in->cpr1_size = H_GET_8 (abfd, ex->cpr1_size);
+ in->cpr2_size = H_GET_8 (abfd, ex->cpr2_size);
+ in->fp_abi = H_GET_8 (abfd, ex->fp_abi);
+ in->isa_ext = H_GET_32 (abfd, ex->isa_ext);
+ in->ases = H_GET_32 (abfd, ex->ases);
+ in->flags1 = H_GET_32 (abfd, ex->flags1);
+ in->flags2 = H_GET_32 (abfd, ex->flags2);
+}
+
+/* Swap out an abiflags structure. */
+
+void
+bfd_mips_elf_swap_abiflags_v0_out (bfd *abfd,
+ const Elf_Internal_ABIFlags_v0 *in,
+ Elf_External_ABIFlags_v0 *ex)
+{
+ H_PUT_16 (abfd, in->version, ex->version);
+ H_PUT_8 (abfd, in->isa_level, ex->isa_level);
+ H_PUT_8 (abfd, in->isa_rev, ex->isa_rev);
+ H_PUT_8 (abfd, in->gpr_size, ex->gpr_size);
+ H_PUT_8 (abfd, in->cpr1_size, ex->cpr1_size);
+ H_PUT_8 (abfd, in->cpr2_size, ex->cpr2_size);
+ H_PUT_8 (abfd, in->fp_abi, ex->fp_abi);
+ H_PUT_32 (abfd, in->isa_ext, ex->isa_ext);
+ H_PUT_32 (abfd, in->ases, ex->ases);
+ H_PUT_32 (abfd, in->flags1, ex->flags1);
+ H_PUT_32 (abfd, in->flags2, ex->flags2);
+}
\f
/* This function is called via qsort() to sort the dynamic relocation
entries by increasing r_symndx value. */
if (hd->needs_lazy_stub)
{
+ BFD_ASSERT (hd->root.plt.plist != NULL);
+ BFD_ASSERT (hd->root.plt.plist->stub_offset != MINUS_ONE);
/* Set type and value for a symbol with a function stub. */
h->esym.asym.st = stProc;
sec = hd->root.root.u.def.section;
{
output_section = sec->output_section;
if (output_section != NULL)
- h->esym.asym.value = (hd->root.plt.offset
+ h->esym.asym.value = (hd->root.plt.plist->stub_offset
+ sec->output_offset
+ output_section->vma);
else
#endif
}
-/* got_entries only match if they're identical, except for gotidx, so
- use all fields to compute the hash, and compare the appropriate
- union members. */
-
static hashval_t
mips_elf_got_entry_hash (const void *entry_)
{
const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
- return entry->symndx
- + ((entry->tls_type & GOT_TLS_LDM) << 17)
- + (! entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address)
- : entry->abfd->id
- + (entry->symndx >= 0 ? mips_elf_hash_bfd_vma (entry->d.addend)
- : entry->d.h->root.root.root.hash));
+ return (entry->symndx
+ + ((entry->tls_type == GOT_TLS_LDM) << 18)
+ + (entry->tls_type == GOT_TLS_LDM ? 0
+ : !entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address)
+ : entry->symndx >= 0 ? (entry->abfd->id
+ + mips_elf_hash_bfd_vma (entry->d.addend))
+ : entry->d.h->root.root.root.hash));
}
static int
const struct mips_got_entry *e1 = (struct mips_got_entry *)entry1;
const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2;
- /* An LDM entry can only match another LDM entry. */
- if ((e1->tls_type ^ e2->tls_type) & GOT_TLS_LDM)
- return 0;
-
- return e1->abfd == e2->abfd && e1->symndx == e2->symndx
- && (! e1->abfd ? e1->d.address == e2->d.address
- : e1->symndx >= 0 ? e1->d.addend == e2->d.addend
- : e1->d.h == e2->d.h);
+ return (e1->symndx == e2->symndx
+ && e1->tls_type == e2->tls_type
+ && (e1->tls_type == GOT_TLS_LDM ? TRUE
+ : !e1->abfd ? !e2->abfd && e1->d.address == e2->d.address
+ : e1->symndx >= 0 ? (e1->abfd == e2->abfd
+ && e1->d.addend == e2->d.addend)
+ : e2->abfd && e1->d.h == e2->d.h));
}
-/* multi_got_entries are still a match in the case of global objects,
- even if the input bfd in which they're referenced differs, so the
- hash computation and compare functions are adjusted
- accordingly. */
-
static hashval_t
-mips_elf_multi_got_entry_hash (const void *entry_)
+mips_got_page_ref_hash (const void *ref_)
{
- const struct mips_got_entry *entry = (struct mips_got_entry *)entry_;
+ const struct mips_got_page_ref *ref;
- return entry->symndx
- + (! entry->abfd
- ? mips_elf_hash_bfd_vma (entry->d.address)
- : entry->symndx >= 0
- ? ((entry->tls_type & GOT_TLS_LDM)
- ? (GOT_TLS_LDM << 17)
- : (entry->abfd->id
- + mips_elf_hash_bfd_vma (entry->d.addend)))
- : entry->d.h->root.root.root.hash);
+ ref = (const struct mips_got_page_ref *) ref_;
+ return ((ref->symndx >= 0
+ ? (hashval_t) (ref->u.abfd->id + ref->symndx)
+ : ref->u.h->root.root.root.hash)
+ + mips_elf_hash_bfd_vma (ref->addend));
}
static int
-mips_elf_multi_got_entry_eq (const void *entry1, const void *entry2)
+mips_got_page_ref_eq (const void *ref1_, const void *ref2_)
{
- 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;
+ const struct mips_got_page_ref *ref1, *ref2;
- return e1->symndx == e2->symndx
- && (e1->symndx >= 0 ? e1->abfd == e2->abfd && e1->d.addend == e2->d.addend
- : e1->abfd == NULL || e2->abfd == NULL
- ? e1->abfd == e2->abfd && e1->d.address == e2->d.address
- : e1->d.h == e2->d.h);
+ ref1 = (const struct mips_got_page_ref *) ref1_;
+ ref2 = (const struct mips_got_page_ref *) ref2_;
+ return (ref1->symndx == ref2->symndx
+ && (ref1->symndx < 0
+ ? ref1->u.h == ref2->u.h
+ : ref1->u.abfd == ref2->u.abfd)
+ && ref1->addend == ref2->addend);
}
static hashval_t
const struct mips_got_page_entry *entry;
entry = (const struct mips_got_page_entry *) entry_;
- return entry->abfd->id + entry->symndx;
+ return entry->sec->id;
}
static int
entry1 = (const struct mips_got_page_entry *) entry1_;
entry2 = (const struct mips_got_page_entry *) entry2_;
- return entry1->abfd == entry2->abfd && entry1->symndx == entry2->symndx;
+ return entry1->sec == entry2->sec;
}
\f
+/* Create and return a new mips_got_info structure. */
+
+static struct mips_got_info *
+mips_elf_create_got_info (bfd *abfd)
+{
+ struct mips_got_info *g;
+
+ g = bfd_zalloc (abfd, sizeof (struct mips_got_info));
+ if (g == NULL)
+ return NULL;
+
+ g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
+ mips_elf_got_entry_eq, NULL);
+ if (g->got_entries == NULL)
+ return NULL;
+
+ g->got_page_refs = htab_try_create (1, mips_got_page_ref_hash,
+ mips_got_page_ref_eq, NULL);
+ if (g->got_page_refs == NULL)
+ return NULL;
+
+ return g;
+}
+
+/* Return the GOT info for input bfd ABFD, trying to create a new one if
+ CREATE_P and if ABFD doesn't already have a GOT. */
+
+static struct mips_got_info *
+mips_elf_bfd_got (bfd *abfd, bfd_boolean create_p)
+{
+ struct mips_elf_obj_tdata *tdata;
+
+ if (!is_mips_elf (abfd))
+ return NULL;
+
+ tdata = mips_elf_tdata (abfd);
+ if (!tdata->got && create_p)
+ tdata->got = mips_elf_create_got_info (abfd);
+ return tdata->got;
+}
+
+/* Record that ABFD should use output GOT G. */
+
+static void
+mips_elf_replace_bfd_got (bfd *abfd, struct mips_got_info *g)
+{
+ struct mips_elf_obj_tdata *tdata;
+
+ BFD_ASSERT (is_mips_elf (abfd));
+ tdata = mips_elf_tdata (abfd);
+ if (tdata->got)
+ {
+ /* The GOT structure itself and the hash table entries are
+ allocated to a bfd, but the hash tables aren't. */
+ htab_delete (tdata->got->got_entries);
+ htab_delete (tdata->got->got_page_refs);
+ if (tdata->got->got_page_entries)
+ htab_delete (tdata->got->got_page_entries);
+ }
+ tdata->got = g;
+}
+
/* Return the dynamic relocation section. If it doesn't exist, try to
create a new it if CREATE_P, otherwise return NULL. Also return NULL
if creation fails. */
dname = MIPS_ELF_REL_DYN_NAME (info);
dynobj = elf_hash_table (info)->dynobj;
- sreloc = bfd_get_section_by_name (dynobj, dname);
+ sreloc = bfd_get_linker_section (dynobj, dname);
if (sreloc == NULL && create_p)
{
- sreloc = bfd_make_section_with_flags (dynobj, dname,
- (SEC_ALLOC
- | SEC_LOAD
- | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY
- | SEC_LINKER_CREATED
- | SEC_READONLY));
+ sreloc = bfd_make_section_anyway_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_alignment (dynobj, sreloc,
MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
return sreloc;
}
+/* Return the GOT_TLS_* type required by relocation type R_TYPE. */
+
+static int
+mips_elf_reloc_tls_type (unsigned int r_type)
+{
+ if (tls_gd_reloc_p (r_type))
+ return GOT_TLS_GD;
+
+ if (tls_ldm_reloc_p (r_type))
+ return GOT_TLS_LDM;
+
+ if (tls_gottprel_reloc_p (r_type))
+ return GOT_TLS_IE;
+
+ return GOT_TLS_NONE;
+}
+
+/* Return the number of GOT slots needed for GOT TLS type TYPE. */
+
+static int
+mips_tls_got_entries (unsigned int type)
+{
+ switch (type)
+ {
+ case GOT_TLS_GD:
+ case GOT_TLS_LDM:
+ return 2;
+
+ case GOT_TLS_IE:
+ return 1;
+
+ case GOT_TLS_NONE:
+ return 0;
+ }
+ abort ();
+}
+
/* Count the number of relocations needed for a TLS GOT entry, with
access types from TLS_TYPE, and symbol H (or a local symbol if H
is NULL). */
struct elf_link_hash_entry *h)
{
int indx = 0;
- int ret = 0;
bfd_boolean need_relocs = FALSE;
bfd_boolean dyn = elf_hash_table (info)->dynamic_sections_created;
need_relocs = TRUE;
if (!need_relocs)
- return FALSE;
+ return 0;
- if (tls_type & GOT_TLS_GD)
+ switch (tls_type)
{
- 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. */
+ case GOT_TLS_GD:
+ return indx != 0 ? 2 : 1;
-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;
+ case GOT_TLS_IE:
+ return 1;
- if (hm->tls_type & GOT_TLS_GD)
- arg->needed += 2;
- if (hm->tls_type & GOT_TLS_IE)
- arg->needed += 1;
+ case GOT_TLS_LDM:
+ return info->shared ? 1 : 0;
- return 1;
+ default:
+ return 0;
+ }
}
-/* Count the number of TLS relocations required for the global (or
- forced-local) symbol in ARG1. */
+/* Add the number of GOT entries and TLS relocations required by ENTRY
+ to G. */
-static int
-mips_elf_count_global_tls_relocs (void *arg1, void *arg2)
+static void
+mips_elf_count_got_entry (struct bfd_link_info *info,
+ struct mips_got_info *g,
+ struct mips_got_entry *entry)
{
- 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;
+ if (entry->tls_type)
+ {
+ g->tls_gotno += mips_tls_got_entries (entry->tls_type);
+ g->relocs += mips_tls_got_relocs (info, entry->tls_type,
+ entry->symndx < 0
+ ? &entry->d.h->root : NULL);
+ }
+ else if (entry->symndx >= 0 || entry->d.h->global_got_area == GGA_NONE)
+ g->local_gotno += 1;
+ else
+ g->global_gotno += 1;
}
/* Output a simple dynamic relocation into SRELOC. */
/* Initialize a set of TLS GOT entries for one symbol. */
static void
-mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset,
- unsigned char *tls_type_p,
- struct bfd_link_info *info,
+mips_elf_initialize_tls_slots (bfd *abfd, struct bfd_link_info *info,
+ struct mips_got_entry *entry,
struct mips_elf_link_hash_entry *h,
bfd_vma value)
{
struct mips_elf_link_hash_table *htab;
int indx;
asection *sreloc, *sgot;
- bfd_vma offset, offset2;
+ bfd_vma got_offset, got_offset2;
bfd_boolean need_relocs = FALSE;
htab = mips_elf_hash_table (info);
indx = h->root.dynindx;
}
- if (*tls_type_p & GOT_TLS_DONE)
+ if (entry->tls_initialized)
return;
if ((info->shared || indx != 0)
/* Emit necessary relocations. */
sreloc = mips_elf_rel_dyn_section (info, FALSE);
+ got_offset = entry->gotidx;
- /* General Dynamic. */
- if (*tls_type_p & GOT_TLS_GD)
+ switch (entry->tls_type)
{
- offset = got_offset;
- offset2 = offset + MIPS_ELF_GOT_SIZE (abfd);
+ case GOT_TLS_GD:
+ /* General Dynamic. */
+ got_offset2 = got_offset + MIPS_ELF_GOT_SIZE (abfd);
if (need_relocs)
{
mips_elf_output_dynamic_relocation
(abfd, sreloc, sreloc->reloc_count++, indx,
ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
- sgot->output_offset + sgot->output_section->vma + offset);
+ sgot->output_offset + sgot->output_section->vma + got_offset);
if (indx)
mips_elf_output_dynamic_relocation
(abfd, sreloc, sreloc->reloc_count++, indx,
ABI_64_P (abfd) ? R_MIPS_TLS_DTPREL64 : R_MIPS_TLS_DTPREL32,
- sgot->output_offset + sgot->output_section->vma + offset2);
+ sgot->output_offset + sgot->output_section->vma + got_offset2);
else
MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
- sgot->contents + offset2);
+ sgot->contents + got_offset2);
}
else
{
MIPS_ELF_PUT_WORD (abfd, 1,
- sgot->contents + offset);
+ sgot->contents + got_offset);
MIPS_ELF_PUT_WORD (abfd, value - dtprel_base (info),
- sgot->contents + offset2);
+ sgot->contents + got_offset2);
}
+ break;
- got_offset += 2 * MIPS_ELF_GOT_SIZE (abfd);
- }
-
- /* Initial Exec model. */
- if (*tls_type_p & GOT_TLS_IE)
- {
- offset = got_offset;
-
+ case GOT_TLS_IE:
+ /* Initial Exec model. */
if (need_relocs)
{
if (indx == 0)
MIPS_ELF_PUT_WORD (abfd, value - elf_hash_table (info)->tls_sec->vma,
- sgot->contents + offset);
+ sgot->contents + got_offset);
else
MIPS_ELF_PUT_WORD (abfd, 0,
- sgot->contents + offset);
+ sgot->contents + got_offset);
mips_elf_output_dynamic_relocation
(abfd, sreloc, sreloc->reloc_count++, indx,
ABI_64_P (abfd) ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32,
- sgot->output_offset + sgot->output_section->vma + offset);
+ sgot->output_offset + sgot->output_section->vma + got_offset);
}
else
MIPS_ELF_PUT_WORD (abfd, value - tprel_base (info),
- sgot->contents + offset);
- }
+ sgot->contents + got_offset);
+ break;
- if (*tls_type_p & GOT_TLS_LDM)
- {
+ case GOT_TLS_LDM:
/* The initial offset is zero, and the LD offsets will include the
bias by DTP_OFFSET. */
MIPS_ELF_PUT_WORD (abfd, 0,
(abfd, sreloc, sreloc->reloc_count++, indx,
ABI_64_P (abfd) ? R_MIPS_TLS_DTPMOD64 : R_MIPS_TLS_DTPMOD32,
sgot->output_offset + sgot->output_section->vma + got_offset);
- }
-
- *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 (tls_gottprel_reloc_p (r_type)
- || tls_gd_reloc_p (r_type)
- || tls_ldm_reloc_p (r_type));
-
- mips_elf_initialize_tls_slots (abfd, got_index, tls_type, info, h, symbol);
-
- if (tls_gottprel_reloc_p (r_type))
- {
- BFD_ASSERT (*tls_type & GOT_TLS_IE);
- if (*tls_type & GOT_TLS_GD)
- return got_index + 2 * MIPS_ELF_GOT_SIZE (abfd);
- else
- return got_index;
- }
-
- if (tls_gd_reloc_p (r_type))
- {
- BFD_ASSERT (*tls_type & GOT_TLS_GD);
- return got_index;
- }
+ break;
- if (tls_ldm_reloc_p (r_type))
- {
- BFD_ASSERT (*tls_type & GOT_TLS_LDM);
- return got_index;
+ default:
+ abort ();
}
- return got_index;
+ entry->tls_initialized = TRUE;
}
/* Return the offset from _GLOBAL_OFFSET_TABLE_ of the .got.plt entry
mips_elf_gotplt_index (struct bfd_link_info *info,
struct elf_link_hash_entry *h)
{
- bfd_vma plt_index, got_address, got_value;
+ bfd_vma got_address, got_value;
struct mips_elf_link_hash_table *htab;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
- BFD_ASSERT (h->plt.offset != (bfd_vma) -1);
-
- /* This function only works for VxWorks, because a non-VxWorks .got.plt
- section starts with reserved entries. */
- BFD_ASSERT (htab->is_vxworks);
-
- /* Calculate the index of the symbol's PLT entry. */
- plt_index = (h->plt.offset - htab->plt_header_size) / htab->plt_entry_size;
+ BFD_ASSERT (h->plt.plist != NULL);
+ BFD_ASSERT (h->plt.plist->gotplt_index != MINUS_ONE);
/* Calculate the address of the associated .got.plt entry. */
got_address = (htab->sgotplt->output_section->vma
+ htab->sgotplt->output_offset
- + plt_index * 4);
+ + (h->plt.plist->gotplt_index
+ * MIPS_ELF_GOT_SIZE (info->output_bfd)));
/* Calculate the value of _GLOBAL_OFFSET_TABLE_. */
got_value = (htab->root.hgot->root.u.def.section->output_section->vma
if (!entry)
return MINUS_ONE;
- if (TLS_RELOC_P (r_type))
- {
- if (entry->symndx == -1 && htab->got_info->next == NULL)
- /* A type (3) entry in the single-GOT case. We use the symbol's
- hash table entry to track the index. */
- return mips_tls_got_index (abfd, h->tls_got_offset, &h->tls_type,
- r_type, info, h, value);
- else
- return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type,
- r_type, info, h, value);
- }
- else
- return entry->gotidx;
+ if (entry->tls_type)
+ mips_elf_initialize_tls_slots (abfd, info, entry, h, value);
+ return entry->gotidx;
}
-/* Returns the GOT index for the global symbol indicated by H. */
+/* Return the GOT index of global symbol H in the primary GOT. */
static bfd_vma
-mips_elf_global_got_index (bfd *abfd, bfd *ibfd, struct elf_link_hash_entry *h,
- int r_type, struct bfd_link_info *info)
+mips_elf_primary_global_got_index (bfd *obfd, struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
{
struct mips_elf_link_hash_table *htab;
+ long global_got_dynindx;
+ struct mips_got_info *g;
bfd_vma got_index;
- struct mips_got_info *g, *gg;
- long global_got_dynindx = 0;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
- gg = g = htab->got_info;
- if (g->bfd2got && ibfd)
- {
- struct mips_got_entry e, *p;
+ global_got_dynindx = 0;
+ if (htab->global_gotsym != NULL)
+ global_got_dynindx = htab->global_gotsym->dynindx;
+
+ /* Once we determine the global GOT entry with the lowest dynamic
+ symbol table index, we must put all dynamic symbols with greater
+ indices into the primary GOT. That makes it easy to calculate the
+ GOT offset. */
+ BFD_ASSERT (h->dynindx >= global_got_dynindx);
+ g = mips_elf_bfd_got (obfd, FALSE);
+ got_index = ((h->dynindx - global_got_dynindx + g->local_gotno)
+ * MIPS_ELF_GOT_SIZE (obfd));
+ BFD_ASSERT (got_index < htab->sgot->size);
- BFD_ASSERT (h->dynindx >= 0);
+ return got_index;
+}
- g = mips_elf_got_for_ibfd (g, ibfd);
- if (g->next != gg || TLS_RELOC_P (r_type))
- {
- e.abfd = ibfd;
- e.symndx = -1;
- e.d.h = (struct mips_elf_link_hash_entry *)h;
- e.tls_type = 0;
+/* Return the GOT index for the global symbol indicated by H, which is
+ referenced by a relocation of type R_TYPE in IBFD. */
- p = htab_find (g->got_entries, &e);
+static bfd_vma
+mips_elf_global_got_index (bfd *obfd, struct bfd_link_info *info, bfd *ibfd,
+ struct elf_link_hash_entry *h, int r_type)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_info *g;
+ struct mips_got_entry lookup, *entry;
+ bfd_vma gotidx;
- BFD_ASSERT (p->gotidx > 0);
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
- 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;
- }
- }
+ g = mips_elf_bfd_got (ibfd, FALSE);
+ BFD_ASSERT (g);
+
+ lookup.tls_type = mips_elf_reloc_tls_type (r_type);
+ if (!lookup.tls_type && g == mips_elf_bfd_got (obfd, FALSE))
+ return mips_elf_primary_global_got_index (obfd, info, h);
+
+ lookup.abfd = ibfd;
+ lookup.symndx = -1;
+ lookup.d.h = (struct mips_elf_link_hash_entry *) h;
+ entry = htab_find (g->got_entries, &lookup);
+ BFD_ASSERT (entry);
- if (gg->global_gotsym != NULL)
- global_got_dynindx = gg->global_gotsym->dynindx;
+ gotidx = entry->gotidx;
+ BFD_ASSERT (gotidx > 0 && gotidx < htab->sgot->size);
- if (TLS_RELOC_P (r_type))
+ if (lookup.tls_type)
{
- struct mips_elf_link_hash_entry *hm
- = (struct mips_elf_link_hash_entry *) h;
bfd_vma value = MINUS_ONE;
if ((h->root.type == bfd_link_hash_defined
+ h->root.u.def.section->output_offset
+ h->root.u.def.section->output_section->vma);
- got_index = mips_tls_got_index (abfd, hm->tls_got_offset, &hm->tls_type,
- r_type, info, hm, value);
+ mips_elf_initialize_tls_slots (obfd, info, entry, lookup.d.h, value);
}
- else
- {
- /* Once we determine the global GOT entry with the lowest dynamic
- symbol table index, we must put all dynamic symbols with greater
- indices into the GOT. That makes it easy to calculate the GOT
- offset. */
- BFD_ASSERT (h->dynindx >= global_got_dynindx);
- got_index = ((h->dynindx - global_got_dynindx + g->local_gotno)
- * MIPS_ELF_GOT_SIZE (abfd));
- }
- BFD_ASSERT (got_index < htab->sgot->size);
-
- return got_index;
+ return gotidx;
}
/* Find a GOT page entry that points to within 32KB of VALUE. These
struct mips_elf_link_hash_entry *h,
int r_type)
{
- struct mips_got_entry entry, **loc;
+ struct mips_got_entry lookup, *entry;
+ void **loc;
struct mips_got_info *g;
struct mips_elf_link_hash_table *htab;
+ bfd_vma gotidx;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
- entry.abfd = NULL;
- entry.symndx = -1;
- entry.d.address = value;
- entry.tls_type = 0;
-
- g = mips_elf_got_for_ibfd (htab->got_info, ibfd);
+ g = mips_elf_bfd_got (ibfd, FALSE);
if (g == NULL)
{
- g = mips_elf_got_for_ibfd (htab->got_info, abfd);
+ g = mips_elf_bfd_got (abfd, FALSE);
BFD_ASSERT (g != NULL);
}
/* This function shouldn't be called for symbols that live in the global
area of the GOT. */
BFD_ASSERT (h == NULL || h->global_got_area == GGA_NONE);
- if (TLS_RELOC_P (r_type))
- {
- struct mips_got_entry *p;
- entry.abfd = ibfd;
+ lookup.tls_type = mips_elf_reloc_tls_type (r_type);
+ if (lookup.tls_type)
+ {
+ lookup.abfd = ibfd;
if (tls_ldm_reloc_p (r_type))
{
- entry.tls_type = GOT_TLS_LDM;
- entry.symndx = 0;
- entry.d.addend = 0;
+ lookup.symndx = 0;
+ lookup.d.addend = 0;
}
else if (h == NULL)
{
- entry.symndx = r_symndx;
- entry.d.addend = 0;
+ lookup.symndx = r_symndx;
+ lookup.d.addend = 0;
}
else
- entry.d.h = h;
-
- p = (struct mips_got_entry *)
- htab_find (g->got_entries, &entry);
-
- BFD_ASSERT (p);
- return p;
- }
+ {
+ lookup.symndx = -1;
+ lookup.d.h = h;
+ }
- loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
- INSERT);
- if (*loc)
- return *loc;
+ entry = (struct mips_got_entry *) htab_find (g->got_entries, &lookup);
+ BFD_ASSERT (entry);
- entry.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++;
- entry.tls_type = 0;
+ gotidx = entry->gotidx;
+ BFD_ASSERT (gotidx > 0 && gotidx < htab->sgot->size);
- *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+ return entry;
+ }
- if (! *loc)
+ lookup.abfd = NULL;
+ lookup.symndx = -1;
+ lookup.d.address = value;
+ loc = htab_find_slot (g->got_entries, &lookup, INSERT);
+ if (!loc)
return NULL;
- memcpy (*loc, &entry, sizeof entry);
+ entry = (struct mips_got_entry *) *loc;
+ if (entry)
+ return entry;
- if (g->assigned_gotno > g->local_gotno)
+ if (g->assigned_low_gotno > g->assigned_high_gotno)
{
- (*loc)->gotidx = -1;
/* We didn't allocate enough space in the GOT. */
(*_bfd_error_handler)
(_("not enough GOT space for local GOT entries"));
return NULL;
}
- MIPS_ELF_PUT_WORD (abfd, value,
- (htab->sgot->contents + entry.gotidx));
+ entry = (struct mips_got_entry *) bfd_alloc (abfd, sizeof (*entry));
+ if (!entry)
+ return NULL;
+
+ if (got16_reloc_p (r_type)
+ || call16_reloc_p (r_type)
+ || got_page_reloc_p (r_type)
+ || got_disp_reloc_p (r_type))
+ lookup.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_low_gotno++;
+ else
+ lookup.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_high_gotno--;
+
+ *entry = lookup;
+ *loc = entry;
+
+ MIPS_ELF_PUT_WORD (abfd, value, htab->sgot->contents + entry->gotidx);
/* These GOT entries need a dynamic relocation on VxWorks. */
if (htab->is_vxworks)
s = mips_elf_rel_dyn_section (info, FALSE);
got_address = (htab->sgot->output_section->vma
+ htab->sgot->output_offset
- + entry.gotidx);
+ + entry->gotidx);
rloc = s->contents + (s->reloc_count++ * sizeof (Elf32_External_Rela));
outrel.r_offset = got_address;
bfd_elf32_swap_reloca_out (abfd, &outrel, rloc);
}
- return *loc;
+ return entry;
}
/* Return the number of dynamic section symbols required by OUTPUT_BFD.
/* Now we know which dynamic symbol has the lowest dynamic symbol
table index in the GOT. */
- g->global_gotsym = hsd.low;
+ htab->global_gotsym = hsd.low;
return TRUE;
}
break;
case GGA_NORMAL:
- BFD_ASSERT (h->tls_type == GOT_NORMAL);
-
h->root.dynindx = --hsd->min_got_dynindx;
hsd->low = (struct elf_link_hash_entry *) h;
break;
case GGA_RELOC_ONLY:
- BFD_ASSERT (h->tls_type == GOT_NORMAL);
-
if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx)
hsd->low = (struct elf_link_hash_entry *) h;
h->root.dynindx = hsd->max_unref_got_dynindx++;
return TRUE;
}
-/* If H is a symbol that needs a global GOT entry, but has a dynamic
- symbol table index lower than any we've seen to date, record it for
- posterity. FOR_CALL is true if the caller is only interested in
+/* Record that input bfd ABFD requires a GOT entry like *LOOKUP
+ (which is owned by the caller and shouldn't be added to the
+ hash table directly). */
+
+static bfd_boolean
+mips_elf_record_got_entry (struct bfd_link_info *info, bfd *abfd,
+ struct mips_got_entry *lookup)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_entry *entry;
+ struct mips_got_info *g;
+ void **loc, **bfd_loc;
+
+ /* Make sure there's a slot for this entry in the master GOT. */
+ htab = mips_elf_hash_table (info);
+ g = htab->got_info;
+ loc = htab_find_slot (g->got_entries, lookup, INSERT);
+ if (!loc)
+ return FALSE;
+
+ /* Populate the entry if it isn't already. */
+ entry = (struct mips_got_entry *) *loc;
+ if (!entry)
+ {
+ entry = (struct mips_got_entry *) bfd_alloc (abfd, sizeof (*entry));
+ if (!entry)
+ return FALSE;
+
+ lookup->tls_initialized = FALSE;
+ lookup->gotidx = -1;
+ *entry = *lookup;
+ *loc = entry;
+ }
+
+ /* Reuse the same GOT entry for the BFD's GOT. */
+ g = mips_elf_bfd_got (abfd, TRUE);
+ if (!g)
+ return FALSE;
+
+ bfd_loc = htab_find_slot (g->got_entries, lookup, INSERT);
+ if (!bfd_loc)
+ return FALSE;
+
+ if (!*bfd_loc)
+ *bfd_loc = entry;
+ return TRUE;
+}
+
+/* ABFD has a GOT relocation of type R_TYPE against H. Reserve a GOT
+ entry for it. FOR_CALL is true if the caller is only interested in
using the GOT entry for calls. */
static bfd_boolean
mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
bfd *abfd, struct bfd_link_info *info,
- bfd_boolean for_call,
- unsigned char tls_flag)
+ bfd_boolean for_call, int r_type)
{
struct mips_elf_link_hash_table *htab;
struct mips_elf_link_hash_entry *hmips;
- struct mips_got_entry entry, **loc;
- struct mips_got_info *g;
+ struct mips_got_entry entry;
+ unsigned char tls_type;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
return FALSE;
}
- /* Make sure we have a GOT to put this entry into. */
- g = htab->got_info;
- BFD_ASSERT (g != NULL);
+ tls_type = mips_elf_reloc_tls_type (r_type);
+ if (tls_type == GOT_TLS_NONE && hmips->global_got_area > GGA_NORMAL)
+ hmips->global_got_area = GGA_NORMAL;
entry.abfd = abfd;
entry.symndx = -1;
entry.d.h = (struct mips_elf_link_hash_entry *) h;
- entry.tls_type = 0;
+ entry.tls_type = tls_type;
+ return mips_elf_record_got_entry (info, abfd, &entry);
+}
+
+/* ABFD has a GOT relocation of type R_TYPE against symbol SYMNDX + ADDEND,
+ where SYMNDX is a local symbol. Reserve a GOT entry for it. */
+
+static bfd_boolean
+mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
+ struct bfd_link_info *info, int r_type)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_info *g;
+ struct mips_got_entry entry;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ g = htab->got_info;
+ BFD_ASSERT (g != NULL);
+
+ entry.abfd = abfd;
+ entry.symndx = symndx;
+ entry.d.addend = addend;
+ entry.tls_type = mips_elf_reloc_tls_type (r_type);
+ return mips_elf_record_got_entry (info, abfd, &entry);
+}
+
+/* Record that ABFD has a page relocation against SYMNDX + ADDEND.
+ H is the symbol's hash table entry, or null if SYMNDX is local
+ to ABFD. */
+
+static bfd_boolean
+mips_elf_record_got_page_ref (struct bfd_link_info *info, bfd *abfd,
+ long symndx, struct elf_link_hash_entry *h,
+ bfd_signed_vma addend)
+{
+ struct mips_elf_link_hash_table *htab;
+ struct mips_got_info *g1, *g2;
+ struct mips_got_page_ref lookup, *entry;
+ void **loc, **bfd_loc;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
- loc = (struct mips_got_entry **) htab_find_slot (g->got_entries, &entry,
- INSERT);
+ g1 = htab->got_info;
+ BFD_ASSERT (g1 != NULL);
- /* If we've already marked this entry as needing GOT space, we don't
- need to do it again. */
- if (*loc)
+ if (h)
{
- (*loc)->tls_type |= tls_flag;
- return TRUE;
+ lookup.symndx = -1;
+ lookup.u.h = (struct mips_elf_link_hash_entry *) h;
+ }
+ else
+ {
+ lookup.symndx = symndx;
+ lookup.u.abfd = abfd;
}
+ lookup.addend = addend;
+ loc = htab_find_slot (g1->got_page_refs, &lookup, INSERT);
+ if (loc == NULL)
+ return FALSE;
- *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+ entry = (struct mips_got_page_ref *) *loc;
+ if (!entry)
+ {
+ entry = bfd_alloc (abfd, sizeof (*entry));
+ if (!entry)
+ return FALSE;
- if (! *loc)
- return FALSE;
+ *entry = lookup;
+ *loc = entry;
+ }
- entry.gotidx = -1;
- entry.tls_type = tls_flag;
+ /* Add the same entry to the BFD's GOT. */
+ g2 = mips_elf_bfd_got (abfd, TRUE);
+ if (!g2)
+ return FALSE;
- memcpy (*loc, &entry, sizeof entry);
+ bfd_loc = htab_find_slot (g2->got_page_refs, &lookup, INSERT);
+ if (!bfd_loc)
+ return FALSE;
- if (tls_flag == 0)
- hmips->global_got_area = GGA_NORMAL;
+ if (!*bfd_loc)
+ *bfd_loc = entry;
return TRUE;
}
-/* Reserve space in G for a GOT entry containing the value of symbol
- SYMNDX in input bfd ABDF, plus ADDEND. */
+/* Add room for N relocations to the .rel(a).dyn section in ABFD. */
-static bfd_boolean
-mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
- struct bfd_link_info *info,
- unsigned char tls_flag)
+static void
+mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info,
+ unsigned int n)
{
+ asection *s;
struct mips_elf_link_hash_table *htab;
- struct mips_got_info *g;
- struct mips_got_entry entry, **loc;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
- g = htab->got_info;
- BFD_ASSERT (g != NULL);
-
- 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);
+ s = mips_elf_rel_dyn_section (info, FALSE);
+ BFD_ASSERT (s != NULL);
- if (*loc)
+ if (htab->is_vxworks)
+ s->size += n * MIPS_ELF_RELA_SIZE (abfd);
+ else
{
- 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))
+ if (s->size == 0)
{
- g->tls_gotno += 1;
- (*loc)->tls_type |= tls_flag;
+ /* Make room for a null element. */
+ s->size += MIPS_ELF_REL_SIZE (abfd);
+ ++s->reloc_count;
}
- return TRUE;
+ s->size += n * MIPS_ELF_REL_SIZE (abfd);
}
+}
+\f
+/* A htab_traverse callback for GOT entries, with DATA pointing to a
+ mips_elf_traverse_got_arg structure. Count the number of GOT
+ entries and TLS relocs. Set DATA->value to true if we need
+ to resolve indirect or warning symbols and then recreate the GOT. */
+
+static int
+mips_elf_check_recreate_got (void **entryp, void *data)
+{
+ struct mips_got_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
- if (tls_flag != 0)
+ entry = (struct mips_got_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ if (entry->abfd != NULL && entry->symndx == -1)
{
- 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)
+ struct mips_elf_link_hash_entry *h;
+
+ h = entry->d.h;
+ if (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
{
- g->tls_ldm_offset = MINUS_TWO;
- g->tls_gotno += 2;
+ arg->value = TRUE;
+ return 0;
}
}
- else
- {
- entry.gotidx = g->local_gotno++;
- entry.tls_type = 0;
- }
+ mips_elf_count_got_entry (arg->info, arg->g, entry);
+ return 1;
+}
- *loc = (struct mips_got_entry *)bfd_alloc (abfd, sizeof entry);
+/* A htab_traverse callback for GOT entries, with DATA pointing to a
+ mips_elf_traverse_got_arg structure. Add all entries to DATA->g,
+ converting entries for indirect and warning symbols into entries
+ for the target symbol. Set DATA->g to null on error. */
- if (! *loc)
- return FALSE;
+static int
+mips_elf_recreate_got (void **entryp, void *data)
+{
+ struct mips_got_entry new_entry, *entry;
+ struct mips_elf_traverse_got_arg *arg;
+ void **slot;
- memcpy (*loc, &entry, sizeof entry);
+ entry = (struct mips_got_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ if (entry->abfd != NULL
+ && entry->symndx == -1
+ && (entry->d.h->root.root.type == bfd_link_hash_indirect
+ || entry->d.h->root.root.type == bfd_link_hash_warning))
+ {
+ struct mips_elf_link_hash_entry *h;
- return TRUE;
+ new_entry = *entry;
+ entry = &new_entry;
+ h = entry->d.h;
+ do
+ {
+ BFD_ASSERT (h->global_got_area == GGA_NONE);
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+ }
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning);
+ entry->d.h = h;
+ }
+ slot = htab_find_slot (arg->g->got_entries, entry, INSERT);
+ if (slot == NULL)
+ {
+ arg->g = NULL;
+ return 0;
+ }
+ if (*slot == NULL)
+ {
+ if (entry == &new_entry)
+ {
+ entry = bfd_alloc (entry->abfd, sizeof (*entry));
+ if (!entry)
+ {
+ arg->g = NULL;
+ return 0;
+ }
+ *entry = new_entry;
+ }
+ *slot = entry;
+ mips_elf_count_got_entry (arg->info, arg->g, entry);
+ }
+ return 1;
}
/* Return the maximum number of GOT page entries required for RANGE. */
return (range->max_addend - range->min_addend + 0x1ffff) >> 16;
}
-/* Record that ABFD has a page relocation against symbol SYMNDX and
- that ADDEND is the addend for that relocation.
-
- This function creates an upper bound on the number of GOT slots
- required; no attempt is made to combine references to non-overridable
- global symbols across multiple input files. */
+/* Record that G requires a page entry that can reach SEC + ADDEND. */
static bfd_boolean
-mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
- long symndx, bfd_signed_vma addend)
+mips_elf_record_got_page_entry (struct mips_elf_traverse_got_arg *arg,
+ asection *sec, bfd_signed_vma addend)
{
- struct mips_elf_link_hash_table *htab;
- struct mips_got_info *g;
+ struct mips_got_info *g = arg->g;
struct mips_got_page_entry lookup, *entry;
struct mips_got_page_range **range_ptr, *range;
bfd_vma old_pages, new_pages;
void **loc;
- htab = mips_elf_hash_table (info);
- BFD_ASSERT (htab != NULL);
-
- g = htab->got_info;
- BFD_ASSERT (g != NULL);
-
- /* Find the mips_got_page_entry hash table entry for this symbol. */
- lookup.abfd = abfd;
- lookup.symndx = symndx;
+ /* Find the mips_got_page_entry hash table entry for this section. */
+ lookup.sec = sec;
loc = htab_find_slot (g->got_page_entries, &lookup, INSERT);
if (loc == NULL)
return FALSE;
/* Create a mips_got_page_entry if this is the first time we've
- seen the symbol. */
+ seen the section. */
entry = (struct mips_got_page_entry *) *loc;
if (!entry)
{
- entry = bfd_alloc (abfd, sizeof (*entry));
+ entry = bfd_zalloc (arg->info->output_bfd, sizeof (*entry));
if (!entry)
return FALSE;
- entry->abfd = abfd;
- entry->symndx = symndx;
- entry->ranges = NULL;
- entry->num_pages = 0;
+ entry->sec = sec;
*loc = entry;
}
range = *range_ptr;
if (!range || addend < range->min_addend - 0xffff)
{
- range = bfd_alloc (abfd, sizeof (*range));
+ range = bfd_zalloc (arg->info->output_bfd, sizeof (*range));
if (!range)
return FALSE;
return TRUE;
}
-/* Add room for N relocations to the .rel(a).dyn section in ABFD. */
+/* A htab_traverse callback for which *REFP points to a mips_got_page_ref
+ and for which DATA points to a mips_elf_traverse_got_arg. Work out
+ whether the page reference described by *REFP needs a GOT page entry,
+ and record that entry in DATA->g if so. Set DATA->g to null on failure. */
-static void
-mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info,
- unsigned int n)
+static bfd_boolean
+mips_elf_resolve_got_page_ref (void **refp, void *data)
{
- asection *s;
+ struct mips_got_page_ref *ref;
+ struct mips_elf_traverse_got_arg *arg;
struct mips_elf_link_hash_table *htab;
+ asection *sec;
+ bfd_vma addend;
- htab = mips_elf_hash_table (info);
- BFD_ASSERT (htab != NULL);
-
- s = mips_elf_rel_dyn_section (info, FALSE);
- BFD_ASSERT (s != NULL);
+ ref = (struct mips_got_page_ref *) *refp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ htab = mips_elf_hash_table (arg->info);
- if (htab->is_vxworks)
- s->size += n * MIPS_ELF_RELA_SIZE (abfd);
- else
+ if (ref->symndx < 0)
{
- if (s->size == 0)
- {
- /* Make room for a null element. */
- s->size += MIPS_ELF_REL_SIZE (abfd);
- ++s->reloc_count;
- }
- s->size += n * MIPS_ELF_REL_SIZE (abfd);
- }
-}
-\f
-/* A htab_traverse callback for GOT entries. Set boolean *DATA to true
- if the GOT entry is for an indirect or warning symbol. */
+ struct mips_elf_link_hash_entry *h;
-static int
-mips_elf_check_recreate_got (void **entryp, void *data)
-{
- struct mips_got_entry *entry;
- bfd_boolean *must_recreate;
+ /* Global GOT_PAGEs decay to GOT_DISP and so don't need page entries. */
+ h = ref->u.h;
+ if (!SYMBOL_REFERENCES_LOCAL (arg->info, &h->root))
+ return 1;
- entry = (struct mips_got_entry *) *entryp;
- must_recreate = (bfd_boolean *) data;
- if (entry->abfd != NULL && entry->symndx == -1)
+ /* Ignore undefined symbols; we'll issue an error later if
+ appropriate. */
+ if (!((h->root.root.type == bfd_link_hash_defined
+ || h->root.root.type == bfd_link_hash_defweak)
+ && h->root.root.u.def.section))
+ return 1;
+
+ sec = h->root.root.u.def.section;
+ addend = h->root.root.u.def.value + ref->addend;
+ }
+ else
{
- struct mips_elf_link_hash_entry *h;
+ Elf_Internal_Sym *isym;
- h = entry->d.h;
- if (h->root.root.type == bfd_link_hash_indirect
- || h->root.root.type == bfd_link_hash_warning)
+ /* Read in the symbol. */
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache, ref->u.abfd,
+ ref->symndx);
+ if (isym == NULL)
{
- *must_recreate = TRUE;
+ arg->g = NULL;
return 0;
}
- }
- return 1;
-}
-
-/* A htab_traverse callback for GOT entries. Add all entries to
- hash table *DATA, converting entries for indirect and warning
- symbols into entries for the target symbol. Set *DATA to null
- on error. */
-
-static int
-mips_elf_recreate_got (void **entryp, void *data)
-{
- htab_t *new_got;
- struct mips_got_entry *entry;
- void **slot;
- new_got = (htab_t *) data;
- entry = (struct mips_got_entry *) *entryp;
- if (entry->abfd != NULL && entry->symndx == -1)
- {
- struct mips_elf_link_hash_entry *h;
+ /* Get the associated input section. */
+ sec = bfd_section_from_elf_index (ref->u.abfd, isym->st_shndx);
+ if (sec == NULL)
+ {
+ arg->g = NULL;
+ return 0;
+ }
- h = entry->d.h;
- while (h->root.root.type == bfd_link_hash_indirect
- || h->root.root.type == bfd_link_hash_warning)
+ /* If this is a mergable section, work out the section and offset
+ of the merged data. For section symbols, the addend specifies
+ of the offset _of_ the first byte in the data, otherwise it
+ specifies the offset _from_ the first byte. */
+ if (sec->flags & SEC_MERGE)
{
- BFD_ASSERT (h->global_got_area == GGA_NONE);
- h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+ void *secinfo;
+
+ secinfo = elf_section_data (sec)->sec_info;
+ if (ELF_ST_TYPE (isym->st_info) == STT_SECTION)
+ addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo,
+ isym->st_value + ref->addend);
+ else
+ addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo,
+ isym->st_value) + ref->addend;
}
- entry->d.h = h;
+ else
+ addend = isym->st_value + ref->addend;
}
- slot = htab_find_slot (*new_got, entry, INSERT);
- if (slot == NULL)
+ if (!mips_elf_record_got_page_entry (arg, sec, addend))
{
- *new_got = NULL;
+ arg->g = NULL;
return 0;
}
- if (*slot == NULL)
- *slot = entry;
- else
- free (entry);
return 1;
}
/* If any entries in G->got_entries are for indirect or warning symbols,
- replace them with entries for the target symbol. */
+ replace them with entries for the target symbol. Convert g->got_page_refs
+ into got_page_entry structures and estimate the number of page entries
+ that they require. */
static bfd_boolean
-mips_elf_resolve_final_got_entries (struct mips_got_info *g)
+mips_elf_resolve_final_got_entries (struct bfd_link_info *info,
+ struct mips_got_info *g)
{
- bfd_boolean must_recreate;
- htab_t new_got;
+ struct mips_elf_traverse_got_arg tga;
+ struct mips_got_info oldg;
+
+ oldg = *g;
- must_recreate = FALSE;
- htab_traverse (g->got_entries, mips_elf_check_recreate_got, &must_recreate);
- if (must_recreate)
+ tga.info = info;
+ tga.g = g;
+ tga.value = FALSE;
+ htab_traverse (g->got_entries, mips_elf_check_recreate_got, &tga);
+ if (tga.value)
{
- new_got = htab_create (htab_size (g->got_entries),
- mips_elf_got_entry_hash,
- mips_elf_got_entry_eq, NULL);
- htab_traverse (g->got_entries, mips_elf_recreate_got, &new_got);
- if (new_got == NULL)
+ *g = oldg;
+ g->got_entries = htab_create (htab_size (oldg.got_entries),
+ mips_elf_got_entry_hash,
+ mips_elf_got_entry_eq, NULL);
+ if (!g->got_entries)
return FALSE;
- /* Each entry in g->got_entries has either been copied to new_got
- or freed. Now delete the hash table itself. */
- htab_delete (g->got_entries);
- g->got_entries = new_got;
+ htab_traverse (oldg.got_entries, mips_elf_recreate_got, &tga);
+ if (!tga.g)
+ return FALSE;
+
+ htab_delete (oldg.got_entries);
}
+
+ g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+ mips_got_page_entry_eq, NULL);
+ if (g->got_page_entries == NULL)
+ return FALSE;
+
+ tga.info = info;
+ tga.g = g;
+ htab_traverse (g->got_page_refs, mips_elf_resolve_got_page_ref, &tga);
+
return TRUE;
}
-/* A mips_elf_link_hash_traverse callback for which DATA points
- to the link_info structure. Count the number of type (3) entries
- in the master GOT. */
+/* Return true if a GOT entry for H should live in the local rather than
+ global GOT area. */
+
+static bfd_boolean
+mips_use_local_got_p (struct bfd_link_info *info,
+ struct mips_elf_link_hash_entry *h)
+{
+ /* Symbols that aren't in the dynamic symbol table must live in the
+ local GOT. This includes symbols that are completely undefined
+ and which therefore don't bind locally. We'll report undefined
+ symbols later if appropriate. */
+ if (h->root.dynindx == -1)
+ return TRUE;
+
+ /* Symbols that bind locally can (and in the case of forced-local
+ symbols, must) live in the local GOT. */
+ if (h->got_only_for_calls
+ ? SYMBOL_CALLS_LOCAL (info, &h->root)
+ : SYMBOL_REFERENCES_LOCAL (info, &h->root))
+ return TRUE;
+
+ /* If this is an executable that must provide a definition of the symbol,
+ either though PLTs or copy relocations, then that address should go in
+ the local rather than global GOT. */
+ if (info->executable && h->has_static_relocs)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* A mips_elf_link_hash_traverse callback for which DATA points to the
+ link_info structure. Decide whether the hash entry needs an entry in
+ the global part of the primary GOT, setting global_got_area accordingly.
+ Count the number of global symbols that are in the primary GOT only
+ because they have relocations against them (reloc_only_gotno). */
static int
mips_elf_count_got_symbols (struct mips_elf_link_hash_entry *h, void *data)
if (h->global_got_area != GGA_NONE)
{
/* Make a final decision about whether the symbol belongs in the
- local or global GOT. Symbols that bind locally can (and in the
- case of forced-local symbols, must) live in the local GOT.
- Those that are aren't in the dynamic symbol table must also
- live in the local GOT.
-
- Note that the former condition does not always imply the
- latter: symbols do not bind locally if they are completely
- undefined. We'll report undefined symbols later if appropriate. */
- if (h->root.dynindx == -1
- || (h->got_only_for_calls
- ? SYMBOL_CALLS_LOCAL (info, &h->root)
- : SYMBOL_REFERENCES_LOCAL (info, &h->root)))
- {
- /* The symbol belongs in the local GOT. We no longer need this
- entry if it was only used for relocations; those relocations
- will be against the null or section symbol instead of H. */
- if (h->global_got_area != GGA_RELOC_ONLY)
- g->local_gotno++;
- h->global_got_area = GGA_NONE;
- }
+ local or global GOT. */
+ if (mips_use_local_got_p (info, h))
+ /* The symbol belongs in the local GOT. We no longer need this
+ entry if it was only used for relocations; those relocations
+ will be against the null or section symbol instead of H. */
+ h->global_got_area = GGA_NONE;
else if (htab->is_vxworks
&& h->got_only_for_calls
- && h->root.plt.offset != MINUS_ONE)
+ && h->root.plt.plist->mips_offset != MINUS_ONE)
/* On VxWorks, calls can refer directly to the .got.plt entry;
they don't need entries in the regular GOT. .got.plt entries
will be allocated by _bfd_mips_elf_adjust_dynamic_symbol. */
h->global_got_area = GGA_NONE;
- else
+ else if (h->global_got_area == GGA_RELOC_ONLY)
{
+ g->reloc_only_gotno++;
g->global_gotno++;
- if (h->global_got_area == GGA_RELOC_ONLY)
- g->reloc_only_gotno++;
}
}
return 1;
}
-\f
-/* Compute the hash value of the bfd in a bfd2got hash entry. */
-
-static hashval_t
-mips_elf_bfd2got_entry_hash (const void *entry_)
-{
- const struct mips_elf_bfd2got_hash *entry
- = (struct mips_elf_bfd2got_hash *)entry_;
-
- return entry->bfd->id;
-}
-
-/* Check whether two hash entries have the same bfd. */
-
-static int
-mips_elf_bfd2got_entry_eq (const void *entry1, const void *entry2)
-{
- const struct mips_elf_bfd2got_hash *e1
- = (const struct mips_elf_bfd2got_hash *)entry1;
- const struct mips_elf_bfd2got_hash *e2
- = (const struct mips_elf_bfd2got_hash *)entry2;
-
- return e1->bfd == e2->bfd;
-}
-
-/* In a multi-got link, determine the GOT to be used for IBFD. G must
- be the master GOT data. */
-
-static struct mips_got_info *
-mips_elf_got_for_ibfd (struct mips_got_info *g, bfd *ibfd)
-{
- struct mips_elf_bfd2got_hash e, *p;
-
- if (! g->bfd2got)
- return g;
-
- e.bfd = ibfd;
- p = htab_find (g->bfd2got, &e);
- return p ? p->g : NULL;
-}
-
-/* Use BFD2GOT to find ABFD's got entry, creating one if none exists.
- Return NULL if an error occured. */
-
-static struct mips_got_info *
-mips_elf_get_got_for_bfd (struct htab *bfd2got, bfd *output_bfd,
- bfd *input_bfd)
-{
- struct mips_elf_bfd2got_hash bfdgot_entry, *bfdgot;
- struct mips_got_info *g;
- void **bfdgotp;
-
- bfdgot_entry.bfd = input_bfd;
- bfdgotp = htab_find_slot (bfd2got, &bfdgot_entry, INSERT);
- bfdgot = (struct mips_elf_bfd2got_hash *) *bfdgotp;
-
- if (bfdgot == NULL)
- {
- bfdgot = ((struct mips_elf_bfd2got_hash *)
- bfd_alloc (output_bfd, sizeof (struct mips_elf_bfd2got_hash)));
- if (bfdgot == NULL)
- return NULL;
-
- *bfdgotp = bfdgot;
-
- g = ((struct mips_got_info *)
- bfd_alloc (output_bfd, sizeof (struct mips_got_info)));
- if (g == NULL)
- return NULL;
-
- bfdgot->bfd = input_bfd;
- bfdgot->g = g;
-
- g->global_gotsym = NULL;
- g->global_gotno = 0;
- g->reloc_only_gotno = 0;
- g->local_gotno = 0;
- g->page_gotno = 0;
- g->assigned_gotno = -1;
- g->tls_gotno = 0;
- g->tls_assigned_gotno = 0;
- g->tls_ldm_offset = MINUS_ONE;
- g->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
- mips_elf_multi_got_entry_eq, NULL);
- if (g->got_entries == NULL)
- return NULL;
-
- g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
- mips_got_page_entry_eq, NULL);
- if (g->got_page_entries == NULL)
- return NULL;
-
- g->bfd2got = NULL;
- g->next = NULL;
- }
-
- return bfdgot->g;
-}
-
-/* A htab_traverse callback for the entries in the master got.
- Create one separate got for each bfd that has entries in the global
- got, such that we can tell how many local and global entries each
- bfd requires. */
+\f
+/* A htab_traverse callback for GOT entries. Add each one to the GOT
+ given in mips_elf_traverse_got_arg DATA. Clear DATA->G on error. */
static int
-mips_elf_make_got_per_bfd (void **entryp, void *p)
+mips_elf_add_got_entry (void **entryp, void *data)
{
- struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
- struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
- struct mips_got_info *g;
+ struct mips_got_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
+ void **slot;
- g = mips_elf_get_got_for_bfd (arg->bfd2got, arg->obfd, entry->abfd);
- if (g == NULL)
+ entry = (struct mips_got_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ slot = htab_find_slot (arg->g->got_entries, entry, INSERT);
+ if (!slot)
{
- arg->obfd = NULL;
+ arg->g = NULL;
return 0;
}
-
- /* Insert the GOT entry in the bfd's got entry hash table. */
- entryp = htab_find_slot (g->got_entries, entry, INSERT);
- if (*entryp != NULL)
- return 1;
-
- *entryp = entry;
-
- if (entry->tls_type)
+ if (!*slot)
{
- 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;
+ *slot = entry;
+ mips_elf_count_got_entry (arg->info, arg->g, entry);
}
- else if (entry->symndx >= 0 || entry->d.h->global_got_area == GGA_NONE)
- ++g->local_gotno;
- else
- ++g->global_gotno;
-
return 1;
}
-/* A htab_traverse callback for the page entries in the master got.
- Associate each page entry with the bfd's got. */
+/* A htab_traverse callback for GOT page entries. Add each one to the GOT
+ given in mips_elf_traverse_got_arg DATA. Clear DATA->G on error. */
static int
-mips_elf_make_got_pages_per_bfd (void **entryp, void *p)
+mips_elf_add_got_page_entry (void **entryp, void *data)
{
- struct mips_got_page_entry *entry = (struct mips_got_page_entry *) *entryp;
- struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *) p;
- struct mips_got_info *g;
+ struct mips_got_page_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
+ void **slot;
- g = mips_elf_get_got_for_bfd (arg->bfd2got, arg->obfd, entry->abfd);
- if (g == NULL)
+ entry = (struct mips_got_page_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ slot = htab_find_slot (arg->g->got_page_entries, entry, INSERT);
+ if (!slot)
{
- arg->obfd = NULL;
+ arg->g = NULL;
return 0;
}
-
- /* Insert the GOT entry in the bfd's got entry hash table. */
- entryp = htab_find_slot (g->got_page_entries, entry, INSERT);
- if (*entryp != NULL)
- return 1;
-
- *entryp = entry;
- g->page_gotno += entry->num_pages;
+ if (!*slot)
+ {
+ *slot = entry;
+ arg->g->page_gotno += entry->num_pages;
+ }
return 1;
}
-/* Consider merging the got described by BFD2GOT with TO, using the
- information given by ARG. Return -1 if this would lead to overflow,
- 1 if they were merged successfully, and 0 if a merge failed due to
- lack of memory. (These values are chosen so that nonnegative return
- values can be returned by a htab_traverse callback.) */
+/* Consider merging FROM, which is ABFD's GOT, into TO. Return -1 if
+ this would lead to overflow, 1 if they were merged successfully,
+ and 0 if a merge failed due to lack of memory. (These values are chosen
+ so that nonnegative return values can be returned by a htab_traverse
+ callback.) */
static int
-mips_elf_merge_got_with (struct mips_elf_bfd2got_hash *bfd2got,
+mips_elf_merge_got_with (bfd *abfd, struct mips_got_info *from,
struct mips_got_info *to,
struct mips_elf_got_per_bfd_arg *arg)
{
- struct mips_got_info *from = bfd2got->g;
+ struct mips_elf_traverse_got_arg tga;
unsigned int estimate;
/* Work out how many page entries we would need for the combined GOT. */
estimate += from->local_gotno + to->local_gotno;
estimate += from->tls_gotno + to->tls_gotno;
- /* If we're merging with the primary got, we will always have
- the full set of global entries. Otherwise estimate those
+ /* If we're merging with the primary got, any TLS relocations will
+ come after the full set of global entries. Otherwise estimate those
conservatively as well. */
- if (to == arg->primary)
+ if (to == arg->primary && from->tls_gotno + to->tls_gotno)
estimate += arg->global_count;
else
estimate += from->global_gotno + to->global_gotno;
if (estimate > arg->max_count)
return -1;
- /* Commit to the merge. Record that TO is now the bfd for this got. */
- bfd2got->g = to;
-
/* Transfer the bfd's got information from FROM to TO. */
- htab_traverse (from->got_entries, mips_elf_make_got_per_bfd, arg);
- if (arg->obfd == NULL)
+ tga.info = arg->info;
+ tga.g = to;
+ htab_traverse (from->got_entries, mips_elf_add_got_entry, &tga);
+ if (!tga.g)
return 0;
- htab_traverse (from->got_page_entries, mips_elf_make_got_pages_per_bfd, arg);
- if (arg->obfd == NULL)
+ htab_traverse (from->got_page_entries, mips_elf_add_got_page_entry, &tga);
+ if (!tga.g)
return 0;
- /* We don't have to worry about releasing memory of the actual
- got entries, since they're all in the master got_entries hash
- table anyway. */
- htab_delete (from->got_entries);
- htab_delete (from->got_page_entries);
+ mips_elf_replace_bfd_got (abfd, to);
return 1;
}
-/* Attempt to merge gots of different input bfds. Try to use as much
+/* Attempt to merge GOT G, which belongs to ABFD. Try to use as much
as possible of the primary got, since it doesn't require explicit
dynamic relocations, but don't use bfds that would reference global
symbols out of the addressable range. Failing the primary got,
attempt to merge with the current got, or finish the current got
and then make make the new got current. */
-static int
-mips_elf_merge_gots (void **bfd2got_, void *p)
+static bfd_boolean
+mips_elf_merge_got (bfd *abfd, struct mips_got_info *g,
+ struct mips_elf_got_per_bfd_arg *arg)
{
- struct mips_elf_bfd2got_hash *bfd2got
- = (struct mips_elf_bfd2got_hash *)*bfd2got_;
- struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
- struct mips_got_info *g;
unsigned int estimate;
int result;
- g = bfd2got->g;
+ if (!mips_elf_resolve_final_got_entries (arg->info, g))
+ return FALSE;
/* Work out the number of page, local and TLS entries. */
estimate = arg->max_pages;
a starting point for the primary GOT. */
if (!arg->primary)
{
- arg->primary = bfd2got->g;
- return 1;
+ arg->primary = g;
+ return TRUE;
}
/* Try merging with the primary GOT. */
- result = mips_elf_merge_got_with (bfd2got, arg->primary, arg);
+ result = mips_elf_merge_got_with (abfd, g, arg->primary, arg);
if (result >= 0)
return result;
}
/* If we can merge with the last-created got, do it. */
if (arg->current)
{
- result = mips_elf_merge_got_with (bfd2got, arg->current, arg);
+ result = mips_elf_merge_got_with (abfd, g, arg->current, arg);
if (result >= 0)
return result;
}
g->next = arg->current;
arg->current = g;
- return 1;
+ return TRUE;
+}
+
+/* ENTRYP is a hash table entry for a mips_got_entry. Set its gotidx
+ to GOTIDX, duplicating the entry if it has already been assigned
+ an index in a different GOT. */
+
+static bfd_boolean
+mips_elf_set_gotidx (void **entryp, long gotidx)
+{
+ struct mips_got_entry *entry;
+
+ entry = (struct mips_got_entry *) *entryp;
+ if (entry->gotidx > 0)
+ {
+ struct mips_got_entry *new_entry;
+
+ new_entry = bfd_alloc (entry->abfd, sizeof (*entry));
+ if (!new_entry)
+ return FALSE;
+
+ *new_entry = *entry;
+ *entryp = new_entry;
+ entry = new_entry;
+ }
+ entry->gotidx = gotidx;
+ return TRUE;
}
-/* Set the TLS GOT index for the GOT entry in ENTRYP. ENTRYP's NEXT field
- is null iff there is just a single GOT. */
+/* Set the TLS GOT index for the GOT entry in ENTRYP. DATA points to a
+ mips_elf_traverse_got_arg in which DATA->value is the size of one
+ GOT entry. Set DATA->g to null on failure. */
static int
-mips_elf_initialize_tls_index (void **entryp, void *p)
+mips_elf_initialize_tls_index (void **entryp, void *data)
{
- struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
- struct mips_got_info *g = p;
- bfd_vma next_index;
- unsigned char tls_type;
+ struct mips_got_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
/* We're only interested in TLS symbols. */
- if (entry->tls_type == 0)
+ entry = (struct mips_got_entry *) *entryp;
+ if (entry->tls_type == GOT_TLS_NONE)
return 1;
- next_index = MIPS_ELF_GOT_SIZE (entry->abfd) * (long) g->tls_assigned_gotno;
-
- if (entry->symndx == -1 && g->next == NULL)
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ if (!mips_elf_set_gotidx (entryp, arg->value * arg->g->tls_assigned_gotno))
{
- /* A type (3) got entry in the single-GOT case. We use the symbol's
- hash table entry to track its index. */
- if (entry->d.h->tls_type & GOT_TLS_OFFSET_DONE)
- return 1;
- entry->d.h->tls_type |= GOT_TLS_OFFSET_DONE;
- entry->d.h->tls_got_offset = next_index;
- tls_type = entry->d.h->tls_type;
- }
- else
- {
- if (entry->tls_type & GOT_TLS_LDM)
- {
- /* There are separate mips_got_entry objects for each input bfd
- that requires an LDM entry. Make sure that all LDM entries in
- a GOT resolve to the same index. */
- if (g->tls_ldm_offset != MINUS_TWO && g->tls_ldm_offset != MINUS_ONE)
- {
- entry->gotidx = g->tls_ldm_offset;
- return 1;
- }
- g->tls_ldm_offset = next_index;
- }
- entry->gotidx = next_index;
- tls_type = entry->tls_type;
+ arg->g = NULL;
+ return 0;
}
/* Account for the entries we've just allocated. */
- if (tls_type & (GOT_TLS_GD | GOT_TLS_LDM))
- g->tls_assigned_gotno += 2;
- if (tls_type & GOT_TLS_IE)
- g->tls_assigned_gotno += 1;
-
+ arg->g->tls_assigned_gotno += mips_tls_got_entries (entry->tls_type);
return 1;
}
-/* If passed a NULL mips_got_info in the argument, set the marker used
- to tell whether a global symbol needs a got entry (in the primary
- got) to the given VALUE.
+/* A htab_traverse callback for GOT entries, where DATA points to a
+ mips_elf_traverse_got_arg. Set the global_got_area of each global
+ symbol to DATA->value. */
- If passed a pointer G to a mips_got_info in the argument (it must
- not be the primary GOT), compute the offset from the beginning of
- the (primary) GOT section to the entry in G corresponding to the
- global symbol. G's assigned_gotno must contain the index of the
- first available global GOT entry in G. VALUE must contain the size
- of a GOT entry in bytes. For each global GOT entry that requires a
- dynamic relocation, NEEDED_RELOCS is incremented, and the symbol is
- marked as not eligible for lazy resolution through a function
- stub. */
static int
-mips_elf_set_global_got_offset (void **entryp, void *p)
+mips_elf_set_global_got_area (void **entryp, void *data)
{
- struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
- struct mips_elf_set_global_got_offset_arg *arg
- = (struct mips_elf_set_global_got_offset_arg *)p;
- struct mips_got_info *g = arg->g;
+ struct mips_got_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
+
+ entry = (struct mips_got_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
+ if (entry->abfd != NULL
+ && entry->symndx == -1
+ && entry->d.h->global_got_area != GGA_NONE)
+ entry->d.h->global_got_area = arg->value;
+ return 1;
+}
- 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);
+/* A htab_traverse callback for secondary GOT entries, where DATA points
+ to a mips_elf_traverse_got_arg. Assign GOT indices to global entries
+ and record the number of relocations they require. DATA->value is
+ the size of one GOT entry. Set DATA->g to null on failure. */
+
+static int
+mips_elf_set_global_gotidx (void **entryp, void *data)
+{
+ struct mips_got_entry *entry;
+ struct mips_elf_traverse_got_arg *arg;
+ entry = (struct mips_got_entry *) *entryp;
+ arg = (struct mips_elf_traverse_got_arg *) data;
if (entry->abfd != NULL
&& entry->symndx == -1
&& entry->d.h->global_got_area != GGA_NONE)
{
- if (g)
+ if (!mips_elf_set_gotidx (entryp, arg->value * arg->g->assigned_low_gotno))
{
- BFD_ASSERT (g->global_gotsym == NULL);
-
- entry->gotidx = arg->value * (long) g->assigned_gotno++;
- if (arg->info->shared
- || (elf_hash_table (arg->info)->dynamic_sections_created
- && entry->d.h->root.def_dynamic
- && !entry->d.h->root.def_regular))
- ++arg->needed_relocs;
+ arg->g = NULL;
+ return 0;
}
- else
- entry->d.h->global_got_area = arg->value;
+ arg->g->assigned_low_gotno += 1;
+
+ if (arg->info->shared
+ || (elf_hash_table (arg->info)->dynamic_sections_created
+ && entry->d.h->root.def_dynamic
+ && !entry->d.h->root.def_regular))
+ arg->g->relocs += 1;
}
return 1;
static bfd_vma
mips_elf_adjust_gp (bfd *abfd, struct mips_got_info *g, bfd *ibfd)
{
- if (g->bfd2got == NULL)
+ if (!g->next)
return 0;
- g = mips_elf_got_for_ibfd (g, ibfd);
+ g = mips_elf_bfd_got (ibfd, FALSE);
if (! g)
return 0;
{
struct mips_elf_link_hash_table *htab;
struct mips_elf_got_per_bfd_arg got_per_bfd_arg;
- struct mips_elf_set_global_got_offset_arg set_got_offset_arg;
+ struct mips_elf_traverse_got_arg tga;
struct mips_got_info *g, *gg;
unsigned int assign, needed_relocs;
- bfd *dynobj;
+ bfd *dynobj, *ibfd;
dynobj = elf_hash_table (info)->dynobj;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
g = htab->got_info;
- g->bfd2got = htab_try_create (1, mips_elf_bfd2got_entry_hash,
- mips_elf_bfd2got_entry_eq, NULL);
- if (g->bfd2got == NULL)
- return FALSE;
- got_per_bfd_arg.bfd2got = g->bfd2got;
got_per_bfd_arg.obfd = abfd;
got_per_bfd_arg.info = info;
-
- /* Count how many GOT entries each input bfd requires, creating a
- map from bfd to got info while at that. */
- htab_traverse (g->got_entries, mips_elf_make_got_per_bfd, &got_per_bfd_arg);
- if (got_per_bfd_arg.obfd == NULL)
- return FALSE;
-
- /* Also count how many page entries each input bfd requires. */
- htab_traverse (g->got_page_entries, mips_elf_make_got_pages_per_bfd,
- &got_per_bfd_arg);
- if (got_per_bfd_arg.obfd == NULL)
- return FALSE;
-
got_per_bfd_arg.current = NULL;
got_per_bfd_arg.primary = NULL;
got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (info)
- htab->reserved_gotno);
got_per_bfd_arg.max_pages = 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
+ See the calls to mips_elf_set_global_got_area 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
to be the primary GOT. */
- htab_traverse (g->bfd2got, mips_elf_merge_gots, &got_per_bfd_arg);
- if (got_per_bfd_arg.obfd == NULL)
- return FALSE;
-
- /* If we do not find any suitable primary GOT, create an empty one. */
- if (got_per_bfd_arg.primary == NULL)
+ for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link.next)
{
- g->next = (struct mips_got_info *)
- bfd_alloc (abfd, sizeof (struct mips_got_info));
- if (g->next == NULL)
- return FALSE;
-
- g->next->global_gotsym = NULL;
- g->next->global_gotno = 0;
- g->next->reloc_only_gotno = 0;
- g->next->local_gotno = 0;
- g->next->page_gotno = 0;
- g->next->tls_gotno = 0;
- g->next->assigned_gotno = 0;
- g->next->tls_assigned_gotno = 0;
- g->next->tls_ldm_offset = MINUS_ONE;
- g->next->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
- mips_elf_multi_got_entry_eq,
- NULL);
- if (g->next->got_entries == NULL)
- return FALSE;
- g->next->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
- mips_got_page_entry_eq,
- NULL);
- if (g->next->got_page_entries == NULL)
+ gg = mips_elf_bfd_got (ibfd, FALSE);
+ if (gg && !mips_elf_merge_got (ibfd, gg, &got_per_bfd_arg))
return FALSE;
- g->next->bfd2got = NULL;
}
+
+ /* If we do not find any suitable primary GOT, create an empty one. */
+ if (got_per_bfd_arg.primary == NULL)
+ g->next = mips_elf_create_got_info (abfd);
else
g->next = got_per_bfd_arg.primary;
g->next->next = got_per_bfd_arg.current;
didn't mark in check_relocs, and we want a quick way to find it.
We can't just use gg->next because we're going to reverse the
list. */
- {
- struct mips_elf_bfd2got_hash *bfdgot;
- void **bfdgotp;
-
- bfdgot = (struct mips_elf_bfd2got_hash *)bfd_alloc
- (abfd, sizeof (struct mips_elf_bfd2got_hash));
-
- if (bfdgot == NULL)
- return FALSE;
-
- bfdgot->bfd = abfd;
- bfdgot->g = g;
- bfdgotp = htab_find_slot (gg->bfd2got, bfdgot, INSERT);
-
- BFD_ASSERT (*bfdgotp == NULL);
- *bfdgotp = bfdgot;
- }
+ mips_elf_replace_bfd_got (abfd, g);
/* Every symbol that is referenced in a dynamic relocation must be
present in the primary GOT, so arrange for them to appear after
gg->reloc_only_gotno = gg->global_gotno - g->global_gotno;
g->global_gotno = gg->global_gotno;
- set_got_offset_arg.g = NULL;
- set_got_offset_arg.value = GGA_RELOC_ONLY;
- htab_traverse (gg->got_entries, mips_elf_set_global_got_offset,
- &set_got_offset_arg);
- set_got_offset_arg.value = GGA_NORMAL;
- htab_traverse (g->got_entries, mips_elf_set_global_got_offset,
- &set_got_offset_arg);
+ tga.info = info;
+ tga.value = GGA_RELOC_ONLY;
+ htab_traverse (gg->got_entries, mips_elf_set_global_got_area, &tga);
+ tga.value = GGA_NORMAL;
+ htab_traverse (g->got_entries, mips_elf_set_global_got_area, &tga);
/* Now go through the GOTs assigning them offset ranges.
- [assigned_gotno, local_gotno[ will be set to the range of local
+ [assigned_low_gotno, local_gotno[ will be set to the range of local
entries in each GOT. We can then compute the end of a GOT by
adding local_gotno to global_gotno. We reverse the list and make
it circular since then we'll be able to quickly compute the
struct mips_got_info *gn;
assign += htab->reserved_gotno;
- g->assigned_gotno = assign;
+ g->assigned_low_gotno = assign;
g->local_gotno += assign;
g->local_gotno += (pages < g->page_gotno ? pages : g->page_gotno);
+ g->assigned_high_gotno = g->local_gotno - 1;
assign = g->local_gotno + g->global_gotno + g->tls_gotno;
/* Take g out of the direct list, and push it onto the reversed
/* Set up any TLS entries. We always place the TLS entries after
all non-TLS entries. */
g->tls_assigned_gotno = g->local_gotno + g->global_gotno;
- htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
+ tga.g = g;
+ tga.value = MIPS_ELF_GOT_SIZE (abfd);
+ htab_traverse (g->got_entries, mips_elf_initialize_tls_index, &tga);
+ if (!tga.g)
+ return FALSE;
+ BFD_ASSERT (g->tls_assigned_gotno == assign);
/* Move onto the next GOT. It will be a secondary GOT if nonull. */
g = gn;
}
while (g);
- got->size = (gg->next->local_gotno
- + gg->next->global_gotno
- + gg->next->tls_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+ got->size = assign * MIPS_ELF_GOT_SIZE (abfd);
needed_relocs = 0;
- set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (abfd);
- set_got_offset_arg.info = info;
for (g = gg->next; g && g->next != gg; g = g->next)
{
unsigned int save_assign;
- /* Assign offsets to global GOT entries. */
- save_assign = g->assigned_gotno;
- g->assigned_gotno = g->local_gotno;
- set_got_offset_arg.g = g;
- set_got_offset_arg.needed_relocs = 0;
- htab_traverse (g->got_entries,
- mips_elf_set_global_got_offset,
- &set_got_offset_arg);
- needed_relocs += set_got_offset_arg.needed_relocs;
- BFD_ASSERT (g->assigned_gotno - g->local_gotno <= g->global_gotno);
-
- g->assigned_gotno = save_assign;
+ /* Assign offsets to global GOT entries and count how many
+ relocations they need. */
+ save_assign = g->assigned_low_gotno;
+ g->assigned_low_gotno = g->local_gotno;
+ tga.info = info;
+ tga.value = MIPS_ELF_GOT_SIZE (abfd);
+ tga.g = g;
+ htab_traverse (g->got_entries, mips_elf_set_global_gotidx, &tga);
+ if (!tga.g)
+ return FALSE;
+ BFD_ASSERT (g->assigned_low_gotno == g->local_gotno + g->global_gotno);
+ g->assigned_low_gotno = save_assign;
+
if (info->shared)
{
- needed_relocs += g->local_gotno - g->assigned_gotno;
- BFD_ASSERT (g->assigned_gotno == g->next->local_gotno
+ g->relocs += g->local_gotno - g->assigned_low_gotno;
+ BFD_ASSERT (g->assigned_low_gotno == g->next->local_gotno
+ g->next->global_gotno
+ g->next->tls_gotno
+ htab->reserved_gotno);
}
+ needed_relocs += g->relocs;
}
+ needed_relocs += g->relocs;
if (needed_relocs)
mips_elf_allocate_dynamic_relocations (dynobj, info,
flagword flags;
register asection *s;
- if (bfd_get_section_by_name (abfd, ".compact_rel") == NULL)
+ if (bfd_get_linker_section (abfd, ".compact_rel") == NULL)
{
flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED
| SEC_READONLY);
- s = bfd_make_section_with_flags (abfd, ".compact_rel", flags);
+ s = bfd_make_section_anyway_with_flags (abfd, ".compact_rel", flags);
if (s == NULL
|| ! bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
register asection *s;
struct elf_link_hash_entry *h;
struct bfd_link_hash_entry *bh;
- struct mips_got_info *g;
- bfd_size_type amt;
struct mips_elf_link_hash_table *htab;
htab = mips_elf_hash_table (info);
/* 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_with_flags (abfd, ".got", flags);
+ s = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
if (s == NULL
|| ! bfd_set_section_alignment (abfd, s, 4))
return FALSE;
h->non_elf = 0;
h->def_regular = 1;
h->type = STT_OBJECT;
+ h->other = (h->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN;
elf_hash_table (info)->hgot = h;
if (info->shared
&& ! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
- amt = sizeof (struct mips_got_info);
- g = bfd_alloc (abfd, amt);
- if (g == NULL)
- return FALSE;
- g->global_gotsym = NULL;
- g->global_gotno = 0;
- g->reloc_only_gotno = 0;
- g->tls_gotno = 0;
- g->local_gotno = 0;
- g->page_gotno = 0;
- g->assigned_gotno = 0;
- g->bfd2got = NULL;
- g->next = NULL;
- g->tls_ldm_offset = MINUS_ONE;
- g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
- mips_elf_got_entry_eq, NULL);
- if (g->got_entries == NULL)
- return FALSE;
- g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
- mips_got_page_entry_eq, NULL);
- if (g->got_page_entries == NULL)
- return FALSE;
- htab->got_info = g;
+ htab->got_info = mips_elf_create_got_info (abfd);
mips_elf_section_data (s)->elf.this_hdr.sh_flags
|= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
/* We also need a .got.plt section when generating PLTs. */
- s = bfd_make_section_with_flags (abfd, ".got.plt",
- SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ s = bfd_make_section_anyway_with_flags (abfd, ".got.plt",
+ SEC_ALLOC | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED);
if (s == NULL)
return FALSE;
htab->sgotplt = s;
stub. */
static bfd_boolean
-mips_elf_relocation_needs_la25_stub (bfd *input_bfd, int r_type)
+mips_elf_relocation_needs_la25_stub (bfd *input_bfd, int r_type,
+ bfd_boolean target_is_16_bit_code_p)
{
/* We specifically ignore branches and jumps from EF_PIC objects,
where the onus is on the compiler or programmer to perform any
{
case R_MIPS_26:
case R_MIPS_PC16:
- case R_MIPS16_26:
+ case R_MIPS_PC21_S2:
+ case R_MIPS_PC26_S2:
case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC7_S1:
case R_MICROMIPS_PC10_S1:
case R_MICROMIPS_PC23_S2:
return TRUE;
+ case R_MIPS16_26:
+ return !target_is_16_bit_code_p;
+
default:
return FALSE;
}
}
target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (h->root.other);
- /* If the output section is the PLT section,
- then the target is not microMIPS. */
- target_is_micromips_code_p = (htab->splt != sec
- && ELF_ST_IS_MICROMIPS (h->root.other));
+ target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (h->root.other);
}
/* If this is a reference to a 16-bit function with a stub, we need
&& h->fn_stub != NULL
&& (r_type != R_MIPS16_CALL16 || h->need_fn_stub))
|| (local_p
- && elf_tdata (input_bfd)->local_stubs != NULL
- && elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
+ && mips_elf_tdata (input_bfd)->local_stubs != NULL
+ && mips_elf_tdata (input_bfd)->local_stubs[r_symndx] != NULL))
&& !section_allows_mips16_refs_p (input_section))
{
/* This is a 32- or 64-bit call to a 16-bit function. We should
have already noticed that we were going to need the
stub. */
if (local_p)
- sec = elf_tdata (input_bfd)->local_stubs[r_symndx];
+ {
+ sec = mips_elf_tdata (input_bfd)->local_stubs[r_symndx];
+ value = 0;
+ }
else
{
BFD_ASSERT (h->need_fn_stub);
- sec = h->fn_stub;
+ if (h->la25_stub)
+ {
+ /* If a LA25 header for the stub itself exists, point to the
+ prepended LUI/ADDIU sequence. */
+ sec = h->la25_stub->stub_section;
+ value = h->la25_stub->offset;
+ }
+ else
+ {
+ sec = h->fn_stub;
+ value = 0;
+ }
}
- symbol = sec->output_section->vma + sec->output_offset;
+ symbol = sec->output_section->vma + sec->output_offset + value;
/* The target is 16-bit, but the stub isn't. */
target_is_16_bit_code_p = FALSE;
}
- /* If this is a 16-bit call to a 32- or 64-bit function with a stub, we
- need to redirect the call to the stub. Note that we specifically
- exclude R_MIPS16_CALL16 from this behavior; indirect calls should
- use an indirect stub instead. */
+ /* If this is a MIPS16 call with a stub, that is made through the PLT or
+ to a standard MIPS function, we need to redirect the call to the stub.
+ Note that we specifically exclude R_MIPS16_CALL16 from this behavior;
+ indirect calls should use an indirect stub instead. */
else if (r_type == R_MIPS16_26 && !info->relocatable
&& ((h != NULL && (h->call_stub != NULL || h->call_fp_stub != NULL))
|| (local_p
- && elf_tdata (input_bfd)->local_call_stubs != NULL
- && elf_tdata (input_bfd)->local_call_stubs[r_symndx] != NULL))
- && !target_is_16_bit_code_p)
+ && mips_elf_tdata (input_bfd)->local_call_stubs != NULL
+ && mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx] != NULL))
+ && ((h != NULL && h->use_plt_entry) || !target_is_16_bit_code_p))
{
if (local_p)
- sec = elf_tdata (input_bfd)->local_call_stubs[r_symndx];
+ sec = mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx];
else
{
/* If both call_stub and call_fp_stub are defined, we can figure
if (h->call_stub != NULL && h->call_fp_stub != NULL)
{
asection *o;
-
+
sec = NULL;
for (o = input_bfd->sections; o != NULL; o = o->next)
{
/* If this is a direct call to a PIC function, redirect to the
non-PIC stub. */
else if (h != NULL && h->la25_stub
- && mips_elf_relocation_needs_la25_stub (input_bfd, r_type))
+ && mips_elf_relocation_needs_la25_stub (input_bfd, r_type,
+ target_is_16_bit_code_p))
symbol = (h->la25_stub->stub_section->output_section->vma
+ h->la25_stub->stub_section->output_offset
+ h->la25_stub->offset);
+ /* For direct MIPS16 and microMIPS calls make sure the compressed PLT
+ entry is used if a standard PLT entry has also been made. In this
+ case the symbol will have been set by mips_elf_set_plt_sym_value
+ to point to the standard PLT entry, so redirect to the compressed
+ one. */
+ else if ((r_type == R_MIPS16_26 || r_type == R_MICROMIPS_26_S1)
+ && !info->relocatable
+ && h != NULL
+ && h->use_plt_entry
+ && h->root.plt.plist->comp_offset != MINUS_ONE
+ && h->root.plt.plist->mips_offset != MINUS_ONE)
+ {
+ bfd_boolean micromips_p = MICROMIPS_P (abfd);
+
+ sec = htab->splt;
+ symbol = (sec->output_section->vma
+ + sec->output_offset
+ + htab->plt_header_size
+ + htab->plt_mips_offset
+ + h->root.plt.plist->comp_offset
+ + 1);
+
+ target_is_16_bit_code_p = !micromips_p;
+ target_is_micromips_code_p = micromips_p;
+ }
/* Make sure MIPS16 and microMIPS are not used together. */
if ((r_type == R_MIPS16_26 && target_is_micromips_code_p)
&& (target_is_16_bit_code_p
|| target_is_micromips_code_p))));
- local_p = h == NULL || SYMBOL_REFERENCES_LOCAL (info, &h->root);
+ local_p = (h == NULL || mips_use_local_got_p (info, h));
gp0 = _bfd_get_gp_value (input_bfd);
gp = _bfd_get_gp_value (abfd);
case R_MIPS_TLS_GD:
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_LDM:
+ case R_MIPS16_TLS_GD:
+ case R_MIPS16_TLS_GOTTPREL:
+ case R_MIPS16_TLS_LDM:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_GOTTPREL:
case R_MICROMIPS_TLS_LDM:
else
{
BFD_ASSERT (addend == 0);
- g = mips_elf_global_got_index (dynobj, input_bfd,
- &h->root, r_type, info);
- if (h->tls_type == GOT_NORMAL
+ g = mips_elf_global_got_index (abfd, info, input_bfd,
+ &h->root, r_type);
+ if (!TLS_RELOC_P (r_type)
&& !elf_hash_table (info)->dynamic_sections_created)
/* This is a static link. We must initialize the GOT entry. */
MIPS_ELF_PUT_WORD (dynobj, symbol, htab->sgot->contents + g);
return bfd_reloc_continue;
case R_MIPS_16:
- value = symbol + _bfd_mips_elf_sign_extend (addend, 16);
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 16);
+ value = symbol + addend;
overflowed_p = mips_elf_overflow_p (value, 16);
break;
if (was_local_p)
value = addend | ((p + 4) & (0xfc000000 << shift));
- else
+ else if (howto->partial_inplace)
value = _bfd_mips_elf_sign_extend (addend, 26 + shift);
+ else
+ value = addend;
value = (value + symbol) >> shift;
if (!was_local_p && h->root.root.type != bfd_link_hash_undefweak)
overflowed_p = (value >> 26) != ((p + 4) >> (26 + shift));
break;
case R_MIPS_TLS_DTPREL_HI16:
+ case R_MIPS16_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_DTPREL_HI16:
value = (mips_elf_high (addend + symbol - dtprel_base (info))
& howto->dst_mask);
case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_DTPREL64:
+ case R_MIPS16_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_DTPREL_LO16:
value = (symbol + addend - dtprel_base (info)) & howto->dst_mask;
break;
case R_MIPS_TLS_TPREL_HI16:
+ case R_MIPS16_TLS_TPREL_HI16:
case R_MICROMIPS_TLS_TPREL_HI16:
value = (mips_elf_high (addend + symbol - tprel_base (info))
& howto->dst_mask);
break;
case R_MIPS_TLS_TPREL_LO16:
+ case R_MIPS_TLS_TPREL32:
+ case R_MIPS_TLS_TPREL64:
+ case R_MIPS16_TLS_TPREL_LO16:
case R_MICROMIPS_TLS_TPREL_LO16:
value = (symbol + addend - tprel_base (info)) & howto->dst_mask;
break;
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. */
+ base $pc is that used by the ADDIUPC instruction at $t9 + 4.
+ ADDIUPC clears the low two bits of the instruction address,
+ so the base is ($t9 + 4) & ~3. */
if (r_type == R_MIPS16_HI16)
- value = mips_elf_high (addend + gp - p - 4);
+ value = mips_elf_high (addend + gp - ((p + 4) & ~(bfd_vma) 0x3));
/* The microMIPS .cpload sequence uses the same assembly
instructions as the traditional psABI version, but the
incoming $t9 has the low bit set. */
/* See the comment for R_MIPS16_HI16 above for the reason
for this conditional. */
if (r_type == R_MIPS16_LO16)
- value = addend + gp - p;
+ value = addend + gp - (p & ~(bfd_vma) 0x3);
else if (r_type == R_MICROMIPS_LO16
|| r_type == R_MICROMIPS_HI0_LO16)
value = addend + gp - p + 3;
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_LDM:
case R_MIPS_GOT_DISP:
+ case R_MIPS16_TLS_GD:
+ case R_MIPS16_TLS_GOTTPREL:
+ case R_MIPS16_TLS_LDM:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_GOTTPREL:
case R_MICROMIPS_TLS_LDM:
case R_MIPS_PC16:
case R_MIPS_GNU_REL16_S2:
- value = symbol + _bfd_mips_elf_sign_extend (addend, 18) - p;
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 18);
+
+ if ((symbol + addend) & 3)
+ return bfd_reloc_outofrange;
+
+ value = symbol + addend - p;
overflowed_p = mips_elf_overflow_p (value, 18);
value >>= howto->rightshift;
value &= howto->dst_mask;
break;
+ case R_MIPS_PC21_S2:
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 23);
+
+ if ((symbol + addend) & 3)
+ return bfd_reloc_outofrange;
+
+ value = symbol + addend - p;
+ overflowed_p = mips_elf_overflow_p (value, 23);
+ value >>= howto->rightshift;
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_PC26_S2:
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 28);
+
+ if ((symbol + addend) & 3)
+ return bfd_reloc_outofrange;
+
+ value = symbol + addend - p;
+ overflowed_p = mips_elf_overflow_p (value, 28);
+ value >>= howto->rightshift;
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_PC18_S3:
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 21);
+
+ if ((symbol + addend) & 7)
+ return bfd_reloc_outofrange;
+
+ value = symbol + addend - ((p | 7) ^ 7);
+ overflowed_p = mips_elf_overflow_p (value, 21);
+ value >>= howto->rightshift;
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_PC19_S2:
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 21);
+
+ if ((symbol + addend) & 3)
+ return bfd_reloc_outofrange;
+
+ value = symbol + addend - p;
+ overflowed_p = mips_elf_overflow_p (value, 21);
+ value >>= howto->rightshift;
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_PCHI16:
+ value = mips_elf_high (symbol + addend - p);
+ overflowed_p = mips_elf_overflow_p (value, 16);
+ value &= howto->dst_mask;
+ break;
+
+ case R_MIPS_PCLO16:
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 16);
+ value = symbol + addend - p;
+ value &= howto->dst_mask;
+ break;
+
case R_MICROMIPS_PC7_S1:
- value = symbol + _bfd_mips_elf_sign_extend (addend, 8) - p;
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 8);
+ value = symbol + addend - p;
overflowed_p = mips_elf_overflow_p (value, 8);
value >>= howto->rightshift;
value &= howto->dst_mask;
break;
case R_MICROMIPS_PC10_S1:
- value = symbol + _bfd_mips_elf_sign_extend (addend, 11) - p;
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 11);
+ value = symbol + addend - p;
overflowed_p = mips_elf_overflow_p (value, 11);
value >>= howto->rightshift;
value &= howto->dst_mask;
break;
case R_MICROMIPS_PC16_S1:
- value = symbol + _bfd_mips_elf_sign_extend (addend, 17) - p;
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 17);
+ value = symbol + addend - p;
overflowed_p = mips_elf_overflow_p (value, 17);
value >>= howto->rightshift;
value &= howto->dst_mask;
break;
case R_MICROMIPS_PC23_S2:
- value = symbol + _bfd_mips_elf_sign_extend (addend, 25) - ((p | 3) ^ 3);
+ if (howto->partial_inplace)
+ addend = _bfd_mips_elf_sign_extend (addend, 25);
+ value = symbol + addend - ((p | 3) ^ 3);
overflowed_p = mips_elf_overflow_p (value, 25);
value >>= howto->rightshift;
value &= howto->dst_mask;
/* It has been determined that the result of the RELOCATION is the
VALUE. Use HOWTO to place VALUE into the output file at the
appropriate position. The SECTION is the section to which the
- relocation applies.
+ relocation applies.
CROSS_MODE_JUMP_P is true if the relocation field
is a MIPS16 or microMIPS jump to standard MIPS code, or vice versa.
jalx_opcode = 0x1d;
}
- /* If the opcode is not JAL or JALX, there's a problem. */
+ /* If the opcode is not JAL or JALX, there's a problem. We cannot
+ convert J or JALS to JALX. */
if (!ok)
{
(*_bfd_error_handler)
- (_("%B: %A+0x%lx: Direct jumps between ISA modes are not allowed; consider recompiling with interlinking enabled."),
+ (_("%B: %A+0x%lx: Unsupported jump between ISA modes; consider recompiling with interlinking enabled."),
input_bfd,
input_section,
(unsigned long) relocation->r_offset);
/* On IRIX5, make an entry of compact relocation info. */
if (IRIX_COMPAT (output_bfd) == ict_irix5)
{
- asection *scpt = bfd_get_section_by_name (dynobj, ".compact_rel");
+ asection *scpt = bfd_get_linker_section (dynobj, ".compact_rel");
bfd_byte *cr;
if (scpt)
case E_MIPS_MACH_5500:
return bfd_mach_mips5500;
+ case E_MIPS_MACH_5900:
+ return bfd_mach_mips5900;
+
case E_MIPS_MACH_9000:
return bfd_mach_mips9000;
case E_MIPS_MACH_LS3A:
return bfd_mach_mips_loongson_3a;
+ case E_MIPS_MACH_OCTEON3:
+ return bfd_mach_mips_octeon3;
+
+ case E_MIPS_MACH_OCTEON2:
+ return bfd_mach_mips_octeon2;
+
case E_MIPS_MACH_OCTEON:
return bfd_mach_mips_octeon;
case E_MIPS_ARCH_64R2:
return bfd_mach_mipsisa64r2;
+
+ case E_MIPS_ARCH_32R6:
+ return bfd_mach_mipsisa32r6;
+
+ case E_MIPS_ARCH_64R6:
+ return bfd_mach_mipsisa64r6;
}
}
{
asection *section = bfd_get_section_by_name (abfd, ".text");
- BFD_ASSERT (SGI_COMPAT (abfd));
if (section != NULL)
{
asym->section = section;
{
asection *section = bfd_get_section_by_name (abfd, ".data");
- BFD_ASSERT (SGI_COMPAT (abfd));
if (section != NULL)
{
asym->section = section;
&& (asym->value & 1) != 0)
{
asym->value--;
- if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS)
+ if (MICROMIPS_P (abfd))
elfsym->internal_elf_sym.st_other
= ELF_ST_SET_MICROMIPS (elfsym->internal_elf_sym.st_other);
else
if (!MIPS_ELF_OPTIONS_SECTION_NAME_P (name))
return FALSE;
break;
+ case SHT_MIPS_ABIFLAGS:
+ if (!MIPS_ELF_ABIFLAGS_SECTION_NAME_P (name))
+ return FALSE;
+ flags = (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_SAME_SIZE);
+ break;
case SHT_MIPS_DWARF:
if (! CONST_STRNEQ (name, ".debug_")
&& ! CONST_STRNEQ (name, ".zdebug_"))
return FALSE;
}
+ if (hdr->sh_type == SHT_MIPS_ABIFLAGS)
+ {
+ Elf_External_ABIFlags_v0 ext;
+
+ if (! bfd_get_section_contents (abfd, hdr->bfd_section,
+ &ext, 0, sizeof ext))
+ return FALSE;
+ bfd_mips_elf_swap_abiflags_v0_in (abfd, &ext,
+ &mips_elf_tdata (abfd)->abiflags);
+ if (mips_elf_tdata (abfd)->abiflags.version != 0)
+ return FALSE;
+ mips_elf_tdata (abfd)->abiflags_valid = TRUE;
+ }
+
/* FIXME: We should record sh_info for a .gptab section. */
/* For a .reginfo section, set the gp value in the tdata information
hdr->sh_entsize = 1;
hdr->sh_flags |= SHF_MIPS_NOSTRIP;
}
+ else if (CONST_STRNEQ (name, ".MIPS.abiflags"))
+ {
+ hdr->sh_type = SHT_MIPS_ABIFLAGS;
+ hdr->sh_entsize = sizeof (Elf_External_ABIFlags_v0);
+ }
else if (CONST_STRNEQ (name, ".debug_")
|| CONST_STRNEQ (name, ".zdebug_"))
{
case SHN_MIPS_TEXT:
/* This section is used in a shared object. */
- if (elf_tdata (abfd)->elf_text_section == NULL)
+ if (mips_elf_tdata (abfd)->elf_text_section == NULL)
{
asymbol *elf_text_symbol;
asection *elf_text_section;
/* Initialize the section. */
- elf_tdata (abfd)->elf_text_section = elf_text_section;
- elf_tdata (abfd)->elf_text_symbol = elf_text_symbol;
+ mips_elf_tdata (abfd)->elf_text_section = elf_text_section;
+ mips_elf_tdata (abfd)->elf_text_symbol = elf_text_symbol;
elf_text_section->symbol = elf_text_symbol;
- elf_text_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_text_symbol;
+ elf_text_section->symbol_ptr_ptr = &mips_elf_tdata (abfd)->elf_text_symbol;
elf_text_section->name = ".text";
elf_text_section->flags = SEC_NO_FLAGS;
/* This code used to do *secp = bfd_und_section_ptr if
info->shared. I don't know why, and that doesn't make sense,
so I took it out. */
- *secp = elf_tdata (abfd)->elf_text_section;
+ *secp = mips_elf_tdata (abfd)->elf_text_section;
break;
case SHN_MIPS_ACOMMON:
/* Fall through. XXX Can we treat this as allocated data? */
case SHN_MIPS_DATA:
/* This section is used in a shared object. */
- if (elf_tdata (abfd)->elf_data_section == NULL)
+ if (mips_elf_tdata (abfd)->elf_data_section == NULL)
{
asymbol *elf_data_symbol;
asection *elf_data_section;
/* Initialize the section. */
- elf_tdata (abfd)->elf_data_section = elf_data_section;
- elf_tdata (abfd)->elf_data_symbol = elf_data_symbol;
+ mips_elf_tdata (abfd)->elf_data_section = elf_data_section;
+ mips_elf_tdata (abfd)->elf_data_symbol = elf_data_symbol;
elf_data_section->symbol = elf_data_symbol;
- elf_data_section->symbol_ptr_ptr = &elf_tdata (abfd)->elf_data_symbol;
+ elf_data_section->symbol_ptr_ptr = &mips_elf_tdata (abfd)->elf_data_symbol;
elf_data_section->name = ".data";
elf_data_section->flags = SEC_NO_FLAGS;
/* This code used to do *secp = bfd_und_section_ptr if
info->shared. I don't know why, and that doesn't make sense,
so I took it out. */
- *secp = elf_tdata (abfd)->elf_data_section;
+ *secp = mips_elf_tdata (abfd)->elf_data_section;
break;
case SHN_MIPS_SUNDEFINED:
return FALSE;
mips_elf_hash_table (info)->use_rld_obj_head = TRUE;
+ mips_elf_hash_table (info)->rld_symbol = h;
}
/* If this is a mips16 text symbol, add 1 to the value to make it
EABI doesn't. */
if (!htab->is_vxworks)
{
- s = bfd_get_section_by_name (abfd, ".dynamic");
+ s = bfd_get_linker_section (abfd, ".dynamic");
if (s != NULL)
{
if (! bfd_set_section_flags (abfd, s, flags))
return FALSE;
/* Create .stub section. */
- s = bfd_make_section_with_flags (abfd,
- MIPS_ELF_STUB_SECTION_NAME (abfd),
- flags | SEC_CODE);
+ s = bfd_make_section_anyway_with_flags (abfd,
+ MIPS_ELF_STUB_SECTION_NAME (abfd),
+ flags | SEC_CODE);
if (s == NULL
|| ! bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
return FALSE;
htab->sstubs = s;
- if ((IRIX_COMPAT (abfd) == ict_irix5 || IRIX_COMPAT (abfd) == ict_none)
+ if (!mips_elf_hash_table (info)->use_rld_obj_head
&& !info->shared
- && bfd_get_section_by_name (abfd, ".rld_map") == NULL)
+ && bfd_get_linker_section (abfd, ".rld_map") == NULL)
{
- s = bfd_make_section_with_flags (abfd, ".rld_map",
- flags &~ (flagword) SEC_READONLY);
+ s = bfd_make_section_anyway_with_flags (abfd, ".rld_map",
+ flags &~ (flagword) SEC_READONLY);
if (s == NULL
|| ! bfd_set_section_alignment (abfd, s,
MIPS_ELF_LOG_FILE_ALIGN (abfd)))
}
/* Change alignments of some sections. */
- s = bfd_get_section_by_name (abfd, ".hash");
+ s = bfd_get_linker_section (abfd, ".hash");
if (s != NULL)
- bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
- s = bfd_get_section_by_name (abfd, ".dynsym");
+ (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+
+ s = bfd_get_linker_section (abfd, ".dynsym");
if (s != NULL)
- bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
- s = bfd_get_section_by_name (abfd, ".dynstr");
+ (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+
+ s = bfd_get_linker_section (abfd, ".dynstr");
if (s != NULL)
- bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+
+ /* ??? */
s = bfd_get_section_by_name (abfd, ".reginfo");
if (s != NULL)
- bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
- s = bfd_get_section_by_name (abfd, ".dynamic");
+ (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+
+ s = bfd_get_linker_section (abfd, ".dynamic");
if (s != NULL)
- bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
+ (void) bfd_set_section_alignment (abfd, s, MIPS_ELF_LOG_FILE_ALIGN (abfd));
}
if (!info->shared)
and is filled in by the rtld to contain a pointer to
the _r_debug structure. Its symbol value will be set in
_bfd_mips_elf_finish_dynamic_symbol. */
- s = bfd_get_section_by_name (abfd, ".rld_map");
+ s = bfd_get_linker_section (abfd, ".rld_map");
BFD_ASSERT (s != NULL);
name = SGI_COMPAT (abfd) ? "__rld_map" : "__RLD_MAP";
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
+ mips_elf_hash_table (info)->rld_symbol = h;
}
}
/* Create the .plt, .rel(a).plt, .dynbss and .rel(a).bss sections.
- Also create the _PROCEDURE_LINKAGE_TABLE symbol. */
+ Also, on VxWorks, create the _PROCEDURE_LINKAGE_TABLE_ symbol. */
if (!_bfd_elf_create_dynamic_sections (abfd, info))
return FALSE;
/* Cache the sections created above. */
- htab->splt = bfd_get_section_by_name (abfd, ".plt");
- htab->sdynbss = bfd_get_section_by_name (abfd, ".dynbss");
+ htab->splt = bfd_get_linker_section (abfd, ".plt");
+ htab->sdynbss = bfd_get_linker_section (abfd, ".dynbss");
if (htab->is_vxworks)
{
- htab->srelbss = bfd_get_section_by_name (abfd, ".rela.bss");
- htab->srelplt = bfd_get_section_by_name (abfd, ".rela.plt");
+ htab->srelbss = bfd_get_linker_section (abfd, ".rela.bss");
+ htab->srelplt = bfd_get_linker_section (abfd, ".rela.plt");
}
else
- htab->srelplt = bfd_get_section_by_name (abfd, ".rel.plt");
+ htab->srelplt = bfd_get_linker_section (abfd, ".rel.plt");
if (!htab->sdynbss
|| (htab->is_vxworks && !htab->srelbss && !info->shared)
|| !htab->srelplt
|| !htab->splt)
abort ();
- if (htab->is_vxworks)
- {
- /* Do the usual VxWorks handling. */
- if (!elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2))
- return FALSE;
-
- /* Work out the PLT sizes. */
- if (info->shared)
- {
- htab->plt_header_size
- = 4 * ARRAY_SIZE (mips_vxworks_shared_plt0_entry);
- htab->plt_entry_size
- = 4 * ARRAY_SIZE (mips_vxworks_shared_plt_entry);
- }
- else
- {
- htab->plt_header_size
- = 4 * ARRAY_SIZE (mips_vxworks_exec_plt0_entry);
- htab->plt_entry_size
- = 4 * ARRAY_SIZE (mips_vxworks_exec_plt_entry);
- }
- }
- else if (!info->shared)
- {
- /* All variants of the plt0 entry are the same size. */
- htab->plt_header_size = 4 * ARRAY_SIZE (mips_o32_exec_plt0_entry);
- htab->plt_entry_size = 4 * ARRAY_SIZE (mips_exec_plt_entry);
- }
+ /* Do the usual VxWorks handling. */
+ if (htab->is_vxworks
+ && !elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2))
+ return FALSE;
return TRUE;
}
lo16_type = R_MIPS16_LO16;
else if (micromips_reloc_p (r_type))
lo16_type = R_MICROMIPS_LO16;
+ else if (r_type == R_MIPS_PCHI16)
+ lo16_type = R_MIPS_PCLO16;
else
lo16_type = R_MIPS_LO16;
return bfd_malloc_and_get_section (abfd, sec, contents);
}
+/* Make a new PLT record to keep internal data. */
+
+static struct plt_entry *
+mips_elf_make_plt_record (bfd *abfd)
+{
+ struct plt_entry *entry;
+
+ entry = bfd_zalloc (abfd, sizeof (*entry));
+ if (entry == NULL)
+ return NULL;
+
+ entry->stub_offset = MINUS_ONE;
+ entry->mips_offset = MINUS_ONE;
+ entry->comp_offset = MINUS_ONE;
+ entry->gotplt_index = MINUS_ONE;
+ return entry;
+}
+
/* Look through the relocs for a section during the first phase, and
- allocate space in the global offset table. */
+ allocate space in the global offset table and record the need for
+ standard MIPS and compressed procedure linkage table entries. */
bfd_boolean
_bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
/* Look at the relocation information to figure out which symbol
this is for. */
- r_symndx = mips16_stub_symndx (sec, relocs, rel_end);
+ r_symndx = mips16_stub_symndx (bed, sec, relocs, rel_end);
if (r_symndx == 0)
{
(*_bfd_error_handler)
/* Record this stub in an array of local symbol stubs for
this BFD. */
- if (elf_tdata (abfd)->local_stubs == NULL)
+ if (mips_elf_tdata (abfd)->local_stubs == NULL)
{
unsigned long symcount;
asection **n;
n = bfd_zalloc (abfd, amt);
if (n == NULL)
return FALSE;
- elf_tdata (abfd)->local_stubs = n;
+ mips_elf_tdata (abfd)->local_stubs = n;
}
sec->flags |= SEC_KEEP;
- elf_tdata (abfd)->local_stubs[r_symndx] = sec;
+ mips_elf_tdata (abfd)->local_stubs[r_symndx] = sec;
/* We don't need to set mips16_stubs_seen in this case.
That flag is used to see whether we need to look through
/* Look at the relocation information to figure out which symbol
this is for. */
- r_symndx = mips16_stub_symndx (sec, relocs, rel_end);
+ r_symndx = mips16_stub_symndx (bed, sec, relocs, rel_end);
if (r_symndx == 0)
{
(*_bfd_error_handler)
/* Record this stub in an array of local symbol call_stubs for
this BFD. */
- if (elf_tdata (abfd)->local_call_stubs == NULL)
+ if (mips_elf_tdata (abfd)->local_call_stubs == NULL)
{
unsigned long symcount;
asection **n;
n = bfd_zalloc (abfd, amt);
if (n == NULL)
return FALSE;
- elf_tdata (abfd)->local_call_stubs = n;
+ mips_elf_tdata (abfd)->local_call_stubs = n;
}
sec->flags |= SEC_KEEP;
- elf_tdata (abfd)->local_call_stubs[r_symndx] = sec;
+ mips_elf_tdata (abfd)->local_call_stubs[r_symndx] = sec;
/* We don't need to set mips16_stubs_seen in this case.
That flag is used to see whether we need to look through
{
h = ((struct mips_elf_link_hash_entry *)
sym_hashes[r_symndx - extsymoff]);
-
+
/* H is the symbol this stub is for. */
-
+
if (CALL_FP_STUB_P (name))
loc = &h->call_fp_stub;
else
loc = &h->call_stub;
-
+
/* If we already have an appropriate stub for this function, we
don't need another one, so we can discard this one. Since
this function is called before the linker maps input sections
unsigned int r_type;
struct elf_link_hash_entry *h;
bfd_boolean can_make_dynamic_p;
+ bfd_boolean call_reloc_p;
+ bfd_boolean constrain_symbol_p;
r_symndx = ELF_R_SYM (abfd, rel->r_info);
r_type = ELF_R_TYPE (abfd, rel->r_info);
else
{
h = sym_hashes[r_symndx - extsymoff];
- while (h != NULL
- && (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning))
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ if (h != NULL)
+ {
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ /* PR15323, ref flags aren't set for references in the
+ same object. */
+ h->root.non_ir_ref = 1;
+ }
}
/* Set CAN_MAKE_DYNAMIC_P to true if we can convert this
relocation into a dynamic one. */
can_make_dynamic_p = FALSE;
+
+ /* Set CALL_RELOC_P to true if the relocation is for a call,
+ and if pointer equality therefore doesn't matter. */
+ call_reloc_p = FALSE;
+
+ /* Set CONSTRAIN_SYMBOL_P if we need to take the relocation
+ into account when deciding how to define the symbol.
+ Relocations in nonallocatable sections such as .pdr and
+ .debug* should have no effect. */
+ constrain_symbol_p = ((sec->flags & SEC_ALLOC) != 0);
+
switch (r_type)
{
- case R_MIPS16_GOT16:
+ case R_MIPS_CALL16:
+ case R_MIPS_CALL_HI16:
+ case R_MIPS_CALL_LO16:
case R_MIPS16_CALL16:
+ case R_MICROMIPS_CALL16:
+ case R_MICROMIPS_CALL_HI16:
+ case R_MICROMIPS_CALL_LO16:
+ call_reloc_p = TRUE;
+ /* Fall through. */
+
case R_MIPS_GOT16:
- case R_MIPS_CALL16:
- case R_MIPS_CALL_HI16:
- case R_MIPS_CALL_LO16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
case R_MIPS_GOT_PAGE:
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_GD:
case R_MIPS_TLS_LDM:
+ case R_MIPS16_GOT16:
+ case R_MIPS16_TLS_GOTTPREL:
+ case R_MIPS16_TLS_GD:
+ case R_MIPS16_TLS_LDM:
case R_MICROMIPS_GOT16:
- case R_MICROMIPS_CALL16:
- case R_MICROMIPS_CALL_HI16:
- case R_MICROMIPS_CALL_LO16:
case R_MICROMIPS_GOT_HI16:
case R_MICROMIPS_GOT_LO16:
case R_MICROMIPS_GOT_PAGE:
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
+ can_make_dynamic_p = TRUE;
break;
- /* This is just a hint; it can safely be ignored. Don't set
- has_static_relocs for the corresponding symbol. */
+ case R_MIPS_NONE:
case R_MIPS_JALR:
case R_MICROMIPS_JALR:
+ /* These relocations have empty fields and are purely there to
+ provide link information. The symbol value doesn't matter. */
+ constrain_symbol_p = FALSE;
+ break;
+
+ case R_MIPS_GPREL16:
+ case R_MIPS_GPREL32:
+ case R_MIPS16_GPREL:
+ case R_MICROMIPS_GPREL16:
+ /* GP-relative relocations always resolve to a definition in a
+ regular input file, ignoring the one-definition rule. This is
+ important for the GP setup sequence in NewABI code, which
+ always resolves to a local function even if other relocations
+ against the symbol wouldn't. */
+ constrain_symbol_p = FALSE;
break;
case R_MIPS_32:
can_make_dynamic_p = TRUE;
if (dynobj == NULL)
elf_hash_table (info)->dynobj = dynobj = abfd;
- break;
}
- /* For sections that are not SEC_ALLOC a copy reloc would be
- output if possible (implying questionable semantics for
- read-only data objects) or otherwise the final link would
- fail as ld.so will not process them and could not therefore
- handle any outstanding dynamic relocations.
-
- For such sections that are also SEC_DEBUGGING, we can avoid
- these problems by simply ignoring any relocs as these
- sections have a predefined use and we know it is safe to do
- so.
-
- This is needed in cases such as a global symbol definition
- in a shared library causing a common symbol from an object
- file to be converted to an undefined reference. If that
- happens, then all the relocations against this symbol from
- SEC_DEBUGGING sections in the object file will resolve to
- nil. */
- if ((sec->flags & SEC_DEBUGGING) != 0)
- break;
- /* Fall through. */
-
- default:
- /* Most static relocations require pointer equality, except
- for branches. */
- if (h)
- h->pointer_equality_needed = TRUE;
- /* Fall through. */
+ break;
case R_MIPS_26:
case R_MIPS_PC16:
+ case R_MIPS_PC21_S2:
+ case R_MIPS_PC26_S2:
case R_MIPS16_26:
case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC7_S1:
case R_MICROMIPS_PC10_S1:
case R_MICROMIPS_PC16_S1:
case R_MICROMIPS_PC23_S2:
- if (h)
- ((struct mips_elf_link_hash_entry *) h)->has_static_relocs = TRUE;
+ call_reloc_p = TRUE;
break;
}
if (h)
{
+ if (constrain_symbol_p)
+ {
+ if (!can_make_dynamic_p)
+ ((struct mips_elf_link_hash_entry *) h)->has_static_relocs = 1;
+
+ if (!call_reloc_p)
+ h->pointer_equality_needed = 1;
+
+ /* We must not create a stub for a symbol that has
+ relocations related to taking the function's address.
+ This doesn't apply to VxWorks, where CALL relocs refer
+ to a .got.plt entry instead of a normal .got entry. */
+ if (!htab->is_vxworks && (!can_make_dynamic_p || !call_reloc_p))
+ ((struct mips_elf_link_hash_entry *) h)->no_fn_stub = TRUE;
+ }
+
/* Relocations against the special VxWorks __GOTT_BASE__ and
__GOTT_INDEX__ symbols must be left to the loader. Allocate
room for them in .rela.dyn. */
R_MIPS_CALL_HI16 because these are always followed by an
R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16. */
if (!mips_elf_record_local_got_symbol (abfd, r_symndx,
- rel->r_addend, info, 0))
+ rel->r_addend, info, r_type))
return FALSE;
}
- if (h != NULL && mips_elf_relocation_needs_la25_stub (abfd, r_type))
+ if (h != NULL
+ && mips_elf_relocation_needs_la25_stub (abfd, r_type,
+ ELF_ST_IS_MIPS16 (h->other)))
((struct mips_elf_link_hash_entry *) h)->has_nonpic_branches = TRUE;
switch (r_type)
/* Make sure there is room in the regular GOT to hold the
function's address. We may eliminate it in favour of
a .got.plt entry later; see mips_elf_count_got_symbols. */
- if (!mips_elf_record_global_got_symbol (h, abfd, info, TRUE, 0))
+ if (!mips_elf_record_global_got_symbol (h, abfd, info, TRUE,
+ r_type))
return FALSE;
/* We need a stub, not a plt entry for the undefined
case R_MIPS_GOT_PAGE:
case R_MICROMIPS_GOT_PAGE:
- /* If this is a global, overridable symbol, GOT_PAGE will
- decay to GOT_DISP, so we'll need a GOT entry for it. */
- if (h)
- {
- struct mips_elf_link_hash_entry *hmips =
- (struct mips_elf_link_hash_entry *) h;
-
- /* This symbol is definitely not overridable. */
- if (hmips->root.def_regular
- && ! (info->shared && ! info->symbolic
- && ! hmips->root.forced_local))
- h = NULL;
- }
- /* Fall through. */
-
case R_MIPS16_GOT16:
case R_MIPS_GOT16:
case R_MIPS_GOT_HI16:
}
else
addend = rel->r_addend;
- if (!mips_elf_record_got_page_entry (info, abfd, r_symndx,
- addend))
+ if (!mips_elf_record_got_page_ref (info, abfd, r_symndx,
+ h, addend))
return FALSE;
+
+ if (h)
+ {
+ struct mips_elf_link_hash_entry *hmips =
+ (struct mips_elf_link_hash_entry *) h;
+
+ /* This symbol is definitely not overridable. */
+ if (hmips->root.def_regular
+ && ! (info->shared && ! info->symbolic
+ && ! hmips->root.forced_local))
+ h = NULL;
+ }
}
+ /* If this is a global, overridable symbol, GOT_PAGE will
+ decay to GOT_DISP, so we'll need a GOT entry for it. */
/* Fall through. */
case R_MIPS_GOT_DISP:
case R_MICROMIPS_GOT_DISP:
if (h && !mips_elf_record_global_got_symbol (h, abfd, info,
- FALSE, 0))
+ FALSE, r_type))
return FALSE;
break;
case R_MIPS_TLS_GOTTPREL:
+ case R_MIPS16_TLS_GOTTPREL:
case R_MICROMIPS_TLS_GOTTPREL:
if (info->shared)
info->flags |= DF_STATIC_TLS;
/* Fall through */
case R_MIPS_TLS_LDM:
+ case R_MIPS16_TLS_LDM:
case R_MICROMIPS_TLS_LDM:
if (tls_ldm_reloc_p (r_type))
{
/* Fall through */
case R_MIPS_TLS_GD:
+ case R_MIPS16_TLS_GD:
case R_MICROMIPS_TLS_GD:
/* This symbol requires a global offset table entry, or two
for TLS GD relocations. */
- {
- unsigned char flag;
-
- flag = (tls_gd_reloc_p (r_type)
- ? GOT_TLS_GD
- : tls_ldm_reloc_p (r_type) ? GOT_TLS_LDM : GOT_TLS_IE);
- if (h != NULL)
- {
- struct mips_elf_link_hash_entry *hmips =
- (struct mips_elf_link_hash_entry *) h;
- hmips->tls_type |= flag;
-
- if (h && !mips_elf_record_global_got_symbol (h, abfd, info,
- FALSE, flag))
- return FALSE;
- }
- else
- {
- BFD_ASSERT (flag == GOT_TLS_LDM || r_symndx != STN_UNDEF);
-
- if (!mips_elf_record_local_got_symbol (abfd, r_symndx,
- rel->r_addend,
- info, flag))
- return FALSE;
- }
- }
+ if (h != NULL)
+ {
+ if (!mips_elf_record_global_got_symbol (h, abfd, info,
+ FALSE, r_type))
+ return FALSE;
+ }
+ else
+ {
+ if (!mips_elf_record_local_got_symbol (abfd, r_symndx,
+ rel->r_addend,
+ info, r_type))
+ return FALSE;
+ }
break;
case R_MIPS_32:
break;
}
- /* We must not create a stub for a symbol that has relocations
- related to taking the function's address. This doesn't apply to
- VxWorks, where CALL relocs refer to a .got.plt entry instead of
- a normal .got entry. */
- if (!htab->is_vxworks && h != NULL)
- switch (r_type)
- {
- default:
- ((struct mips_elf_link_hash_entry *) h)->no_fn_stub = TRUE;
- break;
- case R_MIPS16_CALL16:
- case R_MIPS_CALL16:
- case R_MIPS_CALL_HI16:
- case R_MIPS_CALL_LO16:
- case R_MIPS_JALR:
- case R_MICROMIPS_CALL16:
- case R_MICROMIPS_CALL_HI16:
- case R_MICROMIPS_CALL_LO16:
- case R_MICROMIPS_JALR:
- break;
- }
+ /* Record the need for a PLT entry. At this point we don't know
+ yet if we are going to create a PLT in the first place, but
+ we only record whether the relocation requires a standard MIPS
+ or a compressed code entry anyway. If we don't make a PLT after
+ all, then we'll just ignore these arrangements. Likewise if
+ a PLT entry is not created because the symbol is satisfied
+ locally. */
+ if (h != NULL
+ && jal_reloc_p (r_type)
+ && !SYMBOL_CALLS_LOCAL (info, h))
+ {
+ if (h->plt.plist == NULL)
+ h->plt.plist = mips_elf_make_plt_record (abfd);
+ if (h->plt.plist == NULL)
+ return FALSE;
+
+ if (r_type == R_MIPS_26)
+ h->plt.plist->need_mips = TRUE;
+ else
+ h->plt.plist->need_comp = TRUE;
+ }
/* See if this reloc would need to refer to a MIPS16 hard-float stub,
if there is one. We only need to handle global symbols here;
if (! info->relocatable
&& hmips->possibly_dynamic_relocs != 0
&& (h->root.type == bfd_link_hash_defweak
- || !h->def_regular
+ || (!h->def_regular && !ELF_COMMON_DEF_P (h))
|| info->shared))
{
bfd_boolean do_copy = TRUE;
&& !(ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
&& h->root.type == bfd_link_hash_undefweak))
{
- /* If this is the first symbol to need a PLT entry, allocate room
- for the header. */
- if (htab->splt->size == 0)
+ bfd_boolean micromips_p = MICROMIPS_P (info->output_bfd);
+ bfd_boolean newabi_p = NEWABI_P (info->output_bfd);
+
+ /* If this is the first symbol to need a PLT entry, then make some
+ basic setup. Also work out PLT entry sizes. We'll need them
+ for PLT offset calculations. */
+ if (htab->plt_mips_offset + htab->plt_comp_offset == 0)
{
BFD_ASSERT (htab->sgotplt->size == 0);
+ BFD_ASSERT (htab->plt_got_index == 0);
/* If we're using the PLT additions to the psABI, each PLT
entry is 16 bytes and the PLT0 entry is 32 bytes.
MIPS_ELF_LOG_FILE_ALIGN (dynobj)))
return FALSE;
- htab->splt->size += htab->plt_header_size;
-
/* On non-VxWorks targets, the first two entries in .got.plt
are reserved. */
if (!htab->is_vxworks)
- htab->sgotplt->size += 2 * MIPS_ELF_GOT_SIZE (dynobj);
+ htab->plt_got_index
+ += (get_elf_backend_data (dynobj)->got_header_size
+ / MIPS_ELF_GOT_SIZE (dynobj));
/* On VxWorks, also allocate room for the header's
.rela.plt.unloaded entries. */
if (htab->is_vxworks && !info->shared)
htab->srelplt2->size += 2 * sizeof (Elf32_External_Rela);
+
+ /* Now work out the sizes of individual PLT entries. */
+ if (htab->is_vxworks && info->shared)
+ htab->plt_mips_entry_size
+ = 4 * ARRAY_SIZE (mips_vxworks_shared_plt_entry);
+ else if (htab->is_vxworks)
+ htab->plt_mips_entry_size
+ = 4 * ARRAY_SIZE (mips_vxworks_exec_plt_entry);
+ else if (newabi_p)
+ htab->plt_mips_entry_size
+ = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+ else if (!micromips_p)
+ {
+ htab->plt_mips_entry_size
+ = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+ htab->plt_comp_entry_size
+ = 2 * ARRAY_SIZE (mips16_o32_exec_plt_entry);
+ }
+ else if (htab->insn32)
+ {
+ htab->plt_mips_entry_size
+ = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+ htab->plt_comp_entry_size
+ = 2 * ARRAY_SIZE (micromips_insn32_o32_exec_plt_entry);
+ }
+ else
+ {
+ htab->plt_mips_entry_size
+ = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+ htab->plt_comp_entry_size
+ = 2 * ARRAY_SIZE (micromips_o32_exec_plt_entry);
+ }
+ }
+
+ if (h->plt.plist == NULL)
+ h->plt.plist = mips_elf_make_plt_record (dynobj);
+ if (h->plt.plist == NULL)
+ return FALSE;
+
+ /* There are no defined MIPS16 or microMIPS PLT entries for VxWorks,
+ n32 or n64, so always use a standard entry there.
+
+ If the symbol has a MIPS16 call stub and gets a PLT entry, then
+ all MIPS16 calls will go via that stub, and there is no benefit
+ to having a MIPS16 entry. And in the case of call_stub a
+ standard entry actually has to be used as the stub ends with a J
+ instruction. */
+ if (newabi_p
+ || htab->is_vxworks
+ || hmips->call_stub
+ || hmips->call_fp_stub)
+ {
+ h->plt.plist->need_mips = TRUE;
+ h->plt.plist->need_comp = FALSE;
+ }
+
+ /* Otherwise, if there are no direct calls to the function, we
+ have a free choice of whether to use standard or compressed
+ entries. Prefer microMIPS entries if the object is known to
+ contain microMIPS code, so that it becomes possible to create
+ pure microMIPS binaries. Prefer standard entries otherwise,
+ because MIPS16 ones are no smaller and are usually slower. */
+ if (!h->plt.plist->need_mips && !h->plt.plist->need_comp)
+ {
+ if (micromips_p)
+ h->plt.plist->need_comp = TRUE;
+ else
+ h->plt.plist->need_mips = TRUE;
}
- /* Assign the next .plt entry to this symbol. */
- h->plt.offset = htab->splt->size;
- htab->splt->size += htab->plt_entry_size;
+ if (h->plt.plist->need_mips)
+ {
+ h->plt.plist->mips_offset = htab->plt_mips_offset;
+ htab->plt_mips_offset += htab->plt_mips_entry_size;
+ }
+ if (h->plt.plist->need_comp)
+ {
+ h->plt.plist->comp_offset = htab->plt_comp_offset;
+ htab->plt_comp_offset += htab->plt_comp_entry_size;
+ }
+
+ /* Reserve the corresponding .got.plt entry now too. */
+ h->plt.plist->gotplt_index = htab->plt_got_index++;
/* If the output file has no definition of the symbol, set the
symbol's value to the address of the stub. */
if (!info->shared && !h->def_regular)
- {
- h->root.u.def.section = htab->splt;
- h->root.u.def.value = h->plt.offset;
- /* For VxWorks, point at the PLT load stub rather than the
- lazy resolution stub; this stub will become the canonical
- function address. */
- if (htab->is_vxworks)
- h->root.u.def.value += 8;
- }
+ hmips->use_plt_entry = TRUE;
- /* Make room for the .got.plt entry and the R_MIPS_JUMP_SLOT
- relocation. */
- htab->sgotplt->size += MIPS_ELF_GOT_SIZE (dynobj);
+ /* Make room for the R_MIPS_JUMP_SLOT relocation. */
htab->srelplt->size += (htab->is_vxworks
? MIPS_ELF_RELA_SIZE (dynobj)
: MIPS_ELF_REL_SIZE (dynobj));
_bfd_mips_elf_always_size_sections (bfd *output_bfd,
struct bfd_link_info *info)
{
- asection *ri;
+ asection *sect;
struct mips_elf_link_hash_table *htab;
struct mips_htab_traverse_info hti;
BFD_ASSERT (htab != NULL);
/* The .reginfo section has a fixed size. */
- ri = bfd_get_section_by_name (output_bfd, ".reginfo");
- if (ri != NULL)
- bfd_set_section_size (output_bfd, ri, sizeof (Elf32_External_RegInfo));
+ sect = bfd_get_section_by_name (output_bfd, ".reginfo");
+ if (sect != NULL)
+ bfd_set_section_size (output_bfd, sect, sizeof (Elf32_External_RegInfo));
+
+ /* The .MIPS.abiflags section has a fixed size. */
+ sect = bfd_get_section_by_name (output_bfd, ".MIPS.abiflags");
+ if (sect != NULL)
+ bfd_set_section_size (output_bfd, sect, sizeof (Elf_External_ABIFlags_v0));
hti.info = info;
hti.output_bfd = output_bfd;
struct mips_got_info *g;
bfd_size_type loadable_size = 0;
bfd_size_type page_gotno;
- bfd *sub;
- struct mips_elf_count_tls_arg count_tls_arg;
+ bfd *ibfd;
+ struct mips_elf_traverse_got_arg tga;
struct mips_elf_link_hash_table *htab;
htab = mips_elf_hash_table (info);
/* Allocate room for the reserved entries. VxWorks always reserves
3 entries; other objects only reserve 2 entries. */
- BFD_ASSERT (g->assigned_gotno == 0);
+ BFD_ASSERT (g->assigned_low_gotno == 0);
if (htab->is_vxworks)
htab->reserved_gotno = 3;
else
htab->reserved_gotno = 2;
g->local_gotno += htab->reserved_gotno;
- g->assigned_gotno = htab->reserved_gotno;
-
- /* Replace entries for indirect and warning symbols with entries for
- the target symbol. */
- if (!mips_elf_resolve_final_got_entries (g))
- return FALSE;
+ g->assigned_low_gotno = htab->reserved_gotno;
- /* Count the number of GOT symbols. */
+ /* Decide which symbols need to go in the global part of the GOT and
+ count the number of reloc-only GOT symbols. */
mips_elf_link_hash_traverse (htab, mips_elf_count_got_symbols, info);
+ if (!mips_elf_resolve_final_got_entries (info, g))
+ return FALSE;
+
/* Calculate the total loadable size of the output. That
will give us the maximum number of GOT_PAGE entries
required. */
- for (sub = info->input_bfds; sub; sub = sub->link_next)
+ for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link.next)
{
asection *subsection;
- for (subsection = sub->sections;
+ for (subsection = ibfd->sections;
subsection;
subsection = subsection->next)
{
sections. Is 5 enough? */
page_gotno = (loadable_size >> 16) + 5;
- /* Choose the smaller of the two estimates; both are intended to be
+ /* Choose the smaller of the two page estimates; both are intended to be
conservative. */
if (page_gotno > g->page_gotno)
page_gotno = g->page_gotno;
g->local_gotno += page_gotno;
+ g->assigned_high_gotno = g->local_gotno - 1;
+
s->size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
s->size += g->global_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
-
- /* 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);
/* VxWorks does not support multiple GOTs. It initializes $gp to
__GOTT_BASE__[__GOTT_INDEX__], the value of which is set by the
dynamic loader. */
- if (htab->is_vxworks)
- {
- /* VxWorks executables do not need a GOT. */
- if (info->shared)
- {
- /* Each VxWorks GOT entry needs an explicit relocation. */
- unsigned int count;
-
- count = g->global_gotno + g->local_gotno - htab->reserved_gotno;
- if (count)
- mips_elf_allocate_dynamic_relocations (dynobj, info, count);
- }
- }
- else if (s->size > MIPS_ELF_GOT_MAX_SIZE (info))
+ if (!htab->is_vxworks && s->size > MIPS_ELF_GOT_MAX_SIZE (info))
{
if (!mips_elf_multi_got (output_bfd, info, s, page_gotno))
return FALSE;
}
else
{
- struct mips_elf_count_tls_arg arg;
+ /* Record that all bfds use G. This also has the effect of freeing
+ the per-bfd GOTs, which we no longer need. */
+ for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link.next)
+ if (mips_elf_bfd_got (ibfd, FALSE))
+ mips_elf_replace_bfd_got (ibfd, g);
+ mips_elf_replace_bfd_got (output_bfd, g);
/* Set up TLS entries. */
g->tls_assigned_gotno = g->global_gotno + g->local_gotno;
- htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
+ tga.info = info;
+ tga.g = g;
+ tga.value = MIPS_ELF_GOT_SIZE (output_bfd);
+ htab_traverse (g->got_entries, mips_elf_initialize_tls_index, &tga);
+ if (!tga.g)
+ return FALSE;
+ BFD_ASSERT (g->tls_assigned_gotno
+ == g->global_gotno + g->local_gotno + g->tls_gotno);
+
+ /* Each VxWorks GOT entry needs an explicit relocation. */
+ if (htab->is_vxworks && info->shared)
+ g->relocs += g->global_gotno + g->local_gotno - htab->reserved_gotno;
/* Allocate room for the TLS relocations. */
- arg.info = info;
- arg.needed = 0;
- htab_traverse (g->got_entries, mips_elf_count_local_tls_relocs, &arg);
- elf_link_hash_traverse (elf_hash_table (info),
- mips_elf_count_global_tls_relocs,
- &arg);
- if (arg.needed)
- mips_elf_allocate_dynamic_relocations (dynobj, info, arg.needed);
+ if (g->relocs)
+ mips_elf_allocate_dynamic_relocations (dynobj, info, g->relocs);
}
return TRUE;
dynsymcount = (elf_hash_table (info)->dynsymcount
+ count_section_dynsyms (output_bfd, info));
- /* Determine the size of one stub entry. */
- htab->function_stub_size = (dynsymcount > 0x10000
- ? MIPS_FUNCTION_STUB_BIG_SIZE
- : MIPS_FUNCTION_STUB_NORMAL_SIZE);
+ /* Determine the size of one stub entry. There's no disadvantage
+ from using microMIPS code here, so for the sake of pure-microMIPS
+ binaries we prefer it whenever there's any microMIPS code in
+ output produced at all. This has a benefit of stubs being
+ shorter by 4 bytes each too, unless in the insn32 mode. */
+ if (!MICROMIPS_P (output_bfd))
+ htab->function_stub_size = (dynsymcount > 0x10000
+ ? MIPS_FUNCTION_STUB_BIG_SIZE
+ : MIPS_FUNCTION_STUB_NORMAL_SIZE);
+ else if (htab->insn32)
+ htab->function_stub_size = (dynsymcount > 0x10000
+ ? MICROMIPS_INSN32_FUNCTION_STUB_BIG_SIZE
+ : MICROMIPS_INSN32_FUNCTION_STUB_NORMAL_SIZE);
+ else
+ htab->function_stub_size = (dynsymcount > 0x10000
+ ? MICROMIPS_FUNCTION_STUB_BIG_SIZE
+ : MICROMIPS_FUNCTION_STUB_NORMAL_SIZE);
htab->sstubs->size = htab->lazy_stub_count * htab->function_stub_size;
}
-/* A mips_elf_link_hash_traverse callback for which DATA points to the
- MIPS hash table. If H needs a traditional MIPS lazy-binding stub,
- allocate an entry in the stubs section. */
+/* A mips_elf_link_hash_traverse callback for which DATA points to a
+ mips_htab_traverse_info. If H needs a traditional MIPS lazy-binding
+ stub, allocate an entry in the stubs section. */
static bfd_boolean
-mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void **data)
+mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void *data)
{
+ struct mips_htab_traverse_info *hti = data;
struct mips_elf_link_hash_table *htab;
+ struct bfd_link_info *info;
+ bfd *output_bfd;
+
+ info = hti->info;
+ output_bfd = hti->output_bfd;
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
- htab = (struct mips_elf_link_hash_table *) data;
if (h->needs_lazy_stub)
{
+ bfd_boolean micromips_p = MICROMIPS_P (output_bfd);
+ unsigned int other = micromips_p ? STO_MICROMIPS : 0;
+ bfd_vma isa_bit = micromips_p;
+
+ BFD_ASSERT (htab->root.dynobj != NULL);
+ if (h->root.plt.plist == NULL)
+ h->root.plt.plist = mips_elf_make_plt_record (htab->sstubs->owner);
+ if (h->root.plt.plist == NULL)
+ {
+ hti->error = TRUE;
+ return FALSE;
+ }
h->root.root.u.def.section = htab->sstubs;
- h->root.root.u.def.value = htab->sstubs->size;
- h->root.plt.offset = htab->sstubs->size;
+ h->root.root.u.def.value = htab->sstubs->size + isa_bit;
+ h->root.plt.plist->stub_offset = htab->sstubs->size;
+ h->root.other = other;
htab->sstubs->size += htab->function_stub_size;
}
return TRUE;
/* Allocate offsets in the stubs section to each symbol that needs one.
Set the final size of the .MIPS.stub section. */
-static void
+static bfd_boolean
mips_elf_lay_out_lazy_stubs (struct bfd_link_info *info)
{
+ bfd *output_bfd = info->output_bfd;
+ bfd_boolean micromips_p = MICROMIPS_P (output_bfd);
+ unsigned int other = micromips_p ? STO_MICROMIPS : 0;
+ bfd_vma isa_bit = micromips_p;
struct mips_elf_link_hash_table *htab;
+ struct mips_htab_traverse_info hti;
+ struct elf_link_hash_entry *h;
+ bfd *dynobj;
htab = mips_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
if (htab->lazy_stub_count == 0)
- return;
+ return TRUE;
htab->sstubs->size = 0;
- mips_elf_link_hash_traverse (htab, mips_elf_allocate_lazy_stub, htab);
+ hti.info = info;
+ hti.output_bfd = output_bfd;
+ hti.error = FALSE;
+ mips_elf_link_hash_traverse (htab, mips_elf_allocate_lazy_stub, &hti);
+ if (hti.error)
+ return FALSE;
htab->sstubs->size += htab->function_stub_size;
BFD_ASSERT (htab->sstubs->size
== htab->lazy_stub_count * htab->function_stub_size);
+
+ dynobj = elf_hash_table (info)->dynobj;
+ BFD_ASSERT (dynobj != NULL);
+ h = _bfd_elf_define_linkage_sym (dynobj, info, htab->sstubs, "_MIPS_STUBS_");
+ if (h == NULL)
+ return FALSE;
+ h->root.u.def.value = isa_bit;
+ h->other = other;
+ h->type = STT_FUNC;
+
+ return TRUE;
+}
+
+/* A mips_elf_link_hash_traverse callback for which DATA points to a
+ bfd_link_info. If H uses the address of a PLT entry as the value
+ of the symbol, then set the entry in the symbol table now. Prefer
+ a standard MIPS PLT entry. */
+
+static bfd_boolean
+mips_elf_set_plt_sym_value (struct mips_elf_link_hash_entry *h, void *data)
+{
+ struct bfd_link_info *info = data;
+ bfd_boolean micromips_p = MICROMIPS_P (info->output_bfd);
+ struct mips_elf_link_hash_table *htab;
+ unsigned int other;
+ bfd_vma isa_bit;
+ bfd_vma val;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ if (h->use_plt_entry)
+ {
+ BFD_ASSERT (h->root.plt.plist != NULL);
+ BFD_ASSERT (h->root.plt.plist->mips_offset != MINUS_ONE
+ || h->root.plt.plist->comp_offset != MINUS_ONE);
+
+ val = htab->plt_header_size;
+ if (h->root.plt.plist->mips_offset != MINUS_ONE)
+ {
+ isa_bit = 0;
+ val += h->root.plt.plist->mips_offset;
+ other = 0;
+ }
+ else
+ {
+ isa_bit = 1;
+ val += htab->plt_mips_offset + h->root.plt.plist->comp_offset;
+ other = micromips_p ? STO_MICROMIPS : STO_MIPS16;
+ }
+ val += isa_bit;
+ /* For VxWorks, point at the PLT load stub rather than the lazy
+ resolution stub; this stub will become the canonical function
+ address. */
+ if (htab->is_vxworks)
+ val += 8;
+
+ h->root.root.u.def.section = htab->splt;
+ h->root.root.u.def.value = val;
+ h->root.other = other;
+ }
+
+ return TRUE;
}
/* Set the sizes of the dynamic sections. */
/* Set the contents of the .interp section to the interpreter. */
if (info->executable)
{
- s = bfd_get_section_by_name (dynobj, ".interp");
+ s = bfd_get_linker_section (dynobj, ".interp");
BFD_ASSERT (s != NULL);
s->size
= strlen (ELF_DYNAMIC_INTERPRETER (output_bfd)) + 1;
= (bfd_byte *) ELF_DYNAMIC_INTERPRETER (output_bfd);
}
- /* Create a symbol for the PLT, if we know that we are using it. */
- if (htab->splt && htab->splt->size > 0 && htab->root.hplt == NULL)
+ /* Figure out the size of the PLT header if we know that we
+ are using it. For the sake of cache alignment always use
+ a standard header whenever any standard entries are present
+ even if microMIPS entries are present as well. This also
+ lets the microMIPS header rely on the value of $v0 only set
+ by microMIPS entries, for a small size reduction.
+
+ Set symbol table entry values for symbols that use the
+ address of their PLT entry now that we can calculate it.
+
+ Also create the _PROCEDURE_LINKAGE_TABLE_ symbol if we
+ haven't already in _bfd_elf_create_dynamic_sections. */
+ if (htab->splt && htab->plt_mips_offset + htab->plt_comp_offset != 0)
{
+ bfd_boolean micromips_p = (MICROMIPS_P (output_bfd)
+ && !htab->plt_mips_offset);
+ unsigned int other = micromips_p ? STO_MICROMIPS : 0;
+ bfd_vma isa_bit = micromips_p;
struct elf_link_hash_entry *h;
+ bfd_vma size;
BFD_ASSERT (htab->use_plts_and_copy_relocs);
+ BFD_ASSERT (htab->sgotplt->size == 0);
+ BFD_ASSERT (htab->splt->size == 0);
+
+ if (htab->is_vxworks && info->shared)
+ size = 4 * ARRAY_SIZE (mips_vxworks_shared_plt0_entry);
+ else if (htab->is_vxworks)
+ size = 4 * ARRAY_SIZE (mips_vxworks_exec_plt0_entry);
+ else if (ABI_64_P (output_bfd))
+ size = 4 * ARRAY_SIZE (mips_n64_exec_plt0_entry);
+ else if (ABI_N32_P (output_bfd))
+ size = 4 * ARRAY_SIZE (mips_n32_exec_plt0_entry);
+ else if (!micromips_p)
+ size = 4 * ARRAY_SIZE (mips_o32_exec_plt0_entry);
+ else if (htab->insn32)
+ size = 2 * ARRAY_SIZE (micromips_insn32_o32_exec_plt0_entry);
+ else
+ size = 2 * ARRAY_SIZE (micromips_o32_exec_plt0_entry);
- h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
- "_PROCEDURE_LINKAGE_TABLE_");
- htab->root.hplt = h;
- if (h == NULL)
- return FALSE;
+ htab->plt_header_is_comp = micromips_p;
+ htab->plt_header_size = size;
+ htab->splt->size = (size
+ + htab->plt_mips_offset
+ + htab->plt_comp_offset);
+ htab->sgotplt->size = (htab->plt_got_index
+ * MIPS_ELF_GOT_SIZE (dynobj));
+
+ mips_elf_link_hash_traverse (htab, mips_elf_set_plt_sym_value, info);
+
+ if (htab->root.hplt == NULL)
+ {
+ h = _bfd_elf_define_linkage_sym (dynobj, info, htab->splt,
+ "_PROCEDURE_LINKAGE_TABLE_");
+ htab->root.hplt = h;
+ if (h == NULL)
+ return FALSE;
+ }
+
+ h = htab->root.hplt;
+ h->root.u.def.value = isa_bit;
+ h->other = other;
h->type = STT_FUNC;
}
}
/* Allocate space for global sym dynamic relocs. */
- elf_link_hash_traverse (&htab->root, allocate_dynrelocs, (PTR) info);
+ elf_link_hash_traverse (&htab->root, allocate_dynrelocs, info);
mips_elf_estimate_stub_size (output_bfd, info);
{
/* We add a room for __rld_map. It will be filled in by the
rtld to contain a pointer to the _r_debug structure. */
- s->size += 4;
+ s->size += MIPS_ELF_RLD_MAP_SIZE (output_bfd);
}
else if (SGI_COMPAT (output_bfd)
&& CONST_STRNEQ (name, ".compact_rel"))
/* SGI object has the equivalence of DT_DEBUG in the
DT_MIPS_RLD_MAP entry. This must come first because glibc
- only fills in DT_MIPS_RLD_MAP (not DT_DEBUG) and GDB only
- looks at the first one it sees. */
+ only fills in DT_MIPS_RLD_MAP (not DT_DEBUG) and some tools
+ may only look at the first one they see. */
if (!info->shared
&& !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_RLD_MAP, 0))
return FALSE;
if (IRIX_COMPAT (dynobj) == ict_irix6
&& (bfd_get_section_by_name
- (dynobj, MIPS_ELF_OPTIONS_SECTION_NAME (dynobj)))
+ (output_bfd, MIPS_ELF_OPTIONS_SECTION_NAME (dynobj)))
&& !MIPS_ELF_ADD_DYNAMIC_ENTRY (info, DT_MIPS_OPTIONS, 0))
return FALSE;
}
}
}
+/* Handle relocations against symbols from removed linkonce sections,
+ or sections discarded by a linker script. We use this wrapper around
+ RELOC_AGAINST_DISCARDED_SECTION to handle triplets of compound relocs
+ on 64-bit ELF targets. In this case for any relocation handled, which
+ always be the first in a triplet, the remaining two have to be processed
+ together with the first, even if they are R_MIPS_NONE. It is the symbol
+ index referred by the first reloc that applies to all the three and the
+ remaining two never refer to an object symbol. And it is the final
+ relocation (the last non-null one) that determines the output field of
+ the whole relocation so retrieve the corresponding howto structure for
+ the relocatable field to be cleared by RELOC_AGAINST_DISCARDED_SECTION.
+
+ Note that RELOC_AGAINST_DISCARDED_SECTION is a macro that uses "continue"
+ and therefore requires to be pasted in a loop. It also defines a block
+ and does not protect any of its arguments, hence the extra brackets. */
+
+static void
+mips_reloc_against_discarded_section (bfd *output_bfd,
+ struct bfd_link_info *info,
+ bfd *input_bfd, asection *input_section,
+ Elf_Internal_Rela **rel,
+ const Elf_Internal_Rela **relend,
+ bfd_boolean rel_reloc,
+ reloc_howto_type *howto,
+ bfd_byte *contents)
+{
+ const struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
+ int count = bed->s->int_rels_per_ext_rel;
+ unsigned int r_type;
+ int i;
+
+ for (i = count - 1; i > 0; i--)
+ {
+ r_type = ELF_R_TYPE (output_bfd, (*rel)[i].r_info);
+ if (r_type != R_MIPS_NONE)
+ {
+ howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd, r_type, !rel_reloc);
+ break;
+ }
+ }
+ do
+ {
+ RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+ (*rel), count, (*relend),
+ howto, i, contents);
+ }
+ while (0);
+}
+
/* Relocate a MIPS ELF section. */
bfd_boolean
const char *name;
bfd_vma value = 0;
reloc_howto_type *howto;
- bfd_boolean cross_mode_jump_p;
+ bfd_boolean cross_mode_jump_p = FALSE;
/* TRUE if the relocation is a RELA relocation, rather than a
REL relocation. */
bfd_boolean rela_relocation_p = TRUE;
sec = h->root.u.def.section;
}
- if (sec != NULL && elf_discarded_section (sec))
- RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
- rel, relend, howto, contents);
+ if (sec != NULL && discarded_section (sec))
+ {
+ mips_reloc_against_discarded_section (output_bfd, info, input_bfd,
+ input_section, &rel, &relend,
+ rel_reloc, howto, contents);
+ continue;
+ }
if (r_type == R_MIPS_64 && ! NEWABI_P (input_bfd))
{
(info, msg, name, input_bfd, input_section, rel->r_offset);
return FALSE;
}
+ if (aligned_pcrel_reloc_p (howto->type))
+ {
+ msg = _("PC-relative load from unaligned address");
+ info->callbacks->warning
+ (info, msg, name, input_bfd, input_section, rel->r_offset);
+ return FALSE;
+ }
/* Fall through. */
default:
offset = stub->offset;
/* Work out the target address. */
- target = (stub->h->root.root.u.def.section->output_section->vma
- + stub->h->root.root.u.def.section->output_offset
- + stub->h->root.root.u.def.value);
+ target = mips_elf_get_la25_target (stub, &s);
+ target += s->output_section->vma + s->output_offset;
+
target_high = ((target + 0x8000) >> 16) & 0xffff;
target_low = (target & 0xffff);
loc += offset;
if (ELF_ST_IS_MICROMIPS (stub->h->root.other))
{
- bfd_put_16 (hti->output_bfd, LA25_LUI_MICROMIPS_1 (target_high),
- loc);
- bfd_put_16 (hti->output_bfd, LA25_LUI_MICROMIPS_2 (target_high),
- loc + 2);
- bfd_put_16 (hti->output_bfd, LA25_ADDIU_MICROMIPS_1 (target_low),
- loc + 4);
- bfd_put_16 (hti->output_bfd, LA25_ADDIU_MICROMIPS_2 (target_low),
- loc + 6);
+ bfd_put_micromips_32 (hti->output_bfd,
+ LA25_LUI_MICROMIPS (target_high),
+ loc);
+ bfd_put_micromips_32 (hti->output_bfd,
+ LA25_ADDIU_MICROMIPS (target_low),
+ loc + 4);
}
else
{
loc += offset;
if (ELF_ST_IS_MICROMIPS (stub->h->root.other))
{
- bfd_put_16 (hti->output_bfd, LA25_LUI_MICROMIPS_1 (target_high),
- loc);
- bfd_put_16 (hti->output_bfd, LA25_LUI_MICROMIPS_2 (target_high),
- loc + 2);
- bfd_put_16 (hti->output_bfd, LA25_J_MICROMIPS_1 (target), loc + 4);
- bfd_put_16 (hti->output_bfd, LA25_J_MICROMIPS_2 (target), loc + 6);
- bfd_put_16 (hti->output_bfd, LA25_ADDIU_MICROMIPS_1 (target_low),
- loc + 8);
- bfd_put_16 (hti->output_bfd, LA25_ADDIU_MICROMIPS_2 (target_low),
- loc + 10);
+ bfd_put_micromips_32 (hti->output_bfd,
+ LA25_LUI_MICROMIPS (target_high), loc);
+ bfd_put_micromips_32 (hti->output_bfd,
+ LA25_J_MICROMIPS (target), loc + 4);
+ bfd_put_micromips_32 (hti->output_bfd,
+ LA25_ADDIU_MICROMIPS (target_low), loc + 8);
bfd_put_32 (hti->output_bfd, 0, loc + 12);
}
else
BFD_ASSERT (!htab->is_vxworks);
- if (h->plt.offset != MINUS_ONE && hmips->no_fn_stub)
+ if (h->plt.plist != NULL
+ && (h->plt.plist->mips_offset != MINUS_ONE
+ || h->plt.plist->comp_offset != MINUS_ONE))
{
/* We've decided to create a PLT entry for this symbol. */
bfd_byte *loc;
- bfd_vma header_address, plt_index, got_address;
+ bfd_vma header_address, got_address;
bfd_vma got_address_high, got_address_low, load;
- const bfd_vma *plt_entry;
+ bfd_vma got_index;
+ bfd_vma isa_bit;
+
+ got_index = h->plt.plist->gotplt_index;
BFD_ASSERT (htab->use_plts_and_copy_relocs);
BFD_ASSERT (h->dynindx != -1);
BFD_ASSERT (htab->splt != NULL);
- BFD_ASSERT (h->plt.offset <= htab->splt->size);
+ BFD_ASSERT (got_index != MINUS_ONE);
BFD_ASSERT (!h->def_regular);
/* Calculate the address of the PLT header. */
+ isa_bit = htab->plt_header_is_comp;
header_address = (htab->splt->output_section->vma
- + htab->splt->output_offset);
-
- /* Calculate the index of the entry. */
- plt_index = ((h->plt.offset - htab->plt_header_size)
- / htab->plt_entry_size);
+ + htab->splt->output_offset + isa_bit);
/* Calculate the address of the .got.plt entry. */
got_address = (htab->sgotplt->output_section->vma
+ htab->sgotplt->output_offset
- + (2 + plt_index) * MIPS_ELF_GOT_SIZE (dynobj));
+ + got_index * MIPS_ELF_GOT_SIZE (dynobj));
+
got_address_high = ((got_address + 0x8000) >> 16) & 0xffff;
got_address_low = got_address & 0xffff;
/* Initially point the .got.plt entry at the PLT header. */
- loc = (htab->sgotplt->contents
- + (2 + plt_index) * MIPS_ELF_GOT_SIZE (dynobj));
+ loc = (htab->sgotplt->contents + got_index * MIPS_ELF_GOT_SIZE (dynobj));
if (ABI_64_P (output_bfd))
bfd_put_64 (output_bfd, header_address, loc);
else
bfd_put_32 (output_bfd, header_address, loc);
- /* Find out where the .plt entry should go. */
- loc = htab->splt->contents + h->plt.offset;
+ /* Now handle the PLT itself. First the standard entry (the order
+ does not matter, we just have to pick one). */
+ if (h->plt.plist->mips_offset != MINUS_ONE)
+ {
+ const bfd_vma *plt_entry;
+ bfd_vma plt_offset;
- /* Pick the load opcode. */
- load = MIPS_ELF_LOAD_WORD (output_bfd);
+ plt_offset = htab->plt_header_size + h->plt.plist->mips_offset;
- /* Fill in the PLT entry itself. */
- plt_entry = mips_exec_plt_entry;
- bfd_put_32 (output_bfd, plt_entry[0] | got_address_high, loc);
- bfd_put_32 (output_bfd, plt_entry[1] | got_address_low | load, loc + 4);
+ BFD_ASSERT (plt_offset <= htab->splt->size);
- if (! LOAD_INTERLOCKS_P (output_bfd))
- {
- bfd_put_32 (output_bfd, plt_entry[2] | got_address_low, loc + 8);
- bfd_put_32 (output_bfd, plt_entry[3], loc + 12);
+ /* Find out where the .plt entry should go. */
+ loc = htab->splt->contents + plt_offset;
+
+ /* Pick the load opcode. */
+ load = MIPS_ELF_LOAD_WORD (output_bfd);
+
+ /* Fill in the PLT entry itself. */
+
+ if (MIPSR6_P (output_bfd))
+ plt_entry = mipsr6_exec_plt_entry;
+ else
+ plt_entry = mips_exec_plt_entry;
+ bfd_put_32 (output_bfd, plt_entry[0] | got_address_high, loc);
+ bfd_put_32 (output_bfd, plt_entry[1] | got_address_low | load,
+ loc + 4);
+
+ if (! LOAD_INTERLOCKS_P (output_bfd))
+ {
+ bfd_put_32 (output_bfd, plt_entry[2] | got_address_low, loc + 8);
+ bfd_put_32 (output_bfd, plt_entry[3], loc + 12);
+ }
+ else
+ {
+ bfd_put_32 (output_bfd, plt_entry[3], loc + 8);
+ bfd_put_32 (output_bfd, plt_entry[2] | got_address_low,
+ loc + 12);
+ }
}
- else
+
+ /* Now the compressed entry. They come after any standard ones. */
+ if (h->plt.plist->comp_offset != MINUS_ONE)
{
- bfd_put_32 (output_bfd, plt_entry[3], loc + 8);
- bfd_put_32 (output_bfd, plt_entry[2] | got_address_low, loc + 12);
+ bfd_vma plt_offset;
+
+ plt_offset = (htab->plt_header_size + htab->plt_mips_offset
+ + h->plt.plist->comp_offset);
+
+ BFD_ASSERT (plt_offset <= htab->splt->size);
+
+ /* Find out where the .plt entry should go. */
+ loc = htab->splt->contents + plt_offset;
+
+ /* Fill in the PLT entry itself. */
+ if (!MICROMIPS_P (output_bfd))
+ {
+ const bfd_vma *plt_entry = mips16_o32_exec_plt_entry;
+
+ bfd_put_16 (output_bfd, plt_entry[0], loc);
+ bfd_put_16 (output_bfd, plt_entry[1], loc + 2);
+ bfd_put_16 (output_bfd, plt_entry[2], loc + 4);
+ bfd_put_16 (output_bfd, plt_entry[3], loc + 6);
+ bfd_put_16 (output_bfd, plt_entry[4], loc + 8);
+ bfd_put_16 (output_bfd, plt_entry[5], loc + 10);
+ bfd_put_32 (output_bfd, got_address, loc + 12);
+ }
+ else if (htab->insn32)
+ {
+ const bfd_vma *plt_entry = micromips_insn32_o32_exec_plt_entry;
+
+ bfd_put_16 (output_bfd, plt_entry[0], loc);
+ bfd_put_16 (output_bfd, got_address_high, loc + 2);
+ bfd_put_16 (output_bfd, plt_entry[2], loc + 4);
+ bfd_put_16 (output_bfd, got_address_low, loc + 6);
+ bfd_put_16 (output_bfd, plt_entry[4], loc + 8);
+ bfd_put_16 (output_bfd, plt_entry[5], loc + 10);
+ bfd_put_16 (output_bfd, plt_entry[6], loc + 12);
+ bfd_put_16 (output_bfd, got_address_low, loc + 14);
+ }
+ else
+ {
+ const bfd_vma *plt_entry = micromips_o32_exec_plt_entry;
+ bfd_signed_vma gotpc_offset;
+ bfd_vma loc_address;
+
+ BFD_ASSERT (got_address % 4 == 0);
+
+ loc_address = (htab->splt->output_section->vma
+ + htab->splt->output_offset + plt_offset);
+ gotpc_offset = got_address - ((loc_address | 3) ^ 3);
+
+ /* ADDIUPC has a span of +/-16MB, check we're in range. */
+ if (gotpc_offset + 0x1000000 >= 0x2000000)
+ {
+ (*_bfd_error_handler)
+ (_("%B: `%A' offset of %ld from `%A' "
+ "beyond the range of ADDIUPC"),
+ output_bfd,
+ htab->sgotplt->output_section,
+ htab->splt->output_section,
+ (long) gotpc_offset);
+ bfd_set_error (bfd_error_no_error);
+ return FALSE;
+ }
+ bfd_put_16 (output_bfd,
+ plt_entry[0] | ((gotpc_offset >> 18) & 0x7f), loc);
+ bfd_put_16 (output_bfd, (gotpc_offset >> 2) & 0xffff, loc + 2);
+ bfd_put_16 (output_bfd, plt_entry[2], loc + 4);
+ bfd_put_16 (output_bfd, plt_entry[3], loc + 6);
+ bfd_put_16 (output_bfd, plt_entry[4], loc + 8);
+ bfd_put_16 (output_bfd, plt_entry[5], loc + 10);
+ }
}
/* Emit an R_MIPS_JUMP_SLOT relocation against the .got.plt entry. */
mips_elf_output_dynamic_relocation (output_bfd, htab->srelplt,
- plt_index, h->dynindx,
+ got_index - 2, h->dynindx,
R_MIPS_JUMP_SLOT, got_address);
/* We distinguish between PLT entries and lazy-binding stubs by
binary where pointer equality matters. */
sym->st_shndx = SHN_UNDEF;
if (h->pointer_equality_needed)
- sym->st_other = STO_MIPS_PLT;
+ sym->st_other = ELF_ST_SET_MIPS_PLT (sym->st_other);
else
- sym->st_value = 0;
+ {
+ sym->st_value = 0;
+ sym->st_other = 0;
+ }
}
- else if (h->plt.offset != MINUS_ONE)
+
+ if (h->plt.plist != NULL && h->plt.plist->stub_offset != MINUS_ONE)
{
/* We've decided to create a lazy-binding stub. */
+ bfd_boolean micromips_p = MICROMIPS_P (output_bfd);
+ unsigned int other = micromips_p ? STO_MICROMIPS : 0;
+ bfd_vma stub_size = htab->function_stub_size;
bfd_byte stub[MIPS_FUNCTION_STUB_BIG_SIZE];
+ bfd_vma isa_bit = micromips_p;
+ bfd_vma stub_big_size;
+
+ if (!micromips_p)
+ stub_big_size = MIPS_FUNCTION_STUB_BIG_SIZE;
+ else if (htab->insn32)
+ stub_big_size = MICROMIPS_INSN32_FUNCTION_STUB_BIG_SIZE;
+ else
+ stub_big_size = MICROMIPS_FUNCTION_STUB_BIG_SIZE;
/* This symbol has a stub. Set it up. */
BFD_ASSERT (h->dynindx != -1);
- BFD_ASSERT ((htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
- || (h->dynindx <= 0xffff));
+ BFD_ASSERT (stub_size == stub_big_size || h->dynindx <= 0xffff);
/* Values up to 2^31 - 1 are allowed. Larger values would cause
sign extension at runtime in the stub, resulting in a negative
return FALSE;
/* Fill the stub. */
- idx = 0;
- bfd_put_32 (output_bfd, STUB_LW (output_bfd), stub + idx);
- idx += 4;
- bfd_put_32 (output_bfd, STUB_MOVE (output_bfd), stub + idx);
- idx += 4;
- if (htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
- {
- bfd_put_32 (output_bfd, STUB_LUI ((h->dynindx >> 16) & 0x7fff),
- stub + idx);
- idx += 4;
- }
- bfd_put_32 (output_bfd, STUB_JALR, stub + idx);
- idx += 4;
-
- /* If a large stub is not required and sign extension is not a
- problem, then use legacy code in the stub. */
- if (htab->function_stub_size == MIPS_FUNCTION_STUB_BIG_SIZE)
- bfd_put_32 (output_bfd, STUB_ORI (h->dynindx & 0xffff), stub + idx);
- else if (h->dynindx & ~0x7fff)
- bfd_put_32 (output_bfd, STUB_LI16U (h->dynindx & 0xffff), stub + idx);
+ if (micromips_p)
+ {
+ idx = 0;
+ bfd_put_micromips_32 (output_bfd, STUB_LW_MICROMIPS (output_bfd),
+ stub + idx);
+ idx += 4;
+ if (htab->insn32)
+ {
+ bfd_put_micromips_32 (output_bfd,
+ STUB_MOVE32_MICROMIPS (output_bfd),
+ stub + idx);
+ idx += 4;
+ }
+ else
+ {
+ bfd_put_16 (output_bfd, STUB_MOVE_MICROMIPS, stub + idx);
+ idx += 2;
+ }
+ if (stub_size == stub_big_size)
+ {
+ long dynindx_hi = (h->dynindx >> 16) & 0x7fff;
+
+ bfd_put_micromips_32 (output_bfd,
+ STUB_LUI_MICROMIPS (dynindx_hi),
+ stub + idx);
+ idx += 4;
+ }
+ if (htab->insn32)
+ {
+ bfd_put_micromips_32 (output_bfd, STUB_JALR32_MICROMIPS,
+ stub + idx);
+ idx += 4;
+ }
+ else
+ {
+ bfd_put_16 (output_bfd, STUB_JALR_MICROMIPS, stub + idx);
+ idx += 2;
+ }
+
+ /* If a large stub is not required and sign extension is not a
+ problem, then use legacy code in the stub. */
+ if (stub_size == stub_big_size)
+ bfd_put_micromips_32 (output_bfd,
+ STUB_ORI_MICROMIPS (h->dynindx & 0xffff),
+ stub + idx);
+ else if (h->dynindx & ~0x7fff)
+ bfd_put_micromips_32 (output_bfd,
+ STUB_LI16U_MICROMIPS (h->dynindx & 0xffff),
+ stub + idx);
+ else
+ bfd_put_micromips_32 (output_bfd,
+ STUB_LI16S_MICROMIPS (output_bfd,
+ h->dynindx),
+ stub + idx);
+ }
else
- bfd_put_32 (output_bfd, STUB_LI16S (output_bfd, h->dynindx),
- stub + idx);
+ {
+ idx = 0;
+ bfd_put_32 (output_bfd, STUB_LW (output_bfd), stub + idx);
+ idx += 4;
+ bfd_put_32 (output_bfd, STUB_MOVE (output_bfd), stub + idx);
+ idx += 4;
+ if (stub_size == stub_big_size)
+ {
+ bfd_put_32 (output_bfd, STUB_LUI ((h->dynindx >> 16) & 0x7fff),
+ stub + idx);
+ idx += 4;
+ }
+ bfd_put_32 (output_bfd, STUB_JALR, stub + idx);
+ idx += 4;
+
+ /* If a large stub is not required and sign extension is not a
+ problem, then use legacy code in the stub. */
+ if (stub_size == stub_big_size)
+ bfd_put_32 (output_bfd, STUB_ORI (h->dynindx & 0xffff),
+ stub + idx);
+ else if (h->dynindx & ~0x7fff)
+ bfd_put_32 (output_bfd, STUB_LI16U (h->dynindx & 0xffff),
+ stub + idx);
+ else
+ bfd_put_32 (output_bfd, STUB_LI16S (output_bfd, h->dynindx),
+ stub + idx);
+ }
- BFD_ASSERT (h->plt.offset <= htab->sstubs->size);
- memcpy (htab->sstubs->contents + h->plt.offset,
- stub, htab->function_stub_size);
+ BFD_ASSERT (h->plt.plist->stub_offset <= htab->sstubs->size);
+ memcpy (htab->sstubs->contents + h->plt.plist->stub_offset,
+ stub, stub_size);
- /* Mark the symbol as undefined. plt.offset != -1 occurs
+ /* Mark the symbol as undefined. stub_offset != -1 occurs
only for the referenced symbol. */
sym->st_shndx = SHN_UNDEF;
to its stub address when unlinking a shared object. */
sym->st_value = (htab->sstubs->output_section->vma
+ htab->sstubs->output_offset
- + h->plt.offset);
+ + h->plt.plist->stub_offset
+ + isa_bit);
+ sym->st_other = other;
}
/* If we have a MIPS16 function with a stub, the dynamic symbol must
bfd_vma value;
value = sym->st_value;
- offset = mips_elf_global_got_index (dynobj, output_bfd, h,
- R_MIPS_GOT16, info);
+ offset = mips_elf_primary_global_got_index (output_bfd, info, h);
MIPS_ELF_PUT_WORD (output_bfd, value, sgot->contents + offset);
}
- if (hmips->global_got_area != GGA_NONE && g->next && h->type != STT_TLS)
+ if (hmips->global_got_area != GGA_NONE && g->next)
{
struct mips_got_entry e, *p;
bfd_vma entry;
e.abfd = output_bfd;
e.symndx = -1;
e.d.h = hmips;
- e.tls_type = 0;
+ e.tls_type = GOT_TLS_NONE;
for (g = g->next; g->next != gg; g = g->next)
{
&e)))
{
offset = p->gotidx;
+ BFD_ASSERT (offset > 0 && offset < htab->sgot->size);
if (info->shared
|| (elf_hash_table (info)->dynamic_sections_created
&& p->d.h != NULL
/* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */
name = h->root.root.string;
- if (strcmp (name, "_DYNAMIC") == 0
+ if (h == elf_hash_table (info)->hdynamic
|| h == elf_hash_table (info)->hgot)
sym->st_shndx = SHN_ABS;
else if (strcmp (name, "_DYNAMIC_LINK") == 0
if (IRIX_COMPAT (output_bfd) == ict_irix6)
mips_elf_irix6_finish_dynamic_symbol (output_bfd, name, sym);
- if (! info->shared)
- {
- if (! mips_elf_hash_table (info)->use_rld_obj_head
- && (strcmp (name, "__rld_map") == 0
- || strcmp (name, "__RLD_MAP") == 0))
- {
- asection *s = bfd_get_section_by_name (dynobj, ".rld_map");
- BFD_ASSERT (s != NULL);
- sym->st_value = s->output_section->vma + s->output_offset;
- bfd_put_32 (output_bfd, 0, s->contents);
- if (mips_elf_hash_table (info)->rld_value == 0)
- mips_elf_hash_table (info)->rld_value = sym->st_value;
- }
- else if (mips_elf_hash_table (info)->use_rld_obj_head
- && strcmp (name, "__rld_obj_head") == 0)
- {
- /* IRIX6 does not use a .rld_map section. */
- if (IRIX_COMPAT (output_bfd) == ict_irix5
- || IRIX_COMPAT (output_bfd) == ict_none)
- BFD_ASSERT (bfd_get_section_by_name (dynobj, ".rld_map")
- != NULL);
- mips_elf_hash_table (info)->rld_value = sym->st_value;
- }
- }
-
- /* Keep dynamic MIPS16 symbols odd. This allows the dynamic linker to
- treat MIPS16 symbols like any other. */
+ /* Keep dynamic compressed symbols odd. This allows the dynamic linker
+ to treat compressed symbols like any other. */
if (ELF_ST_IS_MIPS16 (sym->st_other))
{
BFD_ASSERT (sym->st_value & 1);
sym->st_other -= STO_MIPS16;
}
+ else if (ELF_ST_IS_MICROMIPS (sym->st_other))
+ {
+ BFD_ASSERT (sym->st_value & 1);
+ sym->st_other -= STO_MICROMIPS;
+ }
return TRUE;
}
dynobj = elf_hash_table (info)->dynobj;
hmips = (struct mips_elf_link_hash_entry *) h;
- if (h->plt.offset != (bfd_vma) -1)
+ if (h->plt.plist != NULL && h->plt.plist->mips_offset != MINUS_ONE)
{
bfd_byte *loc;
- bfd_vma plt_address, plt_index, got_address, got_offset, branch_offset;
+ bfd_vma plt_address, got_address, got_offset, branch_offset;
Elf_Internal_Rela rel;
static const bfd_vma *plt_entry;
+ bfd_vma gotplt_index;
+ bfd_vma plt_offset;
+
+ plt_offset = htab->plt_header_size + h->plt.plist->mips_offset;
+ gotplt_index = h->plt.plist->gotplt_index;
BFD_ASSERT (h->dynindx != -1);
BFD_ASSERT (htab->splt != NULL);
- BFD_ASSERT (h->plt.offset <= htab->splt->size);
+ BFD_ASSERT (gotplt_index != MINUS_ONE);
+ BFD_ASSERT (plt_offset <= htab->splt->size);
/* Calculate the address of the .plt entry. */
plt_address = (htab->splt->output_section->vma
+ htab->splt->output_offset
- + h->plt.offset);
-
- /* Calculate the index of the entry. */
- plt_index = ((h->plt.offset - htab->plt_header_size)
- / htab->plt_entry_size);
+ + plt_offset);
/* Calculate the address of the .got.plt entry. */
got_address = (htab->sgotplt->output_section->vma
+ htab->sgotplt->output_offset
- + plt_index * 4);
+ + gotplt_index * MIPS_ELF_GOT_SIZE (output_bfd));
/* Calculate the offset of the .got.plt entry from
_GLOBAL_OFFSET_TABLE_. */
/* Calculate the offset for the branch at the start of the PLT
entry. The branch jumps to the beginning of .plt. */
- branch_offset = -(h->plt.offset / 4 + 1) & 0xffff;
+ branch_offset = -(plt_offset / 4 + 1) & 0xffff;
/* Fill in the initial value of the .got.plt entry. */
bfd_put_32 (output_bfd, plt_address,
- htab->sgotplt->contents + plt_index * 4);
+ (htab->sgotplt->contents
+ + gotplt_index * MIPS_ELF_GOT_SIZE (output_bfd)));
/* Find out where the .plt entry should go. */
- loc = htab->splt->contents + h->plt.offset;
+ loc = htab->splt->contents + plt_offset;
if (info->shared)
{
plt_entry = mips_vxworks_shared_plt_entry;
bfd_put_32 (output_bfd, plt_entry[0] | branch_offset, loc);
- bfd_put_32 (output_bfd, plt_entry[1] | plt_index, loc + 4);
+ bfd_put_32 (output_bfd, plt_entry[1] | gotplt_index, loc + 4);
}
else
{
got_address_low = got_address & 0xffff;
bfd_put_32 (output_bfd, plt_entry[0] | branch_offset, loc);
- bfd_put_32 (output_bfd, plt_entry[1] | plt_index, loc + 4);
+ bfd_put_32 (output_bfd, plt_entry[1] | gotplt_index, loc + 4);
bfd_put_32 (output_bfd, plt_entry[2] | got_address_high, loc + 8);
bfd_put_32 (output_bfd, plt_entry[3] | got_address_low, loc + 12);
bfd_put_32 (output_bfd, plt_entry[4], loc + 16);
bfd_put_32 (output_bfd, plt_entry[7], loc + 28);
loc = (htab->srelplt2->contents
- + (plt_index * 3 + 2) * sizeof (Elf32_External_Rela));
+ + (gotplt_index * 3 + 2) * sizeof (Elf32_External_Rela));
/* Emit a relocation for the .got.plt entry. */
rel.r_offset = got_address;
rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_MIPS_32);
- rel.r_addend = h->plt.offset;
+ rel.r_addend = plt_offset;
bfd_elf32_swap_reloca_out (output_bfd, &rel, loc);
/* Emit a relocation for the lui of %hi(<.got.plt slot>). */
}
/* Emit an R_MIPS_JUMP_SLOT relocation against the .got.plt entry. */
- loc = htab->srelplt->contents + plt_index * sizeof (Elf32_External_Rela);
+ loc = (htab->srelplt->contents
+ + gotplt_index * sizeof (Elf32_External_Rela));
rel.r_offset = got_address;
rel.r_info = ELF32_R_INFO (h->dynindx, R_MIPS_JUMP_SLOT);
rel.r_addend = 0;
asection *s;
/* Install the symbol value in the GOT. */
- offset = mips_elf_global_got_index (dynobj, output_bfd, h,
- R_MIPS_GOT16, info);
+ offset = mips_elf_primary_global_got_index (output_bfd, info, h);
MIPS_ELF_PUT_WORD (output_bfd, sym->st_value, sgot->contents + offset);
/* Add a dynamic relocation for it. */
/* Write out a plt0 entry to the beginning of .plt. */
-static void
+static bfd_boolean
mips_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info)
{
bfd_byte *loc;
plt_entry = mips_n64_exec_plt0_entry;
else if (ABI_N32_P (output_bfd))
plt_entry = mips_n32_exec_plt0_entry;
- else
+ else if (!htab->plt_header_is_comp)
plt_entry = mips_o32_exec_plt0_entry;
+ else if (htab->insn32)
+ plt_entry = micromips_insn32_o32_exec_plt0_entry;
+ else
+ plt_entry = micromips_o32_exec_plt0_entry;
/* Calculate the value of .got.plt. */
gotplt_value = (htab->sgotplt->output_section->vma
/* Install the PLT header. */
loc = htab->splt->contents;
- bfd_put_32 (output_bfd, plt_entry[0] | gotplt_value_high, loc);
- bfd_put_32 (output_bfd, plt_entry[1] | gotplt_value_low, loc + 4);
- bfd_put_32 (output_bfd, plt_entry[2] | gotplt_value_low, loc + 8);
- bfd_put_32 (output_bfd, plt_entry[3], loc + 12);
- bfd_put_32 (output_bfd, plt_entry[4], loc + 16);
- bfd_put_32 (output_bfd, plt_entry[5], loc + 20);
- bfd_put_32 (output_bfd, plt_entry[6], loc + 24);
- bfd_put_32 (output_bfd, plt_entry[7], loc + 28);
+ if (plt_entry == micromips_o32_exec_plt0_entry)
+ {
+ bfd_vma gotpc_offset;
+ bfd_vma loc_address;
+ size_t i;
+
+ BFD_ASSERT (gotplt_value % 4 == 0);
+
+ loc_address = (htab->splt->output_section->vma
+ + htab->splt->output_offset);
+ gotpc_offset = gotplt_value - ((loc_address | 3) ^ 3);
+
+ /* ADDIUPC has a span of +/-16MB, check we're in range. */
+ if (gotpc_offset + 0x1000000 >= 0x2000000)
+ {
+ (*_bfd_error_handler)
+ (_("%B: `%A' offset of %ld from `%A' beyond the range of ADDIUPC"),
+ output_bfd,
+ htab->sgotplt->output_section,
+ htab->splt->output_section,
+ (long) gotpc_offset);
+ bfd_set_error (bfd_error_no_error);
+ return FALSE;
+ }
+ bfd_put_16 (output_bfd,
+ plt_entry[0] | ((gotpc_offset >> 18) & 0x7f), loc);
+ bfd_put_16 (output_bfd, (gotpc_offset >> 2) & 0xffff, loc + 2);
+ for (i = 2; i < ARRAY_SIZE (micromips_o32_exec_plt0_entry); i++)
+ bfd_put_16 (output_bfd, plt_entry[i], loc + (i * 2));
+ }
+ else if (plt_entry == micromips_insn32_o32_exec_plt0_entry)
+ {
+ size_t i;
+
+ bfd_put_16 (output_bfd, plt_entry[0], loc);
+ bfd_put_16 (output_bfd, gotplt_value_high, loc + 2);
+ bfd_put_16 (output_bfd, plt_entry[2], loc + 4);
+ bfd_put_16 (output_bfd, gotplt_value_low, loc + 6);
+ bfd_put_16 (output_bfd, plt_entry[4], loc + 8);
+ bfd_put_16 (output_bfd, gotplt_value_low, loc + 10);
+ for (i = 6; i < ARRAY_SIZE (micromips_insn32_o32_exec_plt0_entry); i++)
+ bfd_put_16 (output_bfd, plt_entry[i], loc + (i * 2));
+ }
+ else
+ {
+ bfd_put_32 (output_bfd, plt_entry[0] | gotplt_value_high, loc);
+ bfd_put_32 (output_bfd, plt_entry[1] | gotplt_value_low, loc + 4);
+ bfd_put_32 (output_bfd, plt_entry[2] | gotplt_value_low, loc + 8);
+ bfd_put_32 (output_bfd, plt_entry[3], loc + 12);
+ bfd_put_32 (output_bfd, plt_entry[4], loc + 16);
+ bfd_put_32 (output_bfd, plt_entry[5], loc + 20);
+ bfd_put_32 (output_bfd, plt_entry[6], loc + 24);
+ bfd_put_32 (output_bfd, plt_entry[7], loc + 28);
+ }
+
+ return TRUE;
}
/* Install the PLT header for a VxWorks executable and finalize the
dynobj = elf_hash_table (info)->dynobj;
- sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+ sdyn = bfd_get_linker_section (dynobj, ".dynamic");
sgot = htab->sgot;
gg = htab->got_info;
BFD_ASSERT (sdyn != NULL);
BFD_ASSERT (gg != NULL);
- g = mips_elf_got_for_ibfd (gg, output_bfd);
+ g = mips_elf_bfd_got (output_bfd, FALSE);
BFD_ASSERT (g != NULL);
for (b = sdyn->contents;
break;
case DT_MIPS_GOTSYM:
- if (gg->global_gotsym)
+ if (htab->global_gotsym)
{
- dyn.d_un.d_val = gg->global_gotsym->dynindx;
+ dyn.d_un.d_val = htab->global_gotsym->dynindx;
break;
}
/* In case if we don't have global got symbols we default
name = ".dynsym";
elemsize = MIPS_ELF_SYM_SIZE (output_bfd);
s = bfd_get_section_by_name (output_bfd, name);
- BFD_ASSERT (s != NULL);
- dyn.d_un.d_val = s->size / elemsize;
+ if (s != NULL)
+ dyn.d_un.d_val = s->size / elemsize;
+ else
+ dyn.d_un.d_val = 0;
break;
case DT_MIPS_HIPAGENO:
break;
case DT_MIPS_RLD_MAP:
- dyn.d_un.d_ptr = mips_elf_hash_table (info)->rld_value;
+ {
+ struct elf_link_hash_entry *h;
+ h = mips_elf_hash_table (info)->rld_symbol;
+ if (!h)
+ {
+ dyn_to_skip = MIPS_ELF_DYN_SIZE (dynobj);
+ swap_out_p = FALSE;
+ break;
+ }
+ s = h->root.u.def.section;
+ dyn.d_un.d_ptr = (s->output_section->vma + s->output_offset
+ + h->root.u.def.value);
+ }
break;
case DT_MIPS_OPTIONS:
if (! info->shared)
continue;
- while (got_index < g->assigned_gotno)
+ for (; got_index < g->local_gotno; got_index++)
{
+ if (got_index >= g->assigned_low_gotno
+ && got_index <= g->assigned_high_gotno)
+ continue;
+
rel[0].r_offset = rel[1].r_offset = rel[2].r_offset
- = got_index++ * MIPS_ELF_GOT_SIZE (output_bfd);
+ = got_index * MIPS_ELF_GOT_SIZE (output_bfd);
if (!(mips_elf_create_dynamic_relocation
(output_bfd, info, rel, NULL,
bfd_abs_section_ptr,
if (SGI_COMPAT (output_bfd))
{
/* Write .compact_rel section out. */
- s = bfd_get_section_by_name (dynobj, ".compact_rel");
+ s = bfd_get_linker_section (dynobj, ".compact_rel");
if (s != NULL)
{
cpt.id1 = 1;
else
{
BFD_ASSERT (!info->shared);
- mips_finish_exec_plt (output_bfd, info);
+ if (!mips_finish_exec_plt (output_bfd, info))
+ return FALSE;
}
}
return TRUE;
val = E_MIPS_ARCH_4 | E_MIPS_MACH_5500;
break;
+ case bfd_mach_mips5900:
+ val = E_MIPS_ARCH_3 | E_MIPS_MACH_5900;
+ break;
+
case bfd_mach_mips9000:
val = E_MIPS_ARCH_4 | E_MIPS_MACH_9000;
break;
break;
case bfd_mach_mips_loongson_3a:
- val = E_MIPS_ARCH_64 | E_MIPS_MACH_LS3A;
+ val = E_MIPS_ARCH_64R2 | E_MIPS_MACH_LS3A;
break;
case bfd_mach_mips_octeon:
+ case bfd_mach_mips_octeonp:
val = E_MIPS_ARCH_64R2 | E_MIPS_MACH_OCTEON;
break;
+ case bfd_mach_mips_octeon3:
+ val = E_MIPS_ARCH_64R2 | E_MIPS_MACH_OCTEON3;
+ break;
+
case bfd_mach_mips_xlr:
val = E_MIPS_ARCH_64 | E_MIPS_MACH_XLR;
break;
+ case bfd_mach_mips_octeon2:
+ val = E_MIPS_ARCH_64R2 | E_MIPS_MACH_OCTEON2;
+ break;
+
case bfd_mach_mipsisa32:
val = E_MIPS_ARCH_32;
break;
break;
case bfd_mach_mipsisa32r2:
+ case bfd_mach_mipsisa32r3:
+ case bfd_mach_mipsisa32r5:
val = E_MIPS_ARCH_32R2;
break;
case bfd_mach_mipsisa64r2:
+ case bfd_mach_mipsisa64r3:
+ case bfd_mach_mipsisa64r5:
val = E_MIPS_ARCH_64R2;
break;
+
+ case bfd_mach_mipsisa32r6:
+ val = E_MIPS_ARCH_32R6;
+ break;
+
+ case bfd_mach_mipsisa64r6:
+ val = E_MIPS_ARCH_64R6;
+ break;
}
elf_elfheader (abfd)->e_flags &= ~(EF_MIPS_ARCH | EF_MIPS_MACH);
elf_elfheader (abfd)->e_flags |= val;
if (s && (s->flags & SEC_LOAD))
++ret;
+ /* See if we need a PT_MIPS_ABIFLAGS segment. */
+ if (bfd_get_section_by_name (abfd, ".MIPS.abiflags"))
+ ++ret;
+
/* See if we need a PT_MIPS_OPTIONS segment. */
if (IRIX_COMPAT (abfd) == ict_irix6
&& bfd_get_section_by_name (abfd,
s = bfd_get_section_by_name (abfd, ".reginfo");
if (s != NULL && (s->flags & SEC_LOAD) != 0)
{
- for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+ for (m = elf_seg_map (abfd); m != NULL; m = m->next)
if (m->p_type == PT_MIPS_REGINFO)
break;
if (m == NULL)
m->sections[0] = s;
/* We want to put it after the PHDR and INTERP segments. */
- pm = &elf_tdata (abfd)->segment_map;
+ pm = &elf_seg_map (abfd);
+ while (*pm != NULL
+ && ((*pm)->p_type == PT_PHDR
+ || (*pm)->p_type == PT_INTERP))
+ pm = &(*pm)->next;
+
+ m->next = *pm;
+ *pm = m;
+ }
+ }
+
+ /* If there is a .MIPS.abiflags section, we need a PT_MIPS_ABIFLAGS
+ segment. */
+ s = bfd_get_section_by_name (abfd, ".MIPS.abiflags");
+ if (s != NULL && (s->flags & SEC_LOAD) != 0)
+ {
+ for (m = elf_seg_map (abfd); m != NULL; m = m->next)
+ if (m->p_type == PT_MIPS_ABIFLAGS)
+ break;
+ if (m == NULL)
+ {
+ amt = sizeof *m;
+ m = bfd_zalloc (abfd, amt);
+ if (m == NULL)
+ return FALSE;
+
+ m->p_type = PT_MIPS_ABIFLAGS;
+ m->count = 1;
+ m->sections[0] = s;
+
+ /* We want to put it after the PHDR and INTERP segments. */
+ pm = &elf_seg_map (abfd);
while (*pm != NULL
&& ((*pm)->p_type == PT_PHDR
|| (*pm)->p_type == PT_INTERP))
{
struct elf_segment_map *options_segment;
- pm = &elf_tdata (abfd)->segment_map;
+ pm = &elf_seg_map (abfd);
while (*pm != NULL
&& ((*pm)->p_type == PT_PHDR
|| (*pm)->p_type == PT_INTERP))
&& bfd_get_section_by_name (abfd, ".dynamic") != NULL
&& bfd_get_section_by_name (abfd, ".mdebug") != NULL)
{
- for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+ for (m = elf_seg_map (abfd); m != NULL; m = m->next)
if (m->p_type == PT_MIPS_RTPROC)
break;
if (m == NULL)
}
/* We want to put it after the DYNAMIC segment. */
- pm = &elf_tdata (abfd)->segment_map;
+ pm = &elf_seg_map (abfd);
while (*pm != NULL && (*pm)->p_type != PT_DYNAMIC)
pm = &(*pm)->next;
if (*pm != NULL)
/* On IRIX5, the PT_DYNAMIC segment includes the .dynamic,
.dynstr, .dynsym, and .hash sections, and everything in
between. */
- for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL;
+ for (pm = &elf_seg_map (abfd); *pm != NULL;
pm = &(*pm)->next)
if ((*pm)->p_type == PT_DYNAMIC)
break;
m = *pm;
- if (m != NULL && IRIX_COMPAT (abfd) == ict_none)
- {
- /* For a normal mips executable the permissions for the PT_DYNAMIC
- segment are read, write and execute. We do that here since
- the code in elf.c sets only the read permission. This matters
- sometimes for the dynamic linker. */
- if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
- {
- m->p_flags = PF_R | PF_W | PF_X;
- m->p_flags_valid = 1;
- }
- }
/* GNU/Linux binaries do not need the extended PT_DYNAMIC section.
glibc's dynamic linker has traditionally derived the number of
tags from the p_filesz field, and sometimes allocates stack
&& !SGI_COMPAT (abfd)
&& bfd_get_section_by_name (abfd, ".dynamic"))
{
- for (pm = &elf_tdata (abfd)->segment_map; *pm != NULL; pm = &(*pm)->next)
+ for (pm = &elf_seg_map (abfd); *pm != NULL; pm = &(*pm)->next)
if ((*pm)->p_type == PT_NULL)
break;
if (*pm == NULL)
return TRUE;
}
+
+/* Prevent .MIPS.abiflags from being discarded with --gc-sections. */
+
+bfd_boolean
+_bfd_mips_elf_gc_mark_extra_sections (struct bfd_link_info *info,
+ elf_gc_mark_hook_fn gc_mark_hook)
+{
+ bfd *sub;
+
+ _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
+ for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
+ {
+ asection *o;
+
+ if (! is_mips_elf (sub))
+ continue;
+
+ for (o = sub->sections; o != NULL; o = o->next)
+ if (!o->gc_mark
+ && MIPS_ELF_ABIFLAGS_SECTION_NAME_P
+ (bfd_get_section_name (sub, o)))
+ {
+ if (!_bfd_elf_gc_mark (info, o, gc_mark_hook))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
\f
/* Copy data from a MIPS ELF indirect symbol to its direct symbol,
hiding the old indirect symbol. Process additional relocation
indmips->global_got_area = GGA_NONE;
if (indmips->has_nonpic_branches)
dirmips->has_nonpic_branches = TRUE;
-
- if (dirmips->tls_type == 0)
- dirmips->tls_type = indmips->tls_type;
}
\f
#define PDR_SIZE 32
if (skip != 0)
{
mips_elf_section_data (o)->u.tdata = tdata;
+ if (o->rawsize == 0)
+ o->rawsize = o->size;
o->size -= skip * PDR_SIZE;
ret = TRUE;
}
};
bfd_boolean
-_bfd_mips_elf_find_nearest_line (bfd *abfd, asection *section,
- asymbol **symbols, bfd_vma offset,
+_bfd_mips_elf_find_nearest_line (bfd *abfd, asymbol **symbols,
+ asection *section, bfd_vma offset,
const char **filename_ptr,
const char **functionname_ptr,
- unsigned int *line_ptr)
+ unsigned int *line_ptr,
+ unsigned int *discriminator_ptr)
{
asection *msec;
- if (_bfd_dwarf1_find_nearest_line (abfd, section, symbols, offset,
+ if (_bfd_dwarf2_find_nearest_line (abfd, symbols, NULL, section, offset,
filename_ptr, functionname_ptr,
- line_ptr))
+ line_ptr, discriminator_ptr,
+ dwarf_debug_sections,
+ ABI_64_P (abfd) ? 8 : 0,
+ &elf_tdata (abfd)->dwarf2_find_line_info))
return TRUE;
- if (_bfd_dwarf2_find_nearest_line (abfd, dwarf_debug_sections,
- section, symbols, offset,
+ if (_bfd_dwarf1_find_nearest_line (abfd, symbols, section, offset,
filename_ptr, functionname_ptr,
- line_ptr, ABI_64_P (abfd) ? 8 : 0,
- &elf_tdata (abfd)->dwarf2_find_line_info))
+ line_ptr))
return TRUE;
msec = bfd_get_section_by_name (abfd, ".mdebug");
if (elf_section_data (msec)->this_hdr.sh_type != SHT_NOBITS)
msec->flags |= SEC_HAS_CONTENTS;
- fi = elf_tdata (abfd)->find_line_info;
+ fi = mips_elf_tdata (abfd)->find_line_info;
if (fi == NULL)
{
bfd_size_type external_fdr_size;
for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++)
(*swap->swap_fdr_in) (abfd, fraw_src, fdr_ptr);
- elf_tdata (abfd)->find_line_info = fi;
+ mips_elf_tdata (abfd)->find_line_info = fi;
/* Note that we don't bother to ever free this information.
find_nearest_line is either called all the time, as in
/* Fall back on the generic ELF find_nearest_line routine. */
- return _bfd_elf_find_nearest_line (abfd, section, symbols, offset,
+ return _bfd_elf_find_nearest_line (abfd, symbols, section, offset,
filename_ptr, functionname_ptr,
- line_ptr);
+ line_ptr, discriminator_ptr);
}
bfd_boolean
unsigned long opcode;
int bdsize;
- opcode = (bfd_get_16 (abfd, ptr) << 16) | bfd_get_16 (abfd, ptr + 2);
+ opcode = bfd_get_micromips_32 (abfd, ptr);
if (find_match (opcode, ds_insns_32_bd32) >= 0)
/* 32-bit branch/jump with a 32-bit delay slot. */
bdsize = 4;
{
unsigned long opcode;
- opcode = (bfd_get_16 (abfd, ptr) << 16) | bfd_get_16 (abfd, ptr + 2);
+ opcode = bfd_get_micromips_32 (abfd, ptr);
if (MATCH (opcode, j_insn_32)
/* J */
|| MATCH (opcode, bc_insn_32)
const Elf_Internal_Rela *irel;
unsigned long opcode;
- opcode = bfd_get_16 (abfd, ptr);
- opcode <<= 16;
- opcode |= bfd_get_16 (abfd, ptr + 2);
+ opcode = bfd_get_micromips_32 (abfd, ptr);
if (find_match (opcode, bzc_insns_32) < 0)
return FALSE;
struct bfd_link_info *link_info,
bfd_boolean *again)
{
+ bfd_boolean insn32 = mips_elf_hash_table (link_info)->insn32;
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Rela *internal_relocs;
Elf_Internal_Rela *irel, *irelend;
/* Get a copy of the native relocations. */
internal_relocs = (_bfd_elf_link_read_relocs
- (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL,
+ (abfd, sec, NULL, (Elf_Internal_Rela *) NULL,
link_info->keep_memory));
if (internal_relocs == NULL)
goto error_return;
if (irel->r_offset + 4 > sec->size)
continue;
- opcode = bfd_get_16 (abfd, ptr ) << 16;
- opcode |= bfd_get_16 (abfd, ptr + 2);
+ opcode = bfd_get_micromips_32 (abfd, ptr);
/* This is the pc-relative distance from the instruction the
relocation is applied to, to the symbol referred. */
continue;
}
- nextopc = bfd_get_16 (abfd, contents + irel[1].r_offset ) << 16;
- nextopc |= bfd_get_16 (abfd, contents + irel[1].r_offset + 2);
+ nextopc = bfd_get_micromips_32 (abfd, contents + irel[1].r_offset);
/* Give up unless the same register is used with both
relocations. */
nextopc = (addiupc_insn.match
| ADDIUPC_REG_FIELD (OP32_TREG (nextopc)));
- bfd_put_16 (abfd, (nextopc >> 16) & 0xffff,
- contents + irel[1].r_offset);
- bfd_put_16 (abfd, nextopc & 0xffff,
- contents + irel[1].r_offset + 2);
+ bfd_put_micromips_32 (abfd, nextopc,
+ contents + irel[1].r_offset);
}
/* Can't do anything, give up, sigh... */
&& irel->r_offset + 5 < sec->size
&& ((fndopc = find_match (opcode, bz_rs_insns_32)) >= 0
|| (fndopc = find_match (opcode, bz_rt_insns_32)) >= 0)
- && MATCH (bfd_get_16 (abfd, ptr + 4), nop_insn_16))
+ && ((!insn32
+ && (delcnt = MATCH (bfd_get_16 (abfd, ptr + 4),
+ nop_insn_16) ? 2 : 0))
+ || (irel->r_offset + 7 < sec->size
+ && (delcnt = MATCH (bfd_get_micromips_32 (abfd,
+ ptr + 4),
+ nop_insn_32) ? 4 : 0))))
{
unsigned long reg;
| BZC32_REG_FIELD (reg)
| (opcode & 0xffff)); /* Addend value. */
- bfd_put_16 (abfd, (opcode >> 16) & 0xffff, ptr);
- bfd_put_16 (abfd, opcode & 0xffff, ptr + 2);
+ bfd_put_micromips_32 (abfd, opcode, ptr);
- /* Delete the 16-bit delay slot NOP: two bytes from
- irel->offset + 4. */
- delcnt = 2;
+ /* Delete the delay slot NOP: two or four bytes from
+ irel->offset + 4; delcnt has already been set above. */
deloff = 4;
}
/* R_MICROMIPS_PC16_S1 relaxation to R_MICROMIPS_PC10_S1. We need
to check the distance from the next instruction, so subtract 2. */
- else if (r_type == R_MICROMIPS_PC16_S1
+ else if (!insn32
+ && r_type == R_MICROMIPS_PC16_S1
&& IS_BITSIZE (pcrval - 2, 11)
&& find_match (opcode, b_insns_32) >= 0)
{
/* Fix the relocation's type. */
irel->r_info = ELF32_R_INFO (r_symndx, R_MICROMIPS_PC10_S1);
- /* Replace the the 32-bit opcode with a 16-bit opcode. */
+ /* Replace the 32-bit opcode with a 16-bit opcode. */
bfd_put_16 (abfd,
(b_insn_16.match
| (opcode & 0x3ff)), /* Addend value. */
/* R_MICROMIPS_PC16_S1 relaxation to R_MICROMIPS_PC7_S1. We need
to check the distance from the next instruction, so subtract 2. */
- else if (r_type == R_MICROMIPS_PC16_S1
+ else if (!insn32
+ && r_type == R_MICROMIPS_PC16_S1
&& IS_BITSIZE (pcrval - 2, 8)
&& (((fndopc = find_match (opcode, bz_rs_insns_32)) >= 0
&& OP16_VALID_REG (OP32_SREG (opcode)))
/* Fix the relocation's type. */
irel->r_info = ELF32_R_INFO (r_symndx, R_MICROMIPS_PC7_S1);
- /* Replace the the 32-bit opcode with a 16-bit opcode. */
+ /* Replace the 32-bit opcode with a 16-bit opcode. */
bfd_put_16 (abfd,
(bz_insns_16[fndopc].match
| BZ16_REG_FIELD (reg)
}
/* R_MICROMIPS_26_S1 -- JAL to JALS relaxation for microMIPS targets. */
- else if (r_type == R_MICROMIPS_26_S1
+ else if (!insn32
+ && r_type == R_MICROMIPS_26_S1
&& target_is_micromips_code_p
&& irel->r_offset + 7 < sec->size
&& MATCH (opcode, jal_insn_32_bd32))
unsigned long n32opc;
bfd_boolean relaxed = FALSE;
- n32opc = bfd_get_16 (abfd, ptr + 4) << 16;
- n32opc |= bfd_get_16 (abfd, ptr + 6);
+ n32opc = bfd_get_micromips_32 (abfd, ptr + 4);
if (MATCH (n32opc, nop_insn_32))
{
{
/* JAL with 32-bit delay slot that is changed to a JALS
with 16-bit delay slot. */
- bfd_put_16 (abfd, (jal_insn_32_bd16.match >> 16) & 0xffff,
- ptr);
- bfd_put_16 (abfd, jal_insn_32_bd16.match & 0xffff,
- ptr + 2);
+ bfd_put_micromips_32 (abfd, jal_insn_32_bd16.match, ptr);
/* Delete 2 bytes from irel->r_offset + 6. */
delcnt = 2;
struct mips_elf_link_hash_table *ret;
bfd_size_type amt = sizeof (struct mips_elf_link_hash_table);
- ret = bfd_malloc (amt);
+ ret = bfd_zmalloc (amt);
if (ret == NULL)
return NULL;
free (ret);
return NULL;
}
-
-#if 0
- /* We no longer use this. */
- for (i = 0; i < SIZEOF_MIPS_DYNSYM_SECNAMES; i++)
- ret->dynsym_sec_strindex[i] = (bfd_size_type) -1;
-#endif
- ret->procedure_count = 0;
- ret->compact_rel_size = 0;
- ret->use_rld_obj_head = FALSE;
- ret->rld_value = 0;
- ret->mips16_stubs_seen = FALSE;
- ret->use_plts_and_copy_relocs = FALSE;
- ret->is_vxworks = FALSE;
- ret->small_data_overflow_reported = FALSE;
- ret->srelbss = NULL;
- ret->sdynbss = NULL;
- ret->srelplt = NULL;
- ret->srelplt2 = NULL;
- ret->sgotplt = NULL;
- ret->splt = NULL;
- ret->sstubs = NULL;
- ret->sgot = NULL;
- ret->got_info = NULL;
- ret->plt_header_size = 0;
- ret->plt_entry_size = 0;
- ret->lazy_stub_count = 0;
- ret->function_stub_size = 0;
- ret->strampoline = NULL;
- ret->la25_stubs = NULL;
- ret->add_stub_section = NULL;
+ ret->root.init_plt_refcount.plist = NULL;
+ ret->root.init_plt_offset.plist = NULL;
return &ret->root.root;
}
{
mips_elf_hash_table (info)->use_plts_and_copy_relocs = TRUE;
}
+
+/* A function that the linker calls to select between all or only
+ 32-bit microMIPS instructions. */
+
+void
+_bfd_mips_elf_insn32 (struct bfd_link_info *info, bfd_boolean on)
+{
+ mips_elf_hash_table (info)->insn32 = on;
+}
\f
+/* Return the .MIPS.abiflags value representing each ISA Extension. */
+
+unsigned int
+bfd_mips_isa_ext (bfd *abfd)
+{
+ switch (bfd_get_mach (abfd))
+ {
+ case bfd_mach_mips3900:
+ return AFL_EXT_3900;
+ case bfd_mach_mips4010:
+ return AFL_EXT_4010;
+ case bfd_mach_mips4100:
+ return AFL_EXT_4100;
+ case bfd_mach_mips4111:
+ return AFL_EXT_4111;
+ case bfd_mach_mips4120:
+ return AFL_EXT_4120;
+ case bfd_mach_mips4650:
+ return AFL_EXT_4650;
+ case bfd_mach_mips5400:
+ return AFL_EXT_5400;
+ case bfd_mach_mips5500:
+ return AFL_EXT_5500;
+ case bfd_mach_mips5900:
+ return AFL_EXT_5900;
+ case bfd_mach_mips10000:
+ return AFL_EXT_10000;
+ case bfd_mach_mips_loongson_2e:
+ return AFL_EXT_LOONGSON_2E;
+ case bfd_mach_mips_loongson_2f:
+ return AFL_EXT_LOONGSON_2F;
+ case bfd_mach_mips_loongson_3a:
+ return AFL_EXT_LOONGSON_3A;
+ case bfd_mach_mips_sb1:
+ return AFL_EXT_SB1;
+ case bfd_mach_mips_octeon:
+ return AFL_EXT_OCTEON;
+ case bfd_mach_mips_octeonp:
+ return AFL_EXT_OCTEONP;
+ case bfd_mach_mips_octeon3:
+ return AFL_EXT_OCTEON3;
+ case bfd_mach_mips_octeon2:
+ return AFL_EXT_OCTEON2;
+ case bfd_mach_mips_xlr:
+ return AFL_EXT_XLR;
+ }
+ return 0;
+}
+
+/* Update the isa_level, isa_rev, isa_ext fields of abiflags. */
+
+static void
+update_mips_abiflags_isa (bfd *abfd, Elf_Internal_ABIFlags_v0 *abiflags)
+{
+ switch (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH)
+ {
+ case E_MIPS_ARCH_1:
+ abiflags->isa_level = 1;
+ abiflags->isa_rev = 0;
+ break;
+ case E_MIPS_ARCH_2:
+ abiflags->isa_level = 2;
+ abiflags->isa_rev = 0;
+ break;
+ case E_MIPS_ARCH_3:
+ abiflags->isa_level = 3;
+ abiflags->isa_rev = 0;
+ break;
+ case E_MIPS_ARCH_4:
+ abiflags->isa_level = 4;
+ abiflags->isa_rev = 0;
+ break;
+ case E_MIPS_ARCH_5:
+ abiflags->isa_level = 5;
+ abiflags->isa_rev = 0;
+ break;
+ case E_MIPS_ARCH_32:
+ abiflags->isa_level = 32;
+ abiflags->isa_rev = 1;
+ break;
+ case E_MIPS_ARCH_32R2:
+ abiflags->isa_level = 32;
+ /* Handle MIPS32r3 and MIPS32r5 which do not have a header flag. */
+ if (abiflags->isa_rev < 2)
+ abiflags->isa_rev = 2;
+ break;
+ case E_MIPS_ARCH_32R6:
+ abiflags->isa_level = 32;
+ abiflags->isa_rev = 6;
+ break;
+ case E_MIPS_ARCH_64:
+ abiflags->isa_level = 64;
+ abiflags->isa_rev = 1;
+ break;
+ case E_MIPS_ARCH_64R2:
+ /* Handle MIPS64r3 and MIPS64r5 which do not have a header flag. */
+ abiflags->isa_level = 64;
+ if (abiflags->isa_rev < 2)
+ abiflags->isa_rev = 2;
+ break;
+ case E_MIPS_ARCH_64R6:
+ abiflags->isa_level = 64;
+ abiflags->isa_rev = 6;
+ break;
+ default:
+ (*_bfd_error_handler)
+ (_("%B: Unknown architecture %s"),
+ abfd, bfd_printable_name (abfd));
+ }
+
+ abiflags->isa_ext = bfd_mips_isa_ext (abfd);
+}
+
+/* Return true if the given ELF header flags describe a 32-bit binary. */
+
+static bfd_boolean
+mips_32bit_flags_p (flagword flags)
+{
+ return ((flags & EF_MIPS_32BITMODE) != 0
+ || (flags & EF_MIPS_ABI) == E_MIPS_ABI_O32
+ || (flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2
+ || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R6);
+}
+
+/* Infer the content of the ABI flags based on the elf header. */
+
+static void
+infer_mips_abiflags (bfd *abfd, Elf_Internal_ABIFlags_v0* abiflags)
+{
+ obj_attribute *in_attr;
+
+ memset (abiflags, 0, sizeof (Elf_Internal_ABIFlags_v0));
+ update_mips_abiflags_isa (abfd, abiflags);
+
+ if (mips_32bit_flags_p (elf_elfheader (abfd)->e_flags))
+ abiflags->gpr_size = AFL_REG_32;
+ else
+ abiflags->gpr_size = AFL_REG_64;
+
+ abiflags->cpr1_size = AFL_REG_NONE;
+
+ in_attr = elf_known_obj_attributes (abfd)[OBJ_ATTR_GNU];
+ abiflags->fp_abi = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+
+ if (abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_SINGLE
+ || abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_XX
+ || (abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_DOUBLE
+ && abiflags->gpr_size == AFL_REG_32))
+ abiflags->cpr1_size = AFL_REG_32;
+ else if (abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_DOUBLE
+ || abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_64
+ || abiflags->fp_abi == Val_GNU_MIPS_ABI_FP_64A)
+ abiflags->cpr1_size = AFL_REG_64;
+
+ abiflags->cpr2_size = AFL_REG_NONE;
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MDMX)
+ abiflags->ases |= AFL_ASE_MDMX;
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_M16)
+ abiflags->ases |= AFL_ASE_MIPS16;
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS)
+ abiflags->ases |= AFL_ASE_MICROMIPS;
+
+ if (abiflags->fp_abi != Val_GNU_MIPS_ABI_FP_ANY
+ && abiflags->fp_abi != Val_GNU_MIPS_ABI_FP_SOFT
+ && abiflags->fp_abi != Val_GNU_MIPS_ABI_FP_64A
+ && abiflags->isa_level >= 32
+ && abiflags->isa_ext != AFL_EXT_LOONGSON_3A)
+ abiflags->flags1 |= AFL_FLAGS1_ODDSPREG;
+}
+
/* We need to use a special link routine to handle the .reginfo and
the .mdebug sections. We need to merge all instances of these
sections together, not write them all out sequentially. */
asection *o;
struct bfd_link_order *p;
asection *reginfo_sec, *mdebug_sec, *gptab_data_sec, *gptab_bss_sec;
- asection *rtproc_sec;
+ asection *rtproc_sec, *abiflags_sec;
Elf32_RegInfo reginfo;
struct ecoff_debug_info debug;
struct mips_htab_traverse_info hti;
/* Go through the sections and collect the .reginfo and .mdebug
information. */
+ abiflags_sec = NULL;
reginfo_sec = NULL;
mdebug_sec = NULL;
gptab_data_sec = NULL;
gptab_bss_sec = NULL;
for (o = abfd->sections; o != NULL; o = o->next)
{
+ if (strcmp (o->name, ".MIPS.abiflags") == 0)
+ {
+ /* We have found the .MIPS.abiflags section in the output file.
+ Look through all the link_orders comprising it and remove them.
+ The data is merged in _bfd_mips_elf_merge_private_bfd_data. */
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
+ {
+ asection *input_section;
+
+ if (p->type != bfd_indirect_link_order)
+ {
+ if (p->type == bfd_data_link_order)
+ continue;
+ abort ();
+ }
+
+ input_section = p->u.indirect.section;
+
+ /* Hack: reset the SEC_HAS_CONTENTS flag so that
+ elf_link_input_bfd ignores this section. */
+ input_section->flags &= ~SEC_HAS_CONTENTS;
+ }
+
+ /* Size has been set in _bfd_mips_elf_always_size_sections. */
+ BFD_ASSERT(o->size == sizeof (Elf_External_ABIFlags_v0));
+
+ /* Skip this section later on (I don't think this currently
+ matters, but someday it might). */
+ o->map_head.link_order = NULL;
+
+ abiflags_sec = o;
+ }
+
if (strcmp (o->name, ".reginfo") == 0)
{
memset (®info, 0, sizeof reginfo);
if (SGI_COMPAT (abfd) && info->shared)
{
/* Create .rtproc section. */
- rtproc_sec = bfd_get_section_by_name (abfd, ".rtproc");
+ rtproc_sec = bfd_get_linker_section (abfd, ".rtproc");
if (rtproc_sec == NULL)
{
flagword flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED | SEC_READONLY);
- rtproc_sec = bfd_make_section_with_flags (abfd,
- ".rtproc",
- flags);
+ rtproc_sec = bfd_make_section_anyway_with_flags (abfd,
+ ".rtproc",
+ flags);
if (rtproc_sec == NULL
|| ! bfd_set_section_alignment (abfd, rtproc_sec, 4))
return FALSE;
/* Now write out the computed sections. */
+ if (abiflags_sec != NULL)
+ {
+ Elf_External_ABIFlags_v0 ext;
+ Elf_Internal_ABIFlags_v0 *abiflags;
+
+ abiflags = &mips_elf_tdata (abfd)->abiflags;
+
+ /* Set up the abiflags if no valid input sections were found. */
+ if (!mips_elf_tdata (abfd)->abiflags_valid)
+ {
+ infer_mips_abiflags (abfd, abiflags);
+ mips_elf_tdata (abfd)->abiflags_valid = TRUE;
+ }
+ bfd_mips_elf_swap_abiflags_v0_out (abfd, abiflags, &ext);
+ if (! bfd_set_section_contents (abfd, abiflags_sec, &ext, 0, sizeof ext))
+ return FALSE;
+ }
+
if (reginfo_sec != NULL)
{
Elf32_External_RegInfo ext;
\f
/* Structure for saying that BFD machine EXTENSION extends BASE. */
-struct mips_mach_extension {
+struct mips_mach_extension
+{
unsigned long extension, base;
};
/* An array describing how BFD machines relate to one another. The entries
are ordered topologically with MIPS I extensions listed last. */
-static const struct mips_mach_extension mips_mach_extensions[] = {
+static const struct mips_mach_extension mips_mach_extensions[] =
+{
/* MIPS64r2 extensions. */
+ { bfd_mach_mips_octeon3, bfd_mach_mips_octeon2 },
+ { bfd_mach_mips_octeon2, bfd_mach_mips_octeonp },
+ { bfd_mach_mips_octeonp, bfd_mach_mips_octeon },
{ bfd_mach_mips_octeon, bfd_mach_mipsisa64r2 },
+ { bfd_mach_mips_loongson_3a, bfd_mach_mipsisa64r2 },
/* MIPS64 extensions. */
{ bfd_mach_mipsisa64r2, bfd_mach_mipsisa64 },
{ bfd_mach_mips_sb1, bfd_mach_mipsisa64 },
{ bfd_mach_mips_xlr, bfd_mach_mipsisa64 },
- { bfd_mach_mips_loongson_3a, bfd_mach_mipsisa64 },
/* MIPS V extensions. */
{ bfd_mach_mipsisa64, bfd_mach_mips5 },
{ bfd_mach_mips4300, bfd_mach_mips4000 },
{ bfd_mach_mips4100, bfd_mach_mips4000 },
{ bfd_mach_mips4010, bfd_mach_mips4000 },
+ { bfd_mach_mips5900, bfd_mach_mips4000 },
/* MIPS32 extensions. */
{ bfd_mach_mipsisa32r2, bfd_mach_mipsisa32 },
}
-/* Return true if the given ELF header flags describe a 32-bit binary. */
-
-static bfd_boolean
-mips_32bit_flags_p (flagword flags)
-{
- return ((flags & EF_MIPS_32BITMODE) != 0
- || (flags & EF_MIPS_ABI) == E_MIPS_ABI_O32
- || (flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI32
- || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_1
- || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_2
- || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32
- || (flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R2);
-}
-
-
/* Merge object attributes from IBFD into OBFD. Raise an error if
there are conflicting attributes. */
static bfd_boolean
{
obj_attribute *in_attr;
obj_attribute *out_attr;
+ bfd *abi_fp_bfd;
+ bfd *abi_msa_bfd;
+
+ abi_fp_bfd = mips_elf_tdata (obfd)->abi_fp_bfd;
+ in_attr = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+ if (!abi_fp_bfd && in_attr[Tag_GNU_MIPS_ABI_FP].i != Val_GNU_MIPS_ABI_FP_ANY)
+ mips_elf_tdata (obfd)->abi_fp_bfd = ibfd;
+
+ abi_msa_bfd = mips_elf_tdata (obfd)->abi_msa_bfd;
+ if (!abi_msa_bfd
+ && in_attr[Tag_GNU_MIPS_ABI_MSA].i != Val_GNU_MIPS_ABI_MSA_ANY)
+ mips_elf_tdata (obfd)->abi_msa_bfd = ibfd;
if (!elf_known_obj_attributes_proc (obfd)[0].i)
{
/* Check for conflicting Tag_GNU_MIPS_ABI_FP attributes and merge
non-conflicting ones. */
- in_attr = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
out_attr = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
if (in_attr[Tag_GNU_MIPS_ABI_FP].i != out_attr[Tag_GNU_MIPS_ABI_FP].i)
{
- out_attr[Tag_GNU_MIPS_ABI_FP].type = 1;
- if (out_attr[Tag_GNU_MIPS_ABI_FP].i == 0)
- out_attr[Tag_GNU_MIPS_ABI_FP].i = in_attr[Tag_GNU_MIPS_ABI_FP].i;
- else if (in_attr[Tag_GNU_MIPS_ABI_FP].i == 0)
- ;
- else if (in_attr[Tag_GNU_MIPS_ABI_FP].i > 4)
- _bfd_error_handler
- (_("Warning: %B uses unknown floating point ABI %d"), ibfd,
- in_attr[Tag_GNU_MIPS_ABI_FP].i);
- else if (out_attr[Tag_GNU_MIPS_ABI_FP].i > 4)
- _bfd_error_handler
- (_("Warning: %B uses unknown floating point ABI %d"), obfd,
- out_attr[Tag_GNU_MIPS_ABI_FP].i);
- else
- switch (out_attr[Tag_GNU_MIPS_ABI_FP].i)
- {
- case 1:
- switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
- {
- case 2:
- _bfd_error_handler
- (_("Warning: %B uses -msingle-float, %B uses -mdouble-float"),
- obfd, ibfd);
- break;
-
- case 3:
- _bfd_error_handler
- (_("Warning: %B uses hard float, %B uses soft float"),
- obfd, ibfd);
- break;
-
- case 4:
- _bfd_error_handler
- (_("Warning: %B uses -msingle-float, %B uses -mips32r2 -mfp64"),
- obfd, ibfd);
- break;
-
- default:
- abort ();
- }
- break;
-
- case 2:
- switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
- {
- case 1:
- _bfd_error_handler
- (_("Warning: %B uses -msingle-float, %B uses -mdouble-float"),
- ibfd, obfd);
- break;
+ int out_fp, in_fp;
- case 3:
- _bfd_error_handler
- (_("Warning: %B uses hard float, %B uses soft float"),
- obfd, ibfd);
- break;
-
- case 4:
- _bfd_error_handler
- (_("Warning: %B uses -mdouble-float, %B uses -mips32r2 -mfp64"),
- obfd, ibfd);
- break;
+ out_fp = out_attr[Tag_GNU_MIPS_ABI_FP].i;
+ in_fp = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+ out_attr[Tag_GNU_MIPS_ABI_FP].type = 1;
+ if (out_fp == Val_GNU_MIPS_ABI_FP_ANY)
+ out_attr[Tag_GNU_MIPS_ABI_FP].i = in_fp;
+ else if (out_fp == Val_GNU_MIPS_ABI_FP_XX
+ && (in_fp == Val_GNU_MIPS_ABI_FP_DOUBLE
+ || in_fp == Val_GNU_MIPS_ABI_FP_64
+ || in_fp == Val_GNU_MIPS_ABI_FP_64A))
+ {
+ mips_elf_tdata (obfd)->abi_fp_bfd = ibfd;
+ out_attr[Tag_GNU_MIPS_ABI_FP].i = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+ }
+ else if (in_fp == Val_GNU_MIPS_ABI_FP_XX
+ && (out_fp == Val_GNU_MIPS_ABI_FP_DOUBLE
+ || out_fp == Val_GNU_MIPS_ABI_FP_64
+ || out_fp == Val_GNU_MIPS_ABI_FP_64A))
+ /* Keep the current setting. */;
+ else if (out_fp == Val_GNU_MIPS_ABI_FP_64A
+ && in_fp == Val_GNU_MIPS_ABI_FP_64)
+ {
+ mips_elf_tdata (obfd)->abi_fp_bfd = ibfd;
+ out_attr[Tag_GNU_MIPS_ABI_FP].i = in_attr[Tag_GNU_MIPS_ABI_FP].i;
+ }
+ else if (in_fp == Val_GNU_MIPS_ABI_FP_64A
+ && out_fp == Val_GNU_MIPS_ABI_FP_64)
+ /* Keep the current setting. */;
+ else if (in_fp != Val_GNU_MIPS_ABI_FP_ANY)
+ {
+ const char *out_string, *in_string;
+
+ out_string = _bfd_mips_fp_abi_string (out_fp);
+ in_string = _bfd_mips_fp_abi_string (in_fp);
+ /* First warn about cases involving unrecognised ABIs. */
+ if (!out_string && !in_string)
+ _bfd_error_handler
+ (_("Warning: %B uses unknown floating point ABI %d "
+ "(set by %B), %B uses unknown floating point ABI %d"),
+ obfd, abi_fp_bfd, ibfd, out_fp, in_fp);
+ else if (!out_string)
+ _bfd_error_handler
+ (_("Warning: %B uses unknown floating point ABI %d "
+ "(set by %B), %B uses %s"),
+ obfd, abi_fp_bfd, ibfd, out_fp, in_string);
+ else if (!in_string)
+ _bfd_error_handler
+ (_("Warning: %B uses %s (set by %B), "
+ "%B uses unknown floating point ABI %d"),
+ obfd, abi_fp_bfd, ibfd, out_string, in_fp);
+ else
+ {
+ /* If one of the bfds is soft-float, the other must be
+ hard-float. The exact choice of hard-float ABI isn't
+ really relevant to the error message. */
+ if (in_fp == Val_GNU_MIPS_ABI_FP_SOFT)
+ out_string = "-mhard-float";
+ else if (out_fp == Val_GNU_MIPS_ABI_FP_SOFT)
+ in_string = "-mhard-float";
+ _bfd_error_handler
+ (_("Warning: %B uses %s (set by %B), %B uses %s"),
+ obfd, abi_fp_bfd, ibfd, out_string, in_string);
+ }
+ }
+ }
- default:
- abort ();
- }
+ /* Check for conflicting Tag_GNU_MIPS_ABI_MSA attributes and merge
+ non-conflicting ones. */
+ if (in_attr[Tag_GNU_MIPS_ABI_MSA].i != out_attr[Tag_GNU_MIPS_ABI_MSA].i)
+ {
+ out_attr[Tag_GNU_MIPS_ABI_MSA].type = 1;
+ if (out_attr[Tag_GNU_MIPS_ABI_MSA].i == Val_GNU_MIPS_ABI_MSA_ANY)
+ out_attr[Tag_GNU_MIPS_ABI_MSA].i = in_attr[Tag_GNU_MIPS_ABI_MSA].i;
+ else if (in_attr[Tag_GNU_MIPS_ABI_MSA].i != Val_GNU_MIPS_ABI_MSA_ANY)
+ switch (out_attr[Tag_GNU_MIPS_ABI_MSA].i)
+ {
+ case Val_GNU_MIPS_ABI_MSA_128:
+ _bfd_error_handler
+ (_("Warning: %B uses %s (set by %B), "
+ "%B uses unknown MSA ABI %d"),
+ obfd, abi_msa_bfd, ibfd,
+ "-mmsa", in_attr[Tag_GNU_MIPS_ABI_MSA].i);
break;
- case 3:
- switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
+ default:
+ switch (in_attr[Tag_GNU_MIPS_ABI_MSA].i)
{
- case 1:
- case 2:
- case 4:
+ case Val_GNU_MIPS_ABI_MSA_128:
_bfd_error_handler
- (_("Warning: %B uses hard float, %B uses soft float"),
- ibfd, obfd);
- break;
+ (_("Warning: %B uses unknown MSA ABI %d "
+ "(set by %B), %B uses %s"),
+ obfd, abi_msa_bfd, ibfd,
+ out_attr[Tag_GNU_MIPS_ABI_MSA].i, "-mmsa");
+ break;
default:
- abort ();
- }
- break;
-
- case 4:
- switch (in_attr[Tag_GNU_MIPS_ABI_FP].i)
- {
- case 1:
- _bfd_error_handler
- (_("Warning: %B uses -msingle-float, %B uses -mips32r2 -mfp64"),
- ibfd, obfd);
- break;
-
- case 2:
_bfd_error_handler
- (_("Warning: %B uses -mdouble-float, %B uses -mips32r2 -mfp64"),
- ibfd, obfd);
+ (_("Warning: %B uses unknown MSA ABI %d "
+ "(set by %B), %B uses unknown MSA ABI %d"),
+ obfd, abi_msa_bfd, ibfd,
+ out_attr[Tag_GNU_MIPS_ABI_MSA].i,
+ in_attr[Tag_GNU_MIPS_ABI_MSA].i);
break;
-
- case 3:
- _bfd_error_handler
- (_("Warning: %B uses hard float, %B uses soft float"),
- obfd, ibfd);
- break;
-
- default:
- abort ();
}
- break;
-
- default:
- abort ();
}
}
bfd_boolean ok;
bfd_boolean null_input_bfd = TRUE;
asection *sec;
+ obj_attribute *out_attr;
/* Check if we have the same endianness. */
if (! _bfd_generic_verify_endian_match (ibfd, obfd))
return FALSE;
}
+ /* Set up the FP ABI attribute from the abiflags if it is not already
+ set. */
+ if (mips_elf_tdata (ibfd)->abiflags_valid)
+ {
+ obj_attribute *in_attr = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+ if (in_attr[Tag_GNU_MIPS_ABI_FP].i == Val_GNU_MIPS_ABI_FP_ANY)
+ in_attr[Tag_GNU_MIPS_ABI_FP].i =
+ mips_elf_tdata (ibfd)->abiflags.fp_abi;
+ }
+
if (!mips_elf_merge_obj_attributes (ibfd, obfd))
return FALSE;
- new_flags = elf_elfheader (ibfd)->e_flags;
- elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
- old_flags = elf_elfheader (obfd)->e_flags;
+ /* Check to see if the input BFD actually contains any sections.
+ If not, its flags may not have been initialised either, but it cannot
+ actually cause any incompatibility. */
+ for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+ {
+ /* Ignore synthetic sections and empty .text, .data and .bss sections
+ which are automatically generated by gas. Also ignore fake
+ (s)common sections, since merely defining a common symbol does
+ not affect compatibility. */
+ if ((sec->flags & SEC_IS_COMMON) == 0
+ && strcmp (sec->name, ".reginfo")
+ && strcmp (sec->name, ".mdebug")
+ && (sec->size != 0
+ || (strcmp (sec->name, ".text")
+ && strcmp (sec->name, ".data")
+ && strcmp (sec->name, ".bss"))))
+ {
+ null_input_bfd = FALSE;
+ break;
+ }
+ }
+ if (null_input_bfd)
+ return TRUE;
+
+ /* Populate abiflags using existing information. */
+ if (!mips_elf_tdata (ibfd)->abiflags_valid)
+ {
+ infer_mips_abiflags (ibfd, &mips_elf_tdata (ibfd)->abiflags);
+ mips_elf_tdata (ibfd)->abiflags_valid = TRUE;
+ }
+ else
+ {
+ Elf_Internal_ABIFlags_v0 abiflags;
+ Elf_Internal_ABIFlags_v0 in_abiflags;
+ infer_mips_abiflags (ibfd, &abiflags);
+ in_abiflags = mips_elf_tdata (ibfd)->abiflags;
+
+ /* It is not possible to infer the correct ISA revision
+ for R3 or R5 so drop down to R2 for the checks. */
+ if (in_abiflags.isa_rev == 3 || in_abiflags.isa_rev == 5)
+ in_abiflags.isa_rev = 2;
+
+ if (in_abiflags.isa_level != abiflags.isa_level
+ || in_abiflags.isa_rev != abiflags.isa_rev
+ || in_abiflags.isa_ext != abiflags.isa_ext)
+ (*_bfd_error_handler)
+ (_("%B: warning: Inconsistent ISA between e_flags and "
+ ".MIPS.abiflags"), ibfd);
+ if (abiflags.fp_abi != Val_GNU_MIPS_ABI_FP_ANY
+ && in_abiflags.fp_abi != abiflags.fp_abi)
+ (*_bfd_error_handler)
+ (_("%B: warning: Inconsistent FP ABI between e_flags and "
+ ".MIPS.abiflags"), ibfd);
+ if ((in_abiflags.ases & abiflags.ases) != abiflags.ases)
+ (*_bfd_error_handler)
+ (_("%B: warning: Inconsistent ASEs between e_flags and "
+ ".MIPS.abiflags"), ibfd);
+ if (in_abiflags.isa_ext != abiflags.isa_ext)
+ (*_bfd_error_handler)
+ (_("%B: warning: Inconsistent ISA extensions between e_flags and "
+ ".MIPS.abiflags"), ibfd);
+ if (in_abiflags.flags2 != 0)
+ (*_bfd_error_handler)
+ (_("%B: warning: Unexpected flag in the flags2 field of "
+ ".MIPS.abiflags (0x%lx)"), ibfd,
+ (unsigned long) in_abiflags.flags2);
+ }
+
+ if (!mips_elf_tdata (obfd)->abiflags_valid)
+ {
+ /* Copy input abiflags if output abiflags are not already valid. */
+ mips_elf_tdata (obfd)->abiflags = mips_elf_tdata (ibfd)->abiflags;
+ mips_elf_tdata (obfd)->abiflags_valid = TRUE;
+ }
if (! elf_flags_init (obfd))
{
elf_flags_init (obfd) = TRUE;
- elf_elfheader (obfd)->e_flags = new_flags;
+ elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags;
elf_elfheader (obfd)->e_ident[EI_CLASS]
= elf_elfheader (ibfd)->e_ident[EI_CLASS];
if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
&& (bfd_get_arch_info (obfd)->the_default
- || mips_mach_extends_p (bfd_get_mach (obfd),
+ || mips_mach_extends_p (bfd_get_mach (obfd),
bfd_get_mach (ibfd))))
{
if (! bfd_set_arch_mach (obfd, bfd_get_arch (ibfd),
bfd_get_mach (ibfd)))
return FALSE;
+
+ /* Update the ABI flags isa_level, isa_rev and isa_ext fields. */
+ update_mips_abiflags_isa (obfd, &mips_elf_tdata (obfd)->abiflags);
}
return TRUE;
}
+ /* Update the output abiflags fp_abi using the computed fp_abi. */
+ out_attr = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
+ mips_elf_tdata (obfd)->abiflags.fp_abi = out_attr[Tag_GNU_MIPS_ABI_FP].i;
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+ /* Merge abiflags. */
+ mips_elf_tdata (obfd)->abiflags.isa_rev
+ = max (mips_elf_tdata (obfd)->abiflags.isa_rev,
+ mips_elf_tdata (ibfd)->abiflags.isa_rev);
+ mips_elf_tdata (obfd)->abiflags.gpr_size
+ = max (mips_elf_tdata (obfd)->abiflags.gpr_size,
+ mips_elf_tdata (ibfd)->abiflags.gpr_size);
+ mips_elf_tdata (obfd)->abiflags.cpr1_size
+ = max (mips_elf_tdata (obfd)->abiflags.cpr1_size,
+ mips_elf_tdata (ibfd)->abiflags.cpr1_size);
+ mips_elf_tdata (obfd)->abiflags.cpr2_size
+ = max (mips_elf_tdata (obfd)->abiflags.cpr2_size,
+ mips_elf_tdata (ibfd)->abiflags.cpr2_size);
+#undef max
+ mips_elf_tdata (obfd)->abiflags.ases
+ |= mips_elf_tdata (ibfd)->abiflags.ases;
+ mips_elf_tdata (obfd)->abiflags.flags1
+ |= mips_elf_tdata (ibfd)->abiflags.flags1;
+
+ new_flags = elf_elfheader (ibfd)->e_flags;
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_MIPS_NOREORDER;
+ old_flags = elf_elfheader (obfd)->e_flags;
+
/* Check flag compatibility. */
new_flags &= ~EF_MIPS_NOREORDER;
if (new_flags == old_flags)
return TRUE;
- /* Check to see if the input BFD actually contains any sections.
- If not, its flags may not have been initialised either, but it cannot
- actually cause any incompatibility. */
- for (sec = ibfd->sections; sec != NULL; sec = sec->next)
- {
- /* Ignore synthetic sections and empty .text, .data and .bss sections
- which are automatically generated by gas. Also ignore fake
- (s)common sections, since merely defining a common symbol does
- not affect compatibility. */
- if ((sec->flags & SEC_IS_COMMON) == 0
- && strcmp (sec->name, ".reginfo")
- && strcmp (sec->name, ".mdebug")
- && (sec->size != 0
- || (strcmp (sec->name, ".text")
- && strcmp (sec->name, ".data")
- && strcmp (sec->name, ".bss"))))
- {
- null_input_bfd = FALSE;
- break;
- }
- }
- if (null_input_bfd)
- return TRUE;
-
ok = TRUE;
if (((new_flags & (EF_MIPS_PIC | EF_MIPS_CPIC)) != 0)
elf_elfheader (obfd)->e_flags
|= new_flags & (EF_MIPS_ARCH | EF_MIPS_MACH | EF_MIPS_32BITMODE);
+ /* Update the ABI flags isa_level, isa_rev, isa_ext fields. */
+ update_mips_abiflags_isa (obfd, &mips_elf_tdata (obfd)->abiflags);
+
/* Copy across the ABI flags if OBFD doesn't use them
and if that was what caused us to treat IBFD as 32-bit. */
if ((old_flags & EF_MIPS_ABI) == 0
old_flags &= ~ EF_MIPS_ARCH_ASE;
}
+ /* Compare NaN encodings. */
+ if ((new_flags & EF_MIPS_NAN2008) != (old_flags & EF_MIPS_NAN2008))
+ {
+ _bfd_error_handler (_("%B: linking %s module with previous %s modules"),
+ ibfd,
+ (new_flags & EF_MIPS_NAN2008
+ ? "-mnan=2008" : "-mnan=legacy"),
+ (old_flags & EF_MIPS_NAN2008
+ ? "-mnan=2008" : "-mnan=legacy"));
+ ok = FALSE;
+ new_flags &= ~EF_MIPS_NAN2008;
+ old_flags &= ~EF_MIPS_NAN2008;
+ }
+
+ /* Compare FP64 state. */
+ if ((new_flags & EF_MIPS_FP64) != (old_flags & EF_MIPS_FP64))
+ {
+ _bfd_error_handler (_("%B: linking %s module with previous %s modules"),
+ ibfd,
+ (new_flags & EF_MIPS_FP64
+ ? "-mfp64" : "-mfp32"),
+ (old_flags & EF_MIPS_FP64
+ ? "-mfp64" : "-mfp32"));
+ ok = FALSE;
+ new_flags &= ~EF_MIPS_FP64;
+ old_flags &= ~EF_MIPS_FP64;
+ }
+
/* Warn about any other mismatches */
if (new_flags != old_flags)
{
}
}
+/* Return the meaning of Tag_GNU_MIPS_ABI_FP value FP, or null if
+ not known. */
+
+const char *
+_bfd_mips_fp_abi_string (int fp)
+{
+ switch (fp)
+ {
+ /* These strings aren't translated because they're simply
+ option lists. */
+ case Val_GNU_MIPS_ABI_FP_DOUBLE:
+ return "-mdouble-float";
+
+ case Val_GNU_MIPS_ABI_FP_SINGLE:
+ return "-msingle-float";
+
+ case Val_GNU_MIPS_ABI_FP_SOFT:
+ return "-msoft-float";
+
+ case Val_GNU_MIPS_ABI_FP_OLD_64:
+ return _("-mips32r2 -mfp64 (12 callee-saved)");
+
+ case Val_GNU_MIPS_ABI_FP_XX:
+ return "-mfpxx";
+
+ case Val_GNU_MIPS_ABI_FP_64:
+ return "-mgp32 -mfp64";
+
+ case Val_GNU_MIPS_ABI_FP_64A:
+ return "-mgp32 -mfp64 -mno-odd-spreg";
+
+ default:
+ return 0;
+ }
+}
+
+static void
+print_mips_ases (FILE *file, unsigned int mask)
+{
+ if (mask & AFL_ASE_DSP)
+ fputs ("\n\tDSP ASE", file);
+ if (mask & AFL_ASE_DSPR2)
+ fputs ("\n\tDSP R2 ASE", file);
+ if (mask & AFL_ASE_EVA)
+ fputs ("\n\tEnhanced VA Scheme", file);
+ if (mask & AFL_ASE_MCU)
+ fputs ("\n\tMCU (MicroController) ASE", file);
+ if (mask & AFL_ASE_MDMX)
+ fputs ("\n\tMDMX ASE", file);
+ if (mask & AFL_ASE_MIPS3D)
+ fputs ("\n\tMIPS-3D ASE", file);
+ if (mask & AFL_ASE_MT)
+ fputs ("\n\tMT ASE", file);
+ if (mask & AFL_ASE_SMARTMIPS)
+ fputs ("\n\tSmartMIPS ASE", file);
+ if (mask & AFL_ASE_VIRT)
+ fputs ("\n\tVZ ASE", file);
+ if (mask & AFL_ASE_MSA)
+ fputs ("\n\tMSA ASE", file);
+ if (mask & AFL_ASE_MIPS16)
+ fputs ("\n\tMIPS16 ASE", file);
+ if (mask & AFL_ASE_MICROMIPS)
+ fputs ("\n\tMICROMIPS ASE", file);
+ if (mask & AFL_ASE_XPA)
+ fputs ("\n\tXPA ASE", file);
+ if (mask == 0)
+ fprintf (file, "\n\t%s", _("None"));
+ else if ((mask & ~AFL_ASE_MASK) != 0)
+ fprintf (stdout, "\n\t%s (%x)", _("Unknown"), mask & ~AFL_ASE_MASK);
+}
+
+static void
+print_mips_isa_ext (FILE *file, unsigned int isa_ext)
+{
+ switch (isa_ext)
+ {
+ case 0:
+ fputs (_("None"), file);
+ break;
+ case AFL_EXT_XLR:
+ fputs ("RMI XLR", file);
+ break;
+ case AFL_EXT_OCTEON3:
+ fputs ("Cavium Networks Octeon3", file);
+ break;
+ case AFL_EXT_OCTEON2:
+ fputs ("Cavium Networks Octeon2", file);
+ break;
+ case AFL_EXT_OCTEONP:
+ fputs ("Cavium Networks OcteonP", file);
+ break;
+ case AFL_EXT_LOONGSON_3A:
+ fputs ("Loongson 3A", file);
+ break;
+ case AFL_EXT_OCTEON:
+ fputs ("Cavium Networks Octeon", file);
+ break;
+ case AFL_EXT_5900:
+ fputs ("Toshiba R5900", file);
+ break;
+ case AFL_EXT_4650:
+ fputs ("MIPS R4650", file);
+ break;
+ case AFL_EXT_4010:
+ fputs ("LSI R4010", file);
+ break;
+ case AFL_EXT_4100:
+ fputs ("NEC VR4100", file);
+ break;
+ case AFL_EXT_3900:
+ fputs ("Toshiba R3900", file);
+ break;
+ case AFL_EXT_10000:
+ fputs ("MIPS R10000", file);
+ break;
+ case AFL_EXT_SB1:
+ fputs ("Broadcom SB-1", file);
+ break;
+ case AFL_EXT_4111:
+ fputs ("NEC VR4111/VR4181", file);
+ break;
+ case AFL_EXT_4120:
+ fputs ("NEC VR4120", file);
+ break;
+ case AFL_EXT_5400:
+ fputs ("NEC VR5400", file);
+ break;
+ case AFL_EXT_5500:
+ fputs ("NEC VR5500", file);
+ break;
+ case AFL_EXT_LOONGSON_2E:
+ fputs ("ST Microelectronics Loongson 2E", file);
+ break;
+ case AFL_EXT_LOONGSON_2F:
+ fputs ("ST Microelectronics Loongson 2F", file);
+ break;
+ default:
+ fprintf (file, "%s (%d)", _("Unknown"), isa_ext);
+ break;
+ }
+}
+
+static void
+print_mips_fp_abi_value (FILE *file, int val)
+{
+ switch (val)
+ {
+ case Val_GNU_MIPS_ABI_FP_ANY:
+ fprintf (file, _("Hard or soft float\n"));
+ break;
+ case Val_GNU_MIPS_ABI_FP_DOUBLE:
+ fprintf (file, _("Hard float (double precision)\n"));
+ break;
+ case Val_GNU_MIPS_ABI_FP_SINGLE:
+ fprintf (file, _("Hard float (single precision)\n"));
+ break;
+ case Val_GNU_MIPS_ABI_FP_SOFT:
+ fprintf (file, _("Soft float\n"));
+ break;
+ case Val_GNU_MIPS_ABI_FP_OLD_64:
+ fprintf (file, _("Hard float (MIPS32r2 64-bit FPU 12 callee-saved)\n"));
+ break;
+ case Val_GNU_MIPS_ABI_FP_XX:
+ fprintf (file, _("Hard float (32-bit CPU, Any FPU)\n"));
+ break;
+ case Val_GNU_MIPS_ABI_FP_64:
+ fprintf (file, _("Hard float (32-bit CPU, 64-bit FPU)\n"));
+ break;
+ case Val_GNU_MIPS_ABI_FP_64A:
+ fprintf (file, _("Hard float compat (32-bit CPU, 64-bit FPU)\n"));
+ break;
+ default:
+ fprintf (file, "??? (%d)\n", val);
+ break;
+ }
+}
+
+static int
+get_mips_reg_size (int reg_size)
+{
+ return (reg_size == AFL_REG_NONE) ? 0
+ : (reg_size == AFL_REG_32) ? 32
+ : (reg_size == AFL_REG_64) ? 64
+ : (reg_size == AFL_REG_128) ? 128
+ : -1;
+}
+
bfd_boolean
_bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr)
{
fprintf (file, " [mips32r2]");
else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64R2)
fprintf (file, " [mips64r2]");
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_32R6)
+ fprintf (file, " [mips32r6]");
+ else if ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH) == E_MIPS_ARCH_64R6)
+ fprintf (file, " [mips64r6]");
else
fprintf (file, _(" [unknown ISA]"));
if (elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS)
fprintf (file, " [micromips]");
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_NAN2008)
+ fprintf (file, " [nan2008]");
+
+ if (elf_elfheader (abfd)->e_flags & EF_MIPS_FP64)
+ fprintf (file, " [old fp64]");
+
if (elf_elfheader (abfd)->e_flags & EF_MIPS_32BITMODE)
fprintf (file, " [32bitmode]");
else
fputc ('\n', file);
+ if (mips_elf_tdata (abfd)->abiflags_valid)
+ {
+ Elf_Internal_ABIFlags_v0 *abiflags = &mips_elf_tdata (abfd)->abiflags;
+ fprintf (file, "\nMIPS ABI Flags Version: %d\n", abiflags->version);
+ fprintf (file, "\nISA: MIPS%d", abiflags->isa_level);
+ if (abiflags->isa_rev > 1)
+ fprintf (file, "r%d", abiflags->isa_rev);
+ fprintf (file, "\nGPR size: %d",
+ get_mips_reg_size (abiflags->gpr_size));
+ fprintf (file, "\nCPR1 size: %d",
+ get_mips_reg_size (abiflags->cpr1_size));
+ fprintf (file, "\nCPR2 size: %d",
+ get_mips_reg_size (abiflags->cpr2_size));
+ fputs ("\nFP ABI: ", file);
+ print_mips_fp_abi_value (file, abiflags->fp_abi);
+ fputs ("ISA Extension: ", file);
+ print_mips_isa_ext (file, abiflags->isa_ext);
+ fputs ("\nASEs:", file);
+ print_mips_ases (file, abiflags->ases);
+ fprintf (file, "\nFLAGS 1: %8.8lx", abiflags->flags1);
+ fprintf (file, "\nFLAGS 2: %8.8lx", abiflags->flags2);
+ fputc ('\n', file);
+ }
+
return TRUE;
}
+ i * 4 * ARRAY_SIZE (mips_exec_plt_entry));
}
+/* Build a table of synthetic symbols to represent the PLT. As with MIPS16
+ and microMIPS PLT slots we may have a many-to-one mapping between .plt
+ and .got.plt and also the slots may be of a different size each we walk
+ the PLT manually fetching instructions and matching them against known
+ patterns. To make things easier standard MIPS slots, if any, always come
+ first. As we don't create proper ELF symbols we use the UDATA.I member
+ of ASYMBOL to carry ISA annotation. The encoding used is the same as
+ with the ST_OTHER member of the ELF symbol. */
+
+long
+_bfd_mips_elf_get_synthetic_symtab (bfd *abfd,
+ long symcount ATTRIBUTE_UNUSED,
+ asymbol **syms ATTRIBUTE_UNUSED,
+ long dynsymcount, asymbol **dynsyms,
+ asymbol **ret)
+{
+ static const char pltname[] = "_PROCEDURE_LINKAGE_TABLE_";
+ static const char microsuffix[] = "@micromipsplt";
+ static const char m16suffix[] = "@mips16plt";
+ static const char mipssuffix[] = "@plt";
+
+ bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
+ const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+ bfd_boolean micromips_p = MICROMIPS_P (abfd);
+ Elf_Internal_Shdr *hdr;
+ bfd_byte *plt_data;
+ bfd_vma plt_offset;
+ unsigned int other;
+ bfd_vma entry_size;
+ bfd_vma plt0_size;
+ asection *relplt;
+ bfd_vma opcode;
+ asection *plt;
+ asymbol *send;
+ size_t size;
+ char *names;
+ long counti;
+ arelent *p;
+ asymbol *s;
+ char *nend;
+ long count;
+ long pi;
+ long i;
+ long n;
+
+ *ret = NULL;
+
+ if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0 || dynsymcount <= 0)
+ return 0;
+
+ relplt = bfd_get_section_by_name (abfd, ".rel.plt");
+ if (relplt == NULL)
+ return 0;
+
+ hdr = &elf_section_data (relplt)->this_hdr;
+ if (hdr->sh_link != elf_dynsymtab (abfd) || hdr->sh_type != SHT_REL)
+ return 0;
+
+ plt = bfd_get_section_by_name (abfd, ".plt");
+ if (plt == NULL)
+ return 0;
+
+ slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
+ if (!(*slurp_relocs) (abfd, relplt, dynsyms, TRUE))
+ return -1;
+ p = relplt->relocation;
+
+ /* Calculating the exact amount of space required for symbols would
+ require two passes over the PLT, so just pessimise assuming two
+ PLT slots per relocation. */
+ count = relplt->size / hdr->sh_entsize;
+ counti = count * bed->s->int_rels_per_ext_rel;
+ size = 2 * count * sizeof (asymbol);
+ size += count * (sizeof (mipssuffix) +
+ (micromips_p ? sizeof (microsuffix) : sizeof (m16suffix)));
+ for (pi = 0; pi < counti; pi += bed->s->int_rels_per_ext_rel)
+ size += 2 * strlen ((*p[pi].sym_ptr_ptr)->name);
+
+ /* Add the size of "_PROCEDURE_LINKAGE_TABLE_" too. */
+ size += sizeof (asymbol) + sizeof (pltname);
+
+ if (!bfd_malloc_and_get_section (abfd, plt, &plt_data))
+ return -1;
+
+ if (plt->size < 16)
+ return -1;
+
+ s = *ret = bfd_malloc (size);
+ if (s == NULL)
+ return -1;
+ send = s + 2 * count + 1;
+
+ names = (char *) send;
+ nend = (char *) s + size;
+ n = 0;
+
+ opcode = bfd_get_micromips_32 (abfd, plt_data + 12);
+ if (opcode == 0x3302fffe)
+ {
+ if (!micromips_p)
+ return -1;
+ plt0_size = 2 * ARRAY_SIZE (micromips_o32_exec_plt0_entry);
+ other = STO_MICROMIPS;
+ }
+ else if (opcode == 0x0398c1d0)
+ {
+ if (!micromips_p)
+ return -1;
+ plt0_size = 2 * ARRAY_SIZE (micromips_insn32_o32_exec_plt0_entry);
+ other = STO_MICROMIPS;
+ }
+ else
+ {
+ plt0_size = 4 * ARRAY_SIZE (mips_o32_exec_plt0_entry);
+ other = 0;
+ }
+
+ s->the_bfd = abfd;
+ s->flags = BSF_SYNTHETIC | BSF_FUNCTION | BSF_LOCAL;
+ s->section = plt;
+ s->value = 0;
+ s->name = names;
+ s->udata.i = other;
+ memcpy (names, pltname, sizeof (pltname));
+ names += sizeof (pltname);
+ ++s, ++n;
+
+ pi = 0;
+ for (plt_offset = plt0_size;
+ plt_offset + 8 <= plt->size && s < send;
+ plt_offset += entry_size)
+ {
+ bfd_vma gotplt_addr;
+ const char *suffix;
+ bfd_vma gotplt_hi;
+ bfd_vma gotplt_lo;
+ size_t suffixlen;
+
+ opcode = bfd_get_micromips_32 (abfd, plt_data + plt_offset + 4);
+
+ /* Check if the second word matches the expected MIPS16 instruction. */
+ if (opcode == 0x651aeb00)
+ {
+ if (micromips_p)
+ return -1;
+ /* Truncated table??? */
+ if (plt_offset + 16 > plt->size)
+ break;
+ gotplt_addr = bfd_get_32 (abfd, plt_data + plt_offset + 12);
+ entry_size = 2 * ARRAY_SIZE (mips16_o32_exec_plt_entry);
+ suffixlen = sizeof (m16suffix);
+ suffix = m16suffix;
+ other = STO_MIPS16;
+ }
+ /* Likewise the expected microMIPS instruction (no insn32 mode). */
+ else if (opcode == 0xff220000)
+ {
+ if (!micromips_p)
+ return -1;
+ gotplt_hi = bfd_get_16 (abfd, plt_data + plt_offset) & 0x7f;
+ gotplt_lo = bfd_get_16 (abfd, plt_data + plt_offset + 2) & 0xffff;
+ gotplt_hi = ((gotplt_hi ^ 0x40) - 0x40) << 18;
+ gotplt_lo <<= 2;
+ gotplt_addr = gotplt_hi + gotplt_lo;
+ gotplt_addr += ((plt->vma + plt_offset) | 3) ^ 3;
+ entry_size = 2 * ARRAY_SIZE (micromips_o32_exec_plt_entry);
+ suffixlen = sizeof (microsuffix);
+ suffix = microsuffix;
+ other = STO_MICROMIPS;
+ }
+ /* Likewise the expected microMIPS instruction (insn32 mode). */
+ else if ((opcode & 0xffff0000) == 0xff2f0000)
+ {
+ gotplt_hi = bfd_get_16 (abfd, plt_data + plt_offset + 2) & 0xffff;
+ gotplt_lo = bfd_get_16 (abfd, plt_data + plt_offset + 6) & 0xffff;
+ gotplt_hi = ((gotplt_hi ^ 0x8000) - 0x8000) << 16;
+ gotplt_lo = (gotplt_lo ^ 0x8000) - 0x8000;
+ gotplt_addr = gotplt_hi + gotplt_lo;
+ entry_size = 2 * ARRAY_SIZE (micromips_insn32_o32_exec_plt_entry);
+ suffixlen = sizeof (microsuffix);
+ suffix = microsuffix;
+ other = STO_MICROMIPS;
+ }
+ /* Otherwise assume standard MIPS code. */
+ else
+ {
+ gotplt_hi = bfd_get_32 (abfd, plt_data + plt_offset) & 0xffff;
+ gotplt_lo = bfd_get_32 (abfd, plt_data + plt_offset + 4) & 0xffff;
+ gotplt_hi = ((gotplt_hi ^ 0x8000) - 0x8000) << 16;
+ gotplt_lo = (gotplt_lo ^ 0x8000) - 0x8000;
+ gotplt_addr = gotplt_hi + gotplt_lo;
+ entry_size = 4 * ARRAY_SIZE (mips_exec_plt_entry);
+ suffixlen = sizeof (mipssuffix);
+ suffix = mipssuffix;
+ other = 0;
+ }
+ /* Truncated table??? */
+ if (plt_offset + entry_size > plt->size)
+ break;
+
+ for (i = 0;
+ i < count && p[pi].address != gotplt_addr;
+ i++, pi = (pi + bed->s->int_rels_per_ext_rel) % counti);
+
+ if (i < count)
+ {
+ size_t namelen;
+ size_t len;
+
+ *s = **p[pi].sym_ptr_ptr;
+ /* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL set. Since
+ we are defining a symbol, ensure one of them is set. */
+ if ((s->flags & BSF_LOCAL) == 0)
+ s->flags |= BSF_GLOBAL;
+ s->flags |= BSF_SYNTHETIC;
+ s->section = plt;
+ s->value = plt_offset;
+ s->name = names;
+ s->udata.i = other;
+
+ len = strlen ((*p[pi].sym_ptr_ptr)->name);
+ namelen = len + suffixlen;
+ if (names + namelen > nend)
+ break;
+
+ memcpy (names, (*p[pi].sym_ptr_ptr)->name, len);
+ names += len;
+ memcpy (names, suffix, suffixlen);
+ names += suffixlen;
+
+ ++s, ++n;
+ pi = (pi + bed->s->int_rels_per_ext_rel) % counti;
+ }
+ }
+
+ free (plt_data);
+
+ return n;
+}
+
void
_bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info)
{
if (htab->use_plts_and_copy_relocs && !htab->is_vxworks)
i_ehdrp->e_ident[EI_ABIVERSION] = 1;
}
+
+ _bfd_elf_post_process_headers (abfd, link_info);
+
+ if (mips_elf_tdata (abfd)->abiflags.fp_abi == Val_GNU_MIPS_ABI_FP_64
+ || mips_elf_tdata (abfd)->abiflags.fp_abi == Val_GNU_MIPS_ABI_FP_64A)
+ i_ehdrp->e_ident[EI_ABIVERSION] = 3;
}