aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
Diffstat (limited to 'gold')
-rw-r--r--gold/ChangeLog16
-rw-r--r--gold/powerpc.cc191
2 files changed, 151 insertions, 56 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 1c92cf1..48c87e4 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,19 @@
+2018-04-09 Alan Modra <amodra@gmail.com>
+
+ * powerpc.cc (Target_powerpc::plt_off): New functions.
+ (is_plt16_reloc): New function.
+ (Stub_table::plt_off): Use Target_powerpc::plt_off.
+ (Stub_table::plt_call_size): Use plt_off.
+ (Stub_table::do_write): Likewise.
+ (Target_powerpc::Scan::get_reference_flags): Return RELATIVE_REF
+ for PLT16 relocations.
+ (Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Return true
+ for PLT16 relocations.
+ (Target_powerpc::Scan::global): Make a PLT entry for PLT16 relocations.
+ (Target_powerpc::Relocate::relocate): Support PLT16 relocations.
+ (Powerpc_scan_relocatable_reloc::global_strategy): Return RELOC_SPECIAL
+ for ppc32 plt16 relocs.
+
2018-04-06 Cary Coutant <ccoutant@gmail.com>
* object.cc (Sized_relobj_file::include_section_group): Store
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index a2c9698..0975fee 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -860,6 +860,29 @@ class Target_powerpc : public Sized_target<size, big_endian>
return this->iplt_;
}
+ // Return the plt offset and section for the given global sym.
+ Address
+ plt_off(const Symbol* gsym,
+ const Output_data_plt_powerpc<size, big_endian>** sec) const
+ {
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false))
+ *sec = this->iplt_section();
+ else
+ *sec = this->plt_section();
+ return gsym->plt_offset();
+ }
+
+ // Return the plt offset and section for the given local sym.
+ Address
+ plt_off(const Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int local_sym_index,
+ const Output_data_plt_powerpc<size, big_endian>** sec) const
+ {
+ *sec = this->iplt_section();
+ return relobj->local_plt_offset(local_sym_index);
+ }
+
// Get the .glink section.
const Output_data_glink<size, big_endian>*
glink_section() const
@@ -1730,6 +1753,17 @@ is_branch_reloc(unsigned int r_type)
|| r_type == elfcpp::R_POWERPC_ADDR14_BRNTAKEN);
}
+// Reloc resolves to plt entry.
+template<int size>
+inline bool
+is_plt16_reloc(unsigned int r_type)
+{
+ return (r_type == elfcpp::R_POWERPC_PLT16_LO
+ || r_type == elfcpp::R_POWERPC_PLT16_HI
+ || r_type == elfcpp::R_POWERPC_PLT16_HA
+ || (size == 64 && r_type == elfcpp::R_PPC64_PLT16_LO_DS));
+}
+
// If INSN is an opcode that may be used with an @tls operand, return
// the transformed insn for TLS optimisation, otherwise return 0. If
// REG is non-zero only match an insn with RB or RA equal to REG.
@@ -4408,21 +4442,17 @@ class Stub_table : public Output_relaxed_input_section
// Return the plt offset for the given call stub.
Address
- plt_off(typename Plt_stub_entries::const_iterator p, bool* is_iplt) const
+ plt_off(typename Plt_stub_entries::const_iterator p,
+ const Output_data_plt_powerpc<size, big_endian>** sec) const
{
const Symbol* gsym = p->first.sym_;
if (gsym != NULL)
- {
- *is_iplt = (gsym->type() == elfcpp::STT_GNU_IFUNC
- && gsym->can_use_relative_reloc(false));
- return gsym->plt_offset();
- }
+ return this->targ_->plt_off(gsym, sec);
else
{
- *is_iplt = true;
const Sized_relobj_file<size, big_endian>* relobj = p->first.object_;
unsigned int local_sym_index = p->first.locsym_;
- return relobj->local_plt_offset(local_sym_index);
+ return this->targ_->plt_off(relobj, local_sym_index, sec);
}
}
@@ -4437,12 +4467,9 @@ class Stub_table : public Output_relaxed_input_section
+ (this->targ_->is_tls_get_addr_opt(gsym) ? 8 * 4 : 0));
}
- bool is_iplt;
- Address plt_addr = this->plt_off(p, &is_iplt);
- if (is_iplt)
- plt_addr += this->targ_->iplt_section()->address();
- else
- plt_addr += this->targ_->plt_section()->address();
+ const Output_data_plt_powerpc<size, big_endian>* plt;
+ Address plt_addr = this->plt_off(p, &plt);
+ plt_addr += plt->address();
Address got_addr = this->targ_->got_section()->output_section()->address();
const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<const Powerpc_relobj<size, big_endian>*>(p->first.object_);
@@ -5158,27 +5185,15 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
if (!this->plt_call_stubs_.empty())
{
- // The base address of the .plt section.
- Address plt_base = this->targ_->plt_section()->address();
- Address iplt_base = invalid_address;
-
// Write out plt call stubs.
typename Plt_stub_entries::const_iterator cs;
for (cs = this->plt_call_stubs_.begin();
cs != this->plt_call_stubs_.end();
++cs)
{
- bool is_iplt;
- Address pltoff = this->plt_off(cs, &is_iplt);
- Address plt_addr = pltoff;
- if (is_iplt)
- {
- if (iplt_base == invalid_address)
- iplt_base = this->targ_->iplt_section()->address();
- plt_addr += iplt_base;
- }
- else
- plt_addr += plt_base;
+ const Output_data_plt_powerpc<size, big_endian>* plt;
+ Address pltoff = this->plt_off(cs, &plt);
+ Address plt_addr = pltoff + plt->address();
const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<const Powerpc_relobj<size, big_endian>*>(cs->first.object_);
Address got_addr = got_os_addr + ppcobj->toc_base_offset();
@@ -5397,9 +5412,6 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
{
if (!this->plt_call_stubs_.empty())
{
- // The base address of the .plt section.
- Address plt_base = this->targ_->plt_section()->address();
- Address iplt_base = invalid_address;
// The address of _GLOBAL_OFFSET_TABLE_.
Address g_o_t = invalid_address;
@@ -5409,16 +5421,9 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
cs != this->plt_call_stubs_.end();
++cs)
{
- bool is_iplt;
- Address plt_addr = this->plt_off(cs, &is_iplt);
- if (is_iplt)
- {
- if (iplt_base == invalid_address)
- iplt_base = this->targ_->iplt_section()->address();
- plt_addr += iplt_base;
- }
- else
- plt_addr += plt_base;
+ const Output_data_plt_powerpc<size, big_endian>* plt;
+ Address plt_addr = this->plt_off(cs, &plt);
+ plt_addr += plt->address();
p = oview + cs->second.off_;
const Symbol* gsym = cs->first.sym_;
@@ -6244,6 +6249,10 @@ Target_powerpc<size, big_endian>::Scan::get_reference_flags(
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
+ case elfcpp::R_POWERPC_PLT16_LO:
+ case elfcpp::R_POWERPC_PLT16_HI:
+ case elfcpp::R_POWERPC_PLT16_HA:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
ref = Symbol::RELATIVE_REF;
break;
@@ -6427,6 +6436,14 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
case elfcpp::R_PPC64_GOT16_LO_DS:
return false;
+ // PLT relocs are OK and need a PLT entry.
+ case elfcpp::R_POWERPC_PLT16_LO:
+ case elfcpp::R_POWERPC_PLT16_HI:
+ case elfcpp::R_POWERPC_PLT16_HA:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
+ return true;
+ break;
+
// Function calls are good, and these do need a PLT entry.
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR14:
@@ -7245,6 +7262,14 @@ Target_powerpc<size, big_endian>::Scan::global(
}
break;
+ case elfcpp::R_POWERPC_PLT16_LO:
+ case elfcpp::R_POWERPC_PLT16_HI:
+ case elfcpp::R_POWERPC_PLT16_HA:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
+ if (!pushed_ifunc)
+ target->make_plt_entry(symtab, layout, gsym);
+ break;
+
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_POWERPC_REL24:
if (!is_ifunc)
@@ -8366,9 +8391,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
bool has_stub_value = false;
bool localentry0 = false;
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
- if ((gsym != NULL
+ bool has_plt_offset
+ = (gsym != NULL
? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
- : object->local_has_plt_offset(r_sym))
+ : object->local_has_plt_offset(r_sym));
+ if (has_plt_offset
+ && !is_plt16_reloc<size>(r_type)
&& (!psymval->is_ifunc_symbol()
|| Scan::reloc_needs_plt_for_ifunc(target, object, r_type, false)))
{
@@ -8440,12 +8468,38 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
gold_assert(has_stub_value || !(os->flags() & elfcpp::SHF_ALLOC));
}
- if (r_type == elfcpp::R_POWERPC_GOT16
- || r_type == elfcpp::R_POWERPC_GOT16_LO
- || r_type == elfcpp::R_POWERPC_GOT16_HI
- || r_type == elfcpp::R_POWERPC_GOT16_HA
- || r_type == elfcpp::R_PPC64_GOT16_DS
- || r_type == elfcpp::R_PPC64_GOT16_LO_DS)
+ if (has_plt_offset && is_plt16_reloc<size>(r_type))
+ {
+ const Output_data_plt_powerpc<size, big_endian>* plt;
+ if (gsym)
+ value = target->plt_off(gsym, &plt);
+ else
+ value = target->plt_off(object, r_sym, &plt);
+ value += plt->address();
+
+ if (size == 64)
+ value -= (target->got_section()->output_section()->address()
+ + object->toc_base_offset());
+ else if (parameters->options().output_is_position_independent())
+ {
+ if (rela.get_r_addend() >= 32768)
+ {
+ unsigned int got2 = object->got2_shndx();
+ value -= (object->get_output_section_offset(got2)
+ + object->output_section(got2)->address()
+ + rela.get_r_addend());
+ }
+ else
+ value -= (target->got_section()->address()
+ + target->got_section()->g_o_t());
+ }
+ }
+ else if (r_type == elfcpp::R_POWERPC_GOT16
+ || r_type == elfcpp::R_POWERPC_GOT16_LO
+ || r_type == elfcpp::R_POWERPC_GOT16_HI
+ || r_type == elfcpp::R_POWERPC_GOT16_HA
+ || r_type == elfcpp::R_PPC64_GOT16_DS
+ || r_type == elfcpp::R_PPC64_GOT16_LO_DS)
{
if (gsym != NULL)
{
@@ -8777,7 +8831,11 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
else if (!has_stub_value)
{
Address addend = 0;
- if (!(size == 32 && r_type == elfcpp::R_PPC_PLTREL24))
+ if (!(size == 32
+ && (r_type == elfcpp::R_PPC_PLTREL24
+ || r_type == elfcpp::R_POWERPC_PLT16_LO
+ || r_type == elfcpp::R_POWERPC_PLT16_HI
+ || r_type == elfcpp::R_POWERPC_PLT16_HA)))
addend = rela.get_r_addend();
value = psymval->value(object, addend);
if (size == 64 && is_branch_reloc(r_type))
@@ -8944,6 +9002,23 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
}
break;
+ case elfcpp::R_POWERPC_PLT16_HA:
+ if (size == 32
+ && !parameters->options().output_is_position_independent())
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+
+ // Convert addis to lis.
+ if ((insn & (0x3f << 26)) == 15u << 26
+ && (insn & (0x1f << 16)) != 0)
+ {
+ insn &= ~(0x1f << 16);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ }
+ }
+ break;
+
default:
break;
}
@@ -9345,6 +9420,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_POWERPC_GOT16_LO:
+ case elfcpp::R_POWERPC_PLT16_LO:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_TPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_LO:
@@ -9371,6 +9447,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_PPC64_TOC16_HI:
case elfcpp::R_POWERPC_GOT16_HI:
+ case elfcpp::R_POWERPC_PLT16_HI:
case elfcpp::R_POWERPC_SECTOFF_HI:
case elfcpp::R_POWERPC_TPREL16_HI:
case elfcpp::R_POWERPC_DTPREL16_HI:
@@ -9392,6 +9469,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
case elfcpp::R_POWERPC_REL16_HA:
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_POWERPC_GOT16_HA:
+ case elfcpp::R_POWERPC_PLT16_HA:
case elfcpp::R_POWERPC_SECTOFF_HA:
case elfcpp::R_POWERPC_TPREL16_HA:
case elfcpp::R_POWERPC_DTPREL16_HA:
@@ -9464,6 +9542,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
case elfcpp::R_PPC64_TOC16_LO_DS:
case elfcpp::R_PPC64_GOT16_DS:
case elfcpp::R_PPC64_GOT16_LO_DS:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
case elfcpp::R_PPC64_SECTOFF_DS:
case elfcpp::R_PPC64_SECTOFF_LO_DS:
maybe_dq_reloc = true;
@@ -9522,9 +9601,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
case elfcpp::R_POWERPC_PLT32:
case elfcpp::R_POWERPC_PLTREL32:
- case elfcpp::R_POWERPC_PLT16_LO:
- case elfcpp::R_POWERPC_PLT16_HI:
- case elfcpp::R_POWERPC_PLT16_HA:
case elfcpp::R_PPC_SDAREL16:
case elfcpp::R_POWERPC_ADDR30:
case elfcpp::R_PPC64_PLT64:
@@ -9533,7 +9609,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
case elfcpp::R_PPC64_PLTGOT16_LO:
case elfcpp::R_PPC64_PLTGOT16_HI:
case elfcpp::R_PPC64_PLTGOT16_HA:
- case elfcpp::R_PPC64_PLT16_LO_DS:
case elfcpp::R_PPC64_PLTGOT16_DS:
case elfcpp::R_PPC64_PLTGOT16_LO_DS:
case elfcpp::R_PPC_EMB_RELSDA:
@@ -9660,7 +9735,11 @@ public:
inline Relocatable_relocs::Reloc_strategy
global_strategy(unsigned int r_type, Relobj*, unsigned int)
{
- if (r_type == elfcpp::R_PPC_PLTREL24)
+ if (size == 32
+ && (r_type == elfcpp::R_PPC_PLTREL24
+ || r_type == elfcpp::R_POWERPC_PLT16_LO
+ || r_type == elfcpp::R_POWERPC_PLT16_HI
+ || r_type == elfcpp::R_POWERPC_PLT16_HA))
return Relocatable_relocs::RELOC_SPECIAL;
return Relocatable_relocs::RELOC_COPY;
}