// powerpc.cc -- powerpc target support for gold.
-// Copyright (C) 2008-2020 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>
}
}
- // Return offset in output GOT section that this object will use
- // as a TOC pointer. Won't be just a constant with multi-toc support.
- Address
- toc_base_offset() const
- { return 0x8000; }
-
void
set_has_small_toc_reloc()
{ has_small_toc_reloc_ = true; }
emit(Output_data_reloc<sh_type, true, size, big_endian>*);
};
+// The types of GOT entries needed for this platform.
+// These values are exposed to the ABI in an incremental link, but
+// powerpc does not support incremental linking as yet.
+enum Got_type
+ {
+ GOT_TYPE_STANDARD = 0,
+ GOT_TYPE_TLSGD = 1, // double entry for @got@tlsgd
+ GOT_TYPE_DTPREL = 2, // entry for @got@dtprel
+ GOT_TYPE_TPREL = 3, // entry for @got@tprel
+ GOT_TYPE_SMALL = 4,
+ GOT_TYPE_SMALL_TLSGD = 5,
+ GOT_TYPE_SMALL_DTPREL = 6,
+ 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>
{
Target_powerpc()
: Sized_target<size, big_endian>(&powerpc_info),
- got_(NULL), plt_(NULL), iplt_(NULL), lplt_(NULL), brlt_section_(NULL),
- glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
+ got_(NULL), biggot_(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_(),
- power10_stubs_(false), plt_thread_safe_(false), plt_localentry0_(false),
+ power10_relocs_(false), plt_thread_safe_(false), plt_localentry0_(false),
plt_localentry0_init_(false), has_localentry0_(false),
- has_tls_get_addr_opt_(false),
+ has_tls_get_addr_opt_(false), no_tprel_opt_(false),
relax_failed_(false), relax_fail_count_(0),
stub_group_size_(0), savres_section_(0),
tls_get_addr_(NULL), tls_get_addr_opt_(NULL),
}
// Accessor
- const Tocsave_loc
+ const Tocsave_loc*
tocsave_loc() const
{
- return this->tocsave_loc_;
+ return &this->tocsave_loc_;
}
void
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
int64_t
do_tls_offset_for_local(const Relobj* object,
unsigned int symndx,
- unsigned int got_indx) const;
+ Output_data_got_base* got,
+ unsigned int got_indx,
+ uint64_t addend) const;
// Return the offset to use for the GOT_INDX'th got entry which is
// for global tls symbol GSYM.
int64_t
- do_tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const;
+ do_tls_offset_for_global(Symbol* gsym,
+ Output_data_got_base* got, unsigned int got_indx,
+ uint64_t addend) const;
void
do_function_location(Symbol_location*) const;
return strcmp(sym->name(), "__tls_get_addr") == 0;
}
- // Return the size of the GOT section.
+ // Return the size of the GOT section, for incremental linking
section_size_type
got_size() const
{
gold_assert(this->got_ != NULL);
- return this->got_->data_size();
+ return this->got_->data_size() + (this->biggot_
+ ? this->biggot_->data_size() : 0);
}
// Get the PLT section.
if (gsym->type() == elfcpp::STT_GNU_IFUNC
&& gsym->can_use_relative_reloc(false))
*sec = this->iplt_section();
- else
+ else if (branch_needs_plt_entry(gsym))
*sec = this->plt_section();
+ else
+ *sec = this->lplt_section();
return gsym->plt_offset();
}
// Get the GOT section.
const Output_data_got_powerpc<size, big_endian>*
- got_section() const
+ got_section(Got_type got_type) const
{
gold_assert(this->got_ != NULL);
- return this->got_;
+ if (size == 32 || (got_type & GOT_TYPE_SMALL))
+ return this->got_;
+ gold_assert(this->biggot_ != NULL);
+ return this->biggot_;
}
// Get the GOT section, creating it if necessary.
Output_data_got_powerpc<size, big_endian>*
- got_section(Symbol_table*, Layout*);
+ got_section(Symbol_table*, Layout*, Got_type);
+
+ // The toc/got pointer reg will be set to this value.
+ Address
+ toc_pointer() const
+ {
+ return this->got_->address() + this->got_->g_o_t();
+ }
+
+ // Offset of base used to access the GOT/TOC relative to the GOT section.
+ Address
+ got_base_offset(Got_type got_type) const
+ {
+ if (size == 32 || (got_type & GOT_TYPE_SMALL))
+ return this->got_->g_o_t();
+ return this->toc_pointer() - this->biggot_->address();
+ }
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
sym->set_dynsym_index(-1U);
}
+ void
+ set_power10_relocs()
+ {
+ this->power10_relocs_ = true;
+ }
+
bool
power10_stubs() const
- { return this->power10_stubs_; }
+ {
+ return (this->power10_relocs_
+ && (parameters->options().power10_stubs_enum()
+ != General_options::POWER10_STUBS_NO));
+ }
- void
- set_power10_stubs()
+ bool
+ power10_stubs_auto() const
{
- this->power10_stubs_ = true;
+ return (parameters->options().power10_stubs_enum()
+ == General_options::POWER10_STUBS_AUTO);
}
bool
plt_localentry0() const
{ return this->plt_localentry0_; }
+ bool
+ has_localentry0() const
+ { return this->has_localentry0_; }
+
void
set_has_localentry0()
{
return false;
}
+ bool
+ tprel_opt() const
+ { return !this->no_tprel_opt_ && parameters->options().tls_optimize(); }
+
+ void
+ set_no_tprel_opt()
+ { this->no_tprel_opt_ = true; }
+
// Remember any symbols seen with non-zero localentry, even those
// not providing a definition
bool
void
merge_object_attributes(const Object*, const Attributes_section_data*);
+ bool
+ symval_for_branch(const Symbol_table* symtab,
+ const Sized_symbol<size>* gsym,
+ Powerpc_relobj<size, big_endian>* object,
+ Address *value, unsigned int *dest_shndx);
+
private:
class Track_tls
bool is_tls_call
= ((r_type == elfcpp::R_POWERPC_REL24
|| (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC)
+ || r_type == elfcpp::R_PPC64_REL24_P9NOTOC
|| r_type == elfcpp::R_PPC_PLTREL24
|| is_plt16_reloc<size>(r_type)
|| r_type == elfcpp::R_PPC64_PLT_PCREL34
bool issued_non_pic_error_;
};
- bool
- symval_for_branch(const Symbol_table* symtab,
- const Sized_symbol<size>* gsym,
- Powerpc_relobj<size, big_endian>* object,
- Address *value, unsigned int *dest_shndx);
-
// The class which implements relocation.
class Relocate : protected Track_tls
{
make_iplt_section(Symbol_table*, Layout*);
void
- make_lplt_section(Layout*);
+ make_lplt_section(Symbol_table*, Layout*);
void
make_brlt_section(Layout*);
// Create a PLT entry for a local non-IFUNC symbol.
void
- make_local_plt_entry(Layout*,
+ make_local_plt_entry(Symbol_table*, Layout*,
Sized_relobj_file<size, big_endian>*,
unsigned int);
+ void
+ make_local_plt_entry(Symbol_table*, Layout*, Symbol*);
// Create a GOT entry for local dynamic __tls_get_addr.
unsigned int
// general Target structure.
static Target::Target_info powerpc_info;
- // The types of GOT entries needed for this platform.
- // These values are exposed to the ABI in an incremental link.
- // Do not renumber existing values without changing the version
- // number of the .gnu_incremental_inputs section.
- enum Got_type
- {
- GOT_TYPE_STANDARD,
- GOT_TYPE_TLSGD, // double entry for @got@tlsgd
- GOT_TYPE_DTPREL, // entry for @got@dtprel
- GOT_TYPE_TPREL // entry for @got@tprel
- };
-
- // The GOT section.
+ // The small GOT section used by ppc32, and by ppc64 for entries that
+ // must be addresseed +/-32k from the got pointer.
Output_data_got_powerpc<size, big_endian>* got_;
+ // Another GOT section used for entries that can be addressed +/- 2G
+ // from the got pointer.
+ Output_data_got_powerpc<size, big_endian>* biggot_;
+
// The PLT section. This is a container for a table of addresses,
// and their relocations. Each address in the PLT has a dynamic
// relocation (R_*_JMP_SLOT) and each address will have a
Branches branch_info_;
Tocsave_loc tocsave_loc_;
- bool power10_stubs_;
+ off_t rela_dyn_size_;
+
+ bool power10_relocs_;
bool plt_thread_safe_;
bool plt_localentry0_;
bool plt_localentry0_init_;
bool has_localentry0_;
bool has_tls_get_addr_opt_;
+ bool no_tprel_opt_;
bool relax_failed_;
int relax_fail_count_;
{
return (r_type == elfcpp::R_POWERPC_REL24
|| (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC)
+ || r_type == elfcpp::R_PPC64_REL24_P9NOTOC
|| r_type == elfcpp::R_PPC_PLTREL24
|| r_type == elfcpp::R_PPC_LOCAL24PC
|| r_type == elfcpp::R_POWERPC_REL14
|| (size == 64 && r_type == elfcpp::R_PPC64_PLT16_LO_DS));
}
+// GOT_TYPE_STANDARD or GOT_TYPE_SMALL (ie. not TLS) GOT relocs
+inline bool
+is_got_reloc(unsigned int r_type)
+{
+ return (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
+ || r_type == elfcpp::R_PPC64_GOT_PCREL34);
+}
+
// 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.
// 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)
+ if (this->toc_shndx() == 0
+ || this->output_section(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()
+ // section address and the TOC output offset.
+ Address off = (*value + target->toc_pointer()
+ - this->output_section(this->toc_shndx())->address()
- 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
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());
+ Address got_base = target->toc_pointer();
addr -= got_base;
if (addr + (uint64_t) 0x80008000 >= (uint64_t) 1 << 32)
return false;
Address* value)
{
Address addr = psymval->value(this, addend);
- Address got_base = (target->got_section()->output_section()->address()
- + this->toc_base_offset());
+ Address got_base = target->toc_pointer();
addr -= got_base;
if (addr + 0x80008000 > 0xffffffff)
return false;
}
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.
static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, GOT_TYPE_SMALL);
symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
Symbol_table::PREDEFINED,
got, 0, 0,
static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, GOT_TYPE_SMALL);
symtab->define_in_output_data(".TOC.", NULL,
Symbol_table::PREDEFINED,
got, 0x8000, 0,
typedef typename elfcpp::Elf_types<size>::Elf_Addr Valtype;
typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian> Rela_dyn;
- Output_data_got_powerpc(Symbol_table* symtab, Layout* layout)
+ Output_data_got_powerpc(Symbol_table* symtab, Layout* layout,
+ Got_type got_type)
: Output_data_got<size, big_endian>(),
symtab_(symtab), layout_(layout),
header_ent_cnt_(size == 32 ? 3 : 1),
- header_index_(size == 32 ? 0x2000 : 0)
+ header_index_(size == 32 ? 0x2000 : -1u)
{
if (size == 64)
this->set_addralign(256);
+ if (size == 64 && (got_type & GOT_TYPE_SMALL))
+ this->make_header();
}
// Override all the Output_data_got methods we use so as to first call
// reserve_ent().
bool
- add_global(Symbol* gsym, unsigned int got_type)
+ add_global(Symbol* gsym, unsigned int got_type, uint64_t addend)
{
this->reserve_ent();
- return Output_data_got<size, big_endian>::add_global(gsym, got_type);
+ return Output_data_got<size, big_endian>::add_global(gsym, got_type,
+ addend);
}
bool
- add_global_plt(Symbol* gsym, unsigned int got_type)
+ add_global_plt(Symbol* gsym, unsigned int got_type, uint64_t addend)
{
this->reserve_ent();
- return Output_data_got<size, big_endian>::add_global_plt(gsym, got_type);
+ return Output_data_got<size, big_endian>::add_global_plt(gsym, got_type,
+ addend);
}
bool
- add_global_tls(Symbol* gsym, unsigned int got_type)
- { return this->add_global_plt(gsym, got_type); }
+ add_global_tls(Symbol* gsym, unsigned int got_type, uint64_t addend)
+ { return this->add_global_plt(gsym, got_type, addend); }
void
add_global_with_rel(Symbol* gsym, unsigned int got_type,
- Output_data_reloc_generic* rel_dyn, unsigned int r_type)
+ Output_data_reloc_generic* rel_dyn,
+ unsigned int r_type, uint64_t addend)
{
this->reserve_ent();
Output_data_got<size, big_endian>::
- add_global_with_rel(gsym, got_type, rel_dyn, r_type);
+ add_global_with_rel(gsym, got_type, rel_dyn, r_type, addend);
}
void
add_global_pair_with_rel(Symbol* gsym, unsigned int got_type,
Output_data_reloc_generic* rel_dyn,
- unsigned int r_type_1, unsigned int r_type_2)
+ unsigned int r_type_1, unsigned int r_type_2,
+ uint64_t addend)
{
if (gsym->has_got_offset(got_type))
return;
this->reserve_ent(2);
Output_data_got<size, big_endian>::
- add_global_pair_with_rel(gsym, got_type, rel_dyn, r_type_1, r_type_2);
+ add_global_pair_with_rel(gsym, got_type, rel_dyn, r_type_1, r_type_2,
+ addend);
}
bool
- add_local(Relobj* object, unsigned int sym_index, unsigned int got_type)
+ add_local(Relobj* object, unsigned int sym_index, unsigned int got_type,
+ uint64_t addend)
{
this->reserve_ent();
return Output_data_got<size, big_endian>::add_local(object, sym_index,
- got_type);
+ got_type, addend);
}
bool
- add_local_plt(Relobj* object, unsigned int sym_index, unsigned int got_type)
+ add_local_plt(Relobj* object, unsigned int sym_index,
+ unsigned int got_type, uint64_t addend)
{
this->reserve_ent();
return Output_data_got<size, big_endian>::add_local_plt(object, sym_index,
- got_type);
+ got_type, addend);
}
bool
- add_local_tls(Relobj* object, unsigned int sym_index, unsigned int got_type)
- { return this->add_local_plt(object, sym_index, got_type); }
+ add_local_tls(Relobj* object, unsigned int sym_index,
+ unsigned int got_type, uint64_t addend)
+ { return this->add_local_plt(object, sym_index, got_type, addend); }
void
add_local_tls_pair(Relobj* object, unsigned int sym_index,
unsigned int got_type,
Output_data_reloc_generic* rel_dyn,
- unsigned int r_type)
+ unsigned int r_type, uint64_t addend)
{
- if (object->local_has_got_offset(sym_index, got_type))
+ if (object->local_has_got_offset(sym_index, got_type, addend))
return;
this->reserve_ent(2);
Output_data_got<size, big_endian>::
- add_local_tls_pair(object, sym_index, got_type, rel_dyn, r_type);
+ add_local_tls_pair(object, sym_index, got_type, rel_dyn, r_type, addend);
}
unsigned int
return Output_data_got<size, big_endian>::add_constant_pair(c1, c2);
}
- // Offset of _GLOBAL_OFFSET_TABLE_.
+ // Offset of _GLOBAL_OFFSET_TABLE_ and .TOC. in this section.
unsigned int
g_o_t() const
- {
- return this->got_offset(this->header_index_);
- }
-
- // Offset of base used to access the GOT/TOC.
- // The got/toc pointer reg will be set to this value.
- Valtype
- got_base_offset(const Powerpc_relobj<size, big_endian>* object) const
{
if (size == 32)
- return this->g_o_t();
+ return this->got_offset(this->header_index_);
+ else if (this->header_index_ != -1u)
+ return this->got_offset(this->header_index_) + 0x8000;
else
- return (this->output_section()->address()
- + object->toc_base_offset()
- - this->address());
+ gold_unreachable();
}
// Ensure our GOT has a header.
void
set_final_data_size()
{
- if (this->header_ent_cnt_ != 0)
+ if (size == 32 && this->header_ent_cnt_ != 0)
this->make_header();
Output_data_got<size, big_endian>::set_final_data_size();
}
void
do_write(Output_file* of)
{
- Valtype val = 0;
- if (size == 32 && this->layout_->dynamic_data() != NULL)
- val = this->layout_->dynamic_section()->address();
- if (size == 64)
- val = this->output_section()->address() + 0x8000;
- this->replace_constant(this->header_index_, val);
+ if (this->header_index_ != -1u)
+ {
+ Valtype val = 0;
+ if (size == 32 && this->layout_->dynamic_data() != NULL)
+ val = this->layout_->dynamic_section()->address();
+ if (size == 64)
+ val = this->address() + this->g_o_t();
+ this->replace_constant(this->header_index_, val);
+ }
Output_data_got<size, big_endian>::do_write(of);
}
void
reserve_ent(unsigned int cnt = 1)
{
- if (this->header_ent_cnt_ == 0)
+ if (size != 32 || this->header_ent_cnt_ == 0)
return;
if (this->num_entries() + cnt > this->header_index_)
this->make_header();
template<int size, bool big_endian>
Output_data_got_powerpc<size, big_endian>*
Target_powerpc<size, big_endian>::got_section(Symbol_table* symtab,
- Layout* layout)
+ Layout* layout,
+ Got_type got_type)
{
if (this->got_ == NULL)
{
gold_assert(symtab != NULL && layout != NULL);
this->got_
- = new Output_data_got_powerpc<size, big_endian>(symtab, layout);
+ = new Output_data_got_powerpc<size, big_endian>(symtab, layout,
+ GOT_TYPE_SMALL);
layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
this->got_, ORDER_DATA, false);
}
- return this->got_;
+ if (size == 32 || (got_type & GOT_TYPE_SMALL))
+ return this->got_;
+
+ if (this->biggot_ == NULL)
+ {
+ this->biggot_
+ = new Output_data_got_powerpc<size, big_endian>(symtab, layout,
+ GOT_TYPE_STANDARD);
+
+ layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+ elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
+ this->biggot_, ORDER_DATA, false);
+ }
+
+ return this->biggot_;
}
// Get the dynamic reloc section, creating it if necessary.
return 1L << 15;
if (r_type == elfcpp::R_POWERPC_REL24
|| (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC)
+ || r_type == elfcpp::R_PPC64_REL24_P9NOTOC
|| r_type == elfcpp::R_PPC_PLTREL24
|| r_type == elfcpp::R_PPC_LOCAL24PC)
return 1L << 25;
from += (this->object_->output_section(this->shndx_)->address()
+ this->offset_);
Address to;
+ unsigned int other = 0;
if (gsym != NULL)
{
switch (gsym->source())
if (status != Symbol_table::CFVS_OK)
return true;
if (size == 64)
- to += this->object_->ppc64_local_entry_offset(gsym);
+ other = gsym->nonvis() >> 3;
}
else
{
return true;
to = symval.value(this->object_, 0);
if (size == 64)
- to += this->object_->ppc64_local_entry_offset(this->r_sym_);
+ other = this->object_->st_other(this->r_sym_) >> 5;
}
if (!(size == 32 && this->r_type_ == elfcpp::R_PPC_PLTREL24))
to += this->addend_;
&to, &dest_shndx))
return true;
}
- Address delta = to - from;
+ unsigned int local_ent = 0;
+ if (size == 64
+ && this->r_type_ != elfcpp::R_PPC64_REL24_NOTOC
+ && this->r_type_ != elfcpp::R_PPC64_REL24_P9NOTOC)
+ local_ent = elfcpp::ppc64_decode_local_entry(other);
+ Address delta = to + local_ent - from;
if (delta + max_branch_offset >= 2 * max_branch_offset
|| (size == 64
- && this->r_type_ == elfcpp::R_PPC64_REL24_NOTOC
+ && (this->r_type_ == elfcpp::R_PPC64_REL24_NOTOC
+ || this->r_type_ == elfcpp::R_PPC64_REL24_P9NOTOC)
&& (gsym != NULL
? this->object_->ppc64_needs_toc(gsym)
: this->object_->ppc64_needs_toc(this->r_sym_))))
&& gsym != NULL
&& gsym->source() == Symbol::IN_OUTPUT_DATA
&& gsym->output_data() == target->savres_section());
- ok = stub_table->add_long_branch_entry(this->object_,
- this->r_type_,
- from, to, save_res);
+ ok = stub_table->add_long_branch_entry(this->r_type_,
+ from, to, other, save_res);
}
}
if (!ok)
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
// See Output_data_glink::do_write() for glink contents.
if (len == 0)
{
- gold_assert(parameters->doing_static_link());
// Static linking may need stubs, to support ifunc and long
// branches. We need to create an output section for
// .eh_frame early in the link process, to have a place to
size, big_endian> Reloc_section;
Output_data_plt_powerpc(Target_powerpc<size, big_endian>* targ,
+ Symbol_table* symtab,
Reloc_section* plt_rel,
const char* name)
: Output_section_data_build(size == 32 ? 4 : 8),
- rel_(plt_rel),
- targ_(targ),
- name_(name)
+ rel_(plt_rel), targ_(targ), symtab_(symtab), name_(name), sym_ents_()
{ }
// Add an entry to the PLT.
void
- add_entry(Symbol*);
+ add_entry(Symbol*, bool = false);
void
add_ifunc_entry(Symbol*);
{ mapfile->print_output_data(this, this->name_); }
private:
+ struct Local_plt_ent
+ {
+ Local_plt_ent(Sized_relobj_file<size, big_endian>* obj, unsigned int rsym)
+ { rsym_ = rsym; u.obj_ = obj; }
+ Local_plt_ent(Symbol* sym)
+ { rsym_ = -1u; u.gsym_ = sym; }
+ ~Local_plt_ent()
+ { }
+
+ unsigned int rsym_;
+ union
+ {
+ Sized_relobj_file<size, big_endian>* obj_;
+ Symbol* gsym_;
+ } u;
+ };
+
// Return the offset of the first non-reserved PLT entry.
unsigned int
first_plt_entry_offset() const
Reloc_section* rel_;
// Allows access to .glink for do_write.
Target_powerpc<size, big_endian>* targ_;
+ Symbol_table* symtab_;
// What to report in map file.
const char *name_;
+
+ std::vector<Local_plt_ent> sym_ents_;
};
// Add an entry to the PLT.
template<int size, bool big_endian>
void
-Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym)
+Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym,
+ bool is_local)
{
if (!gsym->has_plt_offset())
{
if (off == 0)
off += this->first_plt_entry_offset();
gsym->set_plt_offset(off);
- gsym->set_needs_dynsym_entry();
- unsigned int dynrel = elfcpp::R_POWERPC_JMP_SLOT;
- this->rel_->add_global(gsym, dynrel, this, off, 0);
+ if (this->rel_)
+ {
+ 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 (is_local)
+ {
+ Local_plt_ent sym(gsym);
+ this->sym_ents_.push_back(sym);
+ }
}
}
}
off += this->plt_entry_size();
this->set_current_data_size(off);
+ Local_plt_ent sym(relobj, local_sym_index);
+ this->sym_ents_.push_back(sym);
}
}
static const uint32_t cror_15_15_15 = 0x4def7b82;
static const uint32_t cror_31_31_31 = 0x4ffffb82;
static const uint32_t ld_0_1 = 0xe8010000;
+static const uint32_t ld_0_11 = 0xe80b0000;
static const uint32_t ld_0_12 = 0xe80c0000;
static const uint32_t ld_2_1 = 0xe8410000;
static const uint32_t ld_2_2 = 0xe8420000;
void
Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
{
+ if (!this->sym_ents_.empty()
+ && !parameters->options().output_is_position_independent())
+ {
+ const section_size_type offset = this->offset();
+ const section_size_type oview_size
+ = convert_to_section_size_type(this->data_size());
+ unsigned char* const oview = of->get_output_view(offset, oview_size);
+ unsigned char* pov = oview;
+ unsigned char* endpov = oview + oview_size;
+
+ for (typename std::vector<Local_plt_ent>::iterator e
+ = this->sym_ents_.begin();
+ e != this->sym_ents_.end();
+ e++)
+ {
+ typename elfcpp::Elf_types<size>::Elf_Addr val;
+ Sized_symbol<size>* gsym = NULL;
+ Powerpc_relobj<size, big_endian>* obj = NULL;
+ if (e->rsym_ == -1u)
+ {
+ gsym = static_cast<Sized_symbol<size>*>(e->u.gsym_);
+ val = gsym->value();
+ }
+ else
+ {
+ obj = static_cast<Powerpc_relobj<size, big_endian>*>(e->u.obj_);
+ val = obj->local_symbol(e->rsym_)->value(obj, 0);
+ }
+ if (this->targ_->abiversion() >= 2)
+ {
+ elfcpp::Swap<size, big_endian>::writeval(pov, val);
+ pov += size / 8;
+ }
+ else
+ {
+ unsigned int shndx;
+ this->targ_->symval_for_branch(this->symtab_, gsym, obj,
+ &val, &shndx);
+ elfcpp::Swap<size, big_endian>::writeval(pov, val);
+ pov += size / 8;
+ val = this->targ_->toc_pointer();
+ elfcpp::Swap<size, big_endian>::writeval(pov, val);
+ pov += size / 8;
+ if (this->plt_entry_size() > 16)
+ {
+ elfcpp::Swap<size, big_endian>::writeval(pov, 0);
+ pov += size / 8;
+ }
+ }
+ }
+ gold_assert(pov == endpov);
+ }
+
if (size == 32 && (this->name_[3] != 'I' && this->name_[3] != 'L'))
{
const section_size_type offset = this->offset();
if (this->plt_ == NULL)
{
if (this->got_ == NULL)
- this->got_section(symtab, layout);
+ this->got_section(symtab, layout, GOT_TYPE_SMALL);
if (this->glink_ == NULL)
make_glink_section(layout);
elfcpp::SHF_ALLOC, plt_rel,
ORDER_DYNAMIC_PLT_RELOCS, false);
this->plt_
- = new Output_data_plt_powerpc<size, big_endian>(this, plt_rel,
+ = new Output_data_plt_powerpc<size, big_endian>(this, symtab, plt_rel,
"** PLT");
layout->add_output_section_data(".plt",
(size == 32
if (this->iplt_ == NULL)
{
this->make_plt_section(symtab, layout);
- this->make_lplt_section(layout);
+ this->make_lplt_section(symtab, layout);
Reloc_section* iplt_rel = new Reloc_section(false);
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,
+ = new Output_data_plt_powerpc<size, big_endian>(this, symtab, iplt_rel,
"** IPLT");
if (this->plt_->output_section())
this->plt_->output_section()->add_output_section_data(this->iplt_);
template<int size, bool big_endian>
void
-Target_powerpc<size, big_endian>::make_lplt_section(Layout* layout)
+Target_powerpc<size, big_endian>::make_lplt_section(Symbol_table* symtab,
+ 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);
- }
+ lplt_rel = this->rela_dyn_section(layout);
this->lplt_
- = new Output_data_plt_powerpc<size, big_endian>(this, lplt_rel,
+ = new Output_data_plt_powerpc<size, big_endian>(this, symtab, lplt_rel,
"** LPLT");
this->make_brlt_section(layout);
if (this->brlt_section_ && this->brlt_section_->output_section())
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_);
0, 0, 0, 0, // Replaced with offset to .glink.
0, 0, 0, 0, // Replaced with size of .glink.
0, // Augmentation size.
- elfcpp::DW_CFA_advance_loc + 1,
+ elfcpp::DW_CFA_advance_loc + 2,
elfcpp::DW_CFA_register, 65, 12,
- elfcpp::DW_CFA_advance_loc + 5,
+ elfcpp::DW_CFA_advance_loc + 4,
elfcpp::DW_CFA_restore_extended, 65
};
0, 0, 0, 0, // Replaced with offset to .glink.
0, 0, 0, 0, // Replaced with size of .glink.
0, // Augmentation size.
- elfcpp::DW_CFA_advance_loc + 1,
+ elfcpp::DW_CFA_advance_loc + 2,
+ elfcpp::DW_CFA_register, 65, 0,
+ elfcpp::DW_CFA_advance_loc + 2,
+ elfcpp::DW_CFA_restore_extended, 65
+};
+
+static const unsigned char glink_eh_frame_fde_64v2_localentry0[] =
+{
+ 0, 0, 0, 0, // Replaced with offset to .glink.
+ 0, 0, 0, 0, // Replaced with size of .glink.
+ 0, // Augmentation size.
+ elfcpp::DW_CFA_advance_loc + 3,
elfcpp::DW_CFA_register, 65, 0,
- elfcpp::DW_CFA_advance_loc + 7,
+ elfcpp::DW_CFA_advance_loc + 2,
elfcpp::DW_CFA_restore_extended, 65
};
struct Plt_stub_ent
{
Plt_stub_ent(unsigned int off, unsigned int indx)
- : off_(off), indx_(indx), iter_(0), notoc_(0), r2save_(0), localentry0_(0)
+ : off_(off), indx_(indx), tocoff_(0), p9off_(0), tsize_ (0), iter_(0),
+ toc_(0), notoc_(0), p9notoc_(0), r2save_(0), localentry0_(0)
{ }
unsigned int off_;
- unsigned int indx_ : 28;
+ unsigned int indx_;
+ // off_ points at p10 notoc stub, tocoff_ is offset from there to
+ // toc stub, p9off_ is offset to p9notoc stub
+ unsigned int tocoff_ : 8;
+ unsigned int p9off_ : 8;
+ // The size of the toc stub, used to locate blr on tls_get_addr stub.
+ unsigned int tsize_ : 8;
+ // Stub revision management
unsigned int iter_ : 1;
+ // The three types of stubs.
+ unsigned int toc_ : 1;
unsigned int notoc_ : 1;
+ unsigned int p9notoc_ : 1;
+ // Each with a possible variant saving r2 first
unsigned int r2save_ : 1;
+ // Handy cached info from symbol
unsigned int localentry0_ : 1;
};
struct Branch_stub_ent
{
- Branch_stub_ent(unsigned int off, bool notoc, bool save_res)
- : off_(off), iter_(false), notoc_(notoc), save_res_(save_res)
+ Branch_stub_ent(unsigned int off)
+ : off_(off), tocoff_(0), p9off_(0), iter_(0), toc_(0), notoc_(0),
+ p9notoc_(0), save_res_(0), other_(0)
{ }
unsigned int off_;
- bool iter_;
- bool notoc_;
- bool save_res_;
+ // off_ points at p10 notoc stub, tocoff_ is offset from there to
+ // toc stub, p9off_ is offset to p9notoc stub
+ unsigned int tocoff_ : 8;
+ unsigned int p9off_ : 8;
+ // Stub revision management
+ unsigned int iter_ : 1;
+ // Four types of stubs.
+ unsigned int toc_ : 1;
+ unsigned int notoc_ : 1;
+ unsigned int p9notoc_ : 1;
+ unsigned int save_res_ : 1;
+ // Handy cached info from symbol
+ unsigned int other_ : 3;
};
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
static const Address invalid_address = static_cast<Address>(0) - 1;
// Add a long branch stub.
bool
- add_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
- unsigned int, Address, Address, bool);
+ add_long_branch_entry(unsigned int, Address, Address, unsigned int, bool);
const Branch_stub_ent*
- find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
- Address) const;
+ find_long_branch_entry(Address) const;
bool
can_reach_stub(Address from, unsigned int off, unsigned int r_type)
// Size of a given plt call stub.
unsigned int
- plt_call_size(typename Plt_stub_entries::const_iterator p) const;
+ plt_call_size(typename Plt_stub_entries::iterator p) const;
unsigned int
plt_call_align(unsigned int bytes) const
// Return long branch stub size.
unsigned int
- branch_stub_size(typename Branch_stub_entries::const_iterator p,
+ branch_stub_size(typename Branch_stub_entries::iterator p,
bool* need_lt);
- bool
- build_tls_opt_head(unsigned char** pp,
- typename Plt_stub_entries::const_iterator cs);
+ void
+ build_tls_opt_head(unsigned char** pp, bool save_lr);
- bool
- build_tls_opt_tail(unsigned char* p,
- typename Plt_stub_entries::const_iterator cs);
+ void
+ build_tls_opt_tail(unsigned char* p);
void
plt_error(const Plt_stub_key& p);
class Branch_stub_key
{
public:
- Branch_stub_key(const Powerpc_relobj<size, big_endian>* obj, Address to)
- : dest_(to), toc_base_off_(0)
- {
- if (size == 64)
- toc_base_off_ = obj->toc_base_offset();
- }
+ Branch_stub_key(Address to)
+ : dest_(to)
+ { }
bool operator==(const Branch_stub_key& that) const
{
- return (this->dest_ == that.dest_
- && (size == 32
- || this->toc_base_off_ == that.toc_base_off_));
+ return this->dest_ == that.dest_;
}
Address dest_;
- unsigned int toc_base_off_;
};
class Branch_stub_key_hash
{
public:
size_t operator()(const Branch_stub_key& key) const
- { return key.dest_ ^ key.toc_base_off_; }
+ { return key.dest_; }
};
// In a sane world this would be a global.
p.first->second.localentry0_ = 1;
this->targ_->set_has_localentry0();
}
- if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+ if (r_type == elfcpp::R_PPC64_REL24_NOTOC
+ || r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
{
- if (!p.second && !p.first->second.notoc_
- && !this->targ_->power10_stubs())
- this->need_resize_ = true;
- p.first->second.notoc_ = 1;
+ if (this->targ_->power10_stubs()
+ && (!this->targ_->power10_stubs_auto()
+ || r_type == elfcpp::R_PPC64_REL24_NOTOC))
+ {
+ if (!p.second && !p.first->second.notoc_)
+ this->need_resize_ = true;
+ p.first->second.notoc_ = 1;
+ }
+ else
+ {
+ if (!p.second && !p.first->second.p9notoc_)
+ this->need_resize_ = true;
+ p.first->second.p9notoc_ = 1;
+ }
}
- else if (!tocsave && !p.first->second.localentry0_)
+ else
{
- if (!p.second && !p.first->second.r2save_)
+ if (!p.second && !p.first->second.toc_)
this->need_resize_ = true;
- p.first->second.r2save_ = 1;
+ p.first->second.toc_ = 1;
+ if (!tocsave && !p.first->second.localentry0_)
+ {
+ if (!p.second && !p.first->second.r2save_)
+ this->need_resize_ = true;
+ p.first->second.r2save_ = 1;
+ }
}
}
if (p.second || (this->resizing_ && !p.first->second.iter_))
this->plt_size_ += this->plt_call_size(p.first);
if (this->targ_->is_tls_get_addr_opt(gsym))
this->targ_->set_has_tls_get_addr_opt();
- this->plt_size_ = this->plt_call_align(this->plt_size_);
}
return this->can_reach_stub(from, p.first->second.off_, r_type);
}
p.first->second.localentry0_ = 1;
this->targ_->set_has_localentry0();
}
- if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+ if (r_type == elfcpp::R_PPC64_REL24_NOTOC
+ || r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
{
- if (!p.second && !p.first->second.notoc_
- && !this->targ_->power10_stubs())
- this->need_resize_ = true;
- p.first->second.notoc_ = 1;
+ if (this->targ_->power10_stubs()
+ && (!this->targ_->power10_stubs_auto()
+ || r_type == elfcpp::R_PPC64_REL24_NOTOC))
+ {
+ if (!p.second && !p.first->second.notoc_)
+ this->need_resize_ = true;
+ p.first->second.notoc_ = 1;
+ }
+ else
+ {
+ if (!p.second && !p.first->second.p9notoc_)
+ this->need_resize_ = true;
+ p.first->second.p9notoc_ = 1;
+ }
}
- else if (!tocsave && !p.first->second.localentry0_)
+ else
{
- if (!p.second && !p.first->second.r2save_)
+ if (!p.second && !p.first->second.toc_)
this->need_resize_ = true;
- p.first->second.r2save_ = 1;
+ p.first->second.toc_ = 1;
+ if (!tocsave && !p.first->second.localentry0_)
+ {
+ if (!p.second && !p.first->second.r2save_)
+ this->need_resize_ = true;
+ p.first->second.r2save_ = 1;
+ }
}
}
if (p.second || (this->resizing_ && !p.first->second.iter_))
p.first->second.off_ = this->plt_size_;
}
this->plt_size_ += this->plt_call_size(p.first);
- this->plt_size_ = this->plt_call_align(this->plt_size_);
}
return this->can_reach_stub(from, p.first->second.off_, r_type);
}
template<int size, bool big_endian>
bool
Stub_table<size, big_endian>::add_long_branch_entry(
- const Powerpc_relobj<size, big_endian>* object,
unsigned int r_type,
Address from,
Address to,
+ unsigned int other,
bool save_res)
{
- Branch_stub_key key(object, to);
- bool notoc = (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC);
- Branch_stub_ent ent(this->branch_size_, notoc, save_res);
+ Branch_stub_key key(to);
+ Branch_stub_ent ent(this->branch_size_);
std::pair<typename Branch_stub_entries::iterator, bool> p
= this->long_branch_stubs_.insert(std::make_pair(key, ent));
- if (notoc && !p.first->second.notoc_)
+ if (save_res)
+ {
+ if (!p.second && !p.first->second.save_res_)
+ this->need_resize_ = true;
+ p.first->second.save_res_ = true;
+ }
+ else if (size == 64
+ && (r_type == elfcpp::R_PPC64_REL24_NOTOC
+ || r_type == elfcpp::R_PPC64_REL24_P9NOTOC))
+ {
+ if (this->targ_->power10_stubs()
+ && (!this->targ_->power10_stubs_auto()
+ || r_type == elfcpp::R_PPC64_REL24_NOTOC))
+ {
+ if (!p.second && !p.first->second.notoc_)
+ this->need_resize_ = true;
+ p.first->second.notoc_ = true;
+ }
+ else
+ {
+ if (!p.second && !p.first->second.p9notoc_)
+ this->need_resize_ = true;
+ p.first->second.p9notoc_ = true;
+ }
+ }
+ else
{
- this->need_resize_ = true;
- p.first->second.notoc_ = true;
+ if (!p.second && !p.first->second.toc_)
+ this->need_resize_ = true;
+ p.first->second.toc_ = true;
}
- gold_assert(save_res == p.first->second.save_res_);
+ if (size == 64 && p.first->second.other_ == 0)
+ p.first->second.other_ = other;
if (p.second || (this->resizing_ && !p.first->second.iter_))
{
if (this->resizing_)
template<int size, bool big_endian>
const typename Stub_table<size, big_endian>::Branch_stub_ent*
-Stub_table<size, big_endian>::find_long_branch_entry(
- const Powerpc_relobj<size, big_endian>* object,
- Address to) const
+Stub_table<size, big_endian>::find_long_branch_entry(Address to) const
{
- Branch_stub_key key(object, to);
+ Branch_stub_key key(to);
typename Branch_stub_entries::const_iterator p
= this->long_branch_stubs_.find(key);
if (p == this->long_branch_stubs_.end())
if (!this->targ_->has_glink())
return;
- typedef typename Plt_stub_entries::const_iterator plt_iter;
+ typedef typename Plt_stub_entries::iterator plt_iter;
std::vector<plt_iter> calls;
if (!this->plt_call_stubs_.empty())
for (plt_iter cs = this->plt_call_stubs_.begin();
cs != this->plt_call_stubs_.end();
++cs)
- if ((this->targ_->is_tls_get_addr_opt(cs->first.sym_)
- && cs->second.r2save_
- && !cs->second.localentry0_)
- || (cs->second.notoc_
- && !this->targ_->power10_stubs()))
+ if (cs->second.p9notoc_
+ || (cs->second.toc_
+ && cs->second.r2save_
+ && !cs->second.localentry0_
+ && this->targ_->is_tls_get_addr_opt(cs->first.sym_)))
calls.push_back(cs);
if (calls.size() > 1)
std::stable_sort(calls.begin(), calls.end(),
if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
{
off += 7 * 4;
- if (cs->second.r2save_
+ if (cs->second.toc_
+ && cs->second.r2save_
&& !cs->second.localentry0_)
{
- off += 2 * 4;
+ off += cs->second.tocoff_ + 2 * 4;
eh_advance<big_endian>(fde, off - last_eh_loc);
fde.resize(fde.size() + 6);
unsigned char* p = &*fde.end() - 6;
*p++ = elfcpp::DW_CFA_offset_extended_sf;
*p++ = 65;
*p++ = -(this->targ_->stk_linker() / 8) & 0x7f;
- unsigned int delta = this->plt_call_size(cs) - 4 - 9 * 4;
+ unsigned int delta = cs->second.tsize_ - 9 * 4 - 4;
*p++ = elfcpp::DW_CFA_advance_loc + delta / 4;
*p++ = elfcpp::DW_CFA_restore_extended;
*p++ = 65;
last_eh_loc = off + delta;
- continue;
+ off = cs->second.off_ + 7 * 4;
}
}
// notoc stubs also should describe LR changes, to support
// asynchronous exceptions.
- off += (cs->second.r2save_ ? 4 : 0) + 8;
- eh_advance<big_endian>(fde, off - last_eh_loc);
- fde.resize(fde.size() + 6);
- unsigned char* p = &*fde.end() - 6;
- *p++ = elfcpp::DW_CFA_register;
- *p++ = 65;
- *p++ = 12;
- *p++ = elfcpp::DW_CFA_advance_loc + 8 / 4;
- *p++ = elfcpp::DW_CFA_restore_extended;
- *p++ = 65;
- last_eh_loc = off + 8;
+ if (cs->second.p9notoc_)
+ {
+ off += cs->second.p9off_;
+ off += (cs->second.r2save_ ? 4 : 0) + 8;
+ eh_advance<big_endian>(fde, off - last_eh_loc);
+ fde.resize(fde.size() + 6);
+ unsigned char* p = &*fde.end() - 6;
+ *p++ = elfcpp::DW_CFA_register;
+ *p++ = 65;
+ *p++ = 12;
+ *p++ = elfcpp::DW_CFA_advance_loc + 8 / 4;
+ *p++ = elfcpp::DW_CFA_restore_extended;
+ *p++ = 65;
+ last_eh_loc = off + 8;
+ }
}
for (unsigned int i = 0; i < branches.size(); i++)
{
if (size == 64)
return (8
- + (this->targ_->abiversion() < 2 ? 11 * 4 : 14 * 4));
+ + (this->targ_->abiversion() < 2 ? 11 * 4
+ : this->targ_->has_localentry0() ? 14 * 4 : 13 * 4));
return 16 * 4;
}
sizeof (Eh_cie<64>::eh_frame_cie),
glink_eh_frame_fde_64v1,
sizeof (glink_eh_frame_fde_64v1));
+ else if (this->targ_->has_localentry0())
+ layout->add_eh_frame_for_plt(this,
+ Eh_cie<64>::eh_frame_cie,
+ sizeof (Eh_cie<64>::eh_frame_cie),
+ glink_eh_frame_fde_64v2_localentry0,
+ sizeof (glink_eh_frame_fde_64v2));
else
layout->add_eh_frame_for_plt(this,
Eh_cie<64>::eh_frame_cie,
// output .symtab ordering depends on the order in which symbols
// are added to the linker symtab. We want reproducible output
// so must sort the call stub symbols.
- typedef typename Plt_stub_entries::const_iterator plt_iter;
+ typedef typename Plt_stub_entries::iterator plt_iter;
std::vector<plt_iter> sorted;
sorted.resize(this->plt_call_stubs_.size());
sprintf(name, "%08x.plt_call.%s%s%s", this->uniq_, obj, symname, add);
Address value
= this->stub_address() - this->address() + cs->second.off_;
- unsigned int stub_size = this->plt_call_align(this->plt_call_size(cs));
+ unsigned int stub_size = this->plt_call_size(cs);
this->targ_->define_local(symtab, name, this, value, stub_size);
}
}
- typedef typename Branch_stub_entries::const_iterator branch_iter;
+ typedef typename Branch_stub_entries::iterator branch_iter;
for (branch_iter bs = this->long_branch_stubs_.begin();
bs != this->long_branch_stubs_.end();
++bs)
// Emit the start of a __tls_get_addr_opt plt call stub.
template<int size, bool big_endian>
-bool
-Stub_table<size, big_endian>::build_tls_opt_head(
- unsigned char** pp,
- typename Plt_stub_entries::const_iterator cs)
+void
+Stub_table<size, big_endian>::build_tls_opt_head(unsigned char** pp,
+ bool save_lr)
{
- if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ unsigned char* p = *pp;
+ if (size == 64)
{
- unsigned char* p = *pp;
- if (size == 64)
+ write_insn<big_endian>(p, ld_11_3 + 0);
+ p += 4;
+ write_insn<big_endian>(p, ld_12_3 + 8);
+ p += 4;
+ write_insn<big_endian>(p, mr_0_3);
+ p += 4;
+ write_insn<big_endian>(p, cmpdi_11_0);
+ p += 4;
+ write_insn<big_endian>(p, add_3_12_13);
+ p += 4;
+ write_insn<big_endian>(p, beqlr);
+ p += 4;
+ write_insn<big_endian>(p, mr_3_0);
+ p += 4;
+ if (save_lr)
{
- write_insn<big_endian>(p, ld_11_3 + 0);
- p += 4;
- write_insn<big_endian>(p, ld_12_3 + 8);
- p += 4;
- write_insn<big_endian>(p, mr_0_3);
- p += 4;
- write_insn<big_endian>(p, cmpdi_11_0);
+ write_insn<big_endian>(p, mflr_11);
p += 4;
- write_insn<big_endian>(p, add_3_12_13);
+ write_insn<big_endian>(p, (std_11_1 + this->targ_->stk_linker()));
p += 4;
- write_insn<big_endian>(p, beqlr);
- p += 4;
- write_insn<big_endian>(p, mr_3_0);
- p += 4;
- if (cs->second.r2save_ && !cs->second.localentry0_)
- {
- write_insn<big_endian>(p, mflr_11);
- p += 4;
- write_insn<big_endian>(p, (std_11_1 + this->targ_->stk_linker()));
- p += 4;
- }
}
- else
- {
- write_insn<big_endian>(p, lwz_11_3 + 0);
- p += 4;
- write_insn<big_endian>(p, lwz_12_3 + 4);
- p += 4;
- write_insn<big_endian>(p, mr_0_3);
- p += 4;
- write_insn<big_endian>(p, cmpwi_11_0);
- p += 4;
- write_insn<big_endian>(p, add_3_12_2);
- p += 4;
- write_insn<big_endian>(p, beqlr);
- p += 4;
- write_insn<big_endian>(p, mr_3_0);
- p += 4;
- write_insn<big_endian>(p, nop);
- p += 4;
- }
- *pp = p;
- return true;
}
- return false;
-}
-
-// Emit the tail of a __tls_get_addr_opt plt call stub.
-
-template<int size, bool big_endian>
-bool
-Stub_table<size, big_endian>::build_tls_opt_tail(
- unsigned char* p,
- typename Plt_stub_entries::const_iterator cs)
-{
- if (size == 64
- && cs->second.r2save_
- && !cs->second.localentry0_
- && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ else
{
- write_insn<big_endian>(p, bctrl);
+ write_insn<big_endian>(p, lwz_11_3 + 0);
p += 4;
- write_insn<big_endian>(p, ld_2_1 + this->targ_->stk_toc());
+ write_insn<big_endian>(p, lwz_12_3 + 4);
p += 4;
- write_insn<big_endian>(p, ld_11_1 + this->targ_->stk_linker());
+ write_insn<big_endian>(p, mr_0_3);
p += 4;
- write_insn<big_endian>(p, mtlr_11);
+ write_insn<big_endian>(p, cmpwi_11_0);
+ p += 4;
+ write_insn<big_endian>(p, add_3_12_2);
+ p += 4;
+ write_insn<big_endian>(p, beqlr);
+ p += 4;
+ write_insn<big_endian>(p, mr_3_0);
+ p += 4;
+ write_insn<big_endian>(p, nop);
p += 4;
- write_insn<big_endian>(p, blr);
- return true;
}
- return false;
+ *pp = p;
+}
+
+// Emit the tail of a __tls_get_addr_opt plt call stub.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::build_tls_opt_tail(unsigned char* p)
+{
+ write_insn<big_endian>(p, bctrl);
+ p += 4;
+ write_insn<big_endian>(p, ld_2_1 + this->targ_->stk_toc());
+ p += 4;
+ write_insn<big_endian>(p, ld_11_1 + this->targ_->stk_linker());
+ p += 4;
+ write_insn<big_endian>(p, mtlr_11);
+ p += 4;
+ write_insn<big_endian>(p, blr);
}
// Emit pc-relative plt call stub code.
template<int size, bool big_endian>
unsigned int
Stub_table<size, big_endian>::plt_call_size(
- typename Plt_stub_entries::const_iterator p) const
+ typename Plt_stub_entries::iterator p) const
{
if (size == 32)
{
- const Symbol* gsym = p->first.sym_;
- return (4 * 4
- + (this->targ_->is_tls_get_addr_opt(gsym) ? 8 * 4 : 0));
+ unsigned int bytes = 4 * 4;
+ if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+ bytes = 12 * 4;
+ return this->plt_call_align(bytes);
}
const Output_data_plt_powerpc<size, big_endian>* plt;
uint64_t plt_addr = this->plt_off(p, &plt);
plt_addr += plt->address();
- unsigned int bytes = 0;
- const Symbol* gsym = p->first.sym_;
- if (this->targ_->is_tls_get_addr_opt(gsym))
+ if (this->targ_->power10_stubs()
+ && this->targ_->power10_stubs_auto())
{
- if (p->second.r2save_ && !p->second.localentry0_)
- bytes = 13 * 4;
- else
- bytes = 7 * 4;
+ unsigned int bytes = 0;
+ if (p->second.notoc_)
+ {
+ if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+ bytes = 7 * 4;
+ uint64_t from = this->stub_address() + p->second.off_ + bytes;
+ uint64_t odd = from & 4;
+ uint64_t off = plt_addr - from;
+ if (off - odd + (1ULL << 33) < 1ULL << 34)
+ bytes += odd + 4 * 4;
+ else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ bytes += 7 * 4;
+ else
+ bytes += 8 * 4;
+ bytes = this->plt_call_align(bytes);
+ }
+ if (p->second.toc_)
+ {
+ p->second.tocoff_ = bytes;
+ if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+ {
+ bytes += 7 * 4;
+ if (p->second.r2save_ && !p->second.localentry0_)
+ bytes += 2 * 4 + 4 * 4;
+ }
+ if (p->second.r2save_)
+ bytes += 4;
+ uint64_t got_addr = this->targ_->toc_pointer();
+ uint64_t off = plt_addr - got_addr;
+ bytes += 3 * 4 + 4 * (ha(off) != 0);
+ p->second.tsize_ = bytes - p->second.tocoff_;
+ bytes = this->plt_call_align(bytes);
+ }
+ if (p->second.p9notoc_)
+ {
+ p->second.p9off_ = bytes;
+ if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+ bytes += 7 * 4;
+ uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
+ uint64_t off = plt_addr - from;
+ if (off + 0x8000 < 0x10000)
+ bytes += 7 * 4;
+ else if (off + 0x80008000ULL < 0x100000000ULL)
+ bytes += 8 * 4;
+ else
+ {
+ bytes += 8 * 4;
+ if (off + 0x800000000000ULL >= 0x1000000000000ULL
+ && ((off >> 32) & 0xffff) != 0)
+ bytes += 4;
+ if (((off >> 32) & 0xffffffffULL) != 0)
+ bytes += 4;
+ if (hi(off) != 0)
+ bytes += 4;
+ if (l(off) != 0)
+ bytes += 4;
+ }
+ bytes = this->plt_call_align(bytes);
+ }
+ return bytes;
}
+ else
+ {
+ unsigned int bytes = 0;
+ unsigned int tail = 0;
+ if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+ {
+ bytes = 7 * 4;
+ if (p->second.r2save_ && !p->second.localentry0_)
+ {
+ bytes = 9 * 4;
+ tail = 4 * 4;
+ }
+ }
- if (p->second.r2save_)
- bytes += 4;
+ if (p->second.r2save_)
+ bytes += 4;
- if (this->targ_->power10_stubs())
- {
- uint64_t from = this->stub_address() + p->second.off_ + bytes;
- if (bytes > 8 * 4)
- from -= 4 * 4;
- uint64_t odd = from & 4;
- uint64_t off = plt_addr - from;
- if (off - odd + (1ULL << 33) < 1ULL << 34)
- bytes += odd + 4 * 4;
- else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
- bytes += 7 * 4;
- else
- bytes += 8 * 4;
- return bytes;
- }
+ if (this->targ_->power10_stubs())
+ {
+ uint64_t from = this->stub_address() + p->second.off_ + bytes;
+ uint64_t odd = from & 4;
+ uint64_t off = plt_addr - from;
+ if (off - odd + (1ULL << 33) < 1ULL << 34)
+ bytes += odd + 4 * 4;
+ else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ bytes += 7 * 4;
+ else
+ bytes += 8 * 4;
+ return this->plt_call_align(bytes + tail);
+ }
- if (p->second.notoc_)
- {
- uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
- if (bytes > 32)
- from -= 4 * 4;
- uint64_t off = plt_addr - from;
- if (off + 0x8000 < 0x10000)
- bytes += 7 * 4;
- else if (off + 0x80008000ULL < 0x100000000ULL)
- bytes += 8 * 4;
- else
+ if (p->second.p9notoc_)
{
- bytes += 8 * 4;
- if (off + 0x800000000000ULL >= 0x1000000000000ULL
- && ((off >> 32) & 0xffff) != 0)
- bytes += 4;
- if (((off >> 32) & 0xffffffffULL) != 0)
- bytes += 4;
- if (hi(off) != 0)
- bytes += 4;
- if (l(off) != 0)
- bytes += 4;
+ uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
+ uint64_t off = plt_addr - from;
+ if (off + 0x8000 < 0x10000)
+ bytes += 7 * 4;
+ else if (off + 0x80008000ULL < 0x100000000ULL)
+ bytes += 8 * 4;
+ else
+ {
+ bytes += 8 * 4;
+ if (off + 0x800000000000ULL >= 0x1000000000000ULL
+ && ((off >> 32) & 0xffff) != 0)
+ bytes += 4;
+ if (((off >> 32) & 0xffffffffULL) != 0)
+ bytes += 4;
+ if (hi(off) != 0)
+ bytes += 4;
+ if (l(off) != 0)
+ bytes += 4;
+ }
+ return this->plt_call_align(bytes + tail);
}
- return bytes;
- }
- uint64_t 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_);
- got_addr += ppcobj->toc_base_offset();
- uint64_t off = plt_addr - got_addr;
- bytes += 3 * 4 + 4 * (ha(off) != 0);
- if (this->targ_->abiversion() < 2)
- {
- bool static_chain = parameters->options().plt_static_chain();
- bool thread_safe = this->targ_->plt_thread_safe();
- bytes += (4
- + 4 * static_chain
- + 8 * thread_safe
- + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
+ uint64_t got_addr = this->targ_->toc_pointer();
+ uint64_t off = plt_addr - got_addr;
+ bytes += 3 * 4 + 4 * (ha(off) != 0);
+ if (this->targ_->abiversion() < 2)
+ {
+ bool static_chain = parameters->options().plt_static_chain();
+ bool thread_safe = this->targ_->plt_thread_safe();
+ bytes += (4
+ + 4 * static_chain
+ + 8 * thread_safe
+ + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
+ }
+ return this->plt_call_align(bytes + tail);
}
- return bytes;
}
// Return long branch stub size.
template<int size, bool big_endian>
unsigned int
Stub_table<size, big_endian>::branch_stub_size(
- typename Branch_stub_entries::const_iterator p,
+ typename Branch_stub_entries::iterator p,
bool* need_lt)
{
Address loc = this->stub_address() + this->last_plt_size_ + p->second.off_;
}
uint64_t off = p->first.dest_ - loc;
+ unsigned int bytes = 0;
if (p->second.notoc_)
{
if (this->targ_->power10_stubs())
{
Address odd = loc & 4;
if (off + (1 << 25) < 2 << 25)
- return odd + 12;
- if (off - odd + (1ULL << 33) < 1ULL << 34)
- return odd + 16;
- if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
- return 28;
- return 32;
- }
- off -= 8;
- if (off + 0x8000 < 0x10000)
- return 24;
- if (off + 0x80008000ULL < 0x100000000ULL)
- {
- if (off + 24 + (1 << 25) < 2 << 25)
- return 28;
- return 32;
- }
- unsigned int bytes = 32;
- if (off + 0x800000000000ULL >= 0x1000000000000ULL
- && ((off >> 32) & 0xffff) != 0)
- bytes += 4;
- if (((off >> 32) & 0xffffffffULL) != 0)
- bytes += 4;
- if (hi(off) != 0)
- bytes += 4;
- if (l(off) != 0)
- bytes += 4;
- return bytes;
+ bytes = odd + 12;
+ else if (off - odd + (1ULL << 33) < 1ULL << 34)
+ bytes = odd + 16;
+ else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ bytes = 28;
+ else
+ bytes = 32;
+ if (!(p->second.toc_ && this->targ_->power10_stubs_auto()))
+ return bytes;
+ p->second.tocoff_ = bytes;
+ }
+ else
+ {
+ off -= 8;
+ if (off + 0x8000 < 0x10000)
+ return 24;
+ if (off + 0x80008000ULL < 0x100000000ULL)
+ {
+ if (off + 24 + (1 << 25) < 2 << 25)
+ return 28;
+ return 32;
+ }
+
+ bytes = 32;
+ if (off + 0x800000000000ULL >= 0x1000000000000ULL
+ && ((off >> 32) & 0xffff) != 0)
+ bytes += 4;
+ if (((off >> 32) & 0xffffffffULL) != 0)
+ bytes += 4;
+ if (hi(off) != 0)
+ bytes += 4;
+ if (l(off) != 0)
+ bytes += 4;
+ return bytes;
+ }
}
+ off += elfcpp::ppc64_decode_local_entry(p->second.other_);
if (off + (1 << 25) < 2 << 25)
- return 4;
- if (!this->targ_->power10_stubs())
+ return bytes + 4;
+ if (!this->targ_->power10_stubs()
+ || (p->second.toc_ && this->targ_->power10_stubs_auto()))
*need_lt = true;
- return 16;
+ return bytes + 16;
}
template<int size, bool big_endian>
++cs)
{
p = oview + cs->second.off_;
- this->build_tls_opt_head(&p, cs);
- if (cs->second.r2save_)
- {
- write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
- p += 4;
- }
const Output_data_plt_powerpc<size, big_endian>* plt;
Address pltoff = this->plt_off(cs, &plt);
Address plt_addr = pltoff + plt->address();
- Address from = this->stub_address() + (p - oview);
- Address delta = plt_addr - from;
- p = build_power10_offset<big_endian>(p, delta, from & 4, true);
- write_insn<big_endian>(p, mtctr_12);
- p += 4;
- if (!this->build_tls_opt_tail(p, cs))
- write_insn<big_endian>(p, bctr);
+ if (this->targ_->power10_stubs_auto())
+ {
+ if (cs->second.notoc_)
+ {
+ if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ this->build_tls_opt_head(&p, false);
+ Address from = this->stub_address() + (p - oview);
+ Address delta = plt_addr - from;
+ p = build_power10_offset<big_endian>(p, delta, from & 4,
+ true);
+ write_insn<big_endian>(p, mtctr_12);
+ p += 4;
+ write_insn<big_endian>(p, bctr);
+ p += 4;
+ p = oview + this->plt_call_align(p - oview);
+ }
+ if (cs->second.toc_)
+ {
+ if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ {
+ bool save_lr
+ = cs->second.r2save_ && !cs->second.localentry0_;
+ this->build_tls_opt_head(&p, save_lr);
+ }
+ Address got_addr = this->targ_->toc_pointer();
+ Address off = plt_addr - got_addr;
+
+ if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
+ this->plt_error(cs->first);
+
+ if (cs->second.r2save_)
+ {
+ write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
+ p += 4;
+ }
+ if (ha(off) != 0)
+ {
+ write_insn<big_endian>(p, addis_12_2 + ha(off));
+ p += 4;
+ write_insn<big_endian>(p, ld_12_12 + l(off));
+ p += 4;
+ }
+ else
+ {
+ write_insn<big_endian>(p, ld_12_2 + l(off));
+ p += 4;
+ }
+ write_insn<big_endian>(p, mtctr_12);
+ p += 4;
+ if (cs->second.r2save_
+ && !cs->second.localentry0_
+ && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ this->build_tls_opt_tail(p);
+ else
+ write_insn<big_endian>(p, bctr);
+ }
+ if (cs->second.p9notoc_)
+ {
+ if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ this->build_tls_opt_head(&p, false);
+ Address from = this->stub_address() + (p - oview);
+ Address delta = plt_addr - from;
+ p = build_notoc_offset<big_endian>(p, delta, true);
+ write_insn<big_endian>(p, mtctr_12);
+ p += 4;
+ write_insn<big_endian>(p, bctr);
+ p += 4;
+ p = oview + this->plt_call_align(p - oview);
+ }
+ }
+ else
+ {
+ if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ {
+ bool save_lr
+ = cs->second.r2save_ && !cs->second.localentry0_;
+ this->build_tls_opt_head(&p, save_lr);
+ }
+ if (cs->second.r2save_)
+ {
+ write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
+ p += 4;
+ }
+ Address from = this->stub_address() + (p - oview);
+ Address delta = plt_addr - from;
+ p = build_power10_offset<big_endian>(p, delta, from & 4, true);
+ write_insn<big_endian>(p, mtctr_12);
+ p += 4;
+ if (cs->second.r2save_
+ && !cs->second.localentry0_
+ && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ this->build_tls_opt_tail(p);
+ else
+ write_insn<big_endian>(p, bctr);
+ }
}
}
p = oview + off;
Address loc = this->stub_address() + off;
Address delta = bs->first.dest_ - loc;
- if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25)
+ if (this->targ_->power10_stubs_auto())
{
- unsigned char* startp = p;
- p = build_power10_offset<big_endian>(p, delta, loc & 4, false);
- delta -= p - startp;
+ if (bs->second.notoc_)
+ {
+ unsigned char* startp = p;
+ p = build_power10_offset<big_endian>(p, delta,
+ loc & 4, false);
+ delta -= p - startp;
+ startp = p;
+ if (delta + (1 << 25) < 2 << 25)
+ write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+ else
+ {
+ write_insn<big_endian>(p, mtctr_12);
+ p += 4;
+ write_insn<big_endian>(p, bctr);
+ }
+ p += 4;
+ delta -= p - startp;
+ }
+ if (bs->second.toc_)
+ {
+ delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
+ if (delta + (1 << 25) >= 2 << 25)
+ {
+ Address brlt_addr
+ = this->targ_->find_branch_lookup_table(bs->first.dest_);
+ gold_assert(brlt_addr != invalid_address);
+ brlt_addr += this->targ_->brlt_section()->address();
+ Address got_addr = this->targ_->toc_pointer();
+ Address brltoff = brlt_addr - got_addr;
+ if (ha(brltoff) == 0)
+ {
+ write_insn<big_endian>(p, ld_12_2 + l(brltoff));
+ p += 4;
+ }
+ else
+ {
+ write_insn<big_endian>(p, addis_12_2 + ha(brltoff));
+ p += 4;
+ write_insn<big_endian>(p, ld_12_12 + l(brltoff));
+ p += 4;
+ }
+ }
+ if (delta + (1 << 25) < 2 << 25)
+ write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+ else
+ {
+ write_insn<big_endian>(p, mtctr_12);
+ p += 4;
+ write_insn<big_endian>(p, bctr);
+ }
+ }
+ if (bs->second.p9notoc_)
+ {
+ unsigned char* startp = p;
+ p = build_notoc_offset<big_endian>(p, delta, false);
+ delta -= p - startp;
+ startp = p;
+ if (delta + (1 << 25) < 2 << 25)
+ write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+ else
+ {
+ write_insn<big_endian>(p, mtctr_12);
+ p += 4;
+ write_insn<big_endian>(p, bctr);
+ }
+ p += 4;
+ delta -= p - startp;
+ }
}
- if (delta + (1 << 25) < 2 << 25)
- write_insn<big_endian>(p, b | (delta & 0x3fffffc));
else
{
- write_insn<big_endian>(p, mtctr_12);
- p += 4;
- write_insn<big_endian>(p, bctr);
+ if (!bs->second.notoc_)
+ delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
+ if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25)
+ {
+ unsigned char* startp = p;
+ p = build_power10_offset<big_endian>(p, delta,
+ loc & 4, false);
+ delta -= p - startp;
+ }
+ if (delta + (1 << 25) < 2 << 25)
+ write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+ else
+ {
+ write_insn<big_endian>(p, mtctr_12);
+ p += 4;
+ write_insn<big_endian>(p, bctr);
+ }
}
}
}
else if (size == 64)
{
- const Output_data_got_powerpc<size, big_endian>* got
- = this->targ_->got_section();
- Address got_os_addr = got->output_section()->address();
if (!this->plt_call_stubs_.empty()
&& this->targ_->abiversion() >= 2)
Address plt_addr = pltoff + plt->address();
p = oview + cs->second.off_;
- this->build_tls_opt_head(&p, cs);
+ if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ {
+ bool save_lr = cs->second.r2save_ && !cs->second.localentry0_;
+ this->build_tls_opt_head(&p, save_lr);
+ }
if (cs->second.r2save_)
{
write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
p += 4;
}
- if (cs->second.notoc_)
+ if (cs->second.p9notoc_)
{
Address from = this->stub_address() + (p - oview) + 8;
Address off = plt_addr - from;
}
else
{
- 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();
+ Address got_addr = this->targ_->toc_pointer();
Address off = plt_addr - got_addr;
if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
}
write_insn<big_endian>(p, mtctr_12);
p += 4;
- if (!this->build_tls_opt_tail(p, cs))
+ if (cs->second.r2save_
+ && !cs->second.localentry0_
+ && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ this->build_tls_opt_tail(p);
+ else
write_insn<big_endian>(p, bctr);
}
}
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();
+ Address got_addr = this->targ_->toc_pointer();
Address off = plt_addr - got_addr;
if (off + 0x80008000 > 0xffffffff || (off & 7) != 0
}
p = oview + cs->second.off_;
- if (this->build_tls_opt_head(&p, cs))
- use_fake_dep = thread_safe;
+ if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ {
+ bool save_lr = cs->second.r2save_ && !cs->second.localentry0_;
+ this->build_tls_opt_head(&p, save_lr);
+ use_fake_dep = thread_safe;
+ }
if (cs->second.r2save_)
{
write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
write_insn<big_endian>(p, ld_2_2 + l(off + 8));
p += 4;
}
- if (this->build_tls_opt_tail(p, cs))
- ;
+ if (cs->second.r2save_
+ && !cs->second.localentry0_
+ && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ this->build_tls_opt_tail(p);
else if (thread_safe && !use_fake_dep)
{
write_insn<big_endian>(p, cmpldi_2_0);
p = oview + off;
Address loc = this->stub_address() + off;
Address delta = bs->first.dest_ - loc;
- if (bs->second.notoc_)
+ if (!bs->second.p9notoc_)
+ delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
+ if (bs->second.p9notoc_)
{
unsigned char* startp = p;
p = build_notoc_offset<big_endian>(p, off, false);
= this->targ_->find_branch_lookup_table(bs->first.dest_);
gold_assert(brlt_addr != invalid_address);
brlt_addr += this->targ_->brlt_section()->address();
- Address got_addr = got_os_addr + bs->first.toc_base_off_;
+ Address got_addr = this->targ_->toc_pointer();
Address brltoff = brlt_addr - got_addr;
if (ha(brltoff) == 0)
{
plt_addr += plt->address();
p = oview + cs->second.off_;
- this->build_tls_opt_head(&p, cs);
+ if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ this->build_tls_opt_head(&p, false);
if (parameters->options().output_is_position_independent())
{
Address got_addr;
else
{
if (g_o_t == invalid_address)
- {
- const Output_data_got_powerpc<size, big_endian>* got
- = this->targ_->got_section();
- g_o_t = got->address() + got->g_o_t();
- }
+ g_o_t = this->targ_->toc_pointer();
got_addr = g_o_t;
}
}
else
{
+ if (this->targ_->has_localentry0())
+ {
+ write_insn<big_endian>(p, std_2_1 + 24), p += 4;
+ }
write_insn<big_endian>(p, mflr_0), p += 4;
write_insn<big_endian>(p, bcl_20_31), p += 4;
write_insn<big_endian>(p, mflr_11), p += 4;
- write_insn<big_endian>(p, std_2_1 + 24), p += 4;
- write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4;
write_insn<big_endian>(p, mtlr_0), p += 4;
+ if (this->targ_->has_localentry0())
+ {
+ write_insn<big_endian>(p, ld_0_11 + l(-20)), p += 4;
+ }
+ else
+ {
+ write_insn<big_endian>(p, ld_0_11 + l(-16)), p += 4;
+ }
write_insn<big_endian>(p, sub_12_12_11), p += 4;
- write_insn<big_endian>(p, add_11_2_11), p += 4;
- write_insn<big_endian>(p, addi_0_12 + l(-48)), p += 4;
+ write_insn<big_endian>(p, add_11_0_11), p += 4;
+ write_insn<big_endian>(p, addi_0_12 + l(-44)), p += 4;
write_insn<big_endian>(p, ld_12_11 + 0), p += 4;
write_insn<big_endian>(p, srdi_0_0_2), p += 4;
write_insn<big_endian>(p, mtctr_12), p += 4;
}
else
{
- const Output_data_got_powerpc<size, big_endian>* got
- = this->targ_->got_section();
// The address of _GLOBAL_OFFSET_TABLE_.
- Address g_o_t = got->address() + got->g_o_t();
+ Address g_o_t = this->targ_->toc_pointer();
// Write out pltresolve branch table.
p = oview;
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::make_local_plt_entry(
+ Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* relobj,
unsigned int r_sym)
{
if (this->lplt_ == NULL)
- this->make_lplt_section(layout);
+ this->make_lplt_section(symtab, layout);
this->lplt_->add_local_entry(relobj, r_sym);
}
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::make_local_plt_entry(Symbol_table* symtab,
+ Layout* layout,
+ Symbol* gsym)
+{
+ if (this->lplt_ == NULL)
+ this->make_lplt_section(symtab, layout);
+ this->lplt_->add_entry(gsym, true);
+}
+
// Make a PLT entry for a local STT_GNU_IFUNC symbol.
template<int size, bool big_endian>
gold_assert(symtab != NULL && layout != NULL && object != NULL);
Reloc_section* rela_dyn = this->rela_dyn_section(layout);
Output_data_got_powerpc<size, big_endian>* got
- = this->got_section(symtab, layout);
+ = this->got_section(symtab, layout, GOT_TYPE_SMALL);
unsigned int got_offset = got->add_constant_pair(0, 0);
rela_dyn->add_local(object, 0, elfcpp::R_POWERPC_DTPMOD, got,
got_offset, 0);
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_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_POWERPC_PLTSEQ:
+ case elfcpp::R_POWERPC_PLTCALL:
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:
- 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_PPC64_PLT_PCREL34:
- case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
+ 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;
if (size == 32)
break;
// Fall through.
+ case elfcpp::R_PPC64_REL24_P9NOTOC:
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
const elfcpp::Sym<size, big_endian>& lsym,
bool is_discarded)
{
+ Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(object);
+
this->maybe_skip_tls_get_addr_call(target, r_type, NULL);
if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
|| (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
{
this->expect_tls_get_addr_call();
- const tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
+ tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
if (tls_type != tls::TLSOPT_NONE)
this->skip_next_tls_get_addr_call();
}
|| (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
{
this->expect_tls_get_addr_call();
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = target->optimize_tls_ld();
if (tls_type != tls::TLSOPT_NONE)
this->skip_next_tls_get_addr_call();
}
- Powerpc_relobj<size, big_endian>* ppc_object
- = static_cast<Powerpc_relobj<size, big_endian>*>(object);
-
if (is_discarded)
{
if (size == 64
case elfcpp::R_PPC64_TOC:
{
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, GOT_TYPE_SMALL);
if (parameters->options().output_is_position_independent())
{
Address off = reloc.get_r_offset();
break;
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
- Powerpc_relobj<size, big_endian>* symobj = ppc_object;
+ Address got_off = got->g_o_t();
rela_dyn->add_output_section_relative(got->output_section(),
elfcpp::R_POWERPC_RELATIVE,
output_section,
object, data_shndx, off,
- symobj->toc_base_offset());
+ got_off);
}
}
break;
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);
+ target->make_local_plt_entry(symtab, layout, object, r_sym);
}
break;
if (size == 32)
break;
// Fall through.
+ case elfcpp::R_PPC64_REL24_P9NOTOC:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_PPC64_GOT16_LO_DS:
{
// The symbol requires a GOT entry.
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT16
+ || r_type == elfcpp::R_PPC64_GOT16_DS)
+ ? GOT_TYPE_SMALL : GOT_TYPE_STANDARD);
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, got_type);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+ uint64_t addend = size == 32 ? 0 : reloc.get_r_addend();
if (!parameters->options().output_is_position_independent())
{
if (is_ifunc
&& (size == 32 || target->abiversion() >= 2))
- got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD);
+ got->add_local_plt(object, r_sym, got_type, addend);
else
- got->add_local(object, r_sym, GOT_TYPE_STANDARD);
+ got->add_local(object, r_sym, got_type, addend);
}
- else if (!object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD))
+ else if (!object->local_has_got_offset(r_sym, got_type, addend))
{
// If we are generating a shared object or a pie, this
// symbol's GOT entry will be set by a dynamic relocation.
unsigned int off;
off = got->add_constant(0);
- object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
+ object->set_local_got_offset(r_sym, got_type, off, addend);
Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
is_ifunc);
unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
: elfcpp::R_POWERPC_RELATIVE);
rela_dyn->add_local_relative(object, r_sym, dynrel,
- got, off, 0, false);
+ got, off, addend, false);
}
}
break;
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
// We need a GOT section.
- target->got_section(symtab, layout);
+ target->got_section(symtab, layout, GOT_TYPE_SMALL);
break;
case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
{
- const tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
+ tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
if (tls_type == tls::TLSOPT_NONE)
{
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_TLSGD16)
+ ? GOT_TYPE_SMALL_TLSGD : GOT_TYPE_TLSGD);
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, got_type);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+ uint64_t addend = size == 32 ? 0 : reloc.get_r_addend();
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
- got->add_local_tls_pair(object, r_sym, GOT_TYPE_TLSGD,
- rela_dyn, elfcpp::R_POWERPC_DTPMOD);
+ got->add_local_tls_pair(object, r_sym, got_type,
+ rela_dyn, elfcpp::R_POWERPC_DTPMOD,
+ addend);
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
{
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_NONE)
target->tlsld_got_offset(symtab, layout, object);
else if (tls_type == tls::TLSOPT_TO_LE)
case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
{
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_DTPREL16)
+ ? GOT_TYPE_SMALL_DTPREL : GOT_TYPE_DTPREL);
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, got_type);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
- got->add_local_tls(object, r_sym, GOT_TYPE_DTPREL);
+ uint64_t addend = size == 32 ? 0 : reloc.get_r_addend();
+ got->add_local_tls(object, r_sym, got_type, addend);
}
break;
case elfcpp::R_POWERPC_GOT_TPREL16_HI:
case elfcpp::R_POWERPC_GOT_TPREL16_HA:
{
- const tls::Tls_optimization tls_type = target->optimize_tls_ie(true);
+ tls::Tls_optimization tls_type = target->optimize_tls_ie(true);
if (tls_type == tls::TLSOPT_NONE)
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
- if (!object->local_has_got_offset(r_sym, GOT_TYPE_TPREL))
+ uint64_t addend = size == 32 ? 0 : reloc.get_r_addend();
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_TPREL16)
+ ? GOT_TYPE_SMALL_TPREL : GOT_TYPE_TPREL);
+ if (!object->local_has_got_offset(r_sym, got_type, addend))
{
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, got_type);
unsigned int off = got->add_constant(0);
- object->set_local_got_offset(r_sym, GOT_TYPE_TPREL, off);
+ object->set_local_got_offset(r_sym, got_type, off, addend);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
rela_dyn->add_symbolless_local_addend(object, r_sym,
elfcpp::R_POWERPC_TPREL,
- got, off, 0);
+ got, off, addend);
}
}
else if (tls_type == tls::TLSOPT_TO_LE)
switch (r_type)
{
- case elfcpp::R_POWERPC_TPREL16:
- case elfcpp::R_POWERPC_TPREL16_LO:
- case elfcpp::R_POWERPC_TPREL16_HI:
- case elfcpp::R_POWERPC_TPREL16_HA:
case elfcpp::R_PPC64_TPREL16_DS:
case elfcpp::R_PPC64_TPREL16_LO_DS:
case elfcpp::R_PPC64_TPREL16_HIGH:
case elfcpp::R_PPC64_TPREL16_HIGHEST:
case elfcpp::R_PPC64_TPREL16_HIGHESTA:
case elfcpp::R_PPC64_TPREL34:
+ if (size != 64)
+ break;
+ // Fall through.
+ case elfcpp::R_POWERPC_TPREL16:
+ case elfcpp::R_POWERPC_TPREL16_LO:
+ case elfcpp::R_POWERPC_TPREL16_HI:
+ case elfcpp::R_POWERPC_TPREL16_HA:
layout->set_has_static_tls();
break;
default:
break;
}
+ switch (r_type)
+ {
+ case elfcpp::R_POWERPC_TPREL16_HA:
+ if (target->tprel_opt())
+ {
+ section_size_type slen;
+ const unsigned char* view = NULL;
+ 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 & ((0x3fu << 26) | 0x1f << 16))
+ != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
+ target->set_no_tprel_opt();
+ }
+ }
+ break;
+
+ case elfcpp::R_PPC64_TPREL16_HIGH:
+ case elfcpp::R_PPC64_TPREL16_HIGHA:
+ case elfcpp::R_PPC64_TPREL16_HIGHER:
+ case elfcpp::R_PPC64_TPREL16_HIGHERA:
+ case elfcpp::R_PPC64_TPREL16_HIGHEST:
+ case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+ if (size != 64)
+ break;
+ // Fall through.
+ case elfcpp::R_POWERPC_TPREL16_HI:
+ target->set_no_tprel_opt();
+ break;
+ default:
+ break;
+ }
+
switch (r_type)
{
case elfcpp::R_PPC64_D34:
case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
- target->set_power10_stubs();
+ target->set_power10_relocs();
break;
default:
break;
unsigned int r_type,
Symbol* gsym)
{
- if (this->maybe_skip_tls_get_addr_call(target, r_type, gsym)
- == Track_tls::SKIP)
- return;
+ Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(object);
+
+ switch (this->maybe_skip_tls_get_addr_call(target, r_type, gsym))
+ {
+ case Track_tls::SKIP:
+ return;
+ default:
+ break;
+ }
if (target->replace_tls_get_addr(gsym))
// Change a __tls_get_addr reference to __tls_get_addr_opt
|| (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
{
this->expect_tls_get_addr_call();
- const bool final = gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+ bool final = gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
if (tls_type != tls::TLSOPT_NONE)
this->skip_next_tls_get_addr_call();
}
|| (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
{
this->expect_tls_get_addr_call();
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = target->optimize_tls_ld();
if (tls_type != tls::TLSOPT_NONE)
this->skip_next_tls_get_addr_call();
}
- Powerpc_relobj<size, big_endian>* ppc_object
- = static_cast<Powerpc_relobj<size, big_endian>*>(object);
-
// A STT_GNU_IFUNC symbol may require a PLT entry.
bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
bool pushed_ifunc = false;
case elfcpp::R_PPC64_TOC:
{
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, GOT_TYPE_SMALL);
if (parameters->options().output_is_position_independent())
{
Address off = reloc.get_r_offset();
break;
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
- Powerpc_relobj<size, big_endian>* symobj = ppc_object;
- if (data_shndx != ppc_object->opd_shndx())
- symobj = static_cast
- <Powerpc_relobj<size, big_endian>*>(gsym->object());
+ Address got_off = got->g_o_t();
rela_dyn->add_output_section_relative(got->output_section(),
elfcpp::R_POWERPC_RELATIVE,
output_section,
object, data_shndx, off,
- symobj->toc_base_offset());
+ got_off);
}
}
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_POWERPC_PLT16_HA:
case elfcpp::R_PPC64_PLT16_LO_DS:
if (!pushed_ifunc)
- target->make_plt_entry(symtab, layout, gsym);
+ {
+ if (branch_needs_plt_entry(gsym))
+ target->make_plt_entry(symtab, layout, gsym);
+ else
+ target->make_local_plt_entry(symtab, layout, gsym);
+ }
break;
case elfcpp::R_PPC64_REL24_NOTOC:
if (size == 32)
break;
// Fall through.
+ case elfcpp::R_PPC64_REL24_P9NOTOC:
case elfcpp::R_PPC_PLTREL24:
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, 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.
{
// The symbol requires a GOT entry.
Output_data_got_powerpc<size, big_endian>* got;
+ uint64_t addend = size == 32 ? 0 : reloc.get_r_addend();
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT16
+ || r_type == elfcpp::R_PPC64_GOT16_DS)
+ ? GOT_TYPE_SMALL : GOT_TYPE_STANDARD);
- got = target->got_section(symtab, layout);
+ got = target->got_section(symtab, layout, got_type);
if (gsym->final_value_is_known())
{
if (is_ifunc
&& (size == 32 || target->abiversion() >= 2))
- got->add_global_plt(gsym, GOT_TYPE_STANDARD);
+ got->add_global_plt(gsym, got_type, addend);
else
- got->add_global(gsym, GOT_TYPE_STANDARD);
+ got->add_global(gsym, got_type, addend);
}
- else if (!gsym->has_got_offset(GOT_TYPE_STANDARD))
+ else if (!gsym->has_got_offset(got_type, addend))
{
// If we are generating a shared object or a pie, this
// symbol's GOT entry will be set by a dynamic relocation.
unsigned int off = got->add_constant(0);
- gsym->set_got_offset(GOT_TYPE_STANDARD, off);
+ gsym->set_got_offset(got_type, off, addend);
Reloc_section* rela_dyn
= target->rela_dyn_section(symtab, layout, is_ifunc);
{
unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
: elfcpp::R_POWERPC_RELATIVE);
- rela_dyn->add_global_relative(gsym, dynrel, got, off, 0, false);
+ rela_dyn->add_global_relative(gsym, dynrel, got, off,
+ addend, false);
}
else
{
unsigned int dynrel = elfcpp::R_POWERPC_GLOB_DAT;
- rela_dyn->add_global(gsym, dynrel, got, off, 0);
+ rela_dyn->add_global(gsym, dynrel, got, off, addend);
}
}
}
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
// We need a GOT section.
- target->got_section(symtab, layout);
+ target->got_section(symtab, layout, GOT_TYPE_SMALL);
break;
case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
{
- const bool final = gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+ bool final = gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
if (tls_type == tls::TLSOPT_NONE)
{
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_TLSGD16)
+ ? GOT_TYPE_SMALL_TLSGD : GOT_TYPE_TLSGD);
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, got_type);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
- got->add_global_pair_with_rel(gsym, GOT_TYPE_TLSGD, rela_dyn,
+ uint64_t addend = size == 32 ? 0 : reloc.get_r_addend();
+ got->add_global_pair_with_rel(gsym, got_type, rela_dyn,
elfcpp::R_POWERPC_DTPMOD,
- elfcpp::R_POWERPC_DTPREL);
+ elfcpp::R_POWERPC_DTPREL,
+ addend);
}
else if (tls_type == tls::TLSOPT_TO_IE)
{
- if (!gsym->has_got_offset(GOT_TYPE_TPREL))
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_TLSGD16)
+ ? GOT_TYPE_SMALL_TPREL : GOT_TYPE_TPREL);
+ if (!gsym->has_got_offset(got_type))
{
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, got_type);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+ uint64_t addend = size == 32 ? 0 : reloc.get_r_addend();
if (gsym->is_undefined()
|| gsym->is_from_dynobj())
{
- got->add_global_with_rel(gsym, GOT_TYPE_TPREL, rela_dyn,
- elfcpp::R_POWERPC_TPREL);
+ got->add_global_with_rel(gsym, got_type, rela_dyn,
+ elfcpp::R_POWERPC_TPREL, addend);
}
else
{
unsigned int off = got->add_constant(0);
- gsym->set_got_offset(GOT_TYPE_TPREL, off);
+ gsym->set_got_offset(got_type, off);
unsigned int dynrel = elfcpp::R_POWERPC_TPREL;
rela_dyn->add_symbolless_global_addend(gsym, dynrel,
- got, off, 0);
+ got, off, addend);
}
}
}
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
{
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_NONE)
target->tlsld_got_offset(symtab, layout, object);
else if (tls_type == tls::TLSOPT_TO_LE)
case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
{
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_DTPREL16)
+ ? GOT_TYPE_SMALL_DTPREL : GOT_TYPE_DTPREL);
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, got_type);
+ uint64_t addend = size == 32 ? 0 : reloc.get_r_addend();
if (!gsym->final_value_is_known()
&& (gsym->is_from_dynobj()
|| gsym->is_undefined()
|| gsym->is_preemptible()))
- got->add_global_with_rel(gsym, GOT_TYPE_DTPREL,
+ got->add_global_with_rel(gsym, got_type,
target->rela_dyn_section(layout),
- elfcpp::R_POWERPC_DTPREL);
+ elfcpp::R_POWERPC_DTPREL, addend);
else
- got->add_global_tls(gsym, GOT_TYPE_DTPREL);
+ got->add_global_tls(gsym, got_type, addend);
}
break;
case elfcpp::R_POWERPC_GOT_TPREL16_HI:
case elfcpp::R_POWERPC_GOT_TPREL16_HA:
{
- const bool final = gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
+ bool final = gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
if (tls_type == tls::TLSOPT_NONE)
{
- if (!gsym->has_got_offset(GOT_TYPE_TPREL))
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_TPREL16)
+ ? GOT_TYPE_SMALL_TPREL : GOT_TYPE_TPREL);
+ if (!gsym->has_got_offset(got_type))
{
Output_data_got_powerpc<size, big_endian>* got
- = target->got_section(symtab, layout);
+ = target->got_section(symtab, layout, got_type);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+ uint64_t addend = size == 32 ? 0 : reloc.get_r_addend();
if (gsym->is_undefined()
|| gsym->is_from_dynobj())
{
- got->add_global_with_rel(gsym, GOT_TYPE_TPREL, rela_dyn,
- elfcpp::R_POWERPC_TPREL);
+ got->add_global_with_rel(gsym, got_type, rela_dyn,
+ elfcpp::R_POWERPC_TPREL, addend);
}
else
{
unsigned int off = got->add_constant(0);
- gsym->set_got_offset(GOT_TYPE_TPREL, off);
+ gsym->set_got_offset(got_type, off);
unsigned int dynrel = elfcpp::R_POWERPC_TPREL;
rela_dyn->add_symbolless_global_addend(gsym, dynrel,
- got, off, 0);
+ got, off, addend);
}
}
}
switch (r_type)
{
- case elfcpp::R_POWERPC_TPREL16:
- case elfcpp::R_POWERPC_TPREL16_LO:
- case elfcpp::R_POWERPC_TPREL16_HI:
- case elfcpp::R_POWERPC_TPREL16_HA:
case elfcpp::R_PPC64_TPREL16_DS:
case elfcpp::R_PPC64_TPREL16_LO_DS:
case elfcpp::R_PPC64_TPREL16_HIGH:
case elfcpp::R_PPC64_TPREL16_HIGHEST:
case elfcpp::R_PPC64_TPREL16_HIGHESTA:
case elfcpp::R_PPC64_TPREL34:
+ if (size != 64)
+ break;
+ // Fall through.
+ case elfcpp::R_POWERPC_TPREL16:
+ case elfcpp::R_POWERPC_TPREL16_LO:
+ case elfcpp::R_POWERPC_TPREL16_HI:
+ case elfcpp::R_POWERPC_TPREL16_HA:
layout->set_has_static_tls();
break;
default:
break;
}
+ switch (r_type)
+ {
+ case elfcpp::R_POWERPC_TPREL16_HA:
+ if (target->tprel_opt())
+ {
+ section_size_type slen;
+ const unsigned char* view = NULL;
+ 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 & ((0x3fu << 26) | 0x1f << 16))
+ != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
+ target->set_no_tprel_opt();
+ }
+ }
+ break;
+
+ case elfcpp::R_PPC64_TPREL16_HIGH:
+ case elfcpp::R_PPC64_TPREL16_HIGHA:
+ case elfcpp::R_PPC64_TPREL16_HIGHER:
+ case elfcpp::R_PPC64_TPREL16_HIGHERA:
+ case elfcpp::R_PPC64_TPREL16_HIGHEST:
+ case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+ if (size != 64)
+ break;
+ // Fall through.
+ case elfcpp::R_POWERPC_TPREL16_HI:
+ target->set_no_tprel_opt();
+ break;
+ default:
+ break;
+ }
+
switch (r_type)
{
case elfcpp::R_PPC64_D34:
case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
- target->set_power10_stubs();
+ target->set_power10_relocs();
break;
default:
break;
needs_special_offset_handling,
local_symbol_count,
plocal_symbols);
+
+ if (this->plt_localentry0_ && this->power10_relocs_)
+ {
+ gold_warning(_("--plt-localentry is incompatible with "
+ "power10 pc-relative code"));
+ this->plt_localentry0_ = false;
+ }
}
// Functor class for processing the global symbol table.
template<bool big_endian>
class Sort_toc_sections
{
+ const Output_section_data*
+ small_got_section() const
+ {
+ return (static_cast<Target_powerpc<64, big_endian>*>(
+ parameters->sized_target<64, big_endian>())
+ ->got_section(GOT_TYPE_SMALL));
+ }
+
+ int
+ rank(const Output_section::Input_section& isec) const
+ {
+ if (!isec.is_input_section())
+ {
+ if (isec.output_section_data() == this->small_got_section())
+ return 0;
+ return 2;
+ }
+ if (static_cast<const Powerpc_relobj<64, big_endian>*>(isec.relobj())
+ ->has_small_toc_reloc())
+ return 1;
+ return 3;
+ }
+
public:
bool
operator()(const Output_section::Input_section& is1,
const Output_section::Input_section& is2) const
{
- if (!is1.is_input_section() && is2.is_input_section())
- return true;
- bool small1
- = (is1.is_input_section()
- && (static_cast<const Powerpc_relobj<64, big_endian>*>(is1.relobj())
- ->has_small_toc_reloc()));
- bool small2
- = (is2.is_input_section()
- && (static_cast<const Powerpc_relobj<64, big_endian>*>(is2.relobj())
- ->has_small_toc_reloc()));
- return small1 && !small2;
+ return rank(is1) < rank(is2);
}
};
// Annoyingly, we need to make these sections now whether or
// not we need them. If we delay until do_relax then we
// need to mess with the relaxation machinery checkpointing.
- this->got_section(symtab, layout);
+ this->got_section(symtab, layout, GOT_TYPE_STANDARD);
this->make_brlt_section(layout);
+ // FIXME, maybe. Here we could run through all the got
+ // entries in the small got section, removing any duplicates
+ // found in the big got section and renumbering offsets.
+
if (parameters->options().toc_sort())
{
Output_section* os = this->got_->output_section();
? 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.
const elfcpp::Rela<size, big_endian> rela(preloc);
unsigned int r_type = elfcpp::elf_r_type<size>(rela.get_r_info());
+ Powerpc_relobj<size, big_endian>* const object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
switch (this->maybe_skip_tls_get_addr_call(target, r_type, gsym))
{
case Track_tls::NOT_EXPECTED:
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("__tls_get_addr call lacks marker reloc"));
+ // No warning. This will result in really old code without tls
+ // marker relocs being mis-optimised, but there shouldn't be too
+ // much of that code around. The problem with warning is that
+ // glibc and libphobos both construct direct calls to
+ // __tls_get_addr in a way that is harmless.
break;
case Track_tls::EXPECTED:
// We have already complained.
// Offset from start of insn to d-field reloc.
const int d_offset = big_endian ? 2 : 0;
- Powerpc_relobj<size, big_endian>* const object
- = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
Address value = 0;
bool has_stub_value = false;
bool localentry0 = false;
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
- bool has_plt_offset
- = (gsym != NULL
- ? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
- : object->local_has_plt_offset(r_sym));
- if (has_plt_offset
- && !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)))
+ 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
+ || r_type == elfcpp::R_POWERPC_PLTSEQ
+ || r_type == elfcpp::R_PPC64_PLTSEQ_NOTOC
+ || r_type == elfcpp::R_POWERPC_PLTCALL
+ || r_type == elfcpp::R_PPC64_PLTCALL_NOTOC)
+ {
+ // It would be possible to replace inline plt calls with direct
+ // calls if the PLTCALL is in range. The only difficulty is
+ // 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().
+ 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
const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
size_t reloc_count = shdr.get_sh_size() / reloc_size;
+ if (size == 64)
+ {
+ if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+ {
+ if (!ent->notoc_)
+ value += ent->p9off_;
+ }
+ else if (r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
+ value += ent->p9off_;
+ else
+ value += ent->tocoff_;
+ }
if (size == 64
&& ent->r2save_
- && r_type == elfcpp::R_PPC64_REL24_NOTOC)
- value += 4;
- else if (size == 64
- && ent->r2save_
- && relnum < reloc_count - 1)
+ && !(gsym != NULL
+ && target->is_tls_get_addr_opt(gsym)))
{
- Reltype next_rela(preloc + reloc_size);
- if (elfcpp::elf_r_type<size>(next_rela.get_r_info())
- == elfcpp::R_PPC64_TOCSAVE
- && next_rela.get_r_offset() == rela.get_r_offset() + 4)
- value += 4;
+ if (r_type == elfcpp::R_PPC64_REL24_NOTOC
+ || r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
+ {
+ if (!(target->power10_stubs()
+ && target->power10_stubs_auto()))
+ value += 4;
+ }
+ else if (relnum < reloc_count - 1)
+ {
+ Reltype next_rela(preloc + reloc_size);
+ if (elfcpp::elf_r_type<size>(next_rela.get_r_info())
+ == elfcpp::R_PPC64_TOCSAVE
+ && (next_rela.get_r_offset()
+ == rela.get_r_offset() + 4))
+ value += 4;
+ }
}
localentry0 = ent->localentry0_;
has_stub_value = true;
gold_assert(has_stub_value || !(os->flags() & elfcpp::SHF_ALLOC));
}
- if (has_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)
{
if (r_type != elfcpp::R_PPC64_PLT_PCREL34
&& r_type != elfcpp::R_PPC64_PLT_PCREL34_NOTOC)
- value -= (target->got_section()->output_section()->address()
- + object->toc_base_offset());
+ value -= target->toc_pointer();
}
else if (parameters->options().output_is_position_independent())
{
+ rela.get_r_addend());
}
else
- value -= (target->got_section()->address()
- + target->got_section()->g_o_t());
+ value -= target->toc_pointer();
}
}
- else if (!has_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 (!has_plt_offset
+ else if (pltcall_to_direct
&& (r_type == elfcpp::R_PPC64_PLT_PCREL34
|| r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC))
{
elfcpp::Swap<32, big_endian>::writeval(iview + 1, pnop & 0xffffffff);
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
- || r_type == elfcpp::R_POWERPC_GOT16_HA
- || r_type == elfcpp::R_PPC64_GOT16_DS
- || r_type == elfcpp::R_PPC64_GOT16_LO_DS
- || r_type == elfcpp::R_PPC64_GOT_PCREL34)
+ else if (is_got_reloc(r_type))
{
+ uint64_t addend = size == 32 ? 0 : rela.get_r_addend();
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT16
+ || r_type == elfcpp::R_PPC64_GOT16_DS)
+ ? GOT_TYPE_SMALL : GOT_TYPE_STANDARD);
if (gsym != NULL)
- {
- gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD));
- value = gsym->got_offset(GOT_TYPE_STANDARD);
- }
+ value = gsym->got_offset(got_type, addend);
else
- {
- gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD));
- value = object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
- }
+ value = object->local_got_offset(r_sym, got_type, addend);
if (r_type == elfcpp::R_PPC64_GOT_PCREL34)
- value += target->got_section()->address();
+ value += target->got_section(got_type)->address();
else
- value -= target->got_section()->got_base_offset(object);
+ value -= target->got_base_offset(got_type);
}
else if (r_type == elfcpp::R_PPC64_TOC)
{
- value = (target->got_section()->output_section()->address()
- + object->toc_base_offset());
+ value = target->toc_pointer();
}
else if (gsym != NULL
&& (r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC64_GOT_TLSGD_PCREL34)
{
// First instruction of a global dynamic sequence, arg setup insn.
- const bool final = gsym == NULL || gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
- enum Got_type got_type = GOT_TYPE_STANDARD;
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_TLSGD16)
+ ? GOT_TYPE_SMALL : GOT_TYPE_STANDARD);
if (tls_type == tls::TLSOPT_NONE)
- got_type = GOT_TYPE_TLSGD;
+ got_type = Got_type(got_type | GOT_TYPE_TLSGD);
else if (tls_type == tls::TLSOPT_TO_IE)
- got_type = GOT_TYPE_TPREL;
- if (got_type != GOT_TYPE_STANDARD)
+ got_type = Got_type(got_type | GOT_TYPE_TPREL);
+ if ((got_type & ~GOT_TYPE_SMALL) != GOT_TYPE_STANDARD)
{
+ uint64_t addend = size == 32 ? 0 : rela.get_r_addend();
if (gsym != NULL)
- {
- gold_assert(gsym->has_got_offset(got_type));
- value = gsym->got_offset(got_type);
- }
+ value = gsym->got_offset(got_type, addend);
else
- {
- gold_assert(object->local_has_got_offset(r_sym, got_type));
- value = object->local_got_offset(r_sym, got_type);
- }
+ value = object->local_got_offset(r_sym, got_type, addend);
if (r_type == elfcpp::R_PPC64_GOT_TLSGD_PCREL34)
- value += target->got_section()->address();
+ value += target->got_section(got_type)->address();
else
- value -= target->got_section()->got_base_offset(object);
+ value -= target->got_base_offset(got_type);
}
if (tls_type == tls::TLSOPT_TO_IE)
{
|| r_type == elfcpp::R_PPC64_GOT_TLSLD_PCREL34)
{
// First instruction of a local dynamic sequence, arg setup insn.
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_NONE)
{
value = target->tlsld_got_offset();
if (r_type == elfcpp::R_PPC64_GOT_TLSLD_PCREL34)
- value += target->got_section()->address();
+ value += target->got_section(GOT_TYPE_SMALL)->address();
else
- value -= target->got_section()->got_base_offset(object);
+ value -= target->got_base_offset(GOT_TYPE_SMALL);
}
else
{
{
// Accesses relative to a local dynamic sequence address,
// no optimisation here.
+ uint64_t addend = size == 32 ? 0 : rela.get_r_addend();
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_DTPREL16)
+ ? GOT_TYPE_SMALL_DTPREL : GOT_TYPE_DTPREL);
if (gsym != NULL)
- {
- gold_assert(gsym->has_got_offset(GOT_TYPE_DTPREL));
- value = gsym->got_offset(GOT_TYPE_DTPREL);
- }
+ value = gsym->got_offset(got_type, addend);
else
- {
- gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_DTPREL));
- value = object->local_got_offset(r_sym, GOT_TYPE_DTPREL);
- }
+ value = object->local_got_offset(r_sym, got_type, addend);
if (r_type == elfcpp::R_PPC64_GOT_DTPREL_PCREL34)
- value += target->got_section()->address();
+ value += target->got_section(got_type)->address();
else
- value -= target->got_section()->got_base_offset(object);
+ value -= target->got_base_offset(got_type);
}
else if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO
|| r_type == elfcpp::R_PPC64_GOT_TPREL_PCREL34)
{
// First instruction of initial exec sequence.
- const bool final = gsym == NULL || gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
if (tls_type == tls::TLSOPT_NONE)
{
+ uint64_t addend = size == 32 ? 0 : rela.get_r_addend();
+ Got_type got_type = ((size == 32
+ || r_type == elfcpp::R_POWERPC_GOT_TPREL16)
+ ? GOT_TYPE_SMALL_TPREL : GOT_TYPE_TPREL);
if (gsym != NULL)
- {
- gold_assert(gsym->has_got_offset(GOT_TYPE_TPREL));
- value = gsym->got_offset(GOT_TYPE_TPREL);
- }
+ value = gsym->got_offset(got_type, addend);
else
- {
- gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_TPREL));
- value = object->local_got_offset(r_sym, GOT_TYPE_TPREL);
- }
+ value = object->local_got_offset(r_sym, got_type, addend);
if (r_type == elfcpp::R_PPC64_GOT_TPREL_PCREL34)
- value += target->got_section()->address();
+ value += target->got_section(got_type)->address();
else
- value -= target->got_section()->got_base_offset(object);
+ value -= target->got_base_offset(got_type);
}
else
{
// Second instruction of a global dynamic sequence,
// the __tls_get_addr call
this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset());
- const bool final = gsym == NULL || gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
if (tls_type != tls::TLSOPT_NONE)
{
if (tls_type == tls::TLSOPT_TO_IE)
unsigned int r_type2
= elfcpp::elf_r_type<size>(next_rela.get_r_info());
if ((r_type2 == elfcpp::R_PPC64_REL24_NOTOC
+ || r_type2 == elfcpp::R_PPC64_REL24_P9NOTOC
|| r_type2 == elfcpp::R_PPC64_PLTCALL_NOTOC)
&& next_rela.get_r_offset() == rela.get_r_offset())
is_pcrel = true;
// Second instruction of a local dynamic sequence,
// the __tls_get_addr call
this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset());
- const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+ tls::Tls_optimization tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_TO_LE)
{
bool is_pcrel = false;
unsigned int r_type2
= elfcpp::elf_r_type<size>(next_rela.get_r_info());
if ((r_type2 == elfcpp::R_PPC64_REL24_NOTOC
+ || r_type2 == elfcpp::R_PPC64_REL24_P9NOTOC
|| r_type2 == elfcpp::R_PPC64_PLTCALL_NOTOC)
&& next_rela.get_r_offset() == rela.get_r_offset())
is_pcrel = true;
else if (r_type == elfcpp::R_POWERPC_TLS)
{
// Second instruction of an initial exec sequence
- const bool final = gsym == NULL || gsym->final_value_is_known();
- const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
if (tls_type == tls::TLSOPT_TO_LE)
{
Address roff = rela.get_r_offset() & 3;
}
else if (!has_stub_value)
{
- if (!has_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);
|| r_type == elfcpp::R_POWERPC_PLT16_HA)))
addend = rela.get_r_addend();
value = psymval->value(object, addend);
+ unsigned int local_ent = 0;
if (size == 64 && is_branch_reloc<size>(r_type))
{
if (target->abiversion() >= 2)
{
if (gsym != NULL)
- value += object->ppc64_local_entry_offset(gsym);
+ local_ent = object->ppc64_local_entry_offset(gsym);
else
- value += object->ppc64_local_entry_offset(r_sym);
+ local_ent = object->ppc64_local_entry_offset(r_sym);
}
else
{
&value, &dest_shndx);
}
}
- Address max_branch_offset = max_branch_delta<size>(r_type);
- if (max_branch_offset != 0
- && (value - address + max_branch_offset >= 2 * max_branch_offset
+ Address max_branch = max_branch_delta<size>(r_type);
+ if (max_branch != 0
+ && (value + local_ent - address + max_branch >= 2 * max_branch
|| (size == 64
- && r_type == elfcpp::R_PPC64_REL24_NOTOC
+ && (r_type == elfcpp::R_PPC64_REL24_NOTOC
+ || r_type == elfcpp::R_PPC64_REL24_NOTOC)
&& (gsym != NULL
? object->ppc64_needs_toc(gsym)
: object->ppc64_needs_toc(r_sym)))))
if (stub_table != NULL)
{
const typename Stub_table<size, big_endian>::Branch_stub_ent* ent
- = stub_table->find_long_branch_entry(object, value);
+ = stub_table->find_long_branch_entry(value);
if (ent != NULL)
{
if (ent->save_res_)
value = (value - target->savres_section()->address()
+ + stub_table->stub_address()
+ + stub_table->plt_size()
+ stub_table->branch_size());
else
- value = (stub_table->stub_address() + stub_table->plt_size()
- + ent->off_);
+ {
+ value = (stub_table->stub_address()
+ + stub_table->plt_size()
+ + ent->off_);
+ if (size == 64)
+ {
+ if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+ {
+ if (!ent->notoc_)
+ value += ent->p9off_;
+ }
+ else if (r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
+ value += ent->p9off_;
+ else
+ value += ent->tocoff_;
+ }
+ }
has_stub_value = true;
}
}
}
+ if (!has_stub_value)
+ value += local_ent;
}
switch (r_type)
if (size == 32)
break;
// Fall through.
+ case elfcpp::R_PPC64_REL24_P9NOTOC:
case elfcpp::R_PPC64_REL64:
case elfcpp::R_POWERPC_REL32:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
// Subtract the TOC base address.
- value -= (target->got_section()->output_section()->address()
- + object->toc_base_offset());
+ value -= target->toc_pointer();
break;
case elfcpp::R_POWERPC_SECTOFF:
break;
}
- if (size == 64
- && (gsym
- ? relative_value_is_known(gsym)
- : relative_value_is_known(psymval)))
+ if (gsym
+ ? relative_value_is_known(gsym)
+ : relative_value_is_known(psymval))
{
Insn* iview;
Insn* iview2;
case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
case elfcpp::R_POWERPC_GOT16_HA:
case elfcpp::R_PPC64_TOC16_HA:
- if (parameters->options().toc_optimize())
+ if (size == 64 && parameters->options().toc_optimize())
{
iview = reinterpret_cast<Insn*>(view - d_offset);
insn = elfcpp::Swap<32, big_endian>::readval(iview);
case elfcpp::R_PPC64_GOT16_LO_DS:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_PPC64_TOC16_LO_DS:
- if (parameters->options().toc_optimize())
+ if (size == 64 && parameters->options().toc_optimize())
{
iview = reinterpret_cast<Insn*>(view - d_offset);
insn = elfcpp::Swap<32, big_endian>::readval(iview);
break;
case elfcpp::R_PPC64_GOT_PCREL34:
- if (parameters->options().toc_optimize())
+ if (size == 64 && parameters->options().toc_optimize())
{
iview = reinterpret_cast<Insn*>(view);
pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
break;
case elfcpp::R_PPC64_PCREL34:
- {
- iview = reinterpret_cast<Insn*>(view);
- pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
- pinsn <<= 32;
- pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
- if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
- != ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
- | (14ULL << 26) /* paddi */))
- break;
+ if (size == 64)
+ {
+ iview = reinterpret_cast<Insn*>(view);
+ pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+ pinsn <<= 32;
+ pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+ if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+ != ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+ | (14ULL << 26) /* paddi */))
+ break;
- pcrelopt:
- const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
- elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
- size_t reloc_count = shdr.get_sh_size() / reloc_size;
- if (relnum >= reloc_count - 1)
- break;
+ pcrelopt:
+ const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
+ size_t reloc_count = shdr.get_sh_size() / reloc_size;
+ if (relnum >= reloc_count - 1)
+ break;
- Reltype next_rela(preloc + reloc_size);
- if ((elfcpp::elf_r_type<size>(next_rela.get_r_info())
- != elfcpp::R_PPC64_PCREL_OPT)
- || next_rela.get_r_offset() != rela.get_r_offset())
- break;
+ Reltype next_rela(preloc + reloc_size);
+ if ((elfcpp::elf_r_type<size>(next_rela.get_r_info())
+ != elfcpp::R_PPC64_PCREL_OPT)
+ || next_rela.get_r_offset() != rela.get_r_offset())
+ break;
- Address off = next_rela.get_r_addend();
- if (off == 0)
- off = 8; // zero means next insn.
- if (off + rela.get_r_offset() + 4 > view_size)
- break;
+ Address off = next_rela.get_r_addend();
+ if (off == 0)
+ off = 8; // zero means next insn.
+ if (off + rela.get_r_offset() + 4 > view_size)
+ break;
- iview2 = reinterpret_cast<Insn*>(view + off);
- pinsn2 = elfcpp::Swap<32, big_endian>::readval(iview2);
- pinsn2 <<= 32;
- if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
- break;
- if (xlate_pcrel_opt(&pinsn, &pinsn2))
- {
- elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
- elfcpp::Swap<32, big_endian>::writeval(iview + 1,
- pinsn & 0xffffffff);
- elfcpp::Swap<32, big_endian>::writeval(iview2, pinsn2 >> 32);
- }
- }
+ iview2 = reinterpret_cast<Insn*>(view + off);
+ pinsn2 = elfcpp::Swap<32, big_endian>::readval(iview2);
+ pinsn2 <<= 32;
+ if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
+ break;
+ if (xlate_pcrel_opt(&pinsn, &pinsn2))
+ {
+ elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+ pinsn & 0xffffffff);
+ elfcpp::Swap<32, big_endian>::writeval(iview2, pinsn2 >> 32);
+ }
+ }
break;
case elfcpp::R_POWERPC_TPREL16_HA:
- if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+ if (target->tprel_opt() && value + 0x8000 < 0x10000)
{
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) | ((size == 32 ? 2 : 13) << 16)))
- ;
- else
- {
- elfcpp::Swap<32, big_endian>::writeval(iview, nop);
- return true;
- }
+ elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+ return true;
}
break;
break;
// Fall through.
case elfcpp::R_POWERPC_TPREL16_LO:
- if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+ if (target->tprel_opt() && value + 0x8000 < 0x10000)
{
Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
break;
case elfcpp::R_PPC64_ENTRY:
- value = (target->got_section()->output_section()->address()
- + object->toc_base_offset());
- if (value + 0x80008000 <= 0xffffffff
- && !parameters->options().output_is_position_independent())
- {
- Insn* iview = reinterpret_cast<Insn*>(view);
- Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
- Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
-
- if ((insn1 & ~0xfffc) == ld_2_12
- && insn2 == add_2_2_12)
- {
- insn1 = lis_2 + ha(value);
- elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
- insn2 = addi_2_2 + l(value);
- elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
- return true;
- }
- }
- else
+ if (size == 64)
{
- value -= address;
- if (value + 0x80008000 <= 0xffffffff)
+ value = target->toc_pointer();
+ if (value + 0x80008000 <= 0xffffffff
+ && !parameters->options().output_is_position_independent())
{
Insn* iview = reinterpret_cast<Insn*>(view);
Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
if ((insn1 & ~0xfffc) == ld_2_12
&& insn2 == add_2_2_12)
{
- insn1 = addis_2_12 + ha(value);
+ insn1 = lis_2 + ha(value);
elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
insn2 = addi_2_2 + l(value);
elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
return true;
}
}
+ else
+ {
+ value -= address;
+ if (value + 0x80008000 <= 0xffffffff)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+ if ((insn1 & ~0xfffc) == ld_2_12
+ && insn2 == add_2_2_12)
+ {
+ insn1 = addis_2_12 + ha(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+ insn2 = addi_2_2 + l(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+ return true;
+ }
+ }
+ }
}
break;
// lis 2,.TOC.@ha
// addi 2,2,.TOC.@l
// if .TOC. is in range. */
- if (value + address - 4 + 0x80008000 <= 0xffffffff
+ if (size == 64
+ && value + address - 4 + 0x80008000 <= 0xffffffff
&& relnum + 1 > 1
&& preloc != NULL
&& target->abiversion() >= 2
if (size == 32)
break;
// Fall through.
+ case elfcpp::R_PPC64_REL24_P9NOTOC:
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
if (size == 32)
goto unsupp; // R_PPC_EMB_RELSDA
// Fall through.
+ case elfcpp::R_PPC64_REL24_P9NOTOC:
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
loc.object = relinfo->object;
loc.shndx = relinfo->data_shndx;
loc.offset = rela.get_r_offset();
- Tocsave_loc::const_iterator p = target->tocsave_loc().find(loc);
- if (p != target->tocsave_loc().end())
+ const Tocsave_loc *tocsave = target->tocsave_loc();
+ if (tocsave->find(loc) != tocsave->end())
{
// If we've generated plt calls using this tocsave, then
// the nop needs to be changed to save r2.
&& 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;
{
// First instruction of a global dynamic sequence,
// arg setup insn.
- const bool final = gsym == NULL || gsym->final_value_is_known();
- switch (this->optimize_tls_gd(final))
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = this->optimize_tls_gd(final);
+ switch (tls_type)
{
case tls::TLSOPT_TO_IE:
r_type += (elfcpp::R_POWERPC_GOT_TPREL16
{
// First instruction of a local dynamic sequence,
// arg setup insn.
- if (this->optimize_tls_ld() == tls::TLSOPT_TO_LE)
+ tls::Tls_optimization tls_type = this->optimize_tls_ld();
+ if (tls_type == tls::TLSOPT_TO_LE)
{
if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_HA)
{
// First instruction of initial exec sequence.
- const bool final = gsym == NULL || gsym->final_value_is_known();
+ bool final = gsym == NULL || gsym->final_value_is_known();
if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
{
if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
{
// Second instruction of a global dynamic sequence,
// the __tls_get_addr call
- const bool final = gsym == NULL || gsym->final_value_is_known();
- switch (this->optimize_tls_gd(final))
+ bool final = gsym == NULL || gsym->final_value_is_known();
+ tls::Tls_optimization tls_type = this->optimize_tls_gd(final);
+ switch (tls_type)
{
case tls::TLSOPT_TO_IE:
r_type = elfcpp::R_POWERPC_NONE;
{
// Second instruction of a local dynamic sequence,
// the __tls_get_addr call
- if (this->optimize_tls_ld() == tls::TLSOPT_TO_LE)
+ tls::Tls_optimization tls_type = this->optimize_tls_ld();
+ if (tls_type == tls::TLSOPT_TO_LE)
{
const Output_section* os = relinfo->layout->tls_segment()
->first_section();
else if (r_type == elfcpp::R_POWERPC_TLS)
{
// Second instruction of an initial exec sequence
- const bool final = gsym == NULL || gsym->final_value_is_known();
+ bool final = gsym == NULL || gsym->final_value_is_known();
if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
{
r_type = elfcpp::R_POWERPC_TPREL16_LO;
Target_powerpc<size, big_endian>::do_tls_offset_for_local(
const Relobj* object,
unsigned int symndx,
- unsigned int got_indx) const
+ Output_data_got_base* got,
+ unsigned int got_indx,
+ uint64_t addend) const
{
const Powerpc_relobj<size, big_endian>* ppc_object
= static_cast<const Powerpc_relobj<size, big_endian>*>(object);
if (ppc_object->local_symbol(symndx)->is_tls_symbol())
{
- for (Got_type got_type = GOT_TYPE_TLSGD;
- got_type <= GOT_TYPE_TPREL;
+ for (Got_type got_type = (size == 32
+ ? GOT_TYPE_SMALL_TLSGD : GOT_TYPE_TLSGD);
+ got_type <= GOT_TYPE_SMALL_TPREL;
got_type = Got_type(got_type + 1))
- if (ppc_object->local_has_got_offset(symndx, got_type))
+ if (got_type != GOT_TYPE_SMALL
+ && ppc_object->local_has_got_offset(symndx, got_type, addend))
{
- unsigned int off = ppc_object->local_got_offset(symndx, got_type);
- if (got_type == GOT_TYPE_TLSGD)
+ unsigned int off
+ = ppc_object->local_got_offset(symndx, got_type, addend);
+ if ((got_type & ~GOT_TYPE_SMALL) == GOT_TYPE_TLSGD)
off += size / 8;
- if (off == got_indx * (size / 8))
+ if (off == got_indx * (size / 8)
+ && (size == 32 || got == this->got_section(got_type)))
{
- if (got_type == GOT_TYPE_TPREL)
+ if ((got_type & ~GOT_TYPE_SMALL) == GOT_TYPE_TPREL)
return -tp_offset;
else
return -dtp_offset;
int64_t
Target_powerpc<size, big_endian>::do_tls_offset_for_global(
Symbol* gsym,
- unsigned int got_indx) const
+ Output_data_got_base* got,
+ unsigned int got_indx,
+ uint64_t addend) const
{
if (gsym->type() == elfcpp::STT_TLS)
{
- for (Got_type got_type = GOT_TYPE_TLSGD;
- got_type <= GOT_TYPE_TPREL;
+ for (Got_type got_type = (size == 32
+ ? GOT_TYPE_SMALL_TLSGD : GOT_TYPE_TLSGD);
+ got_type <= GOT_TYPE_SMALL_TPREL;
got_type = Got_type(got_type + 1))
- if (gsym->has_got_offset(got_type))
+ if (got_type != GOT_TYPE_SMALL
+ && gsym->has_got_offset(got_type, addend))
{
- unsigned int off = gsym->got_offset(got_type);
- if (got_type == GOT_TYPE_TLSGD)
+ unsigned int off = gsym->got_offset(got_type, addend);
+ if ((got_type & ~GOT_TYPE_SMALL) == GOT_TYPE_TLSGD)
off += size / 8;
- if (off == got_indx * (size / 8))
+ if (off == got_indx * (size / 8)
+ && (size == 32 || got == this->got_section(got_type)))
{
- if (got_type == GOT_TYPE_TPREL)
+ if ((got_type & ~GOT_TYPE_SMALL) == GOT_TYPE_TPREL)
return -tp_offset;
else
return -dtp_offset;