aboutsummaryrefslogtreecommitdiff
path: root/gold/aarch64.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gold/aarch64.cc')
-rw-r--r--gold/aarch64.cc688
1 files changed, 566 insertions, 122 deletions
diff --git a/gold/aarch64.cc b/gold/aarch64.cc
index e2bdf95..125953a 100644
--- a/gold/aarch64.cc
+++ b/gold/aarch64.cc
@@ -525,7 +525,7 @@ class Reloc_stub
{ return k1.eq(k2); }
};
- private:
+ private:
// Stub type.
const Stub_type stub_type_;
// If this is a local symbol, this is the index in the defining object.
@@ -645,7 +645,7 @@ Reloc_stub<size, big_endian>::stub_type_for_reloc(
branch_offset = dest - location;
break;
default:
- gold_assert(false);
+ gold_unreachable();
}
if (aarch64_valid_branch_offset_p(branch_offset))
@@ -1762,6 +1762,15 @@ class Target_aarch64 : public Sized_target<size, big_endian>
do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
{ return this->plt_section()->address_for_local(relobj, symndx); }
+ // This function should be defined in targets that can use relocation
+ // types to determine (implemented in local_reloc_may_be_function_pointer
+ // and global_reloc_may_be_function_pointer)
+ // if a function's pointer is taken. ICF uses this in safe mode to only
+ // fold those functions whose pointer is defintely not taken.
+ bool
+ do_can_check_for_function_pointers() const
+ { return true; }
+
// Return the number of entries in the PLT.
unsigned int
plt_entry_count() const;
@@ -1959,6 +1968,10 @@ class Target_aarch64 : public Sized_target<size, big_endian>
void
check_non_pic(Relobj*, unsigned int r_type);
+ bool
+ reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>*,
+ unsigned int r_type);
+
// Whether we have issued an error about a non-PIC compilation.
bool issued_non_pic_error_;
};
@@ -2006,6 +2019,15 @@ class Target_aarch64 : public Sized_target<size, big_endian>
const Symbol_value<size>*);
inline typename AArch64_relocate_functions<size, big_endian>::Status
+ tls_ld_to_le(
+ const Relocate_info<size, big_endian>*,
+ Target_aarch64<size, big_endian>*,
+ const elfcpp::Rela<size, big_endian>&,
+ unsigned int,
+ unsigned char*,
+ const Symbol_value<size>*);
+
+ inline typename AArch64_relocate_functions<size, big_endian>::Status
tls_ie_to_le(
const Relocate_info<size, big_endian>*,
Target_aarch64<size, big_endian>*,
@@ -2206,7 +2228,7 @@ const Target::Target_info Target_aarch64<64, false>::aarch64_info =
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
- false, // can_icf_inline_merge_sections
+ true, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib/ld.so.1", // program interpreter
0x400000, // default_text_segment_address
@@ -2260,7 +2282,7 @@ const Target::Target_info Target_aarch64<64, true>::aarch64_info =
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
- false, // can_icf_inline_merge_sections
+ true, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib/ld.so.1", // program interpreter
0x400000, // default_text_segment_address
@@ -2530,7 +2552,7 @@ Target_aarch64<size, big_endian>::scan_reloc_for_stub(
destination = value + addend;
break;
default:
- gold_assert(false);
+ gold_unreachable();
}
typename The_reloc_stub::Stub_type stub_type = The_reloc_stub::
@@ -2838,7 +2860,7 @@ relocate_stub(The_reloc_stub* stub,
break;
default:
- gold_assert(false);
+ gold_unreachable();
}
}
@@ -2861,7 +2883,7 @@ class Output_data_plt_aarch64 : public Output_section_data
Output_data_got_aarch64<size, big_endian>* got,
Output_data_space* got_plt,
Output_data_space* got_irelative)
- : Output_section_data(addralign), tlsdesc_rel_(NULL),
+ : Output_section_data(addralign), tlsdesc_rel_(NULL), irelative_rel_(NULL),
got_(got), got_plt_(got_plt), got_irelative_(got_irelative),
count_(0), irelative_count_(0), tlsdesc_got_offset_(-1U)
{ this->init(layout); }
@@ -2872,7 +2894,18 @@ class Output_data_plt_aarch64 : public Output_section_data
// Add an entry to the PLT.
void
- add_entry(Symbol* gsym);
+ add_entry(Symbol_table*, Layout*, Symbol* gsym);
+
+ // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
+ unsigned int
+ add_local_ifunc_entry(Symbol_table* symtab, Layout*,
+ Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int local_sym_index);
+
+ // Add the relocation for a PLT entry.
+ void
+ add_relocation(Symbol_table*, Layout*, Symbol* gsym,
+ unsigned int got_offset);
// Add the reserved TLSDESC_PLT entry to the PLT.
void
@@ -3081,32 +3114,100 @@ Output_data_plt_aarch64<size, big_endian>::do_adjust_output_section(
template<int size, bool big_endian>
void
-Output_data_plt_aarch64<size, big_endian>::add_entry(Symbol* gsym)
+Output_data_plt_aarch64<size, big_endian>::add_entry(Symbol_table* symtab,
+ Layout* layout, Symbol* gsym)
{
gold_assert(!gsym->has_plt_offset());
- gsym->set_plt_offset((this->count_) * this->get_plt_entry_size()
- + this->first_plt_entry_offset());
+ unsigned int* pcount;
+ unsigned int plt_reserved;
+ Output_section_data_build* got;
- ++this->count_;
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false))
+ {
+ pcount = &this->irelative_count_;
+ plt_reserved = 0;
+ got = this->got_irelative_;
+ }
+ else
+ {
+ pcount = &this->count_;
+ plt_reserved = this->first_plt_entry_offset();
+ got = this->got_plt_;
+ }
- section_offset_type got_offset = this->got_plt_->current_data_size();
+ gsym->set_plt_offset((*pcount) * this->get_plt_entry_size()
+ + plt_reserved);
+
+ ++*pcount;
+
+ section_offset_type got_offset = got->current_data_size();
// Every PLT entry needs a GOT entry which points back to the PLT
// entry (this will be changed by the dynamic linker, normally
// lazily when the function is called).
- this->got_plt_->set_current_data_size(got_offset + size / 8);
+ got->set_current_data_size(got_offset + size / 8);
// Every PLT entry needs a reloc.
- gsym->set_needs_dynsym_entry();
- this->rel_->add_global(gsym, elfcpp::R_AARCH64_JUMP_SLOT,
- this->got_plt_, got_offset, 0);
+ this->add_relocation(symtab, layout, gsym, got_offset);
// Note that we don't need to save the symbol. The contents of the
// PLT are independent of which symbols are used. The symbols only
// appear in the relocations.
}
+// Add an entry to the PLT for a local STT_GNU_IFUNC symbol. Return
+// the PLT offset.
+
+template<int size, bool big_endian>
+unsigned int
+Output_data_plt_aarch64<size, big_endian>::add_local_ifunc_entry(
+ Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int local_sym_index)
+{
+ unsigned int plt_offset = this->irelative_count_ * this->get_plt_entry_size();
+ ++this->irelative_count_;
+
+ section_offset_type got_offset = this->got_irelative_->current_data_size();
+
+ // Every PLT entry needs a GOT entry which points back to the PLT
+ // entry.
+ this->got_irelative_->set_current_data_size(got_offset + size / 8);
+
+ // Every PLT entry needs a reloc.
+ Reloc_section* rela = this->rela_irelative(symtab, layout);
+ rela->add_symbolless_local_addend(relobj, local_sym_index,
+ elfcpp::R_AARCH64_IRELATIVE,
+ this->got_irelative_, got_offset, 0);
+
+ return plt_offset;
+}
+
+// Add the relocation for a PLT entry.
+
+template<int size, bool big_endian>
+void
+Output_data_plt_aarch64<size, big_endian>::add_relocation(
+ Symbol_table* symtab, Layout* layout, Symbol* gsym, unsigned int got_offset)
+{
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false))
+ {
+ Reloc_section* rela = this->rela_irelative(symtab, layout);
+ rela->add_symbolless_global_addend(gsym, elfcpp::R_AARCH64_IRELATIVE,
+ this->got_irelative_, got_offset, 0);
+ }
+ else
+ {
+ gsym->set_needs_dynsym_entry();
+ this->rel_->add_global(gsym, elfcpp::R_AARCH64_JUMP_SLOT, this->got_plt_,
+ got_offset, 0);
+ }
+}
+
// Return where the TLSDESC relocations should go, creating it if
// necessary. These follow the JUMP_SLOT relocations.
@@ -3588,8 +3689,12 @@ Output_data_plt_aarch64<size, big_endian>::do_write(Output_file* of)
unsigned char* const oview = of->get_output_view(offset, oview_size);
const off_t got_file_offset = this->got_plt_->offset();
+ gold_assert(got_file_offset + this->got_plt_->data_size()
+ == this->got_irelative_->offset());
+
const section_size_type got_size =
- convert_to_section_size_type(this->got_plt_->data_size());
+ convert_to_section_size_type(this->got_plt_->data_size()
+ + this->got_irelative_->data_size());
unsigned char* const got_view = of->get_output_view(got_file_offset,
got_size);
@@ -3697,11 +3802,12 @@ class AArch64_relocate_functions
typedef typename The_reloc_stub::Stub_type The_reloc_stub_type;
typedef Stub_table<size, big_endian> The_stub_table;
typedef elfcpp::Rela<size, big_endian> The_rela;
+ typedef typename elfcpp::Swap<size, big_endian>::Valtype AArch64_valtype;
// Return the page address of the address.
// Page(address) = address & ~0xFFF
- static inline typename elfcpp::Swap<size, big_endian>::Valtype
+ static inline AArch64_valtype
Page(Address address)
{
return (address & (~static_cast<Address>(0xFFF)));
@@ -3714,7 +3820,7 @@ class AArch64_relocate_functions
template<int valsize>
static inline void
update_view(unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype immed,
+ AArch64_valtype immed,
elfcpp::Elf_Xword doffset,
elfcpp::Elf_Xword dst_mask)
{
@@ -3736,8 +3842,8 @@ class AArch64_relocate_functions
static inline void
update_view_two_parts(
unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype immed1,
- typename elfcpp::Swap<size, big_endian>::Valtype immed2,
+ AArch64_valtype immed1,
+ AArch64_valtype immed2,
elfcpp::Elf_Xword doffset1,
elfcpp::Elf_Xword doffset2,
elfcpp::Elf_Xword dst_mask)
@@ -3751,17 +3857,13 @@ class AArch64_relocate_functions
(immed2 << doffset2)));
}
- // Update adr or adrp instruction with [32:12] of X.
+ // Update adr or adrp instruction with immed.
// In adr and adrp: [30:29] immlo [23:5] immhi
static inline void
- update_adr(unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype x,
- const AArch64_reloc_property* /* reloc_property */)
+ update_adr(unsigned char* view, AArch64_valtype immed)
{
elfcpp::Elf_Xword dst_mask = (0x3 << 29) | (0x7ffff << 5);
- typename elfcpp::Swap<32, big_endian>::Valtype immed =
- (x >> 12) & 0x1fffff;
This::template update_view_two_parts<32>(
view,
immed & 0x3,
@@ -3774,9 +3876,10 @@ class AArch64_relocate_functions
// Update movz/movn instruction with bits immed.
// Set instruction to movz if is_movz is true, otherwise set instruction
// to movn.
+
static inline void
update_movnz(unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype immed,
+ AArch64_valtype immed,
bool is_movz)
{
typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
@@ -3789,17 +3892,44 @@ class AArch64_relocate_functions
aarch64_howto[AArch64_reloc_property::INST_MOVW].dst_mask;
// Clear immediate fields and opc code.
- val &= ~(dst_mask | (0x11 << 29));
+ val &= ~(dst_mask | (0x3 << 29));
// Set instruction to movz or movn.
// movz: [30:29] is 10 movn: [30:29] is 00
if (is_movz)
- val |= (0x10 << 29);
+ val |= (0x2 << 29);
elfcpp::Swap<32, big_endian>::writeval(wv,
static_cast<Valtype>(val | (immed << doffset)));
}
+ // Update selected bits in text.
+
+ template<int valsize>
+ static inline typename This::Status
+ reloc_common(unsigned char* view, Address x,
+ const AArch64_reloc_property* reloc_property)
+ {
+ // Select bits from X.
+ Address immed = reloc_property->select_x_value(x);
+
+ // Update view.
+ const AArch64_reloc_property::Reloc_inst inst =
+ reloc_property->reloc_inst();
+ // If it is a data relocation or instruction has 2 parts of immediate
+ // fields, you should not call pcrela_general.
+ gold_assert(aarch64_howto[inst].doffset2 == -1 &&
+ aarch64_howto[inst].doffset != -1);
+ This::template update_view<valsize>(view, immed,
+ aarch64_howto[inst].doffset,
+ aarch64_howto[inst].dst_mask);
+
+ // Do check overflow or alignment if needed.
+ return (reloc_property->checkup_x_value(x)
+ ? This::STATUS_OKAY
+ : This::STATUS_OVERFLOW);
+ }
+
public:
// Do a simple rela relocation at unaligned addresses.
@@ -3809,7 +3939,7 @@ class AArch64_relocate_functions
rela_ua(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
const AArch64_reloc_property* reloc_property)
{
typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
@@ -3830,7 +3960,7 @@ class AArch64_relocate_functions
pcrela_ua(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
Address address,
const AArch64_reloc_property* reloc_property)
{
@@ -3852,15 +3982,13 @@ class AArch64_relocate_functions
unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
const AArch64_reloc_property* reloc_property)
{
- typedef typename elfcpp::Swap<valsize, big_endian>::Valtype
- Valtype;
+ typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Address x = psymval->value(object, addend);
- elfcpp::Swap<valsize, big_endian>::writeval(wv,
- static_cast<Valtype>(x));
+ elfcpp::Swap<valsize, big_endian>::writeval(wv,static_cast<Valtype>(x));
return (reloc_property->checkup_x_value(x)
? This::STATUS_OKAY
: This::STATUS_OVERFLOW);
@@ -3874,30 +4002,12 @@ class AArch64_relocate_functions
rela_general(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
const AArch64_reloc_property* reloc_property)
{
// Calculate relocation.
Address x = psymval->value(object, addend);
-
- // Select bits from X.
- Address immed = reloc_property->select_x_value(x);
-
- // Update view.
- const AArch64_reloc_property::Reloc_inst inst =
- reloc_property->reloc_inst();
- // If it is a data relocation or instruction has 2 parts of immediate
- // fields, you should not call rela_general.
- gold_assert(aarch64_howto[inst].doffset2 == -1 &&
- aarch64_howto[inst].doffset != -1);
- This::template update_view<valsize>(view, immed,
- aarch64_howto[inst].doffset,
- aarch64_howto[inst].dst_mask);
-
- // Do check overflow or alignment if needed.
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
+ return This::template reloc_common<valsize>(view, x, reloc_property);
}
// Do relocate. Update selected bits in text.
@@ -3907,31 +4017,13 @@ class AArch64_relocate_functions
static inline typename This::Status
rela_general(
unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype s,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype s,
+ AArch64_valtype addend,
const AArch64_reloc_property* reloc_property)
{
// Calculate relocation.
Address x = s + addend;
-
- // Select bits from X.
- Address immed = reloc_property->select_x_value(x);
-
- // Update view.
- const AArch64_reloc_property::Reloc_inst inst =
- reloc_property->reloc_inst();
- // If it is a data relocation or instruction has 2 parts of immediate
- // fields, you should not call rela_general.
- gold_assert(aarch64_howto[inst].doffset2 == -1 &&
- aarch64_howto[inst].doffset != -1);
- This::template update_view<valsize>(view, immed,
- aarch64_howto[inst].doffset,
- aarch64_howto[inst].dst_mask);
-
- // Do check overflow or alignment if needed.
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
+ return This::template reloc_common<valsize>(view, x, reloc_property);
}
// Do address relative relocate. Update selected bits in text.
@@ -3943,31 +4035,34 @@ class AArch64_relocate_functions
unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
- typename elfcpp::Swap<size, big_endian>::Valtype addend,
+ AArch64_valtype addend,
Address address,
const AArch64_reloc_property* reloc_property)
{
// Calculate relocation.
Address x = psymval->value(object, addend) - address;
+ return This::template reloc_common<valsize>(view, x, reloc_property);
+ }
- // Select bits from X.
- Address immed = reloc_property->select_x_value(x);
- // Update view.
- const AArch64_reloc_property::Reloc_inst inst =
- reloc_property->reloc_inst();
- // If it is a data relocation or instruction has 2 parts of immediate
- // fields, you should not call pcrela_general.
- gold_assert(aarch64_howto[inst].doffset2 == -1 &&
- aarch64_howto[inst].doffset != -1);
- This::template update_view<valsize>(view, immed,
- aarch64_howto[inst].doffset,
- aarch64_howto[inst].dst_mask);
+ // Calculate (S + A) - address, update adr instruction.
- // Do check overflow or alignment if needed.
- return (reloc_property->checkup_x_value(x)
- ? This::STATUS_OKAY
- : This::STATUS_OVERFLOW);
+ static inline typename This::Status
+ adr(unsigned char* view,
+ const Sized_relobj_file<size, big_endian>* object,
+ const Symbol_value<size>* psymval,
+ Address addend,
+ Address address,
+ const AArch64_reloc_property* /* reloc_property */)
+ {
+ AArch64_valtype x = psymval->value(object, addend) - address;
+ // Pick bits [20:0] of X.
+ AArch64_valtype immed = x & 0x1fffff;
+ update_adr(view, immed);
+ // Check -2^20 <= X < 2^20
+ return (size == 64 && Bits<21>::has_overflow((x))
+ ? This::STATUS_OVERFLOW
+ : This::STATUS_OKAY);
}
// Calculate PG(S+A) - PG(address), update adrp instruction.
@@ -3979,9 +4074,10 @@ class AArch64_relocate_functions
Address sa,
Address address)
{
- typename elfcpp::Swap<size, big_endian>::Valtype x =
- This::Page(sa) - This::Page(address);
- update_adr(view, x, NULL);
+ AArch64_valtype x = This::Page(sa) - This::Page(address);
+ // Pick [32:12] of X.
+ AArch64_valtype immed = (x >> 12) & 0x1fffff;
+ update_adr(view, immed);
// Check -2^32 <= X < 2^32
return (size == 64 && Bits<33>::has_overflow((x))
? This::STATUS_OVERFLOW
@@ -4000,9 +4096,10 @@ class AArch64_relocate_functions
const AArch64_reloc_property* reloc_property)
{
Address sa = psymval->value(object, addend);
- typename elfcpp::Swap<size, big_endian>::Valtype x =
- This::Page(sa) - This::Page(address);
- update_adr(view, x, reloc_property);
+ AArch64_valtype x = This::Page(sa) - This::Page(address);
+ // Pick [32:12] of X.
+ AArch64_valtype immed = (x >> 12) & 0x1fffff;
+ update_adr(view, immed);
return (reloc_property->checkup_x_value(x)
? This::STATUS_OKAY
: This::STATUS_OVERFLOW);
@@ -4016,15 +4113,21 @@ class AArch64_relocate_functions
static inline typename This::Status
movnz(unsigned char* view,
- typename elfcpp::Swap<size, big_endian>::Valtype x,
+ AArch64_valtype x,
const AArch64_reloc_property* reloc_property)
{
// Select bits from X.
- Address immed = reloc_property->select_x_value(x);
- bool is_movz = true;
- if (static_cast<int64_t>(x) < 0)
+ Address immed;
+ bool is_movz;
+ typedef typename elfcpp::Elf_types<size>::Elf_Swxword SignedW;
+ if (static_cast<SignedW>(x) >= 0)
+ {
+ immed = reloc_property->select_x_value(x);
+ is_movz = true;
+ }
+ else
{
- immed = ~immed;
+ immed = reloc_property->select_x_value(~x);;
is_movz = false;
}
@@ -4433,6 +4536,15 @@ Target_aarch64<size, big_endian>::optimize_tls_reloc(bool is_final,
return tls::TLSOPT_TO_LE;
return tls::TLSOPT_TO_IE;
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ // These are Local-Dynamic, which refer to local symbols in the
+ // dynamic TLS block. Since we know that we generating an
+ // executable, we can switch to Local-Exec.
+ return tls::TLSOPT_TO_LE;
+
case elfcpp::R_AARCH64_TLSIE_MOVW_GOTTPREL_G1:
case elfcpp::R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC:
case elfcpp::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
@@ -4472,8 +4584,11 @@ Target_aarch64<size, big_endian>::Scan::possible_function_pointer_reloc(
{
switch (r_type)
{
- case elfcpp::R_AARCH64_ABS64:
- //TODO
+ case elfcpp::R_AARCH64_ADR_PREL_PG_HI21:
+ case elfcpp::R_AARCH64_ADR_PREL_PG_HI21_NC:
+ case elfcpp::R_AARCH64_ADD_ABS_LO12_NC:
+ case elfcpp::R_AARCH64_ADR_GOT_PAGE:
+ case elfcpp::R_AARCH64_LD64_GOT_LO12_NC:
{
return true;
}
@@ -4498,9 +4613,7 @@ Target_aarch64<size, big_endian>::Scan::local_reloc_may_be_function_pointer(
unsigned int r_type,
const elfcpp::Sym<size, big_endian>&)
{
- // When building a shared library, do not fold any local symbols as it is
- // not possible to distinguish pointer taken versus a call by looking at
- // the relocation types.
+ // When building a shared library, do not fold any local symbols.
return (parameters->options().shared()
|| possible_function_pointer_reloc(r_type));
}
@@ -4586,6 +4699,29 @@ Target_aarch64<size, big_endian>::Scan::check_non_pic(Relobj* object,
return;
}
+// Return whether we need to make a PLT entry for a relocation of the
+// given type against a STT_GNU_IFUNC symbol.
+
+template<int size, bool big_endian>
+bool
+Target_aarch64<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
+ Sized_relobj_file<size, big_endian>* object,
+ unsigned int r_type)
+{
+ const AArch64_reloc_property* arp =
+ aarch64_reloc_property_table->get_reloc_property(r_type);
+ gold_assert(arp != NULL);
+
+ int flags = arp->reference_flags();
+ if (flags & Symbol::TLS_REF)
+ {
+ gold_error(_("%s: unsupported TLS reloc %s for IFUNC symbol"),
+ object->name().c_str(), arp->name().c_str());
+ return false;
+ }
+ return flags != 0;
+}
+
// Scan a relocation for a local symbol.
template<int size, bool big_endian>
@@ -4599,7 +4735,7 @@ Target_aarch64<size, big_endian>::Scan::local(
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& rela,
unsigned int r_type,
- const elfcpp::Sym<size, big_endian>& /* lsym */,
+ const elfcpp::Sym<size, big_endian>& lsym,
bool is_discarded)
{
if (is_discarded)
@@ -4611,6 +4747,11 @@ Target_aarch64<size, big_endian>::Scan::local(
target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
+ // A local STT_GNU_IFUNC symbol may require a PLT entry.
+ bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
+ if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))
+ target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
+
switch (r_type)
{
case elfcpp::R_AARCH64_ABS32:
@@ -4634,7 +4775,7 @@ Target_aarch64<size, big_endian>::Scan::local(
data_shndx,
rela.get_r_offset(),
rela.get_r_addend(),
- false /* is ifunc */);
+ is_ifunc);
}
break;
@@ -4725,6 +4866,25 @@ Target_aarch64<size, big_endian>::Scan::local(
}
break;
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC:
+ {
+ tls::Tls_optimization tlsopt = Target_aarch64<size, big_endian>::
+ optimize_tls_reloc(!parameters->options().shared(), r_type);
+ if (tlsopt == tls::TLSOPT_NONE)
+ {
+ // Create a GOT entry for the module index.
+ target->got_mod_index_entry(symtab, layout, object);
+ }
+ else if (tlsopt != tls::TLSOPT_TO_LE)
+ unsupported_reloc_local(object, r_type);
+ }
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ break;
+
case elfcpp::R_AARCH64_TLSDESC_ADR_PAGE21:
case elfcpp::R_AARCH64_TLSDESC_LD64_LO12:
case elfcpp::R_AARCH64_TLSDESC_ADD_LO12:
@@ -4801,6 +4961,11 @@ Target_aarch64<size, big_endian>::Scan::global(
unsigned int r_type,
Symbol* gsym)
{
+ // A STT_GNU_IFUNC symbol may require a PLT entry.
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && this->reloc_needs_plt_for_ifunc(object, r_type))
+ target->make_plt_entry(symtab, layout, gsym);
+
typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian>
Reloc_section;
const AArch64_reloc_property* arp =
@@ -4834,6 +4999,25 @@ Target_aarch64<size, big_endian>::Scan::global(
data_shndx, output_section, gsym, rela);
}
else if (r_type == elfcpp::R_AARCH64_ABS64
+ && gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false)
+ && !gsym->is_from_dynobj()
+ && !gsym->is_undefined()
+ && !gsym->is_preemptible())
+ {
+ // Use an IRELATIVE reloc for a locally defined STT_GNU_IFUNC
+ // symbol. This makes a function address in a PIE executable
+ // match the address in a shared library that it links against.
+ Reloc_section* rela_dyn =
+ target->rela_irelative_section(layout);
+ unsigned int r_type = elfcpp::R_AARCH64_IRELATIVE;
+ rela_dyn->add_symbolless_global_addend(gsym, r_type,
+ output_section, object,
+ data_shndx,
+ rela.get_r_offset(),
+ rela.get_r_addend());
+ }
+ else if (r_type == elfcpp::R_AARCH64_ABS64
&& gsym->can_use_relative_reloc(false))
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
@@ -4905,21 +5089,54 @@ Target_aarch64<size, big_endian>::Scan::global(
target->got_section(symtab, layout);
if (gsym->final_value_is_known())
{
- got->add_global(gsym, GOT_TYPE_STANDARD);
+ // For a STT_GNU_IFUNC symbol we want the PLT address.
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC)
+ got->add_global_plt(gsym, GOT_TYPE_STANDARD);
+ else
+ got->add_global(gsym, GOT_TYPE_STANDARD);
}
else
{
+ // If this symbol is not fully resolved, we need to add a dynamic
+ // relocation for it.
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+
+ // Use a GLOB_DAT rather than a RELATIVE reloc if:
+ //
+ // 1) The symbol may be defined in some other module.
+ // 2) We are building a shared library and this is a protected
+ // symbol; using GLOB_DAT means that the dynamic linker can use
+ // the address of the PLT in the main executable when appropriate
+ // so that function address comparisons work.
+ // 3) This is a STT_GNU_IFUNC symbol in position dependent code,
+ // again so that function address comparisons work.
if (gsym->is_from_dynobj()
|| gsym->is_undefined()
|| gsym->is_preemptible()
|| (gsym->visibility() == elfcpp::STV_PROTECTED
- && parameters->options().shared()))
+ && parameters->options().shared())
+ || (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && parameters->options().output_is_position_independent()))
got->add_global_with_rel(gsym, GOT_TYPE_STANDARD,
rela_dyn, elfcpp::R_AARCH64_GLOB_DAT);
else
{
- if (got->add_global(gsym, GOT_TYPE_STANDARD))
+ // For a STT_GNU_IFUNC symbol we want to write the PLT
+ // offset into the GOT, so that function pointer
+ // comparisons work correctly.
+ bool is_new;
+ if (gsym->type() != elfcpp::STT_GNU_IFUNC)
+ is_new = got->add_global(gsym, GOT_TYPE_STANDARD);
+ else
+ {
+ is_new = got->add_global_plt(gsym, GOT_TYPE_STANDARD);
+ // Tell the dynamic linker to use the PLT address
+ // when resolving relocations.
+ if (gsym->is_from_dynobj()
+ && !parameters->options().shared())
+ gsym->set_needs_dynsym_value();
+ }
+ if (is_new)
{
rela_dyn->add_global_relative(
gsym, elfcpp::R_AARCH64_RELATIVE,
@@ -4974,10 +5191,29 @@ Target_aarch64<size, big_endian>::Scan::global(
}
break;
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC: // Local dynamic
+ {
+ tls::Tls_optimization tlsopt = Target_aarch64<size, big_endian>::
+ optimize_tls_reloc(!parameters->options().shared(), r_type);
+ if (tlsopt == tls::TLSOPT_NONE)
+ {
+ // Create a GOT entry for the module index.
+ target->got_mod_index_entry(symtab, layout, object);
+ }
+ else if (tlsopt != tls::TLSOPT_TO_LE)
+ unsupported_reloc_local(object, r_type);
+ }
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC: // Other local dynamic
+ break;
+
case elfcpp::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case elfcpp::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: // Initial executable
{
- tls::Tls_optimization tlsopt =Target_aarch64<size, big_endian>::
+ tls::Tls_optimization tlsopt = Target_aarch64<size, big_endian>::
optimize_tls_reloc(gsym->final_value_is_known(), r_type);
if (tlsopt == tls::TLSOPT_TO_LE)
break;
@@ -5113,7 +5349,26 @@ Target_aarch64<size, big_endian>::make_plt_entry(
if (this->plt_ == NULL)
this->make_plt_section(symtab, layout);
- this->plt_->add_entry(gsym);
+ this->plt_->add_entry(symtab, layout, gsym);
+}
+
+// Make a PLT entry for a local STT_GNU_IFUNC symbol.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::make_local_ifunc_plt_entry(
+ Symbol_table* symtab, Layout* layout,
+ Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int local_sym_index)
+{
+ if (relobj->local_has_plt_offset(local_sym_index))
+ return;
+ if (this->plt_ == NULL)
+ this->make_plt_section(symtab, layout);
+ unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout,
+ relobj,
+ local_sym_index);
+ relobj->set_local_plt_offset(local_sym_index, plt_offset);
}
template<int size, bool big_endian>
@@ -5441,6 +5696,16 @@ Target_aarch64<size, big_endian>::Relocate::relocate(
view, object, psymval, addend, address, reloc_property);
break;
+ case elfcpp::R_AARCH64_LD_PREL_LO19:
+ reloc_status = Reloc::template pcrela_general<32>(
+ view, object, psymval, addend, address, reloc_property);
+ break;
+
+ case elfcpp::R_AARCH64_ADR_PREL_LO21:
+ reloc_status = Reloc::adr(view, object, psymval, addend,
+ address, reloc_property);
+ break;
+
case elfcpp::R_AARCH64_ADR_PREL_PG_HI21_NC:
case elfcpp::R_AARCH64_ADR_PREL_PG_HI21:
reloc_status = Reloc::adrp(view, object, psymval, addend, address,
@@ -5498,6 +5763,10 @@ Target_aarch64<size, big_endian>::Relocate::relocate(
case elfcpp::R_AARCH64_TLSGD_ADR_PAGE21:
case elfcpp::R_AARCH64_TLSGD_ADD_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
case elfcpp::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case elfcpp::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
@@ -5640,7 +5909,7 @@ Target_aarch64<size, big_endian>::Relocate::relocate_tls(
break;
default:
- gold_assert(false);
+ gold_unreachable();
}
}
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
@@ -5649,6 +5918,81 @@ Target_aarch64<size, big_endian>::Relocate::relocate_tls(
}
break;
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC: // Local-dynamic
+ {
+ if (tlsopt == tls::TLSOPT_TO_LE)
+ {
+ if (tls_segment == NULL)
+ {
+ gold_assert(parameters->errors()->error_count() > 0
+ || issue_undefined_symbol_error(gsym));
+ return aarch64_reloc_funcs::STATUS_BAD_RELOC;
+ }
+ return this->tls_ld_to_le(relinfo, target, rela, r_type, view,
+ psymval);
+ }
+
+ gold_assert(tlsopt == tls::TLSOPT_NONE);
+ // Relocate the field with the offset of the GOT entry for
+ // the module index.
+ typename elfcpp::Elf_types<size>::Elf_Addr got_entry_address;
+ got_entry_address = (target->got_mod_index_entry(NULL, NULL, NULL) +
+ target->got_->address());
+
+ switch (r_type)
+ {
+ case elfcpp::R_AARCH64_TLSLD_ADR_PAGE21:
+ return aarch64_reloc_funcs::adrp(
+ view, got_entry_address + addend, address);
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC:
+ return aarch64_reloc_funcs::template rela_general<32>(
+ view, got_entry_address, addend, reloc_property);
+ break;
+
+ default:
+ gold_unreachable();
+ }
+ }
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC: // Other local-dynamic
+ {
+ AArch64_address value = psymval->value(object, 0);
+ if (tlsopt == tls::TLSOPT_TO_LE)
+ {
+ if (tls_segment == NULL)
+ {
+ gold_assert(parameters->errors()->error_count() > 0
+ || issue_undefined_symbol_error(gsym));
+ return aarch64_reloc_funcs::STATUS_BAD_RELOC;
+ }
+ // If building executable, _TLS_MODULE_BASE_ points to segment
+ // end. Thus we must subtract it from value.
+ value -= tls_segment->memsz();
+ }
+ switch (r_type)
+ {
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G1:
+ return aarch64_reloc_funcs::movnz(view, value + addend,
+ reloc_property);
+ break;
+
+ case elfcpp::R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC:
+ return aarch64_reloc_funcs::template rela_general<32>(
+ view, value, addend, reloc_property);
+ break;
+
+ default:
+ gold_unreachable();
+ }
+ // We should never reach here.
+ }
+ break;
+
case elfcpp::R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case elfcpp::R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: // Initial-exec
{
@@ -5692,7 +6036,7 @@ Target_aarch64<size, big_endian>::Relocate::relocate_tls(
return aarch64_reloc_funcs::template rela_general<32>(
view, got_entry_address, addend, reloc_property);
default:
- gold_assert(false);
+ gold_unreachable();
}
}
// We shall never reach here.
@@ -5918,6 +6262,106 @@ Target_aarch64<size, big_endian>::Relocate::tls_gd_to_le(
template<int size, bool big_endian>
inline
typename AArch64_relocate_functions<size, big_endian>::Status
+Target_aarch64<size, big_endian>::Relocate::tls_ld_to_le(
+ const Relocate_info<size, big_endian>* relinfo,
+ Target_aarch64<size, big_endian>* target,
+ const elfcpp::Rela<size, big_endian>& rela,
+ unsigned int r_type,
+ unsigned char* view,
+ const Symbol_value<size>* psymval)
+{
+ typedef AArch64_relocate_functions<size, big_endian> aarch64_reloc_funcs;
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
+
+ Insntype* ip = reinterpret_cast<Insntype*>(view);
+ Insntype insn1 = elfcpp::Swap<32, big_endian>::readval(ip);
+ Insntype insn2 = elfcpp::Swap<32, big_endian>::readval(ip + 1);
+ Insntype insn3 = elfcpp::Swap<32, big_endian>::readval(ip + 2);
+
+ if (r_type == elfcpp::R_AARCH64_TLSLD_ADD_LO12_NC)
+ {
+ // This is the 2nd relocs, optimization should already have been
+ // done.
+ gold_assert((insn1 & 0xfff00000) == 0x91400000);
+ return aarch64_reloc_funcs::STATUS_OKAY;
+ }
+
+ // The original sequence is -
+ // 90000000 adrp x0, 0 <main>
+ // 91000000 add x0, x0, #0x0
+ // 94000000 bl 0 <__tls_get_addr>
+ // optimized to sequence -
+ // d53bd040 mrs x0, tpidr_el0
+ // 91400000 add x0, x0, #0x0, lsl #12
+ // 91000000 add x0, x0, #0x0
+
+ // Unlike tls_ie_to_le, we change the 3 insns in one function call when we
+ // encounter the first relocation "R_AARCH64_TLSLD_ADR_PAGE21". Because we
+ // have to change "bl tls_get_addr", which does not have a corresponding tls
+ // relocation type. So before proceeding, we need to make sure compiler
+ // does not change the sequence.
+ if(!(insn1 == 0x90000000 // adrp x0,0
+ && insn2 == 0x91000000 // add x0, x0, #0x0
+ && insn3 == 0x94000000)) // bl 0
+ {
+ // Ideally we should give up gd_to_le relaxation and do gd access.
+ // However the gd_to_le relaxation decision has been made early
+ // in the scan stage, where we did not allocate any GOT entry for
+ // this symbol. Therefore we have to exit and report error now.
+ gold_error(_("unexpected reloc insn sequence while relaxing "
+ "tls gd to le for reloc %u."), r_type);
+ return aarch64_reloc_funcs::STATUS_BAD_RELOC;
+ }
+
+ // Write new insns.
+ insn1 = 0xd53bd040; // mrs x0, tpidr_el0
+ insn2 = 0x91400000; // add x0, x0, #0x0, lsl #12
+ insn3 = 0x91000000; // add x0, x0, #0x0
+ elfcpp::Swap<32, big_endian>::writeval(ip, insn1);
+ elfcpp::Swap<32, big_endian>::writeval(ip + 1, insn2);
+ elfcpp::Swap<32, big_endian>::writeval(ip + 2, insn3);
+
+ // Calculate tprel value.
+ Output_segment* tls_segment = relinfo->layout->tls_segment();
+ gold_assert(tls_segment != NULL);
+ AArch64_address value = psymval->value(relinfo->object, 0);
+ const elfcpp::Elf_Xword addend = rela.get_r_addend();
+ AArch64_address aligned_tcb_size =
+ align_address(target->tcb_size(), tls_segment->maximum_alignment());
+ AArch64_address x = value + aligned_tcb_size;
+
+ // After new insns are written, apply TLSLE relocs.
+ const AArch64_reloc_property* rp1 =
+ aarch64_reloc_property_table->get_reloc_property(
+ elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12);
+ const AArch64_reloc_property* rp2 =
+ aarch64_reloc_property_table->get_reloc_property(
+ elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12);
+ gold_assert(rp1 != NULL && rp2 != NULL);
+
+ typename aarch64_reloc_funcs::Status s1 =
+ aarch64_reloc_funcs::template rela_general<32>(view + 4,
+ x,
+ addend,
+ rp1);
+ if (s1 != aarch64_reloc_funcs::STATUS_OKAY)
+ return s1;
+
+ typename aarch64_reloc_funcs::Status s2 =
+ aarch64_reloc_funcs::template rela_general<32>(view + 8,
+ x,
+ addend,
+ rp2);
+
+ this->skip_call_tls_get_addr_ = true;
+ return s2;
+
+} // End of tls_ld_to_le
+
+template<int size, bool big_endian>
+inline
+typename AArch64_relocate_functions<size, big_endian>::Status
Target_aarch64<size, big_endian>::Relocate::tls_ie_to_le(
const Relocate_info<size, big_endian>* relinfo,
Target_aarch64<size, big_endian>* target,
@@ -5963,7 +6407,7 @@ Target_aarch64<size, big_endian>::Relocate::tls_ie_to_le(
newinsn = (0xf2800000 | regno) | ((x & 0xffff) << 5);
}
else
- gold_assert(false);
+ gold_unreachable();
elfcpp::Swap<32, big_endian>::writeval(ip, newinsn);
return aarch64_reloc_funcs::STATUS_OKAY;