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_index_(),
- 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
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
// The stub table to use for a given input section.
std::vector<unsigned int> stub_table_index_;
- // Header e_flags
- elfcpp::Elf_Word e_flags_;
-
// 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), relax_failed_(false), relax_fail_count_(0),
+ stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(),
+ plt_thread_safe_(false), plt_localentry0_(false),
+ plt_localentry0_init_(false), has_localentry0_(false),
+ relax_failed_(false), relax_fail_count_(0),
stub_group_size_(0), savres_section_(0)
{
}
ppc_object->set_has_14bit_branch(data_shndx);
}
+ // 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*);
}
}
+ // 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);
+ }
+
+ 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;
+ }
+
int
abiversion () const
{ return this->processor_specific_flags() & elfcpp::EF_PPC64_ABI; }
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.
bool
make_stub(Stub_table<size, big_endian>*,
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 relax_failed_;
int relax_fail_count_;
}
}
-// 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)
}
}
+// 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>
if ((*t)->owner->is_input_section())
stub_table = new Stub_table<size, big_endian>(this,
(*t)->output_section,
- (*t)->owner);
+ (*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());
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);
+ 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>
if (gsym != NULL)
ok = stub_table->add_plt_call_entry(from,
this->object_, gsym,
- this->r_type_, this->addend_);
+ this->r_type_, this->addend_,
+ this->tocsave_);
else
ok = stub_table->add_plt_call_entry(from,
this->object_, this->r_sym_,
- this->r_type_, this->addend_);
+ this->r_type_, this->addend_,
+ this->tocsave_);
}
}
else
}
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;
}
? 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
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_section* output_section,
- const Output_section::Input_section* owner)
+ const Output_section::Input_section* owner,
+ uint32_t id)
: Output_relaxed_input_section(owner->relobj(), owner->shndx(),
owner->relobj()
->section_addralign(owner->shndx())),
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),
- eh_frame_added_(false), need_save_res_(false)
+ eh_frame_added_(false), need_save_res_(false), uniq_(id)
{
this->set_output_section(output_section);
const Sized_relobj_file<size, big_endian>*,
const Symbol*,
unsigned int,
- Address);
+ Address,
+ bool);
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,
plt_size() const
{ return this->plt_size_; }
- void set_min_size_threshold(Address min_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 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
// 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_)
// 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_;
// 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_;
};
// Add a plt call stub, if we do not already have one for this
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);
- return this->can_reach_stub(from, off, r_type);
+ {
+ 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 (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>
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);
- return this->can_reach_stub(from, off, r_type);
+ {
+ this->plt_size_ = ent.off_ + this->plt_call_size(p.first);
+ 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
{
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)
{
if (save_res)
this->need_save_res_ = true;
else
{
- unsigned int stub_size = this->branch_stub_size(to);
+ 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);
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
+ 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_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;
+ p = oview + cs->second.off_;
if (ha(off) != 0)
{
- 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;
+ }
if (plt_load_toc)
{
write_insn<big_endian>(p, addis_11_2 + 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
else
plt_addr += plt_base;
- p = oview + cs->second;
+ p = oview + cs->second.off_;
if (parameters->options().output_is_position_independent())
{
Address got_addr;
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;
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>
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_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:
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_offset();
+ 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:
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;
}
}
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()
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:
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_offset();
+ 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)
{
case elfcpp::R_POWERPC_GOT_TLSLD16:
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();
+ else
+ plt_localentry0 = symtab->lookup("GLIBC_2.26", NULL) != NULL;
+ }
+ this->plt_localentry0_ = plt_localentry0;
+ this->plt_localentry0_init_ = true;
+ }
+
if (sh_type == elfcpp::SHT_REL)
{
gold_error(_("%s: unsupported REL reloc section"),
(this->glink_->pltresolve_size
- 32));
}
+ if (this->has_localentry0_)
+ odyn->add_constant(elfcpp::DT_PPC64_OPT,
+ elfcpp::PPC64_OPT_LOCALENTRY);
}
}
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.
// descriptor, use the function descriptor code entry address
Powerpc_relobj<size, big_endian>* symobj = object;
if (gsym != NULL
- && gsym->source() != Symbol::FROM_OBJECT)
+ && (gsym->source() != Symbol::FROM_OBJECT
+ || gsym->object()->is_dynamic()))
return true;
if (gsym != NULL)
symobj = static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object());
typedef Powerpc_relocate_functions<size, big_endian> Reloc;
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
- typedef typename Reloc_types<elfcpp::SHT_RELA,
- size, big_endian>::Reloc Reltype;
+ 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;
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
Address value = 0;
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))
}
if (stub_table != NULL)
{
- Address off;
+ const typename Stub_table<size, big_endian>::Plt_stub_ent* ent;
if (gsym != NULL)
- off = stub_table->find_plt_call_entry(object, gsym, r_type,
+ ent = 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,
+ ent = stub_table->find_plt_call_entry(object, r_sym, r_type,
rela.get_r_addend());
- if (off != invalid_address)
+ if (ent != NULL)
{
- value = stub_table->stub_address() + off;
+ 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;
}
}
}
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);
}
{
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;
+ if (!localentry0 && 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);
}
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);
}
}
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 (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:
{
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;
{
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;
&& gsym != NULL
&& strcmp(gsym->name(), ".TOC.") == 0)
{
- const int reloc_size
- = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ 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,
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;
class Powerpc_scan_relocatable_reloc
{
public:
- typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
- Reltype;
- static const int reloc_size =
- Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ 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.
{
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;
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)
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)