+// A class to handle the .got.plt section.
+
+class Output_data_got_plt_x86_64 : public Output_section_data_build
+{
+ public:
+ Output_data_got_plt_x86_64(Layout* layout)
+ : Output_section_data_build(8),
+ layout_(layout)
+ { }
+
+ Output_data_got_plt_x86_64(Layout* layout, off_t data_size)
+ : Output_section_data_build(data_size, 8),
+ layout_(layout)
+ { }
+
+ protected:
+ // Write out the PLT data.
+ void
+ do_write(Output_file*);
+
+ // Write to a map file.
+ void
+ do_print_to_mapfile(Mapfile* mapfile) const
+ { mapfile->print_output_data(this, "** GOT PLT"); }
+
+ private:
+ // A pointer to the Layout class, so that we can find the .dynamic
+ // section when we write out the GOT PLT section.
+ Layout* layout_;
+};
+
+// A class to handle the PLT data.
+// This is an abstract base class that handles most of the linker details
+// but does not know the actual contents of PLT entries. The derived
+// classes below fill in those details.
+
+template<int size>
+class Output_data_plt_x86_64 : public Output_section_data
+{
+ public:
+ typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, false> Reloc_section;
+
+ Output_data_plt_x86_64(Layout* layout, uint64_t addralign,
+ Output_data_got<64, false>* got,
+ Output_data_got_plt_x86_64* got_plt,
+ Output_data_space* got_irelative)
+ : 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), free_list_()
+ { this->init(layout); }
+
+ Output_data_plt_x86_64(Layout* layout, uint64_t plt_entry_size,
+ Output_data_got<64, false>* got,
+ Output_data_got_plt_x86_64* got_plt,
+ Output_data_space* got_irelative,
+ unsigned int plt_count)
+ : Output_section_data((plt_count + 1) * plt_entry_size,
+ plt_entry_size, false),
+ tlsdesc_rel_(NULL), irelative_rel_(NULL), got_(got),
+ got_plt_(got_plt), got_irelative_(got_irelative), count_(plt_count),
+ irelative_count_(0), tlsdesc_got_offset_(-1U), free_list_()
+ {
+ this->init(layout);
+
+ // Initialize the free list and reserve the first entry.
+ this->free_list_.init((plt_count + 1) * plt_entry_size, false);
+ this->free_list_.remove(0, plt_entry_size);
+ }
+
+ // Initialize the PLT section.
+ void
+ init(Layout* layout);
+
+ // Add an entry to the PLT.
+ void
+ 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, false>* 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
+ reserve_tlsdesc_entry(unsigned int got_offset)
+ { this->tlsdesc_got_offset_ = got_offset; }
+
+ // Return true if a TLSDESC_PLT entry has been reserved.
+ bool
+ has_tlsdesc_entry() const
+ { return this->tlsdesc_got_offset_ != -1U; }
+
+ // Return the GOT offset for the reserved TLSDESC_PLT entry.
+ unsigned int
+ get_tlsdesc_got_offset() const
+ { return this->tlsdesc_got_offset_; }
+
+ // Return the offset of the reserved TLSDESC_PLT entry.
+ unsigned int
+ get_tlsdesc_plt_offset() const
+ {
+ return ((this->count_ + this->irelative_count_ + 1)
+ * this->get_plt_entry_size());
+ }
+
+ // Return the .rela.plt section data.
+ Reloc_section*
+ rela_plt()
+ { return this->rel_; }
+
+ // Return where the TLSDESC relocations should go.
+ Reloc_section*
+ rela_tlsdesc(Layout*);
+
+ // Return where the IRELATIVE relocations should go in the PLT
+ // relocations.
+ Reloc_section*
+ rela_irelative(Symbol_table*, Layout*);
+
+ // Return whether we created a section for IRELATIVE relocations.
+ bool
+ has_irelative_section() const
+ { return this->irelative_rel_ != NULL; }
+
+ // Get count of regular PLT entries.
+ unsigned int
+ regular_count() const
+ { return this->count_; }
+
+ // Return the total number of PLT entries.
+ unsigned int
+ entry_count() const
+ { return this->count_ + this->irelative_count_; }
+
+ // Return the offset of the first non-reserved PLT entry.
+ unsigned int
+ first_plt_entry_offset()
+ { return this->get_plt_entry_size(); }
+
+ // Return the size of a PLT entry.
+ unsigned int
+ get_plt_entry_size() const
+ { return this->do_get_plt_entry_size(); }
+
+ // Reserve a slot in the PLT for an existing symbol in an incremental update.
+ void
+ reserve_slot(unsigned int plt_index)
+ {
+ this->free_list_.remove((plt_index + 1) * this->get_plt_entry_size(),
+ (plt_index + 2) * this->get_plt_entry_size());
+ }
+
+ // Return the PLT address to use for a global symbol.
+ uint64_t
+ address_for_global(const Symbol* sym)
+ { return do_address_for_global(sym); }
+
+ // Return the PLT address to use for a local symbol.
+ uint64_t
+ address_for_local(const Relobj* obj, unsigned int symndx)
+ { return do_address_for_local(obj, symndx); }
+
+ // Add .eh_frame information for the PLT.
+ void
+ add_eh_frame(Layout* layout)
+ { this->do_add_eh_frame(layout); }
+
+ protected:
+ Output_data_got<64, false>*
+ got() const
+ { return this->got_; }
+
+ Output_data_got_plt_x86_64*
+ got_plt() const
+ { return this->got_plt_; }
+
+ Output_data_space*
+ got_irelative() const
+ { return this->got_irelative_; }
+
+ // Fill in the first PLT entry.
+ void
+ fill_first_plt_entry(unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_address)
+ { this->do_fill_first_plt_entry(pov, got_address, plt_address); }
+
+ // Fill in a normal PLT entry. Returns the offset into the entry that
+ // should be the initial GOT slot value.
+ unsigned int
+ fill_plt_entry(unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset,
+ unsigned int plt_index)
+ {
+ return this->do_fill_plt_entry(pov, got_address, plt_address,
+ got_offset, plt_offset, plt_index);
+ }
+
+ // Fill in the reserved TLSDESC PLT entry.
+ void
+ fill_tlsdesc_entry(unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_base,
+ unsigned int tlsdesc_got_offset,
+ unsigned int plt_offset)
+ {
+ this->do_fill_tlsdesc_entry(pov, got_address, plt_address, got_base,
+ tlsdesc_got_offset, plt_offset);
+ }
+
+ virtual unsigned int
+ do_get_plt_entry_size() const = 0;
+
+ virtual void
+ do_fill_first_plt_entry(unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_addr,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_addr)
+ = 0;
+
+ virtual unsigned int
+ do_fill_plt_entry(unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset,
+ unsigned int plt_index) = 0;
+
+ virtual void
+ do_fill_tlsdesc_entry(unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_base,
+ unsigned int tlsdesc_got_offset,
+ unsigned int plt_offset) = 0;
+
+ // Return the PLT address to use for a global symbol.
+ virtual uint64_t
+ do_address_for_global(const Symbol* sym);
+
+ // Return the PLT address to use for a local symbol.
+ virtual uint64_t
+ do_address_for_local(const Relobj* obj, unsigned int symndx);
+
+ virtual void
+ do_add_eh_frame(Layout* layout) = 0;
+
+ void
+ do_adjust_output_section(Output_section* os);
+
+ // Write to a map file.
+ void
+ do_print_to_mapfile(Mapfile* mapfile) const
+ { mapfile->print_output_data(this, _("** PLT")); }
+
+ // The CIE of the .eh_frame unwind information for the PLT.
+ static const int plt_eh_frame_cie_size = 16;
+ static const unsigned char plt_eh_frame_cie[plt_eh_frame_cie_size];
+
+ private:
+ // Set the final size.
+ void
+ set_final_data_size();
+
+ // Write out the PLT data.
+ void
+ do_write(Output_file*);
+
+ // The reloc section.
+ Reloc_section* rel_;
+ // The TLSDESC relocs, if necessary. These must follow the regular
+ // PLT relocs.
+ Reloc_section* tlsdesc_rel_;
+ // The IRELATIVE relocs, if necessary. These must follow the
+ // regular PLT relocations and the TLSDESC relocations.
+ Reloc_section* irelative_rel_;
+ // The .got section.
+ Output_data_got<64, false>* got_;
+ // The .got.plt section.
+ Output_data_got_plt_x86_64* 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_X86_64_IRELATIVE relocs. These
+ // follow the regular PLT entries.
+ unsigned int irelative_count_;
+ // Offset of the reserved TLSDESC_GOT entry when needed.
+ unsigned int tlsdesc_got_offset_;
+ // List of available regions within the section, for incremental
+ // update links.
+ Free_list free_list_;
+};
+
+template<int size>
+class Output_data_plt_x86_64_standard : public Output_data_plt_x86_64<size>
+{
+ public:
+ Output_data_plt_x86_64_standard(Layout* layout,
+ Output_data_got<64, false>* got,
+ Output_data_got_plt_x86_64* got_plt,
+ Output_data_space* got_irelative)
+ : Output_data_plt_x86_64<size>(layout, plt_entry_size,
+ got, got_plt, got_irelative)
+ { }
+
+ Output_data_plt_x86_64_standard(Layout* layout,
+ Output_data_got<64, false>* got,
+ Output_data_got_plt_x86_64* got_plt,
+ Output_data_space* got_irelative,
+ unsigned int plt_count)
+ : Output_data_plt_x86_64<size>(layout, plt_entry_size,
+ got, got_plt, got_irelative,
+ plt_count)
+ { }
+
+ protected:
+ virtual unsigned int
+ do_get_plt_entry_size() const
+ { return plt_entry_size; }
+
+ virtual void
+ do_add_eh_frame(Layout* layout)
+ {
+ layout->add_eh_frame_for_plt(this,
+ this->plt_eh_frame_cie,
+ this->plt_eh_frame_cie_size,
+ plt_eh_frame_fde,
+ plt_eh_frame_fde_size);
+ }
+
+ virtual void
+ do_fill_first_plt_entry(unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_addr,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_addr);
+
+ virtual unsigned int
+ do_fill_plt_entry(unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset,
+ unsigned int plt_index);
+
+ virtual void
+ do_fill_tlsdesc_entry(unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr plt_address,
+ typename elfcpp::Elf_types<size>::Elf_Addr got_base,
+ unsigned int tlsdesc_got_offset,
+ unsigned int plt_offset);
+
+ private:
+ // The size of an entry in the PLT.
+ static const int plt_entry_size = 16;
+
+ // The first entry in the PLT.
+ // From the AMD64 ABI: "Unlike Intel386 ABI, this ABI uses the same
+ // procedure linkage table for both programs and shared objects."
+ static const unsigned char first_plt_entry[plt_entry_size];
+
+ // Other entries in the PLT for an executable.
+ static const unsigned char plt_entry[plt_entry_size];
+
+ // The reserved TLSDESC entry in the PLT for an executable.
+ static const unsigned char tlsdesc_plt_entry[plt_entry_size];
+
+ // The .eh_frame unwind information for the PLT.
+ static const int plt_eh_frame_fde_size = 32;
+ static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size];
+};
+
+class Output_data_plt_x86_64_bnd : public Output_data_plt_x86_64<64>
+{
+ public:
+ Output_data_plt_x86_64_bnd(Layout* layout,
+ Output_data_got<64, false>* got,
+ Output_data_got_plt_x86_64* got_plt,
+ Output_data_space* got_irelative)
+ : Output_data_plt_x86_64<64>(layout, plt_entry_size,
+ got, got_plt, got_irelative),
+ aplt_offset_(0)
+ { }
+
+ Output_data_plt_x86_64_bnd(Layout* layout,
+ Output_data_got<64, false>* got,
+ Output_data_got_plt_x86_64* got_plt,
+ Output_data_space* got_irelative,
+ unsigned int plt_count)
+ : Output_data_plt_x86_64<64>(layout, plt_entry_size,
+ got, got_plt, got_irelative,
+ plt_count),
+ aplt_offset_(0)
+ { }
+
+ protected:
+ virtual unsigned int
+ do_get_plt_entry_size() const
+ { return plt_entry_size; }
+
+ // Return the PLT address to use for a global symbol.
+ uint64_t
+ do_address_for_global(const Symbol*);
+
+ // Return the PLT address to use for a local symbol.
+ uint64_t
+ do_address_for_local(const Relobj*, unsigned int symndx);
+
+ virtual void
+ do_add_eh_frame(Layout* layout)
+ {
+ layout->add_eh_frame_for_plt(this,
+ this->plt_eh_frame_cie,
+ this->plt_eh_frame_cie_size,
+ plt_eh_frame_fde,
+ plt_eh_frame_fde_size);
+ }
+
+ virtual void
+ do_fill_first_plt_entry(unsigned char* pov,
+ elfcpp::Elf_types<64>::Elf_Addr got_addr,
+ elfcpp::Elf_types<64>::Elf_Addr plt_addr);
+
+ virtual unsigned int
+ do_fill_plt_entry(unsigned char* pov,
+ elfcpp::Elf_types<64>::Elf_Addr got_address,
+ elfcpp::Elf_types<64>::Elf_Addr plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset,
+ unsigned int plt_index);
+
+ virtual void
+ do_fill_tlsdesc_entry(unsigned char* pov,
+ elfcpp::Elf_types<64>::Elf_Addr got_address,
+ elfcpp::Elf_types<64>::Elf_Addr plt_address,
+ elfcpp::Elf_types<64>::Elf_Addr got_base,
+ unsigned int tlsdesc_got_offset,
+ unsigned int plt_offset);
+
+ void
+ fill_aplt_entry(unsigned char* pov,
+ elfcpp::Elf_types<64>::Elf_Addr got_address,
+ elfcpp::Elf_types<64>::Elf_Addr plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset,
+ unsigned int plt_index);
+
+ private:
+ // Set the final size.
+ void
+ set_final_data_size();
+
+ // Write out the BND PLT data.
+ void
+ do_write(Output_file*);
+
+ // Offset of the Additional PLT (if using -z bndplt).
+ unsigned int aplt_offset_;
+
+ // The size of an entry in the PLT.
+ static const int plt_entry_size = 16;
+
+ // The size of an entry in the additional PLT.
+ static const int aplt_entry_size = 8;
+
+ // The first entry in the PLT.
+ // From the AMD64 ABI: "Unlike Intel386 ABI, this ABI uses the same
+ // procedure linkage table for both programs and shared objects."
+ static const unsigned char first_plt_entry[plt_entry_size];
+
+ // Other entries in the PLT for an executable.
+ static const unsigned char plt_entry[plt_entry_size];
+
+ // Entries in the additional PLT.
+ static const unsigned char aplt_entry[aplt_entry_size];
+
+ // The reserved TLSDESC entry in the PLT for an executable.
+ static const unsigned char tlsdesc_plt_entry[plt_entry_size];
+
+ // The .eh_frame unwind information for the PLT.
+ static const int plt_eh_frame_fde_size = 32;
+ static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size];
+};
+
+template<int size>
+class Lazy_view
+{
+ public:
+ Lazy_view(Sized_relobj_file<size, false>* object, unsigned int data_shndx)
+ : object_(object), data_shndx_(data_shndx), view_(NULL), view_size_(0)
+ { }
+
+ inline unsigned char
+ operator[](size_t offset)
+ {
+ if (this->view_ == NULL)
+ this->view_ = this->object_->section_contents(this->data_shndx_,
+ &this->view_size_,
+ true);
+ if (offset >= this->view_size_)
+ return 0;
+ return this->view_[offset];
+ }
+
+ private:
+ Sized_relobj_file<size, false>* object_;
+ unsigned int data_shndx_;
+ const unsigned char* view_;
+ section_size_type view_size_;
+};