X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=bfd%2Felfxx-mips.c;h=a0cc26e185854243e799161288a7653961bf29b5;hb=cfd4e875aeacf565f945c2818dbfff47807b6199;hp=102e57fa427f33088b41ada346845a5424c2ace1;hpb=6c42ddb92b9064b8792029ce638d984d14fc00cf;p=platform%2Fupstream%2Fbinutils.git diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index 102e57f..a0cc26e 100644 --- a/bfd/elfxx-mips.c +++ b/bfd/elfxx-mips.c @@ -1,7 +1,5 @@ /* 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, 2012, 2013 - Free Software Foundation, Inc. + Copyright (C) 1993-2014 Free Software Foundation, Inc. Most of the information added by Ian Lance Taylor, Cygnus Support, . @@ -47,6 +45,14 @@ #include "hashtab.h" +/* Types of TLS GOT entry. */ +enum mips_got_tls_type { + GOT_TLS_NONE, + GOT_TLS_GD, + GOT_TLS_LDM, + GOT_TLS_IE +}; + /* This structure is used to hold information about one GOT entry. There are four types of entry: @@ -86,23 +92,41 @@ struct mips_got_entry struct mips_elf_link_hash_entry *h; } d; - /* The TLS type of this GOT entry: GOT_NORMAL, GOT_TLS_IE, GOT_TLS_GD - or GOT_TLS_LDM. An LDM GOT entry will be a local symbol entry with - r_symndx == 0. */ -#define GOT_NORMAL 0 -#define GOT_TLS_GD 1 -#define GOT_TLS_LDM 2 -#define GOT_TLS_IE 4 -#define GOT_TLS_TYPE 7 -#define GOT_TLS_DONE 0x80 + /* The TLS type of this GOT entry. An LDM GOT entry will be a local + symbol entry with r_symndx == 0. */ unsigned char tls_type; + /* True if we have filled in the GOT contents for a TLS entry, + and created the associated relocations. */ + unsigned char tls_initialized; + /* The offset from the beginning of the .got section to the entry corresponding to this symbol+addend. If it's a global symbol whose offset is yet to be decided, it's going to be -1. */ long gotidx; }; +/* This structure represents a GOT page reference from an input bfd. + Each instance represents a symbol + ADDEND, where the representation + of the symbol depends on whether it is local to the input bfd. + If it is, then SYMNDX >= 0, and the symbol has index SYMNDX in U.ABFD. + Otherwise, SYMNDX < 0 and U.H points to the symbol's hash table entry. + + Page references with SYMNDX >= 0 always become page references + in the output. Page references with SYMNDX < 0 only become page + references if the symbol binds locally; in other cases, the page + reference decays to a global GOT reference. */ +struct mips_got_page_ref +{ + long symndx; + union + { + struct mips_elf_link_hash_entry *h; + bfd *abfd; + } u; + bfd_vma addend; +}; + /* This structure describes a range of addends: [MIN_ADDEND, MAX_ADDEND]. The structures form a non-overlapping list that is sorted by increasing MIN_ADDEND. */ @@ -114,13 +138,11 @@ struct mips_got_page_range }; /* 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. */ @@ -146,10 +168,14 @@ struct mips_got_info unsigned int page_gotno; /* The number of relocations needed for the GOT entries. */ unsigned int relocs; - /* The number of local .got entries we have used. */ - unsigned int assigned_gotno; + /* 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; /* In multi-got links, a pointer to the next got (err, rather, most @@ -293,6 +319,32 @@ struct mips_elf_hash_sort_data 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. */ @@ -357,6 +409,9 @@ struct mips_elf_link_hash_entry /* 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. */ @@ -384,6 +439,9 @@ struct mips_elf_link_hash_table /* 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; @@ -411,8 +469,20 @@ struct mips_elf_link_hash_table /* 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; @@ -439,6 +509,12 @@ struct mips_elf_link_hash_table 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. */ @@ -468,8 +544,31 @@ struct mips_elf_obj_tdata /* 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. */ @@ -681,6 +780,10 @@ 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) @@ -692,6 +795,15 @@ static bfd *reldyn_sorting_bfd; /* Nonzero if ABFD is using NewABI conventions. */ #define NEWABI_P(abfd) (ABI_N32_P (abfd) || ABI_64_P (abfd)) +/* Nonzero if ABFD has microMIPS code. */ +#define MICROMIPS_P(abfd) \ + ((elf_elfheader (abfd)->e_flags & EF_MIPS_ARCH_ASE_MICROMIPS) != 0) + +/* 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)) @@ -709,6 +821,10 @@ static bfd *reldyn_sorting_bfd; #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)) \ @@ -807,8 +923,35 @@ static bfd *reldyn_sorting_bfd; ? (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. */ @@ -920,7 +1063,40 @@ static const bfd_vma mips_n64_exec_plt0_entry[] = 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) */ @@ -929,6 +1105,50 @@ static const bfd_vma mips_exec_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[] = { @@ -1067,6 +1287,7 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry, 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; @@ -1975,7 +2196,8 @@ hi16_reloc_p (int r_type) { 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 @@ -1983,7 +2205,8 @@ lo16_reloc_p (int r_type) { 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 @@ -2001,6 +2224,13 @@ jal_reloc_p (int r_type) } 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) { return (r_type == R_MICROMIPS_26_S1 @@ -2471,6 +2701,46 @@ bfd_mips_elf_swap_options_out (bfd *abfd, const Elf_Internal_Options *in, 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); +} /* This function is called via qsort() to sort the dynamic relocation entries by increasing r_symndx value. */ @@ -2681,6 +2951,8 @@ mips_elf_output_extsym (struct mips_elf_link_hash_entry *h, void *data) 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; @@ -2690,7 +2962,7 @@ mips_elf_output_extsym (struct mips_elf_link_hash_entry *h, void *data) { 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 @@ -2742,8 +3014,8 @@ mips_elf_got_entry_hash (const void *entry_) const struct mips_got_entry *entry = (struct mips_got_entry *)entry_; return (entry->symndx - + (((entry->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM) << 18) - + ((entry->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM ? 0 + + ((entry->tls_type == GOT_TLS_LDM) << 18) + + (entry->tls_type == GOT_TLS_LDM ? 0 : !entry->abfd ? mips_elf_hash_bfd_vma (entry->d.address) : entry->symndx >= 0 ? (entry->abfd->id + mips_elf_hash_bfd_vma (entry->d.addend)) @@ -2757,8 +3029,8 @@ mips_elf_got_entry_eq (const void *entry1, const void *entry2) const struct mips_got_entry *e2 = (struct mips_got_entry *)entry2; return (e1->symndx == e2->symndx - && (e1->tls_type & GOT_TLS_TYPE) == (e2->tls_type & GOT_TLS_TYPE) - && ((e1->tls_type & GOT_TLS_TYPE) == GOT_TLS_LDM ? TRUE + && e1->tls_type == e2->tls_type + && (e1->tls_type == GOT_TLS_LDM ? TRUE : !e1->abfd ? !e2->abfd && e1->d.address == e2->d.address : e1->symndx >= 0 ? (e1->abfd == e2->abfd && e1->d.addend == e2->d.addend) @@ -2766,12 +3038,38 @@ mips_elf_got_entry_eq (const void *entry1, const void *entry2) } static hashval_t +mips_got_page_ref_hash (const void *ref_) +{ + const struct mips_got_page_ref *ref; + + ref = (const struct mips_got_page_ref *) ref_; + return ((ref->symndx >= 0 + ? (hashval_t) (ref->u.abfd->id + ref->symndx) + : ref->u.h->root.root.root.hash) + + mips_elf_hash_bfd_vma (ref->addend)); +} + +static int +mips_got_page_ref_eq (const void *ref1_, const void *ref2_) +{ + const struct mips_got_page_ref *ref1, *ref2; + + ref1 = (const struct mips_got_page_ref *) ref1_; + ref2 = (const struct mips_got_page_ref *) ref2_; + return (ref1->symndx == ref2->symndx + && (ref1->symndx < 0 + ? ref1->u.h == ref2->u.h + : ref1->u.abfd == ref2->u.abfd) + && ref1->addend == ref2->addend); +} + +static hashval_t mips_got_page_entry_hash (const void *entry_) { const struct mips_got_page_entry *entry; entry = (const struct mips_got_page_entry *) entry_; - return entry->abfd->id + entry->symndx; + return entry->sec->id; } static int @@ -2781,7 +3079,7 @@ mips_got_page_entry_eq (const void *entry1_, const void *entry2_) 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; } /* Create and return a new mips_got_info structure. */ @@ -2800,9 +3098,9 @@ mips_elf_create_got_info (bfd *abfd) if (g->got_entries == NULL) return NULL; - g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash, - mips_got_page_entry_eq, NULL); - if (g->got_page_entries == NULL) + g->got_page_refs = htab_try_create (1, mips_got_page_ref_hash, + mips_got_page_ref_eq, NULL); + if (g->got_page_refs == NULL) return NULL; return g; @@ -2839,7 +3137,9 @@ mips_elf_replace_bfd_got (bfd *abfd, struct mips_got_info *g) /* The GOT structure itself and the hash table entries are allocated to a bfd, but the hash tables aren't. */ htab_delete (tdata->got->got_entries); - htab_delete (tdata->got->got_page_entries); + htab_delete (tdata->got->got_page_refs); + if (tdata->got->got_page_entries) + htab_delete (tdata->got->got_page_entries); } tdata->got = g; } @@ -2889,7 +3189,7 @@ mips_elf_reloc_tls_type (unsigned int r_type) if (tls_gottprel_reloc_p (r_type)) return GOT_TLS_IE; - return GOT_NORMAL; + return GOT_TLS_NONE; } /* Return the number of GOT slots needed for GOT TLS type TYPE. */ @@ -2906,7 +3206,7 @@ mips_tls_got_entries (unsigned int type) case GOT_TLS_IE: return 1; - case GOT_NORMAL: + case GOT_TLS_NONE: return 0; } abort (); @@ -2937,7 +3237,7 @@ mips_tls_got_relocs (struct bfd_link_info *info, unsigned char tls_type, if (!need_relocs) return 0; - switch (tls_type & GOT_TLS_TYPE) + switch (tls_type) { case GOT_TLS_GD: return indx != 0 ? 2 : 1; @@ -2961,13 +3261,10 @@ mips_elf_count_got_entry (struct bfd_link_info *info, struct mips_got_info *g, struct mips_got_entry *entry) { - unsigned char tls_type; - - tls_type = entry->tls_type & GOT_TLS_TYPE; - if (tls_type) + if (entry->tls_type) { - g->tls_gotno += mips_tls_got_entries (tls_type); - g->relocs += mips_tls_got_relocs (info, tls_type, + g->tls_gotno += mips_tls_got_entries (entry->tls_type); + g->relocs += mips_tls_got_relocs (info, entry->tls_type, entry->symndx < 0 ? &entry->d.h->root : NULL); } @@ -2977,23 +3274,6 @@ mips_elf_count_got_entry (struct bfd_link_info *info, g->global_gotno += 1; } -/* A htab_traverse callback. Count the number of GOT entries and - TLS relocations required for the GOT entry in *ENTRYP. DATA points - to a mips_elf_traverse_got_arg structure. */ - -static int -mips_elf_count_got_entries (void **entryp, void *data) -{ - struct mips_got_entry *entry; - struct mips_elf_traverse_got_arg *arg; - - entry = (struct mips_got_entry *) *entryp; - arg = (struct mips_elf_traverse_got_arg *) data; - mips_elf_count_got_entry (arg->info, arg->g, entry); - - return 1; -} - /* Output a simple dynamic relocation into SRELOC. */ static void @@ -3028,16 +3308,15 @@ mips_elf_output_dynamic_relocation (bfd *output_bfd, /* Initialize a set of TLS GOT entries for one symbol. */ static void -mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset, - unsigned char *tls_type_p, - struct bfd_link_info *info, +mips_elf_initialize_tls_slots (bfd *abfd, struct bfd_link_info *info, + struct mips_got_entry *entry, struct mips_elf_link_hash_entry *h, bfd_vma value) { struct mips_elf_link_hash_table *htab; int indx; asection *sreloc, *sgot; - bfd_vma got_offset2; + bfd_vma got_offset, got_offset2; bfd_boolean need_relocs = FALSE; htab = mips_elf_hash_table (info); @@ -3056,7 +3335,7 @@ mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset, indx = h->root.dynindx; } - if (*tls_type_p & GOT_TLS_DONE) + if (entry->tls_initialized) return; if ((info->shared || indx != 0) @@ -3073,8 +3352,9 @@ mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset, /* Emit necessary relocations. */ sreloc = mips_elf_rel_dyn_section (info, FALSE); + got_offset = entry->gotidx; - switch (*tls_type_p & GOT_TLS_TYPE) + switch (entry->tls_type) { case GOT_TLS_GD: /* General Dynamic. */ @@ -3147,21 +3427,7 @@ mips_elf_initialize_tls_slots (bfd *abfd, bfd_vma got_offset, abort (); } - *tls_type_p |= GOT_TLS_DONE; -} - -/* Return the GOT index to use for a relocation against H using the - TLS model in *TLS_TYPE. The GOT entries for this symbol/model - combination start at GOT_INDEX into ABFD's GOT. This function - initializes the GOT entries and corresponding relocations. */ - -static bfd_vma -mips_tls_got_index (bfd *abfd, bfd_vma got_index, unsigned char *tls_type, - struct bfd_link_info *info, - struct mips_elf_link_hash_entry *h, bfd_vma symbol) -{ - mips_elf_initialize_tls_slots (abfd, got_index, tls_type, info, h, symbol); - return got_index; + entry->tls_initialized = TRUE; } /* Return the offset from _GLOBAL_OFFSET_TABLE_ of the .got.plt entry @@ -3172,25 +3438,20 @@ static bfd_vma 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 @@ -3222,10 +3483,8 @@ mips_elf_local_got_index (bfd *abfd, bfd *ibfd, struct bfd_link_info *info, return MINUS_ONE; if (entry->tls_type) - return mips_tls_got_index (abfd, entry->gotidx, &entry->tls_type, - info, h, value); - else - return entry->gotidx; + mips_elf_initialize_tls_slots (abfd, info, entry, h, value); + return entry->gotidx; } /* Return the GOT index of global symbol H in the primary GOT. */ @@ -3301,8 +3560,7 @@ mips_elf_global_got_index (bfd *obfd, struct bfd_link_info *info, bfd *ibfd, + h->root.u.def.section->output_offset + h->root.u.def.section->output_section->vma); - return mips_tls_got_index (obfd, gotidx, &entry->tls_type, - info, lookup.d.h, value); + mips_elf_initialize_tls_slots (obfd, info, entry, lookup.d.h, value); } return gotidx; } @@ -3456,7 +3714,7 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info, if (entry) return entry; - if (g->assigned_gotno >= g->local_gotno) + if (g->assigned_low_gotno > g->assigned_high_gotno) { /* We didn't allocate enough space in the GOT. */ (*_bfd_error_handler) @@ -3469,7 +3727,14 @@ mips_elf_create_local_got_entry (bfd *abfd, struct bfd_link_info *info, if (!entry) return NULL; - lookup.gotidx = MIPS_ELF_GOT_SIZE (abfd) * g->assigned_gotno++; + 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; @@ -3632,6 +3897,7 @@ mips_elf_record_got_entry (struct bfd_link_info *info, bfd *abfd, if (!entry) return FALSE; + lookup->tls_initialized = FALSE; lookup->gotidx = -1; *entry = *lookup; *loc = entry; @@ -3688,7 +3954,7 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h, } tls_type = mips_elf_reloc_tls_type (r_type); - if (tls_type == GOT_NORMAL && hmips->global_got_area > GGA_NORMAL) + if (tls_type == GOT_TLS_NONE && hmips->global_got_area > GGA_NORMAL) hmips->global_got_area = GGA_NORMAL; entry.abfd = abfd; @@ -3722,30 +3988,18 @@ mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend, return mips_elf_record_got_entry (info, abfd, &entry); } -/* Return the maximum number of GOT page entries required for RANGE. */ - -static bfd_vma -mips_elf_pages_for_range (const struct mips_got_page_range *range) -{ - return (range->max_addend - range->min_addend + 0x1ffff) >> 16; -} - -/* Record that ABFD has a page relocation against symbol SYMNDX and - that ADDEND is the addend for that relocation. - - This function creates an upper bound on the number of GOT slots - required; no attempt is made to combine references to non-overridable - global symbols across multiple input files. */ +/* Record that ABFD has a page relocation against SYMNDX + ADDEND. + H is the symbol's hash table entry, or null if SYMNDX is local + to ABFD. */ static bfd_boolean -mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd, - long symndx, bfd_signed_vma addend) +mips_elf_record_got_page_ref (struct bfd_link_info *info, bfd *abfd, + long symndx, struct elf_link_hash_entry *h, + bfd_signed_vma addend) { struct mips_elf_link_hash_table *htab; struct mips_got_info *g1, *g2; - struct mips_got_page_entry lookup, *entry; - struct mips_got_page_range **range_ptr, *range; - bfd_vma old_pages, new_pages; + struct mips_got_page_ref lookup, *entry; void **loc, **bfd_loc; htab = mips_elf_hash_table (info); @@ -3754,26 +4008,29 @@ mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd, g1 = htab->got_info; BFD_ASSERT (g1 != NULL); - /* Find the mips_got_page_entry hash table entry for this symbol. */ - lookup.abfd = abfd; - lookup.symndx = symndx; - loc = htab_find_slot (g1->got_page_entries, &lookup, INSERT); + if (h) + { + lookup.symndx = -1; + lookup.u.h = (struct mips_elf_link_hash_entry *) h; + } + else + { + lookup.symndx = symndx; + lookup.u.abfd = abfd; + } + lookup.addend = addend; + loc = htab_find_slot (g1->got_page_refs, &lookup, INSERT); if (loc == NULL) return FALSE; - /* Create a mips_got_page_entry if this is the first time we've - seen the symbol. */ - entry = (struct mips_got_page_entry *) *loc; + entry = (struct mips_got_page_ref *) *loc; if (!entry) { entry = bfd_alloc (abfd, sizeof (*entry)); if (!entry) return FALSE; - entry->abfd = abfd; - entry->symndx = symndx; - entry->ranges = NULL; - entry->num_pages = 0; + *entry = lookup; *loc = entry; } @@ -3782,67 +4039,13 @@ mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd, if (!g2) return FALSE; - bfd_loc = htab_find_slot (g2->got_page_entries, &lookup, INSERT); + bfd_loc = htab_find_slot (g2->got_page_refs, &lookup, INSERT); if (!bfd_loc) return FALSE; if (!*bfd_loc) *bfd_loc = entry; - /* Skip over ranges whose maximum extent cannot share a page entry - with ADDEND. */ - range_ptr = &entry->ranges; - while (*range_ptr && addend > (*range_ptr)->max_addend + 0xffff) - range_ptr = &(*range_ptr)->next; - - /* If we scanned to the end of the list, or found a range whose - minimum extent cannot share a page entry with ADDEND, create - a new singleton range. */ - range = *range_ptr; - if (!range || addend < range->min_addend - 0xffff) - { - range = bfd_alloc (abfd, sizeof (*range)); - if (!range) - return FALSE; - - range->next = *range_ptr; - range->min_addend = addend; - range->max_addend = addend; - - *range_ptr = range; - entry->num_pages++; - g1->page_gotno++; - g2->page_gotno++; - return TRUE; - } - - /* Remember how many pages the old range contributed. */ - old_pages = mips_elf_pages_for_range (range); - - /* Update the ranges. */ - if (addend < range->min_addend) - range->min_addend = addend; - else if (addend > range->max_addend) - { - if (range->next && addend >= range->next->min_addend - 0xffff) - { - old_pages += mips_elf_pages_for_range (range->next); - range->max_addend = range->next->max_addend; - range->next = range->next->next; - } - else - range->max_addend = addend; - } - - /* Record any change in the total estimate. */ - new_pages = mips_elf_pages_for_range (range); - if (old_pages != new_pages) - { - entry->num_pages += new_pages - old_pages; - g1->page_gotno += new_pages - old_pages; - g2->page_gotno += new_pages - old_pages; - } - return TRUE; } @@ -3875,17 +4078,19 @@ mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info, } } -/* A htab_traverse callback for GOT entries. Set boolean *DATA to true - if the GOT entry is for an indirect or warning symbol. */ +/* A htab_traverse callback for GOT entries, with DATA pointing to a + mips_elf_traverse_got_arg structure. Count the number of GOT + entries and TLS relocs. Set DATA->value to true if we need + to resolve indirect or warning symbols and then recreate the GOT. */ static int mips_elf_check_recreate_got (void **entryp, void *data) { struct mips_got_entry *entry; - bfd_boolean *must_recreate; + struct mips_elf_traverse_got_arg *arg; entry = (struct mips_got_entry *) *entryp; - must_recreate = (bfd_boolean *) data; + arg = (struct mips_elf_traverse_got_arg *) data; if (entry->abfd != NULL && entry->symndx == -1) { struct mips_elf_link_hash_entry *h; @@ -3894,27 +4099,28 @@ mips_elf_check_recreate_got (void **entryp, void *data) if (h->root.root.type == bfd_link_hash_indirect || h->root.root.type == bfd_link_hash_warning) { - *must_recreate = TRUE; + arg->value = TRUE; return 0; } } + mips_elf_count_got_entry (arg->info, arg->g, entry); return 1; } -/* A htab_traverse callback for GOT entries. Add all entries to - hash table *DATA, converting entries for indirect and warning - symbols into entries for the target symbol. Set *DATA to null - on error. */ +/* A htab_traverse callback for GOT entries, with DATA pointing to a + mips_elf_traverse_got_arg structure. Add all entries to DATA->g, + converting entries for indirect and warning symbols into entries + for the target symbol. Set DATA->g to null on error. */ static int mips_elf_recreate_got (void **entryp, void *data) { - htab_t *new_got; struct mips_got_entry new_entry, *entry; + struct mips_elf_traverse_got_arg *arg; void **slot; - new_got = (htab_t *) data; entry = (struct mips_got_entry *) *entryp; + arg = (struct mips_elf_traverse_got_arg *) data; if (entry->abfd != NULL && entry->symndx == -1 && (entry->d.h->root.root.type == bfd_link_hash_indirect @@ -3934,10 +4140,10 @@ mips_elf_recreate_got (void **entryp, void *data) || h->root.root.type == bfd_link_hash_warning); entry->d.h = h; } - slot = htab_find_slot (*new_got, entry, INSERT); + slot = htab_find_slot (arg->g->got_entries, entry, INSERT); if (slot == NULL) { - *new_got = NULL; + arg->g = NULL; return 0; } if (*slot == NULL) @@ -3947,89 +4153,309 @@ mips_elf_recreate_got (void **entryp, void *data) entry = bfd_alloc (entry->abfd, sizeof (*entry)); if (!entry) { - *new_got = NULL; + arg->g = NULL; return 0; } *entry = new_entry; } *slot = entry; + mips_elf_count_got_entry (arg->info, arg->g, entry); } return 1; } -/* If any entries in G->got_entries are for indirect or warning symbols, - replace them with entries for the target symbol. */ +/* Return the maximum number of GOT page entries required for RANGE. */ + +static bfd_vma +mips_elf_pages_for_range (const struct mips_got_page_range *range) +{ + return (range->max_addend - range->min_addend + 0x1ffff) >> 16; +} + +/* Record that G requires a page entry that can reach SEC + ADDEND. */ static bfd_boolean -mips_elf_resolve_final_got_entries (struct mips_got_info *g) +mips_elf_record_got_page_entry (struct mips_elf_traverse_got_arg *arg, + asection *sec, bfd_signed_vma addend) { - bfd_boolean must_recreate; - htab_t new_got; + 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; - must_recreate = FALSE; - htab_traverse (g->got_entries, mips_elf_check_recreate_got, &must_recreate); - if (must_recreate) + /* Find the mips_got_page_entry hash table entry for this section. */ + lookup.sec = sec; + loc = htab_find_slot (g->got_page_entries, &lookup, INSERT); + if (loc == NULL) + return FALSE; + + /* Create a mips_got_page_entry if this is the first time we've + seen the section. */ + entry = (struct mips_got_page_entry *) *loc; + if (!entry) { - 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) + entry = bfd_zalloc (arg->info->output_bfd, sizeof (*entry)); + if (!entry) return FALSE; - htab_delete (g->got_entries); - g->got_entries = new_got; + entry->sec = sec; + *loc = entry; } - return TRUE; -} - -/* 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) -{ - struct bfd_link_info *info; - struct mips_elf_link_hash_table *htab; - struct mips_got_info *g; + /* Skip over ranges whose maximum extent cannot share a page entry + with ADDEND. */ + range_ptr = &entry->ranges; + while (*range_ptr && addend > (*range_ptr)->max_addend + 0xffff) + range_ptr = &(*range_ptr)->next; - info = (struct bfd_link_info *) data; - htab = mips_elf_hash_table (info); - g = htab->got_info; - if (h->global_got_area != GGA_NONE) + /* If we scanned to the end of the list, or found a range whose + minimum extent cannot share a page entry with ADDEND, create + a new singleton range. */ + range = *range_ptr; + if (!range || addend < range->min_addend - 0xffff) { - /* 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. */ - h->global_got_area = GGA_NONE; - else if (htab->is_vxworks - && h->got_only_for_calls - && h->root.plt.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 if (h->global_got_area == GGA_RELOC_ONLY) - { - g->reloc_only_gotno++; - g->global_gotno++; - } + range = bfd_zalloc (arg->info->output_bfd, sizeof (*range)); + if (!range) + return FALSE; + + range->next = *range_ptr; + range->min_addend = addend; + range->max_addend = addend; + + *range_ptr = range; + entry->num_pages++; + g->page_gotno++; + return TRUE; + } + + /* Remember how many pages the old range contributed. */ + old_pages = mips_elf_pages_for_range (range); + + /* Update the ranges. */ + if (addend < range->min_addend) + range->min_addend = addend; + else if (addend > range->max_addend) + { + if (range->next && addend >= range->next->min_addend - 0xffff) + { + old_pages += mips_elf_pages_for_range (range->next); + range->max_addend = range->next->max_addend; + range->next = range->next->next; + } + else + range->max_addend = addend; + } + + /* Record any change in the total estimate. */ + new_pages = mips_elf_pages_for_range (range); + if (old_pages != new_pages) + { + entry->num_pages += new_pages - old_pages; + g->page_gotno += new_pages - old_pages; + } + + return TRUE; +} + +/* A htab_traverse callback for which *REFP points to a mips_got_page_ref + and for which DATA points to a mips_elf_traverse_got_arg. Work out + whether the page reference described by *REFP needs a GOT page entry, + and record that entry in DATA->g if so. Set DATA->g to null on failure. */ + +static bfd_boolean +mips_elf_resolve_got_page_ref (void **refp, void *data) +{ + struct mips_got_page_ref *ref; + struct mips_elf_traverse_got_arg *arg; + struct mips_elf_link_hash_table *htab; + asection *sec; + bfd_vma addend; + + ref = (struct mips_got_page_ref *) *refp; + arg = (struct mips_elf_traverse_got_arg *) data; + htab = mips_elf_hash_table (arg->info); + + if (ref->symndx < 0) + { + struct mips_elf_link_hash_entry *h; + + /* Global GOT_PAGEs decay to GOT_DISP and so don't need page entries. */ + h = ref->u.h; + if (!SYMBOL_REFERENCES_LOCAL (arg->info, &h->root)) + return 1; + + /* Ignore undefined symbols; we'll issue an error later if + appropriate. */ + if (!((h->root.root.type == bfd_link_hash_defined + || h->root.root.type == bfd_link_hash_defweak) + && h->root.root.u.def.section)) + return 1; + + sec = h->root.root.u.def.section; + addend = h->root.root.u.def.value + ref->addend; + } + else + { + Elf_Internal_Sym *isym; + + /* Read in the symbol. */ + isym = bfd_sym_from_r_symndx (&htab->sym_cache, ref->u.abfd, + ref->symndx); + if (isym == NULL) + { + arg->g = NULL; + return 0; + } + + /* Get the associated input section. */ + sec = bfd_section_from_elf_index (ref->u.abfd, isym->st_shndx); + if (sec == NULL) + { + arg->g = NULL; + return 0; + } + + /* If this is a mergable section, work out the section and offset + of the merged data. For section symbols, the addend specifies + of the offset _of_ the first byte in the data, otherwise it + specifies the offset _from_ the first byte. */ + if (sec->flags & SEC_MERGE) + { + void *secinfo; + + secinfo = elf_section_data (sec)->sec_info; + if (ELF_ST_TYPE (isym->st_info) == STT_SECTION) + addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo, + isym->st_value + ref->addend); + else + addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo, + isym->st_value) + ref->addend; + } + else + addend = isym->st_value + ref->addend; + } + if (!mips_elf_record_got_page_entry (arg, sec, addend)) + { + arg->g = NULL; + return 0; + } + return 1; +} + +/* If any entries in G->got_entries are for indirect or warning symbols, + replace them with entries for the target symbol. 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 bfd_link_info *info, + struct mips_got_info *g) +{ + struct mips_elf_traverse_got_arg tga; + struct mips_got_info oldg; + + oldg = *g; + + tga.info = info; + tga.g = g; + tga.value = FALSE; + htab_traverse (g->got_entries, mips_elf_check_recreate_got, &tga); + if (tga.value) + { + *g = oldg; + g->got_entries = htab_create (htab_size (oldg.got_entries), + mips_elf_got_entry_hash, + mips_elf_got_entry_eq, NULL); + if (!g->got_entries) + return FALSE; + + htab_traverse (oldg.got_entries, mips_elf_recreate_got, &tga); + if (!tga.g) + return FALSE; + + htab_delete (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; +} + +/* 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) +{ + struct bfd_link_info *info; + struct mips_elf_link_hash_table *htab; + struct mips_got_info *g; + + info = (struct bfd_link_info *) data; + htab = mips_elf_hash_table (info); + g = htab->got_info; + if (h->global_got_area != GGA_NONE) + { + /* Make a final decision about whether the symbol belongs in the + 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.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 if (h->global_got_area == GGA_RELOC_ONLY) + { + g->reloc_only_gotno++; + g->global_gotno++; + } } return 1; } @@ -4148,17 +4574,12 @@ 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_traverse_got_arg tga; unsigned int estimate; int result; - if (!mips_elf_resolve_final_got_entries (g)) + if (!mips_elf_resolve_final_got_entries (arg->info, g)) return FALSE; - tga.info = arg->info; - tga.g = g; - htab_traverse (g->got_entries, mips_elf_count_got_entries, &tga); - /* Work out the number of page, local and TLS entries. */ estimate = arg->max_pages; if (estimate > g->page_gotno) @@ -4239,12 +4660,10 @@ mips_elf_initialize_tls_index (void **entryp, void *data) { struct mips_got_entry *entry; struct mips_elf_traverse_got_arg *arg; - unsigned char tls_type; /* We're only interested in TLS symbols. */ entry = (struct mips_got_entry *) *entryp; - tls_type = (entry->tls_type & GOT_TLS_TYPE); - if (tls_type == 0) + if (entry->tls_type == GOT_TLS_NONE) return 1; arg = (struct mips_elf_traverse_got_arg *) data; @@ -4255,7 +4674,7 @@ mips_elf_initialize_tls_index (void **entryp, void *data) } /* Account for the entries we've just allocated. */ - arg->g->tls_assigned_gotno += mips_tls_got_entries (tls_type); + arg->g->tls_assigned_gotno += mips_tls_got_entries (entry->tls_type); return 1; } @@ -4295,12 +4714,12 @@ mips_elf_set_global_gotidx (void **entryp, void *data) && entry->symndx == -1 && entry->d.h->global_got_area != GGA_NONE) { - if (!mips_elf_set_gotidx (entryp, arg->value * arg->g->assigned_gotno)) + if (!mips_elf_set_gotidx (entryp, arg->value * arg->g->assigned_low_gotno)) { arg->g = NULL; return 0; } - arg->g->assigned_gotno += 1; + arg->g->assigned_low_gotno += 1; if (arg->info->shared || (elf_hash_table (arg->info)->dynamic_sections_created @@ -4395,7 +4814,7 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info, /* Try to merge the GOTs of input bfds together, as long as they don't seem to exceed the maximum GOT size, choosing one of them to be the primary GOT. */ - for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link_next) + for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link.next) { gg = mips_elf_bfd_got (ibfd, FALSE); if (gg && !mips_elf_merge_got (ibfd, gg, &got_per_bfd_arg)) @@ -4433,7 +4852,7 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info, 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 @@ -4456,9 +4875,10 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info, 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 @@ -4497,21 +4917,21 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info, /* Assign offsets to global GOT entries and count how many relocations they need. */ - save_assign = g->assigned_gotno; - g->assigned_gotno = g->local_gotno; + 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_gotno == g->local_gotno + g->global_gotno); - g->assigned_gotno = save_assign; + BFD_ASSERT (g->assigned_low_gotno == g->local_gotno + g->global_gotno); + g->assigned_low_gotno = save_assign; if (info->shared) { - g->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); @@ -4708,6 +5128,7 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info) 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 @@ -4765,6 +5186,8 @@ mips_elf_relocation_needs_la25_stub (bfd *input_bfd, int r_type, { case R_MIPS_26: case R_MIPS_PC16: + 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: @@ -5006,10 +5429,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, } 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 @@ -5027,8 +5447,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, && 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 @@ -5036,7 +5456,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, 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 @@ -5060,19 +5480,19 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, /* 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 @@ -5111,6 +5531,31 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, 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) @@ -5136,10 +5581,7 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, && (target_is_16_bit_code_p || target_is_micromips_code_p)))); - local_p = (h == NULL - || (h->got_only_for_calls - ? SYMBOL_CALLS_LOCAL (info, &h->root) - : 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); @@ -5272,7 +5714,9 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, 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; @@ -5344,8 +5788,10 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, 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)); @@ -5482,7 +5928,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, to them before. */ if (was_local_p) value += gp0; - overflowed_p = mips_elf_overflow_p (value, 16); + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + overflowed_p = mips_elf_overflow_p (value, 16); break; case R_MIPS16_GOT16: @@ -5530,36 +5977,125 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd, case R_MIPS_PC16: case R_MIPS_GNU_REL16_S2: - value = symbol + _bfd_mips_elf_sign_extend (addend, 18) - p; - overflowed_p = mips_elf_overflow_p (value, 18); + if (howto->partial_inplace) + addend = _bfd_mips_elf_sign_extend (addend, 18); + + if ((symbol + addend) & 3) + return bfd_reloc_outofrange; + + value = symbol + addend - p; + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + 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; + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + 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; + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + 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); + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + 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; + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + 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); + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + 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; - overflowed_p = mips_elf_overflow_p (value, 8); + if (howto->partial_inplace) + addend = _bfd_mips_elf_sign_extend (addend, 8); + value = symbol + addend - p; + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + 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; - overflowed_p = mips_elf_overflow_p (value, 11); + if (howto->partial_inplace) + addend = _bfd_mips_elf_sign_extend (addend, 11); + value = symbol + addend - p; + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + 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; - overflowed_p = mips_elf_overflow_p (value, 17); + if (howto->partial_inplace) + addend = _bfd_mips_elf_sign_extend (addend, 17); + value = symbol + addend - p; + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + 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); - overflowed_p = mips_elf_overflow_p (value, 25); + if (howto->partial_inplace) + addend = _bfd_mips_elf_sign_extend (addend, 25); + value = symbol + addend - ((p | 3) ^ 3); + if (was_local_p || h->root.root.type != bfd_link_hash_undefweak) + overflowed_p = mips_elf_overflow_p (value, 25); value >>= howto->rightshift; value &= howto->dst_mask; break; @@ -6102,6 +6638,12 @@ _bfd_elf_mips_mach (flagword flags) 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; } } @@ -6254,7 +6796,7 @@ _bfd_mips_elf_symbol_processing (bfd *abfd, asymbol *asym) && (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 @@ -6547,6 +7089,11 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd, 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_")) @@ -6577,6 +7124,20 @@ _bfd_mips_elf_section_from_shdr (bfd *abfd, 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 @@ -6743,6 +7304,11 @@ _bfd_mips_elf_fake_sections (bfd *abfd, Elf_Internal_Shdr *hdr, asection *sec) 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_")) { @@ -6856,7 +7422,7 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info, 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; @@ -6873,11 +7439,11 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info, /* 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; @@ -6890,14 +7456,14 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info, /* 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; @@ -6914,11 +7480,11 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info, /* 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; @@ -6931,7 +7497,7 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info, /* 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: @@ -7092,20 +7658,24 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) /* Change alignments of some sections. */ s = bfd_get_linker_section (abfd, ".hash"); 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_linker_section (abfd, ".dynsym"); 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_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)); + (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) @@ -7155,7 +7725,7 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) } /* 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; @@ -7175,34 +7745,10 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) || !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; } @@ -7274,6 +7820,8 @@ mips_elf_add_lo16_rel_addend (bfd *abfd, 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; @@ -7330,8 +7878,27 @@ mips_elf_get_section_contents (bfd *abfd, asection *sec, bfd_byte **contents) 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, @@ -7437,7 +8004,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* 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; @@ -7451,11 +8018,11 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, 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 @@ -7562,7 +8129,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, /* 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; @@ -7576,11 +8143,11 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, 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 @@ -7624,6 +8191,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, 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); @@ -7641,21 +8210,45 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *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_MIPS_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_GOT_HI16: case R_MIPS_GOT_LO16: case R_MIPS_GOT_PAGE: @@ -7665,14 +8258,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_MIPS_TLS_GD: case R_MIPS_TLS_LDM: case R_MIPS16_GOT16: - case R_MIPS16_CALL16: 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: @@ -7693,12 +8282,27 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, 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: @@ -7725,51 +8329,41 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, 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. */ @@ -7849,21 +8443,6 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, 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: @@ -7892,10 +8471,24 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, } 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: @@ -8029,27 +8622,27 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, 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; @@ -8431,11 +9024,16 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info, && !(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. @@ -8451,40 +9049,107 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info, 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 - += get_elf_backend_data (dynobj)->got_header_size; + 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); + } } - /* 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 == 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; + } + + 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)); @@ -8567,7 +9232,7 @@ bfd_boolean _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; @@ -8575,9 +9240,14 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd, 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; @@ -8616,27 +9286,25 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *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; /* 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 (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link_next) + for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link.next) { asection *subsection; @@ -8661,17 +9329,13 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info) sections. Is 5 enough? */ page_gotno = (loadable_size >> 16) + 5; - /* Choose the smaller of the two estimates; both are intended to be + /* Choose the smaller of the two page estimates; both are intended to be conservative. */ if (page_gotno > g->page_gotno) page_gotno = g->page_gotno; g->local_gotno += page_gotno; - - /* Count the number of GOT entries and TLS relocs. */ - tga.info = info; - tga.g = g; - htab_traverse (g->got_entries, mips_elf_count_got_entries, &tga); + 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); @@ -8680,20 +9344,7 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info) /* 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; @@ -8702,7 +9353,7 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info) { /* 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) + 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); @@ -8718,6 +9369,10 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info) BFD_ASSERT (g->tls_assigned_gotno == g->global_gotno + g->local_gotno + g->tls_gotno); + /* Each VxWorks GOT entry needs an explicit relocation. */ + if (htab->is_vxworks && info->shared) + g->relocs += g->global_gotno + g->local_gotno - htab->reserved_gotno; + /* Allocate room for the TLS relocations. */ if (g->relocs) mips_elf_allocate_dynamic_relocations (dynobj, info, g->relocs); @@ -8751,29 +9406,62 @@ mips_elf_estimate_stub_size (bfd *output_bfd, struct bfd_link_info *info) 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; @@ -8782,22 +9470,97 @@ mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void **data) /* 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. */ @@ -8829,18 +9592,68 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, = (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; } } @@ -9190,7 +10003,7 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, 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; @@ -9447,6 +10260,13 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, (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: @@ -9679,68 +10499,162 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, 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 @@ -9749,21 +10663,36 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, 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 @@ -9772,35 +10701,95 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, 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; @@ -9809,7 +10798,9 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, 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 @@ -9854,7 +10845,7 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, 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) { @@ -9962,13 +10953,18 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, if (IRIX_COMPAT (output_bfd) == ict_irix6) mips_elf_irix6_finish_dynamic_symbol (output_bfd, name, sym); - /* Keep dynamic MIPS16 symbols odd. This allows the dynamic linker to - treat MIPS16 symbols like any other. */ + /* Keep dynamic compressed symbols odd. This allows the dynamic linker + to treat compressed symbols like any other. */ if (ELF_ST_IS_MIPS16 (sym->st_other)) { BFD_ASSERT (sym->st_value & 1); sym->st_other -= STO_MIPS16; } + else if (ELF_ST_IS_MICROMIPS (sym->st_other)) + { + BFD_ASSERT (sym->st_value & 1); + sym->st_other -= STO_MICROMIPS; + } return TRUE; } @@ -9992,30 +10988,32 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd, 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_. */ @@ -10023,20 +11021,21 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd, /* 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 { @@ -10047,7 +11046,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd, 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); @@ -10056,12 +11055,12 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd, 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>). */ @@ -10079,7 +11078,8 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd, } /* 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; @@ -10146,7 +11146,7 @@ _bfd_mips_vxworks_finish_dynamic_symbol (bfd *output_bfd, /* 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; @@ -10161,8 +11161,12 @@ mips_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info) 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 @@ -10177,17 +11181,65 @@ mips_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info) /* 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; -/* Install the PLT header for a VxWorks executable and finalize the + 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 contents of .rela.plt.unloaded. */ static void @@ -10569,10 +11621,14 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, 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, @@ -10706,7 +11762,8 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd, else { BFD_ASSERT (!info->shared); - mips_finish_exec_plt (output_bfd, info); + if (!mips_finish_exec_plt (output_bfd, info)) + return FALSE; } } return TRUE; @@ -10805,7 +11862,7 @@ mips_set_isa_flags (bfd *abfd) 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: @@ -10830,12 +11887,24 @@ mips_set_isa_flags (bfd *abfd) 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; @@ -10944,6 +12013,10 @@ _bfd_mips_elf_additional_program_headers (bfd *abfd, 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, @@ -10980,7 +12053,7 @@ _bfd_mips_elf_modify_segment_map (bfd *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) @@ -10995,7 +12068,38 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd, 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)) @@ -11025,7 +12129,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd, { 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)) @@ -11055,7 +12159,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd, && 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) @@ -11081,7 +12185,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd, } /* 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) @@ -11095,23 +12199,11 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd, /* 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 @@ -11202,7 +12294,7 @@ _bfd_mips_elf_modify_segment_map (bfd *abfd, && !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) @@ -11301,6 +12393,36 @@ _bfd_mips_elf_gc_sweep_hook (bfd *abfd ATTRIBUTE_UNUSED, 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; +} /* Copy data from a MIPS ELF indirect symbol to its direct symbol, hiding the old indirect symbol. Process additional relocation @@ -11408,6 +12530,8 @@ _bfd_mips_elf_discard_info (bfd *abfd, struct elf_reloc_cookie *cookie, 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; } @@ -11478,24 +12602,26 @@ struct mips_elf_find_line }; 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, NULL, 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"); @@ -11513,7 +12639,7 @@ _bfd_mips_elf_find_nearest_line (bfd *abfd, asection *section, 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; @@ -11551,7 +12677,7 @@ _bfd_mips_elf_find_nearest_line (bfd *abfd, asection *section, 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 @@ -11574,9 +12700,9 @@ _bfd_mips_elf_find_nearest_line (bfd *abfd, asection *section, /* 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 @@ -12224,6 +13350,7 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec, 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; @@ -12506,7 +13633,13 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec, && 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; @@ -12519,15 +13652,15 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec, 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) { @@ -12547,7 +13680,8 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec, /* 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))) @@ -12574,7 +13708,8 @@ _bfd_mips_elf_relax_section (bfd *abfd, asection *sec, } /* 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)) @@ -12699,6 +13834,8 @@ _bfd_mips_elf_link_hash_table_create (bfd *abfd) free (ret); return NULL; } + ret->root.init_plt_refcount.plist = NULL; + ret->root.init_plt_offset.plist = NULL; return &ret->root.root; } @@ -12730,7 +13867,189 @@ _bfd_mips_elf_use_plts_and_copy_relocs (struct bfd_link_info *info) { 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; +} +/* 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_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. */ @@ -12741,7 +14060,7 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) 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; @@ -12823,12 +14142,46 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) /* 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); @@ -13313,6 +14666,24 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) /* 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; @@ -13366,7 +14737,8 @@ _bfd_mips_elf_final_link (bfd *abfd, struct bfd_link_info *info) /* Structure for saying that BFD machine EXTENSION extends BASE. */ -struct mips_mach_extension { +struct mips_mach_extension +{ unsigned long extension, base; }; @@ -13374,17 +14746,18 @@ struct mips_mach_extension { /* 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_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 }, @@ -13468,21 +14841,6 @@ mips_mach_extends_p (unsigned long base, unsigned long extension) } -/* 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 @@ -13491,12 +14849,18 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd) 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 != 0) + 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) { /* This is the first object. Copy the attributes. */ @@ -13514,175 +14878,111 @@ mips_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd) 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) - 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 %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, "-mdouble-float", "-msingle-float"); - break; - - case 3: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float"); - break; - - case 4: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, - "-mdouble-float", "-mips32r2 -mfp64"); - break; - - default: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), " - "%B uses unknown floating point ABI %d"), - obfd, abi_fp_bfd, ibfd, - "-mdouble-float", in_attr[Tag_GNU_MIPS_ABI_FP].i); - break; - } - break; - - case 2: - switch (in_attr[Tag_GNU_MIPS_ABI_FP].i) - { - case 1: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, "-msingle-float", "-mdouble-float"); - break; - - case 3: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float"); - break; - - case 4: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, - "-msingle-float", "-mips32r2 -mfp64"); - break; - - default: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), " - "%B uses unknown floating point ABI %d"), - obfd, abi_fp_bfd, ibfd, - "-msingle-float", in_attr[Tag_GNU_MIPS_ABI_FP].i); - break; - } - break; - - case 3: - switch (in_attr[Tag_GNU_MIPS_ABI_FP].i) - { - case 1: - case 2: - case 4: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, "-msoft-float", "-mhard-float"); - break; - - default: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), " - "%B uses unknown floating point ABI %d"), - obfd, abi_fp_bfd, ibfd, - "-msoft-float", in_attr[Tag_GNU_MIPS_ABI_FP].i); - break; - } - break; + int out_fp, in_fp; - case 4: - switch (in_attr[Tag_GNU_MIPS_ABI_FP].i) - { - case 1: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, - "-mips32r2 -mfp64", "-mdouble-float"); - break; - - case 2: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, - "-mips32r2 -mfp64", "-msingle-float"); - break; - - case 3: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, "-mhard-float", "-msoft-float"); - 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: - _bfd_error_handler - (_("Warning: %B uses %s (set by %B), " - "%B uses unknown floating point ABI %d"), - obfd, abi_fp_bfd, ibfd, - "-mips32r2 -mfp64", in_attr[Tag_GNU_MIPS_ABI_FP].i); - break; - } + /* 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; default: - switch (in_attr[Tag_GNU_MIPS_ABI_FP].i) + switch (in_attr[Tag_GNU_MIPS_ABI_MSA].i) { - case 1: - _bfd_error_handler - (_("Warning: %B uses unknown floating point ABI %d " - "(set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, - out_attr[Tag_GNU_MIPS_ABI_FP].i, "-mdouble-float"); - break; - - case 2: - _bfd_error_handler - (_("Warning: %B uses unknown floating point ABI %d " - "(set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, - out_attr[Tag_GNU_MIPS_ABI_FP].i, "-msingle-float"); - break; - - case 3: + case Val_GNU_MIPS_ABI_MSA_128: _bfd_error_handler - (_("Warning: %B uses unknown floating point ABI %d " + (_("Warning: %B uses unknown MSA ABI %d " "(set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, - out_attr[Tag_GNU_MIPS_ABI_FP].i, "-msoft-float"); - break; - - case 4: - _bfd_error_handler - (_("Warning: %B uses unknown floating point ABI %d " - "(set by %B), %B uses %s"), - obfd, abi_fp_bfd, ibfd, - out_attr[Tag_GNU_MIPS_ABI_FP].i, "-mips32r2 -mfp64"); - break; + obfd, abi_msa_bfd, ibfd, + out_attr[Tag_GNU_MIPS_ABI_MSA].i, "-mmsa"); + break; default: _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_attr[Tag_GNU_MIPS_ABI_FP].i, - in_attr[Tag_GNU_MIPS_ABI_FP].i); + (_("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; } - break; } } @@ -13703,6 +15003,7 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) 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)) @@ -13724,17 +15025,98 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *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]; @@ -13746,11 +15128,42 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) 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; @@ -13773,30 +15186,6 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) 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) @@ -13837,6 +15226,9 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) 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 @@ -13908,6 +15300,34 @@ _bfd_mips_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd) 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) { @@ -14039,6 +15459,190 @@ _bfd_mips_elf_get_target_dtag (bfd_vma dtag) } } +/* 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_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) { @@ -14087,6 +15691,10 @@ _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]")); @@ -14099,6 +15707,12 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr) 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 @@ -14121,6 +15735,30 @@ _bfd_mips_elf_print_private_bfd_data (bfd *abfd, void *ptr) 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; } @@ -14186,6 +15824,246 @@ _bfd_mips_elf_plt_sym_val (bfd_vma i, const asection *plt, + 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) { @@ -14201,4 +16079,10 @@ _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; }