bfd_elf_generic_reloc, /* special_function */
"R_ARM_MOVW_ABS_NC", /* name */
FALSE, /* partial_inplace */
- 0x0000ffff, /* src_mask */
- 0x0000ffff, /* dst_mask */
+ 0x000f0fff, /* src_mask */
+ 0x000f0fff, /* dst_mask */
FALSE), /* pcrel_offset */
HOWTO (R_ARM_MOVT_ABS, /* type */
bfd_elf_generic_reloc, /* special_function */
"R_ARM_MOVT_ABS", /* name */
FALSE, /* partial_inplace */
- 0x0000ffff, /* src_mask */
- 0x0000ffff, /* dst_mask */
+ 0x000f0fff, /* src_mask */
+ 0x000f0fff, /* dst_mask */
FALSE), /* pcrel_offset */
HOWTO (R_ARM_MOVW_PREL_NC, /* type */
bfd_elf_generic_reloc, /* special_function */
"R_ARM_MOVW_PREL_NC", /* name */
FALSE, /* partial_inplace */
- 0x0000ffff, /* src_mask */
- 0x0000ffff, /* dst_mask */
+ 0x000f0fff, /* src_mask */
+ 0x000f0fff, /* dst_mask */
TRUE), /* pcrel_offset */
HOWTO (R_ARM_MOVT_PREL, /* type */
bfd_elf_generic_reloc, /* special_function */
"R_ARM_MOVT_PREL", /* name */
FALSE, /* partial_inplace */
- 0x0000ffff, /* src_mask */
- 0x0000ffff, /* dst_mask */
+ 0x000f0fff, /* src_mask */
+ 0x000f0fff, /* dst_mask */
TRUE), /* pcrel_offset */
HOWTO (R_ARM_THM_MOVW_ABS_NC, /* type */
elf_arm_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
}
-/* The thumb form of a long branch is a bit finicky, because the offset
- encoding is split over two fields, each in it's own instruction. They
- can occur in any order. So given a thumb form of long branch, and an
- offset, insert the offset into the thumb branch and return finished
- instruction.
+/* Replace the target offset of a Thumb bl or b.w instruction. */
- It takes two thumb instructions to encode the target address. Each has
- 11 bits to invest. The upper 11 bits are stored in one (identified by
- H-0.. see below), the lower 11 bits are stored in the other (identified
- by H-1).
-
- Combine together and shifted left by 1 (it's a half word address) and
- there you have it.
-
- Op: 1111 = F,
- H-0, upper address-0 = 000
- Op: 1111 = F,
- H-1, lower address-0 = 800
-
- They can be ordered either way, but the arm tools I've seen always put
- the lower one first. It probably doesn't matter. krk@cygnus.com
-
- XXX: Actually the order does matter. The second instruction (H-1)
- moves the computed address into the PC, so it must be the second one
- in the sequence. The problem, however is that whilst little endian code
- stores the instructions in HI then LOW order, big endian code does the
- reverse. nickc@cygnus.com. */
-
-#define LOW_HI_ORDER 0xF800F000
-#define HI_LOW_ORDER 0xF000F800
-
-static insn32
-insert_thumb_branch (insn32 br_insn, int rel_off)
+static void
+insert_thumb_branch (bfd *abfd, long int offset, bfd_byte *insn)
{
- unsigned int low_bits;
- unsigned int high_bits;
-
- BFD_ASSERT ((rel_off & 1) != 1);
-
- rel_off >>= 1; /* Half word aligned address. */
- low_bits = rel_off & 0x000007FF; /* The bottom 11 bits. */
- high_bits = (rel_off >> 11) & 0x000007FF; /* The top 11 bits. */
-
- if ((br_insn & LOW_HI_ORDER) == LOW_HI_ORDER)
- br_insn = LOW_HI_ORDER | (low_bits << 16) | high_bits;
- else if ((br_insn & HI_LOW_ORDER) == HI_LOW_ORDER)
- br_insn = HI_LOW_ORDER | (high_bits << 16) | low_bits;
- else
- /* FIXME: abort is probably not the right call. krk@cygnus.com */
- abort (); /* Error - not a valid branch instruction form. */
-
- return br_insn;
+ bfd_vma upper;
+ bfd_vma lower;
+ int reloc_sign;
+
+ BFD_ASSERT ((offset & 1) == 0);
+
+ upper = bfd_get_16 (abfd, insn);
+ lower = bfd_get_16 (abfd, insn + 2);
+ reloc_sign = (offset < 0) ? 1 : 0;
+ upper = (upper & ~(bfd_vma) 0x7ff)
+ | ((offset >> 12) & 0x3ff)
+ | (reloc_sign << 10);
+ lower = (lower & ~(bfd_vma) 0x2fff)
+ | (((!((offset >> 23) & 1)) ^ reloc_sign) << 13)
+ | (((!((offset >> 22) & 1)) ^ reloc_sign) << 11)
+ | ((offset >> 1) & 0x7ff);
+ bfd_put_16 (abfd, upper, insn);
+ bfd_put_16 (abfd, lower, insn + 2);
}
{
asection * s = 0;
bfd_vma my_offset;
- unsigned long int tmp;
long int ret_offset;
struct elf_link_hash_entry * myh;
struct elf32_arm_link_hash_table * globals;
/* Biassing for PC-relative addressing. */
- 8;
- tmp = bfd_get_32 (input_bfd, hit_data
- - input_section->vma);
-
- bfd_put_32 (output_bfd,
- (bfd_vma) insert_thumb_branch (tmp, ret_offset),
- hit_data - input_section->vma);
+ insert_thumb_branch (input_bfd, ret_offset, hit_data - input_section->vma);
return TRUE;
}
if (globals->use_rel)
{
addend = ((insn >> 4) & 0xf000) | (insn & 0xfff);
- signed_addend = (addend ^ 0x10000) - 0x10000;
+ signed_addend = (addend ^ 0x8000) - 0x8000;
}
value += signed_addend;
| ((insn >> 15) & 0x0800)
| ((insn >> 4) & 0x0700)
| (insn & 0x00ff);
- signed_addend = (addend ^ 0x10000) - 0x10000;
+ signed_addend = (addend ^ 0x8000) - 0x8000;
}
value += signed_addend;
asection *msec;
bfd_vma addend, value;
- if (howto->rightshift)
+ switch (r_type)
{
- (*_bfd_error_handler)
- (_("%B(%A+0x%lx): %s relocation against SEC_MERGE section"),
- input_bfd, input_section,
- (long) rel->r_offset, howto->name);
- return FALSE;
- }
+ case R_ARM_MOVW_ABS_NC:
+ case R_ARM_MOVT_ABS:
+ value = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ addend = ((value & 0xf0000) >> 4) | (value & 0xfff);
+ addend = (addend ^ 0x8000) - 0x8000;
+ break;
- value = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ case R_ARM_THM_MOVW_ABS_NC:
+ case R_ARM_THM_MOVT_ABS:
+ value = bfd_get_16 (input_bfd, contents + rel->r_offset)
+ << 16;
+ value |= bfd_get_16 (input_bfd,
+ contents + rel->r_offset + 2);
+ addend = ((value & 0xf7000) >> 4) | (value & 0xff)
+ | ((value & 0x04000000) >> 15);
+ addend = (addend ^ 0x8000) - 0x8000;
+ break;
- /* Get the (signed) value from the instruction. */
- addend = value & howto->src_mask;
- if (addend & ((howto->src_mask + 1) >> 1))
- {
- bfd_signed_vma mask;
+ default:
+ if (howto->rightshift
+ || (howto->src_mask & (howto->src_mask + 1)))
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): %s relocation against SEC_MERGE section"),
+ input_bfd, input_section,
+ (long) rel->r_offset, howto->name);
+ return FALSE;
+ }
+
+ value = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+ /* Get the (signed) value from the instruction. */
+ addend = value & howto->src_mask;
+ if (addend & ((howto->src_mask + 1) >> 1))
+ {
+ bfd_signed_vma mask;
- mask = -1;
- mask &= ~ howto->src_mask;
- addend |= mask;
+ mask = -1;
+ mask &= ~ howto->src_mask;
+ addend |= mask;
+ }
+ break;
}
+
msec = sec;
addend =
_bfd_elf_rel_local_sym (output_bfd, sym, &msec, addend)
- relocation;
addend += msec->output_section->vma + msec->output_offset;
- value = (value & ~ howto->dst_mask) | (addend & howto->dst_mask);
- bfd_put_32 (input_bfd, value, contents + rel->r_offset);
+
+ /* Cases here must match those in the preceeding
+ switch statement. */
+ switch (r_type)
+ {
+ case R_ARM_MOVW_ABS_NC:
+ case R_ARM_MOVT_ABS:
+ value = (value & 0xfff0f000) | ((addend & 0xf000) << 4)
+ | (addend & 0xfff);
+ bfd_put_32 (input_bfd, value, contents + rel->r_offset);
+ break;
+
+ case R_ARM_THM_MOVW_ABS_NC:
+ case R_ARM_THM_MOVT_ABS:
+ value = (value & 0xfbf08f00) | ((addend & 0xf700) << 4)
+ | (addend & 0xff) | ((addend & 0x0800) << 15);
+ bfd_put_16 (input_bfd, value >> 16,
+ contents + rel->r_offset);
+ bfd_put_16 (input_bfd, value,
+ contents + rel->r_offset + 2);
+ break;
+
+ default:
+ value = (value & ~ howto->dst_mask)
+ | (addend & howto->dst_mask);
+ bfd_put_32 (input_bfd, value, contents + rel->r_offset);
+ break;
+ }
}
}
else
/* Some tags have 0 = don't care, 1 = strong requirement,
2 = weak requirement. */
static const int order_312[3] = {3, 1, 2};
+ /* For use with Tag_VFP_arch. */
+ static const int order_01243[5] = {0, 1, 2, 4, 3};
int i;
if (!elf_known_obj_attributes_proc (obfd)[0].i)
case Tag_CPU_arch:
case Tag_ARM_ISA_use:
case Tag_THUMB_ISA_use:
- case Tag_VFP_arch:
case Tag_WMMX_arch:
case Tag_NEON_arch:
/* ??? Do NEON and WMMX conflict? */
if (in_attr[i].i)
out_attr[i].i = in_attr[i].i;
break;
+ case Tag_VFP_arch:
+ if (in_attr[i].i > 4 || out_attr[i].i > 4
+ || order_01243[in_attr[i].i] > order_01243[out_attr[i].i])
+ out_attr[i].i = in_attr[i].i;
+ break;
case Tag_PCS_config:
if (out_attr[i].i == 0)
out_attr[i].i = in_attr[i].i;
asection *sreloc;
bfd_vma *local_got_offsets;
struct elf32_arm_link_hash_table *htab;
+ bfd_boolean needs_plt;
if (info->relocatable)
return TRUE;
break;
/* Fall through */
- case R_ARM_ABS32:
- case R_ARM_ABS32_NOI:
- case R_ARM_REL32:
- case R_ARM_REL32_NOI:
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_CALL:
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
case R_ARM_THM_JUMP19:
+ needs_plt = 1;
+ goto normal_reloc;
+
+ case R_ARM_ABS32:
+ case R_ARM_ABS32_NOI:
+ case R_ARM_REL32:
+ case R_ARM_REL32_NOI:
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
case R_ARM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
+ needs_plt = 0;
+ normal_reloc:
+
/* Should the interworking branches be listed here? */
if (h != NULL)
{
refers to is in a different object. We can't tell for
sure yet, because something later might force the
symbol local. */
- if (r_type != R_ARM_ABS32
- && r_type != R_ARM_REL32
- && r_type != R_ARM_ABS32_NOI
- && r_type != R_ARM_REL32_NOI
- && r_type != R_ARM_ABS12)
+ if (needs_plt)
h->needs_plt = 1;
/* If we create a PLT entry, this relocation will reference
Elf_Internal_Shdr *hdr;
hdr = &elf_section_data (o)->this_hdr;
- if (hdr->sh_type == SHT_ARM_EXIDX && hdr->sh_link
+ if (hdr->sh_type == SHT_ARM_EXIDX
+ && hdr->sh_link
+ && hdr->sh_link < elf_numsections (sub)
&& !o->gc_mark
&& elf_shdrp[hdr->sh_link]->bfd_section->gc_mark)
{