binutils-gdb/gold/tilegx.cc
Sriraman Tallam a82bef932e With -pie and x86, the linker complains if it sees a PC-relative relocation
to access a global as it expects a GOTPCREL relocation.  This is really not
necessary as the linker could use a copy relocation to get around it.  This
patch enables copy relocations with pie.

Context:
This is useful because currently the GCC compiler with option -fpie makes
every extern global access go through the GOT. That is because the compiler
cannot tell if a global will end up being defined in the executable or not
and is conservative. This ends up hurting performance when the binary is linked
as mostly static where most of the globals do end up being defined in the
executable.  By allowing copy relocs with fPIE, the compiler need not generate
a GOTPCREL(GOT access) for any global access.  It can safely assume that all
globals will be defined in the executable and generate a PC-relative access
instead.  Gold can then create a copy reloc for only the undefined globals.

	gold/
	* symtab.h (may_need_copy_reloc): Remove check for position independent
	code.
	* x86_64.cc (Target_x86_64<size>::Scan::global): Add check for no
	position independence before pc absolute may_need_copy_reloc call.
	Add check for executable output befor pc relative may_need_copy_reloc
	call.
	* i386.cc: Ditto.
	* arm.cc: Ditto.
	* sparc.cc: Ditto.
	* tilegx.cc: Ditto.
	* powerpc.cc: Add check for no position independence before
	may_need_copy_reloc calls.
	* testsuite/pie_copyrelocs_test.cc: New file.
	* testsuite/pie_copyrelocs_shared_test.cc: New file.
	* Makefile.am (pie_copyrelocs_test): New test.
	* Makefile.in: Regenerate.
2014-05-13 10:55:11 -07:00

4927 lines
188 KiB
C++

