// x86_64.cc -- x86_64 target support for gold.
-// Copyright (C) 2006-2017 Free Software Foundation, Inc.
+// Copyright (C) 2006-2018 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
virtual void
do_fill_first_plt_entry(unsigned char* pov,
- typename elfcpp::Elf_types<64>::Elf_Addr got_addr,
- typename elfcpp::Elf_types<64>::Elf_Addr plt_addr);
+ 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,
- typename elfcpp::Elf_types<64>::Elf_Addr got_address,
- typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
+ 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,
- typename elfcpp::Elf_types<64>::Elf_Addr got_address,
- typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
- typename elfcpp::Elf_types<64>::Elf_Addr got_base,
+ 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,
- typename elfcpp::Elf_types<64>::Elf_Addr got_address,
- typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
+ 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);
// and global_reloc_may_be_function_pointer)
// if a function's pointer is taken. ICF uses this in safe mode to only
// fold those functions whose pointer is defintely not taken. For x86_64
- // pie binaries, safe ICF cannot be done by looking at relocation types.
+ // pie binaries, safe ICF cannot be done by looking at only relocation
+ // types, and for certain cases (e.g. R_X86_64_PC32), the instruction
+ // opcode is checked as well to distinguish a function call from taking
+ // a function's pointer.
bool
do_can_check_for_function_pointers() const
- { return !parameters->options().pie(); }
+ { return true; }
// Return the base for a DW_EH_PE_datarel encoding.
uint64_t
check_non_pic(Relobj*, unsigned int r_type, Symbol*);
inline bool
- possible_function_pointer_reloc(unsigned int r_type);
+ possible_function_pointer_reloc(Sized_relobj_file<size, false>* src_obj,
+ unsigned int src_indx,
+ unsigned int r_offset,
+ unsigned int r_type);
bool
reloc_needs_plt_for_ifunc(Sized_relobj_file<size, false>*,
return false;
// We cannot convert references to IFUNC symbols, or to symbols that
// are not local to the current module.
+ // We can't do predefined symbols because they may become undefined
+ // (e.g., __ehdr_start when the headers aren't mapped to a segment).
if (gsym->type() == elfcpp::STT_GNU_IFUNC
- || gsym->is_undefined ()
+ || gsym->is_undefined()
+ || gsym->is_predefined()
|| gsym->is_from_dynobj()
|| gsym->is_preemptible())
return false;
NULL, // attributes_vendor
"_start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_X86_64_UNWIND, // unwind_section_type
};
template<>
NULL, // attributes_vendor
"_start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_X86_64_UNWIND, // unwind_section_type
};
// This is called when a new output section is created. This is where
void
Output_data_plt_x86_64_bnd::do_fill_first_plt_entry(
unsigned char* pov,
- typename elfcpp::Elf_types<64>::Elf_Addr got_address,
- typename elfcpp::Elf_types<64>::Elf_Addr plt_address)
+ elfcpp::Elf_types<64>::Elf_Addr got_address,
+ elfcpp::Elf_types<64>::Elf_Addr plt_address)
{
memcpy(pov, first_plt_entry, plt_entry_size);
// We do a jmp relative to the PC at the end of this instruction.
unsigned int
Output_data_plt_x86_64_bnd::do_fill_plt_entry(
unsigned char* pov,
- typename elfcpp::Elf_types<64>::Elf_Addr,
- typename elfcpp::Elf_types<64>::Elf_Addr,
+ elfcpp::Elf_types<64>::Elf_Addr,
+ elfcpp::Elf_types<64>::Elf_Addr,
unsigned int,
unsigned int plt_offset,
unsigned int plt_index)
void
Output_data_plt_x86_64_bnd::fill_aplt_entry(
unsigned char* pov,
- typename elfcpp::Elf_types<64>::Elf_Addr got_address,
- typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
+ 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)
void
Output_data_plt_x86_64_bnd::do_fill_tlsdesc_entry(
unsigned char* pov,
- typename elfcpp::Elf_types<64>::Elf_Addr got_address,
- typename elfcpp::Elf_types<64>::Elf_Addr plt_address,
- typename elfcpp::Elf_types<64>::Elf_Addr got_base,
+ 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)
{
unsigned char* pov = oview;
// The base address of the .plt section.
- typename elfcpp::Elf_types<64>::Elf_Addr plt_address = this->address();
+ elfcpp::Elf_types<64>::Elf_Addr plt_address = this->address();
// The base address of the .got section.
- typename elfcpp::Elf_types<64>::Elf_Addr got_base = got->address();
+ elfcpp::Elf_types<64>::Elf_Addr got_base = got->address();
// The base address of the PLT portion of the .got section,
// which is where the GOT pointer will point, and where the
// three reserved GOT entries are located.
- typename elfcpp::Elf_types<64>::Elf_Addr got_address = got_plt->address();
+ elfcpp::Elf_types<64>::Elf_Addr got_address = got_plt->address();
this->fill_first_plt_entry(pov, got_address, plt_address);
pov += plt_entry_size;
// 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)
+ if (!parameters->incremental()
+ && (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)
{
break;
}
-
// The symbol requires a GOT entry.
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
// Returns true if this relocation type could be that of a function pointer.
template<int size>
inline bool
-Target_x86_64<size>::Scan::possible_function_pointer_reloc(unsigned int r_type)
+Target_x86_64<size>::Scan::possible_function_pointer_reloc(
+ Sized_relobj_file<size, false>* src_obj,
+ unsigned int src_indx,
+ unsigned int r_offset,
+ unsigned int r_type)
{
switch (r_type)
{
{
return true;
}
+ case elfcpp::R_X86_64_PC32:
+ {
+ // This relocation may be used both for function calls and
+ // for taking address of a function. We distinguish between
+ // them by checking the opcodes.
+ uint64_t sh_flags = src_obj->section_flags(src_indx);
+ bool is_executable = (sh_flags & elfcpp::SHF_EXECINSTR) != 0;
+ if (is_executable)
+ {
+ section_size_type stype;
+ const unsigned char* view = src_obj->section_contents(src_indx,
+ &stype,
+ true);
+
+ // call
+ if (r_offset >= 1
+ && view[r_offset - 1] == 0xe8)
+ return false;
+
+ // jmp
+ if (r_offset >= 1
+ && view[r_offset - 1] == 0xe9)
+ return false;
+
+ // jo/jno/jb/jnb/je/jne/jna/ja/js/jns/jp/jnp/jl/jge/jle/jg
+ if (r_offset >= 2
+ && view[r_offset - 2] == 0x0f
+ && view[r_offset - 1] >= 0x80
+ && view[r_offset - 1] <= 0x8f)
+ return false;
+ }
+
+ // Be conservative and treat all others as function pointers.
+ return true;
+ }
}
return false;
}
Symbol_table* ,
Layout* ,
Target_x86_64<size>* ,
- Sized_relobj_file<size, false>* ,
- unsigned int ,
+ Sized_relobj_file<size, false>* src_obj,
+ unsigned int src_indx,
Output_section* ,
- const elfcpp::Rela<size, false>& ,
+ const elfcpp::Rela<size, false>& reloc,
unsigned int r_type,
const elfcpp::Sym<size, false>&)
{
// When building a shared library, do not fold any local symbols as it is
// not possible to distinguish pointer taken versus a call by looking at
// the relocation types.
- return (parameters->options().shared()
- || possible_function_pointer_reloc(r_type));
+ if (parameters->options().shared())
+ return true;
+
+ return possible_function_pointer_reloc(src_obj, src_indx,
+ reloc.get_r_offset(), r_type);
}
// For safe ICF, scan a relocation for a global symbol to check if it
Symbol_table*,
Layout* ,
Target_x86_64<size>* ,
- Sized_relobj_file<size, false>* ,
- unsigned int ,
+ Sized_relobj_file<size, false>* src_obj,
+ unsigned int src_indx,
Output_section* ,
- const elfcpp::Rela<size, false>& ,
+ const elfcpp::Rela<size, false>& reloc,
unsigned int r_type,
Symbol* gsym)
{
// When building a shared library, do not fold symbols whose visibility
// is hidden, internal or protected.
- return ((parameters->options().shared()
- && (gsym->visibility() == elfcpp::STV_INTERNAL
- || gsym->visibility() == elfcpp::STV_PROTECTED
- || gsym->visibility() == elfcpp::STV_HIDDEN))
- || possible_function_pointer_reloc(r_type));
+ if (parameters->options().shared()
+ && (gsym->visibility() == elfcpp::STV_INTERNAL
+ || gsym->visibility() == elfcpp::STV_PROTECTED
+ || gsym->visibility() == elfcpp::STV_HIDDEN))
+ return true;
+
+ return possible_function_pointer_reloc(src_obj, src_indx,
+ reloc.get_r_offset(), r_type);
}
// Scan a relocation for a global symbol.
// (callq|jmpq) *foo@GOTPCRELX(%rip) to
// (callq|jmpq) foo
// in Relocate::relocate, then there is nothing to do here.
+ // We cannot make these optimizations in incremental linking mode,
+ // because we look at the opcode to decide whether or not to make
+ // change, and during an incremental update, the change may have
+ // already been applied.
Lazy_view<size> view(object, data_shndx);
size_t r_offset = reloc.get_r_offset();
- if (r_offset >= 2
+ if (!parameters->incremental()
+ && r_offset >= 2
&& Target_x86_64<size>::can_convert_mov_to_lea(gsym, r_type,
r_offset, &view))
break;
- if (r_offset >= 2
+ if (!parameters->incremental()
+ && r_offset >= 2
&& Target_x86_64<size>::can_convert_callq_to_direct(gsym, r_type,
r_offset,
&view))
case elfcpp::R_X86_64_GOT64:
case elfcpp::R_X86_64_GOTPLT64:
- // R_X86_64_GOTPLT64 is obsolete and treated the the same as
+ // R_X86_64_GOTPLT64 is obsolete and treated the same as
// GOT64.
gold_assert(have_got_offset);
Reloc_funcs::rela64(view, got_offset, addend);
// mov foo@GOTPCREL(%rip), %reg
// to lea foo(%rip), %reg.
// if possible.
- if ((gsym == NULL
- && rela.get_r_offset() >= 2
- && view[-2] == 0x8b
- && !psymval->is_ifunc_symbol())
- || (gsym != NULL
- && rela.get_r_offset() >= 2
- && Target_x86_64<size>::can_convert_mov_to_lea(gsym, r_type,
- 0, &view)))
+ if (!parameters->incremental()
+ && ((gsym == NULL
+ && rela.get_r_offset() >= 2
+ && view[-2] == 0x8b
+ && !psymval->is_ifunc_symbol())
+ || (gsym != NULL
+ && rela.get_r_offset() >= 2
+ && Target_x86_64<size>::can_convert_mov_to_lea(gsym, r_type,
+ 0, &view))))
{
view[-2] = 0x8d;
Reloc_funcs::pcrela32(view, object, psymval, addend, address);
// and jmpq *foo@GOTPCRELX(%rip) to
// jmpq foo
// nop
- else if (gsym != NULL
+ else if (!parameters->incremental()
+ && gsym != NULL
&& rela.get_r_offset() >= 2
&& Target_x86_64<size>::can_convert_callq_to_direct(gsym,
r_type,
if (gsym != NULL)
{
gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD));
- got_offset = gsym->got_offset(GOT_TYPE_STANDARD) - target->got_size();
+ 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));
+ 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());
}
NULL, // attributes_vendor
"_start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_X86_64_UNWIND, // unwind_section_type
};
template<>
NULL, // attributes_vendor
"_start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_X86_64_UNWIND, // unwind_section_type
};
#define NACLMASK 0xe0 // 32-byte alignment mask.