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,
get_size_for_reloc(unsigned int, Relobj*);
};
+ // 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)
+ {
+ 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.
static tls::Tls_optimization
elfcpp::SHF_X86_64_LARGE, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
elfcpp::SHF_X86_64_LARGE, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
// This is called when a new output section is created. This is where
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;
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;
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.
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)
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;
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;
+ Relocate_functions<size, false>::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;
+ Relocate_functions<size, false>::pcrela32(view, value, addend, address);
+ }
}
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)
{
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);
}
case elfcpp::R_X86_64_GOT64:
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 8;
// 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
elfcpp::SHF_X86_64_LARGE, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
elfcpp::SHF_X86_64_LARGE, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
#define NACLMASK 0xe0 // 32-byte alignment mask.