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 */
{BFD_RELOC_ARM_LDRS_SB_G2, R_ARM_LDRS_SB_G2},
{BFD_RELOC_ARM_LDC_SB_G0, R_ARM_LDC_SB_G0},
{BFD_RELOC_ARM_LDC_SB_G1, R_ARM_LDC_SB_G1},
- {BFD_RELOC_ARM_LDC_SB_G2, R_ARM_LDC_SB_G2}
+ {BFD_RELOC_ARM_LDC_SB_G2, R_ARM_LDC_SB_G2},
+ {BFD_RELOC_ARM_V4BX, R_ARM_V4BX}
};
static reloc_howto_type *
#define VFP11_ERRATUM_VENEER_SECTION_NAME ".vfp11_veneer"
#define VFP11_ERRATUM_VENEER_ENTRY_NAME "__vfp11_veneer_%x"
+#define ARM_BX_GLUE_SECTION_NAME ".v4_bx"
+#define ARM_BX_GLUE_ENTRY_NAME "__bx_r%d"
+
/* The name of the dynamic interpreter. This is put in the .interp
section. */
#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
/* The size in bytes of the section containing the ARM-to-Thumb glue. */
bfd_size_type arm_glue_size;
+ /* The size in bytes of section containing the ARMv4 BX veneers. */
+ bfd_size_type bx_glue_size;
+
+ /* Offsets of ARMv4 BX veneers. Bit1 set if present, and Bit0 set when
+ veneer has been populated. */
+ bfd_vma bx_glue_offset[15];
+
/* The size in bytes of the section containing glue for VFP11 erratum
veneers. */
bfd_size_type vfp11_erratum_glue_size;
/* The relocation to use for R_ARM_TARGET2 relocations. */
int target2_reloc;
- /* Nonzero to fix BX instructions for ARMv4 targets. */
+ /* 0 = Ignore R_ARM_V4BX.
+ 1 = Convert BX to MOV PC.
+ 2 = Generate v4 interworing stubs. */
int fix_v4bx;
/* Nonzero if the ARM/Thumb BLX instructions are available for use. */
ret->srelplt2 = NULL;
ret->thumb_glue_size = 0;
ret->arm_glue_size = 0;
+ ret->bx_glue_size = 0;
+ memset (ret->bx_glue_offset, 0, sizeof(ret->bx_glue_offset));
ret->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
ret->vfp11_erratum_glue_size = 0;
ret->num_vfp11_fixes = 0;
#define VFP11_ERRATUM_VENEER_SIZE 8
+#define ARM_BX_VENEER_SIZE 12
+static const insn32 armbx1_tst_insn = 0xe3100001;
+static const insn32 armbx2_moveq_insn = 0x01a0f000;
+static const insn32 armbx3_bx_insn = 0xe12fff10;
+
#ifndef ELFARM_NABI_C_INCLUDED
bfd_boolean
bfd_elf32_arm_allocate_interworking_sections (struct bfd_link_info * info)
s->contents = foo;
}
+ if (globals->bx_glue_size != 0)
+ {
+ BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
+
+ s = bfd_get_section_by_name (globals->bfd_of_glue_owner,
+ ARM_BX_GLUE_SECTION_NAME);
+
+ BFD_ASSERT (s != NULL);
+
+ foo = bfd_alloc (globals->bfd_of_glue_owner, globals->bx_glue_size);
+
+ BFD_ASSERT (s->size == globals->bx_glue_size);
+ s->contents = foo;
+ }
+
return TRUE;
}
}
+/* Allocate space for ARMv4 BX veneers. */
+
+static void
+record_arm_bx_glue (struct bfd_link_info * link_info, int reg)
+{
+ asection * s;
+ struct elf32_arm_link_hash_table *globals;
+ char *tmp_name;
+ struct elf_link_hash_entry *myh;
+ struct bfd_link_hash_entry *bh;
+ bfd_vma val;
+
+ /* BX PC does not need a veneer. */
+ if (reg == 15)
+ return;
+
+ globals = elf32_arm_hash_table (link_info);
+
+ BFD_ASSERT (globals != NULL);
+ BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
+
+ /* Check if this veneer has already been allocated. */
+ if (globals->bx_glue_offset[reg])
+ return;
+
+ s = bfd_get_section_by_name
+ (globals->bfd_of_glue_owner, ARM_BX_GLUE_SECTION_NAME);
+
+ BFD_ASSERT (s != NULL);
+
+ /* Add symbol for veneer. */
+ tmp_name = bfd_malloc ((bfd_size_type) strlen (ARM_BX_GLUE_ENTRY_NAME) + 1);
+
+ BFD_ASSERT (tmp_name);
+
+ sprintf (tmp_name, ARM_BX_GLUE_ENTRY_NAME, reg);
+
+ myh = elf_link_hash_lookup
+ (&(globals)->root, tmp_name, FALSE, FALSE, FALSE);
+
+ BFD_ASSERT (myh == NULL);
+
+ bh = NULL;
+ val = globals->bx_glue_size;
+ _bfd_generic_link_add_one_symbol (link_info, globals->bfd_of_glue_owner,
+ tmp_name, BSF_FUNCTION | BSF_LOCAL, s, val,
+ NULL, TRUE, FALSE, &bh);
+
+ myh = (struct elf_link_hash_entry *) bh;
+ myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
+ myh->forced_local = 1;
+
+ s->size += ARM_BX_VENEER_SIZE;
+ globals->bx_glue_offset[reg] = globals->bx_glue_size | 2;
+ globals->bx_glue_size += ARM_BX_VENEER_SIZE;
+}
+
+
/* Add an entry to the code/data map for section SEC. */
static void
if (sec_data->mapcount > sec_data->mapsize)
{
sec_data->mapsize *= 2;
- sec_data->map = bfd_realloc (sec_data->map, sec_data->mapsize
- * sizeof (elf32_arm_section_map));
+ sec_data->map = bfd_realloc_or_free (sec_data->map, sec_data->mapsize
+ * sizeof (elf32_arm_section_map));
+ }
+
+ if (sec_data->map)
+ {
+ sec_data->map[newidx].vma = vma;
+ sec_data->map[newidx].type = type;
}
-
- sec_data->map[newidx].vma = vma;
- sec_data->map[newidx].type = type;
}
sec->gc_mark = 1;
}
+ sec = bfd_get_section_by_name (abfd, ARM_BX_GLUE_SECTION_NAME);
+
+ if (sec == NULL)
+ {
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_CODE | SEC_READONLY);
+
+ sec = bfd_make_section_with_flags (abfd,
+ ARM_BX_GLUE_SECTION_NAME,
+ flags);
+
+ if (sec == NULL
+ || !bfd_set_section_alignment (abfd, sec, 2))
+ return FALSE;
+
+ sec->gc_mark = 1;
+ }
+
return TRUE;
}
&& r_type != R_ARM_CALL
&& r_type != R_ARM_JUMP24
&& r_type != R_ARM_THM_CALL
- && r_type != R_ARM_THM_JUMP24)
+ && r_type != R_ARM_THM_JUMP24
+ && (r_type != R_ARM_V4BX || globals->fix_v4bx < 2))
continue;
/* Get the section contents if we haven't done so already. */
}
}
+ if (r_type == R_ARM_V4BX)
+ {
+ int reg;
+
+ reg = bfd_get_32 (abfd, contents + irel->r_offset) & 0xf;
+ record_arm_bx_glue (link_info, reg);
+ continue;
+ }
+
/* If the relocation is not against a symbol it cannot concern us. */
h = NULL;
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;
}
return TRUE;
}
+/* Populate ARMv4 BX veneers. Returns the absolute adress of the veneer. */
+
+static bfd_vma
+elf32_arm_bx_glue (struct bfd_link_info * info, int reg)
+{
+ bfd_byte *p;
+ bfd_vma glue_addr;
+ asection *s;
+ struct elf32_arm_link_hash_table *globals;
+
+ globals = elf32_arm_hash_table (info);
+
+ BFD_ASSERT (globals != NULL);
+ BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
+
+ s = bfd_get_section_by_name (globals->bfd_of_glue_owner,
+ ARM_BX_GLUE_SECTION_NAME);
+ BFD_ASSERT (s != NULL);
+ BFD_ASSERT (s->contents != NULL);
+ BFD_ASSERT (s->output_section != NULL);
+
+ BFD_ASSERT (globals->bx_glue_offset[reg] & 2);
+
+ glue_addr = globals->bx_glue_offset[reg] & ~(bfd_vma)3;
+
+ if ((globals->bx_glue_offset[reg] & 1) == 0)
+ {
+ p = s->contents + glue_addr;
+ bfd_put_32 (globals->obfd, armbx1_tst_insn + (reg << 16), p);
+ bfd_put_32 (globals->obfd, armbx2_moveq_insn + reg, p + 4);
+ bfd_put_32 (globals->obfd, armbx3_bx_insn + reg, p + 8);
+ globals->bx_glue_offset[reg] |= 1;
+ }
+
+ return glue_addr + s->output_section->vma + s->output_offset;
+}
+
/* Generate Arm stubs for exported Thumb symbols. */
static void
elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED,
case R_ARM_V4BX:
if (globals->fix_v4bx)
- {
- bfd_vma insn = bfd_get_32 (input_bfd, hit_data);
+ {
+ bfd_vma insn = bfd_get_32 (input_bfd, hit_data);
- /* Ensure that we have a BX instruction. */
- BFD_ASSERT ((insn & 0x0ffffff0) == 0x012fff10);
+ /* Ensure that we have a BX instruction. */
+ BFD_ASSERT ((insn & 0x0ffffff0) == 0x012fff10);
- /* Preserve Rm (lowest four bits) and the condition code
- (highest four bits). Other bits encode MOV PC,Rm. */
- insn = (insn & 0xf000000f) | 0x01a0f000;
+ if (globals->fix_v4bx == 2 && (insn & 0xf) != 0xf)
+ {
+ /* Branch to veneer. */
+ bfd_vma glue_addr;
+ glue_addr = elf32_arm_bx_glue (info, insn & 0xf);
+ glue_addr -= input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset + 8;
+ insn = (insn & 0xf0000000) | 0x0a000000
+ | ((glue_addr >> 2) & 0x00ffffff);
+ }
+ else
+ {
+ /* Preserve Rm (lowest four bits) and the condition code
+ (highest four bits). Other bits encode MOV PC,Rm. */
+ insn = (insn & 0xf000000f) | 0x01a0f000;
+ }
- bfd_put_32 (input_bfd, insn, hit_data);
- }
+ bfd_put_32 (input_bfd, insn, hit_data);
+ }
return bfd_reloc_ok;
case R_ARM_MOVW_ABS_NC:
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)
{
}
}
+ /* ARMv4 BX veneers. */
+ if (htab->bx_glue_size > 0)
+ {
+ osi.sec = bfd_get_section_by_name (htab->bfd_of_glue_owner,
+ ARM_BX_GLUE_SECTION_NAME);
+
+ osi.sec_shndx = _bfd_elf_section_from_bfd_section
+ (output_bfd, osi.sec->output_section);
+
+ elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0);
+ }
+
/* Finally, output mapping symbols for the PLT. */
if (!htab->splt || htab->splt->size == 0)
return TRUE;