const char *prefix, asection *s, bfd_vma value,
bfd_vma size)
{
+ bfd_boolean micromips_p = ELF_ST_IS_MICROMIPS (h->root.other);
struct bfd_link_hash_entry *bh;
struct elf_link_hash_entry *elfh;
char *name;
bfd_boolean res;
- if (ELF_ST_IS_MICROMIPS (h->root.other))
+ if (micromips_p)
value |= 1;
/* Create a new symbol. */
elfh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
elfh->size = size;
elfh->forced_local = 1;
+ if (micromips_p)
+ elfh->other = ELF_ST_SET_MICROMIPS (elfh->other);
return TRUE;
}
/* Prefer to use LUI/ADDIU stubs if the function is at the beginning
of the section and if we would need no more than 2 nops. */
value = mips_elf_get_la25_target (stub, &s);
+ if (ELF_ST_IS_MICROMIPS (stub->h->root.other))
+ value &= ~1;
use_trampoline_p = (value != 0 || s->alignment_power > 4);
h->la25_stub = stub;
|| r_type == R_MIPS_PC21_S2
|| r_type == R_MIPS_PC16
|| r_type == R_MIPS_GNU_REL16_S2
- || r_type == R_MIPS16_PC16_S1);
+ || r_type == R_MIPS16_PC16_S1
+ || r_type == R_MICROMIPS_PC16_S1
+ || r_type == R_MICROMIPS_PC10_S1
+ || r_type == R_MICROMIPS_PC7_S1);
}
static inline bfd_boolean
|| r_type == R_MIPS_PC19_S2);
}
+static inline bfd_boolean
+branch_reloc_p (int r_type)
+{
+ return (r_type == R_MIPS_26
+ || r_type == R_MIPS_PC26_S2
+ || r_type == R_MIPS_PC21_S2
+ || r_type == R_MIPS_PC16
+ || r_type == R_MIPS_GNU_REL16_S2);
+}
+
static inline bfd_boolean
mips16_branch_reloc_p (int r_type)
{
/* Figure out the value of the symbol. */
if (local_p)
{
+ bfd_boolean micromips_p = MICROMIPS_P (abfd);
Elf_Internal_Sym *sym;
sym = local_syms + r_symndx;
if (*namep == NULL || **namep == '\0')
*namep = bfd_section_name (input_bfd, sec);
- target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (sym->st_other);
- target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (sym->st_other);
+ /* For relocations against a section symbol and ones against no
+ symbol (absolute relocations) infer the ISA mode from the addend. */
+ if (section_p || r_symndx == STN_UNDEF)
+ {
+ target_is_16_bit_code_p = (addend & 1) && !micromips_p;
+ target_is_micromips_code_p = (addend & 1) && micromips_p;
+ }
+ /* For relocations against an absolute symbol infer the ISA mode
+ from the value of the symbol plus addend. */
+ else if (bfd_is_abs_section (sec))
+ {
+ target_is_16_bit_code_p = ((symbol + addend) & 1) && !micromips_p;
+ target_is_micromips_code_p = ((symbol + addend) & 1) && micromips_p;
+ }
+ /* Otherwise just use the regular symbol annotation available. */
+ else
+ {
+ target_is_16_bit_code_p = ELF_ST_IS_MIPS16 (sym->st_other);
+ target_is_micromips_code_p = ELF_ST_IS_MICROMIPS (sym->st_other);
+ }
}
else
{
else if (h != NULL && h->la25_stub
&& mips_elf_relocation_needs_la25_stub (input_bfd, r_type,
target_is_16_bit_code_p))
- symbol = (h->la25_stub->stub_section->output_section->vma
- + h->la25_stub->stub_section->output_offset
- + h->la25_stub->offset);
+ {
+ symbol = (h->la25_stub->stub_section->output_section->vma
+ + h->la25_stub->stub_section->output_offset
+ + h->la25_stub->offset);
+ if (ELF_ST_IS_MICROMIPS (h->root.other))
+ symbol |= 1;
+ }
/* 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)
+ else if ((mips16_branch_reloc_p (r_type)
+ || micromips_branch_reloc_p (r_type))
&& !bfd_link_relocatable (info)
&& h != NULL
&& h->use_plt_entry
acceptable. */
*cross_mode_jump_p = (!bfd_link_relocatable (info)
&& !(h && h->root.root.type == bfd_link_hash_undefweak)
- && ((r_type == R_MIPS16_26 && !target_is_16_bit_code_p)
- || (r_type == R_MICROMIPS_26_S1
+ && ((mips16_branch_reloc_p (r_type)
+ && !target_is_16_bit_code_p)
+ || (micromips_branch_reloc_p (r_type)
&& !target_is_micromips_code_p)
- || ((r_type == R_MIPS_26 || r_type == R_MIPS_JALR)
+ || ((branch_reloc_p (r_type)
+ || r_type == R_MIPS_JALR)
&& (target_is_16_bit_code_p
|| target_is_micromips_code_p))));
value = addend;
value += symbol;
- /* Make sure the target of JALX is word-aligned. Bit 0 must be
- the correct ISA mode selector and bit 1 must be 0. */
- if (*cross_mode_jump_p && (value & 3) != (r_type == R_MIPS_26))
+ /* Make sure the target of a jump is suitably aligned. Bit 0 must
+ be the correct ISA mode selector except for weak undefined
+ symbols. */
+ if ((was_local_p || h->root.root.type != bfd_link_hash_undefweak)
+ && (*cross_mode_jump_p
+ ? (value & 3) != (r_type == R_MIPS_26)
+ : (value & ((1 << shift) - 1)) != (r_type != R_MIPS_26)))
return bfd_reloc_outofrange;
value >>= shift;
if (howto->partial_inplace)
addend = _bfd_mips_elf_sign_extend (addend, 18);
- if ((symbol + addend) & 3)
+ /* No need to exclude weak undefined symbols here as they resolve
+ to 0 and never set `*cross_mode_jump_p', so this alignment check
+ will never trigger for them. */
+ if (*cross_mode_jump_p
+ ? ((symbol + addend) & 3) != 1
+ : ((symbol + addend) & 3) != 0)
return bfd_reloc_outofrange;
value = symbol + addend - p;
addend = _bfd_mips_elf_sign_extend (addend, 17);
if ((was_local_p || h->root.root.type != bfd_link_hash_undefweak)
- && ((symbol + addend) & 1) == 0)
+ && (*cross_mode_jump_p
+ ? ((symbol + addend) & 3) != 0
+ : ((symbol + addend) & 1) == 0))
return bfd_reloc_outofrange;
value = symbol + addend - p;
case R_MICROMIPS_PC7_S1:
if (howto->partial_inplace)
addend = _bfd_mips_elf_sign_extend (addend, 8);
+
+ if ((was_local_p || h->root.root.type != bfd_link_hash_undefweak)
+ && (*cross_mode_jump_p
+ ? ((symbol + addend + 2) & 3) != 0
+ : ((symbol + addend + 2) & 1) == 0))
+ 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, 8);
case R_MICROMIPS_PC10_S1:
if (howto->partial_inplace)
addend = _bfd_mips_elf_sign_extend (addend, 11);
+
+ if ((was_local_p || h->root.root.type != bfd_link_hash_undefweak)
+ && (*cross_mode_jump_p
+ ? ((symbol + addend + 2) & 3) != 0
+ : ((symbol + addend + 2) & 1) == 0))
+ 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, 11);
case R_MICROMIPS_PC16_S1:
if (howto->partial_inplace)
addend = _bfd_mips_elf_sign_extend (addend, 17);
+
+ if ((was_local_p || h->root.root.type != bfd_link_hash_undefweak)
+ && (*cross_mode_jump_p
+ ? ((symbol + addend) & 3) != 0
+ : ((symbol + addend) & 1) == 0))
+ 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, 17);
/* Set the field. */
x |= (value & howto->dst_mask);
- /* If required, turn JAL into JALX. */
+ /* Detect incorrect JALX usage. If required, turn JAL or BAL into JALX. */
+ if (!cross_mode_jump_p && jal_reloc_p (r_type))
+ {
+ bfd_vma opcode = x >> 26;
+
+ if (r_type == R_MIPS16_26 ? opcode == 0x7
+ : r_type == R_MICROMIPS_26_S1 ? opcode == 0x3c
+ : opcode == 0x1d)
+ {
+ info->callbacks->einfo
+ (_("%X%H: Unsupported JALX to the same ISA mode\n"),
+ input_bfd, input_section, relocation->r_offset);
+ return TRUE;
+ }
+ }
if (cross_mode_jump_p && jal_reloc_p (r_type))
{
bfd_boolean ok;
/* Make this the JALX opcode. */
x = (x & ~(0x3f << 26)) | (jalx_opcode << 26);
}
+ else if (cross_mode_jump_p && b_reloc_p (r_type))
+ {
+ bfd_boolean ok = FALSE;
+ bfd_vma opcode = x >> 16;
+ bfd_vma jalx_opcode = 0;
+ bfd_vma addr;
+ bfd_vma dest;
+
+ if (r_type == R_MICROMIPS_PC16_S1)
+ {
+ ok = opcode == 0x4060;
+ jalx_opcode = 0x3c;
+ value <<= 1;
+ }
+ else if (r_type == R_MIPS_PC16 || r_type == R_MIPS_GNU_REL16_S2)
+ {
+ ok = opcode == 0x411;
+ jalx_opcode = 0x1d;
+ value <<= 2;
+ }
+
+ if (bfd_link_pic (info) || !ok)
+ {
+ info->callbacks->einfo
+ (_("%X%H: Unsupported branch between ISA modes\n"),
+ input_bfd, input_section, relocation->r_offset);
+ return TRUE;
+ }
+
+ addr = (input_section->output_section->vma
+ + input_section->output_offset
+ + relocation->r_offset
+ + 4);
+ dest = addr + (((value & 0x3ffff) ^ 0x20000) - 0x20000);
+
+ if ((addr >> 28) << 28 != (dest >> 28) << 28)
+ {
+ info->callbacks->einfo
+ (_("%X%H: Cannot convert branch between ISA modes "
+ "to JALX: relocation out of range\n"),
+ input_bfd, input_section, relocation->r_offset);
+ return TRUE;
+ }
+
+ /* Make this the JALX opcode. */
+ x = ((dest >> 2) & 0x3ffffff) | jalx_opcode << 26;
+ }
/* Try converting JAL to BAL and J(AL)R to B(AL), if the target is in
range. */
a PLT entry is not created because the symbol is satisfied
locally. */
if (h != NULL
- && jal_reloc_p (r_type)
+ && (branch_reloc_p (r_type)
+ || mips16_branch_reloc_p (r_type)
+ || micromips_branch_reloc_p (r_type))
&& !SYMBOL_CALLS_LOCAL (info, h))
{
if (h->plt.plist == NULL)
if (h->plt.plist == NULL)
return FALSE;
- if (r_type == R_MIPS_26)
+ if (branch_reloc_p (r_type))
h->plt.plist->need_mips = TRUE;
else
h->plt.plist->need_comp = TRUE;
case bfd_reloc_outofrange:
msg = NULL;
if (jal_reloc_p (howto->type))
- msg = _("JALX to a non-word-aligned address");
+ msg = (cross_mode_jump_p
+ ? _("Cannot convert a jump to JALX "
+ "for a non-word-aligned address")
+ : (howto->type == R_MIPS16_26
+ ? _("Jump to a non-word-aligned address")
+ : _("Jump to a non-instruction-aligned address")));
else if (b_reloc_p (howto->type))
- msg = _("Branch to a non-instruction-aligned address");
+ msg = (cross_mode_jump_p
+ ? _("Cannot convert a branch to JALX "
+ "for a non-word-aligned address")
+ : _("Branch to a non-instruction-aligned address"));
else if (aligned_pcrel_reloc_p (howto->type))
msg = _("PC-relative load from unaligned address");
if (msg)