// sparc.cc -- sparc target support for gold.
-// Copyright 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+// Copyright 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
// Written by David S. Miller <davem@davemloft.net>.
// This file is part of gold.
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), dynbss_(NULL),
- got_mod_index_offset_(-1U), tls_get_addr_sym_(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
+ // 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);
+ 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
const unsigned char* plocal_symbols,
Relocatable_relocs*);
- // Relocate a section during a relocatable link.
+ // Emit relocations for a section.
void
- relocate_for_relocatable(const Relocate_info<size, big_endian>*,
- unsigned int sh_type,
- const unsigned char* prelocs,
- size_t reloc_count,
- Output_section* output_section,
- off_t offset_in_output_section,
- const Relocatable_relocs*,
- unsigned char* view,
- typename elfcpp::Elf_types<size>::Elf_Addr view_address,
- section_size_type view_size,
- unsigned char* reloc_view,
- section_size_type reloc_view_size);
+ 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
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.
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);
+ const elfcpp::Sym<size, big_endian>& lsym,
+ bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_sparc* target,
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>& ,
+ Target_sparc* ,
+ Sized_relobj_file<size, big_endian>* ,
unsigned int ,
- const elfcpp::Sym<size, big_endian>&)
+ 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*)
+ Target_sparc* ,
+ Sized_relobj_file<size, big_endian>* ,
+ unsigned int ,
+ Output_section* ,
+ const elfcpp::Rela<size,
+ big_endian>& ,
+ unsigned int , Symbol*)
{ return false; }
// 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>&,
+ 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_;
// Copy a relocation against a global symbol.
void
copy_reloc(Symbol_table* symtab, Layout* layout,
- Sized_relobj_file<size, big_endian>* object,
+ Sized_relobj_file<size, big_endian>* object,
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rela<size, big_endian>& reloc)
{
Reloc_section* rela_ifunc_;
// Relocs saved to avoid a COPY reloc.
Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
- // Space for variables copied with a COPY reloc.
- Output_data_space* dynbss_;
// 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<>
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
+ NULL, // attributes_vendor
+ "_start" // entry_symbol_name
};
template<>
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
+ NULL, // attributes_vendor
+ "_start" // entry_symbol_name
};
// We have to take care here, even when operating in little-endian
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;
+ return this->address() + offset + gsym->plt_offset();
}
// Return the PLT address to use for a local symbol. These are always
template<int size, bool big_endian>
uint64_t
Output_data_plt_sparc<size, big_endian>::address_for_local(
- const Relobj*,
- unsigned int)
+ const Relobj* object,
+ unsigned int r_sym)
{
- return this->address() + plt_index_to_offset(this->count_ + 4);
+ return (this->address()
+ + plt_index_to_offset(this->count_ + 4)
+ + object->local_plt_offset(r_sym));
}
static const unsigned int sparc_nop = 0x01000000;
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);
+ object->name().c_str(), r_type);
return flags != 0;
}
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
- const elfcpp::Sym<size, big_endian>& lsym)
+ 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;
// 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,
+ {
+ 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:
// 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());
+ {
+ 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,
+ 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);
+ }
+ 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_GOT13:
case elfcpp::R_SPARC_GOT22:
{
- // The symbol requires a GOT entry.
- Output_data_got<size, big_endian>* got;
- unsigned int r_sym;
+ // 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());
{
bool output_is_shared = parameters->options().shared();
const tls::Tls_optimization optimized_type
- = optimize_tls_reloc(!output_is_shared, r_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_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());
+ // 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);
object->error(_("local symbol %u has bad shndx %u"),
r_sym, shndx);
else
- got->add_local_pair_with_rel(object, r_sym,
+ 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),
- 0);
+ : elfcpp::R_SPARC_TLS_DTPMOD32));
if (r_type == elfcpp::R_SPARC_TLS_GD_CALL)
generate_tls_call(symtab, layout, target);
}
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,
+ // 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);
}
// 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())
+ && !gsym->is_from_dynobj()
+ && !gsym->is_preemptible())
break;
target->make_plt_entry(symtab, layout, gsym);
break;
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)))
- {
+ // 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.
+ // for dwarf2 cfi directives.
switch (r_type)
{
case elfcpp::R_SPARC_16:
break;
}
- if (gsym->may_need_copy_reloc())
- {
- target->copy_reloc(symtab, layout, object,
- data_shndx, output_section, gsym, reloc);
- }
+ if (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
reloc.get_r_offset(),
reloc.get_r_addend());
}
- else if ((r_type == elfcpp::R_SPARC_32
+ 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,
+ && 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);
+ }
+ else
+ {
+ Reloc_section* rela_dyn = target->rela_dyn_section(layout);
check_non_pic(object, r_type);
if (gsym->is_from_dynobj()
object, data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend());
- }
- }
+ }
+ }
}
break;
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()
+ && !gsym->is_from_dynobj()
+ && !gsym->is_preemptible()
&& !is_ifunc)
{
// We will optimize this into a GOT relative relocation
case elfcpp::R_SPARC_GOT13:
case elfcpp::R_SPARC_GOT22:
{
- // The symbol requires a GOT entry.
- Output_data_got<size, big_endian>* got;
+ // 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())
+ if (gsym->final_value_is_known())
{
// For a STT_GNU_IFUNC symbol we want the PLT address.
if (gsym->type() == elfcpp::STT_GNU_IFUNC)
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.
+ 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:
//
// 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()
+ 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
got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, rela_dyn,
r_type);
}
- else if (!gsym->has_got_offset(GOT_TYPE_STANDARD))
- {
+ else if (!gsym->has_got_offset(GOT_TYPE_STANDARD))
+ {
unsigned int off = got->add_constant(0);
gsym->set_got_offset(GOT_TYPE_STANDARD, off);
rela_dyn->add_global_relative(gsym, elfcpp::R_SPARC_RELATIVE,
got, off, 0, is_ifunc);
}
- }
+ }
}
break;
{
const bool is_final = gsym->final_value_is_known();
const tls::Tls_optimization optimized_type
- = optimize_tls_reloc(is_final, r_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_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,
+ // 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
}
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,
+ // 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 :
return false;
}
}
+
+ if (view == NULL)
+ return true;
+
if (this->reloc_adjust_addr_ == view)
view -= 4;
{
elfcpp::Elf_Xword value;
- value = target->plt_address_for_global(gsym) + gsym->plt_offset();
+ value = target->plt_address_for_global(gsym);
symval.set_output_value(value);
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)
- + object->local_plt_offset(r_sym));
+ symval.set_output_value(target->plt_address_for_local(object, r_sym));
psymval = &symval;
}
}
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);
- }
+ {
+ 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);
- }
+ {
+ 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:
if (rela.get_r_offset() & 0x1)
{
// The assembler can sometimes emit unaligned relocations
- // for dwarf2 cfi directives.
+ // for dwarf2 cfi directives.
Reloc::ua16(view, object, psymval, addend);
}
else
if (rela.get_r_offset() & 0x3)
{
// The assembler can sometimes emit unaligned relocations
- // for dwarf2 cfi directives.
+ // for dwarf2 cfi directives.
Reloc::ua32(view, object, psymval, addend);
}
else
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:
if (rela.get_r_offset() & 0x7)
{
// The assembler can sometimes emit unaligned relocations
- // for dwarf2 cfi directives.
+ // for dwarf2 cfi directives.
Reloc::ua64(view, object, psymval, addend);
}
else
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)
+ {
+ 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;
elfcpp::Swap<32, true>::writeval(wv, 0x9001c008);
break;
}
- break;
+ break;
}
- else if (optimized_type == tls::TLSOPT_NONE)
- {
+ else if (optimized_type == tls::TLSOPT_NONE)
+ {
switch (r_type)
{
case elfcpp::R_SPARC_TLS_GD_HI22:
break;
}
break;
- }
- }
+ }
+ }
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unsupported reloc %u"),
r_type);
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;
+ {
+ // 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)
}
break;
}
- break;
- }
+ break;
+ }
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unsupported reloc %u"),
r_type);
}
}
+// 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>
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::relocate_section<size, big_endian, Sparc, elfcpp::SHT_RELA,
- Sparc_relocate>(
+ Sparc_relocate, gold::Default_comdat_behavior>(
relinfo,
this,
prelocs,
rr);
}
-// Relocate a section during a relocatable link.
+// Emit relocations for a section.
template<int size, bool big_endian>
void
-Target_sparc<size, big_endian>::relocate_for_relocatable(
+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,
- off_t offset_in_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,
{
gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::relocate_for_relocatable<size, big_endian, elfcpp::SHT_RELA>(
+ gold::relocate_relocs<size, big_endian, elfcpp::SHT_RELA>(
relinfo,
prelocs,
reloc_count,
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>
(size == 64 ? "elf64_sparc" : "elf32_sparc"))
{ }
- Target* do_recognize(int machine, int, int)
+ virtual Target*
+ do_recognize(Input_file*, off_t, int machine, int, int)
{
switch (size)
{
return this->instantiate_target();
}
- Target* do_instantiate_target()
+ virtual Target*
+ do_instantiate_target()
{ return new Target_sparc<size, big_endian>(); }
};