mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
a82bef932e
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.
4398 lines
130 KiB
C++
4398 lines
130 KiB
C++
// sparc.cc -- sparc target support for gold.
|
|
|
|
// Copyright (C) 2008-2014 Free Software Foundation, Inc.
|
|
// Written by David S. Miller <davem@davemloft.net>.
|
|
|
|
// 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 <cstdlib>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
#include "elfcpp.h"
|
|
#include "parameters.h"
|
|
#include "reloc.h"
|
|
#include "sparc.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 "errors.h"
|
|
#include "gc.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
using namespace gold;
|
|
|
|
template<int size, bool big_endian>
|
|
class Output_data_plt_sparc;
|
|
|
|
template<int size, bool big_endian>
|
|
class Target_sparc : public Sized_target<size, big_endian>
|
|
{
|
|
public:
|
|
typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian> Reloc_section;
|
|
|
|
Target_sparc()
|
|
: Sized_target<size, big_endian>(&sparc_info),
|
|
got_(NULL), plt_(NULL), rela_dyn_(NULL), rela_ifunc_(NULL),
|
|
copy_relocs_(elfcpp::R_SPARC_COPY),
|
|
got_mod_index_offset_(-1U), tls_get_addr_sym_(NULL),
|
|
elf_machine_(sparc_info.machine_code), elf_flags_(0),
|
|
elf_flags_set_(false)
|
|
{
|
|
}
|
|
|
|
// Process the relocations to determine unreferenced sections for
|
|
// garbage collection.
|
|
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*);
|
|
|
|
// Emit relocations for a section.
|
|
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
|
|
{
|
|
// XXX Really need to support this better...
|
|
if (sym->type() == elfcpp::STT_SPARC_REGISTER)
|
|
return 1;
|
|
|
|
return strcmp(sym->name(), "___tls_get_addr") == 0;
|
|
}
|
|
|
|
// Return the PLT address to use for a global symbol.
|
|
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); }
|
|
|
|
// 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 address of the GOT.
|
|
uint64_t
|
|
got_address() const
|
|
{
|
|
if (this->got_ == NULL)
|
|
return 0;
|
|
return this->got_->address();
|
|
}
|
|
|
|
// 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;
|
|
|
|
protected:
|
|
// Make an ELF object.
|
|
Object*
|
|
do_make_elf_object(const std::string&, Input_file*, off_t,
|
|
const elfcpp::Ehdr<size, big_endian>& ehdr);
|
|
|
|
void
|
|
do_adjust_elf_header(unsigned char* view, int len);
|
|
|
|
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_sparc* 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_sparc* 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* , Layout* ,
|
|
Target_sparc* ,
|
|
Sized_relobj_file<size, big_endian>* ,
|
|
unsigned int ,
|
|
Output_section* ,
|
|
const elfcpp::Rela<size, big_endian>& ,
|
|
unsigned int ,
|
|
const elfcpp::Sym<size, big_endian>&)
|
|
{ return false; }
|
|
|
|
inline bool
|
|
global_reloc_may_be_function_pointer(Symbol_table* , Layout* ,
|
|
Target_sparc* ,
|
|
Sized_relobj_file<size, big_endian>* ,
|
|
unsigned int ,
|
|
Output_section* ,
|
|
const elfcpp::Rela<size,
|
|
big_endian>& ,
|
|
unsigned int , Symbol*)
|
|
{ return false; }
|
|
|
|
|
|
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*);
|
|
|
|
static void
|
|
generate_tls_call(Symbol_table* symtab, Layout* layout,
|
|
Target_sparc* target);
|
|
|
|
void
|
|
check_non_pic(Relobj*, 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()
|
|
: ignore_gd_add_(false), reloc_adjust_addr_(NULL)
|
|
{ }
|
|
|
|
~Relocate()
|
|
{
|
|
if (this->ignore_gd_add_)
|
|
{
|
|
// FIXME: This needs to specify the location somehow.
|
|
gold_error(_("missing expected TLS relocation"));
|
|
}
|
|
}
|
|
|
|
// 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_sparc*,
|
|
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);
|
|
|
|
private:
|
|
// Do a TLS relocation.
|
|
inline void
|
|
relocate_tls(const Relocate_info<size, big_endian>*, Target_sparc* target,
|
|
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);
|
|
|
|
inline void
|
|
relax_call(Target_sparc<size, big_endian>* target,
|
|
unsigned char* view,
|
|
const elfcpp::Rela<size, big_endian>& rela,
|
|
section_size_type view_size);
|
|
|
|
// Ignore the next relocation which should be R_SPARC_TLS_GD_ADD
|
|
bool ignore_gd_add_;
|
|
|
|
// If we hit a reloc at this view address, adjust it back by 4 bytes.
|
|
unsigned char *reloc_adjust_addr_;
|
|
};
|
|
|
|
// A class which returns the size required for a relocation type,
|
|
// used while scanning relocs during a relocatable link.
|
|
class Relocatable_size_for_reloc
|
|
{
|
|
public:
|
|
unsigned int
|
|
get_size_for_reloc(unsigned int, Relobj*);
|
|
};
|
|
|
|
// Get the GOT section, creating it if necessary.
|
|
Output_data_got<size, big_endian>*
|
|
got_section(Symbol_table*, Layout*);
|
|
|
|
// 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);
|
|
|
|
// Return the gsym for "__tls_get_addr". Cache if not already
|
|
// cached.
|
|
Symbol*
|
|
tls_get_addr_sym(Symbol_table* symtab)
|
|
{
|
|
if (!this->tls_get_addr_sym_)
|
|
this->tls_get_addr_sym_ = symtab->lookup("__tls_get_addr", NULL);
|
|
gold_assert(this->tls_get_addr_sym_);
|
|
return this->tls_get_addr_sym_;
|
|
}
|
|
|
|
// Get the PLT section.
|
|
Output_data_plt_sparc<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 IFUNC relocations.
|
|
Reloc_section*
|
|
rela_ifunc_section(Layout*);
|
|
|
|
// Copy a relocation against a global symbol.
|
|
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 Target::Target_info sparc_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
|
|
};
|
|
|
|
// The GOT section.
|
|
Output_data_got<size, big_endian>* got_;
|
|
// The PLT section.
|
|
Output_data_plt_sparc<size, big_endian>* plt_;
|
|
// The dynamic reloc section.
|
|
Reloc_section* rela_dyn_;
|
|
// The section to use for IFUNC relocs.
|
|
Reloc_section* rela_ifunc_;
|
|
// 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_;
|
|
// Cached pointer to __tls_get_addr symbol
|
|
Symbol* tls_get_addr_sym_;
|
|
// Accumulated elf machine type
|
|
elfcpp::Elf_Half elf_machine_;
|
|
// Accumulated elf header flags
|
|
elfcpp::Elf_Word elf_flags_;
|
|
// Whether elf_flags_ has been set for the first time yet
|
|
bool elf_flags_set_;
|
|
};
|
|
|
|
template<>
|
|
Target::Target_info Target_sparc<32, true>::sparc_info =
|
|
{
|
|
32, // size
|
|
true, // is_big_endian
|
|
elfcpp::EM_SPARC, // 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
|
|
"/usr/lib/ld.so.1", // dynamic_linker
|
|
0x00010000, // default_text_segment_address
|
|
64 * 1024, // abi_pagesize (overridable by -z max-page-size)
|
|
8 * 1024, // 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<>
|
|
Target::Target_info Target_sparc<64, true>::sparc_info =
|
|
{
|
|
64, // size
|
|
true, // is_big_endian
|
|
elfcpp::EM_SPARCV9, // 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
|
|
"/usr/lib/sparcv9/ld.so.1", // dynamic_linker
|
|
0x100000, // default_text_segment_address
|
|
64 * 1024, // abi_pagesize (overridable by -z max-page-size)
|
|
8 * 1024, // 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
|
|
};
|
|
|
|
// We have to take care here, even when operating in little-endian
|
|
// mode, sparc instructions are still big endian.
|
|
template<int size, bool big_endian>
|
|
class Sparc_relocate_functions
|
|
{
|
|
private:
|
|
// Do a simple relocation with the addend in the relocation.
|
|
template<int valsize>
|
|
static inline void
|
|
rela(unsigned char* view,
|
|
unsigned int right_shift,
|
|
typename elfcpp::Elf_types<valsize>::Elf_Addr dst_mask,
|
|
typename elfcpp::Swap<size, big_endian>::Valtype value,
|
|
typename elfcpp::Swap<size, big_endian>::Valtype addend)
|
|
{
|
|
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 = ((value + addend) >> right_shift);
|
|
|
|
val &= ~dst_mask;
|
|
reloc &= dst_mask;
|
|
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// Do a simple relocation using a symbol value with the addend in
|
|
// the relocation.
|
|
template<int valsize>
|
|
static inline void
|
|
rela(unsigned char* view,
|
|
unsigned int right_shift,
|
|
typename elfcpp::Elf_types<valsize>::Elf_Addr dst_mask,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Swap<valsize, big_endian>::Valtype addend)
|
|
{
|
|
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 = (psymval->value(object, addend) >> right_shift);
|
|
|
|
val &= ~dst_mask;
|
|
reloc &= dst_mask;
|
|
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// Do a simple relocation using a symbol value with the addend in
|
|
// the relocation, unaligned.
|
|
template<int valsize>
|
|
static inline void
|
|
rela_ua(unsigned char* view,
|
|
unsigned int right_shift, elfcpp::Elf_Xword dst_mask,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Swap<size, big_endian>::Valtype addend)
|
|
{
|
|
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 = (psymval->value(object, addend) >> right_shift);
|
|
|
|
val &= ~dst_mask;
|
|
reloc &= dst_mask;
|
|
|
|
elfcpp::Swap_unaligned<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,
|
|
unsigned int right_shift,
|
|
typename elfcpp::Elf_types<valsize>::Elf_Addr dst_mask,
|
|
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)
|
|
{
|
|
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 = ((psymval->value(object, addend) - address)
|
|
>> right_shift);
|
|
|
|
val &= ~dst_mask;
|
|
reloc &= dst_mask;
|
|
|
|
elfcpp::Swap<valsize, big_endian>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
template<int valsize>
|
|
static inline void
|
|
pcrela_unaligned(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)
|
|
{
|
|
typedef typename elfcpp::Swap_unaligned<valsize,
|
|
big_endian>::Valtype Valtype;
|
|
unsigned char* wv = view;
|
|
Valtype reloc = (psymval->value(object, addend) - address);
|
|
|
|
elfcpp::Swap_unaligned<valsize, big_endian>::writeval(wv, reloc);
|
|
}
|
|
|
|
typedef Sparc_relocate_functions<size, big_endian> This;
|
|
typedef Sparc_relocate_functions<size, true> This_insn;
|
|
|
|
public:
|
|
// R_SPARC_WDISP30: (Symbol + Addend - Address) >> 2
|
|
static inline void
|
|
wdisp30(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_insn::template pcrela<32>(view, 2, 0x3fffffff, object,
|
|
psymval, addend, address);
|
|
}
|
|
|
|
// R_SPARC_WDISP22: (Symbol + Addend - Address) >> 2
|
|
static inline void
|
|
wdisp22(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_insn::template pcrela<32>(view, 2, 0x003fffff, object,
|
|
psymval, addend, address);
|
|
}
|
|
|
|
// R_SPARC_WDISP19: (Symbol + Addend - Address) >> 2
|
|
static inline void
|
|
wdisp19(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_insn::template pcrela<32>(view, 2, 0x0007ffff, object,
|
|
psymval, addend, address);
|
|
}
|
|
|
|
// R_SPARC_WDISP16: (Symbol + Addend - Address) >> 2
|
|
static inline void
|
|
wdisp16(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)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
Valtype reloc = ((psymval->value(object, addend) - address)
|
|
>> 2);
|
|
|
|
// The relocation value is split between the low 14 bits,
|
|
// and bits 20-21.
|
|
val &= ~((0x3 << 20) | 0x3fff);
|
|
reloc = (((reloc & 0xc000) << (20 - 14))
|
|
| (reloc & 0x3ffff));
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// R_SPARC_WDISP10: (Symbol + Addend - Address) >> 2
|
|
static inline void
|
|
wdisp10(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)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
Valtype reloc = ((psymval->value(object, addend) - address)
|
|
>> 2);
|
|
|
|
// The relocation value is split between the low bits 5-12,
|
|
// and high bits 19-20.
|
|
val &= ~((0x3 << 19) | (0xff << 5));
|
|
reloc = (((reloc & 0x300) << (19 - 8))
|
|
| ((reloc & 0xff) << (5 - 0)));
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// R_SPARC_PC22: (Symbol + Addend - Address) >> 10
|
|
static inline void
|
|
pc22(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_insn::template pcrela<32>(view, 10, 0x003fffff, object,
|
|
psymval, addend, address);
|
|
}
|
|
|
|
// R_SPARC_PC10: (Symbol + Addend - Address) & 0x3ff
|
|
static inline void
|
|
pc10(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_insn::template pcrela<32>(view, 0, 0x000003ff, object,
|
|
psymval, addend, address);
|
|
}
|
|
|
|
// R_SPARC_HI22: (Symbol + Addend) >> 10
|
|
static inline void
|
|
hi22(unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
This_insn::template rela<32>(view, 10, 0x003fffff, value, addend);
|
|
}
|
|
|
|
// R_SPARC_HI22: (Symbol + Addend) >> 10
|
|
static inline void
|
|
hi22(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_insn::template rela<32>(view, 10, 0x003fffff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_PCPLT22: (Symbol + Addend - Address) >> 10
|
|
static inline void
|
|
pcplt22(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_insn::template pcrela<32>(view, 10, 0x003fffff, object,
|
|
psymval, addend, address);
|
|
}
|
|
|
|
// R_SPARC_LO10: (Symbol + Addend) & 0x3ff
|
|
static inline void
|
|
lo10(unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
This_insn::template rela<32>(view, 0, 0x000003ff, value, addend);
|
|
}
|
|
|
|
// R_SPARC_LO10: (Symbol + Addend) & 0x3ff
|
|
static inline void
|
|
lo10(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_insn::template rela<32>(view, 0, 0x000003ff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_LO10: (Symbol + Addend) & 0x3ff
|
|
static inline void
|
|
lo10(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_insn::template pcrela<32>(view, 0, 0x000003ff, object,
|
|
psymval, addend, address);
|
|
}
|
|
|
|
// R_SPARC_OLO10: ((Symbol + Addend) & 0x3ff) + Addend2
|
|
static inline void
|
|
olo10(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 addend2)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
Valtype reloc = psymval->value(object, addend);
|
|
|
|
val &= ~0x1fff;
|
|
reloc &= 0x3ff;
|
|
reloc += addend2;
|
|
reloc &= 0x1fff;
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// R_SPARC_22: (Symbol + Addend)
|
|
static inline void
|
|
rela32_22(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_insn::template rela<32>(view, 0, 0x003fffff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_13: (Symbol + Addend)
|
|
static inline void
|
|
rela32_13(unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
This_insn::template rela<32>(view, 0, 0x00001fff, value, addend);
|
|
}
|
|
|
|
// R_SPARC_13: (Symbol + Addend)
|
|
static inline void
|
|
rela32_13(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_insn::template rela<32>(view, 0, 0x00001fff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_UA16: (Symbol + Addend)
|
|
static inline void
|
|
ua16(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, 0, 0xffff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_UA32: (Symbol + Addend)
|
|
static inline void
|
|
ua32(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, 0, 0xffffffff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_UA64: (Symbol + Addend)
|
|
static inline void
|
|
ua64(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, 0, ~(elfcpp::Elf_Xword) 0,
|
|
object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_DISP8: (Symbol + Addend - Address)
|
|
static inline void
|
|
disp8(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_unaligned<8>(view, object, psymval,
|
|
addend, address);
|
|
}
|
|
|
|
// R_SPARC_DISP16: (Symbol + Addend - Address)
|
|
static inline void
|
|
disp16(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_unaligned<16>(view, object, psymval,
|
|
addend, address);
|
|
}
|
|
|
|
// R_SPARC_DISP32: (Symbol + Addend - Address)
|
|
static inline void
|
|
disp32(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_unaligned<32>(view, object, psymval,
|
|
addend, address);
|
|
}
|
|
|
|
// R_SPARC_DISP64: (Symbol + Addend - Address)
|
|
static inline void
|
|
disp64(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
elfcpp::Elf_Xword addend,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr address)
|
|
{
|
|
This::template pcrela_unaligned<64>(view, object, psymval,
|
|
addend, address);
|
|
}
|
|
|
|
// R_SPARC_H34: (Symbol + Addend) >> 12
|
|
static inline void
|
|
h34(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_insn::template rela<32>(view, 12, 0x003fffff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_H44: (Symbol + Addend) >> 22
|
|
static inline void
|
|
h44(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_insn::template rela<32>(view, 22, 0x003fffff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_M44: ((Symbol + Addend) >> 12) & 0x3ff
|
|
static inline void
|
|
m44(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_insn::template rela<32>(view, 12, 0x000003ff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_L44: (Symbol + Addend) & 0xfff
|
|
static inline void
|
|
l44(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_insn::template rela<32>(view, 0, 0x00000fff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_HH22: (Symbol + Addend) >> 42
|
|
static inline void
|
|
hh22(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_insn::template rela<32>(view, 42, 0x003fffff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_PC_HH22: (Symbol + Addend - Address) >> 42
|
|
static inline void
|
|
pc_hh22(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_insn::template pcrela<32>(view, 42, 0x003fffff, object,
|
|
psymval, addend, address);
|
|
}
|
|
|
|
// R_SPARC_HM10: ((Symbol + Addend) >> 32) & 0x3ff
|
|
static inline void
|
|
hm10(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_insn::template rela<32>(view, 32, 0x000003ff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_PC_HM10: ((Symbol + Addend - Address) >> 32) & 0x3ff
|
|
static inline void
|
|
pc_hm10(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_insn::template pcrela<32>(view, 32, 0x000003ff, object,
|
|
psymval, addend, address);
|
|
}
|
|
|
|
// R_SPARC_11: (Symbol + Addend)
|
|
static inline void
|
|
rela32_11(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_insn::template rela<32>(view, 0, 0x000007ff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_10: (Symbol + Addend)
|
|
static inline void
|
|
rela32_10(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_insn::template rela<32>(view, 0, 0x000003ff, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_7: (Symbol + Addend)
|
|
static inline void
|
|
rela32_7(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_insn::template rela<32>(view, 0, 0x0000007f, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_6: (Symbol + Addend)
|
|
static inline void
|
|
rela32_6(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_insn::template rela<32>(view, 0, 0x0000003f, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_5: (Symbol + Addend)
|
|
static inline void
|
|
rela32_5(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_insn::template rela<32>(view, 0, 0x0000001f, object, psymval, addend);
|
|
}
|
|
|
|
// R_SPARC_TLS_LDO_HIX22: @dtpoff(Symbol + Addend) >> 10
|
|
static inline void
|
|
ldo_hix22(unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
This_insn::hi22(view, value, addend);
|
|
}
|
|
|
|
// R_SPARC_TLS_LDO_LOX10: @dtpoff(Symbol + Addend) & 0x3ff
|
|
static inline void
|
|
ldo_lox10(unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
Valtype reloc = (value + addend);
|
|
|
|
val &= ~0x1fff;
|
|
reloc &= 0x3ff;
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// R_SPARC_TLS_LE_HIX22: (@tpoff(Symbol + Addend) ^ 0xffffffffffffffff) >> 10
|
|
static inline void
|
|
hix22(unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
Valtype reloc = (value + addend);
|
|
|
|
val &= ~0x3fffff;
|
|
|
|
reloc ^= ~(Valtype)0;
|
|
reloc >>= 10;
|
|
|
|
reloc &= 0x3fffff;
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// R_SPARC_GOTDATA_OP_HIX22: @gdopoff(Symbol + Addend) >> 10
|
|
static inline void
|
|
gdop_hix22(unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
int32_t reloc = static_cast<int32_t>(value + addend);
|
|
|
|
val &= ~0x3fffff;
|
|
|
|
if (reloc < 0)
|
|
reloc ^= ~static_cast<int32_t>(0);
|
|
reloc >>= 10;
|
|
|
|
reloc &= 0x3fffff;
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// R_SPARC_HIX22: ((Symbol + Addend) ^ 0xffffffffffffffff) >> 10
|
|
static inline void
|
|
hix22(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
Valtype reloc = psymval->value(object, addend);
|
|
|
|
val &= ~0x3fffff;
|
|
|
|
reloc ^= ~(Valtype)0;
|
|
reloc >>= 10;
|
|
|
|
reloc &= 0x3fffff;
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
|
|
// R_SPARC_TLS_LE_LOX10: (@tpoff(Symbol + Addend) & 0x3ff) | 0x1c00
|
|
static inline void
|
|
lox10(unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
Valtype reloc = (value + addend);
|
|
|
|
val &= ~0x1fff;
|
|
reloc &= 0x3ff;
|
|
reloc |= 0x1c00;
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// R_SPARC_GOTDATA_OP_LOX10: (@gdopoff(Symbol + Addend) & 0x3ff) | 0x1c00
|
|
static inline void
|
|
gdop_lox10(unsigned char* view,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
int32_t reloc = static_cast<int32_t>(value + addend);
|
|
|
|
if (reloc < 0)
|
|
reloc = (reloc & 0x3ff) | 0x1c00;
|
|
else
|
|
reloc = (reloc & 0x3ff);
|
|
|
|
val &= ~0x1fff;
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
|
|
// R_SPARC_LOX10: ((Symbol + Addend) & 0x3ff) | 0x1c00
|
|
static inline void
|
|
lox10(unsigned char* view,
|
|
const Sized_relobj_file<size, big_endian>* object,
|
|
const Symbol_value<size>* psymval,
|
|
typename elfcpp::Elf_types<size>::Elf_Addr addend)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Valtype;
|
|
Valtype* wv = reinterpret_cast<Valtype*>(view);
|
|
Valtype val = elfcpp::Swap<32, true>::readval(wv);
|
|
Valtype reloc = psymval->value(object, addend);
|
|
|
|
val &= ~0x1fff;
|
|
reloc &= 0x3ff;
|
|
reloc |= 0x1c00;
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val | reloc);
|
|
}
|
|
};
|
|
|
|
// Get the GOT section, creating it if necessary.
|
|
|
|
template<int size, bool big_endian>
|
|
Output_data_got<size, big_endian>*
|
|
Target_sparc<size, big_endian>::got_section(Symbol_table* symtab,
|
|
Layout* layout)
|
|
{
|
|
if (this->got_ == NULL)
|
|
{
|
|
gold_assert(symtab != NULL && layout != NULL);
|
|
|
|
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_, ORDER_RELRO, true);
|
|
|
|
// Define _GLOBAL_OFFSET_TABLE_ at the start of the .got section.
|
|
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);
|
|
}
|
|
|
|
return this->got_;
|
|
}
|
|
|
|
// Get the dynamic reloc section, creating it if necessary.
|
|
|
|
template<int size, bool big_endian>
|
|
typename Target_sparc<size, big_endian>::Reloc_section*
|
|
Target_sparc<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 IFUNC 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_sparc<size, big_endian>::Reloc_section*
|
|
Target_sparc<size, big_endian>::rela_ifunc_section(Layout* layout)
|
|
{
|
|
if (this->rela_ifunc_ == NULL)
|
|
{
|
|
// Make sure we have already created the dynamic reloc section.
|
|
this->rela_dyn_section(layout);
|
|
this->rela_ifunc_ = new Reloc_section(false);
|
|
layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
|
|
elfcpp::SHF_ALLOC, this->rela_ifunc_,
|
|
ORDER_DYNAMIC_RELOCS, false);
|
|
gold_assert(this->rela_dyn_->output_section()
|
|
== this->rela_ifunc_->output_section());
|
|
}
|
|
return this->rela_ifunc_;
|
|
}
|
|
|
|
// A class to handle the PLT data.
|
|
|
|
template<int size, bool big_endian>
|
|
class Output_data_plt_sparc : public Output_section_data
|
|
{
|
|
public:
|
|
typedef Output_data_reloc<elfcpp::SHT_RELA, true,
|
|
size, big_endian> Reloc_section;
|
|
|
|
Output_data_plt_sparc(Layout*);
|
|
|
|
// Add an entry to the PLT.
|
|
void add_entry(Symbol_table* symtab, Layout* 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>* relobj,
|
|
unsigned int local_sym_index);
|
|
|
|
// Return the .rela.plt section data.
|
|
const Reloc_section* rel_plt() const
|
|
{
|
|
return this->rel_;
|
|
}
|
|
|
|
// Return where the IFUNC relocations should go.
|
|
Reloc_section*
|
|
rela_ifunc(Symbol_table*, Layout*);
|
|
|
|
void
|
|
emit_pending_ifunc_relocs();
|
|
|
|
// Return whether we created a section for IFUNC relocations.
|
|
bool
|
|
has_ifunc_section() const
|
|
{ return this->ifunc_rel_ != NULL; }
|
|
|
|
// Return the number of PLT entries.
|
|
unsigned int
|
|
entry_count() const
|
|
{ return this->count_ + this->ifunc_count_; }
|
|
|
|
// Return the offset of the first non-reserved PLT entry.
|
|
static unsigned int
|
|
first_plt_entry_offset()
|
|
{ return 4 * base_plt_entry_size; }
|
|
|
|
// Return the size of a PLT entry.
|
|
static unsigned int
|
|
get_plt_entry_size()
|
|
{ return base_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:
|
|
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:
|
|
// The size of an entry in the PLT.
|
|
static const int base_plt_entry_size = (size == 32 ? 12 : 32);
|
|
|
|
static const unsigned int plt_entries_per_block = 160;
|
|
static const unsigned int plt_insn_chunk_size = 24;
|
|
static const unsigned int plt_pointer_chunk_size = 8;
|
|
static const unsigned int plt_block_size =
|
|
(plt_entries_per_block
|
|
* (plt_insn_chunk_size + plt_pointer_chunk_size));
|
|
|
|
section_offset_type
|
|
plt_index_to_offset(unsigned int index)
|
|
{
|
|
section_offset_type offset;
|
|
|
|
if (size == 32 || index < 32768)
|
|
offset = index * base_plt_entry_size;
|
|
else
|
|
{
|
|
unsigned int ext_index = index - 32768;
|
|
|
|
offset = (32768 * base_plt_entry_size)
|
|
+ ((ext_index / plt_entries_per_block)
|
|
* plt_block_size)
|
|
+ ((ext_index % plt_entries_per_block)
|
|
* plt_insn_chunk_size);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
// Set the final size.
|
|
void
|
|
set_final_data_size()
|
|
{
|
|
unsigned int full_count = this->entry_count() + 4;
|
|
unsigned int extra = (size == 32 ? 4 : 0);
|
|
section_offset_type sz = plt_index_to_offset(full_count) + extra;
|
|
|
|
return this->set_data_size(sz);
|
|
}
|
|
|
|
// Write out the PLT data.
|
|
void
|
|
do_write(Output_file*);
|
|
|
|
struct Global_ifunc
|
|
{
|
|
Reloc_section* rel;
|
|
Symbol* gsym;
|
|
unsigned int plt_index;
|
|
};
|
|
|
|
struct Local_ifunc
|
|
{
|
|
Reloc_section* rel;
|
|
Sized_relobj_file<size, big_endian>* object;
|
|
unsigned int local_sym_index;
|
|
unsigned int plt_index;
|
|
};
|
|
|
|
// The reloc section.
|
|
Reloc_section* rel_;
|
|
// The IFUNC relocations, if necessary. These must follow the
|
|
// regular relocations.
|
|
Reloc_section* ifunc_rel_;
|
|
// The number of PLT entries.
|
|
unsigned int count_;
|
|
// The number of PLT entries for IFUNC symbols.
|
|
unsigned int ifunc_count_;
|
|
// Global STT_GNU_IFUNC symbols.
|
|
std::vector<Global_ifunc> global_ifuncs_;
|
|
// Local STT_GNU_IFUNC symbols.
|
|
std::vector<Local_ifunc> local_ifuncs_;
|
|
};
|
|
|
|
// Define the constants as required by C++ standard.
|
|
|
|
template<int size, bool big_endian>
|
|
const int Output_data_plt_sparc<size, big_endian>::base_plt_entry_size;
|
|
|
|
template<int size, bool big_endian>
|
|
const unsigned int
|
|
Output_data_plt_sparc<size, big_endian>::plt_entries_per_block;
|
|
|
|
template<int size, bool big_endian>
|
|
const unsigned int Output_data_plt_sparc<size, big_endian>::plt_insn_chunk_size;
|
|
|
|
template<int size, bool big_endian>
|
|
const unsigned int
|
|
Output_data_plt_sparc<size, big_endian>::plt_pointer_chunk_size;
|
|
|
|
template<int size, bool big_endian>
|
|
const unsigned int Output_data_plt_sparc<size, big_endian>::plt_block_size;
|
|
|
|
// Create the PLT section. The ordinary .got section is an argument,
|
|
// since we need to refer to the start.
|
|
|
|
template<int size, bool big_endian>
|
|
Output_data_plt_sparc<size, big_endian>::Output_data_plt_sparc(Layout* layout)
|
|
: Output_section_data(size == 32 ? 4 : 8), ifunc_rel_(NULL),
|
|
count_(0), ifunc_count_(0), global_ifuncs_(), local_ifuncs_()
|
|
{
|
|
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_sparc<size, big_endian>::do_adjust_output_section(Output_section* os)
|
|
{
|
|
os->set_entsize(0);
|
|
}
|
|
|
|
// Add an entry to the PLT.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_data_plt_sparc<size, big_endian>::add_entry(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Symbol* gsym)
|
|
{
|
|
gold_assert(!gsym->has_plt_offset());
|
|
|
|
section_offset_type plt_offset;
|
|
unsigned int index;
|
|
|
|
if (gsym->type() == elfcpp::STT_GNU_IFUNC
|
|
&& gsym->can_use_relative_reloc(false))
|
|
{
|
|
index = this->ifunc_count_;
|
|
plt_offset = plt_index_to_offset(index);
|
|
gsym->set_plt_offset(plt_offset);
|
|
++this->ifunc_count_;
|
|
Reloc_section* rel = this->rela_ifunc(symtab, layout);
|
|
|
|
struct Global_ifunc gi;
|
|
gi.rel = rel;
|
|
gi.gsym = gsym;
|
|
gi.plt_index = index;
|
|
this->global_ifuncs_.push_back(gi);
|
|
}
|
|
else
|
|
{
|
|
plt_offset = plt_index_to_offset(this->count_ + 4);
|
|
gsym->set_plt_offset(plt_offset);
|
|
++this->count_;
|
|
gsym->set_needs_dynsym_entry();
|
|
this->rel_->add_global(gsym, elfcpp::R_SPARC_JMP_SLOT, this,
|
|
plt_offset, 0);
|
|
}
|
|
|
|
// 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.
|
|
}
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Output_data_plt_sparc<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 index = this->ifunc_count_;
|
|
section_offset_type plt_offset;
|
|
|
|
plt_offset = plt_index_to_offset(index);
|
|
++this->ifunc_count_;
|
|
|
|
Reloc_section* rel = this->rela_ifunc(symtab, layout);
|
|
|
|
struct Local_ifunc li;
|
|
li.rel = rel;
|
|
li.object = relobj;
|
|
li.local_sym_index = local_sym_index;
|
|
li.plt_index = index;
|
|
this->local_ifuncs_.push_back(li);
|
|
|
|
return plt_offset;
|
|
}
|
|
|
|
// Emit any pending IFUNC plt relocations.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_data_plt_sparc<size, big_endian>::emit_pending_ifunc_relocs()
|
|
{
|
|
// Emit any pending IFUNC relocs.
|
|
for (typename std::vector<Global_ifunc>::const_iterator p =
|
|
this->global_ifuncs_.begin();
|
|
p != this->global_ifuncs_.end();
|
|
++p)
|
|
{
|
|
section_offset_type plt_offset;
|
|
unsigned int index;
|
|
|
|
index = this->count_ + p->plt_index + 4;
|
|
plt_offset = this->plt_index_to_offset(index);
|
|
p->rel->add_symbolless_global_addend(p->gsym, elfcpp::R_SPARC_JMP_IREL,
|
|
this, plt_offset, 0);
|
|
}
|
|
|
|
for (typename std::vector<Local_ifunc>::const_iterator p =
|
|
this->local_ifuncs_.begin();
|
|
p != this->local_ifuncs_.end();
|
|
++p)
|
|
{
|
|
section_offset_type plt_offset;
|
|
unsigned int index;
|
|
|
|
index = this->count_ + p->plt_index + 4;
|
|
plt_offset = this->plt_index_to_offset(index);
|
|
p->rel->add_symbolless_local_addend(p->object, p->local_sym_index,
|
|
elfcpp::R_SPARC_JMP_IREL,
|
|
this, plt_offset, 0);
|
|
}
|
|
}
|
|
|
|
// Return where the IFUNC relocations should go in the PLT. These
|
|
// follow the non-IFUNC relocations.
|
|
|
|
template<int size, bool big_endian>
|
|
typename Output_data_plt_sparc<size, big_endian>::Reloc_section*
|
|
Output_data_plt_sparc<size, big_endian>::rela_ifunc(
|
|
Symbol_table* symtab,
|
|
Layout* layout)
|
|
{
|
|
if (this->ifunc_rel_ == NULL)
|
|
{
|
|
this->ifunc_rel_ = new Reloc_section(false);
|
|
layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
|
|
elfcpp::SHF_ALLOC, this->ifunc_rel_,
|
|
ORDER_DYNAMIC_PLT_RELOCS, false);
|
|
gold_assert(this->ifunc_rel_->output_section()
|
|
== this->rel_->output_section());
|
|
|
|
if (parameters->doing_static_link())
|
|
{
|
|
// A statically linked executable will only have a .rel.plt
|
|
// section to hold R_SPARC_IRELATIVE and R_SPARC_JMP_IREL
|
|
// relocs for STT_GNU_IFUNC symbols. The library will use
|
|
// these symbols to locate the IRELATIVE and JMP_IREL relocs
|
|
// at program startup time.
|
|
symtab->define_in_output_data("__rela_iplt_start", NULL,
|
|
Symbol_table::PREDEFINED,
|
|
this->ifunc_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->ifunc_rel_, 0, 0,
|
|
elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
|
|
elfcpp::STV_HIDDEN, 0, true, true);
|
|
}
|
|
}
|
|
return this->ifunc_rel_;
|
|
}
|
|
|
|
// Return the PLT address to use for a global symbol.
|
|
|
|
template<int size, bool big_endian>
|
|
uint64_t
|
|
Output_data_plt_sparc<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 = plt_index_to_offset(this->count_ + 4);
|
|
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_sparc<size, big_endian>::address_for_local(
|
|
const Relobj* object,
|
|
unsigned int r_sym)
|
|
{
|
|
return (this->address()
|
|
+ plt_index_to_offset(this->count_ + 4)
|
|
+ object->local_plt_offset(r_sym));
|
|
}
|
|
|
|
static const unsigned int sparc_nop = 0x01000000;
|
|
static const unsigned int sparc_sethi_g1 = 0x03000000;
|
|
static const unsigned int sparc_branch_always = 0x30800000;
|
|
static const unsigned int sparc_branch_always_pt = 0x30680000;
|
|
static const unsigned int sparc_mov = 0x80100000;
|
|
static const unsigned int sparc_mov_g0_o0 = 0x90100000;
|
|
static const unsigned int sparc_mov_o7_g5 = 0x8a10000f;
|
|
static const unsigned int sparc_call_plus_8 = 0x40000002;
|
|
static const unsigned int sparc_ldx_o7_imm_g1 = 0xc25be000;
|
|
static const unsigned int sparc_jmpl_o7_g1_g1 = 0x83c3c001;
|
|
static const unsigned int sparc_mov_g5_o7 = 0x9e100005;
|
|
|
|
// Write out the PLT.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_data_plt_sparc<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);
|
|
unsigned char* pov = oview;
|
|
|
|
memset(pov, 0, base_plt_entry_size * 4);
|
|
pov += this->first_plt_entry_offset();
|
|
|
|
unsigned int plt_offset = base_plt_entry_size * 4;
|
|
const unsigned int count = this->entry_count();
|
|
|
|
if (size == 64)
|
|
{
|
|
unsigned int limit;
|
|
|
|
limit = (count > 32768 ? 32768 : count);
|
|
|
|
for (unsigned int i = 0; i < limit; ++i)
|
|
{
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x00,
|
|
sparc_sethi_g1 + plt_offset);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x04,
|
|
sparc_branch_always_pt +
|
|
(((base_plt_entry_size -
|
|
(plt_offset + 4)) >> 2) &
|
|
0x7ffff));
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x08, sparc_nop);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x0c, sparc_nop);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x10, sparc_nop);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x14, sparc_nop);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x18, sparc_nop);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x1c, sparc_nop);
|
|
|
|
pov += base_plt_entry_size;
|
|
plt_offset += base_plt_entry_size;
|
|
}
|
|
|
|
if (count > 32768)
|
|
{
|
|
unsigned int ext_cnt = count - 32768;
|
|
unsigned int blks = ext_cnt / plt_entries_per_block;
|
|
|
|
for (unsigned int i = 0; i < blks; ++i)
|
|
{
|
|
unsigned int data_off = (plt_entries_per_block
|
|
* plt_insn_chunk_size) - 4;
|
|
|
|
for (unsigned int j = 0; j < plt_entries_per_block; ++j)
|
|
{
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x00,
|
|
sparc_mov_o7_g5);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x04,
|
|
sparc_call_plus_8);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x08,
|
|
sparc_nop);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x0c,
|
|
sparc_ldx_o7_imm_g1 +
|
|
(data_off & 0x1fff));
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x10,
|
|
sparc_jmpl_o7_g1_g1);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x14,
|
|
sparc_mov_g5_o7);
|
|
|
|
elfcpp::Swap<64, big_endian>::writeval(
|
|
pov + 0x4 + data_off,
|
|
(elfcpp::Elf_Xword) (oview - (pov + 0x04)));
|
|
|
|
pov += plt_insn_chunk_size;
|
|
data_off -= 16;
|
|
}
|
|
}
|
|
|
|
unsigned int sub_blk_cnt = ext_cnt % plt_entries_per_block;
|
|
for (unsigned int i = 0; i < sub_blk_cnt; ++i)
|
|
{
|
|
unsigned int data_off = (sub_blk_cnt
|
|
* plt_insn_chunk_size) - 4;
|
|
|
|
for (unsigned int j = 0; j < plt_entries_per_block; ++j)
|
|
{
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x00,
|
|
sparc_mov_o7_g5);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x04,
|
|
sparc_call_plus_8);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x08,
|
|
sparc_nop);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x0c,
|
|
sparc_ldx_o7_imm_g1 +
|
|
(data_off & 0x1fff));
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x10,
|
|
sparc_jmpl_o7_g1_g1);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x14,
|
|
sparc_mov_g5_o7);
|
|
|
|
elfcpp::Swap<64, big_endian>::writeval(
|
|
pov + 0x4 + data_off,
|
|
(elfcpp::Elf_Xword) (oview - (pov + 0x04)));
|
|
|
|
pov += plt_insn_chunk_size;
|
|
data_off -= 16;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (unsigned int i = 0; i < count; ++i)
|
|
{
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x00,
|
|
sparc_sethi_g1 + plt_offset);
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x04,
|
|
sparc_branch_always +
|
|
(((- (plt_offset + 4)) >> 2) &
|
|
0x003fffff));
|
|
elfcpp::Swap<32, true>::writeval(pov + 0x08, sparc_nop);
|
|
|
|
pov += base_plt_entry_size;
|
|
plt_offset += base_plt_entry_size;
|
|
}
|
|
|
|
elfcpp::Swap<32, true>::writeval(pov, sparc_nop);
|
|
pov += 4;
|
|
}
|
|
|
|
gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
|
|
|
|
of->write_output_view(offset, oview_size, oview);
|
|
}
|
|
|
|
// Create the PLT section.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<size, big_endian>::make_plt_section(Symbol_table* symtab,
|
|
Layout* layout)
|
|
{
|
|
// Create the GOT sections first.
|
|
this->got_section(symtab, layout);
|
|
|
|
// Ensure that .rela.dyn always appears before .rela.plt This is
|
|
// necessary due to how, on Sparc and some other targets, .rela.dyn
|
|
// needs to include .rela.plt in it's range.
|
|
this->rela_dyn_section(layout);
|
|
|
|
this->plt_ = new Output_data_plt_sparc<size, big_endian>(layout);
|
|
layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
|
|
(elfcpp::SHF_ALLOC
|
|
| elfcpp::SHF_EXECINSTR
|
|
| elfcpp::SHF_WRITE),
|
|
this->plt_, ORDER_NON_RELRO_FIRST, false);
|
|
|
|
// Define _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt section.
|
|
symtab->define_in_output_data("_PROCEDURE_LINKAGE_TABLE_", NULL,
|
|
Symbol_table::PREDEFINED,
|
|
this->plt_,
|
|
0, 0, elfcpp::STT_OBJECT,
|
|
elfcpp::STB_LOCAL,
|
|
elfcpp::STV_HIDDEN, 0,
|
|
false, false);
|
|
}
|
|
|
|
// Create a PLT entry for a global symbol.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<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_sparc<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_sparc<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_sparc<size, big_endian>::first_plt_entry_offset() const
|
|
{
|
|
return Output_data_plt_sparc<size, big_endian>::first_plt_entry_offset();
|
|
}
|
|
|
|
// Return the size of each PLT entry.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Target_sparc<size, big_endian>::plt_entry_size() const
|
|
{
|
|
return Output_data_plt_sparc<size, big_endian>::get_plt_entry_size();
|
|
}
|
|
|
|
// Create a GOT entry for the TLS module index.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Target_sparc<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;
|
|
unsigned int got_offset;
|
|
|
|
got = this->got_section(symtab, layout);
|
|
got_offset = got->add_constant(0);
|
|
rela_dyn->add_local(object, 0,
|
|
(size == 64 ?
|
|
elfcpp::R_SPARC_TLS_DTPMOD64 :
|
|
elfcpp::R_SPARC_TLS_DTPMOD32), 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.
|
|
|
|
static tls::Tls_optimization
|
|
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)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_GD_HI22: // Global-dynamic
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
// 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;
|
|
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22: // Local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
// This is Local-Dynamic, which refers to a local symbol in the
|
|
// dynamic TLS block. Since we know that we generating an
|
|
// executable, we can switch to Local-Exec.
|
|
return tls::TLSOPT_TO_LE;
|
|
|
|
case elfcpp::R_SPARC_TLS_LDO_HIX22: // Alternate local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDO_LOX10:
|
|
case elfcpp::R_SPARC_TLS_LDO_ADD:
|
|
// Another type of Local-Dynamic relocation.
|
|
return tls::TLSOPT_TO_LE;
|
|
|
|
case elfcpp::R_SPARC_TLS_IE_HI22: // Initial-exec
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
case elfcpp::R_SPARC_TLS_IE_LD:
|
|
case elfcpp::R_SPARC_TLS_IE_LDX:
|
|
case elfcpp::R_SPARC_TLS_IE_ADD:
|
|
// 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;
|
|
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22: // Local-exec
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
// 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_sparc<size, big_endian>::Scan::get_reference_flags(unsigned int r_type)
|
|
{
|
|
r_type &= 0xff;
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_NONE:
|
|
case elfcpp::R_SPARC_REGISTER:
|
|
case elfcpp::R_SPARC_GNU_VTINHERIT:
|
|
case elfcpp::R_SPARC_GNU_VTENTRY:
|
|
// No symbol reference.
|
|
return 0;
|
|
|
|
case elfcpp::R_SPARC_UA64:
|
|
case elfcpp::R_SPARC_64:
|
|
case elfcpp::R_SPARC_HIX22:
|
|
case elfcpp::R_SPARC_LOX10:
|
|
case elfcpp::R_SPARC_H34:
|
|
case elfcpp::R_SPARC_H44:
|
|
case elfcpp::R_SPARC_M44:
|
|
case elfcpp::R_SPARC_L44:
|
|
case elfcpp::R_SPARC_HH22:
|
|
case elfcpp::R_SPARC_HM10:
|
|
case elfcpp::R_SPARC_LM22:
|
|
case elfcpp::R_SPARC_HI22:
|
|
case elfcpp::R_SPARC_LO10:
|
|
case elfcpp::R_SPARC_OLO10:
|
|
case elfcpp::R_SPARC_UA32:
|
|
case elfcpp::R_SPARC_32:
|
|
case elfcpp::R_SPARC_UA16:
|
|
case elfcpp::R_SPARC_16:
|
|
case elfcpp::R_SPARC_11:
|
|
case elfcpp::R_SPARC_10:
|
|
case elfcpp::R_SPARC_8:
|
|
case elfcpp::R_SPARC_7:
|
|
case elfcpp::R_SPARC_6:
|
|
case elfcpp::R_SPARC_5:
|
|
return Symbol::ABSOLUTE_REF;
|
|
|
|
case elfcpp::R_SPARC_DISP8:
|
|
case elfcpp::R_SPARC_DISP16:
|
|
case elfcpp::R_SPARC_DISP32:
|
|
case elfcpp::R_SPARC_DISP64:
|
|
case elfcpp::R_SPARC_PC_HH22:
|
|
case elfcpp::R_SPARC_PC_HM10:
|
|
case elfcpp::R_SPARC_PC_LM22:
|
|
case elfcpp::R_SPARC_PC10:
|
|
case elfcpp::R_SPARC_PC22:
|
|
case elfcpp::R_SPARC_WDISP30:
|
|
case elfcpp::R_SPARC_WDISP22:
|
|
case elfcpp::R_SPARC_WDISP19:
|
|
case elfcpp::R_SPARC_WDISP16:
|
|
case elfcpp::R_SPARC_WDISP10:
|
|
return Symbol::RELATIVE_REF;
|
|
|
|
case elfcpp::R_SPARC_PLT64:
|
|
case elfcpp::R_SPARC_PLT32:
|
|
case elfcpp::R_SPARC_HIPLT22:
|
|
case elfcpp::R_SPARC_LOPLT10:
|
|
case elfcpp::R_SPARC_PCPLT10:
|
|
return Symbol::FUNCTION_CALL | Symbol::ABSOLUTE_REF;
|
|
|
|
case elfcpp::R_SPARC_PCPLT32:
|
|
case elfcpp::R_SPARC_PCPLT22:
|
|
case elfcpp::R_SPARC_WPLT30:
|
|
return Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF;
|
|
|
|
case elfcpp::R_SPARC_GOTDATA_OP:
|
|
case elfcpp::R_SPARC_GOTDATA_OP_HIX22:
|
|
case elfcpp::R_SPARC_GOTDATA_OP_LOX10:
|
|
case elfcpp::R_SPARC_GOT10:
|
|
case elfcpp::R_SPARC_GOT13:
|
|
case elfcpp::R_SPARC_GOT22:
|
|
// Absolute in GOT.
|
|
return Symbol::ABSOLUTE_REF;
|
|
|
|
case elfcpp::R_SPARC_TLS_GD_HI22: // Global-dynamic
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22: // Local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
case elfcpp::R_SPARC_TLS_LDO_HIX22: // Alternate local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDO_LOX10:
|
|
case elfcpp::R_SPARC_TLS_LDO_ADD:
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22:
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
case elfcpp::R_SPARC_TLS_IE_HI22: // Initial-exec
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
case elfcpp::R_SPARC_TLS_IE_LD:
|
|
case elfcpp::R_SPARC_TLS_IE_LDX:
|
|
case elfcpp::R_SPARC_TLS_IE_ADD:
|
|
return Symbol::TLS_REF;
|
|
|
|
case elfcpp::R_SPARC_COPY:
|
|
case elfcpp::R_SPARC_GLOB_DAT:
|
|
case elfcpp::R_SPARC_JMP_SLOT:
|
|
case elfcpp::R_SPARC_JMP_IREL:
|
|
case elfcpp::R_SPARC_RELATIVE:
|
|
case elfcpp::R_SPARC_IRELATIVE:
|
|
case elfcpp::R_SPARC_TLS_DTPMOD64:
|
|
case elfcpp::R_SPARC_TLS_DTPMOD32:
|
|
case elfcpp::R_SPARC_TLS_DTPOFF64:
|
|
case elfcpp::R_SPARC_TLS_DTPOFF32:
|
|
case elfcpp::R_SPARC_TLS_TPOFF64:
|
|
case elfcpp::R_SPARC_TLS_TPOFF32:
|
|
default:
|
|
// Not expected. We will give an error later.
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Generate a PLT entry slot for a call to __tls_get_addr
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<size, big_endian>::Scan::generate_tls_call(Symbol_table* symtab,
|
|
Layout* layout,
|
|
Target_sparc<size, big_endian>* target)
|
|
{
|
|
Symbol* gsym = target->tls_get_addr_sym(symtab);
|
|
|
|
target->make_plt_entry(symtab, layout, gsym);
|
|
}
|
|
|
|
// Report an unsupported relocation against a local symbol.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<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_sparc<size, big_endian>::Scan::check_non_pic(Relobj* object, unsigned int r_type)
|
|
{
|
|
gold_assert(r_type != elfcpp::R_SPARC_NONE);
|
|
|
|
if (size == 64)
|
|
{
|
|
switch (r_type)
|
|
{
|
|
// These are the relocation types supported by glibc for sparc 64-bit.
|
|
case elfcpp::R_SPARC_RELATIVE:
|
|
case elfcpp::R_SPARC_IRELATIVE:
|
|
case elfcpp::R_SPARC_COPY:
|
|
case elfcpp::R_SPARC_64:
|
|
case elfcpp::R_SPARC_GLOB_DAT:
|
|
case elfcpp::R_SPARC_JMP_SLOT:
|
|
case elfcpp::R_SPARC_JMP_IREL:
|
|
case elfcpp::R_SPARC_TLS_DTPMOD64:
|
|
case elfcpp::R_SPARC_TLS_DTPOFF64:
|
|
case elfcpp::R_SPARC_TLS_TPOFF64:
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22:
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
case elfcpp::R_SPARC_8:
|
|
case elfcpp::R_SPARC_16:
|
|
case elfcpp::R_SPARC_DISP8:
|
|
case elfcpp::R_SPARC_DISP16:
|
|
case elfcpp::R_SPARC_DISP32:
|
|
case elfcpp::R_SPARC_WDISP30:
|
|
case elfcpp::R_SPARC_LO10:
|
|
case elfcpp::R_SPARC_HI22:
|
|
case elfcpp::R_SPARC_OLO10:
|
|
case elfcpp::R_SPARC_H34:
|
|
case elfcpp::R_SPARC_H44:
|
|
case elfcpp::R_SPARC_M44:
|
|
case elfcpp::R_SPARC_L44:
|
|
case elfcpp::R_SPARC_HH22:
|
|
case elfcpp::R_SPARC_HM10:
|
|
case elfcpp::R_SPARC_LM22:
|
|
case elfcpp::R_SPARC_UA16:
|
|
case elfcpp::R_SPARC_UA32:
|
|
case elfcpp::R_SPARC_UA64:
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (r_type)
|
|
{
|
|
// These are the relocation types supported by glibc for sparc 32-bit.
|
|
case elfcpp::R_SPARC_RELATIVE:
|
|
case elfcpp::R_SPARC_IRELATIVE:
|
|
case elfcpp::R_SPARC_COPY:
|
|
case elfcpp::R_SPARC_GLOB_DAT:
|
|
case elfcpp::R_SPARC_32:
|
|
case elfcpp::R_SPARC_JMP_SLOT:
|
|
case elfcpp::R_SPARC_JMP_IREL:
|
|
case elfcpp::R_SPARC_TLS_DTPMOD32:
|
|
case elfcpp::R_SPARC_TLS_DTPOFF32:
|
|
case elfcpp::R_SPARC_TLS_TPOFF32:
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22:
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
case elfcpp::R_SPARC_8:
|
|
case elfcpp::R_SPARC_16:
|
|
case elfcpp::R_SPARC_DISP8:
|
|
case elfcpp::R_SPARC_DISP16:
|
|
case elfcpp::R_SPARC_DISP32:
|
|
case elfcpp::R_SPARC_LO10:
|
|
case elfcpp::R_SPARC_WDISP30:
|
|
case elfcpp::R_SPARC_HI22:
|
|
case elfcpp::R_SPARC_UA16:
|
|
case elfcpp::R_SPARC_UA32:
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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; "
|
|
"recompile with -fPIC"));
|
|
this->issued_non_pic_error_ = true;
|
|
return;
|
|
}
|
|
|
|
// 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_sparc<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_sparc<size, big_endian>::Scan::local(
|
|
Symbol_table* symtab,
|
|
Layout* layout,
|
|
Target_sparc<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;
|
|
|
|
bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
|
|
unsigned int orig_r_type = r_type;
|
|
r_type &= 0xff;
|
|
|
|
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_SPARC_NONE:
|
|
case elfcpp::R_SPARC_REGISTER:
|
|
case elfcpp::R_SPARC_GNU_VTINHERIT:
|
|
case elfcpp::R_SPARC_GNU_VTENTRY:
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_64:
|
|
case elfcpp::R_SPARC_32:
|
|
// If building a shared library (or a position-independent
|
|
// executable), we need to create a dynamic relocation for
|
|
// this location. The relocation applied at link time will
|
|
// apply the link-time value, so we flag the location with
|
|
// an R_SPARC_RELATIVE relocation so the dynamic loader can
|
|
// relocate it easily.
|
|
if (parameters->options().output_is_position_independent())
|
|
{
|
|
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
|
|
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
|
|
rela_dyn->add_local_relative(object, r_sym, elfcpp::R_SPARC_RELATIVE,
|
|
output_section, data_shndx,
|
|
reloc.get_r_offset(),
|
|
reloc.get_r_addend(), is_ifunc);
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_HIX22:
|
|
case elfcpp::R_SPARC_LOX10:
|
|
case elfcpp::R_SPARC_H34:
|
|
case elfcpp::R_SPARC_H44:
|
|
case elfcpp::R_SPARC_M44:
|
|
case elfcpp::R_SPARC_L44:
|
|
case elfcpp::R_SPARC_HH22:
|
|
case elfcpp::R_SPARC_HM10:
|
|
case elfcpp::R_SPARC_LM22:
|
|
case elfcpp::R_SPARC_UA64:
|
|
case elfcpp::R_SPARC_UA32:
|
|
case elfcpp::R_SPARC_UA16:
|
|
case elfcpp::R_SPARC_HI22:
|
|
case elfcpp::R_SPARC_LO10:
|
|
case elfcpp::R_SPARC_OLO10:
|
|
case elfcpp::R_SPARC_16:
|
|
case elfcpp::R_SPARC_11:
|
|
case elfcpp::R_SPARC_10:
|
|
case elfcpp::R_SPARC_8:
|
|
case elfcpp::R_SPARC_7:
|
|
case elfcpp::R_SPARC_6:
|
|
case elfcpp::R_SPARC_5:
|
|
// If building a shared library (or a position-independent
|
|
// executable), we need to create a dynamic relocation for
|
|
// this location.
|
|
if (parameters->options().output_is_position_independent())
|
|
{
|
|
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
|
|
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
|
|
|
|
check_non_pic(object, r_type);
|
|
if (lsym.get_st_type() != elfcpp::STT_SECTION)
|
|
{
|
|
rela_dyn->add_local(object, r_sym, orig_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, orig_r_type,
|
|
output_section, data_shndx,
|
|
reloc.get_r_offset(),
|
|
reloc.get_r_addend());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_WDISP30:
|
|
case elfcpp::R_SPARC_WPLT30:
|
|
case elfcpp::R_SPARC_WDISP22:
|
|
case elfcpp::R_SPARC_WDISP19:
|
|
case elfcpp::R_SPARC_WDISP16:
|
|
case elfcpp::R_SPARC_WDISP10:
|
|
case elfcpp::R_SPARC_DISP8:
|
|
case elfcpp::R_SPARC_DISP16:
|
|
case elfcpp::R_SPARC_DISP32:
|
|
case elfcpp::R_SPARC_DISP64:
|
|
case elfcpp::R_SPARC_PC10:
|
|
case elfcpp::R_SPARC_PC22:
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_GOTDATA_OP:
|
|
case elfcpp::R_SPARC_GOTDATA_OP_HIX22:
|
|
case elfcpp::R_SPARC_GOTDATA_OP_LOX10:
|
|
// We will optimize this into a GOT relative relocation
|
|
// and code transform the GOT load into an addition.
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_GOT10:
|
|
case elfcpp::R_SPARC_GOT13:
|
|
case elfcpp::R_SPARC_GOT22:
|
|
{
|
|
// The symbol requires a GOT entry.
|
|
Output_data_got<size, big_endian>* got;
|
|
unsigned int r_sym;
|
|
|
|
got = target->got_section(symtab, layout);
|
|
r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
|
|
|
|
// If we are generating a shared object, we need to add a
|
|
// dynamic relocation for this symbol's GOT entry.
|
|
if (parameters->options().output_is_position_independent())
|
|
{
|
|
if (!object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD))
|
|
{
|
|
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
|
|
unsigned int off = got->add_constant(0);
|
|
object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
|
|
rela_dyn->add_local_relative(object, r_sym,
|
|
elfcpp::R_SPARC_RELATIVE,
|
|
got, off, 0, is_ifunc);
|
|
}
|
|
}
|
|
else
|
|
got->add_local(object, r_sym, GOT_TYPE_STANDARD);
|
|
}
|
|
break;
|
|
|
|
// These are initial TLS relocs, which are expected when
|
|
// linking.
|
|
case elfcpp::R_SPARC_TLS_GD_HI22: // Global-dynamic
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22 : // Local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
case elfcpp::R_SPARC_TLS_LDO_HIX22: // Alternate local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDO_LOX10:
|
|
case elfcpp::R_SPARC_TLS_LDO_ADD:
|
|
case elfcpp::R_SPARC_TLS_IE_HI22: // Initial-exec
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
case elfcpp::R_SPARC_TLS_IE_LD:
|
|
case elfcpp::R_SPARC_TLS_IE_LDX:
|
|
case elfcpp::R_SPARC_TLS_IE_ADD:
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22: // Local-exec
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
{
|
|
bool output_is_shared = parameters->options().shared();
|
|
const tls::Tls_optimization optimized_type
|
|
= optimize_tls_reloc(!output_is_shared, r_type);
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_GD_HI22: // Global-dynamic
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
if (optimized_type == tls::TLSOPT_NONE)
|
|
{
|
|
// Create a pair of GOT entries for the module index and
|
|
// dtv-relative offset.
|
|
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,
|
|
lsym.get_st_shndx(),
|
|
GOT_TYPE_TLS_PAIR,
|
|
target->rela_dyn_section(layout),
|
|
(size == 64
|
|
? elfcpp::R_SPARC_TLS_DTPMOD64
|
|
: elfcpp::R_SPARC_TLS_DTPMOD32));
|
|
if (r_type == elfcpp::R_SPARC_TLS_GD_CALL)
|
|
generate_tls_call(symtab, layout, target);
|
|
}
|
|
else if (optimized_type != tls::TLSOPT_TO_LE)
|
|
unsupported_reloc_local(object, r_type);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22 : // Local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
if (optimized_type == tls::TLSOPT_NONE)
|
|
{
|
|
// Create a GOT entry for the module index.
|
|
target->got_mod_index_entry(symtab, layout, object);
|
|
|
|
if (r_type == elfcpp::R_SPARC_TLS_LDM_CALL)
|
|
generate_tls_call(symtab, layout, target);
|
|
}
|
|
else if (optimized_type != tls::TLSOPT_TO_LE)
|
|
unsupported_reloc_local(object, r_type);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LDO_HIX22: // Alternate local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDO_LOX10:
|
|
case elfcpp::R_SPARC_TLS_LDO_ADD:
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_IE_HI22: // Initial-exec
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
case elfcpp::R_SPARC_TLS_IE_LD:
|
|
case elfcpp::R_SPARC_TLS_IE_LDX:
|
|
case elfcpp::R_SPARC_TLS_IE_ADD:
|
|
layout->set_has_static_tls();
|
|
if (optimized_type == tls::TLSOPT_NONE)
|
|
{
|
|
// Create a GOT entry for the tp-relative offset.
|
|
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());
|
|
|
|
if (!object->local_has_got_offset(r_sym, GOT_TYPE_TLS_OFFSET))
|
|
{
|
|
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
|
|
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 == 64 ?
|
|
elfcpp::R_SPARC_TLS_TPOFF64 :
|
|
elfcpp::R_SPARC_TLS_TPOFF32),
|
|
got, off, 0);
|
|
}
|
|
}
|
|
else if (optimized_type != tls::TLSOPT_TO_LE)
|
|
unsupported_reloc_local(object, r_type);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22: // Local-exec
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
layout->set_has_static_tls();
|
|
if (output_is_shared)
|
|
{
|
|
// We need to create a dynamic relocation.
|
|
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;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// These are relocations which should only be seen by the
|
|
// dynamic linker, and should never be seen here.
|
|
case elfcpp::R_SPARC_COPY:
|
|
case elfcpp::R_SPARC_GLOB_DAT:
|
|
case elfcpp::R_SPARC_JMP_SLOT:
|
|
case elfcpp::R_SPARC_JMP_IREL:
|
|
case elfcpp::R_SPARC_RELATIVE:
|
|
case elfcpp::R_SPARC_IRELATIVE:
|
|
case elfcpp::R_SPARC_TLS_DTPMOD64:
|
|
case elfcpp::R_SPARC_TLS_DTPMOD32:
|
|
case elfcpp::R_SPARC_TLS_DTPOFF64:
|
|
case elfcpp::R_SPARC_TLS_DTPOFF32:
|
|
case elfcpp::R_SPARC_TLS_TPOFF64:
|
|
case elfcpp::R_SPARC_TLS_TPOFF32:
|
|
gold_error(_("%s: unexpected reloc %u in object file"),
|
|
object->name().c_str(), r_type);
|
|
break;
|
|
|
|
default:
|
|
unsupported_reloc_local(object, r_type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Report an unsupported relocation against a global symbol.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<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());
|
|
}
|
|
|
|
// Scan a relocation for a global symbol.
|
|
|
|
template<int size, bool big_endian>
|
|
inline void
|
|
Target_sparc<size, big_endian>::Scan::global(
|
|
Symbol_table* symtab,
|
|
Layout* layout,
|
|
Target_sparc<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)
|
|
{
|
|
unsigned int orig_r_type = r_type;
|
|
bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
|
|
|
|
// 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);
|
|
|
|
r_type &= 0xff;
|
|
|
|
// A STT_GNU_IFUNC symbol may require a PLT entry.
|
|
if (is_ifunc
|
|
&& this->reloc_needs_plt_for_ifunc(object, r_type))
|
|
target->make_plt_entry(symtab, layout, gsym);
|
|
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_NONE:
|
|
case elfcpp::R_SPARC_REGISTER:
|
|
case elfcpp::R_SPARC_GNU_VTINHERIT:
|
|
case elfcpp::R_SPARC_GNU_VTENTRY:
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PLT64:
|
|
case elfcpp::R_SPARC_PLT32:
|
|
case elfcpp::R_SPARC_HIPLT22:
|
|
case elfcpp::R_SPARC_LOPLT10:
|
|
case elfcpp::R_SPARC_PCPLT32:
|
|
case elfcpp::R_SPARC_PCPLT22:
|
|
case elfcpp::R_SPARC_PCPLT10:
|
|
case elfcpp::R_SPARC_WPLT30:
|
|
// 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_SPARC_DISP8:
|
|
case elfcpp::R_SPARC_DISP16:
|
|
case elfcpp::R_SPARC_DISP32:
|
|
case elfcpp::R_SPARC_DISP64:
|
|
case elfcpp::R_SPARC_PC_HH22:
|
|
case elfcpp::R_SPARC_PC_HM10:
|
|
case elfcpp::R_SPARC_PC_LM22:
|
|
case elfcpp::R_SPARC_PC10:
|
|
case elfcpp::R_SPARC_PC22:
|
|
case elfcpp::R_SPARC_WDISP30:
|
|
case elfcpp::R_SPARC_WDISP22:
|
|
case elfcpp::R_SPARC_WDISP19:
|
|
case elfcpp::R_SPARC_WDISP16:
|
|
case elfcpp::R_SPARC_WDISP10:
|
|
{
|
|
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
|
|
{
|
|
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
|
|
check_non_pic(object, r_type);
|
|
rela_dyn->add_global(gsym, orig_r_type, output_section, object,
|
|
data_shndx, reloc.get_r_offset(),
|
|
reloc.get_r_addend());
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_UA64:
|
|
case elfcpp::R_SPARC_64:
|
|
case elfcpp::R_SPARC_HIX22:
|
|
case elfcpp::R_SPARC_LOX10:
|
|
case elfcpp::R_SPARC_H34:
|
|
case elfcpp::R_SPARC_H44:
|
|
case elfcpp::R_SPARC_M44:
|
|
case elfcpp::R_SPARC_L44:
|
|
case elfcpp::R_SPARC_HH22:
|
|
case elfcpp::R_SPARC_HM10:
|
|
case elfcpp::R_SPARC_LM22:
|
|
case elfcpp::R_SPARC_HI22:
|
|
case elfcpp::R_SPARC_LO10:
|
|
case elfcpp::R_SPARC_OLO10:
|
|
case elfcpp::R_SPARC_UA32:
|
|
case elfcpp::R_SPARC_32:
|
|
case elfcpp::R_SPARC_UA16:
|
|
case elfcpp::R_SPARC_16:
|
|
case elfcpp::R_SPARC_11:
|
|
case elfcpp::R_SPARC_10:
|
|
case elfcpp::R_SPARC_8:
|
|
case elfcpp::R_SPARC_7:
|
|
case elfcpp::R_SPARC_6:
|
|
case elfcpp::R_SPARC_5:
|
|
{
|
|
// 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)))
|
|
{
|
|
unsigned int r_off = reloc.get_r_offset();
|
|
|
|
// The assembler can sometimes emit unaligned relocations
|
|
// for dwarf2 cfi directives.
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_16:
|
|
if (r_off & 0x1)
|
|
orig_r_type = r_type = elfcpp::R_SPARC_UA16;
|
|
break;
|
|
case elfcpp::R_SPARC_32:
|
|
if (r_off & 0x3)
|
|
orig_r_type = r_type = elfcpp::R_SPARC_UA32;
|
|
break;
|
|
case elfcpp::R_SPARC_64:
|
|
if (r_off & 0x7)
|
|
orig_r_type = r_type = elfcpp::R_SPARC_UA64;
|
|
break;
|
|
case elfcpp::R_SPARC_UA16:
|
|
if (!(r_off & 0x1))
|
|
orig_r_type = r_type = elfcpp::R_SPARC_16;
|
|
break;
|
|
case elfcpp::R_SPARC_UA32:
|
|
if (!(r_off & 0x3))
|
|
orig_r_type = r_type = elfcpp::R_SPARC_32;
|
|
break;
|
|
case elfcpp::R_SPARC_UA64:
|
|
if (!(r_off & 0x7))
|
|
orig_r_type = r_type = elfcpp::R_SPARC_64;
|
|
break;
|
|
}
|
|
|
|
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_SPARC_64)
|
|
|| (size == 32 && r_type == elfcpp::R_SPARC_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_ifunc_section(layout);
|
|
unsigned int r_type = elfcpp::R_SPARC_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_SPARC_32
|
|
|| r_type == elfcpp::R_SPARC_64)
|
|
&& gsym->can_use_relative_reloc(false))
|
|
{
|
|
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
|
|
rela_dyn->add_global_relative(gsym, elfcpp::R_SPARC_RELATIVE,
|
|
output_section, object,
|
|
data_shndx, reloc.get_r_offset(),
|
|
reloc.get_r_addend(), is_ifunc);
|
|
}
|
|
else
|
|
{
|
|
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
|
|
|
|
check_non_pic(object, r_type);
|
|
if (gsym->is_from_dynobj()
|
|
|| gsym->is_undefined()
|
|
|| gsym->is_preemptible())
|
|
rela_dyn->add_global(gsym, orig_r_type, output_section,
|
|
object, data_shndx,
|
|
reloc.get_r_offset(),
|
|
reloc.get_r_addend());
|
|
else
|
|
rela_dyn->add_symbolless_global_addend(gsym, orig_r_type,
|
|
output_section,
|
|
object, data_shndx,
|
|
reloc.get_r_offset(),
|
|
reloc.get_r_addend());
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_GOTDATA_OP:
|
|
case elfcpp::R_SPARC_GOTDATA_OP_HIX22:
|
|
case elfcpp::R_SPARC_GOTDATA_OP_LOX10:
|
|
if (gsym->is_defined()
|
|
&& !gsym->is_from_dynobj()
|
|
&& !gsym->is_preemptible()
|
|
&& !is_ifunc)
|
|
{
|
|
// We will optimize this into a GOT relative relocation
|
|
// and code transform the GOT load into an addition.
|
|
break;
|
|
}
|
|
case elfcpp::R_SPARC_GOT10:
|
|
case elfcpp::R_SPARC_GOT13:
|
|
case elfcpp::R_SPARC_GOT22:
|
|
{
|
|
// The symbol requires a GOT entry.
|
|
Output_data_got<size, big_endian>* got;
|
|
|
|
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
|
|
// GOT entry with a dynamic relocation.
|
|
bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
|
|
|
|
// 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.
|
|
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
|
|
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()
|
|
&& !gsym->is_forced_local()))
|
|
{
|
|
unsigned int r_type = elfcpp::R_SPARC_GLOB_DAT;
|
|
|
|
// If this symbol is forced local, this relocation will
|
|
// not work properly. That's because ld.so on sparc
|
|
// (and 32-bit powerpc) expects st_value in the r_addend
|
|
// of relocations for STB_LOCAL symbols. Curiously the
|
|
// BFD linker does not promote global hidden symbols to be
|
|
// STB_LOCAL in the dynamic symbol table like Gold does.
|
|
gold_assert(!gsym->is_forced_local());
|
|
got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, rela_dyn,
|
|
r_type);
|
|
}
|
|
else if (!gsym->has_got_offset(GOT_TYPE_STANDARD))
|
|
{
|
|
unsigned int off = got->add_constant(0);
|
|
|
|
gsym->set_got_offset(GOT_TYPE_STANDARD, off);
|
|
if (is_ifunc)
|
|
{
|
|
// 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();
|
|
}
|
|
rela_dyn->add_global_relative(gsym, elfcpp::R_SPARC_RELATIVE,
|
|
got, off, 0, is_ifunc);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// These are initial tls relocs, which are expected when
|
|
// linking.
|
|
case elfcpp::R_SPARC_TLS_GD_HI22: // Global-dynamic
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22: // Local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
case elfcpp::R_SPARC_TLS_LDO_HIX22: // Alternate local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDO_LOX10:
|
|
case elfcpp::R_SPARC_TLS_LDO_ADD:
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22:
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
case elfcpp::R_SPARC_TLS_IE_HI22: // Initial-exec
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
case elfcpp::R_SPARC_TLS_IE_LD:
|
|
case elfcpp::R_SPARC_TLS_IE_LDX:
|
|
case elfcpp::R_SPARC_TLS_IE_ADD:
|
|
{
|
|
const bool is_final = gsym->final_value_is_known();
|
|
const tls::Tls_optimization optimized_type
|
|
= optimize_tls_reloc(is_final, r_type);
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_GD_HI22: // Global-dynamic
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
if (optimized_type == tls::TLSOPT_NONE)
|
|
{
|
|
// Create a pair of GOT entries for the module index and
|
|
// dtv-relative offset.
|
|
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 == 64
|
|
? elfcpp::R_SPARC_TLS_DTPMOD64
|
|
: elfcpp::R_SPARC_TLS_DTPMOD32),
|
|
(size == 64
|
|
? elfcpp::R_SPARC_TLS_DTPOFF64
|
|
: elfcpp::R_SPARC_TLS_DTPOFF32));
|
|
|
|
// Emit R_SPARC_WPLT30 against "__tls_get_addr"
|
|
if (r_type == elfcpp::R_SPARC_TLS_GD_CALL)
|
|
generate_tls_call(symtab, layout, target);
|
|
}
|
|
else if (optimized_type == 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 == 64 ?
|
|
elfcpp::R_SPARC_TLS_TPOFF64 :
|
|
elfcpp::R_SPARC_TLS_TPOFF32));
|
|
}
|
|
else if (optimized_type != tls::TLSOPT_TO_LE)
|
|
unsupported_reloc_global(object, r_type, gsym);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22: // Local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
if (optimized_type == tls::TLSOPT_NONE)
|
|
{
|
|
// Create a GOT entry for the module index.
|
|
target->got_mod_index_entry(symtab, layout, object);
|
|
|
|
if (r_type == elfcpp::R_SPARC_TLS_LDM_CALL)
|
|
generate_tls_call(symtab, layout, target);
|
|
}
|
|
else if (optimized_type != tls::TLSOPT_TO_LE)
|
|
unsupported_reloc_global(object, r_type, gsym);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LDO_HIX22: // Alternate local-dynamic
|
|
case elfcpp::R_SPARC_TLS_LDO_LOX10:
|
|
case elfcpp::R_SPARC_TLS_LDO_ADD:
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22:
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
layout->set_has_static_tls();
|
|
if (parameters->options().shared())
|
|
{
|
|
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
|
|
rela_dyn->add_symbolless_global_addend(gsym, orig_r_type,
|
|
output_section, object,
|
|
data_shndx, reloc.get_r_offset(),
|
|
0);
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_IE_HI22: // Initial-exec
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
case elfcpp::R_SPARC_TLS_IE_LD:
|
|
case elfcpp::R_SPARC_TLS_IE_LDX:
|
|
case elfcpp::R_SPARC_TLS_IE_ADD:
|
|
layout->set_has_static_tls();
|
|
if (optimized_type == 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 == 64
|
|
? elfcpp::R_SPARC_TLS_TPOFF64
|
|
: elfcpp::R_SPARC_TLS_TPOFF32));
|
|
}
|
|
else if (optimized_type != tls::TLSOPT_TO_LE)
|
|
unsupported_reloc_global(object, r_type, gsym);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// These are relocations which should only be seen by the
|
|
// dynamic linker, and should never be seen here.
|
|
case elfcpp::R_SPARC_COPY:
|
|
case elfcpp::R_SPARC_GLOB_DAT:
|
|
case elfcpp::R_SPARC_JMP_SLOT:
|
|
case elfcpp::R_SPARC_JMP_IREL:
|
|
case elfcpp::R_SPARC_RELATIVE:
|
|
case elfcpp::R_SPARC_IRELATIVE:
|
|
case elfcpp::R_SPARC_TLS_DTPMOD64:
|
|
case elfcpp::R_SPARC_TLS_DTPMOD32:
|
|
case elfcpp::R_SPARC_TLS_DTPOFF64:
|
|
case elfcpp::R_SPARC_TLS_DTPOFF32:
|
|
case elfcpp::R_SPARC_TLS_TPOFF64:
|
|
case elfcpp::R_SPARC_TLS_TPOFF32:
|
|
gold_error(_("%s: unexpected reloc %u in object file"),
|
|
object->name().c_str(), r_type);
|
|
break;
|
|
|
|
default:
|
|
unsupported_reloc_global(object, r_type, gsym);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Process relocations for gc.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<size, big_endian>::gc_process_relocs(
|
|
Symbol_table* symtab,
|
|
Layout* layout,
|
|
Sized_relobj_file<size, big_endian>* object,
|
|
unsigned int data_shndx,
|
|
unsigned int,
|
|
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_sparc<size, big_endian> Sparc;
|
|
typedef typename Target_sparc<size, big_endian>::Scan Scan;
|
|
|
|
gold::gc_process_relocs<size, big_endian, Sparc, elfcpp::SHT_RELA, Scan,
|
|
typename Target_sparc::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_sparc<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_sparc<size, big_endian> Sparc;
|
|
typedef typename Target_sparc<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, Sparc, elfcpp::SHT_RELA, Scan>(
|
|
symtab,
|
|
layout,
|
|
this,
|
|
object,
|
|
data_shndx,
|
|
prelocs,
|
|
reloc_count,
|
|
output_section,
|
|
needs_special_offset_handling,
|
|
local_symbol_count,
|
|
plocal_symbols);
|
|
}
|
|
|
|
// Finalize the sections.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<size, big_endian>::do_finalize_sections(
|
|
Layout* layout,
|
|
const Input_objects*,
|
|
Symbol_table* symtab)
|
|
{
|
|
if (this->plt_)
|
|
this->plt_->emit_pending_ifunc_relocs();
|
|
|
|
// Fill in some more dynamic tags.
|
|
const Reloc_section* rel_plt = (this->plt_ == NULL
|
|
? NULL
|
|
: this->plt_->rel_plt());
|
|
layout->add_target_dynamic_tags(false, this->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));
|
|
|
|
if (parameters->doing_static_link()
|
|
&& (this->plt_ == NULL || !this->plt_->has_ifunc_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_sparc<size, big_endian>::Relocate::relocate(
|
|
const Relocate_info<size, big_endian>* relinfo,
|
|
Target_sparc* 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 view_size)
|
|
{
|
|
bool orig_is_ifunc = psymval->is_ifunc_symbol();
|
|
r_type &= 0xff;
|
|
|
|
if (this->ignore_gd_add_)
|
|
{
|
|
if (r_type != elfcpp::R_SPARC_TLS_GD_ADD)
|
|
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
|
|
_("missing expected TLS relocation"));
|
|
else
|
|
{
|
|
this->ignore_gd_add_ = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (view == NULL)
|
|
return true;
|
|
|
|
if (this->reloc_adjust_addr_ == view)
|
|
view -= 4;
|
|
|
|
typedef Sparc_relocate_functions<size, big_endian> Reloc;
|
|
const Sized_relobj_file<size, big_endian>* object = relinfo->object;
|
|
|
|
// Pick the value to use for symbols defined in shared objects.
|
|
Symbol_value<size> symval;
|
|
if (gsym != NULL
|
|
&& gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
|
|
{
|
|
elfcpp::Elf_Xword value;
|
|
|
|
value = target->plt_address_for_global(gsym);
|
|
|
|
symval.set_output_value(value);
|
|
|
|
psymval = &symval;
|
|
}
|
|
else if (gsym == NULL && orig_is_ifunc)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
const elfcpp::Elf_Xword addend = rela.get_r_addend();
|
|
|
|
// Get the GOT offset if needed. Unlike i386 and x86_64, our GOT
|
|
// pointer points to the beginning, not the end, of the table.
|
|
// So we just use the plain offset.
|
|
unsigned int got_offset = 0;
|
|
bool gdop_valid = false;
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_GOTDATA_OP:
|
|
case elfcpp::R_SPARC_GOTDATA_OP_HIX22:
|
|
case elfcpp::R_SPARC_GOTDATA_OP_LOX10:
|
|
// If this is local, we did not create a GOT entry because we
|
|
// intend to transform this into a GOT relative relocation.
|
|
if (gsym == NULL
|
|
|| (gsym->is_defined()
|
|
&& !gsym->is_from_dynobj()
|
|
&& !gsym->is_preemptible()
|
|
&& !orig_is_ifunc))
|
|
{
|
|
got_offset = psymval->value(object, 0) - target->got_address();
|
|
gdop_valid = true;
|
|
break;
|
|
}
|
|
case elfcpp::R_SPARC_GOT10:
|
|
case elfcpp::R_SPARC_GOT13:
|
|
case elfcpp::R_SPARC_GOT22:
|
|
if (gsym != NULL)
|
|
{
|
|
gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD));
|
|
got_offset = gsym->got_offset(GOT_TYPE_STANDARD);
|
|
}
|
|
else
|
|
{
|
|
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
|
|
gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD));
|
|
got_offset = object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_NONE:
|
|
case elfcpp::R_SPARC_REGISTER:
|
|
case elfcpp::R_SPARC_GNU_VTINHERIT:
|
|
case elfcpp::R_SPARC_GNU_VTENTRY:
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_8:
|
|
Relocate_functions<size, big_endian>::rela8(view, object,
|
|
psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_16:
|
|
if (rela.get_r_offset() & 0x1)
|
|
{
|
|
// The assembler can sometimes emit unaligned relocations
|
|
// for dwarf2 cfi directives.
|
|
Reloc::ua16(view, object, psymval, addend);
|
|
}
|
|
else
|
|
Relocate_functions<size, big_endian>::rela16(view, object,
|
|
psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_32:
|
|
if (!parameters->options().output_is_position_independent())
|
|
{
|
|
if (rela.get_r_offset() & 0x3)
|
|
{
|
|
// The assembler can sometimes emit unaligned relocations
|
|
// for dwarf2 cfi directives.
|
|
Reloc::ua32(view, object, psymval, addend);
|
|
}
|
|
else
|
|
Relocate_functions<size, big_endian>::rela32(view, object,
|
|
psymval, addend);
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_DISP8:
|
|
Reloc::disp8(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_DISP16:
|
|
Reloc::disp16(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_DISP32:
|
|
Reloc::disp32(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_DISP64:
|
|
Reloc::disp64(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_WDISP30:
|
|
case elfcpp::R_SPARC_WPLT30:
|
|
Reloc::wdisp30(view, object, psymval, addend, address);
|
|
if (target->may_relax())
|
|
relax_call(target, view, rela, view_size);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_WDISP22:
|
|
Reloc::wdisp22(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_WDISP19:
|
|
Reloc::wdisp19(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_WDISP16:
|
|
Reloc::wdisp16(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_WDISP10:
|
|
Reloc::wdisp10(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_HI22:
|
|
Reloc::hi22(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_22:
|
|
Reloc::rela32_22(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_13:
|
|
Reloc::rela32_13(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_LO10:
|
|
Reloc::lo10(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_GOT10:
|
|
Reloc::lo10(view, got_offset, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_GOTDATA_OP:
|
|
if (gdop_valid)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Insntype;
|
|
Insntype* wv = reinterpret_cast<Insntype*>(view);
|
|
Insntype val;
|
|
|
|
// {ld,ldx} [%rs1 + %rs2], %rd --> add %rs1, %rs2, %rd
|
|
val = elfcpp::Swap<32, true>::readval(wv);
|
|
val = 0x80000000 | (val & 0x3e07c01f);
|
|
elfcpp::Swap<32, true>::writeval(wv, val);
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_GOTDATA_OP_LOX10:
|
|
if (gdop_valid)
|
|
{
|
|
Reloc::gdop_lox10(view, got_offset, addend);
|
|
break;
|
|
}
|
|
/* Fall through. */
|
|
case elfcpp::R_SPARC_GOT13:
|
|
Reloc::rela32_13(view, got_offset, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_GOTDATA_OP_HIX22:
|
|
if (gdop_valid)
|
|
{
|
|
Reloc::gdop_hix22(view, got_offset, addend);
|
|
break;
|
|
}
|
|
/* Fall through. */
|
|
case elfcpp::R_SPARC_GOT22:
|
|
Reloc::hi22(view, got_offset, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PC10:
|
|
Reloc::pc10(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PC22:
|
|
Reloc::pc22(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_DTPOFF32:
|
|
case elfcpp::R_SPARC_UA32:
|
|
Reloc::ua32(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PLT64:
|
|
Relocate_functions<size, big_endian>::rela64(view, object,
|
|
psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PLT32:
|
|
Relocate_functions<size, big_endian>::rela32(view, object,
|
|
psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_HIPLT22:
|
|
Reloc::hi22(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_LOPLT10:
|
|
Reloc::lo10(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PCPLT32:
|
|
Reloc::disp32(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PCPLT22:
|
|
Reloc::pcplt22(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PCPLT10:
|
|
Reloc::lo10(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_64:
|
|
if (!parameters->options().output_is_position_independent())
|
|
{
|
|
if (rela.get_r_offset() & 0x7)
|
|
{
|
|
// The assembler can sometimes emit unaligned relocations
|
|
// for dwarf2 cfi directives.
|
|
Reloc::ua64(view, object, psymval, addend);
|
|
}
|
|
else
|
|
Relocate_functions<size, big_endian>::rela64(view, object,
|
|
psymval, addend);
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_OLO10:
|
|
{
|
|
unsigned int addend2 = rela.get_r_info() & 0xffffffff;
|
|
addend2 = ((addend2 >> 8) ^ 0x800000) - 0x800000;
|
|
Reloc::olo10(view, object, psymval, addend, addend2);
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_HH22:
|
|
Reloc::hh22(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PC_HH22:
|
|
Reloc::pc_hh22(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_HM10:
|
|
Reloc::hm10(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PC_HM10:
|
|
Reloc::pc_hm10(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_LM22:
|
|
Reloc::hi22(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_PC_LM22:
|
|
Reloc::pcplt22(view, object, psymval, addend, address);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_11:
|
|
Reloc::rela32_11(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_10:
|
|
Reloc::rela32_10(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_7:
|
|
Reloc::rela32_7(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_6:
|
|
Reloc::rela32_6(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_5:
|
|
Reloc::rela32_5(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_HIX22:
|
|
Reloc::hix22(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_LOX10:
|
|
Reloc::lox10(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_H34:
|
|
Reloc::h34(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_H44:
|
|
Reloc::h44(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_M44:
|
|
Reloc::m44(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_L44:
|
|
Reloc::l44(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_DTPOFF64:
|
|
case elfcpp::R_SPARC_UA64:
|
|
Reloc::ua64(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_UA16:
|
|
Reloc::ua16(view, object, psymval, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_GD_HI22:
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22:
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
case elfcpp::R_SPARC_TLS_LDO_HIX22:
|
|
case elfcpp::R_SPARC_TLS_LDO_LOX10:
|
|
case elfcpp::R_SPARC_TLS_LDO_ADD:
|
|
case elfcpp::R_SPARC_TLS_IE_HI22:
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
case elfcpp::R_SPARC_TLS_IE_LD:
|
|
case elfcpp::R_SPARC_TLS_IE_LDX:
|
|
case elfcpp::R_SPARC_TLS_IE_ADD:
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22:
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
this->relocate_tls(relinfo, target, relnum, rela,
|
|
r_type, gsym, psymval, view,
|
|
address, view_size);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_COPY:
|
|
case elfcpp::R_SPARC_GLOB_DAT:
|
|
case elfcpp::R_SPARC_JMP_SLOT:
|
|
case elfcpp::R_SPARC_JMP_IREL:
|
|
case elfcpp::R_SPARC_RELATIVE:
|
|
case elfcpp::R_SPARC_IRELATIVE:
|
|
// These are outstanding tls relocs, which are unexpected when
|
|
// linking.
|
|
case elfcpp::R_SPARC_TLS_DTPMOD64:
|
|
case elfcpp::R_SPARC_TLS_DTPMOD32:
|
|
case elfcpp::R_SPARC_TLS_TPOFF64:
|
|
case elfcpp::R_SPARC_TLS_TPOFF32:
|
|
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;
|
|
}
|
|
|
|
// Perform a TLS relocation.
|
|
|
|
template<int size, bool big_endian>
|
|
inline void
|
|
Target_sparc<size, big_endian>::Relocate::relocate_tls(
|
|
const Relocate_info<size, big_endian>* relinfo,
|
|
Target_sparc<size, big_endian>* target,
|
|
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)
|
|
{
|
|
Output_segment* tls_segment = relinfo->layout->tls_segment();
|
|
typedef Sparc_relocate_functions<size, big_endian> Reloc;
|
|
const Sized_relobj_file<size, big_endian>* object = relinfo->object;
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Insntype;
|
|
|
|
const elfcpp::Elf_Xword addend = rela.get_r_addend();
|
|
typename elfcpp::Elf_types<size>::Elf_Addr value = psymval->value(object, 0);
|
|
|
|
const bool is_final =
|
|
(gsym == NULL
|
|
? !parameters->options().output_is_position_independent()
|
|
: gsym->final_value_is_known());
|
|
const tls::Tls_optimization optimized_type
|
|
= optimize_tls_reloc(is_final, r_type);
|
|
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_GD_HI22:
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
if (optimized_type == tls::TLSOPT_TO_LE)
|
|
{
|
|
Insntype* wv = reinterpret_cast<Insntype*>(view);
|
|
Insntype val;
|
|
|
|
value -= tls_segment->memsz();
|
|
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_GD_HI22:
|
|
// TLS_GD_HI22 --> TLS_LE_HIX22
|
|
Reloc::hix22(view, value, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
// TLS_GD_LO10 --> TLS_LE_LOX10
|
|
Reloc::lox10(view, value, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
// add %reg1, %reg2, %reg3 --> mov %g7, %reg2, %reg3
|
|
val = elfcpp::Swap<32, true>::readval(wv);
|
|
val = (val & ~0x7c000) | 0x1c000;
|
|
elfcpp::Swap<32, true>::writeval(wv, val);
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
// call __tls_get_addr --> nop
|
|
elfcpp::Swap<32, true>::writeval(wv, sparc_nop);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE
|
|
? GOT_TYPE_TLS_OFFSET
|
|
: GOT_TYPE_TLS_PAIR);
|
|
if (gsym != NULL)
|
|
{
|
|
gold_assert(gsym->has_got_offset(got_type));
|
|
value = gsym->got_offset(got_type);
|
|
}
|
|
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));
|
|
value = object->local_got_offset(r_sym, got_type);
|
|
}
|
|
if (optimized_type == tls::TLSOPT_TO_IE)
|
|
{
|
|
Insntype* wv = reinterpret_cast<Insntype*>(view);
|
|
Insntype val;
|
|
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_GD_HI22:
|
|
// TLS_GD_HI22 --> TLS_IE_HI22
|
|
Reloc::hi22(view, value, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
// TLS_GD_LO10 --> TLS_IE_LO10
|
|
Reloc::lo10(view, value, addend);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
// add %reg1, %reg2, %reg3 --> ld [%reg1 + %reg2], %reg3
|
|
val = elfcpp::Swap<32, true>::readval(wv);
|
|
|
|
if (size == 64)
|
|
val |= 0xc0580000;
|
|
else
|
|
val |= 0xc0000000;
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
// The compiler can put the TLS_GD_ADD instruction
|
|
// into the delay slot of the call. If so, we need
|
|
// to transpose the two instructions so that the
|
|
// new sequence works properly.
|
|
//
|
|
// The test we use is if the instruction in the
|
|
// delay slot is an add with destination register
|
|
// equal to %o0
|
|
val = elfcpp::Swap<32, true>::readval(wv + 1);
|
|
if ((val & 0x81f80000) == 0x80000000
|
|
&& ((val >> 25) & 0x1f) == 0x8)
|
|
{
|
|
if (size == 64)
|
|
val |= 0xc0580000;
|
|
else
|
|
val |= 0xc0000000;
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val);
|
|
|
|
wv += 1;
|
|
this->ignore_gd_add_ = true;
|
|
}
|
|
else
|
|
{
|
|
// Even if the delay slot isn't the TLS_GD_ADD
|
|
// instruction, we still have to handle the case
|
|
// where it sets up %o0 in some other way.
|
|
elfcpp::Swap<32, true>::writeval(wv, val);
|
|
wv += 1;
|
|
this->reloc_adjust_addr_ = view + 4;
|
|
}
|
|
// call __tls_get_addr --> add %g7, %o0, %o0
|
|
elfcpp::Swap<32, true>::writeval(wv, 0x9001c008);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
else if (optimized_type == tls::TLSOPT_NONE)
|
|
{
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_GD_HI22:
|
|
Reloc::hi22(view, value, addend);
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_GD_LO10:
|
|
Reloc::lo10(view, value, addend);
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_GD_ADD:
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_GD_CALL:
|
|
{
|
|
Symbol_value<size> symval;
|
|
elfcpp::Elf_Xword value;
|
|
Symbol* tsym;
|
|
|
|
tsym = target->tls_get_addr_sym_;
|
|
gold_assert(tsym);
|
|
value = (target->plt_section()->address() +
|
|
tsym->plt_offset());
|
|
symval.set_output_value(value);
|
|
Reloc::wdisp30(view, object, &symval, addend, address);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
|
|
_("unsupported reloc %u"),
|
|
r_type);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22:
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
if (optimized_type == tls::TLSOPT_TO_LE)
|
|
{
|
|
Insntype* wv = reinterpret_cast<Insntype*>(view);
|
|
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22:
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
elfcpp::Swap<32, true>::writeval(wv, sparc_nop);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
elfcpp::Swap<32, true>::writeval(wv, sparc_mov_g0_o0);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
else if (optimized_type == tls::TLSOPT_NONE)
|
|
{
|
|
// Relocate the field with the offset of the GOT entry for
|
|
// the module index.
|
|
unsigned int got_offset;
|
|
|
|
got_offset = target->got_mod_index_entry(NULL, NULL, NULL);
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_LDM_HI22:
|
|
Reloc::hi22(view, got_offset, addend);
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_LDM_LO10:
|
|
Reloc::lo10(view, got_offset, addend);
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_LDM_ADD:
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_LDM_CALL:
|
|
{
|
|
Symbol_value<size> symval;
|
|
elfcpp::Elf_Xword value;
|
|
Symbol* tsym;
|
|
|
|
tsym = target->tls_get_addr_sym_;
|
|
gold_assert(tsym);
|
|
value = (target->plt_section()->address() +
|
|
tsym->plt_offset());
|
|
symval.set_output_value(value);
|
|
Reloc::wdisp30(view, object, &symval, addend, address);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
|
|
_("unsupported reloc %u"),
|
|
r_type);
|
|
break;
|
|
|
|
// These relocs can appear in debugging sections, in which case
|
|
// we won't see the TLS_LDM relocs. The local_dynamic_type
|
|
// field tells us this.
|
|
case elfcpp::R_SPARC_TLS_LDO_HIX22:
|
|
if (optimized_type == tls::TLSOPT_TO_LE)
|
|
{
|
|
value -= tls_segment->memsz();
|
|
Reloc::hix22(view, value, addend);
|
|
}
|
|
else
|
|
Reloc::ldo_hix22(view, value, addend);
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_LDO_LOX10:
|
|
if (optimized_type == tls::TLSOPT_TO_LE)
|
|
{
|
|
value -= tls_segment->memsz();
|
|
Reloc::lox10(view, value, addend);
|
|
}
|
|
else
|
|
Reloc::ldo_lox10(view, value, addend);
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_LDO_ADD:
|
|
if (optimized_type == tls::TLSOPT_TO_LE)
|
|
{
|
|
Insntype* wv = reinterpret_cast<Insntype*>(view);
|
|
Insntype val;
|
|
|
|
// add %reg1, %reg2, %reg3 --> add %g7, %reg2, %reg3
|
|
val = elfcpp::Swap<32, true>::readval(wv);
|
|
val = (val & ~0x7c000) | 0x1c000;
|
|
elfcpp::Swap<32, true>::writeval(wv, val);
|
|
}
|
|
break;
|
|
|
|
// When optimizing IE --> LE, the only relocation that is handled
|
|
// differently is R_SPARC_TLS_IE_LD, it is rewritten from
|
|
// 'ld{,x} [rs1 + rs2], rd' into 'mov rs2, rd' or simply a NOP is
|
|
// rs2 and rd are the same.
|
|
case elfcpp::R_SPARC_TLS_IE_LD:
|
|
case elfcpp::R_SPARC_TLS_IE_LDX:
|
|
if (optimized_type == tls::TLSOPT_TO_LE)
|
|
{
|
|
Insntype* wv = reinterpret_cast<Insntype*>(view);
|
|
Insntype val = elfcpp::Swap<32, true>::readval(wv);
|
|
Insntype rs2 = val & 0x1f;
|
|
Insntype rd = (val >> 25) & 0x1f;
|
|
|
|
if (rs2 == rd)
|
|
val = sparc_nop;
|
|
else
|
|
val = sparc_mov | (val & 0x3e00001f);
|
|
|
|
elfcpp::Swap<32, true>::writeval(wv, val);
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_IE_HI22:
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
if (optimized_type == tls::TLSOPT_TO_LE)
|
|
{
|
|
value -= tls_segment->memsz();
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_IE_HI22:
|
|
// IE_HI22 --> LE_HIX22
|
|
Reloc::hix22(view, value, addend);
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
// IE_LO10 --> LE_LOX10
|
|
Reloc::lox10(view, value, addend);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
else if (optimized_type == tls::TLSOPT_NONE)
|
|
{
|
|
// Relocate the field with the offset of the GOT entry for
|
|
// the tp-relative offset of the symbol.
|
|
if (gsym != NULL)
|
|
{
|
|
gold_assert(gsym->has_got_offset(GOT_TYPE_TLS_OFFSET));
|
|
value = gsym->got_offset(GOT_TYPE_TLS_OFFSET);
|
|
}
|
|
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_TLS_OFFSET));
|
|
value = object->local_got_offset(r_sym,
|
|
GOT_TYPE_TLS_OFFSET);
|
|
}
|
|
switch (r_type)
|
|
{
|
|
case elfcpp::R_SPARC_TLS_IE_HI22:
|
|
Reloc::hi22(view, value, addend);
|
|
break;
|
|
case elfcpp::R_SPARC_TLS_IE_LO10:
|
|
Reloc::lo10(view, value, addend);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
|
|
_("unsupported reloc %u"),
|
|
r_type);
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_IE_ADD:
|
|
// This seems to be mainly so that we can find the addition
|
|
// instruction if there is one. There doesn't seem to be any
|
|
// actual relocation to apply.
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LE_HIX22:
|
|
// If we're creating a shared library, a dynamic relocation will
|
|
// have been created for this location, so do not apply it now.
|
|
if (!parameters->options().shared())
|
|
{
|
|
value -= tls_segment->memsz();
|
|
Reloc::hix22(view, value, addend);
|
|
}
|
|
break;
|
|
|
|
case elfcpp::R_SPARC_TLS_LE_LOX10:
|
|
// If we're creating a shared library, a dynamic relocation will
|
|
// have been created for this location, so do not apply it now.
|
|
if (!parameters->options().shared())
|
|
{
|
|
value -= tls_segment->memsz();
|
|
Reloc::lox10(view, value, addend);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Relax a call instruction.
|
|
|
|
template<int size, bool big_endian>
|
|
inline void
|
|
Target_sparc<size, big_endian>::Relocate::relax_call(
|
|
Target_sparc<size, big_endian>* target,
|
|
unsigned char* view,
|
|
const elfcpp::Rela<size, big_endian>& rela,
|
|
section_size_type view_size)
|
|
{
|
|
typedef typename elfcpp::Swap<32, true>::Valtype Insntype;
|
|
Insntype *wv = reinterpret_cast<Insntype*>(view);
|
|
Insntype call_insn, delay_insn, set_insn;
|
|
uint32_t op3, reg, off;
|
|
|
|
// This code tries to relax call instructions that meet
|
|
// certain criteria.
|
|
//
|
|
// The first criteria is that the call must be such that the return
|
|
// address which the call writes into %o7 is unused. Two sequences
|
|
// meet this criteria, and are used to implement tail calls.
|
|
//
|
|
// Leaf function tail call:
|
|
//
|
|
// or %o7, %g0, %ANY_REG
|
|
// call FUNC
|
|
// or %ANY_REG, %g0, %o7
|
|
//
|
|
// Non-leaf function tail call:
|
|
//
|
|
// call FUNC
|
|
// restore
|
|
//
|
|
// The second criteria is that the call destination is close. If
|
|
// the displacement can fit in a signed 22-bit immediate field of a
|
|
// pre-V9 branch, we can do it. If we are generating a 64-bit
|
|
// object or a 32-bit object with ELF machine type EF_SPARC32PLUS,
|
|
// and the displacement fits in a signed 19-bit immediate field,
|
|
// then we can use a V9 branch.
|
|
|
|
// Make sure the delay instruction can be safely accessed.
|
|
if (rela.get_r_offset() + 8 > view_size)
|
|
return;
|
|
|
|
call_insn = elfcpp::Swap<32, true>::readval(wv);
|
|
delay_insn = elfcpp::Swap<32, true>::readval(wv + 1);
|
|
|
|
// Make sure it is really a call instruction.
|
|
if (((call_insn >> 30) & 0x3) != 1)
|
|
return;
|
|
|
|
if (((delay_insn >> 30) & 0x3) != 2)
|
|
return;
|
|
|
|
// Accept only a restore or an integer arithmetic operation whose
|
|
// sole side effect is to write the %o7 register (and perhaps set
|
|
// the condition codes, which are considered clobbered across
|
|
// function calls).
|
|
//
|
|
// For example, we don't want to match a tagged addition or
|
|
// subtraction. We also don't want to match something like a
|
|
// divide.
|
|
//
|
|
// Specifically we accept add{,cc}, and{,cc}, or{,cc},
|
|
// xor{,cc}, sub{,cc}, andn{,cc}, orn{,cc}, and xnor{,cc}.
|
|
|
|
op3 = (delay_insn >> 19) & 0x3f;
|
|
reg = (delay_insn >> 25) & 0x1f;
|
|
if (op3 != 0x3d
|
|
&& ((op3 & 0x28) != 0 || reg != 15))
|
|
return;
|
|
|
|
// For non-restore instructions, make sure %o7 isn't
|
|
// an input.
|
|
if (op3 != 0x3d)
|
|
{
|
|
// First check RS1
|
|
reg = (delay_insn >> 14) & 0x15;
|
|
if (reg == 15)
|
|
return;
|
|
|
|
// And if non-immediate, check RS2
|
|
if (((delay_insn >> 13) & 1) == 0)
|
|
{
|
|
reg = (delay_insn & 0x1f);
|
|
if (reg == 15)
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Now check the branch distance. We are called after the
|
|
// call has been relocated, so we just have to peek at the
|
|
// offset contained in the instruction.
|
|
off = call_insn & 0x3fffffff;
|
|
if ((off & 0x3fe00000) != 0
|
|
&& (off & 0x3fe00000) != 0x3fe00000)
|
|
return;
|
|
|
|
if ((size == 64 || target->elf_machine_ == elfcpp::EM_SPARC32PLUS)
|
|
&& ((off & 0x3c0000) == 0
|
|
|| (off & 0x3c0000) == 0x3c0000))
|
|
{
|
|
// ba,pt %xcc, FUNC
|
|
call_insn = 0x10680000 | (off & 0x07ffff);
|
|
}
|
|
else
|
|
{
|
|
// ba FUNC
|
|
call_insn = 0x10800000 | (off & 0x3fffff);
|
|
}
|
|
elfcpp::Swap<32, true>::writeval(wv, call_insn);
|
|
|
|
// See if we can NOP out the delay slot instruction. We peek
|
|
// at the instruction before the call to make sure we're dealing
|
|
// with exactly the:
|
|
//
|
|
// or %o7, %g0, %ANY_REG
|
|
// call
|
|
// or %ANY_REG, %g0, %o7
|
|
//
|
|
// case. Otherwise this might be a tricky piece of hand written
|
|
// assembler calculating %o7 in some non-trivial way, and therefore
|
|
// we can't be sure that NOP'ing out the delay slot is safe.
|
|
if (op3 == 0x02
|
|
&& rela.get_r_offset() >= 4)
|
|
{
|
|
if ((delay_insn & ~(0x1f << 14)) != 0x9e100000)
|
|
return;
|
|
|
|
set_insn = elfcpp::Swap<32, true>::readval(wv - 1);
|
|
if ((set_insn & ~(0x1f << 25)) != 0x8013c000)
|
|
return;
|
|
|
|
reg = (set_insn >> 25) & 0x1f;
|
|
if (reg == 0 || reg == 15)
|
|
return;
|
|
if (reg != ((delay_insn >> 14) & 0x1f))
|
|
return;
|
|
|
|
// All tests pass, nop it out.
|
|
elfcpp::Swap<32, true>::writeval(wv + 1, sparc_nop);
|
|
}
|
|
}
|
|
|
|
// Relocate section data.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<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_sparc<size, big_endian> Sparc;
|
|
typedef typename Target_sparc<size, big_endian>::Relocate Sparc_relocate;
|
|
|
|
gold_assert(sh_type == elfcpp::SHT_RELA);
|
|
|
|
gold::relocate_section<size, big_endian, Sparc, elfcpp::SHT_RELA,
|
|
Sparc_relocate, gold::Default_comdat_behavior>(
|
|
relinfo,
|
|
this,
|
|
prelocs,
|
|
reloc_count,
|
|
output_section,
|
|
needs_special_offset_handling,
|
|
view,
|
|
address,
|
|
view_size,
|
|
reloc_symbol_changes);
|
|
}
|
|
|
|
// Return the size of a relocation while scanning during a relocatable
|
|
// link.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned int
|
|
Target_sparc<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_sparc<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);
|
|
}
|
|
|
|
// Emit relocations for a section.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<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_sparc<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
|
|
{
|
|
gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
|
|
return this->plt_section()->address() + gsym->plt_offset();
|
|
}
|
|
|
|
// do_make_elf_object to override the same function in the base class.
|
|
// We need to use a target-specific sub-class of
|
|
// Sized_relobj_file<size, big_endian> to process SPARC specific bits
|
|
// of the ELF headers. Hence we need to have our own ELF object creation.
|
|
|
|
template<int size, bool big_endian>
|
|
Object*
|
|
Target_sparc<size, big_endian>::do_make_elf_object(
|
|
const std::string& name,
|
|
Input_file* input_file,
|
|
off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr)
|
|
{
|
|
elfcpp::Elf_Half machine = ehdr.get_e_machine();
|
|
elfcpp::Elf_Word flags = ehdr.get_e_flags();
|
|
elfcpp::Elf_Word omm, mm;
|
|
|
|
switch (machine)
|
|
{
|
|
case elfcpp::EM_SPARC32PLUS:
|
|
this->elf_machine_ = elfcpp::EM_SPARC32PLUS;
|
|
break;
|
|
|
|
case elfcpp::EM_SPARC:
|
|
case elfcpp::EM_SPARCV9:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!this->elf_flags_set_)
|
|
{
|
|
this->elf_flags_ = flags;
|
|
this->elf_flags_set_ = true;
|
|
}
|
|
else
|
|
{
|
|
// Accumulate cpu feature bits.
|
|
this->elf_flags_ |= (flags & (elfcpp::EF_SPARC_32PLUS
|
|
| elfcpp::EF_SPARC_SUN_US1
|
|
| elfcpp::EF_SPARC_HAL_R1
|
|
| elfcpp::EF_SPARC_SUN_US3));
|
|
|
|
// Bump the memory model setting to the most restrictive
|
|
// one we encounter.
|
|
omm = (this->elf_flags_ & elfcpp::EF_SPARCV9_MM);
|
|
mm = (flags & elfcpp::EF_SPARCV9_MM);
|
|
if (omm != mm)
|
|
{
|
|
if (mm == elfcpp::EF_SPARCV9_TSO)
|
|
{
|
|
this->elf_flags_ &= ~elfcpp::EF_SPARCV9_MM;
|
|
this->elf_flags_ |= elfcpp::EF_SPARCV9_TSO;
|
|
}
|
|
else if (mm == elfcpp::EF_SPARCV9_PSO
|
|
&& omm == elfcpp::EF_SPARCV9_RMO)
|
|
{
|
|
this->elf_flags_ &= ~elfcpp::EF_SPARCV9_MM;
|
|
this->elf_flags_ |= elfcpp::EF_SPARCV9_PSO;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate that the little-endian flag matches how we've
|
|
// been instantiated.
|
|
if (!(flags & elfcpp::EF_SPARC_LEDATA) != big_endian)
|
|
{
|
|
if (big_endian)
|
|
gold_error(_("%s: little endian elf flag set on BE object"),
|
|
name.c_str());
|
|
else
|
|
gold_error(_("%s: little endian elf flag clear on LE object"),
|
|
name.c_str());
|
|
}
|
|
|
|
return Target::do_make_elf_object(name, input_file, offset, ehdr);
|
|
}
|
|
|
|
// Adjust ELF file header.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Target_sparc<size, big_endian>::do_adjust_elf_header(
|
|
unsigned char* view,
|
|
int len)
|
|
{
|
|
elfcpp::Ehdr_write<size, big_endian> oehdr(view);
|
|
|
|
oehdr.put_e_machine(this->elf_machine_);
|
|
oehdr.put_e_flags(this->elf_flags_);
|
|
|
|
Sized_target<size, big_endian>::do_adjust_elf_header(view, len);
|
|
}
|
|
|
|
// The selector for sparc object files.
|
|
|
|
template<int size, bool big_endian>
|
|
class Target_selector_sparc : public Target_selector
|
|
{
|
|
public:
|
|
Target_selector_sparc()
|
|
: Target_selector(elfcpp::EM_NONE, size, big_endian,
|
|
(size == 64 ? "elf64-sparc" : "elf32-sparc"),
|
|
(size == 64 ? "elf64_sparc" : "elf32_sparc"))
|
|
{ }
|
|
|
|
virtual Target*
|
|
do_recognize(Input_file*, off_t, int machine, int, int)
|
|
{
|
|
switch (size)
|
|
{
|
|
case 64:
|
|
if (machine != elfcpp::EM_SPARCV9)
|
|
return NULL;
|
|
break;
|
|
|
|
case 32:
|
|
if (machine != elfcpp::EM_SPARC
|
|
&& machine != elfcpp::EM_SPARC32PLUS)
|
|
return NULL;
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
return this->instantiate_target();
|
|
}
|
|
|
|
virtual Target*
|
|
do_instantiate_target()
|
|
{ return new Target_sparc<size, big_endian>(); }
|
|
};
|
|
|
|
Target_selector_sparc<32, true> target_selector_sparc32;
|
|
Target_selector_sparc<64, true> target_selector_sparc64;
|
|
|
|
} // End anonymous namespace.
|