+// Return the APLT address to use for a global symbol (for IBT).
+
+template<int size>
+uint64_t
+Output_data_plt_x86_64_ibt<size>::do_address_for_global(const Symbol* gsym)
+{
+ uint64_t offset = this->aplt_offset_;
+ // Convert the PLT offset into an APLT offset.
+ unsigned int plt_offset = gsym->plt_offset();
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false))
+ offset += this->regular_count() * aplt_entry_size;
+ else
+ plt_offset -= plt_entry_size;
+ plt_offset = plt_offset / (plt_entry_size / aplt_entry_size);
+ return this->address() + offset + plt_offset;
+}
+
+// Return the PLT address to use for a local symbol. These are always
+// IRELATIVE relocs.
+
+template<int size>
+uint64_t
+Output_data_plt_x86_64_ibt<size>::do_address_for_local(const Relobj* object,
+ unsigned int r_sym)
+{
+ // Convert the PLT offset into an APLT offset.
+ unsigned int plt_offset = ((object->local_plt_offset(r_sym) - plt_entry_size)
+ / (plt_entry_size / aplt_entry_size));
+ return (this->address()
+ + this->aplt_offset_
+ + this->regular_count() * aplt_entry_size
+ + plt_offset);
+}
+
+// Set the final size.
+
+template<int size>
+void
+Output_data_plt_x86_64_ibt<size>::set_final_data_size()
+{
+ // Number of regular and IFUNC PLT entries.
+ unsigned int count = this->entry_count();
+ // Count the first entry and the TLSDESC entry, if present.
+ unsigned int extra = this->has_tlsdesc_entry() ? 2 : 1;
+ unsigned int plt_size = (count + extra) * plt_entry_size;
+ // Offset of the APLT.
+ this->aplt_offset_ = plt_size;
+ // Size of the APLT.
+ plt_size += count * aplt_entry_size;
+ this->set_data_size(plt_size);
+}
+
+// The first entry in the IBT PLT.
+
+template<>
+const unsigned char
+Output_data_plt_x86_64_ibt<32>::first_plt_entry[plt_entry_size] =
+{
+ // MPX isn't supported for x32, so we don't need the BND prefix.
+ // From AMD64 ABI Draft 0.98, page 76
+ 0xff, 0x35, // pushq contents of memory address
+ 0, 0, 0, 0, // replaced with address of .got + 8
+ 0xff, 0x25, // jmp indirect
+ 0, 0, 0, 0, // replaced with address of .got + 16
+ 0x90, 0x90, 0x90, 0x90 // noop (x4)
+};
+
+template<>
+const unsigned char
+Output_data_plt_x86_64_ibt<64>::first_plt_entry[plt_entry_size] =
+{
+ // Use the BND prefix so that IBT is compatible with MPX.
+ 0xff, 0x35, // pushq contents of memory address
+ 0, 0, 0, 0, // replaced with address of .got + 8
+ 0xf2, 0xff, 0x25, // bnd jmp indirect
+ 0, 0, 0, 0, // replaced with address of .got + 16
+ 0x0f, 0x1f, 0x00 // nop
+};
+
+template<int size>
+void
+Output_data_plt_x86_64_ibt<size>::do_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)
+{
+ // Offsets to the addresses needing relocation.
+ const unsigned int roff1 = 2;
+ const unsigned int roff2 = (size == 32) ? 8 : 9;
+
+ memcpy(pov, first_plt_entry, plt_entry_size);
+ // We do a jmp relative to the PC at the end of this instruction.
+ elfcpp::Swap_unaligned<32, false>::writeval(pov + roff1,
+ (got_address + 8
+ - (plt_address + roff1 + 4)));
+ elfcpp::Swap<32, false>::writeval(pov + roff2,
+ (got_address + 16
+ - (plt_address + roff2 + 4)));
+}
+
+// Subsequent entries in the IBT PLT.
+
+template<>
+const unsigned char
+Output_data_plt_x86_64_ibt<32>::plt_entry[plt_entry_size] =
+{
+ // From AMD64 ABI Draft 1.0-rc1, Chapter 13.
+ 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
+ 0x68, // pushq immediate
+ 0, 0, 0, 0, // replaced with offset into relocation table
+ 0xe9, // jmpq relative
+ 0, 0, 0, 0, // replaced with offset to start of .plt
+ 0x90, 0x90 // nop
+};
+
+template<>
+const unsigned char
+Output_data_plt_x86_64_ibt<64>::plt_entry[plt_entry_size] =
+{
+ // From AMD64 ABI Draft 1.0-rc1, Chapter 13.
+ 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
+ 0x68, // pushq immediate
+ 0, 0, 0, 0, // replaced with offset into relocation table
+ 0xf2, 0xe9, // bnd jmpq relative
+ 0, 0, 0, 0, // replaced with offset to start of .plt
+ 0x90 // nop
+};
+
+// Entries in the IBT Additional PLT.
+
+template<>
+const unsigned char
+Output_data_plt_x86_64_ibt<32>::aplt_entry[aplt_entry_size] =
+{
+ // From AMD64 ABI Draft 1.0-rc1, Chapter 13.
+ 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
+ 0xff, 0x25, // jmpq indirect
+ 0, 0, 0, 0, // replaced with address of symbol in .got
+ 0x0f, 0x1f, 0x04, 0x00, // nop
+ 0x90, 0x90 // nop
+};
+
+template<>
+const unsigned char
+Output_data_plt_x86_64_ibt<64>::aplt_entry[aplt_entry_size] =
+{
+ // From AMD64 ABI Draft 1.0-rc1, Chapter 13.
+ 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
+ 0xf2, 0xff, 0x25, // bnd jmpq indirect
+ 0, 0, 0, 0, // replaced with address of symbol in .got
+ 0x0f, 0x1f, 0x04, 0x00, // nop
+ 0x90, // nop
+};
+
+template<int size>
+unsigned int
+Output_data_plt_x86_64_ibt<size>::do_fill_plt_entry(
+ unsigned char* pov,
+ typename elfcpp::Elf_types<size>::Elf_Addr,
+ typename elfcpp::Elf_types<size>::Elf_Addr,
+ unsigned int,
+ unsigned int plt_offset,
+ unsigned int plt_index)
+{
+ // Offsets to the addresses needing relocation.
+ const unsigned int roff1 = 5;
+ const unsigned int roff2 = (size == 32) ? 10 : 11;
+
+ memcpy(pov, plt_entry, plt_entry_size);
+ elfcpp::Swap_unaligned<32, false>::writeval(pov + roff1, plt_index);
+ elfcpp::Swap<32, false>::writeval(pov + roff2, -(plt_offset + roff2 + 4));
+ return 0;
+}
+
+template<int size>
+void
+Output_data_plt_x86_64_ibt<size>::fill_aplt_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)
+{
+ // Offset to the address needing relocation.
+ const unsigned int roff = (size == 32) ? 6 : 7;
+
+ // Check PC-relative offset overflow in PLT entry.
+ uint64_t plt_got_pcrel_offset = (got_address + got_offset
+ - (plt_address + plt_offset + roff + 4));
+ if (Bits<32>::has_overflow(plt_got_pcrel_offset))
+ gold_error(_("PC-relative offset overflow in APLT entry %d"),
+ plt_index + 1);
+
+ memcpy(pov, aplt_entry, aplt_entry_size);
+ elfcpp::Swap_unaligned<32, false>::writeval(pov + roff, plt_got_pcrel_offset);
+}
+
+// The reserved TLSDESC entry in the IBT PLT for an executable.
+
+template<int size>
+const unsigned char
+Output_data_plt_x86_64_ibt<size>::tlsdesc_plt_entry[plt_entry_size] =
+{
+ // From Alexandre Oliva, "Thread-Local Storage Descriptors for IA32
+ // and AMD64/EM64T", Version 0.9.4 (2005-10-10).
+ 0xff, 0x35, // pushq x(%rip)
+ 0, 0, 0, 0, // replaced with address of linkmap GOT entry (at PLTGOT + 8)
+ 0xf2, 0xff, 0x25, // jmpq *y(%rip)
+ 0, 0, 0, 0, // replaced with offset of reserved TLSDESC_GOT entry
+ 0x0f, 0x1f, 0 // nop
+};
+
+template<int size>
+void
+Output_data_plt_x86_64_ibt<size>::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)
+{
+ memcpy(pov, tlsdesc_plt_entry, plt_entry_size);
+ elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
+ (got_address + 8
+ - (plt_address + plt_offset
+ + 6)));
+ elfcpp::Swap_unaligned<32, false>::writeval(pov + 9,
+ (got_base
+ + tlsdesc_got_offset
+ - (plt_address + plt_offset
+ + 13)));
+}
+