// tilegx.cc -- tilegx target support for gold.
// Copyright (C) 2012-2014 Free Software Foundation, Inc.
// Written by Jiong Wang (jiwang@tilera.com)
// This file is part of gold.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "gold.h"
#include <cstring>
#include "elfcpp.h"
#include "dwarf.h"
#include "parameters.h"
#include "reloc.h"
#include "tilegx.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"
#include "gc.h"
#include "icf.h"
// the first got entry reserved
const int32_t TILEGX_GOT_RESERVE_COUNT = 1;
// the first two .got.plt entry reserved
const int32_t TILEGX_GOTPLT_RESERVE_COUNT = 2;
// 1. for both 64/32 bit mode, the instruction bundle is always 64bit.
// 2. thus .plt section should always be aligned to 64 bit.
const int32_t TILEGX_INST_BUNDLE_SIZE = 64;
namespace
{
using namespace gold;
// 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
// classes below fill in those details.
template<int size, bool big_endian>
class Output_data_plt_tilegx : public Output_section_data
{
public:
typedef Output_data_reloc<elfcpp::SHT_RELA, true,size, big_endian>
Reloc_section;
Output_data_plt_tilegx(Layout* layout, uint64_t addralign,
Output_data_got<size, big_endian>* got,
Output_data_space* got_plt,
Output_data_space* got_irelative)
: Output_section_data(addralign), layout_(layout),
irelative_rel_(NULL), got_(got), got_plt_(got_plt),
got_irelative_(got_irelative), count_(0),
irelative_count_(0), free_list_()
{ this->init(layout); }
Output_data_plt_tilegx(Layout* layout, uint64_t plt_entry_size,
Output_data_got<size, big_endian>* got,
Output_data_space* got_plt,
Output_data_space* got_irelative,
unsigned int plt_count)
: Output_section_data((plt_count + 1) * plt_entry_size,
TILEGX_INST_BUNDLE_SIZE, false),
layout_(layout), irelative_rel_(NULL), got_(got),
got_plt_(got_plt), got_irelative_(got_irelative), count_(plt_count),
irelative_count_(0), free_list_()
{
this->init(layout);
// Initialize the free list and reserve the first entry.
this->free_list_.init((plt_count + 1) * plt_entry_size, false);
this->free_list_.remove(0, plt_entry_size);
}
// Initialize the PLT section.
void
init(Layout* layout);
// Add an entry to the PLT.
void
add_entry(Symbol_table*, Layout*, Symbol* gsym);
// Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
unsigned int
add_local_ifunc_entry(Symbol_table*, Layout*,
Sized_relobj_file<size, big_endian>*, unsigned int);
// Add the relocation for a PLT entry.
void
add_relocation(Symbol_table*, Layout*, Symbol*, unsigned int);
// Return the .rela.plt section data.
Reloc_section*
rela_plt()
{ return this->rel_; }
// Return where the IRELATIVE relocations should go in the PLT
// relocations.
Reloc_section*
rela_irelative(Symbol_table*, Layout*);
// Return whether we created a section for IRELATIVE relocations.
bool
has_irelative_section() const
{ return this->irelative_rel_ != NULL; }
// Return the number of PLT entries.
unsigned int
entry_count() const
{ return this->count_ + this->irelative_count_; }
// Return the offset of the first non-reserved PLT entry.
unsigned int
first_plt_entry_offset()
{ return this->get_plt_entry_size(); }
// Return the size of a PLT entry.
unsigned int
get_plt_entry_size() const
{ return plt_entry_size; }
// Reserve a slot in the PLT for an existing symbol in an incremental update.
void
reserve_slot(unsigned int plt_index)
{
this->free_list_.remove((plt_index + 1) * this->get_plt_entry_size(),
(plt_index + 2) * this->get_plt_entry_size());
}
// Return the PLT address to use for a global symbol.
uint64_t
address_for_global(const Symbol*);
// Return the PLT address to use for a local symbol.
uint64_t
address_for_local(const Relobj*, unsigned int symndx);
protected:
// Fill in the first PLT entry.
void
fill_first_plt_entry(unsigned char*);
// Fill in a normal PLT entry. Returns the offset into the entry that
// should be the initial GOT slot value.
void
fill_plt_entry(unsigned char*,
typename elfcpp::Elf_types<size>::Elf_Addr,
unsigned int,
typename elfcpp::Elf_types<size>::Elf_Addr,
unsigned int, unsigned int);
void
do_adjust_output_section(Output_section* os);
// Write to a map file.
void
do_print_to_mapfile(Mapfile* mapfile) const
{ mapfile->print_output_data(this, _("** PLT")); }
private:
// Set the final size.
void
set_final_data_size();
// Write out the PLT data.
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 IRELATIVE relocs, if necessary. These must follow the
// regular PLT relocations.
Reloc_section* irelative_rel_;
// The .got section.
Output_data_got<size, big_endian>* got_;
// The .got.plt section.
Output_data_space* got_plt_;
// The part of the .got.plt section used for IRELATIVE relocs.
Output_data_space* got_irelative_;
// The number of PLT entries.
unsigned int count_;
// Number of PLT entries with R_TILEGX_IRELATIVE relocs. These
// follow the regular PLT entries.
unsigned int irelative_count_;
// List of available regions within the section, for incremental
// update links.
Free_list free_list_;
// The size of an entry in the PLT.
static const int plt_entry_size = 40;
// The first entry in the PLT.
static const unsigned char first_plt_entry[plt_entry_size];
// Other entries in the PLT for an executable.
static const unsigned char plt_entry[plt_entry_size];
};
// The tilegx target class.
// See the ABI at
// http://www.tilera.com/scm
// TLS info comes from
// http://people.redhat.com/drepper/tls.pdf
template<int size, bool big_endian>
class Target_tilegx : public Sized_target<size, big_endian>
{
public:
// TileGX use RELA
typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian>
Reloc_section;
Target_tilegx(const Target::Target_info* info = &tilegx_info)
: Sized_target<size, big_endian>(info),
got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
global_offset_table_(NULL), tilegx_dynamic_(NULL), rela_dyn_(NULL),
rela_irelative_(NULL), copy_relocs_(elfcpp::R_TILEGX_COPY),
got_mod_index_offset_(-1U),
tls_get_addr_sym_defined_(false)
{ }
// Scan the relocations to look for symbol adjustments.
void
gc_process_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* 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);
// Scan the relocations to look for symbol adjustments.
void
scan_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* 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);
// Finalize the sections.
void
do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
// Return the value to use for a dynamic which requires special
// treatment.
uint64_t
do_dynsym_value(const Symbol*) const;
// Relocate a section.
void
relocate_section(const Relocate_info<size, big_endian>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr view_address,
section_size_type view_size,
const Reloc_symbol_changes*);
// Scan the relocs during a relocatable link.
void
scan_relocatable_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* 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*);
// Relocate a section during a relocatable link.
void
relocate_relocs(
const Relocate_info<size, big_endian>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section,
const Relocatable_relocs*,
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);
// Return whether SYM is defined by the ABI.
bool
do_is_defined_by_abi(const Symbol* sym) const
{ return strcmp(sym->name(), "__tls_get_addr") == 0; }
// define tilegx specific symbols
virtual void
do_define_standard_symbols(Symbol_table*, Layout*);
// Return the PLT section.
uint64_t
do_plt_address_for_global(const Symbol* gsym) const
{ return this->plt_section()->address_for_global(gsym); }
uint64_t
do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
{ return this->plt_section()->address_for_local(relobj, symndx); }
// This function should be defined in targets that can use relocation
// types to determine (implemented in local_reloc_may_be_function_pointer
// 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 tilegx
// pie binaries, safe ICF cannot be done by looking at relocation types.
bool
do_can_check_for_function_pointers() const
{ return true; }
// Return the base for a DW_EH_PE_datarel encoding.
uint64_t
do_ehframe_datarel_base() const;
// Return whether there is a GOT section.
bool
has_got_section() const
{ return this->got_ != NULL; }
// Return the size of the GOT section.
section_size_type
got_size() const
{
gold_assert(this->got_ != NULL);
return this->got_->data_size();
}
// Return the number of entries in the GOT.
unsigned int
got_entry_count() const
{
if (this->got_ == NULL)
return 0;
return this->got_size() / (size / 8);
}
// Return the number of entries in the PLT.
unsigned int
plt_entry_count() const;
// Return the offset of the first non-reserved PLT entry.
unsigned int
first_plt_entry_offset() const;
// Return the size of each PLT entry.
unsigned int
plt_entry_size() const;
// Create the GOT section for an incremental update.
Output_data_got_base*
init_got_plt_for_update(Symbol_table* symtab,
Layout* layout,
unsigned int got_count,
unsigned int plt_count);
// Reserve a GOT entry for a local symbol, and regenerate any
// necessary dynamic relocations.
void
reserve_local_got_entry(unsigned int got_index,
Sized_relobj<size, big_endian>* obj,
unsigned int r_sym,
unsigned int got_type);
// Reserve a GOT entry for a global symbol, and regenerate any
// necessary dynamic relocations.
void
reserve_global_got_entry(unsigned int got_index, Symbol* gsym,
unsigned int got_type);
// Register an existing PLT entry for a global symbol.
void
register_global_plt_entry(Symbol_table*, Layout*, unsigned int plt_index,
Symbol* gsym);
// Force a COPY relocation for a given symbol.
void
emit_copy_reloc(Symbol_table*, Symbol*, Output_section*, off_t);
// Apply an incremental relocation.
void
apply_relocation(const Relocate_info<size, big_endian>* relinfo,
typename elfcpp::Elf_types<size>::Elf_Addr r_offset,
unsigned int r_type,
typename elfcpp::Elf_types<size>::Elf_Swxword r_addend,
const Symbol* gsym,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr address,
section_size_type view_size);
private:
// The class which scans relocations.
class Scan
{
public:
Scan()
: issued_non_pic_error_(false)
{ }
static inline int
get_reference_flags(unsigned int r_type);
inline void
local(Symbol_table* symtab, Layout* layout, Target_tilegx* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
const elfcpp::Sym<size, big_endian>& lsym,
bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_tilegx* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
Symbol* gsym);
inline bool
local_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout,
Target_tilegx* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
const elfcpp::Sym<size, big_endian>& lsym);
inline bool
global_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout,
Target_tilegx* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
Symbol* gsym);
private:
static void
unsupported_reloc_local(Sized_relobj_file<size, big_endian>*,
unsigned int r_type);
static void
unsupported_reloc_global(Sized_relobj_file<size, big_endian>*,
unsigned int r_type, Symbol*);
void
check_non_pic(Relobj*, unsigned int r_type);
inline bool
possible_function_pointer_reloc(unsigned int r_type);
bool
reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>*,
unsigned int r_type);
// Whether we have issued an error about a non-PIC compilation.
bool issued_non_pic_error_;
};
// The class which implements relocation.
class Relocate
{
public:
Relocate()
{ }
~Relocate()
{
}
// Do a relocation. Return false if the caller should not issue
// any warnings about this relocation.
inline bool
relocate(const Relocate_info<size, big_endian>*, Target_tilegx*,
Output_section*,
size_t relnum, const elfcpp::Rela<size, big_endian>&,
unsigned int r_type, const Sized_symbol<size>*,
const Symbol_value<size>*,
unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr,
section_size_type);
};
// A class which returns the size required for a relocation type,
// used while scanning relocs during a relocatable link.
class Relocatable_size_for_reloc
{
public:
unsigned int
get_size_for_reloc(unsigned int, Relobj*);
};
// Adjust TLS relocation type based on the options and whether this
// is a local symbol.
static tls::Tls_optimization
optimize_tls_reloc(bool is_final, int r_type);
// Get the GOT section, creating it if necessary.
Output_data_got<size, big_endian>*
got_section(Symbol_table*, Layout*);
// Get the GOT PLT section.
Output_data_space*
got_plt_section() const
{
gold_assert(this->got_plt_ != NULL);
return this->got_plt_;
}
// Create the PLT section.
void
make_plt_section(Symbol_table* symtab, Layout* layout);
// Create a PLT entry for a global symbol.
void
make_plt_entry(Symbol_table*, Layout*, Symbol*);
// Create a PLT entry for a local STT_GNU_IFUNC symbol.
void
make_local_ifunc_plt_entry(Symbol_table*, Layout*,
Sized_relobj_file<size, big_endian>* relobj,
unsigned int local_sym_index);
// Create a GOT entry for the TLS module index.
unsigned int
got_mod_index_entry(Symbol_table* symtab, Layout* layout,
Sized_relobj_file<size, big_endian>* object);
// Get the PLT section.
Output_data_plt_tilegx<size, big_endian>*
plt_section() const
{
gold_assert(this->plt_ != NULL);
return this->plt_;
}
// Get the dynamic reloc section, creating it if necessary.
Reloc_section*
rela_dyn_section(Layout*);
// Get the section to use for IRELATIVE relocations.
Reloc_section*
rela_irelative_section(Layout*);
// Add a potential copy relocation.
void
copy_reloc(Symbol_table* symtab, Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rela<size, big_endian>& reloc)
{
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<size>(sym),
object, shndx, output_section,
reloc, this->rela_dyn_section(layout));
}
// Information about this specific target which we pass to the
// general Target structure.
static const Target::Target_info tilegx_info;
// The types of GOT entries needed for this platform.
// These values are exposed to the ABI in an incremental link.
// Do not renumber existing values without changing the version
// number of the .gnu_incremental_inputs section.
enum Got_type
{
GOT_TYPE_STANDARD = 0, // GOT entry for a regular symbol
GOT_TYPE_TLS_OFFSET = 1, // GOT entry for TLS offset
GOT_TYPE_TLS_PAIR = 2, // GOT entry for TLS module/offset pair
GOT_TYPE_TLS_DESC = 3 // GOT entry for TLS_DESC pair
};
// This type is used as the argument to the target specific
// relocation routines. The only target specific reloc is
// R_X86_64_TLSDESC against a local symbol.
struct Tlsdesc_info
{
Tlsdesc_info(Sized_relobj_file<size, big_endian>* a_object,
unsigned int a_r_sym)
: object(a_object), r_sym(a_r_sym)
{ }
// The object in which the local symbol is defined.
Sized_relobj_file<size, big_endian>* object;
// The local symbol index in the object.
unsigned int r_sym;
};
// The GOT section.
Output_data_got<size, big_endian>* got_;
// The PLT section.
Output_data_plt_tilegx<size, big_endian>* plt_;
// The GOT PLT section.
Output_data_space* got_plt_;
// The GOT section for IRELATIVE relocations.
Output_data_space* got_irelative_;
// The _GLOBAL_OFFSET_TABLE_ symbol.
Symbol* global_offset_table_;
// The _TILEGX_DYNAMIC_ symbol.
Symbol* tilegx_dynamic_;
// The dynamic reloc section.
Reloc_section* rela_dyn_;
// The section to use for IRELATIVE relocs.
Reloc_section* rela_irelative_;
// Relocs saved to avoid a COPY reloc.
Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
// Offset of the GOT entry for the TLS module index.
unsigned int got_mod_index_offset_;
// True if the _tls_get_addr symbol has been defined.
bool tls_get_addr_sym_defined_;
};
template<>
const Target::Target_info Target_tilegx<64, false>::tilegx_info =
{
64, // size
false, // is_big_endian
elfcpp::EM_TILEGX, // machine_code
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib/ld.so.1", // program interpreter
0x10000, // default_text_segment_address
0x10000, // abi_pagesize (overridable by -z max-page-size)
0x10000, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
"_start" // entry_symbol_name
};
template<>
const Target::Target_info Target_tilegx<32, false>::tilegx_info =
{
32, // size
false, // is_big_endian
elfcpp::EM_TILEGX, // machine_code
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib32/ld.so.1", // program interpreter
0x10000, // default_text_segment_address
0x10000, // abi_pagesize (overridable by -z max-page-size)
0x10000, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
"_start" // entry_symbol_name
};
template<>
const Target::Target_info Target_tilegx<64, true>::tilegx_info =
{
64, // size
true, // is_big_endian
elfcpp::EM_TILEGX, // machine_code
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib/ld.so.1", // program interpreter
0x10000, // default_text_segment_address
0x10000, // abi_pagesize (overridable by -z max-page-size)
0x10000, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
"_start" // entry_symbol_name
};
template<>
const Target::Target_info Target_tilegx<32, true>::tilegx_info =
{
32, // size
true, // is_big_endian
elfcpp::EM_TILEGX, // machine_code
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/lib32/ld.so.1", // program interpreter
0x10000, // default_text_segment_address
0x10000, // abi_pagesize (overridable by -z max-page-size)
0x10000, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
"_start" // entry_symbol_name
};
// tilegx relocation handlers
template<int size, bool big_endian>
class Tilegx_relocate_functions
{
public:
// overflow check will be supported later
typedef enum
{
STATUS_OKAY, // No error during relocation.
STATUS_OVERFLOW, // Relocation overflow.
STATUS_BAD_RELOC // Relocation cannot be applied.
} Status;
struct Tilegx_howto
{
// right shift operand by this number of bits.
unsigned char srshift;
// the offset to apply relocation.
unsigned char doffset;
// set to 1 for pc-relative relocation.
unsigned char is_pcrel;
// size in bits, or 0 if this table entry should be ignored.
unsigned char bsize;
// whether we need to check overflow.
unsigned char overflow;
};
static const Tilegx_howto howto[elfcpp::R_TILEGX_NUM];
private:
// Do a simple rela relocation
template<int valsize>
static inline void
rela(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Swap<size, big_endian>::Valtype addend,
elfcpp::Elf_Xword srshift, elfcpp::Elf_Xword doffset,
elfcpp::Elf_Xword bitmask)
{
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Valtype val = elfcpp::Swap<valsize, big_endian>::readval(wv);
Valtype reloc = 0;
if (size == 32)
reloc = Bits<32>::sign_extend(psymval->value(object, addend)) >> srshift;
else
reloc = psymval->value(object, addend) >> srshift;
elfcpp::Elf_Xword dst_mask = bitmask << doffset;
val &= ~dst_mask;
reloc &= bitmask;
elfcpp::Swap<valsize, big_endian>::writeval(wv, val | (reloc<<doffset));
}
// Do a simple rela relocation
template<int valsize>
static inline void
rela_ua(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Swap<size, big_endian>::Valtype addend,
elfcpp::Elf_Xword srshift, elfcpp::Elf_Xword doffset,
elfcpp::Elf_Xword bitmask)
{
typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
Valtype;
unsigned char* wv = view;
Valtype val = elfcpp::Swap_unaligned<valsize, big_endian>::readval(wv);
Valtype reloc = 0;
if (size == 32)
reloc = Bits<32>::sign_extend(psymval->value(object, addend)) >> srshift;
else
reloc = psymval->value(object, addend) >> srshift;
elfcpp::Elf_Xword dst_mask = bitmask << doffset;
val &= ~dst_mask;
reloc &= bitmask;
elfcpp::Swap_unaligned<valsize, big_endian>::writeval(wv,
val | (reloc<<doffset));
}
template<int valsize>
static inline void
rela(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Swap<size, big_endian>::Valtype addend,
elfcpp::Elf_Xword srshift, elfcpp::Elf_Xword doffset1,
elfcpp::Elf_Xword bitmask1, elfcpp::Elf_Xword doffset2,
elfcpp::Elf_Xword bitmask2)
{
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Valtype val = elfcpp::Swap<valsize, big_endian>::readval(wv);
Valtype reloc = 0;
if (size == 32)
reloc = Bits<32>::sign_extend(psymval->value(object, addend)) >> srshift;
else
reloc = psymval->value(object, addend) >> srshift;
elfcpp::Elf_Xword dst_mask = (bitmask1 << doffset1)
| (bitmask2 << doffset2);
val &= ~dst_mask;
reloc = ((reloc & bitmask1) << doffset1)
| ((reloc & bitmask2) << doffset2);
elfcpp::Swap<valsize, big_endian>::writeval(wv, val | reloc);
}
// Do a simple PC relative relocation with a Symbol_value with the
// addend in the relocation.
template<int valsize>
static inline void
pcrela(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Swap<size, big_endian>::Valtype addend,
typename elfcpp::Elf_types<size>::Elf_Addr address,
elfcpp::Elf_Xword srshift, elfcpp::Elf_Xword doffset,
elfcpp::Elf_Xword bitmask)
{
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Valtype val = elfcpp::Swap<valsize, big_endian>::readval(wv);
Valtype reloc = 0;
if (size == 32)
reloc = Bits<32>::sign_extend(psymval->value(object, addend) - address)
>> srshift;
else
reloc = (psymval->value(object, addend) - address) >> srshift;
elfcpp::Elf_Xword dst_mask = bitmask << doffset;
val &= ~dst_mask;
reloc &= bitmask;
elfcpp::Swap<valsize, big_endian>::writeval(wv, val | (reloc<<doffset));
}
template<int valsize>
static inline void
pcrela_ua(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Swap<size, big_endian>::Valtype addend,
typename elfcpp::Elf_types<size>::Elf_Addr address,
elfcpp::Elf_Xword srshift, elfcpp::Elf_Xword doffset,
elfcpp::Elf_Xword bitmask)
{
typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
Valtype;
unsigned char* wv = view;
Valtype reloc = 0;
if (size == 32)
reloc = Bits<32>::sign_extend(psymval->value(object, addend) - address)
>> srshift;
else
reloc = (psymval->value(object, addend) - address) >> srshift;
reloc &= bitmask;
elfcpp::Swap<valsize, big_endian>::writeval(wv, reloc << doffset);
}
template<int valsize>
static inline void
pcrela(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Swap<size, big_endian>::Valtype addend,
typename elfcpp::Elf_types<size>::Elf_Addr address,
elfcpp::Elf_Xword srshift, elfcpp::Elf_Xword doffset1,
elfcpp::Elf_Xword bitmask1, elfcpp::Elf_Xword doffset2,
elfcpp::Elf_Xword bitmask2)
{
typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Valtype val = elfcpp::Swap<valsize, big_endian>::readval(wv);
Valtype reloc = 0;
if (size == 32)
reloc = Bits<32>::sign_extend(psymval->value(object, addend) - address)
>> srshift;
else
reloc = (psymval->value(object, addend) - address) >> srshift;
elfcpp::Elf_Xword dst_mask = (bitmask1 << doffset1)
| (bitmask2 << doffset2);
val &= ~dst_mask;
reloc = ((reloc & bitmask1) << doffset1)
| ((reloc & bitmask2) << doffset2);
elfcpp::Swap<valsize, big_endian>::writeval(wv, val | reloc);
}
typedef Tilegx_relocate_functions<size, big_endian> This;
typedef Relocate_functions<size, big_endian> Base;
public:
static inline void
abs64(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Elf_types<size>::Elf_Addr addend)
{
This::template rela_ua<64>(view, object, psymval, addend, 0, 0,
0xffffffffffffffffllu);
}
static inline void
abs32(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Elf_types<size>::Elf_Addr addend)
{
This::template rela_ua<32>(view, object, psymval, addend, 0, 0,
0xffffffff);
}
static inline void
abs16(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Elf_types<size>::Elf_Addr addend)
{
This::template rela_ua<16>(view, object, psymval, addend, 0, 0,
0xffff);
}
static inline void
pc_abs64(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Elf_types<size>::Elf_Addr addend,
typename elfcpp::Elf_types<size>::Elf_Addr address)
{
This::template pcrela_ua<64>(view, object, psymval, addend, address, 0, 0,
0xffffffffffffffffllu);
}
static inline void
pc_abs32(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Elf_types<size>::Elf_Addr addend,
typename elfcpp::Elf_types<size>::Elf_Addr address)
{
This::template pcrela_ua<32>(view, object, psymval, addend, address, 0, 0,
0xffffffff);
}
static inline void
pc_abs16(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Elf_types<size>::Elf_Addr addend,
typename elfcpp::Elf_types<size>::Elf_Addr address)
{
This::template pcrela_ua<16>(view, object, psymval, addend, address, 0, 0,
0xffff);
}
static inline void
imm_x_general(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Elf_types<size>::Elf_Addr addend,
Tilegx_howto &r_howto)
{
This::template rela<64>(view, object, psymval, addend,
(elfcpp::Elf_Xword)(r_howto.srshift),
(elfcpp::Elf_Xword)(r_howto.doffset),
(elfcpp::Elf_Xword)((1 << r_howto.bsize) - 1));
}
static inline void
imm_x_pcrel_general(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Elf_types<size>::Elf_Addr addend,
typename elfcpp::Elf_types<size>::Elf_Addr address,
Tilegx_howto &r_howto)
{
This::template pcrela<64>(view, object, psymval, addend, address,
(elfcpp::Elf_Xword)(r_howto.srshift),
(elfcpp::Elf_Xword)(r_howto.doffset),
(elfcpp::Elf_Xword)((1 << r_howto.bsize) - 1));
}
static inline void
imm_x_two_part_general(unsigned char* view,
const Sized_relobj_file<size, big_endian>* object,
const Symbol_value<size>* psymval,
typename elfcpp::Elf_types<size>::Elf_Addr addend,
typename elfcpp::Elf_types<size>::Elf_Addr address,
unsigned int r_type)
{
elfcpp::Elf_Xword doffset1 = 0llu;
elfcpp::Elf_Xword doffset2 = 0llu;
elfcpp::Elf_Xword dmask1 = 0llu;
elfcpp::Elf_Xword dmask2 = 0llu;
elfcpp::Elf_Xword rshift = 0llu;
unsigned int pc_rel = 0;
switch (r_type)
{
case elfcpp::R_TILEGX_BROFF_X1:
doffset1 = 31llu;
doffset2 = 37llu;
dmask1 = 0x3fllu;
dmask2 = 0x1ffc0llu;
rshift = 3llu;
pc_rel = 1;
break;
case elfcpp::R_TILEGX_DEST_IMM8_X1:
doffset1 = 31llu;
doffset2 = 43llu;
dmask1 = 0x3fllu;
dmask2 = 0xc0llu;
rshift = 0llu;
break;
}
if (pc_rel)
This::template pcrela<64>(view, object, psymval, addend, address,
rshift, doffset1, dmask1, doffset2, dmask2);
else
This::template rela<64>(view, object, psymval, addend, rshift,
doffset1, dmask1, doffset2, dmask2);
}
static inline void
tls_relax(unsigned char* view, unsigned int r_type,
tls::Tls_optimization opt_t)
{
const uint64_t TILEGX_X_MOVE_R0_R0 = 0x283bf8005107f000llu;
const uint64_t TILEGX_Y_MOVE_R0_R0 = 0xae05f800540bf000llu;
const uint64_t TILEGX_X_LD = 0x286ae80000000000llu;
const uint64_t TILEGX_X_LD4S = 0x286a980000000000llu;
const uint64_t TILEGX_X1_FULL_MASK = 0x3fffffff80000000llu;
const uint64_t TILEGX_X0_RRR_MASK = 0x000000007ffc0000llu;
const uint64_t TILEGX_X1_RRR_MASK = 0x3ffe000000000000llu;
const uint64_t TILEGX_Y0_RRR_MASK = 0x00000000780c0000llu;
const uint64_t TILEGX_Y1_RRR_MASK = 0x3c06000000000000llu;
const uint64_t TILEGX_X0_RRR_SRCB_MASK = 0x000000007ffff000llu;
const uint64_t TILEGX_X1_RRR_SRCB_MASK = 0x3ffff80000000000llu;
const uint64_t TILEGX_Y0_RRR_SRCB_MASK = 0x00000000780ff000llu;
const uint64_t TILEGX_Y1_RRR_SRCB_MASK = 0x3c07f80000000000llu;
const uint64_t TILEGX_X_ADD_R0_R0_TP = 0x2807a800500f5000llu;
const uint64_t TILEGX_Y_ADD_R0_R0_TP = 0x9a13a8002c275000llu;
const uint64_t TILEGX_X_ADDX_R0_R0_TP = 0x2805a800500b5000llu;
const uint64_t TILEGX_Y_ADDX_R0_R0_TP = 0x9a01a8002c035000llu;
const uint64_t R_TILEGX_IMM8_X0_TLS_ADD_MASK =
(TILEGX_X0_RRR_MASK | (0x3Fllu << 12));
const uint64_t R_TILEGX_IMM8_X1_TLS_ADD_MASK =
(TILEGX_X1_RRR_MASK | (0x3Fllu << 43));
const uint64_t R_TILEGX_IMM8_Y0_TLS_ADD_MASK =
(TILEGX_Y0_RRR_MASK | (0x3Fllu << 12));
const uint64_t R_TILEGX_IMM8_Y1_TLS_ADD_MASK =
(TILEGX_Y1_RRR_MASK | (0x3Fllu << 43));
const uint64_t R_TILEGX_IMM8_X0_TLS_ADD_LE_MASK =
(TILEGX_X0_RRR_SRCB_MASK | (0x3Fllu << 6));
const uint64_t R_TILEGX_IMM8_X1_TLS_ADD_LE_MASK =
(TILEGX_X1_RRR_SRCB_MASK | (0x3Fllu << 37));
const uint64_t R_TILEGX_IMM8_Y0_TLS_ADD_LE_MASK =
(TILEGX_Y0_RRR_SRCB_MASK | (0x3Fllu << 6));
const uint64_t R_TILEGX_IMM8_Y1_TLS_ADD_LE_MASK =
(TILEGX_Y1_RRR_SRCB_MASK | (0x3Fllu << 37));
typedef typename elfcpp::Swap<64, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Valtype val = elfcpp::Swap<64, big_endian>::readval(wv);
Valtype reloc = 0;
switch (r_type)
{
case elfcpp::R_TILEGX_IMM8_X0_TLS_ADD:
if (opt_t == tls::TLSOPT_NONE) {
// GD/IE: 1. copy dest operand into the second source operand
// 2. change the opcode to "add"
reloc = (val & 0x3Fllu) << 12; // featch the dest reg
reloc |= ((size == 32
? TILEGX_X_ADDX_R0_R0_TP
: TILEGX_X_ADD_R0_R0_TP)
& TILEGX_X0_RRR_MASK); // change opcode
val &= ~R_TILEGX_IMM8_X0_TLS_ADD_MASK;
} else if (opt_t == tls::TLSOPT_TO_LE) {
// LE: 1. copy dest operand into the first source operand
// 2. change the opcode to "move"
reloc = (val & 0x3Fllu) << 6;
reloc |= (TILEGX_X_MOVE_R0_R0 & TILEGX_X0_RRR_SRCB_MASK);
val &= ~R_TILEGX_IMM8_X0_TLS_ADD_LE_MASK;
} else
gold_unreachable();
break;
case elfcpp::R_TILEGX_IMM8_X1_TLS_ADD:
if (opt_t == tls::TLSOPT_NONE) {
reloc = (val & (0x3Fllu << 31)) << 12;
reloc |= ((size == 32
? TILEGX_X_ADDX_R0_R0_TP
: TILEGX_X_ADD_R0_R0_TP)
& TILEGX_X1_RRR_MASK);
val &= ~R_TILEGX_IMM8_X1_TLS_ADD_MASK;
} else if (opt_t == tls::TLSOPT_TO_LE) {
reloc = (val & (0x3Fllu << 31)) << 6;
reloc |= (TILEGX_X_MOVE_R0_R0 & TILEGX_X1_RRR_SRCB_MASK);
val &= ~R_TILEGX_IMM8_X1_TLS_ADD_LE_MASK;
} else
gold_unreachable();
break;
case elfcpp::R_TILEGX_IMM8_Y0_TLS_ADD:
if (opt_t == tls::TLSOPT_NONE) {
reloc = (val & 0x3Fllu) << 12;
reloc |= ((size == 32
? TILEGX_Y_ADDX_R0_R0_TP
: TILEGX_Y_ADD_R0_R0_TP)
& TILEGX_Y0_RRR_MASK);
val &= ~R_TILEGX_IMM8_Y0_TLS_ADD_MASK;
} else if (opt_t == tls::TLSOPT_TO_LE) {
reloc = (val & 0x3Fllu) << 6;
reloc |= (TILEGX_Y_MOVE_R0_R0 & TILEGX_Y0_RRR_SRCB_MASK);
val &= ~R_TILEGX_IMM8_Y0_TLS_ADD_LE_MASK;
} else
gold_unreachable();
break;
case elfcpp::R_TILEGX_IMM8_Y1_TLS_ADD:
if (opt_t == tls::TLSOPT_NONE) {
reloc = (val & (0x3Fllu << 31)) << 12;
reloc |= ((size == 32
? TILEGX_Y_ADDX_R0_R0_TP
: TILEGX_Y_ADD_R0_R0_TP)
& TILEGX_Y1_RRR_MASK);
val &= ~R_TILEGX_IMM8_Y1_TLS_ADD_MASK;
} else if (opt_t == tls::TLSOPT_TO_LE) {
reloc = (val & (0x3Fllu << 31)) << 6;
reloc |= (TILEGX_Y_MOVE_R0_R0 & TILEGX_Y1_RRR_SRCB_MASK);
val &= ~R_TILEGX_IMM8_Y1_TLS_ADD_LE_MASK;
} else
gold_unreachable();
break;
case elfcpp::R_TILEGX_IMM8_X0_TLS_GD_ADD:
if (opt_t == tls::TLSOPT_NONE) {
// GD see comments for optimize_tls_reloc
reloc = TILEGX_X_MOVE_R0_R0 & TILEGX_X0_RRR_SRCB_MASK;
val &= ~TILEGX_X0_RRR_SRCB_MASK;
} else if (opt_t == tls::TLSOPT_TO_IE
|| opt_t == tls::TLSOPT_TO_LE) {
// IE/LE
reloc = (size == 32
? TILEGX_X_ADDX_R0_R0_TP
: TILEGX_X_ADD_R0_R0_TP)
& TILEGX_X0_RRR_SRCB_MASK;
val &= ~TILEGX_X0_RRR_SRCB_MASK;
}
break;
case elfcpp::R_TILEGX_IMM8_X1_TLS_GD_ADD:
if (opt_t == tls::TLSOPT_NONE) {
reloc = TILEGX_X_MOVE_R0_R0 & TILEGX_X1_RRR_SRCB_MASK;
val &= ~TILEGX_X1_RRR_SRCB_MASK;
} else if (opt_t == tls::TLSOPT_TO_IE
|| opt_t == tls::TLSOPT_TO_LE) {
reloc = (size == 32
? TILEGX_X_ADDX_R0_R0_TP
: TILEGX_X_ADD_R0_R0_TP)
& TILEGX_X1_RRR_SRCB_MASK;
val &= ~TILEGX_X1_RRR_SRCB_MASK;
}
break;
case elfcpp::R_TILEGX_IMM8_Y0_TLS_GD_ADD:
if (opt_t == tls::TLSOPT_NONE) {
reloc = TILEGX_Y_MOVE_R0_R0 & TILEGX_Y0_RRR_SRCB_MASK;
val &= ~TILEGX_Y0_RRR_SRCB_MASK;
} else if (opt_t == tls::TLSOPT_TO_IE
|| opt_t == tls::TLSOPT_TO_LE) {
reloc = (size == 32
? TILEGX_Y_ADDX_R0_R0_TP
: TILEGX_Y_ADD_R0_R0_TP)
& TILEGX_Y0_RRR_SRCB_MASK;
val &= ~TILEGX_Y0_RRR_SRCB_MASK;
}
break;
case elfcpp::R_TILEGX_IMM8_Y1_TLS_GD_ADD:
if (opt_t == tls::TLSOPT_NONE) {
reloc = TILEGX_Y_MOVE_R0_R0 & TILEGX_Y1_RRR_SRCB_MASK;
val &= ~TILEGX_Y1_RRR_SRCB_MASK;
} else if (opt_t == tls::TLSOPT_TO_IE
|| opt_t == tls::TLSOPT_TO_LE) {
reloc = (size == 32
? TILEGX_Y_ADDX_R0_R0_TP
: TILEGX_Y_ADD_R0_R0_TP)
& TILEGX_Y1_RRR_SRCB_MASK;
val &= ~TILEGX_Y1_RRR_SRCB_MASK;
}
break;
case elfcpp::R_TILEGX_TLS_IE_LOAD:
if (opt_t == tls::TLSOPT_NONE) {
// IE
reloc = (size == 32
? TILEGX_X_LD4S
: TILEGX_X_LD)
& TILEGX_X1_RRR_SRCB_MASK;
val &= ~TILEGX_X1_RRR_SRCB_MASK;
} else if (opt_t == tls::TLSOPT_TO_LE) {
// LE
reloc = TILEGX_X_MOVE_R0_R0 & TILEGX_X1_RRR_SRCB_MASK;
val &= ~TILEGX_X1_RRR_SRCB_MASK;
} else
gold_unreachable();
break;
case elfcpp::R_TILEGX_TLS_GD_CALL:
if (opt_t == tls::TLSOPT_TO_IE) {
// ld/ld4s r0, r0
reloc = (size == 32
? TILEGX_X_LD4S
: TILEGX_X_LD) & TILEGX_X1_FULL_MASK;
val &= ~TILEGX_X1_FULL_MASK;
} else if (opt_t == tls::TLSOPT_TO_LE) {
// move r0, r0
reloc = TILEGX_X_MOVE_R0_R0 & TILEGX_X1_FULL_MASK;
val &= ~TILEGX_X1_FULL_MASK;
} else
// should be handled in ::relocate
gold_unreachable();
break;
default:
gold_unreachable();
break;
}
elfcpp::Swap<64, big_endian>::writeval(wv, val | reloc);
}
};
template<>
const Tilegx_relocate_functions<64, false>::Tilegx_howto
Tilegx_relocate_functions<64, false>::howto[elfcpp::R_TILEGX_NUM] =
{
{ 0, 0, 0, 0, 0}, // R_TILEGX_NONE
{ 0, 0, 0, 64, 0}, // R_TILEGX_64
{ 0, 0, 0, 32, 0}, // R_TILEGX_32
{ 0, 0, 0, 16, 0}, // R_TILEGX_16
{ 0, 0, 0, 8, 0}, // R_TILEGX_8
{ 0, 0, 1, 64, 0}, // R_TILEGX_64_PCREL
{ 0, 0, 1, 32, 0}, // R_TILEGX_32_PCREL
{ 0, 0, 1, 16, 0}, // R_TILEGX_16_PCREL
{ 0, 0, 1, 8, 0}, // R_TILEGX_8_PCREL
{ 0, 0, 0, 0, 0}, // R_TILEGX_HW0
{ 16, 0, 0, 0, 0}, // R_TILEGX_HW1
{ 32, 0, 0, 0, 0}, // R_TILEGX_HW2
{ 48, 0, 0, 0, 0}, // R_TILEGX_HW3
{ 0, 0, 0, 0, 0}, // R_TILEGX_HW0_LAST
{ 16, 0, 0, 0, 0}, // R_TILEGX_HW1_LAST
{ 32, 0, 0, 0, 0}, // R_TILEGX_HW2_LAST
{ 0, 0, 0, 0, 0}, // R_TILEGX_COPY
{ 0, 0, 0, 8, 0}, // R_TILEGX_GLOB_DAT
{ 0, 0, 0, 0, 0}, // R_TILEGX_JMP_SLOT
{ 0, 0, 0, 0, 0}, // R_TILEGX_RELATIVE
{ 3, 1, 1, 0, 0}, // R_TILEGX_BROFF_X1
{ 3, 31, 1, 27, 0}, // R_TILEGX_JUMPOFF_X1
{ 3, 31, 1, 27, 0}, // R_TILEGX_JUMPOFF_X1_PLT
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_Y0
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_Y1
{ 0, 1, 0, 8, 0}, // R_TILEGX_DEST_IMM8_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MT_IMM14_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MF_IMM14_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MMSTART_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_MMEND_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_Y0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_Y1
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0
{ 16, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW1
{ 16, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW1
{ 32, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW2
{ 32, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW2
{ 48, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW3
{ 48, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW3
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST
{ 32, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST
{ 32, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST
{ 0, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW0_PCREL
{ 0, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW0_PCREL
{ 16, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW1_PCREL
{ 16, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW1_PCREL
{ 32, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW2_PCREL
{ 32, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW2_PCREL
{ 48, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW3_PCREL
{ 48, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW3_PCREL
{ 0, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_PCREL
{ 0, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_PCREL
{ 16, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_PCREL
{ 16, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_PCREL
{ 32, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_PCREL
{ 32, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_PCREL
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_GOT
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_GOT
{ 0, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW0_PLT_PCREL
{ 0, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW0_PLT_PCREL
{ 16, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW1_PLT_PCREL
{ 16, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW1_PLT_PCREL
{ 32, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW2_PLT_PCREL
{ 32, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW2_PLT_PCREL
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_GOT
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_GOT
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_GOT
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_GOT
{ 32, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_GOT
{ 32, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_GOT
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_GD
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_GD
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_LE
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_LE
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IRELATIVE
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_IE
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_IE
{ 0, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL
{ 0, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL
{ 16, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL
{ 16, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL
{ 32, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL
{ 32, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPMOD64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPOFF64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_TPOFF64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPMOD32
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPOFF32
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_TPOFF32
{ 3, 31, 1, 27, 0}, // R_TILEGX_TLS_GD_CALL
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X0_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X1_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y0_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y1_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_IE_LOAD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X0_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X1_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y0_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y1_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_GNU_VTINHERIT
{ 0, 0, 0, 0, 0}, // R_TILEGX_GNU_VTENTRY
};
template<>
const Tilegx_relocate_functions<32, false>::Tilegx_howto
Tilegx_relocate_functions<32, false>::howto[elfcpp::R_TILEGX_NUM] =
{
{ 0, 0, 0, 0, 0}, // R_TILEGX_NONE
{ 0, 0, 0, 64, 0}, // R_TILEGX_64
{ 0, 0, 0, 32, 0}, // R_TILEGX_32
{ 0, 0, 0, 16, 0}, // R_TILEGX_16
{ 0, 0, 0, 8, 0}, // R_TILEGX_8
{ 0, 0, 1, 64, 0}, // R_TILEGX_64_PCREL
{ 0, 0, 1, 32, 0}, // R_TILEGX_32_PCREL
{ 0, 0, 1, 16, 0}, // R_TILEGX_16_PCREL
{ 0, 0, 1, 8, 0}, // R_TILEGX_8_PCREL
{ 0, 0, 0, 0, 0}, // R_TILEGX_HW0
{ 16, 0, 0, 0, 0}, // R_TILEGX_HW1
{ 31, 0, 0, 0, 0}, // R_TILEGX_HW2
{ 31, 0, 0, 0, 0}, // R_TILEGX_HW3
{ 0, 0, 0, 0, 0}, // R_TILEGX_HW0_LAST
{ 16, 0, 0, 0, 0}, // R_TILEGX_HW1_LAST
{ 31, 0, 0, 0, 0}, // R_TILEGX_HW2_LAST
{ 0, 0, 0, 0, 0}, // R_TILEGX_COPY
{ 0, 0, 0, 8, 0}, // R_TILEGX_GLOB_DAT
{ 0, 0, 0, 0, 0}, // R_TILEGX_JMP_SLOT
{ 0, 0, 0, 0, 0}, // R_TILEGX_RELATIVE
{ 3, 1, 1, 0, 0}, // R_TILEGX_BROFF_X1
{ 3, 31, 1, 27, 0}, // R_TILEGX_JUMPOFF_X1
{ 3, 31, 1, 27, 0}, // R_TILEGX_JUMPOFF_X1_PLT
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_Y0
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_Y1
{ 0, 1, 0, 8, 0}, // R_TILEGX_DEST_IMM8_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MT_IMM14_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MF_IMM14_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MMSTART_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_MMEND_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_Y0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_Y1
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0
{ 16, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW1
{ 16, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW1
{ 31, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW2
{ 31, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW2
{ 31, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW3
{ 31, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW3
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST
{ 31, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST
{ 31, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST
{ 0, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW0_PCREL
{ 0, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW0_PCREL
{ 16, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW1_PCREL
{ 16, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW1_PCREL
{ 31, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW2_PCREL
{ 31, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW2_PCREL
{ 31, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW3_PCREL
{ 31, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW3_PCREL
{ 0, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_PCREL
{ 0, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_PCREL
{ 16, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_PCREL
{ 16, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_PCREL
{ 31, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_PCREL
{ 31, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_PCREL
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_GOT
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_GOT
{ 0, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW0_PLT_PCREL
{ 0, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW0_PLT_PCREL
{ 16, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW1_PLT_PCREL
{ 16, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW1_PLT_PCREL
{ 31, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW2_PLT_PCREL
{ 31, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW2_PLT_PCREL
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_GOT
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_GOT
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_GOT
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_GOT
{ 31, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_GOT
{ 31, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_GOT
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_GD
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_GD
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_LE
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_LE
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IRELATIVE
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_IE
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_IE
{ 0, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL
{ 0, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL
{ 16, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL
{ 16, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL
{ 31, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL
{ 31, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPMOD64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPOFF64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_TPOFF64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPMOD32
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPOFF32
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_TPOFF32
{ 3, 31, 1, 27, 0}, // R_TILEGX_TLS_GD_CALL
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X0_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X1_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y0_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y1_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_IE_LOAD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X0_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X1_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y0_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y1_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_GNU_VTINHERIT
{ 0, 0, 0, 0, 0}, // R_TILEGX_GNU_VTENTRY
};
template<>
const Tilegx_relocate_functions<64, true>::Tilegx_howto
Tilegx_relocate_functions<64, true>::howto[elfcpp::R_TILEGX_NUM] =
{
{ 0, 0, 0, 0, 0}, // R_TILEGX_NONE
{ 0, 0, 0, 64, 0}, // R_TILEGX_64
{ 0, 0, 0, 32, 0}, // R_TILEGX_32
{ 0, 0, 0, 16, 0}, // R_TILEGX_16
{ 0, 0, 0, 8, 0}, // R_TILEGX_8
{ 0, 0, 1, 64, 0}, // R_TILEGX_64_PCREL
{ 0, 0, 1, 32, 0}, // R_TILEGX_32_PCREL
{ 0, 0, 1, 16, 0}, // R_TILEGX_16_PCREL
{ 0, 0, 1, 8, 0}, // R_TILEGX_8_PCREL
{ 0, 0, 0, 0, 0}, // R_TILEGX_HW0
{ 16, 0, 0, 0, 0}, // R_TILEGX_HW1
{ 32, 0, 0, 0, 0}, // R_TILEGX_HW2
{ 48, 0, 0, 0, 0}, // R_TILEGX_HW3
{ 0, 0, 0, 0, 0}, // R_TILEGX_HW0_LAST
{ 16, 0, 0, 0, 0}, // R_TILEGX_HW1_LAST
{ 32, 0, 0, 0, 0}, // R_TILEGX_HW2_LAST
{ 0, 0, 0, 0, 0}, // R_TILEGX_COPY
{ 0, 0, 0, 8, 0}, // R_TILEGX_GLOB_DAT
{ 0, 0, 0, 0, 0}, // R_TILEGX_JMP_SLOT
{ 0, 0, 0, 0, 0}, // R_TILEGX_RELATIVE
{ 3, 1, 1, 0, 0}, // R_TILEGX_BROFF_X1
{ 3, 31, 1, 27, 0}, // R_TILEGX_JUMPOFF_X1
{ 3, 31, 1, 27, 0}, // R_TILEGX_JUMPOFF_X1_PLT
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_Y0
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_Y1
{ 0, 1, 0, 8, 0}, // R_TILEGX_DEST_IMM8_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MT_IMM14_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MF_IMM14_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MMSTART_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_MMEND_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_Y0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_Y1
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0
{ 16, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW1
{ 16, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW1
{ 32, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW2
{ 32, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW2
{ 48, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW3
{ 48, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW3
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST
{ 32, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST
{ 32, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST
{ 0, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW0_PCREL
{ 0, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW0_PCREL
{ 16, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW1_PCREL
{ 16, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW1_PCREL
{ 32, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW2_PCREL
{ 32, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW2_PCREL
{ 48, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW3_PCREL
{ 48, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW3_PCREL
{ 0, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_PCREL
{ 0, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_PCREL
{ 16, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_PCREL
{ 16, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_PCREL
{ 32, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_PCREL
{ 32, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_PCREL
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_GOT
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_GOT
{ 0, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW0_PLT_PCREL
{ 0, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW0_PLT_PCREL
{ 16, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW1_PLT_PCREL
{ 16, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW1_PLT_PCREL
{ 32, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW2_PLT_PCREL
{ 32, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW2_PLT_PCREL
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_GOT
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_GOT
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_GOT
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_GOT
{ 32, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_GOT
{ 32, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_GOT
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_GD
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_GD
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_LE
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_LE
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IRELATIVE
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_IE
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_IE
{ 0, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL
{ 0, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL
{ 16, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL
{ 16, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL
{ 32, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL
{ 32, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPMOD64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPOFF64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_TPOFF64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPMOD32
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPOFF32
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_TPOFF32
{ 3, 31, 1, 27, 0}, // R_TILEGX_TLS_GD_CALL
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X0_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X1_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y0_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y1_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_IE_LOAD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X0_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X1_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y0_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y1_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_GNU_VTINHERIT
{ 0, 0, 0, 0, 0}, // R_TILEGX_GNU_VTENTRY
};
template<>
const Tilegx_relocate_functions<32, true>::Tilegx_howto
Tilegx_relocate_functions<32, true>::howto[elfcpp::R_TILEGX_NUM] =
{
{ 0, 0, 0, 0, 0}, // R_TILEGX_NONE
{ 0, 0, 0, 64, 0}, // R_TILEGX_64
{ 0, 0, 0, 32, 0}, // R_TILEGX_32
{ 0, 0, 0, 16, 0}, // R_TILEGX_16
{ 0, 0, 0, 8, 0}, // R_TILEGX_8
{ 0, 0, 1, 64, 0}, // R_TILEGX_64_PCREL
{ 0, 0, 1, 32, 0}, // R_TILEGX_32_PCREL
{ 0, 0, 1, 16, 0}, // R_TILEGX_16_PCREL
{ 0, 0, 1, 8, 0}, // R_TILEGX_8_PCREL
{ 0, 0, 0, 0, 0}, // R_TILEGX_HW0
{ 16, 0, 0, 0, 0}, // R_TILEGX_HW1
{ 31, 0, 0, 0, 0}, // R_TILEGX_HW2
{ 31, 0, 0, 0, 0}, // R_TILEGX_HW3
{ 0, 0, 0, 0, 0}, // R_TILEGX_HW0_LAST
{ 16, 0, 0, 0, 0}, // R_TILEGX_HW1_LAST
{ 31, 0, 0, 0, 0}, // R_TILEGX_HW2_LAST
{ 0, 0, 0, 0, 0}, // R_TILEGX_COPY
{ 0, 0, 0, 8, 0}, // R_TILEGX_GLOB_DAT
{ 0, 0, 0, 0, 0}, // R_TILEGX_JMP_SLOT
{ 0, 0, 0, 0, 0}, // R_TILEGX_RELATIVE
{ 3, 1, 1, 0, 0}, // R_TILEGX_BROFF_X1
{ 3, 31, 1, 27, 0}, // R_TILEGX_JUMPOFF_X1
{ 3, 31, 1, 27, 0}, // R_TILEGX_JUMPOFF_X1_PLT
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_Y0
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_IMM8_Y1
{ 0, 1, 0, 8, 0}, // R_TILEGX_DEST_IMM8_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MT_IMM14_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MF_IMM14_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_MMSTART_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_MMEND_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_X0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_X1
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_Y0
{ 0, 1, 0, 8, 0}, // R_TILEGX_SHAMT_Y1
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0
{ 16, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW1
{ 16, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW1
{ 31, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW2
{ 31, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW2
{ 31, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW3
{ 31, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW3
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST
{ 31, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST
{ 31, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST
{ 0, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW0_PCREL
{ 0, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW0_PCREL
{ 16, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW1_PCREL
{ 16, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW1_PCREL
{ 31, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW2_PCREL
{ 31, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW2_PCREL
{ 31, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW3_PCREL
{ 31, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW3_PCREL
{ 0, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_PCREL
{ 0, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_PCREL
{ 16, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_PCREL
{ 16, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_PCREL
{ 31, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_PCREL
{ 31, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_PCREL
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_GOT
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_GOT
{ 0, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW0_PLT_PCREL
{ 0, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW0_PLT_PCREL
{ 16, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW1_PLT_PCREL
{ 16, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW1_PLT_PCREL
{ 31, 12, 1, 16, 0}, // R_TILEGX_IMM16_X0_HW2_PLT_PCREL
{ 31, 43, 1, 16, 0}, // R_TILEGX_IMM16_X1_HW2_PLT_PCREL
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_GOT
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_GOT
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_GOT
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_GOT
{ 31, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_GOT
{ 31, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_GOT
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_GD
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_GD
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_LE
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_LE
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IRELATIVE
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 12, 0, 16, 0}, // R_TILEGX_IMM16_X0_HW0_TLS_IE
{ 0, 43, 0, 16, 0}, // R_TILEGX_IMM16_X1_HW0_TLS_IE
{ 0, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL
{ 0, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL
{ 16, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL
{ 16, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL
{ 31, 12, 1, 16, 1}, // R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL
{ 31, 43, 1, 16, 1}, // R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL
{ 0, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE
{ 0, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE
{ 16, 12, 0, 16, 1}, // R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE
{ 16, 43, 0, 16, 1}, // R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 0, 0, 0, 0}, // R_TILEGX_INVALID
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPMOD64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPOFF64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_TPOFF64
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPMOD32
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_DTPOFF32
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_TPOFF32
{ 3, 31, 1, 27, 0}, // R_TILEGX_TLS_GD_CALL
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X0_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X1_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y0_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y1_TLS_GD_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_TLS_IE_LOAD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X0_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_X1_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y0_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_IMM8_Y1_TLS_ADD
{ 0, 0, 0, 0, 0}, // R_TILEGX_GNU_VTINHERIT
{ 0, 0, 0, 0, 0}, // R_TILEGX_GNU_VTENTRY
};
// Get the GOT section, creating it if necessary.
template<int size, bool big_endian>
Output_data_got<size, big_endian>*
Target_tilegx<size, big_endian>::got_section(Symbol_table* symtab,
Layout* layout)
{
if (this->got_ == NULL)
{
gold_assert(symtab != NULL && layout != NULL);
// When using -z now, we can treat .got.plt as a relro section.
// Without -z now, it is modified after program startup by lazy
// PLT relocations.
bool is_got_plt_relro = parameters->options().now();
Output_section_order got_order = (is_got_plt_relro
? ORDER_RELRO
: ORDER_RELRO_LAST);
Output_section_order got_plt_order = (is_got_plt_relro
? ORDER_RELRO
: ORDER_NON_RELRO_FIRST);
this->got_ = new Output_data_got<size, big_endian>();
layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
this->got_, got_order, true);
// Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT.
this->global_offset_table_ =
symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
Symbol_table::PREDEFINED,
this->got_,
0, 0, elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
false, false);
if (parameters->options().shared()) {
// we need to keep the address of .dynamic section in the
// first got entry for .so
this->tilegx_dynamic_ =
symtab->define_in_output_data("_TILEGX_DYNAMIC_", NULL,
Symbol_table::PREDEFINED,
layout->dynamic_section(),
0, 0, elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
false, false);
this->got_->add_global(this->tilegx_dynamic_, GOT_TYPE_STANDARD);
} else
// for executable, just set the first entry to zero.
this->got_->set_current_data_size(size / 8);
this->got_plt_ = new Output_data_space(size / 8, "** GOT PLT");
layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
this->got_plt_, got_plt_order,
is_got_plt_relro);
// The first two entries are reserved.
this->got_plt_->set_current_data_size
(TILEGX_GOTPLT_RESERVE_COUNT * (size / 8));
if (!is_got_plt_relro)
{
// Those bytes can go into the relro segment.
layout->increase_relro(size / 8);
}
// If there are any IRELATIVE relocations, they get GOT entries
// in .got.plt after the jump slot entries.
this->got_irelative_
= new Output_data_space(size / 8, "** GOT IRELATIVE PLT");
layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
this->got_irelative_,
got_plt_order, is_got_plt_relro);
}
return this->got_;
}
// Get the dynamic reloc section, creating it if necessary.
template<int size, bool big_endian>
typename Target_tilegx<size, big_endian>::Reloc_section*
Target_tilegx<size, big_endian>::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_,
ORDER_DYNAMIC_RELOCS, false);
}
return this->rela_dyn_;
}
// Get the section to use for IRELATIVE relocs, creating it if
// necessary. These go in .rela.dyn, but only after all other dynamic
// relocations. They need to follow the other dynamic relocations so
// that they can refer to global variables initialized by those
// relocs.
template<int size, bool big_endian>
typename Target_tilegx<size, big_endian>::Reloc_section*
Target_tilegx<size, big_endian>::rela_irelative_section(Layout* layout)
{
if (this->rela_irelative_ == NULL)
{
// Make sure we have already created the dynamic reloc section.
this->rela_dyn_section(layout);
this->rela_irelative_ = new Reloc_section(false);
layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
elfcpp::SHF_ALLOC, this->rela_irelative_,
ORDER_DYNAMIC_RELOCS, false);
gold_assert(this->rela_dyn_->output_section()
== this->rela_irelative_->output_section());
}
return this->rela_irelative_;
}
// Initialize the PLT section.
template<int size, bool big_endian>
void
Output_data_plt_tilegx<size, big_endian>::init(Layout* layout)
{
this->rel_ = new Reloc_section(false);
layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
elfcpp::SHF_ALLOC, this->rel_,
ORDER_DYNAMIC_PLT_RELOCS, false);
}
template<int size, bool big_endian>
void
Output_data_plt_tilegx<size, big_endian>::do_adjust_output_section(
Output_section* os)
{
os->set_entsize(this->get_plt_entry_size());
}
// Add an entry to the PLT.
template<int size, bool big_endian>
void
Output_data_plt_tilegx<size, big_endian>::add_entry(Symbol_table* symtab,
Layout* layout, Symbol* gsym)
{
gold_assert(!gsym->has_plt_offset());
unsigned int plt_index;
off_t plt_offset;
section_offset_type got_offset;
unsigned int* pcount;
unsigned int reserved;
Output_data_space* got;
if (gsym->type() == elfcpp::STT_GNU_IFUNC
&& gsym->can_use_relative_reloc(false))
{
pcount = &this->irelative_count_;
reserved = 0;
got = this->got_irelative_;
}
else
{
pcount = &this->count_;
reserved = TILEGX_GOTPLT_RESERVE_COUNT;
got = this->got_plt_;
}
if (!this->is_data_size_valid())
{
plt_index = *pcount;
// TILEGX .plt section layout
//
// ----
// plt_header
// ----
// plt stub
// ----
// ...
// ----
//
// TILEGX .got.plt section layout
//
// ----
// reserv1
// ----
// reserv2
// ----
// entries for normal function
// ----
// ...
// ----
// entries for ifunc
// ----
// ...
// ----
if (got == this->got_irelative_)
plt_offset = plt_index * this->get_plt_entry_size();
else
plt_offset = (plt_index + 1) * this->get_plt_entry_size();
++*pcount;
got_offset = (plt_index + reserved) * (size / 8);
gold_assert(got_offset == got->current_data_size());
// Every PLT entry needs a GOT entry which points back to the PLT
// entry (this will be changed by the dynamic linker, normally
// lazily when the function is called).
got->set_current_data_size(got_offset + size / 8);
}
else
{
// FIXME: This is probably not correct for IRELATIVE relocs.
// For incremental updates, find an available slot.
plt_offset = this->free_list_.allocate(this->get_plt_entry_size(),
this->get_plt_entry_size(), 0);
if (plt_offset == -1)
gold_fallback(_("out of patch space (PLT);"
" relink with --incremental-full"));
// The GOT and PLT entries have a 1-1 correspondance, so the GOT offset
// can be calculated from the PLT index, adjusting for the three
// reserved entries at the beginning of the GOT.
plt_index = plt_offset / this->get_plt_entry_size() - 1;
got_offset = (plt_index + reserved) * (size / 8);
}
gsym->set_plt_offset(plt_offset);
// Every PLT entry needs a reloc.
this->add_relocation(symtab, layout, gsym, got_offset);
// Note that we don't need to save the symbol. The contents of the
// PLT are independent of which symbols are used. The symbols only
// appear in the relocations.
}
// Add an entry to the PLT for a local STT_GNU_IFUNC symbol. Return
// the PLT offset.
template<int size, bool big_endian>
unsigned int
Output_data_plt_tilegx<size, big_endian>::add_local_ifunc_entry(
Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* relobj,
unsigned int local_sym_index)
{
unsigned int plt_offset =
this->irelative_count_ * this->get_plt_entry_size();
++this->irelative_count_;
section_offset_type got_offset = this->got_irelative_->current_data_size();
// Every PLT entry needs a GOT entry which points back to the PLT
// entry.
this->got_irelative_->set_current_data_size(got_offset + size / 8);
// Every PLT entry needs a reloc.
Reloc_section* rela = this->rela_irelative(symtab, layout);
rela->add_symbolless_local_addend(relobj, local_sym_index,
elfcpp::R_TILEGX_IRELATIVE,
this->got_irelative_, got_offset, 0);
return plt_offset;
}
// Add the relocation for a PLT entry.
template<int size, bool big_endian>
void
Output_data_plt_tilegx<size, big_endian>::add_relocation(Symbol_table* symtab,
Layout* layout,
Symbol* gsym,
unsigned int got_offset)
{
if (gsym->type() == elfcpp::STT_GNU_IFUNC
&& gsym->can_use_relative_reloc(false))
{
Reloc_section* rela = this->rela_irelative(symtab, layout);
rela->add_symbolless_global_addend(gsym, elfcpp::R_TILEGX_IRELATIVE,
this->got_irelative_, got_offset, 0);
}
else
{
gsym->set_needs_dynsym_entry();
this->rel_->add_global(gsym, elfcpp::R_TILEGX_JMP_SLOT, this->got_plt_,
got_offset, 0);
}
}
// Return where the IRELATIVE relocations should go in the PLT. These
// follow the JUMP_SLOT and the TLSDESC relocations.
template<int size, bool big_endian>
typename Output_data_plt_tilegx<size, big_endian>::Reloc_section*
Output_data_plt_tilegx<size, big_endian>::rela_irelative(Symbol_table* symtab,
Layout* layout)
{
if (this->irelative_rel_ == NULL)
{
// case we see any later on.
this->irelative_rel_ = new Reloc_section(false);
layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
elfcpp::SHF_ALLOC, this->irelative_rel_,
ORDER_DYNAMIC_PLT_RELOCS, false);
gold_assert(this->irelative_rel_->output_section()
== this->rel_->output_section());
if (parameters->doing_static_link())
{
// A statically linked executable will only have a .rela.plt
// section to hold R_TILEGX_IRELATIVE relocs for
// STT_GNU_IFUNC symbols. The library will use these
// symbols to locate the IRELATIVE relocs at program startup
// time.
symtab->define_in_output_data("__rela_iplt_start", NULL,
Symbol_table::PREDEFINED,
this->irelative_rel_, 0, 0,
elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
elfcpp::STV_HIDDEN, 0, false, true);
symtab->define_in_output_data("__rela_iplt_end", NULL,
Symbol_table::PREDEFINED,
this->irelative_rel_, 0, 0,
elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
elfcpp::STV_HIDDEN, 0, true, true);
}
}
return this->irelative_rel_;
}
// Return the PLT address to use for a global symbol.
template<int size, bool big_endian>
uint64_t
Output_data_plt_tilegx<size, big_endian>::address_for_global(
const Symbol* gsym)
{
uint64_t offset = 0;
if (gsym->type() == elfcpp::STT_GNU_IFUNC
&& gsym->can_use_relative_reloc(false))
offset = (this->count_ + 1) * this->get_plt_entry_size();
return this->address() + offset + gsym->plt_offset();
}
// Return the PLT address to use for a local symbol. These are always
// IRELATIVE relocs.
template<int size, bool big_endian>
uint64_t
Output_data_plt_tilegx<size, big_endian>::address_for_local(
const Relobj* object,
unsigned int r_sym)
{
return (this->address()
+ (this->count_ + 1) * this->get_plt_entry_size()
+ object->local_plt_offset(r_sym));
}
// Set the final size.
template<int size, bool big_endian>
void
Output_data_plt_tilegx<size, big_endian>::set_final_data_size()
{
unsigned int count = this->count_ + this->irelative_count_;
this->set_data_size((count + 1) * this->get_plt_entry_size());
}
// The first entry in the PLT for an executable.
template<>
const unsigned char
Output_data_plt_tilegx<64, false>::first_plt_entry[plt_entry_size] =
{
0x00, 0x30, 0x48, 0x51,
0x6e, 0x43, 0xa0, 0x18, // { ld_add r28, r27, 8 }
0x00, 0x30, 0xbc, 0x35,
0x00, 0x40, 0xde, 0x9e, // { ld r27, r27 }
0xff, 0xaf, 0x30, 0x40,
0x60, 0x73, 0x6a, 0x28, // { info 10 ; jr r27 }
// padding
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
template<>
const unsigned char
Output_data_plt_tilegx<32, false>::first_plt_entry[plt_entry_size] =
{
0x00, 0x30, 0x48, 0x51,
0x6e, 0x23, 0x58, 0x18, // { ld4s_add r28, r27, 4 }
0x00, 0x30, 0xbc, 0x35,
0x00, 0x40, 0xde, 0x9c, // { ld4s r27, r27 }
0xff, 0xaf, 0x30, 0x40,
0x60, 0x73, 0x6a, 0x28, // { info 10 ; jr r27 }
// padding
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
template<>
const unsigned char
Output_data_plt_tilegx<64, true>::first_plt_entry[plt_entry_size] =
{
0x00, 0x30, 0x48, 0x51,
0x6e, 0x43, 0xa0, 0x18, // { ld_add r28, r27, 8 }
0x00, 0x30, 0xbc, 0x35,
0x00, 0x40, 0xde, 0x9e, // { ld r27, r27 }
0xff, 0xaf, 0x30, 0x40,
0x60, 0x73, 0x6a, 0x28, // { info 10 ; jr r27 }
// padding
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
template<>
const unsigned char
Output_data_plt_tilegx<32, true>::first_plt_entry[plt_entry_size] =
{
0x00, 0x30, 0x48, 0x51,
0x6e, 0x23, 0x58, 0x18, // { ld4s_add r28, r27, 4 }
0x00, 0x30, 0xbc, 0x35,
0x00, 0x40, 0xde, 0x9c, // { ld4s r27, r27 }
0xff, 0xaf, 0x30, 0x40,
0x60, 0x73, 0x6a, 0x28, // { info 10 ; jr r27 }
// padding
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
template<int size, bool big_endian>
void
Output_data_plt_tilegx<size, big_endian>::fill_first_plt_entry(
unsigned char* pov)
{
memcpy(pov, first_plt_entry, plt_entry_size);
}
// Subsequent entries in the PLT for an executable.
template<>
const unsigned char
Output_data_plt_tilegx<64, false>::plt_entry[plt_entry_size] =
{
0xdc, 0x0f, 0x00, 0x10,
0x0d, 0xf0, 0x6a, 0x28, // { moveli r28, 0 ; lnk r26 }
0xdb, 0x0f, 0x00, 0x10,
0x8e, 0x03, 0x00, 0x38, // { moveli r27, 0 ; shl16insli r28, r28, 0 }
0x9c, 0xc6, 0x0d, 0xd0,
0x6d, 0x03, 0x00, 0x38, // { add r28, r26, r28 ; shl16insli r27, r27, 0 }
0x9b, 0xb6, 0xc5, 0xad,
0xff, 0x57, 0xe0, 0x8e, // { add r27, r26, r27 ; info 10 ; ld r28, r28 }
0xdd, 0x0f, 0x00, 0x70,
0x80, 0x73, 0x6a, 0x28, // { shl16insli r29, zero, 0 ; jr r28 }
};
template<>
const unsigned char
Output_data_plt_tilegx<32, false>::plt_entry[plt_entry_size] =
{
0xdc, 0x0f, 0x00, 0x10,
0x0d, 0xf0, 0x6a, 0x28, // { moveli r28, 0 ; lnk r26 }
0xdb, 0x0f, 0x00, 0x10,
0x8e, 0x03, 0x00, 0x38, // { moveli r27, 0 ; shl16insli r28, r28, 0 }
0x9c, 0xc6, 0x0d, 0xd0,
0x6d, 0x03, 0x00, 0x38, // { add r28, r26, r28 ; shl16insli r27, r27, 0 }
0x9b, 0xb6, 0xc5, 0xad,
0xff, 0x57, 0xe0, 0x8c, // { add r27, r26, r27 ; info 10 ; ld4s r28, r28 }
0xdd, 0x0f, 0x00, 0x70,
0x80, 0x73, 0x6a, 0x28, // { shl16insli r29, zero, 0 ; jr r28 }
};
template<>
const unsigned char
Output_data_plt_tilegx<64, true>::plt_entry[plt_entry_size] =
{
0xdc, 0x0f, 0x00, 0x10,
0x0d, 0xf0, 0x6a, 0x28, // { moveli r28, 0 ; lnk r26 }
0xdb, 0x0f, 0x00, 0x10,
0x8e, 0x03, 0x00, 0x38, // { moveli r27, 0 ; shl16insli r28, r28, 0 }
0x9c, 0xc6, 0x0d, 0xd0,
0x6d, 0x03, 0x00, 0x38, // { add r28, r26, r28 ; shl16insli r27, r27, 0 }
0x9b, 0xb6, 0xc5, 0xad,
0xff, 0x57, 0xe0, 0x8e, // { add r27, r26, r27 ; info 10 ; ld r28, r28 }
0xdd, 0x0f, 0x00, 0x70,
0x80, 0x73, 0x6a, 0x28, // { shl16insli r29, zero, 0 ; jr r28 }
};
template<>
const unsigned char
Output_data_plt_tilegx<32, true>::plt_entry[plt_entry_size] =
{
0xdc, 0x0f, 0x00, 0x10,
0x0d, 0xf0, 0x6a, 0x28, // { moveli r28, 0 ; lnk r26 }
0xdb, 0x0f, 0x00, 0x10,
0x8e, 0x03, 0x00, 0x38, // { moveli r27, 0 ; shl16insli r28, r28, 0 }
0x9c, 0xc6, 0x0d, 0xd0,
0x6d, 0x03, 0x00, 0x38, // { add r28, r26, r28 ; shl16insli r27, r27, 0 }
0x9b, 0xb6, 0xc5, 0xad,
0xff, 0x57, 0xe0, 0x8c, // { add r27, r26, r27 ; info 10 ; ld4s r28, r28 }
0xdd, 0x0f, 0x00, 0x70,
0x80, 0x73, 0x6a, 0x28, // { shl16insli r29, zero, 0 ; jr r28 }
};
template<int size, bool big_endian>
void
Output_data_plt_tilegx<size, big_endian>::fill_plt_entry(
unsigned char* pov,
typename elfcpp::Elf_types<size>::Elf_Addr gotplt_base,
unsigned int got_offset,
typename elfcpp::Elf_types<size>::Elf_Addr plt_base,
unsigned int plt_offset, unsigned int plt_index)
{
const uint32_t TILEGX_IMM16_MASK = 0xFFFF;
const uint32_t TILEGX_X0_IMM16_BITOFF = 12;
const uint32_t TILEGX_X1_IMM16_BITOFF = 43;
typedef typename elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::Valtype
Valtype;
memcpy(pov, plt_entry, plt_entry_size);
// first bundle in plt stub - x0
Valtype* wv = reinterpret_cast<Valtype*>(pov);
Valtype val = elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::readval(wv);
Valtype reloc =
((gotplt_base + got_offset) - (plt_base + plt_offset + 8)) >> 16;
elfcpp::Elf_Xword dst_mask =
(elfcpp::Elf_Xword)(TILEGX_IMM16_MASK) << TILEGX_X0_IMM16_BITOFF;
val &= ~dst_mask;
reloc &= TILEGX_IMM16_MASK;
elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::writeval(wv,
val | (reloc<<TILEGX_X0_IMM16_BITOFF));
// second bundle in plt stub - x1
wv = reinterpret_cast<Valtype*>(pov + 8);
val = elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::readval(wv);
reloc = (gotplt_base + got_offset) - (plt_base + plt_offset + 8);
dst_mask = (elfcpp::Elf_Xword)(TILEGX_IMM16_MASK) << TILEGX_X1_IMM16_BITOFF;
val &= ~dst_mask;
reloc &= TILEGX_IMM16_MASK;
elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::writeval(wv,
val | (reloc<<TILEGX_X1_IMM16_BITOFF));
// second bundle in plt stub - x0
wv = reinterpret_cast<Valtype*>(pov + 8);
val = elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::readval(wv);
reloc = (gotplt_base - (plt_base + plt_offset + 8)) >> 16;
dst_mask = (elfcpp::Elf_Xword)(TILEGX_IMM16_MASK) << TILEGX_X0_IMM16_BITOFF;
val &= ~dst_mask;
reloc &= TILEGX_IMM16_MASK;
elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::writeval(wv,
val | (reloc<<TILEGX_X0_IMM16_BITOFF));
// third bundle in plt stub - x1
wv = reinterpret_cast<Valtype*>(pov + 16);
val = elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::readval(wv);
reloc = gotplt_base - (plt_base + plt_offset + 8);
dst_mask = (elfcpp::Elf_Xword)(TILEGX_IMM16_MASK) << TILEGX_X1_IMM16_BITOFF;
val &= ~dst_mask;
reloc &= TILEGX_IMM16_MASK;
elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::writeval(wv,
val | (reloc<<TILEGX_X1_IMM16_BITOFF));
// fifth bundle in plt stub - carry plt_index x0
wv = reinterpret_cast<Valtype*>(pov + 32);
val = elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::readval(wv);
dst_mask = (elfcpp::Elf_Xword)(TILEGX_IMM16_MASK) << TILEGX_X0_IMM16_BITOFF;
val &= ~dst_mask;
plt_index &= TILEGX_IMM16_MASK;
elfcpp::Swap<TILEGX_INST_BUNDLE_SIZE, big_endian>::writeval(wv,
val | (plt_index<<TILEGX_X0_IMM16_BITOFF));
}
// Write out the PLT. This uses the hand-coded instructions above.
template<int size, bool big_endian>
void
Output_data_plt_tilegx<size, big_endian>::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();
gold_assert(parameters->incremental_update()
|| (got_file_offset + this->got_plt_->data_size()
== this->got_irelative_->offset()));
const section_size_type got_size =
convert_to_section_size_type(this->got_plt_->data_size()
+ this->got_irelative_->data_size());
unsigned char* const got_view = of->get_output_view(got_file_offset,
got_size);
unsigned char* pov = oview;
// The base address of the .plt section.
typename elfcpp::Elf_types<size>::Elf_Addr plt_address = this->address();
typename elfcpp::Elf_types<size>::Elf_Addr got_address =
this->got_plt_->address();
this->fill_first_plt_entry(pov);
pov += this->get_plt_entry_size();
unsigned char* got_pov = got_view;
// first entry of .got.plt are set to -1
// second entry of .got.plt are set to 0
memset(got_pov, 0xff, size / 8);
got_pov += size / 8;
memset(got_pov, 0x0, size / 8);
got_pov += size / 8;
unsigned int plt_offset = this->get_plt_entry_size();
const unsigned int count = this->count_ + this->irelative_count_;
unsigned int got_offset = (size / 8) * TILEGX_GOTPLT_RESERVE_COUNT;
for (unsigned int plt_index = 0;
plt_index < count;
++plt_index,
pov += this->get_plt_entry_size(),
got_pov += size / 8,
plt_offset += this->get_plt_entry_size(),
got_offset += size / 8)
{
// Set and adjust the PLT entry itself.
this->fill_plt_entry(pov, got_address, got_offset,
plt_address, plt_offset, plt_index);
// Initialize entry in .got.plt to plt start address
elfcpp::Swap<size, big_endian>::writeval(got_pov, plt_address);
}
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);
}
// Create the PLT section.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::make_plt_section(Symbol_table* symtab,
Layout* layout)
{
if (this->plt_ == NULL)
{
// Create the GOT sections first.
this->got_section(symtab, layout);
// Ensure that .rela.dyn always appears before .rela.plt,
// becuase on TILE-Gx, .rela.dyn needs to include .rela.plt
// in it's range.
this->rela_dyn_section(layout);
this->plt_ = new Output_data_plt_tilegx<size, big_endian>(layout,
TILEGX_INST_BUNDLE_SIZE, this->got_, this->got_plt_,
this->got_irelative_);
layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_EXECINSTR),
this->plt_, ORDER_NON_RELRO_FIRST,
false);
// Make the sh_info field of .rela.plt point to .plt.
Output_section* rela_plt_os = this->plt_->rela_plt()->output_section();
rela_plt_os->set_info_section(this->plt_->output_section());
}
}
// Create a PLT entry for a global symbol.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::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(symtab, layout, gsym);
}
// Make a PLT entry for a local STT_GNU_IFUNC symbol.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::make_local_ifunc_plt_entry(
Symbol_table* symtab, Layout* layout,
Sized_relobj_file<size, big_endian>* relobj,
unsigned int local_sym_index)
{
if (relobj->local_has_plt_offset(local_sym_index))
return;
if (this->plt_ == NULL)
this->make_plt_section(symtab, layout);
unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout,
relobj,
local_sym_index);
relobj->set_local_plt_offset(local_sym_index, plt_offset);
}
// Return the number of entries in the PLT.
template<int size, bool big_endian>
unsigned int
Target_tilegx<size, big_endian>::plt_entry_count() const
{
if (this->plt_ == NULL)
return 0;
return this->plt_->entry_count();
}
// Return the offset of the first non-reserved PLT entry.
template<int size, bool big_endian>
unsigned int
Target_tilegx<size, big_endian>::first_plt_entry_offset() const
{
return this->plt_->first_plt_entry_offset();
}
// Return the size of each PLT entry.
template<int size, bool big_endian>
unsigned int
Target_tilegx<size, big_endian>::plt_entry_size() const
{
return this->plt_->get_plt_entry_size();
}
// Create the GOT and PLT sections for an incremental update.
template<int size, bool big_endian>
Output_data_got_base*
Target_tilegx<size, big_endian>::init_got_plt_for_update(Symbol_table* symtab,
Layout* layout,
unsigned int got_count,
unsigned int plt_count)
{
gold_assert(this->got_ == NULL);
this->got_ =
new Output_data_got<size, big_endian>((got_count
+ TILEGX_GOT_RESERVE_COUNT)
* (size / 8));
layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
this->got_, ORDER_RELRO_LAST,
true);
// Define _GLOBAL_OFFSET_TABLE_ at the start of the GOT.
this->global_offset_table_ =
symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
Symbol_table::PREDEFINED,
this->got_,
0, 0, elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
false, false);
if (parameters->options().shared()) {
this->tilegx_dynamic_ =
symtab->define_in_output_data("_TILEGX_DYNAMIC_", NULL,
Symbol_table::PREDEFINED,
layout->dynamic_section(),
0, 0, elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
false, false);
this->got_->add_global(this->tilegx_dynamic_, GOT_TYPE_STANDARD);
} else
this->got_->set_current_data_size(size / 8);
// Add the two reserved entries.
this->got_plt_
= new Output_data_space((plt_count + TILEGX_GOTPLT_RESERVE_COUNT)
* (size / 8), size / 8, "** GOT PLT");
layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
this->got_plt_, ORDER_NON_RELRO_FIRST,
false);
// If there are any IRELATIVE relocations, they get GOT entries in
// .got.plt after the jump slot.
this->got_irelative_
= new Output_data_space(0, size / 8, "** GOT IRELATIVE PLT");
layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
this->got_irelative_,
ORDER_NON_RELRO_FIRST, false);
// Create the PLT section.
this->plt_ = new Output_data_plt_tilegx<size, big_endian>(layout,
this->plt_entry_size(), this->got_, this->got_plt_, this->got_irelative_,
plt_count);
layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
this->plt_, ORDER_PLT, false);
// Make the sh_info field of .rela.plt point to .plt.
Output_section* rela_plt_os = this->plt_->rela_plt()->output_section();
rela_plt_os->set_info_section(this->plt_->output_section());
// Create the rela_dyn section.
this->rela_dyn_section(layout);
return this->got_;
}
// Reserve a GOT entry for a local symbol, and regenerate any
// necessary dynamic relocations.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::reserve_local_got_entry(
unsigned int got_index,
Sized_relobj<size, big_endian>* obj,
unsigned int r_sym,
unsigned int got_type)
{
unsigned int got_offset = (got_index + TILEGX_GOT_RESERVE_COUNT)
* (size / 8);
Reloc_section* rela_dyn = this->rela_dyn_section(NULL);
this->got_->reserve_local(got_index, obj, r_sym, got_type);
switch (got_type)
{
case GOT_TYPE_STANDARD:
if (parameters->options().output_is_position_independent())
rela_dyn->add_local_relative(obj, r_sym, elfcpp::R_TILEGX_RELATIVE,
this->got_, got_offset, 0, false);
break;
case GOT_TYPE_TLS_OFFSET:
rela_dyn->add_local(obj, r_sym,
size == 32 ? elfcpp::R_TILEGX_TLS_DTPOFF32
: elfcpp::R_TILEGX_TLS_DTPOFF64,
this->got_, got_offset, 0);
break;
case GOT_TYPE_TLS_PAIR:
this->got_->reserve_slot(got_index + 1);
rela_dyn->add_local(obj, r_sym,
size == 32 ? elfcpp::R_TILEGX_TLS_DTPMOD32
: elfcpp::R_TILEGX_TLS_DTPMOD64,
this->got_, got_offset, 0);
break;
case GOT_TYPE_TLS_DESC:
gold_fatal(_("TLS_DESC not yet supported for incremental linking"));
break;
default:
gold_unreachable();
}
}
// Reserve a GOT entry for a global symbol, and regenerate any
// necessary dynamic relocations.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::reserve_global_got_entry(
unsigned int got_index, Symbol* gsym, unsigned int got_type)
{
unsigned int got_offset = (got_index + TILEGX_GOT_RESERVE_COUNT)
* (size / 8);
Reloc_section* rela_dyn = this->rela_dyn_section(NULL);
this->got_->reserve_global(got_index, gsym, got_type);
switch (got_type)
{
case GOT_TYPE_STANDARD:
if (!gsym->final_value_is_known())
{
if (gsym->is_from_dynobj()
|| gsym->is_undefined()
|| gsym->is_preemptible()
|| gsym->type() == elfcpp::STT_GNU_IFUNC)
rela_dyn->add_global(gsym, elfcpp::R_TILEGX_GLOB_DAT,
this->got_, got_offset, 0);
else
rela_dyn->add_global_relative(gsym, elfcpp::R_TILEGX_RELATIVE,
this->got_, got_offset, 0, false);
}
break;
case GOT_TYPE_TLS_OFFSET:
rela_dyn->add_global_relative(gsym,
size == 32 ? elfcpp::R_TILEGX_TLS_TPOFF32
: elfcpp::R_TILEGX_TLS_TPOFF64,
this->got_, got_offset, 0, false);
break;
case GOT_TYPE_TLS_PAIR:
this->got_->reserve_slot(got_index + 1);
rela_dyn->add_global_relative(gsym,
size == 32 ? elfcpp::R_TILEGX_TLS_DTPMOD32
: elfcpp::R_TILEGX_TLS_DTPMOD64,
this->got_, got_offset, 0, false);
rela_dyn->add_global_relative(gsym,
size == 32 ? elfcpp::R_TILEGX_TLS_DTPOFF32
: elfcpp::R_TILEGX_TLS_DTPOFF64,
this->got_, got_offset + size / 8,
0, false);
break;
case GOT_TYPE_TLS_DESC:
gold_fatal(_("TLS_DESC not yet supported for TILEGX"));
break;
default:
gold_unreachable();
}
}
// Register an existing PLT entry for a global symbol.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::register_global_plt_entry(
Symbol_table* symtab, Layout* layout, unsigned int plt_index, Symbol* gsym)
{
gold_assert(this->plt_ != NULL);
gold_assert(!gsym->has_plt_offset());
this->plt_->reserve_slot(plt_index);
gsym->set_plt_offset((plt_index + 1) * this->plt_entry_size());
unsigned int got_offset = (plt_index + 2) * (size / 8);
this->plt_->add_relocation(symtab, layout, gsym, got_offset);
}
// Force a COPY relocation for a given symbol.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::emit_copy_reloc(
Symbol_table* symtab, Symbol* sym, Output_section* os, off_t offset)
{
this->copy_relocs_.emit_copy_reloc(symtab,
symtab->get_sized_symbol<size>(sym),
os,
offset,
this->rela_dyn_section(NULL));
}
// Create a GOT entry for the TLS module index.
template<int size, bool big_endian>
unsigned int
Target_tilegx<size, big_endian>::got_mod_index_entry(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* 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<size, big_endian>* got
= this->got_section(symtab, layout);
unsigned int got_offset = got->add_constant(0);
rela_dyn->add_local(object, 0,
size == 32 ? elfcpp::R_TILEGX_TLS_DTPMOD32
: elfcpp::R_TILEGX_TLS_DTPMOD64, got,
got_offset, 0);
got->add_constant(0);
this->got_mod_index_offset_ = got_offset;
}
return this->got_mod_index_offset_;
}
// Optimize the TLS relocation type based on what we know about the
// symbol. IS_FINAL is true if the final address of this symbol is
// known at link time.
//
// the transformation rules is described below:
//
// compiler GD reference
// |
// V
// moveli tmp, hw1_last_tls_gd(x) X0/X1
// shl16insli r0, tmp, hw0_tls_gd(x) X0/X1
// addi r0, got, tls_add(x) Y0/Y1/X0/X1
// jal tls_gd_call(x) X1
// addi adr, r0, tls_gd_add(x) Y0/Y1/X0/X1
//
// linker tranformation of GD insn sequence
// |
// V
// ==> GD:
// moveli tmp, hw1_last_tls_gd(x) X0/X1
// shl16insli r0, tmp, hw0_tls_gd(x) X0/X1
// add r0, got, r0 Y0/Y1/X0/X1
// jal plt(__tls_get_addr) X1
// move adr, r0 Y0/Y1/X0/X1
// ==> IE:
// moveli tmp, hw1_last_tls_ie(x) X0/X1
// shl16insli r0, tmp, hw0_tls_ie(x) X0/X1
// add r0, got, r0 Y0/Y1/X0/X1
// ld r0, r0 X1
// add adr, r0, tp Y0/Y1/X0/X1
// ==> LE:
// moveli tmp, hw1_last_tls_le(x) X0/X1
// shl16insli r0, tmp, hw0_tls_le(x) X0/X1
// move r0, r0 Y0/Y1/X0/X1
// move r0, r0 Y0/Y1/X0/X1
// add adr, r0, tp Y0/Y1/X0/X1
//
//
// compiler IE reference
// |
// V
// moveli tmp, hw1_last_tls_ie(x) X0/X1
// shl16insli tmp, tmp, hw0_tls_ie(x) X0/X1
// addi tmp, got, tls_add(x) Y0/Y1/X0/X1
// ld_tls tmp, tmp, tls_ie_load(x) X1
// add adr, tmp, tp Y0/Y1/X0/X1
//
// linker transformation for IE insn sequence
// |
// V
// ==> IE:
// moveli tmp, hw1_last_tls_ie(x) X0/X1
// shl16insli tmp, tmp, hw0_tls_ie(x) X0/X1
// add tmp, got, tmp Y0/Y1/X0/X1
// ld tmp, tmp X1
// add adr, tmp, tp Y0/Y1/X0/X1
// ==> LE:
// moveli tmp, hw1_last_tls_le(x) X0/X1
// shl16insli tmp, tmp, hw0_tls_le(x) X0/X1
// move tmp, tmp Y0/Y1/X0/X1
// move tmp, tmp Y0/Y1/X0/X1
//
//
// compiler LE reference
// |
// V
// moveli tmp, hw1_last_tls_le(x) X0/X1
// shl16insli tmp, tmp, hw0_tls_le(x) X0/X1
// add adr, tmp, tp Y0/Y1/X0/X1
template<int size, bool big_endian>
tls::Tls_optimization
Target_tilegx<size, big_endian>::optimize_tls_reloc(bool is_final, int r_type)
{
// If we are generating a shared library, then we can't do anything
// in the linker.
if (parameters->options().shared())
return tls::TLSOPT_NONE;
switch (r_type)
{
// unique GD relocations
case elfcpp::R_TILEGX_TLS_GD_CALL:
case elfcpp::R_TILEGX_IMM8_X0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
// These are General-Dynamic which permits fully general TLS
// access. Since we know that we are generating an executable,
// we can convert this to Initial-Exec. If we also know that
// this is a local symbol, we can further switch to Local-Exec.
if (is_final)
return tls::TLSOPT_TO_LE;
return tls::TLSOPT_TO_IE;
// unique IE relocations
case elfcpp::R_TILEGX_TLS_IE_LOAD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
// These are Initial-Exec relocs which get the thread offset
// from the GOT. If we know that we are linking against the
// local symbol, we can switch to Local-Exec, which links the
// thread offset into the instruction.
if (is_final)
return tls::TLSOPT_TO_LE;
return tls::TLSOPT_NONE;
// could be created for both GD and IE
// but they are expanded into the same
// instruction in GD and IE.
case elfcpp::R_TILEGX_IMM8_X0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_ADD:
if (is_final)
return tls::TLSOPT_TO_LE;
return tls::TLSOPT_NONE;
// unique LE relocations
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE:
// When we already have Local-Exec, there is nothing further we
// can do.
return tls::TLSOPT_NONE;
default:
gold_unreachable();
}
}
// Get the Reference_flags for a particular relocation.
template<int size, bool big_endian>
int
Target_tilegx<size, big_endian>::Scan::get_reference_flags(unsigned int r_type)
{
switch (r_type)
{
case elfcpp::R_TILEGX_NONE:
case elfcpp::R_TILEGX_GNU_VTINHERIT:
case elfcpp::R_TILEGX_GNU_VTENTRY:
// No symbol reference.
return 0;
case elfcpp::R_TILEGX_64:
case elfcpp::R_TILEGX_32:
case elfcpp::R_TILEGX_16:
case elfcpp::R_TILEGX_8:
return Symbol::ABSOLUTE_REF;
case elfcpp::R_TILEGX_BROFF_X1:
case elfcpp::R_TILEGX_64_PCREL:
case elfcpp::R_TILEGX_32_PCREL:
case elfcpp::R_TILEGX_16_PCREL:
case elfcpp::R_TILEGX_8_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PCREL:
return Symbol::RELATIVE_REF;
case elfcpp::R_TILEGX_JUMPOFF_X1:
case elfcpp::R_TILEGX_JUMPOFF_X1_PLT:
case elfcpp::R_TILEGX_IMM16_X0_HW0_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL:
return Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF;
case elfcpp::R_TILEGX_IMM16_X0_HW0:
case elfcpp::R_TILEGX_IMM16_X1_HW0:
case elfcpp::R_TILEGX_IMM16_X0_HW1:
case elfcpp::R_TILEGX_IMM16_X1_HW1:
case elfcpp::R_TILEGX_IMM16_X0_HW2:
case elfcpp::R_TILEGX_IMM16_X1_HW2:
case elfcpp::R_TILEGX_IMM16_X0_HW3:
case elfcpp::R_TILEGX_IMM16_X1_HW3:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST:
return Symbol::ABSOLUTE_REF;
case elfcpp::R_TILEGX_IMM16_X0_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_GOT:
// Absolute in GOT.
return Symbol::ABSOLUTE_REF;
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_TLS_DTPOFF64:
case elfcpp::R_TILEGX_TLS_DTPMOD32:
case elfcpp::R_TILEGX_TLS_DTPOFF32:
case elfcpp::R_TILEGX_TLS_TPOFF32:
case elfcpp::R_TILEGX_TLS_GD_CALL:
case elfcpp::R_TILEGX_IMM8_X0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_GD_ADD:
case elfcpp::R_TILEGX_TLS_IE_LOAD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_ADD:
return Symbol::TLS_REF;
case elfcpp::R_TILEGX_COPY:
case elfcpp::R_TILEGX_GLOB_DAT:
case elfcpp::R_TILEGX_JMP_SLOT:
case elfcpp::R_TILEGX_RELATIVE:
case elfcpp::R_TILEGX_TLS_TPOFF64:
case elfcpp::R_TILEGX_TLS_DTPMOD64:
default:
// Not expected. We will give an error later.
return 0;
}
}
// Report an unsupported relocation against a local symbol.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::Scan::unsupported_reloc_local(
Sized_relobj_file<size, big_endian>* object,
unsigned int r_type)
{
gold_error(_("%s: unsupported reloc %u against local symbol"),
object->name().c_str(), r_type);
}
// We are about to emit a dynamic relocation of type R_TYPE. If the
// dynamic linker does not support it, issue an error.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::Scan::check_non_pic(Relobj* object,
unsigned int r_type)
{
switch (r_type)
{
// These are the relocation types supported by glibc for tilegx
// which should always work.
case elfcpp::R_TILEGX_RELATIVE:
case elfcpp::R_TILEGX_GLOB_DAT:
case elfcpp::R_TILEGX_JMP_SLOT:
case elfcpp::R_TILEGX_TLS_DTPMOD64:
case elfcpp::R_TILEGX_TLS_DTPOFF64:
case elfcpp::R_TILEGX_TLS_TPOFF64:
case elfcpp::R_TILEGX_8:
case elfcpp::R_TILEGX_16:
case elfcpp::R_TILEGX_32:
case elfcpp::R_TILEGX_64:
case elfcpp::R_TILEGX_COPY:
case elfcpp::R_TILEGX_IMM16_X0_HW0:
case elfcpp::R_TILEGX_IMM16_X1_HW0:
case elfcpp::R_TILEGX_IMM16_X0_HW1:
case elfcpp::R_TILEGX_IMM16_X1_HW1:
case elfcpp::R_TILEGX_IMM16_X0_HW2:
case elfcpp::R_TILEGX_IMM16_X1_HW2:
case elfcpp::R_TILEGX_IMM16_X0_HW3:
case elfcpp::R_TILEGX_IMM16_X1_HW3:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST:
case elfcpp::R_TILEGX_BROFF_X1:
case elfcpp::R_TILEGX_JUMPOFF_X1:
case elfcpp::R_TILEGX_IMM16_X0_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PCREL:
return;
default:
// This prevents us from issuing more than one error per reloc
// section. But we can still wind up issuing more than one
// error per object file.
if (this->issued_non_pic_error_)
return;
gold_assert(parameters->options().output_is_position_independent());
object->error(_("requires unsupported dynamic reloc %u; "
"recompile with -fPIC"),
r_type);
this->issued_non_pic_error_ = true;
return;
case elfcpp::R_TILEGX_NONE:
gold_unreachable();
}
}
// Return whether we need to make a PLT entry for a relocation of the
// given type against a STT_GNU_IFUNC symbol.
template<int size, bool big_endian>
bool
Target_tilegx<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
Sized_relobj_file<size, big_endian>* object, unsigned int r_type)
{
int flags = Scan::get_reference_flags(r_type);
if (flags & Symbol::TLS_REF)
gold_error(_("%s: unsupported TLS reloc %u for IFUNC symbol"),
object->name().c_str(), r_type);
return flags != 0;
}
// Scan a relocation for a local symbol.
template<int size, bool big_endian>
inline void
Target_tilegx<size, big_endian>::Scan::local(Symbol_table* symtab,
Layout* layout,
Target_tilegx<size, big_endian>* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
const elfcpp::Sym<size, big_endian>& lsym,
bool is_discarded)
{
if (is_discarded)
return;
// A local STT_GNU_IFUNC symbol may require a PLT entry.
bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
}
switch (r_type)
{
case elfcpp::R_TILEGX_NONE:
case elfcpp::R_TILEGX_GNU_VTINHERIT:
case elfcpp::R_TILEGX_GNU_VTENTRY:
break;
// If building a shared library (or a position-independent
// executable), because the runtime address needs plus
// the module base address, so generate a R_TILEGX_RELATIVE.
case elfcpp::R_TILEGX_32:
case elfcpp::R_TILEGX_64:
if (parameters->options().output_is_position_independent())
{
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,
elfcpp::R_TILEGX_RELATIVE,
output_section, data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend(), is_ifunc);
}
break;
// If building a shared library (or a position-independent
// executable), we need to create a dynamic relocation for this
// location.
case elfcpp::R_TILEGX_8:
case elfcpp::R_TILEGX_16:
case elfcpp::R_TILEGX_IMM16_X0_HW0:
case elfcpp::R_TILEGX_IMM16_X1_HW0:
case elfcpp::R_TILEGX_IMM16_X0_HW1:
case elfcpp::R_TILEGX_IMM16_X1_HW1:
case elfcpp::R_TILEGX_IMM16_X0_HW2:
case elfcpp::R_TILEGX_IMM16_X1_HW2:
case elfcpp::R_TILEGX_IMM16_X0_HW3:
case elfcpp::R_TILEGX_IMM16_X1_HW3:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST:
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<size>(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);
rela_dyn->add_symbolless_local_addend(object, r_sym, r_type,
output_section,
data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend());
}
}
break;
// R_TILEGX_JUMPOFF_X1_PLT against local symbol
// may happen for ifunc case.
case elfcpp::R_TILEGX_JUMPOFF_X1_PLT:
case elfcpp::R_TILEGX_JUMPOFF_X1:
case elfcpp::R_TILEGX_64_PCREL:
case elfcpp::R_TILEGX_32_PCREL:
case elfcpp::R_TILEGX_16_PCREL:
case elfcpp::R_TILEGX_8_PCREL:
case elfcpp::R_TILEGX_BROFF_X1:
case elfcpp::R_TILEGX_IMM16_X0_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL:
break;
case elfcpp::R_TILEGX_IMM16_X0_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_GOT:
{
// The symbol requires a GOT entry.
Output_data_got<size, big_endian>* got
= target->got_section(symtab, layout);
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
// lets function pointers compare correctly with shared
// libraries. Otherwise we would need an IRELATIVE reloc.
bool is_new;
if (is_ifunc)
is_new = got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD);
else
is_new = got->add_local(object, r_sym, GOT_TYPE_STANDARD);
if (is_new)
{
// tilegx dynamic linker will not update local got entry,
// so, if we are generating a shared object, we need to add a
// dynamic relocation for this symbol's GOT entry to inform
// dynamic linker plus the load base explictly.
if (parameters->options().output_is_position_independent())
{
unsigned int got_offset
= object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_local_relative(object, r_sym,
r_type,
got, got_offset, 0, is_ifunc);
}
}
}
break;
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_TLS_GD_CALL:
case elfcpp::R_TILEGX_IMM8_X0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_GD_ADD:
case elfcpp::R_TILEGX_TLS_IE_LOAD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_ADD:
{
bool output_is_shared = parameters->options().shared();
const tls::Tls_optimization opt_t =
Target_tilegx<size, big_endian>::optimize_tls_reloc(
!output_is_shared, r_type);
switch (r_type)
{
case elfcpp::R_TILEGX_TLS_GD_CALL:
// FIXME: predefine __tls_get_addr
//
// R_TILEGX_TLS_GD_CALL implicitly reference __tls_get_addr,
// while all other target, x86/arm/mips/powerpc/sparc
// generate tls relocation against __tls_get_addr explictly,
// so for TILEGX, we need the following hack.
if (opt_t == tls::TLSOPT_NONE) {
if (!target->tls_get_addr_sym_defined_) {
Symbol* sym = NULL;
options::parse_set(NULL, "__tls_get_addr",
(gold::options::String_set*)
&parameters->options().undefined());
symtab->add_undefined_symbols_from_command_line(layout);
target->tls_get_addr_sym_defined_ = true;
sym = symtab->lookup("__tls_get_addr");
sym->set_in_reg();
}
target->make_plt_entry(symtab, layout,
symtab->lookup("__tls_get_addr"));
}
break;
// only make effect when applying relocation
case elfcpp::R_TILEGX_TLS_IE_LOAD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_GD_ADD:
break;
// GD: requires two GOT entry for module index and offset
// IE: requires one GOT entry for tp-relative offset
// LE: shouldn't happen for global symbol
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
{
if (opt_t == tls::TLSOPT_NONE) {
Output_data_got<size, big_endian> *got
= target->got_section(symtab, layout);
unsigned int r_sym
= elfcpp::elf_r_sym<size>(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_rel(object, r_sym, shndx,
GOT_TYPE_TLS_PAIR,
target->rela_dyn_section(layout),
size == 32
? elfcpp::R_TILEGX_TLS_DTPMOD32
: elfcpp::R_TILEGX_TLS_DTPMOD64);
} else if (opt_t == tls::TLSOPT_TO_IE) {
Output_data_got<size, big_endian>* got
= target->got_section(symtab, layout);
Reloc_section* rela_dyn
= target->rela_dyn_section(layout);
unsigned int r_sym
= elfcpp::elf_r_sym<size>(reloc.get_r_info());
unsigned int off = got->add_constant(0);
object->set_local_got_offset(r_sym,
GOT_TYPE_TLS_OFFSET,off);
rela_dyn->add_symbolless_local_addend(object, r_sym,
size == 32
? elfcpp::R_TILEGX_TLS_TPOFF32
: elfcpp::R_TILEGX_TLS_TPOFF64,
got, off, 0);
} else if (opt_t != tls::TLSOPT_TO_LE)
// only TO_LE is allowed for local symbol
unsupported_reloc_local(object, r_type);
}
break;
// IE
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
{
layout->set_has_static_tls();
if (opt_t == tls::TLSOPT_NONE) {
Output_data_got<size, big_endian>* got
= target->got_section(symtab, layout);
Reloc_section* rela_dyn
= target->rela_dyn_section(layout);
unsigned int r_sym
= elfcpp::elf_r_sym<size>(reloc.get_r_info());
unsigned int off = got->add_constant(0);
object->set_local_got_offset(r_sym,
GOT_TYPE_TLS_OFFSET, off);
rela_dyn->add_symbolless_local_addend(object, r_sym,
size == 32
? elfcpp::R_TILEGX_TLS_TPOFF32
: elfcpp::R_TILEGX_TLS_TPOFF64,
got, off, 0);
} else if (opt_t != tls::TLSOPT_TO_LE)
unsupported_reloc_local(object, r_type);
}
break;
// LE
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE:
layout->set_has_static_tls();
if (parameters->options().shared()) {
// defer to dynamic linker
gold_assert(lsym.get_st_type() != elfcpp::STT_SECTION);
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_symbolless_local_addend(object, r_sym, r_type,
output_section, data_shndx,
reloc.get_r_offset(), 0);
}
break;
default:
gold_unreachable();
}
}
break;
case elfcpp::R_TILEGX_COPY:
case elfcpp::R_TILEGX_GLOB_DAT:
case elfcpp::R_TILEGX_JMP_SLOT:
case elfcpp::R_TILEGX_RELATIVE:
// These are outstanding tls relocs, which are unexpected when linking
case elfcpp::R_TILEGX_TLS_TPOFF32:
case elfcpp::R_TILEGX_TLS_TPOFF64:
case elfcpp::R_TILEGX_TLS_DTPMOD32:
case elfcpp::R_TILEGX_TLS_DTPMOD64:
case elfcpp::R_TILEGX_TLS_DTPOFF32:
case elfcpp::R_TILEGX_TLS_DTPOFF64:
gold_error(_("%s: unexpected reloc %u in object file"),
object->name().c_str(), r_type);
break;
default:
gold_error(_("%s: unsupported reloc %u against local symbol"),
object->name().c_str(), r_type);
break;
}
}
// Report an unsupported relocation against a global symbol.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::Scan::unsupported_reloc_global(
Sized_relobj_file<size, big_endian>* 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());
}
// Returns true if this relocation type could be that of a function pointer.
template<int size, bool big_endian>
inline bool
Target_tilegx<size, big_endian>::Scan::possible_function_pointer_reloc(
unsigned int r_type)
{
switch (r_type)
{
case elfcpp::R_TILEGX_IMM16_X0_HW0:
case elfcpp::R_TILEGX_IMM16_X1_HW0:
case elfcpp::R_TILEGX_IMM16_X0_HW1:
case elfcpp::R_TILEGX_IMM16_X1_HW1:
case elfcpp::R_TILEGX_IMM16_X0_HW2:
case elfcpp::R_TILEGX_IMM16_X1_HW2:
case elfcpp::R_TILEGX_IMM16_X0_HW3:
case elfcpp::R_TILEGX_IMM16_X1_HW3:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_GOT:
{
return true;
}
}
return false;
}
// For safe ICF, scan a relocation for a local symbol to check if it
// corresponds to a function pointer being taken. In that case mark
// the function whose pointer was taken as not foldable.
template<int size, bool big_endian>
inline bool
Target_tilegx<size, big_endian>::Scan::local_reloc_may_be_function_pointer(
Symbol_table* ,
Layout* ,
Target_tilegx<size, big_endian>* ,
Sized_relobj_file<size, big_endian>* ,
unsigned int ,
Output_section* ,
const elfcpp::Rela<size, big_endian>& ,
unsigned int r_type,
const elfcpp::Sym<size, big_endian>&)
{
return possible_function_pointer_reloc(r_type);
}
// For safe ICF, scan a relocation for a global symbol to check if it
// corresponds to a function pointer being taken. In that case mark
// the function whose pointer was taken as not foldable.
template<int size, bool big_endian>
inline bool
Target_tilegx<size, big_endian>::Scan::global_reloc_may_be_function_pointer(
Symbol_table*,
Layout* ,
Target_tilegx<size, big_endian>* ,
Sized_relobj_file<size, big_endian>* ,
unsigned int ,
Output_section* ,
const elfcpp::Rela<size, big_endian>& ,
unsigned int r_type,
Symbol* gsym)
{
// GOT is not a function.
if (strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
return false;
// 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));
}
// Scan a relocation for a global symbol.
template<int size, bool big_endian>
inline void
Target_tilegx<size, big_endian>::Scan::global(Symbol_table* symtab,
Layout* layout,
Target_tilegx<size, big_endian>* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
Symbol* gsym)
{
// A reference to _GLOBAL_OFFSET_TABLE_ implies that we need a got
// section. We check here to avoid creating a dynamic reloc against
// _GLOBAL_OFFSET_TABLE_.
if (!target->has_got_section()
&& strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
target->got_section(symtab, layout);
// A STT_GNU_IFUNC symbol may require a PLT entry.
if (gsym->type() == elfcpp::STT_GNU_IFUNC
&& this->reloc_needs_plt_for_ifunc(object, r_type))
target->make_plt_entry(symtab, layout, gsym);
switch (r_type)
{
case elfcpp::R_TILEGX_NONE:
case elfcpp::R_TILEGX_GNU_VTINHERIT:
case elfcpp::R_TILEGX_GNU_VTENTRY:
break;
case elfcpp::R_TILEGX_DEST_IMM8_X1:
case elfcpp::R_TILEGX_IMM16_X0_HW0:
case elfcpp::R_TILEGX_IMM16_X1_HW0:
case elfcpp::R_TILEGX_IMM16_X0_HW1:
case elfcpp::R_TILEGX_IMM16_X1_HW1:
case elfcpp::R_TILEGX_IMM16_X0_HW2:
case elfcpp::R_TILEGX_IMM16_X1_HW2:
case elfcpp::R_TILEGX_IMM16_X0_HW3:
case elfcpp::R_TILEGX_IMM16_X1_HW3:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST:
case elfcpp::R_TILEGX_64:
case elfcpp::R_TILEGX_32:
case elfcpp::R_TILEGX_16:
case elfcpp::R_TILEGX_8:
{
// Make a PLT entry if necessary.
if (gsym->needs_plt_entry())
{
target->make_plt_entry(symtab, layout, gsym);
// Since this is not a PC-relative relocation, we may be
// taking the address of a function. In that case we need to
// set the entry in the dynamic symbol table to the address of
// the PLT entry.
if (gsym->is_from_dynobj() && !parameters->options().shared())
gsym->set_needs_dynsym_value();
}
// Make a dynamic relocation if necessary.
if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
{
if (!parameters->options().output_is_position_independent()
&& gsym->may_need_copy_reloc())
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
}
else if (((size == 64 && r_type == elfcpp::R_TILEGX_64)
|| (size == 32 && r_type == elfcpp::R_TILEGX_32))
&& gsym->type() == elfcpp::STT_GNU_IFUNC
&& gsym->can_use_relative_reloc(false)
&& !gsym->is_from_dynobj()
&& !gsym->is_undefined()
&& !gsym->is_preemptible())
{
// Use an IRELATIVE reloc for a locally defined
// STT_GNU_IFUNC symbol. This makes a function
// address in a PIE executable match the address in a
// shared library that it links against.
Reloc_section* rela_dyn =
target->rela_irelative_section(layout);
unsigned int r_type = elfcpp::R_TILEGX_IRELATIVE;
rela_dyn->add_symbolless_global_addend(gsym, r_type,
output_section, object,
data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend());
} else if ((r_type == elfcpp::R_TILEGX_64
|| r_type == elfcpp::R_TILEGX_32)
&& gsym->can_use_relative_reloc(false))
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_global_relative(gsym, elfcpp::R_TILEGX_RELATIVE,
output_section, object,
data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend(), false);
}
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_TILEGX_BROFF_X1:
case elfcpp::R_TILEGX_IMM16_X0_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PCREL:
case elfcpp::R_TILEGX_64_PCREL:
case elfcpp::R_TILEGX_32_PCREL:
case elfcpp::R_TILEGX_16_PCREL:
case elfcpp::R_TILEGX_8_PCREL:
{
// Make a PLT entry if necessary.
if (gsym->needs_plt_entry())
target->make_plt_entry(symtab, layout, gsym);
// Make a dynamic relocation if necessary.
if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
{
if (parameters->options().output_is_executable()
&& gsym->may_need_copy_reloc())
{
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_TILEGX_IMM16_X0_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_GOT:
{
// The symbol requires a GOT entry.
Output_data_got<size, big_endian>* got
= target->got_section(symtab, layout);
if (gsym->final_value_is_known())
{
// For a STT_GNU_IFUNC symbol we want the PLT address.
if (gsym->type() == elfcpp::STT_GNU_IFUNC)
got->add_global_plt(gsym, GOT_TYPE_STANDARD);
else
got->add_global(gsym, GOT_TYPE_STANDARD);
}
else
{
// If this symbol is not fully resolved, we need to add a
// dynamic relocation for it.
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
// Use a GLOB_DAT rather than a RELATIVE reloc if:
//
// 1) The symbol may be defined in some other module.
//
// 2) We are building a shared library and this is a
// protected symbol; using GLOB_DAT means that the dynamic
// linker can use the address of the PLT in the main
// executable when appropriate so that function address
// comparisons work.
//
// 3) This is a STT_GNU_IFUNC symbol in position dependent
// code, again so that function address comparisons work.
if (gsym->is_from_dynobj()
|| gsym->is_undefined()
|| gsym->is_preemptible()
|| (gsym->visibility() == elfcpp::STV_PROTECTED
&& parameters->options().shared())
|| (gsym->type() == elfcpp::STT_GNU_IFUNC
&& parameters->options().output_is_position_independent()))
got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, rela_dyn,
elfcpp::R_TILEGX_GLOB_DAT);
else
{
// For a STT_GNU_IFUNC symbol we want to write the PLT
// offset into the GOT, so that function pointer
// comparisons work correctly.
bool is_new;
if (gsym->type() != elfcpp::STT_GNU_IFUNC)
is_new = got->add_global(gsym, GOT_TYPE_STANDARD);
else
{
is_new = got->add_global_plt(gsym, GOT_TYPE_STANDARD);
// Tell the dynamic linker to use the PLT address
// when resolving relocations.
if (gsym->is_from_dynobj()
&& !parameters->options().shared())
gsym->set_needs_dynsym_value();
}
if (is_new)
{
unsigned int got_off = gsym->got_offset(GOT_TYPE_STANDARD);
rela_dyn->add_global_relative(gsym,
r_type,
got, got_off, 0, false);
}
}
}
}
break;
// a minor difference here for R_TILEGX_JUMPOFF_X1
// between bfd linker and gold linker for gold, when
// R_TILEGX_JUMPOFF_X1 against global symbol, we
// turn it into JUMPOFF_X1_PLT, otherwise the distance
// to the symbol function may overflow at runtime.
case elfcpp::R_TILEGX_JUMPOFF_X1:
case elfcpp::R_TILEGX_JUMPOFF_X1_PLT:
case elfcpp::R_TILEGX_IMM16_X0_HW0_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL:
// If the symbol is fully resolved, this is just a PC32 reloc.
// Otherwise we need a PLT entry.
if (gsym->final_value_is_known())
break;
// If building a shared library, we can also skip the PLT entry
// if the symbol is defined in the output file and is protected
// or hidden.
if (gsym->is_defined()
&& !gsym->is_from_dynobj()
&& !gsym->is_preemptible())
break;
target->make_plt_entry(symtab, layout, gsym);
break;
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_TLS_GD_CALL:
case elfcpp::R_TILEGX_IMM8_X0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_GD_ADD:
case elfcpp::R_TILEGX_TLS_IE_LOAD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_ADD:
{
const bool is_final = gsym->final_value_is_known();
const tls::Tls_optimization opt_t =
Target_tilegx<size, big_endian>::optimize_tls_reloc(is_final,
r_type);
switch (r_type)
{
// only expand to plt against __tls_get_addr in GD model
case elfcpp::R_TILEGX_TLS_GD_CALL:
if (opt_t == tls::TLSOPT_NONE) {
// FIXME: it's better '__tls_get_addr' referenced explictly
if (!target->tls_get_addr_sym_defined_) {
Symbol* sym = NULL;
options::parse_set(NULL, "__tls_get_addr",
(gold::options::String_set*)
&parameters->options().undefined());
symtab->add_undefined_symbols_from_command_line(layout);
target->tls_get_addr_sym_defined_ = true;
sym = symtab->lookup("__tls_get_addr");
sym->set_in_reg();
}
target->make_plt_entry(symtab, layout,
symtab->lookup("__tls_get_addr"));
}
break;
// only make effect when applying relocation
case elfcpp::R_TILEGX_TLS_IE_LOAD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_GD_ADD:
break;
// GD: requires two GOT entry for module index and offset
// IE: requires one GOT entry for tp-relative offset
// LE: shouldn't happen for global symbol
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
{
if (opt_t == tls::TLSOPT_NONE) {
Output_data_got<size, big_endian>* got
= target->got_section(symtab, layout);
got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_PAIR,
target->rela_dyn_section(layout),
size == 32
? elfcpp::R_TILEGX_TLS_DTPMOD32
: elfcpp::R_TILEGX_TLS_DTPMOD64,
size == 32
? elfcpp::R_TILEGX_TLS_DTPOFF32
: elfcpp::R_TILEGX_TLS_DTPOFF64);
} else if (opt_t == tls::TLSOPT_TO_IE) {
// Create a GOT entry for the tp-relative offset.
Output_data_got<size, big_endian>* got
= target->got_section(symtab, layout);
got->add_global_with_rel(gsym, GOT_TYPE_TLS_OFFSET,
target->rela_dyn_section(layout),
size == 32
? elfcpp::R_TILEGX_TLS_TPOFF32
: elfcpp::R_TILEGX_TLS_TPOFF64);
} else if (opt_t != tls::TLSOPT_TO_LE)
// exteranl symbol should not be optimized to TO_LE
unsupported_reloc_global(object, r_type, gsym);
}
break;
// IE
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
{
layout->set_has_static_tls();
if (opt_t == tls::TLSOPT_NONE) {
// Create a GOT entry for the tp-relative offset.
Output_data_got<size, big_endian>* got
= target->got_section(symtab, layout);
got->add_global_with_rel(gsym, GOT_TYPE_TLS_OFFSET,
target->rela_dyn_section(layout),
size == 32
? elfcpp::R_TILEGX_TLS_TPOFF32
: elfcpp::R_TILEGX_TLS_TPOFF64);
} else if (opt_t != tls::TLSOPT_TO_LE)
unsupported_reloc_global(object, r_type, gsym);
}
break;
// LE
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE:
layout->set_has_static_tls();
if (parameters->options().shared()) {
// defer to dynamic linker
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_symbolless_global_addend(gsym, r_type,
output_section, object,
data_shndx,
reloc.get_r_offset(), 0);
}
break;
default:
gold_unreachable();
}
}
break;
// below are outstanding relocs
// should not existed in static linking stage
case elfcpp::R_TILEGX_COPY:
case elfcpp::R_TILEGX_GLOB_DAT:
case elfcpp::R_TILEGX_JMP_SLOT:
case elfcpp::R_TILEGX_RELATIVE:
case elfcpp::R_TILEGX_TLS_TPOFF32:
case elfcpp::R_TILEGX_TLS_TPOFF64:
case elfcpp::R_TILEGX_TLS_DTPMOD32:
case elfcpp::R_TILEGX_TLS_DTPMOD64:
case elfcpp::R_TILEGX_TLS_DTPOFF32:
case elfcpp::R_TILEGX_TLS_DTPOFF64:
gold_error(_("%s: unexpected reloc %u in object file"),
object->name().c_str(), r_type);
break;
default:
gold_error(_("%s: unsupported reloc %u against global symbol %s"),
object->name().c_str(), r_type,
gsym->demangled_name().c_str());
break;
}
}
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::gc_process_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* 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)
{
typedef Target_tilegx<size, big_endian> Tilegx;
typedef typename Target_tilegx<size, big_endian>::Scan Scan;
if (sh_type == elfcpp::SHT_REL)
{
return;
}
gold::gc_process_relocs<size, big_endian,
Tilegx, elfcpp::SHT_RELA, Scan,
typename Target_tilegx<size, big_endian>::Relocatable_size_for_reloc>(
symtab,
layout,
this,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_symbols);
}
// Scan relocations for a section.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::scan_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* 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)
{
typedef Target_tilegx<size, big_endian> Tilegx;
typedef typename Target_tilegx<size, big_endian>::Scan Scan;
if (sh_type == elfcpp::SHT_REL)
{
gold_error(_("%s: unsupported REL reloc section"),
object->name().c_str());
return;
}
gold::scan_relocs<size, big_endian, Tilegx, elfcpp::SHT_RELA, Scan>(
symtab,
layout,
this,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_symbols);
}
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::do_define_standard_symbols(
Symbol_table* symtab,
Layout* layout)
{
Output_section* feedback_section = layout->find_output_section(".feedback");
if (feedback_section != NULL)
{
symtab->define_in_output_data("__feedback_section_end",
NULL,
Symbol_table::PREDEFINED,
feedback_section,
0,
0,
elfcpp::STT_NOTYPE,
elfcpp::STB_GLOBAL,
elfcpp::STV_HIDDEN,
0,
true, // offset_is_from_end
false);
}
}
// Finalize the sections.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::do_finalize_sections(
Layout* layout,
const Input_objects*,
Symbol_table* symtab)
{
const Reloc_section* rel_plt = (this->plt_ == NULL
? NULL
: this->plt_->rela_plt());
layout->add_target_dynamic_tags(false, this->got_plt_, rel_plt,
this->rela_dyn_, true, true);
// Emit any relocs we saved in an attempt to avoid generating COPY
// relocs.
if (this->copy_relocs_.any_saved_relocs())
this->copy_relocs_.emit(this->rela_dyn_section(layout));
// Set the size of the _GLOBAL_OFFSET_TABLE_ symbol to the size of
// the .got section.
Symbol* sym = this->global_offset_table_;
if (sym != NULL)
{
uint64_t data_size = this->got_->current_data_size();
symtab->get_sized_symbol<size>(sym)->set_symsize(data_size);
// If the .got section is more than 0x8000 bytes, we add
// 0x8000 to the value of _GLOBAL_OFFSET_TABLE_, so that 16
// bit relocations have a greater chance of working.
if (data_size >= 0x8000)
symtab->get_sized_symbol<size>(sym)->set_value(
symtab->get_sized_symbol<size>(sym)->value() + 0x8000);
}
if (parameters->doing_static_link()
&& (this->plt_ == NULL || !this->plt_->has_irelative_section()))
{
// If linking statically, make sure that the __rela_iplt symbols
// were defined if necessary, even if we didn't create a PLT.
static const Define_symbol_in_segment syms[] =
{
{
"__rela_iplt_start", // name
elfcpp::PT_LOAD, // segment_type
elfcpp::PF_W, // segment_flags_set
elfcpp::PF(0), // segment_flags_clear
0, // value
0, // size
elfcpp::STT_NOTYPE, // type
elfcpp::STB_GLOBAL, // binding
elfcpp::STV_HIDDEN, // visibility
0, // nonvis
Symbol::SEGMENT_START, // offset_from_base
true // only_if_ref
},
{
"__rela_iplt_end", // name
elfcpp::PT_LOAD, // segment_type
elfcpp::PF_W, // segment_flags_set
elfcpp::PF(0), // segment_flags_clear
0, // value
0, // size
elfcpp::STT_NOTYPE, // type
elfcpp::STB_GLOBAL, // binding
elfcpp::STV_HIDDEN, // visibility
0, // nonvis
Symbol::SEGMENT_START, // offset_from_base
true // only_if_ref
}
};
symtab->define_symbols(layout, 2, syms,
layout->script_options()->saw_sections_clause());
}
}
// Perform a relocation.
template<int size, bool big_endian>
inline bool
Target_tilegx<size, big_endian>::Relocate::relocate(
const Relocate_info<size, big_endian>* relinfo,
Target_tilegx<size, big_endian>* target,
Output_section*,
size_t relnum,
const elfcpp::Rela<size, big_endian>& rela,
unsigned int r_type,
const Sized_symbol<size>* gsym,
const Symbol_value<size>* psymval,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr address,
section_size_type)
{
if (view == NULL)
return true;
typedef Tilegx_relocate_functions<size, big_endian> TilegxReloc;
typename TilegxReloc::Tilegx_howto r_howto;
const Sized_relobj_file<size, big_endian>* object = relinfo->object;
// Pick the value to use for symbols defined in the PLT.
Symbol_value<size> symval;
if (gsym != NULL
&& gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
{
symval.set_output_value(target->plt_address_for_global(gsym));
psymval = &symval;
}
else if (gsym == NULL && psymval->is_ifunc_symbol())
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
if (object->local_has_plt_offset(r_sym))
{
symval.set_output_value(target->plt_address_for_local(object, r_sym));
psymval = &symval;
}
}
elfcpp::Elf_Xword addend = rela.get_r_addend();
// Get the GOT offset if needed.
// For tilegx, the GOT pointer points to the start of the GOT section.
bool have_got_offset = false;
int got_offset = 0;
int got_base = target->got_ != NULL
? target->got_->current_data_size() >= 0x8000 ? 0x8000 : 0
: 0;
unsigned int got_type = GOT_TYPE_STANDARD;
bool always_apply_relocation = false;
switch (r_type)
{
case elfcpp::R_TILEGX_IMM16_X0_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_GOT:
if (gsym != NULL)
{
gold_assert(gsym->has_got_offset(got_type));
got_offset = gsym->got_offset(got_type) - got_base;
}
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));
got_offset =
object->local_got_offset(r_sym, got_type) - got_base;
}
have_got_offset = true;
break;
default:
break;
}
r_howto = TilegxReloc::howto[r_type];
switch (r_type)
{
case elfcpp::R_TILEGX_NONE:
case elfcpp::R_TILEGX_GNU_VTINHERIT:
case elfcpp::R_TILEGX_GNU_VTENTRY:
break;
case elfcpp::R_TILEGX_IMM16_X0_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_GOT:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_GOT:
gold_assert(have_got_offset);
symval.set_output_value(got_offset);
psymval = &symval;
always_apply_relocation = true;
addend = 0;
// when under PIC mode, these relocations are deferred to rtld
case elfcpp::R_TILEGX_IMM16_X0_HW0:
case elfcpp::R_TILEGX_IMM16_X1_HW0:
case elfcpp::R_TILEGX_IMM16_X0_HW1:
case elfcpp::R_TILEGX_IMM16_X1_HW1:
case elfcpp::R_TILEGX_IMM16_X0_HW2:
case elfcpp::R_TILEGX_IMM16_X1_HW2:
case elfcpp::R_TILEGX_IMM16_X0_HW3:
case elfcpp::R_TILEGX_IMM16_X1_HW3:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST:
if (always_apply_relocation
|| !parameters->options().output_is_position_independent())
TilegxReloc::imm_x_general(view, object, psymval, addend, r_howto);
break;
case elfcpp::R_TILEGX_JUMPOFF_X1:
case elfcpp::R_TILEGX_JUMPOFF_X1_PLT:
gold_assert(gsym == NULL
|| gsym->has_plt_offset()
|| gsym->final_value_is_known()
|| (gsym->is_defined()
&& !gsym->is_from_dynobj()
&& !gsym->is_preemptible()));
TilegxReloc::imm_x_pcrel_general(view, object, psymval, addend,
address, r_howto);
break;
case elfcpp::R_TILEGX_IMM16_X0_HW0_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW3_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X0_HW2_LAST_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL:
case elfcpp::R_TILEGX_IMM16_X1_HW2_LAST_PCREL:
TilegxReloc::imm_x_pcrel_general(view, object, psymval, addend,
address, r_howto);
break;
case elfcpp::R_TILEGX_BROFF_X1:
case elfcpp::R_TILEGX_DEST_IMM8_X1:
TilegxReloc::imm_x_two_part_general(view, object, psymval,
addend, address, r_type);
break;
// below are general relocation types, which can be
// handled by target-independent handlers
case elfcpp::R_TILEGX_64:
TilegxReloc::abs64(view, object, psymval, addend);
break;
case elfcpp::R_TILEGX_64_PCREL:
TilegxReloc::pc_abs64(view, object, psymval, addend, address);
break;
case elfcpp::R_TILEGX_32:
TilegxReloc::abs32(view, object, psymval, addend);
break;
case elfcpp::R_TILEGX_32_PCREL:
TilegxReloc::pc_abs32(view, object, psymval, addend, address);
break;
case elfcpp::R_TILEGX_16:
TilegxReloc::abs16(view, object, psymval, addend);
break;
case elfcpp::R_TILEGX_16_PCREL:
TilegxReloc::pc_abs16(view, object, psymval, addend, address);
break;
case elfcpp::R_TILEGX_8:
Relocate_functions<size, big_endian>::rela8(view, object,
psymval, addend);
break;
case elfcpp::R_TILEGX_8_PCREL:
Relocate_functions<size, big_endian>::pcrela8(view, object,
psymval, addend, address);
break;
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_TLS_GD_CALL:
case elfcpp::R_TILEGX_IMM8_X0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_GD_ADD:
case elfcpp::R_TILEGX_TLS_IE_LOAD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_ADD:
{
const bool is_final = (gsym == NULL
? !parameters->options().shared()
: gsym->final_value_is_known());
tls::Tls_optimization opt_t =
Target_tilegx<size, big_endian>::optimize_tls_reloc(is_final,
r_type);
switch (r_type)
{
case elfcpp::R_TILEGX_TLS_GD_CALL:
{
if (opt_t == tls::TLSOPT_NONE) {
Symbol *tls_sym = relinfo->symtab->lookup("__tls_get_addr");
symval.set_output_value(
target->plt_address_for_global(tls_sym));
psymval = &symval;
TilegxReloc::imm_x_pcrel_general(view, object, psymval,
addend, address, r_howto);
}
else if (opt_t == tls::TLSOPT_TO_IE
|| opt_t == tls::TLSOPT_TO_LE)
TilegxReloc::tls_relax(view, r_type, opt_t);
}
break;
// XX_TLS_GD is the same as normal X_GOT relocation
// except allocating a got entry pair,
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
if (opt_t == tls::TLSOPT_NONE) {
got_type = GOT_TYPE_TLS_PAIR;
have_got_offset = true;
} else if (opt_t == tls::TLSOPT_TO_IE) {
got_type = GOT_TYPE_TLS_OFFSET;
have_got_offset = true;
}
goto do_update_value;
// XX_TLS_IE is the same as normal X_GOT relocation
// except allocating one additional runtime relocation
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
if (opt_t == tls::TLSOPT_NONE) {
got_type = GOT_TYPE_TLS_OFFSET;
have_got_offset = true;
}
do_update_value:
if (have_got_offset) {
if (gsym != NULL) {
gold_assert(gsym->has_got_offset(got_type));
got_offset = gsym->got_offset(got_type) - got_base;
} 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));
got_offset =
object->local_got_offset(r_sym, got_type) - got_base;
}
}
if (opt_t == tls::TLSOPT_NONE
|| opt_t == tls::TLSOPT_TO_IE) {
// for both GD/IE, these relocations
// actually calculate got offset, so
// there behavior are the same
gold_assert(have_got_offset);
symval.set_output_value(got_offset);
psymval = &symval;
addend = 0;
TilegxReloc::imm_x_general(view, object, psymval,
addend, r_howto);
break;
} // else if (opt_t == tls::TLSOPT_TO_LE)
// both GD/IE are turned into LE, which
// is absolute relocation.
//
// | go through
// |
// V
// LE
//
// tp
// |
// V
// t_var1 | t_var2 | t_var3 | ...
// --------------------------------------------------
//
// so offset to tp should be negative, we get offset
// from the following formular for LE
//
// t_var1_off = t_var1_sym_value - tls_section_start
//
case elfcpp::R_TILEGX_IMM16_X0_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE:
case elfcpp::R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE:
{
Output_segment *tls_segment = relinfo->layout->tls_segment();
if (tls_segment == NULL) {
gold_assert(parameters->errors()->error_count() > 0
|| issue_undefined_symbol_error(gsym));
return false;
}
typename elfcpp::Elf_types<size>::Elf_Addr value
= psymval->value(relinfo->object, 0);
symval.set_output_value(value);
psymval = &symval;
TilegxReloc::imm_x_general(view, object, psymval,
addend, r_howto);
}
break;
// tls relaxation
case elfcpp::R_TILEGX_TLS_IE_LOAD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_ADD:
case elfcpp::R_TILEGX_IMM8_X0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_X1_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y0_TLS_GD_ADD:
case elfcpp::R_TILEGX_IMM8_Y1_TLS_GD_ADD:
TilegxReloc::tls_relax(view, r_type, opt_t);
break;
default:
gold_unreachable();
}
}
break;
// below are outstanding relocs
// should not existed in static linking stage
case elfcpp::R_TILEGX_COPY:
case elfcpp::R_TILEGX_GLOB_DAT:
case elfcpp::R_TILEGX_JMP_SLOT:
case elfcpp::R_TILEGX_RELATIVE:
case elfcpp::R_TILEGX_TLS_TPOFF32:
case elfcpp::R_TILEGX_TLS_TPOFF64:
case elfcpp::R_TILEGX_TLS_DTPMOD32:
case elfcpp::R_TILEGX_TLS_DTPMOD64:
case elfcpp::R_TILEGX_TLS_DTPOFF32:
case elfcpp::R_TILEGX_TLS_DTPOFF64:
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unexpected reloc %u in object file"),
r_type);
break;
default:
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unsupported reloc %u"),
r_type);
break;
}
return true;
}
// Relocate section data.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::relocate_section(
const Relocate_info<size, big_endian>* 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,
typename elfcpp::Elf_types<size>::Elf_Addr address,
section_size_type view_size,
const Reloc_symbol_changes* reloc_symbol_changes)
{
typedef Target_tilegx<size, big_endian> Tilegx;
typedef typename Target_tilegx<size, big_endian>::Relocate Tilegx_relocate;
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::relocate_section<size, big_endian, Tilegx, elfcpp::SHT_RELA,
Tilegx_relocate, gold::Default_comdat_behavior>(
relinfo,
this,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
view,
address,
view_size,
reloc_symbol_changes);
}
// Apply an incremental relocation. Incremental relocations always refer
// to global symbols.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::apply_relocation(
const Relocate_info<size, big_endian>* relinfo,
typename elfcpp::Elf_types<size>::Elf_Addr r_offset,
unsigned int r_type,
typename elfcpp::Elf_types<size>::Elf_Swxword r_addend,
const Symbol* gsym,
unsigned char* view,
typename elfcpp::Elf_types<size>::Elf_Addr address,
section_size_type view_size)
{
gold::apply_relocation<size, big_endian, Target_tilegx<size, big_endian>,
typename Target_tilegx<size, big_endian>::Relocate>(
relinfo,
this,
r_offset,
r_type,
r_addend,
gsym,
view,
address,
view_size);
}
// Return the size of a relocation while scanning during a relocatable
// link.
template<int size, bool big_endian>
unsigned int
Target_tilegx<size,big_endian>::Relocatable_size_for_reloc::get_size_for_reloc(
unsigned int, Relobj*)
{
// We are always SHT_RELA, so we should never get here.
gold_unreachable();
return 0;
}
// Scan the relocs during a relocatable link.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::scan_relocatable_relocs(
Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* 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<size, big_endian, elfcpp::SHT_RELA,
Scan_relocatable_relocs>(
symtab,
layout,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_symbols,
rr);
}
// Relocate a section during a relocatable link.
template<int size, bool big_endian>
void
Target_tilegx<size, big_endian>::relocate_relocs(
const Relocate_info<size, big_endian>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section,
const Relocatable_relocs* rr,
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)
{
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::relocate_relocs<size, big_endian, elfcpp::SHT_RELA>(
relinfo,
prelocs,
reloc_count,
output_section,
offset_in_output_section,
rr,
view,
view_address,
view_size,
reloc_view,
reloc_view_size);
}
// Return the value to use for a dynamic which requires special
// treatment. This is how we support equality comparisons of function
// pointers across shared library boundaries, as described in the
// processor specific ABI supplement.
template<int size, bool big_endian>
uint64_t
Target_tilegx<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
{
gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
return this->plt_address_for_global(gsym);
}
// Return the value to use for the base of a DW_EH_PE_datarel offset
// in an FDE. Solaris and SVR4 use DW_EH_PE_datarel because their
// assembler can not write out the difference between two labels in
// different sections, so instead of using a pc-relative value they
// use an offset from the GOT.
template<int size, bool big_endian>
uint64_t
Target_tilegx<size, big_endian>::do_ehframe_datarel_base() const
{
gold_assert(this->global_offset_table_ != NULL);
Symbol* sym = this->global_offset_table_;
Sized_symbol<size>* ssym = static_cast<Sized_symbol<size>*>(sym);
return ssym->value();
}
// The selector for tilegx object files.
template<int size, bool big_endian>
class Target_selector_tilegx : public Target_selector
{
public:
Target_selector_tilegx()
: Target_selector(elfcpp::EM_TILEGX, size, big_endian,
(size == 64
? (big_endian ? "elf64-tilegx-be" : "elf64-tilegx-le")
: (big_endian ? "elf32-tilegx-be"
: "elf32-tilegx-le")),
(size == 64
? (big_endian ? "elf64tilegx_be" : "elf64tilegx")
: (big_endian ? "elf32tilegx_be" : "elf32tilegx")))
{ }
Target*
do_instantiate_target()
{ return new Target_tilegx<size, big_endian>(); }
};
Target_selector_tilegx<64, false> target_selector_tilegx64_le;
Target_selector_tilegx<32, false> target_selector_tilegx32_le;
Target_selector_tilegx<64, true> target_selector_tilegx64_be;
Target_selector_tilegx<32, true> target_selector_tilegx32_be;
} // End anonymous namespace.