// powerpc.cc -- powerpc target support for gold.
-// Copyright (C) 2008-2017 Free Software Foundation, Inc.
+// Copyright (C) 2008-2018 Free Software Foundation, Inc.
// Written by David S. Miller <davem@davemloft.net>
// and David Edelsohn <edelsohn@gnu.org>
const Output_section::Input_section* owner;
};
-inline bool
-is_branch_reloc(unsigned int r_type);
+inline bool is_branch_reloc(unsigned int);
+
+template<int size>
+inline bool is_plt16_reloc(unsigned int);
// Counter incremented on every Powerpc_relobj constructed.
static uint32_t object_id = 0;
Target_powerpc()
: Sized_target<size, big_endian>(&powerpc_info),
- got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL),
+ got_(NULL), plt_(NULL), iplt_(NULL), lplt_(NULL), brlt_section_(NULL),
glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
tlsld_got_offset_(-1U),
stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(),
return this->iplt_;
}
+ // Get the LPLT section.
+ const Output_data_plt_powerpc<size, big_endian>*
+ lplt_section() const
+ {
+ return this->lplt_;
+ }
+
+ // Return the plt offset and section for the given global sym.
+ Address
+ plt_off(const Symbol* gsym,
+ const Output_data_plt_powerpc<size, big_endian>** sec) const
+ {
+ if (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false))
+ *sec = this->iplt_section();
+ else
+ *sec = this->plt_section();
+ return gsym->plt_offset();
+ }
+
+ // Return the plt offset and section for the given local sym.
+ Address
+ plt_off(const Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int local_sym_index,
+ const Output_data_plt_powerpc<size, big_endian>** sec) const
+ {
+ const Symbol_value<size>* lsym = relobj->local_symbol(local_sym_index);
+ if (lsym->is_ifunc_symbol())
+ *sec = this->iplt_section();
+ else
+ *sec = this->lplt_section();
+ return relobj->local_plt_offset(local_sym_index);
+ }
+
// Get the .glink section.
const Output_data_glink<size, big_endian>*
glink_section() const
unsigned int r_type, const Symbol* gsym)
{
bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24
- || r_type == elfcpp::R_PPC_PLTREL24)
+ || r_type == elfcpp::R_PPC_PLTREL24
+ || is_plt16_reloc<size>(r_type)
+ || r_type == elfcpp::R_POWERPC_PLTSEQ
+ || r_type == elfcpp::R_POWERPC_PLTCALL)
&& gsym != NULL
&& (gsym == target->tls_get_addr()
|| gsym == target->tls_get_addr_opt()));
{
gold::Default_comdat_behavior default_behavior;
Comdat_behavior ret = default_behavior.get(name);
- if (ret == CB_WARNING)
+ if (ret == CB_ERROR)
{
if (size == 32
&& (strcmp(name, ".fixup") == 0
void
make_iplt_section(Symbol_table*, Layout*);
+ void
+ make_lplt_section(Layout*);
+
void
make_brlt_section(Layout*);
Sized_relobj_file<size, big_endian>*,
unsigned int);
+ // Create a PLT entry for a local non-IFUNC symbol.
+ void
+ make_local_plt_entry(Layout*,
+ Sized_relobj_file<size, big_endian>*,
+ unsigned int);
+
// Create a GOT entry for local dynamic __tls_get_addr.
unsigned int
// section is emitted and marked with __rela_iplt_start and
// __rela_iplt_end symbols.
Output_data_plt_powerpc<size, big_endian>* iplt_;
+ // A PLT style section for local, non-ifunc symbols
+ Output_data_plt_powerpc<size, big_endian>* lplt_;
// Section holding long branch destinations.
Output_data_brlt_powerpc<size, big_endian>* brlt_section_;
// The .glink section.
NULL, // attributes_vendor
"_start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
template<>
NULL, // attributes_vendor
"_start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
template<>
NULL, // attributes_vendor
"_start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
template<>
NULL, // attributes_vendor
"_start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
inline bool
|| r_type == elfcpp::R_POWERPC_ADDR14_BRNTAKEN);
}
+// Reloc resolves to plt entry.
+template<int size>
+inline bool
+is_plt16_reloc(unsigned int r_type)
+{
+ return (r_type == elfcpp::R_POWERPC_PLT16_LO
+ || r_type == elfcpp::R_POWERPC_PLT16_HI
+ || r_type == elfcpp::R_POWERPC_PLT16_HA
+ || (size == 64 && r_type == elfcpp::R_PPC64_PLT16_LO_DS));
+}
+
// If INSN is an opcode that may be used with an @tls operand, return
// the transformed insn for TLS optimisation, otherwise return 0. If
// REG is non-zero only match an insn with RB or RA equal to REG.
}
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.
return false;
Symbol* sym = this->object_->global_symbol(this->r_sym_);
- if (target->replace_tls_get_addr(sym))
- sym = target->tls_get_addr_opt();
if (sym != NULL && sym->is_forwarder())
sym = symtab->resolve_forwards(sym);
+ if (target->replace_tls_get_addr(sym))
+ sym = target->tls_get_addr_opt();
const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym);
if (gsym != NULL
? (gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
Target_powerpc<size, big_endian>* target =
static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
- if (target->replace_tls_get_addr(sym))
- sym = target->tls_get_addr_opt();
if (sym != NULL && sym->is_forwarder())
sym = symtab->resolve_forwards(sym);
+ if (target->replace_tls_get_addr(sym))
+ sym = target->tls_get_addr_opt();
const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym);
bool ok = true;
if (this->glink_ != NULL)
{
- int stub_size = this->glink_->pltresolve_size;
+ int stub_size = this->glink_->pltresolve_size();
Address value = -stub_size;
if (size == 64)
{
// There are two FDEs for a position independent glink.
// The first covers the branch table, the second
// __glink_PLTresolve at the end of glink.
- off_t resolve_size = this->glink_->pltresolve_size;
+ off_t resolve_size = this->glink_->pltresolve_size();
if (oview[9] == elfcpp::DW_CFA_nop)
len -= resolve_size;
else
void
add_ifunc_entry(Symbol*);
+ void
+ add_local_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
+
void
add_local_ifunc_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
unsigned int
first_plt_entry_offset() const
{
- // IPLT has no reserved entry.
- if (this->name_[3] == 'I')
+ // IPLT and LPLT have no reserved entry.
+ if (this->name_[3] == 'I' || this->name_[3] == 'L')
return 0;
return this->targ_->first_plt_entry_offset();
}
}
}
+// Add an entry for a local symbol to the PLT.
+
+template<int size, bool big_endian>
+void
+Output_data_plt_powerpc<size, big_endian>::add_local_entry(
+ Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int local_sym_index)
+{
+ if (!relobj->local_has_plt_offset(local_sym_index))
+ {
+ section_size_type off = this->current_data_size();
+ relobj->set_local_plt_offset(local_sym_index, off);
+ if (this->rel_)
+ {
+ unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
+ if (size == 64 && this->targ_->abiversion() < 2)
+ dynrel = elfcpp::R_POWERPC_JMP_SLOT;
+ this->rel_->add_symbolless_local_addend(relobj, local_sym_index,
+ dynrel, this, off, 0);
+ }
+ off += this->plt_entry_size();
+ this->set_current_data_size(off);
+ }
+}
+
// Add an entry for a local ifunc symbol to the IPLT.
template<int size, bool big_endian>
void
Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
{
- if (size == 32 && this->name_[3] != 'I')
+ if (size == 32 && (this->name_[3] != 'I' && this->name_[3] != 'L'))
{
const section_size_type offset = this->offset();
const section_size_type oview_size
if (this->iplt_ == NULL)
{
this->make_plt_section(symtab, layout);
+ this->make_lplt_section(layout);
Reloc_section* iplt_rel = new Reloc_section(false);
if (this->rela_dyn_->output_section())
}
}
+// Create the LPLT section.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::make_lplt_section(Layout* layout)
+{
+ if (this->lplt_ == NULL)
+ {
+ Reloc_section* lplt_rel = NULL;
+ if (parameters->options().output_is_position_independent())
+ {
+ lplt_rel = new Reloc_section(false);
+ this->rela_dyn_section(layout);
+ if (this->rela_dyn_->output_section())
+ this->rela_dyn_->output_section()
+ ->add_output_section_data(lplt_rel);
+ }
+ this->lplt_
+ = new Output_data_plt_powerpc<size, big_endian>(this, lplt_rel,
+ "** LPLT");
+ this->make_brlt_section(layout);
+ if (this->brlt_section_ && this->brlt_section_->output_section())
+ this->brlt_section_->output_section()
+ ->add_output_section_data(this->lplt_);
+ else
+ layout->add_output_section_data(".branch_lt",
+ elfcpp::SHT_PROGBITS,
+ elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
+ this->lplt_,
+ ORDER_RELRO,
+ true);
+ }
+}
+
// A section for huge long branch addresses, similar to plt section.
template<int size, bool big_endian>
bool is_pic = parameters->options().output_is_position_independent();
if (is_pic)
{
- // When PIC we can't fill in .branch_lt (like .plt it can be
- // a bss style section) but must initialise at runtime via
- // dynamic relocations.
+ // 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())
->add_output_section_data(this->brlt_section_);
else
layout->add_output_section_data(".branch_lt",
- (is_pic ? elfcpp::SHT_NOBITS
- : elfcpp::SHT_PROGBITS),
+ elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
this->brlt_section_,
- (is_pic ? ORDER_SMALL_BSS
- : ORDER_SMALL_DATA),
- false);
+ ORDER_RELRO,
+ true);
}
}
elfcpp::Swap<32, big_endian>::writeval(p, v);
}
+template<int size>
+static inline unsigned int
+param_plt_align()
+{
+ if (!parameters->options().user_set_plt_align())
+ return size == 64 ? 32 : 8;
+ return 1 << parameters->options().plt_align();
+}
+
// Stub_table holds information about plt and long branch stubs.
// Stubs are built in an area following some input section determined
// by group_sections(). This input section is converted to a relaxed
unsigned int
stub_align() const
{
- if (size == 32)
- return 16;
- unsigned int min_align = 32;
+ unsigned int min_align = size == 64 ? 32 : 16;
unsigned int user_align = 1 << parameters->options().plt_align();
return std::max(user_align, min_align);
}
// Return the plt offset for the given call stub.
Address
- plt_off(typename Plt_stub_entries::const_iterator p, bool* is_iplt) const
+ plt_off(typename Plt_stub_entries::const_iterator p,
+ const Output_data_plt_powerpc<size, big_endian>** sec) const
{
const Symbol* gsym = p->first.sym_;
if (gsym != NULL)
- {
- *is_iplt = (gsym->type() == elfcpp::STT_GNU_IFUNC
- && gsym->can_use_relative_reloc(false));
- return gsym->plt_offset();
- }
+ return this->targ_->plt_off(gsym, sec);
else
{
- *is_iplt = true;
const Sized_relobj_file<size, big_endian>* relobj = p->first.object_;
unsigned int local_sym_index = p->first.locsym_;
- return relobj->local_plt_offset(local_sym_index);
+ return this->targ_->plt_off(relobj, local_sym_index, sec);
}
}
if (size == 32)
{
const Symbol* gsym = p->first.sym_;
- if (this->targ_->is_tls_get_addr_opt(gsym))
- return 12 * 4;
- return 4 * 4;
+ return (4 * 4
+ + (this->targ_->is_tls_get_addr_opt(gsym) ? 8 * 4 : 0));
}
- bool is_iplt;
- Address plt_addr = this->plt_off(p, &is_iplt);
- if (is_iplt)
- plt_addr += this->targ_->iplt_section()->address();
- else
- plt_addr += this->targ_->plt_section()->address();
+ const Output_data_plt_powerpc<size, big_endian>* plt;
+ Address plt_addr = this->plt_off(p, &plt);
+ plt_addr += plt->address();
Address got_addr = this->targ_->got_section()->output_section()->address();
const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<const Powerpc_relobj<size, big_endian>*>(p->first.object_);
unsigned int
plt_call_align(unsigned int bytes) const
{
- unsigned int align = 1 << parameters->options().plt_align();
- if (align > 1)
- bytes = (bytes + align - 1) & -align;
- return bytes;
+ unsigned int align = param_plt_align<size>();
+ return (bytes + align - 1) & -align;
}
// Return long branch stub size.
Address loc = this->stub_address() + this->last_plt_size_ + p->second;
if (p->first.dest_ - loc + (1 << 25) < 2 << 25)
return 4;
- if (size == 64 || !parameters->options().output_is_position_independent())
- return 16;
- return 32;
+ unsigned int bytes = 16;
+ if (size == 32 && parameters->options().output_is_position_independent())
+ bytes += 16;
+ return bytes;
}
// Write out stubs.
if (size != 32)
this->addend_ = addend;
else if (parameters->options().output_is_position_independent()
- && r_type == elfcpp::R_PPC_PLTREL24)
+ && (r_type == elfcpp::R_PPC_PLTREL24
+ || r_type == elfcpp::R_POWERPC_PLTCALL))
{
this->addend_ = addend;
if (this->addend_ >= 32768)
if (size != 32)
this->addend_ = addend;
else if (parameters->options().output_is_position_independent()
- && r_type == elfcpp::R_PPC_PLTREL24)
+ && (r_type == elfcpp::R_PPC_PLTREL24
+ || r_type == elfcpp::R_POWERPC_PLTCALL))
this->addend_ = addend;
}
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
static const Address invalid_address = static_cast<Address>(0) - 1;
- static const int pltresolve_size = 16*4;
Output_data_glink(Target_powerpc<size, big_endian>* targ)
: Output_section_data(16), targ_(targ), global_entry_stubs_(),
Address
find_global_entry(const Symbol*) const;
+ unsigned int
+ global_entry_align(unsigned int off) const
+ {
+ unsigned int align = param_plt_align<size>();
+ return (off + align - 1) & -align;
+ }
+
+ unsigned int
+ global_entry_off() const
+ {
+ return this->global_entry_align(this->end_branch_table_);
+ }
+
Address
global_entry_address() const
{
gold_assert(this->is_data_size_valid());
- unsigned int global_entry_off = (this->end_branch_table_ + 15) & -16;
- return this->address() + global_entry_off;
+ return this->address() + this->global_entry_off();
+ }
+
+ int
+ pltresolve_size() const
+ {
+ if (size == 64)
+ return (8
+ + (this->targ_->abiversion() < 2 ? 11 * 4 : 14 * 4));
+ return 16 * 4;
}
protected:
void
Output_data_glink<size, big_endian>::add_global_entry(const Symbol* gsym)
{
+ unsigned int off = this->global_entry_align(this->ge_size_);
std::pair<typename Global_entry_stub_entries::iterator, bool> p
- = this->global_entry_stubs_.insert(std::make_pair(gsym, this->ge_size_));
+ = this->global_entry_stubs_.insert(std::make_pair(gsym, off));
if (p.second)
- this->ge_size_ += 16;
+ this->ge_size_ = off + 16;
}
template<int size, bool big_endian>
total += 4 * (count - 1);
total += -total & 15;
- total += this->pltresolve_size;
+ total += this->pltresolve_size();
}
else
{
- total += this->pltresolve_size;
+ total += this->pltresolve_size();
// space for branch table
total += 4 * count;
}
}
this->end_branch_table_ = total;
- total = (total + 15) & -16;
+ total = this->global_entry_align(total);
total += this->ge_size_;
this->set_data_size(total);
if (!this->plt_call_stubs_.empty())
{
- // The base address of the .plt section.
- Address plt_base = this->targ_->plt_section()->address();
- Address iplt_base = invalid_address;
-
// Write out plt call stubs.
typename Plt_stub_entries::const_iterator cs;
for (cs = this->plt_call_stubs_.begin();
cs != this->plt_call_stubs_.end();
++cs)
{
- bool is_iplt;
- Address pltoff = this->plt_off(cs, &is_iplt);
- Address plt_addr = pltoff;
- if (is_iplt)
- {
- if (iplt_base == invalid_address)
- iplt_base = this->targ_->iplt_section()->address();
- plt_addr += iplt_base;
- }
- else
- plt_addr += plt_base;
+ const Output_data_plt_powerpc<size, big_endian>* plt;
+ Address pltoff = this->plt_off(cs, &plt);
+ Address plt_addr = pltoff + plt->address();
const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<const Powerpc_relobj<size, big_endian>*>(cs->first.object_);
Address got_addr = got_os_addr + ppcobj->toc_base_offset();
= ((pltoff - this->targ_->first_plt_entry_offset())
/ this->targ_->plt_entry_size());
Address glinkoff
- = (this->targ_->glink_section()->pltresolve_size
+ = (this->targ_->glink_section()->pltresolve_size()
+ pltindex * 8);
if (pltindex > 32768)
glinkoff += (pltindex - 32768) * 4;
{
if (!this->plt_call_stubs_.empty())
{
- // The base address of the .plt section.
- Address plt_base = this->targ_->plt_section()->address();
- Address iplt_base = invalid_address;
// The address of _GLOBAL_OFFSET_TABLE_.
Address g_o_t = invalid_address;
cs != this->plt_call_stubs_.end();
++cs)
{
- bool is_iplt;
- Address plt_addr = this->plt_off(cs, &is_iplt);
- if (is_iplt)
- {
- if (iplt_base == invalid_address)
- iplt_base = this->targ_->iplt_section()->address();
- plt_addr += iplt_base;
- }
- else
- plt_addr += plt_base;
+ const Output_data_plt_powerpc<size, big_endian>* plt;
+ Address plt_addr = this->plt_off(cs, &plt);
+ plt_addr += plt->address();
p = oview + cs->second.off_;
const Symbol* gsym = cs->first.sym_;
Address off = plt_addr - got_addr;
if (ha(off) == 0)
- {
- write_insn<big_endian>(p + 0, lwz_11_30 + l(off));
- write_insn<big_endian>(p + 4, mtctr_11);
- write_insn<big_endian>(p + 8, bctr);
- }
+ write_insn<big_endian>(p, lwz_11_30 + l(off));
else
{
- write_insn<big_endian>(p + 0, addis_11_30 + ha(off));
- write_insn<big_endian>(p + 4, lwz_11_11 + l(off));
- write_insn<big_endian>(p + 8, mtctr_11);
- write_insn<big_endian>(p + 12, bctr);
+ write_insn<big_endian>(p, addis_11_30 + ha(off));
+ p += 4;
+ write_insn<big_endian>(p, lwz_11_11 + l(off));
}
}
else
{
- write_insn<big_endian>(p + 0, lis_11 + ha(plt_addr));
- write_insn<big_endian>(p + 4, lwz_11_11 + l(plt_addr));
- write_insn<big_endian>(p + 8, mtctr_11);
- write_insn<big_endian>(p + 12, bctr);
+ write_insn<big_endian>(p, lis_11 + ha(plt_addr));
+ p += 4;
+ write_insn<big_endian>(p, lwz_11_11 + l(plt_addr));
}
+ p += 4;
+ write_insn<big_endian>(p, mtctr_11);
+ p += 4;
+ write_insn<big_endian>(p, bctr);
}
}
write_insn<big_endian>(p, b | (delta & 0x3fffffc));
else if (!parameters->options().output_is_position_independent())
{
- write_insn<big_endian>(p + 0, lis_12 + ha(bs->first.dest_));
- write_insn<big_endian>(p + 4, addi_12_12 + l(bs->first.dest_));
- write_insn<big_endian>(p + 8, mtctr_12);
- write_insn<big_endian>(p + 12, bctr);
+ write_insn<big_endian>(p, lis_12 + ha(bs->first.dest_));
+ p += 4;
+ write_insn<big_endian>(p, addi_12_12 + l(bs->first.dest_));
}
else
{
delta -= 8;
- write_insn<big_endian>(p + 0, mflr_0);
- write_insn<big_endian>(p + 4, bcl_20_31);
- write_insn<big_endian>(p + 8, mflr_12);
- write_insn<big_endian>(p + 12, addis_12_12 + ha(delta));
- write_insn<big_endian>(p + 16, addi_12_12 + l(delta));
- write_insn<big_endian>(p + 20, mtlr_0);
- write_insn<big_endian>(p + 24, mtctr_12);
- write_insn<big_endian>(p + 28, bctr);
+ 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_12);
+ p += 4;
+ write_insn<big_endian>(p, addis_12_12 + ha(delta));
+ p += 4;
+ write_insn<big_endian>(p, addi_12_12 + l(delta));
+ p += 4;
+ write_insn<big_endian>(p, mtlr_0);
}
+ p += 4;
+ write_insn<big_endian>(p, mtctr_12);
+ p += 4;
+ write_insn<big_endian>(p, bctr);
}
}
if (this->need_save_res_)
write_insn<big_endian>(p, ld_11_11 + 8), p += 4;
}
write_insn<big_endian>(p, bctr), p += 4;
- while (p < oview + this->pltresolve_size)
- write_insn<big_endian>(p, nop), p += 4;
+ gold_assert(p == oview + this->pltresolve_size());
// Write lazy link call stubs.
uint32_t indx = 0;
Address plt_base = this->targ_->plt_section()->address();
Address iplt_base = invalid_address;
- unsigned int global_entry_off = (this->end_branch_table_ + 15) & -16;
+ unsigned int global_entry_off = this->global_entry_off();
Address global_entry_base = this->address() + global_entry_off;
typename Global_entry_stub_entries::const_iterator ge;
for (ge = this->global_entry_stubs_.begin();
// Write out pltresolve branch table.
p = oview;
- unsigned int the_end = oview_size - this->pltresolve_size;
+ unsigned int the_end = oview_size - this->pltresolve_size();
unsigned char* end_p = oview + the_end;
while (p < end_p - 8 * 4)
write_insn<big_endian>(p, b + end_p - p), p += 4;
write_insn<big_endian>(p, nop), p += 4;
// Write out pltresolve call stub.
+ end_p = oview + oview_size;
if (parameters->options().output_is_position_independent())
{
Address res0_off = 0;
Address after_bcl_off = the_end + 12;
Address bcl_res0 = after_bcl_off - res0_off;
- write_insn<big_endian>(p + 0, addis_11_11 + ha(bcl_res0));
- write_insn<big_endian>(p + 4, mflr_0);
- write_insn<big_endian>(p + 8, bcl_20_31);
- write_insn<big_endian>(p + 12, addi_11_11 + l(bcl_res0));
- write_insn<big_endian>(p + 16, mflr_12);
- write_insn<big_endian>(p + 20, mtlr_0);
- write_insn<big_endian>(p + 24, sub_11_11_12);
+ write_insn<big_endian>(p, addis_11_11 + ha(bcl_res0));
+ 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, addi_11_11 + l(bcl_res0));
+ p += 4;
+ write_insn<big_endian>(p, mflr_12);
+ p += 4;
+ write_insn<big_endian>(p, mtlr_0);
+ p += 4;
+ write_insn<big_endian>(p, sub_11_11_12);
+ p += 4;
Address got_bcl = g_o_t + 4 - (after_bcl_off + this->address());
- write_insn<big_endian>(p + 28, addis_12_12 + ha(got_bcl));
+ write_insn<big_endian>(p, addis_12_12 + ha(got_bcl));
+ p += 4;
if (ha(got_bcl) == ha(got_bcl + 4))
{
- write_insn<big_endian>(p + 32, lwz_0_12 + l(got_bcl));
- write_insn<big_endian>(p + 36, lwz_12_12 + l(got_bcl + 4));
+ write_insn<big_endian>(p, lwz_0_12 + l(got_bcl));
+ p += 4;
+ write_insn<big_endian>(p, lwz_12_12 + l(got_bcl + 4));
}
else
{
- write_insn<big_endian>(p + 32, lwzu_0_12 + l(got_bcl));
- write_insn<big_endian>(p + 36, lwz_12_12 + 4);
+ write_insn<big_endian>(p, lwzu_0_12 + l(got_bcl));
+ p += 4;
+ write_insn<big_endian>(p, lwz_12_12 + 4);
}
- write_insn<big_endian>(p + 40, mtctr_0);
- write_insn<big_endian>(p + 44, add_0_11_11);
- write_insn<big_endian>(p + 48, add_11_0_11);
- write_insn<big_endian>(p + 52, bctr);
- write_insn<big_endian>(p + 56, nop);
- write_insn<big_endian>(p + 60, nop);
+ p += 4;
+ write_insn<big_endian>(p, mtctr_0);
+ p += 4;
+ write_insn<big_endian>(p, add_0_11_11);
+ p += 4;
+ write_insn<big_endian>(p, add_11_0_11);
}
else
{
Address res0 = this->address();
- write_insn<big_endian>(p + 0, lis_12 + ha(g_o_t + 4));
- write_insn<big_endian>(p + 4, addis_11_11 + ha(-res0));
+ write_insn<big_endian>(p, lis_12 + ha(g_o_t + 4));
+ p += 4;
+ write_insn<big_endian>(p, addis_11_11 + ha(-res0));
+ p += 4;
if (ha(g_o_t + 4) == ha(g_o_t + 8))
- write_insn<big_endian>(p + 8, lwz_0_12 + l(g_o_t + 4));
+ write_insn<big_endian>(p, lwz_0_12 + l(g_o_t + 4));
else
- write_insn<big_endian>(p + 8, lwzu_0_12 + l(g_o_t + 4));
- write_insn<big_endian>(p + 12, addi_11_11 + l(-res0));
- write_insn<big_endian>(p + 16, mtctr_0);
- write_insn<big_endian>(p + 20, add_0_11_11);
+ write_insn<big_endian>(p, lwzu_0_12 + l(g_o_t + 4));
+ p += 4;
+ write_insn<big_endian>(p, addi_11_11 + l(-res0));
+ p += 4;
+ write_insn<big_endian>(p, mtctr_0);
+ p += 4;
+ write_insn<big_endian>(p, add_0_11_11);
+ p += 4;
if (ha(g_o_t + 4) == ha(g_o_t + 8))
- write_insn<big_endian>(p + 24, lwz_12_12 + l(g_o_t + 8));
+ write_insn<big_endian>(p, lwz_12_12 + l(g_o_t + 8));
else
- write_insn<big_endian>(p + 24, lwz_12_12 + 4);
- write_insn<big_endian>(p + 28, add_11_0_11);
- write_insn<big_endian>(p + 32, bctr);
- write_insn<big_endian>(p + 36, nop);
- write_insn<big_endian>(p + 40, nop);
- write_insn<big_endian>(p + 44, nop);
- write_insn<big_endian>(p + 48, nop);
- write_insn<big_endian>(p + 52, nop);
- write_insn<big_endian>(p + 56, nop);
- write_insn<big_endian>(p + 60, nop);
+ write_insn<big_endian>(p, lwz_12_12 + 4);
+ p += 4;
+ write_insn<big_endian>(p, add_11_0_11);
+ }
+ p += 4;
+ write_insn<big_endian>(p, bctr);
+ p += 4;
+ while (p < end_p)
+ {
+ write_insn<big_endian>(p, nop);
+ p += 4;
}
- p += 64;
}
of->write_output_view(off, oview_size, oview);
}
}
+// Make a PLT entry for a local symbol.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::make_local_plt_entry(
+ Layout* layout,
+ Sized_relobj_file<size, big_endian>* relobj,
+ unsigned int r_sym)
+{
+ if (this->lplt_ == NULL)
+ this->make_lplt_section(layout);
+ this->lplt_->add_local_entry(relobj, r_sym);
+}
+
// Make a PLT entry for a local STT_GNU_IFUNC symbol.
template<int size, bool big_endian>
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
+ case elfcpp::R_POWERPC_PLT16_LO:
+ case elfcpp::R_POWERPC_PLT16_HI:
+ case elfcpp::R_POWERPC_PLT16_HA:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
ref = Symbol::RELATIVE_REF;
break;
case elfcpp::R_PPC64_GOT16_LO_DS:
return false;
+ // PLT relocs are OK and need a PLT entry.
+ case elfcpp::R_POWERPC_PLT16_LO:
+ case elfcpp::R_POWERPC_PLT16_HI:
+ case elfcpp::R_POWERPC_PLT16_HA:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
+ case elfcpp::R_POWERPC_PLTSEQ:
+ case elfcpp::R_POWERPC_PLTCALL:
+ return true;
+ break;
+
// Function calls are good, and these do need a PLT entry.
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_POWERPC_TLS:
case elfcpp::R_PPC64_ENTRY:
+ case elfcpp::R_POWERPC_PLTSEQ:
+ case elfcpp::R_POWERPC_PLTCALL:
break;
case elfcpp::R_PPC64_TOC:
}
break;
+ case elfcpp::R_POWERPC_PLT16_LO:
+ case elfcpp::R_POWERPC_PLT16_HI:
+ case elfcpp::R_POWERPC_PLT16_HA:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
+ if (!is_ifunc)
+ {
+ unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+ target->make_local_plt_entry(layout, object, r_sym);
+ }
+ break;
+
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_POWERPC_TLS:
case elfcpp::R_PPC64_ENTRY:
+ case elfcpp::R_POWERPC_PLTSEQ:
+ case elfcpp::R_POWERPC_PLTCALL:
break;
case elfcpp::R_PPC64_TOC:
}
break;
+ case elfcpp::R_POWERPC_PLT16_LO:
+ case elfcpp::R_POWERPC_PLT16_HI:
+ case elfcpp::R_POWERPC_PLT16_HA:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
+ if (!pushed_ifunc)
+ target->make_plt_entry(symtab, layout, gsym);
+ break;
+
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_POWERPC_REL24:
if (!is_ifunc)
this->glink_->finalize_data_size();
odyn->add_section_plus_offset(elfcpp::DT_PPC64_GLINK,
this->glink_,
- (this->glink_->pltresolve_size
+ (this->glink_->pltresolve_size()
- 32));
}
if (this->has_localentry0_ || this->has_tls_get_addr_opt_)
Address address,
section_size_type view_size)
{
+ typedef Powerpc_relocate_functions<size, big_endian> Reloc;
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
+ typedef typename elfcpp::Rela<size, big_endian> Reltype;
+
if (view == NULL)
return true;
// We have already complained.
break;
case Track_tls::SKIP:
+ if (is_plt16_reloc<size>(r_type)
+ || r_type == elfcpp::R_POWERPC_PLTSEQ)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+ }
+ else if (size == 64 && r_type == elfcpp::R_POWERPC_PLTCALL)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1, nop);
+ }
return true;
case Track_tls::NORMAL:
break;
}
- typedef Powerpc_relocate_functions<size, big_endian> Reloc;
- typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
- typedef typename elfcpp::Rela<size, big_endian> Reltype;
// Offset from start of insn to d-field reloc.
const int d_offset = big_endian ? 2 : 0;
bool has_stub_value = false;
bool localentry0 = false;
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
- if ((gsym != NULL
+ bool has_plt_offset
+ = (gsym != NULL
? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
- : object->local_has_plt_offset(r_sym))
+ : object->local_has_plt_offset(r_sym));
+ if (has_plt_offset
+ && !is_plt16_reloc<size>(r_type)
+ && r_type != elfcpp::R_POWERPC_PLTSEQ
+ && r_type != elfcpp::R_POWERPC_PLTCALL
&& (!psymval->is_ifunc_symbol()
|| Scan::reloc_needs_plt_for_ifunc(target, object, r_type, false)))
{
gold_assert(has_stub_value || !(os->flags() & elfcpp::SHF_ALLOC));
}
- if (r_type == elfcpp::R_POWERPC_GOT16
- || r_type == elfcpp::R_POWERPC_GOT16_LO
- || r_type == elfcpp::R_POWERPC_GOT16_HI
- || r_type == elfcpp::R_POWERPC_GOT16_HA
- || r_type == elfcpp::R_PPC64_GOT16_DS
- || r_type == elfcpp::R_PPC64_GOT16_LO_DS)
+ if (has_plt_offset && is_plt16_reloc<size>(r_type))
+ {
+ const Output_data_plt_powerpc<size, big_endian>* plt;
+ if (gsym)
+ value = target->plt_off(gsym, &plt);
+ else
+ value = target->plt_off(object, r_sym, &plt);
+ value += plt->address();
+
+ if (size == 64)
+ value -= (target->got_section()->output_section()->address()
+ + object->toc_base_offset());
+ else if (parameters->options().output_is_position_independent())
+ {
+ if (rela.get_r_addend() >= 32768)
+ {
+ unsigned int got2 = object->got2_shndx();
+ value -= (object->get_output_section_offset(got2)
+ + object->output_section(got2)->address()
+ + rela.get_r_addend());
+ }
+ else
+ value -= (target->got_section()->address()
+ + target->got_section()->g_o_t());
+ }
+ }
+ else if (!has_plt_offset
+ && (is_plt16_reloc<size>(r_type)
+ || r_type == elfcpp::R_POWERPC_PLTSEQ))
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+ r_type = elfcpp::R_POWERPC_NONE;
+ }
+ else if (r_type == elfcpp::R_POWERPC_GOT16
+ || r_type == elfcpp::R_POWERPC_GOT16_LO
+ || r_type == elfcpp::R_POWERPC_GOT16_HI
+ || r_type == elfcpp::R_POWERPC_GOT16_HA
+ || r_type == elfcpp::R_PPC64_GOT16_DS
+ || r_type == elfcpp::R_PPC64_GOT16_LO_DS)
{
if (gsym != NULL)
{
}
else if (!has_stub_value)
{
+ if (!has_plt_offset && r_type == elfcpp::R_POWERPC_PLTCALL)
+ {
+ // PLTCALL without plt entry => convert to direct call
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn = (insn & 1) | b;
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ if (size == 32)
+ r_type = elfcpp::R_PPC_PLTREL24;
+ else
+ r_type = elfcpp::R_POWERPC_REL24;
+ }
Address addend = 0;
- if (!(size == 32 && r_type == elfcpp::R_PPC_PLTREL24))
+ if (!(size == 32
+ && (r_type == elfcpp::R_PPC_PLTREL24
+ || r_type == elfcpp::R_POWERPC_PLT16_LO
+ || r_type == elfcpp::R_POWERPC_PLT16_HI
+ || r_type == elfcpp::R_POWERPC_PLT16_HA)))
addend = rela.get_r_addend();
value = psymval->value(object, addend);
if (size == 64 && is_branch_reloc(r_type))
}
break;
+ case elfcpp::R_POWERPC_PLT16_HA:
+ if (size == 32
+ && !parameters->options().output_is_position_independent())
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+
+ // Convert addis to lis.
+ if ((insn & (0x3f << 26)) == 15u << 26
+ && (insn & (0x1f << 16)) != 0)
+ {
+ insn &= ~(0x1f << 16);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+ }
+ }
+ break;
+
default:
break;
}
case elfcpp::R_POWERPC_TLS:
case elfcpp::R_POWERPC_GNU_VTINHERIT:
case elfcpp::R_POWERPC_GNU_VTENTRY:
+ case elfcpp::R_POWERPC_PLTSEQ:
+ case elfcpp::R_POWERPC_PLTCALL:
break;
case elfcpp::R_PPC64_ADDR64:
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_POWERPC_GOT16_LO:
+ case elfcpp::R_POWERPC_PLT16_LO:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_TPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_PPC64_TOC16_HI:
case elfcpp::R_POWERPC_GOT16_HI:
+ case elfcpp::R_POWERPC_PLT16_HI:
case elfcpp::R_POWERPC_SECTOFF_HI:
case elfcpp::R_POWERPC_TPREL16_HI:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_POWERPC_GOT16_HA:
+ case elfcpp::R_POWERPC_PLT16_HA:
case elfcpp::R_POWERPC_SECTOFF_HA:
case elfcpp::R_POWERPC_TPREL16_HA:
case elfcpp::R_POWERPC_DTPREL16_HA:
case elfcpp::R_PPC64_TOC16_LO_DS:
case elfcpp::R_PPC64_GOT16_DS:
case elfcpp::R_PPC64_GOT16_LO_DS:
+ case elfcpp::R_PPC64_PLT16_LO_DS:
case elfcpp::R_PPC64_SECTOFF_DS:
case elfcpp::R_PPC64_SECTOFF_LO_DS:
maybe_dq_reloc = true;
case elfcpp::R_POWERPC_PLT32:
case elfcpp::R_POWERPC_PLTREL32:
- case elfcpp::R_POWERPC_PLT16_LO:
- case elfcpp::R_POWERPC_PLT16_HI:
- case elfcpp::R_POWERPC_PLT16_HA:
case elfcpp::R_PPC_SDAREL16:
case elfcpp::R_POWERPC_ADDR30:
case elfcpp::R_PPC64_PLT64:
case elfcpp::R_PPC64_PLTGOT16_LO:
case elfcpp::R_PPC64_PLTGOT16_HI:
case elfcpp::R_PPC64_PLTGOT16_HA:
- case elfcpp::R_PPC64_PLT16_LO_DS:
case elfcpp::R_PPC64_PLTGOT16_DS:
case elfcpp::R_PPC64_PLTGOT16_LO_DS:
case elfcpp::R_PPC_EMB_RELSDA:
inline Relocatable_relocs::Reloc_strategy
global_strategy(unsigned int r_type, Relobj*, unsigned int)
{
- if (r_type == elfcpp::R_PPC_PLTREL24)
+ if (size == 32
+ && (r_type == elfcpp::R_PPC_PLTREL24
+ || r_type == elfcpp::R_POWERPC_PLT16_LO
+ || r_type == elfcpp::R_POWERPC_PLT16_HI
+ || r_type == elfcpp::R_POWERPC_PLT16_HA))
return Relocatable_relocs::RELOC_SPECIAL;
return Relocatable_relocs::RELOC_COPY;
}
gold_assert(got2_addend != invalid_address);
}
+ const bool relocatable = parameters->options().relocatable();
+
unsigned char* pwrite = reloc_view;
bool zap_next = false;
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
// In an object file, r_offset is an offset within the section.
// In an executable or dynamic object, generated by
// --emit-relocs, r_offset is an absolute address.
- if (!parameters->options().relocatable())
+ if (!relocatable)
{
offset += view_address;
if (static_cast<Address>(offset_in_output_section) != invalid_address)
else if (strategy == Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA)
{
const Symbol_value<size>* psymval = object->local_symbol(orig_r_sym);
- gold_assert(os != NULL);
- addend = psymval->value(object, addend) - os->address();
+ addend = psymval->value(object, addend);
+ // In a relocatable link, the symbol value is relative to
+ // the start of the output section. For a non-relocatable
+ // link, we need to adjust the addend.
+ if (!relocatable)
+ {
+ gold_assert(os != NULL);
+ addend -= os->address();
+ }
}
else if (strategy == Relocatable_relocs::RELOC_SPECIAL)
{
else
gold_unreachable();
- if (!parameters->options().relocatable())
+ if (!relocatable)
{
if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO
// Instantiate these constants for -O0
template<int size, bool big_endian>
-const int Output_data_glink<size, big_endian>::pltresolve_size;
-template<int size, bool big_endian>
const typename Output_data_glink<size, big_endian>::Address
Output_data_glink<size, big_endian>::invalid_address;
template<int size, bool big_endian>