/* 32-bit ELF support for ARM
- Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
-#include "elf/arm.h"
#include "bfd.h"
#include "sysdep.h"
+#include "libiberty.h"
#include "libbfd.h"
#include "elf-bfd.h"
+#include "elf-vxworks.h"
+#include "elf/arm.h"
#ifndef NUM_ELEM
#define NUM_ELEM(a) (sizeof (a) / (sizeof (a)[0]))
#endif
+/* Return the relocation section associated with NAME. HTAB is the
+ bfd's elf32_arm_link_hash_entry. */
+#define RELOC_SECTION(HTAB, NAME) \
+ ((HTAB)->use_rel ? ".rel" NAME : ".rela" NAME)
+
+/* Return size of a relocation entry. HTAB is the bfd's
+ elf32_arm_link_hash_entry. */
+#define RELOC_SIZE(HTAB) \
+ ((HTAB)->use_rel \
+ ? sizeof (Elf32_External_Rel) \
+ : sizeof (Elf32_External_Rela))
+
+/* Return function to swap relocations in. HTAB is the bfd's
+ elf32_arm_link_hash_entry. */
+#define SWAP_RELOC_IN(HTAB) \
+ ((HTAB)->use_rel \
+ ? bfd_elf32_swap_reloc_in \
+ : bfd_elf32_swap_reloca_in)
+
+/* Return function to swap relocations out. HTAB is the bfd's
+ elf32_arm_link_hash_entry. */
+#define SWAP_RELOC_OUT(HTAB) \
+ ((HTAB)->use_rel \
+ ? bfd_elf32_swap_reloc_out \
+ : bfd_elf32_swap_reloca_out)
+
#define elf_info_to_howto 0
#define elf_info_to_howto_rel elf32_arm_info_to_howto
#define ARM_ELF_ABI_VERSION 0
#define ARM_ELF_OS_ABI_VERSION ELFOSABI_ARM
+static const struct elf_backend_data elf32_arm_vxworks_bed;
+
/* Note: code such as elf32_arm_reloc_type_lookup expect to use e.g.
R_ARM_PC24 as an index into this, and find the R_ARM_PC24 HOWTO
in that slot. */
bfd_elf_generic_reloc, /* special_function */
"R_ARM_ABS12", /* name */
FALSE, /* partial_inplace */
- 0x000008ff, /* src_mask */
- 0x000008ff, /* dst_mask */
+ 0x00000fff, /* src_mask */
+ 0x00000fff, /* dst_mask */
FALSE), /* pcrel_offset */
HOWTO (R_ARM_THM_ABS5, /* type */
{
{BFD_RELOC_NONE, R_ARM_NONE},
{BFD_RELOC_ARM_PCREL_BRANCH, R_ARM_PC24},
+ {BFD_RELOC_ARM_PCREL_CALL, R_ARM_CALL},
+ {BFD_RELOC_ARM_PCREL_JUMP, R_ARM_JUMP24},
{BFD_RELOC_ARM_PCREL_BLX, R_ARM_XPC25},
{BFD_RELOC_THUMB_PCREL_BLX, R_ARM_THM_XPC22},
{BFD_RELOC_32, R_ARM_ABS32},
typedef unsigned long int insn32;
typedef unsigned short int insn16;
-/* In lieu of proper flags, assume all EABIv4 objects are interworkable. */
+/* In lieu of proper flags, assume all EABIv4 or later objects are
+ interworkable. */
#define INTERWORK_FLAG(abfd) \
- (EF_ARM_EABI_VERSION (elf_elfheader (abfd)->e_flags) == EF_ARM_EABI_VER4 \
+ (EF_ARM_EABI_VERSION (elf_elfheader (abfd)->e_flags) >= EF_ARM_EABI_VER4 \
|| (elf_elfheader (abfd)->e_flags & EF_ARM_INTERWORK))
/* The linker script knows the section names for placement.
#endif
+/* The format of the first entry in the procedure linkage table
+ for a VxWorks executable. */
+static const bfd_vma elf32_arm_vxworks_exec_plt0_entry[] =
+ {
+ 0xe52dc008, /* str ip,[sp,#-8]! */
+ 0xe59fc000, /* ldr ip,[pc] */
+ 0xe59cf008, /* ldr pc,[ip,#8] */
+ 0x00000000, /* .long _GLOBAL_OFFSET_TABLE_ */
+ };
+
+/* The format of subsequent entries in a VxWorks executable. */
+static const bfd_vma elf32_arm_vxworks_exec_plt_entry[] =
+ {
+ 0xe59fc000, /* ldr ip,[pc] */
+ 0xe59cf000, /* ldr pc,[ip] */
+ 0x00000000, /* .long @got */
+ 0xe59fc000, /* ldr ip,[pc] */
+ 0xea000000, /* b _PLT */
+ 0x00000000, /* .long @pltindex*sizeof(Elf32_Rela) */
+ };
+
+/* The format of entries in a VxWorks shared library. */
+static const bfd_vma elf32_arm_vxworks_shared_plt_entry[] =
+ {
+ 0xe59fc000, /* ldr ip,[pc] */
+ 0xe79cf009, /* ldr pc,[ip,r9] */
+ 0x00000000, /* .long @got */
+ 0xe59fc000, /* ldr ip,[pc] */
+ 0xe599f008, /* ldr pc,[r9,#8] */
+ 0x00000000, /* .long @pltindex*sizeof(Elf32_Rela) */
+ };
+
/* An initial stub used if the PLT entry is referenced from Thumb code. */
#define PLT_THUMB_STUB_SIZE 4
static const bfd_vma elf32_arm_plt_thumb_stub [] =
/* 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;
};
#define elf32_arm_tdata(abfd) \
/* The number of bytes in the subsequent PLT etries. */
bfd_size_type plt_entry_size;
+ /* True if the target system is VxWorks. */
+ int vxworks_p;
+
/* True if the target system is Symbian OS. */
int symbian_p;
asection *sdynbss;
asection *srelbss;
+ /* The (unloaded but important) VxWorks .rela.plt.unloaded section. */
+ asection *srelplt2;
+
/* Data for R_ARM_TLS_LDM32 relocations. */
union {
bfd_signed_vma refcount;
return (struct bfd_hash_entry *) ret;
}
-/* Create .got, .gotplt, and .rel.got sections in DYNOBJ, and set up
+/* Return true if NAME is the name of the relocation section associated
+ with S. */
+
+static bfd_boolean
+reloc_section_p (struct elf32_arm_link_hash_table *htab,
+ const char *name, asection *s)
+{
+ if (htab->use_rel)
+ return strncmp (name, ".rel", 4) == 0 && strcmp (s->name, name + 4) == 0;
+ else
+ return strncmp (name, ".rela", 5) == 0 && strcmp (s->name, name + 5) == 0;
+}
+
+/* Create .got, .gotplt, and .rel(a).got sections in DYNOBJ, and set up
shortcuts to them in our hash table. */
static bfd_boolean
if (!htab->sgot || !htab->sgotplt)
abort ();
- htab->srelgot = bfd_make_section_with_flags (dynobj, ".rel.got",
+ htab->srelgot = bfd_make_section_with_flags (dynobj,
+ RELOC_SECTION (htab, ".got"),
(SEC_ALLOC | SEC_LOAD
| SEC_HAS_CONTENTS
| SEC_IN_MEMORY
return TRUE;
}
-/* Create .plt, .rel.plt, .got, .got.plt, .rel.got, .dynbss, and
- .rel.bss sections in DYNOBJ, and set up shortcuts to them in our
+/* Create .plt, .rel(a).plt, .got, .got.plt, .rel(a).got, .dynbss, and
+ .rel(a).bss sections in DYNOBJ, and set up shortcuts to them in our
hash table. */
static bfd_boolean
return FALSE;
htab->splt = bfd_get_section_by_name (dynobj, ".plt");
- htab->srelplt = bfd_get_section_by_name (dynobj, ".rel.plt");
+ htab->srelplt = bfd_get_section_by_name (dynobj,
+ RELOC_SECTION (htab, ".plt"));
htab->sdynbss = bfd_get_section_by_name (dynobj, ".dynbss");
if (!info->shared)
- htab->srelbss = bfd_get_section_by_name (dynobj, ".rel.bss");
+ htab->srelbss = bfd_get_section_by_name (dynobj,
+ RELOC_SECTION (htab, ".bss"));
+
+ if (htab->vxworks_p)
+ {
+ if (!elf_vxworks_create_dynamic_sections (dynobj, info, &htab->srelplt2))
+ return FALSE;
+
+ if (info->shared)
+ {
+ htab->plt_header_size = 0;
+ htab->plt_entry_size
+ = 4 * ARRAY_SIZE (elf32_arm_vxworks_shared_plt_entry);
+ }
+ else
+ {
+ htab->plt_header_size
+ = 4 * ARRAY_SIZE (elf32_arm_vxworks_exec_plt0_entry);
+ htab->plt_entry_size
+ = 4 * ARRAY_SIZE (elf32_arm_vxworks_exec_plt_entry);
+ }
+ }
if (!htab->splt
|| !htab->srelplt
/* Copy the extra info we tack onto an elf_link_hash_entry. */
static void
-elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed,
+elf32_arm_copy_indirect_symbol (struct bfd_link_info *info,
struct elf_link_hash_entry *dir,
struct elf_link_hash_entry *ind)
{
struct elf32_arm_relocs_copied **pp;
struct elf32_arm_relocs_copied *p;
- if (ind->root.type == bfd_link_hash_indirect)
- abort ();
-
- /* Add reloc counts against the weak sym to the strong sym
+ /* Add reloc counts against the indirect sym to the direct sym
list. Merge any entries against the same section. */
for (pp = &eind->relocs_copied; (p = *pp) != NULL; )
{
eind->relocs_copied = NULL;
}
- /* If the direct symbol already has an associated PLT entry, the
- indirect symbol should not. If it doesn't, swap refcount information
- from the indirect symbol. */
- if (edir->plt_thumb_refcount == 0)
- {
- edir->plt_thumb_refcount = eind->plt_thumb_refcount;
- eind->plt_thumb_refcount = 0;
- }
- else
- BFD_ASSERT (eind->plt_thumb_refcount == 0);
+ /* Copy over PLT info. */
+ edir->plt_thumb_refcount += eind->plt_thumb_refcount;
+ eind->plt_thumb_refcount = 0;
if (ind->root.type == bfd_link_hash_indirect
&& dir->got.refcount <= 0)
eind->tls_type = GOT_UNKNOWN;
}
- _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
+ _bfd_elf_link_hash_copy_indirect (info, dir, ind);
}
/* Create an ARM elf linker hash table. */
return NULL;
if (!_bfd_elf_link_hash_table_init (& ret->root, abfd,
- elf32_arm_link_hash_newfunc))
+ elf32_arm_link_hash_newfunc,
+ sizeof (struct elf32_arm_link_hash_entry)))
{
free (ret);
return NULL;
ret->srelplt = NULL;
ret->sdynbss = NULL;
ret->srelbss = NULL;
+ ret->srelplt2 = NULL;
ret->thumb_glue_size = 0;
ret->arm_glue_size = 0;
ret->bfd_of_glue_owner = NULL;
#endif
ret->fix_v4bx = 0;
ret->use_blx = 0;
+ ret->vxworks_p = 0;
ret->symbian_p = 0;
ret->use_rel = 1;
ret->sym_sec.abfd = NULL;
return TRUE;
}
+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)
+ globals->use_blx = 1;
+}
+
bfd_boolean
bfd_elf32_arm_process_before_allocation (bfd *abfd,
struct bfd_link_info *link_info,
/* 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);
/* This one is a call from arm code. We need to look up
the target of the call. If it is a thumb target, we
insert glue. */
- if (ELF_ST_TYPE(h->type) == STT_ARM_TFUNC)
+ if (ELF_ST_TYPE(h->type) == STT_ARM_TFUNC
+ && !(r_type == R_ARM_CALL && globals->use_blx))
record_arm_to_thumb_glue (link_info, h);
break;
/* 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)
+ if (ELF_ST_TYPE (h->type) != STT_ARM_TFUNC && !globals->use_blx)
record_thumb_to_arm_glue (link_info, h);
break;
return address - htab->tls_sec->vma + base;
}
+/* Perform an R_ARM_ABS12 relocation on the field pointed to by DATA.
+ VALUE is the relocation value. */
+
+static bfd_reloc_status_type
+elf32_arm_abs12_reloc (bfd *abfd, void *data, bfd_vma value)
+{
+ if (value > 0xfff)
+ return bfd_reloc_overflow;
+
+ value |= bfd_get_32 (abfd, data) & 0xfffff000;
+ bfd_put_32 (abfd, value, data);
+ return bfd_reloc_ok;
+}
+
/* Perform a relocation as part of a final link. */
static bfd_reloc_status_type
*unresolved_reloc_p = FALSE;
return bfd_reloc_ok;
+ case R_ARM_ABS12:
+ if (!globals->vxworks_p)
+ return elf32_arm_abs12_reloc (input_bfd, hit_data, value + addend);
+
case R_ARM_PC24:
case R_ARM_ABS32:
case R_ARM_REL32:
*unresolved_reloc_p = FALSE;
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, value,
- (bfd_vma) 0);
+ rel->r_addend);
}
/* When generating a shared object or relocatable executable, these
if (name == NULL)
return bfd_reloc_notsupported;
- BFD_ASSERT (strncmp (name, ".rel", 4) == 0
- && strcmp (bfd_get_section_name (input_bfd,
- input_section),
- name + 4) == 0);
+ BFD_ASSERT (reloc_section_p (globals, name, input_section));
sreloc = bfd_get_section_by_name (dynobj, name);
BFD_ASSERT (sreloc != NULL);
skip = FALSE;
relocate = FALSE;
+ outrel.r_addend = addend;
outrel.r_offset =
_bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset);
int symbol;
/* This symbol is local, or marked to become local. */
- relocate = TRUE;
if (sym_flags == STT_ARM_TFUNC)
value |= 1;
if (globals->symbian_p)
so the symbol does not matter. */
symbol = 0;
outrel.r_info = ELF32_R_INFO (symbol, R_ARM_RELATIVE);
+ if (globals->use_rel)
+ relocate = TRUE;
+ else
+ outrel.r_addend += value;
}
loc = sreloc->contents;
- loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rel);
- bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ loc += sreloc->reloc_count++ * RELOC_SIZE (globals);
+ SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
/* If this reloc is against an external symbol, we do not want to
fiddle with the addend. Otherwise, we need to include the symbol
}
else switch (r_type)
{
+ case R_ARM_ABS12:
+ return elf32_arm_abs12_reloc (input_bfd, hit_data, value + addend);
+
case R_ARM_XPC25: /* Arm BLX instruction. */
case R_ARM_CALL:
case R_ARM_JUMP24:
input_bfd,
h ? h->root.root.string : "(local)");
}
- else
+ else if (r_type != R_ARM_CALL || !globals->use_blx)
{
/* Check for Arm calling Thumb function. */
if (sym_flags == STT_ARM_TFUNC)
return bfd_reloc_overflow;
}
- /* If necessary set the H bit in the BLX instruction. */
- if (r_type == R_ARM_XPC25 && ((value & 2) == 2))
- value = (signed_addend & howto->dst_mask)
- | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask))
- | (1 << 24);
- else
- value = (signed_addend & howto->dst_mask)
- | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask));
+ addend = (value & 2);
+
+ 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). */
+ if (sym_flags == STT_ARM_TFUNC)
+ value |= (1 << 28);
+ else
+ {
+ value &= ~(bfd_vma)(1 << 28);
+ value |= (1 << 24);
+ }
+ }
break;
case R_ARM_ABS32:
bfd_put_16 (input_bfd, value, hit_data);
return bfd_reloc_ok;
- case R_ARM_ABS12:
- /* Support ldr and str instruction for the arm */
- /* Also thumb b (unconditional branch). ??? Really? */
- value += addend;
-
- if ((long) value > 0x7ff || (long) value < -0x800)
- return bfd_reloc_overflow;
-
- value |= (bfd_get_32 (input_bfd, hit_data) & 0xfffff000);
- bfd_put_32 (input_bfd, value, hit_data);
- return bfd_reloc_ok;
-
case R_ARM_THM_ABS5:
/* Support ldr and str instructions for the thumb. */
if (globals->use_rel)
bfd_signed_vma reloc_signed_min = ~ reloc_signed_max;
bfd_vma check;
bfd_signed_vma signed_check;
- bfd_boolean thumb_plt_call = FALSE;
/* Need to refetch the addend and squish the two 11 bit pieces
together. */
&& (h == NULL || splt == NULL
|| h->plt.offset == (bfd_vma) -1))
{
- if (elf32_thumb_to_arm_stub
+ if (globals->use_blx)
+ {
+ /* Convert BL to BLX. */
+ lower_insn = (lower_insn & ~0x1000) | 0x0800;
+ }
+ else if (elf32_thumb_to_arm_stub
(info, sym_name, input_bfd, output_bfd, input_section,
hit_data, sym_sec, rel->r_offset, signed_addend, value))
return bfd_reloc_ok;
else
return bfd_reloc_dangerous;
}
+ else if (sym_flags == STT_ARM_TFUNC && globals->use_blx)
+ {
+ /* Make sure this is a BL. */
+ lower_insn |= 0x1800;
+ }
}
/* Handle calls via the PLT. */
{
/* If the Thumb BLX instruction is available, convert the
BL to a BLX instruction to call the ARM-mode PLT entry. */
- if ((lower_insn & (0x3 << 11)) == 0x3 << 11)
- {
- lower_insn = (lower_insn & ~(0x3 << 11)) | 0x1 << 11;
- thumb_plt_call = TRUE;
- }
+ lower_insn = (lower_insn & ~0x1000) | 0x0800;
}
else
/* Target the Thumb stub before the ARM PLT entry. */
if (signed_check > reloc_signed_max || signed_check < reloc_signed_min)
overflow = TRUE;
- if ((r_type == R_ARM_THM_XPC22
- && ((lower_insn & 0x1800) == 0x0800))
- || thumb_plt_call)
+ if ((lower_insn & 0x1800) == 0x0800)
/* 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
value -= sgot->output_section->vma;
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, value,
- (bfd_vma) 0);
+ rel->r_addend);
case R_ARM_GOTPC:
/* Use global offset table as symbol value. */
value = sgot->output_section->vma;
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, value,
- (bfd_vma) 0);
+ rel->r_addend);
case R_ARM_GOT32:
case R_ARM_GOT_PREL:
always be a multiple of 4, we use the least significant bit
to record whether we have initialized it already.
- When doing a dynamic link, we create a .rel.got relocation
+ When doing a dynamic link, we create a .rel(a).got relocation
entry to initialize the value. This is done in the
finish_dynamic_symbol routine. */
if ((off & 1) != 0)
if (sym_flags == STT_ARM_TFUNC)
value |= 1;
- bfd_put_32 (output_bfd, value, sgot->contents + off);
+ if (globals->use_rel)
+ bfd_put_32 (output_bfd, value, sgot->contents + off);
if (info->shared)
{
Elf_Internal_Rela outrel;
bfd_byte *loc;
- srelgot = bfd_get_section_by_name (dynobj, ".rel.got");
+ srelgot = (bfd_get_section_by_name
+ (dynobj, RELOC_SECTION (globals, ".got")));
BFD_ASSERT (srelgot != NULL);
+ outrel.r_addend = addend + value;
outrel.r_offset = (sgot->output_section->vma
+ sgot->output_offset
+ off);
outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
loc = srelgot->contents;
- loc += srelgot->reloc_count++ * sizeof (Elf32_External_Rel);
- bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ loc += srelgot->reloc_count++ * RELOC_SIZE (globals);
+ SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
}
local_got_offsets[r_symndx] |= 1;
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, value,
- (bfd_vma) 0);
+ rel->r_addend);
case R_ARM_TLS_LDO32:
value = value - dtpoff_base (info);
return _bfd_final_link_relocate (howto, input_bfd, input_section,
- contents, rel->r_offset, value, (bfd_vma) 0);
+ contents, rel->r_offset, value,
+ rel->r_addend);
case R_ARM_TLS_LDM32:
{
if (globals->srelgot == NULL)
abort ();
+ outrel.r_addend = 0;
outrel.r_offset = (globals->sgot->output_section->vma
+ globals->sgot->output_offset + off);
outrel.r_info = ELF32_R_INFO (0, R_ARM_TLS_DTPMOD32);
- bfd_put_32 (output_bfd, 0, globals->sgot->contents + off);
+ if (globals->use_rel)
+ bfd_put_32 (output_bfd, outrel.r_addend,
+ globals->sgot->contents + off);
loc = globals->srelgot->contents;
- loc += globals->srelgot->reloc_count++ * sizeof (Elf32_External_Rel);
- bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ loc += globals->srelgot->reloc_count++ * RELOC_SIZE (globals);
+ SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
}
else
bfd_put_32 (output_bfd, 1, globals->sgot->contents + off);
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, value,
- (bfd_vma) 0);
+ rel->r_addend);
}
case R_ARM_TLS_GD32:
if (globals->srelgot == NULL)
abort ();
loc = globals->srelgot->contents;
- loc += globals->srelgot->reloc_count * sizeof (Elf32_External_Rel);
+ loc += globals->srelgot->reloc_count * RELOC_SIZE (globals);
}
if (tls_type & GOT_TLS_GD)
{
if (need_relocs)
{
+ outrel.r_addend = 0;
outrel.r_offset = (globals->sgot->output_section->vma
- + globals->sgot->output_offset + cur_off);
+ + globals->sgot->output_offset
+ + cur_off);
outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DTPMOD32);
- bfd_put_32 (output_bfd, 0, globals->sgot->contents + cur_off);
- bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ if (globals->use_rel)
+ bfd_put_32 (output_bfd, outrel.r_addend,
+ globals->sgot->contents + cur_off);
+
+ SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
globals->srelgot->reloc_count++;
- loc += sizeof (Elf32_External_Rel);
+ loc += RELOC_SIZE (globals);
if (indx == 0)
bfd_put_32 (output_bfd, value - dtpoff_base (info),
globals->sgot->contents + cur_off + 4);
else
{
- bfd_put_32 (output_bfd, 0,
- globals->sgot->contents + cur_off + 4);
-
+ outrel.r_addend = 0;
outrel.r_info = ELF32_R_INFO (indx,
R_ARM_TLS_DTPOFF32);
outrel.r_offset += 4;
- bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+
+ if (globals->use_rel)
+ bfd_put_32 (output_bfd, outrel.r_addend,
+ globals->sgot->contents + cur_off + 4);
+
+
+ SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
globals->srelgot->reloc_count++;
- loc += sizeof (Elf32_External_Rel);
+ loc += RELOC_SIZE (globals);
}
}
else
{
if (need_relocs)
{
+ if (indx == 0)
+ outrel.r_addend = value - dtpoff_base (info);
+ else
+ outrel.r_addend = 0;
outrel.r_offset = (globals->sgot->output_section->vma
+ globals->sgot->output_offset
+ cur_off);
outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_TPOFF32);
- if (indx == 0)
- bfd_put_32 (output_bfd, value - dtpoff_base (info),
- globals->sgot->contents + cur_off);
- else
- bfd_put_32 (output_bfd, 0,
+ if (globals->use_rel)
+ bfd_put_32 (output_bfd, outrel.r_addend,
globals->sgot->contents + cur_off);
- bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
globals->srelgot->reloc_count++;
- loc += sizeof (Elf32_External_Rel);
+ loc += RELOC_SIZE (globals);
}
else
bfd_put_32 (output_bfd, tpoff (info, value),
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, value,
- (bfd_vma) 0);
+ rel->r_addend);
}
case R_ARM_TLS_LE32:
value = tpoff (info, value);
return _bfd_final_link_relocate (howto, input_bfd, input_section,
- contents, rel->r_offset, value, (bfd_vma) 0);
+ contents, rel->r_offset, value,
+ rel->r_addend);
case R_ARM_V4BX:
if (globals->fix_v4bx)
}
}
+
+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,
&& h->def_dynamic))
{
(*_bfd_error_handler)
- (_("%B(%A+0x%lx): warning: unresolvable relocation %d against symbol `%s'"),
- input_bfd, input_section, (long) rel->r_offset,
- r_type, h->root.root.string);
+ (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
+ input_bfd,
+ input_section,
+ (long) rel->r_offset,
+ howto->name,
+ h->root.root.string);
return FALSE;
}
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;
+ out_attr = elf32_arm_tdata (obfd)->known_eabi_attributes;
+ 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);
+
+ return TRUE;
+}
+
+/* Values for Tag_ABI_PCS_R9_use. */
+enum
+{
+ AEABI_R9_V6,
+ AEABI_R9_SB,
+ AEABI_R9_TLS,
+ AEABI_R9_unused
+};
+
+/* Values for Tag_ABI_PCS_RW_data. */
+enum
+{
+ AEABI_PCS_RW_data_absolute,
+ AEABI_PCS_RW_data_PCrel,
+ AEABI_PCS_RW_data_SBrel,
+ AEABI_PCS_RW_data_unused
+};
+
+/* Values for Tag_ABI_enum_size. */
+enum
+{
+ AEABI_enum_unused,
+ AEABI_enum_short,
+ AEABI_enum_wide,
+ AEABI_enum_forced_wide
+};
+
+/* 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;
+ /* 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 (ibfd)->known_eabi_attributes[0].i)
+ {
+ /* This is the first object. Copy the attributes. */
+ copy_eabi_attributes (ibfd, obfd);
+ return TRUE;
+ }
+
+ /* Use the Tag_null value to indicate the attributes have been
+ initialized. */
+ elf32_arm_tdata (ibfd)->known_eabi_attributes[0].i = 1;
+
+ in_attr = elf32_arm_tdata (ibfd)->known_eabi_attributes;
+ out_attr = elf32_arm_tdata (obfd)->known_eabi_attributes;
+ /* 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)
+ {
+ /* Ignore mismatches if teh object doesn't use floating point. */
+ if (out_attr[Tag_ABI_FP_number_model].i == 0)
+ out_attr[Tag_ABI_VFP_args].i = in_attr[Tag_ABI_VFP_args].i;
+ else if (in_attr[Tag_ABI_FP_number_model].i != 0)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B uses VFP register arguments, %B does not"),
+ ibfd, obfd);
+ return FALSE;
+ }
+ }
+
+ for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+ {
+ /* Merge this attribute with existing attributes. */
+ switch (i)
+ {
+ case Tag_CPU_raw_name:
+ case Tag_CPU_name:
+ /* Use whichever has the greatest architecture requirements. */
+ if (in_attr[Tag_CPU_arch].i > out_attr[Tag_CPU_arch].i)
+ out_attr[i].s = attr_strdup(obfd, in_attr[i].s);
+ break;
+
+ case Tag_ABI_optimization_goals:
+ case Tag_ABI_FP_optimization_goals:
+ /* Use the first value seen. */
+ break;
+
+ 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? */
+ case Tag_ABI_FP_rounding:
+ case Tag_ABI_FP_denormal:
+ case Tag_ABI_FP_exceptions:
+ case Tag_ABI_FP_user_exceptions:
+ case Tag_ABI_FP_number_model:
+ case Tag_ABI_align8_preserved:
+ case Tag_ABI_HardFP_use:
+ /* Use the largest value specified. */
+ if (in_attr[i].i > out_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+
+ case Tag_CPU_arch_profile:
+ /* Warn if conflicting architecture profiles used. */
+ if (out_attr[i].i && in_attr[i].i && in_attr[i].i != out_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Conflicting architecture profiles %c/%c"),
+ ibfd, in_attr[i].i, out_attr[i].i);
+ return FALSE;
+ }
+ if (in_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;
+ else if (in_attr[i].i != 0 && out_attr[i].i != 0)
+ {
+ /* It's sometimes ok to mix different configs, so this is only
+ a warning. */
+ _bfd_error_handler
+ (_("Warning: %B: Conflicting platform configuration"), ibfd);
+ }
+ break;
+ case Tag_ABI_PCS_R9_use:
+ if (out_attr[i].i != AEABI_R9_unused
+ && in_attr[i].i != AEABI_R9_unused)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Conflicting use of R9"), ibfd);
+ return FALSE;
+ }
+ if (out_attr[i].i == AEABI_R9_unused)
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_PCS_RW_data:
+ if (in_attr[i].i == AEABI_PCS_RW_data_SBrel
+ && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_SB
+ && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_unused)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: SB relative addressing conflicts with use of R9"),
+ ibfd);
+ return FALSE;
+ }
+ /* Use the smallest value specified. */
+ if (in_attr[i].i < out_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_PCS_RO_data:
+ /* Use the smallest value specified. */
+ if (in_attr[i].i < out_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_PCS_GOT_use:
+ if (in_attr[i].i > 2 || out_attr[i].i > 2
+ || order_312[in_attr[i].i] < order_312[out_attr[i].i])
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_PCS_wchar_t:
+ if (out_attr[i].i && in_attr[i].i && out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Conflicting definitions of wchar_t"), ibfd);
+ return FALSE;
+ }
+ if (in_attr[i].i)
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_align8_needed:
+ /* ??? Check against Tag_ABI_align8_preserved. */
+ if (in_attr[i].i > 2 || out_attr[i].i > 2
+ || order_312[in_attr[i].i] < order_312[out_attr[i].i])
+ out_attr[i].i = in_attr[i].i;
+ break;
+ case Tag_ABI_enum_size:
+ if (in_attr[i].i != AEABI_enum_unused)
+ {
+ if (out_attr[i].i == AEABI_enum_unused
+ || out_attr[i].i == AEABI_enum_forced_wide)
+ {
+ /* The existing object is compatible with anything.
+ Use whatever requirements the new object has. */
+ out_attr[i].i = in_attr[i].i;
+ }
+ else if (in_attr[i].i != AEABI_enum_forced_wide
+ && out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B: Conflicting enum sizes"), ibfd);
+ }
+ }
+ break;
+ case Tag_ABI_VFP_args:
+ /* Aready done. */
+ break;
+ case Tag_ABI_WMMX_args:
+ if (in_attr[i].i != out_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("ERROR: %B uses iWMMXt register arguments, %B does not"),
+ ibfd, obfd);
+ return FALSE;
+ }
+ break;
+ 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)
+ 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;
+ }
+ }
+
+ for (; in_list; in_list = in_list->next)
+ {
+ if ((in_list->tag & 128) < 64)
+ {
+ _bfd_error_handler
+ (_("Warning: %B: Unknown EABI object attribute %d"),
+ ibfd, in_list->tag);
+ break;
+ }
+ }
return TRUE;
}
+
+/* Return TRUE if the two EABI versions are incompatible. */
+
+static bfd_boolean
+elf32_arm_versions_compatible (unsigned iver, unsigned over)
+{
+ /* v4 and v5 are the same spec before and after it was released,
+ so allow mixing them. */
+ if ((iver == EF_ARM_EABI_VER4 && over == EF_ARM_EABI_VER5)
+ || (iver == EF_ARM_EABI_VER5 && over == EF_ARM_EABI_VER4))
+ return TRUE;
+
+ return (iver == over);
+}
+
/* Merge backend specific data from an object file to the output
object file when linking. */
|| bfd_get_flavour (obfd) != bfd_target_elf_flavour)
return TRUE;
+ if (!elf32_arm_merge_eabi_attributes (ibfd, obfd))
+ return FALSE;
+
/* The input BFD must have had its flags initialised. */
/* The following seems bogus to me -- The flags are initialized in
the assembler but I don't think an elf_flags_init field is
}
/* Complain about various flag mismatches. */
- if (EF_ARM_EABI_VERSION (in_flags) != EF_ARM_EABI_VERSION (out_flags))
+ if (!elf32_arm_versions_compatible (EF_ARM_EABI_VERSION (in_flags),
+ EF_ARM_EABI_VERSION (out_flags)))
{
_bfd_error_handler
(_("ERROR: Source object %B has EABI version %d, but target %B has EABI version %d"),
}
/* Not sure what needs to be checked for EABI versions >= 1. */
- if (EF_ARM_EABI_VERSION (in_flags) == EF_ARM_EABI_UNKNOWN)
+ /* VxWorks libraries do not use these flags. */
+ if (get_elf_backend_data (obfd) != &elf32_arm_vxworks_bed
+ && get_elf_backend_data (ibfd) != &elf32_arm_vxworks_bed
+ && EF_ARM_EABI_VERSION (in_flags) == EF_ARM_EABI_UNKNOWN)
{
if ((in_flags & EF_ARM_APCS_26) != (out_flags & EF_ARM_APCS_26))
{
case EF_ARM_EABI_VER4:
fprintf (file, _(" [Version4 EABI]"));
+ goto eabi;
+ case EF_ARM_EABI_VER5:
+ fprintf (file, _(" [Version5 EABI]"));
+ eabi:
if (flags & EF_ARM_BE8)
fprintf (file, _(" [BE8]"));
This allows us to distinguish between data used by Thumb instructions
and non-data (which is probably code) inside Thumb regions of an
executable. */
- if (type != STT_OBJECT)
+ if (type != STT_OBJECT && type != STT_TLS)
return ELF_ST_TYPE (elf_sym->st_info);
break;
}
break;
+ case R_ARM_ABS12:
+ /* VxWorks uses dynamic R_ARM_ABS12 relocations for
+ ldr __GOTT_INDEX__ offsets. */
+ if (!htab->vxworks_p)
+ break;
+ /* Fall through */
+
case R_ARM_ABS32:
case R_ARM_REL32:
case R_ARM_PC24:
if (name == NULL)
return FALSE;
- BFD_ASSERT (strncmp (name, ".rel", 4) == 0
- && strcmp (bfd_get_section_name (abfd, sec),
- name + 4) == 0);
+ BFD_ASSERT (reloc_section_p (htab, name, sec));
sreloc = bfd_get_section_by_name (dynobj, name);
if (sreloc == NULL)
easily. Oh well. */
asection *s;
+ void *vpp;
+
s = bfd_section_from_r_symndx (abfd, &htab->sym_sec,
sec, r_symndx);
if (s == NULL)
return FALSE;
- head = ((struct elf32_arm_relocs_copied **)
- &elf_section_data (s)->local_dynrel);
+ vpp = &elf_section_data (s)->local_dynrel;
+ head = (struct elf32_arm_relocs_copied **) vpp;
}
p = *head;
/* We must generate a R_ARM_COPY reloc to tell the dynamic linker to
copy the initial value out of the dynamic object and into the
runtime process image. We need to remember the offset into the
- .rel.bss section we are going to use. */
+ .rel(a).bss section we are going to use. */
if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
{
asection *srel;
- srel = bfd_get_section_by_name (dynobj, ".rel.bss");
+ srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (globals, ".bss"));
BFD_ASSERT (srel != NULL);
- srel->size += sizeof (Elf32_External_Rel);
+ srel->size += RELOC_SIZE (globals);
h->needs_copy = 1;
}
htab->sgotplt->size += 4;
}
- /* We also need to make an entry in the .rel.plt section. */
- htab->srelplt->size += sizeof (Elf32_External_Rel);
+ /* We also need to make an entry in the .rel(a).plt section. */
+ htab->srelplt->size += RELOC_SIZE (htab);
+
+ /* VxWorks executables have a second set of relocations for
+ each PLT entry. They go in a separate relocation section,
+ which is processed by the kernel loader. */
+ if (htab->vxworks_p && !info->shared)
+ {
+ /* There is a relocation for the initial PLT entry:
+ an R_ARM_32 relocation for _GLOBAL_OFFSET_TABLE_. */
+ if (h->plt.offset == htab->plt_header_size)
+ htab->srelplt2->size += RELOC_SIZE (htab);
+
+ /* There are two extra relocations for each subsequent
+ PLT entry: an R_ARM_32 relocation for the GOT entry,
+ and an R_ARM_32 relocation for the PLT entry. */
+ htab->srelplt2->size += RELOC_SIZE (htab) * 2;
+ }
}
else
{
|| h->root.type != bfd_link_hash_undefweak))
{
if (tls_type & GOT_TLS_IE)
- htab->srelgot->size += sizeof (Elf32_External_Rel);
+ htab->srelgot->size += RELOC_SIZE (htab);
if (tls_type & GOT_TLS_GD)
- htab->srelgot->size += sizeof (Elf32_External_Rel);
+ htab->srelgot->size += RELOC_SIZE (htab);
if ((tls_type & GOT_TLS_GD) && indx != 0)
- htab->srelgot->size += sizeof (Elf32_External_Rel);
+ htab->srelgot->size += RELOC_SIZE (htab);
}
else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
|| h->root.type != bfd_link_hash_undefweak)
&& (info->shared
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
- htab->srelgot->size += sizeof (Elf32_External_Rel);
+ htab->srelgot->size += RELOC_SIZE (htab);
}
}
else
/* Also discard relocs on undefined weak syms with non-default
visibility. */
- if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ if (eh->relocs_copied != NULL
&& h->root.type == bfd_link_hash_undefweak)
- eh->relocs_copied = NULL;
+ {
+ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ eh->relocs_copied = NULL;
+
+ /* Make sure undefined weak symbols are output as a dynamic
+ symbol in PIEs. */
+ else if (h->dynindx == -1
+ && !h->forced_local)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+ }
+
else if (htab->root.is_relocatable_executable && h->dynindx == -1
&& h->root.type == bfd_link_hash_new)
{
for (p = eh->relocs_copied; p != NULL; p = p->next)
{
asection *sreloc = elf_section_data (p->section)->sreloc;
- sreloc->size += p->count * sizeof (Elf32_External_Rel);
+ sreloc->size += p->count * RELOC_SIZE (htab);
}
return TRUE;
htab = elf32_arm_hash_table (info);
dynobj = elf_hash_table (info)->dynobj;
BFD_ASSERT (dynobj != NULL);
+ check_use_blx (htab);
if (elf_hash_table (info)->dynamic_sections_created)
{
{
struct elf32_arm_relocs_copied *p;
- for (p = *((struct elf32_arm_relocs_copied **)
- &elf_section_data (s)->local_dynrel);
- p != NULL;
- p = p->next)
+ for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next)
{
if (!bfd_is_abs_section (p->section)
&& bfd_is_abs_section (p->section->output_section))
else if (p->count != 0)
{
srel = elf_section_data (p->section)->sreloc;
- srel->size += p->count * sizeof (Elf32_External_Rel);
+ srel->size += p->count * RELOC_SIZE (htab);
if ((p->section->output_section->flags & SEC_READONLY) != 0)
info->flags |= DF_TEXTREL;
}
s->size += 4;
if (info->shared || *local_tls_type == GOT_TLS_GD)
- srel->size += sizeof (Elf32_External_Rel);
+ srel->size += RELOC_SIZE (htab);
}
else
*local_got = (bfd_vma) -1;
htab->tls_ldm_got.offset = htab->sgot->size;
htab->sgot->size += 8;
if (info->shared)
- htab->srelgot->size += sizeof (Elf32_External_Rel);
+ htab->srelgot->size += RELOC_SIZE (htab);
}
else
htab->tls_ldm_got.offset = -1;
if (s->size != 0)
{
/* Remember whether there are any reloc sections other
- than .rel.plt. */
- if (strcmp (name, ".rel.plt") != 0)
+ than .rel(a).plt and .rela.plt.unloaded. */
+ if (s != htab->srelplt && s != htab->srelplt2)
relocs = TRUE;
/* We use the reloc_count field as a counter if we need
if (s->size == 0)
{
/* If we don't need this section, strip it from the
- output file. This is mostly to handle .rel.bss and
- .rel.plt. We must create both sections in
+ output file. This is mostly to handle .rel(a).bss and
+ .rel(a).plt. We must create both sections in
create_dynamic_sections, because they must be created
before the linker maps input sections to output
sections. The linker does that before
{
if ( !add_dynamic_entry (DT_PLTGOT, 0)
|| !add_dynamic_entry (DT_PLTRELSZ, 0)
- || !add_dynamic_entry (DT_PLTREL, DT_REL)
+ || !add_dynamic_entry (DT_PLTREL,
+ htab->use_rel ? DT_REL : DT_RELA)
|| !add_dynamic_entry (DT_JMPREL, 0))
return FALSE;
}
if (relocs)
{
- if ( !add_dynamic_entry (DT_REL, 0)
- || !add_dynamic_entry (DT_RELSZ, 0)
- || !add_dynamic_entry (DT_RELENT, sizeof (Elf32_External_Rel)))
- return FALSE;
+ if (htab->use_rel)
+ {
+ if (!add_dynamic_entry (DT_REL, 0)
+ || !add_dynamic_entry (DT_RELSZ, 0)
+ || !add_dynamic_entry (DT_RELENT, RELOC_SIZE (htab)))
+ return FALSE;
+ }
+ else
+ {
+ if (!add_dynamic_entry (DT_RELA, 0)
+ || !add_dynamic_entry (DT_RELASZ, 0)
+ || !add_dynamic_entry (DT_RELAENT, RELOC_SIZE (htab)))
+ return FALSE;
+ }
}
/* If any dynamic relocs apply to a read-only section,
BFD_ASSERT (h->dynindx != -1);
splt = bfd_get_section_by_name (dynobj, ".plt");
- srel = bfd_get_section_by_name (dynobj, ".rel.plt");
+ srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".plt"));
BFD_ASSERT (splt != NULL && srel != NULL);
/* Fill in the entry in the procedure linkage table. */
}
else
{
- bfd_vma got_offset;
+ bfd_vma got_offset, got_address, plt_address;
bfd_vma got_displacement;
asection * sgot;
symbols appear in the same order as in .plt. */
plt_index = (got_offset - 12) / 4;
- /* 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
- of the PLT stub. */
- got_displacement = (sgot->output_section->vma
- + sgot->output_offset
- + got_offset
- - splt->output_section->vma
- - splt->output_offset
- - h->plt.offset
- - 8);
+ /* Calculate the address of the GOT entry. */
+ got_address = (sgot->output_section->vma
+ + sgot->output_offset
+ + got_offset);
- BFD_ASSERT ((got_displacement & 0xf0000000) == 0);
+ /* ...and the address of the PLT entry. */
+ plt_address = (splt->output_section->vma
+ + splt->output_offset
+ + h->plt.offset);
- if (!htab->use_blx && eh->plt_thumb_refcount > 0)
+ if (htab->vxworks_p && info->shared)
{
- bfd_put_16 (output_bfd, elf32_arm_plt_thumb_stub[0],
- splt->contents + h->plt.offset - 4);
- bfd_put_16 (output_bfd, elf32_arm_plt_thumb_stub[1],
- splt->contents + h->plt.offset - 2);
+ unsigned int i;
+ bfd_vma val;
+
+ for (i = 0; i != htab->plt_entry_size / 4; i++)
+ {
+ val = elf32_arm_vxworks_shared_plt_entry[i];
+ if (i == 2)
+ val |= got_address - sgot->output_section->vma;
+ if (i == 5)
+ val |= plt_index * RELOC_SIZE (htab);
+ bfd_put_32 (output_bfd, val,
+ htab->splt->contents + h->plt.offset + i * 4);
+ }
}
+ else if (htab->vxworks_p)
+ {
+ unsigned int i;
+ bfd_vma val;
- bfd_put_32 (output_bfd, elf32_arm_plt_entry[0] | ((got_displacement & 0x0ff00000) >> 20),
- splt->contents + h->plt.offset + 0);
- bfd_put_32 (output_bfd, elf32_arm_plt_entry[1] | ((got_displacement & 0x000ff000) >> 12),
- splt->contents + h->plt.offset + 4);
- bfd_put_32 (output_bfd, elf32_arm_plt_entry[2] | (got_displacement & 0x00000fff),
- splt->contents + h->plt.offset + 8);
+ for (i = 0; i != htab->plt_entry_size / 4; i++)
+ {
+ val = elf32_arm_vxworks_exec_plt_entry[i];
+ if (i == 2)
+ val |= got_address;
+ if (i == 4)
+ val |= 0xffffff & -((h->plt.offset + i * 4 + 8) >> 2);
+ if (i == 5)
+ val |= plt_index * RELOC_SIZE (htab);
+ bfd_put_32 (output_bfd, val,
+ htab->splt->contents + h->plt.offset + i * 4);
+ }
+
+ loc = (htab->srelplt2->contents
+ + (plt_index * 2 + 1) * RELOC_SIZE (htab));
+
+ /* Create the .rela.plt.unloaded R_ARM_ABS32 relocation
+ referencing the GOT for this PLT entry. */
+ rel.r_offset = plt_address + 8;
+ rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32);
+ rel.r_addend = got_offset;
+ SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc);
+ loc += RELOC_SIZE (htab);
+
+ /* Create the R_ARM_ABS32 relocation referencing the
+ beginning of the PLT for this GOT entry. */
+ rel.r_offset = got_address;
+ rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_ARM_ABS32);
+ rel.r_addend = 0;
+ SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc);
+ }
+ else
+ {
+ /* 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
+ of the PLT stub. */
+ got_displacement = got_address - (plt_address + 8);
+
+ BFD_ASSERT ((got_displacement & 0xf0000000) == 0);
+
+ if (!htab->use_blx && eh->plt_thumb_refcount > 0)
+ {
+ bfd_put_16 (output_bfd, elf32_arm_plt_thumb_stub[0],
+ splt->contents + h->plt.offset - 4);
+ bfd_put_16 (output_bfd, elf32_arm_plt_thumb_stub[1],
+ splt->contents + h->plt.offset - 2);
+ }
+
+ bfd_put_32 (output_bfd,
+ elf32_arm_plt_entry[0]
+ | ((got_displacement & 0x0ff00000) >> 20),
+ splt->contents + h->plt.offset + 0);
+ bfd_put_32 (output_bfd,
+ elf32_arm_plt_entry[1]
+ | ((got_displacement & 0x000ff000) >> 12),
+ splt->contents + h->plt.offset + 4);
+ bfd_put_32 (output_bfd,
+ elf32_arm_plt_entry[2]
+ | (got_displacement & 0x00000fff),
+ splt->contents + h->plt.offset + 8);
#ifdef FOUR_WORD_PLT
- bfd_put_32 (output_bfd, elf32_arm_plt_entry[3],
- splt->contents + h->plt.offset + 12);
+ bfd_put_32 (output_bfd, elf32_arm_plt_entry[3],
+ splt->contents + h->plt.offset + 12);
#endif
+ }
/* Fill in the entry in the global offset table. */
bfd_put_32 (output_bfd,
+ splt->output_offset),
sgot->contents + got_offset);
- /* Fill in the entry in the .rel.plt section. */
- rel.r_offset = (sgot->output_section->vma
- + sgot->output_offset
- + got_offset);
+ /* Fill in the entry in the .rel(a).plt section. */
+ rel.r_addend = 0;
+ rel.r_offset = got_address;
rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_JUMP_SLOT);
}
- loc = srel->contents + plt_index * sizeof (Elf32_External_Rel);
- bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+ loc = srel->contents + plt_index * RELOC_SIZE (htab);
+ SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc);
if (!h->def_regular)
{
asection * srel;
Elf_Internal_Rela rel;
bfd_byte *loc;
+ bfd_vma offset;
/* This symbol has an entry in the global offset table. Set it
up. */
sgot = bfd_get_section_by_name (dynobj, ".got");
- srel = bfd_get_section_by_name (dynobj, ".rel.got");
+ srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".got"));
BFD_ASSERT (sgot != NULL && srel != NULL);
+ offset = (h->got.offset & ~(bfd_vma) 1);
+ rel.r_addend = 0;
rel.r_offset = (sgot->output_section->vma
+ sgot->output_offset
- + (h->got.offset &~ (bfd_vma) 1));
+ + offset);
/* If this is a static link, or it is a -Bsymbolic link and the
symbol is defined locally or was forced to be local because
{
BFD_ASSERT((h->got.offset & 1) != 0);
rel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+ if (!htab->use_rel)
+ {
+ rel.r_addend = bfd_get_32 (output_bfd, sgot->contents + offset);
+ bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset);
+ }
}
else
{
BFD_ASSERT((h->got.offset & 1) == 0);
- bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset);
+ bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset);
rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_GLOB_DAT);
}
- loc = srel->contents + srel->reloc_count++ * sizeof (Elf32_External_Rel);
- bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+ loc = srel->contents + srel->reloc_count++ * RELOC_SIZE (htab);
+ SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc);
}
if (h->needs_copy)
|| h->root.type == bfd_link_hash_defweak));
s = bfd_get_section_by_name (h->root.u.def.section->owner,
- ".rel.bss");
+ RELOC_SECTION (htab, ".bss"));
BFD_ASSERT (s != NULL);
+ rel.r_addend = 0;
rel.r_offset = (h->root.u.def.value
+ h->root.u.def.section->output_section->vma
+ h->root.u.def.section->output_offset);
rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_COPY);
- loc = s->contents + s->reloc_count++ * sizeof (Elf32_External_Rel);
- bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+ loc = s->contents + s->reloc_count++ * RELOC_SIZE (htab);
+ SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc);
}
- /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */
+ /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. On VxWorks,
+ the _GLOBAL_OFFSET_TABLE_ symbol is not absolute: it is relative
+ to the ".got" section. */
if (strcmp (h->root.root.string, "_DYNAMIC") == 0
- || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
+ || (!htab->vxworks_p && h == htab->root.hgot))
sym->st_shndx = SHN_ABS;
return TRUE;
name = ".got";
goto get_vma;
case DT_JMPREL:
- name = ".rel.plt";
+ name = RELOC_SECTION (htab, ".plt");
get_vma:
s = bfd_get_section_by_name (output_bfd, name);
BFD_ASSERT (s != NULL);
break;
case DT_PLTRELSZ:
- s = bfd_get_section_by_name (output_bfd, ".rel.plt");
+ s = bfd_get_section_by_name (output_bfd,
+ RELOC_SECTION (htab, ".plt"));
BFD_ASSERT (s != NULL);
dyn.d_un.d_val = s->size;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
break;
case DT_RELSZ:
+ case DT_RELASZ:
if (!htab->symbian_p)
{
/* My reading of the SVR4 ABI indicates that the
what Solaris does. However, UnixWare can not handle
that case. Therefore, we override the DT_RELSZ entry
here to make it not include the JMPREL relocs. Since
- the linker script arranges for .rel.plt to follow all
+ the linker script arranges for .rel(a).plt to follow all
other relocation sections, we don't have to worry
about changing the DT_REL entry. */
- s = bfd_get_section_by_name (output_bfd, ".rel.plt");
+ s = bfd_get_section_by_name (output_bfd,
+ RELOC_SECTION (htab, ".plt"));
if (s != NULL)
dyn.d_un.d_val -= s->size;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
case DT_REL:
case DT_RELA:
- case DT_RELASZ:
/* In the BPABI, the DT_REL tag must point at the file
offset, not the VMA, of the first relocation
section. So, we use code similar to that in
/* Fill in the first entry in the procedure linkage table. */
if (splt->size > 0 && elf32_arm_hash_table (info)->plt_header_size)
{
- bfd_vma got_displacement;
+ const bfd_vma *plt0_entry;
+ bfd_vma got_address, plt_address, got_displacement;
+
+ /* Calculate the addresses of the GOT and PLT. */
+ got_address = sgot->output_section->vma + sgot->output_offset;
+ plt_address = splt->output_section->vma + splt->output_offset;
+
+ if (htab->vxworks_p)
+ {
+ /* The VxWorks GOT is relocated by the dynamic linker.
+ Therefore, we must emit relocations rather than simply
+ computing the values now. */
+ Elf_Internal_Rela rel;
+
+ plt0_entry = elf32_arm_vxworks_exec_plt0_entry;
+ bfd_put_32 (output_bfd, plt0_entry[0], splt->contents + 0);
+ bfd_put_32 (output_bfd, plt0_entry[1], splt->contents + 4);
+ bfd_put_32 (output_bfd, plt0_entry[2], splt->contents + 8);
+ bfd_put_32 (output_bfd, got_address, splt->contents + 12);
+
+ /* Generate a relocation for _GLOBAL_OFFSET_TABLE_. */
+ rel.r_offset = plt_address + 12;
+ rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32);
+ rel.r_addend = 0;
+ SWAP_RELOC_OUT (htab) (output_bfd, &rel,
+ htab->srelplt2->contents);
+ }
+ else
+ {
+ got_displacement = got_address - (plt_address + 16);
+
+ plt0_entry = elf32_arm_plt0_entry;
+ bfd_put_32 (output_bfd, plt0_entry[0], splt->contents + 0);
+ bfd_put_32 (output_bfd, plt0_entry[1], splt->contents + 4);
+ bfd_put_32 (output_bfd, plt0_entry[2], splt->contents + 8);
+ bfd_put_32 (output_bfd, plt0_entry[3], splt->contents + 12);
- /* Calculate the displacement between the PLT slot and &GOT[0]. */
- got_displacement = (sgot->output_section->vma
- + sgot->output_offset
- - splt->output_section->vma
- - splt->output_offset
- - 16);
-
- bfd_put_32 (output_bfd, elf32_arm_plt0_entry[0], splt->contents + 0);
- bfd_put_32 (output_bfd, elf32_arm_plt0_entry[1], splt->contents + 4);
- bfd_put_32 (output_bfd, elf32_arm_plt0_entry[2], splt->contents + 8);
- bfd_put_32 (output_bfd, elf32_arm_plt0_entry[3], splt->contents + 12);
#ifdef FOUR_WORD_PLT
- /* The displacement value goes in the otherwise-unused last word of
- the second entry. */
- bfd_put_32 (output_bfd, got_displacement, splt->contents + 28);
+ /* The displacement value goes in the otherwise-unused
+ last word of the second entry. */
+ bfd_put_32 (output_bfd, got_displacement, splt->contents + 28);
#else
- bfd_put_32 (output_bfd, got_displacement, splt->contents + 16);
+ bfd_put_32 (output_bfd, got_displacement, splt->contents + 16);
#endif
+ }
}
/* UnixWare sets the entsize of .plt to 4, although that doesn't
really seem like the right value. */
elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4;
+
+ if (htab->vxworks_p && !info->shared && htab->splt->size > 0)
+ {
+ /* Correct the .rel(a).plt.unloaded relocations. They will have
+ incorrect symbol indexes. */
+ int num_plts;
+ unsigned char *p;
+
+ num_plts = ((htab->splt->size - htab->plt_header_size)
+ / htab->plt_entry_size);
+ p = htab->srelplt2->contents + RELOC_SIZE (htab);
+
+ for (; num_plts; num_plts--)
+ {
+ Elf_Internal_Rela rel;
+
+ SWAP_RELOC_IN (htab) (output_bfd, p, &rel);
+ rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32);
+ SWAP_RELOC_OUT (htab) (output_bfd, &rel, p);
+ p += RELOC_SIZE (htab);
+
+ SWAP_RELOC_IN (htab) (output_bfd, p, &rel);
+ rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_ARM_ABS32);
+ SWAP_RELOC_OUT (htab) (output_bfd, &rel, p);
+ p += RELOC_SIZE (htab);
+ }
+ }
}
/* Fill in the first three entries in the global offset table. */
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. */
switch (hdr->sh_type)
{
case SHT_ARM_EXIDX:
+ case SHT_ARM_PREEMPTMAP:
+ case SHT_ARM_ATTRIBUTES:
break;
default:
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;
}
sections_with_arm_elf_section_data = entry;
}
+static struct section_list *
+find_arm_elf_section_entry (asection * sec)
+{
+ struct section_list * entry;
+ static struct section_list * last_entry = NULL;
+
+ /* This is a short cut for the typical case where the sections are added
+ to the sections_with_arm_elf_section_data list in forward order and
+ then looked up here in backwards order. This makes a real difference
+ to the ld-srec/sec64k.exp linker test. */
+ entry = sections_with_arm_elf_section_data;
+ if (last_entry != NULL)
+ {
+ if (last_entry->sec == sec)
+ entry = last_entry;
+ else if (last_entry->next != NULL
+ && last_entry->next->sec == sec)
+ entry = last_entry->next;
+ }
+
+ for (; entry; entry = entry->next)
+ if (entry->sec == sec)
+ break;
+
+ if (entry)
+ /* Record the entry prior to this one - it is the entry we are most
+ likely to want to locate next time. Also this way if we have been
+ called from unrecord_section_with_arm_elf_section_data() we will not
+ be caching a pointer that is about to be freed. */
+ last_entry = entry->prev;
+
+ return entry;
+}
+
static _arm_elf_section_data *
get_arm_elf_section_data (asection * sec)
{
struct section_list * entry;
- for (entry = sections_with_arm_elf_section_data; entry; entry = entry->next)
- if (entry->sec == sec)
- return elf32_arm_section_data (sec);
- return NULL;
+ entry = find_arm_elf_section_entry (sec);
+
+ if (entry)
+ return elf32_arm_section_data (entry->sec);
+ else
+ return NULL;
}
static void
{
struct section_list * entry;
- for (entry = sections_with_arm_elf_section_data; entry; entry = entry->next)
- if (entry->sec == sec)
- {
- if (entry->prev != NULL)
- entry->prev->next = entry->next;
- if (entry->next != NULL)
- entry->next->prev = entry->prev;
- if (entry == sections_with_arm_elf_section_data)
- sections_with_arm_elf_section_data = entry->next;
- free (entry);
- break;
- }
+ entry = find_arm_elf_section_entry (sec);
+
+ if (entry)
+ {
+ if (entry->prev != NULL)
+ entry->prev->next = entry->next;
+ if (entry->next != NULL)
+ entry->next->prev = entry->prev;
+ if (entry == sections_with_arm_elf_section_data)
+ sections_with_arm_elf_section_data = entry->next;
+ free (entry);
+ }
}
/* Called for each symbol. Builds a section map based on mapping symbols.
const char *name,
Elf_Internal_Sym *elfsym,
asection *input_sec,
- struct elf_link_hash_entry *h ATTRIBUTE_UNUSED)
+ struct elf_link_hash_entry *h)
{
int mapcount;
elf32_arm_section_map *map;
_arm_elf_section_data *arm_data;
struct elf32_arm_link_hash_table *globals;
+ globals = elf32_arm_hash_table (info);
+ if (globals->vxworks_p
+ && !elf_vxworks_link_output_symbol_hook (info, name, elfsym,
+ input_sec, h))
+ return FALSE;
+
/* Only do this on final link. */
if (info->relocatable)
return TRUE;
/* Only build a map if we need to byteswap code. */
- globals = elf32_arm_hash_table (info);
if (!globals->byteswap_code)
return TRUE;
mapcount = arm_data->mapcount + 1;
map = arm_data->map;
+
/* TODO: This may be inefficient, but we probably don't usually have many
mapping symbols per section. */
newmap = bfd_realloc (map, mapcount * sizeof (* map));
arm_data->map = newmap;
arm_data->mapcount = mapcount;
- map[mapcount - 1].vma = elfsym->st_value;
- map[mapcount - 1].type = name[1];
+ newmap[mapcount - 1].vma = elfsym->st_value;
+ newmap[mapcount - 1].type = name[1];
}
return TRUE;
static bfd_boolean
elf32_arm_close_and_cleanup (bfd * abfd)
{
- bfd_map_over_sections (abfd, unrecord_section_via_map_over_sections, NULL);
+ if (abfd->sections)
+ bfd_map_over_sections (abfd,
+ unrecord_section_via_map_over_sections,
+ NULL);
return _bfd_elf_close_and_cleanup (abfd);
}
+static bfd_boolean
+elf32_arm_bfd_free_cached_info (bfd * abfd)
+{
+ if (abfd->sections)
+ bfd_map_over_sections (abfd,
+ unrecord_section_via_map_over_sections,
+ NULL);
+
+ return _bfd_free_cached_info (abfd);
+}
+
/* Display STT_ARM_TFUNC symbols as functions. */
static void
#define bfd_elf32_new_section_hook elf32_arm_new_section_hook
#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
if (ret)
{
struct elf32_arm_link_hash_table *htab
- = (struct elf32_arm_link_hash_table *)ret;
+ = (struct elf32_arm_link_hash_table *) ret;
htab->use_rel = 0;
+ htab->vxworks_p = 1;
}
return ret;
}
+static void
+elf32_arm_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
+{
+ elf32_arm_final_write_processing (abfd, linker);
+ elf_vxworks_final_write_processing (abfd, linker);
+}
+
#undef elf32_bed
#define elf32_bed elf32_arm_vxworks_bed
#undef bfd_elf32_bfd_link_hash_table_create
#define bfd_elf32_bfd_link_hash_table_create \
elf32_arm_vxworks_link_hash_table_create
+#undef elf_backend_add_symbol_hook
+#define elf_backend_add_symbol_hook \
+ elf_vxworks_add_symbol_hook
+#undef elf_backend_final_write_processing
+#define elf_backend_final_write_processing \
+ elf32_arm_vxworks_final_write_processing
+#undef elf_backend_emit_relocs
+#define elf_backend_emit_relocs \
+ elf_vxworks_emit_relocs
#undef elf_backend_may_use_rel_p
-#define elf_backend_may_use_rel_p 0
+#define elf_backend_may_use_rel_p 0
#undef elf_backend_may_use_rela_p
-#define elf_backend_may_use_rela_p 1
+#define elf_backend_may_use_rela_p 1
#undef elf_backend_default_use_rela_p
-#define elf_backend_default_use_rela_p 1
+#define elf_backend_default_use_rela_p 1
#undef elf_backend_rela_normal
-#define elf_backend_rela_normal 1
+#define elf_backend_rela_normal 1
+#undef elf_backend_want_plt_sym
+#define elf_backend_want_plt_sym 1
+#undef ELF_MAXPAGESIZE
+#define ELF_MAXPAGESIZE 0x1000
#include "elf32-target.h"
#undef bfd_elf32_bfd_link_hash_table_create
#define bfd_elf32_bfd_link_hash_table_create \
elf32_arm_symbian_link_hash_table_create
+#undef elf_backend_add_symbol_hook
#undef elf_backend_special_sections
#define elf_backend_special_sections elf32_arm_symbian_special_sections
#undef elf_backend_begin_write_processing
#define elf_backend_begin_write_processing \
elf32_arm_symbian_begin_write_processing
+#undef elf_backend_final_write_processing
+#define elf_backend_final_write_processing \
+ elf32_arm_final_write_processing
+#undef elf_backend_emit_relocs
#undef elf_backend_modify_segment_map
#define elf_backend_modify_segment_map elf32_arm_symbian_modify_segment_map
#define elf_backend_want_got_plt 0
#undef elf_backend_may_use_rel_p
-#define elf_backend_may_use_rel_p 1
+#define elf_backend_may_use_rel_p 1
#undef elf_backend_may_use_rela_p
-#define elf_backend_may_use_rela_p 0
+#define elf_backend_may_use_rela_p 0
#undef elf_backend_default_use_rela_p
-#define elf_backend_default_use_rela_p 0
+#define elf_backend_default_use_rela_p 0
#undef elf_backend_rela_normal
-#define elf_backend_rela_normal 0
+#define elf_backend_rela_normal 0
+#undef elf_backend_want_plt_sym
+#define elf_backend_want_plt_sym 0
+#undef ELF_MAXPAGESIZE
+#define ELF_MAXPAGESIZE 0x8000
#include "elf32-target.h"