summaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
Diffstat (limited to 'gold')
-rw-r--r--gold/ChangeLog131
-rw-r--r--gold/aarch64.cc8
-rw-r--r--gold/arm.cc8
-rw-r--r--gold/debug.h8
-rw-r--r--gold/object.h7
-rw-r--r--gold/options.h12
-rw-r--r--gold/powerpc.cc922
-rw-r--r--gold/reloc.cc73
8 files changed, 986 insertions, 183 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index f5005ef96a3..59c60e7929c 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,134 @@
+2017-02-22 Alan Modra <amodra@gmail.com>
+
+ Apply from master
+ * powerpc.cc (Target_powerpc::make_iplt_section): Check that
+ output_section exists before attempting add_output_section_data.
+ (Target_powerpc::make_brlt_section): Likewise.
+
+ 2017-02-03 Alan Modra <amodra@gmail.com>
+ * powerpc.cc (Powerpc_relobj::make_toc_relative): Don't crash
+ when no .toc section exists.
+
+ 2017-01-13 H.J. Lu <hongjiu.lu@intel.com>
+ PR gold/21040
+ * powerpc.cc (Powerpc_relobj<size, big_endian>::make_toc_relative):
+ Cast 0x80008000 to uint64_t.
+
+ 2017-01-11 Alan Modra <amodra@gmail.com>
+ * powerpc.cc (class Powerpc_copy_relocs): New.
+ (Powerpc_copy_relocs::emit): New function.
+ (Powerpc_relobj::relatoc_, toc_, no_toc_opt_): New variables.
+ (Powerpc_relobj::toc_shndx, set_no_toc_opt, no_toc_opt): New inlines.
+ (Powerpc_relobj::do_relocate_sections): New function.
+ (Powerpc_relobj::make_toc_relative): Likewise.
+ (Powerpc_relobj::do_find_special_sections): Stash away .rela.toc
+ and .toc too.
+ (ok_lo_toc_insn): Move earlier, and handle more insns.
+ (Target_powerpc::Scan::local): If optimizing toc accesses, set
+ no_toc_opt for entries we can't edit. Check insn validity.
+ Emit "toc optimization is not supported" warning, downgraded
+ from error.
+ (Target_powerpc::Scan::global): Likewise.
+ (Target_powerpc::Relocate::relocate): Edit TOC indirect code
+ to TOC relative. Don't emit "toc optimization is not supported"
+ error here.
+
+ 2017-01-10 Cary Coutant <ccoutant@gmail.com>
+ * aarch64.cc (AArch64_relobj::do_relocate_sections): Call
+ Sized_relobj_file::relocate_section_range().
+ * arm.cc (Arm_relobj::do_relocate_sections): Likewise.
+ * object.h (Sized_relobj_file::relocate_section_range): New method.
+ * reloc.cc (Sized_relobj_file::do_relocate_sections): Move
+ implementation...
+ (Sized_relobj_file::relocate_section_range): ...to new method.
+
+ 2017-01-10 Alan Modra <amodra@gmail.com>
+ * options.h: Add --secure-plt option.
+ * powerpc.cc (Target_powerpc::Scan::local): Detect and error
+ on -fPIC -mbss-plt code.
+ (Target_powerpc::Scan::global): Likewise.
+
+ 2017-01-09 Alan Modra <amodra@gmail.com>
+ * powerpc.cc (Target_powerpc::make_plt_section): Point sh_info of
+ ".rela.plt" at ".plt".
+
+ 2017-01-07 Alan Modra <amodra@gmail.com>
+ * powerpc.cc: Use shorter equivalent elfcpp typedef for
+ Reltype and reloc_size throughout.
+ (Target_powerpc::symval_for_branch): Exclude dynamic symbols.
+ (Target_powerpc::Scan::local): Use local var r_sym.
+ (Target_powerpc::Scan::global: Likewise.
+ (Target_powerpc::Relocate::relocate): Delete shadowing r_sym.
+
+ 2016-12-08 Alan Modra <amodra@gmail.com>
+ * powerpc.cc (Powerpc_relobj::stub_table): Return NULL rather
+ then asserting.
+
+ 2016-12-08 Alan Modra <amodra@gmail.com>
+ * options.h (--stub-group-multi): Fix typo.
+
+ 2016-12-07 Alan Modra <amodra@gmail.com>
+ * options.h (--stub-group-multi): New PowerPC option.
+ * powerpc.cc (Stub_control): Add multi_os_ var and param
+ to constructor. Sort start_ var later. Comment State.
+ (Stub_control::can_add_to_stub_group): Heed multi_os_.
+ (Target_powerpc::group_sections): Update.
+
+ 2016-12-07 Alan Modra <amodra@gmail.com>
+ PR gold/20878
+ * powerpc.cc (Stub_control): Replace stubs_always_before_branch_
+ with stubs_always_after_branch_, group_end_addr_ with
+ group_start_addr_.
+ (Stub_control::can_add_to_stub_group): Rewrite to suit scanning
+ sections by increasing address.
+ (Target_powerpc::group_sections): Scan that way. Delete corner
+ case.
+ * options.h (--stub-group-size): Update help string.
+
+ 2016-12-07 Alan Modra <amodra@gmail.com>
+ * powerpc.cc (Stub_table_owner): Provide constructor.
+ (Powerpc_relobj::set_stub_table): Resize fill with -1.
+ (Target_powerpc::Branch_info::make_stub): Provide target debug
+ output on returning false.
+
+ 2016-12-01 Cary Coutant <ccoutant@gmail.com>
+ PR gold/20807
+ * aarch64.cc (Target_aarch64::scan_reloc_section_for_stubs): Handle
+ section symbols correctly.
+ * arm.cc (Target_arm): Likewise.
+ * powerpc.cc (Target_powerpc): Likewise.
+
+ 2016-08-31 Alan Modra <amodra@gmail.com>
+ * powerpc.cc (class Stub_control): Delete stub14_group_size_
+ and has14_. Add group_size_.
+ (Stub_control::can_add_to_stub_group): Adjust to suit. Print
+ debug info when switching to adding sections before stubs.
+
+ 2016-08-31 Alan Modra <amodra@gmail.com>
+ * debug.h (DEBUG_TARGET): New.
+ (DEBUG_ALL): Add DEBUG_TARGET.
+ (gold_debug): Delete FORMAT param.
+ * powerpc.cc (Stub_control::can_add_to_stub_group): Print debug ourput.
+
+ 2016-08-30 Alan Modra <amodra@gmail.com>
+ PR 20523
+ * powerpc.cc (class Stub_control): Add has14_. Comment owner_.
+ (Stub_control::can_add_to_stub_group): Correct grouping of
+ sections containing 14-bit external branches. When returning
+ false, set state_ to reflect the fact that we have one section
+ for the next group. Rewrite most of function for clarity.
+ Add and expand comments.
+ (Target_powerpc::do_relax): Print stub group size retry in hex.
+
+ 2016-08-26 Han Shen <shenhan@google.com>
+ PR gold/20529 - relaxing loop never ends.
+ * powerpc.cc (Stub_table::min_size_threshold_): New member to
+ limit size.
+ (Stub_table::set_min_size_threshold): New member function.
+ (Stub_table::set_address_and_size): Add code to only allow size
+ increase.
+ (Target_powerpc::do_relax): Add code to record last size.
+
2016-09-26 Cary Coutant <ccoutant@gmail.com>
PR gold/20238
diff --git a/gold/aarch64.cc b/gold/aarch64.cc
index db9f06c1c67..96ed66e3ef7 100644
--- a/gold/aarch64.cc
+++ b/gold/aarch64.cc
@@ -2044,9 +2044,9 @@ AArch64_relobj<size, big_endian>::do_relocate_sections(
const unsigned char* pshdrs, Output_file* of,
typename Sized_relobj_file<size, big_endian>::Views* pviews)
{
- // Call parent to relocate sections.
- Sized_relobj_file<size, big_endian>::do_relocate_sections(symtab, layout,
- pshdrs, of, pviews);
+ // Relocate the section data.
+ this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+ 1, this->shnum() - 1);
// We do not generate stubs if doing a relocatable link.
if (parameters->options().relocatable())
@@ -3865,6 +3865,8 @@ Target_aarch64<size, big_endian>::scan_reloc_section_for_stubs(
if (!is_defined_in_discarded_section)
{
typedef Sized_relobj_file<size, big_endian> ObjType;
+ if (psymval->is_section_symbol())
+ symval.set_is_section_symbol();
typename ObjType::Compute_final_local_value_status status =
object->compute_final_local_value(r_sym, psymval, &symval,
relinfo->symtab);
diff --git a/gold/arm.cc b/gold/arm.cc
index c47b00224cc..d9c0a2b20de 100644
--- a/gold/arm.cc
+++ b/gold/arm.cc
@@ -6555,9 +6555,9 @@ Arm_relobj<big_endian>::do_relocate_sections(
Output_file* of,
typename Sized_relobj_file<32, big_endian>::Views* pviews)
{
- // Call parent to relocate sections.
- Sized_relobj_file<32, big_endian>::do_relocate_sections(symtab, layout,
- pshdrs, of, pviews);
+ // Relocate the section data.
+ this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+ 1, this->shnum() - 1);
// We do not generate stubs if doing a relocatable link.
if (parameters->options().relocatable())
@@ -11998,6 +11998,8 @@ Target_arm<big_endian>::scan_reloc_section_for_stubs(
if (!is_defined_in_discarded_section)
{
typedef Sized_relobj_file<32, big_endian> ObjType;
+ if (psymval->is_section_symbol())
+ symval.set_is_section_symbol();
typename ObjType::Compute_final_local_value_status status =
arm_object->compute_final_local_value(r_sym, psymval, &symval,
relinfo->symtab);
diff --git a/gold/debug.h b/gold/debug.h
index e95408ffd9e..6fd72c2c1c8 100644
--- a/gold/debug.h
+++ b/gold/debug.h
@@ -39,10 +39,11 @@ const int DEBUG_FILES = 0x4;
const int DEBUG_RELAXATION = 0x8;
const int DEBUG_INCREMENTAL = 0x10;
const int DEBUG_LOCATION = 0x20;
+const int DEBUG_TARGET = 0x40;
const int DEBUG_ALL = (DEBUG_TASK | DEBUG_SCRIPT | DEBUG_FILES
| DEBUG_RELAXATION | DEBUG_INCREMENTAL
- | DEBUG_LOCATION);
+ | DEBUG_LOCATION | DEBUG_TARGET);
// Convert a debug string to the appropriate enum.
inline int
@@ -57,6 +58,7 @@ debug_string_to_enum(const char* arg)
{ "relaxation", DEBUG_RELAXATION },
{ "incremental", DEBUG_INCREMENTAL },
{ "location", DEBUG_LOCATION },
+ { "target", DEBUG_TARGET },
{ "all", DEBUG_ALL }
};
@@ -70,11 +72,11 @@ debug_string_to_enum(const char* arg)
// Print a debug message if TYPE is enabled. This is a macro so that
// we only evaluate the arguments if necessary.
-#define gold_debug(TYPE, FORMAT, ...) \
+#define gold_debug(TYPE, ...) \
do \
{ \
if (is_debugging_enabled(TYPE)) \
- parameters->errors()->debug(FORMAT, __VA_ARGS__); \
+ parameters->errors()->debug(__VA_ARGS__); \
} \
while (0)
diff --git a/gold/object.h b/gold/object.h
index 95f6d56e086..aa1a815f031 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -2562,6 +2562,13 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
const unsigned char* pshdrs, Output_file* of,
Views* pviews);
+ // Relocate section data for a range of sections.
+ void
+ relocate_section_range(const Symbol_table* symtab, const Layout* layout,
+ const unsigned char* pshdrs, Output_file* of,
+ Views* pviews, unsigned int start_shndx,
+ unsigned int end_shndx);
+
// Adjust this local symbol value. Return false if the symbol
// should be discarded from the output file.
virtual bool
diff --git a/gold/options.h b/gold/options.h
index 4c5b2aea122..6786e9378e9 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -1069,6 +1069,9 @@ class General_options
DEFINE_special(section_start, options::TWO_DASHES, '\0',
N_("Set address of section"), N_("SECTION=ADDRESS"));
+ DEFINE_bool(secure_plt, options::TWO_DASHES , '\0', true,
+ N_("(PowerPC only) Use new-style PLT"), NULL);
+
DEFINE_optional_string(sort_common, options::TWO_DASHES, '\0', NULL,
N_("Sort common symbols by alignment"),
N_("[={ascending,descending}]"));
@@ -1097,11 +1100,14 @@ class General_options
DEFINE_int(stub_group_size, options::TWO_DASHES , '\0', 1,
N_("(ARM, PowerPC only) The maximum distance from instructions "
- "in a group of sections to their stubs. Negative values mean "
- "stubs are always after (PowerPC before) the group. 1 means "
- "use default size.\n"),
+ "in a group of sections to their stubs. Negative values mean "
+ "stubs are always after the group. 1 means use default size"),
N_("SIZE"));
+ DEFINE_bool(stub_group_multi, options::TWO_DASHES, '\0', false,
+ N_("(PowerPC only) Allow a group of stubs to serve multiple "
+ "output sections"), NULL);
+
DEFINE_bool(no_keep_memory, options::TWO_DASHES, '\0', false,
N_("Use less memory and more disk I/O "
"(included only for compatibility with GNU ld)"), NULL);
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 60530bab984..2c70f9f8e4c 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -70,6 +70,10 @@ 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;
};
@@ -88,8 +92,9 @@ 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), has_small_toc_reloc_(false), opd_valid_(false),
- opd_ent_(), access_from_map_(), has14_(), stub_table_index_(),
+ 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_()
{
this->set_abiversion(0);
@@ -102,6 +107,52 @@ public:
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
@@ -184,6 +235,12 @@ public:
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
@@ -275,7 +332,7 @@ public:
set_stub_table(unsigned int shndx, unsigned int stub_index)
{
if (shndx >= this->stub_table_index_.size())
- this->stub_table_index_.resize(shndx + 1);
+ this->stub_table_index_.resize(shndx + 1, -1);
this->stub_table_index_[shndx] = stub_index;
}
@@ -288,8 +345,8 @@ public:
= static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
unsigned int indx = this->stub_table_index_[shndx];
- gold_assert(indx < target->stub_tables().size());
- return target->stub_tables()[indx];
+ if (indx < target->stub_tables().size())
+ return target->stub_tables()[indx];
}
return NULL;
}
@@ -342,6 +399,14 @@ private:
// 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, 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_;
@@ -493,6 +558,23 @@ private:
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>
class Target_powerpc : public Sized_target<size, big_endian>
{
@@ -509,7 +591,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
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),
@@ -1310,7 +1392,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
// 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_;
@@ -1768,8 +1850,8 @@ Powerpc_relobj<size, big_endian>::set_abiversion(int ver)
}
}
-// 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
@@ -1798,6 +1880,18 @@ Powerpc_relobj<size, big_endian>::do_find_special_sections(
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);
}
@@ -1812,10 +1906,8 @@ Powerpc_relobj<size, big_endian>::scan_opd_relocs(
{
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;
@@ -1880,6 +1972,60 @@ Powerpc_relobj<size, big_endian>::scan_opd_relocs(
}
}
+// 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)
@@ -2075,6 +2221,31 @@ Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
}
}
+// 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>
@@ -2435,14 +2606,13 @@ class Stub_control
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, bool no_size_errors)
- : state_(NO_GROUP), stub_group_size_(abs(size)),
- stub14_group_size_(abs(size) >> 10),
- stubs_always_before_branch_(size < 0),
- suppress_size_errors_(no_size_errors),
- 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)
{
}
@@ -2472,31 +2642,40 @@ class Stub_control
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();
@@ -2510,46 +2689,88 @@ Stub_control::can_add_to_stub_group(Output_section* o,
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.
@@ -2560,7 +2781,8 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout,
const Task*,
bool no_size_errors)
{
- Stub_control stub_control(this->stub_group_size_, no_size_errors);
+ 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_owner* table_owner = NULL;
@@ -2568,14 +2790,14 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout,
Layout::Section_list section_list;
layout->get_executable_sections(&section_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()
@@ -2602,26 +2824,8 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout,
}
if (table_owner != NULL)
{
- const Output_section::Input_section* i = stub_control.owner();
-
- if (tables.size() >= 2 && tables[tables.size() - 2]->owner == i)
- {
- // 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.
- tables.pop_back();
- delete table_owner;
- Powerpc_relobj<size, big_endian>* ppcobj = static_cast
- <Powerpc_relobj<size, big_endian>*>(i->relobj());
- ppcobj->set_stub_table(i->shndx(), tables.size() - 1);
- }
- else
- {
- table_owner->output_section = stub_control.output_section();
- table_owner->owner = i;
- }
+ 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();
@@ -2673,6 +2877,8 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
Target_powerpc<size, big_endian>* target =
static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
+ 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_))
@@ -2698,13 +2904,13 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
from += (this->object_->output_section(this->shndx_)->address()
+ this->offset_);
if (gsym != NULL)
- return stub_table->add_plt_call_entry(from,
- this->object_, gsym,
- this->r_type_, this->addend_);
+ ok = stub_table->add_plt_call_entry(from,
+ this->object_, gsym,
+ this->r_type_, this->addend_);
else
- return stub_table->add_plt_call_entry(from,
- 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_);
}
}
else
@@ -2752,6 +2958,8 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
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,
@@ -2789,12 +2997,22 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
&& gsym != NULL
&& gsym->source() == Symbol::IN_OUTPUT_DATA
&& gsym->output_data() == target->savres_section());
- return stub_table->add_long_branch_entry(this->object_,
- this->r_type_,
- from, to, save_res);
+ ok = stub_table->add_long_branch_entry(this->object_,
+ this->r_type_,
+ from, to, save_res);
}
}
- return true;
+ 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.
@@ -2887,7 +3105,7 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
}
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 %d"),
+ gold_info(_("%s: stub group size is too large; retrying with %#x"),
program_name, this->stub_group_size_);
this->group_sections(layout, task, true);
}
@@ -2982,7 +3200,13 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
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();
@@ -3366,6 +3590,9 @@ Target_powerpc<size, big_endian>::make_plt_section(Symbol_table* symtab,
? 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());
}
}
@@ -3381,11 +3608,13 @@ Target_powerpc<size, big_endian>::make_iplt_section(Symbol_table* symtab,
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_);
}
}
@@ -3436,8 +3665,7 @@ class Output_data_brlt_powerpc : public Output_section_data_build
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();
@@ -3482,14 +3710,16 @@ Target_powerpc<size, big_endian>::make_brlt_section(Layout* layout)
{
// 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
@@ -3634,8 +3864,8 @@ class Stub_table : public Output_relaxed_input_section
targ_(targ), plt_call_stubs_(), long_branch_stubs_(),
orig_data_size_(owner->current_data_size()),
plt_size_(0), last_plt_size_(0),
- branch_size_(0), last_branch_size_(0), eh_frame_added_(false),
- need_save_res_(false)
+ branch_size_(0), last_branch_size_(0), min_size_threshold_(0),
+ eh_frame_added_(false), need_save_res_(false)
{
this->set_output_section(output_section);
@@ -3726,6 +3956,11 @@ class Stub_table : public Output_relaxed_input_section
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,
@@ -3751,6 +3986,9 @@ class Stub_table : public Output_relaxed_input_section
plt_size() const
{ return this->plt_size_; }
+ void set_min_size_threshold(Address min_size)
+ { this->min_size_threshold_ = min_size; }
+
bool
size_update()
{
@@ -4015,6 +4253,13 @@ class Stub_table : public Output_relaxed_input_section
section_size_type orig_data_size_;
// size of stubs
section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_;
+ // 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_;
// Whether .eh_frame info has been created for this stub section.
bool eh_frame_added_;
// Set if this stub group needs a copy of out-of-line register
@@ -5545,6 +5790,45 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
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>
@@ -5708,9 +5992,11 @@ Target_powerpc<size, big_endian>::Scan::local(
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_REL64:
@@ -5898,6 +6184,150 @@ Target_powerpc<size, big_endian>::Scan::local(
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:
@@ -5971,9 +6401,9 @@ Target_powerpc<size, big_endian>::Scan::global(
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;
}
@@ -6063,9 +6493,9 @@ Target_powerpc<size, big_endian>::Scan::global(
}
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);
}
@@ -6111,6 +6541,11 @@ Target_powerpc<size, big_endian>::Scan::global(
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());
}
}
}
@@ -6120,10 +6555,9 @@ Target_powerpc<size, big_endian>::Scan::global(
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()
@@ -6161,9 +6595,11 @@ Target_powerpc<size, big_endian>::Scan::global(
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_POWERPC_REL16:
@@ -6398,6 +6834,136 @@ Target_powerpc<size, big_endian>::Scan::global(
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:
@@ -6918,31 +7484,40 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
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.
@@ -6964,7 +7539,8 @@ Target_powerpc<size, big_endian>::symval_for_branch(
// 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());
@@ -7035,8 +7611,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
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;
@@ -7110,7 +7685,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
}
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);
}
@@ -7212,7 +7786,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
}
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);
}
@@ -7312,7 +7885,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
}
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);
}
@@ -7335,7 +7907,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
}
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);
}
@@ -7607,14 +8178,24 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
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:
@@ -7625,12 +8206,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
{
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;
@@ -7650,11 +8234,17 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
{
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 */)
{
@@ -7667,8 +8257,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
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;
@@ -7731,8 +8323,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
&& 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,
@@ -8205,10 +8796,8 @@ template<int size, bool big_endian>
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.
@@ -8348,12 +8937,9 @@ Target_powerpc<size, big_endian>::relocate_relocs(
{
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;
diff --git a/gold/reloc.cc b/gold/reloc.cc
index ca54f153a3f..8bbb07d3960 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -871,7 +871,30 @@ Sized_relobj_file<size, big_endian>::do_relocate_sections(
Output_file* of,
Views* pviews)
{
- unsigned int shnum = this->shnum();
+ this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
+ 1, this->shnum() - 1);
+}
+
+// Relocate section data for the range of sections START_SHNDX through
+// END_SHNDX.
+
+template<int size, bool big_endian>
+void
+Sized_relobj_file<size, big_endian>::relocate_section_range(
+ const Symbol_table* symtab,
+ const Layout* layout,
+ const unsigned char* pshdrs,
+ Output_file* of,
+ Views* pviews,
+ unsigned int start_shndx,
+ unsigned int end_shndx)
+{
+ gold_assert(start_shndx >= 1);
+ gold_assert(end_shndx < this->shnum());
+
+ if (end_shndx < start_shndx)
+ return;
+
Sized_target<size, big_endian>* target =
parameters->sized_target<size, big_endian>();
@@ -883,8 +906,8 @@ Sized_relobj_file<size, big_endian>::do_relocate_sections(
relinfo.layout = layout;
relinfo.object = this;
- const unsigned char* p = pshdrs + This::shdr_size;
- for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size)
+ const unsigned char* p = pshdrs + start_shndx * This::shdr_size;
+ for (unsigned int i = start_shndx; i <= end_shndx; ++i, p += This::shdr_size)
{
typename This::Shdr shdr(p);
@@ -1718,6 +1741,17 @@ Sized_relobj_file<32, false>::do_relocate_sections(
Views* pviews);
template
+void
+Sized_relobj_file<32, false>::relocate_section_range(
+ const Symbol_table* symtab,
+ const Layout* layout,
+ const unsigned char* pshdrs,
+ Output_file* of,
+ Views* pviews,
+ unsigned int start_shndx,
+ unsigned int end_shndx);
+
+template
unsigned char*
Sized_relobj_file<32, false>::do_get_output_view(
unsigned int shndx,
@@ -1735,6 +1769,17 @@ Sized_relobj_file<32, true>::do_relocate_sections(
Views* pviews);
template
+void
+Sized_relobj_file<32, true>::relocate_section_range(
+ const Symbol_table* symtab,
+ const Layout* layout,
+ const unsigned char* pshdrs,
+ Output_file* of,
+ Views* pviews,
+ unsigned int start_shndx,
+ unsigned int end_shndx);
+
+template
unsigned char*
Sized_relobj_file<32, true>::do_get_output_view(
unsigned int shndx,
@@ -1752,6 +1797,17 @@ Sized_relobj_file<64, false>::do_relocate_sections(
Views* pviews);
template
+void
+Sized_relobj_file<64, false>::relocate_section_range(
+ const Symbol_table* symtab,
+ const Layout* layout,
+ const unsigned char* pshdrs,
+ Output_file* of,
+ Views* pviews,
+ unsigned int start_shndx,
+ unsigned int end_shndx);
+
+template
unsigned char*
Sized_relobj_file<64, false>::do_get_output_view(
unsigned int shndx,
@@ -1769,6 +1825,17 @@ Sized_relobj_file<64, true>::do_relocate_sections(
Views* pviews);
template
+void
+Sized_relobj_file<64, true>::relocate_section_range(
+ const Symbol_table* symtab,
+ const Layout* layout,
+ const unsigned char* pshdrs,
+ Output_file* of,
+ Views* pviews,
+ unsigned int start_shndx,
+ unsigned int end_shndx);
+
+template
unsigned char*
Sized_relobj_file<64, true>::do_get_output_view(
unsigned int shndx,