// arm.cc -- arm target support for gold.
-// Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+// Copyright (C) 2009-2016 Free Software Foundation, Inc.
// Written by Doug Kwan <dougkwan@google.com> based on the i386 code
// by Ian Lance Taylor <iant@google.com>.
// This file also contains borrowed and adapted code from
class Output_data_plt_arm;
template<bool big_endian>
-class Output_data_plt_arm_standard;
+class Output_data_plt_arm_short;
+
+template<bool big_endian>
+class Output_data_plt_arm_long;
template<bool big_endian>
class Stub_table;
// Name of key. This is mainly for debugging.
std::string
- name() const;
+ name() const ATTRIBUTE_UNUSED;
private:
// Stub type.
// bits. The default handling of relocatable relocation cannot process these
// relocations. So we have to extend the default code.
-template<bool big_endian, int sh_type, typename Classify_reloc>
+template<bool big_endian, typename Classify_reloc>
class Arm_scan_relocatable_relocs :
- public Default_scan_relocatable_relocs<sh_type, Classify_reloc>
+ public Default_scan_relocatable_relocs<Classify_reloc>
{
public:
// Return the strategy to use for a local symbol which is a section
inline Relocatable_relocs::Reloc_strategy
local_section_strategy(unsigned int r_type, Relobj*)
{
- if (sh_type == elfcpp::SHT_RELA)
+ if (Classify_reloc::sh_type == elfcpp::SHT_RELA)
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA;
else
{
Target_arm(const Target::Target_info* info = &arm_info)
: Sized_target<32, big_endian>(info),
- got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
- copy_relocs_(elfcpp::R_ARM_COPY), dynbss_(NULL),
+ got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
+ rel_dyn_(NULL), rel_irelative_(NULL), copy_relocs_(elfcpp::R_ARM_COPY),
got_mod_index_offset_(-1U), tls_base_symbol_defined_(false),
stub_tables_(), stub_factory_(Stub_factory::get_instance()),
should_force_pic_veneer_(false),
uint64_t
do_dynsym_value(const Symbol*) const;
+ // Return the plt address for globals. Since we have irelative plt entries,
+ // address calculation is not as straightforward as plt_address + plt_offset.
+ uint64_t
+ do_plt_address_for_global(const Symbol* gsym) const
+ { return this->plt_section()->address_for_global(gsym); }
+
+ // Return the plt address for locals. Since we have irelative plt entries,
+ // address calculation is not as straightforward as plt_address + plt_offset.
+ uint64_t
+ do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
+ { return this->plt_section()->address_for_local(relobj, symndx); }
+
// Relocate a section.
void
relocate_section(const Relocate_info<32, big_endian>*,
const unsigned char* plocal_symbols,
Relocatable_relocs*);
- // Relocate a section during a relocatable link.
+ // Scan the relocs for --emit-relocs.
+ void
+ emit_relocs_scan(Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj_file<32, big_endian>* object,
+ unsigned int data_shndx,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
+ size_t local_symbol_count,
+ const unsigned char* plocal_syms,
+ Relocatable_relocs* rr);
+
+ // Emit relocations for a section.
void
- relocate_for_relocatable(const Relocate_info<32, big_endian>*,
- unsigned int sh_type,
- const unsigned char* prelocs,
- size_t reloc_count,
- Output_section* output_section,
- off_t offset_in_output_section,
- const Relocatable_relocs*,
- unsigned char* view,
- Arm_address view_address,
- section_size_type view_size,
- unsigned char* reloc_view,
- section_size_type reloc_view_size);
+ relocate_relocs(const Relocate_info<32, big_endian>*,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ typename elfcpp::Elf_types<32>::Elf_Off
+ offset_in_output_section,
+ unsigned char* view,
+ Arm_address view_address,
+ section_size_type view_size,
+ unsigned char* reloc_view,
+ section_size_type reloc_view_size);
// Perform target-specific processing in a relocatable link. This is
// only used if we use the relocation strategy RELOC_SPECIAL.
const unsigned char* preloc_in,
size_t relnum,
Output_section* output_section,
- off_t offset_in_output_section,
+ typename elfcpp::Elf_types<32>::Elf_Off
+ offset_in_output_section,
unsigned char* view,
typename elfcpp::Elf_types<32>::Elf_Addr
view_address,
unsigned int
plt_entry_size() const;
+ // Get the section to use for IRELATIVE relocations, create it if necessary.
+ Reloc_section*
+ rel_irelative_section(Layout*);
+
// Map platform-specific reloc types
static unsigned int
get_real_reloc_type(unsigned int r_type);
protected:
// Make the PLT-generator object.
Output_data_plt_arm<big_endian>*
- make_data_plt(Layout* layout, Output_data_space* got_plt)
- { return this->do_make_data_plt(layout, got_plt); }
+ make_data_plt(Layout* layout,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
+ { return this->do_make_data_plt(layout, got, got_plt, got_irelative); }
// Make an ELF object.
Object*
{ return new Arm_output_section<big_endian>(name, type, flags); }
void
- do_adjust_elf_header(unsigned char* view, int len) const;
+ do_adjust_elf_header(unsigned char* view, int len);
// We only need to generate stubs, and hence perform relaxation if we are
// not doing relocatable linking.
do_define_standard_symbols(Symbol_table*, Layout*);
virtual Output_data_plt_arm<big_endian>*
- do_make_data_plt(Layout* layout, Output_data_space* got_plt)
+ do_make_data_plt(Layout* layout,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
{
- return new Output_data_plt_arm_standard<big_endian>(layout, got_plt);
+ gold_assert(got_plt != NULL && got_irelative != NULL);
+ if (parameters->options().long_plt())
+ return new Output_data_plt_arm_long<big_endian>(
+ layout, got, got_plt, got_irelative);
+ else
+ return new Output_data_plt_arm_short<big_endian>(
+ layout, got, got_plt, got_irelative);
}
private:
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type,
- const elfcpp::Sym<32, big_endian>& lsym);
+ const elfcpp::Sym<32, big_endian>& lsym,
+ bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_arm* target,
if (sym->is_undefined() && !parameters->options().shared())
return false;
+ if (sym->type() == elfcpp::STT_GNU_IFUNC)
+ return true;
+
return (!parameters->doing_static_link()
&& (sym->type() == elfcpp::STT_FUNC
|| sym->type() == elfcpp::STT_ARM_TFUNC)
inline bool
possible_function_pointer_reloc(unsigned int r_type);
+ // Whether a plt entry is needed for ifunc.
+ bool
+ reloc_needs_plt_for_ifunc(Sized_relobj_file<32, big_endian>*,
+ unsigned int r_type);
+
// Whether we have issued an error about a non-PIC compilation.
bool issued_non_pic_error_;
};
// Do a relocation. Return false if the caller should not issue
// any warnings about this relocation.
inline bool
- relocate(const Relocate_info<32, big_endian>*, Target_arm*,
- Output_section*, size_t relnum,
- const elfcpp::Rel<32, big_endian>&,
- unsigned int r_type, const Sized_symbol<32>*,
- const Symbol_value<32>*,
- unsigned char*, Arm_address,
- section_size_type);
+ relocate(const Relocate_info<32, big_endian>*, unsigned int,
+ Target_arm*, Output_section*, size_t, const unsigned char*,
+ const Sized_symbol<32>*, const Symbol_value<32>*,
+ unsigned char*, Arm_address, section_size_type);
// Return whether we want to pass flag NON_PIC_REF for this
// reloc. This means the relocation type accesses a symbol not via
};
- // A class which returns the size required for a relocation type,
- // used while scanning relocs during a relocatable link.
- class Relocatable_size_for_reloc
+ // A class for inquiring about properties of a relocation,
+ // used while scanning relocs during a relocatable link and
+ // garbage collection.
+ class Classify_reloc :
+ public gold::Default_classify_reloc<elfcpp::SHT_REL, 32, big_endian>
{
public:
- unsigned int
+ typedef typename Reloc_types<elfcpp::SHT_REL, 32, big_endian>::Reloc
+ Reltype;
+
+ // Return the explicit addend of the relocation (return 0 for SHT_REL).
+ static typename elfcpp::Elf_types<32>::Elf_Swxword
+ get_r_addend(const Reltype*)
+ { return 0; }
+
+ // Return the size of the addend of the relocation (only used for SHT_REL).
+ static unsigned int
get_size_for_reloc(unsigned int, Relobj*);
};
return this->got_plt_;
}
+ // Create the PLT section.
+ void
+ make_plt_section(Symbol_table* symtab, Layout* layout);
+
// Create a PLT entry for a global symbol.
void
make_plt_entry(Symbol_table*, Layout*, Symbol*);
+ // Create a PLT entry for a local STT_GNU_IFUNC symbol.
+ void
+ make_local_ifunc_plt_entry(Symbol_table*, Layout*,
+ Sized_relobj_file<32, big_endian>* relobj,
+ unsigned int local_sym_index);
+
// Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
void
define_tls_base_symbol(Symbol_table*, Layout*);
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rel<32, big_endian>& reloc)
{
+ unsigned int r_type = elfcpp::elf_r_type<32>(reloc.get_r_info());
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<32>(sym),
- object, shndx, output_section, reloc,
+ object, shndx, output_section,
+ r_type, reloc.get_r_offset(), 0,
this->rel_dyn_section(layout));
}
static std::string
tag_cpu_name_value(unsigned int);
+ // Query attributes object to see if integer divide instructions may be
+ // present in an object.
+ static bool
+ attributes_accept_div(int arch, int profile,
+ const Object_attribute* div_attr);
+
+ // Query attributes object to see if integer divide instructions are
+ // forbidden to be in the object. This is not the inverse of
+ // attributes_accept_div.
+ static bool
+ attributes_forbid_div(const Object_attribute* div_attr);
+
// Merge object attributes from input object and those in the output.
void
merge_object_attributes(const char*, const Attributes_section_data*);
Output_data_plt_arm<big_endian>* plt_;
// The GOT PLT section.
Output_data_space* got_plt_;
+ // The GOT section for IRELATIVE relocations.
+ Output_data_space* got_irelative_;
// The dynamic reloc section.
Reloc_section* rel_dyn_;
+ // The section to use for IRELATIVE relocs.
+ Reloc_section* rel_irelative_;
// Relocs saved to avoid a COPY reloc.
Copy_relocs<elfcpp::SHT_REL, 32, big_endian> copy_relocs_;
- // Space for variables copied with a COPY reloc.
- Output_data_space* dynbss_;
// Offset of the GOT entry for the TLS module index.
unsigned int got_mod_index_offset_;
// True if the _TLS_MODULE_BASE_ symbol has been defined.
0, // small_common_section_flags
0, // large_common_section_flags
".ARM.attributes", // attributes_section
- "aeabi" // attributes_vendor
+ "aeabi", // attributes_vendor
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
// Arm relocate functions class
const Symbol_value<32>* psymval)
{
typedef typename elfcpp::Swap_unaligned<16, big_endian>::Valtype Valtype;
- typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
Valtype val = elfcpp::Swap_unaligned<16, big_endian>::readval(view);
int32_t addend = Bits<16>::sign_extend32(val);
Arm_address x = psymval->value(object, addend);
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
false, false);
+
+ // If there are any IRELATIVE relocations, they get GOT entries
+ // in .got.plt after the jump slot entries.
+ this->got_irelative_ = new Output_data_space(4, "** GOT IRELATIVE PLT");
+ layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+ (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
+ this->got_irelative_,
+ got_order, is_got_relro);
+
}
return this->got_;
}
if (this->rel_dyn_ == NULL)
{
gold_assert(layout != NULL);
+ // Create both relocation sections in the same place, so as to ensure
+ // their relative order in the output section.
this->rel_dyn_ = new Reloc_section(parameters->options().combreloc());
+ this->rel_irelative_ = new Reloc_section(false);
layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
elfcpp::SHF_ALLOC, this->rel_dyn_,
ORDER_DYNAMIC_RELOCS, false);
+ layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
+ elfcpp::SHF_ALLOC, this->rel_irelative_,
+ ORDER_DYNAMIC_RELOCS, false);
}
return this->rel_dyn_;
}
+
+// Get the section to use for IRELATIVE relocs, creating it if necessary. These
+// go in .rela.dyn, but only after all other dynamic relocations. They need to
+// follow the other dynamic relocations so that they can refer to global
+// variables initialized by those relocs.
+
+template<bool big_endian>
+typename Target_arm<big_endian>::Reloc_section*
+Target_arm<big_endian>::rel_irelative_section(Layout* layout)
+{
+ if (this->rel_irelative_ == NULL)
+ {
+ // Delegate the creation to rel_dyn_section so as to ensure their order in
+ // the output section.
+ this->rel_dyn_section(layout);
+ gold_assert(this->rel_irelative_ != NULL
+ && (this->rel_dyn_->output_section()
+ == this->rel_irelative_->output_section()));
+ }
+ return this->rel_irelative_;
+}
+
+
// Insn_template methods.
// Return byte size of an instruction template.
// This is a bit ugly but we want to avoid using a templated class for
// big and little endianities.
bool may_use_blx;
- bool should_force_pic_veneer;
+ bool should_force_pic_veneer = parameters->options().pic_veneer();
bool thumb2;
bool thumb_only;
if (parameters->target().is_big_endian())
const Target_arm<true>* big_endian_target =
Target_arm<true>::default_target();
may_use_blx = big_endian_target->may_use_v5t_interworking();
- should_force_pic_veneer = big_endian_target->should_force_pic_veneer();
+ should_force_pic_veneer |= big_endian_target->should_force_pic_veneer();
thumb2 = big_endian_target->using_thumb2();
thumb_only = big_endian_target->using_thumb_only();
}
const Target_arm<false>* little_endian_target =
Target_arm<false>::default_target();
may_use_blx = little_endian_target->may_use_v5t_interworking();
- should_force_pic_veneer = little_endian_target->should_force_pic_veneer();
+ should_force_pic_veneer |=
+ little_endian_target->should_force_pic_veneer();
thumb2 = little_endian_target->using_thumb2();
thumb_only = little_endian_target->using_thumb_only();
}
const section_size_type oview_size = 8;
unsigned char* const oview = of->get_output_view(offset, oview_size);
- typedef typename elfcpp::Swap_unaligned<32, big_endian>::Valtype Valtype;
-
Output_section* os = this->relobj_->output_section(this->shndx_);
gold_assert(os != NULL);
Target_arm<big_endian>* target,
const Task* task)
{
- // We only care about sections containing code.
- if ((this->flags() & elfcpp::SHF_EXECINSTR) == 0)
- return;
-
// States for grouping.
typedef enum
{
this->mapping_symbols_info_.lower_bound(section_start);
// There are no mapping symbols for this section. Treat it as a data-only
- // section. Issue a warning if section is marked as containing
- // instructions.
+ // section.
if (p == this->mapping_symbols_info_.end() || p->first.first != shndx)
- {
- if ((this->section_flags(shndx) & elfcpp::SHF_EXECINSTR) != 0)
- gold_warning(_("cannot scan executable section %u of %s for Cortex-A8 "
- "erratum because it has no mapping symbols."),
- shndx, this->name().c_str());
- return;
- }
+ return;
Arm_address output_address =
this->simple_input_section_output_address(shndx, os);
Arm_relobj<big_endian>::do_read_symbols(Read_symbols_data* sd)
{
// Call parent class to read symbol information.
- Sized_relobj_file<32, big_endian>::do_read_symbols(sd);
+ this->base_read_symbols(sd);
// If this input file is a binary file, it has no processor
// specific flags and attributes section.
Arm_dynobj<big_endian>::do_read_symbols(Read_symbols_data* sd)
{
// Call parent class to read symbol information.
- Sized_dynobj<32, big_endian>::do_read_symbols(sd);
+ this->base_read_symbols(sd);
// Read processor-specific flags in ELF file header.
const unsigned char* pehdr = this->get_view(elfcpp::file_header_offset,
class Output_data_plt_arm : public Output_section_data
{
public:
+ // Unlike aarch64, which records symbol value in "addend" field of relocations
+ // and could be done at the same time an IRelative reloc is created for the
+ // symbol, arm puts the symbol value into "GOT" table, which, however, is
+ // issued later in Output_data_plt_arm::do_write(). So we have a struct here
+ // to keep necessary symbol information for later use in do_write. We usually
+ // have only a very limited number of ifuncs, so the extra data required here
+ // is also limited.
+
+ struct IRelative_data
+ {
+ IRelative_data(Sized_symbol<32>* sized_symbol)
+ : symbol_is_global_(true)
+ {
+ u_.global = sized_symbol;
+ }
+
+ IRelative_data(Sized_relobj_file<32, big_endian>* relobj,
+ unsigned int index)
+ : symbol_is_global_(false)
+ {
+ u_.local.relobj = relobj;
+ u_.local.index = index;
+ }
+
+ union
+ {
+ Sized_symbol<32>* global;
+
+ struct
+ {
+ Sized_relobj_file<32, big_endian>* relobj;
+ unsigned int index;
+ } local;
+ } u_;
+
+ bool symbol_is_global_;
+ };
+
typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
Reloc_section;
- Output_data_plt_arm(Layout*, uint64_t addralign, Output_data_space*);
+ Output_data_plt_arm(Layout* layout, uint64_t addralign,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative);
// Add an entry to the PLT.
void
- add_entry(Symbol* gsym);
+ add_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym);
+
+ // Add the relocation for a plt entry.
+ void
+ add_relocation(Symbol_table* symtab, Layout* layout,
+ Symbol* gsym, unsigned int got_offset);
+
+ // 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<32, big_endian>* relobj,
+ unsigned int local_sym_index);
// Return the .rel.plt section data.
const Reloc_section*
rel_plt() const
{ return this->rel_; }
+ // Return the PLT relocation container for IRELATIVE.
+ Reloc_section*
+ rel_irelative(Symbol_table*, Layout*);
+
// Return the number of PLT entries.
unsigned int
entry_count() const
- { return this->count_; }
+ { return this->count_ + this->irelative_count_; }
// Return the offset of the first non-reserved PLT entry.
unsigned int
get_plt_entry_size() const
{ return this->do_get_plt_entry_size(); }
+ // Return the PLT address for globals.
+ uint32_t
+ address_for_global(const Symbol*) const;
+
+ // Return the PLT address for locals.
+ uint32_t
+ address_for_local(const Relobj*, unsigned int symndx) const;
+
protected:
// Fill in the first PLT entry.
void
set_final_data_size()
{
this->set_data_size(this->first_plt_entry_offset()
- + this->count_ * this->get_plt_entry_size());
+ + ((this->count_ + this->irelative_count_)
+ * this->get_plt_entry_size()));
}
// Write out the PLT data.
void
do_write(Output_file*);
+ // Record irelative symbol data.
+ void insert_irelative_data(const IRelative_data& idata)
+ { irelative_data_vec_.push_back(idata); }
+
// The reloc section.
Reloc_section* rel_;
+ // The IRELATIVE relocs, if necessary. These must follow the
+ // regular PLT relocations.
+ Reloc_section* irelative_rel_;
+ // The .got section.
+ Arm_output_data_got<big_endian>* got_;
// The .got.plt section.
Output_data_space* got_plt_;
+ // The part of the .got.plt section used for IRELATIVE relocs.
+ Output_data_space* got_irelative_;
// The number of PLT entries.
unsigned int count_;
+ // Number of PLT entries with R_ARM_IRELATIVE relocs. These
+ // follow the regular PLT entries.
+ unsigned int irelative_count_;
+ // Vector for irelative data.
+ typedef std::vector<IRelative_data> IRelative_data_vec;
+ IRelative_data_vec irelative_data_vec_;
};
// Create the PLT section. The ordinary .got section is an argument,
// section just for PLT entries.
template<bool big_endian>
-Output_data_plt_arm<big_endian>::Output_data_plt_arm(Layout* layout,
- uint64_t addralign,
- Output_data_space* got_plt)
- : Output_section_data(addralign), got_plt_(got_plt), count_(0)
+Output_data_plt_arm<big_endian>::Output_data_plt_arm(
+ Layout* layout, uint64_t addralign,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
+ : Output_section_data(addralign), irelative_rel_(NULL),
+ got_(got), got_plt_(got_plt), got_irelative_(got_irelative),
+ count_(0), irelative_count_(0)
{
this->rel_ = new Reloc_section(false);
layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
template<bool big_endian>
void
-Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym)
+Output_data_plt_arm<big_endian>::add_entry(Symbol_table* symtab,
+ Layout* layout,
+ Symbol* gsym)
{
gold_assert(!gsym->has_plt_offset());
- // Note that when setting the PLT offset we skip the initial
- // reserved PLT entry.
- gsym->set_plt_offset((this->count_) * this->get_plt_entry_size()
- + this->first_plt_entry_offset());
+ unsigned int* entry_count;
+ Output_section_data_build* got;
+
+ // We have 2 different types of plt entry here, normal and ifunc.
+
+ // For normal plt, the offset begins with first_plt_entry_offset(20), and the
+ // 1st entry offset would be 20, the second 32, third 44 ... etc.
+
+ // For ifunc plt, the offset begins with 0. So the first offset would 0,
+ // second 12, third 24 ... etc.
+
+ // IFunc plt entries *always* come after *normal* plt entries.
+
+ // Notice, when computing the plt address of a certain symbol, "plt_address +
+ // plt_offset" is no longer correct. Use target->plt_address_for_global() or
+ // target->plt_address_for_local() instead.
+
+ int begin_offset = 0;
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false))
+ {
+ entry_count = &this->irelative_count_;
+ got = this->got_irelative_;
+ // For irelative plt entries, offset is relative to the end of normal plt
+ // entries, so it starts from 0.
+ begin_offset = 0;
+ // Record symbol information.
+ this->insert_irelative_data(
+ IRelative_data(symtab->get_sized_symbol<32>(gsym)));
+ }
+ else
+ {
+ entry_count = &this->count_;
+ got = this->got_plt_;
+ // Note that for normal plt entries, when setting the PLT offset we skip
+ // the initial reserved PLT entry.
+ begin_offset = this->first_plt_entry_offset();
+ }
+
+ gsym->set_plt_offset(begin_offset
+ + (*entry_count) * this->get_plt_entry_size());
- ++this->count_;
+ ++(*entry_count);
- section_offset_type got_offset = this->got_plt_->current_data_size();
+ 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 + 4);
+ got->set_current_data_size(got_offset + 4);
// Every PLT entry needs a reloc.
- gsym->set_needs_dynsym_entry();
- this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_,
- got_offset);
+ 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<bool big_endian>
+unsigned int
+Output_data_plt_arm<big_endian>::add_local_ifunc_entry(
+ Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj_file<32, big_endian>* relobj,
+ unsigned int local_sym_index)
+{
+ this->insert_irelative_data(IRelative_data(relobj, local_sym_index));
+
+ // Notice, when computingthe plt entry address, "plt_address + plt_offset" is
+ // no longer correct. Use target->plt_address_for_local() instead.
+ 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 + 4);
+
+
+ // Every PLT entry needs a reloc.
+ Reloc_section* rel = this->rel_irelative(symtab, layout);
+ rel->add_symbolless_local_addend(relobj, local_sym_index,
+ elfcpp::R_ARM_IRELATIVE,
+ this->got_irelative_, got_offset);
+ return plt_offset;
+}
+
+
+// Add the relocation for a PLT entry.
+
+template<bool big_endian>
+void
+Output_data_plt_arm<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* rel = this->rel_irelative(symtab, layout);
+ rel->add_symbolless_global_addend(gsym, elfcpp::R_ARM_IRELATIVE,
+ this->got_irelative_, got_offset);
+ }
+ else
+ {
+ gsym->set_needs_dynsym_entry();
+ this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_,
+ got_offset);
+ }
+}
+
+
+// Create the irelative relocation data.
+
+template<bool big_endian>
+typename Output_data_plt_arm<big_endian>::Reloc_section*
+Output_data_plt_arm<big_endian>::rel_irelative(Symbol_table* symtab,
+ Layout* layout)
+{
+ if (this->irelative_rel_ == NULL)
+ {
+ // Since irelative relocations goes into 'rel.dyn', we delegate the
+ // creation of irelative_rel_ to where rel_dyn section gets created.
+ Target_arm<big_endian>* arm_target =
+ Target_arm<big_endian>::default_target();
+ this->irelative_rel_ = arm_target->rel_irelative_section(layout);
+
+ // Make sure we have a place for the TLSDESC relocations, in
+ // case we see any later on.
+ // this->rel_tlsdesc(layout);
+ if (parameters->doing_static_link())
+ {
+ // A statically linked executable will only have a .rel.plt section to
+ // hold R_ARM_IRELATIVE relocs for STT_GNU_IFUNC symbols. The library
+ // will use these symbols to locate the IRELATIVE relocs at program
+ // startup time.
+ symtab->define_in_output_data("__rel_iplt_start", NULL,
+ Symbol_table::PREDEFINED,
+ this->irelative_rel_, 0, 0,
+ elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+ elfcpp::STV_HIDDEN, 0, false, true);
+ symtab->define_in_output_data("__rel_iplt_end", NULL,
+ Symbol_table::PREDEFINED,
+ this->irelative_rel_, 0, 0,
+ elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+ elfcpp::STV_HIDDEN, 0, true, true);
+ }
+ }
+ return this->irelative_rel_;
+}
+
+
+// Return the PLT address for a global symbol.
+
+template<bool big_endian>
+uint32_t
+Output_data_plt_arm<big_endian>::address_for_global(const Symbol* gsym) const
+{
+ uint64_t begin_offset = 0;
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false))
+ {
+ begin_offset = (this->first_plt_entry_offset() +
+ this->count_ * this->get_plt_entry_size());
+ }
+ return this->address() + begin_offset + gsym->plt_offset();
+}
+
+
+// Return the PLT address for a local symbol. These are always
+// IRELATIVE relocs.
+
+template<bool big_endian>
+uint32_t
+Output_data_plt_arm<big_endian>::address_for_local(
+ const Relobj* object,
+ unsigned int r_sym) const
+{
+ return (this->address()
+ + this->first_plt_entry_offset()
+ + this->count_ * this->get_plt_entry_size()
+ + object->local_plt_offset(r_sym));
+}
+
+
template<bool big_endian>
class Output_data_plt_arm_standard : public Output_data_plt_arm<big_endian>
{
public:
- Output_data_plt_arm_standard(Layout* layout, Output_data_space* got_plt)
- : Output_data_plt_arm<big_endian>(layout, 4, got_plt)
+ Output_data_plt_arm_standard(Layout* layout,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
+ : Output_data_plt_arm<big_endian>(layout, 4, got, got_plt, got_irelative)
{ }
protected:
do_first_plt_entry_offset() const
{ return sizeof(first_plt_entry); }
- // Return the size of a PLT entry.
- virtual unsigned int
- do_get_plt_entry_size() const
- { return sizeof(plt_entry); }
-
virtual void
do_fill_first_plt_entry(unsigned char* pov,
Arm_address got_address,
Arm_address plt_address);
- virtual void
- do_fill_plt_entry(unsigned char* pov,
- Arm_address got_address,
- Arm_address plt_address,
- unsigned int got_offset,
- unsigned int plt_offset);
-
private:
// Template for the first PLT entry.
static const uint32_t first_plt_entry[5];
-
- // Template for subsequent PLT entries.
- static const uint32_t plt_entry[3];
};
// ARM PLTs.
{
// Write first PLT entry. All but the last word are constants.
const size_t num_first_plt_words = (sizeof(first_plt_entry)
- / sizeof(plt_entry[0]));
+ / sizeof(first_plt_entry[0]));
for (size_t i = 0; i < num_first_plt_words - 1; i++)
elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, first_plt_entry[i]);
// Last word in first PLT entry is &GOT[0] - .
}
// Subsequent entries in the PLT.
+// This class generates short (12-byte) entries, for displacements up to 2^28.
template<bool big_endian>
-const uint32_t Output_data_plt_arm_standard<big_endian>::plt_entry[3] =
+class Output_data_plt_arm_short : public Output_data_plt_arm_standard<big_endian>
+{
+ public:
+ Output_data_plt_arm_short(Layout* layout,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
+ : Output_data_plt_arm_standard<big_endian>(layout, got, got_plt, got_irelative)
+ { }
+
+ protected:
+ // Return the size of a PLT entry.
+ virtual unsigned int
+ do_get_plt_entry_size() const
+ { return sizeof(plt_entry); }
+
+ virtual void
+ do_fill_plt_entry(unsigned char* pov,
+ Arm_address got_address,
+ Arm_address plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset);
+
+ private:
+ // Template for subsequent PLT entries.
+ static const uint32_t plt_entry[3];
+};
+
+template<bool big_endian>
+const uint32_t Output_data_plt_arm_short<big_endian>::plt_entry[3] =
{
0xe28fc600, // add ip, pc, #0xNN00000
0xe28cca00, // add ip, ip, #0xNN000
template<bool big_endian>
void
-Output_data_plt_arm_standard<big_endian>::do_fill_plt_entry(
+Output_data_plt_arm_short<big_endian>::do_fill_plt_entry(
unsigned char* pov,
Arm_address got_address,
Arm_address plt_address,
{
int32_t offset = ((got_address + got_offset)
- (plt_address + plt_offset + 8));
+ if (offset < 0 || offset > 0x0fffffff)
+ gold_error(_("PLT offset too large, try linking with --long-plt"));
- gold_assert(offset >= 0 && offset < 0x0fffffff);
uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff);
elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
uint32_t plt_insn1 = plt_entry[1] | ((offset >> 12) & 0xff);
elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2);
}
+// This class generates long (16-byte) entries, for arbitrary displacements.
+
+template<bool big_endian>
+class Output_data_plt_arm_long : public Output_data_plt_arm_standard<big_endian>
+{
+ public:
+ Output_data_plt_arm_long(Layout* layout,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
+ : Output_data_plt_arm_standard<big_endian>(layout, got, got_plt, got_irelative)
+ { }
+
+ protected:
+ // Return the size of a PLT entry.
+ virtual unsigned int
+ do_get_plt_entry_size() const
+ { return sizeof(plt_entry); }
+
+ virtual void
+ do_fill_plt_entry(unsigned char* pov,
+ Arm_address got_address,
+ Arm_address plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset);
+
+ private:
+ // Template for subsequent PLT entries.
+ static const uint32_t plt_entry[4];
+};
+
+template<bool big_endian>
+const uint32_t Output_data_plt_arm_long<big_endian>::plt_entry[4] =
+{
+ 0xe28fc200, // add ip, pc, #0xN0000000
+ 0xe28cc600, // add ip, ip, #0xNN00000
+ 0xe28cca00, // add ip, ip, #0xNN000
+ 0xe5bcf000, // ldr pc, [ip, #0xNNN]!
+};
+
+template<bool big_endian>
+void
+Output_data_plt_arm_long<big_endian>::do_fill_plt_entry(
+ unsigned char* pov,
+ Arm_address got_address,
+ Arm_address plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset)
+{
+ int32_t offset = ((got_address + got_offset)
+ - (plt_address + plt_offset + 8));
+
+ uint32_t plt_insn0 = plt_entry[0] | (offset >> 28);
+ elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
+ uint32_t plt_insn1 = plt_entry[1] | ((offset >> 20) & 0xff);
+ elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1);
+ uint32_t plt_insn2 = plt_entry[2] | ((offset >> 12) & 0xff);
+ elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2);
+ uint32_t plt_insn3 = plt_entry[3] | (offset & 0xfff);
+ elfcpp::Swap<32, big_endian>::writeval(pov + 12, plt_insn3);
+}
+
// Write out the PLT. This uses the hand-coded instructions above,
// and adjusts them as needed. This is all specified by the arm ELF
// Processor Supplement.
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);
unsigned char* pov = oview;
unsigned int plt_offset = this->first_plt_entry_offset();
unsigned int got_offset = 12;
- const unsigned int count = this->count_;
+ const unsigned int count = this->count_ + this->irelative_count_;
+ gold_assert(this->irelative_count_ == this->irelative_data_vec_.size());
for (unsigned int i = 0;
i < count;
++i,
this->fill_plt_entry(pov, got_address, plt_address,
got_offset, plt_offset);
- // Set the entry in the GOT.
- elfcpp::Swap<32, big_endian>::writeval(got_pov, plt_address);
+ Arm_address value;
+ if (i < this->count_)
+ {
+ // For non-irelative got entries, the value is the beginning of plt.
+ value = plt_address;
+ }
+ else
+ {
+ // For irelative got entries, the value is the (global/local) symbol
+ // address.
+ const IRelative_data& idata =
+ this->irelative_data_vec_[i - this->count_];
+ if (idata.symbol_is_global_)
+ {
+ // Set the entry in the GOT for irelative symbols. The content is
+ // the address of the ifunc, not the address of plt start.
+ const Sized_symbol<32>* sized_symbol = idata.u_.global;
+ gold_assert(sized_symbol->type() == elfcpp::STT_GNU_IFUNC);
+ value = sized_symbol->value();
+ }
+ else
+ {
+ value = idata.u_.local.relobj->local_symbol_value(
+ idata.u_.local.index, 0);
+ }
+ }
+ elfcpp::Swap<32, big_endian>::writeval(got_pov, value);
}
gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
of->write_output_view(got_file_offset, got_size, got_view);
}
+
// Create a PLT entry for a global symbol.
template<bool big_endian>
if (gsym->has_plt_offset())
return;
+ if (this->plt_ == NULL)
+ this->make_plt_section(symtab, layout);
+
+ this->plt_->add_entry(symtab, layout, gsym);
+}
+
+
+// Create the PLT section.
+template<bool big_endian>
+void
+Target_arm<big_endian>::make_plt_section(
+ Symbol_table* symtab, Layout* layout)
+{
if (this->plt_ == NULL)
{
- // Create the GOT sections first.
+ // Create the GOT section first.
this->got_section(symtab, layout);
- this->plt_ = this->make_data_plt(layout, this->got_plt_);
+ // GOT for irelatives is create along with got.plt.
+ gold_assert(this->got_ != NULL
+ && this->got_plt_ != NULL
+ && this->got_irelative_ != NULL);
+ this->plt_ = this->make_data_plt(layout, this->got_, this->got_plt_,
+ this->got_irelative_);
layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_EXECINSTR),
this->plt_, ORDER_PLT, false);
+ symtab->define_in_output_data("$a", NULL,
+ Symbol_table::PREDEFINED,
+ this->plt_,
+ 0, 0, elfcpp::STT_NOTYPE,
+ elfcpp::STB_LOCAL,
+ elfcpp::STV_DEFAULT, 0,
+ false, false);
}
- this->plt_->add_entry(gsym);
}
+
+// Make a PLT entry for a local STT_GNU_IFUNC symbol.
+
+template<bool big_endian>
+void
+Target_arm<big_endian>::make_local_ifunc_plt_entry(
+ Symbol_table* symtab, Layout* layout,
+ Sized_relobj_file<32, 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);
+}
+
+
// Return the number of entries in the PLT.
template<bool big_endian>
case elfcpp::R_ARM_JUMP_SLOT:
case elfcpp::R_ARM_ABS32:
case elfcpp::R_ARM_ABS32_NOI:
+ case elfcpp::R_ARM_IRELATIVE:
case elfcpp::R_ARM_PC24:
// FIXME: The following 3 types are not supported by Android's dynamic
// linker.
}
}
+
+// Return whether we need to make a PLT entry for a relocation of the
+// given type against a STT_GNU_IFUNC symbol.
+
+template<bool big_endian>
+bool
+Target_arm<big_endian>::Scan::reloc_needs_plt_for_ifunc(
+ Sized_relobj_file<32, big_endian>* object,
+ unsigned int r_type)
+{
+ int flags = Scan::get_reference_flags(r_type);
+ if (flags & Symbol::TLS_REF)
+ {
+ gold_error(_("%s: unsupported TLS reloc %u for IFUNC symbol"),
+ object->name().c_str(), r_type);
+ return false;
+ }
+ return flags != 0;
+}
+
+
// Scan a relocation for a local symbol.
// FIXME: This only handles a subset of relocation types used by Android
// on ARM v5te devices.
Output_section* output_section,
const elfcpp::Rel<32, big_endian>& reloc,
unsigned int r_type,
- const elfcpp::Sym<32, big_endian>& lsym)
+ const elfcpp::Sym<32, big_endian>& lsym,
+ bool is_discarded)
{
+ if (is_discarded)
+ return;
+
r_type = get_real_reloc_type(r_type);
+
+ // 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))
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+ target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
+ }
+
switch (r_type)
{
case elfcpp::R_ARM_NONE:
// we need to add check_non_pic(object, r_type) here.
rel_dyn->add_local_relative(object, r_sym, elfcpp::R_ARM_RELATIVE,
output_section, data_shndx,
- reloc.get_r_offset());
+ reloc.get_r_offset(), is_ifunc);
}
break;
got->add_local_pair_with_rel(object, r_sym, shndx,
GOT_TYPE_TLS_PAIR,
target->rel_dyn_section(layout),
- elfcpp::R_ARM_TLS_DTPMOD32, 0);
+ elfcpp::R_ARM_TLS_DTPMOD32);
else
got->add_tls_gd32_with_static_reloc(GOT_TYPE_TLS_PAIR,
object, r_sym);
&& strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
target->got_section(symtab, layout);
+ // 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);
+
r_type = get_real_reloc_type(r_type);
switch (r_type)
{
// Make a dynamic relocation if necessary.
if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
{
- if (gsym->may_need_copy_reloc())
+ if (!parameters->options().output_is_position_independent()
+ && gsym->may_need_copy_reloc())
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
}
+ else if ((r_type == elfcpp::R_ARM_ABS32
+ || r_type == elfcpp::R_ARM_ABS32_NOI)
+ && 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* rel_irelative =
+ target->rel_irelative_section(layout);
+ unsigned int r_type = elfcpp::R_ARM_IRELATIVE;
+ rel_irelative->add_symbolless_global_addend(
+ gsym, r_type, output_section, object,
+ data_shndx, reloc.get_r_offset());
+ }
else if ((r_type == elfcpp::R_ARM_ABS32
|| r_type == elfcpp::R_ARM_ABS32_NOI)
&& gsym->can_use_relative_reloc(false))
// Make a dynamic relocation if necessary.
if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
{
- if (target->may_need_copy_reloc(gsym))
+ if (parameters->options().output_is_executable()
+ && target->may_need_copy_reloc(gsym))
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
Arm_output_data_got<big_endian>* got =
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
|| 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,
rel_dyn, elfcpp::R_ARM_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)
rel_dyn->add_global_relative(
gsym, elfcpp::R_ARM_RELATIVE, got,
gsym->got_offset(GOT_TYPE_STANDARD));
typedef Target_arm<big_endian> Arm;
typedef typename Target_arm<big_endian>::Scan Scan;
- gold::gc_process_relocs<32, big_endian, Arm, elfcpp::SHT_REL, Scan,
- typename Target_arm::Relocatable_size_for_reloc>(
+ gold::gc_process_relocs<32, big_endian, Arm, Scan, Classify_reloc>(
symtab,
layout,
this,
size_t local_symbol_count,
const unsigned char* plocal_symbols)
{
- typedef typename Target_arm<big_endian>::Scan Scan;
if (sh_type == elfcpp::SHT_RELA)
{
gold_error(_("%s: unsupported RELA reloc section"),
return;
}
- gold::scan_relocs<32, big_endian, Target_arm, elfcpp::SHT_REL, Scan>(
+ gold::scan_relocs<32, big_endian, Target_arm, Scan, Classify_reloc>(
symtab,
layout,
this,
inline bool
Target_arm<big_endian>::Relocate::relocate(
const Relocate_info<32, big_endian>* relinfo,
+ unsigned int,
Target_arm* target,
Output_section* output_section,
size_t relnum,
- const elfcpp::Rel<32, big_endian>& rel,
- unsigned int r_type,
+ const unsigned char* preloc,
const Sized_symbol<32>* gsym,
const Symbol_value<32>* psymval,
unsigned char* view,
Arm_address address,
section_size_type view_size)
{
+ if (view == NULL)
+ return true;
+
typedef Arm_relocate_functions<big_endian> Arm_relocate_functions;
+ const elfcpp::Rel<32, big_endian> rel(preloc);
+ unsigned int r_type = elfcpp::elf_r_type<32>(rel.get_r_info());
r_type = get_real_reloc_type(r_type);
const Arm_reloc_property* reloc_property =
arm_reloc_property_table->get_implemented_static_reloc_property(r_type);
if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
{
// This uses a PLT, change the symbol value.
- symval.set_output_value(target->plt_section()->address()
- + gsym->plt_offset());
+ symval.set_output_value(target->plt_address_for_global(gsym));
psymval = &symval;
}
else if (gsym->is_weak_undefined())
elfcpp::Elf_types<32>::Elf_WXword r_info = rel.get_r_info();
unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
thumb_bit = object->local_symbol_is_thumb_function(r_sym) ? 1 : 0;
+
+ if (psymval->is_ifunc_symbol() && object->local_has_plt_offset(r_sym))
+ {
+ symval.set_output_value(
+ target->plt_address_for_local(object, r_sym));
+ psymval = &symval;
+ }
}
}
else
}
}
- gold::relocate_section<32, big_endian, Target_arm, elfcpp::SHT_REL,
- Arm_relocate>(
+ gold::relocate_section<32, big_endian, Target_arm, Arm_relocate,
+ gold::Default_comdat_behavior, Classify_reloc>(
relinfo,
this,
prelocs,
template<bool big_endian>
unsigned int
-Target_arm<big_endian>::Relocatable_size_for_reloc::get_size_for_reloc(
+Target_arm<big_endian>::Classify_reloc::get_size_for_reloc(
unsigned int r_type,
Relobj* object)
{
const unsigned char* plocal_symbols,
Relocatable_relocs* rr)
{
- gold_assert(sh_type == elfcpp::SHT_REL);
+ typedef Arm_scan_relocatable_relocs<big_endian, Classify_reloc>
+ Scan_relocatable_relocs;
- typedef Arm_scan_relocatable_relocs<big_endian, elfcpp::SHT_REL,
- Relocatable_size_for_reloc> Scan_relocatable_relocs;
+ gold_assert(sh_type == elfcpp::SHT_REL);
- gold::scan_relocatable_relocs<32, big_endian, elfcpp::SHT_REL,
- Scan_relocatable_relocs>(
+ gold::scan_relocatable_relocs<32, big_endian, Scan_relocatable_relocs>(
symtab,
layout,
object,
rr);
}
-// Relocate a section during a relocatable link.
+// Scan the relocs for --emit-relocs.
+
+template<bool big_endian>
+void
+Target_arm<big_endian>::emit_relocs_scan(Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj_file<32, big_endian>* object,
+ unsigned int data_shndx,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
+ size_t local_symbol_count,
+ const unsigned char* plocal_syms,
+ Relocatable_relocs* rr)
+{
+ typedef gold::Default_classify_reloc<elfcpp::SHT_REL, 32, big_endian>
+ Classify_reloc;
+ typedef gold::Default_emit_relocs_strategy<Classify_reloc>
+ Emit_relocs_strategy;
+
+ gold_assert(sh_type == elfcpp::SHT_REL);
+
+ gold::scan_relocatable_relocs<32, big_endian, Emit_relocs_strategy>(
+ symtab,
+ layout,
+ object,
+ data_shndx,
+ prelocs,
+ reloc_count,
+ output_section,
+ needs_special_offset_handling,
+ local_symbol_count,
+ plocal_syms,
+ rr);
+}
+
+// Emit relocations for a section.
template<bool big_endian>
void
-Target_arm<big_endian>::relocate_for_relocatable(
+Target_arm<big_endian>::relocate_relocs(
const Relocate_info<32, big_endian>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
- off_t offset_in_output_section,
- const Relocatable_relocs* rr,
+ typename elfcpp::Elf_types<32>::Elf_Off offset_in_output_section,
unsigned char* view,
Arm_address view_address,
section_size_type view_size,
{
gold_assert(sh_type == elfcpp::SHT_REL);
- gold::relocate_for_relocatable<32, big_endian, elfcpp::SHT_REL>(
+ gold::relocate_relocs<32, big_endian, Classify_reloc>(
relinfo,
prelocs,
reloc_count,
output_section,
offset_in_output_section,
- rr,
view,
view_address,
view_size,
const unsigned char* preloc_in,
size_t relnum,
Output_section* output_section,
- off_t offset_in_output_section,
+ typename elfcpp::Elf_types<32>::Elf_Off offset_in_output_section,
unsigned char* view,
elfcpp::Elf_types<32>::Elf_Addr view_address,
section_size_type,
Target_arm<big_endian>::do_dynsym_value(const Symbol* gsym) const
{
gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
- return this->plt_section()->address() + gsym->plt_offset();
+ return this->plt_address_for_global(gsym);
}
// Map platform-specific relocs to real relocs
void
Target_arm<big_endian>::do_adjust_elf_header(
unsigned char* view,
- int len) const
+ int len)
{
gold_assert(len == elfcpp::Elf_sizes<32>::ehdr_size);
elfcpp::Ehdr<32, big_endian> ehdr(view);
+ elfcpp::Elf_Word flags = this->processor_specific_flags();
unsigned char e_ident[elfcpp::EI_NIDENT];
memcpy(e_ident, ehdr.get_e_ident(), elfcpp::EI_NIDENT);
- if (elfcpp::arm_eabi_version(this->processor_specific_flags())
+ if (elfcpp::arm_eabi_version(flags)
== elfcpp::EF_ARM_EABI_UNKNOWN)
e_ident[elfcpp::EI_OSABI] = elfcpp::ELFOSABI_ARM;
else
// FIXME: Do EF_ARM_BE8 adjustment.
+ // If we're working in EABI_VER5, set the hard/soft float ABI flags
+ // as appropriate.
+ if (elfcpp::arm_eabi_version(flags) == elfcpp::EF_ARM_EABI_VER5)
+ {
+ elfcpp::Elf_Half type = ehdr.get_e_type();
+ if (type == elfcpp::ET_EXEC || type == elfcpp::ET_DYN)
+ {
+ Object_attribute* attr = this->get_aeabi_object_attribute(elfcpp::Tag_ABI_VFP_args);
+ if (attr->int_value() == elfcpp::AEABI_VFP_args_vfp)
+ flags |= elfcpp::EF_ARM_ABI_FLOAT_HARD;
+ else
+ flags |= elfcpp::EF_ARM_ABI_FLOAT_SOFT;
+ this->set_processor_specific_flags(flags);
+ }
+ }
elfcpp::Ehdr_write<32, big_endian> oehdr(view);
oehdr.put_e_ident(e_ident);
+ oehdr.put_e_flags(this->processor_specific_flags());
}
// do_make_elf_object to override the same function in the base class.
T(V7E_M), // V6S_M.
T(V7E_M) // V7E_M.
};
+ static const int v8[] =
+ {
+ T(V8), // PRE_V4.
+ T(V8), // V4.
+ T(V8), // V4T.
+ T(V8), // V5T.
+ T(V8), // V5TE.
+ T(V8), // V5TEJ.
+ T(V8), // V6.
+ T(V8), // V6KZ.
+ T(V8), // V6T2.
+ T(V8), // V6K.
+ T(V8), // V7.
+ T(V8), // V6_M.
+ T(V8), // V6S_M.
+ T(V8), // V7E_M.
+ T(V8) // V8.
+ };
static const int v4t_plus_v6_m[] =
{
-1, // PRE_V4.
T(V6_M), // V6_M.
T(V6S_M), // V6S_M.
T(V7E_M), // V7E_M.
+ T(V8), // V8.
T(V4T_PLUS_V6_M) // V4T plus V6_M.
};
static const int* comb[] =
v6_m,
v6s_m,
v7e_m,
+ v8,
// Pseudo-architecture.
v4t_plus_v6_m
};
"ARM v7",
"ARM v6-M",
"ARM v6S-M",
- "ARM v7E-M"
+ "ARM v7E-M",
+ "ARM v8"
};
const size_t name_table_size = sizeof(name_table) / sizeof(name_table[0]);
}
}
+// Query attributes object to see if integer divide instructions may be
+// present in an object.
+
+template<bool big_endian>
+bool
+Target_arm<big_endian>::attributes_accept_div(int arch, int profile,
+ const Object_attribute* div_attr)
+{
+ switch (div_attr->int_value())
+ {
+ case 0:
+ // Integer divide allowed if instruction contained in
+ // archetecture.
+ if (arch == elfcpp::TAG_CPU_ARCH_V7 && (profile == 'R' || profile == 'M'))
+ return true;
+ else if (arch >= elfcpp::TAG_CPU_ARCH_V7E_M)
+ return true;
+ else
+ return false;
+
+ case 1:
+ // Integer divide explicitly prohibited.
+ return false;
+
+ default:
+ // Unrecognised case - treat as allowing divide everywhere.
+ case 2:
+ // Integer divide allowed in ARM state.
+ return true;
+ }
+}
+
+// Query attributes object to see if integer divide instructions are
+// forbidden to be in the object. This is not the inverse of
+// attributes_accept_div.
+
+template<bool big_endian>
+bool
+Target_arm<big_endian>::attributes_forbid_div(const Object_attribute* div_attr)
+{
+ return div_attr->int_value() == 1;
+}
+
// Merge object attributes from input file called NAME with those of the
// output. The input object attributes are in the object pointed by PASD.
!= out_attr[elfcpp::Tag_ABI_VFP_args].int_value())
{
// Ignore mismatches if the object doesn't use floating point. */
- if (out_attr[elfcpp::Tag_ABI_FP_number_model].int_value() == 0)
+ if (out_attr[elfcpp::Tag_ABI_FP_number_model].int_value()
+ == elfcpp::AEABI_FP_number_model_none
+ || (in_attr[elfcpp::Tag_ABI_FP_number_model].int_value()
+ != elfcpp::AEABI_FP_number_model_none
+ && out_attr[elfcpp::Tag_ABI_VFP_args].int_value()
+ == elfcpp::AEABI_VFP_args_compatible))
out_attr[elfcpp::Tag_ABI_VFP_args].set_int_value(
in_attr[elfcpp::Tag_ABI_VFP_args].int_value());
- else if (in_attr[elfcpp::Tag_ABI_FP_number_model].int_value() != 0
+ else if (in_attr[elfcpp::Tag_ABI_FP_number_model].int_value()
+ != elfcpp::AEABI_FP_number_model_none
+ && in_attr[elfcpp::Tag_ABI_VFP_args].int_value()
+ != elfcpp::AEABI_VFP_args_compatible
&& parameters->options().warn_mismatch())
gold_error(_("%s uses VFP register arguments, output does not"),
name);
break;
case elfcpp::Tag_DIV_use:
- // This tag is set to zero if we can use UDIV and SDIV in Thumb
- // mode on a v7-M or v7-R CPU; to one if we can not use UDIV or
- // SDIV at all; and to two if we can use UDIV or SDIV on a v7-A
- // CPU. We will merge as follows: If the input attribute's value
- // is one then the output attribute's value remains unchanged. If
- // the input attribute's value is zero or two then if the output
- // attribute's value is one the output value is set to the input
- // value, otherwise the output value must be the same as the
- // inputs. */
- if (in_attr[i].int_value() != 1 && out_attr[i].int_value() != 1)
- {
- if (in_attr[i].int_value() != out_attr[i].int_value())
- {
- gold_error(_("DIV usage mismatch between %s and output"),
- name);
- }
- }
-
- if (in_attr[i].int_value() != 1)
- out_attr[i].set_int_value(in_attr[i].int_value());
-
+ {
+ // A value of zero on input means that the divide
+ // instruction may be used if available in the base
+ // architecture as specified via Tag_CPU_arch and
+ // Tag_CPU_arch_profile. A value of 1 means that the user
+ // did not want divide instructions. A value of 2
+ // explicitly means that divide instructions were allowed
+ // in ARM and Thumb state.
+ int arch = this->
+ get_aeabi_object_attribute(elfcpp::Tag_CPU_arch)->
+ int_value();
+ int profile = this->
+ get_aeabi_object_attribute(elfcpp::Tag_CPU_arch_profile)->
+ int_value();
+ if (in_attr[i].int_value() == out_attr[i].int_value())
+ {
+ // Do nothing.
+ }
+ else if (attributes_forbid_div(&in_attr[i])
+ && !attributes_accept_div(arch, profile, &out_attr[i]))
+ out_attr[i].set_int_value(1);
+ else if (attributes_forbid_div(&out_attr[i])
+ && attributes_accept_div(arch, profile, &in_attr[i]))
+ out_attr[i].set_int_value(in_attr[i].int_value());
+ else if (in_attr[i].int_value() == 2)
+ out_attr[i].set_int_value(in_attr[i].int_value());
+ }
break;
case elfcpp::Tag_MPextension_use_legacy:
elfcpp::Elf_types<32>::Elf_Swxword addend,
Arm_address address)
{
- typedef typename Target_arm<big_endian>::Relocate Relocate;
-
const Arm_relobj<big_endian>* arm_relobj =
Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
{
// This uses a PLT, change the symbol value.
- symval.set_output_value(this->plt_section()->address()
- + gsym->plt_offset());
+ symval.set_output_value(this->plt_address_for_global(gsym));
psymval = &symval;
target_is_thumb = false;
}
Arm_relobj<big_endian>::as_arm_relobj(relinfo->object);
unsigned int local_count = arm_object->local_symbol_count();
+ gold::Default_comdat_behavior default_comdat_behavior;
Comdat_behavior comdat_behavior = CB_UNDETERMINED;
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
if (comdat_behavior == CB_UNDETERMINED)
{
std::string name = arm_object->section_name(relinfo->data_shndx);
- comdat_behavior = get_comdat_behavior(name.c_str());
+ comdat_behavior = default_comdat_behavior.get(name.c_str());
}
if (comdat_behavior == CB_PRETEND)
{
{
// Group input sections and insert stub table
Layout::Section_list section_list;
- layout->get_allocated_sections(§ion_list);
+ layout->get_executable_sections(§ion_list);
for (Layout::Section_list::const_iterator p = section_list.begin();
p != section_list.end();
++p)
elfcpp::Rel_write<32, big_endian> reloc_write(reloc_buffer);
reloc_write.put_r_offset(reloc_offset);
reloc_write.put_r_info(elfcpp::elf_r_info<32>(0, r_type));
- elfcpp::Rel<32, big_endian> rel(reloc_buffer);
- relocate.relocate(relinfo, this, output_section,
- this->fake_relnum_for_stubs, rel, r_type,
+ relocate.relocate(relinfo, elfcpp::SHT_REL, this, output_section,
+ this->fake_relnum_for_stubs, reloc_buffer,
NULL, &symval, view + reloc_offset,
address + reloc_offset, reloc_size);
}
protected:
virtual Output_data_plt_arm<big_endian>*
- do_make_data_plt(Layout* layout, Output_data_space* got_plt)
- { return new Output_data_plt_arm_nacl<big_endian>(layout, got_plt); }
+ do_make_data_plt(
+ Layout* layout,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
+ { return new Output_data_plt_arm_nacl<big_endian>(
+ layout, got, got_plt, got_irelative); }
private:
static const Target::Target_info arm_nacl_info;
0, // small_common_section_flags
0, // large_common_section_flags
".ARM.attributes", // attributes_section
- "aeabi" // attributes_vendor
+ "aeabi", // attributes_vendor
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<bool big_endian>
class Output_data_plt_arm_nacl : public Output_data_plt_arm<big_endian>
{
public:
- Output_data_plt_arm_nacl(Layout* layout, Output_data_space* got_plt)
- : Output_data_plt_arm<big_endian>(layout, 16, got_plt)
+ Output_data_plt_arm_nacl(
+ Layout* layout,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
+ : Output_data_plt_arm<big_endian>(layout, 16, got, got_plt, got_irelative)
{ }
protected:
0xe08cc00f, // add ip, ip, pc
0xe52dc008, // str ip, [sp, #-8]!
// Second bundle:
- 0xe7dfcf1f, // bfc ip, #30, #2
+ 0xe3ccc103, // bic ip, ip, #0xc0000000
0xe59cc000, // ldr ip, [ip]
0xe3ccc13f, // bic ip, ip, #0xc000000f
0xe12fff1c, // bx ip
// .Lplt_tail:
0xe50dc004, // str ip, [sp, #-4]
// Fourth bundle:
- 0xe7dfcf1f, // bfc ip, #30, #2
+ 0xe3ccc103, // bic ip, ip, #0xc0000000
0xe59cc000, // ldr ip, [ip]
0xe3ccc13f, // bic ip, ip, #0xc000000f
0xe12fff1c, // bx ip