PR21503, Gold doesn't create linker stub symbols on ppc64

PR 21503
	* options.h: Add --emit-stub-syms option.
	* powerpc.cc (object_id): New.
	(Powerpc_relobj): Add uniq_ and accessor.  Sort variables for
	better packing.
	(Powerpc_dynobj): Sort variables for better packing.
	(Target_powerpc::define_local): New function.
	(Target_powerpc::group_sections): Pass stub table size to
	Stub_table constructor.
	(Target_powerpc::do_relax): Define stub and glink symbols.
	(Stub_table): Add uniq_ variable, and id param to constructor.
	(Stub_table::Plt_stub_ent): Add indx_ variable.
	(Stub_table::Branch_stub_entries): Move typedef earlier.
	(Stub_table::branch_stub_size): Replace "to" parameter with a
	Branch_stub_entries iterator.
	(Stub_table::add_long_branch_entry): Adjust to suit.
	(Stub_table::add_plt_call_entry): Set indx_.
	(Stub_table::define_stub_syms): New function.
This commit is contained in:
Alan Modra 2017-05-22 21:31:34 +09:30
parent 6e3f3473e2
commit 590b87ffa3
3 changed files with 190 additions and 27 deletions

View File

@ -1,3 +1,24 @@
2017-05-23 Alan Modra <amodra@gmail.com>
PR 21503
* options.h: Add --emit-stub-syms option.
* powerpc.cc (object_id): New.
(Powerpc_relobj): Add uniq_ and accessor. Sort variables for
better packing.
(Powerpc_dynobj): Sort variables for better packing.
(Target_powerpc::define_local): New function.
(Target_powerpc::group_sections): Pass stub table size to
Stub_table constructor.
(Target_powerpc::do_relax): Define stub and glink symbols.
(Stub_table): Add uniq_ variable, and id param to constructor.
(Stub_table::Plt_stub_ent): Add indx_ variable.
(Stub_table::Branch_stub_entries): Move typedef earlier.
(Stub_table::branch_stub_size): Replace "to" parameter with a
Branch_stub_entries iterator.
(Stub_table::add_long_branch_entry): Adjust to suit.
(Stub_table::add_plt_call_entry): Set indx_.
(Stub_table::define_stub_syms): New function.
2017-05-15 Eric Christopher <echristo@gmail.com>
* layout.cc (Layout::segment_precedes): Add a case for testing

View File

@ -814,6 +814,10 @@ class General_options
// e
DEFINE_bool(emit_stub_syms, options::TWO_DASHES, '\0', true,
N_("(PowerPC only) Label linker stubs with a symbol"),
N_("(PowerPC only) Do not label linker stubs with a symbol"));
DEFINE_string(entry, options::TWO_DASHES, 'e', NULL,
N_("Set program start address"), N_("ADDRESS"));

View File

@ -81,6 +81,9 @@ struct Stub_table_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>
{
@ -92,10 +95,10 @@ public:
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), relatoc_(0), toc_(0), no_toc_opt_(),
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);
}
@ -357,6 +360,10 @@ public:
this->stub_table_index_.clear();
}
uint32_t
uniq() const
{ return this->uniq_; }
int
abiversion() const
{ return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
@ -396,6 +403,9 @@ private:
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_;
@ -403,10 +413,6 @@ private:
unsigned int relatoc_;
unsigned int toc_;
// 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_;
// For 64-bit, whether this object uses small model relocs to access
// the toc.
bool has_small_toc_reloc_;
@ -418,6 +424,13 @@ private:
// 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
@ -435,9 +448,6 @@ private:
// 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_;
};
@ -451,7 +461,7 @@ public:
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);
}
@ -548,14 +558,14 @@ private:
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
@ -935,6 +945,23 @@ class Target_powerpc : public Sized_target<size, big_endian>
}
}
// 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_; }
@ -2836,7 +2863,8 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout,
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());
@ -3232,6 +3260,36 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
}
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;
}
@ -3857,7 +3915,8 @@ class Stub_table : public Output_relaxed_input_section
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())),
@ -3865,7 +3924,7 @@ class Stub_table : public Output_relaxed_input_section
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);
@ -3986,9 +4045,13 @@ class Stub_table : public Output_relaxed_input_section
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()
{
@ -4058,6 +4121,10 @@ class Stub_table : public Output_relaxed_input_section
class Plt_stub_ent_hash;
typedef Unordered_map<Plt_stub_ent, unsigned int,
Plt_stub_ent_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
@ -4126,11 +4193,10 @@ class Stub_table : public Output_relaxed_input_section
// 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;
@ -4196,6 +4262,7 @@ class Stub_table : public Output_relaxed_input_section
const Sized_relobj_file<size, big_endian>* object_;
typename elfcpp::Elf_types<size>::Elf_Addr addend_;
unsigned int locsym_;
unsigned int indx_;
};
class Plt_stub_ent_hash
@ -4246,8 +4313,6 @@ class Stub_table : public Output_relaxed_input_section
// 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_;
@ -4265,6 +4330,8 @@ class Stub_table : public Output_relaxed_input_section
// 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
@ -4281,6 +4348,7 @@ Stub_table<size, big_endian>::add_plt_call_entry(
{
Plt_stub_ent ent(object, gsym, r_type, addend);
unsigned int off = this->plt_size_;
ent.indx_ = this->plt_call_stubs_.size();
std::pair<typename Plt_stub_entries::iterator, bool> p
= this->plt_call_stubs_.insert(std::make_pair(ent, off));
if (p.second)
@ -4299,6 +4367,7 @@ Stub_table<size, big_endian>::add_plt_call_entry(
{
Plt_stub_ent ent(object, locsym_index, r_type, addend);
unsigned int off = this->plt_size_;
ent.indx_ = this->plt_call_stubs_.size();
std::pair<typename Plt_stub_entries::iterator, bool> p
= this->plt_call_stubs_.insert(std::make_pair(ent, off));
if (p.second)
@ -4368,13 +4437,15 @@ Stub_table<size, big_endian>::add_long_branch_entry(
{
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);
@ -4555,6 +4626,73 @@ Output_data_glink<size, big_endian>::set_final_data_size()
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->first.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 localname[18];
const char *symname;
if (cs->first.sym_ == NULL)
{
const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<const Powerpc_relobj<size, big_endian>*>(cs->first.object_);
sprintf(localname, "%x:%x", ppcobj->uniq(), cs->first.locsym_);
symname = localname;
}
else
symname = cs->first.sym_->name();
char* name = new char[8 + 10 + strlen(symname) + strlen(add) + 1];
sprintf(name, "%08x.plt_call.%s%s", this->uniq_, symname, add);
Address value = this->stub_address() - this->address() + cs->second;
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>