// powerpc.cc -- powerpc target support for gold.
-// Copyright (C) 2008-2014 Free Software Foundation, Inc.
+// Copyright (C) 2008-2017 Free Software Foundation, Inc.
// Written by David S. Miller <davem@davemloft.net>
// and David Edelsohn <edelsohn@gnu.org>
template<int size, bool big_endian>
class Stub_table;
+template<int size, bool big_endian>
+class Output_data_save_res;
+
+template<int size, bool big_endian>
+class Target_powerpc;
+
+struct Stub_table_owner
+{
+ Stub_table_owner()
+ : output_section(NULL), owner(NULL)
+ { }
+
+ Output_section* output_section;
+ const Output_section::Input_section* owner;
+};
+
inline bool
is_branch_reloc(unsigned int r_type);
+// Counter incremented on every Powerpc_relobj constructed.
+static uint32_t object_id = 0;
+
template<int size, bool big_endian>
class Powerpc_relobj : public Sized_relobj_file<size, big_endian>
{
Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
: Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
- special_(0), has_small_toc_reloc_(false), opd_valid_(false),
- opd_ent_(), access_from_map_(), has14_(), stub_table_(),
- e_flags_(ehdr.get_e_flags()), st_other_()
+ uniq_(object_id++), special_(0), relatoc_(0), toc_(0),
+ has_small_toc_reloc_(false), opd_valid_(false),
+ e_flags_(ehdr.get_e_flags()), no_toc_opt_(), opd_ent_(),
+ access_from_map_(), has14_(), stub_table_index_(), st_other_()
{
this->set_abiversion(0);
}
void
do_read_symbols(Read_symbols_data*);
+ // Arrange to always relocate .toc first.
+ virtual void
+ do_relocate_sections(
+ const Symbol_table* symtab, const Layout* layout,
+ const unsigned char* pshdrs, Output_file* of,
+ typename Sized_relobj_file<size, big_endian>::Views* pviews);
+
+ // The .toc section index.
+ unsigned int
+ toc_shndx() const
+ {
+ return this->toc_;
+ }
+
+ // Mark .toc entry at OFF as not optimizable.
+ void
+ set_no_toc_opt(Address off)
+ {
+ if (this->no_toc_opt_.empty())
+ this->no_toc_opt_.resize(this->section_size(this->toc_shndx())
+ / (size / 8));
+ off /= size / 8;
+ if (off < this->no_toc_opt_.size())
+ this->no_toc_opt_[off] = true;
+ }
+
+ // Mark the entire .toc as not optimizable.
+ void
+ set_no_toc_opt()
+ {
+ this->no_toc_opt_.resize(1);
+ this->no_toc_opt_[0] = true;
+ }
+
+ // Return true if code using the .toc entry at OFF should not be edited.
+ bool
+ no_toc_opt(Address off) const
+ {
+ if (this->no_toc_opt_.empty())
+ return false;
+ off /= size / 8;
+ if (off >= this->no_toc_opt_.size())
+ return true;
+ return this->no_toc_opt_[off];
+ }
+
// The .got2 section shndx.
unsigned int
got2_shndx() const
const unsigned char* prelocs,
const unsigned char* plocal_syms);
+ // Returns true if a code sequence loading a TOC entry can be
+ // converted into code calculating a TOC pointer relative offset.
+ bool
+ make_toc_relative(Target_powerpc<size, big_endian>* target,
+ Address* value);
+
// Perform the Sized_relobj_file method, then set up opd info from
// .opd relocs.
void
// Add a reference from SRC_OBJ, SRC_INDX to this object's .opd
// section at DST_OFF.
void
- add_reference(Object* src_obj,
+ add_reference(Relobj* src_obj,
unsigned int src_indx,
typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
{
if (this->opd_ent_[i].gc_mark)
{
unsigned int shndx = this->opd_ent_[i].shndx;
- symtab->gc()->worklist().push(Section_id(this, shndx));
+ symtab->gc()->worklist().push_back(Section_id(this, shndx));
}
}
{ return shndx < this->has14_.size() && this->has14_[shndx]; }
void
- set_stub_table(unsigned int shndx, Stub_table<size, big_endian>* stub_table)
+ set_stub_table(unsigned int shndx, unsigned int stub_index)
{
- if (shndx >= this->stub_table_.size())
- this->stub_table_.resize(shndx + 1);
- this->stub_table_[shndx] = stub_table;
+ if (shndx >= this->stub_table_index_.size())
+ this->stub_table_index_.resize(shndx + 1, -1);
+ this->stub_table_index_[shndx] = stub_index;
}
Stub_table<size, big_endian>*
stub_table(unsigned int shndx)
{
- if (shndx < this->stub_table_.size())
- return this->stub_table_[shndx];
+ if (shndx < this->stub_table_index_.size())
+ {
+ Target_powerpc<size, big_endian>* target
+ = static_cast<Target_powerpc<size, big_endian>*>(
+ parameters->sized_target<size, big_endian>());
+ unsigned int indx = this->stub_table_index_[shndx];
+ if (indx < target->stub_tables().size())
+ return target->stub_tables()[indx];
+ }
return NULL;
}
+ void
+ clear_stub_table()
+ {
+ this->stub_table_index_.clear();
+ }
+
+ uint32_t
+ uniq() const
+ { return this->uniq_; }
+
int
abiversion() const
{ return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
void
set_abiversion(int ver);
+ unsigned int
+ st_other (unsigned int symndx) const
+ {
+ return this->st_other_[symndx];
+ }
+
unsigned int
ppc64_local_entry_offset(const Symbol* sym) const
{ return elfcpp::ppc64_decode_local_entry(sym->nonvis() >> 3); }
opd_ent_ndx(size_t off) const
{ return off >> 4;}
+ // Per object unique identifier
+ uint32_t uniq_;
+
// For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx.
unsigned int special_;
+ // For 64-bit the .rela.toc and .toc section shdnx.
+ unsigned int relatoc_;
+ unsigned int toc_;
+
// For 64-bit, whether this object uses small model relocs to access
// the toc.
bool has_small_toc_reloc_;
// access_from_map_.
bool opd_valid_;
+ // Header e_flags
+ elfcpp::Elf_Word e_flags_;
+
+ // For 64-bit, an array with one entry per 64-bit word in the .toc
+ // section, set if accesses using that word cannot be optimised.
+ std::vector<bool> no_toc_opt_;
+
// The first 8-byte word of an OPD entry gives the address of the
// entry point of the function. Relocatable object files have a
// relocation on this word. The following vector records the
std::vector<bool> has14_;
// The stub table to use for a given input section.
- std::vector<Stub_table<size, big_endian>*> stub_table_;
-
- // Header e_flags
- elfcpp::Elf_Word e_flags_;
+ std::vector<unsigned int> stub_table_index_;
// ELF st_other field for local symbols.
std::vector<unsigned char> st_other_;
Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
: Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr),
- opd_shndx_(0), opd_ent_(), e_flags_(ehdr.get_e_flags())
+ opd_shndx_(0), e_flags_(ehdr.get_e_flags()), opd_ent_()
{
this->set_abiversion(0);
}
unsigned int opd_shndx_;
Address opd_address_;
+ // Header e_flags
+ elfcpp::Elf_Word e_flags_;
+
// The first 8-byte word of an OPD entry gives the address of the
// entry point of the function. Records the section and offset
// corresponding to the address. Note that in dynamic objects,
// offset is *not* relative to the section.
std::vector<Opd_ent> opd_ent_;
+};
- // Header e_flags
- elfcpp::Elf_Word e_flags_;
+// Powerpc_copy_relocs class. Needed to peek at dynamic relocs the
+// base class will emit.
+
+template<int sh_type, int size, bool big_endian>
+class Powerpc_copy_relocs : public Copy_relocs<sh_type, size, big_endian>
+{
+ public:
+ Powerpc_copy_relocs()
+ : Copy_relocs<sh_type, size, big_endian>(elfcpp::R_POWERPC_COPY)
+ { }
+
+ // Emit any saved relocations which turn out to be needed. This is
+ // called after all the relocs have been scanned.
+ void
+ emit(Output_data_reloc<sh_type, true, size, big_endian>*);
};
template<int size, bool big_endian>
Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian> Reloc_section;
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
typedef typename elfcpp::Elf_types<size>::Elf_Swxword Signed_address;
+ typedef Unordered_set<Symbol_location, Symbol_location_hash> Tocsave_loc;
static const Address invalid_address = static_cast<Address>(0) - 1;
// Offset of tp and dtp pointers from start of TLS block.
static const Address tp_offset = 0x7000;
Target_powerpc()
: Sized_target<size, big_endian>(&powerpc_info),
got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL),
- glink_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_POWERPC_COPY),
+ glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
tlsld_got_offset_(-1U),
- stub_tables_(), branch_lookup_table_(), branch_info_(),
- plt_thread_safe_(false)
+ stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(),
+ plt_thread_safe_(false), plt_localentry0_(false),
+ plt_localentry0_init_(false), has_localentry0_(false),
+ has_tls_get_addr_opt_(false),
+ relax_failed_(false), relax_fail_count_(0),
+ stub_group_size_(0), savres_section_(0),
+ tls_get_addr_(NULL), tls_get_addr_opt_(NULL)
{
}
ppc_object->set_has_14bit_branch(data_shndx);
}
- Stub_table<size, big_endian>*
- new_stub_table();
+ // Return whether the last branch is a plt call, and if so, mark the
+ // branch as having an R_PPC64_TOCSAVE.
+ bool
+ mark_pltcall(Powerpc_relobj<size, big_endian>* ppc_object,
+ unsigned int data_shndx, Address r_offset, Symbol_table* symtab)
+ {
+ return (size == 64
+ && !this->branch_info_.empty()
+ && this->branch_info_.back().mark_pltcall(ppc_object, data_shndx,
+ r_offset, this, symtab));
+ }
+
+ // Say the given location, that of a nop in a function prologue with
+ // an R_PPC64_TOCSAVE reloc, will be used to save r2.
+ // R_PPC64_TOCSAVE relocs on nops following calls point at this nop.
+ void
+ add_tocsave(Powerpc_relobj<size, big_endian>* ppc_object,
+ unsigned int shndx, Address offset)
+ {
+ Symbol_location loc;
+ loc.object = ppc_object;
+ loc.shndx = shndx;
+ loc.offset = offset;
+ this->tocsave_loc_.insert(loc);
+ }
+
+ // Accessor
+ const Tocsave_loc
+ tocsave_loc() const
+ {
+ return this->tocsave_loc_;
+ }
void
do_define_standard_symbols(Symbol_table*, Layout*);
do_can_check_for_function_pointers() const
{ return true; }
+ // Adjust -fsplit-stack code which calls non-split-stack code.
+ void
+ do_calls_non_split(Relobj* object, unsigned int shndx,
+ section_offset_type fnoffset, section_size_type fnsize,
+ const unsigned char* prelocs, size_t reloc_count,
+ unsigned char* view, section_size_type view_size,
+ std::string* from, std::string* to) const;
+
// Relocate a section.
void
relocate_section(const Relocate_info<size, big_endian>*,
const unsigned char* plocal_symbols,
Relocatable_relocs*);
+ // Scan the relocs for --emit-relocs.
+ void
+ emit_relocs_scan(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_syms,
+ Relocatable_relocs* rr);
+
// Emit relocations for a section.
void
relocate_relocs(const Relocate_info<size, big_endian>*,
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off
offset_in_output_section,
- const Relocatable_relocs*,
unsigned char*,
Address view_address,
section_size_type,
return 24;
}
+ Output_data_save_res<size, big_endian>*
+ savres_section() const
+ {
+ return this->savres_section_;
+ }
+
// Add any special sections for this symbol to the gc work list.
// For powerpc64, this adds the code section of a function
// descriptor.
// section of a function descriptor.
void
do_gc_add_reference(Symbol_table* symtab,
- Object* src_obj,
+ Relobj* src_obj,
unsigned int src_shndx,
- Object* dst_obj,
+ Relobj* dst_obj,
unsigned int dst_shndx,
Address dst_off) const;
}
}
+ // Wrapper used after relax to define a local symbol in output data,
+ // from the end if value < 0.
+ void
+ define_local(Symbol_table* symtab, const char* name,
+ Output_data* od, Address value, unsigned int symsize)
+ {
+ Symbol* sym
+ = symtab->define_in_output_data(name, NULL, Symbol_table::PREDEFINED,
+ od, value, symsize, elfcpp::STT_NOTYPE,
+ elfcpp::STB_LOCAL, elfcpp::STV_HIDDEN, 0,
+ static_cast<Signed_address>(value) < 0,
+ false);
+ // We are creating this symbol late, so need to fix up things
+ // done early in Layout::finalize.
+ sym->set_dynsym_index(-1U);
+ }
+
bool
plt_thread_safe() const
{ return this->plt_thread_safe_; }
+ bool
+ plt_localentry0() const
+ { return this->plt_localentry0_; }
+
+ void
+ set_has_localentry0()
+ {
+ this->has_localentry0_ = true;
+ }
+
+ bool
+ is_elfv2_localentry0(const Symbol* gsym) const
+ {
+ return (size == 64
+ && this->abiversion() >= 2
+ && this->plt_localentry0()
+ && gsym->type() == elfcpp::STT_FUNC
+ && gsym->is_defined()
+ && gsym->nonvis() >> 3 == 0
+ && !gsym->non_zero_localentry());
+ }
+
+ bool
+ is_elfv2_localentry0(const Sized_relobj_file<size, big_endian>* object,
+ unsigned int r_sym) const
+ {
+ const Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<const Powerpc_relobj<size, big_endian>*>(object);
+
+ if (size == 64
+ && this->abiversion() >= 2
+ && this->plt_localentry0()
+ && ppc_object->st_other(r_sym) >> 5 == 0)
+ {
+ const Symbol_value<size>* psymval = object->local_symbol(r_sym);
+ bool is_ordinary;
+ if (!psymval->is_ifunc_symbol()
+ && psymval->input_shndx(&is_ordinary) != elfcpp::SHN_UNDEF
+ && is_ordinary)
+ return true;
+ }
+ return false;
+ }
+
+ // Remember any symbols seen with non-zero localentry, even those
+ // not providing a definition
+ bool
+ resolve(Symbol* to, const elfcpp::Sym<size, big_endian>& sym, Object*,
+ const char*)
+ {
+ if (size == 64)
+ {
+ unsigned char st_other = sym.get_st_other();
+ if ((st_other & elfcpp::STO_PPC64_LOCAL_MASK) != 0)
+ to->set_non_zero_localentry();
+ }
+ // We haven't resolved anything, continue normal processing.
+ return false;
+ }
+
int
- abiversion () const
+ abiversion() const
{ return this->processor_specific_flags() & elfcpp::EF_PPC64_ABI; }
void
- set_abiversion (int ver)
+ set_abiversion(int ver)
{
elfcpp::Elf_Word flags = this->processor_specific_flags();
flags &= ~elfcpp::EF_PPC64_ABI;
this->set_processor_specific_flags(flags);
}
- // Offset to to save stack slot
+ Symbol*
+ tls_get_addr_opt() const
+ { return this->tls_get_addr_opt_; }
+
+ Symbol*
+ tls_get_addr() const
+ { return this->tls_get_addr_; }
+
+ // If optimizing __tls_get_addr calls, whether this is the
+ // "__tls_get_addr" symbol.
+ bool
+ is_tls_get_addr_opt(const Symbol* gsym) const
+ {
+ return this->tls_get_addr_opt_ && (gsym == this->tls_get_addr_
+ || gsym == this->tls_get_addr_opt_);
+ }
+
+ bool
+ replace_tls_get_addr(const Symbol* gsym) const
+ { return this->tls_get_addr_opt_ && gsym == this->tls_get_addr_; }
+
+ void
+ set_has_tls_get_addr_opt()
+ { this->has_tls_get_addr_opt_ = true; }
+
+ // Offset to toc save stack slot
int
- stk_toc () const
+ stk_toc() const
{ return this->abiversion() < 2 ? 40 : 24; }
+ // Offset to linker save stack slot. ELFv2 doesn't have a linker word,
+ // so use the CR save slot. Used only by __tls_get_addr call stub,
+ // relying on __tls_get_addr not saving CR itself.
+ int
+ stk_linker() const
+ { return this->abiversion() < 2 ? 32 : 8; }
+
private:
class Track_tls
};
Track_tls()
- : tls_get_addr_(NOT_EXPECTED),
+ : tls_get_addr_state_(NOT_EXPECTED),
relinfo_(NULL), relnum_(0), r_offset_(0)
{ }
~Track_tls()
{
- if (this->tls_get_addr_ != NOT_EXPECTED)
+ if (this->tls_get_addr_state_ != NOT_EXPECTED)
this->missing();
}
size_t relnum,
Address r_offset)
{
- this->tls_get_addr_ = EXPECTED;
+ this->tls_get_addr_state_ = EXPECTED;
this->relinfo_ = relinfo;
this->relnum_ = relnum;
this->r_offset_ = r_offset;
void
expect_tls_get_addr_call()
- { this->tls_get_addr_ = EXPECTED; }
+ { this->tls_get_addr_state_ = EXPECTED; }
void
skip_next_tls_get_addr_call()
- {this->tls_get_addr_ = SKIP; }
+ {this->tls_get_addr_state_ = SKIP; }
Tls_get_addr
- maybe_skip_tls_get_addr_call(unsigned int r_type, const Symbol* gsym)
+ maybe_skip_tls_get_addr_call(Target_powerpc<size, big_endian>* target,
+ unsigned int r_type, const Symbol* gsym)
{
bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC_PLTREL24)
&& gsym != NULL
- && strcmp(gsym->name(), "__tls_get_addr") == 0);
- Tls_get_addr last_tls = this->tls_get_addr_;
- this->tls_get_addr_ = NOT_EXPECTED;
+ && (gsym == target->tls_get_addr()
+ || gsym == target->tls_get_addr_opt()));
+ Tls_get_addr last_tls = this->tls_get_addr_state_;
+ this->tls_get_addr_state_ = NOT_EXPECTED;
if (is_tls_call && last_tls != EXPECTED)
return last_tls;
else if (!is_tls_call && last_tls != NOT_EXPECTED)
// allowing ld to safely optimize away the call. We check that
// every call to __tls_get_addr has a marker relocation, and that
// every marker relocation is on a call to __tls_get_addr.
- Tls_get_addr tls_get_addr_;
+ Tls_get_addr tls_get_addr_state_;
// Info about the last reloc for error message.
const Relocate_info<size, big_endian>* relinfo_;
size_t relnum_;
bool issued_non_pic_error_;
};
- Address
- symval_for_branch(const Symbol_table* symtab, Address value,
+ bool
+ symval_for_branch(const Symbol_table* symtab,
const Sized_symbol<size>* gsym,
Powerpc_relobj<size, big_endian>* object,
- unsigned int *dest_shndx);
+ Address *value, unsigned int *dest_shndx);
// The class which implements relocation.
class Relocate : protected Track_tls
// 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_powerpc*,
- 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,
+ relocate(const Relocate_info<size, big_endian>*, unsigned int,
+ Target_powerpc*, Output_section*, size_t, const unsigned char*,
+ const Sized_symbol<size>*, const Symbol_value<size>*,
+ unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr,
section_size_type);
};
}
};
- // A class which returns the size required for a relocation type,
- // used while scanning relocs during a relocatable link.
- class Relocatable_size_for_reloc
- {
- public:
- unsigned int
- get_size_for_reloc(unsigned int, Relobj*)
- {
- gold_unreachable();
- return 0;
- }
- };
-
// 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.
{
// If we are generating a shared library, then we can't do anything
// in the linker.
- if (parameters->options().shared())
+ if (parameters->options().shared()
+ || !parameters->options().tls_optimize())
return tls::TLSOPT_NONE;
if (!is_final)
tls::Tls_optimization
optimize_tls_ld()
{
- if (parameters->options().shared())
+ if (parameters->options().shared()
+ || !parameters->options().tls_optimize())
return tls::TLSOPT_NONE;
return tls::TLSOPT_TO_LE;
tls::Tls_optimization
optimize_tls_ie(bool is_final)
{
- if (!is_final || parameters->options().shared())
+ if (!is_final
+ || parameters->options().shared()
+ || !parameters->options().tls_optimize())
return tls::TLSOPT_NONE;
return tls::TLSOPT_TO_LE;
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rela<size, big_endian>& reloc)
{
+ unsigned int r_type = elfcpp::elf_r_type<size>(reloc.get_r_info());
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<size>(sym),
object, shndx, output_section,
- reloc, this->rela_dyn_section(layout));
+ r_type, reloc.get_r_offset(),
+ reloc.get_r_addend(),
+ this->rela_dyn_section(layout));
}
// Look over all the input sections, deciding where to place stubs.
void
- group_sections(Layout*, const Task*);
+ group_sections(Layout*, const Task*, bool);
// Sort output sections by address.
struct Sort_sections
unsigned int r_sym,
Address addend)
: object_(ppc_object), shndx_(data_shndx), offset_(r_offset),
- r_type_(r_type), r_sym_(r_sym), addend_(addend)
+ r_type_(r_type), tocsave_ (0), r_sym_(r_sym), addend_(addend)
{ }
~Branch_info()
{ }
+ // Return whether this branch is going via a plt call stub, and if
+ // so, mark it as having an R_PPC64_TOCSAVE.
+ bool
+ mark_pltcall(Powerpc_relobj<size, big_endian>* ppc_object,
+ unsigned int shndx, Address offset,
+ Target_powerpc* target, Symbol_table* symtab);
+
// If this branch needs a plt call stub, or a long branch stub, make one.
- void
+ bool
make_stub(Stub_table<size, big_endian>*,
Stub_table<size, big_endian>*,
Symbol_table*) const;
unsigned int shndx_;
Address offset_;
// ..and the branch type and destination.
- unsigned int r_type_;
+ unsigned int r_type_ : 31;
+ unsigned int tocsave_ : 1;
unsigned int r_sym_;
Address addend_;
};
// The dynamic reloc section.
Reloc_section* rela_dyn_;
// Relocs saved to avoid a COPY reloc.
- Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
+ Powerpc_copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
// Offset of the GOT entry for local dynamic __tls_get_addr calls.
unsigned int tlsld_got_offset_;
typedef std::vector<Branch_info> Branches;
Branches branch_info_;
+ Tocsave_loc tocsave_loc_;
bool plt_thread_safe_;
+ bool plt_localentry0_;
+ bool plt_localentry0_init_;
+ bool has_localentry0_;
+ bool has_tls_get_addr_opt_;
+
+ bool relax_failed_;
+ int relax_fail_count_;
+ int32_t stub_group_size_;
+
+ Output_data_save_res<size, big_endian> *savres_section_;
+
+ // The "__tls_get_addr" symbol, if present
+ Symbol* tls_get_addr_;
+ // If optimizing __tls_get_addr calls, the "__tls_get_addr_opt" symbol.
+ Symbol* tls_get_addr_opt_;
};
template<>
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
true, // is_big_endian
elfcpp::EM_PPC64, // machine_code
false, // has_make_symbol
- false, // has_resolve
+ true, // has_resolve
false, // has_code_fill
- true, // is_default_stack_executable
+ false, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/usr/lib/ld.so.1", // dynamic_linker
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
false, // is_big_endian
elfcpp::EM_PPC64, // machine_code
false, // has_make_symbol
- false, // has_resolve
+ true, // has_resolve
false, // has_code_fill
- true, // is_default_stack_executable
+ false, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/usr/lib/ld.so.1", // dynamic_linker
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
inline bool
private:
typedef Powerpc_relocate_functions<size, big_endian> This;
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ typedef typename elfcpp::Elf_types<size>::Elf_Swxword SignedAddress;
template<int valsize>
static inline bool
}
// Do a simple RELA relocation
- template<int valsize>
+ template<int fieldsize, int valsize>
static inline Status
rela(unsigned char* view, Address value, Overflow_check overflow)
{
- typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
+ typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
- elfcpp::Swap<valsize, big_endian>::writeval(wv, value);
+ elfcpp::Swap<fieldsize, big_endian>::writeval(wv, value);
return overflowed<valsize>(value, overflow);
}
- template<int valsize>
+ template<int fieldsize, int valsize>
static inline Status
rela(unsigned char* view,
unsigned int right_shift,
- typename elfcpp::Valtype_base<valsize>::Valtype dst_mask,
+ typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask,
Address value,
Overflow_check overflow)
{
- typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
+ typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
- Valtype val = elfcpp::Swap<valsize, big_endian>::readval(wv);
+ Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(wv);
Valtype reloc = value >> right_shift;
val &= ~dst_mask;
reloc &= dst_mask;
- elfcpp::Swap<valsize, big_endian>::writeval(wv, val | reloc);
+ elfcpp::Swap<fieldsize, big_endian>::writeval(wv, val | reloc);
return overflowed<valsize>(value >> right_shift, overflow);
}
// Do a simple RELA relocation, unaligned.
- template<int valsize>
+ template<int fieldsize, int valsize>
static inline Status
rela_ua(unsigned char* view, Address value, Overflow_check overflow)
{
- elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view, value);
+ elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, value);
return overflowed<valsize>(value, overflow);
}
- template<int valsize>
+ template<int fieldsize, int valsize>
static inline Status
rela_ua(unsigned char* view,
unsigned int right_shift,
- typename elfcpp::Valtype_base<valsize>::Valtype dst_mask,
+ typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask,
Address value,
Overflow_check overflow)
{
- typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
+ typedef typename elfcpp::Swap_unaligned<fieldsize, big_endian>::Valtype
Valtype;
- Valtype val = elfcpp::Swap<valsize, big_endian>::readval(view);
+ Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(view);
Valtype reloc = value >> right_shift;
val &= ~dst_mask;
reloc &= dst_mask;
- elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view, val | reloc);
+ elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, val | reloc);
return overflowed<valsize>(value >> right_shift, overflow);
}
// R_PPC64_ADDR64: (Symbol + Addend)
static inline void
addr64(unsigned char* view, Address value)
- { This::template rela<64>(view, value, CHECK_NONE); }
+ { This::template rela<64,64>(view, value, CHECK_NONE); }
// R_PPC64_UADDR64: (Symbol + Addend) unaligned
static inline void
addr64_u(unsigned char* view, Address value)
- { This::template rela_ua<64>(view, value, CHECK_NONE); }
+ { This::template rela_ua<64,64>(view, value, CHECK_NONE); }
// R_POWERPC_ADDR32: (Symbol + Addend)
static inline Status
addr32(unsigned char* view, Address value, Overflow_check overflow)
- { return This::template rela<32>(view, value, overflow); }
+ { return This::template rela<32,32>(view, value, overflow); }
// R_POWERPC_UADDR32: (Symbol + Addend) unaligned
static inline Status
addr32_u(unsigned char* view, Address value, Overflow_check overflow)
- { return This::template rela_ua<32>(view, value, overflow); }
+ { return This::template rela_ua<32,32>(view, value, overflow); }
// R_POWERPC_ADDR24: (Symbol + Addend) & 0x3fffffc
static inline Status
addr24(unsigned char* view, Address value, Overflow_check overflow)
{
- Status stat = This::template rela<32>(view, 0, 0x03fffffc, value, overflow);
+ Status stat = This::template rela<32,26>(view, 0, 0x03fffffc,
+ value, overflow);
if (overflow != CHECK_NONE && (value & 3) != 0)
stat = STATUS_OVERFLOW;
return stat;
// R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff
static inline Status
addr16(unsigned char* view, Address value, Overflow_check overflow)
- { return This::template rela<16>(view, value, overflow); }
+ { return This::template rela<16,16>(view, value, overflow); }
// R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff, unaligned
static inline Status
addr16_u(unsigned char* view, Address value, Overflow_check overflow)
- { return This::template rela_ua<16>(view, value, overflow); }
+ { return This::template rela_ua<16,16>(view, value, overflow); }
// R_POWERPC_ADDR16_DS: (Symbol + Addend) & 0xfffc
static inline Status
addr16_ds(unsigned char* view, Address value, Overflow_check overflow)
{
- Status stat = This::template rela<16>(view, 0, 0xfffc, value, overflow);
- if (overflow != CHECK_NONE && (value & 3) != 0)
+ Status stat = This::template rela<16,16>(view, 0, 0xfffc, value, overflow);
+ if ((value & 3) != 0)
+ stat = STATUS_OVERFLOW;
+ return stat;
+ }
+
+ // R_POWERPC_ADDR16_DQ: (Symbol + Addend) & 0xfff0
+ static inline Status
+ addr16_dq(unsigned char* view, Address value, Overflow_check overflow)
+ {
+ Status stat = This::template rela<16,16>(view, 0, 0xfff0, value, overflow);
+ if ((value & 15) != 0)
stat = STATUS_OVERFLOW;
return stat;
}
// R_POWERPC_ADDR16_HI: ((Symbol + Addend) >> 16) & 0xffff
static inline void
addr16_hi(unsigned char* view, Address value)
- { This::template rela<16>(view, 16, 0xffff, value, CHECK_NONE); }
+ { This::template rela<16,16>(view, 16, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HA: ((Symbol + Addend + 0x8000) >> 16) & 0xffff
static inline void
// R_POWERPC_ADDR16_HIGHER: ((Symbol + Addend) >> 32) & 0xffff
static inline void
addr16_hi2(unsigned char* view, Address value)
- { This::template rela<16>(view, 32, 0xffff, value, CHECK_NONE); }
+ { This::template rela<16,16>(view, 32, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHERA: ((Symbol + Addend + 0x8000) >> 32) & 0xffff
static inline void
// R_POWERPC_ADDR16_HIGHEST: ((Symbol + Addend) >> 48) & 0xffff
static inline void
addr16_hi3(unsigned char* view, Address value)
- { This::template rela<16>(view, 48, 0xffff, value, CHECK_NONE); }
+ { This::template rela<16,16>(view, 48, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHESTA: ((Symbol + Addend + 0x8000) >> 48) & 0xffff
static inline void
static inline Status
addr14(unsigned char* view, Address value, Overflow_check overflow)
{
- Status stat = This::template rela<32>(view, 0, 0xfffc, value, overflow);
+ Status stat = This::template rela<32,16>(view, 0, 0xfffc, value, overflow);
if (overflow != CHECK_NONE && (value & 3) != 0)
stat = STATUS_OVERFLOW;
return stat;
}
+
+ // R_POWERPC_REL16DX_HA
+ static inline Status
+ addr16dx_ha(unsigned char *view, Address value, Overflow_check overflow)
+ {
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+ Valtype* wv = reinterpret_cast<Valtype*>(view);
+ Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+ value += 0x8000;
+ value = static_cast<SignedAddress>(value) >> 16;
+ val |= (value & 0xffc1) | ((value & 0x3e) << 15);
+ elfcpp::Swap<32, big_endian>::writeval(wv, val);
+ return overflowed<16>(value, overflow);
+ }
};
// Set ABI version for input and output.
}
}
-// Stash away the index of .got2 or .opd in a relocatable object, if
-// such a section exists.
+// Stash away the index of .got2, .opd, .rela.toc, and .toc in a
+// relocatable object, if such sections exists.
template<int size, bool big_endian>
bool
this->name().c_str(), this->abiversion());
}
}
+ if (size == 64)
+ {
+ s = this->template find_shdr<size, big_endian>(pshdrs, ".rela.toc",
+ names, names_size, NULL);
+ if (s != NULL)
+ {
+ unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size;
+ this->relatoc_ = ndx;
+ typename elfcpp::Shdr<size, big_endian> shdr(s);
+ this->toc_ = this->adjust_shndx(shdr.get_sh_info());
+ }
+ }
return Sized_relobj_file<size, big_endian>::do_find_special_sections(sd);
}
{
if (size == 64)
{
- typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
- Reltype;
- const int reloc_size
- = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ typedef typename elfcpp::Rela<size, big_endian> Reltype;
+ const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
Address expected_off = 0;
bool regular = true;
}
}
+// Returns true if a code sequence loading the TOC entry at VALUE
+// relative to the TOC pointer can be converted into code calculating
+// a TOC pointer relative offset.
+// If so, the TOC pointer relative offset is stored to VALUE.
+
+template<int size, bool big_endian>
+bool
+Powerpc_relobj<size, big_endian>::make_toc_relative(
+ Target_powerpc<size, big_endian>* target,
+ Address* value)
+{
+ if (size != 64)
+ return false;
+
+ // With -mcmodel=medium code it is quite possible to have
+ // toc-relative relocs referring to objects outside the TOC.
+ // Don't try to look at a non-existent TOC.
+ if (this->toc_shndx() == 0)
+ return false;
+
+ // Convert VALUE back to an address by adding got_base (see below),
+ // then to an offset in the TOC by subtracting the TOC output
+ // section address and the TOC output offset. Since this TOC output
+ // section and the got output section are one and the same, we can
+ // omit adding and subtracting the output section address.
+ Address off = (*value + this->toc_base_offset()
+ - this->output_section_offset(this->toc_shndx()));
+ // Is this offset in the TOC? -mcmodel=medium code may be using
+ // TOC relative access to variables outside the TOC. Those of
+ // course can't be optimized. We also don't try to optimize code
+ // that is using a different object's TOC.
+ if (off >= this->section_size(this->toc_shndx()))
+ return false;
+
+ if (this->no_toc_opt(off))
+ return false;
+
+ section_size_type vlen;
+ unsigned char* view = this->get_output_view(this->toc_shndx(), &vlen);
+ Address addr = elfcpp::Swap<size, big_endian>::readval(view + off);
+ // The TOC pointer
+ Address got_base = (target->got_section()->output_section()->address()
+ + this->toc_base_offset());
+ addr -= got_base;
+ if (addr + (uint64_t) 0x80008000 >= (uint64_t) 1 << 32)
+ return false;
+
+ *value = addr;
+ return true;
+}
+
+// Perform the Sized_relobj_file method, then set up opd info from
+// .opd relocs.
+
template<int size, bool big_endian>
void
Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
void
Powerpc_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
{
- Sized_relobj_file<size, big_endian>::do_read_symbols(sd);
+ this->base_read_symbols(sd);
if (size == 64)
{
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
}
}
-// Call Sized_dynobj::do_read_symbols to read the symbols then
+// Call Sized_dynobj::base_read_symbols to read the symbols then
// read .opd from a dynamic object, filling in opd_ent_ vector,
template<int size, bool big_endian>
void
Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
{
- Sized_dynobj<size, big_endian>::do_read_symbols(sd);
+ this->base_read_symbols(sd);
if (size == 64)
{
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
}
}
+// Relocate sections.
+
+template<int size, bool big_endian>
+void
+Powerpc_relobj<size, big_endian>::do_relocate_sections(
+ const Symbol_table* symtab, const Layout* layout,
+ const unsigned char* pshdrs, Output_file* of,
+ typename Sized_relobj_file<size, big_endian>::Views* pviews)
+{
+ unsigned int start = 1;
+ if (size == 64
+ && this->relatoc_ != 0
+ && !parameters->options().relocatable())
+ {
+ // Relocate .toc first.
+ this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+ this->relatoc_, this->relatoc_);
+ this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+ 1, this->relatoc_ - 1);
+ start = this->relatoc_ + 1;
+ }
+ this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+ start, this->shnum() - 1);
+}
+
// Set up some symbols.
template<int size, bool big_endian>
false, false);
}
}
+
+ this->tls_get_addr_ = symtab->lookup("__tls_get_addr");
+ if (parameters->options().tls_get_addr_optimize()
+ && this->tls_get_addr_ != NULL
+ && this->tls_get_addr_->in_reg())
+ this->tls_get_addr_opt_ = symtab->lookup("__tls_get_addr_opt");
+ if (this->tls_get_addr_opt_ != NULL)
+ {
+ if (this->tls_get_addr_->is_undefined()
+ || this->tls_get_addr_->is_from_dynobj())
+ {
+ // Make it seem as if references to __tls_get_addr are
+ // really to __tls_get_addr_opt, so the latter symbol is
+ // made dynamic, not the former.
+ this->tls_get_addr_->clear_in_reg();
+ this->tls_get_addr_opt_->set_in_reg();
+ }
+ // We have a non-dynamic definition for __tls_get_addr.
+ // Make __tls_get_addr_opt the same, if it does not already have
+ // a non-dynamic definition.
+ else if (this->tls_get_addr_opt_->is_undefined()
+ || this->tls_get_addr_opt_->is_from_dynobj())
+ {
+ Sized_symbol<size>* from
+ = static_cast<Sized_symbol<size>*>(this->tls_get_addr_);
+ Sized_symbol<size>* to
+ = static_cast<Sized_symbol<size>*>(this->tls_get_addr_opt_);
+ symtab->clone<size>(to, from);
+ }
+ }
}
// Set up PowerPC target specific relobj.
symtab_(symtab), layout_(layout),
header_ent_cnt_(size == 32 ? 3 : 1),
header_index_(size == 32 ? 0x2000 : 0)
- { }
+ {
+ if (size == 64)
+ this->set_addralign(256);
+ }
// Override all the Output_data_got methods we use so as to first call
// reserve_ent().
Output_data_reloc_generic* rel_dyn,
unsigned int r_type_1, unsigned int r_type_2)
{
+ if (gsym->has_got_offset(got_type))
+ return;
+
this->reserve_ent(2);
Output_data_got<size, big_endian>::
add_global_pair_with_rel(gsym, got_type, rel_dyn, r_type_1, r_type_2);
Output_data_reloc_generic* rel_dyn,
unsigned int r_type)
{
+ if (object->local_has_got_offset(sym_index, got_type))
+ return;
+
this->reserve_ent(2);
Output_data_got<size, big_endian>::
add_local_tls_pair(object, sym_index, got_type, rel_dyn, r_type);
public:
// Determine the stub group size. The group size is the absolute
// value of the parameter --stub-group-size. If --stub-group-size
- // is passed a negative value, we restrict stubs to be always before
+ // is passed a negative value, we restrict stubs to be always after
// the stubbed branches.
- Stub_control(int32_t size)
- : state_(NO_GROUP), stub_group_size_(abs(size)),
- stub14_group_size_(abs(size)),
- stubs_always_before_branch_(size < 0), suppress_size_errors_(false),
- group_end_addr_(0), owner_(NULL), output_section_(NULL)
+ Stub_control(int32_t size, bool no_size_errors, bool multi_os)
+ : stub_group_size_(abs(size)), stubs_always_after_branch_(size < 0),
+ suppress_size_errors_(no_size_errors), multi_os_(multi_os),
+ state_(NO_GROUP), group_size_(0), group_start_addr_(0),
+ owner_(NULL), output_section_(NULL)
{
- if (stub_group_size_ == 1)
- {
- // Default values.
- if (stubs_always_before_branch_)
- {
- stub_group_size_ = 0x1e00000;
- stub14_group_size_ = 0x7800;
- }
- else
- {
- stub_group_size_ = 0x1c00000;
- stub14_group_size_ = 0x7000;
- }
- suppress_size_errors_ = true;
- }
}
// Return true iff input section can be handled by current stub
output_section()
{ return output_section_; }
+ void
+ set_output_and_owner(Output_section* o,
+ const Output_section::Input_section* i)
+ {
+ this->output_section_ = o;
+ this->owner_ = i;
+ }
+
private:
typedef enum
{
+ // Initial state.
NO_GROUP,
+ // Adding group sections before the stubs.
FINDING_STUB_SECTION,
+ // Adding group sections after the stubs.
HAS_STUB_SECTION
} State;
- State state_;
uint32_t stub_group_size_;
- uint32_t stub14_group_size_;
- bool stubs_always_before_branch_;
+ bool stubs_always_after_branch_;
bool suppress_size_errors_;
- uint64_t group_end_addr_;
+ // True if a stub group can serve multiple output sections.
+ bool multi_os_;
+ State state_;
+ // Current max size of group. Starts at stub_group_size_ but is
+ // reduced to stub_group_size_/1024 on seeing a section with
+ // external conditional branches.
+ uint32_t group_size_;
+ uint64_t group_start_addr_;
+ // owner_ and output_section_ specify the section to which stubs are
+ // attached. The stubs are placed at the end of this section.
const Output_section::Input_section* owner_;
Output_section* output_section_;
};
// Return true iff input section can be handled by current stub
-// group.
+// group. Sections are presented to this function in order,
+// so the first section is the head of the group.
bool
Stub_control::can_add_to_stub_group(Output_section* o,
const Output_section::Input_section* i,
bool has14)
{
- uint32_t group_size
- = has14 ? this->stub14_group_size_ : this->stub_group_size_;
bool whole_sec = o->order() == ORDER_INIT || o->order() == ORDER_FINI;
uint64_t this_size;
uint64_t start_addr = o->address();
start_addr += i->relobj()->output_section_offset(i->shndx());
this_size = i->data_size();
}
+
uint64_t end_addr = start_addr + this_size;
- bool toobig = this_size > group_size;
+ uint32_t group_size = this->stub_group_size_;
+ if (has14)
+ this->group_size_ = group_size = group_size >> 10;
- if (toobig && !this->suppress_size_errors_)
+ if (this_size > group_size && !this->suppress_size_errors_)
gold_warning(_("%s:%s exceeds group size"),
i->relobj()->name().c_str(),
i->relobj()->section_name(i->shndx()).c_str());
- if (this->state_ != HAS_STUB_SECTION
- && (!whole_sec || this->output_section_ != o)
- && (this->state_ == NO_GROUP
- || this->group_end_addr_ - end_addr < group_size))
- {
- this->owner_ = i;
- this->output_section_ = o;
- }
+ gold_debug(DEBUG_TARGET, "maybe add%s %s:%s size=%#llx total=%#llx",
+ has14 ? " 14bit" : "",
+ i->relobj()->name().c_str(),
+ i->relobj()->section_name(i->shndx()).c_str(),
+ (long long) this_size,
+ (this->state_ == NO_GROUP
+ ? this_size
+ : (long long) end_addr - this->group_start_addr_));
if (this->state_ == NO_GROUP)
{
+ // Only here on very first use of Stub_control
+ this->owner_ = i;
+ this->output_section_ = o;
this->state_ = FINDING_STUB_SECTION;
- this->group_end_addr_ = end_addr;
+ this->group_size_ = group_size;
+ this->group_start_addr_ = start_addr;
+ return true;
}
- else if (this->group_end_addr_ - start_addr < group_size)
+ else if (!this->multi_os_ && this->output_section_ != o)
;
- // Adding this section would make the group larger than GROUP_SIZE.
- else if (this->state_ == FINDING_STUB_SECTION
- && !this->stubs_always_before_branch_
- && !toobig)
+ else if (this->state_ == HAS_STUB_SECTION)
{
- // But wait, there's more! Input sections up to GROUP_SIZE
- // bytes before the stub table can be handled by it too.
- this->state_ = HAS_STUB_SECTION;
- this->group_end_addr_ = end_addr;
+ // Can we add this section, which is after the stubs, to the
+ // group?
+ if (end_addr - this->group_start_addr_ <= this->group_size_)
+ return true;
}
- else
+ else if (this->state_ == FINDING_STUB_SECTION)
{
- this->state_ = NO_GROUP;
- return false;
+ if ((whole_sec && this->output_section_ == o)
+ || end_addr - this->group_start_addr_ <= this->group_size_)
+ {
+ // Stubs are added at the end of "owner_".
+ this->owner_ = i;
+ this->output_section_ = o;
+ return true;
+ }
+ // The group before the stubs has reached maximum size.
+ // Now see about adding sections after the stubs to the
+ // group. If the current section has a 14-bit branch and
+ // the group before the stubs exceeds group_size_ (because
+ // they didn't have 14-bit branches), don't add sections
+ // after the stubs: The size of stubs for such a large
+ // group may exceed the reach of a 14-bit branch.
+ if (!this->stubs_always_after_branch_
+ && this_size <= this->group_size_
+ && start_addr - this->group_start_addr_ <= this->group_size_)
+ {
+ gold_debug(DEBUG_TARGET, "adding after stubs");
+ this->state_ = HAS_STUB_SECTION;
+ this->group_start_addr_ = start_addr;
+ return true;
+ }
}
- return true;
+ else
+ gold_unreachable();
+
+ gold_debug(DEBUG_TARGET,
+ !this->multi_os_ && this->output_section_ != o
+ ? "nope, new output section\n"
+ : "nope, didn't fit\n");
+
+ // The section fails to fit in the current group. Set up a few
+ // things for the next group. owner_ and output_section_ will be
+ // set later after we've retrieved those values for the current
+ // group.
+ this->state_ = FINDING_STUB_SECTION;
+ this->group_size_ = group_size;
+ this->group_start_addr_ = start_addr;
+ return false;
}
// Look over all the input sections, deciding where to place stubs.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::group_sections(Layout* layout,
- const Task*)
+ const Task*,
+ bool no_size_errors)
{
- Stub_control stub_control(parameters->options().stub_group_size());
+ Stub_control stub_control(this->stub_group_size_, no_size_errors,
+ parameters->options().stub_group_multi());
// Group input sections and insert stub table
- Stub_table<size, big_endian>* stub_table = NULL;
+ Stub_table_owner* table_owner = NULL;
+ std::vector<Stub_table_owner*> tables;
Layout::Section_list section_list;
layout->get_executable_sections(§ion_list);
std::stable_sort(section_list.begin(), section_list.end(), Sort_sections());
- for (Layout::Section_list::reverse_iterator o = section_list.rbegin();
- o != section_list.rend();
+ for (Layout::Section_list::iterator o = section_list.begin();
+ o != section_list.end();
++o)
{
typedef Output_section::Input_section_list Input_section_list;
- for (Input_section_list::const_reverse_iterator i
- = (*o)->input_sections().rbegin();
- i != (*o)->input_sections().rend();
+ for (Input_section_list::const_iterator i
+ = (*o)->input_sections().begin();
+ i != (*o)->input_sections().end();
++i)
{
- if (i->is_input_section())
+ if (i->is_input_section()
+ || i->is_relaxed_input_section())
{
Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<Powerpc_relobj<size, big_endian>*>(i->relobj());
bool has14 = ppcobj->has_14bit_branch(i->shndx());
if (!stub_control.can_add_to_stub_group(*o, &*i, has14))
{
- stub_table->init(stub_control.owner(),
- stub_control.output_section());
- stub_table = NULL;
+ table_owner->output_section = stub_control.output_section();
+ table_owner->owner = stub_control.owner();
+ stub_control.set_output_and_owner(*o, &*i);
+ table_owner = NULL;
+ }
+ if (table_owner == NULL)
+ {
+ table_owner = new Stub_table_owner;
+ tables.push_back(table_owner);
}
- if (stub_table == NULL)
- stub_table = this->new_stub_table();
- ppcobj->set_stub_table(i->shndx(), stub_table);
+ ppcobj->set_stub_table(i->shndx(), tables.size() - 1);
}
}
}
- if (stub_table != NULL)
+ if (table_owner != NULL)
{
- const Output_section::Input_section* i = stub_control.owner();
- if (!i->is_input_section())
- {
- // Corner case. A new stub group was made for the first
- // section (last one looked at here) for some reason, but
- // the first section is already being used as the owner for
- // a stub table for following sections. Force it into that
- // stub group.
- gold_assert(this->stub_tables_.size() >= 2);
- this->stub_tables_.pop_back();
- delete stub_table;
- Powerpc_relobj<size, big_endian>* ppcobj = static_cast
- <Powerpc_relobj<size, big_endian>*>(i->relobj());
- ppcobj->set_stub_table(i->shndx(), this->stub_tables_.back());
- }
+ table_owner->output_section = stub_control.output_section();
+ table_owner->owner = stub_control.owner();;
+ }
+ for (typename std::vector<Stub_table_owner*>::iterator t = tables.begin();
+ t != tables.end();
+ ++t)
+ {
+ Stub_table<size, big_endian>* stub_table;
+
+ if ((*t)->owner->is_input_section())
+ stub_table = new Stub_table<size, big_endian>(this,
+ (*t)->output_section,
+ (*t)->owner,
+ this->stub_tables_.size());
+ else if ((*t)->owner->is_relaxed_input_section())
+ stub_table = static_cast<Stub_table<size, big_endian>*>(
+ (*t)->owner->relaxed_input_section());
else
- stub_table->init(i, stub_control.output_section());
+ gold_unreachable();
+ this->stub_tables_.push_back(stub_table);
+ delete *t;
+ }
+}
+
+static unsigned long
+max_branch_delta (unsigned int r_type)
+{
+ if (r_type == elfcpp::R_POWERPC_REL14
+ || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
+ || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
+ return 1L << 15;
+ if (r_type == elfcpp::R_POWERPC_REL24
+ || r_type == elfcpp::R_PPC_PLTREL24
+ || r_type == elfcpp::R_PPC_LOCAL24PC)
+ return 1L << 25;
+ return 0;
+}
+
+// Return whether this branch is going via a plt call stub.
+
+template<int size, bool big_endian>
+bool
+Target_powerpc<size, big_endian>::Branch_info::mark_pltcall(
+ Powerpc_relobj<size, big_endian>* ppc_object,
+ unsigned int shndx,
+ Address offset,
+ Target_powerpc* target,
+ Symbol_table* symtab)
+{
+ if (this->object_ != ppc_object
+ || this->shndx_ != shndx
+ || this->offset_ != offset)
+ return false;
+
+ Symbol* sym = this->object_->global_symbol(this->r_sym_);
+ if (sym != NULL && sym->is_forwarder())
+ sym = symtab->resolve_forwards(sym);
+ if (target->replace_tls_get_addr(sym))
+ sym = target->tls_get_addr_opt();
+ const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym);
+ if (gsym != NULL
+ ? (gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
+ && !target->is_elfv2_localentry0(gsym))
+ : (this->object_->local_has_plt_offset(this->r_sym_)
+ && !target->is_elfv2_localentry0(this->object_, this->r_sym_)))
+ {
+ this->tocsave_ = 1;
+ return true;
}
+ return false;
}
// If this branch needs a plt call stub, or a long branch stub, make one.
template<int size, bool big_endian>
-void
+bool
Target_powerpc<size, big_endian>::Branch_info::make_stub(
Stub_table<size, big_endian>* stub_table,
Stub_table<size, big_endian>* ifunc_stub_table,
Symbol_table* symtab) const
{
Symbol* sym = this->object_->global_symbol(this->r_sym_);
- if (sym != NULL && sym->is_forwarder())
- sym = symtab->resolve_forwards(sym);
- const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym);
Target_powerpc<size, big_endian>* target =
static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
+ if (sym != NULL && sym->is_forwarder())
+ sym = symtab->resolve_forwards(sym);
+ if (target->replace_tls_get_addr(sym))
+ sym = target->tls_get_addr_opt();
+ const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym);
+ bool ok = true;
+
if (gsym != NULL
? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
: this->object_->local_has_plt_offset(this->r_sym_))
target->glink_section()->add_global_entry(gsym);
else
{
- if (stub_table == NULL)
+ if (stub_table == NULL
+ && !(size == 32
+ && gsym != NULL
+ && !parameters->options().output_is_position_independent()
+ && !is_branch_reloc(this->r_type_)))
stub_table = this->object_->stub_table(this->shndx_);
if (stub_table == NULL)
{
- // This is a ref from a data section to an ifunc symbol.
+ // This is a ref from a data section to an ifunc symbol,
+ // or a non-branch reloc for which we always want to use
+ // one set of stubs for resolving function addresses.
stub_table = ifunc_stub_table;
}
gold_assert(stub_table != NULL);
+ Address from = this->object_->get_output_section_offset(this->shndx_);
+ if (from != invalid_address)
+ from += (this->object_->output_section(this->shndx_)->address()
+ + this->offset_);
if (gsym != NULL)
- stub_table->add_plt_call_entry(this->object_, gsym,
- this->r_type_, this->addend_);
+ ok = stub_table->add_plt_call_entry(from,
+ this->object_, gsym,
+ this->r_type_, this->addend_,
+ this->tocsave_);
else
- stub_table->add_plt_call_entry(this->object_, this->r_sym_,
- this->r_type_, this->addend_);
+ ok = stub_table->add_plt_call_entry(from,
+ this->object_, this->r_sym_,
+ this->r_type_, this->addend_,
+ this->tocsave_);
}
}
else
{
- unsigned long max_branch_offset;
- if (this->r_type_ == elfcpp::R_POWERPC_REL14
- || this->r_type_ == elfcpp::R_POWERPC_REL14_BRTAKEN
- || this->r_type_ == elfcpp::R_POWERPC_REL14_BRNTAKEN)
- max_branch_offset = 1 << 15;
- else if (this->r_type_ == elfcpp::R_POWERPC_REL24
- || this->r_type_ == elfcpp::R_PPC_PLTREL24
- || this->r_type_ == elfcpp::R_PPC_LOCAL24PC)
- max_branch_offset = 1 << 25;
- else
- return;
+ Address max_branch_offset = max_branch_delta(this->r_type_);
+ if (max_branch_offset == 0)
+ return true;
Address from = this->object_->get_output_section_offset(this->shndx_);
gold_assert(from != invalid_address);
from += (this->object_->output_section(this->shndx_)->address()
Object* symobj = gsym->object();
if (symobj->is_dynamic()
|| symobj->pluginobj() != NULL)
- return;
+ return true;
bool is_ordinary;
unsigned int shndx = gsym->shndx(&is_ordinary);
if (shndx == elfcpp::SHN_UNDEF)
- return;
+ return true;
}
break;
case Symbol::IS_UNDEFINED:
- return;
+ return true;
default:
break;
Symbol_table::Compute_final_value_status status;
to = symtab->compute_final_value<size>(gsym, &status);
if (status != Symbol_table::CFVS_OK)
- return;
+ return true;
if (size == 64)
to += this->object_->ppc64_local_entry_offset(gsym);
}
const Symbol_value<size>* psymval
= this->object_->local_symbol(this->r_sym_);
Symbol_value<size> symval;
+ if (psymval->is_section_symbol())
+ symval.set_is_section_symbol();
typedef Sized_relobj_file<size, big_endian> ObjType;
typename ObjType::Compute_final_local_value_status status
= this->object_->compute_final_local_value(this->r_sym_, psymval,
&symval, symtab);
if (status != ObjType::CFLV_OK
|| !symval.has_output_value())
- return;
+ return true;
to = symval.value(this->object_, 0);
if (size == 64)
to += this->object_->ppc64_local_entry_offset(this->r_sym_);
}
- to += this->addend_;
+ if (!(size == 32 && this->r_type_ == elfcpp::R_PPC_PLTREL24))
+ to += this->addend_;
if (stub_table == NULL)
stub_table = this->object_->stub_table(this->shndx_);
if (size == 64 && target->abiversion() < 2)
{
unsigned int dest_shndx;
- to = target->symval_for_branch(symtab, to, gsym,
- this->object_, &dest_shndx);
+ if (!target->symval_for_branch(symtab, gsym, this->object_,
+ &to, &dest_shndx))
+ return true;
}
Address delta = to - from;
if (delta + max_branch_offset >= 2 * max_branch_offset)
" no long branch stub for you"),
this->object_->name().c_str(),
this->object_->section_name(this->shndx_).c_str());
- return;
+ return true;
}
- stub_table->add_long_branch_entry(this->object_, to);
+ bool save_res = (size == 64
+ && gsym != NULL
+ && gsym->source() == Symbol::IN_OUTPUT_DATA
+ && gsym->output_data() == target->savres_section());
+ ok = stub_table->add_long_branch_entry(this->object_,
+ this->r_type_,
+ from, to, save_res);
}
}
+ if (!ok)
+ gold_debug(DEBUG_TARGET,
+ "branch at %s:%s+%#lx\n"
+ "can't reach stub attached to %s:%s",
+ this->object_->name().c_str(),
+ this->object_->section_name(this->shndx_).c_str(),
+ (unsigned long) this->offset_,
+ stub_table->relobj()->name().c_str(),
+ stub_table->relobj()->section_name(stub_table->shndx()).c_str());
+
+ return ok;
}
// Relaxation hook. This is where we do stub generation.
/* libanl */
"getaddrinfo_a",
/* libgomp */
+ "GOMP_parallel",
"GOMP_parallel_start",
+ "GOMP_parallel_loop_static",
"GOMP_parallel_loop_static_start",
+ "GOMP_parallel_loop_dynamic",
"GOMP_parallel_loop_dynamic_start",
+ "GOMP_parallel_loop_guided",
"GOMP_parallel_loop_guided_start",
+ "GOMP_parallel_loop_runtime",
"GOMP_parallel_loop_runtime_start",
+ "GOMP_parallel_sections",
"GOMP_parallel_sections_start",
+ /* libgo */
+ "__go_go",
};
if (parameters->options().shared())
}
}
this->plt_thread_safe_ = thread_safe;
- this->group_sections(layout, task);
+ }
+
+ if (pass == 1)
+ {
+ this->stub_group_size_ = parameters->options().stub_group_size();
+ bool no_size_errors = true;
+ if (this->stub_group_size_ == 1)
+ this->stub_group_size_ = 0x1c00000;
+ else if (this->stub_group_size_ == -1)
+ this->stub_group_size_ = -0x1e00000;
+ else
+ no_size_errors = false;
+ this->group_sections(layout, task, no_size_errors);
+ }
+ else if (this->relax_failed_ && this->relax_fail_count_ < 3)
+ {
+ this->branch_lookup_table_.clear();
+ for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+ p != this->stub_tables_.end();
+ ++p)
+ {
+ (*p)->clear_stubs(true);
+ }
+ this->stub_tables_.clear();
+ this->stub_group_size_ = this->stub_group_size_ / 4 * 3;
+ gold_info(_("%s: stub group size is too large; retrying with %#x"),
+ program_name, this->stub_group_size_);
+ this->group_sections(layout, task, true);
}
// We need address of stub tables valid for make_stub.
p != this->stub_tables_.end();
++p)
{
- (*p)->clear_stubs();
+ (*p)->clear_stubs(false);
}
}
// Build all the stubs.
+ this->relax_failed_ = false;
Stub_table<size, big_endian>* ifunc_stub_table
= this->stub_tables_.size() == 0 ? NULL : this->stub_tables_[0];
Stub_table<size, big_endian>* one_stub_table
b != this->branch_info_.end();
b++)
{
- b->make_stub(one_stub_table, ifunc_stub_table, symtab);
+ if (!b->make_stub(one_stub_table, ifunc_stub_table, symtab)
+ && !this->relax_failed_)
+ {
+ this->relax_failed_ = true;
+ this->relax_fail_count_++;
+ if (this->relax_fail_count_ < 3)
+ return true;
+ }
}
// Did anything change size?
if (size == 64 && again)
this->brlt_section_->set_current_size(num_huge_branches);
+ for (typename Stub_tables::reverse_iterator p = this->stub_tables_.rbegin();
+ p != this->stub_tables_.rend();
+ ++p)
+ (*p)->remove_eh_frame(layout);
+
+ for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+ p != this->stub_tables_.end();
+ ++p)
+ (*p)->add_eh_frame(layout);
+
typedef Unordered_set<Output_section*> Output_sections;
Output_sections os_need_update;
for (typename Stub_tables::iterator p = this->stub_tables_.begin();
if ((*p)->size_update())
{
again = true;
- (*p)->add_eh_frame(layout);
os_need_update.insert((*p)->output_section());
}
}
Stub_table<size, big_endian>* stub_table
= static_cast<Stub_table<size, big_endian>*>(
i->relaxed_input_section());
- off += stub_table->set_address_and_size(os, off);
+ Address stub_table_size = stub_table->set_address_and_size(os, off);
+ off += stub_table_size;
+ // After a few iterations, set current stub table size
+ // as min size threshold, so later stub tables can only
+ // grow in size.
+ if (pass >= 4)
+ stub_table->set_min_size_threshold(stub_table_size);
}
else
off += i->data_size();
}
this->brlt_section_->finalize_brlt_sizes();
}
+
+ if (!again
+ && (parameters->options().user_set_emit_stub_syms()
+ ? parameters->options().emit_stub_syms()
+ : (size == 64
+ || parameters->options().output_is_position_independent()
+ || parameters->options().emit_relocs())))
+ {
+ for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+ p != this->stub_tables_.end();
+ ++p)
+ (*p)->define_stub_syms(symtab);
+
+ if (this->glink_ != NULL)
+ {
+ int stub_size = this->glink_->pltresolve_size;
+ Address value = -stub_size;
+ if (size == 64)
+ {
+ value = 8;
+ stub_size -= 8;
+ }
+ this->define_local(symtab, "__glink_PLTresolve",
+ this->glink_, value, stub_size);
+
+ if (size != 64)
+ this->define_local(symtab, "__glink", this->glink_, 0, 0);
+ }
+ }
+
return again;
}
static const uint32_t add_0_11_11 = 0x7c0b5a14;
static const uint32_t add_2_2_11 = 0x7c425a14;
+static const uint32_t add_2_2_12 = 0x7c426214;
static const uint32_t add_3_3_2 = 0x7c631214;
static const uint32_t add_3_3_13 = 0x7c636a14;
+static const uint32_t add_3_12_2 = 0x7c6c1214;
+static const uint32_t add_3_12_13 = 0x7c6c6a14;
static const uint32_t add_11_0_11 = 0x7d605a14;
static const uint32_t add_11_2_11 = 0x7d625a14;
static const uint32_t add_11_11_2 = 0x7d6b1214;
static const uint32_t addi_2_2 = 0x38420000;
static const uint32_t addi_3_3 = 0x38630000;
static const uint32_t addi_11_11 = 0x396b0000;
+static const uint32_t addi_12_1 = 0x39810000;
static const uint32_t addi_12_12 = 0x398c0000;
static const uint32_t addis_0_2 = 0x3c020000;
static const uint32_t addis_0_13 = 0x3c0d0000;
-static const uint32_t addis_3_2 = 0x3c620000;
-static const uint32_t addis_3_13 = 0x3c6d0000;
+static const uint32_t addis_2_12 = 0x3c4c0000;
static const uint32_t addis_11_2 = 0x3d620000;
static const uint32_t addis_11_11 = 0x3d6b0000;
static const uint32_t addis_11_30 = 0x3d7e0000;
+static const uint32_t addis_12_1 = 0x3d810000;
+static const uint32_t addis_12_2 = 0x3d820000;
static const uint32_t addis_12_12 = 0x3d8c0000;
static const uint32_t b = 0x48000000;
static const uint32_t bcl_20_31 = 0x429f0005;
static const uint32_t bctr = 0x4e800420;
+static const uint32_t bctrl = 0x4e800421;
+static const uint32_t beqlr = 0x4d820020;
static const uint32_t blr = 0x4e800020;
static const uint32_t bnectr_p4 = 0x4ce20420;
+static const uint32_t cmpld_7_12_0 = 0x7fac0040;
static const uint32_t cmpldi_2_0 = 0x28220000;
+static const uint32_t cmpdi_11_0 = 0x2c2b0000;
+static const uint32_t cmpwi_11_0 = 0x2c0b0000;
static const uint32_t cror_15_15_15 = 0x4def7b82;
static const uint32_t cror_31_31_31 = 0x4ffffb82;
static const uint32_t ld_0_1 = 0xe8010000;
static const uint32_t ld_2_1 = 0xe8410000;
static const uint32_t ld_2_2 = 0xe8420000;
static const uint32_t ld_2_11 = 0xe84b0000;
+static const uint32_t ld_2_12 = 0xe84c0000;
+static const uint32_t ld_11_1 = 0xe9610000;
static const uint32_t ld_11_2 = 0xe9620000;
+static const uint32_t ld_11_3 = 0xe9630000;
static const uint32_t ld_11_11 = 0xe96b0000;
static const uint32_t ld_12_2 = 0xe9820000;
+static const uint32_t ld_12_3 = 0xe9830000;
static const uint32_t ld_12_11 = 0xe98b0000;
static const uint32_t ld_12_12 = 0xe98c0000;
static const uint32_t lfd_0_1 = 0xc8010000;
static const uint32_t li_0_0 = 0x38000000;
static const uint32_t li_12_0 = 0x39800000;
-static const uint32_t lis_0_0 = 0x3c000000;
+static const uint32_t lis_0 = 0x3c000000;
+static const uint32_t lis_2 = 0x3c400000;
static const uint32_t lis_11 = 0x3d600000;
static const uint32_t lis_12 = 0x3d800000;
static const uint32_t lvx_0_12_0 = 0x7c0c00ce;
static const uint32_t lwz_0_12 = 0x800c0000;
+static const uint32_t lwz_11_3 = 0x81630000;
static const uint32_t lwz_11_11 = 0x816b0000;
static const uint32_t lwz_11_30 = 0x817e0000;
+static const uint32_t lwz_12_3 = 0x81830000;
static const uint32_t lwz_12_12 = 0x818c0000;
static const uint32_t lwzu_0_12 = 0x840c0000;
static const uint32_t mflr_0 = 0x7c0802a6;
static const uint32_t mflr_11 = 0x7d6802a6;
static const uint32_t mflr_12 = 0x7d8802a6;
+static const uint32_t mr_0_3 = 0x7c601b78;
+static const uint32_t mr_3_0 = 0x7c030378;
static const uint32_t mtctr_0 = 0x7c0903a6;
static const uint32_t mtctr_11 = 0x7d6903a6;
static const uint32_t mtctr_12 = 0x7d8903a6;
static const uint32_t mtlr_0 = 0x7c0803a6;
+static const uint32_t mtlr_11 = 0x7d6803a6;
static const uint32_t mtlr_12 = 0x7d8803a6;
static const uint32_t nop = 0x60000000;
static const uint32_t ori_0_0_0 = 0x60000000;
static const uint32_t std_0_1 = 0xf8010000;
static const uint32_t std_0_12 = 0xf80c0000;
static const uint32_t std_2_1 = 0xf8410000;
+static const uint32_t std_11_1 = 0xf9610000;
static const uint32_t stfd_0_1 = 0xd8010000;
static const uint32_t stvx_0_12_0 = 0x7c0c01ce;
static const uint32_t sub_11_11_12 = 0x7d6c5850;
? ORDER_SMALL_DATA
: ORDER_SMALL_BSS),
false);
+
+ Output_section* rela_plt_os = plt_rel->output_section();
+ rela_plt_os->set_info_section(this->plt_->output_section());
}
}
this->make_plt_section(symtab, layout);
Reloc_section* iplt_rel = new Reloc_section(false);
- this->rela_dyn_->output_section()->add_output_section_data(iplt_rel);
+ if (this->rela_dyn_->output_section())
+ this->rela_dyn_->output_section()->add_output_section_data(iplt_rel);
this->iplt_
= new Output_data_plt_powerpc<size, big_endian>(this, iplt_rel,
"** IPLT");
- this->plt_->output_section()->add_output_section_data(this->iplt_);
+ if (this->plt_->output_section())
+ this->plt_->output_section()->add_output_section_data(this->iplt_);
}
}
os->set_section_offsets_need_adjustment();
if (this->rel_ != NULL)
{
- unsigned int reloc_size
- = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ const unsigned int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
this->rel_->reset_address_and_file_offset();
this->rel_->set_current_data_size(num_branches * reloc_size);
this->rel_->finalize_data_size();
{
// When PIC we can't fill in .branch_lt (like .plt it can be
// a bss style section) but must initialise at runtime via
- // dynamic relocats.
+ // dynamic relocations.
this->rela_dyn_section(layout);
brlt_rel = new Reloc_section(false);
- this->rela_dyn_->output_section()->add_output_section_data(brlt_rel);
+ if (this->rela_dyn_->output_section())
+ this->rela_dyn_->output_section()
+ ->add_output_section_data(brlt_rel);
}
this->brlt_section_
= new Output_data_brlt_powerpc<size, big_endian>(this, brlt_rel);
- if (this->plt_ && is_pic)
+ if (this->plt_ && is_pic && this->plt_->output_section())
this->plt_->output_section()
->add_output_section_data(this->brlt_section_);
else
0, // Augmentation size.
elfcpp::DW_CFA_advance_loc + 1,
elfcpp::DW_CFA_register, 65, 12,
- elfcpp::DW_CFA_advance_loc + 4,
+ elfcpp::DW_CFA_advance_loc + 5,
elfcpp::DW_CFA_restore_extended, 65
};
0, // Augmentation size.
elfcpp::DW_CFA_advance_loc + 1,
elfcpp::DW_CFA_register, 65, 0,
- elfcpp::DW_CFA_advance_loc + 4,
+ elfcpp::DW_CFA_advance_loc + 7,
elfcpp::DW_CFA_restore_extended, 65
};
class Stub_table : public Output_relaxed_input_section
{
public:
+ struct Plt_stub_ent
+ {
+ Plt_stub_ent(unsigned int off, unsigned int indx)
+ : off_(off), indx_(indx), r2save_(0), localentry0_(0)
+ { }
+
+ unsigned int off_;
+ unsigned int indx_ : 30;
+ unsigned int r2save_ : 1;
+ unsigned int localentry0_ : 1;
+ };
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
static const Address invalid_address = static_cast<Address>(0) - 1;
- Stub_table(Target_powerpc<size, big_endian>* targ)
- : Output_relaxed_input_section(NULL, 0, 0),
+ Stub_table(Target_powerpc<size, big_endian>* targ,
+ Output_section* output_section,
+ const Output_section::Input_section* owner,
+ uint32_t id)
+ : Output_relaxed_input_section(owner->relobj(), owner->shndx(),
+ owner->relobj()
+ ->section_addralign(owner->shndx())),
targ_(targ), plt_call_stubs_(), long_branch_stubs_(),
- orig_data_size_(0), plt_size_(0), last_plt_size_(0),
- branch_size_(0), last_branch_size_(0), eh_frame_added_(false)
- { }
+ orig_data_size_(owner->current_data_size()),
+ plt_size_(0), last_plt_size_(0),
+ branch_size_(0), last_branch_size_(0), min_size_threshold_(0),
+ need_save_res_(false), uniq_(id), tls_get_addr_opt_bctrl_(-1u),
+ plt_fde_len_(0)
+ {
+ this->set_output_section(output_section);
- // Delayed Output_relaxed_input_section init.
- void
- init(const Output_section::Input_section*, Output_section*);
+ std::vector<Output_relaxed_input_section*> new_relaxed;
+ new_relaxed.push_back(this);
+ output_section->convert_input_sections_to_relaxed_sections(new_relaxed);
+ }
// Add a plt call stub.
- void
- add_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+ bool
+ add_plt_call_entry(Address,
+ const Sized_relobj_file<size, big_endian>*,
const Symbol*,
unsigned int,
- Address);
+ Address,
+ bool);
- void
- add_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+ bool
+ add_plt_call_entry(Address,
+ const Sized_relobj_file<size, big_endian>*,
unsigned int,
unsigned int,
- Address);
+ Address,
+ bool);
// Find a given plt call stub.
- Address
+ const Plt_stub_ent*
find_plt_call_entry(const Symbol*) const;
- Address
+ const Plt_stub_ent*
find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
unsigned int) const;
- Address
+ const Plt_stub_ent*
find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
const Symbol*,
unsigned int,
Address) const;
- Address
+ const Plt_stub_ent*
find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
unsigned int,
unsigned int,
Address) const;
// Add a long branch stub.
- void
- add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address);
+ bool
+ add_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
+ unsigned int, Address, Address, bool);
Address
find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
Address) const;
+ bool
+ can_reach_stub(Address from, unsigned int off, unsigned int r_type)
+ {
+ Address max_branch_offset = max_branch_delta(r_type);
+ if (max_branch_offset == 0)
+ return true;
+ gold_assert(from != invalid_address);
+ Address loc = off + this->stub_address();
+ return loc - from + max_branch_offset < 2 * max_branch_offset;
+ }
+
void
- clear_stubs()
+ clear_stubs(bool all)
{
this->plt_call_stubs_.clear();
this->plt_size_ = 0;
this->long_branch_stubs_.clear();
this->branch_size_ = 0;
+ this->need_save_res_ = false;
+ if (all)
+ {
+ this->last_plt_size_ = 0;
+ this->last_branch_size_ = 0;
+ }
}
Address
Address start_off = off;
off += this->orig_data_size_;
Address my_size = this->plt_size_ + this->branch_size_;
+ if (this->need_save_res_)
+ my_size += this->targ_->savres_section()->data_size();
if (my_size != 0)
off = align_address(off, this->stub_align());
// Include original section size and alignment padding in size
my_size += off - start_off;
+ // Ensure new size is always larger than min size
+ // threshold. Alignment requirement is included in "my_size", so
+ // increase "my_size" does not invalidate alignment.
+ if (my_size < this->min_size_threshold_)
+ my_size = this->min_size_threshold_;
this->reset_address_and_file_offset();
this->set_current_data_size(my_size);
this->set_address_and_file_offset(os->address() + start_off,
plt_size() const
{ return this->plt_size_; }
+ void
+ set_min_size_threshold(Address min_size)
+ { this->min_size_threshold_ = min_size; }
+
+ void
+ define_stub_syms(Symbol_table*);
+
bool
size_update()
{
return false;
}
- // Add .eh_frame info for this stub section. Unlike other linker
- // generated .eh_frame this is added late in the link, because we
- // only want the .eh_frame info if this particular stub section is
- // non-empty.
+ // Generate a suitable FDE to describe code in this stub group.
void
- add_eh_frame(Layout* layout)
- {
- if (!this->eh_frame_added_)
- {
- if (!parameters->options().ld_generated_unwind_info())
- return;
+ init_plt_fde();
- // Since we add stub .eh_frame info late, it must be placed
- // after all other linker generated .eh_frame info so that
- // merge mapping need not be updated for input sections.
- // There is no provision to use a different CIE to that used
- // by .glink.
- if (!this->targ_->has_glink())
- return;
+ // Add .eh_frame info for this stub section.
+ void
+ add_eh_frame(Layout* layout);
- layout->add_eh_frame_for_plt(this,
- Eh_cie<size>::eh_frame_cie,
- sizeof (Eh_cie<size>::eh_frame_cie),
- default_fde,
- sizeof (default_fde));
- this->eh_frame_added_ = true;
- }
- }
+ // Remove .eh_frame info for this stub section.
+ void
+ remove_eh_frame(Layout* layout);
Target_powerpc<size, big_endian>*
targ() const
{ return targ_; }
private:
- class Plt_stub_ent;
- class Plt_stub_ent_hash;
- typedef Unordered_map<Plt_stub_ent, unsigned int,
- Plt_stub_ent_hash> Plt_stub_entries;
+ class Plt_stub_key;
+ class Plt_stub_key_hash;
+ typedef Unordered_map<Plt_stub_key, Plt_stub_ent,
+ Plt_stub_key_hash> Plt_stub_entries;
+ class Branch_stub_ent;
+ class Branch_stub_ent_hash;
+ typedef Unordered_map<Branch_stub_ent, unsigned int,
+ Branch_stub_ent_hash> Branch_stub_entries;
// Alignment of stub section.
unsigned int
plt_call_size(typename Plt_stub_entries::const_iterator p) const
{
if (size == 32)
- return 16;
+ {
+ const Symbol* gsym = p->first.sym_;
+ if (this->targ_->is_tls_get_addr_opt(gsym))
+ return 12 * 4;
+ return 4 * 4;
+ }
bool is_iplt;
Address plt_addr = this->plt_off(p, &is_iplt);
got_addr += ppcobj->toc_base_offset();
Address off = plt_addr - got_addr;
unsigned int bytes = 4 * 4 + 4 * (ha(off) != 0);
+ const Symbol* gsym = p->first.sym_;
+ if (this->targ_->is_tls_get_addr_opt(gsym))
+ bytes += 13 * 4;
if (this->targ_->abiversion() < 2)
{
bool static_chain = parameters->options().plt_static_chain();
+ 8 * thread_safe
+ 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
}
+ return bytes;
+ }
+
+ unsigned int
+ plt_call_align(unsigned int bytes) const
+ {
unsigned int align = 1 << parameters->options().plt_align();
if (align > 1)
bytes = (bytes + align - 1) & -align;
// Return long branch stub size.
unsigned int
- branch_stub_size(Address to)
+ branch_stub_size(typename Branch_stub_entries::const_iterator p)
{
- Address loc
- = this->stub_address() + this->last_plt_size_ + this->branch_size_;
- if (to - loc + (1 << 25) < 2 << 25)
+ Address loc = this->stub_address() + this->last_plt_size_ + p->second;
+ if (p->first.dest_ - loc + (1 << 25) < 2 << 25)
return 4;
if (size == 64 || !parameters->options().output_is_position_independent())
return 16;
do_write(Output_file*);
// Plt call stub keys.
- class Plt_stub_ent
+ class Plt_stub_key
{
public:
- Plt_stub_ent(const Symbol* sym)
+ Plt_stub_key(const Symbol* sym)
: sym_(sym), object_(0), addend_(0), locsym_(0)
{ }
- Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
+ Plt_stub_key(const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index)
: sym_(NULL), object_(object), addend_(0), locsym_(locsym_index)
{ }
- Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
+ Plt_stub_key(const Sized_relobj_file<size, big_endian>* object,
const Symbol* sym,
unsigned int r_type,
Address addend)
}
}
- Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
+ Plt_stub_key(const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index,
unsigned int r_type,
Address addend)
this->addend_ = addend;
}
- bool operator==(const Plt_stub_ent& that) const
+ bool operator==(const Plt_stub_key& that) const
{
return (this->sym_ == that.sym_
&& this->object_ == that.object_
unsigned int locsym_;
};
- class Plt_stub_ent_hash
+ class Plt_stub_key_hash
{
public:
- size_t operator()(const Plt_stub_ent& ent) const
+ size_t operator()(const Plt_stub_key& ent) const
{
return (reinterpret_cast<uintptr_t>(ent.sym_)
^ reinterpret_cast<uintptr_t>(ent.object_)
class Branch_stub_ent
{
public:
- Branch_stub_ent(const Powerpc_relobj<size, big_endian>* obj, Address to)
- : dest_(to), toc_base_off_(0)
+ Branch_stub_ent(const Powerpc_relobj<size, big_endian>* obj,
+ Address to, bool save_res)
+ : dest_(to), toc_base_off_(0), save_res_(save_res)
{
if (size == 64)
toc_base_off_ = obj->toc_base_offset();
Address dest_;
unsigned int toc_base_off_;
+ bool save_res_;
};
class Branch_stub_ent_hash
// Map sym/object/addend to stub offset.
Plt_stub_entries plt_call_stubs_;
// Map destination address to stub offset.
- typedef Unordered_map<Branch_stub_ent, unsigned int,
- Branch_stub_ent_hash> Branch_stub_entries;
Branch_stub_entries long_branch_stubs_;
// size of input section
section_size_type orig_data_size_;
// size of stubs
section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_;
- // Whether .eh_frame info has been created for this stub section.
- bool eh_frame_added_;
+ // Some rare cases cause (PR/20529) fluctuation in stub table
+ // size, which leads to an endless relax loop. This is to be fixed
+ // by, after the first few iterations, allowing only increase of
+ // stub table size. This variable sets the minimal possible size of
+ // a stub table, it is zero for the first few iterations, then
+ // increases monotonically.
+ Address min_size_threshold_;
+ // Set if this stub group needs a copy of out-of-line register
+ // save/restore functions.
+ bool need_save_res_;
+ // Per stub table unique identifier.
+ uint32_t uniq_;
+ // The bctrl in the __tls_get_addr_opt stub, if present.
+ unsigned int tls_get_addr_opt_bctrl_;
+ // FDE unwind info for this stub group.
+ unsigned int plt_fde_len_;
+ unsigned char plt_fde_[20];
};
-// Make a new stub table, and record.
-
-template<int size, bool big_endian>
-Stub_table<size, big_endian>*
-Target_powerpc<size, big_endian>::new_stub_table()
-{
- Stub_table<size, big_endian>* stub_table
- = new Stub_table<size, big_endian>(this);
- this->stub_tables_.push_back(stub_table);
- return stub_table;
-}
-
-// Delayed stub table initialisation, because we create the stub table
-// before we know to which section it will be attached.
-
-template<int size, bool big_endian>
-void
-Stub_table<size, big_endian>::init(
- const Output_section::Input_section* owner,
- Output_section* output_section)
-{
- this->set_relobj(owner->relobj());
- this->set_shndx(owner->shndx());
- this->set_addralign(this->relobj()->section_addralign(this->shndx()));
- this->set_output_section(output_section);
- this->orig_data_size_ = owner->current_data_size();
-
- std::vector<Output_relaxed_input_section*> new_relaxed;
- new_relaxed.push_back(this);
- output_section->convert_input_sections_to_relaxed_sections(new_relaxed);
-}
-
// Add a plt call stub, if we do not already have one for this
// sym/object/addend combo.
template<int size, bool big_endian>
-void
+bool
Stub_table<size, big_endian>::add_plt_call_entry(
+ Address from,
const Sized_relobj_file<size, big_endian>* object,
const Symbol* gsym,
unsigned int r_type,
- Address addend)
+ Address addend,
+ bool tocsave)
{
- Plt_stub_ent ent(object, gsym, r_type, addend);
- unsigned int off = this->plt_size_;
+ Plt_stub_key key(object, gsym, r_type, addend);
+ Plt_stub_ent ent(this->plt_size_, this->plt_call_stubs_.size());
std::pair<typename Plt_stub_entries::iterator, bool> p
- = this->plt_call_stubs_.insert(std::make_pair(ent, off));
+ = this->plt_call_stubs_.insert(std::make_pair(key, ent));
if (p.second)
- this->plt_size_ = off + this->plt_call_size(p.first);
+ {
+ this->plt_size_ = ent.off_ + this->plt_call_size(p.first);
+ if (size == 64
+ && this->targ_->is_elfv2_localentry0(gsym))
+ {
+ p.first->second.localentry0_ = 1;
+ this->targ_->set_has_localentry0();
+ }
+ if (this->targ_->is_tls_get_addr_opt(gsym))
+ {
+ this->targ_->set_has_tls_get_addr_opt();
+ this->tls_get_addr_opt_bctrl_ = this->plt_size_ - 5 * 4;
+ }
+ this->plt_size_ = this->plt_call_align(this->plt_size_);
+ }
+ if (size == 64
+ && !tocsave
+ && !p.first->second.localentry0_)
+ p.first->second.r2save_ = 1;
+ return this->can_reach_stub(from, ent.off_, r_type);
}
template<int size, bool big_endian>
-void
+bool
Stub_table<size, big_endian>::add_plt_call_entry(
+ Address from,
const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index,
unsigned int r_type,
- Address addend)
+ Address addend,
+ bool tocsave)
{
- Plt_stub_ent ent(object, locsym_index, r_type, addend);
- unsigned int off = this->plt_size_;
+ Plt_stub_key key(object, locsym_index, r_type, addend);
+ Plt_stub_ent ent(this->plt_size_, this->plt_call_stubs_.size());
std::pair<typename Plt_stub_entries::iterator, bool> p
- = this->plt_call_stubs_.insert(std::make_pair(ent, off));
+ = this->plt_call_stubs_.insert(std::make_pair(key, ent));
if (p.second)
- this->plt_size_ = off + this->plt_call_size(p.first);
+ {
+ this->plt_size_ = ent.off_ + this->plt_call_size(p.first);
+ this->plt_size_ = this->plt_call_align(this->plt_size_);
+ if (size == 64
+ && this->targ_->is_elfv2_localentry0(object, locsym_index))
+ {
+ p.first->second.localentry0_ = 1;
+ this->targ_->set_has_localentry0();
+ }
+ }
+ if (size == 64
+ && !tocsave
+ && !p.first->second.localentry0_)
+ p.first->second.r2save_ = 1;
+ return this->can_reach_stub(from, ent.off_, r_type);
}
// Find a plt call stub.
template<int size, bool big_endian>
-typename Stub_table<size, big_endian>::Address
+const typename Stub_table<size, big_endian>::Plt_stub_ent*
Stub_table<size, big_endian>::find_plt_call_entry(
const Sized_relobj_file<size, big_endian>* object,
const Symbol* gsym,
unsigned int r_type,
Address addend) const
{
- Plt_stub_ent ent(object, gsym, r_type, addend);
- typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
- return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
+ Plt_stub_key key(object, gsym, r_type, addend);
+ typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(key);
+ if (p == this->plt_call_stubs_.end())
+ return NULL;
+ return &p->second;
}
template<int size, bool big_endian>
-typename Stub_table<size, big_endian>::Address
+const typename Stub_table<size, big_endian>::Plt_stub_ent*
Stub_table<size, big_endian>::find_plt_call_entry(const Symbol* gsym) const
{
- Plt_stub_ent ent(gsym);
- typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
- return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
+ Plt_stub_key key(gsym);
+ typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(key);
+ if (p == this->plt_call_stubs_.end())
+ return NULL;
+ return &p->second;
}
template<int size, bool big_endian>
-typename Stub_table<size, big_endian>::Address
+const typename Stub_table<size, big_endian>::Plt_stub_ent*
Stub_table<size, big_endian>::find_plt_call_entry(
const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index,
unsigned int r_type,
Address addend) const
{
- Plt_stub_ent ent(object, locsym_index, r_type, addend);
- typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
- return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
+ Plt_stub_key key(object, locsym_index, r_type, addend);
+ typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(key);
+ if (p == this->plt_call_stubs_.end())
+ return NULL;
+ return &p->second;
}
template<int size, bool big_endian>
-typename Stub_table<size, big_endian>::Address
+const typename Stub_table<size, big_endian>::Plt_stub_ent*
Stub_table<size, big_endian>::find_plt_call_entry(
const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index) const
{
- Plt_stub_ent ent(object, locsym_index);
- typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
- return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
+ Plt_stub_key key(object, locsym_index);
+ typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(key);
+ if (p == this->plt_call_stubs_.end())
+ return NULL;
+ return &p->second;
}
// Add a long branch stub if we don't already have one to given
// destination.
template<int size, bool big_endian>
-void
+bool
Stub_table<size, big_endian>::add_long_branch_entry(
const Powerpc_relobj<size, big_endian>* object,
- Address to)
+ unsigned int r_type,
+ Address from,
+ Address to,
+ bool save_res)
{
- Branch_stub_ent ent(object, to);
+ Branch_stub_ent ent(object, to, save_res);
Address off = this->branch_size_;
- if (this->long_branch_stubs_.insert(std::make_pair(ent, off)).second)
+ std::pair<typename Branch_stub_entries::iterator, bool> p
+ = this->long_branch_stubs_.insert(std::make_pair(ent, off));
+ if (p.second)
{
- unsigned int stub_size = this->branch_stub_size(to);
- this->branch_size_ = off + stub_size;
- if (size == 64 && stub_size != 4)
- this->targ_->add_branch_lookup_table(to);
+ if (save_res)
+ this->need_save_res_ = true;
+ else
+ {
+ unsigned int stub_size = this->branch_stub_size(p.first);
+ this->branch_size_ = off + stub_size;
+ if (size == 64 && stub_size != 4)
+ this->targ_->add_branch_lookup_table(to);
+ }
}
+ return this->can_reach_stub(from, off, r_type);
}
-// Find long branch stub.
+// Find long branch stub offset.
template<int size, bool big_endian>
typename Stub_table<size, big_endian>::Address
const Powerpc_relobj<size, big_endian>* object,
Address to) const
{
- Branch_stub_ent ent(object, to);
+ Branch_stub_ent ent(object, to, false);
typename Branch_stub_entries::const_iterator p
= this->long_branch_stubs_.find(ent);
- return p == this->long_branch_stubs_.end() ? invalid_address : p->second;
+ if (p == this->long_branch_stubs_.end())
+ return invalid_address;
+ if (p->first.save_res_)
+ return to - this->targ_->savres_section()->address() + this->branch_size_;
+ return p->second;
+}
+
+// Generate a suitable FDE to describe code in this stub group.
+// The __tls_get_addr_opt call stub needs to describe where it saves
+// LR, to support exceptions that might be thrown from __tls_get_addr.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::init_plt_fde()
+{
+ unsigned char* p = this->plt_fde_;
+ // offset pcrel sdata4, size udata4, and augmentation size byte.
+ memset (p, 0, 9);
+ p += 9;
+ if (this->tls_get_addr_opt_bctrl_ != -1u)
+ {
+ unsigned int to_bctrl = this->tls_get_addr_opt_bctrl_ / 4;
+ if (to_bctrl < 64)
+ *p++ = elfcpp::DW_CFA_advance_loc + to_bctrl;
+ else if (to_bctrl < 256)
+ {
+ *p++ = elfcpp::DW_CFA_advance_loc1;
+ *p++ = to_bctrl;
+ }
+ else if (to_bctrl < 65536)
+ {
+ *p++ = elfcpp::DW_CFA_advance_loc2;
+ elfcpp::Swap<16, big_endian>::writeval(p, to_bctrl);
+ p += 2;
+ }
+ else
+ {
+ *p++ = elfcpp::DW_CFA_advance_loc4;
+ elfcpp::Swap<32, big_endian>::writeval(p, to_bctrl);
+ p += 4;
+ }
+ *p++ = elfcpp::DW_CFA_offset_extended_sf;
+ *p++ = 65;
+ *p++ = -(this->targ_->stk_linker() / 8) & 0x7f;
+ *p++ = elfcpp::DW_CFA_advance_loc + 4;
+ *p++ = elfcpp::DW_CFA_restore_extended;
+ *p++ = 65;
+ }
+ this->plt_fde_len_ = p - this->plt_fde_;
+}
+
+// Add .eh_frame info for this stub section. Unlike other linker
+// generated .eh_frame this is added late in the link, because we
+// only want the .eh_frame info if this particular stub section is
+// non-empty.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::add_eh_frame(Layout* layout)
+{
+ if (!parameters->options().ld_generated_unwind_info())
+ return;
+
+ // Since we add stub .eh_frame info late, it must be placed
+ // after all other linker generated .eh_frame info so that
+ // merge mapping need not be updated for input sections.
+ // There is no provision to use a different CIE to that used
+ // by .glink.
+ if (!this->targ_->has_glink())
+ return;
+
+ if (this->plt_size_ + this->branch_size_ + this->need_save_res_ == 0)
+ return;
+
+ this->init_plt_fde();
+ layout->add_eh_frame_for_plt(this,
+ Eh_cie<size>::eh_frame_cie,
+ sizeof (Eh_cie<size>::eh_frame_cie),
+ this->plt_fde_, this->plt_fde_len_);
+}
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::remove_eh_frame(Layout* layout)
+{
+ if (this->plt_fde_len_ != 0)
+ {
+ layout->remove_eh_frame_for_plt(this,
+ Eh_cie<size>::eh_frame_cie,
+ sizeof (Eh_cie<size>::eh_frame_cie),
+ this->plt_fde_, this->plt_fde_len_);
+ this->plt_fde_len_ = 0;
+ }
}
// A class to handle .glink.
this->set_data_size(total);
}
+// Define symbols on stubs, identifying the stub.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
+{
+ if (!this->plt_call_stubs_.empty())
+ {
+ // The key for the plt call stub hash table includes addresses,
+ // therefore traversal order depends on those addresses, which
+ // can change between runs if gold is a PIE. Unfortunately the
+ // output .symtab ordering depends on the order in which symbols
+ // are added to the linker symtab. We want reproducible output
+ // so must sort the call stub symbols.
+ typedef typename Plt_stub_entries::const_iterator plt_iter;
+ std::vector<plt_iter> sorted;
+ sorted.resize(this->plt_call_stubs_.size());
+
+ for (plt_iter cs = this->plt_call_stubs_.begin();
+ cs != this->plt_call_stubs_.end();
+ ++cs)
+ sorted[cs->second.indx_] = cs;
+
+ for (unsigned int i = 0; i < this->plt_call_stubs_.size(); ++i)
+ {
+ plt_iter cs = sorted[i];
+ char add[10];
+ add[0] = 0;
+ if (cs->first.addend_ != 0)
+ sprintf(add, "+%x", static_cast<uint32_t>(cs->first.addend_));
+ char obj[10];
+ obj[0] = 0;
+ if (cs->first.object_)
+ {
+ const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+ <const Powerpc_relobj<size, big_endian>*>(cs->first.object_);
+ sprintf(obj, "%x:", ppcobj->uniq());
+ }
+ char localname[9];
+ const char *symname;
+ if (cs->first.sym_ == NULL)
+ {
+ sprintf(localname, "%x", cs->first.locsym_);
+ symname = localname;
+ }
+ else if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ symname = this->targ_->tls_get_addr_opt()->name();
+ else
+ symname = cs->first.sym_->name();
+ char* name = new char[8 + 10 + strlen(obj) + strlen(symname) + strlen(add) + 1];
+ sprintf(name, "%08x.plt_call.%s%s%s", this->uniq_, obj, symname, add);
+ Address value
+ = this->stub_address() - this->address() + cs->second.off_;
+ unsigned int stub_size = this->plt_call_align(this->plt_call_size(cs));
+ this->targ_->define_local(symtab, name, this, value, stub_size);
+ }
+ }
+
+ typedef typename Branch_stub_entries::const_iterator branch_iter;
+ for (branch_iter bs = this->long_branch_stubs_.begin();
+ bs != this->long_branch_stubs_.end();
+ ++bs)
+ {
+ if (bs->first.save_res_)
+ continue;
+
+ char* name = new char[8 + 13 + 16 + 1];
+ sprintf(name, "%08x.long_branch.%llx", this->uniq_,
+ static_cast<unsigned long long>(bs->first.dest_));
+ Address value = (this->stub_address() - this->address()
+ + this->plt_size_ + bs->second);
+ unsigned int stub_size = this->branch_stub_size(bs);
+ this->targ_->define_local(symtab, name, this, value, stub_size);
+ }
+}
+
// Write out plt and long branch stub code.
template<int size, bool big_endian>
Address to
= this->targ_->glink_section()->address() + glinkoff;
Address from
- = (this->stub_address() + cs->second + 24
+ = (this->stub_address() + cs->second.off_ + 20
+ + 4 * cs->second.r2save_
+ 4 * (ha(off) != 0)
+ 4 * (ha(off + 8 + 8 * static_chain) != ha(off))
+ 4 * static_chain);
use_fake_dep = cmp_branch_off + (1 << 25) >= (1 << 26);
}
- p = oview + cs->second;
- if (ha(off) != 0)
+ p = oview + cs->second.off_;
+ const Symbol* gsym = cs->first.sym_;
+ if (this->targ_->is_tls_get_addr_opt(gsym))
{
- write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
+ write_insn<big_endian>(p, ld_11_3 + 0);
p += 4;
- write_insn<big_endian>(p, addis_11_2 + ha(off));
+ write_insn<big_endian>(p, ld_12_3 + 8);
p += 4;
- write_insn<big_endian>(p, ld_12_11 + l(off));
+ write_insn<big_endian>(p, mr_0_3);
p += 4;
+ write_insn<big_endian>(p, cmpdi_11_0);
+ p += 4;
+ write_insn<big_endian>(p, add_3_12_13);
+ p += 4;
+ write_insn<big_endian>(p, beqlr);
+ p += 4;
+ write_insn<big_endian>(p, mr_3_0);
+ p += 4;
+ if (!cs->second.localentry0_)
+ {
+ write_insn<big_endian>(p, mflr_11);
+ p += 4;
+ write_insn<big_endian>(p, (std_11_1
+ + this->targ_->stk_linker()));
+ p += 4;
+ }
+ use_fake_dep = thread_safe;
+ }
+ if (ha(off) != 0)
+ {
+ if (cs->second.r2save_)
+ {
+ write_insn<big_endian>(p,
+ std_2_1 + this->targ_->stk_toc());
+ p += 4;
+ }
+ if (plt_load_toc)
+ {
+ write_insn<big_endian>(p, addis_11_2 + ha(off));
+ p += 4;
+ write_insn<big_endian>(p, ld_12_11 + l(off));
+ p += 4;
+ }
+ else
+ {
+ write_insn<big_endian>(p, addis_12_2 + ha(off));
+ p += 4;
+ write_insn<big_endian>(p, ld_12_12 + l(off));
+ p += 4;
+ }
if (plt_load_toc
&& ha(off + 8 + 8 * static_chain) != ha(off))
{
}
else
{
- write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
- p += 4;
+ if (cs->second.r2save_)
+ {
+ write_insn<big_endian>(p,
+ std_2_1 + this->targ_->stk_toc());
+ p += 4;
+ }
write_insn<big_endian>(p, ld_12_2 + l(off));
p += 4;
if (plt_load_toc
p += 4;
}
}
- if (thread_safe && !use_fake_dep)
+ if (!cs->second.localentry0_
+ && this->targ_->is_tls_get_addr_opt(gsym))
+ {
+ write_insn<big_endian>(p, bctrl);
+ p += 4;
+ write_insn<big_endian>(p, ld_2_1 + this->targ_->stk_toc());
+ p += 4;
+ write_insn<big_endian>(p, ld_11_1 + this->targ_->stk_linker());
+ p += 4;
+ write_insn<big_endian>(p, mtlr_11);
+ p += 4;
+ write_insn<big_endian>(p, blr);
+ }
+ else if (thread_safe && !use_fake_dep)
{
write_insn<big_endian>(p, cmpldi_2_0);
p += 4;
bs != this->long_branch_stubs_.end();
++bs)
{
+ if (bs->first.save_res_)
+ continue;
p = oview + this->plt_size_ + bs->second;
Address loc = this->stub_address() + this->plt_size_ + bs->second;
Address delta = bs->first.dest_ - loc;
}
else
{
- write_insn<big_endian>(p, addis_11_2 + ha(brltoff)), p += 4;
- write_insn<big_endian>(p, ld_12_11 + l(brltoff)), p += 4;
+ write_insn<big_endian>(p, addis_12_2 + ha(brltoff)), p += 4;
+ write_insn<big_endian>(p, ld_12_12 + l(brltoff)), p += 4;
}
write_insn<big_endian>(p, mtctr_12), p += 4;
write_insn<big_endian>(p, bctr);
else
plt_addr += plt_base;
- p = oview + cs->second;
+ p = oview + cs->second.off_;
+ const Symbol* gsym = cs->first.sym_;
+ if (this->targ_->is_tls_get_addr_opt(gsym))
+ {
+ write_insn<big_endian>(p, lwz_11_3 + 0);
+ p += 4;
+ write_insn<big_endian>(p, lwz_12_3 + 4);
+ p += 4;
+ write_insn<big_endian>(p, mr_0_3);
+ p += 4;
+ write_insn<big_endian>(p, cmpwi_11_0);
+ p += 4;
+ write_insn<big_endian>(p, add_3_12_2);
+ p += 4;
+ write_insn<big_endian>(p, beqlr);
+ p += 4;
+ write_insn<big_endian>(p, mr_3_0);
+ p += 4;
+ write_insn<big_endian>(p, nop);
+ p += 4;
+ }
if (parameters->options().output_is_position_independent())
{
Address got_addr;
bs != this->long_branch_stubs_.end();
++bs)
{
+ if (bs->first.save_res_)
+ continue;
p = oview + this->plt_size_ + bs->second;
Address loc = this->stub_address() + this->plt_size_ + bs->second;
Address delta = bs->first.dest_ - loc;
}
}
}
+ if (this->need_save_res_)
+ {
+ p = oview + this->plt_size_ + this->branch_size_;
+ memcpy (p, this->targ_->savres_section()->contents(),
+ this->targ_->savres_section()->data_size());
+ }
}
// Write out .glink.
write_insn<big_endian>(p, mflr_0), p += 4;
write_insn<big_endian>(p, bcl_20_31), p += 4;
write_insn<big_endian>(p, mflr_11), p += 4;
+ write_insn<big_endian>(p, std_2_1 + 24), p += 4;
write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4;
write_insn<big_endian>(p, mtlr_0), p += 4;
write_insn<big_endian>(p, sub_12_12_11), p += 4;
}
else
{
- write_insn<big_endian>(p, lis_0_0 + hi(indx)), p += 4;
+ write_insn<big_endian>(p, lis_0 + hi(indx)), p += 4;
write_insn<big_endian>(p, ori_0_0_0 + l(indx)), p += 4;
}
}
public:
Output_data_save_res(Symbol_table* symtab);
+ const unsigned char*
+ contents() const
+ {
+ return contents_;
+ }
+
protected:
// Write to a map file.
void
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
- // Absolute in GOT.
- ref = Symbol::ABSOLUTE_REF;
+ ref = Symbol::RELATIVE_REF;
break;
case elfcpp::R_POWERPC_GOT_TPREL16:
return false;
}
+// Return TRUE iff INSN is one we expect on a _LO variety toc/got
+// reloc.
+
+static bool
+ok_lo_toc_insn(uint32_t insn, unsigned int r_type)
+{
+ return ((insn & (0x3f << 26)) == 12u << 26 /* addic */
+ || (insn & (0x3f << 26)) == 14u << 26 /* addi */
+ || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
+ || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
+ || (insn & (0x3f << 26)) == 36u << 26 /* stw */
+ || (insn & (0x3f << 26)) == 38u << 26 /* stb */
+ || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
+ || (insn & (0x3f << 26)) == 42u << 26 /* lha */
+ || (insn & (0x3f << 26)) == 44u << 26 /* sth */
+ || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
+ || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
+ || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
+ || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
+ || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
+ || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
+ || (insn & (0x3f << 26)) == 56u << 26 /* lq,lfq */
+ || ((insn & (0x3f << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */
+ /* Exclude lfqu by testing reloc. If relocs are ever
+ defined for the reduced D field in psq_lu then those
+ will need testing too. */
+ && r_type != elfcpp::R_PPC64_TOC16_LO
+ && r_type != elfcpp::R_POWERPC_GOT16_LO)
+ || ((insn & (0x3f << 26)) == 58u << 26 /* ld,lwa */
+ && (insn & 1) == 0)
+ || (insn & (0x3f << 26)) == 60u << 26 /* stfq */
+ || ((insn & (0x3f << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */
+ /* Exclude stfqu. psq_stu as above for psq_lu. */
+ && r_type != elfcpp::R_PPC64_TOC16_LO
+ && r_type != elfcpp::R_POWERPC_GOT16_LO)
+ || ((insn & (0x3f << 26)) == 62u << 26 /* std,stq */
+ && (insn & 1) == 0));
+}
+
// Scan a relocation for a local symbol.
template<int size, bool big_endian>
const elfcpp::Sym<size, big_endian>& lsym,
bool is_discarded)
{
- this->maybe_skip_tls_get_addr_call(r_type, NULL);
+ this->maybe_skip_tls_get_addr_call(target, r_type, NULL);
if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
|| (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
case elfcpp::R_POWERPC_NONE:
case elfcpp::R_POWERPC_GNU_VTINHERIT:
case elfcpp::R_POWERPC_GNU_VTENTRY:
- case elfcpp::R_PPC64_TOCSAVE:
case elfcpp::R_POWERPC_TLS:
+ case elfcpp::R_PPC64_ENTRY:
break;
case elfcpp::R_PPC64_TOC:
{
Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
is_ifunc);
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
if ((size == 32 && r_type == elfcpp::R_POWERPC_ADDR32)
|| (size == 64 && r_type == elfcpp::R_PPC64_ADDR64))
{
- unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
: elfcpp::R_POWERPC_RELATIVE);
rela_dyn->add_local_relative(object, r_sym, dynrel,
reloc.get_r_offset(),
reloc.get_r_addend(), false);
}
- else
+ else if (lsym.get_st_type() != elfcpp::STT_SECTION)
{
check_non_pic(object, r_type);
- unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
rela_dyn->add_local(object, r_sym, r_type, output_section,
data_shndx, reloc.get_r_offset(),
reloc.get_r_addend());
}
+ else
+ {
+ gold_assert(lsym.get_st_value() == 0);
+ unsigned int shndx = lsym.get_st_shndx();
+ bool is_ordinary;
+ shndx = object->adjust_sym_shndx(r_sym, shndx,
+ &is_ordinary);
+ if (!is_ordinary)
+ object->error(_("section symbol %u has bad shndx %u"),
+ r_sym, shndx);
+ else
+ rela_dyn->add_local_section(object, shndx, r_type,
+ output_section, data_shndx,
+ reloc.get_r_offset());
+ }
}
break;
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
if (!is_ifunc)
- target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
- r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
- reloc.get_r_addend());
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+ target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+ r_type, r_sym, reloc.get_r_addend());
+ }
+ break;
+
+ case elfcpp::R_PPC64_TOCSAVE:
+ // R_PPC64_TOCSAVE follows a call instruction to indicate the
+ // caller has already saved r2 and thus a plt call stub need not
+ // save r2.
+ if (size == 64
+ && target->mark_pltcall(ppc_object, data_shndx,
+ reloc.get_r_offset() - 4, symtab))
+ {
+ 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(_("tocsave symbol %u has bad shndx %u"),
+ r_sym, shndx);
+ else
+ target->add_tocsave(ppc_object, shndx,
+ lsym.get_st_value() + reloc.get_r_addend());
+ }
break;
case elfcpp::R_PPC64_REL64:
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
+ case elfcpp::R_POWERPC_REL16DX_HA:
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_SECTOFF_HI:
if (!parameters->options().output_is_position_independent())
{
- if ((size == 32 && is_ifunc)
- || (size == 64 && target->abiversion() >= 2))
+ if (is_ifunc
+ && (size == 32 || target->abiversion() >= 2))
got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD);
else
got->add_local(object, r_sym, GOT_TYPE_STANDARD);
break;
}
+ if (size == 64
+ && parameters->options().toc_optimize())
+ {
+ if (data_shndx == ppc_object->toc_shndx())
+ {
+ bool ok = true;
+ if (r_type != elfcpp::R_PPC64_ADDR64
+ || (is_ifunc && target->abiversion() < 2))
+ ok = false;
+ else if (parameters->options().output_is_position_independent())
+ {
+ if (is_ifunc)
+ ok = false;
+ else
+ {
+ unsigned int shndx = lsym.get_st_shndx();
+ if (shndx >= elfcpp::SHN_LORESERVE
+ && shndx != elfcpp::SHN_XINDEX)
+ ok = false;
+ }
+ }
+ if (!ok)
+ ppc_object->set_no_toc_opt(reloc.get_r_offset());
+ }
+
+ enum {no_check, check_lo, check_ha} insn_check;
+ switch (r_type)
+ {
+ default:
+ insn_check = no_check;
+ break;
+
+ case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
+ case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
+ case elfcpp::R_POWERPC_GOT_TPREL16_HA:
+ case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
+ case elfcpp::R_POWERPC_GOT16_HA:
+ case elfcpp::R_PPC64_TOC16_HA:
+ insn_check = check_ha;
+ break;
+
+ case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
+ case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
+ case elfcpp::R_POWERPC_GOT_TPREL16_LO:
+ case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
+ case elfcpp::R_POWERPC_GOT16_LO:
+ case elfcpp::R_PPC64_GOT16_LO_DS:
+ case elfcpp::R_PPC64_TOC16_LO:
+ case elfcpp::R_PPC64_TOC16_LO_DS:
+ insn_check = check_lo;
+ break;
+ }
+
+ section_size_type slen;
+ const unsigned char* view = NULL;
+ if (insn_check != no_check)
+ {
+ view = ppc_object->section_contents(data_shndx, &slen, false);
+ section_size_type off =
+ convert_to_section_size_type(reloc.get_r_offset()) & -4;
+ if (off < slen)
+ {
+ uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+ if (insn_check == check_lo
+ ? !ok_lo_toc_insn(insn, r_type)
+ : ((insn & ((0x3f << 26) | 0x1f << 16))
+ != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
+ {
+ ppc_object->set_no_toc_opt();
+ gold_warning(_("%s: toc optimization is not supported "
+ "for %#08x instruction"),
+ ppc_object->name().c_str(), insn);
+ }
+ }
+ }
+
+ switch (r_type)
+ {
+ default:
+ break;
+ case elfcpp::R_PPC64_TOC16:
+ case elfcpp::R_PPC64_TOC16_LO:
+ case elfcpp::R_PPC64_TOC16_HI:
+ case elfcpp::R_PPC64_TOC16_HA:
+ case elfcpp::R_PPC64_TOC16_DS:
+ case elfcpp::R_PPC64_TOC16_LO_DS:
+ unsigned int shndx = lsym.get_st_shndx();
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+ bool is_ordinary;
+ shndx = ppc_object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
+ if (is_ordinary && shndx == ppc_object->toc_shndx())
+ {
+ Address dst_off = lsym.get_st_value() + reloc.get_r_addend();
+ if (dst_off < ppc_object->section_size(shndx))
+ {
+ bool ok = false;
+ if (r_type == elfcpp::R_PPC64_TOC16_HA)
+ ok = true;
+ else if (r_type == elfcpp::R_PPC64_TOC16_LO_DS)
+ {
+ // Need to check that the insn is a ld
+ if (!view)
+ view = ppc_object->section_contents(data_shndx,
+ &slen,
+ false);
+ section_size_type off =
+ (convert_to_section_size_type(reloc.get_r_offset())
+ + (big_endian ? -2 : 3));
+ if (off < slen
+ && (view[off] & (0x3f << 2)) == 58u << 2)
+ ok = true;
+ }
+ if (!ok)
+ ppc_object->set_no_toc_opt(dst_off);
+ }
+ }
+ break;
+ }
+ }
+
+ if (size == 32)
+ {
+ switch (r_type)
+ {
+ case elfcpp::R_POWERPC_REL32:
+ if (ppc_object->got2_shndx() != 0
+ && parameters->options().output_is_position_independent())
+ {
+ unsigned int shndx = lsym.get_st_shndx();
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+ bool is_ordinary;
+ shndx = ppc_object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
+ if (is_ordinary && shndx == ppc_object->got2_shndx()
+ && (ppc_object->section_flags(data_shndx)
+ & elfcpp::SHF_EXECINSTR) != 0)
+ gold_error(_("%s: unsupported -mbss-plt code"),
+ ppc_object->name().c_str());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
switch (r_type)
{
case elfcpp::R_POWERPC_GOT_TLSLD16:
unsigned int r_type,
Symbol* gsym)
{
- if (this->maybe_skip_tls_get_addr_call(r_type, gsym) == Track_tls::SKIP)
+ if (this->maybe_skip_tls_get_addr_call(target, r_type, gsym)
+ == Track_tls::SKIP)
return;
+ if (target->replace_tls_get_addr(gsym))
+ // Change a __tls_get_addr reference to __tls_get_addr_opt
+ // so dynamic relocs are emitted against the latter symbol.
+ gsym = target->tls_get_addr_opt();
+
if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
|| (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
{
bool pushed_ifunc = false;
if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, object, r_type, true))
{
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
- r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
- reloc.get_r_addend());
+ r_type, r_sym, reloc.get_r_addend());
target->make_plt_entry(symtab, layout, gsym);
pushed_ifunc = true;
}
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_POWERPC_TLS:
+ case elfcpp::R_PPC64_ENTRY:
break;
case elfcpp::R_PPC64_TOC:
ppc_object->set_opd_discard(reloc.get_r_offset());
break;
}
- // Fall thru
+ // Fall through.
case elfcpp::R_PPC64_UADDR64:
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_UADDR32:
}
if (!is_ifunc || (!pushed_ifunc && need_ifunc_plt))
{
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
target->push_branch(ppc_object, data_shndx,
- reloc.get_r_offset(), r_type,
- elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+ reloc.get_r_offset(), r_type, r_sym,
reloc.get_r_addend());
target->make_plt_entry(symtab, layout, gsym);
}
object, data_shndx,
reloc.get_r_offset(),
reloc.get_r_addend());
+
+ if (size == 64
+ && parameters->options().toc_optimize()
+ && data_shndx == ppc_object->toc_shndx())
+ ppc_object->set_no_toc_opt(reloc.get_r_offset());
}
}
}
case elfcpp::R_POWERPC_REL24:
if (!is_ifunc)
{
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
- r_type,
- elfcpp::elf_r_sym<size>(reloc.get_r_info()),
- reloc.get_r_addend());
+ r_type, r_sym, reloc.get_r_addend());
if (gsym->needs_plt_entry()
|| (!gsym->final_value_is_known()
&& (gsym->is_undefined()
|| gsym->is_preemptible())))
target->make_plt_entry(symtab, layout, gsym);
}
- // Fall thru
+ // Fall through.
case elfcpp::R_PPC64_REL64:
case elfcpp::R_POWERPC_REL32:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
if (!is_ifunc)
- target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
- r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
- reloc.get_r_addend());
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+ target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+ r_type, r_sym, reloc.get_r_addend());
+ }
+ break;
+
+ case elfcpp::R_PPC64_TOCSAVE:
+ // R_PPC64_TOCSAVE follows a call instruction to indicate the
+ // caller has already saved r2 and thus a plt call stub need not
+ // save r2.
+ if (size == 64
+ && target->mark_pltcall(ppc_object, data_shndx,
+ reloc.get_r_offset() - 4, symtab))
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+ bool is_ordinary;
+ unsigned int shndx = gsym->shndx(&is_ordinary);
+ if (!is_ordinary)
+ object->error(_("tocsave symbol %u has bad shndx %u"),
+ r_sym, shndx);
+ else
+ {
+ Sized_symbol<size>* sym = symtab->get_sized_symbol<size>(gsym);
+ target->add_tocsave(ppc_object, shndx,
+ sym->value() + reloc.get_r_addend());
+ }
+ }
break;
case elfcpp::R_POWERPC_REL16:
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
+ case elfcpp::R_POWERPC_REL16DX_HA:
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_SECTOFF_HI:
got = target->got_section(symtab, layout);
if (gsym->final_value_is_known())
{
- if ((size == 32 && is_ifunc)
- || (size == 64 && target->abiversion() >= 2))
+ if (is_ifunc
+ && (size == 32 || target->abiversion() >= 2))
got->add_global_plt(gsym, GOT_TYPE_STANDARD);
else
got->add_global(gsym, GOT_TYPE_STANDARD);
}
break;
- default:
- unsupported_reloc_global(object, r_type, gsym);
- break;
+ default:
+ unsupported_reloc_global(object, r_type, gsym);
+ break;
+ }
+
+ if (size == 64
+ && parameters->options().toc_optimize())
+ {
+ if (data_shndx == ppc_object->toc_shndx())
+ {
+ bool ok = true;
+ if (r_type != elfcpp::R_PPC64_ADDR64
+ || (is_ifunc && target->abiversion() < 2))
+ ok = false;
+ else if (parameters->options().output_is_position_independent()
+ && (is_ifunc || gsym->is_absolute() || gsym->is_undefined()))
+ ok = false;
+ if (!ok)
+ ppc_object->set_no_toc_opt(reloc.get_r_offset());
+ }
+
+ enum {no_check, check_lo, check_ha} insn_check;
+ switch (r_type)
+ {
+ default:
+ insn_check = no_check;
+ break;
+
+ case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
+ case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
+ case elfcpp::R_POWERPC_GOT_TPREL16_HA:
+ case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
+ case elfcpp::R_POWERPC_GOT16_HA:
+ case elfcpp::R_PPC64_TOC16_HA:
+ insn_check = check_ha;
+ break;
+
+ case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
+ case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
+ case elfcpp::R_POWERPC_GOT_TPREL16_LO:
+ case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
+ case elfcpp::R_POWERPC_GOT16_LO:
+ case elfcpp::R_PPC64_GOT16_LO_DS:
+ case elfcpp::R_PPC64_TOC16_LO:
+ case elfcpp::R_PPC64_TOC16_LO_DS:
+ insn_check = check_lo;
+ break;
+ }
+
+ section_size_type slen;
+ const unsigned char* view = NULL;
+ if (insn_check != no_check)
+ {
+ view = ppc_object->section_contents(data_shndx, &slen, false);
+ section_size_type off =
+ convert_to_section_size_type(reloc.get_r_offset()) & -4;
+ if (off < slen)
+ {
+ uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+ if (insn_check == check_lo
+ ? !ok_lo_toc_insn(insn, r_type)
+ : ((insn & ((0x3f << 26) | 0x1f << 16))
+ != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
+ {
+ ppc_object->set_no_toc_opt();
+ gold_warning(_("%s: toc optimization is not supported "
+ "for %#08x instruction"),
+ ppc_object->name().c_str(), insn);
+ }
+ }
+ }
+
+ switch (r_type)
+ {
+ default:
+ break;
+ case elfcpp::R_PPC64_TOC16:
+ case elfcpp::R_PPC64_TOC16_LO:
+ case elfcpp::R_PPC64_TOC16_HI:
+ case elfcpp::R_PPC64_TOC16_HA:
+ case elfcpp::R_PPC64_TOC16_DS:
+ case elfcpp::R_PPC64_TOC16_LO_DS:
+ if (gsym->source() == Symbol::FROM_OBJECT
+ && !gsym->object()->is_dynamic())
+ {
+ Powerpc_relobj<size, big_endian>* sym_object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object());
+ bool is_ordinary;
+ unsigned int shndx = gsym->shndx(&is_ordinary);
+ if (shndx == sym_object->toc_shndx())
+ {
+ Sized_symbol<size>* sym = symtab->get_sized_symbol<size>(gsym);
+ Address dst_off = sym->value() + reloc.get_r_addend();
+ if (dst_off < sym_object->section_size(shndx))
+ {
+ bool ok = false;
+ if (r_type == elfcpp::R_PPC64_TOC16_HA)
+ ok = true;
+ else if (r_type == elfcpp::R_PPC64_TOC16_LO_DS)
+ {
+ // Need to check that the insn is a ld
+ if (!view)
+ view = ppc_object->section_contents(data_shndx,
+ &slen,
+ false);
+ section_size_type off =
+ (convert_to_section_size_type(reloc.get_r_offset())
+ + (big_endian ? -2 : 3));
+ if (off < slen
+ && (view[off] & (0x3f << 2)) == (58u << 2))
+ ok = true;
+ }
+ if (!ok)
+ sym_object->set_no_toc_opt(dst_off);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ if (size == 32)
+ {
+ switch (r_type)
+ {
+ case elfcpp::R_PPC_LOCAL24PC:
+ if (strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
+ gold_error(_("%s: unsupported -mbss-plt code"),
+ ppc_object->name().c_str());
+ break;
+ default:
+ break;
+ }
}
switch (r_type)
const unsigned char* plocal_symbols)
{
typedef Target_powerpc<size, big_endian> Powerpc;
- typedef typename Target_powerpc<size, big_endian>::Scan Scan;
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+
Powerpc_relobj<size, big_endian>* ppc_object
= static_cast<Powerpc_relobj<size, big_endian>*>(object);
if (size == 64)
typename Powerpc_relobj<size, big_endian>::Section_refs::iterator s;
for (s = p->second.begin(); s != p->second.end(); ++s)
{
- Object* src_obj = s->first;
+ Relobj* src_obj = s->first;
unsigned int src_indx = s->second;
symtab->gc()->add_reference(src_obj, src_indx,
ppc_object, dst_indx);
return;
}
- gold::gc_process_relocs<size, big_endian, Powerpc, elfcpp::SHT_RELA, Scan,
- typename Target_powerpc::Relocatable_size_for_reloc>(
+ gold::gc_process_relocs<size, big_endian, Powerpc, Scan, Classify_reloc>(
symtab,
layout,
this,
void
Target_powerpc<size, big_endian>::do_gc_add_reference(
Symbol_table* symtab,
- Object* src_obj,
+ Relobj* src_obj,
unsigned int src_shndx,
- Object* dst_obj,
+ Relobj* dst_obj,
unsigned int dst_shndx,
Address dst_off) const
{
if (ppc_object->opd_valid())
{
unsigned int dst_indx = ppc_object->get_opd_ent(dst_off);
- symtab->gc()->worklist().push(Section_id(ppc_object, dst_indx));
+ symtab->gc()->worklist().push_back(Section_id(ppc_object,
+ dst_indx));
}
else
ppc_object->add_gc_mark(dst_off);
}
}
+// FNOFFSET in section SHNDX in OBJECT is the start of a function
+// compiled with -fsplit-stack. The function calls non-split-stack
+// code. Change the function to ensure it has enough stack space to
+// call some random function.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_calls_non_split(
+ Relobj* object,
+ unsigned int shndx,
+ section_offset_type fnoffset,
+ section_size_type fnsize,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ unsigned char* view,
+ section_size_type view_size,
+ std::string* from,
+ std::string* to) const
+{
+ // 32-bit not supported.
+ if (size == 32)
+ {
+ // warn
+ Target::do_calls_non_split(object, shndx, fnoffset, fnsize,
+ prelocs, reloc_count, view, view_size,
+ from, to);
+ return;
+ }
+
+ // The function always starts with
+ // ld %r0,-0x7000-64(%r13) # tcbhead_t.__private_ss
+ // addis %r12,%r1,-allocate@ha
+ // addi %r12,%r12,-allocate@l
+ // cmpld %r12,%r0
+ // but note that the addis or addi may be replaced with a nop
+
+ unsigned char *entry = view + fnoffset;
+ uint32_t insn = elfcpp::Swap<32, big_endian>::readval(entry);
+
+ if ((insn & 0xffff0000) == addis_2_12)
+ {
+ /* Skip ELFv2 global entry code. */
+ entry += 8;
+ insn = elfcpp::Swap<32, big_endian>::readval(entry);
+ }
+
+ unsigned char *pinsn = entry;
+ bool ok = false;
+ const uint32_t ld_private_ss = 0xe80d8fc0;
+ if (insn == ld_private_ss)
+ {
+ int32_t allocate = 0;
+ while (1)
+ {
+ pinsn += 4;
+ insn = elfcpp::Swap<32, big_endian>::readval(pinsn);
+ if ((insn & 0xffff0000) == addis_12_1)
+ allocate += (insn & 0xffff) << 16;
+ else if ((insn & 0xffff0000) == addi_12_1
+ || (insn & 0xffff0000) == addi_12_12)
+ allocate += ((insn & 0xffff) ^ 0x8000) - 0x8000;
+ else if (insn != nop)
+ break;
+ }
+ if (insn == cmpld_7_12_0 && pinsn == entry + 12)
+ {
+ int extra = parameters->options().split_stack_adjust_size();
+ allocate -= extra;
+ if (allocate >= 0 || extra < 0)
+ {
+ object->error(_("split-stack stack size overflow at "
+ "section %u offset %0zx"),
+ shndx, static_cast<size_t>(fnoffset));
+ return;
+ }
+ pinsn = entry + 4;
+ insn = addis_12_1 | (((allocate + 0x8000) >> 16) & 0xffff);
+ if (insn != addis_12_1)
+ {
+ elfcpp::Swap<32, big_endian>::writeval(pinsn, insn);
+ pinsn += 4;
+ insn = addi_12_12 | (allocate & 0xffff);
+ if (insn != addi_12_12)
+ {
+ elfcpp::Swap<32, big_endian>::writeval(pinsn, insn);
+ pinsn += 4;
+ }
+ }
+ else
+ {
+ insn = addi_12_1 | (allocate & 0xffff);
+ elfcpp::Swap<32, big_endian>::writeval(pinsn, insn);
+ pinsn += 4;
+ }
+ if (pinsn != entry + 12)
+ elfcpp::Swap<32, big_endian>::writeval(pinsn, nop);
+
+ ok = true;
+ }
+ }
+
+ if (!ok)
+ {
+ if (!object->has_no_split_stack())
+ object->error(_("failed to match split-stack sequence at "
+ "section %u offset %0zx"),
+ shndx, static_cast<size_t>(fnoffset));
+ }
+}
+
// Scan relocations for a section.
template<int size, bool big_endian>
const unsigned char* plocal_symbols)
{
typedef Target_powerpc<size, big_endian> Powerpc;
- typedef typename Target_powerpc<size, big_endian>::Scan Scan;
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+
+ if (!this->plt_localentry0_init_)
+ {
+ bool plt_localentry0 = false;
+ if (size == 64
+ && this->abiversion() >= 2)
+ {
+ if (parameters->options().user_set_plt_localentry())
+ plt_localentry0 = parameters->options().plt_localentry();
+ if (plt_localentry0
+ && symtab->lookup("GLIBC_2.26", NULL) == NULL)
+ gold_warning(_("--plt-localentry is especially dangerous without "
+ "ld.so support to detect ABI violations"));
+ }
+ this->plt_localentry0_ = plt_localentry0;
+ this->plt_localentry0_init_ = true;
+ }
if (sh_type == elfcpp::SHT_REL)
{
return;
}
- gold::scan_relocs<size, big_endian, Powerpc, elfcpp::SHT_RELA, Scan>(
+ gold::scan_relocs<size, big_endian, Powerpc, Scan, Classify_reloc>(
symtab,
layout,
this,
unsigned int shndx = sym->shndx(&is_ordinary);
if (shndx == symobj->opd_shndx()
&& symobj->get_opd_discard(sym->value()))
- sym->set_symtab_index(-1U);
+ {
+ sym->set_undefined();
+ sym->set_visibility(elfcpp::STV_DEFAULT);
+ sym->set_is_defined_in_discarded_section();
+ sym->set_symtab_index(-1U);
+ }
}
};
{
if (size == 64)
{
- Output_data_save_res<64, big_endian>* savres
- = new Output_data_save_res<64, big_endian>(symtab);
+ Output_data_save_res<size, big_endian>* savres
+ = new Output_data_save_res<size, big_endian>(symtab);
+ this->savres_section_ = savres;
layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
savres, ORDER_TEXT, false);
odyn->add_section_plus_offset(elfcpp::DT_PPC_GOT,
this->got_, this->got_->g_o_t());
}
+ if (this->has_tls_get_addr_opt_)
+ odyn->add_constant(elfcpp::DT_PPC_OPT, elfcpp::PPC_OPT_TLS);
}
else
{
(this->glink_->pltresolve_size
- 32));
}
+ if (this->has_localentry0_ || this->has_tls_get_addr_opt_)
+ odyn->add_constant(elfcpp::DT_PPC64_OPT,
+ ((this->has_localentry0_
+ ? elfcpp::PPC64_OPT_LOCALENTRY : 0)
+ | (this->has_tls_get_addr_opt_
+ ? elfcpp::PPC64_OPT_TLS : 0)));
}
}
this->copy_relocs_.emit(this->rela_dyn_section(layout));
}
-// Return TRUE iff INSN is one we expect on a _LO variety toc/got
-// reloc.
+// Emit any saved relocs, and mark toc entries using any of these
+// relocs as not optimizable.
-static bool
-ok_lo_toc_insn(uint32_t insn)
+template<int sh_type, int size, bool big_endian>
+void
+Powerpc_copy_relocs<sh_type, size, big_endian>::emit(
+ Output_data_reloc<sh_type, true, size, big_endian>* reloc_section)
{
- return ((insn & (0x3f << 26)) == 14u << 26 /* addi */
- || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
- || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
- || (insn & (0x3f << 26)) == 36u << 26 /* stw */
- || (insn & (0x3f << 26)) == 38u << 26 /* stb */
- || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
- || (insn & (0x3f << 26)) == 42u << 26 /* lha */
- || (insn & (0x3f << 26)) == 44u << 26 /* sth */
- || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
- || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
- || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
- || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
- || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
- || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
- || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
- && (insn & 3) != 1)
- || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
- && ((insn & 3) == 0 || (insn & 3) == 3))
- || (insn & (0x3f << 26)) == 12u << 26 /* addic */);
+ if (size == 64
+ && parameters->options().toc_optimize())
+ {
+ for (typename Copy_relocs<sh_type, size, big_endian>::
+ Copy_reloc_entries::iterator p = this->entries_.begin();
+ p != this->entries_.end();
+ ++p)
+ {
+ typename Copy_relocs<sh_type, size, big_endian>::Copy_reloc_entry&
+ entry = *p;
+
+ // If the symbol is no longer defined in a dynamic object,
+ // then we emitted a COPY relocation. If it is still
+ // dynamic then we'll need dynamic relocations and thus
+ // can't optimize toc entries.
+ if (entry.sym_->is_from_dynobj())
+ {
+ Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(entry.relobj_);
+ if (entry.shndx_ == ppc_object->toc_shndx())
+ ppc_object->set_no_toc_opt(entry.address_);
+ }
+ }
+ }
+
+ Copy_relocs<sh_type, size, big_endian>::emit(reloc_section);
}
// Return the value to use for a branch relocation.
template<int size, bool big_endian>
-typename Target_powerpc<size, big_endian>::Address
+bool
Target_powerpc<size, big_endian>::symval_for_branch(
const Symbol_table* symtab,
- Address value,
const Sized_symbol<size>* gsym,
Powerpc_relobj<size, big_endian>* object,
+ Address *value,
unsigned int *dest_shndx)
{
if (size == 32 || this->abiversion() >= 2)
// descriptor, use the function descriptor code entry address
Powerpc_relobj<size, big_endian>* symobj = object;
if (gsym != NULL
- && gsym->source() != Symbol::FROM_OBJECT)
- return value;
+ && (gsym->source() != Symbol::FROM_OBJECT
+ || gsym->object()->is_dynamic()))
+ return true;
if (gsym != NULL)
symobj = static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object());
unsigned int shndx = symobj->opd_shndx();
if (shndx == 0)
- return value;
+ return true;
Address opd_addr = symobj->get_output_section_offset(shndx);
if (opd_addr == invalid_address)
- return value;
+ return true;
opd_addr += symobj->output_section_address(shndx);
- if (value >= opd_addr && value < opd_addr + symobj->section_size(shndx))
+ if (*value >= opd_addr && *value < opd_addr + symobj->section_size(shndx))
{
Address sec_off;
- *dest_shndx = symobj->get_opd_ent(value - opd_addr, &sec_off);
+ *dest_shndx = symobj->get_opd_ent(*value - opd_addr, &sec_off);
if (symtab->is_section_folded(symobj, *dest_shndx))
{
Section_id folded
*dest_shndx = folded.second;
}
Address sec_addr = symobj->get_output_section_offset(*dest_shndx);
- gold_assert(sec_addr != invalid_address);
+ if (sec_addr == invalid_address)
+ return false;
+
sec_addr += symobj->output_section(*dest_shndx)->address();
- value = sec_addr + sec_off;
+ *value = sec_addr + sec_off;
}
- return value;
+ return true;
}
// Perform a relocation.
inline bool
Target_powerpc<size, big_endian>::Relocate::relocate(
const Relocate_info<size, big_endian>* relinfo,
+ unsigned int,
Target_powerpc* target,
Output_section* os,
size_t relnum,
- const elfcpp::Rela<size, big_endian>& rela,
- unsigned int r_type,
+ const unsigned char* preloc,
const Sized_symbol<size>* gsym,
const Symbol_value<size>* psymval,
unsigned char* view,
if (view == NULL)
return true;
- switch (this->maybe_skip_tls_get_addr_call(r_type, gsym))
+ if (target->replace_tls_get_addr(gsym))
+ gsym = static_cast<const Sized_symbol<size>*>(target->tls_get_addr_opt());
+
+ const elfcpp::Rela<size, big_endian> rela(preloc);
+ unsigned int r_type = elfcpp::elf_r_type<size>(rela.get_r_info());
+ switch (this->maybe_skip_tls_get_addr_call(target, r_type, gsym))
{
case Track_tls::NOT_EXPECTED:
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
typedef Powerpc_relocate_functions<size, big_endian> Reloc;
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
+ typedef typename elfcpp::Rela<size, big_endian> Reltype;
+ // Offset from start of insn to d-field reloc.
+ const int d_offset = big_endian ? 2 : 0;
+
Powerpc_relobj<size, big_endian>* const object
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
Address value = 0;
- bool has_plt_value = false;
+ bool has_stub_value = false;
+ bool localentry0 = false;
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
if ((gsym != NULL
? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
&& !parameters->options().output_is_position_independent()
&& !is_branch_reloc(r_type))
{
- unsigned int off = target->glink_section()->find_global_entry(gsym);
- gold_assert(off != (unsigned int)-1);
- value = target->glink_section()->global_entry_address() + off;
+ Address off = target->glink_section()->find_global_entry(gsym);
+ if (off != invalid_address)
+ {
+ value = target->glink_section()->global_entry_address() + off;
+ has_stub_value = true;
+ }
}
else
{
- Stub_table<size, big_endian>* stub_table
- = object->stub_table(relinfo->data_shndx);
+ Stub_table<size, big_endian>* stub_table = NULL;
+ if (target->stub_tables().size() == 1)
+ stub_table = target->stub_tables()[0];
+ if (stub_table == NULL
+ && !(size == 32
+ && gsym != NULL
+ && !parameters->options().output_is_position_independent()
+ && !is_branch_reloc(r_type)))
+ stub_table = object->stub_table(relinfo->data_shndx);
if (stub_table == NULL)
{
- // This is a ref from a data section to an ifunc symbol.
+ // This is a ref from a data section to an ifunc symbol,
+ // or a non-branch reloc for which we always want to use
+ // one set of stubs for resolving function addresses.
if (target->stub_tables().size() != 0)
stub_table = target->stub_tables()[0];
}
- gold_assert(stub_table != NULL);
- Address off;
- if (gsym != NULL)
- off = stub_table->find_plt_call_entry(object, gsym, r_type,
- rela.get_r_addend());
- else
- off = stub_table->find_plt_call_entry(object, r_sym, r_type,
- rela.get_r_addend());
- gold_assert(off != invalid_address);
- value = stub_table->stub_address() + off;
+ if (stub_table != NULL)
+ {
+ const typename Stub_table<size, big_endian>::Plt_stub_ent* ent;
+ if (gsym != NULL)
+ ent = stub_table->find_plt_call_entry(object, gsym, r_type,
+ rela.get_r_addend());
+ else
+ ent = stub_table->find_plt_call_entry(object, r_sym, r_type,
+ rela.get_r_addend());
+ if (ent != NULL)
+ {
+ value = stub_table->stub_address() + ent->off_;
+ const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
+ size_t reloc_count = shdr.get_sh_size() / reloc_size;
+ if (size == 64
+ && ent->r2save_
+ && relnum + 1 < reloc_count)
+ {
+ Reltype next_rela(preloc + reloc_size);
+ if (elfcpp::elf_r_type<size>(next_rela.get_r_info())
+ == elfcpp::R_PPC64_TOCSAVE
+ && next_rela.get_r_offset() == rela.get_r_offset() + 4)
+ value += 4;
+ }
+ localentry0 = ent->localentry0_;
+ has_stub_value = true;
+ }
+ }
}
- has_plt_value = true;
+ // We don't care too much about bogus debug references to
+ // non-local functions, but otherwise there had better be a plt
+ // call stub or global entry stub as appropriate.
+ gold_assert(has_stub_value || !(os->flags() & elfcpp::SHF_ALLOC));
}
if (r_type == elfcpp::R_POWERPC_GOT16
}
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));
value = object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
}
else if (gsym != NULL
&& (r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC_PLTREL24)
- && has_plt_value)
+ && has_stub_value)
{
if (size == 64)
{
typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
- bool can_plt_call = false;
- if (rela.get_r_offset() + 8 <= view_size)
+ bool can_plt_call = localentry0 || target->is_tls_get_addr_opt(gsym);
+ if (!can_plt_call && rela.get_r_offset() + 8 <= view_size)
{
Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
Valtype insn2 = elfcpp::Swap<32, big_endian>::readval(wv + 1);
if (target->abiversion() < 2)
{
Address addend = rela.get_r_addend();
- Address opdent = psymval->value(object, addend);
- code = target->symval_for_branch(relinfo->symtab,
- opdent, gsym, object,
- &dest_shndx);
+ code = psymval->value(object, addend);
+ target->symval_for_branch(relinfo->symtab, gsym, object,
+ &code, &dest_shndx);
}
bool is_ordinary;
if (dest_shndx == 0)
}
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 (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= (1 << 26) - (1 << 16); // extract rt,ra from addi
if (size == 32)
if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
- Insn insn = addis_3_13;
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn &= (1 << 26) - (1 << 21); // extract rt
if (size == 32)
- insn = addis_3_2;
+ insn |= addis_0_2;
+ else
+ insn |= addis_0_13;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_HA;
value = psymval->value(object, rela.get_r_addend());
}
else
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = nop;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
- Insn insn = addis_3_13;
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn &= (1 << 26) - (1 << 21); // extract rt
if (size == 32)
- insn = addis_3_2;
+ insn |= addis_0_2;
+ else
+ insn |= addis_0_13;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_HA;
value = dtp_offset;
}
else
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = nop;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
}
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_DTPREL));
value = object->local_got_offset(r_sym, GOT_TYPE_DTPREL);
}
}
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_TPREL));
value = object->local_got_offset(r_sym, GOT_TYPE_TPREL);
}
if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO)
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= (1 << 26) - (1 << 21); // extract rt from ld
if (size == 32)
}
else
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = nop;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
Insn insn = addi_3_3;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += 2 * big_endian;
+ view += d_offset;
value = psymval->value(object, rela.get_r_addend());
}
this->skip_next_tls_get_addr_call();
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
this->skip_next_tls_get_addr_call();
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += 2 * big_endian;
+ view += d_offset;
value = dtp_offset;
}
}
gold_assert(insn != 0);
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += 2 * big_endian;
+ view += d_offset;
value = psymval->value(object, rela.get_r_addend());
}
}
- else if (!has_plt_value)
+ else if (!has_stub_value)
{
Address addend = 0;
- unsigned int dest_shndx;
- if (r_type != elfcpp::R_PPC_PLTREL24)
+ if (!(size == 32 && r_type == elfcpp::R_PPC_PLTREL24))
addend = rela.get_r_addend();
value = psymval->value(object, addend);
if (size == 64 && is_branch_reloc(r_type))
value += object->ppc64_local_entry_offset(r_sym);
}
else
- value = target->symval_for_branch(relinfo->symtab, value,
- gsym, object, &dest_shndx);
+ {
+ unsigned int dest_shndx;
+ target->symval_for_branch(relinfo->symtab, gsym, object,
+ &value, &dest_shndx);
+ }
}
- unsigned int max_branch_offset = 0;
- if (r_type == elfcpp::R_POWERPC_REL24
- || r_type == elfcpp::R_PPC_PLTREL24
- || r_type == elfcpp::R_PPC_LOCAL24PC)
- max_branch_offset = 1 << 25;
- else if (r_type == elfcpp::R_POWERPC_REL14
- || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
- || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
- max_branch_offset = 1 << 15;
+ Address max_branch_offset = max_branch_delta(r_type);
if (max_branch_offset != 0
&& value - address + max_branch_offset >= 2 * max_branch_offset)
{
{
Address off = stub_table->find_long_branch_entry(object, value);
if (off != invalid_address)
- value = (stub_table->stub_address() + stub_table->plt_size()
- + off);
+ {
+ value = (stub_table->stub_address() + stub_table->plt_size()
+ + off);
+ has_stub_value = true;
+ }
}
}
}
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
+ case elfcpp::R_POWERPC_REL16DX_HA:
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
if (size != 64)
// R_PPC_TLSGD, R_PPC_TLSLD, R_PPC_EMB_RELST_LO, R_PPC_EMB_RELST_HI
break;
+ // Fall through.
case elfcpp::R_POWERPC_TPREL16:
case elfcpp::R_POWERPC_TPREL16_LO:
case elfcpp::R_POWERPC_TPREL16_HI:
// R_PPC_EMB_NADDR32, R_PPC_EMB_NADDR16, R_PPC_EMB_NADDR16_LO
// R_PPC_EMB_NADDR16_HI, R_PPC_EMB_NADDR16_HA, R_PPC_EMB_SDAI16
break;
+ // Fall through.
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
branch_bit = 1 << 21;
+ // Fall through.
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
{
if (size == 64)
{
- // Multi-instruction sequences that access the TOC can be
- // optimized, eg. addis ra,r2,0; addi rb,ra,x;
- // to nop; addi rb,r2,x;
switch (r_type)
{
default:
break;
+ // Multi-instruction sequences that access the GOT/TOC can
+ // be optimized, eg.
+ // addis ra,r2,x@got@ha; ld rb,x@got@l(ra);
+ // to addis ra,r2,x@toc@ha; addi rb,ra,x@toc@l;
+ // and
+ // addis ra,r2,0; addi rb,ra,x@toc@l;
+ // to nop; addi rb,r2,x@toc;
+ // FIXME: the @got sequence shown above is not yet
+ // optimized. Note that gcc as of 2017-01-07 doesn't use
+ // the ELF @got relocs except for TLS, instead using the
+ // PowerOpen variant of a compiler managed GOT (called TOC).
+ // The PowerOpen TOC sequence equivalent to the first
+ // example is optimized.
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
case elfcpp::R_POWERPC_GOT_TPREL16_HA:
case elfcpp::R_PPC64_TOC16_HA:
if (parameters->options().toc_optimize())
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
- if ((insn & ((0x3f << 26) | 0x1f << 16))
- != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("toc optimization is not supported "
- "for %#08x instruction"), insn);
- else if (value + 0x8000 < 0x10000)
+ if (r_type == elfcpp::R_PPC64_TOC16_HA
+ && object->make_toc_relative(target, &value))
+ {
+ gold_assert((insn & ((0x3f << 26) | 0x1f << 16))
+ == ((15u << 26) | (2 << 16)));
+ }
+ if (((insn & ((0x3f << 26) | 0x1f << 16))
+ == ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
+ && value + 0x8000 < 0x10000)
{
elfcpp::Swap<32, big_endian>::writeval(iview, nop);
return true;
case elfcpp::R_PPC64_TOC16_LO_DS:
if (parameters->options().toc_optimize())
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
- if (!ok_lo_toc_insn(insn))
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("toc optimization is not supported "
- "for %#08x instruction"), insn);
- else if (value + 0x8000 < 0x10000)
+ bool changed = false;
+ if (r_type == elfcpp::R_PPC64_TOC16_LO_DS
+ && object->make_toc_relative(target, &value))
+ {
+ gold_assert ((insn & (0x3f << 26)) == 58u << 26 /* ld */);
+ insn ^= (14u << 26) ^ (58u << 26);
+ r_type = elfcpp::R_PPC64_TOC16_LO;
+ changed = true;
+ }
+ if (ok_lo_toc_insn(insn, r_type)
+ && value + 0x8000 < 0x10000)
{
if ((insn & (0x3f << 26)) == 12u << 26 /* addic */)
{
insn &= ~(0x1f << 16);
insn |= 2 << 16;
}
- elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ changed = true;
+ }
+ if (changed)
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ }
+ break;
+
+ case elfcpp::R_POWERPC_TPREL16_HA:
+ if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ if ((insn & ((0x3f << 26) | 0x1f << 16))
+ != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
+ ;
+ else
+ {
+ elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+ return true;
+ }
+ }
+ break;
+
+ case elfcpp::R_PPC64_TPREL16_LO_DS:
+ if (size == 32)
+ // R_PPC_TLSGD, R_PPC_TLSLD
+ break;
+ // Fall through.
+ case elfcpp::R_POWERPC_TPREL16_LO:
+ if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn &= ~(0x1f << 16);
+ insn |= (size == 32 ? 2 : 13) << 16;
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ }
+ break;
+
+ case elfcpp::R_PPC64_ENTRY:
+ value = (target->got_section()->output_section()->address()
+ + object->toc_base_offset());
+ if (value + 0x80008000 <= 0xffffffff
+ && !parameters->options().output_is_position_independent())
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+ if ((insn1 & ~0xfffc) == ld_2_12
+ && insn2 == add_2_2_12)
+ {
+ insn1 = lis_2 + ha(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+ insn2 = addi_2_2 + l(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+ return true;
+ }
+ }
+ else
+ {
+ value -= address;
+ if (value + 0x80008000 <= 0xffffffff)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+ if ((insn1 & ~0xfffc) == ld_2_12
+ && insn2 == add_2_2_12)
+ {
+ insn1 = addis_2_12 + ha(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+ insn2 = addi_2_2 + l(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+ return true;
+ }
+ }
+ }
+ break;
+
+ case elfcpp::R_POWERPC_REL16_LO:
+ // If we are generating a non-PIC executable, edit
+ // 0: addis 2,12,.TOC.-0b@ha
+ // addi 2,2,.TOC.-0b@l
+ // used by ELFv2 global entry points to set up r2, to
+ // lis 2,.TOC.@ha
+ // addi 2,2,.TOC.@l
+ // if .TOC. is in range. */
+ if (value + address - 4 + 0x80008000 <= 0xffffffff
+ && relnum != 0
+ && preloc != NULL
+ && target->abiversion() >= 2
+ && !parameters->options().output_is_position_independent()
+ && rela.get_r_addend() == d_offset + 4
+ && gsym != NULL
+ && strcmp(gsym->name(), ".TOC.") == 0)
+ {
+ const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ Reltype prev_rela(preloc - reloc_size);
+ if ((prev_rela.get_r_info()
+ == elfcpp::elf_r_info<size>(r_sym,
+ elfcpp::R_POWERPC_REL16_HA))
+ && prev_rela.get_r_offset() + 4 == rela.get_r_offset()
+ && prev_rela.get_r_addend() + 4 == rela.get_r_addend())
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview - 1);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview);
+
+ if ((insn1 & 0xffff0000) == addis_2_12
+ && (insn2 & 0xffff0000) == addi_2_2)
+ {
+ insn1 = lis_2 + ha(value + address - 4);
+ elfcpp::Swap<32, big_endian>::writeval(iview - 1, insn1);
+ insn2 = addi_2_2 + l(value + address - 4);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn2);
+ if (relinfo->rr)
+ {
+ relinfo->rr->set_strategy(relnum - 1,
+ Relocatable_relocs::RELOC_SPECIAL);
+ relinfo->rr->set_strategy(relnum,
+ Relocatable_relocs::RELOC_SPECIAL);
+ }
+ return true;
+ }
}
}
break;
break;
case elfcpp::R_POWERPC_REL32:
+ case elfcpp::R_POWERPC_REL16DX_HA:
if (size == 64)
overflow = Reloc::CHECK_SIGNED;
break;
break;
}
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = 0;
+
if (overflow == Reloc::CHECK_LOW_INSN
|| overflow == Reloc::CHECK_HIGH_INSN)
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
- Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn = elfcpp::Swap<32, big_endian>::readval(iview);
- overflow = Reloc::CHECK_SIGNED;
- if (overflow == Reloc::CHECK_LOW_INSN
- ? ((insn & (0x3f << 26)) == 28u << 26 /* andi */
- || (insn & (0x3f << 26)) == 24u << 26 /* ori */
- || (insn & (0x3f << 26)) == 26u << 26 /* xori */
- || (insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
- : ((insn & (0x3f << 26)) == 29u << 26 /* andis */
- || (insn & (0x3f << 26)) == 25u << 26 /* oris */
- || (insn & (0x3f << 26)) == 27u << 26 /* xoris */))
+ if ((insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
+ overflow = Reloc::CHECK_BITFIELD;
+ else if (overflow == Reloc::CHECK_LOW_INSN
+ ? ((insn & (0x3f << 26)) == 28u << 26 /* andi */
+ || (insn & (0x3f << 26)) == 24u << 26 /* ori */
+ || (insn & (0x3f << 26)) == 26u << 26 /* xori */)
+ : ((insn & (0x3f << 26)) == 29u << 26 /* andis */
+ || (insn & (0x3f << 26)) == 25u << 26 /* oris */
+ || (insn & (0x3f << 26)) == 27u << 26 /* xoris */))
overflow = Reloc::CHECK_UNSIGNED;
+ else
+ overflow = Reloc::CHECK_SIGNED;
}
+ bool maybe_dq_reloc = false;
typename Powerpc_relocate_functions<size, big_endian>::Status status
= Powerpc_relocate_functions<size, big_endian>::STATUS_OK;
switch (r_type)
case elfcpp::R_POWERPC_GOT_DTPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
+ case elfcpp::R_POWERPC_GOT_TPREL16:
+ case elfcpp::R_POWERPC_GOT_TPREL16_LO:
if (size == 64)
{
- status = Reloc::addr16_ds(view, value, overflow);
+ // On ppc64 these are all ds form
+ maybe_dq_reloc = true;
break;
}
+ // Fall through.
case elfcpp::R_POWERPC_ADDR16:
case elfcpp::R_POWERPC_REL16:
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TLSLD16:
- case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_ADDR16_LO:
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
- case elfcpp::R_POWERPC_GOT_TPREL16_LO:
- status = Reloc::addr16(view, value, overflow);
+ if (size == 64)
+ status = Reloc::addr16(view, value, overflow);
+ else
+ maybe_dq_reloc = true;
break;
case elfcpp::R_POWERPC_UADDR16:
if (size == 32)
// R_PPC_EMB_MRKREF, R_PPC_EMB_RELST_LO, R_PPC_EMB_RELST_HA
goto unsupp;
+ // Fall through.
case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_PPC64_TOC16_HI:
if (size == 32)
// R_PPC_EMB_RELSEC16, R_PPC_EMB_RELST_HI, R_PPC_EMB_BIT_FLD
goto unsupp;
+ // Fall through.
case elfcpp::R_POWERPC_ADDR16_HA:
case elfcpp::R_POWERPC_REL16_HA:
case elfcpp::R_PPC64_TOC16_HA:
Reloc::addr16_ha(view, value);
break;
+ case elfcpp::R_POWERPC_REL16DX_HA:
+ status = Reloc::addr16dx_ha(view, value, overflow);
+ break;
+
case elfcpp::R_PPC64_DTPREL16_HIGHER:
if (size == 32)
// R_PPC_EMB_NADDR16_LO
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_HIGHER:
case elfcpp::R_PPC64_TPREL16_HIGHER:
Reloc::addr16_hi2(view, value);
if (size == 32)
// R_PPC_EMB_NADDR16_HI
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_HIGHERA:
case elfcpp::R_PPC64_TPREL16_HIGHERA:
Reloc::addr16_ha2(view, value);
if (size == 32)
// R_PPC_EMB_NADDR16_HA
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_HIGHEST:
case elfcpp::R_PPC64_TPREL16_HIGHEST:
Reloc::addr16_hi3(view, value);
if (size == 32)
// R_PPC_EMB_SDAI16
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_HIGHESTA:
case elfcpp::R_PPC64_TPREL16_HIGHESTA:
Reloc::addr16_ha3(view, value);
if (size == 32)
// R_PPC_EMB_NADDR32, R_PPC_EMB_NADDR16
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_TPREL16_DS:
case elfcpp::R_PPC64_TPREL16_LO_DS:
if (size == 32)
// R_PPC_TLSGD, R_PPC_TLSLD
break;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_DS:
case elfcpp::R_PPC64_ADDR16_LO_DS:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_GOT16_LO_DS:
case elfcpp::R_PPC64_SECTOFF_DS:
case elfcpp::R_PPC64_SECTOFF_LO_DS:
- status = Reloc::addr16_ds(view, value, overflow);
+ maybe_dq_reloc = true;
break;
case elfcpp::R_POWERPC_ADDR14:
r_type);
break;
- case elfcpp::R_PPC_EMB_SDA21:
+ case elfcpp::R_PPC64_TOCSAVE:
if (size == 32)
+ // R_PPC_EMB_SDA21
goto unsupp;
else
{
- // R_PPC64_TOCSAVE. For the time being this can be ignored.
+ Symbol_location loc;
+ loc.object = relinfo->object;
+ loc.shndx = relinfo->data_shndx;
+ loc.offset = rela.get_r_offset();
+ Tocsave_loc::const_iterator p = target->tocsave_loc().find(loc);
+ if (p != target->tocsave_loc().end())
+ {
+ // If we've generated plt calls using this tocsave, then
+ // the nop needs to be changed to save r2.
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ if (elfcpp::Swap<32, big_endian>::readval(iview) == nop)
+ elfcpp::Swap<32, big_endian>::
+ writeval(iview, std_2_1 + target->stk_toc());
+ }
}
break;
r_type);
break;
}
- if (status != Powerpc_relocate_functions<size, big_endian>::STATUS_OK)
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("relocation overflow"));
+
+ if (maybe_dq_reloc)
+ {
+ if (insn == 0)
+ insn = elfcpp::Swap<32, big_endian>::readval(iview);
+
+ if ((insn & (0x3f << 26)) == 56u << 26 /* lq */
+ || ((insn & (0x3f << 26)) == (61u << 26) /* lxv, stxv */
+ && (insn & 3) == 1))
+ status = Reloc::addr16_dq(view, value, overflow);
+ else if (size == 64
+ || (insn & (0x3f << 26)) == 58u << 26 /* ld,ldu,lwa */
+ || (insn & (0x3f << 26)) == 62u << 26 /* std,stdu,stq */
+ || (insn & (0x3f << 26)) == 57u << 26 /* lfdp */
+ || (insn & (0x3f << 26)) == 61u << 26 /* stfdp */)
+ status = Reloc::addr16_ds(view, value, overflow);
+ else
+ status = Reloc::addr16(view, value, overflow);
+ }
+
+ if (status != Powerpc_relocate_functions<size, big_endian>::STATUS_OK
+ && (has_stub_value
+ || !(gsym != NULL
+ && gsym->is_undefined()
+ && is_branch_reloc(r_type))))
+ {
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("relocation overflow"));
+ if (has_stub_value)
+ gold_info(_("try relinking with a smaller --stub-group-size"));
+ }
return true;
}
typedef typename Target_powerpc<size, big_endian>::Relocate Powerpc_relocate;
typedef typename Target_powerpc<size, big_endian>::Relocate_comdat_behavior
Powerpc_comdat_behavior;
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::relocate_section<size, big_endian, Powerpc, elfcpp::SHT_RELA,
- Powerpc_relocate, Powerpc_comdat_behavior>(
+ gold::relocate_section<size, big_endian, Powerpc, Powerpc_relocate,
+ Powerpc_comdat_behavior, Classify_reloc>(
relinfo,
this,
prelocs,
reloc_symbol_changes);
}
+template<int size, bool big_endian>
class Powerpc_scan_relocatable_reloc
{
public:
+ typedef typename elfcpp::Rela<size, big_endian> Reltype;
+ static const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ static const int sh_type = elfcpp::SHT_RELA;
+
+ // Return the symbol referred to by the relocation.
+ static inline unsigned int
+ get_r_sym(const Reltype* reloc)
+ { return elfcpp::elf_r_sym<size>(reloc->get_r_info()); }
+
+ // Return the type of the relocation.
+ static inline unsigned int
+ get_r_type(const Reltype* reloc)
+ { return elfcpp::elf_r_type<size>(reloc->get_r_info()); }
+
// Return the strategy to use for a local symbol which is not a
// section symbol, given the relocation type.
inline Relocatable_relocs::Reloc_strategy
const unsigned char* plocal_symbols,
Relocatable_relocs* rr)
{
+ typedef Powerpc_scan_relocatable_reloc<size, big_endian> Scan_strategy;
+
gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::scan_relocatable_relocs<size, big_endian, elfcpp::SHT_RELA,
- Powerpc_scan_relocatable_reloc>(
+ gold::scan_relocatable_relocs<size, big_endian, Scan_strategy>(
symtab,
layout,
object,
rr);
}
+// Scan the relocs for --emit-relocs.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::emit_relocs_scan(
+ 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_syms,
+ Relocatable_relocs* rr)
+{
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+ typedef gold::Default_emit_relocs_strategy<Classify_reloc>
+ Emit_relocs_strategy;
+
+ gold_assert(sh_type == elfcpp::SHT_RELA);
+
+ gold::scan_relocatable_relocs<size, big_endian, Emit_relocs_strategy>(
+ symtab,
+ layout,
+ object,
+ data_shndx,
+ prelocs,
+ reloc_count,
+ output_section,
+ needs_special_offset_handling,
+ local_symbol_count,
+ plocal_syms,
+ rr);
+}
+
// Emit relocations for a section.
// This is a modified version of the function by the same name in
// target-reloc.h. Using relocate_special_relocatable for
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*,
Address view_address,
section_size_type,
{
gold_assert(sh_type == elfcpp::SHT_RELA);
- typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
- Reltype;
- typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc_write
- Reltype_write;
- const int reloc_size
- = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ typedef typename elfcpp::Rela<size, big_endian> Reltype;
+ typedef typename elfcpp::Rela_write<size, big_endian> Reltype_write;
+ const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ // Offset from start of insn to d-field reloc.
+ const int d_offset = big_endian ? 2 : 0;
Powerpc_relobj<size, big_endian>* const object
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
gold_assert(got2_addend != invalid_address);
}
+ const bool relocatable = parameters->options().relocatable();
+
unsigned char* pwrite = reloc_view;
bool zap_next = false;
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
{
- Relocatable_relocs::Reloc_strategy strategy = rr->strategy(i);
+ Relocatable_relocs::Reloc_strategy strategy = relinfo->rr->strategy(i);
if (strategy == Relocatable_relocs::RELOC_DISCARD)
continue;
}
// Get the new symbol index.
+ Output_section* os = NULL;
if (r_sym < local_count)
{
switch (strategy)
unsigned int shndx =
object->local_symbol_input_shndx(r_sym, &is_ordinary);
gold_assert(is_ordinary);
- Output_section* os = object->output_section(shndx);
+ os = object->output_section(shndx);
gold_assert(os != NULL);
gold_assert(os->needs_symtab_index());
r_sym = os->symtab_index();
// In an object file, r_offset is an offset within the section.
// In an executable or dynamic object, generated by
// --emit-relocs, r_offset is an absolute address.
- if (!parameters->options().relocatable())
+ if (!relocatable)
{
offset += view_address;
if (static_cast<Address>(offset_in_output_section) != invalid_address)
{
const Symbol_value<size>* psymval = object->local_symbol(orig_r_sym);
addend = psymval->value(object, addend);
+ // In a relocatable link, the symbol value is relative to
+ // the start of the output section. For a non-relocatable
+ // link, we need to adjust the addend.
+ if (!relocatable)
+ {
+ gold_assert(os != NULL);
+ addend -= os->address();
+ }
}
else if (strategy == Relocatable_relocs::RELOC_SPECIAL)
{
- if (addend >= 32768)
- addend += got2_addend;
+ if (size == 32)
+ {
+ if (addend >= 32768)
+ addend += got2_addend;
+ }
+ else if (r_type == elfcpp::R_POWERPC_REL16_HA)
+ {
+ r_type = elfcpp::R_POWERPC_ADDR16_HA;
+ addend -= d_offset;
+ }
+ else if (r_type == elfcpp::R_POWERPC_REL16_LO)
+ {
+ r_type = elfcpp::R_POWERPC_ADDR16_LO;
+ addend -= d_offset + 4;
+ }
}
else
gold_unreachable();
- if (!parameters->options().relocatable())
+ if (!relocatable)
{
if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO
else
{
r_type = elfcpp::R_POWERPC_NONE;
- offset -= 2 * big_endian;
+ offset -= d_offset;
}
break;
default:
else
{
r_type = elfcpp::R_POWERPC_NONE;
- offset -= 2 * big_endian;
+ offset -= d_offset;
}
}
}
else
{
r_type = elfcpp::R_POWERPC_NONE;
- offset -= 2 * big_endian;
+ offset -= d_offset;
}
}
}
break;
case tls::TLSOPT_TO_LE:
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- offset += 2 * big_endian;
+ offset += d_offset;
zap_next = true;
break;
default:
r_sym = os->symtab_index();
addend = dtp_offset;
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- offset += 2 * big_endian;
+ offset += d_offset;
zap_next = true;
}
}
if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
{
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- offset += 2 * big_endian;
+ offset += d_offset;
}
}
}
p != this->stub_tables_.end();
++p)
{
- Address off = (*p)->find_plt_call_entry(gsym);
- if (off != invalid_address)
- return (*p)->stub_address() + off;
+ const typename Stub_table<size, big_endian>::Plt_stub_ent* ent
+ = (*p)->find_plt_call_entry(gsym);
+ if (ent != NULL)
+ return (*p)->stub_address() + ent->off_;
}
}
else if (this->abiversion() >= 2)
{
- unsigned int off = this->glink_section()->find_global_entry(gsym);
- if (off != (unsigned int)-1)
+ Address off = this->glink_section()->find_global_entry(gsym);
+ if (off != invalid_address)
return this->glink_section()->global_entry_address() + off;
}
gold_unreachable();
p != this->stub_tables_.end();
++p)
{
- Address off = (*p)->find_plt_call_entry(relobj->sized_relobj(),
- symndx);
- if (off != invalid_address)
- return (*p)->stub_address() + off;
+ const typename Stub_table<size, big_endian>::Plt_stub_ent* ent
+ = (*p)->find_plt_call_entry(relobj->sized_relobj(), symndx);
+ if (ent != NULL)
+ return (*p)->stub_address() + ent->off_;
}
}
gold_unreachable();
p != this->stub_tables_.end();
++p)
{
- Address off = (*p)->find_plt_call_entry(gsym);
- if (off != invalid_address)
- return (*p)->stub_address() + off;
+ const typename Stub_table<size, big_endian>::Plt_stub_ent* ent
+ = (*p)->find_plt_call_entry(gsym);
+ if (ent != NULL)
+ return (*p)->stub_address() + ent->off_;
}
}
else if (this->abiversion() >= 2)
{
- unsigned int off = this->glink_section()->find_global_entry(gsym);
- if (off != (unsigned int)-1)
+ Address off = this->glink_section()->find_global_entry(gsym);
+ if (off != invalid_address)
return this->glink_section()->global_entry_address() + off;
}
gold_unreachable();