This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
#include "sysdep.h"
#include "bfd.h"
/* The size of the thread control block. */
#define TCB_SIZE 8
-#define NUM_KNOWN_ATTRIBUTES 32
-
-typedef struct aeabi_attribute
-{
- int type;
- unsigned int i;
- char *s;
-} aeabi_attribute;
-
-typedef struct aeabi_attribute_list
-{
- struct aeabi_attribute_list *next;
- int tag;
- aeabi_attribute attr;
-} aeabi_attribute_list;
-
struct elf32_arm_obj_tdata
{
struct elf_obj_tdata root;
/* tls_type for each local got entry. */
char *local_got_tls_type;
- aeabi_attribute known_eabi_attributes[NUM_KNOWN_ATTRIBUTES];
- aeabi_attribute_list *other_eabi_attributes;
-
/* Zero to warn when linking objects with incompatible enum sizes. */
int no_enum_size_warning;
};
so that we can emit the Thumb trampoline only if needed. */
bfd_signed_vma plt_thumb_refcount;
+ /* Some references from Thumb code may be eliminated by BL->BLX
+ conversion, so record them separately. */
+ bfd_signed_vma plt_maybe_thumb_refcount;
+
/* Since PLT entries have variable size if the Thumb prologue is
used, we need to record the index into .got.plt instead of
recomputing it from the PLT offset. */
ret->relocs_copied = NULL;
ret->tls_type = GOT_UNKNOWN;
ret->plt_thumb_refcount = 0;
+ ret->plt_maybe_thumb_refcount = 0;
ret->plt_got_offset = -1;
ret->export_glue = NULL;
}
/* Copy over PLT info. */
edir->plt_thumb_refcount += eind->plt_thumb_refcount;
eind->plt_thumb_refcount = 0;
+ edir->plt_maybe_thumb_refcount += eind->plt_maybe_thumb_refcount;
+ eind->plt_maybe_thumb_refcount = 0;
if (dir->got.refcount <= 0)
{
hash = elf_link_hash_lookup
(&(hash_table)->root, tmp_name, FALSE, FALSE, TRUE);
- if (hash == NULL)
- asprintf (error_message, _("unable to find THUMB glue '%s' for '%s'"),
- tmp_name, name);
+ if (hash == NULL
+ && asprintf (error_message, _("unable to find THUMB glue '%s' for '%s'"),
+ tmp_name, name) == -1)
+ *error_message = (char *) bfd_errmsg (bfd_error_system_call);
free (tmp_name);
myh = elf_link_hash_lookup
(&(hash_table)->root, tmp_name, FALSE, FALSE, TRUE);
- if (myh == NULL)
- asprintf (error_message, _("unable to find ARM glue '%s' for '%s'"),
- tmp_name, name);
+ if (myh == NULL
+ && asprintf (error_message, _("unable to find ARM glue '%s' for '%s'"),
+ tmp_name, name) == -1)
+ *error_message = (char *) bfd_errmsg (bfd_error_system_call);
free (tmp_name);
static void check_use_blx(struct elf32_arm_link_hash_table *globals)
{
- if (elf32_arm_get_eabi_attr_int (globals->obfd, Tag_CPU_arch) > 2)
+ if (bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch) > 2)
globals->use_blx = 1;
}
if (link_info->relocatable)
return TRUE;
- /* Here we have a bfd that is to be included on the link. We have a hook
- to do reloc rummaging, before section sizes are nailed down. */
+ /* Here we have a bfd that is to be included on the link. We have a
+ hook to do reloc rummaging, before section sizes are nailed down. */
globals = elf32_arm_hash_table (link_info);
- check_use_blx (globals);
BFD_ASSERT (globals != NULL);
- BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
+
+ check_use_blx (globals);
if (globals->byteswap_code && !bfd_big_endian (abfd))
{
return FALSE;
}
+ /* PR 5398: If we have not decided to include any loadable sections in
+ the output then we will not have a glue owner bfd. This is OK, it
+ just means that there is nothing else for us to do here. */
+ if (globals->bfd_of_glue_owner == NULL)
+ return TRUE;
+
/* Rummage around all the relocs and map the glue vectors. */
sec = abfd->sections;
&& r_type != R_ARM_PLT32
&& r_type != R_ARM_CALL
&& r_type != R_ARM_JUMP24
- && r_type != R_ARM_THM_CALL)
+ && r_type != R_ARM_THM_CALL
+ && r_type != R_ARM_THM_JUMP24)
continue;
/* Get the section contents if we haven't done so already. */
break;
case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
/* This one is a call from thumb code. We look
up the target of the call. If it is not a thumb
target, we insert glue. */
- if (ELF_ST_TYPE (h->type) != STT_ARM_TFUNC && !globals->use_blx)
+ if (ELF_ST_TYPE (h->type) != STT_ARM_TFUNC
+ && !(globals->use_blx && r_type == R_ARM_THM_CALL)
+ && h->root.type != bfd_link_hash_undefweak)
record_thumb_to_arm_glue (link_info, h);
break;
bfd_elf32_arm_set_vfp11_fix (bfd *obfd, struct bfd_link_info *link_info)
{
struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
- aeabi_attribute *out_attr = elf32_arm_tdata (obfd)->known_eabi_attributes;
+ obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd);
/* We assume that ARMv7+ does not need the VFP11 denorm erratum fix. */
if (out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V7)
static int using_thumb2 (struct elf32_arm_link_hash_table *globals)
{
- int arch = elf32_arm_get_eabi_attr_int (globals->obfd, Tag_CPU_arch);
+ int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch);
return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7;
}
signed_addend = value;
signed_addend >>= howto->rightshift;
- /* It is not an error for an undefined weak reference to be
- out of range. Any program that branches to such a symbol
- is going to crash anyway, so there is no point worrying
- about getting the destination exactly right. */
- if (! h || h->root.type != bfd_link_hash_undefweak)
+ /* A branch to an undefined weak symbol is turned into a jump to
+ the next instruction. */
+ if (h && h->root.type == bfd_link_hash_undefweak)
+ {
+ value = (bfd_get_32 (input_bfd, hit_data) & 0xf0000000)
+ | 0x0affffff;
+ }
+ else
{
/* Perform a signed range check. */
if ( signed_addend > ((bfd_signed_vma) (howto->dst_mask >> 1))
|| signed_addend < - ((bfd_signed_vma) ((howto->dst_mask + 1) >> 1)))
return bfd_reloc_overflow;
- }
- addend = (value & 2);
+ addend = (value & 2);
- value = (signed_addend & howto->dst_mask)
- | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask));
+ value = (signed_addend & howto->dst_mask)
+ | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask));
- /* Set the H bit in the BLX instruction. */
- if (sym_flags == STT_ARM_TFUNC)
- {
- if (addend)
- value |= (1 << 24);
- else
- value &= ~(bfd_vma)(1 << 24);
- }
- if (r_type == R_ARM_CALL)
- {
- /* Select the correct instruction (BL or BLX). */
+ /* Set the H bit in the BLX instruction. */
if (sym_flags == STT_ARM_TFUNC)
- value |= (1 << 28);
- else
{
- value &= ~(bfd_vma)(1 << 28);
- value |= (1 << 24);
+ if (addend)
+ value |= (1 << 24);
+ else
+ value &= ~(bfd_vma)(1 << 24);
+ }
+ if (r_type == R_ARM_CALL)
+ {
+ /* Select the correct instruction (BL or BLX). */
+ if (sym_flags == STT_ARM_TFUNC)
+ value |= (1 << 28);
+ else
+ {
+ value &= ~(bfd_vma)(1 << 28);
+ value |= (1 << 24);
+ }
}
}
break;
case R_ARM_THM_XPC22:
case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
/* Thumb BL (branch long instruction). */
{
bfd_vma relocation;
int bitsize;
int thumb2 = using_thumb2 (globals);
+ /* A branch to an undefined weak symbol is turned into a jump to
+ the next instruction. */
+ if (h && h->root.type == bfd_link_hash_undefweak)
+ {
+ bfd_put_16 (input_bfd, 0xe000, hit_data);
+ bfd_put_16 (input_bfd, 0xbf00, hit_data + 2);
+ return bfd_reloc_ok;
+ }
+
/* Fetch the addend. We use the Thumb-2 encoding (backwards compatible
with Thumb-1) involving the J1 and J2 bits. */
if (globals->use_rel)
&& (h == NULL || splt == NULL
|| h->plt.offset == (bfd_vma) -1))
{
- if (globals->use_blx)
+ if (globals->use_blx && r_type == R_ARM_THM_CALL)
{
/* Convert BL to BLX. */
lower_insn = (lower_insn & ~0x1000) | 0x0800;
else
return bfd_reloc_dangerous;
}
- else if (sym_flags == STT_ARM_TFUNC && globals->use_blx)
+ else if (sym_flags == STT_ARM_TFUNC && globals->use_blx
+ && r_type == R_ARM_THM_CALL)
{
/* Make sure this is a BL. */
lower_insn |= 0x1800;
value = (splt->output_section->vma
+ splt->output_offset
+ h->plt.offset);
- if (globals->use_blx)
+ if (globals->use_blx && r_type == R_ARM_THM_CALL)
{
/* If the Thumb BLX instruction is available, convert the
BL to a BLX instruction to call the ARM-mode PLT entry. */
if (signed_check > reloc_signed_max || signed_check < reloc_signed_min)
overflow = TRUE;
- if ((lower_insn & 0x1800) == 0x0800)
+ if ((lower_insn & 0x5000) == 0x4000)
/* For a BLX instruction, make sure that the relocation is rounded up
to a word boundary. This follows the semantics of the instruction
which specifies that bit 1 of the target address will come from bit
}
break;
- case R_ARM_THM_JUMP24:
- /* Thumb32 unconditional branch instruction. */
- {
- bfd_vma relocation;
- bfd_boolean overflow = FALSE;
- bfd_vma upper_insn = bfd_get_16 (input_bfd, hit_data);
- bfd_vma lower_insn = bfd_get_16 (input_bfd, hit_data + 2);
- bfd_signed_vma reloc_signed_max = ((1 << (howto->bitsize - 1)) - 1) >> howto->rightshift;
- bfd_signed_vma reloc_signed_min = ~ reloc_signed_max;
- bfd_vma check;
- bfd_signed_vma signed_check;
-
- /* Need to refetch the addend, reconstruct the top three bits, and glue the
- two pieces together. */
- if (globals->use_rel)
- {
- bfd_vma S = (upper_insn & 0x0400) >> 10;
- bfd_vma hi = (upper_insn & 0x03ff);
- bfd_vma I1 = (lower_insn & 0x2000) >> 13;
- bfd_vma I2 = (lower_insn & 0x0800) >> 11;
- bfd_vma lo = (lower_insn & 0x07ff);
-
- I1 = !(I1 ^ S);
- I2 = !(I2 ^ S);
- S = !S;
-
- signed_addend = (S << 24) | (I1 << 23) | (I2 << 22) | (hi << 12) | (lo << 1);
- signed_addend -= (1 << 24); /* Sign extend. */
- }
-
- /* ??? Should handle interworking? GCC might someday try to
- use this for tail calls. */
-
- relocation = value + signed_addend;
- relocation -= (input_section->output_section->vma
- + input_section->output_offset
- + rel->r_offset);
-
- check = relocation >> howto->rightshift;
-
- /* If this is a signed value, the rightshift just dropped
- leading 1 bits (assuming twos complement). */
- if ((bfd_signed_vma) relocation >= 0)
- signed_check = check;
- else
- signed_check = check | ~((bfd_vma) -1 >> howto->rightshift);
-
- /* Assumes two's complement. */
- if (signed_check > reloc_signed_max || signed_check < reloc_signed_min)
- overflow = TRUE;
-
- /* Put RELOCATION back into the insn. */
- {
- bfd_vma S = (relocation & 0x01000000) >> 24;
- bfd_vma I1 = (relocation & 0x00800000) >> 23;
- bfd_vma I2 = (relocation & 0x00400000) >> 22;
- bfd_vma hi = (relocation & 0x003ff000) >> 12;
- bfd_vma lo = (relocation & 0x00000ffe) >> 1;
-
- I1 = !(I1 ^ S);
- I2 = !(I2 ^ S);
-
- upper_insn = (upper_insn & (bfd_vma) 0xf800) | (S << 10) | hi;
- lower_insn = (lower_insn & (bfd_vma) 0xd000) | (I1 << 13) | (I2 << 11) | lo;
- }
-
- /* Put the relocated value back in the object file: */
- bfd_put_16 (input_bfd, upper_insn, hit_data);
- bfd_put_16 (input_bfd, lower_insn, hit_data + 2);
-
- return (overflow ? bfd_reloc_overflow : bfd_reloc_ok);
- }
-
case R_ARM_THM_JUMP19:
/* Thumb32 conditional branch instruction. */
{
signed_addend = addend;
}
+ /* Handle calls via the PLT. */
+ if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1)
+ {
+ value = (splt->output_section->vma
+ + splt->output_offset
+ + h->plt.offset);
+ /* Target the Thumb stub before the ARM PLT entry. */
+ value -= PLT_THUMB_STUB_SIZE;
+ *unresolved_reloc_p = FALSE;
+ }
+
/* ??? Should handle interworking? GCC might someday try to
use this for tail calls. */
}
}
-
-static int
-uleb128_size (unsigned int i)
-{
- int size;
- size = 1;
- while (i >= 0x80)
- {
- i >>= 7;
- size++;
- }
- return size;
-}
-
-/* Return TRUE if the attribute has the default value (0/""). */
-static bfd_boolean
-is_default_attr (aeabi_attribute *attr)
-{
- if ((attr->type & 1) && attr->i != 0)
- return FALSE;
- if ((attr->type & 2) && attr->s && *attr->s)
- return FALSE;
-
- return TRUE;
-}
-
-/* Return the size of a single attribute. */
-static bfd_vma
-eabi_attr_size(int tag, aeabi_attribute *attr)
-{
- bfd_vma size;
-
- if (is_default_attr (attr))
- return 0;
-
- size = uleb128_size (tag);
- if (attr->type & 1)
- size += uleb128_size (attr->i);
- if (attr->type & 2)
- size += strlen ((char *)attr->s) + 1;
- return size;
-}
-
-/* Returns the size of the eabi object attributess section. */
-bfd_vma
-elf32_arm_eabi_attr_size (bfd *abfd)
-{
- bfd_vma size;
- aeabi_attribute *attr;
- aeabi_attribute_list *list;
- int i;
-
- attr = elf32_arm_tdata (abfd)->known_eabi_attributes;
- size = 16; /* 'A' <size> "aeabi" 0x1 <size>. */
- for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
- size += eabi_attr_size (i, &attr[i]);
-
- for (list = elf32_arm_tdata (abfd)->other_eabi_attributes;
- list;
- list = list->next)
- size += eabi_attr_size (list->tag, &list->attr);
-
- return size;
-}
-
-static bfd_byte *
-write_uleb128 (bfd_byte *p, unsigned int val)
-{
- bfd_byte c;
- do
- {
- c = val & 0x7f;
- val >>= 7;
- if (val)
- c |= 0x80;
- *(p++) = c;
- }
- while (val);
- return p;
-}
-
-/* Write attribute ATTR to butter P, and return a pointer to the following
- byte. */
-static bfd_byte *
-write_eabi_attribute (bfd_byte *p, int tag, aeabi_attribute *attr)
-{
- /* Suppress default entries. */
- if (is_default_attr(attr))
- return p;
-
- p = write_uleb128 (p, tag);
- if (attr->type & 1)
- p = write_uleb128 (p, attr->i);
- if (attr->type & 2)
- {
- int len;
-
- len = strlen (attr->s) + 1;
- memcpy (p, attr->s, len);
- p += len;
- }
-
- return p;
-}
-
-/* Write the contents of the eabi attributes section to p. */
-void
-elf32_arm_set_eabi_attr_contents (bfd *abfd, bfd_byte *contents, bfd_vma size)
-{
- bfd_byte *p;
- aeabi_attribute *attr;
- aeabi_attribute_list *list;
- int i;
-
- p = contents;
- *(p++) = 'A';
- bfd_put_32 (abfd, size - 1, p);
- p += 4;
- memcpy (p, "aeabi", 6);
- p += 6;
- *(p++) = Tag_File;
- bfd_put_32 (abfd, size - 11, p);
- p += 4;
-
- attr = elf32_arm_tdata (abfd)->known_eabi_attributes;
- for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
- p = write_eabi_attribute (p, i, &attr[i]);
-
- for (list = elf32_arm_tdata (abfd)->other_eabi_attributes;
- list;
- list = list->next)
- p = write_eabi_attribute (p, list->tag, &list->attr);
-}
-
-/* Override final_link to handle EABI object attribute sections. */
-
-static bfd_boolean
-elf32_arm_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
-{
- asection *o;
- struct bfd_link_order *p;
- asection *attr_section = NULL;
- bfd_byte *contents;
- bfd_vma size = 0;
-
- /* elf32_arm_merge_private_bfd_data will already have merged the
- object attributes. Remove the input sections from the link, and set
- the contents of the output secton. */
- for (o = abfd->sections; o != NULL; o = o->next)
- {
- if (strcmp (o->name, ".ARM.attributes") == 0)
- {
- for (p = o->map_head.link_order; p != NULL; p = p->next)
- {
- asection *input_section;
-
- if (p->type != bfd_indirect_link_order)
- continue;
- input_section = p->u.indirect.section;
- /* Hack: reset the SEC_HAS_CONTENTS flag so that
- elf_link_input_bfd ignores this section. */
- input_section->flags &= ~SEC_HAS_CONTENTS;
- }
-
- size = elf32_arm_eabi_attr_size (abfd);
- bfd_set_section_size (abfd, o, size);
- attr_section = o;
- /* Skip this section later on. */
- o->map_head.link_order = NULL;
- }
- }
- /* Invoke the ELF linker to do all the work. */
- if (!bfd_elf_final_link (abfd, info))
- return FALSE;
-
- if (attr_section)
- {
- contents = bfd_malloc(size);
- if (contents == NULL)
- return FALSE;
- elf32_arm_set_eabi_attr_contents (abfd, contents, size);
- bfd_set_section_contents (abfd, attr_section, contents, 0, size);
- free (contents);
- }
- return TRUE;
-}
-
-
/* Add INCREMENT to the reloc (of type HOWTO) at ADDRESS. */
static void
arm_add_to_rel (bfd * abfd,
{
bfd_signed_vma addend;
- if (howto->type == R_ARM_THM_CALL)
+ if (howto->type == R_ARM_THM_CALL
+ || howto->type == R_ARM_THM_JUMP24)
{
int upper_insn, lower_insn;
int upper, lower;
return TRUE;
}
-/* Allocate/find an object attribute. */
-static aeabi_attribute *
-elf32_arm_new_eabi_attr (bfd *abfd, int tag)
-{
- aeabi_attribute *attr;
- aeabi_attribute_list *list;
- aeabi_attribute_list *p;
- aeabi_attribute_list **lastp;
-
-
- if (tag < NUM_KNOWN_ATTRIBUTES)
- {
- /* Knwon tags are preallocated. */
- attr = &elf32_arm_tdata (abfd)->known_eabi_attributes[tag];
- }
- else
- {
- /* Create a new tag. */
- list = (aeabi_attribute_list *)
- bfd_alloc (abfd, sizeof (aeabi_attribute_list));
- memset (list, 0, sizeof (aeabi_attribute_list));
- list->tag = tag;
- /* Keep the tag list in order. */
- lastp = &elf32_arm_tdata (abfd)->other_eabi_attributes;
- for (p = *lastp; p; p = p->next)
- {
- if (tag < p->tag)
- break;
- lastp = &p->next;
- }
- list->next = *lastp;
- *lastp = list;
- attr = &list->attr;
- }
-
- return attr;
-}
-
-int
-elf32_arm_get_eabi_attr_int (bfd *abfd, int tag)
-{
- aeabi_attribute_list *p;
-
- if (tag < NUM_KNOWN_ATTRIBUTES)
- {
- /* Knwon tags are preallocated. */
- return elf32_arm_tdata (abfd)->known_eabi_attributes[tag].i;
- }
- else
- {
- for (p = elf32_arm_tdata (abfd)->other_eabi_attributes;
- p;
- p = p->next)
- {
- if (tag == p->tag)
- return p->attr.i;
- if (tag < p->tag)
- break;
- }
- return 0;
- }
-}
-
-void
-elf32_arm_add_eabi_attr_int (bfd *abfd, int tag, unsigned int i)
-{
- aeabi_attribute *attr;
-
- attr = elf32_arm_new_eabi_attr (abfd, tag);
- attr->type = 1;
- attr->i = i;
-}
-
-static char *
-attr_strdup (bfd *abfd, const char * s)
-{
- char * p;
- int len;
-
- len = strlen (s) + 1;
- p = (char *)bfd_alloc(abfd, len);
- return memcpy (p, s, len);
-}
-
-void
-elf32_arm_add_eabi_attr_string (bfd *abfd, int tag, const char *s)
-{
- aeabi_attribute *attr;
-
- attr = elf32_arm_new_eabi_attr (abfd, tag);
- attr->type = 2;
- attr->s = attr_strdup (abfd, s);
-}
-
-void
-elf32_arm_add_eabi_attr_compat (bfd *abfd, unsigned int i, const char *s)
-{
- aeabi_attribute_list *list;
- aeabi_attribute_list *p;
- aeabi_attribute_list **lastp;
-
- list = (aeabi_attribute_list *)
- bfd_alloc (abfd, sizeof (aeabi_attribute_list));
- memset (list, 0, sizeof (aeabi_attribute_list));
- list->tag = Tag_compatibility;
- list->attr.type = 3;
- list->attr.i = i;
- list->attr.s = attr_strdup (abfd, s);
-
- lastp = &elf32_arm_tdata (abfd)->other_eabi_attributes;
- for (p = *lastp; p; p = p->next)
- {
- int cmp;
- if (p->tag != Tag_compatibility)
- break;
- cmp = strcmp(s, p->attr.s);
- if (cmp < 0 || (cmp == 0 && i < p->attr.i))
- break;
- lastp = &p->next;
- }
- list->next = *lastp;
- *lastp = list;
-}
-
/* Set the right machine number. */
static bfd_boolean
return TRUE;
}
-/* Copy the eabi object attribute from IBFD to OBFD. */
-static void
-copy_eabi_attributes (bfd *ibfd, bfd *obfd)
-{
- aeabi_attribute *in_attr;
- aeabi_attribute *out_attr;
- aeabi_attribute_list *list;
- int i;
-
- in_attr = &elf32_arm_tdata (ibfd)->known_eabi_attributes[4];
- out_attr = &elf32_arm_tdata (obfd)->known_eabi_attributes[4];
- for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
- {
- out_attr->i = in_attr->i;
- if (in_attr->s && *in_attr->s)
- out_attr->s = attr_strdup (obfd, in_attr->s);
- in_attr++;
- out_attr++;
- }
-
- for (list = elf32_arm_tdata (ibfd)->other_eabi_attributes;
- list;
- list = list->next)
- {
- in_attr = &list->attr;
- switch (in_attr->type)
- {
- case 1:
- elf32_arm_add_eabi_attr_int (obfd, list->tag, in_attr->i);
- break;
- case 2:
- elf32_arm_add_eabi_attr_string (obfd, list->tag, in_attr->s);
- break;
- case 3:
- elf32_arm_add_eabi_attr_compat (obfd, in_attr->i, in_attr->s);
- break;
- default:
- abort();
- }
- }
-}
-
-
/* Copy backend specific data from one object module to another. */
static bfd_boolean
elf_elfheader (obfd)->e_ident[EI_OSABI] =
elf_elfheader (ibfd)->e_ident[EI_OSABI];
- /* Copy EABI object attributes. */
- copy_eabi_attributes (ibfd, obfd);
+ /* Copy object attributes. */
+ _bfd_elf_copy_obj_attributes (ibfd, obfd);
return TRUE;
}
AEABI_enum_forced_wide
};
+/* Determine whether an object attribute tag takes an integer, a
+ string or both. */
+static int
+elf32_arm_obj_attrs_arg_type (int tag)
+{
+ if (tag == Tag_compatibility)
+ return 3;
+ else if (tag == 4 || tag == 5)
+ return 2;
+ else if (tag < 32)
+ return 1;
+ else
+ return (tag & 1) != 0 ? 2 : 1;
+}
+
/* Merge EABI object attributes from IBFD into OBFD. Raise an error if there
are conflicting attributes. */
static bfd_boolean
elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
{
- aeabi_attribute *in_attr;
- aeabi_attribute *out_attr;
- aeabi_attribute_list *in_list;
- aeabi_attribute_list *out_list;
+ obj_attribute *in_attr;
+ obj_attribute *out_attr;
+ obj_attribute_list *in_list;
/* Some tags have 0 = don't care, 1 = strong requirement,
2 = weak requirement. */
static const int order_312[3] = {3, 1, 2};
int i;
- if (!elf32_arm_tdata (obfd)->known_eabi_attributes[0].i)
+ if (!elf_known_obj_attributes_proc (obfd)[0].i)
{
/* This is the first object. Copy the attributes. */
- copy_eabi_attributes (ibfd, obfd);
+ _bfd_elf_copy_obj_attributes (ibfd, obfd);
/* Use the Tag_null value to indicate the attributes have been
initialized. */
- elf32_arm_tdata (obfd)->known_eabi_attributes[0].i = 1;
+ elf_known_obj_attributes_proc (obfd)[0].i = 1;
return TRUE;
}
- in_attr = elf32_arm_tdata (ibfd)->known_eabi_attributes;
- out_attr = elf32_arm_tdata (obfd)->known_eabi_attributes;
+ in_attr = elf_known_obj_attributes_proc (ibfd);
+ out_attr = elf_known_obj_attributes_proc (obfd);
/* This needs to happen before Tag_ABI_FP_number_model is merged. */
if (in_attr[Tag_ABI_VFP_args].i != out_attr[Tag_ABI_VFP_args].i)
{
}
}
- for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+ for (i = 4; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
{
/* Merge this attribute with existing attributes. */
switch (i)
name is non-NULL. */
if (in_attr[Tag_CPU_arch].i > out_attr[Tag_CPU_arch].i
&& in_attr[i].s)
- out_attr[i].s = attr_strdup(obfd, in_attr[i].s);
+ out_attr[i].s = _bfd_elf_attr_strdup (obfd, in_attr[i].s);
break;
case Tag_ABI_optimization_goals:
default: /* All known attributes should be explicitly covered. */
abort ();
}
- }
- in_list = elf32_arm_tdata (ibfd)->other_eabi_attributes;
- out_list = elf32_arm_tdata (ibfd)->other_eabi_attributes;
- while (in_list && in_list->tag == Tag_compatibility)
- {
- in_attr = &in_list->attr;
- if (in_attr->i == 0)
- continue;
- if (in_attr->i == 1)
- {
- _bfd_error_handler
- (_("ERROR: %B: Must be processed by '%s' toolchain"),
- ibfd, in_attr->s);
- return FALSE;
- }
- if (!out_list || out_list->tag != Tag_compatibility
- || strcmp (in_attr->s, out_list->attr.s) != 0)
- {
- /* Add this compatibility tag to the output. */
- elf32_arm_add_eabi_attr_compat (obfd, in_attr->i, in_attr->s);
- continue;
- }
- out_attr = &out_list->attr;
- /* Check all the input tags with the same identifier. */
- for (;;)
- {
- if (out_list->tag != Tag_compatibility
- || in_attr->i != out_attr->i
- || strcmp (in_attr->s, out_attr->s) != 0)
- {
- _bfd_error_handler
- (_("ERROR: %B: Incompatible object tag '%s':%d"),
- ibfd, in_attr->s, in_attr->i);
- return FALSE;
- }
- in_list = in_list->next;
- if (in_list->tag != Tag_compatibility
- || strcmp (in_attr->s, in_list->attr.s) != 0)
+ if (in_attr[i].type && !out_attr[i].type)
+ switch (in_attr[i].type)
+ {
+ case 1:
+ if (out_attr[i].i)
+ out_attr[i].type = 1;
break;
- in_attr = &in_list->attr;
- out_list = out_list->next;
- if (out_list)
- out_attr = &out_list->attr;
- }
- /* Check the output doesn't have extra tags with this identifier. */
- if (out_list && out_list->tag == Tag_compatibility
- && strcmp (in_attr->s, out_list->attr.s) == 0)
- {
- _bfd_error_handler
- (_("ERROR: %B: Incompatible object tag '%s':%d"),
- ibfd, in_attr->s, out_list->attr.i);
- return FALSE;
- }
+ case 2:
+ if (out_attr[i].s)
+ out_attr[i].type = 2;
+ break;
+
+ default:
+ abort ();
+ }
}
+ /* Merge Tag_compatibility attributes and any common GNU ones. */
+ _bfd_elf_merge_object_attributes (ibfd, obfd);
+
+ /* Check for any attributes not known on ARM. */
+ in_list = elf_other_obj_attributes_proc (ibfd);
+ while (in_list && in_list->tag == Tag_compatibility)
+ in_list = in_list->next;
+
for (; in_list; in_list = in_list->next)
{
if ((in_list->tag & 128) < 64)
sym_hashes = elf_sym_hashes (abfd);
local_got_refcounts = elf_local_got_refcounts (abfd);
+ check_use_blx(globals);
+
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; rel++)
{
case R_ARM_JUMP24:
case R_ARM_PREL31:
case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
+ case R_ARM_THM_JUMP19:
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
case R_ARM_MOVW_PREL_NC:
if (h->plt.refcount > 0)
{
h->plt.refcount -= 1;
- if (ELF32_R_TYPE (rel->r_info) == R_ARM_THM_CALL)
+ if (r_type == R_ARM_THM_CALL)
+ eh->plt_maybe_thumb_refcount--;
+
+ if (r_type == R_ARM_THM_JUMP24
+ || r_type == R_ARM_THM_JUMP19)
eh->plt_thumb_refcount--;
}
{
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
- struct elf_link_hash_entry **sym_hashes_end;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
bfd *dynobj;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (abfd);
- sym_hashes_end = sym_hashes
- + symtab_hdr->sh_size / sizeof (Elf32_External_Sym);
-
- if (!elf_bad_symtab (abfd))
- sym_hashes_end -= symtab_hdr->sh_info;
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
case R_ARM_JUMP24:
case R_ARM_PREL31:
case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
+ case R_ARM_THM_JUMP19:
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
case R_ARM_MOVW_PREL_NC:
it, even if it's an ABS32 relocation. */
h->plt.refcount += 1;
+ /* It's too early to use htab->use_blx here, so we have to
+ record possible blx references separately from
+ relocs that definitely need a thumb stub. */
+
if (r_type == R_ARM_THM_CALL)
+ eh->plt_maybe_thumb_refcount += 1;
+
+ if (r_type == R_ARM_THM_JUMP24
+ || r_type == R_ARM_THM_JUMP19)
eh->plt_thumb_refcount += 1;
}
/* This relocation describes which C++ vtable entries are actually
used. Record for later use during GC. */
case R_ARM_GNU_VTENTRY:
- if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
+ BFD_ASSERT (h != NULL);
+ if (h != NULL
+ && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset))
return FALSE;
break;
}
linkage table, and we can just do a PC24 reloc instead. */
h->plt.offset = (bfd_vma) -1;
eh->plt_thumb_refcount = 0;
+ eh->plt_maybe_thumb_refcount = 0;
h->needs_plt = 0;
}
the link may change h->type. So fix it now. */
h->plt.offset = (bfd_vma) -1;
eh->plt_thumb_refcount = 0;
+ eh->plt_maybe_thumb_refcount = 0;
}
/* If this is a weak symbol, and there is a real definition, the
struct elf32_arm_link_hash_table *htab;
struct elf32_arm_link_hash_entry *eh;
struct elf32_arm_relocs_copied *p;
+ bfd_signed_vma thumb_refs;
eh = (struct elf32_arm_link_hash_entry *) h;
/* If we will insert a Thumb trampoline before this PLT, leave room
for it. */
- if (!htab->use_blx && eh->plt_thumb_refcount > 0)
+ thumb_refs = eh->plt_thumb_refcount;
+ if (!htab->use_blx)
+ thumb_refs += eh->plt_maybe_thumb_refcount;
+
+ if (thumb_refs > 0)
{
h->plt.offset += PLT_THUMB_STUB_SIZE;
s->size += PLT_THUMB_STUB_SIZE;
if (!add_dynamic_entry (DT_TEXTREL, 0))
return FALSE;
}
+ if (htab->vxworks_p
+ && !elf_vxworks_add_dynamic_entries (output_bfd, info))
+ return FALSE;
}
#undef add_dynamic_entry
}
else
{
+ bfd_signed_vma thumb_refs;
/* Calculate the displacement between the PLT slot and the
entry in the GOT. The eight-byte offset accounts for the
value produced by adding to pc in the first instruction
BFD_ASSERT ((got_displacement & 0xf0000000) == 0);
- if (!htab->use_blx && eh->plt_thumb_refcount > 0)
+ thumb_refs = eh->plt_thumb_refcount;
+ if (!htab->use_blx)
+ thumb_refs += eh->plt_maybe_thumb_refcount;
+
+ if (thumb_refs > 0)
{
put_thumb_insn (htab, output_bfd,
elf32_arm_plt_thumb_stub[0], ptr - 4);
unsigned int type;
default:
+ if (htab->vxworks_p
+ && elf_vxworks_finish_dynamic_entry (output_bfd, &dyn))
+ bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_HASH:
hdr->sh_type = SHT_ARM_EXIDX;
hdr->sh_flags |= SHF_LINK_ORDER;
}
- else if (strcmp(name, ".ARM.attributes") == 0)
- {
- hdr->sh_type = SHT_ARM_ATTRIBUTES;
- }
return TRUE;
}
-/* Parse an Arm EABI attributes section. */
-static void
-elf32_arm_parse_attributes (bfd *abfd, Elf_Internal_Shdr * hdr)
-{
- bfd_byte *contents;
- bfd_byte *p;
- bfd_vma len;
-
- contents = bfd_malloc (hdr->sh_size);
- if (!contents)
- return;
- if (!bfd_get_section_contents (abfd, hdr->bfd_section, contents, 0,
- hdr->sh_size))
- {
- free (contents);
- return;
- }
- p = contents;
- if (*(p++) == 'A')
- {
- len = hdr->sh_size - 1;
- while (len > 0)
- {
- int namelen;
- bfd_vma section_len;
-
- section_len = bfd_get_32 (abfd, p);
- p += 4;
- if (section_len > len)
- section_len = len;
- len -= section_len;
- namelen = strlen ((char *)p) + 1;
- section_len -= namelen + 4;
- if (strcmp((char *)p, "aeabi") != 0)
- {
- /* Vendor section. Ignore it. */
- p += namelen + section_len;
- }
- else
- {
- p += namelen;
- while (section_len > 0)
- {
- int tag;
- unsigned int n;
- unsigned int val;
- bfd_vma subsection_len;
- bfd_byte *end;
-
- tag = read_unsigned_leb128 (abfd, p, &n);
- p += n;
- subsection_len = bfd_get_32 (abfd, p);
- p += 4;
- if (subsection_len > section_len)
- subsection_len = section_len;
- section_len -= subsection_len;
- subsection_len -= n + 4;
- end = p + subsection_len;
- switch (tag)
- {
- case Tag_File:
- while (p < end)
- {
- bfd_boolean is_string;
-
- tag = read_unsigned_leb128 (abfd, p, &n);
- p += n;
- if (tag == 4 || tag == 5)
- is_string = 1;
- else if (tag < 32)
- is_string = 0;
- else
- is_string = (tag & 1) != 0;
- if (tag == Tag_compatibility)
- {
- val = read_unsigned_leb128 (abfd, p, &n);
- p += n;
- elf32_arm_add_eabi_attr_compat (abfd, val,
- (char *)p);
- p += strlen ((char *)p) + 1;
- }
- else if (is_string)
- {
- elf32_arm_add_eabi_attr_string (abfd, tag,
- (char *)p);
- p += strlen ((char *)p) + 1;
- }
- else
- {
- val = read_unsigned_leb128 (abfd, p, &n);
- p += n;
- elf32_arm_add_eabi_attr_int (abfd, tag, val);
- }
- }
- break;
- case Tag_Section:
- case Tag_Symbol:
- /* Don't have anywhere convenient to attach these.
- Fall through for now. */
- default:
- /* Ignore things we don't kow about. */
- p += subsection_len;
- subsection_len = 0;
- break;
- }
- }
- }
- }
- }
- free (contents);
-}
-
/* Handle an ARM specific section when reading an object file. This is
called when bfd_section_from_shdr finds a section with an unknown
type. */
if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
return FALSE;
- if (hdr->sh_type == SHT_ARM_ATTRIBUTES)
- elf32_arm_parse_attributes(abfd, hdr);
return TRUE;
}
{
void *finfo;
struct bfd_link_info *info;
- int plt_shndx;
- bfd_vma plt_offset;
+ asection *sec;
+ int sec_shndx;
bfd_boolean (*func) (void *, const char *, Elf_Internal_Sym *,
asection *, struct elf_link_hash_entry *);
} output_arch_syminfo;
Elf_Internal_Sym sym;
htab = elf32_arm_hash_table (osi->info);
- sym.st_value = osi->plt_offset + offset;
+ sym.st_value = osi->sec->output_section->vma
+ + osi->sec->output_offset
+ + offset;
sym.st_size = 0;
sym.st_other = 0;
sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
- sym.st_shndx = osi->plt_shndx;
- if (!osi->func (osi->finfo, names[type], &sym, htab->splt, NULL))
+ sym.st_shndx = osi->sec_shndx;
+ if (!osi->func (osi->finfo, names[type], &sym, osi->sec, NULL))
return FALSE;
return TRUE;
}
}
else
{
- bfd_boolean thumb_stub;
+ bfd_signed_vma thumb_refs;
+
+ thumb_refs = eh->plt_thumb_refcount;
+ if (!htab->use_blx)
+ thumb_refs += eh->plt_maybe_thumb_refcount;
- thumb_stub = eh->plt_thumb_refcount > 0 && !htab->use_blx;
- if (thumb_stub)
+ if (thumb_refs > 0)
{
if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_THUMB, addr - 4))
return FALSE;
/* A three-word PLT with no Thumb thunk contains only Arm code,
so only need to output a mapping symbol for the first PLT entry and
entries with thumb thunks. */
- if (thumb_stub || addr == 20)
+ if (thumb_refs > 0 || addr == 20)
{
if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr))
return FALSE;
}
-/* Output mapping symbols for the PLT. */
+/* Output mapping symbols for linker generated sections. */
static bfd_boolean
elf32_arm_output_arch_local_syms (bfd *output_bfd,
{
output_arch_syminfo osi;
struct elf32_arm_link_hash_table *htab;
+ bfd_vma offset;
+ bfd_size_type size;
htab = elf32_arm_hash_table (info);
- if (!htab->splt || htab->splt->size == 0)
- return TRUE;
-
check_use_blx(htab);
+
osi.finfo = finfo;
osi.info = info;
osi.func = func;
- osi.plt_shndx = _bfd_elf_section_from_bfd_section (output_bfd,
- htab->splt->output_section);
- osi.plt_offset = htab->splt->output_section->vma;
+
+ /* ARM->Thumb glue. */
+ if (htab->arm_glue_size > 0)
+ {
+ osi.sec = bfd_get_section_by_name (htab->bfd_of_glue_owner,
+ ARM2THUMB_GLUE_SECTION_NAME);
+
+ osi.sec_shndx = _bfd_elf_section_from_bfd_section
+ (output_bfd, osi.sec->output_section);
+ if (info->shared || htab->root.is_relocatable_executable
+ || htab->pic_veneer)
+ size = ARM2THUMB_PIC_GLUE_SIZE;
+ else if (htab->use_blx)
+ size = ARM2THUMB_V5_STATIC_GLUE_SIZE;
+ else
+ size = ARM2THUMB_STATIC_GLUE_SIZE;
+ for (offset = 0; offset < htab->arm_glue_size; offset += size)
+ {
+ elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset);
+ elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, offset + size - 4);
+ }
+ }
+
+ /* Thumb->ARM glue. */
+ if (htab->thumb_glue_size > 0)
+ {
+ osi.sec = bfd_get_section_by_name (htab->bfd_of_glue_owner,
+ THUMB2ARM_GLUE_SECTION_NAME);
+
+ osi.sec_shndx = _bfd_elf_section_from_bfd_section
+ (output_bfd, osi.sec->output_section);
+ size = THUMB2ARM_GLUE_SIZE;
+
+ for (offset = 0; offset < htab->thumb_glue_size; offset += size)
+ {
+ elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_THUMB, offset);
+ elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset + 4);
+ }
+ }
+
+ /* Finally, output mapping symbols for the PLT. */
+ if (!htab->splt || htab->splt->size == 0)
+ return TRUE;
+
+ osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd,
+ htab->splt->output_section);
+ osi.sec = htab->splt;
/* Output mapping symbols for the plt header. SymbianOS does not have a
plt header. */
if (htab->vxworks_p)
static int
elf32_arm_compare_mapping (const void * a, const void * b)
{
- return ((const elf32_arm_section_map *) a)->vma
- > ((const elf32_arm_section_map *) b)->vma;
+ const elf32_arm_section_map *amap = (const elf32_arm_section_map *) a;
+ const elf32_arm_section_map *bmap = (const elf32_arm_section_map *) b;
+
+ if (amap->vma > bmap->vma)
+ return 1;
+ else if (amap->vma < bmap->vma)
+ return -1;
+ else if (amap->type > bmap->type)
+ /* Ensure results do not depend on the host qsort for objects with
+ multiple mapping symbols at the same address by sorting on type
+ after vma. */
+ return 1;
+ else if (amap->type < bmap->type)
+ return -1;
+ else
+ return 0;
}
ELFCLASS32, EV_CURRENT,
bfd_elf32_write_out_phdrs,
bfd_elf32_write_shdrs_and_ehdr,
+ bfd_elf32_checksum_contents,
bfd_elf32_write_relocs,
elf32_arm_swap_symbol_in,
elf32_arm_swap_symbol_out,
#define bfd_elf32_bfd_is_target_special_symbol elf32_arm_is_target_special_symbol
#define bfd_elf32_close_and_cleanup elf32_arm_close_and_cleanup
#define bfd_elf32_bfd_free_cached_info elf32_arm_bfd_free_cached_info
-#define bfd_elf32_bfd_final_link elf32_arm_bfd_final_link
#define elf_backend_get_symbol_type elf32_arm_get_symbol_type
#define elf_backend_gc_mark_hook elf32_arm_gc_mark_hook
#define elf_backend_got_header_size 12
+#undef elf_backend_obj_attrs_vendor
+#define elf_backend_obj_attrs_vendor "aeabi"
+#undef elf_backend_obj_attrs_section
+#define elf_backend_obj_attrs_section ".ARM.attributes"
+#undef elf_backend_obj_attrs_arg_type
+#define elf_backend_obj_attrs_arg_type elf32_arm_obj_attrs_arg_type
+#undef elf_backend_obj_attrs_section_type
+#define elf_backend_obj_attrs_section_type SHT_ARM_ATTRIBUTES
+
#include "elf32-target.h"
/* VxWorks Targets */