aboutsummaryrefslogtreecommitdiff
path: root/gold/powerpc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gold/powerpc.cc')
-rw-r--r--gold/powerpc.cc108
1 files changed, 97 insertions, 11 deletions
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index c8d4414..3748f11 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -249,6 +249,12 @@ public:
make_toc_relative(Target_powerpc<size, big_endian>* target,
Address* value);
+ bool
+ make_got_relative(Target_powerpc<size, big_endian>* target,
+ const Symbol_value<size>* psymval,
+ Address addend,
+ Address* value);
+
// Perform the Sized_relobj_file method, then set up opd info from
// .opd relocs.
void
@@ -2387,6 +2393,25 @@ Powerpc_relobj<size, big_endian>::make_toc_relative(
return true;
}
+template<int size, bool big_endian>
+bool
+Powerpc_relobj<size, big_endian>::make_got_relative(
+ Target_powerpc<size, big_endian>* target,
+ const Symbol_value<size>* psymval,
+ Address addend,
+ Address* value)
+{
+ Address addr = psymval->value(this, addend);
+ Address got_base = (target->got_section()->output_section()->address()
+ + this->toc_base_offset());
+ addr -= got_base;
+ if (addr + 0x80008000 > 0xffffffff)
+ return false;
+
+ *value = addr;
+ return true;
+}
+
// Perform the Sized_relobj_file method, then set up opd info from
// .opd relocs.
@@ -9660,6 +9685,37 @@ Target_powerpc<size, big_endian>::symval_for_branch(
return true;
}
+template<int size>
+static bool
+relative_value_is_known(const Sized_symbol<size>* gsym)
+{
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC)
+ return false;
+
+ if (gsym->is_from_dynobj()
+ || gsym->is_undefined()
+ || gsym->is_preemptible())
+ return false;
+
+ if (gsym->is_absolute())
+ return !parameters->options().output_is_position_independent();
+
+ return true;
+}
+
+template<int size>
+static bool
+relative_value_is_known(const Symbol_value<size>* psymval)
+{
+ if (psymval->is_ifunc_symbol())
+ return false;
+
+ bool is_ordinary;
+ unsigned int shndx = psymval->input_shndx(&is_ordinary);
+
+ return is_ordinary && shndx != elfcpp::SHN_UNDEF;
+}
+
// Perform a relocation.
template<int size, bool big_endian>
@@ -10446,7 +10502,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
break;
}
- if (size == 64)
+ if (size == 64
+ && (gsym
+ ? relative_value_is_known(gsym)
+ : relative_value_is_known(psymval)))
{
switch (r_type)
{
@@ -10460,12 +10519,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
// 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:
@@ -10476,8 +10529,12 @@ 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 (r_type == elfcpp::R_PPC64_TOC16_HA
- && object->make_toc_relative(target, &value))
+ if ((r_type == elfcpp::R_PPC64_TOC16_HA
+ && object->make_toc_relative(target, &value))
+ || (r_type == elfcpp::R_POWERPC_GOT16_HA
+ && object->make_got_relative(target, psymval,
+ rela.get_r_addend(),
+ &value)))
{
gold_assert((insn & ((0x3f << 26) | 0x1f << 16))
== ((15u << 26) | (2 << 16)));
@@ -10505,8 +10562,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
bool changed = false;
- if (r_type == elfcpp::R_PPC64_TOC16_LO_DS
- && object->make_toc_relative(target, &value))
+ if ((r_type == elfcpp::R_PPC64_TOC16_LO_DS
+ && object->make_toc_relative(target, &value))
+ || (r_type == elfcpp::R_PPC64_GOT16_LO_DS
+ && object->make_got_relative(target, psymval,
+ rela.get_r_addend(),
+ &value)))
{
gold_assert ((insn & (0x3f << 26)) == 58u << 26 /* ld */);
insn ^= (14u << 26) ^ (58u << 26);
@@ -10534,6 +10595,31 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
}
break;
+ case elfcpp::R_PPC64_GOT_PCREL34:
+ if (parameters->options().toc_optimize())
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ uint64_t insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn <<= 32;
+ insn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+ if ((insn & ((-1ULL << 50) | (63ULL << 26)))
+ != ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */))
+ break;
+
+ Address relval = psymval->value(object, rela.get_r_addend());
+ relval -= address;
+ if (relval + (1ULL << 33) < 1ULL << 34)
+ {
+ value = relval;
+ // Replace with paddi
+ insn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn >> 32);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+ insn & 0xffffffff);
+ }
+ }
+ break;
+
case elfcpp::R_POWERPC_TPREL16_HA:
if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
{