/* AArch64-specific support for NN-bit ELF.
- Copyright (C) 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2009-2016 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of BFD, the Binary File Descriptor library.
/* Basic data relocations. */
-#if ARCH_SIZE == 64
- HOWTO (R_AARCH64_NULL, /* type */
+ /* Deprecated, but retained for backwards compatibility. */
+ HOWTO64 (R_AARCH64_NULL, /* type */
0, /* rightshift */
3, /* size (0 = byte, 1 = short, 2 = long) */
0, /* bitsize */
0, /* src_mask */
0, /* dst_mask */
FALSE), /* pcrel_offset */
-#else
HOWTO (R_AARCH64_NONE, /* type */
0, /* rightshift */
3, /* size (0 = byte, 1 = short, 2 = long) */
0, /* src_mask */
0, /* dst_mask */
FALSE), /* pcrel_offset */
-#endif
/* .xword: (S+A) */
HOWTO64 (AARCH64_R (ABS64), /* type */
HOWTO (AARCH64_R (MOVW_SABS_G0), /* type */
0, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
- 16, /* bitsize */
+ 17, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
HOWTO64 (AARCH64_R (MOVW_SABS_G1), /* type */
16, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
- 16, /* bitsize */
+ 17, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
HOWTO64 (AARCH64_R (MOVW_SABS_G2), /* type */
32, /* rightshift */
2, /* size (0 = byte, 1 = short, 2 = long) */
- 16, /* bitsize */
+ 17, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
/* Enable ADRP->ADR rewrite for erratum 843419 workaround. */
int fix_erratum_843419_adr;
+ /* Don't apply link-time values for dynamic relocations. */
+ int no_apply_dynamic_relocs;
+
/* The number of bytes in the initial entry in the PLT. */
bfd_size_type plt_header_size;
/* Determine the type of stub needed, if any, for a call. */
static enum elf_aarch64_stub_type
-aarch64_type_of_stub (struct bfd_link_info *info,
- asection *input_sec,
+aarch64_type_of_stub (asection *input_sec,
const Elf_Internal_Rela *rel,
asection *sym_sec,
unsigned char st_type,
- struct elf_aarch64_link_hash_entry *hash,
bfd_vma destination)
{
bfd_vma location;
bfd_signed_vma branch_offset;
unsigned int r_type;
- struct elf_aarch64_link_hash_table *globals;
enum elf_aarch64_stub_type stub_type = aarch64_stub_none;
- bfd_boolean via_plt_p;
if (st_type != STT_FUNC
- && (sym_sec != bfd_abs_section_ptr))
+ && (sym_sec == input_sec))
return stub_type;
- globals = elf_aarch64_hash_table (info);
- via_plt_p = (globals->root.splt != NULL && hash != NULL
- && hash->root.plt.offset != (bfd_vma) - 1);
- /* Make sure call to plt stub can fit into the branch range. */
- if (via_plt_p)
- destination = (globals->root.splt->output_section->vma
- + globals->root.splt->output_offset
- + hash->root.plt.offset);
-
/* Determine where the call point is. */
location = (input_sec->output_offset
+ input_sec->output_section->vma + rel->r_offset);
TRUE, FALSE);
if (stub_entry == NULL)
{
- (*_bfd_error_handler) (_("%s: cannot create stub entry %s"),
- section->owner, stub_name);
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%s: cannot create stub entry %s"),
+ section->owner, stub_name);
return NULL;
}
TRUE, FALSE);
if (stub_entry == NULL)
{
- (*_bfd_error_handler) (_("cannot create stub entry %s"), stub_name);
+ _bfd_error_handler (_("cannot create stub entry %s"), stub_name);
return NULL;
}
}
/* Determine what (if any) linker stub is needed. */
- stub_type = aarch64_type_of_stub
- (info, section, irela, sym_sec, st_type, hash, destination);
+ stub_type = aarch64_type_of_stub (section, irela, sym_sec,
+ st_type, destination);
if (stub_type == aarch64_stub_none)
continue;
goto error_ret_free_internal;
}
- stub_entry->target_value = sym_value;
+ stub_entry->target_value = sym_value + irela->r_addend;
stub_entry->target_section = sym_sec;
stub_entry->stub_type = stub_type;
stub_entry->h = hash;
int no_enum_warn,
int no_wchar_warn, int pic_veneer,
int fix_erratum_835769,
- int fix_erratum_843419)
+ int fix_erratum_843419,
+ int no_apply_dynamic_relocs)
{
struct elf_aarch64_link_hash_table *globals;
globals->fix_erratum_835769 = fix_erratum_835769;
globals->fix_erratum_843419 = fix_erratum_843419;
globals->fix_erratum_843419_adr = TRUE;
+ globals->no_apply_dynamic_relocs = no_apply_dynamic_relocs;
BFD_ASSERT (is_aarch64_elf (output_bfd));
elf_aarch64_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
abfd = stub_entry->target_section->owner;
if (!aarch64_valid_branch_p (veneer_entry_loc, veneered_insn_loc))
- (*_bfd_error_handler)
- (_("%B: error: Erratum 835769 stub out "
- "of range (input file too large)"), abfd);
+ _bfd_error_handler
+ (_("%B: error: Erratum 835769 stub out "
+ "of range (input file too large)"), abfd);
target = stub_entry->target_value;
branch_insn = 0x14000000;
abfd = stub_entry->target_section->owner;
if (!aarch64_valid_branch_p (veneer_entry_loc, veneered_insn_loc))
- (*_bfd_error_handler)
+ _bfd_error_handler
(_("%B: error: Erratum 843419 stub out "
"of range (input file too large)"), abfd);
return FALSE;
}
-/* Perform a relocation as part of a final link. */
+/* Perform a relocation as part of a final link. The input relocation type
+ should be TLS relaxed. */
+
static bfd_reloc_status_type
elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
bfd *input_bfd,
unsigned int r_type = howto->type;
bfd_reloc_code_real_type bfd_r_type
= elfNN_aarch64_bfd_reloc_from_howto (howto);
- bfd_reloc_code_real_type new_bfd_r_type;
unsigned long r_symndx;
bfd_byte *hit_data = contents + rel->r_offset;
bfd_vma place, off;
r_symndx = ELFNN_R_SYM (rel->r_info);
- /* It is possible to have linker relaxations on some TLS access
- models. Update our information here. */
- new_bfd_r_type = aarch64_tls_transition (input_bfd, info, r_type, h, r_symndx);
- if (new_bfd_r_type != bfd_r_type)
- {
- bfd_r_type = new_bfd_r_type;
- howto = elfNN_aarch64_howto_from_bfd_reloc (bfd_r_type);
- BFD_ASSERT (howto != NULL);
- r_type = howto->type;
- }
-
place = input_section->output_section->vma
+ input_section->output_offset + rel->r_offset;
else
name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
NULL);
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: relocation %s against STT_GNU_IFUNC "
"symbol `%s' isn't handled by %s"), input_bfd,
howto->name, name, __FUNCTION__);
else
name = bfd_elf_sym_name (input_bfd, symtab_hdr,
sym, NULL);
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: relocation %s against STT_GNU_IFUNC "
"symbol `%s' has non-zero addend: %d"),
input_bfd, howto->name, name, rel->r_addend);
else if (h != NULL
&& h->dynindx != -1
&& (!bfd_link_pic (info)
- || !SYMBOLIC_BIND (info, h)
+ || !(bfd_link_pie (info)
+ || SYMBOLIC_BIND (info, h))
|| !h->def_regular))
outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type);
else
relocate the text and data segments independently,
so the symbol does not matter. */
symbol = 0;
+ relocate = globals->no_apply_dynamic_relocs ? FALSE : TRUE;
outrel.r_info = ELFNN_R_INFO (symbol, AARCH64_R (RELATIVE));
outrel.r_addend += value;
}
/* Check if a stub has to be inserted because the destination
is too far away. */
struct elf_aarch64_stub_hash_entry *stub_entry = NULL;
- if (! aarch64_valid_branch_p (value, place))
+
+ /* If the branch destination is directed to plt stub, "value" will be
+ the final destination, otherwise we should plus signed_addend, it may
+ contain non-zero value, for example call to local function symbol
+ which are turned into "sec_sym + sec_off", and sec_off is kept in
+ signed_addend. */
+ if (! aarch64_valid_branch_p (via_plt_p ? value : value + signed_addend,
+ place))
/* The target is out of reach, so redirect the branch to
the local stub for this function. */
stub_entry = elfNN_aarch64_get_stub_entry (input_section, sym_sec, h,
rel, globals);
if (stub_entry != NULL)
- value = (stub_entry->stub_offset
- + stub_entry->stub_sec->output_offset
- + stub_entry->stub_sec->output_section->vma);
+ {
+ value = (stub_entry->stub_offset
+ + stub_entry->stub_sec->output_offset
+ + stub_entry->stub_sec->output_section->vma);
+
+ /* We have redirected the destination to stub entry address,
+ so ignore any addend record in the original rela entry. */
+ signed_addend = 0;
+ }
}
value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
signed_addend, weak_undef_p);
{
int howto_index = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: relocation %s against external symbol `%s' can not be used"
" when making a shared object; recompile with -fPIC"),
input_bfd, elfNN_aarch64_howto_table[howto_index].name,
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
+ /* Fall through. */
case BFD_RELOC_AARCH64_16:
#if ARCH_SIZE == 64
if (locals == NULL)
{
int howto_index = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: Local symbol descriptor table be NULL when applying "
"relocation %s against local symbol"),
input_bfd, elfNN_aarch64_howto_table[howto_index].name);
if (locals == NULL)
{
int howto_index = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: Local symbol descriptor table be NULL when applying "
"relocation %s against local symbol"),
input_bfd, elfNN_aarch64_howto_table[howto_index].name);
if (howto == NULL)
{
- (*_bfd_error_handler)
+ /* xgettext:c-format */
+ _bfd_error_handler
(_("%B: unrecognized relocation (0x%x) in section `%A'"),
input_bfd, input_section, r_type);
return FALSE;
if (r_type != R_AARCH64_NONE && r_type != R_AARCH64_NULL
&& bfd_is_und_section (sec)
&& ELF_ST_BIND (sym->st_info) != STB_WEAK)
- {
- if (!info->callbacks->undefined_symbol
- (info, bfd_elf_string_from_elf_section
- (input_bfd, symtab_hdr->sh_link, sym->st_name),
- input_bfd, input_section, rel->r_offset, TRUE))
- return FALSE;
- }
+ (*info->callbacks->undefined_symbol)
+ (info, bfd_elf_string_from_elf_section
+ (input_bfd, symtab_hdr->sh_link, sym->st_name),
+ input_bfd, input_section, rel->r_offset, TRUE);
relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
|| h->root.type == bfd_link_hash_defweak)
&& IS_AARCH64_TLS_RELOC (bfd_r_type) != (sym_type == STT_TLS))
{
- (*_bfd_error_handler)
+ _bfd_error_handler
((sym_type == STT_TLS
+ /* xgettext:c-format */
? _("%B(%A+0x%lx): %s used with TLS symbol %s")
+ /* xgettext:c-format */
: _("%B(%A+0x%lx): %s used with non-TLS symbol %s")),
input_bfd,
input_section, (long) rel->r_offset, howto->name, name);
break;
}
- if (!save_addend)
- addend = 0;
-
-
/* Dynamic relocs are not propagated for SEC_DEBUGGING sections
because such sections are not SEC_ALLOC and thus ld.so will
not process them. */
&& _bfd_elf_section_offset (output_bfd, info, input_section,
+rel->r_offset) != (bfd_vma) - 1)
{
- (*_bfd_error_handler)
- (_
- ("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
+ _bfd_error_handler
+ /* xgettext:c-format */
+ (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
input_bfd, input_section, (long) rel->r_offset, howto->name,
h->root.root.string);
return FALSE;
switch (r)
{
case bfd_reloc_overflow:
- if (!(*info->callbacks->reloc_overflow)
- (info, (h ? &h->root : NULL), name, howto->name, (bfd_vma) 0,
- input_bfd, input_section, rel->r_offset))
- return FALSE;
+ (*info->callbacks->reloc_overflow)
+ (info, (h ? &h->root : NULL), name, howto->name, (bfd_vma) 0,
+ input_bfd, input_section, rel->r_offset);
if (real_r_type == BFD_RELOC_AARCH64_LD64_GOTPAGE_LO15
|| real_r_type == BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14)
{
name, input_bfd, input_section, rel->r_offset);
return FALSE;
}
+ /* Overflow can occur when a variable is referenced with a type
+ that has a larger alignment than the type with which it was
+ declared. eg:
+ file1.c: extern int foo; int a (void) { return foo; }
+ file2.c: char bar, foo, baz;
+ If the variable is placed into a data section at an offset
+ that is incompatible with the larger alignment requirement
+ overflow will occur. (Strictly speaking this is not overflow
+ but rather an alignment problem, but the bfd_reloc_ error
+ enum does not have a value to cover that situation).
+
+ Try to catch this situation here and provide a more helpful
+ error message to the user. */
+ if (addend & ((1 << howto->rightshift) - 1)
+ /* FIXME: Are we testing all of the appropriate reloc
+ types here ? */
+ && (real_r_type == BFD_RELOC_AARCH64_LD_LO19_PCREL
+ || real_r_type == BFD_RELOC_AARCH64_LDST16_LO12
+ || real_r_type == BFD_RELOC_AARCH64_LDST32_LO12
+ || real_r_type == BFD_RELOC_AARCH64_LDST64_LO12
+ || real_r_type == BFD_RELOC_AARCH64_LDST128_LO12))
+ {
+ info->callbacks->warning
+ (info, _("One possible cause of this error is that the \
+symbol is being referenced in the indicated code as if it had a larger \
+alignment than was declared where it was defined."),
+ name, input_bfd, input_section, rel->r_offset);
+ }
break;
case bfd_reloc_undefined:
- if (!((*info->callbacks->undefined_symbol)
- (info, name, input_bfd, input_section,
- rel->r_offset, TRUE)))
- return FALSE;
+ (*info->callbacks->undefined_symbol)
+ (info, name, input_bfd, input_section, rel->r_offset, TRUE);
break;
case bfd_reloc_outofrange:
common_error:
BFD_ASSERT (error_message != NULL);
- if (!((*info->callbacks->reloc_dangerous)
- (info, error_message, input_bfd, input_section,
- rel->r_offset)))
- return FALSE;
+ (*info->callbacks->reloc_dangerous)
+ (info, error_message, input_bfd, input_section, rel->r_offset);
break;
}
}
+
+ if (!save_addend)
+ addend = 0;
}
return TRUE;
object file when linking. */
static bfd_boolean
-elfNN_aarch64_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+elfNN_aarch64_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
{
+ bfd *obfd = info->output_bfd;
flagword out_flags;
flagword in_flags;
bfd_boolean flags_compatible = TRUE;
asection *sec;
/* Check if we have the same endianess. */
- if (!_bfd_generic_verify_endian_match (ibfd, obfd))
+ if (!_bfd_generic_verify_endian_match (ibfd, info))
return FALSE;
if (!is_aarch64_elf (ibfd) || !is_aarch64_elf (obfd))
struct elf_link_hash_table *htab = elf_hash_table (info);
/* This function may be called more than once. */
- s = bfd_get_linker_section (abfd, ".got");
- if (s != NULL)
+ if (htab->sgot != NULL)
return TRUE;
flags = bed->dynamic_sec_flags;
if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
{
- (*_bfd_error_handler) (_("%B: bad symbol index: %d"), abfd,
- r_symndx);
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%B: bad symbol index: %d"), abfd, r_symndx);
return FALSE;
}
if (h != NULL)
{
+ /* If a relocation refers to _GLOBAL_OFFSET_TABLE_, create the .got.
+ This shows up in particular in an R_AARCH64_PREL64 in large model
+ when calculating the pc-relative address to .got section which is
+ used to initialize the gp register. */
+ if (h->root.root.string
+ && strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
+ {
+ if (htab->root.dynobj == NULL)
+ htab->root.dynobj = abfd;
+
+ if (! aarch64_elf_create_got_section (htab->root.dynobj, info))
+ return FALSE;
+
+ BFD_ASSERT (h == htab->root.hgot);
+ }
+
/* Create the ifunc sections for static executables. If we
never see an indirect function symbol nor we are building
a static executable, those sections will be empty and
if (bfd_link_pic (info))
{
int howto_index = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: relocation %s against `%s' can not be used when making "
"a shared object; recompile with -fPIC"),
abfd, elfNN_aarch64_howto_table[howto_index].name,
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
+ /* Fall through. */
case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
&& h->def_regular)
return _bfd_elf_allocate_ifunc_dyn_relocs (info, h,
&eh->dyn_relocs,
+ NULL,
htab->plt_entry_size,
htab->plt_header_size,
- GOT_ENTRY_SIZE);
+ GOT_ENTRY_SIZE,
+ FALSE);
return TRUE;
}
break;
case DT_JMPREL:
- dyn.d_un.d_ptr = htab->root.srelplt->output_section->vma;
+ s = htab->root.srelplt;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
break;
case DT_PLTRELSZ:
dyn.d_un.d_val = s->size;
break;
- case DT_RELASZ:
- /* The procedure linkage table relocs (DT_JMPREL) should
- not be included in the overall relocs (DT_RELA).
- Therefore, we override the DT_RELASZ entry here to
- make it not include the JMPREL relocs. Since the
- linker script arranges for .rela.plt to follow all
- other relocation sections, we don't have to worry
- about changing the DT_RELA entry. */
- if (htab->root.srelplt != NULL)
- {
- s = htab->root.srelplt;
- dyn.d_un.d_val -= s->size;
- }
- break;
-
case DT_TLSDESC_PLT:
s = htab->root.splt;
dyn.d_un.d_ptr = s->output_section->vma + s->output_offset
{
if (bfd_is_abs_section (htab->root.sgotplt->output_section))
{
- (*_bfd_error_handler)
+ _bfd_error_handler
(_("discarded output section: `%A'"), htab->root.sgotplt);
return FALSE;
}
return plt->vma + PLT_ENTRY_SIZE + i * PLT_SMALL_ENTRY_SIZE;
}
+/* Returns TRUE if NAME is an AArch64 mapping symbol.
+ The ARM ELF standard defines $x (for A64 code) and $d (for data).
+ It also allows a period initiated suffix to be added to the symbol, ie:
+ "$[adtx]\.[:sym_char]+". */
+
+static bfd_boolean
+is_aarch64_mapping_symbol (const char * name)
+{
+ return name != NULL /* Paranoia. */
+ && name[0] == '$' /* Note: if objcopy --prefix-symbols has been used then
+ the mapping symbols could have acquired a prefix.
+ We do not support this here, since such symbols no
+ longer conform to the ARM ELF ABI. */
+ && (name[1] == 'd' || name[1] == 'x')
+ && (name[2] == 0 || name[2] == '.');
+ /* FIXME: Strictly speaking the symbol is only a valid mapping symbol if
+ any characters that follow the period are legal characters for the body
+ of a symbol's name. For now we just assume that this is the case. */
+}
+
+/* Make sure that mapping symbols in object files are not removed via the
+ "strip --strip-unneeded" tool. These symbols might needed in order to
+ correctly generate linked files. Once an object file has been linked,
+ it should be safe to remove them. */
+
+static void
+elfNN_aarch64_backend_symbol_processing (bfd *abfd, asymbol *sym)
+{
+ if (((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
+ && sym->section != bfd_abs_section_ptr
+ && is_aarch64_mapping_symbol (sym->name))
+ sym->flags |= BSF_KEEP;
+}
+
/* We use this so we can override certain functions
(though currently we don't). */
#define elf_backend_write_section \
elfNN_aarch64_write_section
+#define elf_backend_symbol_processing \
+ elfNN_aarch64_backend_symbol_processing
+
#define elf_backend_can_refcount 1
#define elf_backend_can_gc_sections 1
#define elf_backend_plt_readonly 1
#define elf_backend_may_use_rela_p 1
#define elf_backend_default_use_rela_p 1
#define elf_backend_rela_normal 1
+#define elf_backend_dtrel_excludes_plt 1
#define elf_backend_got_header_size (GOT_ENTRY_SIZE * 3)
#define elf_backend_default_execstack 0
#define elf_backend_extern_protected_data 1