bfd_elf_generic_reloc, /* special_function */
"R_ARM_THM_CALL", /* name */
FALSE, /* partial_inplace */
- 0x07ff07ff, /* src_mask */
- 0x07ff07ff, /* dst_mask */
+ 0x07ff2fff, /* src_mask */
+ 0x07ff2fff, /* dst_mask */
TRUE), /* pcrel_offset */
HOWTO (R_ARM_THM_PC8, /* type */
HOWTO (R_ARM_XPC25, /* type */
2, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
- 25, /* bitsize */
+ 24, /* bitsize */
TRUE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed,/* complain_on_overflow */
HOWTO (R_ARM_THM_XPC22, /* type */
2, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
- 22, /* bitsize */
+ 24, /* bitsize */
TRUE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed,/* complain_on_overflow */
bfd_elf_generic_reloc, /* special_function */
"R_ARM_THM_XPC22", /* name */
FALSE, /* partial_inplace */
- 0x07ff07ff, /* src_mask */
- 0x07ff07ff, /* dst_mask */
+ 0x07ff2fff, /* src_mask */
+ 0x07ff2fff, /* dst_mask */
TRUE), /* pcrel_offset */
/* Dynamic TLS relocations. */
0x00000fff, /* dst_mask */
FALSE), /* pcrel_offset */
+ /* 112-127 private relocations. */
EMPTY_HOWTO (112),
EMPTY_HOWTO (113),
EMPTY_HOWTO (114),
EMPTY_HOWTO (125),
EMPTY_HOWTO (126),
EMPTY_HOWTO (127),
+
+ /* R_ARM_ME_TOO, obsolete. */
EMPTY_HOWTO (128),
HOWTO (R_ARM_THM_TLS_DESCSEQ, /* type */
FALSE), /* pcrel_offset */
};
-/* 112-127 private relocations
- 128 R_ARM_ME_TOO, obsolete
- 129-255 unallocated in AAELF.
-
- 249-255 extended, currently unused, relocations: */
+/* 160 onwards: */
+static reloc_howto_type elf32_arm_howto_table_2[1] =
+{
+ HOWTO (R_ARM_IRELATIVE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_ARM_IRELATIVE", /* name */
+ TRUE, /* partial_inplace */
+ 0xffffffff, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE) /* pcrel_offset */
+};
-static reloc_howto_type elf32_arm_howto_table_2[4] =
+/* 249-255 extended, currently unused, relocations: */
+static reloc_howto_type elf32_arm_howto_table_3[4] =
{
HOWTO (R_ARM_RREL32, /* type */
0, /* rightshift */
if (r_type < ARRAY_SIZE (elf32_arm_howto_table_1))
return &elf32_arm_howto_table_1[r_type];
+ if (r_type == R_ARM_IRELATIVE)
+ return &elf32_arm_howto_table_2[r_type - R_ARM_IRELATIVE];
+
if (r_type >= R_ARM_RREL32
- && r_type < R_ARM_RREL32 + ARRAY_SIZE (elf32_arm_howto_table_2))
- return &elf32_arm_howto_table_2[r_type - R_ARM_RREL32];
+ && r_type < R_ARM_RREL32 + ARRAY_SIZE (elf32_arm_howto_table_3))
+ return &elf32_arm_howto_table_3[r_type - R_ARM_RREL32];
return NULL;
}
{BFD_RELOC_ARM_TLS_TPOFF32, R_ARM_TLS_TPOFF32},
{BFD_RELOC_ARM_TLS_IE32, R_ARM_TLS_IE32},
{BFD_RELOC_ARM_TLS_LE32, R_ARM_TLS_LE32},
+ {BFD_RELOC_ARM_IRELATIVE, R_ARM_IRELATIVE},
{BFD_RELOC_VTABLE_INHERIT, R_ARM_GNU_VTINHERIT},
{BFD_RELOC_VTABLE_ENTRY, R_ARM_GNU_VTENTRY},
{BFD_RELOC_ARM_MOVW, R_ARM_MOVW_ABS_NC},
&& strcasecmp (elf32_arm_howto_table_2[i].name, r_name) == 0)
return &elf32_arm_howto_table_2[i];
+ for (i = 0; i < ARRAY_SIZE (elf32_arm_howto_table_3); i++)
+ if (elf32_arm_howto_table_3[i].name != NULL
+ && strcasecmp (elf32_arm_howto_table_3[i].name, r_name) == 0)
+ return &elf32_arm_howto_table_3[i];
+
return NULL;
}
/* The symbol table entry, if any, that this was derived from. */
struct elf32_arm_link_hash_entry *h;
- /* Destination symbol type (STT_ARM_TFUNC, ...) */
- unsigned char st_type;
+ /* Type of branch. */
+ enum arm_st_branch_type branch_type;
/* Where this stub is being called from, or, in the case of combined
stub sections, the first input section in the group. */
unsigned long orig_insn;
char *stub_name;
enum elf32_arm_stub_type stub_type;
- int st_type;
+ enum arm_st_branch_type branch_type;
};
/* A table of relocs applied to branches which might trigger Cortex-A8
struct elf32_arm_link_hash_entry *hash;
const char *sym_name;
unsigned int r_type;
- unsigned char st_type;
+ enum arm_st_branch_type branch_type;
bfd_boolean non_a8_stub;
};
/* The size of the thread control block. */
#define TCB_SIZE 8
+/* ARM-specific information about a PLT entry, over and above the usual
+ gotplt_union. */
+struct arm_plt_info {
+ /* We reference count Thumb references to a PLT entry separately,
+ so that we can emit the Thumb trampoline only if needed. */
+ bfd_signed_vma thumb_refcount;
+
+ /* Some references from Thumb code may be eliminated by BL->BLX
+ conversion, so record them separately. */
+ bfd_signed_vma maybe_thumb_refcount;
+
+ /* How many of the recorded PLT accesses were from non-call relocations.
+ This information is useful when deciding whether anything takes the
+ address of an STT_GNU_IFUNC PLT. A value of 0 means that all
+ non-call references to the function should resolve directly to the
+ real runtime target. */
+ unsigned int noncall_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. */
+ bfd_signed_vma got_offset;
+};
+
+/* Information about an .iplt entry for a local STT_GNU_IFUNC symbol. */
+struct arm_local_iplt_info {
+ /* The information that is usually found in the generic ELF part of
+ the hash table entry. */
+ union gotplt_union root;
+
+ /* The information that is usually found in the ARM-specific part of
+ the hash table entry. */
+ struct arm_plt_info arm;
+
+ /* A list of all potential dynamic relocations against this symbol. */
+ struct elf_dyn_relocs *dyn_relocs;
+};
+
struct elf_arm_obj_tdata
{
struct elf_obj_tdata root;
/* GOTPLT entries for TLS descriptors. */
bfd_vma *local_tlsdesc_gotent;
+ /* Information for local symbols that need entries in .iplt. */
+ struct arm_local_iplt_info **local_iplt;
+
/* Zero to warn when linking objects with incompatible enum sizes. */
int no_enum_size_warning;
#define elf32_arm_local_tlsdesc_gotent(bfd) \
(elf_arm_tdata (bfd)->local_tlsdesc_gotent)
+#define elf32_arm_local_iplt(bfd) \
+ (elf_arm_tdata (bfd)->local_iplt)
+
#define is_arm_elf(bfd) \
(bfd_get_flavour (bfd) == bfd_target_elf_flavour \
&& elf_tdata (bfd) != NULL \
/* Track dynamic relocs copied for this symbol. */
struct elf_dyn_relocs *dyn_relocs;
- /* We reference count Thumb references to a PLT entry separately,
- 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. */
- bfd_signed_vma plt_got_offset;
+ /* ARM-specific PLT information. */
+ struct arm_plt_info plt;
#define GOT_UNKNOWN 0
#define GOT_NORMAL 1
#define GOT_TLS_IE 4
#define GOT_TLS_GDESC 8
#define GOT_TLS_GD_ANY_P(type) ((type & GOT_TLS_GD) || (type & GOT_TLS_GDESC))
- unsigned char tls_type;
+ unsigned int tls_type : 8;
+
+ /* True if the symbol's PLT entry is in .iplt rather than .plt. */
+ unsigned int is_iplt : 1;
+
+ unsigned int unused : 23;
/* Offset of the GOTPLT entry reserved for the TLS descriptor,
starting at the end of the jump table. */
/* Whether we should fix the Cortex-A8 Thumb-2 branch/TLB erratum. */
int fix_cortex_a8;
+ /* Whether we should fix the ARM1176 BLX immediate issue. */
+ int fix_arm1176;
+
/* Nonzero if the ARM/Thumb BLX instructions are available for use. */
int use_blx;
ret->dyn_relocs = NULL;
ret->tls_type = GOT_UNKNOWN;
ret->tlsdesc_got = (bfd_vma) -1;
- ret->plt_thumb_refcount = 0;
- ret->plt_maybe_thumb_refcount = 0;
- ret->plt_got_offset = -1;
+ ret->plt.thumb_refcount = 0;
+ ret->plt.maybe_thumb_refcount = 0;
+ ret->plt.noncall_refcount = 0;
+ ret->plt.got_offset = -1;
+ ret->is_iplt = FALSE;
ret->export_glue = NULL;
ret->stub_cache = NULL;
return (struct bfd_hash_entry *) ret;
}
+/* Ensure that we have allocated bookkeeping structures for ABFD's local
+ symbols. */
+
+static bfd_boolean
+elf32_arm_allocate_local_sym_info (bfd *abfd)
+{
+ if (elf_local_got_refcounts (abfd) == NULL)
+ {
+ bfd_size_type num_syms;
+ bfd_size_type size;
+ char *data;
+
+ num_syms = elf_tdata (abfd)->symtab_hdr.sh_info;
+ size = num_syms * (sizeof (bfd_signed_vma)
+ + sizeof (struct arm_local_iplt_info *)
+ + sizeof (bfd_vma)
+ + sizeof (char));
+ data = bfd_zalloc (abfd, size);
+ if (data == NULL)
+ return FALSE;
+
+ elf_local_got_refcounts (abfd) = (bfd_signed_vma *) data;
+ data += num_syms * sizeof (bfd_signed_vma);
+
+ elf32_arm_local_iplt (abfd) = (struct arm_local_iplt_info **) data;
+ data += num_syms * sizeof (struct arm_local_iplt_info *);
+
+ elf32_arm_local_tlsdesc_gotent (abfd) = (bfd_vma *) data;
+ data += num_syms * sizeof (bfd_vma);
+
+ elf32_arm_local_got_tls_type (abfd) = data;
+ }
+ return TRUE;
+}
+
+/* Return the .iplt information for local symbol R_SYMNDX, which belongs
+ to input bfd ABFD. Create the information if it doesn't already exist.
+ Return null if an allocation fails. */
+
+static struct arm_local_iplt_info *
+elf32_arm_create_local_iplt (bfd *abfd, unsigned long r_symndx)
+{
+ struct arm_local_iplt_info **ptr;
+
+ if (!elf32_arm_allocate_local_sym_info (abfd))
+ return NULL;
+
+ BFD_ASSERT (r_symndx < elf_tdata (abfd)->symtab_hdr.sh_info);
+ ptr = &elf32_arm_local_iplt (abfd)[r_symndx];
+ if (*ptr == NULL)
+ *ptr = bfd_zalloc (abfd, sizeof (**ptr));
+ return *ptr;
+}
+
+/* Try to obtain PLT information for the symbol with index R_SYMNDX
+ in ABFD's symbol table. If the symbol is global, H points to its
+ hash table entry, otherwise H is null.
+
+ Return true if the symbol does have PLT information. When returning
+ true, point *ROOT_PLT at the target-independent reference count/offset
+ union and *ARM_PLT at the ARM-specific information. */
+
+static bfd_boolean
+elf32_arm_get_plt_info (bfd *abfd, struct elf32_arm_link_hash_entry *h,
+ unsigned long r_symndx, union gotplt_union **root_plt,
+ struct arm_plt_info **arm_plt)
+{
+ struct arm_local_iplt_info *local_iplt;
+
+ if (h != NULL)
+ {
+ *root_plt = &h->root.plt;
+ *arm_plt = &h->plt;
+ return TRUE;
+ }
+
+ if (elf32_arm_local_iplt (abfd) == NULL)
+ return FALSE;
+
+ local_iplt = elf32_arm_local_iplt (abfd)[r_symndx];
+ if (local_iplt == NULL)
+ return FALSE;
+
+ *root_plt = &local_iplt->root;
+ *arm_plt = &local_iplt->arm;
+ return TRUE;
+}
+
+/* Return true if the PLT described by ARM_PLT requires a Thumb stub
+ before it. */
+
+static bfd_boolean
+elf32_arm_plt_needs_thumb_stub_p (struct bfd_link_info *info,
+ struct arm_plt_info *arm_plt)
+{
+ struct elf32_arm_link_hash_table *htab;
+
+ htab = elf32_arm_hash_table (info);
+ return (arm_plt->thumb_refcount != 0
+ || (!htab->use_blx && arm_plt->maybe_thumb_refcount != 0));
+}
+
+/* Return a pointer to the head of the dynamic reloc list that should
+ be used for local symbol ISYM, which is symbol number R_SYMNDX in
+ ABFD's symbol table. Return null if an error occurs. */
+
+static struct elf_dyn_relocs **
+elf32_arm_get_local_dynreloc_list (bfd *abfd, unsigned long r_symndx,
+ Elf_Internal_Sym *isym)
+{
+ if (ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ {
+ struct arm_local_iplt_info *local_iplt;
+
+ local_iplt = elf32_arm_create_local_iplt (abfd, r_symndx);
+ if (local_iplt == NULL)
+ return NULL;
+ return &local_iplt->dyn_relocs;
+ }
+ else
+ {
+ /* Track dynamic relocs needed for local syms too.
+ We really need local syms available to do this
+ easily. Oh well. */
+ asection *s;
+ void *vpp;
+
+ s = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ if (s == NULL)
+ abort ();
+
+ vpp = &elf_section_data (s)->local_dynrel;
+ return (struct elf_dyn_relocs **) vpp;
+ }
+}
+
/* Initialize an entry in the stub hash table. */
static struct bfd_hash_entry *
return TRUE;
}
+/* Create the .iplt, .rel(a).iplt and .igot.plt sections. */
+
+static bfd_boolean
+create_ifunc_sections (struct bfd_link_info *info)
+{
+ struct elf32_arm_link_hash_table *htab;
+ const struct elf_backend_data *bed;
+ bfd *dynobj;
+ asection *s;
+ flagword flags;
+
+ htab = elf32_arm_hash_table (info);
+ dynobj = htab->root.dynobj;
+ bed = get_elf_backend_data (dynobj);
+ flags = bed->dynamic_sec_flags;
+
+ if (htab->root.iplt == NULL)
+ {
+ s = bfd_make_section_with_flags (dynobj, ".iplt",
+ flags | SEC_READONLY | SEC_CODE);
+ if (s == NULL
+ || !bfd_set_section_alignment (abfd, s, bed->plt_alignment))
+ return FALSE;
+ htab->root.iplt = s;
+ }
+
+ if (htab->root.irelplt == NULL)
+ {
+ s = bfd_make_section_with_flags (dynobj, RELOC_SECTION (htab, ".iplt"),
+ flags | SEC_READONLY);
+ if (s == NULL
+ || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+ return FALSE;
+ htab->root.irelplt = s;
+ }
+
+ if (htab->root.igotplt == NULL)
+ {
+ s = bfd_make_section_with_flags (dynobj, ".igot.plt", flags);
+ if (s == NULL
+ || !bfd_set_section_alignment (dynobj, s, bed->s->log_file_align))
+ return FALSE;
+ htab->root.igotplt = s;
+ }
+ return TRUE;
+}
+
/* 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. */
if (ind->root.type == bfd_link_hash_indirect)
{
/* 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;
+ 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;
+ edir->plt.noncall_refcount += eind->plt.noncall_refcount;
+ eind->plt.noncall_refcount = 0;
+
+ /* We should only allocate a function to .iplt once the final
+ symbol information is known. */
+ BFD_ASSERT (!eind->is_iplt);
if (dir->got.refcount <= 0)
{
ret->vfp11_erratum_glue_size = 0;
ret->num_vfp11_fixes = 0;
ret->fix_cortex_a8 = 0;
+ ret->fix_arm1176 = 0;
ret->bfd_of_glue_owner = NULL;
ret->byteswap_code = 0;
ret->target1_is_rel = 0;
case arm_stub_long_branch_v4t_thumb_arm:
case arm_stub_short_branch_v4t_thumb_arm:
case arm_stub_long_branch_v4t_thumb_arm_pic:
+ case arm_stub_long_branch_v4t_thumb_tls_pic:
case arm_stub_long_branch_thumb_only_pic:
return TRUE;
case arm_stub_none:
arm_type_of_stub (struct bfd_link_info *info,
asection *input_sec,
const Elf_Internal_Rela *rel,
- int *actual_st_type,
+ unsigned char st_type,
+ enum arm_st_branch_type *actual_branch_type,
struct elf32_arm_link_hash_entry *hash,
bfd_vma destination,
asection *sym_sec,
int thumb_only;
enum elf32_arm_stub_type stub_type = arm_stub_none;
int use_plt = 0;
- int st_type = *actual_st_type;
+ enum arm_st_branch_type branch_type = *actual_branch_type;
+ union gotplt_union *root_plt;
+ struct arm_plt_info *arm_plt;
- /* We don't know the actual type of destination in case it is of
- type STT_SECTION: give up. */
- if (st_type == STT_SECTION)
+ if (branch_type == ST_BRANCH_LONG)
return stub_type;
globals = elf32_arm_hash_table (info);
r_type = ELF32_R_TYPE (rel->r_info);
- /* Keep a simpler condition, for the sake of clarity. */
- if (globals->root.splt != NULL
- && hash != NULL
- && hash->root.plt.offset != (bfd_vma) -1)
+ /* For TLS call relocs, it is the caller's responsibility to provide
+ the address of the appropriate trampoline. */
+ if (r_type != R_ARM_TLS_CALL
+ && r_type != R_ARM_THM_TLS_CALL
+ && elf32_arm_get_plt_info (input_bfd, hash, ELF32_R_SYM (rel->r_info),
+ &root_plt, &arm_plt)
+ && root_plt->offset != (bfd_vma) -1)
{
- use_plt = 1;
-
- /* Note when dealing with PLT entries: the main PLT stub is in
- ARM mode, so if the branch is in Thumb mode, another
- Thumb->ARM stub will be inserted later just before the ARM
- PLT stub. We don't take this extra distance into account
- here, because if a long branch stub is needed, we'll add a
- Thumb->Arm one and branch directly to the ARM PLT entry
- because it avoids spreading offset corrections in several
- places. */
+ asection *splt;
- destination = (globals->root.splt->output_section->vma
- + globals->root.splt->output_offset
- + hash->root.plt.offset);
- st_type = STT_FUNC;
+ if (hash == NULL || hash->is_iplt)
+ splt = globals->root.iplt;
+ else
+ splt = globals->root.splt;
+ if (splt != NULL)
+ {
+ use_plt = 1;
+
+ /* Note when dealing with PLT entries: the main PLT stub is in
+ ARM mode, so if the branch is in Thumb mode, another
+ Thumb->ARM stub will be inserted later just before the ARM
+ PLT stub. We don't take this extra distance into account
+ here, because if a long branch stub is needed, we'll add a
+ Thumb->Arm one and branch directly to the ARM PLT entry
+ because it avoids spreading offset corrections in several
+ places. */
+
+ destination = (splt->output_section->vma
+ + splt->output_offset
+ + root_plt->offset);
+ st_type = STT_FUNC;
+ branch_type = ST_BRANCH_TO_ARM;
+ }
}
+ /* Calls to STT_GNU_IFUNC symbols should go through a PLT. */
+ BFD_ASSERT (st_type != STT_GNU_IFUNC);
branch_offset = (bfd_signed_vma)(destination - location);
|| (thumb2
&& (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
|| (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
- || ((st_type != STT_ARM_TFUNC)
+ || (branch_type == ST_BRANCH_TO_ARM
&& (((r_type == R_ARM_THM_CALL
|| r_type == R_ARM_THM_TLS_CALL) && !globals->use_blx)
|| (r_type == R_ARM_THM_JUMP24))
&& !use_plt))
{
- if (st_type == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
{
/* Thumb to thumb. */
if (!thumb_only)
|| r_type == R_ARM_PLT32
|| r_type == R_ARM_TLS_CALL)
{
- if (st_type == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
{
/* Arm to thumb. */
/* If a stub is needed, record the actual destination type. */
if (stub_type != arm_stub_none)
- *actual_st_type = st_type;
+ *actual_branch_type = branch_type;
return stub_type;
}
static bfd_reloc_status_type elf32_arm_final_link_relocate
(reloc_howto_type *, bfd *, bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *, bfd_vma, struct bfd_link_info *, asection *,
- const char *, int, struct elf_link_hash_entry *, bfd_boolean *, char **);
+ const char *, unsigned char, enum arm_st_branch_type,
+ struct elf_link_hash_entry *, bfd_boolean *, char **);
static unsigned int
arm_stub_required_alignment (enum elf32_arm_stub_type stub_type)
BFD_ASSERT (size == stub_entry->stub_size);
/* Destination is Thumb. Force bit 0 to 1 to reflect this. */
- if (stub_entry->st_type == STT_ARM_TFUNC)
+ if (stub_entry->branch_type == ST_BRANCH_TO_THUMB)
sym_value |= 1;
/* Assume there is at least one and at most MAXRELOCS entries to relocate
Elf_Internal_Rela rel;
bfd_boolean unresolved_reloc;
char *error_message;
- int sym_flags
- = (template_sequence[stub_reloc_idx[i]].r_type != R_ARM_THM_XPC22)
- ? STT_ARM_TFUNC : 0;
+ enum arm_st_branch_type branch_type
+ = (template_sequence[stub_reloc_idx[i]].r_type != R_ARM_THM_XPC22
+ ? ST_BRANCH_TO_THUMB : ST_BRANCH_TO_ARM);
bfd_vma points_to = sym_value + stub_entry->target_addend;
rel.r_offset = stub_entry->stub_offset + stub_reloc_offset[i];
elf32_arm_final_link_relocate (elf32_arm_howto_from_type
(template_sequence[stub_reloc_idx[i]].r_type),
stub_bfd, info->output_bfd, stub_sec, stub_sec->contents, &rel,
- points_to, info, stub_entry->target_section, "", sym_flags,
- (struct elf_link_hash_entry *) stub_entry->h, &unresolved_reloc,
- &error_message);
+ points_to, info, stub_entry->target_section, "", STT_FUNC,
+ branch_type, (struct elf_link_hash_entry *) stub_entry->h,
+ &unresolved_reloc, &error_message);
}
else
{
elf32_arm_final_link_relocate (elf32_arm_howto_from_type
(template_sequence[stub_reloc_idx[i]].r_type),
stub_bfd, info->output_bfd, stub_sec, stub_sec->contents, &rel,
- points_to, info, stub_entry->target_section, "", stub_entry->st_type,
+ points_to, info, stub_entry->target_section, "", STT_FUNC,
+ stub_entry->branch_type,
(struct elf_link_hash_entry *) stub_entry->h, &unresolved_reloc,
&error_message);
}
bfd_vma target;
enum elf32_arm_stub_type stub_type = arm_stub_none;
struct a8_erratum_reloc key, *found;
+ bfd_boolean use_plt = FALSE;
key.from = base_vma + i;
found = (struct a8_erratum_reloc *)
{
char *error_message = NULL;
struct elf_link_hash_entry *entry;
- bfd_boolean use_plt = FALSE;
/* We don't care about the error returned from this
function, only if there is glue or not. */
if (found->r_type == R_ARM_THM_CALL)
{
- if (found->st_type != STT_ARM_TFUNC || use_plt)
+ if (found->branch_type == ST_BRANCH_TO_ARM
+ || use_plt)
force_target_arm = TRUE;
else
force_target_thumb = TRUE;
offset =
(bfd_signed_vma) (found->destination - pc_for_insn);
+ /* If the stub will use a Thumb-mode branch to a
+ PLT target, redirect it to the preceding Thumb
+ entry point. */
+ if (stub_type != arm_stub_a8_veneer_blx && use_plt)
+ offset -= PLT_THUMB_STUB_SIZE;
+
target = pc_for_insn + offset;
/* The BLX stub is ARM-mode code. Adjust the offset to
a8_fixes[num_a8_fixes].orig_insn = insn;
a8_fixes[num_a8_fixes].stub_name = stub_name;
a8_fixes[num_a8_fixes].stub_type = stub_type;
- a8_fixes[num_a8_fixes].st_type =
- is_blx ? STT_FUNC : STT_ARM_TFUNC;
+ a8_fixes[num_a8_fixes].branch_type =
+ is_blx ? ST_BRANCH_TO_ARM : ST_BRANCH_TO_THUMB;
num_a8_fixes++;
}
const char *sym_name;
char *stub_name;
const asection *id_sec;
- int st_type;
+ unsigned char st_type;
+ enum arm_st_branch_type branch_type;
bfd_boolean created_stub = FALSE;
r_type = ELF32_R_TYPE (irela->r_info);
sym_value = htab->tls_trampoline;
hash = 0;
st_type = STT_FUNC;
+ branch_type = ST_BRANCH_TO_ARM;
}
else if (!hash)
{
+ sym_sec->output_offset
+ sym_sec->output_section->vma);
st_type = ELF_ST_TYPE (sym->st_info);
+ branch_type = ARM_SYM_BRANCH_TYPE (sym);
sym_name
= bfd_elf_string_from_elf_section (input_bfd,
symtab_hdr->sh_link,
bfd_set_error (bfd_error_bad_value);
goto error_ret_free_internal;
}
- st_type = ELF_ST_TYPE (hash->root.type);
+ st_type = hash->root.type;
+ branch_type = hash->root.target_internal;
sym_name = hash->root.root.root.string;
}
{
/* Determine what (if any) linker stub is needed. */
stub_type = arm_type_of_stub (info, section, irela,
- &st_type, hash,
- destination, sym_sec,
+ st_type, &branch_type,
+ hash, destination, sym_sec,
input_bfd, sym_name);
if (stub_type == arm_stub_none)
break;
stub_entry->target_section = sym_sec;
stub_entry->stub_type = stub_type;
stub_entry->h = hash;
- stub_entry->st_type = st_type;
+ stub_entry->branch_type = branch_type;
if (sym_name == NULL)
sym_name = "unnamed";
/* For historical reasons, use the existing names for
ARM-to-Thumb and Thumb-to-ARM stubs. */
- if ( ((r_type == (unsigned int) R_ARM_THM_CALL)
- || (r_type == (unsigned int) R_ARM_THM_JUMP24))
- && st_type != STT_ARM_TFUNC)
+ if ((r_type == (unsigned int) R_ARM_THM_CALL
+ || r_type == (unsigned int) R_ARM_THM_JUMP24)
+ && branch_type == ST_BRANCH_TO_ARM)
sprintf (stub_entry->output_name,
THUMB2ARM_GLUE_ENTRY_NAME, sym_name);
- else if ( ((r_type == (unsigned int) R_ARM_CALL)
- || (r_type == (unsigned int) R_ARM_JUMP24))
- && st_type == STT_ARM_TFUNC)
+ else if ((r_type == (unsigned int) R_ARM_CALL
+ || r_type == (unsigned int) R_ARM_JUMP24)
+ && branch_type == ST_BRANCH_TO_THUMB)
sprintf (stub_entry->output_name,
ARM2THUMB_GLUE_ENTRY_NAME, sym_name);
else
a8_relocs[num_a8_relocs].from = from;
a8_relocs[num_a8_relocs].destination = destination;
a8_relocs[num_a8_relocs].r_type = r_type;
- a8_relocs[num_a8_relocs].st_type = st_type;
+ a8_relocs[num_a8_relocs].branch_type = branch_type;
a8_relocs[num_a8_relocs].sym_name = sym_name;
a8_relocs[num_a8_relocs].non_a8_stub = created_stub;
a8_relocs[num_a8_relocs].hash = hash;
stub_entry->target_value = a8_fixes[i].offset;
stub_entry->target_addend = a8_fixes[i].addend;
stub_entry->orig_insn = a8_fixes[i].orig_insn;
- stub_entry->st_type = a8_fixes[i].st_type;
+ stub_entry->branch_type = a8_fixes[i].branch_type;
size = find_stub_size_and_template (a8_fixes[i].stub_type,
&template_sequence,
static void
check_use_blx (struct elf32_arm_link_hash_table *globals)
{
- if (bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
- Tag_CPU_arch) > 2)
- globals->use_blx = 1;
+ int cpu_arch;
+
+ cpu_arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch);
+
+ if (globals->fix_arm1176)
+ {
+ if (cpu_arch == TAG_CPU_ARCH_V6T2 || cpu_arch > TAG_CPU_ARCH_V6K)
+ globals->use_blx = 1;
+ }
+ else
+ {
+ if (cpu_arch > TAG_CPU_ARCH_V4T)
+ globals->use_blx = 1;
+ }
}
bfd_boolean
/* 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 (h->target_internal == ST_BRANCH_TO_THUMB)
record_arm_to_thumb_glue (link_info, h);
break;
int use_blx,
bfd_arm_vfp11_fix vfp11_fix,
int no_enum_warn, int no_wchar_warn,
- int pic_veneer, int fix_cortex_a8)
+ int pic_veneer, int fix_cortex_a8,
+ int fix_arm1176)
{
struct elf32_arm_link_hash_table *globals;
globals->vfp11_fix = vfp11_fix;
globals->pic_veneer = pic_veneer;
globals->fix_cortex_a8 = fix_cortex_a8;
+ globals->fix_arm1176 = fix_arm1176;
BFD_ASSERT (is_arm_elf (output_bfd));
elf_arm_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
sreloc->size += RELOC_SIZE (htab) * count;
}
+/* Reserve space for COUNT R_ARM_IRELATIVE relocations. If the link is
+ dynamic, the relocations should go in SRELOC, otherwise they should
+ go in the special .rel.iplt section. */
+
+static void
+elf32_arm_allocate_irelocs (struct bfd_link_info *info, asection *sreloc,
+ bfd_size_type count)
+{
+ struct elf32_arm_link_hash_table *htab;
+
+ htab = elf32_arm_hash_table (info);
+ if (!htab->root.dynamic_sections_created)
+ htab->root.irelplt->size += RELOC_SIZE (htab) * count;
+ else
+ {
+ BFD_ASSERT (sreloc != NULL);
+ sreloc->size += RELOC_SIZE (htab) * count;
+ }
+}
+
/* Add relocation REL to the end of relocation section SRELOC. */
static void
struct elf32_arm_link_hash_table *htab;
htab = elf32_arm_hash_table (info);
+ if (!htab->root.dynamic_sections_created
+ && ELF32_R_TYPE (rel->r_info) == R_ARM_IRELATIVE)
+ sreloc = htab->root.irelplt;
if (sreloc == NULL)
abort ();
loc = sreloc->contents;
SWAP_RELOC_OUT (htab) (output_bfd, rel, loc);
}
+/* Allocate room for a PLT entry described by ROOT_PLT and ARM_PLT.
+ IS_IPLT_ENTRY says whether the entry belongs to .iplt rather than
+ to .plt. */
+
+static void
+elf32_arm_allocate_plt_entry (struct bfd_link_info *info,
+ bfd_boolean is_iplt_entry,
+ union gotplt_union *root_plt,
+ struct arm_plt_info *arm_plt)
+{
+ struct elf32_arm_link_hash_table *htab;
+ asection *splt;
+ asection *sgotplt;
+
+ htab = elf32_arm_hash_table (info);
+
+ if (is_iplt_entry)
+ {
+ splt = htab->root.iplt;
+ sgotplt = htab->root.igotplt;
+
+ /* Allocate room for an R_ARM_IRELATIVE relocation in .rel.iplt. */
+ elf32_arm_allocate_irelocs (info, htab->root.irelplt, 1);
+ }
+ else
+ {
+ splt = htab->root.splt;
+ sgotplt = htab->root.sgotplt;
+
+ /* Allocate room for an R_JUMP_SLOT relocation in .rel.plt. */
+ elf32_arm_allocate_dynrelocs (info, htab->root.srelplt, 1);
+
+ /* If this is the first .plt entry, make room for the special
+ first entry. */
+ if (splt->size == 0)
+ splt->size += htab->plt_header_size;
+ }
+
+ /* Allocate the PLT entry itself, including any leading Thumb stub. */
+ if (elf32_arm_plt_needs_thumb_stub_p (info, arm_plt))
+ splt->size += PLT_THUMB_STUB_SIZE;
+ root_plt->offset = splt->size;
+ splt->size += htab->plt_entry_size;
+
+ if (!htab->symbian_p)
+ {
+ /* We also need to make an entry in the .got.plt section, which
+ will be placed in the .got section by the linker script. */
+ arm_plt->got_offset = sgotplt->size - 8 * htab->num_tls_desc;
+ sgotplt->size += 4;
+ }
+}
+
+/* Fill in a PLT entry and its associated GOT slot. If DYNINDX == -1,
+ the entry lives in .iplt and resolves to (*SYM_VALUE)().
+ Otherwise, DYNINDX is the index of the symbol in the dynamic
+ symbol table and SYM_VALUE is undefined.
+
+ ROOT_PLT points to the offset of the PLT entry from the start of its
+ section (.iplt or .plt). ARM_PLT points to the symbol's ARM-specific
+ bookkeeping information. */
+
+static void
+elf32_arm_populate_plt_entry (bfd *output_bfd, struct bfd_link_info *info,
+ union gotplt_union *root_plt,
+ struct arm_plt_info *arm_plt,
+ int dynindx, bfd_vma sym_value)
+{
+ struct elf32_arm_link_hash_table *htab;
+ asection *sgot;
+ asection *splt;
+ asection *srel;
+ bfd_byte *loc;
+ bfd_vma plt_index;
+ Elf_Internal_Rela rel;
+ bfd_vma plt_header_size;
+ bfd_vma got_header_size;
+
+ htab = elf32_arm_hash_table (info);
+
+ /* Pick the appropriate sections and sizes. */
+ if (dynindx == -1)
+ {
+ splt = htab->root.iplt;
+ sgot = htab->root.igotplt;
+ srel = htab->root.irelplt;
+
+ /* There are no reserved entries in .igot.plt, and no special
+ first entry in .iplt. */
+ got_header_size = 0;
+ plt_header_size = 0;
+ }
+ else
+ {
+ splt = htab->root.splt;
+ sgot = htab->root.sgotplt;
+ srel = htab->root.srelplt;
+
+ got_header_size = get_elf_backend_data (output_bfd)->got_header_size;
+ plt_header_size = htab->plt_header_size;
+ }
+ BFD_ASSERT (splt != NULL && srel != NULL);
+
+ /* Fill in the entry in the procedure linkage table. */
+ if (htab->symbian_p)
+ {
+ BFD_ASSERT (dynindx >= 0);
+ put_arm_insn (htab, output_bfd,
+ elf32_arm_symbian_plt_entry[0],
+ splt->contents + root_plt->offset);
+ bfd_put_32 (output_bfd,
+ elf32_arm_symbian_plt_entry[1],
+ splt->contents + root_plt->offset + 4);
+
+ /* Fill in the entry in the .rel.plt section. */
+ rel.r_offset = (splt->output_section->vma
+ + splt->output_offset
+ + root_plt->offset + 4);
+ rel.r_info = ELF32_R_INFO (dynindx, R_ARM_GLOB_DAT);
+
+ /* Get the index in the procedure linkage table which
+ corresponds to this symbol. This is the index of this symbol
+ in all the symbols for which we are making plt entries. The
+ first entry in the procedure linkage table is reserved. */
+ plt_index = ((root_plt->offset - plt_header_size)
+ / htab->plt_entry_size);
+ }
+ else
+ {
+ bfd_vma got_offset, got_address, plt_address;
+ bfd_vma got_displacement, initial_got_entry;
+ bfd_byte * ptr;
+
+ BFD_ASSERT (sgot != NULL);
+
+ /* Get the offset into the .(i)got.plt table of the entry that
+ corresponds to this function. */
+ got_offset = (arm_plt->got_offset & -2);
+
+ /* Get the index in the procedure linkage table which
+ corresponds to this symbol. This is the index of this symbol
+ in all the symbols for which we are making plt entries.
+ After the reserved .got.plt entries, all symbols appear in
+ the same order as in .plt. */
+ plt_index = (got_offset - got_header_size) / 4;
+
+ /* Calculate the address of the GOT entry. */
+ got_address = (sgot->output_section->vma
+ + sgot->output_offset
+ + got_offset);
+
+ /* ...and the address of the PLT entry. */
+ plt_address = (splt->output_section->vma
+ + splt->output_offset
+ + root_plt->offset);
+
+ ptr = splt->contents + root_plt->offset;
+ if (htab->vxworks_p && info->shared)
+ {
+ unsigned int i;
+ bfd_vma val;
+
+ for (i = 0; i != htab->plt_entry_size / 4; i++, ptr += 4)
+ {
+ 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);
+ if (i == 2 || i == 5)
+ bfd_put_32 (output_bfd, val, ptr);
+ else
+ put_arm_insn (htab, output_bfd, val, ptr);
+ }
+ }
+ else if (htab->vxworks_p)
+ {
+ unsigned int i;
+ bfd_vma val;
+
+ for (i = 0; i != htab->plt_entry_size / 4; i++, ptr += 4)
+ {
+ val = elf32_arm_vxworks_exec_plt_entry[i];
+ if (i == 2)
+ val |= got_address;
+ if (i == 4)
+ val |= 0xffffff & -((root_plt->offset + i * 4 + 8) >> 2);
+ if (i == 5)
+ val |= plt_index * RELOC_SIZE (htab);
+ if (i == 2 || i == 5)
+ bfd_put_32 (output_bfd, val, ptr);
+ else
+ put_arm_insn (htab, output_bfd, val, ptr);
+ }
+
+ 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 (elf32_arm_plt_needs_thumb_stub_p (info, arm_plt))
+ {
+ put_thumb_insn (htab, output_bfd,
+ elf32_arm_plt_thumb_stub[0], ptr - 4);
+ put_thumb_insn (htab, output_bfd,
+ elf32_arm_plt_thumb_stub[1], ptr - 2);
+ }
+
+ put_arm_insn (htab, output_bfd,
+ elf32_arm_plt_entry[0]
+ | ((got_displacement & 0x0ff00000) >> 20),
+ ptr + 0);
+ put_arm_insn (htab, output_bfd,
+ elf32_arm_plt_entry[1]
+ | ((got_displacement & 0x000ff000) >> 12),
+ ptr+ 4);
+ put_arm_insn (htab, output_bfd,
+ elf32_arm_plt_entry[2]
+ | (got_displacement & 0x00000fff),
+ ptr + 8);
+#ifdef FOUR_WORD_PLT
+ bfd_put_32 (output_bfd, elf32_arm_plt_entry[3], ptr + 12);
+#endif
+ }
+
+ /* Fill in the entry in the .rel(a).(i)plt section. */
+ rel.r_offset = got_address;
+ rel.r_addend = 0;
+ if (dynindx == -1)
+ {
+ /* .igot.plt entries use IRELATIVE relocations against SYM_VALUE.
+ The dynamic linker or static executable then calls SYM_VALUE
+ to determine the correct run-time value of the .igot.plt entry. */
+ rel.r_info = ELF32_R_INFO (0, R_ARM_IRELATIVE);
+ initial_got_entry = sym_value;
+ }
+ else
+ {
+ rel.r_info = ELF32_R_INFO (dynindx, R_ARM_JUMP_SLOT);
+ initial_got_entry = (splt->output_section->vma
+ + splt->output_offset);
+ }
+
+ /* Fill in the entry in the global offset table. */
+ bfd_put_32 (output_bfd, initial_got_entry,
+ sgot->contents + got_offset);
+ }
+
+ loc = srel->contents + plt_index * RELOC_SIZE (htab);
+ SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc);
+}
+
/* Some relocations map to different relocations depending on the
target. Return the real relocation. */
struct bfd_link_info * info,
asection * sym_sec,
const char * sym_name,
- int sym_flags,
+ unsigned char st_type,
+ enum arm_st_branch_type branch_type,
struct elf_link_hash_entry * h,
bfd_boolean * unresolved_reloc_p,
char ** error_message)
bfd_byte * hit_data = contents + rel->r_offset;
bfd_vma * local_got_offsets;
bfd_vma * local_tlsdesc_gotents;
- asection * sgot = NULL;
- asection * splt = NULL;
+ asection * sgot;
+ asection * splt;
asection * sreloc = NULL;
asection * srelgot;
bfd_vma addend;
bfd_signed_vma signed_addend;
+ unsigned char dynreloc_st_type;
+ bfd_vma dynreloc_value;
struct elf32_arm_link_hash_table * globals;
+ struct elf32_arm_link_hash_entry *eh;
+ union gotplt_union *root_plt;
+ struct arm_plt_info *arm_plt;
+ bfd_vma plt_offset;
+ bfd_vma gotplt_offset;
+ bfd_boolean has_iplt_entry;
globals = elf32_arm_hash_table (info);
if (globals == NULL)
if (bfd_get_start_address (output_bfd) != 0)
elf_elfheader (output_bfd)->e_flags |= EF_ARM_HASENTRY;
+ eh = (struct elf32_arm_link_hash_entry *) h;
sgot = globals->root.sgot;
- splt = globals->root.splt;
- srelgot = globals->root.srelgot;
local_got_offsets = elf_local_got_offsets (input_bfd);
local_tlsdesc_gotents = elf32_arm_local_tlsdesc_gotent (input_bfd);
+ if (globals->root.dynamic_sections_created)
+ srelgot = globals->root.srelgot;
+ else
+ srelgot = NULL;
+
r_symndx = ELF32_R_SYM (rel->r_info);
if (globals->use_rel)
else
addend = signed_addend = rel->r_addend;
- switch (r_type)
+ /* Record the symbol information that should be used in dynamic
+ relocations. */
+ dynreloc_st_type = st_type;
+ dynreloc_value = value;
+ if (branch_type == ST_BRANCH_TO_THUMB)
+ dynreloc_value |= 1;
+
+ /* Find out whether the symbol has a PLT. Set ST_VALUE, BRANCH_TYPE and
+ VALUE appropriately for relocations that we resolve at link time. */
+ has_iplt_entry = FALSE;
+ if (elf32_arm_get_plt_info (input_bfd, eh, r_symndx, &root_plt, &arm_plt)
+ && root_plt->offset != (bfd_vma) -1)
{
- case R_ARM_NONE:
- /* We don't need to find a value for this symbol. It's just a
- marker. */
+ plt_offset = root_plt->offset;
+ gotplt_offset = arm_plt->got_offset;
+
+ if (h == NULL || eh->is_iplt)
+ {
+ has_iplt_entry = TRUE;
+ splt = globals->root.iplt;
+
+ /* Populate .iplt entries here, because not all of them will
+ be seen by finish_dynamic_symbol. The lower bit is set if
+ we have already populated the entry. */
+ if (plt_offset & 1)
+ plt_offset--;
+ else
+ {
+ elf32_arm_populate_plt_entry (output_bfd, info, root_plt, arm_plt,
+ -1, dynreloc_value);
+ root_plt->offset |= 1;
+ }
+
+ /* Static relocations always resolve to the .iplt entry. */
+ st_type = STT_FUNC;
+ value = (splt->output_section->vma
+ + splt->output_offset
+ + plt_offset);
+ branch_type = ST_BRANCH_TO_ARM;
+
+ /* If there are non-call relocations that resolve to the .iplt
+ entry, then all dynamic ones must too. */
+ if (arm_plt->noncall_refcount != 0)
+ {
+ dynreloc_st_type = st_type;
+ dynreloc_value = value;
+ }
+ }
+ else
+ /* We populate the .plt entry in finish_dynamic_symbol. */
+ splt = globals->root.splt;
+ }
+ else
+ {
+ splt = NULL;
+ plt_offset = (bfd_vma) -1;
+ gotplt_offset = (bfd_vma) -1;
+ }
+
+ switch (r_type)
+ {
+ case R_ARM_NONE:
+ /* We don't need to find a value for this symbol. It's just a
+ marker. */
*unresolved_reloc_p = FALSE;
return bfd_reloc_ok;
&& r_type != R_ARM_CALL
&& r_type != R_ARM_JUMP24
&& r_type != R_ARM_PLT32)
- && h != NULL
- && splt != NULL
- && h->plt.offset != (bfd_vma) -1)
+ && plt_offset != (bfd_vma) -1)
{
- /* If we've created a .plt section, and assigned a PLT entry to
- this function, it should not be known to bind locally. If
- it were, we would have cleared the PLT entry. */
- BFD_ASSERT (!SYMBOL_CALLS_LOCAL (info, h));
+ /* If we've created a .plt section, and assigned a PLT entry
+ to this function, it must either be a STT_GNU_IFUNC reference
+ or not be known to bind locally. In other cases, we should
+ have cleared the PLT entry by now. */
+ BFD_ASSERT (has_iplt_entry || !SYMBOL_CALLS_LOCAL (info, h));
value = (splt->output_section->vma
+ splt->output_offset
- + h->plt.offset);
+ + plt_offset);
*unresolved_reloc_p = FALSE;
return _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset, value,
*unresolved_reloc_p = FALSE;
- if (sreloc == NULL)
+ if (sreloc == NULL && globals->root.dynamic_sections_created)
{
sreloc = _bfd_elf_get_dynamic_reloc_section (input_bfd, input_section,
! globals->use_rel);
int symbol;
/* This symbol is local, or marked to become local. */
- if (sym_flags == STT_ARM_TFUNC)
- value |= 1;
+ BFD_ASSERT (r_type == R_ARM_ABS32 || r_type == R_ARM_ABS32_NOI);
if (globals->symbian_p)
{
asection *osec;
relocate the text and data segments independently,
so the symbol does not matter. */
symbol = 0;
- outrel.r_info = ELF32_R_INFO (symbol, R_ARM_RELATIVE);
+ if (dynreloc_st_type == STT_GNU_IFUNC)
+ /* We have an STT_GNU_IFUNC symbol that doesn't resolve
+ to the .iplt entry. Instead, every non-call reference
+ must use an R_ARM_IRELATIVE relocation to obtain the
+ correct run-time address. */
+ outrel.r_info = ELF32_R_INFO (symbol, R_ARM_IRELATIVE);
+ else
+ outrel.r_info = ELF32_R_INFO (symbol, R_ARM_RELATIVE);
if (globals->use_rel)
relocate = TRUE;
else
- outrel.r_addend += value;
+ outrel.r_addend += dynreloc_value;
}
elf32_arm_add_dynreloc (output_bfd, info, sreloc, &outrel);
return bfd_reloc_ok;
return _bfd_final_link_relocate (howto, input_bfd, input_section,
- contents, rel->r_offset, value,
- (bfd_vma) 0);
+ contents, rel->r_offset,
+ dynreloc_value, (bfd_vma) 0);
}
else switch (r_type)
{
/* Check for Arm calling Arm function. */
/* FIXME: Should we translate the instruction into a BL
instruction instead ? */
- if (sym_flags != STT_ARM_TFUNC)
+ if (branch_type != ST_BRANCH_TO_THUMB)
(*_bfd_error_handler)
(_("\%B: Warning: Arm BLX instruction targets Arm function '%s'."),
input_bfd,
else if (r_type == R_ARM_PC24)
{
/* Check for Arm calling Thumb function. */
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
{
if (elf32_arm_to_thumb_stub (info, sym_name, input_bfd,
output_bfd, input_section,
hash = (struct elf32_arm_link_hash_entry *) h;
stub_type = arm_type_of_stub (info, input_section, rel,
- &sym_flags, hash,
- value, sym_sec,
+ st_type, &branch_type,
+ hash, value, sym_sec,
input_bfd, sym_name);
if (stub_type != arm_stub_none)
{
/* If the call goes through a PLT entry, make sure to
check distance to the right destination address. */
- if (h != NULL
- && splt != NULL
- && h->plt.offset != (bfd_vma) -1)
+ if (plt_offset != (bfd_vma) -1)
{
value = (splt->output_section->vma
+ splt->output_offset
- + h->plt.offset);
+ + plt_offset);
*unresolved_reloc_p = FALSE;
/* The PLT entry is in ARM mode, regardless of the
target function. */
- sym_flags = STT_FUNC;
+ branch_type = ST_BRANCH_TO_ARM;
}
}
}
The jump to the next instruction is optimized as a NOP depending
on the architecture. */
if (h ? (h->root.type == bfd_link_hash_undefweak
- && !(splt != NULL && h->plt.offset != (bfd_vma) -1))
+ && plt_offset == (bfd_vma) -1)
: r_symndx != STN_UNDEF && bfd_is_und_section (sym_sec))
{
value = (bfd_get_32 (input_bfd, hit_data) & 0xf0000000);
if (r_type == R_ARM_CALL)
{
/* Set the H bit in the BLX instruction. */
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
{
if (addend)
value |= (1 << 24);
/* Select the correct instruction (BL or BLX). */
/* Only if we are not handling a BL to a stub. In this
case, mode switching is performed by the stub. */
- if (sym_flags == STT_ARM_TFUNC && !stub_entry)
+ if (branch_type == ST_BRANCH_TO_THUMB && !stub_entry)
value |= (1 << 28);
- else
+ else if (stub_entry || branch_type != ST_BRANCH_UNKNOWN)
{
value &= ~(bfd_vma)(1 << 28);
value |= (1 << 24);
case R_ARM_ABS32:
value += addend;
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
value |= 1;
break;
case R_ARM_REL32:
value += addend;
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
value |= 1;
value -= (input_section->output_section->vma
+ input_section->output_offset + rel->r_offset);
}
value &= 0x7fffffff;
value |= (bfd_get_32 (input_bfd, hit_data) & 0x80000000);
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
value |= 1;
break;
}
The jump to the next instruction is optimized as a NOP.W for
Thumb-2 enabled architectures. */
if (h && h->root.type == bfd_link_hash_undefweak
- && !(splt != NULL && h->plt.offset != (bfd_vma) -1))
+ && plt_offset == (bfd_vma) -1)
{
if (arch_has_thumb2_nop (globals))
{
/* Check for Thumb to Thumb call. */
/* FIXME: Should we translate the instruction into a BL
instruction instead ? */
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
(*_bfd_error_handler)
(_("%B: Warning: Thumb BLX instruction targets thumb function '%s'."),
input_bfd,
If it is a call relative to a section name, then it is not a
function call at all, but rather a long jump. Calls through
the PLT do not require stubs. */
- if (sym_flags != STT_ARM_TFUNC && sym_flags != STT_SECTION
- && (h == NULL || splt == NULL
- || h->plt.offset == (bfd_vma) -1))
+ if (branch_type == ST_BRANCH_TO_ARM && plt_offset == (bfd_vma) -1)
{
if (globals->use_blx && r_type == R_ARM_THM_CALL)
{
return bfd_reloc_dangerous;
}
}
- else if (sym_flags == STT_ARM_TFUNC && globals->use_blx
+ else if (branch_type == ST_BRANCH_TO_THUMB
+ && globals->use_blx
&& r_type == R_ARM_THM_CALL)
{
/* Make sure this is a BL. */
hash = (struct elf32_arm_link_hash_entry *) h;
stub_type = arm_type_of_stub (info, input_section, rel,
- &sym_flags, hash, value, sym_sec,
+ st_type, &branch_type,
+ hash, value, sym_sec,
input_bfd, sym_name);
if (stub_type != arm_stub_none)
{
if ((stub_entry
&& !arm_stub_is_thumb (stub_entry->stub_type))
- || (sym_flags != STT_ARM_TFUNC))
+ || branch_type != ST_BRANCH_TO_THUMB)
lower_insn = (lower_insn & ~0x1000) | 0x0800;
}
}
}
/* Handle calls via the PLT. */
- if (stub_type == arm_stub_none
- && h != NULL
- && splt != NULL
- && h->plt.offset != (bfd_vma) -1)
+ if (stub_type == arm_stub_none && plt_offset != (bfd_vma) -1)
{
value = (splt->output_section->vma
+ splt->output_offset
- + h->plt.offset);
+ + plt_offset);
if (globals->use_blx && r_type == R_ARM_THM_CALL)
{
the BL to a BLX instruction to call the ARM-mode
PLT entry. */
lower_insn = (lower_insn & ~0x1000) | 0x0800;
- sym_flags = STT_FUNC;
+ branch_type = ST_BRANCH_TO_ARM;
}
else
{
/* Target the Thumb stub before the ARM PLT entry. */
value -= PLT_THUMB_STUB_SIZE;
- sym_flags = STT_ARM_TFUNC;
+ branch_type = ST_BRANCH_TO_THUMB;
}
*unresolved_reloc_p = FALSE;
}
}
/* Handle calls via the PLT. */
- if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1)
+ if (plt_offset != (bfd_vma) -1)
{
value = (splt->output_section->vma
+ splt->output_offset
- + h->plt.offset);
+ + plt_offset);
/* Target the Thumb stub before the ARM PLT entry. */
value -= PLT_THUMB_STUB_SIZE;
*unresolved_reloc_p = FALSE;
/* If we are addressing a Thumb function, we need to adjust the
address by one, so that attempts to call the function pointer will
correctly interpret it as Thumb code. */
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
value += 1;
/* Note that sgot->output_offset is not involved in this
if (sgot == NULL)
return bfd_reloc_notsupported;
- if (h != NULL)
+ if (dynreloc_st_type == STT_GNU_IFUNC
+ && plt_offset != (bfd_vma) -1
+ && (h == NULL || SYMBOL_REFERENCES_LOCAL (info, h)))
+ {
+ /* We have a relocation against a locally-binding STT_GNU_IFUNC
+ symbol, and the relocation resolves directly to the runtime
+ target rather than to the .iplt entry. This means that any
+ .got entry would be the same value as the .igot.plt entry,
+ so there's no point creating both. */
+ sgot = globals->root.igotplt;
+ value = sgot->output_offset + gotplt_offset;
+ }
+ else if (h != NULL)
{
bfd_vma off;
}
else
{
- if (info->shared)
- outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
- else
- outrel.r_info = 0;
- outrel.r_addend = value;
- if (sym_flags == STT_ARM_TFUNC)
- outrel.r_addend |= 1;
+ if (dynreloc_st_type == STT_GNU_IFUNC)
+ outrel.r_info = ELF32_R_INFO (0, R_ARM_IRELATIVE);
+ else if (info->shared)
+ outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+ else
+ outrel.r_info = 0;
+ outrel.r_addend = dynreloc_value;
}
/* The GOT entry is initialized to zero by default.
off &= ~1;
else
{
- /* If we are addressing a Thumb function, we need to
- adjust the address by one, so that attempts to
- call the function pointer will correctly
- interpret it as Thumb code. */
- if (sym_flags == STT_ARM_TFUNC)
- value |= 1;
-
if (globals->use_rel)
- bfd_put_32 (output_bfd, value, sgot->contents + off);
+ bfd_put_32 (output_bfd, dynreloc_value, sgot->contents + off);
- if (info->shared)
+ if (info->shared || dynreloc_st_type == STT_GNU_IFUNC)
{
Elf_Internal_Rela outrel;
- BFD_ASSERT (srelgot != NULL);
-
- outrel.r_addend = addend + value;
+ outrel.r_addend = addend + dynreloc_value;
outrel.r_offset = (sgot->output_section->vma
+ sgot->output_offset
+ off);
- outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
+ if (dynreloc_st_type == STT_GNU_IFUNC)
+ outrel.r_info = ELF32_R_INFO (0, R_ARM_IRELATIVE);
+ else
+ outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE);
elf32_arm_add_dynreloc (output_bfd, info, srelgot, &outrel);
}
|| ELF32_R_TYPE(rel->r_info) == R_ARM_THM_TLS_CALL)
{
bfd_signed_vma offset;
+ /* TLS stubs are arm mode. The original symbol is a
+ data object, so branch_type is bogus. */
+ branch_type = ST_BRANCH_TO_ARM;
enum elf32_arm_stub_type stub_type
- = arm_type_of_stub (info, input_section, rel, &sym_flags,
+ = arm_type_of_stub (info, input_section, rel,
+ st_type, &branch_type,
(struct elf32_arm_link_hash_entry *)h,
globals->tls_trampoline, globals->root.splt,
input_bfd, sym_name);
input_section->output_offset
+ rel->r_offset + 4);
- /* Round up the offset to a word boundary */
- offset = (offset + 2) & ~2;
+ if (stub_type != arm_stub_none
+ && arm_stub_is_thumb (stub_type))
+ {
+ lower_insn = 0xd000;
+ }
+ else
+ {
+ lower_insn = 0xc000;
+ /* Round up the offset to a word boundary */
+ offset = (offset + 2) & ~2;
+ }
+
neg = offset < 0;
upper_insn = (0xf000
| ((offset >> 12) & 0x3ff)
| (neg << 10));
- lower_insn = (0xc000
- | (((!((offset >> 23) & 1)) ^ neg) << 13)
+ lower_insn |= (((!((offset >> 23) & 1)) ^ neg) << 13)
| (((!((offset >> 22) & 1)) ^ neg) << 11)
- | ((offset >> 1) & 0x7ff));
+ | ((offset >> 1) & 0x7ff);
bfd_put_16 (input_bfd, upper_insn, hit_data);
bfd_put_16 (input_bfd, lower_insn, hit_data + 2);
return bfd_reloc_ok;
}
case R_ARM_TLS_LE32:
- if (info->shared)
+ if (info->shared && !info->pie)
{
(*_bfd_error_handler)
(_("%B(%A+0x%lx): R_ARM_TLS_LE32 relocation not permitted in shared object"),
if (r_type == R_ARM_MOVW_BREL && value >= 0x10000)
return bfd_reloc_overflow;
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
value |= 1;
if (r_type == R_ARM_MOVT_ABS || r_type == R_ARM_MOVT_PREL
if (r_type == R_ARM_THM_MOVW_BREL && value >= 0x10000)
return bfd_reloc_overflow;
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
value |= 1;
if (r_type == R_ARM_THM_MOVT_ABS || r_type == R_ARM_THM_MOVT_PREL
/* If the target symbol is a Thumb function, then set the
Thumb bit in the address. */
- if (sym_flags == STT_ARM_TFUNC)
+ if (branch_type == ST_BRANCH_TO_THUMB)
signed_value |= 1;
/* Calculate the value of the relevant G_n, in encoded
- relocation;
addend += msec->output_section->vma + msec->output_offset;
- /* Cases here must match those in the preceeding
+ /* Cases here must match those in the preceding
switch statement. */
switch (r_type)
{
if (r == bfd_reloc_continue)
r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
input_section, contents, rel,
- relocation, info, sec, name,
- (h ? ELF_ST_TYPE (h->type) :
- ELF_ST_TYPE (sym->st_info)), h,
+ relocation, info, sec, name, sym_type,
+ (h ? h->target_internal
+ : ARM_SYM_BRANCH_TYPE (sym)), h,
&unresolved_reloc, &error_message);
/* Dynamic relocs are not propagated for SEC_DEBUGGING sections
{
unsigned long r_symndx;
struct elf_link_hash_entry *h = NULL;
+ struct elf32_arm_link_hash_entry *eh;
int r_type;
+ bfd_boolean call_reloc_p;
+ bfd_boolean may_become_dynamic_p;
+ bfd_boolean may_need_local_target_p;
+ union gotplt_union *root_plt;
+ struct arm_plt_info *arm_plt;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx >= symtab_hdr->sh_info)
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
+ eh = (struct elf32_arm_link_hash_entry *) h;
+
+ call_reloc_p = FALSE;
+ may_become_dynamic_p = FALSE;
+ may_need_local_target_p = FALSE;
r_type = ELF32_R_TYPE (rel->r_info);
r_type = arm_real_reloc_type (globals, r_type);
globals->tls_ldm_got.refcount -= 1;
break;
- case R_ARM_ABS32:
- case R_ARM_ABS32_NOI:
- case R_ARM_REL32:
- case R_ARM_REL32_NOI:
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_CALL:
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
case R_ARM_THM_JUMP19:
+ call_reloc_p = TRUE;
+ may_need_local_target_p = TRUE;
+ break;
+
+ case R_ARM_ABS12:
+ if (!globals->vxworks_p)
+ {
+ may_need_local_target_p = TRUE;
+ break;
+ }
+ /* Fall through. */
+ case R_ARM_ABS32:
+ case R_ARM_ABS32_NOI:
+ case R_ARM_REL32:
+ case R_ARM_REL32_NOI:
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
case R_ARM_MOVW_PREL_NC:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
/* Should the interworking branches be here also? */
-
- if (h != NULL)
+ if ((info->shared || globals->root.is_relocatable_executable)
+ && (sec->flags & SEC_ALLOC) != 0)
{
- struct elf32_arm_link_hash_entry *eh;
- struct elf_dyn_relocs **pp;
- struct elf_dyn_relocs *p;
-
- eh = (struct elf32_arm_link_hash_entry *) h;
-
- if (h->plt.refcount > 0)
+ if (h == NULL
+ && (r_type == R_ARM_REL32 || r_type == R_ARM_REL32_NOI))
{
- h->plt.refcount -= 1;
- 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--;
+ call_reloc_p = TRUE;
+ may_need_local_target_p = TRUE;
}
-
- for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
- if (p->sec == sec)
- {
- /* Everything must go for SEC. */
- *pp = p->next;
- break;
- }
+ else
+ may_become_dynamic_p = TRUE;
}
+ else
+ may_need_local_target_p = TRUE;
break;
default:
break;
}
+
+ if (may_need_local_target_p
+ && elf32_arm_get_plt_info (abfd, eh, r_symndx, &root_plt, &arm_plt))
+ {
+ BFD_ASSERT (root_plt->refcount > 0);
+ root_plt->refcount -= 1;
+
+ if (!call_reloc_p)
+ arm_plt->noncall_refcount--;
+
+ if (r_type == R_ARM_THM_CALL)
+ arm_plt->maybe_thumb_refcount--;
+
+ if (r_type == R_ARM_THM_JUMP24
+ || r_type == R_ARM_THM_JUMP19)
+ arm_plt->thumb_refcount--;
+ }
+
+ if (may_become_dynamic_p)
+ {
+ struct elf_dyn_relocs **pp;
+ struct elf_dyn_relocs *p;
+
+ if (h != NULL)
+ pp = &(eh->dyn_relocs);
+ else
+ {
+ Elf_Internal_Sym *isym;
+
+ isym = bfd_sym_from_r_symndx (&globals->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+ pp = elf32_arm_get_local_dynreloc_list (abfd, r_symndx, isym);
+ if (pp == NULL)
+ return FALSE;
+ }
+ for (; (p = *pp) != NULL; pp = &p->next)
+ if (p->sec == sec)
+ {
+ /* Everything must go for SEC. */
+ *pp = p->next;
+ break;
+ }
+ }
}
return TRUE;
bfd *dynobj;
asection *sreloc;
struct elf32_arm_link_hash_table *htab;
- bfd_boolean needs_plt;
+ bfd_boolean call_reloc_p;
+ bfd_boolean may_become_dynamic_p;
+ bfd_boolean may_need_local_target_p;
unsigned long nsyms;
if (info->relocatable)
return FALSE;
}
- dynobj = elf_hash_table (info)->dynobj;
+ if (htab->root.dynobj == NULL)
+ htab->root.dynobj = abfd;
+ if (!create_ifunc_sections (info))
+ return FALSE;
+
+ dynobj = htab->root.dynobj;
+
symtab_hdr = & elf_symtab_hdr (abfd);
sym_hashes = elf_sym_hashes (abfd);
nsyms = NUM_SHDR_ENTRIES (symtab_hdr);
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
+ Elf_Internal_Sym *isym;
struct elf_link_hash_entry *h;
struct elf32_arm_link_hash_entry *eh;
unsigned long r_symndx;
return FALSE;
}
- if (nsyms == 0 || r_symndx < symtab_hdr->sh_info)
- h = NULL;
- else
+ h = NULL;
+ isym = NULL;
+ if (nsyms > 0)
{
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ if (r_symndx < symtab_hdr->sh_info)
+ {
+ /* A local symbol. */
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+ }
+ else
+ {
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ }
}
eh = (struct elf32_arm_link_hash_entry *) h;
+ call_reloc_p = FALSE;
+ may_become_dynamic_p = FALSE;
+ may_need_local_target_p = FALSE;
+
/* Could be done earlier, if h were already available. */
r_type = elf32_arm_tls_transition (info, r_type, h);
switch (r_type)
}
else
{
- bfd_signed_vma *local_got_refcounts;
-
/* This is a global offset table entry for a local symbol. */
- local_got_refcounts = elf_local_got_refcounts (abfd);
- if (local_got_refcounts == NULL)
- {
- bfd_size_type size;
-
- size = symtab_hdr->sh_info;
- size *= (sizeof (bfd_signed_vma)
- + sizeof (bfd_vma) + sizeof (char));
- local_got_refcounts = (bfd_signed_vma *)
- bfd_zalloc (abfd, size);
- if (local_got_refcounts == NULL)
- return FALSE;
- elf_local_got_refcounts (abfd) = local_got_refcounts;
- elf32_arm_local_tlsdesc_gotent (abfd)
- = (bfd_vma *) (local_got_refcounts
- + symtab_hdr->sh_info);
- elf32_arm_local_got_tls_type (abfd)
- = (char *) (elf32_arm_local_tlsdesc_gotent (abfd)
- + symtab_hdr->sh_info);
- }
- local_got_refcounts[r_symndx] += 1;
+ if (!elf32_arm_allocate_local_sym_info (abfd))
+ return FALSE;
+ elf_local_got_refcounts (abfd)[r_symndx] += 1;
old_tls_type = elf32_arm_local_got_tls_type (abfd) [r_symndx];
}
case R_ARM_GOTOFF32:
case R_ARM_GOTPC:
- if (htab->root.sgot == NULL)
- {
- if (htab->root.dynobj == NULL)
- htab->root.dynobj = abfd;
- if (!create_got_section (htab->root.dynobj, info))
- return FALSE;
- }
+ if (htab->root.sgot == NULL
+ && !create_got_section (htab->root.dynobj, info))
+ return FALSE;
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_PC24:
case R_ARM_PLT32:
case R_ARM_CALL:
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
case R_ARM_THM_JUMP19:
- needs_plt = 1;
- goto normal_reloc;
+ call_reloc_p = TRUE;
+ may_need_local_target_p = TRUE;
+ break;
+
+ case R_ARM_ABS12:
+ /* VxWorks uses dynamic R_ARM_ABS12 relocations for
+ ldr __GOTT_INDEX__ offsets. */
+ if (!htab->vxworks_p)
+ {
+ may_need_local_target_p = TRUE;
+ break;
+ }
+ /* Fall through. */
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
- needs_plt = 0;
- normal_reloc:
/* Should the interworking branches be listed here? */
- if (h != NULL)
- {
- /* If this reloc is in a read-only section, we might
- need a copy reloc. We can't check reliably at this
- stage whether the section is read-only, as input
- sections have not yet been mapped to output sections.
- Tentatively set the flag for now, and correct in
- adjust_dynamic_symbol. */
- if (!info->shared)
- h->non_got_ref = 1;
-
- /* We may need a .plt entry if the function this reloc
- refers to is in a different object. We can't tell for
- sure yet, because something later might force the
- symbol local. */
- if (needs_plt)
- h->needs_plt = 1;
-
- /* If we create a PLT entry, this relocation will reference
- 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;
- }
-
- /* If we are creating a shared library or relocatable executable,
- and this is a reloc against a global symbol, or a non PC
- relative reloc against a local symbol, then we need to copy
- the reloc into the shared library. However, if we are linking
- with -Bsymbolic, we do not need to copy a reloc against a
- global symbol which is defined in an object we are
- including in the link (i.e., DEF_REGULAR is set). At
- this point we have not seen all the input files, so it is
- possible that DEF_REGULAR is not set now but will be set
- later (it is never cleared). We account for that
- possibility below by storing information in the
- dyn_relocs field of the hash table entry. */
if ((info->shared || htab->root.is_relocatable_executable)
- && (sec->flags & SEC_ALLOC) != 0
- && ((r_type == R_ARM_ABS32 || r_type == R_ARM_ABS32_NOI)
- || (h != NULL && ! needs_plt
- && (! info->symbolic || ! h->def_regular))))
+ && (sec->flags & SEC_ALLOC) != 0)
{
- struct elf_dyn_relocs *p, **head;
-
- /* When creating a shared object, we must copy these
- reloc types into the output file. We create a reloc
- section in dynobj and make room for this reloc. */
- if (sreloc == NULL)
+ if (h == NULL
+ && (r_type == R_ARM_REL32 || r_type == R_ARM_REL32_NOI))
{
- sreloc = _bfd_elf_make_dynamic_reloc_section
- (sec, dynobj, 2, abfd, ! htab->use_rel);
-
- if (sreloc == NULL)
- return FALSE;
-
- /* BPABI objects never have dynamic relocations mapped. */
- if (htab->symbian_p)
- {
- flagword flags;
-
- flags = bfd_get_section_flags (dynobj, sreloc);
- flags &= ~(SEC_LOAD | SEC_ALLOC);
- bfd_set_section_flags (dynobj, sreloc, flags);
- }
- }
-
- /* If this is a global symbol, we count the number of
- relocations we need for this symbol. */
- if (h != NULL)
- {
- head = &((struct elf32_arm_link_hash_entry *) h)->dyn_relocs;
+ /* In shared libraries and relocatable executables,
+ we treat local relative references as calls;
+ see the related SYMBOL_CALLS_LOCAL code in
+ allocate_dynrelocs. */
+ call_reloc_p = TRUE;
+ may_need_local_target_p = TRUE;
}
else
- {
- /* Track dynamic relocs needed for local syms too.
- We really need local syms available to do this
- easily. Oh well. */
- asection *s;
- void *vpp;
- Elf_Internal_Sym *isym;
-
- isym = bfd_sym_from_r_symndx (&htab->sym_cache,
- abfd, r_symndx);
- if (isym == NULL)
- return FALSE;
-
- s = bfd_section_from_elf_index (abfd, isym->st_shndx);
- if (s == NULL)
- s = sec;
-
- vpp = &elf_section_data (s)->local_dynrel;
- head = (struct elf_dyn_relocs **) vpp;
- }
-
- p = *head;
- if (p == NULL || p->sec != sec)
- {
- bfd_size_type amt = sizeof *p;
-
- p = (struct elf_dyn_relocs *)
- bfd_alloc (htab->root.dynobj, amt);
- if (p == NULL)
- return FALSE;
- p->next = *head;
- *head = p;
- p->sec = sec;
- p->count = 0;
- p->pc_count = 0;
- }
-
- if (r_type == R_ARM_REL32 || r_type == R_ARM_REL32_NOI)
- p->pc_count += 1;
- p->count += 1;
+ /* We are creating a shared library or relocatable
+ executable, and this is a reloc against a global symbol,
+ or a non-PC-relative reloc against a local symbol.
+ We may need to copy the reloc into the output. */
+ may_become_dynamic_p = TRUE;
}
+ else
+ may_need_local_target_p = TRUE;
break;
/* This relocation describes the C++ object vtable hierarchy.
return FALSE;
break;
}
+
+ if (h != NULL)
+ {
+ if (call_reloc_p)
+ /* We may need a .plt entry if the function this reloc
+ refers to is in a different object, regardless of the
+ symbol's type. We can't tell for sure yet, because
+ something later might force the symbol local. */
+ h->needs_plt = 1;
+ else if (may_need_local_target_p)
+ /* If this reloc is in a read-only section, we might
+ need a copy reloc. We can't check reliably at this
+ stage whether the section is read-only, as input
+ sections have not yet been mapped to output sections.
+ Tentatively set the flag for now, and correct in
+ adjust_dynamic_symbol. */
+ h->non_got_ref = 1;
+ }
+
+ if (may_need_local_target_p
+ && (h != NULL || ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC))
+ {
+ union gotplt_union *root_plt;
+ struct arm_plt_info *arm_plt;
+ struct arm_local_iplt_info *local_iplt;
+
+ if (h != NULL)
+ {
+ root_plt = &h->plt;
+ arm_plt = &eh->plt;
+ }
+ else
+ {
+ local_iplt = elf32_arm_create_local_iplt (abfd, r_symndx);
+ if (local_iplt == NULL)
+ return FALSE;
+ root_plt = &local_iplt->root;
+ arm_plt = &local_iplt->arm;
+ }
+
+ /* If the symbol is a function that doesn't bind locally,
+ this relocation will need a PLT entry. */
+ root_plt->refcount += 1;
+
+ if (!call_reloc_p)
+ arm_plt->noncall_refcount++;
+
+ /* 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)
+ arm_plt->maybe_thumb_refcount += 1;
+
+ if (r_type == R_ARM_THM_JUMP24
+ || r_type == R_ARM_THM_JUMP19)
+ arm_plt->thumb_refcount += 1;
+ }
+
+ if (may_become_dynamic_p)
+ {
+ struct elf_dyn_relocs *p, **head;
+
+ /* Create a reloc section in dynobj. */
+ if (sreloc == NULL)
+ {
+ sreloc = _bfd_elf_make_dynamic_reloc_section
+ (sec, dynobj, 2, abfd, ! htab->use_rel);
+
+ if (sreloc == NULL)
+ return FALSE;
+
+ /* BPABI objects never have dynamic relocations mapped. */
+ if (htab->symbian_p)
+ {
+ flagword flags;
+
+ flags = bfd_get_section_flags (dynobj, sreloc);
+ flags &= ~(SEC_LOAD | SEC_ALLOC);
+ bfd_set_section_flags (dynobj, sreloc, flags);
+ }
+ }
+
+ /* If this is a global symbol, count the number of
+ relocations we need for this symbol. */
+ if (h != NULL)
+ head = &((struct elf32_arm_link_hash_entry *) h)->dyn_relocs;
+ else
+ {
+ head = elf32_arm_get_local_dynreloc_list (abfd, r_symndx, isym);
+ if (head == NULL)
+ return FALSE;
+ }
+
+ p = *head;
+ if (p == NULL || p->sec != sec)
+ {
+ bfd_size_type amt = sizeof *p;
+
+ p = (struct elf_dyn_relocs *) bfd_alloc (htab->root.dynobj, amt);
+ if (p == NULL)
+ return FALSE;
+ p->next = *head;
+ *head = p;
+ p->sec = sec;
+ p->count = 0;
+ p->pc_count = 0;
+ }
+
+ if (r_type == R_ARM_REL32 || r_type == R_ARM_REL32_NOI)
+ p->pc_count += 1;
+ p->count += 1;
+ }
}
return TRUE;
Elf_Internal_Shdr **elf_shdrp;
bfd_boolean again;
+ _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
+
/* Marking EH data may cause additional code sections to be marked,
requiring multiple passes. */
again = TRUE;
/* We skip _bfd_dwarf1_find_nearest_line since no known ARM toolchain uses it. */
- if (_bfd_dwarf2_find_nearest_line (abfd, section, symbols, offset,
+ if (_bfd_dwarf2_find_nearest_line (abfd, dwarf_debug_sections,
+ section, symbols, offset,
filename_ptr, functionname_ptr,
line_ptr, 0,
& elf_tdata (abfd)->dwarf2_find_line_info))
/* Make sure we know what is going on here. */
BFD_ASSERT (dynobj != NULL
&& (h->needs_plt
+ || h->type == STT_GNU_IFUNC
|| h->u.weakdef != NULL
|| (h->def_dynamic
&& h->ref_regular
/* If this is a function, put it in the procedure linkage table. We
will fill in the contents of the procedure linkage table later,
when we know the address of the .got section. */
- if (h->type == STT_FUNC || h->type == STT_ARM_TFUNC
- || h->needs_plt)
+ if (h->type == STT_FUNC || h->type == STT_GNU_IFUNC || h->needs_plt)
{
+ /* Calls to STT_GNU_IFUNC symbols always use a PLT, even if the
+ symbol binds locally. */
if (h->plt.refcount <= 0
- || SYMBOL_CALLS_LOCAL (info, h)
- || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
- && h->root.type == bfd_link_hash_undefweak))
+ || (h->type != STT_GNU_IFUNC
+ && (SYMBOL_CALLS_LOCAL (info, h)
+ || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ && h->root.type == bfd_link_hash_undefweak))))
{
/* This case can occur if we saw a PLT32 reloc in an input
file, but the symbol was never referred to by a dynamic
such a case, we don't actually need to build a procedure
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;
+ eh->plt.thumb_refcount = 0;
+ eh->plt.maybe_thumb_refcount = 0;
+ eh->plt.noncall_refcount = 0;
h->needs_plt = 0;
}
and non-function syms in check-relocs; Objects loaded later in
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;
+ eh->plt.thumb_refcount = 0;
+ eh->plt.maybe_thumb_refcount = 0;
+ eh->plt.noncall_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 elf_dyn_relocs *p;
- bfd_signed_vma thumb_refs;
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
- if (h->root.type == bfd_link_hash_warning)
- /* When warning symbols are created, they **replace** the "real"
- entry in the hash table, thus we never get to see the real
- symbol in a hash traversal. So look at it now. */
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
eh = (struct elf32_arm_link_hash_entry *) h;
info = (struct bfd_link_info *) inf;
if (htab == NULL)
return FALSE;
- if (htab->root.dynamic_sections_created
+ if ((htab->root.dynamic_sections_created || h->type == STT_GNU_IFUNC)
&& h->plt.refcount > 0)
{
/* Make sure this symbol is output as a dynamic symbol.
return FALSE;
}
+ /* If the call in the PLT entry binds locally, the associated
+ GOT entry should use an R_ARM_IRELATIVE relocation instead of
+ the usual R_ARM_JUMP_SLOT. Put it in the .iplt section rather
+ than the .plt section. */
+ if (h->type == STT_GNU_IFUNC && SYMBOL_CALLS_LOCAL (info, h))
+ {
+ eh->is_iplt = 1;
+ if (eh->plt.noncall_refcount == 0
+ && SYMBOL_REFERENCES_LOCAL (info, h))
+ /* All non-call references can be resolved directly.
+ This means that they can (and in some cases, must)
+ resolve directly to the run-time target, rather than
+ to the PLT. That in turns means that any .got entry
+ would be equal to the .igot.plt entry, so there's
+ no point having both. */
+ h->got.refcount = 0;
+ }
+
if (info->shared
+ || eh->is_iplt
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
{
- asection *s = htab->root.splt;
-
- /* If this is the first .plt entry, make room for the special
- first entry. */
- if (s->size == 0)
- s->size += htab->plt_header_size;
-
- h->plt.offset = s->size;
-
- /* If we will insert a Thumb trampoline before this PLT, leave room
- for it. */
- 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;
- }
+ elf32_arm_allocate_plt_entry (info, eh->is_iplt, &h->plt, &eh->plt);
/* If this symbol is not defined in a regular file, and we are
not generating a shared library, then set the symbol to this
if (! info->shared
&& !h->def_regular)
{
- h->root.u.def.section = s;
+ h->root.u.def.section = htab->root.splt;
h->root.u.def.value = h->plt.offset;
/* Make sure the function is not marked as Thumb, in case
it is the target of an ABS32 relocation, which will
point to the PLT entry. */
- if (ELF_ST_TYPE (h->type) == STT_ARM_TFUNC)
- h->type = ELF_ST_INFO (ELF_ST_BIND (h->type), STT_FUNC);
- }
-
- /* Make room for this entry. */
- s->size += htab->plt_entry_size;
-
- if (!htab->symbian_p)
- {
- /* We also need to make an entry in the .got.plt section, which
- will be placed in the .got section by the linker script. */
- eh->plt_got_offset = (htab->root.sgotplt->size
- - 8 * htab->num_tls_desc);
- htab->root.sgotplt->size += 4;
+ h->target_internal = ST_BRANCH_TO_ARM;
}
- /* We also need to make an entry in the .rel(a).plt section. */
- elf32_arm_allocate_dynrelocs (info, htab->root.srelplt, 1);
htab->next_tls_desc_index++;
/* VxWorks executables have a second set of relocations for
- elf32_arm_compute_jump_table_size (htab));
htab->root.sgotplt->size += 8;
h->got.offset = (bfd_vma) -2;
- /* plt_got_offset needs to know there's a TLS_DESC
+ /* plt.got_offset needs to know there's a TLS_DESC
reloc in the middle of .got.plt. */
htab->num_tls_desc++;
}
/* Reserve room for the GOT entry's R_ARM_GLOB_DAT relocation. */
elf32_arm_allocate_dynrelocs (info, htab->root.srelgot, 1);
}
+ else if (h->type == STT_GNU_IFUNC
+ && eh->plt.noncall_refcount == 0)
+ /* No non-call references resolve the STT_GNU_IFUNC's PLT entry;
+ they all resolve dynamically instead. Reserve room for the
+ GOT entry's R_ARM_IRELATIVE relocation. */
+ elf32_arm_allocate_irelocs (info, htab->root.srelgot, 1);
else if (info->shared)
/* Reserve room for the GOT entry's R_ARM_RELATIVE relocation. */
elf32_arm_allocate_dynrelocs (info, htab->root.srelgot, 1);
/* Allocate stubs for exported Thumb functions on v4t. */
if (!htab->use_blx && h->dynindx != -1
&& h->def_regular
- && ELF_ST_TYPE (h->type) == STT_ARM_TFUNC
+ && h->target_internal == ST_BRANCH_TO_THUMB
&& ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
{
struct elf_link_hash_entry * th;
NULL, TRUE, FALSE, &bh);
myh = (struct elf_link_hash_entry *) bh;
- myh->type = ELF_ST_INFO (STB_LOCAL, STT_ARM_TFUNC);
+ myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
myh->forced_local = 1;
+ myh->target_internal = ST_BRANCH_TO_THUMB;
eh->export_glue = myh;
th = record_arm_to_thumb_glue (info, h);
/* Point the symbol at the stub. */
h->type = ELF_ST_INFO (ELF_ST_BIND (h->type), STT_FUNC);
+ h->target_internal = ST_BRANCH_TO_ARM;
h->root.u.def.section = th->root.u.def.section;
h->root.u.def.value = th->root.u.def.value & ~1;
}
for (p = eh->dyn_relocs; p != NULL; p = p->next)
{
asection *sreloc = elf_section_data (p->sec)->sreloc;
- elf32_arm_allocate_dynrelocs (info, sreloc, p->count);
+ if (h->type == STT_GNU_IFUNC
+ && eh->plt.noncall_refcount == 0
+ && SYMBOL_REFERENCES_LOCAL (info, h))
+ elf32_arm_allocate_irelocs (info, sreloc, p->count);
+ else
+ elf32_arm_allocate_dynrelocs (info, sreloc, p->count);
}
return TRUE;
struct elf32_arm_link_hash_entry * eh;
struct elf_dyn_relocs * p;
- if (h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
eh = (struct elf32_arm_link_hash_entry *) h;
for (p = eh->dyn_relocs; p != NULL; p = p->next)
{
{
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
+ struct arm_local_iplt_info **local_iplt_ptr, *local_iplt;
char *local_tls_type;
bfd_vma *local_tlsdesc_gotent;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
asection *srel;
bfd_boolean is_vxworks = htab->vxworks_p;
+ unsigned int symndx;
if (! is_arm_elf (ibfd))
continue;
symtab_hdr = & elf_symtab_hdr (ibfd);
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got + locsymcount;
+ local_iplt_ptr = elf32_arm_local_iplt (ibfd);
local_tls_type = elf32_arm_local_got_tls_type (ibfd);
local_tlsdesc_gotent = elf32_arm_local_tlsdesc_gotent (ibfd);
+ symndx = 0;
s = htab->root.sgot;
srel = htab->root.srelgot;
for (; local_got < end_local_got;
- ++local_got, ++local_tls_type, ++local_tlsdesc_gotent)
+ ++local_got, ++local_iplt_ptr, ++local_tls_type,
+ ++local_tlsdesc_gotent, ++symndx)
{
*local_tlsdesc_gotent = (bfd_vma) -1;
+ local_iplt = *local_iplt_ptr;
+ if (local_iplt != NULL)
+ {
+ struct elf_dyn_relocs *p;
+
+ if (local_iplt->root.refcount > 0)
+ {
+ elf32_arm_allocate_plt_entry (info, TRUE,
+ &local_iplt->root,
+ &local_iplt->arm);
+ if (local_iplt->arm.noncall_refcount == 0)
+ /* All references to the PLT are calls, so all
+ non-call references can resolve directly to the
+ run-time target. This means that the .got entry
+ would be the same as the .igot.plt entry, so there's
+ no point creating both. */
+ *local_got = 0;
+ }
+ else
+ {
+ BFD_ASSERT (local_iplt->arm.noncall_refcount == 0);
+ local_iplt->root.offset = (bfd_vma) -1;
+ }
+
+ for (p = local_iplt->dyn_relocs; p != NULL; p = p->next)
+ {
+ asection *psrel;
+
+ psrel = elf_section_data (p->sec)->sreloc;
+ if (local_iplt->arm.noncall_refcount == 0)
+ elf32_arm_allocate_irelocs (info, psrel, p->count);
+ else
+ elf32_arm_allocate_dynrelocs (info, psrel, p->count);
+ }
+ }
if (*local_got > 0)
{
+ Elf_Internal_Sym *isym;
+
*local_got = s->size;
if (*local_tls_type & GOT_TLS_GD)
/* TLS_GD relocs need an 8-byte structure in the GOT. */
- elf32_arm_compute_jump_table_size (htab);
htab->root.sgotplt->size += 8;
*local_got = (bfd_vma) -2;
- /* plt_got_offset needs to know there's a TLS_DESC
+ /* plt.got_offset needs to know there's a TLS_DESC
reloc in the middle of .got.plt. */
htab->num_tls_desc++;
}
s->size += 4;
}
- if ((info->shared && !(*local_tls_type & GOT_TLS_GDESC))
- || *local_tls_type & GOT_TLS_GD)
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache, ibfd, symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ /* If all references to an STT_GNU_IFUNC PLT are calls,
+ then all non-call references, including this GOT entry,
+ resolve directly to the run-time target. */
+ if (ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
+ && (local_iplt == NULL
+ || local_iplt->arm.noncall_refcount == 0))
+ elf32_arm_allocate_irelocs (info, srel, 1);
+ else if ((info->shared && !(*local_tls_type & GOT_TLS_GDESC))
+ || *local_tls_type & GOT_TLS_GD)
elf32_arm_allocate_dynrelocs (info, srel, 1);
if (info->shared && *local_tls_type & GOT_TLS_GDESC)
of the dynobj section names depend upon the input files. */
name = bfd_get_section_name (dynobj, s);
- if (strcmp (name, ".plt") == 0)
+ if (s == htab->root.splt)
{
/* Remember whether there is a PLT. */
plt = s->size != 0;
s->reloc_count = 0;
}
}
- else if (! CONST_STRNEQ (name, ".got")
- && strcmp (name, ".dynbss") != 0)
+ else if (s != htab->root.sgot
+ && s != htab->root.sgotplt
+ && s != htab->root.iplt
+ && s != htab->root.igotplt
+ && s != htab->sdynbss)
{
/* It's not one of our sections, so don't allocate space. */
continue;
if (h->plt.offset != (bfd_vma) -1)
{
- asection * splt;
- asection * srel;
- bfd_byte *loc;
- bfd_vma plt_index;
- Elf_Internal_Rela rel;
-
- /* This symbol has an entry in the procedure linkage table. Set
- it up. */
-
- BFD_ASSERT (h->dynindx != -1);
-
- splt = htab->root.splt;
- srel = htab->root.srelplt;
- BFD_ASSERT (splt != NULL && srel != NULL);
-
- /* Fill in the entry in the procedure linkage table. */
- if (htab->symbian_p)
+ if (!eh->is_iplt)
{
- put_arm_insn (htab, output_bfd,
- elf32_arm_symbian_plt_entry[0],
- splt->contents + h->plt.offset);
- bfd_put_32 (output_bfd,
- elf32_arm_symbian_plt_entry[1],
- splt->contents + h->plt.offset + 4);
-
- /* Fill in the entry in the .rel.plt section. */
- rel.r_offset = (splt->output_section->vma
- + splt->output_offset
- + h->plt.offset + 4);
- rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_GLOB_DAT);
-
- /* Get the index in the procedure linkage table which
- corresponds to this symbol. This is the index of this symbol
- in all the symbols for which we are making plt entries. The
- first entry in the procedure linkage table is reserved. */
- plt_index = ((h->plt.offset - htab->plt_header_size)
- / htab->plt_entry_size);
+ BFD_ASSERT (h->dynindx != -1);
+ elf32_arm_populate_plt_entry (output_bfd, info, &h->plt, &eh->plt,
+ h->dynindx, 0);
}
- else
- {
- bfd_vma got_offset, got_address, plt_address;
- bfd_vma got_displacement;
- asection * sgot;
- bfd_byte * ptr;
-
- sgot = htab->root.sgotplt;
- BFD_ASSERT (sgot != NULL);
-
- /* Get the offset into the .got.plt table of the entry that
- corresponds to this function. */
- got_offset = eh->plt_got_offset;
-
- /* Get the index in the procedure linkage table which
- corresponds to this symbol. This is the index of this symbol
- in all the symbols for which we are making plt entries. The
- first three entries in .got.plt are reserved; after that
- symbols appear in the same order as in .plt. */
- plt_index = (got_offset - 12) / 4;
-
- /* Calculate the address of the GOT entry. */
- got_address = (sgot->output_section->vma
- + sgot->output_offset
- + got_offset);
-
- /* ...and the address of the PLT entry. */
- plt_address = (splt->output_section->vma
- + splt->output_offset
- + h->plt.offset);
-
- ptr = splt->contents + h->plt.offset;
- if (htab->vxworks_p && info->shared)
- {
- unsigned int i;
- bfd_vma val;
-
- for (i = 0; i != htab->plt_entry_size / 4; i++, ptr += 4)
- {
- 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);
- if (i == 2 || i == 5)
- bfd_put_32 (output_bfd, val, ptr);
- else
- put_arm_insn (htab, output_bfd, val, ptr);
- }
- }
- else if (htab->vxworks_p)
- {
- unsigned int i;
- bfd_vma val;
-
- for (i = 0; i != htab->plt_entry_size / 4; i++, ptr += 4)
- {
- 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);
- if (i == 2 || i == 5)
- bfd_put_32 (output_bfd, val, ptr);
- else
- put_arm_insn (htab, output_bfd, val, ptr);
- }
-
- 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
- {
- 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
- of the PLT stub. */
- got_displacement = got_address - (plt_address + 8);
-
- BFD_ASSERT ((got_displacement & 0xf0000000) == 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);
- put_thumb_insn (htab, output_bfd,
- elf32_arm_plt_thumb_stub[1], ptr - 2);
- }
-
- put_arm_insn (htab, output_bfd,
- elf32_arm_plt_entry[0]
- | ((got_displacement & 0x0ff00000) >> 20),
- ptr + 0);
- put_arm_insn (htab, output_bfd,
- elf32_arm_plt_entry[1]
- | ((got_displacement & 0x000ff000) >> 12),
- ptr+ 4);
- put_arm_insn (htab, output_bfd,
- elf32_arm_plt_entry[2]
- | (got_displacement & 0x00000fff),
- ptr + 8);
-#ifdef FOUR_WORD_PLT
- bfd_put_32 (output_bfd, elf32_arm_plt_entry[3], ptr + 12);
-#endif
- }
-
- /* Fill in the entry in the global offset table. */
- bfd_put_32 (output_bfd,
- (splt->output_section->vma
- + splt->output_offset),
- sgot->contents + 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 * RELOC_SIZE (htab);
- SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc);
if (!h->def_regular)
{
if (!h->ref_regular_nonweak)
sym->st_value = 0;
}
+ else if (eh->is_iplt && eh->plt.noncall_refcount != 0)
+ {
+ /* At least one non-call relocation references this .iplt entry,
+ so the .iplt entry is the function's canonical address. */
+ sym->st_info = ELF_ST_INFO (ELF_ST_BIND (sym->st_info), STT_FUNC);
+ sym->st_target_internal = ST_BRANCH_TO_ARM;
+ sym->st_shndx = (_bfd_elf_section_from_bfd_section
+ (output_bfd, htab->root.iplt->output_section));
+ sym->st_value = (h->plt.offset
+ + htab->root.iplt->output_section->vma
+ + htab->root.iplt->output_offset);
+ }
}
if (h->needs_copy)
dynobj = elf_hash_table (info)->dynobj;
sgot = htab->root.sgotplt;
- BFD_ASSERT (htab->symbian_p || sgot != NULL);
+ /* A broken linker script might have discarded the dynamic sections.
+ Catch this here so that we do not seg-fault later on. */
+ if (sgot != NULL && bfd_is_abs_section (sgot->output_section))
+ return FALSE;
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
if (elf_hash_table (info)->dynamic_sections_created)
splt = htab->root.splt;
BFD_ASSERT (splt != NULL && sdyn != NULL);
+ BFD_ASSERT (htab->symbian_p || sgot != NULL);
dyncon = (Elf32_External_Dyn *) sdyn->contents;
dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size);
eh = elf_link_hash_lookup (elf_hash_table (info), name,
FALSE, FALSE, TRUE);
- if (eh != NULL
- && ELF_ST_TYPE (eh->type) == STT_ARM_TFUNC)
+ if (eh != NULL && eh->target_internal == ST_BRANCH_TO_THUMB)
{
dyn.d_un.d_val |= 1;
bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
}
}
-/* Set the right machine number for an Arm ELF file. */
-
-static bfd_boolean
-elf32_arm_section_flags (flagword *flags, const Elf_Internal_Shdr *hdr)
-{
- if (hdr->sh_type == SHT_NOTE)
- *flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_SAME_CONTENTS;
-
- return TRUE;
-}
-
static void
elf32_arm_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED)
{
sym.st_other = 0;
sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
sym.st_shndx = osi->sec_shndx;
+ sym.st_target_internal = 0;
elf32_arm_section_map_add (osi->sec, names[type][1], offset);
return osi->func (osi->finfo, names[type], &sym, osi->sec, NULL) == 1;
}
-
-/* Output mapping symbols for PLT entries associated with H. */
+/* Output mapping symbols for the PLT entry described by ROOT_PLT and ARM_PLT.
+ IS_IPLT_ENTRY_P says whether the PLT is in .iplt rather than .plt. */
static bfd_boolean
-elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
+elf32_arm_output_plt_map_1 (output_arch_syminfo *osi,
+ bfd_boolean is_iplt_entry_p,
+ union gotplt_union *root_plt,
+ struct arm_plt_info *arm_plt)
{
- output_arch_syminfo *osi = (output_arch_syminfo *) inf;
struct elf32_arm_link_hash_table *htab;
- struct elf32_arm_link_hash_entry *eh;
- bfd_vma addr;
-
- if (h->root.type == bfd_link_hash_indirect)
- return TRUE;
-
- if (h->root.type == bfd_link_hash_warning)
- /* When warning symbols are created, they **replace** the "real"
- entry in the hash table, thus we never get to see the real
- symbol in a hash traversal. So look at it now. */
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ bfd_vma addr, plt_header_size;
- if (h->plt.offset == (bfd_vma) -1)
+ if (root_plt->offset == (bfd_vma) -1)
return TRUE;
htab = elf32_arm_hash_table (osi->info);
if (htab == NULL)
return FALSE;
- eh = (struct elf32_arm_link_hash_entry *) h;
- addr = h->plt.offset;
+ if (is_iplt_entry_p)
+ {
+ osi->sec = htab->root.iplt;
+ plt_header_size = 0;
+ }
+ else
+ {
+ osi->sec = htab->root.splt;
+ plt_header_size = htab->plt_header_size;
+ }
+ osi->sec_shndx = (_bfd_elf_section_from_bfd_section
+ (osi->info->output_bfd, osi->sec->output_section));
+
+ addr = root_plt->offset & -2;
if (htab->symbian_p)
{
if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
}
else
{
- bfd_signed_vma thumb_refs;
+ bfd_boolean thumb_stub_p;
- thumb_refs = eh->plt_thumb_refcount;
- if (!htab->use_blx)
- thumb_refs += eh->plt_maybe_thumb_refcount;
-
- if (thumb_refs > 0)
+ thumb_stub_p = elf32_arm_plt_needs_thumb_stub_p (osi->info, arm_plt);
+ if (thumb_stub_p)
{
if (!elf32_arm_output_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_refs > 0 || addr == 20)
+ if (thumb_stub_p || addr == plt_header_size)
{
if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
return FALSE;
return TRUE;
}
+/* Output mapping symbols for PLT entries associated with H. */
+
+static bfd_boolean
+elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
+{
+ output_arch_syminfo *osi = (output_arch_syminfo *) inf;
+ struct elf32_arm_link_hash_entry *eh;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ if (h->root.type == bfd_link_hash_warning)
+ /* When warning symbols are created, they **replace** the "real"
+ entry in the hash table, thus we never get to see the real
+ symbol in a hash traversal. So look at it now. */
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ eh = (struct elf32_arm_link_hash_entry *) h;
+ return elf32_arm_output_plt_map_1 (osi, SYMBOL_CALLS_LOCAL (osi->info, h),
+ &h->plt, &eh->plt);
+}
+
/* Output a single local symbol for a generated stub. */
static bfd_boolean
sym.st_other = 0;
sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
sym.st_shndx = osi->sec_shndx;
+ sym.st_target_internal = 0;
return osi->func (osi->finfo, name, &sym, osi->sec, NULL) == 1;
}
== SEC_HAS_CONTENTS
&& get_arm_elf_section_data (osi.sec) != NULL
&& get_arm_elf_section_data (osi.sec)->mapcount == 0
- && osi.sec->size > 0)
+ && osi.sec->size > 0
+ && (osi.sec->flags & SEC_EXCLUDE) == 0)
{
osi.sec_shndx = _bfd_elf_section_from_bfd_section
(output_bfd, osi.sec->output_section);
}
/* Finally, output mapping symbols for the PLT. */
- if (!htab->root.splt || htab->root.splt->size == 0)
- return TRUE;
-
- osi.sec = htab->root.splt;
- osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd,
- osi.sec->output_section);
- /* Output mapping symbols for the plt header. SymbianOS does not have a
- plt header. */
- if (htab->vxworks_p)
+ if (htab->root.splt && htab->root.splt->size > 0)
{
- /* VxWorks shared libraries have no PLT header. */
- if (!info->shared)
+ osi.sec = htab->root.splt;
+ osi.sec_shndx = (_bfd_elf_section_from_bfd_section
+ (output_bfd, osi.sec->output_section));
+
+ /* Output mapping symbols for the plt header. SymbianOS does not have a
+ plt header. */
+ if (htab->vxworks_p)
+ {
+ /* VxWorks shared libraries have no PLT header. */
+ if (!info->shared)
+ {
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0))
+ return FALSE;
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, 12))
+ return FALSE;
+ }
+ }
+ else if (!htab->symbian_p)
{
if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0))
return FALSE;
- if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, 12))
+#ifndef FOUR_WORD_PLT
+ if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, 16))
return FALSE;
+#endif
}
}
- else if (!htab->symbian_p)
+ if ((htab->root.splt && htab->root.splt->size > 0)
+ || (htab->root.iplt && htab->root.iplt->size > 0))
{
- if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0))
- return FALSE;
-#ifndef FOUR_WORD_PLT
- if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, 16))
- return FALSE;
-#endif
- }
+ elf_link_hash_traverse (&htab->root, elf32_arm_output_plt_map, &osi);
+ for (input_bfd = info->input_bfds;
+ input_bfd != NULL;
+ input_bfd = input_bfd->link_next)
+ {
+ struct arm_local_iplt_info **local_iplt;
+ unsigned int i, num_syms;
+ local_iplt = elf32_arm_local_iplt (input_bfd);
+ if (local_iplt != NULL)
+ {
+ num_syms = elf_symtab_hdr (input_bfd).sh_info;
+ for (i = 0; i < num_syms; i++)
+ if (local_iplt[i] != NULL
+ && !elf32_arm_output_plt_map_1 (&osi, TRUE,
+ &local_iplt[i]->root,
+ &local_iplt[i]->arm))
+ return FALSE;
+ }
+ }
+ }
if (htab->dt_tlsdesc_plt != 0)
{
/* Mapping symbols for the lazy tls trampoline. */
#endif
}
- elf_link_hash_traverse (&htab->root, elf32_arm_output_plt_map, (void *) &osi);
return TRUE;
}
return FALSE;
}
-/* Display STT_ARM_TFUNC symbols as functions. */
-
-static void
-elf32_arm_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED,
- asymbol *asym)
-{
- elf_symbol_type *elfsym = (elf_symbol_type *) asym;
-
- if (ELF_ST_TYPE (elfsym->internal_elf_sym.st_info) == STT_ARM_TFUNC)
- elfsym->symbol.flags |= BSF_FUNCTION;
-}
-
-
/* Mangle thumb function symbols as we read them in. */
static bfd_boolean
return FALSE;
/* New EABI objects mark thumb function symbols by setting the low bit of
- the address. Turn these into STT_ARM_TFUNC. */
- if ((ELF_ST_TYPE (dst->st_info) == STT_FUNC)
- && (dst->st_value & 1))
+ the address. */
+ if (ELF_ST_TYPE (dst->st_info) == STT_FUNC
+ || ELF_ST_TYPE (dst->st_info) == STT_GNU_IFUNC)
+ {
+ if (dst->st_value & 1)
+ {
+ dst->st_value &= ~(bfd_vma) 1;
+ dst->st_target_internal = ST_BRANCH_TO_THUMB;
+ }
+ else
+ dst->st_target_internal = ST_BRANCH_TO_ARM;
+ }
+ else if (ELF_ST_TYPE (dst->st_info) == STT_ARM_TFUNC)
{
- dst->st_info = ELF_ST_INFO (ELF_ST_BIND (dst->st_info), STT_ARM_TFUNC);
- dst->st_value &= ~(bfd_vma) 1;
+ dst->st_info = ELF_ST_INFO (ELF_ST_BIND (dst->st_info), STT_FUNC);
+ dst->st_target_internal = ST_BRANCH_TO_THUMB;
}
+ else if (ELF_ST_TYPE (dst->st_info) == STT_SECTION)
+ dst->st_target_internal = ST_BRANCH_LONG;
+ else
+ dst->st_target_internal = ST_BRANCH_UNKNOWN;
+
return TRUE;
}
of the address set, as per the new EABI. We do this unconditionally
because objcopy does not set the elf header flags until after
it writes out the symbol table. */
- if (ELF_ST_TYPE (src->st_info) == STT_ARM_TFUNC)
+ if (src->st_target_internal == ST_BRANCH_TO_THUMB)
{
newsym = *src;
- newsym.st_info = ELF_ST_INFO (ELF_ST_BIND (src->st_info), STT_FUNC);
+ if (ELF_ST_TYPE (src->st_info) != STT_GNU_IFUNC)
+ newsym.st_info = ELF_ST_INFO (ELF_ST_BIND (src->st_info), STT_FUNC);
if (newsym.st_shndx != SHN_UNDEF)
{
/* Do this only for defined symbols. At link type, the static
return 0;
}
-/* We have two function types: STT_FUNC and STT_ARM_TFUNC. */
+/* Hook called by the linker routine which adds symbols from an object
+ file. */
static bfd_boolean
-elf32_arm_is_function_type (unsigned int type)
-{
- return (type == STT_FUNC) || (type == STT_ARM_TFUNC);
+elf32_arm_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
+ Elf_Internal_Sym *sym, const char **namep,
+ flagword *flagsp, asection **secp, bfd_vma *valp)
+{
+ if ((abfd->flags & DYNAMIC) == 0
+ && (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
+ || ELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE))
+ elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+
+ if (elf32_arm_hash_table (info)->vxworks_p
+ && !elf_vxworks_add_symbol_hook (abfd, info, sym, namep,
+ flagsp, secp, valp))
+ return FALSE;
+
+ return TRUE;
}
/* We use this to override swap_symbol_in and swap_symbol_out. */
#define elf_backend_post_process_headers elf32_arm_post_process_headers
#define elf_backend_reloc_type_class elf32_arm_reloc_type_class
#define elf_backend_object_p elf32_arm_object_p
-#define elf_backend_section_flags elf32_arm_section_flags
#define elf_backend_fake_sections elf32_arm_fake_sections
#define elf_backend_section_from_shdr elf32_arm_section_from_shdr
#define elf_backend_final_write_processing elf32_arm_final_write_processing
#define elf_backend_copy_indirect_symbol elf32_arm_copy_indirect_symbol
-#define elf_backend_symbol_processing elf32_arm_symbol_processing
#define elf_backend_size_info elf32_arm_size_info
#define elf_backend_modify_segment_map elf32_arm_modify_segment_map
#define elf_backend_additional_program_headers elf32_arm_additional_program_headers
#define elf_backend_output_arch_local_syms elf32_arm_output_arch_local_syms
#define elf_backend_begin_write_processing elf32_arm_begin_write_processing
-#define elf_backend_is_function_type elf32_arm_is_function_type
+#define elf_backend_add_symbol_hook elf32_arm_add_symbol_hook
#define elf_backend_can_refcount 1
#define elf_backend_can_gc_sections 1
#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
bfd_boolean flags_compatible = TRUE;
asection *sec;
- /* Check if we have the same endianess. */
+ /* Check if we have the same endianness. */
if (! _bfd_generic_verify_endian_match (ibfd, obfd))
return FALSE;
#define ELF_DYNAMIC_SEC_FLAGS \
(SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED)
-#undef elf_backend_add_symbol_hook
#undef elf_backend_emit_relocs
#undef bfd_elf32_bfd_link_hash_table_create