#include "gold.h"
#include <cstring>
#include "elfcpp.h"
#include "parameters.h"
#include "reloc.h"
#include "x86_64.h"
#include "object.h"
#include "symtab.h"
#include "layout.h"
#include "output.h"
#include "copy-relocs.h"
#include "target.h"
#include "target-reloc.h"
#include "target-select.h"
#include "tls.h"
namespace
{
using namespace gold;
class Output_data_plt_x86_64;
class Target_x86_64 : public Sized_target<64, false>
{
public:
typedef Output_data_reloc<elfcpp::SHT_RELA, true, 64, false> Reloc_section;
Target_x86_64()
: Sized_target<64, false>(&x86_64_info),
got_(NULL), plt_(NULL), got_plt_(NULL), rela_dyn_(NULL),
copy_relocs_(elfcpp::R_X86_64_COPY), dynbss_(NULL),
got_mod_index_offset_(-1U), tls_base_symbol_defined_(false)
{ }
void
scan_relocs(const General_options& options,
Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, 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);
void
do_finalize_sections(Layout*);
uint64_t
do_dynsym_value(const Symbol*) const;
void
relocate_section(const Relocate_info<64, false>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr view_address,
section_size_type view_size);
void
scan_relocatable_relocs(const General_options& options,
Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, 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*);
void
relocate_for_relocatable(const Relocate_info<64, false>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
off_t offset_in_output_section,
const Relocatable_relocs*,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr view_address,
section_size_type view_size,
unsigned char* reloc_view,
section_size_type reloc_view_size);
std::string
do_code_fill(section_size_type length) const;
bool
do_is_defined_by_abi(Symbol* sym) const
{ return strcmp(sym->name(), "__tls_get_addr") == 0; }
section_size_type
got_size()
{
gold_assert(this->got_ != NULL);
return this->got_->data_size();
}
private:
class Scan
{
public:
Scan()
: issued_non_pic_error_(false)
{ }
inline void
local(const General_options& options, Symbol_table* symtab,
Layout* layout, Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<64, false>& reloc, unsigned int r_type,
const elfcpp::Sym<64, false>& lsym);
inline void
global(const General_options& options, Symbol_table* symtab,
Layout* layout, Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<64, false>& reloc, unsigned int r_type,
Symbol* gsym);
private:
static void
unsupported_reloc_local(Sized_relobj<64, false>*, unsigned int r_type);
static void
unsupported_reloc_global(Sized_relobj<64, false>*, unsigned int r_type,
Symbol*);
void
check_non_pic(Relobj*, unsigned int r_type);
bool issued_non_pic_error_;
};
class Relocate
{
public:
Relocate()
: skip_call_tls_get_addr_(false), saw_tls_block_reloc_(false)
{ }
~Relocate()
{
if (this->skip_call_tls_get_addr_)
{
gold_error(_("missing expected TLS relocation"));
}
}
inline bool
relocate(const Relocate_info<64, false>*, Target_x86_64*, size_t relnum,
const elfcpp::Rela<64, false>&,
unsigned int r_type, const Sized_symbol<64>*,
const Symbol_value<64>*,
unsigned char*, elfcpp::Elf_types<64>::Elf_Addr,
section_size_type);
private:
inline void
relocate_tls(const Relocate_info<64, false>*, Target_x86_64*,
size_t relnum, const elfcpp::Rela<64, false>&,
unsigned int r_type, const Sized_symbol<64>*,
const Symbol_value<64>*,
unsigned char*, elfcpp::Elf_types<64>::Elf_Addr,
section_size_type);
inline void
tls_gd_to_ie(const Relocate_info<64, false>*, size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rela<64, false>&, unsigned int r_type,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr,
section_size_type view_size);
inline void
tls_gd_to_le(const Relocate_info<64, false>*, size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rela<64, false>&, unsigned int r_type,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
section_size_type view_size);
inline void
tls_desc_gd_to_ie(const Relocate_info<64, false>*, size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rela<64, false>&, unsigned int r_type,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr,
section_size_type view_size);
inline void
tls_desc_gd_to_le(const Relocate_info<64, false>*, size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rela<64, false>&, unsigned int r_type,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
section_size_type view_size);
inline void
tls_ld_to_le(const Relocate_info<64, false>*, size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rela<64, false>&, unsigned int r_type,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
section_size_type view_size);
static inline void
tls_ie_to_le(const Relocate_info<64, false>*, size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rela<64, false>&, unsigned int r_type,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
section_size_type view_size);
bool skip_call_tls_get_addr_;
bool saw_tls_block_reloc_;
};
class Relocatable_size_for_reloc
{
public:
unsigned int
get_size_for_reloc(unsigned int, Relobj*);
};
static tls::Tls_optimization
optimize_tls_reloc(bool is_final, int r_type);
Output_data_got<64, false>*
got_section(Symbol_table*, Layout*);
Output_data_space*
got_plt_section() const
{
gold_assert(this->got_plt_ != NULL);
return this->got_plt_;
}
void
make_plt_section(Symbol_table* symtab, Layout* layout);
void
make_plt_entry(Symbol_table*, Layout*, Symbol*);
void
define_tls_base_symbol(Symbol_table*, Layout*);
void
reserve_tlsdesc_entries(Symbol_table* symtab, Layout* layout);
unsigned int
got_mod_index_entry(Symbol_table* symtab, Layout* layout,
Sized_relobj<64, false>* object);
Output_data_plt_x86_64*
plt_section() const
{
gold_assert(this->plt_ != NULL);
return this->plt_;
}
Reloc_section*
rela_dyn_section(Layout*);
bool
may_need_copy_reloc(Symbol* gsym)
{
return (!parameters->options().shared()
&& gsym->is_from_dynobj()
&& gsym->type() != elfcpp::STT_FUNC);
}
void
copy_reloc(Symbol_table* symtab, Layout* layout,
Sized_relobj<64, false>* object,
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rela<64, false>& reloc)
{
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<64>(sym),
object, shndx, output_section,
reloc, this->rela_dyn_section(layout));
}
static const Target::Target_info x86_64_info;
enum Got_type
{
GOT_TYPE_STANDARD = 0,
GOT_TYPE_TLS_OFFSET = 1,
GOT_TYPE_TLS_PAIR = 2,
GOT_TYPE_TLS_DESC = 3
};
Output_data_got<64, false>* got_;
Output_data_plt_x86_64* plt_;
Output_data_space* got_plt_;
Reloc_section* rela_dyn_;
Copy_relocs<elfcpp::SHT_RELA, 64, false> copy_relocs_;
Output_data_space* dynbss_;
unsigned int got_mod_index_offset_;
bool tls_base_symbol_defined_;
};
const Target::Target_info Target_x86_64::x86_64_info =
{
64,
false,
elfcpp::EM_X86_64,
false,
false,
true,
true,
'\0',
"/lib/ld64.so.1",
0x400000,
0x1000,
0x1000
};
Output_data_got<64, false>*
Target_x86_64::got_section(Symbol_table* symtab, Layout* layout)
{
if (this->got_ == NULL)
{
gold_assert(symtab != NULL && layout != NULL);
this->got_ = new Output_data_got<64, false>();
Output_section* os;
os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
this->got_);
os->set_is_relro();
this->got_plt_ = new Output_data_space(8, "** GOT PLT");
os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
this->got_plt_);
os->set_is_relro();
this->got_plt_->set_current_data_size(3 * 8);
symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
this->got_plt_,
0, 0, elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
false, false);
}
return this->got_;
}
Target_x86_64::Reloc_section*
Target_x86_64::rela_dyn_section(Layout* layout)
{
if (this->rela_dyn_ == NULL)
{
gold_assert(layout != NULL);
this->rela_dyn_ = new Reloc_section(parameters->options().combreloc());
layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
elfcpp::SHF_ALLOC, this->rela_dyn_);
}
return this->rela_dyn_;
}
class Output_data_plt_x86_64 : public Output_section_data
{
public:
typedef Output_data_reloc<elfcpp::SHT_RELA, true, 64, false> Reloc_section;
Output_data_plt_x86_64(Layout*, Output_data_got<64, false>*,
Output_data_space*);
void
add_entry(Symbol* gsym);
void
reserve_tlsdesc_entry(unsigned int got_offset)
{ this->tlsdesc_got_offset_ = got_offset; }
bool
has_tlsdesc_entry() const
{ return this->tlsdesc_got_offset_ != -1U; }
unsigned int
get_tlsdesc_got_offset() const
{ return this->tlsdesc_got_offset_; }
unsigned int
get_tlsdesc_plt_offset() const
{ return (this->count_ + 1) * plt_entry_size; }
const Reloc_section*
rel_plt() const
{ return this->rel_; }
protected:
void
do_adjust_output_section(Output_section* os);
void
do_print_to_mapfile(Mapfile* mapfile) const
{ mapfile->print_output_data(this, _("** PLT")); }
private:
static const int plt_entry_size = 16;
static unsigned char first_plt_entry[plt_entry_size];
static unsigned char plt_entry[plt_entry_size];
static unsigned char tlsdesc_plt_entry[plt_entry_size];
void
set_final_data_size();
void
do_write(Output_file*);
Reloc_section* rel_;
Output_data_got<64, false>* got_;
Output_data_space* got_plt_;
unsigned int count_;
unsigned int tlsdesc_got_offset_;
};
Output_data_plt_x86_64::Output_data_plt_x86_64(Layout* layout,
Output_data_got<64, false>* got,
Output_data_space* got_plt)
: Output_section_data(8), got_(got), got_plt_(got_plt), count_(0),
tlsdesc_got_offset_(-1U)
{
this->rel_ = new Reloc_section(false);
layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
elfcpp::SHF_ALLOC, this->rel_);
}
void
Output_data_plt_x86_64::do_adjust_output_section(Output_section* os)
{
os->set_entsize(4);
}
void
Output_data_plt_x86_64::add_entry(Symbol* gsym)
{
gold_assert(!gsym->has_plt_offset());
gsym->set_plt_offset((this->count_ + 1) * plt_entry_size);
++this->count_;
section_offset_type got_offset = this->got_plt_->current_data_size();
this->got_plt_->set_current_data_size(got_offset + 8);
gsym->set_needs_dynsym_entry();
this->rel_->add_global(gsym, elfcpp::R_X86_64_JUMP_SLOT, this->got_plt_,
got_offset, 0);
}
void
Output_data_plt_x86_64::set_final_data_size()
{
unsigned int count = this->count_;
if (this->has_tlsdesc_entry())
++count;
this->set_data_size((count + 1) * plt_entry_size);
}
unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] =
{
0xff, 0x35,
0, 0, 0, 0,
0xff, 0x25,
0, 0, 0, 0,
0x90, 0x90, 0x90, 0x90
};
unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] =
{
0xff, 0x25,
0, 0, 0, 0,
0x68,
0, 0, 0, 0,
0xe9,
0, 0, 0, 0
};
unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] =
{
0xff, 0x35,
0, 0, 0, 0,
0xff, 0x25,
0, 0, 0, 0,
0x0f, 0x1f,
0x40, 0
};
void
Output_data_plt_x86_64::do_write(Output_file* of)
{
const off_t offset = this->offset();
const section_size_type oview_size =
convert_to_section_size_type(this->data_size());
unsigned char* const oview = of->get_output_view(offset, oview_size);
const off_t got_file_offset = this->got_plt_->offset();
const section_size_type got_size =
convert_to_section_size_type(this->got_plt_->data_size());
unsigned char* const got_view = of->get_output_view(got_file_offset,
got_size);
unsigned char* pov = oview;
elfcpp::Elf_types<64>::Elf_Addr plt_address = this->address();
elfcpp::Elf_types<64>::Elf_Addr got_base = this->got_->address();
elfcpp::Elf_types<64>::Elf_Addr got_address = this->got_plt_->address();
memcpy(pov, first_plt_entry, plt_entry_size);
elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
(got_address + 8
- (plt_address + 6)));
elfcpp::Swap<32, false>::writeval(pov + 8,
(got_address + 16
- (plt_address + 12)));
pov += plt_entry_size;
unsigned char* got_pov = got_view;
memset(got_pov, 0, 24);
got_pov += 24;
unsigned int plt_offset = plt_entry_size;
unsigned int got_offset = 24;
const unsigned int count = this->count_;
for (unsigned int plt_index = 0;
plt_index < count;
++plt_index,
pov += plt_entry_size,
got_pov += 8,
plt_offset += plt_entry_size,
got_offset += 8)
{
memcpy(pov, plt_entry, plt_entry_size);
elfcpp::Swap_unaligned<32, false>::writeval(pov + 2,
(got_address + got_offset
- (plt_address + plt_offset
+ 6)));
elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_index);
elfcpp::Swap<32, false>::writeval(pov + 12,
- (plt_offset + plt_entry_size));
elfcpp::Swap<64, false>::writeval(got_pov, plt_address + plt_offset + 6);
}
if (this->has_tlsdesc_entry())
{
unsigned int tlsdesc_got_offset = this->get_tlsdesc_got_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 + 8,
(got_base
+ tlsdesc_got_offset
- (plt_address + plt_offset
+ 12)));
pov += plt_entry_size;
}
gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
gold_assert(static_cast<section_size_type>(got_pov - got_view) == got_size);
of->write_output_view(offset, oview_size, oview);
of->write_output_view(got_file_offset, got_size, got_view);
}
void
Target_x86_64::make_plt_section(Symbol_table* symtab, Layout* layout)
{
if (this->plt_ == NULL)
{
this->got_section(symtab, layout);
this->plt_ = new Output_data_plt_x86_64(layout, this->got_,
this->got_plt_);
layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_EXECINSTR),
this->plt_);
}
}
void
Target_x86_64::make_plt_entry(Symbol_table* symtab, Layout* layout,
Symbol* gsym)
{
if (gsym->has_plt_offset())
return;
if (this->plt_ == NULL)
this->make_plt_section(symtab, layout);
this->plt_->add_entry(gsym);
}
void
Target_x86_64::define_tls_base_symbol(Symbol_table* symtab, Layout* layout)
{
if (this->tls_base_symbol_defined_)
return;
Output_segment* tls_segment = layout->tls_segment();
if (tls_segment != NULL)
{
symtab->define_in_output_segment("_TLS_MODULE_BASE_", NULL,
tls_segment, 0, 0,
elfcpp::STT_TLS,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
Symbol::SEGMENT_END, true);
}
this->tls_base_symbol_defined_ = true;
}
void
Target_x86_64::reserve_tlsdesc_entries(Symbol_table* symtab,
Layout* layout)
{
if (this->plt_ == NULL)
this->make_plt_section(symtab, layout);
if (!this->plt_->has_tlsdesc_entry())
{
Output_data_got<64, false>* got = this->got_section(symtab, layout);
unsigned int got_offset = got->add_constant(0);
this->plt_->reserve_tlsdesc_entry(got_offset);
}
}
unsigned int
Target_x86_64::got_mod_index_entry(Symbol_table* symtab, Layout* layout,
Sized_relobj<64, false>* object)
{
if (this->got_mod_index_offset_ == -1U)
{
gold_assert(symtab != NULL && layout != NULL && object != NULL);
Reloc_section* rela_dyn = this->rela_dyn_section(layout);
Output_data_got<64, false>* got = this->got_section(symtab, layout);
unsigned int got_offset = got->add_constant(0);
rela_dyn->add_local(object, 0, elfcpp::R_X86_64_DTPMOD64, got,
got_offset, 0);
got->add_constant(0);
this->got_mod_index_offset_ = got_offset;
}
return this->got_mod_index_offset_;
}
tls::Tls_optimization
Target_x86_64::optimize_tls_reloc(bool is_final, int r_type)
{
if (parameters->options().shared())
return tls::TLSOPT_NONE;
switch (r_type)
{
case elfcpp::R_X86_64_TLSGD:
case elfcpp::R_X86_64_GOTPC32_TLSDESC:
case elfcpp::R_X86_64_TLSDESC_CALL:
if (is_final)
return tls::TLSOPT_TO_LE;
return tls::TLSOPT_TO_IE;
case elfcpp::R_X86_64_TLSLD:
return tls::TLSOPT_TO_LE;
case elfcpp::R_X86_64_DTPOFF32:
case elfcpp::R_X86_64_DTPOFF64:
return tls::TLSOPT_TO_LE;
case elfcpp::R_X86_64_GOTTPOFF:
if (is_final)
return tls::TLSOPT_TO_LE;
return tls::TLSOPT_NONE;
case elfcpp::R_X86_64_TPOFF32:
return tls::TLSOPT_NONE;
default:
gold_unreachable();
}
}
void
Target_x86_64::Scan::unsupported_reloc_local(Sized_relobj<64, false>* object,
unsigned int r_type)
{
gold_error(_("%s: unsupported reloc %u against local symbol"),
object->name().c_str(), r_type);
}
void
Target_x86_64::Scan::check_non_pic(Relobj* object, unsigned int r_type)
{
switch (r_type)
{
case elfcpp::R_X86_64_RELATIVE:
case elfcpp::R_X86_64_GLOB_DAT:
case elfcpp::R_X86_64_JUMP_SLOT:
case elfcpp::R_X86_64_DTPMOD64:
case elfcpp::R_X86_64_DTPOFF64:
case elfcpp::R_X86_64_TPOFF64:
case elfcpp::R_X86_64_64:
case elfcpp::R_X86_64_32:
case elfcpp::R_X86_64_PC32:
case elfcpp::R_X86_64_COPY:
return;
default:
if (this->issued_non_pic_error_)
return;
object->error(_("requires unsupported dynamic reloc; "
"recompile with -fPIC"));
this->issued_non_pic_error_ = true;
return;
case elfcpp::R_X86_64_NONE:
gold_unreachable();
}
}
inline void
Target_x86_64::Scan::local(const General_options&,
Symbol_table* symtab,
Layout* layout,
Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<64, false>& reloc,
unsigned int r_type,
const elfcpp::Sym<64, false>& lsym)
{
switch (r_type)
{
case elfcpp::R_X86_64_NONE:
case elfcpp::R_386_GNU_VTINHERIT:
case elfcpp::R_386_GNU_VTENTRY:
break;
case elfcpp::R_X86_64_64:
if (parameters->options().output_is_position_independent())
{
unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_local_relative(object, r_sym,
elfcpp::R_X86_64_RELATIVE,
output_section, data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend());
}
break;
case elfcpp::R_X86_64_32:
case elfcpp::R_X86_64_32S:
case elfcpp::R_X86_64_16:
case elfcpp::R_X86_64_8:
if (parameters->options().output_is_position_independent())
{
this->check_non_pic(object, r_type);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
if (lsym.get_st_type() != elfcpp::STT_SECTION)
rela_dyn->add_local(object, r_sym, r_type, output_section,
data_shndx, reloc.get_r_offset(),
reloc.get_r_addend());
else
{
gold_assert(lsym.get_st_value() == 0);
unsigned int shndx = lsym.get_st_shndx();
bool is_ordinary;
shndx = object->adjust_sym_shndx(r_sym, shndx,
&is_ordinary);
if (!is_ordinary)
object->error(_("section symbol %u has bad shndx %u"),
r_sym, shndx);
else
rela_dyn->add_local_section(object, shndx,
r_type, output_section,
data_shndx, reloc.get_r_offset(),
reloc.get_r_addend());
}
}
break;
case elfcpp::R_X86_64_PC64:
case elfcpp::R_X86_64_PC32:
case elfcpp::R_X86_64_PC16:
case elfcpp::R_X86_64_PC8:
break;
case elfcpp::R_X86_64_PLT32:
break;
case elfcpp::R_X86_64_GOTPC32:
case elfcpp::R_X86_64_GOTOFF64:
case elfcpp::R_X86_64_GOTPC64:
case elfcpp::R_X86_64_PLTOFF64:
target->got_section(symtab, layout);
break;
case elfcpp::R_X86_64_GOT64:
case elfcpp::R_X86_64_GOT32:
case elfcpp::R_X86_64_GOTPCREL64:
case elfcpp::R_X86_64_GOTPCREL:
case elfcpp::R_X86_64_GOTPLT64:
{
Output_data_got<64, false>* got = target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
if (got->add_local(object, r_sym, GOT_TYPE_STANDARD))
{
if (parameters->options().output_is_position_independent())
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
if (r_type != elfcpp::R_X86_64_GOT32)
rela_dyn->add_local_relative(
object, r_sym, elfcpp::R_X86_64_RELATIVE, got,
object->local_got_offset(r_sym, GOT_TYPE_STANDARD), 0);
else
{
this->check_non_pic(object, r_type);
gold_assert(lsym.get_st_type() != elfcpp::STT_SECTION);
rela_dyn->add_local(
object, r_sym, r_type, got,
object->local_got_offset(r_sym, GOT_TYPE_STANDARD), 0);
}
}
}
}
break;
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_TPOFF64:
case elfcpp::R_X86_64_DTPMOD64:
case elfcpp::R_X86_64_TLSDESC:
gold_error(_("%s: unexpected reloc %u in object file"),
object->name().c_str(), r_type);
break;
case elfcpp::R_X86_64_TLSGD:
case elfcpp::R_X86_64_GOTPC32_TLSDESC:
case elfcpp::R_X86_64_TLSDESC_CALL:
case elfcpp::R_X86_64_TLSLD:
case elfcpp::R_X86_64_DTPOFF32:
case elfcpp::R_X86_64_DTPOFF64:
case elfcpp::R_X86_64_GOTTPOFF:
case elfcpp::R_X86_64_TPOFF32:
{
bool output_is_shared = parameters->options().shared();
const tls::Tls_optimization optimized_type
= Target_x86_64::optimize_tls_reloc(!output_is_shared, r_type);
switch (r_type)
{
case elfcpp::R_X86_64_TLSGD:
if (optimized_type == tls::TLSOPT_NONE)
{
Output_data_got<64, false>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
unsigned int shndx = lsym.get_st_shndx();
bool is_ordinary;
shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
if (!is_ordinary)
object->error(_("local symbol %u has bad shndx %u"),
r_sym, shndx);
else
got->add_local_pair_with_rela(object, r_sym,
shndx,
GOT_TYPE_TLS_PAIR,
target->rela_dyn_section(layout),
elfcpp::R_X86_64_DTPMOD64, 0);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_X86_64_GOTPC32_TLSDESC:
target->define_tls_base_symbol(symtab, layout);
if (optimized_type == tls::TLSOPT_NONE)
{
target->reserve_tlsdesc_entries(symtab, layout);
Output_data_got<64, false>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
unsigned int shndx = lsym.get_st_shndx();
bool is_ordinary;
shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
if (!is_ordinary)
object->error(_("local symbol %u has bad shndx %u"),
r_sym, shndx);
else
got->add_local_pair_with_rela(object, r_sym,
shndx,
GOT_TYPE_TLS_DESC,
target->rela_dyn_section(layout),
elfcpp::R_X86_64_TLSDESC, 0);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_X86_64_TLSDESC_CALL:
break;
case elfcpp::R_X86_64_TLSLD:
if (optimized_type == tls::TLSOPT_NONE)
{
target->got_mod_index_entry(symtab, layout, object);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_X86_64_DTPOFF32:
case elfcpp::R_X86_64_DTPOFF64:
break;
case elfcpp::R_X86_64_GOTTPOFF:
layout->set_has_static_tls();
if (optimized_type == tls::TLSOPT_NONE)
{
Output_data_got<64, false>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info());
got->add_local_with_rela(object, r_sym, GOT_TYPE_TLS_OFFSET,
target->rela_dyn_section(layout),
elfcpp::R_X86_64_TPOFF64);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
break;
case elfcpp::R_X86_64_TPOFF32:
layout->set_has_static_tls();
if (output_is_shared)
unsupported_reloc_local(object, r_type);
break;
default:
gold_unreachable();
}
}
break;
case elfcpp::R_X86_64_SIZE32:
case elfcpp::R_X86_64_SIZE64:
default:
gold_error(_("%s: unsupported reloc %u against local symbol"),
object->name().c_str(), r_type);
break;
}
}
void
Target_x86_64::Scan::unsupported_reloc_global(Sized_relobj<64, false>* object,
unsigned int r_type,
Symbol* gsym)
{
gold_error(_("%s: unsupported reloc %u against global symbol %s"),
object->name().c_str(), r_type, gsym->demangled_name().c_str());
}
inline void
Target_x86_64::Scan::global(const General_options&,
Symbol_table* symtab,
Layout* layout,
Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<64, false>& reloc,
unsigned int r_type,
Symbol* gsym)
{
switch (r_type)
{
case elfcpp::R_X86_64_NONE:
case elfcpp::R_386_GNU_VTINHERIT:
case elfcpp::R_386_GNU_VTENTRY:
break;
case elfcpp::R_X86_64_64:
case elfcpp::R_X86_64_32:
case elfcpp::R_X86_64_32S:
case elfcpp::R_X86_64_16:
case elfcpp::R_X86_64_8:
{
if (gsym->needs_plt_entry())
{
target->make_plt_entry(symtab, layout, gsym);
if (gsym->is_from_dynobj() && !parameters->options().shared())
gsym->set_needs_dynsym_value();
}
if (gsym->needs_dynamic_reloc(Symbol::ABSOLUTE_REF))
{
if (target->may_need_copy_reloc(gsym))
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
}
else if (r_type == elfcpp::R_X86_64_64
&& gsym->can_use_relative_reloc(false))
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_global_relative(gsym, elfcpp::R_X86_64_RELATIVE,
output_section, object,
data_shndx, reloc.get_r_offset(),
reloc.get_r_addend());
}
else
{
this->check_non_pic(object, r_type);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_global(gsym, r_type, output_section, object,
data_shndx, reloc.get_r_offset(),
reloc.get_r_addend());
}
}
}
break;
case elfcpp::R_X86_64_PC64:
case elfcpp::R_X86_64_PC32:
case elfcpp::R_X86_64_PC16:
case elfcpp::R_X86_64_PC8:
{
if (gsym->needs_plt_entry())
target->make_plt_entry(symtab, layout, gsym);
int flags = Symbol::NON_PIC_REF;
if (gsym->type() == elfcpp::STT_FUNC)
flags |= Symbol::FUNCTION_CALL;
if (gsym->needs_dynamic_reloc(flags))
{
if (target->may_need_copy_reloc(gsym))
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
}
else
{
this->check_non_pic(object, r_type);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_global(gsym, r_type, output_section, object,
data_shndx, reloc.get_r_offset(),
reloc.get_r_addend());
}
}
}
break;
case elfcpp::R_X86_64_GOT64:
case elfcpp::R_X86_64_GOT32:
case elfcpp::R_X86_64_GOTPCREL64:
case elfcpp::R_X86_64_GOTPCREL:
case elfcpp::R_X86_64_GOTPLT64:
{
Output_data_got<64, false>* got = target->got_section(symtab, layout);
if (gsym->final_value_is_known())
got->add_global(gsym, GOT_TYPE_STANDARD);
else
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
if (gsym->is_from_dynobj()
|| gsym->is_undefined()
|| gsym->is_preemptible())
got->add_global_with_rela(gsym, GOT_TYPE_STANDARD, rela_dyn,
elfcpp::R_X86_64_GLOB_DAT);
else
{
if (got->add_global(gsym, GOT_TYPE_STANDARD))
rela_dyn->add_global_relative(
gsym, elfcpp::R_X86_64_RELATIVE, got,
gsym->got_offset(GOT_TYPE_STANDARD), 0);
}
}
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:
if (gsym->final_value_is_known())
break;
if (gsym->is_defined()
&& !gsym->is_from_dynobj()
&& !gsym->is_preemptible())
break;
target->make_plt_entry(symtab, layout, gsym);
break;
case elfcpp::R_X86_64_GOTPC32:
case elfcpp::R_X86_64_GOTOFF64:
case elfcpp::R_X86_64_GOTPC64:
case elfcpp::R_X86_64_PLTOFF64:
target->got_section(symtab, layout);
if (r_type == elfcpp::R_X86_64_PLTOFF64
&& !gsym->final_value_is_known())
target->make_plt_entry(symtab, layout, gsym);
break;
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_TPOFF64:
case elfcpp::R_X86_64_DTPMOD64:
case elfcpp::R_X86_64_TLSDESC:
gold_error(_("%s: unexpected reloc %u in object file"),
object->name().c_str(), r_type);
break;
case elfcpp::R_X86_64_TLSGD:
case elfcpp::R_X86_64_GOTPC32_TLSDESC:
case elfcpp::R_X86_64_TLSDESC_CALL:
case elfcpp::R_X86_64_TLSLD:
case elfcpp::R_X86_64_DTPOFF32:
case elfcpp::R_X86_64_DTPOFF64:
case elfcpp::R_X86_64_GOTTPOFF:
case elfcpp::R_X86_64_TPOFF32:
{
const bool is_final = gsym->final_value_is_known();
const tls::Tls_optimization optimized_type
= Target_x86_64::optimize_tls_reloc(is_final, r_type);
switch (r_type)
{
case elfcpp::R_X86_64_TLSGD:
if (optimized_type == tls::TLSOPT_NONE)
{
Output_data_got<64, false>* got
= target->got_section(symtab, layout);
got->add_global_pair_with_rela(gsym, GOT_TYPE_TLS_PAIR,
target->rela_dyn_section(layout),
elfcpp::R_X86_64_DTPMOD64,
elfcpp::R_X86_64_DTPOFF64);
}
else if (optimized_type == tls::TLSOPT_TO_IE)
{
Output_data_got<64, false>* got
= target->got_section(symtab, layout);
got->add_global_with_rela(gsym, GOT_TYPE_TLS_OFFSET,
target->rela_dyn_section(layout),
elfcpp::R_X86_64_TPOFF64);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_X86_64_GOTPC32_TLSDESC:
target->define_tls_base_symbol(symtab, layout);
if (optimized_type == tls::TLSOPT_NONE)
{
target->reserve_tlsdesc_entries(symtab, layout);
Output_data_got<64, false>* got
= target->got_section(symtab, layout);
got->add_global_pair_with_rela(gsym, GOT_TYPE_TLS_DESC,
target->rela_dyn_section(layout),
elfcpp::R_X86_64_TLSDESC, 0);
}
else if (optimized_type == tls::TLSOPT_TO_IE)
{
Output_data_got<64, false>* got
= target->got_section(symtab, layout);
got->add_global_with_rela(gsym, GOT_TYPE_TLS_OFFSET,
target->rela_dyn_section(layout),
elfcpp::R_X86_64_TPOFF64);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_X86_64_TLSDESC_CALL:
break;
case elfcpp::R_X86_64_TLSLD:
if (optimized_type == tls::TLSOPT_NONE)
{
target->got_mod_index_entry(symtab, layout, object);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_X86_64_DTPOFF32:
case elfcpp::R_X86_64_DTPOFF64:
break;
case elfcpp::R_X86_64_GOTTPOFF:
layout->set_has_static_tls();
if (optimized_type == tls::TLSOPT_NONE)
{
Output_data_got<64, false>* got
= target->got_section(symtab, layout);
got->add_global_with_rela(gsym, GOT_TYPE_TLS_OFFSET,
target->rela_dyn_section(layout),
elfcpp::R_X86_64_TPOFF64);
}
else if (optimized_type != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
break;
case elfcpp::R_X86_64_TPOFF32:
layout->set_has_static_tls();
if (parameters->options().shared())
unsupported_reloc_local(object, r_type);
break;
default:
gold_unreachable();
}
}
break;
case elfcpp::R_X86_64_SIZE32:
case elfcpp::R_X86_64_SIZE64:
default:
gold_error(_("%s: unsupported reloc %u against global symbol %s"),
object->name().c_str(), r_type,
gsym->demangled_name().c_str());
break;
}
}
void
Target_x86_64::scan_relocs(const General_options& options,
Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, 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)
{
if (sh_type == elfcpp::SHT_REL)
{
gold_error(_("%s: unsupported REL reloc section"),
object->name().c_str());
return;
}
gold::scan_relocs<64, false, Target_x86_64, elfcpp::SHT_RELA,
Target_x86_64::Scan>(
options,
symtab,
layout,
this,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_symbols);
}
void
Target_x86_64::do_finalize_sections(Layout* layout)
{
Output_data_dynamic* const odyn = layout->dynamic_data();
if (odyn != NULL)
{
if (this->got_plt_ != NULL)
odyn->add_section_address(elfcpp::DT_PLTGOT, this->got_plt_);
if (this->plt_ != NULL)
{
const Output_data* od = this->plt_->rel_plt();
odyn->add_section_size(elfcpp::DT_PLTRELSZ, od);
odyn->add_section_address(elfcpp::DT_JMPREL, od);
odyn->add_constant(elfcpp::DT_PLTREL, elfcpp::DT_RELA);
if (this->plt_->has_tlsdesc_entry())
{
unsigned int plt_offset = this->plt_->get_tlsdesc_plt_offset();
unsigned int got_offset = this->plt_->get_tlsdesc_got_offset();
this->got_->finalize_data_size();
odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_PLT,
this->plt_, plt_offset);
odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_GOT,
this->got_, got_offset);
}
}
if (this->rela_dyn_ != NULL)
{
const Output_data* od = this->rela_dyn_;
odyn->add_section_address(elfcpp::DT_RELA, od);
odyn->add_section_size(elfcpp::DT_RELASZ, od);
odyn->add_constant(elfcpp::DT_RELAENT,
elfcpp::Elf_sizes<64>::rela_size);
}
if (!parameters->options().shared())
{
odyn->add_constant(elfcpp::DT_DEBUG, 0);
}
}
if (this->copy_relocs_.any_saved_relocs())
this->copy_relocs_.emit(this->rela_dyn_section(layout));
}
inline bool
Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo,
Target_x86_64* target,
size_t relnum,
const elfcpp::Rela<64, false>& rela,
unsigned int r_type,
const Sized_symbol<64>* gsym,
const Symbol_value<64>* psymval,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr address,
section_size_type view_size)
{
if (this->skip_call_tls_get_addr_)
{
if (r_type != elfcpp::R_X86_64_PLT32
|| gsym == NULL
|| strcmp(gsym->name(), "__tls_get_addr") != 0)
{
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("missing expected TLS relocation"));
}
else
{
this->skip_call_tls_get_addr_ = false;
return false;
}
}
Symbol_value<64> symval;
if (gsym != NULL
&& gsym->use_plt_offset(r_type == elfcpp::R_X86_64_PC64
|| r_type == elfcpp::R_X86_64_PC32
|| r_type == elfcpp::R_X86_64_PC16
|| r_type == elfcpp::R_X86_64_PC8))
{
symval.set_output_value(target->plt_section()->address()
+ gsym->plt_offset());
psymval = &symval;
}
const Sized_relobj<64, false>* object = relinfo->object;
const elfcpp::Elf_Xword addend = rela.get_r_addend();
bool have_got_offset = false;
unsigned 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)
{
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<64>(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());
}
have_got_offset = true;
break;
default:
break;
}
switch (r_type)
{
case elfcpp::R_X86_64_NONE:
case elfcpp::R_386_GNU_VTINHERIT:
case elfcpp::R_386_GNU_VTENTRY:
break;
case elfcpp::R_X86_64_64:
Relocate_functions<64, false>::rela64(view, object, psymval, addend);
break;
case elfcpp::R_X86_64_PC64:
Relocate_functions<64, false>::pcrela64(view, object, psymval, addend,
address);
break;
case elfcpp::R_X86_64_32:
Relocate_functions<64, false>::rela32(view, object, psymval, addend);
break;
case elfcpp::R_X86_64_32S:
Relocate_functions<64, false>::rela32(view, object, psymval, addend);
break;
case elfcpp::R_X86_64_PC32:
Relocate_functions<64, false>::pcrela32(view, object, psymval, addend,
address);
break;
case elfcpp::R_X86_64_16:
Relocate_functions<64, false>::rela16(view, object, psymval, addend);
break;
case elfcpp::R_X86_64_PC16:
Relocate_functions<64, false>::pcrela16(view, object, psymval, addend,
address);
break;
case elfcpp::R_X86_64_8:
Relocate_functions<64, false>::rela8(view, object, psymval, addend);
break;
case elfcpp::R_X86_64_PC8:
Relocate_functions<64, false>::pcrela8(view, object, psymval, addend,
address);
break;
case elfcpp::R_X86_64_PLT32:
gold_assert(gsym == NULL
|| gsym->has_plt_offset()
|| gsym->final_value_is_known()
|| (gsym->is_defined()
&& !gsym->is_from_dynobj()
&& !gsym->is_preemptible()));
Relocate_functions<64, false>::pcrela32(view, object, psymval, addend,
address);
break;
case elfcpp::R_X86_64_PLTOFF64:
{
gold_assert(gsym);
gold_assert(gsym->has_plt_offset()
|| gsym->final_value_is_known());
elfcpp::Elf_types<64>::Elf_Addr got_address;
got_address = target->got_section(NULL, NULL)->address();
Relocate_functions<64, false>::rela64(view, object, psymval,
addend - got_address);
}
case elfcpp::R_X86_64_GOT32:
gold_assert(have_got_offset);
Relocate_functions<64, false>::rela32(view, got_offset, addend);
break;
case elfcpp::R_X86_64_GOTPC32:
{
gold_assert(gsym);
elfcpp::Elf_types<64>::Elf_Addr value;
value = target->got_plt_section()->address();
Relocate_functions<64, false>::pcrela32(view, value, addend, address);
}
break;
case elfcpp::R_X86_64_GOT64:
case elfcpp::R_X86_64_GOTPLT64:
gold_assert(have_got_offset);
Relocate_functions<64, false>::rela64(view, got_offset, addend);
break;
case elfcpp::R_X86_64_GOTPC64:
{
gold_assert(gsym);
elfcpp::Elf_types<64>::Elf_Addr value;
value = target->got_plt_section()->address();
Relocate_functions<64, false>::pcrela64(view, value, addend, address);
}
break;
case elfcpp::R_X86_64_GOTOFF64:
{
elfcpp::Elf_types<64>::Elf_Addr value;
value = (psymval->value(object, 0)
- target->got_plt_section()->address());
Relocate_functions<64, false>::rela64(view, value, addend);
}
break;
case elfcpp::R_X86_64_GOTPCREL:
{
gold_assert(have_got_offset);
elfcpp::Elf_types<64>::Elf_Addr value;
value = target->got_plt_section()->address() + got_offset;
Relocate_functions<64, false>::pcrela32(view, value, addend, address);
}
break;
case elfcpp::R_X86_64_GOTPCREL64:
{
gold_assert(have_got_offset);
elfcpp::Elf_types<64>::Elf_Addr value;
value = target->got_plt_section()->address() + got_offset;
Relocate_functions<64, false>::pcrela64(view, value, addend, address);
}
break;
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_TPOFF64:
case elfcpp::R_X86_64_DTPMOD64:
case elfcpp::R_X86_64_TLSDESC:
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unexpected reloc %u in object file"),
r_type);
break;
case elfcpp::R_X86_64_TLSGD:
case elfcpp::R_X86_64_GOTPC32_TLSDESC:
case elfcpp::R_X86_64_TLSDESC_CALL:
case elfcpp::R_X86_64_TLSLD:
case elfcpp::R_X86_64_DTPOFF32:
case elfcpp::R_X86_64_DTPOFF64:
case elfcpp::R_X86_64_GOTTPOFF:
case elfcpp::R_X86_64_TPOFF32:
this->relocate_tls(relinfo, target, relnum, rela, r_type, gsym, psymval,
view, address, view_size);
break;
case elfcpp::R_X86_64_SIZE32:
case elfcpp::R_X86_64_SIZE64:
default:
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unsupported reloc %u"),
r_type);
break;
}
return true;
}
inline void
Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo,
Target_x86_64* target,
size_t relnum,
const elfcpp::Rela<64, false>& rela,
unsigned int r_type,
const Sized_symbol<64>* gsym,
const Symbol_value<64>* psymval,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr address,
section_size_type view_size)
{
Output_segment* tls_segment = relinfo->layout->tls_segment();
const Sized_relobj<64, false>* object = relinfo->object;
const elfcpp::Elf_Xword addend = rela.get_r_addend();
elfcpp::Elf_types<64>::Elf_Addr value = psymval->value(relinfo->object, 0);
const bool is_final = (gsym == NULL
? !parameters->options().output_is_position_independent()
: gsym->final_value_is_known());
const tls::Tls_optimization optimized_type
= Target_x86_64::optimize_tls_reloc(is_final, r_type);
switch (r_type)
{
case elfcpp::R_X86_64_TLSGD:
this->saw_tls_block_reloc_ = true;
if (optimized_type == tls::TLSOPT_TO_LE)
{
gold_assert(tls_segment != NULL);
this->tls_gd_to_le(relinfo, relnum, tls_segment,
rela, r_type, value, view,
view_size);
break;
}
else
{
unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE
? GOT_TYPE_TLS_OFFSET
: GOT_TYPE_TLS_PAIR);
unsigned int got_offset;
if (gsym != NULL)
{
gold_assert(gsym->has_got_offset(got_type));
got_offset = gsym->got_offset(got_type) - target->got_size();
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<64>(rela.get_r_info());
gold_assert(object->local_has_got_offset(r_sym, got_type));
got_offset = (object->local_got_offset(r_sym, got_type)
- target->got_size());
}
if (optimized_type == tls::TLSOPT_TO_IE)
{
gold_assert(tls_segment != NULL);
value = target->got_plt_section()->address() + got_offset;
this->tls_gd_to_ie(relinfo, relnum, tls_segment, rela, r_type,
value, view, address, view_size);
break;
}
else if (optimized_type == tls::TLSOPT_NONE)
{
value = target->got_plt_section()->address() + got_offset;
Relocate_functions<64, false>::pcrela32(view, value, addend,
address);
break;
}
}
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unsupported reloc %u"), r_type);
break;
case elfcpp::R_X86_64_GOTPC32_TLSDESC:
case elfcpp::R_X86_64_TLSDESC_CALL:
this->saw_tls_block_reloc_ = true;
if (optimized_type == tls::TLSOPT_TO_LE)
{
gold_assert(tls_segment != NULL);
this->tls_desc_gd_to_le(relinfo, relnum, tls_segment,
rela, r_type, value, view,
view_size);
break;
}
else
{
unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE
? GOT_TYPE_TLS_OFFSET
: GOT_TYPE_TLS_DESC);
unsigned int got_offset;
if (gsym != NULL)
{
gold_assert(gsym->has_got_offset(got_type));
got_offset = gsym->got_offset(got_type) - target->got_size();
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<64>(rela.get_r_info());
gold_assert(object->local_has_got_offset(r_sym, got_type));
got_offset = (object->local_got_offset(r_sym, got_type)
- target->got_size());
}
if (optimized_type == tls::TLSOPT_TO_IE)
{
gold_assert(tls_segment != NULL);
value = target->got_plt_section()->address() + got_offset;
this->tls_desc_gd_to_ie(relinfo, relnum, tls_segment,
rela, r_type, value, view, address,
view_size);
break;
}
else if (optimized_type == tls::TLSOPT_NONE)
{
if (r_type == elfcpp::R_X86_64_GOTPC32_TLSDESC)
{
value = target->got_plt_section()->address() + got_offset;
Relocate_functions<64, false>::pcrela32(view, value, addend,
address);
}
break;
}
}
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unsupported reloc %u"), r_type);
break;
case elfcpp::R_X86_64_TLSLD:
this->saw_tls_block_reloc_ = true;
if (optimized_type == tls::TLSOPT_TO_LE)
{
gold_assert(tls_segment != NULL);
this->tls_ld_to_le(relinfo, relnum, tls_segment, rela, r_type,
value, view, view_size);
break;
}
else if (optimized_type == tls::TLSOPT_NONE)
{
unsigned int got_offset;
got_offset = (target->got_mod_index_entry(NULL, NULL, NULL)
- target->got_size());
value = target->got_plt_section()->address() + got_offset;
Relocate_functions<64, false>::pcrela32(view, value, addend,
address);
break;
}
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unsupported reloc %u"), r_type);
break;
case elfcpp::R_X86_64_DTPOFF32:
gold_assert(tls_segment != NULL);
if (optimized_type == tls::TLSOPT_TO_LE)
{
if (this->saw_tls_block_reloc_)
value -= tls_segment->memsz();
}
Relocate_functions<64, false>::rela32(view, value, addend);
break;
case elfcpp::R_X86_64_DTPOFF64:
gold_assert(tls_segment != NULL);
if (optimized_type == tls::TLSOPT_TO_LE)
{
if (this->saw_tls_block_reloc_)
value -= tls_segment->memsz();
}
Relocate_functions<64, false>::rela64(view, value, addend);
break;
case elfcpp::R_X86_64_GOTTPOFF:
if (optimized_type == tls::TLSOPT_TO_LE)
{
gold_assert(tls_segment != NULL);
Target_x86_64::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment,
rela, r_type, value, view,
view_size);
break;
}
else if (optimized_type == tls::TLSOPT_NONE)
{
unsigned int got_offset;
if (gsym != NULL)
{
gold_assert(gsym->has_got_offset(GOT_TYPE_TLS_OFFSET));
got_offset = (gsym->got_offset(GOT_TYPE_TLS_OFFSET)
- target->got_size());
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<64>(rela.get_r_info());
gold_assert(object->local_has_got_offset(r_sym,
GOT_TYPE_TLS_OFFSET));
got_offset = (object->local_got_offset(r_sym, GOT_TYPE_TLS_OFFSET)
- target->got_size());
}
value = target->got_plt_section()->address() + got_offset;
Relocate_functions<64, false>::pcrela32(view, value, addend, address);
break;
}
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unsupported reloc type %u"),
r_type);
break;
case elfcpp::R_X86_64_TPOFF32:
value -= tls_segment->memsz();
Relocate_functions<64, false>::rela32(view, value, addend);
break;
}
}
inline void
Target_x86_64::Relocate::tls_gd_to_ie(const Relocate_info<64, false>* relinfo,
size_t relnum,
Output_segment*,
const elfcpp::Rela<64, false>& rela,
unsigned int,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr address,
section_size_type view_size)
{
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -4);
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 12);
tls::check_tls(relinfo, relnum, rela.get_r_offset(),
(memcmp(view - 4, "\x66\x48\x8d\x3d", 4) == 0));
tls::check_tls(relinfo, relnum, rela.get_r_offset(),
(memcmp(view + 4, "\x66\x66\x48\xe8", 4) == 0));
memcpy(view - 4, "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0\0", 16);
const elfcpp::Elf_Xword addend = rela.get_r_addend();
Relocate_functions<64, false>::pcrela32(view + 8, value, addend - 8, address);
this->skip_call_tls_get_addr_ = true;
}
inline void
Target_x86_64::Relocate::tls_gd_to_le(const Relocate_info<64, false>* relinfo,
size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rela<64, false>& rela,
unsigned int,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
section_size_type view_size)
{
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -4);
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 12);
tls::check_tls(relinfo, relnum, rela.get_r_offset(),
(memcmp(view - 4, "\x66\x48\x8d\x3d", 4) == 0));
tls::check_tls(relinfo, relnum, rela.get_r_offset(),
(memcmp(view + 4, "\x66\x66\x48\xe8", 4) == 0));
memcpy(view - 4, "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x8d\x80\0\0\0\0", 16);
value -= tls_segment->memsz();
Relocate_functions<64, false>::rela32(view + 8, value, 0);
this->skip_call_tls_get_addr_ = true;
}
inline void
Target_x86_64::Relocate::tls_desc_gd_to_ie(
const Relocate_info<64, false>* relinfo,
size_t relnum,
Output_segment*,
const elfcpp::Rela<64, false>& rela,
unsigned int r_type,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr address,
section_size_type view_size)
{
if (r_type == elfcpp::R_X86_64_GOTPC32_TLSDESC)
{
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -3);
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4);
tls::check_tls(relinfo, relnum, rela.get_r_offset(),
view[-3] == 0x48 && view[-2] == 0x8d && view[-1] == 0x05);
view[-2] = 0x8b;
const elfcpp::Elf_Xword addend = rela.get_r_addend();
Relocate_functions<64, false>::pcrela32(view, value, addend, address);
}
else
{
gold_assert(r_type == elfcpp::R_X86_64_TLSDESC_CALL);
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 2);
tls::check_tls(relinfo, relnum, rela.get_r_offset(),
view[0] == 0xff && view[1] == 0x10);
view[0] = 0x66;
view[1] = 0x90;
}
}
inline void
Target_x86_64::Relocate::tls_desc_gd_to_le(
const Relocate_info<64, false>* relinfo,
size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rela<64, false>& rela,
unsigned int r_type,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
section_size_type view_size)
{
if (r_type == elfcpp::R_X86_64_GOTPC32_TLSDESC)
{
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -3);
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4);
tls::check_tls(relinfo, relnum, rela.get_r_offset(),
view[-3] == 0x48 && view[-2] == 0x8d && view[-1] == 0x05);
view[-2] = 0xc7;
view[-1] = 0xc0;
value -= tls_segment->memsz();
Relocate_functions<64, false>::rela32(view, value, 0);
}
else
{
gold_assert(r_type == elfcpp::R_X86_64_TLSDESC_CALL);
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 2);
tls::check_tls(relinfo, relnum, rela.get_r_offset(),
view[0] == 0xff && view[1] == 0x10);
view[0] = 0x66;
view[1] = 0x90;
}
}
inline void
Target_x86_64::Relocate::tls_ld_to_le(const Relocate_info<64, false>* relinfo,
size_t relnum,
Output_segment*,
const elfcpp::Rela<64, false>& rela,
unsigned int,
elfcpp::Elf_types<64>::Elf_Addr,
unsigned char* view,
section_size_type view_size)
{
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[-3] == 0x48 && view[-2] == 0x8d && view[-1] == 0x3d);
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);
this->skip_call_tls_get_addr_ = true;
}
inline void
Target_x86_64::Relocate::tls_ie_to_le(const Relocate_info<64, false>* relinfo,
size_t relnum,
Output_segment* tls_segment,
const elfcpp::Rela<64, false>& rela,
unsigned int,
elfcpp::Elf_types<64>::Elf_Addr value,
unsigned char* view,
section_size_type view_size)
{
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -3);
tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4);
unsigned char op1 = view[-3];
unsigned char op2 = view[-2];
unsigned char op3 = view[-1];
unsigned char reg = op3 >> 3;
if (op2 == 0x8b)
{
if (op1 == 0x4c)
view[-3] = 0x49;
view[-2] = 0xc7;
view[-1] = 0xc0 | reg;
}
else if (reg == 4)
{
if (op1 == 0x4c)
view[-3] = 0x49;
view[-2] = 0x81;
view[-1] = 0xc0 | reg;
}
else
{
if (op1 == 0x4c)
view[-3] = 0x4d;
view[-2] = 0x8d;
view[-1] = 0x80 | reg | (reg << 3);
}
value -= tls_segment->memsz();
Relocate_functions<64, false>::rela32(view, value, 0);
}
void
Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr address,
section_size_type view_size)
{
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::relocate_section<64, false, Target_x86_64, elfcpp::SHT_RELA,
Target_x86_64::Relocate>(
relinfo,
this,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
view,
address,
view_size);
}
unsigned int
Target_x86_64::Relocatable_size_for_reloc::get_size_for_reloc(
unsigned int r_type,
Relobj* object)
{
switch (r_type)
{
case elfcpp::R_X86_64_NONE:
case elfcpp::R_386_GNU_VTINHERIT:
case elfcpp::R_386_GNU_VTENTRY:
case elfcpp::R_X86_64_TLSGD:
case elfcpp::R_X86_64_GOTPC32_TLSDESC:
case elfcpp::R_X86_64_TLSDESC_CALL:
case elfcpp::R_X86_64_TLSLD:
case elfcpp::R_X86_64_DTPOFF32:
case elfcpp::R_X86_64_DTPOFF64:
case elfcpp::R_X86_64_GOTTPOFF:
case elfcpp::R_X86_64_TPOFF32:
return 0;
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_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;
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;
}
}
void
Target_x86_64::scan_relocatable_relocs(const General_options& options,
Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, 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)
{
gold_assert(sh_type == elfcpp::SHT_RELA);
typedef gold::Default_scan_relocatable_relocs<elfcpp::SHT_RELA,
Relocatable_size_for_reloc> Scan_relocatable_relocs;
gold::scan_relocatable_relocs<64, false, elfcpp::SHT_RELA,
Scan_relocatable_relocs>(
options,
symtab,
layout,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_symbols,
rr);
}
void
Target_x86_64::relocate_for_relocatable(
const Relocate_info<64, false>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
off_t offset_in_output_section,
const Relocatable_relocs* rr,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr view_address,
section_size_type view_size,
unsigned char* reloc_view,
section_size_type reloc_view_size)
{
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::relocate_for_relocatable<64, false, elfcpp::SHT_RELA>(
relinfo,
prelocs,
reloc_count,
output_section,
offset_in_output_section,
rr,
view,
view_address,
view_size,
reloc_view,
reloc_view_size);
}
uint64_t
Target_x86_64::do_dynsym_value(const Symbol* gsym) const
{
gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
return this->plt_section()->address() + gsym->plt_offset();
}
std::string
Target_x86_64::do_code_fill(section_size_type length) const
{
if (length >= 16)
{
unsigned char jmp[5];
jmp[0] = 0xe9;
elfcpp::Swap_unaligned<32, false>::writeval(jmp + 1, length - 5);
return (std::string(reinterpret_cast<char*>(&jmp[0]), 5)
+ std::string(length - 5, '\0'));
}
const char nop1[1] = { 0x90 };
const char nop2[2] = { 0x66, 0x90 };
const char nop3[3] = { 0x8d, 0x76, 0x00 };
const char nop4[4] = { 0x8d, 0x74, 0x26, 0x00};
const char nop5[5] = { 0x90, 0x8d, 0x74, 0x26,
0x00 };
const char nop6[6] = { 0x8d, 0xb6, 0x00, 0x00,
0x00, 0x00 };
const char nop7[7] = { 0x8d, 0xb4, 0x26, 0x00,
0x00, 0x00, 0x00 };
const char nop8[8] = { 0x90, 0x8d, 0xb4, 0x26,
0x00, 0x00, 0x00, 0x00 };
const char nop9[9] = { 0x89, 0xf6, 0x8d, 0xbc,
0x27, 0x00, 0x00, 0x00,
0x00 };
const char nop10[10] = { 0x8d, 0x76, 0x00, 0x8d,
0xbc, 0x27, 0x00, 0x00,
0x00, 0x00 };
const char nop11[11] = { 0x8d, 0x74, 0x26, 0x00,
0x8d, 0xbc, 0x27, 0x00,
0x00, 0x00, 0x00 };
const char nop12[12] = { 0x8d, 0xb6, 0x00, 0x00,
0x00, 0x00, 0x8d, 0xbf,
0x00, 0x00, 0x00, 0x00 };
const char nop13[13] = { 0x8d, 0xb6, 0x00, 0x00,
0x00, 0x00, 0x8d, 0xbc,
0x27, 0x00, 0x00, 0x00,
0x00 };
const char nop14[14] = { 0x8d, 0xb4, 0x26, 0x00,
0x00, 0x00, 0x00, 0x8d,
0xbc, 0x27, 0x00, 0x00,
0x00, 0x00 };
const char nop15[15] = { 0xeb, 0x0d, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90 };
const char* nops[16] = {
NULL,
nop1, nop2, nop3, nop4, nop5, nop6, nop7,
nop8, nop9, nop10, nop11, nop12, nop13, nop14, nop15
};
return std::string(nops[length], length);
}
class Target_selector_x86_64 : public Target_selector
{
public:
Target_selector_x86_64()
: Target_selector(elfcpp::EM_X86_64, 64, false, "elf64-x86-64")
{ }
Target*
do_instantiate_target()
{ return new Target_x86_64(); }
};
Target_selector_x86_64 target_selector_x86_64;
}