diff options
author | Matthew Fortune <matthew.fortune@mips.com> | 2019-05-13 17:03:19 -0700 |
---|---|---|
committer | Faraz Shahbazker <fshahbazker@wavecomp.com> | 2019-05-21 09:22:28 -0700 |
commit | 3734320dc054bd9f6632607e9e5c901c57450791 (patch) | |
tree | 09ada7b6ef14bf220ce429dbd2d4a0dcd0faa654 /bfd/elfxx-mips.c | |
parent | 6467207116c66ff2c58f8bc35cb15b2596f5c457 (diff) | |
download | gdb-3734320dc054bd9f6632607e9e5c901c57450791.zip gdb-3734320dc054bd9f6632607e9e5c901c57450791.tar.gz gdb-3734320dc054bd9f6632607e9e5c901c57450791.tar.bz2 |
[MIPS] Add generation of PLT entries with compact jumps for MIPS R6
Add a new option to get the linker to emit PLTs that use compact
branches instead of delay slot branches.
bfd/
* elfxx-mips.c (LA25_BC): New macro.
(mips_elf_link_hash_table)<compact_branches>: New field.
(STUB_JALRC): New macro.
(mipsr6_o32_exec_plt0_entry_compact): New array.
(mipsr6_n32_exec_plt0_entry_compact): Likewise.
(mipsr6_n64_exec_plt0_entry_compact): Likewise.
(mipsr6_exec_plt_entry_compact): Likewise.
(mips_elf_create_la25_stub): Use BC instead of J for stubs
when compact_branches is true.
(_bfd_mips_elf_finish_dynamic_symbol): Choose the compact
PLT for MIPSR6 with compact_branches. Do not reorder the
compact branches PLT. Switch the lazy stub for MIPSR6
with compact_branches to use JALRC.
(mips_finish_exec_plt): Choose the compact PLT0 for MIPSR6
when compact_branches is true.
(_bfd_mips_elf_compact_branches): New function.
* elfxx-mips.h (_bfd_mips_elf_compact_branches): New prototype.
ld/
* emultempl/mipself.em (compact_branches): New static variable.
(mips_create_output_section_statements): Call
_bfd_mips_elf_compact_branches.
(PARSE_AND_LIST_PROLOGUE): Add OPTION_COMPACT_BRANCHES and
OPTION_NO_COMPACT_BRANCHES.
(PARSE_AND_LIST_LONGOPTS): Add compact-branches,
no-compact-branches.
(PARSE_AND_LIST_OPTIONS): Add --compact-branches,
--no-compact-branches.
(PARSE_AND_LIST_ARGS_CASES): Handle the above.
* ld.texinfo: Document --compact-branches, --no-compact-branches.
* testsuite/ld-mips-elf/pic-and-nonpic-1-r6.dd: New test.
* testsuite/ld-mips-elf/pic-and-nonpic-1-r6.nd: New test.
* testsuite/ld-mips-elf/pic-and-nonpic-3a-r6.dd: New test.
* testsuite/ld-mips-elf/pic-and-nonpic-3a-r6.gd: New test.
* testsuite/ld-mips-elf/pic-and-nonpic-1a-r6.s: New test source.
* testsuite/ld-mips-elf/pic-and-nonpic-3a-r6.s: New test source.
* testsuite/ld-mips-elf/mips-elf.exp: Run the new tests.
Diffstat (limited to 'bfd/elfxx-mips.c')
-rw-r--r-- | bfd/elfxx-mips.c | 127 |
1 files changed, 115 insertions, 12 deletions
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index 74dadf4..8c1ad72 100644 --- a/bfd/elfxx-mips.c +++ b/bfd/elfxx-mips.c @@ -292,6 +292,7 @@ struct mips_elf_la25_stub { #define LA25_LUI(VAL) (0x3c190000 | (VAL)) /* lui t9,VAL */ #define LA25_J(VAL) (0x08000000 | (((VAL) >> 2) & 0x3ffffff)) /* j VAL */ +#define LA25_BC(VAL) (0xc8000000 | (((VAL) >> 2) & 0x3ffffff)) /* bc VAL */ #define LA25_ADDIU(VAL) (0x27390000 | (VAL)) /* addiu t9,t9,VAL */ #define LA25_LUI_MICROMIPS(VAL) \ (0x41b90000 | (VAL)) /* lui t9,VAL */ @@ -449,6 +450,9 @@ struct mips_elf_link_hash_table /* True if we suppress checks for invalid branches between ISA modes. */ bfd_boolean ignore_branch_isa; + /* True if we are targetting R6 compact branches. */ + bfd_boolean compact_branches; + /* True if we're generating code for VxWorks. */ bfd_boolean is_vxworks; @@ -920,6 +924,7 @@ static bfd *reldyn_sorting_bfd; #define STUB_MOVE 0x03e07825 /* or t7,ra,zero */ #define STUB_LUI(VAL) (0x3c180000 + (VAL)) /* lui t8,VAL */ #define STUB_JALR 0x0320f809 /* jalr ra,t9 */ +#define STUB_JALRC 0xf8190000 /* jalrc ra,t9 */ #define STUB_ORI(VAL) (0x37180000 + (VAL)) /* ori t8,t8,VAL */ #define STUB_LI16U(VAL) (0x34180000 + (VAL)) /* ori t8,zero,VAL unsigned */ #define STUB_LI16S(abfd, VAL) \ @@ -1036,6 +1041,20 @@ static const bfd_vma mips_o32_exec_plt0_entry[] = 0x2718fffe /* subu $24, $24, 2 */ }; +/* The format of the first PLT entry in an O32 executable using compact + jumps. */ +static const bfd_vma mipsr6_o32_exec_plt0_entry_compact[] = +{ + 0x3c1c0000, /* lui $28, %hi(&GOTPLT[0]) */ + 0x8f990000, /* lw $25, %lo(&GOTPLT[0])($28) */ + 0x279c0000, /* addiu $28, $28, %lo(&GOTPLT[0]) */ + 0x031cc023, /* subu $24, $24, $28 */ + 0x03e07821, /* move $15, $31 # 32-bit move (addu) */ + 0x0018c082, /* srl $24, $24, 2 */ + 0x2718fffe, /* subu $24, $24, 2 */ + 0xf8190000 /* jalrc $25 */ +}; + /* The format of the first PLT entry in an N32 executable. Different because gp ($28) is not available; we use t2 ($14) instead. */ static const bfd_vma mips_n32_exec_plt0_entry[] = @@ -1050,6 +1069,21 @@ static const bfd_vma mips_n32_exec_plt0_entry[] = 0x2718fffe /* subu $24, $24, 2 */ }; +/* The format of the first PLT entry in an N32 executable using compact + jumps. Different because gp ($28) is not available; we use t2 ($14) + instead. */ +static const bfd_vma mipsr6_n32_exec_plt0_entry_compact[] = +{ + 0x3c0e0000, /* lui $14, %hi(&GOTPLT[0]) */ + 0x8dd90000, /* lw $25, %lo(&GOTPLT[0])($14) */ + 0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */ + 0x030ec023, /* subu $24, $24, $14 */ + 0x03e07821, /* move $15, $31 # 32-bit move (addu) */ + 0x0018c082, /* srl $24, $24, 2 */ + 0x2718fffe, /* subu $24, $24, 2 */ + 0xf8190000 /* jalrc $25 */ +}; + /* The format of the first PLT entry in an N64 executable. Different from N32 because of the increased size of GOT entries. */ static const bfd_vma mips_n64_exec_plt0_entry[] = @@ -1064,6 +1098,22 @@ static const bfd_vma mips_n64_exec_plt0_entry[] = 0x2718fffe /* subu $24, $24, 2 */ }; +/* The format of the first PLT entry in an N64 executable using compact + jumps. Different from N32 because of the increased size of GOT + entries. */ +static const bfd_vma mipsr6_n64_exec_plt0_entry_compact[] = +{ + 0x3c0e0000, /* lui $14, %hi(&GOTPLT[0]) */ + 0xddd90000, /* ld $25, %lo(&GOTPLT[0])($14) */ + 0x25ce0000, /* addiu $14, $14, %lo(&GOTPLT[0]) */ + 0x030ec023, /* subu $24, $24, $14 */ + 0x03e0782d, /* move $15, $31 # 64-bit move (daddu) */ + 0x0018c0c2, /* srl $24, $24, 3 */ + 0x2718fffe, /* subu $24, $24, 2 */ + 0xf8190000 /* jalrc $25 */ +}; + + /* 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 @@ -1106,9 +1156,6 @@ 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) */ @@ -1117,6 +1164,14 @@ static const bfd_vma mipsr6_exec_plt_entry[] = 0x03200009 /* jr $25 */ }; +static const bfd_vma mipsr6_exec_plt_entry_compact[] = +{ + 0x3c0f0000, /* lui $15, %hi(.got.plt entry) */ + 0x01f90000, /* l[wd] $25, %lo(.got.plt entry)($15) */ + 0x25f80000, /* addiu $24, $15, %lo(.got.plt entry) */ + 0xd8190000 /* jic $25, 0 */ +}; + /* 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. */ @@ -10604,6 +10659,8 @@ mips_elf_create_la25_stub (void **slot, void *data) asection *s; bfd_byte *loc; bfd_vma offset, target, target_high, target_low; + bfd_vma branch_pc; + bfd_signed_vma pcrel_offset = 0; stub = (struct mips_elf_la25_stub *) *slot; hti = (struct mips_htab_traverse_info *) data; @@ -10627,6 +10684,12 @@ mips_elf_create_la25_stub (void **slot, void *data) /* Work out where in the section this stub should go. */ offset = stub->offset; + /* We add 8 here to account for the LUI/ADDIU instructions + before the branch instruction. This cannot be moved down to + where pcrel_offset is calculated as 's' is updated in + mips_elf_get_la25_target. */ + branch_pc = s->output_section->vma + s->output_offset + offset + 8; + /* Work out the target address. */ target = mips_elf_get_la25_target (stub, &s); target += s->output_section->vma + s->output_offset; @@ -10634,6 +10697,12 @@ mips_elf_create_la25_stub (void **slot, void *data) target_high = ((target + 0x8000) >> 16) & 0xffff; target_low = (target & 0xffff); + /* Calculate the PC of the compact branch instruction (for the case where + compact branches are used for either microMIPSR6 or MIPSR6 with + compact branches. Add 4-bytes to account for BC using the PC of the + next instruction as the base. */ + pcrel_offset = target - (branch_pc + 4); + if (stub->stub_section != htab->strampoline) { /* This is a simple LUI/ADDIU stub. Zero out the beginning @@ -10672,8 +10741,16 @@ mips_elf_create_la25_stub (void **slot, void *data) else { bfd_put_32 (hti->output_bfd, LA25_LUI (target_high), loc); - bfd_put_32 (hti->output_bfd, LA25_J (target), loc + 4); - bfd_put_32 (hti->output_bfd, LA25_ADDIU (target_low), loc + 8); + if (MIPSR6_P (hti->output_bfd) && htab->compact_branches) + { + bfd_put_32 (hti->output_bfd, LA25_ADDIU (target_low), loc + 4); + bfd_put_32 (hti->output_bfd, LA25_BC (pcrel_offset), loc + 8); + } + else + { + bfd_put_32 (hti->output_bfd, LA25_J (target), loc + 4); + bfd_put_32 (hti->output_bfd, LA25_ADDIU (target_low), loc + 8); + } bfd_put_32 (hti->output_bfd, 0, loc + 12); } } @@ -10830,14 +10907,16 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, /* Fill in the PLT entry itself. */ if (MIPSR6_P (output_bfd)) - plt_entry = mipsr6_exec_plt_entry; + plt_entry = htab->compact_branches ? mipsr6_exec_plt_entry_compact + : 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)) + if (! LOAD_INTERLOCKS_P (output_bfd) + || (MIPSR6_P (output_bfd) && htab->compact_branches)) { bfd_put_32 (output_bfd, plt_entry[2] | got_address_low, loc + 8); bfd_put_32 (output_bfd, plt_entry[3], loc + 12); @@ -11041,8 +11120,12 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, stub + idx); idx += 4; } - bfd_put_32 (output_bfd, STUB_JALR, stub + idx); - idx += 4; + + if (!(MIPSR6_P (output_bfd) && htab->compact_branches)) + { + 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. */ @@ -11055,6 +11138,10 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd, else bfd_put_32 (output_bfd, STUB_LI16S (output_bfd, h->dynindx), stub + idx); + idx += 4; + + if (MIPSR6_P (output_bfd) && htab->compact_branches) + bfd_put_32 (output_bfd, STUB_JALRC, stub + idx); } BFD_ASSERT (h->plt.plist->stub_offset <= htab->sstubs->size); @@ -11428,11 +11515,17 @@ mips_finish_exec_plt (bfd *output_bfd, struct bfd_link_info *info) BFD_ASSERT (htab != NULL); if (ABI_64_P (output_bfd)) - plt_entry = mips_n64_exec_plt0_entry; + plt_entry = (htab->compact_branches + ? mipsr6_n64_exec_plt0_entry_compact + : mips_n64_exec_plt0_entry); else if (ABI_N32_P (output_bfd)) - plt_entry = mips_n32_exec_plt0_entry; + plt_entry = (htab->compact_branches + ? mipsr6_n32_exec_plt0_entry_compact + : mips_n32_exec_plt0_entry); else if (!htab->plt_header_is_comp) - plt_entry = mips_o32_exec_plt0_entry; + plt_entry = (htab->compact_branches + ? mipsr6_o32_exec_plt0_entry_compact + : mips_o32_exec_plt0_entry); else if (htab->insn32) plt_entry = micromips_insn32_o32_exec_plt0_entry; else @@ -14190,6 +14283,16 @@ _bfd_mips_elf_linker_flags (struct bfd_link_info *info, bfd_boolean insn32, mips_elf_hash_table (info)->ignore_branch_isa = ignore_branch_isa; mips_elf_hash_table (info)->gnu_target = gnu_target; } + +/* A function that the linker calls to enable use of compact branches in + linker generated code for MIPSR6. */ + +void +_bfd_mips_elf_compact_branches (struct bfd_link_info *info, bfd_boolean on) +{ + mips_elf_hash_table (info)->compact_branches = on; +} + /* Structure for saying that BFD machine EXTENSION extends BASE. */ |