const Output_section::Input_section* owner;
};
-inline bool
-is_branch_reloc(unsigned int r_type);
+inline bool is_branch_reloc(unsigned int);
+
+template<int size>
+inline bool is_plt16_reloc(unsigned int);
// Counter incremented on every Powerpc_relobj constructed.
static uint32_t object_id = 0;
Target_powerpc()
: Sized_target<size, big_endian>(&powerpc_info),
- got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL),
+ got_(NULL), plt_(NULL), iplt_(NULL), lplt_(NULL), brlt_section_(NULL),
glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
tlsld_got_offset_(-1U),
stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(),
return this->iplt_;
}
+ // Get the LPLT section.
+ const Output_data_plt_powerpc<size, big_endian>*
+ lplt_section() const
+ {
+ return this->lplt_;
+ }
+
// Return the plt offset and section for the given global sym.
Address
plt_off(const Symbol* gsym,
unsigned int local_sym_index,
const Output_data_plt_powerpc<size, big_endian>** sec) const
{
- *sec = this->iplt_section();
+ const Symbol_value<size>* lsym = relobj->local_symbol(local_sym_index);
+ if (lsym->is_ifunc_symbol())
+ *sec = this->iplt_section();
+ else
+ *sec = this->lplt_section();
return relobj->local_plt_offset(local_sym_index);
}
unsigned int r_type, const Symbol* gsym)
{
bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24
- || r_type == elfcpp::R_PPC_PLTREL24)
+ || r_type == elfcpp::R_PPC_PLTREL24
+ || is_plt16_reloc<size>(r_type)
+ || r_type == elfcpp::R_POWERPC_PLTSEQ
+ || r_type == elfcpp::R_POWERPC_PLTCALL)
&& gsym != NULL
&& (gsym == target->tls_get_addr()
|| gsym == target->tls_get_addr_opt()));
void
make_iplt_section(Symbol_table*, Layout*);
+ void
+ make_lplt_section(Layout*);
+
void
make_brlt_section(Layout*);
Sized_relobj_file<size, big_endian>*,
unsigned int);
+ // Create a PLT entry for a local non-IFUNC symbol.
+ void
+ make_local_plt_entry(Layout*,
+ Sized_relobj_file<size, big_endian>*,
+ unsigned int);
+
// Create a GOT entry for local dynamic __tls_get_addr.
unsigned int
// section is emitted and marked with __rela_iplt_start and
// __rela_iplt_end symbols.
Output_data_plt_powerpc<size, big_endian>* iplt_;
+ // A PLT style section for local, non-ifunc symbols
+ Output_data_plt_powerpc<size, big_endian>* lplt_;
// Section holding long branch destinations.
Output_data_brlt_powerpc<size, big_endian>* brlt_section_;
// The .glink section.
}
this->relocate_section_range(symtab, layout, pshdrs, of, pviews,
start, this->shnum() - 1);
+
+ if (!parameters->options().output_is_position_independent())
+ {
+ Target_powerpc<size, big_endian>* target
+ = static_cast<Target_powerpc<size, big_endian>*>(
+ parameters->sized_target<size, big_endian>());
+ if (target->lplt_section() && target->lplt_section()->data_size() != 0)
+ {
+ const section_size_type offset = target->lplt_section()->offset();
+ const section_size_type oview_size
+ = convert_to_section_size_type(target->lplt_section()->data_size());
+ unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+ bool modified = false;
+ unsigned int nsyms = this->local_symbol_count();
+ for (unsigned int i = 0; i < nsyms; i++)
+ if (this->local_has_plt_offset(i))
+ {
+ Address value = this->local_symbol_value(i, 0);
+ if (size == 64)
+ value += ppc64_local_entry_offset(i);
+ size_t off = this->local_plt_offset(i);
+ elfcpp::Swap<size, big_endian>::writeval(oview + off, value);
+ modified = true;
+ }
+ if (modified)
+ of->write_output_view(offset, oview_size, oview);
+ }
+ }
}
// Set up some symbols.
void
add_ifunc_entry(Symbol*);
+ void
+ add_local_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
+
void
add_local_ifunc_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
unsigned int
first_plt_entry_offset() const
{
- // IPLT has no reserved entry.
- if (this->name_[3] == 'I')
+ // IPLT and LPLT have no reserved entry.
+ if (this->name_[3] == 'I' || this->name_[3] == 'L')
return 0;
return this->targ_->first_plt_entry_offset();
}
}
}
+// Add an entry for a local symbol to the PLT.
+
+template<int size, bool big_endian>
+void
+Output_data_plt_powerpc<size, big_endian>::add_local_entry(
+ Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int local_sym_index)
+{
+ if (!relobj->local_has_plt_offset(local_sym_index))
+ {
+ section_size_type off = this->current_data_size();
+ relobj->set_local_plt_offset(local_sym_index, off);
+ if (this->rel_)
+ {
+ unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
+ if (size == 64 && this->targ_->abiversion() < 2)
+ dynrel = elfcpp::R_POWERPC_JMP_SLOT;
+ this->rel_->add_symbolless_local_addend(relobj, local_sym_index,
+ dynrel, this, off, 0);
+ }
+ off += this->plt_entry_size();
+ this->set_current_data_size(off);
+ }
+}
+
// Add an entry for a local ifunc symbol to the IPLT.
template<int size, bool big_endian>
void
Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
{
- if (size == 32 && this->name_[3] != 'I')
+ if (size == 32 && (this->name_[3] != 'I' && this->name_[3] != 'L'))
{
const section_size_type offset = this->offset();
const section_size_type oview_size
if (this->iplt_ == NULL)
{
this->make_plt_section(symtab, layout);
+ this->make_lplt_section(layout);
Reloc_section* iplt_rel = new Reloc_section(false);
if (this->rela_dyn_->output_section())
}
}
+// Create the LPLT section.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::make_lplt_section(Layout* layout)
+{
+ if (this->lplt_ == NULL)
+ {
+ Reloc_section* lplt_rel = NULL;
+ if (parameters->options().output_is_position_independent())
+ {
+ lplt_rel = new Reloc_section(false);
+ this->rela_dyn_section(layout);
+ if (this->rela_dyn_->output_section())
+ this->rela_dyn_->output_section()
+ ->add_output_section_data(lplt_rel);
+ }
+ this->lplt_
+ = new Output_data_plt_powerpc<size, big_endian>(this, lplt_rel,
+ "** LPLT");
+ this->make_brlt_section(layout);
+ if (this->brlt_section_ && this->brlt_section_->output_section())
+ this->brlt_section_->output_section()
+ ->add_output_section_data(this->lplt_);
+ else
+ layout->add_output_section_data(".branch_lt",
+ elfcpp::SHT_PROGBITS,
+ elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
+ this->lplt_,
+ ORDER_RELRO,
+ true);
+ }
+}
+
// A section for huge long branch addresses, similar to plt section.
template<int size, bool big_endian>
if (size != 32)
this->addend_ = addend;
else if (parameters->options().output_is_position_independent()
- && r_type == elfcpp::R_PPC_PLTREL24)
+ && (r_type == elfcpp::R_PPC_PLTREL24
+ || r_type == elfcpp::R_POWERPC_PLTCALL))
{
this->addend_ = addend;
if (this->addend_ >= 32768)
if (size != 32)
this->addend_ = addend;
else if (parameters->options().output_is_position_independent()
- && r_type == elfcpp::R_PPC_PLTREL24)
+ && (r_type == elfcpp::R_PPC_PLTREL24
+ || r_type == elfcpp::R_POWERPC_PLTCALL))
this->addend_ = addend;
}
}
}
+// Make a PLT entry for a local symbol.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::make_local_plt_entry(
+ Layout* layout,
+ Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int r_sym)
+{
+ if (this->lplt_ == NULL)
+ this->make_lplt_section(layout);
+ this->lplt_->add_local_entry(relobj, r_sym);
+}
+
// Make a PLT entry for a local STT_GNU_IFUNC symbol.
template<int size, bool big_endian>
case elfcpp::R_POWERPC_PLT16_HI:
case elfcpp::R_POWERPC_PLT16_HA:
case elfcpp::R_PPC64_PLT16_LO_DS:
+ case elfcpp::R_POWERPC_PLTSEQ:
+ case elfcpp::R_POWERPC_PLTCALL:
return true;
break;
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_POWERPC_TLS:
case elfcpp::R_PPC64_ENTRY:
+ case elfcpp::R_POWERPC_PLTSEQ:
+ case elfcpp::R_POWERPC_PLTCALL:
break;
case elfcpp::R_PPC64_TOC:
}
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 (!is_ifunc)
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+ target->make_local_plt_entry(layout, object, r_sym);
+ }
+ break;
+
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_POWERPC_TLS:
case elfcpp::R_PPC64_ENTRY:
+ case elfcpp::R_POWERPC_PLTSEQ:
+ case elfcpp::R_POWERPC_PLTCALL:
break;
case elfcpp::R_PPC64_TOC:
Address address,
section_size_type view_size)
{
+ typedef Powerpc_relocate_functions<size, big_endian> Reloc;
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
+ typedef typename elfcpp::Rela<size, big_endian> Reltype;
+
if (view == NULL)
return true;
// We have already complained.
break;
case Track_tls::SKIP:
+ if (is_plt16_reloc<size>(r_type)
+ || r_type == elfcpp::R_POWERPC_PLTSEQ)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+ }
+ else if (size == 64 && r_type == elfcpp::R_POWERPC_PLTCALL)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1, nop);
+ }
return true;
case Track_tls::NORMAL:
break;
}
- typedef Powerpc_relocate_functions<size, big_endian> Reloc;
- typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
- 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;
: object->local_has_plt_offset(r_sym));
if (has_plt_offset
&& !is_plt16_reloc<size>(r_type)
+ && r_type != elfcpp::R_POWERPC_PLTSEQ
+ && r_type != elfcpp::R_POWERPC_PLTCALL
&& (!psymval->is_ifunc_symbol()
|| Scan::reloc_needs_plt_for_ifunc(target, object, r_type, false)))
{
+ target->got_section()->g_o_t());
}
}
+ else if (!has_plt_offset
+ && (is_plt16_reloc<size>(r_type)
+ || r_type == elfcpp::R_POWERPC_PLTSEQ))
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+ r_type = elfcpp::R_POWERPC_NONE;
+ }
else if (r_type == elfcpp::R_POWERPC_GOT16
|| r_type == elfcpp::R_POWERPC_GOT16_LO
|| r_type == elfcpp::R_POWERPC_GOT16_HI
}
else if (!has_stub_value)
{
+ if (!has_plt_offset && r_type == elfcpp::R_POWERPC_PLTCALL)
+ {
+ // PLTCALL without plt entry => convert to direct call
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn = (insn & 1) | b;
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ if (size == 32)
+ r_type = elfcpp::R_PPC_PLTREL24;
+ else
+ r_type = elfcpp::R_POWERPC_REL24;
+ }
Address addend = 0;
if (!(size == 32
&& (r_type == elfcpp::R_PPC_PLTREL24
case elfcpp::R_POWERPC_TLS:
case elfcpp::R_POWERPC_GNU_VTINHERIT:
case elfcpp::R_POWERPC_GNU_VTENTRY:
+ case elfcpp::R_POWERPC_PLTSEQ:
+ case elfcpp::R_POWERPC_PLTCALL:
break;
case elfcpp::R_PPC64_ADDR64: