// x86_64.cc -- x86_64 target support for gold.
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012
-// Free Software Foundation, Inc.
+// Copyright (C) 2006-2016 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,
got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
got_tlsdesc_(NULL), global_offset_table_(NULL), rela_dyn_(NULL),
rela_irelative_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY),
- dynbss_(NULL), got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
+ got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
tls_base_symbol_defined_(false)
{ }
const unsigned char* plocal_symbols,
Relocatable_relocs*);
+ // Scan the relocs for --emit-relocs.
+ void
+ emit_relocs_scan(Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj_file<size, false>* 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_relocs(
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
- off_t offset_in_output_section,
- const Relocatable_relocs*,
+ typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr view_address,
section_size_type view_size,
void
do_calls_non_split(Relobj* object, unsigned int shndx,
section_offset_type fnoffset, section_size_type fnsize,
+ const unsigned char* prelocs, size_t reloc_count,
unsigned char* view, section_size_type view_size,
std::string* from, std::string* to) const;
unsigned int
plt_entry_size() const;
+ // Return the size of each GOT entry.
+ unsigned int
+ got_entry_size() const
+ { return 8; };
+
// Create the GOT section for an incremental update.
Output_data_got_base*
init_got_plt_for_update(Symbol_table* symtab,
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)
{
// Do a relocation. Return false if the caller should not issue
// any warnings about this relocation.
inline bool
- relocate(const Relocate_info<size, false>*, Target_x86_64*,
- Output_section*,
- size_t relnum, const elfcpp::Rela<size, false>&,
- unsigned int r_type, const Sized_symbol<size>*,
- const Symbol_value<size>*,
+ relocate(const Relocate_info<size, false>*, unsigned int,
+ Target_x86_64*, Output_section*, size_t, const unsigned char*,
+ const Sized_symbol<size>*, const Symbol_value<size>*,
unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr,
section_size_type);
// Do a TLS General-Dynamic to Initial-Exec transition.
inline void
tls_gd_to_ie(const Relocate_info<size, false>*, size_t relnum,
- Output_segment* tls_segment,
const elfcpp::Rela<size, false>&, unsigned int r_type,
typename elfcpp::Elf_types<size>::Elf_Addr value,
unsigned char* view,
// Do a TLSDESC-style General-Dynamic to Initial-Exec transition.
inline void
tls_desc_gd_to_ie(const Relocate_info<size, false>*, size_t relnum,
- Output_segment* tls_segment,
const elfcpp::Rela<size, false>&, unsigned int r_type,
typename elfcpp::Elf_types<size>::Elf_Addr value,
unsigned char* view,
bool skip_call_tls_get_addr_;
};
- // A class which returns the size required for a relocation type,
- // used while scanning relocs during a relocatable link.
- class Relocatable_size_for_reloc
+ // Check if relocation against this symbol is a candidate for
+ // conversion from
+ // mov foo@GOTPCREL(%rip), %reg
+ // to lea foo(%rip), %reg.
+ static bool
+ can_convert_mov_to_lea(const Symbol* gsym)
{
- public:
- unsigned int
- get_size_for_reloc(unsigned int, Relobj*);
- };
+ gold_assert(gsym != NULL);
+ return (gsym->type() != elfcpp::STT_GNU_IFUNC
+ && !gsym->is_undefined ()
+ && !gsym->is_from_dynobj()
+ && !gsym->is_preemptible()
+ && (!parameters->options().shared()
+ || (gsym->visibility() != elfcpp::STV_DEFAULT
+ && gsym->visibility() != elfcpp::STV_PROTECTED)
+ || parameters->options().Bsymbolic())
+ && strcmp(gsym->name(), "_DYNAMIC") != 0);
+ }
// Adjust TLS relocation type based on the options and whether this
// is a local symbol.
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);
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rela<size, false>& reloc)
{
+ unsigned int r_type = elfcpp::elf_r_type<size>(reloc.get_r_info());
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<size>(sym),
object, shndx, output_section,
- reloc, this->rela_dyn_section(layout));
+ r_type, reloc.get_r_offset(),
+ reloc.get_r_addend(),
+ this->rela_dyn_section(layout));
}
// Information about this specific target which we pass to the
// 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.
Reloc_section* rela_irelative_;
// Relocs saved to avoid a COPY reloc.
Copy_relocs<elfcpp::SHT_RELA, size, false> 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_;
// We handle R_X86_64_TLSDESC against a local symbol as a target
0, // small_common_section_flags
elfcpp::SHF_X86_64_LARGE, // large_common_section_flags
NULL, // attributes_section
- NULL // attributes_vendor
+ NULL, // attributes_vendor
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
0, // small_common_section_flags
elfcpp::SHF_X86_64_LARGE, // large_common_section_flags
NULL, // attributes_section
- NULL // attributes_vendor
+ NULL, // attributes_vendor
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
// This is called when a new output section is created. This is where
| 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),
case elfcpp::R_X86_64_PC64:
case elfcpp::R_X86_64_PC32:
+ case elfcpp::R_X86_64_PC32_BND:
case elfcpp::R_X86_64_PC16:
case elfcpp::R_X86_64_PC8:
case elfcpp::R_X86_64_GOTOFF64:
return Symbol::RELATIVE_REF;
case elfcpp::R_X86_64_PLT32:
+ case elfcpp::R_X86_64_PLT32_BND:
case elfcpp::R_X86_64_PLTOFF64:
return Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF;
case elfcpp::R_X86_64_GOT32:
case elfcpp::R_X86_64_GOTPCREL64:
case elfcpp::R_X86_64_GOTPCREL:
+ case elfcpp::R_X86_64_GOTPCRELX:
+ case elfcpp::R_X86_64_REX_GOTPCRELX:
case elfcpp::R_X86_64_GOTPLT64:
// Absolute in GOT.
return Symbol::ABSOLUTE_REF;
// glibc supports these reloc types, but they can overflow.
case elfcpp::R_X86_64_PC32:
+ case elfcpp::R_X86_64_PC32_BND:
// A PC relative reference is OK against a local symbol or if
// the symbol is defined locally.
if (gsym == NULL
object->error(_("requires dynamic R_X86_64_32 reloc which may "
"overflow at runtime; recompile with -fPIC"));
else
- object->error(_("requires dynamic %s reloc against '%s' which may "
- "overflow at runtime; recompile with -fPIC"),
- (r_type == elfcpp::R_X86_64_32
- ? "R_X86_64_32"
- : "R_X86_64_PC32"),
- gsym->name());
+ {
+ const char *r_name;
+ switch (r_type)
+ {
+ case elfcpp::R_X86_64_32:
+ r_name = "R_X86_64_32";
+ break;
+ case elfcpp::R_X86_64_PC32:
+ r_name = "R_X86_64_PC32";
+ break;
+ case elfcpp::R_X86_64_PC32_BND:
+ r_name = "R_X86_64_PC32_BND";
+ break;
+ default:
+ gold_unreachable();
+ break;
+ }
+ object->error(_("requires dynamic %s reloc against '%s' "
+ "which may overflow at runtime; recompile "
+ "with -fPIC"),
+ r_name, gsym->name());
+ }
this->issued_non_pic_error_ = true;
return;
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_local_relative(object, r_sym,
- (size == 32
+ (size == 32
? elfcpp::R_X86_64_RELATIVE64
: elfcpp::R_X86_64_RELATIVE),
output_section, data_shndx,
case elfcpp::R_X86_64_PC64:
case elfcpp::R_X86_64_PC32:
+ case elfcpp::R_X86_64_PC32_BND:
case elfcpp::R_X86_64_PC16:
case elfcpp::R_X86_64_PC8:
break;
case elfcpp::R_X86_64_PLT32:
+ case elfcpp::R_X86_64_PLT32_BND:
// Since we know this is a local symbol, we can handle this as a
// PC32 reloc.
break;
case elfcpp::R_X86_64_GOT32:
case elfcpp::R_X86_64_GOTPCREL64:
case elfcpp::R_X86_64_GOTPCREL:
+ case elfcpp::R_X86_64_GOTPCRELX:
+ case elfcpp::R_X86_64_REX_GOTPCRELX:
case elfcpp::R_X86_64_GOTPLT64:
{
- // The symbol requires a GOT entry.
+ // The symbol requires a GOT section.
Output_data_got<64, false>* got = target->got_section(symtab, layout);
+
+ // If the relocation symbol isn't IFUNC,
+ // and is local, then we will convert
+ // mov foo@GOTPCREL(%rip), %reg
+ // to lea foo(%rip), %reg.
+ // in Relocate::relocate.
+ if ((r_type == elfcpp::R_X86_64_GOTPCREL
+ || r_type == elfcpp::R_X86_64_GOTPCRELX
+ || r_type == elfcpp::R_X86_64_REX_GOTPCRELX)
+ && reloc.get_r_offset() >= 2
+ && !is_ifunc)
+ {
+ section_size_type stype;
+ const unsigned char* view = object->section_contents(data_shndx,
+ &stype, true);
+ if (view[reloc.get_r_offset() - 2] == 0x8b)
+ break;
+ }
+
+
+ // The symbol requires a GOT entry.
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
// For a STT_GNU_IFUNC symbol we want the PLT offset. That
case elfcpp::R_X86_64_GOT32:
case elfcpp::R_X86_64_GOTPCREL64:
case elfcpp::R_X86_64_GOTPCREL:
+ case elfcpp::R_X86_64_GOTPCRELX:
+ case elfcpp::R_X86_64_REX_GOTPCRELX:
case elfcpp::R_X86_64_GOTPLT64:
{
return true;
// 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);
reloc.get_r_offset(),
reloc.get_r_addend());
}
- else if (r_type == elfcpp::R_X86_64_64
+ else if (((size == 64 && r_type == elfcpp::R_X86_64_64)
+ || (size == 32 && r_type == elfcpp::R_X86_64_32))
&& gsym->can_use_relative_reloc(false))
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
case elfcpp::R_X86_64_PC64:
case elfcpp::R_X86_64_PC32:
+ case elfcpp::R_X86_64_PC32_BND:
case elfcpp::R_X86_64_PC16:
case elfcpp::R_X86_64_PC8:
{
// 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);
case elfcpp::R_X86_64_GOT32:
case elfcpp::R_X86_64_GOTPCREL64:
case elfcpp::R_X86_64_GOTPCREL:
+ case elfcpp::R_X86_64_GOTPCRELX:
+ case elfcpp::R_X86_64_REX_GOTPCRELX:
case elfcpp::R_X86_64_GOTPLT64:
{
// The symbol requires a GOT entry.
Output_data_got<64, false>* got = target->got_section(symtab, layout);
+
+ // If we convert this from
+ // mov foo@GOTPCREL(%rip), %reg
+ // to lea foo(%rip), %reg.
+ // in Relocate::relocate, then there is nothing to do here.
+ if ((r_type == elfcpp::R_X86_64_GOTPCREL
+ || r_type == elfcpp::R_X86_64_GOTPCRELX
+ || r_type == elfcpp::R_X86_64_REX_GOTPCRELX)
+ && reloc.get_r_offset() >= 2
+ && Target_x86_64<size>::can_convert_mov_to_lea(gsym))
+ {
+ section_size_type stype;
+ const unsigned char* view = object->section_contents(data_shndx,
+ &stype, true);
+ if (view[reloc.get_r_offset() - 2] == 0x8b)
+ break;
+ }
+
if (gsym->final_value_is_known())
{
// For a STT_GNU_IFUNC symbol we want the PLT address.
}
}
}
- // 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_PLT32:
+ case elfcpp::R_X86_64_PLT32_BND:
// If the symbol is fully resolved, this is just a PC32 reloc.
// Otherwise we need a PLT entry.
if (gsym->final_value_is_known())
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)
size_t local_symbol_count,
const unsigned char* plocal_symbols)
{
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, false>
+ Classify_reloc;
if (sh_type == elfcpp::SHT_REL)
{
return;
}
- gold::gc_process_relocs<size, false, Target_x86_64<size>, elfcpp::SHT_RELA,
- typename Target_x86_64<size>::Scan,
- typename Target_x86_64<size>::Relocatable_size_for_reloc>(
+ gold::gc_process_relocs<size, false, Target_x86_64<size>, Scan,
+ Classify_reloc>(
symtab,
layout,
this,
size_t local_symbol_count,
const unsigned char* plocal_symbols)
{
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, false>
+ Classify_reloc;
+
if (sh_type == elfcpp::SHT_REL)
{
gold_error(_("%s: unsupported REL reloc section"),
return;
}
- gold::scan_relocs<size, false, Target_x86_64<size>, elfcpp::SHT_RELA,
- typename Target_x86_64<size>::Scan>(
+ gold::scan_relocs<size, false, Target_x86_64<size>, Scan, Classify_reloc>(
symtab,
layout,
this,
}
}
+// For x32, we need to handle PC-relative relocations using full 64-bit
+// arithmetic, so that we can detect relocation overflows properly.
+// This class overrides the pcrela32_check methods from the defaults in
+// Relocate_functions in reloc.h.
+
+template<int size>
+class X86_64_relocate_functions : public Relocate_functions<size, false>
+{
+ public:
+ typedef Relocate_functions<size, false> Base;
+
+ // Do a simple PC relative relocation with the addend in the
+ // relocation.
+ static inline typename Base::Reloc_status
+ pcrela32_check(unsigned char* view,
+ typename elfcpp::Elf_types<64>::Elf_Addr value,
+ typename elfcpp::Elf_types<64>::Elf_Swxword addend,
+ typename elfcpp::Elf_types<64>::Elf_Addr address)
+ {
+ typedef typename elfcpp::Swap<32, false>::Valtype Valtype;
+ Valtype* wv = reinterpret_cast<Valtype*>(view);
+ value = value + addend - address;
+ elfcpp::Swap<32, false>::writeval(wv, value);
+ return (Bits<32>::has_overflow(value)
+ ? Base::RELOC_OVERFLOW : Base::RELOC_OK);
+ }
+
+ // Do a simple PC relative relocation with a Symbol_value with the
+ // addend in the relocation.
+ static inline typename Base::Reloc_status
+ pcrela32_check(unsigned char* view,
+ const Sized_relobj_file<size, false>* object,
+ const Symbol_value<size>* psymval,
+ typename elfcpp::Elf_types<64>::Elf_Swxword addend,
+ typename elfcpp::Elf_types<64>::Elf_Addr address)
+ {
+ typedef typename elfcpp::Swap<32, false>::Valtype Valtype;
+ Valtype* wv = reinterpret_cast<Valtype*>(view);
+ typename elfcpp::Elf_types<64>::Elf_Addr value;
+ if (addend >= 0)
+ value = psymval->value(object, addend);
+ else
+ {
+ // For negative addends, get the symbol value without
+ // the addend, then add the addend using 64-bit arithmetic.
+ value = psymval->value(object, 0);
+ value += addend;
+ }
+ value -= address;
+ elfcpp::Swap<32, false>::writeval(wv, value);
+ return (Bits<32>::has_overflow(value)
+ ? Base::RELOC_OVERFLOW : Base::RELOC_OK);
+ }
+};
+
// Perform a relocation.
template<int size>
inline bool
Target_x86_64<size>::Relocate::relocate(
const Relocate_info<size, false>* relinfo,
+ unsigned int,
Target_x86_64<size>* target,
Output_section*,
size_t relnum,
- const elfcpp::Rela<size, false>& rela,
- unsigned int r_type,
+ const unsigned char* preloc,
const Sized_symbol<size>* gsym,
const Symbol_value<size>* psymval,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr address,
section_size_type view_size)
{
+ typedef X86_64_relocate_functions<size> Reloc_funcs;
+ const elfcpp::Rela<size, false> rela(preloc);
+ unsigned int r_type = elfcpp::elf_r_type<size>(rela.get_r_info());
+
if (this->skip_call_tls_get_addr_)
{
if ((r_type != elfcpp::R_X86_64_PLT32
+ && r_type != elfcpp::R_X86_64_PLT32_BND
+ && r_type != elfcpp::R_X86_64_PC32_BND
&& r_type != elfcpp::R_X86_64_PC32)
|| gsym == NULL
|| strcmp(gsym->name(), "__tls_get_addr") != 0)
}
}
+ if (view == NULL)
+ return true;
+
const Sized_relobj_file<size, false>* object = relinfo->object;
// Pick the value to use for symbols defined in the PLT.
// 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:
case elfcpp::R_X86_64_GOT64:
case elfcpp::R_X86_64_GOTPLT64:
- case elfcpp::R_X86_64_GOTPCREL:
case elfcpp::R_X86_64_GOTPCREL64:
if (gsym != NULL)
{
break;
}
+ typename Reloc_funcs::Reloc_status rstatus = Reloc_funcs::RELOC_OK;
+
switch (r_type)
{
case elfcpp::R_X86_64_NONE:
break;
case elfcpp::R_X86_64_64:
- Relocate_functions<size, false>::rela64(view, object, psymval, addend);
+ Reloc_funcs::rela64(view, object, psymval, addend);
break;
case elfcpp::R_X86_64_PC64:
- Relocate_functions<size, false>::pcrela64(view, object, psymval, addend,
+ Reloc_funcs::pcrela64(view, object, psymval, addend,
address);
break;
case elfcpp::R_X86_64_32:
- // FIXME: we need to verify that value + addend fits into 32 bits:
- // uint64_t x = value + addend;
- // x == static_cast<uint64_t>(static_cast<uint32_t>(x))
- // Likewise for other <=32-bit relocations (but see R_X86_64_32S).
- Relocate_functions<size, false>::rela32(view, object, psymval, addend);
+ rstatus = Reloc_funcs::rela32_check(view, object, psymval, addend,
+ Reloc_funcs::CHECK_UNSIGNED);
break;
case elfcpp::R_X86_64_32S:
- // FIXME: we need to verify that value + addend fits into 32 bits:
- // int64_t x = value + addend; // note this quantity is signed!
- // x == static_cast<int64_t>(static_cast<int32_t>(x))
- Relocate_functions<size, false>::rela32(view, object, psymval, addend);
+ rstatus = Reloc_funcs::rela32_check(view, object, psymval, addend,
+ Reloc_funcs::CHECK_SIGNED);
break;
case elfcpp::R_X86_64_PC32:
- Relocate_functions<size, false>::pcrela32(view, object, psymval, addend,
- address);
+ case elfcpp::R_X86_64_PC32_BND:
+ rstatus = Reloc_funcs::pcrela32_check(view, object, psymval, addend,
+ address);
break;
case elfcpp::R_X86_64_16:
- Relocate_functions<size, false>::rela16(view, object, psymval, addend);
+ Reloc_funcs::rela16(view, object, psymval, addend);
break;
case elfcpp::R_X86_64_PC16:
- Relocate_functions<size, false>::pcrela16(view, object, psymval, addend,
- address);
+ Reloc_funcs::pcrela16(view, object, psymval, addend, address);
break;
case elfcpp::R_X86_64_8:
- Relocate_functions<size, false>::rela8(view, object, psymval, addend);
+ Reloc_funcs::rela8(view, object, psymval, addend);
break;
case elfcpp::R_X86_64_PC8:
- Relocate_functions<size, false>::pcrela8(view, object, psymval, addend,
- address);
+ Reloc_funcs::pcrela8(view, object, psymval, addend, address);
break;
case elfcpp::R_X86_64_PLT32:
+ case elfcpp::R_X86_64_PLT32_BND:
gold_assert(gsym == NULL
|| gsym->has_plt_offset()
|| gsym->final_value_is_known()
// Note: while this code looks the same as for R_X86_64_PC32, it
// behaves differently because psymval was set to point to
// the PLT entry, rather than the symbol, in Scan::global().
- Relocate_functions<size, false>::pcrela32(view, object, psymval, addend,
- address);
+ rstatus = Reloc_funcs::pcrela32_check(view, object, psymval, addend,
+ address);
break;
case elfcpp::R_X86_64_PLTOFF64:
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();
- Relocate_functions<size, false>::rela64(view, object, psymval,
- addend - got_address);
+ // This is the address of GLOBAL_OFFSET_TABLE.
+ got_address = target->got_plt_section()->address();
+ Reloc_funcs::rela64(view, object, psymval, addend - got_address);
}
+ break;
case elfcpp::R_X86_64_GOT32:
gold_assert(have_got_offset);
- Relocate_functions<size, false>::rela32(view, got_offset, addend);
+ Reloc_funcs::rela32(view, got_offset, addend);
break;
case elfcpp::R_X86_64_GOTPC32:
gold_assert(gsym);
typename elfcpp::Elf_types<size>::Elf_Addr value;
value = target->got_plt_section()->address();
- Relocate_functions<size, false>::pcrela32(view, value, addend, address);
+ Reloc_funcs::pcrela32_check(view, value, addend, address);
}
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);
+ Reloc_funcs::rela64(view, got_offset, addend);
break;
case elfcpp::R_X86_64_GOTPC64:
gold_assert(gsym);
typename elfcpp::Elf_types<size>::Elf_Addr value;
value = target->got_plt_section()->address();
- Relocate_functions<size, false>::pcrela64(view, value, addend, address);
+ Reloc_funcs::pcrela64(view, value, addend, address);
}
break;
typename elfcpp::Elf_types<size>::Elf_Addr value;
value = (psymval->value(object, 0)
- target->got_plt_section()->address());
- Relocate_functions<size, false>::rela64(view, value, addend);
+ Reloc_funcs::rela64(view, value, addend);
}
break;
case elfcpp::R_X86_64_GOTPCREL:
+ case elfcpp::R_X86_64_GOTPCRELX:
+ case elfcpp::R_X86_64_REX_GOTPCRELX:
{
- gold_assert(have_got_offset);
- typename elfcpp::Elf_types<size>::Elf_Addr value;
- value = target->got_plt_section()->address() + got_offset;
- Relocate_functions<size, false>::pcrela32(view, value, addend, address);
+ // Convert
+ // mov foo@GOTPCREL(%rip), %reg
+ // to lea foo(%rip), %reg.
+ // if possible.
+ if (rela.get_r_offset() >= 2
+ && view[-2] == 0x8b
+ && ((gsym == NULL && !psymval->is_ifunc_symbol())
+ || (gsym != NULL
+ && Target_x86_64<size>::can_convert_mov_to_lea(gsym))))
+ {
+ view[-2] = 0x8d;
+ Reloc_funcs::pcrela32(view, object, psymval, addend, address);
+ }
+ else
+ {
+ if (gsym != NULL)
+ {
+ gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD));
+ got_offset = gsym->got_offset(GOT_TYPE_STANDARD) - target->got_size();
+ }
+ else
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
+ gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD));
+ got_offset = (object->local_got_offset(r_sym, GOT_TYPE_STANDARD)
+ - target->got_size());
+ }
+ typename elfcpp::Elf_types<size>::Elf_Addr value;
+ value = target->got_plt_section()->address() + got_offset;
+ Reloc_funcs::pcrela32_check(view, value, addend, address);
+ }
}
break;
gold_assert(have_got_offset);
typename elfcpp::Elf_types<size>::Elf_Addr value;
value = target->got_plt_section()->address() + got_offset;
- Relocate_functions<size, false>::pcrela64(view, value, addend, address);
+ Reloc_funcs::pcrela64(view, value, addend, address);
}
break;
break;
}
+ if (rstatus == Reloc_funcs::RELOC_OVERFLOW)
+ {
+ if (gsym == NULL)
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("relocation overflow: "
+ "reference to local symbol %u in %s"),
+ r_sym, object->name().c_str());
+ }
+ else if (gsym->is_defined() && gsym->source() == Symbol::FROM_OBJECT)
+ {
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("relocation overflow: "
+ "reference to '%s' defined in %s"),
+ gsym->name(),
+ gsym->object()->name().c_str());
+ }
+ else
+ {
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("relocation overflow: reference to '%s'"),
+ gsym->name());
+ }
+ }
+
return true;
}
if (optimized_type == tls::TLSOPT_TO_IE)
{
value = target->got_plt_section()->address() + got_offset;
- this->tls_gd_to_ie(relinfo, relnum, tls_segment, rela, r_type,
+ this->tls_gd_to_ie(relinfo, relnum, rela, r_type,
value, view, address, view_size);
break;
}
}
if (optimized_type == tls::TLSOPT_TO_IE)
{
- if (tls_segment == NULL)
- {
- gold_assert(parameters->errors()->error_count() > 0
- || issue_undefined_symbol_error(gsym));
- return;
- }
value = target->got_plt_section()->address() + got_offset;
- this->tls_desc_gd_to_ie(relinfo, relnum, tls_segment,
+ this->tls_desc_gd_to_ie(relinfo, relnum,
rela, r_type, value, view, address,
view_size);
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)
{
Target_x86_64<size>::Relocate::tls_gd_to_ie(
const Relocate_info<size, false>* relinfo,
size_t relnum,
- Output_segment*,
const elfcpp::Rela<size, false>& rela,
unsigned int,
typename elfcpp::Elf_types<size>::Elf_Addr value,
Target_x86_64<size>::Relocate::tls_desc_gd_to_ie(
const Relocate_info<size, false>* relinfo,
size_t relnum,
- Output_segment*,
const elfcpp::Rela<size, false>& rela,
unsigned int r_type,
typename elfcpp::Elf_types<size>::Elf_Addr value,
section_size_type view_size)
{
// leaq foo@tlsld(%rip),%rdi; call __tls_get_addr@plt;
+ // For SIZE == 64:
// ... leq foo@dtpoff(%rax),%reg
// ==> .word 0x6666; .byte 0x66; movq %fs:0,%rax ... leaq x@tpoff(%rax),%rdx
+ // For SIZE == 32:
+ // ... leq foo@dtpoff(%rax),%reg
+ // ==> nopl 0x0(%rax); movl %fs:0,%eax ... leaq x@tpoff(%rax),%rdx
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -3);
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 9);
tls::check_tls(relinfo, relnum, rela.get_r_offset(), view[4] == 0xe8);
- memcpy(view - 3, "\x66\x66\x66\x64\x48\x8b\x04\x25\0\0\0\0", 12);
+ if (size == 64)
+ memcpy(view - 3, "\x66\x66\x66\x64\x48\x8b\x04\x25\0\0\0\0", 12);
+ else
+ memcpy(view - 3, "\x0f\x1f\x40\x00\x64\x8b\x04\x25\0\0\0\0", 12);
// The next reloc should be a PLT32 reloc against __tls_get_addr.
// We can skip it.
// 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);
}
section_size_type view_size,
const Reloc_symbol_changes* reloc_symbol_changes)
{
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, false>
+ Classify_reloc;
+
gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::relocate_section<size, false, Target_x86_64<size>, elfcpp::SHT_RELA,
- typename Target_x86_64<size>::Relocate>(
+ gold::relocate_section<size, false, Target_x86_64<size>, Relocate,
+ gold::Default_comdat_behavior, Classify_reloc>(
relinfo,
this,
prelocs,
view_size);
}
-// Return the size of a relocation while scanning during a relocatable
-// link.
+// Scan the relocs during a relocatable link.
template<int size>
-unsigned int
-Target_x86_64<size>::Relocatable_size_for_reloc::get_size_for_reloc(
- unsigned int r_type,
- Relobj* object)
+void
+Target_x86_64<size>::scan_relocatable_relocs(
+ Symbol_table* symtab,
+ Layout* layout,
+ Sized_relobj_file<size, false>* 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_symbols,
+ Relocatable_relocs* rr)
{
- switch (r_type)
- {
- case elfcpp::R_X86_64_NONE:
- case elfcpp::R_X86_64_GNU_VTINHERIT:
- case elfcpp::R_X86_64_GNU_VTENTRY:
- case elfcpp::R_X86_64_TLSGD: // Global-dynamic
- case elfcpp::R_X86_64_GOTPC32_TLSDESC: // Global-dynamic (from ~oliva url)
- case elfcpp::R_X86_64_TLSDESC_CALL:
- case elfcpp::R_X86_64_TLSLD: // Local-dynamic
- case elfcpp::R_X86_64_DTPOFF32:
- case elfcpp::R_X86_64_DTPOFF64:
- case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec
- case elfcpp::R_X86_64_TPOFF32: // Local-exec
- return 0;
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, false>
+ Classify_reloc;
+ typedef gold::Default_scan_relocatable_relocs<Classify_reloc>
+ Scan_relocatable_relocs;
- case elfcpp::R_X86_64_64:
- case elfcpp::R_X86_64_PC64:
- case elfcpp::R_X86_64_GOTOFF64:
- case elfcpp::R_X86_64_GOTPC64:
- case elfcpp::R_X86_64_PLTOFF64:
- case elfcpp::R_X86_64_GOT64:
- case elfcpp::R_X86_64_GOTPCREL64:
- case elfcpp::R_X86_64_GOTPCREL:
- case elfcpp::R_X86_64_GOTPLT64:
- return 8;
-
- case elfcpp::R_X86_64_32:
- case elfcpp::R_X86_64_32S:
- case elfcpp::R_X86_64_PC32:
- case elfcpp::R_X86_64_PLT32:
- case elfcpp::R_X86_64_GOTPC32:
- case elfcpp::R_X86_64_GOT32:
- return 4;
-
- case elfcpp::R_X86_64_16:
- case elfcpp::R_X86_64_PC16:
- return 2;
-
- case elfcpp::R_X86_64_8:
- case elfcpp::R_X86_64_PC8:
- return 1;
-
- case elfcpp::R_X86_64_COPY:
- case elfcpp::R_X86_64_GLOB_DAT:
- case elfcpp::R_X86_64_JUMP_SLOT:
- case elfcpp::R_X86_64_RELATIVE:
- case elfcpp::R_X86_64_IRELATIVE:
- // These are outstanding tls relocs, which are unexpected when linking
- case elfcpp::R_X86_64_TPOFF64:
- case elfcpp::R_X86_64_DTPMOD64:
- case elfcpp::R_X86_64_TLSDESC:
- object->error(_("unexpected reloc %u in object file"), r_type);
- return 0;
+ gold_assert(sh_type == elfcpp::SHT_RELA);
- case elfcpp::R_X86_64_SIZE32:
- case elfcpp::R_X86_64_SIZE64:
- default:
- object->error(_("unsupported reloc %u against local symbol"), r_type);
- return 0;
- }
+ gold::scan_relocatable_relocs<size, false, Scan_relocatable_relocs>(
+ symtab,
+ layout,
+ object,
+ data_shndx,
+ prelocs,
+ reloc_count,
+ output_section,
+ needs_special_offset_handling,
+ local_symbol_count,
+ plocal_symbols,
+ rr);
}
-// Scan the relocs during a relocatable link.
+// Scan the relocs for --emit-relocs.
template<int size>
void
-Target_x86_64<size>::scan_relocatable_relocs(
+Target_x86_64<size>::emit_relocs_scan(
Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, false>* object,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
- const unsigned char* plocal_symbols,
+ const unsigned char* plocal_syms,
Relocatable_relocs* rr)
{
- gold_assert(sh_type == elfcpp::SHT_RELA);
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, false>
+ Classify_reloc;
+ typedef gold::Default_emit_relocs_strategy<Classify_reloc>
+ Emit_relocs_strategy;
- typedef gold::Default_scan_relocatable_relocs<elfcpp::SHT_RELA,
- Relocatable_size_for_reloc> Scan_relocatable_relocs;
+ gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::scan_relocatable_relocs<size, false, elfcpp::SHT_RELA,
- Scan_relocatable_relocs>(
+ gold::scan_relocatable_relocs<size, false, Emit_relocs_strategy>(
symtab,
layout,
object,
output_section,
needs_special_offset_handling,
local_symbol_count,
- plocal_symbols,
+ plocal_syms,
rr);
}
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<size>::Elf_Off offset_in_output_section,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr view_address,
section_size_type view_size,
unsigned char* reloc_view,
section_size_type reloc_view_size)
{
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, false>
+ Classify_reloc;
+
gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::relocate_relocs<size, false, elfcpp::SHT_RELA>(
+ gold::relocate_relocs<size, false, Classify_reloc>(
relinfo,
prelocs,
reloc_count,
output_section,
offset_in_output_section,
- rr,
view,
view_address,
view_size,
// 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,
section_offset_type fnoffset,
section_size_type fnsize,
+ const unsigned char*,
+ size_t,
unsigned char* view,
section_size_type view_size,
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)
{
plt_count);
}
+ virtual std::string
+ do_code_fill(section_size_type length) const;
+
private:
static const Target::Target_info x86_64_nacl_info;
};
0, // small_common_section_flags
elfcpp::SHF_X86_64_LARGE, // large_common_section_flags
NULL, // attributes_section
- NULL // attributes_vendor
+ NULL, // attributes_vendor
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
0, // small_common_section_flags
elfcpp::SHF_X86_64_LARGE, // large_common_section_flags
NULL, // attributes_section
- NULL // attributes_vendor
+ NULL, // attributes_vendor
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
#define NACLMASK 0xe0 // 32-byte alignment mask.
0x41, 0xff, 0xe3, // jmpq *%r11
// 9-byte nop sequence to pad out to the next 32-byte boundary.
- 0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopl %cs:0x0(%rax,%rax,1)
+ 0x66, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0, // nopw 0x0(%rax,%rax,1)
// 32 bytes of nop to pad out to the standard size
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, // excess data32 prefixes
elfcpp::DW_CFA_nop
};
+// Return a string used to fill a code section with nops.
+// For NaCl, long NOPs are only valid if they do not cross
+// bundle alignment boundaries, so keep it simple with one-byte NOPs.
+template<int size>
+std::string
+Target_x86_64_nacl<size>::do_code_fill(section_size_type length) const
+{
+ return std::string(length, static_cast<char>(0x90));
+}
+
// The selector for x86_64-nacl object files.
template<int size>