#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 */
/* 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;
#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) \
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[] =
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[] =
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
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) */
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. */
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;
/* 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;
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
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);
}
}
/* 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);
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. */
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);
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
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;
+}
+
\f
/* Structure for saying that BFD machine EXTENSION extends BASE. */