aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2017-05-22 21:31:34 +0930
committerAlan Modra <amodra@gmail.com>2017-05-23 21:49:33 +0930
commit590b87ffa386ea403e2cb61525c6aafef77097a2 (patch)
tree42104f2329f587aa7a5990631b61c4aa122bb395
parent6e3f3473e2136e77d6346d5bca894c38e5389116 (diff)
downloadfsf-binutils-gdb-590b87ffa386ea403e2cb61525c6aafef77097a2.zip
fsf-binutils-gdb-590b87ffa386ea403e2cb61525c6aafef77097a2.tar.gz
fsf-binutils-gdb-590b87ffa386ea403e2cb61525c6aafef77097a2.tar.bz2
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.
-rw-r--r--gold/ChangeLog21
-rw-r--r--gold/options.h4
-rw-r--r--gold/powerpc.cc192
3 files changed, 190 insertions, 27 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 42bd6c3..1f7d01e 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -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
diff --git a/gold/options.h b/gold/options.h
index a8b1d46..202d4b0 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -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"));
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 1477a10..1f2bc9e 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -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>