diff options
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 63 | ||||
-rw-r--r-- | bfd/elf32-mips.c | 5 | ||||
-rw-r--r-- | bfd/elfxx-mips.c | 1122 | ||||
-rw-r--r-- | bfd/elfxx-mips.h | 2 |
4 files changed, 1008 insertions, 184 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 19c4c5f..967d2aa 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,66 @@ +2013-06-24 Maciej W. Rozycki <macro@codesourcery.com> + + * elfxx-mips.h (_bfd_mips_elf_get_synthetic_symtab): New + prototype. + * elf32-mips.c (elf_backend_plt_sym_val): Remove macro. + (bfd_elf32_get_synthetic_symtab): New macro. + * elfxx-mips.c (plt_entry): New structure. + (mips_elf_link_hash_entry): Add use_plt_entry member. + (mips_elf_link_hash_table): Rename plt_entry_size member to + plt_mips_entry_size. Add plt_comp_entry_size, plt_mips_offset, + plt_comp_offset, plt_got_index entries and plt_header_is_comp + members. + (STUB_LW_MICROMIPS, STUB_MOVE_MICROMIPS): New macros. + (STUB_LUI_MICROMIPS, STUB_JALR_MICROMIPS): Likewise. + (STUB_ORI_MICROMIPS, STUB_LI16U_MICROMIPS): Likewise. + (STUB_LI16S_MICROMIPS): Likewise. + (MICROMIPS_FUNCTION_STUB_NORMAL_SIZE): Likewise. + (MICROMIPS_FUNCTION_STUB_BIG_SIZE): Likewise. + (micromips_o32_exec_plt0_entry): New variable. + (mips16_o32_exec_plt_entry): Likewise. + (micromips_o32_exec_plt_entry): Likewise. + (mips_elf_link_hash_newfunc): Initialize use_plt_entry. + (mips_elf_output_extsym): Update to use gotplt_union's plist + member rather than offset. + (mips_elf_gotplt_index): Likewise. Remove the VxWorks + restriction. Use MIPS_ELF_GOT_SIZE to calculate GOT address. + (mips_elf_count_got_symbols): Update to use gotplt_union's plist + member rather than offset. + (mips_elf_calculate_relocation): Handle MIPS16/microMIPS PLT + entries. + (_bfd_mips_elf_create_dynamic_sections): Don't set PLT sizes + here. + (mips_elf_make_plt_record): New function. + (_bfd_mips_elf_check_relocs): Update comment. Record occurences + of JAL relocations that might need a PLT entry. + (_bfd_mips_elf_adjust_dynamic_symbol): Update to use + gotplt_union's plist member rather than offset. Set individual + PLT entry sizes here. Handle MIPS16/microMIPS PLT entries. + Don't set the symbol's value in the symbol table for PLT + references here. Don't set the PLT or PLT GOT section sizes + here. + (mips_elf_estimate_stub_size): Handle microMIPS stubs. + (mips_elf_allocate_lazy_stub): Likewise. + (mips_elf_lay_out_lazy_stubs): Likewise. Define a _MIPS_STUBS_ + magic symbol. + (mips_elf_set_plt_sym_value): New function. + (_bfd_mips_elf_size_dynamic_sections): Set PLT header size and + PLT and PLT GOT section sizes here. Set the symbol values in + the symbol table for PLT references here. Handle microMIPS + annotation of the _PROCEDURE_LINKAGE_TABLE_ magic symbol. + (_bfd_mips_elf_finish_dynamic_symbol): Update to use + gotplt_union's plist member rather than offset. Handle + MIPS16/microMIPS PLT entries. Handle microMIPS stubs. + (_bfd_mips_vxworks_finish_dynamic_symbol): Update to use + gotplt_union's plist member rather than offset. Use + MIPS_ELF_GOT_SIZE to calculate GOT address. + (mips_finish_exec_plt): Handle microMIPS PLT. Return status. + (_bfd_mips_elf_finish_dynamic_sections): Handle result from + mips_finish_exec_plt. + (_bfd_mips_elf_link_hash_table_create): Update to use + gotplt_union's plist member rather than offset. + (_bfd_mips_elf_get_synthetic_symtab): New function. + 2013-06-24 Wawa <caojinyu@msn.com> PR 15657 diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c index c3c58c6..0e62708 100644 --- a/bfd/elf32-mips.c +++ b/bfd/elf32-mips.c @@ -2366,7 +2366,6 @@ static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = { #define elf_backend_default_use_rela_p 0 #define elf_backend_sign_extend_vma TRUE #define elf_backend_plt_readonly 1 -#define elf_backend_plt_sym_val _bfd_mips_elf_plt_sym_val #define elf_backend_discard_info _bfd_mips_elf_discard_info #define elf_backend_ignore_discarded_relocs \ @@ -2378,6 +2377,7 @@ static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = { mips_elf_is_local_label_name #define bfd_elf32_bfd_is_target_special_symbol \ _bfd_mips_elf_is_target_special_symbol +#define bfd_elf32_get_synthetic_symtab _bfd_mips_elf_get_synthetic_symtab #define bfd_elf32_find_nearest_line _bfd_mips_elf_find_nearest_line #define bfd_elf32_find_inliner_info _bfd_mips_elf_find_inliner_info #define bfd_elf32_new_section_hook _bfd_mips_elf_new_section_hook @@ -2505,7 +2505,6 @@ mips_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker) #define elf_backend_default_use_rela_p 1 #undef elf_backend_got_header_size #define elf_backend_got_header_size (4 * 3) -#undef elf_backend_plt_sym_val #undef elf_backend_finish_dynamic_symbol #define elf_backend_finish_dynamic_symbol \ @@ -2531,4 +2530,6 @@ mips_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker) #undef elf_backend_symbol_processing /* NOTE: elf_backend_rela_normal is not defined for MIPS. */ +#undef bfd_elf32_get_synthetic_symtab + #include "elf32-target.h" diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index a3b50f3..dd61049 100644 --- a/bfd/elfxx-mips.c +++ b/bfd/elfxx-mips.c @@ -317,6 +317,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. */ @@ -381,6 +407,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. */ @@ -435,8 +464,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; @@ -466,6 +507,9 @@ struct mips_elf_link_hash_table /* 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. */ @@ -854,8 +898,28 @@ 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_LUI_MICROMIPS(VAL) \ + (0x41b80000 + (VAL)) /* lui t8,VAL */ +#define STUB_JALR_MICROMIPS 0x45d9 /* jalr 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 /* The name of the dynamic interpreter. This is put in the .interp section. */ @@ -967,7 +1031,26 @@ 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 subsequent standard PLT entries. */ static const bfd_vma mips_exec_plt_entry[] = { 0x3c0f0000, /* lui $15, %hi(.got.plt entry) */ @@ -976,6 +1059,30 @@ static const bfd_vma mips_exec_plt_entry[] = 0x03200008 /* 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 the first PLT entry in a VxWorks executable. */ static const bfd_vma mips_vxworks_exec_plt0_entry[] = { @@ -1114,6 +1221,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; @@ -2728,6 +2836,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; @@ -2737,7 +2847,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 @@ -3213,25 +3323,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 @@ -4198,7 +4303,7 @@ mips_elf_count_got_symbols (struct mips_elf_link_hash_entry *h, void *data) h->global_got_area = GGA_NONE; else if (htab->is_vxworks && h->got_only_for_calls - && h->root.plt.offset != MINUS_ONE) + && h->root.plt.plist->mips_offset != MINUS_ONE) /* On VxWorks, calls can refer directly to the .got.plt entry; they don't need entries in the regular GOT. .got.plt entries will be allocated by _bfd_mips_elf_adjust_dynamic_symbol. */ @@ -5177,10 +5282,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 @@ -5231,16 +5333,16 @@ 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 && mips_elf_tdata (input_bfd)->local_call_stubs != NULL && mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx] != NULL)) - && !target_is_16_bit_code_p) + && ((h != NULL && h->use_plt_entry) || !target_is_16_bit_code_p)) { if (local_p) sec = mips_elf_tdata (input_bfd)->local_call_stubs[r_symndx]; @@ -5282,6 +5384,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) @@ -7350,34 +7477,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; } @@ -7505,8 +7608,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, @@ -8209,6 +8331,28 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, 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; + } + /* 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 @@ -8611,11 +8755,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. @@ -8631,40 +8780,100 @@ _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 (micromips_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 (mips16_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)); @@ -8915,29 +9124,58 @@ 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. */ + if (MICROMIPS_P (output_bfd)) + htab->function_stub_size = (dynsymcount > 0x10000 + ? MICROMIPS_FUNCTION_STUB_BIG_SIZE + : MICROMIPS_FUNCTION_STUB_NORMAL_SIZE); + else + htab->function_stub_size = (dynsymcount > 0x10000 + ? MIPS_FUNCTION_STUB_BIG_SIZE + : MIPS_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) { + 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; @@ -8946,22 +9184,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. */ @@ -8993,18 +9306,66 @@ _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 + 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; } } @@ -9843,68 +10204,145 @@ _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. */ + 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 = 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); + } + else + { + 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); + } } /* 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 @@ -9913,21 +10351,34 @@ _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 = MICROMIPS_FUNCTION_STUB_BIG_SIZE; + else + stub_big_size = MIPS_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 @@ -9936,35 +10387,76 @@ _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; + 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; + } + 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; @@ -9973,7 +10465,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 @@ -10161,30 +10655,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_. */ @@ -10192,20 +10688,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 { @@ -10216,7 +10713,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); @@ -10225,12 +10722,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>). */ @@ -10248,7 +10745,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; @@ -10315,7 +10813,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; @@ -10330,6 +10828,8 @@ 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 if (htab->plt_header_is_comp) + plt_entry = micromips_o32_exec_plt0_entry; else plt_entry = mips_o32_exec_plt0_entry; @@ -10346,14 +10846,49 @@ 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; + + 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 + { + 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 @@ -10875,7 +11410,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; @@ -12868,6 +13404,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; } @@ -14357,6 +14895,226 @@ _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 + { + 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. */ + 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; + } + /* 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) { diff --git a/bfd/elfxx-mips.h b/bfd/elfxx-mips.h index 589f1ce..45ccabb 100644 --- a/bfd/elfxx-mips.h +++ b/bfd/elfxx-mips.h @@ -152,6 +152,8 @@ extern bfd_boolean _bfd_mips_elf_init_stubs asection *(*) (const char *, asection *, asection *)); extern bfd_vma _bfd_mips_elf_plt_sym_val (bfd_vma, const asection *, const arelent *rel); +extern long _bfd_mips_elf_get_synthetic_symtab + (bfd *, long, asymbol **, long, asymbol **, asymbol **); extern void _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info); |