#include "gold.h"
+#include <set>
#include <algorithm>
#include "elfcpp.h"
#include "dwarf.h"
template<int size, bool big_endian>
class Stub_table;
+inline bool
+is_branch_reloc(unsigned int r_type);
+
template<int size, bool big_endian>
class Powerpc_relobj : public Sized_relobj_file<size, big_endian>
{
std::vector<Stub_table<size, big_endian>*> stub_table_;
};
+template<int size, bool big_endian>
+class Powerpc_dynobj : public Sized_dynobj<size, big_endian>
+{
+public:
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+
+ Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset,
+ const typename elfcpp::Ehdr<size, big_endian>& ehdr)
+ : Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr),
+ opd_shndx_(0), opd_ent_()
+ { }
+
+ ~Powerpc_dynobj()
+ { }
+
+ // Call Sized_dynobj::do_read_symbols to read the symbols then
+ // read .opd from a dynamic object, filling in opd_ent_ vector,
+ void
+ do_read_symbols(Read_symbols_data*);
+
+ // The .opd section shndx.
+ unsigned int
+ opd_shndx() const
+ {
+ return this->opd_shndx_;
+ }
+
+ // The .opd section address.
+ Address
+ opd_address() const
+ {
+ return this->opd_address_;
+ }
+
+ // Init OPD entry arrays.
+ void
+ init_opd(size_t opd_size)
+ {
+ size_t count = this->opd_ent_ndx(opd_size);
+ this->opd_ent_.resize(count);
+ }
+
+ // Return section and offset of function entry for .opd + R_OFF.
+ unsigned int
+ get_opd_ent(Address r_off, Address* value = NULL) const
+ {
+ size_t ndx = this->opd_ent_ndx(r_off);
+ gold_assert(ndx < this->opd_ent_.size());
+ gold_assert(this->opd_ent_[ndx].shndx != 0);
+ if (value != NULL)
+ *value = this->opd_ent_[ndx].off;
+ return this->opd_ent_[ndx].shndx;
+ }
+
+ // Set section and offset of function entry for .opd + R_OFF.
+ void
+ set_opd_ent(Address r_off, unsigned int shndx, Address value)
+ {
+ size_t ndx = this->opd_ent_ndx(r_off);
+ gold_assert(ndx < this->opd_ent_.size());
+ this->opd_ent_[ndx].shndx = shndx;
+ this->opd_ent_[ndx].off = value;
+ }
+
+private:
+ // Used to specify extent of executable sections.
+ struct Sec_info
+ {
+ Sec_info(Address start_, Address len_, unsigned int shndx_)
+ : start(start_), len(len_), shndx(shndx_)
+ { }
+
+ bool
+ operator<(const Sec_info& that) const
+ { return this->start < that.start; }
+
+ Address start;
+ Address len;
+ unsigned int shndx;
+ };
+
+ struct Opd_ent
+ {
+ unsigned int shndx;
+ Address off;
+ };
+
+ // Return index into opd_ent_ array for .opd entry at OFF.
+ size_t
+ opd_ent_ndx(size_t off) const
+ { return off >> 4;}
+
+ // For 64-bit the .opd section shndx and address.
+ unsigned int opd_shndx_;
+ Address opd_address_;
+
+ // The first 8-byte word of an OPD entry gives the address of the
+ // entry point of the function. Records the section and offset
+ // corresponding to the address. Note that in dynamic objects,
+ // offset is *not* relative to the section.
+ std::vector<Opd_ent> opd_ent_;
+};
+
template<int size, bool big_endian>
class Target_powerpc : public Sized_target<size, big_endian>
{
int64_t
do_tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const;
+ void
+ do_function_location(Symbol_location*) const;
+
+ bool
+ do_can_check_for_function_pointers() const
+ { return true; }
+
// Relocate a section.
void
relocate_section(const Relocate_info<size, big_endian>*,
unsigned int ,
Output_section* ,
const elfcpp::Rela<size, big_endian>& ,
- unsigned int ,
+ unsigned int r_type,
const elfcpp::Sym<size, big_endian>&)
- { return false; }
+ {
+ // PowerPC64 .opd is not folded, so any identical function text
+ // may be folded and we'll still keep function addresses distinct.
+ // That means no reloc is of concern here.
+ if (size == 64)
+ return false;
+ // For 32-bit, conservatively assume anything but calls to
+ // function code might be taking the address of the function.
+ return !is_branch_reloc(r_type);
+ }
inline bool
global_reloc_may_be_function_pointer(Symbol_table* , Layout* ,
Sized_relobj_file<size, big_endian>* ,
unsigned int ,
Output_section* ,
- const elfcpp::Rela<size,
- big_endian>& ,
- unsigned int , Symbol*)
- { return false; }
+ const elfcpp::Rela<size, big_endian>& ,
+ unsigned int r_type,
+ Symbol*)
+ {
+ // As above.
+ if (size == 64)
+ return false;
+ return !is_branch_reloc(r_type);
+ }
+
+ static bool
+ reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
+ unsigned int r_type, bool report_err);
private:
static void
void
check_non_pic(Relobj*, unsigned int r_type);
- bool
- reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
- unsigned int r_type);
-
// Whether we have issued an error about a non-PIC compilation.
bool issued_non_pic_error_;
};
Reloc_section*
rela_dyn_section(Layout*);
+ // Similarly, but for ifunc symbols get the one for ifunc.
+ Reloc_section*
+ rela_dyn_section(Symbol_table*, Layout*, bool for_ifunc);
+
// Copy a relocation against a global symbol.
void
copy_reloc(Symbol_table* symtab, Layout* layout,
// The GOT section.
Output_data_got_powerpc<size, big_endian>* got_;
- // The PLT section.
+ // 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
+ // corresponding entry in .glink for lazy resolution of the PLT.
+ // ppc32 initialises the PLT to point at the .glink entry, while
+ // ppc64 leaves this to ld.so. To make a call via the PLT, the
+ // linker adds a stub that loads the PLT entry into ctr then
+ // branches to ctr. There may be more than one stub for each PLT
+ // entry. DT_JMPREL points at the first PLT dynamic relocation and
+ // DT_PLTRELSZ gives the total size of PLT dynamic relocations.
Output_data_plt_powerpc<size, big_endian>* plt_;
- // The IPLT section.
+ // The IPLT section. Like plt_, this is a container for a table of
+ // addresses and their relocations, specifically for STT_GNU_IFUNC
+ // functions that resolve locally (STT_GNU_IFUNC functions that
+ // don't resolve locally go in PLT). Unlike plt_, these have no
+ // entry in .glink for lazy resolution, and the relocation section
+ // does not have a 1-1 correspondence with IPLT addresses. In fact,
+ // the relocation section may contain relocations against
+ // STT_GNU_IFUNC symbols at locations outside of IPLT. The
+ // relocation section will appear at the end of other dynamic
+ // relocations, so that ld.so applies these relocations after other
+ // dynamic relocations. In a static executable, the relocation
+ // section is emitted and marked with __rela_iplt_start and
+ // __rela_iplt_end symbols.
Output_data_plt_powerpc<size, big_endian>* iplt_;
// Section holding long branch destinations.
Output_data_brlt_powerpc<size, big_endian>* brlt_section_;
section_size_type names_size = sd->section_names_size;
const unsigned char* s;
- s = this->find_shdr(pshdrs, size == 32 ? ".got2" : ".opd",
- names, names_size, NULL);
+ s = this->template find_shdr<size, big_endian>(pshdrs,
+ size == 32 ? ".got2" : ".opd",
+ names, names_size, NULL);
if (s != NULL)
{
unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size;
}
}
+// Call Sized_dynobj::do_read_symbols to read the symbols then
+// read .opd from a dynamic object, filling in opd_ent_ vector,
+
+template<int size, bool big_endian>
+void
+Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
+{
+ Sized_dynobj<size, big_endian>::do_read_symbols(sd);
+ if (size == 64)
+ {
+ const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+ const unsigned char* const pshdrs = sd->section_headers->data();
+ const unsigned char* namesu = sd->section_names->data();
+ const char* names = reinterpret_cast<const char*>(namesu);
+ const unsigned char* s = NULL;
+ const unsigned char* opd;
+ section_size_type opd_size;
+
+ // Find and read .opd section.
+ while (1)
+ {
+ s = this->template find_shdr<size, big_endian>(pshdrs, ".opd", names,
+ sd->section_names_size,
+ s);
+ if (s == NULL)
+ return;
+
+ typename elfcpp::Shdr<size, big_endian> shdr(s);
+ if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS
+ && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0)
+ {
+ this->opd_shndx_ = (s - pshdrs) / shdr_size;
+ this->opd_address_ = shdr.get_sh_addr();
+ opd_size = convert_to_section_size_type(shdr.get_sh_size());
+ opd = this->get_view(shdr.get_sh_offset(), opd_size,
+ true, false);
+ break;
+ }
+ }
+
+ // Build set of executable sections.
+ // Using a set is probably overkill. There is likely to be only
+ // a few executable sections, typically .init, .text and .fini,
+ // and they are generally grouped together.
+ typedef std::set<Sec_info> Exec_sections;
+ Exec_sections exec_sections;
+ s = pshdrs;
+ for (unsigned int i = 1; i < this->shnum(); ++i, s += shdr_size)
+ {
+ typename elfcpp::Shdr<size, big_endian> shdr(s);
+ if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS
+ && ((shdr.get_sh_flags()
+ & (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR))
+ == (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR))
+ && shdr.get_sh_size() != 0)
+ {
+ exec_sections.insert(Sec_info(shdr.get_sh_addr(),
+ shdr.get_sh_size(), i));
+ }
+ }
+ if (exec_sections.empty())
+ return;
+
+ // Look over the OPD entries. This is complicated by the fact
+ // that some binaries will use two-word entries while others
+ // will use the standard three-word entries. In most cases
+ // the third word (the environment pointer for languages like
+ // Pascal) is unused and will be zero. If the third word is
+ // used it should not be pointing into executable sections,
+ // I think.
+ this->init_opd(opd_size);
+ for (const unsigned char* p = opd; p < opd + opd_size; p += 8)
+ {
+ typedef typename elfcpp::Swap<64, big_endian>::Valtype Valtype;
+ const Valtype* valp = reinterpret_cast<const Valtype*>(p);
+ Valtype val = elfcpp::Swap<64, big_endian>::readval(valp);
+ if (val == 0)
+ // Chances are that this is the third word of an OPD entry.
+ continue;
+ typename Exec_sections::const_iterator e
+ = exec_sections.upper_bound(Sec_info(val, 0, 0));
+ if (e != exec_sections.begin())
+ {
+ --e;
+ if (e->start <= val && val < e->start + e->len)
+ {
+ // We have an address in an executable section.
+ // VAL ought to be the function entry, set it up.
+ this->set_opd_ent(p - opd, e->shndx, val);
+ // Skip second word of OPD entry, the TOC pointer.
+ p += 8;
+ }
+ }
+ // If we didn't match any executable sections, we likely
+ // have a non-zero third word in the OPD entry.
+ }
+ }
+}
+
// Set up some symbols.
template<int size, bool big_endian>
}
else if (et == elfcpp::ET_DYN)
{
- Sized_dynobj<size, big_endian>* obj =
- new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr);
+ Powerpc_dynobj<size, big_endian>* obj =
+ new Powerpc_dynobj<size, big_endian>(name, input_file, offset, ehdr);
obj->setup();
return obj;
}
return this->rela_dyn_;
}
+// Similarly, but for ifunc symbols get the one for ifunc.
+
+template<int size, bool big_endian>
+typename Target_powerpc<size, big_endian>::Reloc_section*
+Target_powerpc<size, big_endian>::rela_dyn_section(Symbol_table* symtab,
+ Layout* layout,
+ bool for_ifunc)
+{
+ if (!for_ifunc)
+ return this->rela_dyn_section(layout);
+
+ if (this->iplt_ == NULL)
+ this->make_iplt_section(symtab, layout);
+ return this->iplt_->rel_plt();
+}
+
class Stub_control
{
public:
else
off += i->data_size();
}
- // If .brlt is part of this output section, then we have just
- // done the offset adjustment.
+ // If .branch_lt is part of this output section, then we have
+ // just done the offset adjustment.
os->clear_section_offsets_need_adjustment();
}
unsigned int
entry_count() const
{
+ if (this->current_data_size() == 0)
+ return 0;
return ((this->current_data_size() - this->initial_plt_entry_size_)
/ plt_entry_size);
}
void
Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
{
- if (size == 32)
+ if (size == 32 && this->name_[3] != 'I')
{
const section_size_type offset = this->offset();
const section_size_type oview_size
// Ensure that .rela.dyn always appears before .rela.plt This is
// necessary due to how, on PowerPC and some other targets, .rela.dyn
- // needs to include .rela.plt in it's range.
+ // needs to include .rela.plt in its range.
this->rela_dyn_section(layout);
Reloc_section* plt_rel = new Reloc_section(false);
bool is_pic = parameters->options().output_is_position_independent();
if (is_pic)
{
- // When PIC we can't fill in .brlt (like .plt it can be a
- // bss style section) but must initialise at runtime via
+ // 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 relocats.
this->rela_dyn_section(layout);
brlt_rel = new Reloc_section(false);
this->plt_->output_section()
->add_output_section_data(this->brlt_section_);
else
- layout->add_output_section_data(".brlt",
+ layout->add_output_section_data(".branch_lt",
(is_pic ? elfcpp::SHT_NOBITS
: elfcpp::SHT_PROGBITS),
elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
}
}
-// Write out .brlt when non-PIC.
+// Write out .branch_lt when non-PIC.
template<int size, bool big_endian>
void
{
if (this->plt_ == NULL)
return 0;
- unsigned int count = this->plt_->entry_count();
- if (this->iplt_ != NULL)
- count += this->iplt_->entry_count();
- return count;
+ return this->plt_->entry_count();
}
// Return the offset of the first non-reserved PLT entry.
bool
Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
Sized_relobj_file<size, big_endian>* object,
- unsigned int r_type)
+ unsigned int r_type,
+ bool report_err)
{
// In non-pic code any reference will resolve to the plt call stub
// for the ifunc symbol.
switch (r_type)
{
- // Word size refs from data sections are OK.
+ // Word size refs from data sections are OK, but don't need a PLT entry.
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_UADDR32:
if (size == 32)
- return true;
+ return false;
break;
case elfcpp::R_PPC64_ADDR64:
case elfcpp::R_PPC64_UADDR64:
if (size == 64)
- return true;
+ return false;
break;
- // GOT refs are good.
+ // GOT refs are good, but also don't need a PLT entry.
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:
- return true;
+ return false;
- // So are function calls.
+ // 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_ADDR14_BRTAKEN:
// writable and non-executable to apply text relocations. So we'll
// segfault when trying to run the indirection function to resolve
// the reloc.
- gold_error(_("%s: unsupported reloc %u for IFUNC symbol"),
+ if (report_err)
+ gold_error(_("%s: unsupported reloc %u for IFUNC symbol"),
object->name().c_str(), r_type);
return false;
}
// A local STT_GNU_IFUNC symbol may require a PLT entry.
bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
- if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))
+ if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
if (parameters->options().output_is_position_independent()
|| (size == 64 && is_ifunc))
{
- Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-
+ Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
+ is_ifunc);
if ((size == 32 && r_type == elfcpp::R_POWERPC_ADDR32)
|| (size == 64 && r_type == elfcpp::R_PPC64_ADDR64))
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
- unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
- if (is_ifunc)
- {
- rela_dyn = target->iplt_section()->rel_plt();
- dynrel = elfcpp::R_POWERPC_IRELATIVE;
- }
+ unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+ : elfcpp::R_POWERPC_RELATIVE);
rela_dyn->add_local_relative(object, r_sym, dynrel,
output_section, data_shndx,
reloc.get_r_offset(),
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
- target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
- r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
- reloc.get_r_addend());
- break;
-
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
- target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
- r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
- reloc.get_r_addend());
+ if (!is_ifunc)
+ target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+ r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+ reloc.get_r_addend());
break;
case elfcpp::R_PPC64_REL64:
off = got->add_constant(0);
object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
- Reloc_section* rela_dyn = target->rela_dyn_section(layout);
- unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
- if (is_ifunc)
- {
- rela_dyn = target->iplt_section()->rel_plt();
- dynrel = elfcpp::R_POWERPC_IRELATIVE;
- }
+ 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);
}
= static_cast<Powerpc_relobj<size, big_endian>*>(object);
// A STT_GNU_IFUNC symbol may require a PLT entry.
- if (gsym->type() == elfcpp::STT_GNU_IFUNC
- && this->reloc_needs_plt_for_ifunc(object, r_type))
+ bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
+ if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
{
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
// Make a PLT entry if necessary.
if (gsym->needs_plt_entry())
{
- target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
- r_type,
- elfcpp::elf_r_sym<size>(reloc.get_r_info()),
- reloc.get_r_addend());
- target->make_plt_entry(symtab, layout, gsym);
+ if (!is_ifunc)
+ {
+ target->push_branch(ppc_object, data_shndx,
+ reloc.get_r_offset(), r_type,
+ elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+ reloc.get_r_addend());
+ target->make_plt_entry(symtab, layout, gsym);
+ }
// Since this is not a PC-relative relocation, we may be
// taking the address of a function. In that case we need to
// set the entry in the dynamic symbol table to the address of
}
// Make a dynamic relocation if necessary.
if (needs_dynamic_reloc<size>(gsym, Scan::get_reference_flags(r_type))
- || (size == 64 && gsym->type() == elfcpp::STT_GNU_IFUNC))
+ || (size == 64 && is_ifunc))
{
if (gsym->may_need_copy_reloc())
{
&& (gsym->can_use_relative_reloc(false)
|| data_shndx == ppc_object->opd_shndx())))
{
- Reloc_section* rela_dyn = target->rela_dyn_section(layout);
- unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
- if (gsym->type() == elfcpp::STT_GNU_IFUNC)
- {
- rela_dyn = target->iplt_section()->rel_plt();
- dynrel = elfcpp::R_POWERPC_IRELATIVE;
- }
+ 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_symbolless_global_addend(
gsym, dynrel, output_section, object, data_shndx,
reloc.get_r_offset(), reloc.get_r_addend());
}
else
{
- Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+ Reloc_section* rela_dyn
+ = target->rela_dyn_section(symtab, layout, is_ifunc);
check_non_pic(object, r_type);
rela_dyn->add_global(gsym, r_type, output_section,
object, data_shndx,
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_POWERPC_REL24:
- target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
- r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
- reloc.get_r_addend());
- if (gsym->needs_plt_entry()
- || (!gsym->final_value_is_known()
- && (gsym->is_undefined()
- || gsym->is_from_dynobj()
- || gsym->is_preemptible())))
- target->make_plt_entry(symtab, layout, gsym);
+ if (!is_ifunc)
+ {
+ target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+ r_type,
+ elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+ reloc.get_r_addend());
+ if (gsym->needs_plt_entry()
+ || (!gsym->final_value_is_known()
+ && (gsym->is_undefined()
+ || gsym->is_from_dynobj()
+ || gsym->is_preemptible())))
+ target->make_plt_entry(symtab, layout, gsym);
+ }
// Fall thru
case elfcpp::R_PPC64_REL64:
}
else
{
- Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+ Reloc_section* rela_dyn
+ = target->rela_dyn_section(symtab, layout, is_ifunc);
check_non_pic(object, r_type);
rela_dyn->add_global(gsym, r_type, output_section, object,
data_shndx, reloc.get_r_offset(),
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
- target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
- r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
- reloc.get_r_addend());
+ if (!is_ifunc)
+ target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+ r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+ reloc.get_r_addend());
break;
case elfcpp::R_POWERPC_REL16:
got = target->got_section(symtab, layout);
if (gsym->final_value_is_known())
{
- if (size == 32 && gsym->type() == elfcpp::STT_GNU_IFUNC)
+ if (size == 32 && is_ifunc)
got->add_global_plt(gsym, GOT_TYPE_STANDARD);
else
got->add_global(gsym, GOT_TYPE_STANDARD);
unsigned int off = got->add_constant(0);
gsym->set_got_offset(GOT_TYPE_STANDARD, off);
- Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+ Reloc_section* rela_dyn
+ = target->rela_dyn_section(symtab, layout, is_ifunc);
+
if (gsym->can_use_relative_reloc(false)
&& !(size == 32
&& gsym->visibility() == elfcpp::STV_PROTECTED
&& parameters->options().shared()))
{
- unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
- if (gsym->type() == elfcpp::STT_GNU_IFUNC)
- {
- rela_dyn = target->iplt_section()->rel_plt();
- dynrel = elfcpp::R_POWERPC_IRELATIVE;
- }
+ unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+ : elfcpp::R_POWERPC_RELATIVE);
rela_dyn->add_global_relative(gsym, dynrel, got, off, 0, false);
}
else
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
- got->add_global_pair_with_rel(gsym, GOT_TYPE_TLSGD,
- target->rela_dyn_section(layout),
+ Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+ got->add_global_pair_with_rel(gsym, GOT_TYPE_TLSGD, rela_dyn,
elfcpp::R_POWERPC_DTPMOD,
elfcpp::R_POWERPC_DTPREL);
}
}
}
+// For a symbol location in .opd, set LOC to the location of the
+// function entry.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_function_location(
+ Symbol_location* loc) const
+{
+ if (size == 64)
+ {
+ if (loc->object->is_dynamic())
+ {
+ Powerpc_dynobj<size, big_endian>* ppc_object
+ = static_cast<Powerpc_dynobj<size, big_endian>*>(loc->object);
+ if (loc->shndx == ppc_object->opd_shndx())
+ {
+ Address dest_off;
+ Address off = loc->offset - ppc_object->opd_address();
+ loc->shndx = ppc_object->get_opd_ent(off, &dest_off);
+ loc->offset = dest_off;
+ }
+ }
+ else
+ {
+ const Powerpc_relobj<size, big_endian>* ppc_object
+ = static_cast<const Powerpc_relobj<size, big_endian>*>(loc->object);
+ if (loc->shndx == ppc_object->opd_shndx())
+ {
+ Address dest_off;
+ loc->shndx = ppc_object->get_opd_ent(loc->offset, &dest_off);
+ loc->offset = dest_off;
+ }
+ }
+ }
+}
+
// Scan relocations for a section.
template<int size, bool big_endian>
Address value = 0;
bool has_plt_value = false;
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
- if (gsym != NULL
- ? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type))
- : object->local_has_plt_offset(r_sym))
+ if ((gsym != NULL
+ ? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type))
+ : object->local_has_plt_offset(r_sym))
+ && (!psymval->is_ifunc_symbol()
+ || Scan::reloc_needs_plt_for_ifunc(object, r_type, false)))
{
Stub_table<size, big_endian>* stub_table
= object->stub_table(relinfo->data_shndx);