summaryrefslogtreecommitdiff
path: root/bfd/elfxx-mips.c
diff options
context:
space:
mode:
authorMatthew Fortune <matthew.fortune@mips.com>2019-05-13 17:03:19 -0700
committerFaraz Shahbazker <fshahbazker@wavecomp.com>2019-05-21 09:22:28 -0700
commit3734320dc054bd9f6632607e9e5c901c57450791 (patch)
tree09ada7b6ef14bf220ce429dbd2d4a0dcd0faa654 /bfd/elfxx-mips.c
parent6467207116c66ff2c58f8bc35cb15b2596f5c457 (diff)
downloadbinutils-gdb-3734320dc054bd9f6632607e9e5c901c57450791.tar.gz
[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.c127
1 files changed, 115 insertions, 12 deletions
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 74dadf48f7d..8c1ad72aa83 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. */