// x86_64.cc -- x86_64 target support for gold.
-// Copyright (C) 2006-2014 Free Software Foundation, Inc.
+// Copyright (C) 2006-2015 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
using namespace gold;
+// 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
Output_data_plt_x86_64(Layout* layout, uint64_t addralign,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ Output_data_got_plt_x86_64* got_plt,
Output_data_space* got_irelative)
- : Output_section_data(addralign), layout_(layout), 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), free_list_()
Output_data_plt_x86_64(Layout* layout, uint64_t plt_entry_size,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ 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),
- layout_(layout), tlsdesc_rel_(NULL), irelative_rel_(NULL), got_(got),
+ 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_()
{
void
do_write(Output_file*);
- // A pointer to the Layout class, so that we can find the .dynamic
- // section when we write out the GOT PLT section.
- Layout* layout_;
// The reloc section.
Reloc_section* rel_;
// The TLSDESC relocs, if necessary. These must follow the regular
// The .got section.
Output_data_got<64, false>* got_;
// The .got.plt section.
- Output_data_space* got_plt_;
+ 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.
public:
Output_data_plt_x86_64_standard(Layout* layout,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ 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_space* got_plt,
+ 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,
Output_data_plt_x86_64<size>*
make_data_plt(Layout* layout,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ Output_data_got_plt_x86_64* got_plt,
Output_data_space* got_irelative)
{
return this->do_make_data_plt(layout, got, got_plt, got_irelative);
Output_data_plt_x86_64<size>*
make_data_plt(Layout* layout,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ Output_data_got_plt_x86_64* got_plt,
Output_data_space* got_irelative,
unsigned int plt_count)
{
virtual Output_data_plt_x86_64<size>*
do_make_data_plt(Layout* layout,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ Output_data_got_plt_x86_64* got_plt,
Output_data_space* got_irelative)
{
return new Output_data_plt_x86_64_standard<size>(layout, got, got_plt,
virtual Output_data_plt_x86_64<size>*
do_make_data_plt(Layout* layout,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ Output_data_got_plt_x86_64* got_plt,
Output_data_space* got_irelative,
unsigned int plt_count)
{
got_section(Symbol_table*, Layout*);
// Get the GOT PLT section.
- Output_data_space*
+ Output_data_got_plt_x86_64*
got_plt_section() const
{
gold_assert(this->got_plt_ != NULL);
// The PLT section.
Output_data_plt_x86_64<size>* plt_;
// The GOT PLT section.
- Output_data_space* got_plt_;
+ Output_data_got_plt_x86_64* got_plt_;
// The GOT section for IRELATIVE relocations.
Output_data_space* got_irelative_;
// The GOT section for TLSDESC relocations.
| elfcpp::SHF_WRITE),
this->got_, got_order, true);
- this->got_plt_ = new Output_data_space(8, "** GOT PLT");
+ this->got_plt_ = new Output_data_got_plt_x86_64(layout);
layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
return this->rela_irelative_;
}
+// Write the first three reserved words of the .got.plt section.
+// The remainder of the section is written while writing the PLT
+// in Output_data_plt_i386::do_write.
+
+void
+Output_data_got_plt_x86_64::do_write(Output_file* of)
+{
+ // The first entry in the GOT is the address of the .dynamic section
+ // aka the PT_DYNAMIC segment. The next two entries are reserved.
+ // We saved space for them when we created the section in
+ // Target_x86_64::got_section.
+ const off_t got_file_offset = this->offset();
+ gold_assert(this->data_size() >= 24);
+ unsigned char* const got_view = of->get_output_view(got_file_offset, 24);
+ Output_section* dynamic = this->layout_->dynamic_section();
+ uint64_t dynamic_addr = dynamic == NULL ? 0 : dynamic->address();
+ elfcpp::Swap<64, false>::writeval(got_view, dynamic_addr);
+ memset(got_view + 8, 0, 16);
+ of->write_output_view(got_file_offset, 24, got_view);
+}
+
// Initialize the PLT section.
template<int size>
unsigned int* pcount;
unsigned int offset;
unsigned int reserved;
- Output_data_space* got;
+ Output_section_data_build* got;
if (gsym->type() == elfcpp::STT_GNU_IFUNC
&& gsym->can_use_relative_reloc(false))
{
unsigned int plt_offset,
unsigned int plt_index)
{
+ // Check PC-relative offset overflow in PLT entry.
+ uint64_t plt_got_pcrel_offset = (got_address + got_offset
+ - (plt_address + plt_offset + 6));
+ if (Bits<32>::has_overflow(plt_got_pcrel_offset))
+ gold_error(_("PC-relative offset overflow in PLT entry %d"),
+ plt_index + 1);
+
memcpy(pov, plt_entry, plt_entry_size);
elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
- (got_address + got_offset
- - (plt_address + plt_offset
- + 6)));
+ plt_got_pcrel_offset);
elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_index);
elfcpp::Swap<32, false>::writeval(pov + 12,
this->fill_first_plt_entry(pov, got_address, plt_address);
pov += this->get_plt_entry_size();
- unsigned char* got_pov = got_view;
-
- // The first entry in the GOT is the address of the .dynamic section
- // aka the PT_DYNAMIC segment. The next two entries are reserved.
- // We saved space for them when we created the section in
- // Target_x86_64::got_section.
- Output_section* dynamic = this->layout_->dynamic_section();
- uint32_t dynamic_addr = dynamic == NULL ? 0 : dynamic->address();
- elfcpp::Swap<64, false>::writeval(got_pov, dynamic_addr);
- got_pov += 8;
- memset(got_pov, 0, 16);
- got_pov += 16;
+ // The first three entries in the GOT are reserved, and are written
+ // by Output_data_got_plt_x86_64::do_write.
+ unsigned char* got_pov = got_view + 24;
unsigned int plt_offset = this->get_plt_entry_size();
unsigned int got_offset = 24;
true);
// Add the three reserved entries.
- this->got_plt_ = new Output_data_space((plt_count + 3) * 8, 8, "** GOT PLT");
+ this->got_plt_ = new Output_data_got_plt_x86_64(layout, (plt_count + 3) * 8);
layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
// 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);
// 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_executable()
+ && gsym->may_need_copy_reloc())
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
}
}
}
- // For GOTPLT64, we also need a PLT entry (but only if the
- // symbol is not fully resolved).
- if (r_type == elfcpp::R_X86_64_GOTPLT64
- && !gsym->final_value_is_known())
- target->make_plt_entry(symtab, layout, gsym);
}
break;
case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec
case elfcpp::R_X86_64_TPOFF32: // Local-exec
{
- const bool is_final = gsym->final_value_is_known();
+ // For the Initial-Exec model, we can treat undef symbols as final
+ // when building an executable.
+ const bool is_final = (gsym->final_value_is_known() ||
+ (r_type == elfcpp::R_X86_64_GOTTPOFF &&
+ gsym->is_undefined() &&
+ parameters->options().output_is_executable()));
const tls::Tls_optimization optimized_type
= Target_x86_64<size>::optimize_tls_reloc(is_final, r_type);
switch (r_type)
// We need to subtract the size of the GOT section to get
// the actual offset to use in the relocation.
bool have_got_offset = false;
- unsigned int got_offset = 0;
+ // Since the actual offset is always negative, we use signed int to
+ // support 64-bit GOT relocations.
+ int got_offset = 0;
switch (r_type)
{
case elfcpp::R_X86_64_GOT32:
gold_assert(gsym->has_plt_offset()
|| gsym->final_value_is_known());
typename elfcpp::Elf_types<size>::Elf_Addr got_address;
- got_address = target->got_section(NULL, NULL)->address();
+ // This is the address of GLOBAL_OFFSET_TABLE.
+ got_address = target->got_plt_section()->address();
Relocate_functions<size, false>::rela64(view, object, psymval,
addend - got_address);
}
+ break;
case elfcpp::R_X86_64_GOT32:
gold_assert(have_got_offset);
break;
case elfcpp::R_X86_64_GOT64:
- // The ABI doc says "Like GOT64, but indicates a PLT entry is needed."
- // Since we always add a PLT entry, this is equivalent.
case elfcpp::R_X86_64_GOTPLT64:
+ // R_X86_64_GOTPLT64 is obsolete and treated the the same as
+ // GOT64.
gold_assert(have_got_offset);
Relocate_functions<size, false>::rela64(view, got_offset, addend);
break;
break;
case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec
- if (optimized_type == tls::TLSOPT_TO_LE)
+ if (gsym != NULL
+ && gsym->is_undefined()
+ && parameters->options().output_is_executable())
+ {
+ Target_x86_64<size>::Relocate::tls_ie_to_le(relinfo, relnum,
+ NULL, rela,
+ r_type, value, view,
+ view_size);
+ break;
+ }
+ else if (optimized_type == tls::TLSOPT_TO_LE)
{
if (tls_segment == NULL)
{
// movq
if (op1 == 0x4c)
view[-3] = 0x49;
+ else if (size == 32 && op1 == 0x44)
+ view[-3] = 0x41;
view[-2] = 0xc7;
view[-1] = 0xc0 | reg;
}
// Special handling for %rsp.
if (op1 == 0x4c)
view[-3] = 0x49;
+ else if (size == 32 && op1 == 0x44)
+ view[-3] = 0x41;
view[-2] = 0x81;
view[-1] = 0xc0 | reg;
}
// addq
if (op1 == 0x4c)
view[-3] = 0x4d;
+ else if (size == 32 && op1 == 0x44)
+ view[-3] = 0x45;
view[-2] = 0x8d;
view[-1] = 0x80 | reg | (reg << 3);
}
- value -= tls_segment->memsz();
+ if (tls_segment != NULL)
+ value -= tls_segment->memsz();
Relocate_functions<size, false>::rela32(view, value, 0);
}
// code. We have to change the function so that it always ensures
// that it has enough stack space to run some random function.
+static const unsigned char cmp_insn_32[] = { 0x64, 0x3b, 0x24, 0x25 };
+static const unsigned char lea_r10_insn_32[] = { 0x44, 0x8d, 0x94, 0x24 };
+static const unsigned char lea_r11_insn_32[] = { 0x44, 0x8d, 0x9c, 0x24 };
+
+static const unsigned char cmp_insn_64[] = { 0x64, 0x48, 0x3b, 0x24, 0x25 };
+static const unsigned char lea_r10_insn_64[] = { 0x4c, 0x8d, 0x94, 0x24 };
+static const unsigned char lea_r11_insn_64[] = { 0x4c, 0x8d, 0x9c, 0x24 };
+
template<int size>
void
Target_x86_64<size>::do_calls_non_split(Relobj* object, unsigned int shndx,
std::string* from,
std::string* to) const
{
+ const char* const cmp_insn = reinterpret_cast<const char*>
+ (size == 32 ? cmp_insn_32 : cmp_insn_64);
+ const char* const lea_r10_insn = reinterpret_cast<const char*>
+ (size == 32 ? lea_r10_insn_32 : lea_r10_insn_64);
+ const char* const lea_r11_insn = reinterpret_cast<const char*>
+ (size == 32 ? lea_r11_insn_32 : lea_r11_insn_64);
+
+ const size_t cmp_insn_len =
+ (size == 32 ? sizeof(cmp_insn_32) : sizeof(cmp_insn_64));
+ const size_t lea_r10_insn_len =
+ (size == 32 ? sizeof(lea_r10_insn_32) : sizeof(lea_r10_insn_64));
+ const size_t lea_r11_insn_len =
+ (size == 32 ? sizeof(lea_r11_insn_32) : sizeof(lea_r11_insn_64));
+ const size_t nop_len = (size == 32 ? 7 : 8);
+
// The function starts with a comparison of the stack pointer and a
// field in the TCB. This is followed by a jump.
// cmp %fs:NN,%rsp
- if (this->match_view(view, view_size, fnoffset, "\x64\x48\x3b\x24\x25", 5)
- && fnsize > 9)
+ if (this->match_view(view, view_size, fnoffset, cmp_insn, cmp_insn_len)
+ && fnsize > nop_len + 1)
{
// We will call __morestack if the carry flag is set after this
// comparison. We turn the comparison into an stc instruction
// and some nops.
view[fnoffset] = '\xf9';
- this->set_view_to_nop(view, view_size, fnoffset + 1, 8);
+ this->set_view_to_nop(view, view_size, fnoffset + 1, nop_len);
}
// lea NN(%rsp),%r10
// lea NN(%rsp),%r11
else if ((this->match_view(view, view_size, fnoffset,
- "\x4c\x8d\x94\x24", 4)
+ lea_r10_insn, lea_r10_insn_len)
|| this->match_view(view, view_size, fnoffset,
- "\x4c\x8d\x9c\x24", 4))
+ lea_r11_insn, lea_r11_insn_len))
&& fnsize > 8)
{
// This is loading an offset from the stack pointer for a
public:
Output_data_plt_x86_64_nacl(Layout* layout,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ 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_nacl(Layout* layout,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ 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,
virtual Output_data_plt_x86_64<size>*
do_make_data_plt(Layout* layout,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ Output_data_got_plt_x86_64* got_plt,
Output_data_space* got_irelative)
{
return new Output_data_plt_x86_64_nacl<size>(layout, got, got_plt,
virtual Output_data_plt_x86_64<size>*
do_make_data_plt(Layout* layout,
Output_data_got<64, false>* got,
- Output_data_space* got_plt,
+ Output_data_got_plt_x86_64* got_plt,
Output_data_space* got_irelative,
unsigned int plt_count)
{