// powerpc.cc -- powerpc target support for gold.
-// Copyright (C) 2008-2021 Free Software Foundation, Inc.
+// Copyright (C) 2008-2023 Free Software Foundation, Inc.
// Written by David S. Miller <davem@davemloft.net>
// and David Edelsohn <edelsohn@gnu.org>
GOT_TYPE_SMALL_TPREL = 7
};
+// gsym->needs_plt_entry purpose is to decide whether a non-branch
+// reloc should reference a plt entry. It can't be used to decide
+// whether branches need a plt entry. In fact the call to
+// needs_plt_entry here is not needed; All cases where it might
+// return true ought to be covered already. However, since this
+// function is used to decide between plt_ and lplt_ sections in
+// plt_off, make certain that every case where make_plt_entry puts
+// entries in plt_ is covered here.
+static bool
+branch_needs_plt_entry(const Symbol* gsym)
+{
+ return (((!gsym->is_defined()
+ || gsym->is_from_dynobj()
+ || gsym->is_preemptible())
+ && !gsym->final_value_is_known())
+ || gsym->needs_plt_entry());
+}
+
template<int size, bool big_endian>
class Target_powerpc : public Sized_target<size, big_endian>
{
void
do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
+ // Get the custom dynamic tag value.
+ unsigned int
+ do_dynamic_tag_custom_value(elfcpp::DT) const;
+
// Return the value to use for a dynamic which requires special
// treatment.
uint64_t
if (gsym->type() == elfcpp::STT_GNU_IFUNC
&& gsym->can_use_relative_reloc(false))
*sec = this->iplt_section();
- else if (!parameters->doing_static_link())
+ else if (branch_needs_plt_entry(gsym))
*sec = this->plt_section();
else
*sec = this->lplt_section();
Branches branch_info_;
Tocsave_loc tocsave_loc_;
+ off_t rela_dyn_size_;
+
bool power10_relocs_;
bool plt_thread_safe_;
bool plt_localentry0_;
return ok;
}
+// Helper for do_relax, avoiding checks that size, address and offset
+// are not set more than once.
+
+static inline void
+update_current_size(Output_section_data_build* od, off_t cur_size)
+{
+ od->reset_address_and_file_offset();
+ od->set_current_data_size(cur_size);
+ od->finalize_data_size();
+ od->output_section()->set_section_offsets_need_adjustment();
+}
+
// Relaxation hook. This is where we do stub generation.
template<int size, bool big_endian>
}
}
this->plt_thread_safe_ = thread_safe;
- }
- if (pass == 1)
- {
+ if (parameters->options().output_is_position_independent())
+ this->rela_dyn_size_
+ = this->rela_dyn_section(layout)->current_data_size();
+
this->stub_group_size_ = parameters->options().stub_group_size();
bool no_size_errors = true;
if (this->stub_group_size_ == 1)
if (size == 64 && num_huge_branches != 0)
this->make_brlt_section(layout);
if (size == 64 && again)
- this->brlt_section_->set_current_size(num_huge_branches);
+ {
+ update_current_size(this->brlt_section_, num_huge_branches * 16);
+ if (parameters->options().output_is_position_independent())
+ {
+ const unsigned int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ off_t cur = this->rela_dyn_size_ + num_huge_branches * reloc_size;
+ update_current_size(this->rela_dyn_, cur);
+ }
+ }
for (typename Stub_tables::reverse_iterator p = this->stub_tables_.rbegin();
p != this->stub_tables_.rend();
&& parameters->options().output_is_position_independent())
{
// Fill in the BRLT relocs.
- this->brlt_section_->reset_brlt_sizes();
+ this->rela_dyn_->reset_data_size();
+ this->rela_dyn_->set_current_data_size(this->rela_dyn_size_);
for (typename Branch_lookup_table::const_iterator p
= this->branch_lookup_table_.begin();
p != this->branch_lookup_table_.end();
++p)
{
- this->brlt_section_->add_reloc(p->first, p->second);
+ this->rela_dyn_->add_relative(elfcpp::R_POWERPC_RELATIVE,
+ this->brlt_section_, p->second,
+ p->first);
}
- this->brlt_section_->finalize_brlt_sizes();
+ this->rela_dyn_->finalize_data_size();
+ const unsigned int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ gold_assert(this->rela_dyn_->data_size()
+ == this->rela_dyn_size_ + num_huge_branches * reloc_size);
}
if (!again
template<int size, bool big_endian>
void
Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym,
- bool stash)
+ bool is_local)
{
if (!gsym->has_plt_offset())
{
gsym->set_plt_offset(off);
if (this->rel_)
{
- gsym->set_needs_dynsym_entry();
- unsigned int dynrel = elfcpp::R_POWERPC_JMP_SLOT;
- this->rel_->add_global(gsym, dynrel, this, off, 0);
+ if (is_local)
+ {
+ unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
+ if (size == 64 && this->targ_->abiversion() < 2)
+ dynrel = elfcpp::R_POWERPC_JMP_SLOT;
+ this->rel_->add_symbolless_global_addend(gsym, dynrel,
+ this, off, 0);
+ }
+ else
+ {
+ gsym->set_needs_dynsym_entry();
+ unsigned int dynrel = elfcpp::R_POWERPC_JMP_SLOT;
+ this->rel_->add_global(gsym, dynrel, this, off, 0);
+ }
}
off += this->plt_entry_size();
this->set_current_data_size(off);
- if (stash)
+ if (is_local)
{
Local_plt_ent sym(gsym);
this->sym_ents_.push_back(sym);
void
Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
{
- if (!this->sym_ents_.empty())
+ if (!this->sym_ents_.empty()
+ && !parameters->options().output_is_position_independent())
{
const section_size_type offset = this->offset();
const section_size_type oview_size
{
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);
- }
+ lplt_rel = this->rela_dyn_section(layout);
this->lplt_
= new Output_data_plt_powerpc<size, big_endian>(this, symtab, lplt_rel,
"** LPLT");
typedef Output_data_reloc<elfcpp::SHT_RELA, true,
size, big_endian> Reloc_section;
- Output_data_brlt_powerpc(Target_powerpc<size, big_endian>* targ,
- Reloc_section* brlt_rel)
+ Output_data_brlt_powerpc(Target_powerpc<size, big_endian>* targ)
: Output_section_data_build(size == 32 ? 4 : 8),
- rel_(brlt_rel),
targ_(targ)
{ }
- void
- reset_brlt_sizes()
- {
- this->reset_data_size();
- this->rel_->reset_data_size();
- }
-
- void
- finalize_brlt_sizes()
- {
- this->finalize_data_size();
- this->rel_->finalize_data_size();
- }
-
- // Add a reloc for an entry in the BRLT.
- void
- add_reloc(Address to, unsigned int off)
- { this->rel_->add_relative(elfcpp::R_POWERPC_RELATIVE, this, off, to); }
-
- // Update section and reloc section size.
- void
- set_current_size(unsigned int num_branches)
- {
- this->reset_address_and_file_offset();
- this->set_current_data_size(num_branches * 16);
- this->finalize_data_size();
- Output_section* os = this->output_section();
- os->set_section_offsets_need_adjustment();
- if (this->rel_ != NULL)
- {
- 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();
- Output_section* os = this->rel_->output_section();
- os->set_section_offsets_need_adjustment();
- }
- }
-
protected:
void
do_adjust_output_section(Output_section* os)
void
do_write(Output_file*);
- // The reloc section.
- Reloc_section* rel_;
Target_powerpc<size, big_endian>* targ_;
};
{
if (size == 64 && this->brlt_section_ == NULL)
{
- Reloc_section* brlt_rel = NULL;
bool is_pic = parameters->options().output_is_position_independent();
if (is_pic)
{
// When PIC we can't fill in .branch_lt but must initialise at
// runtime via dynamic relocations.
this->rela_dyn_section(layout);
- brlt_rel = new Reloc_section(false);
- 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);
+ = new Output_data_brlt_powerpc<size, big_endian>(this);
if (this->plt_ && is_pic && this->plt_->output_section())
this->plt_->output_section()
->add_output_section_data(this->brlt_section_);
switch (r_type)
{
+ case elfcpp::R_PPC64_TOC:
+ if (size != 64)
+ break;
+ // Fall through.
case elfcpp::R_POWERPC_NONE:
case elfcpp::R_POWERPC_GNU_VTINHERIT:
case elfcpp::R_POWERPC_GNU_VTENTRY:
- case elfcpp::R_PPC64_TOC:
// No symbol reference.
break;
case elfcpp::R_PPC64_ADDR64:
case elfcpp::R_PPC64_UADDR64:
- case elfcpp::R_POWERPC_ADDR32:
- case elfcpp::R_POWERPC_UADDR32:
- case elfcpp::R_POWERPC_ADDR16:
- case elfcpp::R_POWERPC_UADDR16:
- case elfcpp::R_POWERPC_ADDR16_LO:
- case elfcpp::R_POWERPC_ADDR16_HI:
- case elfcpp::R_POWERPC_ADDR16_HA:
case elfcpp::R_PPC64_ADDR16_HIGHER34:
case elfcpp::R_PPC64_ADDR16_HIGHERA34:
case elfcpp::R_PPC64_ADDR16_HIGHEST34:
case elfcpp::R_PPC64_D34_HI30:
case elfcpp::R_PPC64_D34_HA30:
case elfcpp::R_PPC64_D28:
+ if (size != 64)
+ break;
+ // Fall through.
+ case elfcpp::R_POWERPC_ADDR32:
+ case elfcpp::R_POWERPC_UADDR32:
+ case elfcpp::R_POWERPC_ADDR16:
+ case elfcpp::R_POWERPC_UADDR16:
+ case elfcpp::R_POWERPC_ADDR16_LO:
+ case elfcpp::R_POWERPC_ADDR16_HI:
+ case elfcpp::R_POWERPC_ADDR16_HA:
ref = Symbol::ABSOLUTE_REF;
break;
ref = Symbol::FUNCTION_CALL | Symbol::ABSOLUTE_REF;
break;
- case elfcpp::R_PPC64_REL64:
- case elfcpp::R_POWERPC_REL32:
case elfcpp::R_PPC_LOCAL24PC:
- case elfcpp::R_POWERPC_REL16:
- case elfcpp::R_POWERPC_REL16_LO:
- case elfcpp::R_POWERPC_REL16_HI:
- case elfcpp::R_POWERPC_REL16_HA:
+ if (size != 32)
+ break;
+ // Fall through.
+ ref = Symbol::RELATIVE_REF;
+ break;
+
+ case elfcpp::R_PPC64_REL64:
case elfcpp::R_PPC64_REL16_HIGH:
case elfcpp::R_PPC64_REL16_HIGHA:
case elfcpp::R_PPC64_REL16_HIGHER:
case elfcpp::R_PPC64_REL16_HIGHEST34:
case elfcpp::R_PPC64_REL16_HIGHESTA34:
case elfcpp::R_PPC64_PCREL28:
+ if (size != 64)
+ break;
+ // Fall through.
+ case elfcpp::R_POWERPC_REL32:
+ case elfcpp::R_POWERPC_REL16:
+ case elfcpp::R_POWERPC_REL16_LO:
+ case elfcpp::R_POWERPC_REL16_HI:
+ case elfcpp::R_POWERPC_REL16_HA:
ref = Symbol::RELATIVE_REF;
break;
+ case elfcpp::R_PPC_PLTREL24:
+ if (size != 32)
+ break;
+ ref = Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF;
+ break;
+
case elfcpp::R_PPC64_REL24_NOTOC:
- if (size == 32)
+ case elfcpp::R_PPC64_REL24_P9NOTOC:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
+ case elfcpp::R_PPC64_PLTSEQ_NOTOC:
+ case elfcpp::R_PPC64_PLTCALL_NOTOC:
+ case elfcpp::R_PPC64_PLT_PCREL34:
+ case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
+ if (size != 64)
break;
// Fall through.
- case elfcpp::R_PPC64_REL24_P9NOTOC:
case elfcpp::R_POWERPC_REL24:
- case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
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:
case elfcpp::R_POWERPC_PLTSEQ:
- case elfcpp::R_PPC64_PLTSEQ_NOTOC:
case elfcpp::R_POWERPC_PLTCALL:
- case elfcpp::R_PPC64_PLTCALL_NOTOC:
- case elfcpp::R_PPC64_PLT_PCREL34:
- case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
ref = Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF;
break;
- case elfcpp::R_POWERPC_GOT16:
- case elfcpp::R_POWERPC_GOT16_LO:
- case elfcpp::R_POWERPC_GOT16_HI:
- case elfcpp::R_POWERPC_GOT16_HA:
case elfcpp::R_PPC64_GOT16_DS:
case elfcpp::R_PPC64_GOT16_LO_DS:
case elfcpp::R_PPC64_GOT_PCREL34:
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
+ if (size != 64)
+ break;
+ // Fall through.
+ case elfcpp::R_POWERPC_GOT16:
+ case elfcpp::R_POWERPC_GOT16_LO:
+ case elfcpp::R_POWERPC_GOT16_HI:
+ case elfcpp::R_POWERPC_GOT16_HA:
ref = Symbol::RELATIVE_REF;
break;
- case elfcpp::R_POWERPC_GOT_TPREL16:
- case elfcpp::R_POWERPC_TLS:
case elfcpp::R_PPC64_TLSGD:
case elfcpp::R_PPC64_TLSLD:
case elfcpp::R_PPC64_TPREL34:
case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
+ if (size != 64)
+ break;
+ // Fall through.
+ case elfcpp::R_POWERPC_GOT_TPREL16:
+ case elfcpp::R_POWERPC_TLS:
ref = Symbol::TLS_REF;
break;
= target->rela_dyn_section(symtab, layout, is_ifunc);
unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
: elfcpp::R_POWERPC_RELATIVE);
- rela_dyn->add_symbolless_global_addend(
+ // Use the "add" method that marks the reloc as being
+ // relative. This is proper here and in other places
+ // that add IRELATIVE relocs because those relocs go
+ // into a separate section that isn't sorted, so it
+ // doesn't matter that they are marked is_relative.
+ rela_dyn->add_global_relative(
gsym, dynrel, output_section, object, data_shndx,
- reloc.get_r_offset(), reloc.get_r_addend());
+ reloc.get_r_offset(), reloc.get_r_addend(), false);
}
else
{
case elfcpp::R_PPC64_PLT16_LO_DS:
if (!pushed_ifunc)
{
- if (!parameters->doing_static_link())
+ if (branch_needs_plt_entry(gsym))
target->make_plt_entry(symtab, layout, gsym);
else
target->make_local_plt_entry(symtab, layout, gsym);
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());
- if (gsym->needs_plt_entry()
- || (!gsym->final_value_is_known()
- && (gsym->is_undefined()
- || gsym->is_from_dynobj()
- || gsym->is_preemptible())))
+ if (branch_needs_plt_entry(gsym))
target->make_plt_entry(symtab, layout, gsym);
}
// Fall through.
? NULL
: this->plt_->rel_plt());
layout->add_target_dynamic_tags(false, this->plt_, rel_plt,
- this->rela_dyn_, true, size == 32);
+ this->rela_dyn_, true, size == 32, true);
if (size == 32)
{
}
}
+// Get the custom dynamic tag value.
+
+template<int size, bool big_endian>
+unsigned int
+Target_powerpc<size, big_endian>::do_dynamic_tag_custom_value(
+ elfcpp::DT tag) const
+{
+ if (tag != elfcpp::DT_RELACOUNT)
+ gold_unreachable();
+ return this->rela_dyn_->relative_reloc_count();
+}
+
// Merge object attributes from input file called NAME with those of the
// output. The input object attributes are in the object pointed by PASD.
bool has_stub_value = false;
bool localentry0 = false;
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
- bool use_plt_offset
- = (gsym != NULL
- ? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
- : object->local_has_plt_offset(r_sym));
+ bool pltcall_to_direct = false;
+
if (is_plt16_reloc<size>(r_type)
|| r_type == elfcpp::R_PPC64_PLT_PCREL34
|| r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC
// that the decision depends on the PLTCALL reloc, and we don't
// know the address of that instruction when processing others
// in the sequence. So the decision needs to be made in
- // do_relax(). For now, don't optimise inline plt calls.
- if (gsym)
- use_plt_offset = gsym->has_plt_offset();
- }
- if (use_plt_offset
- && !is_got_reloc(r_type)
- && !is_plt16_reloc<size>(r_type)
- && r_type != elfcpp::R_PPC64_PLT_PCREL34
- && r_type != elfcpp::R_PPC64_PLT_PCREL34_NOTOC
- && r_type != elfcpp::R_POWERPC_PLTSEQ
- && r_type != elfcpp::R_POWERPC_PLTCALL
- && r_type != elfcpp::R_PPC64_PLTSEQ_NOTOC
- && r_type != elfcpp::R_PPC64_PLTCALL_NOTOC
- && (!psymval->is_ifunc_symbol()
- || Scan::reloc_needs_plt_for_ifunc(target, object, r_type, false)))
+ // do_relax().
+ pltcall_to_direct = !(gsym != NULL
+ ? gsym->has_plt_offset()
+ : object->local_has_plt_offset(r_sym));
+ }
+ else if ((gsym != NULL
+ ? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
+ : psymval->is_ifunc_symbol() && object->local_has_plt_offset(r_sym))
+ && !is_got_reloc(r_type)
+ && (!psymval->is_ifunc_symbol()
+ || Scan::reloc_needs_plt_for_ifunc(target, object, r_type,
+ false)))
{
if (size == 64
&& gsym != NULL
gold_assert(has_stub_value || !(os->flags() & elfcpp::SHF_ALLOC));
}
- if (use_plt_offset && (is_plt16_reloc<size>(r_type)
- || r_type == elfcpp::R_PPC64_PLT_PCREL34
- || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC))
+ if (!pltcall_to_direct && (is_plt16_reloc<size>(r_type)
+ || r_type == elfcpp::R_PPC64_PLT_PCREL34
+ || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC))
{
const Output_data_plt_powerpc<size, big_endian>* plt;
if (gsym)
value -= target->toc_pointer();
}
}
- else if (!use_plt_offset
+ else if (pltcall_to_direct
&& (is_plt16_reloc<size>(r_type)
|| r_type == elfcpp::R_POWERPC_PLTSEQ
|| r_type == elfcpp::R_PPC64_PLTSEQ_NOTOC))
elfcpp::Swap<32, big_endian>::writeval(iview, nop);
r_type = elfcpp::R_POWERPC_NONE;
}
- else if (!use_plt_offset
+ else if (pltcall_to_direct
&& (r_type == elfcpp::R_PPC64_PLT_PCREL34
|| r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC))
{
}
else if (!has_stub_value)
{
- if (!use_plt_offset && (r_type == elfcpp::R_POWERPC_PLTCALL
- || r_type == elfcpp::R_PPC64_PLTCALL_NOTOC))
+ if (pltcall_to_direct && (r_type == elfcpp::R_POWERPC_PLTCALL
+ || r_type == elfcpp::R_PPC64_PLTCALL_NOTOC))
{
// PLTCALL without plt entry => convert to direct call
Insn* iview = reinterpret_cast<Insn*>(view);
&& gsym->is_undefined()
&& is_branch_reloc<size>(r_type))))
{
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("relocation overflow"));
- if (has_stub_value)
- gold_info(_("try relinking with a smaller --stub-group-size"));
+ if (os->flags() & elfcpp::SHF_ALLOC)
+ {
+ gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("relocation overflow"));
+ if (has_stub_value)
+ gold_info(_("try relinking with a smaller --stub-group-size"));
+ }
+ else
+ {
+ gold_warning_at_location(relinfo, relnum, rela.get_r_offset(),
+ _("relocation overflow"));
+ gold_info(_("debug info may be unreliable, compile with -gdwarf64"));
+ }
}
return true;