+template<bool big_endian>
+void
+Output_data_plt_arm_standard<big_endian>::do_fill_first_plt_entry(
+ unsigned char* pov,
+ Arm_address got_address,
+ Arm_address plt_address)
+{
+ // Write first PLT entry. All but the last word are constants.
+ const size_t num_first_plt_words = (sizeof(first_plt_entry)
+ / sizeof(first_plt_entry[0]));
+ for (size_t i = 0; i < num_first_plt_words - 1; i++)
+ {
+ if (parameters->options().be8())
+ {
+ elfcpp::Swap<32, false>::writeval(pov + i * 4,
+ first_plt_entry[i]);
+ }
+ else
+ {
+ elfcpp::Swap<32, big_endian>::writeval(pov + i * 4,
+ first_plt_entry[i]);
+ }
+ }
+ // Last word in first PLT entry is &GOT[0] - .
+ elfcpp::Swap<32, big_endian>::writeval(pov + 16,
+ got_address - (plt_address + 16));
+}
+
+// Subsequent entries in the PLT.
+// This class generates short (12-byte) entries, for displacements up to 2^28.
+
+template<bool big_endian>
+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
+ 0xe5bcf000, // ldr pc, [ip, #0xNNN]!
+};
+
+template<bool big_endian>
+void
+Output_data_plt_arm_short<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));
+ if (offset < 0 || offset > 0x0fffffff)
+ gold_error(_("PLT offset too large, try linking with --long-plt"));
+
+ uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff);
+ uint32_t plt_insn1 = plt_entry[1] | ((offset >> 12) & 0xff);
+ uint32_t plt_insn2 = plt_entry[2] | (offset & 0xfff);
+
+ if (parameters->options().be8())
+ {
+ elfcpp::Swap<32, false>::writeval(pov, plt_insn0);
+ elfcpp::Swap<32, false>::writeval(pov + 4, plt_insn1);
+ elfcpp::Swap<32, false>::writeval(pov + 8, plt_insn2);
+ }
+ else
+ {
+ elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
+ elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1);
+ 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);
+ uint32_t plt_insn1 = plt_entry[1] | ((offset >> 20) & 0xff);
+ uint32_t plt_insn2 = plt_entry[2] | ((offset >> 12) & 0xff);
+ uint32_t plt_insn3 = plt_entry[3] | (offset & 0xfff);
+
+ if (parameters->options().be8())
+ {
+ elfcpp::Swap<32, false>::writeval(pov, plt_insn0);
+ elfcpp::Swap<32, false>::writeval(pov + 4, plt_insn1);
+ elfcpp::Swap<32, false>::writeval(pov + 8, plt_insn2);
+ elfcpp::Swap<32, false>::writeval(pov + 12, plt_insn3);
+ }
+ else
+ {
+ elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
+ elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1);
+ elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2);
+ 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.
+
+template<bool big_endian>
+void
+Output_data_plt_arm<big_endian>::do_write(Output_file* of)
+{
+ const off_t offset = this->offset();
+ const section_size_type oview_size =
+ convert_to_section_size_type(this->data_size());
+ 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()
+ + this->got_irelative_->data_size());
+ unsigned char* const got_view = of->get_output_view(got_file_offset,
+ got_size);
+ unsigned char* pov = oview;
+
+ Arm_address plt_address = this->address();
+ Arm_address got_address = this->got_plt_->address();
+
+ // Write first PLT entry.
+ this->fill_first_plt_entry(pov, got_address, plt_address);
+ pov += this->first_plt_entry_offset();
+
+ unsigned char* got_pov = got_view;
+
+ memset(got_pov, 0, 12);
+ got_pov += 12;
+
+ unsigned int plt_offset = this->first_plt_entry_offset();
+ unsigned int got_offset = 12;
+ 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,
+ pov += this->get_plt_entry_size(),
+ got_pov += 4,
+ plt_offset += this->get_plt_entry_size(),
+ got_offset += 4)
+ {
+ // Set and adjust the PLT entry itself.
+ this->fill_plt_entry(pov, got_address, plt_address,
+ got_offset, plt_offset);
+
+ 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);
+ gold_assert(static_cast<section_size_type>(got_pov - got_view) == got_size);
+
+ of->write_output_view(offset, oview_size, oview);
+ of->write_output_view(got_file_offset, got_size, got_view);
+}
+
+
+// Create a PLT entry for a global symbol.
+
+template<bool big_endian>
+void
+Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout,
+ Symbol* gsym)
+{
+ 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 section first.
+ this->got_section(symtab, layout);
+
+ // 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);
+ }
+}
+
+
+// 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>
+unsigned int
+Target_arm<big_endian>::plt_entry_count() const
+{
+ if (this->plt_ == NULL)
+ return 0;
+ return this->plt_->entry_count();
+}
+
+// Return the offset of the first non-reserved PLT entry.
+
+template<bool big_endian>
+unsigned int
+Target_arm<big_endian>::first_plt_entry_offset() const
+{
+ return this->plt_->first_plt_entry_offset();
+}
+
+// Return the size of each PLT entry.
+
+template<bool big_endian>
+unsigned int
+Target_arm<big_endian>::plt_entry_size() const
+{
+ return this->plt_->get_plt_entry_size();
+}
+
+// Get the section to use for TLS_DESC relocations.