binutils-gdb/gold/powerpc.cc
Alan Modra 282c975030 PR18147, relocation overflow when --unresolved-symbols=ignore-all
If ignoring unresolved symbols, ignore reloc overflows too.  If not
ignoring unresolved symbols we will report an error about the symbol
being undefined, making any report about reloc overflow superfluous.

	PR18147
	* powerpc.cc (Target_powerpc::Relocate::relocate): Don't report
	relocation errors for branches to strong undefined symbols.
2015-03-24 17:06:12 +10:30

8392 lines
247 KiB
C++

// powerpc.cc -- powerpc target support for gold.
// Copyright (C) 2008-2015 Free Software Foundation, Inc.
// Written by David S. Miller <davem@davemloft.net>
// and David Edelsohn <edelsohn@gnu.org>
// This file is part of gold.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "gold.h"
#include <set>
#include <algorithm>
#include "elfcpp.h"
#include "dwarf.h"
#include "parameters.h"
#include "reloc.h"
#include "powerpc.h"
#include "object.h"
#include "symtab.h"
#include "layout.h"
#include "output.h"
#include "copy-relocs.h"
#include "target.h"
#include "target-reloc.h"
#include "target-select.h"
#include "tls.h"
#include "errors.h"
#include "gc.h"
namespace
{
using namespace gold;
template<int size, bool big_endian>
class Output_data_plt_powerpc;
template<int size, bool big_endian>
class Output_data_brlt_powerpc;
template<int size, bool big_endian>
class Output_data_got_powerpc;
template<int size, bool big_endian>
class Output_data_glink;
template<int size, bool big_endian>
class Stub_table;
template<int size, bool big_endian>
class Target_powerpc;
struct Stub_table_owner
{
Output_section* output_section;
const Output_section::Input_section* owner;
};
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>
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
typedef Unordered_set<Section_id, Section_id_hash> Section_refs;
typedef Unordered_map<Address, Section_refs> Access_from;
Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
: Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
special_(0), has_small_toc_reloc_(false), opd_valid_(false),
opd_ent_(), access_from_map_(), has14_(), stub_table_index_(),
e_flags_(ehdr.get_e_flags()), st_other_()
{
this->set_abiversion(0);
}
~Powerpc_relobj()
{ }
// Read the symbols then set up st_other vector.
void
do_read_symbols(Read_symbols_data*);
// The .got2 section shndx.
unsigned int
got2_shndx() const
{
if (size == 32)
return this->special_;
else
return 0;
}
// The .opd section shndx.
unsigned int
opd_shndx() const
{
if (size == 32)
return 0;
else
return this->special_;
}
// 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;
}
// Return discard flag for .opd + R_OFF.
bool
get_opd_discard(Address r_off) const
{
size_t ndx = this->opd_ent_ndx(r_off);
gold_assert(ndx < this->opd_ent_.size());
return this->opd_ent_[ndx].discard;
}
// Set discard flag for .opd + R_OFF.
void
set_opd_discard(Address r_off)
{
size_t ndx = this->opd_ent_ndx(r_off);
gold_assert(ndx < this->opd_ent_.size());
this->opd_ent_[ndx].discard = true;
}
bool
opd_valid() const
{ return this->opd_valid_; }
void
set_opd_valid()
{ this->opd_valid_ = true; }
// Examine .rela.opd to build info about function entry points.
void
scan_opd_relocs(size_t reloc_count,
const unsigned char* prelocs,
const unsigned char* plocal_syms);
// Perform the Sized_relobj_file method, then set up opd info from
// .opd relocs.
void
do_read_relocs(Read_relocs_data*);
bool
do_find_special_sections(Read_symbols_data* sd);
// Adjust this local symbol value. Return false if the symbol
// should be discarded from the output file.
bool
do_adjust_local_symbol(Symbol_value<size>* lv) const
{
if (size == 64 && this->opd_shndx() != 0)
{
bool is_ordinary;
if (lv->input_shndx(&is_ordinary) != this->opd_shndx())
return true;
if (this->get_opd_discard(lv->input_value()))
return false;
}
return true;
}
Access_from*
access_from_map()
{ return &this->access_from_map_; }
// Add a reference from SRC_OBJ, SRC_INDX to this object's .opd
// section at DST_OFF.
void
add_reference(Object* src_obj,
unsigned int src_indx,
typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
{
Section_id src_id(src_obj, src_indx);
this->access_from_map_[dst_off].insert(src_id);
}
// Add a reference to the code section specified by the .opd entry
// at DST_OFF
void
add_gc_mark(typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
{
size_t ndx = this->opd_ent_ndx(dst_off);
if (ndx >= this->opd_ent_.size())
this->opd_ent_.resize(ndx + 1);
this->opd_ent_[ndx].gc_mark = true;
}
void
process_gc_mark(Symbol_table* symtab)
{
for (size_t i = 0; i < this->opd_ent_.size(); i++)
if (this->opd_ent_[i].gc_mark)
{
unsigned int shndx = this->opd_ent_[i].shndx;
symtab->gc()->worklist().push(Section_id(this, shndx));
}
}
// 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; }
bool
has_small_toc_reloc() const
{ return has_small_toc_reloc_; }
void
set_has_14bit_branch(unsigned int shndx)
{
if (shndx >= this->has14_.size())
this->has14_.resize(shndx + 1);
this->has14_[shndx] = true;
}
bool
has_14bit_branch(unsigned int shndx) const
{ return shndx < this->has14_.size() && this->has14_[shndx]; }
void
set_stub_table(unsigned int shndx, unsigned int stub_index)
{
if (shndx >= this->stub_table_index_.size())
this->stub_table_index_.resize(shndx + 1);
this->stub_table_index_[shndx] = stub_index;
}
Stub_table<size, big_endian>*
stub_table(unsigned int shndx)
{
if (shndx < this->stub_table_index_.size())
{
Target_powerpc<size, big_endian>* target
= static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
unsigned int indx = this->stub_table_index_[shndx];
gold_assert(indx < target->stub_tables().size());
return target->stub_tables()[indx];
}
return NULL;
}
void
clear_stub_table()
{
this->stub_table_index_.clear();
}
int
abiversion() const
{ return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
// Set ABI version for input and output
void
set_abiversion(int ver);
unsigned int
ppc64_local_entry_offset(const Symbol* sym) const
{ return elfcpp::ppc64_decode_local_entry(sym->nonvis() >> 3); }
unsigned int
ppc64_local_entry_offset(unsigned int symndx) const
{ return elfcpp::ppc64_decode_local_entry(this->st_other_[symndx] >> 5); }
private:
struct Opd_ent
{
unsigned int shndx;
bool discard : 1;
bool gc_mark : 1;
Address off;
};
// Return index into opd_ent_ array for .opd entry at OFF.
// .opd entries are 24 bytes long, but they can be spaced 16 bytes
// apart when the language doesn't use the last 8-byte word, the
// environment pointer. Thus dividing the entry section offset by
// 16 will give an index into opd_ent_ that works for either layout
// of .opd. (It leaves some elements of the vector unused when .opd
// entries are spaced 24 bytes apart, but we don't know the spacing
// until relocations are processed, and in any case it is possible
// for an object to have some entries spaced 16 bytes apart and
// others 24 bytes apart.)
size_t
opd_ent_ndx(size_t off) const
{ return off >> 4;}
// For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx.
unsigned int special_;
// For 64-bit, whether this object uses small model relocs to access
// the toc.
bool has_small_toc_reloc_;
// Set at the start of gc_process_relocs, when we know opd_ent_
// vector is valid. The flag could be made atomic and set in
// do_read_relocs with memory_order_release and then tested with
// memory_order_acquire, potentially resulting in fewer entries in
// access_from_map_.
bool opd_valid_;
// The first 8-byte word of an OPD entry gives the address of the
// entry point of the function. Relocatable object files have a
// relocation on this word. The following vector records the
// section and offset specified by these relocations.
std::vector<Opd_ent> opd_ent_;
// References made to this object's .opd section when running
// gc_process_relocs for another object, before the opd_ent_ vector
// is valid for this object.
Access_from access_from_map_;
// Whether input section has a 14-bit branch reloc.
std::vector<bool> has14_;
// The stub table to use for a given input section.
std::vector<unsigned int> stub_table_index_;
// Header e_flags
elfcpp::Elf_Word e_flags_;
// ELF st_other field for local symbols.
std::vector<unsigned char> st_other_;
};
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_(), e_flags_(ehdr.get_e_flags())
{
this->set_abiversion(0);
}
~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;
}
int
abiversion() const
{ return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
// Set ABI version for input and output.
void
set_abiversion(int ver);
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_;
// Header e_flags
elfcpp::Elf_Word e_flags_;
};
template<int size, bool big_endian>
class Target_powerpc : public Sized_target<size, big_endian>
{
public:
typedef
Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian> Reloc_section;
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
typedef typename elfcpp::Elf_types<size>::Elf_Swxword Signed_address;
static const Address invalid_address = static_cast<Address>(0) - 1;
// Offset of tp and dtp pointers from start of TLS block.
static const Address tp_offset = 0x7000;
static const Address dtp_offset = 0x8000;
Target_powerpc()
: Sized_target<size, big_endian>(&powerpc_info),
got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL),
glink_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_POWERPC_COPY),
tlsld_got_offset_(-1U),
stub_tables_(), branch_lookup_table_(), branch_info_(),
plt_thread_safe_(false), relax_failed_(false), relax_fail_count_(0),
stub_group_size_(0)
{
}
// Process the relocations to determine unreferenced sections for
// garbage collection.
void
gc_process_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols);
// Scan the relocations to look for symbol adjustments.
void
scan_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols);
// Map input .toc section to output .got section.
const char*
do_output_section_name(const Relobj*, const char* name, size_t* plen) const
{
if (size == 64 && strcmp(name, ".toc") == 0)
{
*plen = 4;
return ".got";
}
return NULL;
}
// Provide linker defined save/restore functions.
void
define_save_restore_funcs(Layout*, Symbol_table*);
// No stubs unless a final link.
bool
do_may_relax() const
{ return !parameters->options().relocatable(); }
bool
do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*);
void
do_plt_fde_location(const Output_data*, unsigned char*,
uint64_t*, off_t*) const;
// Stash info about branches, for stub generation.
void
push_branch(Powerpc_relobj<size, big_endian>* ppc_object,
unsigned int data_shndx, Address r_offset,
unsigned int r_type, unsigned int r_sym, Address addend)
{
Branch_info info(ppc_object, data_shndx, r_offset, r_type, r_sym, addend);
this->branch_info_.push_back(info);
if (r_type == elfcpp::R_POWERPC_REL14
|| r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
|| r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
ppc_object->set_has_14bit_branch(data_shndx);
}
void
do_define_standard_symbols(Symbol_table*, Layout*);
// Finalize the sections.
void
do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
// Return the value to use for a dynamic which requires special
// treatment.
uint64_t
do_dynsym_value(const Symbol*) const;
// Return the PLT address to use for a local symbol.
uint64_t
do_plt_address_for_local(const Relobj*, unsigned int) const;
// Return the PLT address to use for a global symbol.
uint64_t
do_plt_address_for_global(const Symbol*) const;
// Return the offset to use for the GOT_INDX'th got entry which is
// for a local tls symbol specified by OBJECT, SYMNDX.
int64_t
do_tls_offset_for_local(const Relobj* object,
unsigned int symndx,
unsigned int got_indx) 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;
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 sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
unsigned char* view,
Address view_address,
section_size_type view_size,
const Reloc_symbol_changes*);
// Scan the relocs during a relocatable link.
void
scan_relocatable_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols,
Relocatable_relocs*);
// Emit relocations for a section.
void
relocate_relocs(const Relocate_info<size, big_endian>*,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off
offset_in_output_section,
const Relocatable_relocs*,
unsigned char*,
Address view_address,
section_size_type,
unsigned char* reloc_view,
section_size_type reloc_view_size);
// Return whether SYM is defined by the ABI.
bool
do_is_defined_by_abi(const Symbol* sym) const
{
return strcmp(sym->name(), "__tls_get_addr") == 0;
}
// Return the size of the GOT section.
section_size_type
got_size() const
{
gold_assert(this->got_ != NULL);
return this->got_->data_size();
}
// Get the PLT section.
const Output_data_plt_powerpc<size, big_endian>*
plt_section() const
{
gold_assert(this->plt_ != NULL);
return this->plt_;
}
// Get the IPLT section.
const Output_data_plt_powerpc<size, big_endian>*
iplt_section() const
{
gold_assert(this->iplt_ != NULL);
return this->iplt_;
}
// Get the .glink section.
const Output_data_glink<size, big_endian>*
glink_section() const
{
gold_assert(this->glink_ != NULL);
return this->glink_;
}
Output_data_glink<size, big_endian>*
glink_section()
{
gold_assert(this->glink_ != NULL);
return this->glink_;
}
bool has_glink() const
{ return this->glink_ != NULL; }
// Get the GOT section.
const Output_data_got_powerpc<size, big_endian>*
got_section() const
{
gold_assert(this->got_ != NULL);
return this->got_;
}
// Get the GOT section, creating it if necessary.
Output_data_got_powerpc<size, big_endian>*
got_section(Symbol_table*, Layout*);
Object*
do_make_elf_object(const std::string&, Input_file*, off_t,
const elfcpp::Ehdr<size, big_endian>&);
// Return the number of entries in the GOT.
unsigned int
got_entry_count() const
{
if (this->got_ == NULL)
return 0;
return this->got_size() / (size / 8);
}
// Return the number of entries in the PLT.
unsigned int
plt_entry_count() const;
// Return the offset of the first non-reserved PLT entry.
unsigned int
first_plt_entry_offset() const
{
if (size == 32)
return 0;
if (this->abiversion() >= 2)
return 16;
return 24;
}
// Return the size of each PLT entry.
unsigned int
plt_entry_size() const
{
if (size == 32)
return 4;
if (this->abiversion() >= 2)
return 8;
return 24;
}
// Add any special sections for this symbol to the gc work list.
// For powerpc64, this adds the code section of a function
// descriptor.
void
do_gc_mark_symbol(Symbol_table* symtab, Symbol* sym) const;
// Handle target specific gc actions when adding a gc reference from
// SRC_OBJ, SRC_SHNDX to a location specified by DST_OBJ, DST_SHNDX
// and DST_OFF. For powerpc64, this adds a referenc to the code
// section of a function descriptor.
void
do_gc_add_reference(Symbol_table* symtab,
Object* src_obj,
unsigned int src_shndx,
Object* dst_obj,
unsigned int dst_shndx,
Address dst_off) const;
typedef std::vector<Stub_table<size, big_endian>*> Stub_tables;
const Stub_tables&
stub_tables() const
{ return this->stub_tables_; }
const Output_data_brlt_powerpc<size, big_endian>*
brlt_section() const
{ return this->brlt_section_; }
void
add_branch_lookup_table(Address to)
{
unsigned int off = this->branch_lookup_table_.size() * (size / 8);
this->branch_lookup_table_.insert(std::make_pair(to, off));
}
Address
find_branch_lookup_table(Address to)
{
typename Branch_lookup_table::const_iterator p
= this->branch_lookup_table_.find(to);
return p == this->branch_lookup_table_.end() ? invalid_address : p->second;
}
void
write_branch_lookup_table(unsigned char *oview)
{
for (typename Branch_lookup_table::const_iterator p
= this->branch_lookup_table_.begin();
p != this->branch_lookup_table_.end();
++p)
{
elfcpp::Swap<size, big_endian>::writeval(oview + p->second, p->first);
}
}
bool
plt_thread_safe() const
{ return this->plt_thread_safe_; }
int
abiversion () const
{ return this->processor_specific_flags() & elfcpp::EF_PPC64_ABI; }
void
set_abiversion (int ver)
{
elfcpp::Elf_Word flags = this->processor_specific_flags();
flags &= ~elfcpp::EF_PPC64_ABI;
flags |= ver & elfcpp::EF_PPC64_ABI;
this->set_processor_specific_flags(flags);
}
// Offset to to save stack slot
int
stk_toc () const
{ return this->abiversion() < 2 ? 40 : 24; }
private:
class Track_tls
{
public:
enum Tls_get_addr
{
NOT_EXPECTED = 0,
EXPECTED = 1,
SKIP = 2,
NORMAL = 3
};
Track_tls()
: tls_get_addr_(NOT_EXPECTED),
relinfo_(NULL), relnum_(0), r_offset_(0)
{ }
~Track_tls()
{
if (this->tls_get_addr_ != NOT_EXPECTED)
this->missing();
}
void
missing(void)
{
if (this->relinfo_ != NULL)
gold_error_at_location(this->relinfo_, this->relnum_, this->r_offset_,
_("missing expected __tls_get_addr call"));
}
void
expect_tls_get_addr_call(
const Relocate_info<size, big_endian>* relinfo,
size_t relnum,
Address r_offset)
{
this->tls_get_addr_ = EXPECTED;
this->relinfo_ = relinfo;
this->relnum_ = relnum;
this->r_offset_ = r_offset;
}
void
expect_tls_get_addr_call()
{ this->tls_get_addr_ = EXPECTED; }
void
skip_next_tls_get_addr_call()
{this->tls_get_addr_ = SKIP; }
Tls_get_addr
maybe_skip_tls_get_addr_call(unsigned int r_type, const Symbol* gsym)
{
bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC_PLTREL24)
&& gsym != NULL
&& strcmp(gsym->name(), "__tls_get_addr") == 0);
Tls_get_addr last_tls = this->tls_get_addr_;
this->tls_get_addr_ = NOT_EXPECTED;
if (is_tls_call && last_tls != EXPECTED)
return last_tls;
else if (!is_tls_call && last_tls != NOT_EXPECTED)
{
this->missing();
return EXPECTED;
}
return NORMAL;
}
private:
// What we're up to regarding calls to __tls_get_addr.
// On powerpc, the branch and link insn making a call to
// __tls_get_addr is marked with a relocation, R_PPC64_TLSGD,
// R_PPC64_TLSLD, R_PPC_TLSGD or R_PPC_TLSLD, in addition to the
// usual R_POWERPC_REL24 or R_PPC_PLTREL25 relocation on a call.
// The marker relocation always comes first, and has the same
// symbol as the reloc on the insn setting up the __tls_get_addr
// argument. This ties the arg setup insn with the call insn,
// allowing ld to safely optimize away the call. We check that
// every call to __tls_get_addr has a marker relocation, and that
// every marker relocation is on a call to __tls_get_addr.
Tls_get_addr tls_get_addr_;
// Info about the last reloc for error message.
const Relocate_info<size, big_endian>* relinfo_;
size_t relnum_;
Address r_offset_;
};
// The class which scans relocations.
class Scan : protected Track_tls
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
Scan()
: Track_tls(), issued_non_pic_error_(false)
{ }
static inline int
get_reference_flags(unsigned int r_type, const Target_powerpc* target);
inline void
local(Symbol_table* symtab, Layout* layout, Target_powerpc* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
const elfcpp::Sym<size, big_endian>& lsym,
bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_powerpc* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
Symbol* gsym);
inline bool
local_reloc_may_be_function_pointer(Symbol_table* , Layout* ,
Target_powerpc* ,
Sized_relobj_file<size, big_endian>* relobj,
unsigned int ,
Output_section* ,
const elfcpp::Rela<size, big_endian>& ,
unsigned int r_type,
const elfcpp::Sym<size, big_endian>&)
{
// 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)
{
Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<Powerpc_relobj<size, big_endian>*>(relobj);
if (ppcobj->abiversion() == 1)
return false;
}
// For 32-bit and ELFv2, 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* ,
Target_powerpc* ,
Sized_relobj_file<size, big_endian>* relobj,
unsigned int ,
Output_section* ,
const elfcpp::Rela<size, big_endian>& ,
unsigned int r_type,
Symbol*)
{
// As above.
if (size == 64)
{
Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<Powerpc_relobj<size, big_endian>*>(relobj);
if (ppcobj->abiversion() == 1)
return false;
}
return !is_branch_reloc(r_type);
}
static bool
reloc_needs_plt_for_ifunc(Target_powerpc<size, big_endian>* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int r_type, bool report_err);
private:
static void
unsupported_reloc_local(Sized_relobj_file<size, big_endian>*,
unsigned int r_type);
static void
unsupported_reloc_global(Sized_relobj_file<size, big_endian>*,
unsigned int r_type, Symbol*);
static void
generate_tls_call(Symbol_table* symtab, Layout* layout,
Target_powerpc* target);
void
check_non_pic(Relobj*, unsigned int r_type);
// Whether we have issued an error about a non-PIC compilation.
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
{
public:
// Use 'at' branch hints when true, 'y' when false.
// FIXME maybe: set this with an option.
static const bool is_isa_v2 = true;
Relocate()
: Track_tls()
{ }
// Do a relocation. Return false if the caller should not issue
// any warnings about this relocation.
inline bool
relocate(const Relocate_info<size, big_endian>*, Target_powerpc*,
Output_section*, size_t relnum,
const elfcpp::Rela<size, big_endian>&,
unsigned int r_type, const Sized_symbol<size>*,
const Symbol_value<size>*,
unsigned char*,
typename elfcpp::Elf_types<size>::Elf_Addr,
section_size_type);
};
class Relocate_comdat_behavior
{
public:
// Decide what the linker should do for relocations that refer to
// discarded comdat sections.
inline Comdat_behavior
get(const char* name)
{
gold::Default_comdat_behavior default_behavior;
Comdat_behavior ret = default_behavior.get(name);
if (ret == CB_WARNING)
{
if (size == 32
&& (strcmp(name, ".fixup") == 0
|| strcmp(name, ".got2") == 0))
ret = CB_IGNORE;
if (size == 64
&& (strcmp(name, ".opd") == 0
|| strcmp(name, ".toc") == 0
|| strcmp(name, ".toc1") == 0))
ret = CB_IGNORE;
}
return ret;
}
};
// A class which returns the size required for a relocation type,
// used while scanning relocs during a relocatable link.
class Relocatable_size_for_reloc
{
public:
unsigned int
get_size_for_reloc(unsigned int, Relobj*)
{
gold_unreachable();
return 0;
}
};
// Optimize the TLS relocation type based on what we know about the
// symbol. IS_FINAL is true if the final address of this symbol is
// known at link time.
tls::Tls_optimization
optimize_tls_gd(bool is_final)
{
// If we are generating a shared library, then we can't do anything
// in the linker.
if (parameters->options().shared())
return tls::TLSOPT_NONE;
if (!is_final)
return tls::TLSOPT_TO_IE;
return tls::TLSOPT_TO_LE;
}
tls::Tls_optimization
optimize_tls_ld()
{
if (parameters->options().shared())
return tls::TLSOPT_NONE;
return tls::TLSOPT_TO_LE;
}
tls::Tls_optimization
optimize_tls_ie(bool is_final)
{
if (!is_final || parameters->options().shared())
return tls::TLSOPT_NONE;
return tls::TLSOPT_TO_LE;
}
// Create glink.
void
make_glink_section(Layout*);
// Create the PLT section.
void
make_plt_section(Symbol_table*, Layout*);
void
make_iplt_section(Symbol_table*, Layout*);
void
make_brlt_section(Layout*);
// Create a PLT entry for a global symbol.
void
make_plt_entry(Symbol_table*, Layout*, Symbol*);
// Create a PLT entry for a local IFUNC symbol.
void
make_local_ifunc_plt_entry(Symbol_table*, Layout*,
Sized_relobj_file<size, big_endian>*,
unsigned int);
// Create a GOT entry for local dynamic __tls_get_addr.
unsigned int
tlsld_got_offset(Symbol_table* symtab, Layout* layout,
Sized_relobj_file<size, big_endian>* object);
unsigned int
tlsld_got_offset() const
{
return this->tlsld_got_offset_;
}
// Get the dynamic reloc section, creating it if necessary.
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,
Sized_relobj_file<size, big_endian>* object,
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rela<size, big_endian>& reloc)
{
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<size>(sym),
object, shndx, output_section,
reloc, this->rela_dyn_section(layout));
}
// Look over all the input sections, deciding where to place stubs.
void
group_sections(Layout*, const Task*, bool);
// Sort output sections by address.
struct Sort_sections
{
bool
operator()(const Output_section* sec1, const Output_section* sec2)
{ return sec1->address() < sec2->address(); }
};
class Branch_info
{
public:
Branch_info(Powerpc_relobj<size, big_endian>* ppc_object,
unsigned int data_shndx,
Address r_offset,
unsigned int r_type,
unsigned int r_sym,
Address addend)
: object_(ppc_object), shndx_(data_shndx), offset_(r_offset),
r_type_(r_type), r_sym_(r_sym), addend_(addend)
{ }
~Branch_info()
{ }
// If this branch needs a plt call stub, or a long branch stub, make one.
bool
make_stub(Stub_table<size, big_endian>*,
Stub_table<size, big_endian>*,
Symbol_table*) const;
private:
// The branch location..
Powerpc_relobj<size, big_endian>* object_;
unsigned int shndx_;
Address offset_;
// ..and the branch type and destination.
unsigned int r_type_;
unsigned int r_sym_;
Address addend_;
};
// Information about this specific target which we pass to the
// 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.
Output_data_got_powerpc<size, big_endian>* got_;
// 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. 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_;
// The .glink section.
Output_data_glink<size, big_endian>* glink_;
// The dynamic reloc section.
Reloc_section* rela_dyn_;
// Relocs saved to avoid a COPY reloc.
Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
// Offset of the GOT entry for local dynamic __tls_get_addr calls.
unsigned int tlsld_got_offset_;
Stub_tables stub_tables_;
typedef Unordered_map<Address, unsigned int> Branch_lookup_table;
Branch_lookup_table branch_lookup_table_;
typedef std::vector<Branch_info> Branches;
Branches branch_info_;
bool plt_thread_safe_;
bool relax_failed_;
int relax_fail_count_;
int32_t stub_group_size_;
};
template<>
Target::Target_info Target_powerpc<32, true>::powerpc_info =
{
32, // size
true, // is_big_endian
elfcpp::EM_PPC, // machine_code
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/usr/lib/ld.so.1", // dynamic_linker
0x10000000, // default_text_segment_address
64 * 1024, // abi_pagesize (overridable by -z max-page-size)
4 * 1024, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
"_start" // entry_symbol_name
};
template<>
Target::Target_info Target_powerpc<32, false>::powerpc_info =
{
32, // size
false, // is_big_endian
elfcpp::EM_PPC, // machine_code
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/usr/lib/ld.so.1", // dynamic_linker
0x10000000, // default_text_segment_address
64 * 1024, // abi_pagesize (overridable by -z max-page-size)
4 * 1024, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
"_start" // entry_symbol_name
};
template<>
Target::Target_info Target_powerpc<64, true>::powerpc_info =
{
64, // size
true, // is_big_endian
elfcpp::EM_PPC64, // machine_code
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/usr/lib/ld.so.1", // dynamic_linker
0x10000000, // default_text_segment_address
64 * 1024, // abi_pagesize (overridable by -z max-page-size)
4 * 1024, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
"_start" // entry_symbol_name
};
template<>
Target::Target_info Target_powerpc<64, false>::powerpc_info =
{
64, // size
false, // is_big_endian
elfcpp::EM_PPC64, // machine_code
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
"/usr/lib/ld.so.1", // dynamic_linker
0x10000000, // default_text_segment_address
64 * 1024, // abi_pagesize (overridable by -z max-page-size)
4 * 1024, // common_pagesize (overridable by -z common-page-size)
false, // isolate_execinstr
0, // rosegment_gap
elfcpp::SHN_UNDEF, // small_common_shndx
elfcpp::SHN_UNDEF, // large_common_shndx
0, // small_common_section_flags
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
"_start" // entry_symbol_name
};
inline bool
is_branch_reloc(unsigned int r_type)
{
return (r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC_PLTREL24
|| r_type == elfcpp::R_PPC_LOCAL24PC
|| r_type == elfcpp::R_POWERPC_REL14
|| r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
|| r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN
|| r_type == elfcpp::R_POWERPC_ADDR24
|| r_type == elfcpp::R_POWERPC_ADDR14
|| r_type == elfcpp::R_POWERPC_ADDR14_BRTAKEN
|| r_type == elfcpp::R_POWERPC_ADDR14_BRNTAKEN);
}
// 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.
uint32_t
at_tls_transform(uint32_t insn, unsigned int reg)
{
if ((insn & (0x3f << 26)) != 31 << 26)
return 0;
unsigned int rtra;
if (reg == 0 || ((insn >> 11) & 0x1f) == reg)
rtra = insn & ((1 << 26) - (1 << 16));
else if (((insn >> 16) & 0x1f) == reg)
rtra = (insn & (0x1f << 21)) | ((insn & (0x1f << 11)) << 5);
else
return 0;
if ((insn & (0x3ff << 1)) == 266 << 1)
// add -> addi
insn = 14 << 26;
else if ((insn & (0x1f << 1)) == 23 << 1
&& ((insn & (0x1f << 6)) < 14 << 6
|| ((insn & (0x1f << 6)) >= 16 << 6
&& (insn & (0x1f << 6)) < 24 << 6)))
// load and store indexed -> dform
insn = (32 | ((insn >> 6) & 0x1f)) << 26;
else if ((insn & (((0x1a << 5) | 0x1f) << 1)) == 21 << 1)
// ldx, ldux, stdx, stdux -> ld, ldu, std, stdu
insn = ((58 | ((insn >> 6) & 4)) << 26) | ((insn >> 6) & 1);
else if ((insn & (((0x1f << 5) | 0x1f) << 1)) == 341 << 1)
// lwax -> lwa
insn = (58 << 26) | 2;
else
return 0;
insn |= rtra;
return insn;
}
template<int size, bool big_endian>
class Powerpc_relocate_functions
{
public:
enum Overflow_check
{
CHECK_NONE,
CHECK_SIGNED,
CHECK_UNSIGNED,
CHECK_BITFIELD,
CHECK_LOW_INSN,
CHECK_HIGH_INSN
};
enum Status
{
STATUS_OK,
STATUS_OVERFLOW
};
private:
typedef Powerpc_relocate_functions<size, big_endian> This;
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
template<int valsize>
static inline bool
has_overflow_signed(Address value)
{
// limit = 1 << (valsize - 1) without shift count exceeding size of type
Address limit = static_cast<Address>(1) << ((valsize - 1) >> 1);
limit <<= ((valsize - 1) >> 1);
limit <<= ((valsize - 1) - 2 * ((valsize - 1) >> 1));
return value + limit > (limit << 1) - 1;
}
template<int valsize>
static inline bool
has_overflow_unsigned(Address value)
{
Address limit = static_cast<Address>(1) << ((valsize - 1) >> 1);
limit <<= ((valsize - 1) >> 1);
limit <<= ((valsize - 1) - 2 * ((valsize - 1) >> 1));
return value > (limit << 1) - 1;
}
template<int valsize>
static inline bool
has_overflow_bitfield(Address value)
{
return (has_overflow_unsigned<valsize>(value)
&& has_overflow_signed<valsize>(value));
}
template<int valsize>
static inline Status
overflowed(Address value, Overflow_check overflow)
{
if (overflow == CHECK_SIGNED)
{
if (has_overflow_signed<valsize>(value))
return STATUS_OVERFLOW;
}
else if (overflow == CHECK_UNSIGNED)
{
if (has_overflow_unsigned<valsize>(value))
return STATUS_OVERFLOW;
}
else if (overflow == CHECK_BITFIELD)
{
if (has_overflow_bitfield<valsize>(value))
return STATUS_OVERFLOW;
}
return STATUS_OK;
}
// Do a simple RELA relocation
template<int fieldsize, int valsize>
static inline Status
rela(unsigned char* view, Address value, Overflow_check overflow)
{
typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
elfcpp::Swap<fieldsize, big_endian>::writeval(wv, value);
return overflowed<valsize>(value, overflow);
}
template<int fieldsize, int valsize>
static inline Status
rela(unsigned char* view,
unsigned int right_shift,
typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask,
Address value,
Overflow_check overflow)
{
typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(wv);
Valtype reloc = value >> right_shift;
val &= ~dst_mask;
reloc &= dst_mask;
elfcpp::Swap<fieldsize, big_endian>::writeval(wv, val | reloc);
return overflowed<valsize>(value >> right_shift, overflow);
}
// Do a simple RELA relocation, unaligned.
template<int fieldsize, int valsize>
static inline Status
rela_ua(unsigned char* view, Address value, Overflow_check overflow)
{
elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, value);
return overflowed<valsize>(value, overflow);
}
template<int fieldsize, int valsize>
static inline Status
rela_ua(unsigned char* view,
unsigned int right_shift,
typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask,
Address value,
Overflow_check overflow)
{
typedef typename elfcpp::Swap_unaligned<fieldsize, big_endian>::Valtype
Valtype;
Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(view);
Valtype reloc = value >> right_shift;
val &= ~dst_mask;
reloc &= dst_mask;
elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, val | reloc);
return overflowed<valsize>(value >> right_shift, overflow);
}
public:
// R_PPC64_ADDR64: (Symbol + Addend)
static inline void
addr64(unsigned char* view, Address value)
{ This::template rela<64,64>(view, value, CHECK_NONE); }
// R_PPC64_UADDR64: (Symbol + Addend) unaligned
static inline void
addr64_u(unsigned char* view, Address value)
{ This::template rela_ua<64,64>(view, value, CHECK_NONE); }
// R_POWERPC_ADDR32: (Symbol + Addend)
static inline Status
addr32(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela<32,32>(view, value, overflow); }
// R_POWERPC_UADDR32: (Symbol + Addend) unaligned
static inline Status
addr32_u(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela_ua<32,32>(view, value, overflow); }
// R_POWERPC_ADDR24: (Symbol + Addend) & 0x3fffffc
static inline Status
addr24(unsigned char* view, Address value, Overflow_check overflow)
{
Status stat = This::template rela<32,26>(view, 0, 0x03fffffc,
value, overflow);
if (overflow != CHECK_NONE && (value & 3) != 0)
stat = STATUS_OVERFLOW;
return stat;
}
// R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff
static inline Status
addr16(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela<16,16>(view, value, overflow); }
// R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff, unaligned
static inline Status
addr16_u(unsigned char* view, Address value, Overflow_check overflow)
{ return This::template rela_ua<16,16>(view, value, overflow); }
// R_POWERPC_ADDR16_DS: (Symbol + Addend) & 0xfffc
static inline Status
addr16_ds(unsigned char* view, Address value, Overflow_check overflow)
{
Status stat = This::template rela<16,16>(view, 0, 0xfffc, value, overflow);
if ((value & 3) != 0)
stat = STATUS_OVERFLOW;
return stat;
}
// R_POWERPC_ADDR16_HI: ((Symbol + Addend) >> 16) & 0xffff
static inline void
addr16_hi(unsigned char* view, Address value)
{ This::template rela<16,16>(view, 16, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HA: ((Symbol + Addend + 0x8000) >> 16) & 0xffff
static inline void
addr16_ha(unsigned char* view, Address value)
{ This::addr16_hi(view, value + 0x8000); }
// R_POWERPC_ADDR16_HIGHER: ((Symbol + Addend) >> 32) & 0xffff
static inline void
addr16_hi2(unsigned char* view, Address value)
{ This::template rela<16,16>(view, 32, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHERA: ((Symbol + Addend + 0x8000) >> 32) & 0xffff
static inline void
addr16_ha2(unsigned char* view, Address value)
{ This::addr16_hi2(view, value + 0x8000); }
// R_POWERPC_ADDR16_HIGHEST: ((Symbol + Addend) >> 48) & 0xffff
static inline void
addr16_hi3(unsigned char* view, Address value)
{ This::template rela<16,16>(view, 48, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHESTA: ((Symbol + Addend + 0x8000) >> 48) & 0xffff
static inline void
addr16_ha3(unsigned char* view, Address value)
{ This::addr16_hi3(view, value + 0x8000); }
// R_POWERPC_ADDR14: (Symbol + Addend) & 0xfffc
static inline Status
addr14(unsigned char* view, Address value, Overflow_check overflow)
{
Status stat = This::template rela<32,16>(view, 0, 0xfffc, value, overflow);
if (overflow != CHECK_NONE && (value & 3) != 0)
stat = STATUS_OVERFLOW;
return stat;
}
};
// Set ABI version for input and output.
template<int size, bool big_endian>
void
Powerpc_relobj<size, big_endian>::set_abiversion(int ver)
{
this->e_flags_ |= ver;
if (this->abiversion() != 0)
{
Target_powerpc<size, big_endian>* target =
static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
if (target->abiversion() == 0)
target->set_abiversion(this->abiversion());
else if (target->abiversion() != this->abiversion())
gold_error(_("%s: ABI version %d is not compatible "
"with ABI version %d output"),
this->name().c_str(),
this->abiversion(), target->abiversion());
}
}
// Stash away the index of .got2 or .opd in a relocatable object, if
// such a section exists.
template<int size, bool big_endian>
bool
Powerpc_relobj<size, big_endian>::do_find_special_sections(
Read_symbols_data* sd)
{
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);
section_size_type names_size = sd->section_names_size;
const unsigned char* s;
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;
this->special_ = ndx;
if (size == 64)
{
if (this->abiversion() == 0)
this->set_abiversion(1);
else if (this->abiversion() > 1)
gold_error(_("%s: .opd invalid in abiv%d"),
this->name().c_str(), this->abiversion());
}
}
return Sized_relobj_file<size, big_endian>::do_find_special_sections(sd);
}
// Examine .rela.opd to build info about function entry points.
template<int size, bool big_endian>
void
Powerpc_relobj<size, big_endian>::scan_opd_relocs(
size_t reloc_count,
const unsigned char* prelocs,
const unsigned char* plocal_syms)
{
if (size == 64)
{
typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
Reltype;
const int reloc_size
= Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
Address expected_off = 0;
bool regular = true;
unsigned int opd_ent_size = 0;
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
{
Reltype reloc(prelocs);
typename elfcpp::Elf_types<size>::Elf_WXword r_info
= reloc.get_r_info();
unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
if (r_type == elfcpp::R_PPC64_ADDR64)
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
typename elfcpp::Elf_types<size>::Elf_Addr value;
bool is_ordinary;
unsigned int shndx;
if (r_sym < this->local_symbol_count())
{
typename elfcpp::Sym<size, big_endian>
lsym(plocal_syms + r_sym * sym_size);
shndx = lsym.get_st_shndx();
shndx = this->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
value = lsym.get_st_value();
}
else
shndx = this->symbol_section_and_value(r_sym, &value,
&is_ordinary);
this->set_opd_ent(reloc.get_r_offset(), shndx,
value + reloc.get_r_addend());
if (i == 2)
{
expected_off = reloc.get_r_offset();
opd_ent_size = expected_off;
}
else if (expected_off != reloc.get_r_offset())
regular = false;
expected_off += opd_ent_size;
}
else if (r_type == elfcpp::R_PPC64_TOC)
{
if (expected_off - opd_ent_size + 8 != reloc.get_r_offset())
regular = false;
}
else
{
gold_warning(_("%s: unexpected reloc type %u in .opd section"),
this->name().c_str(), r_type);
regular = false;
}
}
if (reloc_count <= 2)
opd_ent_size = this->section_size(this->opd_shndx());
if (opd_ent_size != 24 && opd_ent_size != 16)
regular = false;
if (!regular)
{
gold_warning(_("%s: .opd is not a regular array of opd entries"),
this->name().c_str());
opd_ent_size = 0;
}
}
}
template<int size, bool big_endian>
void
Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
{
Sized_relobj_file<size, big_endian>::do_read_relocs(rd);
if (size == 64)
{
for (Read_relocs_data::Relocs_list::iterator p = rd->relocs.begin();
p != rd->relocs.end();
++p)
{
if (p->data_shndx == this->opd_shndx())
{
uint64_t opd_size = this->section_size(this->opd_shndx());
gold_assert(opd_size == static_cast<size_t>(opd_size));
if (opd_size != 0)
{
this->init_opd(opd_size);
this->scan_opd_relocs(p->reloc_count, p->contents->data(),
rd->local_symbols->data());
}
break;
}
}
}
}
// Read the symbols then set up st_other vector.
template<int size, bool big_endian>
void
Powerpc_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
{
this->base_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 int loccount = this->do_local_symbol_count();
if (loccount != 0)
{
this->st_other_.resize(loccount);
const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
off_t locsize = loccount * sym_size;
const unsigned int symtab_shndx = this->symtab_shndx();
const unsigned char *psymtab = pshdrs + symtab_shndx * shdr_size;
typename elfcpp::Shdr<size, big_endian> shdr(psymtab);
const unsigned char* psyms = this->get_view(shdr.get_sh_offset(),
locsize, true, false);
psyms += sym_size;
for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
{
elfcpp::Sym<size, big_endian> sym(psyms);
unsigned char st_other = sym.get_st_other();
this->st_other_[i] = st_other;
if ((st_other & elfcpp::STO_PPC64_LOCAL_MASK) != 0)
{
if (this->abiversion() == 0)
this->set_abiversion(2);
else if (this->abiversion() < 2)
gold_error(_("%s: local symbol %d has invalid st_other"
" for ABI version 1"),
this->name().c_str(), i);
}
}
}
}
}
template<int size, bool big_endian>
void
Powerpc_dynobj<size, big_endian>::set_abiversion(int ver)
{
this->e_flags_ |= ver;
if (this->abiversion() != 0)
{
Target_powerpc<size, big_endian>* target =
static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
if (target->abiversion() == 0)
target->set_abiversion(this->abiversion());
else if (target->abiversion() != this->abiversion())
gold_error(_("%s: ABI version %d is not compatible "
"with ABI version %d output"),
this->name().c_str(),
this->abiversion(), target->abiversion());
}
}
// Call Sized_dynobj::base_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)
{
this->base_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)
{
if (this->abiversion() == 0)
this->set_abiversion(1);
else if (this->abiversion() > 1)
gold_error(_("%s: .opd invalid in abiv%d"),
this->name().c_str(), this->abiversion());
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>
void
Target_powerpc<size, big_endian>::do_define_standard_symbols(
Symbol_table* symtab,
Layout* layout)
{
if (size == 32)
{
// Define _GLOBAL_OFFSET_TABLE_ to ensure it isn't seen as
// undefined when scanning relocs (and thus requires
// non-relative dynamic relocs). The proper value will be
// updated later.
Symbol *gotsym = symtab->lookup("_GLOBAL_OFFSET_TABLE_", NULL);
if (gotsym != NULL && gotsym->is_undefined())
{
Target_powerpc<size, big_endian>* target =
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);
symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
Symbol_table::PREDEFINED,
got, 0, 0,
elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
false, false);
}
// Define _SDA_BASE_ at the start of the .sdata section + 32768.
Symbol *sdasym = symtab->lookup("_SDA_BASE_", NULL);
if (sdasym != NULL && sdasym->is_undefined())
{
Output_data_space* sdata = new Output_data_space(4, "** sdata");
Output_section* os
= layout->add_output_section_data(".sdata", 0,
elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE,
sdata, ORDER_SMALL_DATA, false);
symtab->define_in_output_data("_SDA_BASE_", NULL,
Symbol_table::PREDEFINED,
os, 32768, 0, elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL, elfcpp::STV_HIDDEN,
0, false, false);
}
}
else
{
// Define .TOC. as for 32-bit _GLOBAL_OFFSET_TABLE_
Symbol *gotsym = symtab->lookup(".TOC.", NULL);
if (gotsym != NULL && gotsym->is_undefined())
{
Target_powerpc<size, big_endian>* target =
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);
symtab->define_in_output_data(".TOC.", NULL,
Symbol_table::PREDEFINED,
got, 0x8000, 0,
elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
false, false);
}
}
}
// Set up PowerPC target specific relobj.
template<int size, bool big_endian>
Object*
Target_powerpc<size, big_endian>::do_make_elf_object(
const std::string& name,
Input_file* input_file,
off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr)
{
int et = ehdr.get_e_type();
// ET_EXEC files are valid input for --just-symbols/-R,
// and we treat them as relocatable objects.
if (et == elfcpp::ET_REL
|| (et == elfcpp::ET_EXEC && input_file->just_symbols()))
{
Powerpc_relobj<size, big_endian>* obj =
new Powerpc_relobj<size, big_endian>(name, input_file, offset, ehdr);
obj->setup();
return obj;
}
else if (et == elfcpp::ET_DYN)
{
Powerpc_dynobj<size, big_endian>* obj =
new Powerpc_dynobj<size, big_endian>(name, input_file, offset, ehdr);
obj->setup();
return obj;
}
else
{
gold_error(_("%s: unsupported ELF file type %d"), name.c_str(), et);
return NULL;
}
}
template<int size, bool big_endian>
class Output_data_got_powerpc : public Output_data_got<size, big_endian>
{
public:
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<size, big_endian>(),
symtab_(symtab), layout_(layout),
header_ent_cnt_(size == 32 ? 3 : 1),
header_index_(size == 32 ? 0x2000 : 0)
{ }
// 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)
{
this->reserve_ent();
return Output_data_got<size, big_endian>::add_global(gsym, got_type);
}
bool
add_global_plt(Symbol* gsym, unsigned int got_type)
{
this->reserve_ent();
return Output_data_got<size, big_endian>::add_global_plt(gsym, got_type);
}
bool
add_global_tls(Symbol* gsym, unsigned int got_type)
{ return this->add_global_plt(gsym, got_type); }
void
add_global_with_rel(Symbol* gsym, unsigned int got_type,
Output_data_reloc_generic* rel_dyn, unsigned int r_type)
{
this->reserve_ent();
Output_data_got<size, big_endian>::
add_global_with_rel(gsym, got_type, rel_dyn, r_type);
}
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)
{
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);
}
bool
add_local(Relobj* object, unsigned int sym_index, unsigned int got_type)
{
this->reserve_ent();
return Output_data_got<size, big_endian>::add_local(object, sym_index,
got_type);
}
bool
add_local_plt(Relobj* object, unsigned int sym_index, unsigned int got_type)
{
this->reserve_ent();
return Output_data_got<size, big_endian>::add_local_plt(object, sym_index,
got_type);
}
bool
add_local_tls(Relobj* object, unsigned int sym_index, unsigned int got_type)
{ return this->add_local_plt(object, sym_index, got_type); }
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)
{
this->reserve_ent(2);
Output_data_got<size, big_endian>::
add_local_tls_pair(object, sym_index, got_type, rel_dyn, r_type);
}
unsigned int
add_constant(Valtype constant)
{
this->reserve_ent();
return Output_data_got<size, big_endian>::add_constant(constant);
}
unsigned int
add_constant_pair(Valtype c1, Valtype c2)
{
this->reserve_ent(2);
return Output_data_got<size, big_endian>::add_constant_pair(c1, c2);
}
// Offset of _GLOBAL_OFFSET_TABLE_.
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();
else
return (this->output_section()->address()
+ object->toc_base_offset()
- this->address());
}
// Ensure our GOT has a header.
void
set_final_data_size()
{
if (this->header_ent_cnt_ != 0)
this->make_header();
Output_data_got<size, big_endian>::set_final_data_size();
}
// First word of GOT header needs some values that are not
// handled by Output_data_got so poke them in here.
// For 32-bit, address of .dynamic, for 64-bit, address of TOCbase.
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);
Output_data_got<size, big_endian>::do_write(of);
}
private:
void
reserve_ent(unsigned int cnt = 1)
{
if (this->header_ent_cnt_ == 0)
return;
if (this->num_entries() + cnt > this->header_index_)
this->make_header();
}
void
make_header()
{
this->header_ent_cnt_ = 0;
this->header_index_ = this->num_entries();
if (size == 32)
{
Output_data_got<size, big_endian>::add_constant(0);
Output_data_got<size, big_endian>::add_constant(0);
Output_data_got<size, big_endian>::add_constant(0);
// Define _GLOBAL_OFFSET_TABLE_ at the header
Symbol *gotsym = this->symtab_->lookup("_GLOBAL_OFFSET_TABLE_", NULL);
if (gotsym != NULL)
{
Sized_symbol<size>* sym = static_cast<Sized_symbol<size>*>(gotsym);
sym->set_value(this->g_o_t());
}
else
this->symtab_->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
Symbol_table::PREDEFINED,
this, this->g_o_t(), 0,
elfcpp::STT_OBJECT,
elfcpp::STB_LOCAL,
elfcpp::STV_HIDDEN, 0,
false, false);
}
else
Output_data_got<size, big_endian>::add_constant(0);
}
// Stashed pointers.
Symbol_table* symtab_;
Layout* layout_;
// GOT header size.
unsigned int header_ent_cnt_;
// GOT header index.
unsigned int header_index_;
};
// Get the GOT section, creating it if necessary.
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)
{
if (this->got_ == NULL)
{
gold_assert(symtab != NULL && layout != NULL);
this->got_
= new Output_data_got_powerpc<size, big_endian>(symtab, layout);
layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
this->got_, ORDER_DATA, false);
}
return this->got_;
}
// Get the dynamic reloc section, creating it if necessary.
template<int size, bool big_endian>
typename Target_powerpc<size, big_endian>::Reloc_section*
Target_powerpc<size, big_endian>::rela_dyn_section(Layout* layout)
{
if (this->rela_dyn_ == NULL)
{
gold_assert(layout != NULL);
this->rela_dyn_ = new Reloc_section(parameters->options().combreloc());
layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
elfcpp::SHF_ALLOC, this->rela_dyn_,
ORDER_DYNAMIC_RELOCS, false);
}
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:
// Determine the stub group size. The group size is the absolute
// value of the parameter --stub-group-size. If --stub-group-size
// is passed a negative value, we restrict stubs to be always before
// the stubbed branches.
Stub_control(int32_t size, bool no_size_errors)
: state_(NO_GROUP), stub_group_size_(abs(size)),
stub14_group_size_(abs(size) >> 10),
stubs_always_before_branch_(size < 0),
suppress_size_errors_(no_size_errors),
group_end_addr_(0), owner_(NULL), output_section_(NULL)
{
}
// Return true iff input section can be handled by current stub
// group.
bool
can_add_to_stub_group(Output_section* o,
const Output_section::Input_section* i,
bool has14);
const Output_section::Input_section*
owner()
{ return owner_; }
Output_section*
output_section()
{ return output_section_; }
void
set_output_and_owner(Output_section* o,
const Output_section::Input_section* i)
{
this->output_section_ = o;
this->owner_ = i;
}
private:
typedef enum
{
NO_GROUP,
FINDING_STUB_SECTION,
HAS_STUB_SECTION
} State;
State state_;
uint32_t stub_group_size_;
uint32_t stub14_group_size_;
bool stubs_always_before_branch_;
bool suppress_size_errors_;
uint64_t group_end_addr_;
const Output_section::Input_section* owner_;
Output_section* output_section_;
};
// Return true iff input section can be handled by current stub
// group.
bool
Stub_control::can_add_to_stub_group(Output_section* o,
const Output_section::Input_section* i,
bool has14)
{
uint32_t group_size
= has14 ? this->stub14_group_size_ : this->stub_group_size_;
bool whole_sec = o->order() == ORDER_INIT || o->order() == ORDER_FINI;
uint64_t this_size;
uint64_t start_addr = o->address();
if (whole_sec)
// .init and .fini sections are pasted together to form a single
// function. We can't be adding stubs in the middle of the function.
this_size = o->data_size();
else
{
start_addr += i->relobj()->output_section_offset(i->shndx());
this_size = i->data_size();
}
uint64_t end_addr = start_addr + this_size;
bool toobig = this_size > group_size;
if (toobig && !this->suppress_size_errors_)
gold_warning(_("%s:%s exceeds group size"),
i->relobj()->name().c_str(),
i->relobj()->section_name(i->shndx()).c_str());
if (this->state_ != HAS_STUB_SECTION
&& (!whole_sec || this->output_section_ != o)
&& (this->state_ == NO_GROUP
|| this->group_end_addr_ - end_addr < group_size))
{
this->owner_ = i;
this->output_section_ = o;
}
if (this->state_ == NO_GROUP)
{
this->state_ = FINDING_STUB_SECTION;
this->group_end_addr_ = end_addr;
}
else if (this->group_end_addr_ - start_addr < group_size)
;
// Adding this section would make the group larger than GROUP_SIZE.
else if (this->state_ == FINDING_STUB_SECTION
&& !this->stubs_always_before_branch_
&& !toobig)
{
// But wait, there's more! Input sections up to GROUP_SIZE
// bytes before the stub table can be handled by it too.
this->state_ = HAS_STUB_SECTION;
this->group_end_addr_ = end_addr;
}
else
{
this->state_ = NO_GROUP;
return false;
}
return true;
}
// Look over all the input sections, deciding where to place stubs.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::group_sections(Layout* layout,
const Task*,
bool no_size_errors)
{
Stub_control stub_control(this->stub_group_size_, no_size_errors);
// Group input sections and insert stub table
Stub_table_owner* table_owner = NULL;
std::vector<Stub_table_owner*> tables;
Layout::Section_list section_list;
layout->get_executable_sections(&section_list);
std::stable_sort(section_list.begin(), section_list.end(), Sort_sections());
for (Layout::Section_list::reverse_iterator o = section_list.rbegin();
o != section_list.rend();
++o)
{
typedef Output_section::Input_section_list Input_section_list;
for (Input_section_list::const_reverse_iterator i
= (*o)->input_sections().rbegin();
i != (*o)->input_sections().rend();
++i)
{
if (i->is_input_section()
|| i->is_relaxed_input_section())
{
Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<Powerpc_relobj<size, big_endian>*>(i->relobj());
bool has14 = ppcobj->has_14bit_branch(i->shndx());
if (!stub_control.can_add_to_stub_group(*o, &*i, has14))
{
table_owner->output_section = stub_control.output_section();
table_owner->owner = stub_control.owner();
stub_control.set_output_and_owner(*o, &*i);
table_owner = NULL;
}
if (table_owner == NULL)
{
table_owner = new Stub_table_owner;
tables.push_back(table_owner);
}
ppcobj->set_stub_table(i->shndx(), tables.size() - 1);
}
}
}
if (table_owner != NULL)
{
const Output_section::Input_section* i = stub_control.owner();
if (tables.size() >= 2 && tables[tables.size() - 2]->owner == i)
{
// Corner case. A new stub group was made for the first
// section (last one looked at here) for some reason, but
// the first section is already being used as the owner for
// a stub table for following sections. Force it into that
// stub group.
tables.pop_back();
delete table_owner;
Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<Powerpc_relobj<size, big_endian>*>(i->relobj());
ppcobj->set_stub_table(i->shndx(), tables.size() - 1);
}
else
{
table_owner->output_section = stub_control.output_section();
table_owner->owner = i;
}
}
for (typename std::vector<Stub_table_owner*>::iterator t = tables.begin();
t != tables.end();
++t)
{
Stub_table<size, big_endian>* stub_table;
if ((*t)->owner->is_input_section())
stub_table = new Stub_table<size, big_endian>(this,
(*t)->output_section,
(*t)->owner);
else if ((*t)->owner->is_relaxed_input_section())
stub_table = static_cast<Stub_table<size, big_endian>*>(
(*t)->owner->relaxed_input_section());
else
gold_unreachable();
this->stub_tables_.push_back(stub_table);
delete *t;
}
}
static unsigned long
max_branch_delta (unsigned int r_type)
{
if (r_type == elfcpp::R_POWERPC_REL14
|| r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
|| r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
return 1L << 15;
if (r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC_PLTREL24
|| r_type == elfcpp::R_PPC_LOCAL24PC)
return 1L << 25;
return 0;
}
// If this branch needs a plt call stub, or a long branch stub, make one.
template<int size, bool big_endian>
bool
Target_powerpc<size, big_endian>::Branch_info::make_stub(
Stub_table<size, big_endian>* stub_table,
Stub_table<size, big_endian>* ifunc_stub_table,
Symbol_table* symtab) const
{
Symbol* sym = this->object_->global_symbol(this->r_sym_);
if (sym != NULL && sym->is_forwarder())
sym = symtab->resolve_forwards(sym);
const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym);
Target_powerpc<size, big_endian>* target =
static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
if (gsym != NULL
? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
: this->object_->local_has_plt_offset(this->r_sym_))
{
if (size == 64
&& gsym != NULL
&& target->abiversion() >= 2
&& !parameters->options().output_is_position_independent()
&& !is_branch_reloc(this->r_type_))
target->glink_section()->add_global_entry(gsym);
else
{
if (stub_table == NULL)
stub_table = this->object_->stub_table(this->shndx_);
if (stub_table == NULL)
{
// This is a ref from a data section to an ifunc symbol.
stub_table = ifunc_stub_table;
}
gold_assert(stub_table != NULL);
Address from = this->object_->get_output_section_offset(this->shndx_);
if (from != invalid_address)
from += (this->object_->output_section(this->shndx_)->address()
+ this->offset_);
if (gsym != NULL)
return stub_table->add_plt_call_entry(from,
this->object_, gsym,
this->r_type_, this->addend_);
else
return stub_table->add_plt_call_entry(from,
this->object_, this->r_sym_,
this->r_type_, this->addend_);
}
}
else
{
Address max_branch_offset = max_branch_delta(this->r_type_);
if (max_branch_offset == 0)
return true;
Address from = this->object_->get_output_section_offset(this->shndx_);
gold_assert(from != invalid_address);
from += (this->object_->output_section(this->shndx_)->address()
+ this->offset_);
Address to;
if (gsym != NULL)
{
switch (gsym->source())
{
case Symbol::FROM_OBJECT:
{
Object* symobj = gsym->object();
if (symobj->is_dynamic()
|| symobj->pluginobj() != NULL)
return true;
bool is_ordinary;
unsigned int shndx = gsym->shndx(&is_ordinary);
if (shndx == elfcpp::SHN_UNDEF)
return true;
}
break;
case Symbol::IS_UNDEFINED:
return true;
default:
break;
}
Symbol_table::Compute_final_value_status status;
to = symtab->compute_final_value<size>(gsym, &status);
if (status != Symbol_table::CFVS_OK)
return true;
if (size == 64)
to += this->object_->ppc64_local_entry_offset(gsym);
}
else
{
const Symbol_value<size>* psymval
= this->object_->local_symbol(this->r_sym_);
Symbol_value<size> symval;
typedef Sized_relobj_file<size, big_endian> ObjType;
typename ObjType::Compute_final_local_value_status status
= this->object_->compute_final_local_value(this->r_sym_, psymval,
&symval, symtab);
if (status != ObjType::CFLV_OK
|| !symval.has_output_value())
return true;
to = symval.value(this->object_, 0);
if (size == 64)
to += this->object_->ppc64_local_entry_offset(this->r_sym_);
}
if (!(size == 32 && this->r_type_ == elfcpp::R_PPC_PLTREL24))
to += this->addend_;
if (stub_table == NULL)
stub_table = this->object_->stub_table(this->shndx_);
if (size == 64 && target->abiversion() < 2)
{
unsigned int dest_shndx;
if (!target->symval_for_branch(symtab, gsym, this->object_,
&to, &dest_shndx))
return true;
}
Address delta = to - from;
if (delta + max_branch_offset >= 2 * max_branch_offset)
{
if (stub_table == NULL)
{
gold_warning(_("%s:%s: branch in non-executable section,"
" no long branch stub for you"),
this->object_->name().c_str(),
this->object_->section_name(this->shndx_).c_str());
return true;
}
return stub_table->add_long_branch_entry(this->object_,
this->r_type_, from, to);
}
}
return true;
}
// Relaxation hook. This is where we do stub generation.
template<int size, bool big_endian>
bool
Target_powerpc<size, big_endian>::do_relax(int pass,
const Input_objects*,
Symbol_table* symtab,
Layout* layout,
const Task* task)
{
unsigned int prev_brlt_size = 0;
if (pass == 1)
{
bool thread_safe
= this->abiversion() < 2 && parameters->options().plt_thread_safe();
if (size == 64
&& this->abiversion() < 2
&& !thread_safe
&& !parameters->options().user_set_plt_thread_safe())
{
static const char* const thread_starter[] =
{
"pthread_create",
/* libstdc++ */
"_ZNSt6thread15_M_start_threadESt10shared_ptrINS_10_Impl_baseEE",
/* librt */
"aio_init", "aio_read", "aio_write", "aio_fsync", "lio_listio",
"mq_notify", "create_timer",
/* libanl */
"getaddrinfo_a",
/* libgomp */
"GOMP_parallel",
"GOMP_parallel_start",
"GOMP_parallel_loop_static",
"GOMP_parallel_loop_static_start",
"GOMP_parallel_loop_dynamic",
"GOMP_parallel_loop_dynamic_start",
"GOMP_parallel_loop_guided",
"GOMP_parallel_loop_guided_start",
"GOMP_parallel_loop_runtime",
"GOMP_parallel_loop_runtime_start",
"GOMP_parallel_sections",
"GOMP_parallel_sections_start",
/* libgo */
"__go_go",
};
if (parameters->options().shared())
thread_safe = true;
else
{
for (unsigned int i = 0;
i < sizeof(thread_starter) / sizeof(thread_starter[0]);
i++)
{
Symbol* sym = symtab->lookup(thread_starter[i], NULL);
thread_safe = (sym != NULL
&& sym->in_reg()
&& sym->in_real_elf());
if (thread_safe)
break;
}
}
}
this->plt_thread_safe_ = thread_safe;
}
if (pass == 1)
{
this->stub_group_size_ = parameters->options().stub_group_size();
bool no_size_errors = true;
if (this->stub_group_size_ == 1)
this->stub_group_size_ = 0x1c00000;
else if (this->stub_group_size_ == -1)
this->stub_group_size_ = -0x1e00000;
else
no_size_errors = false;
this->group_sections(layout, task, no_size_errors);
}
else if (this->relax_failed_ && this->relax_fail_count_ < 3)
{
this->branch_lookup_table_.clear();
for (typename Stub_tables::iterator p = this->stub_tables_.begin();
p != this->stub_tables_.end();
++p)
{
(*p)->clear_stubs(true);
}
this->stub_tables_.clear();
this->stub_group_size_ = this->stub_group_size_ / 4 * 3;
gold_info(_("%s: stub group size is too large; retrying with %d"),
program_name, this->stub_group_size_);
this->group_sections(layout, task, true);
}
// We need address of stub tables valid for make_stub.
for (typename Stub_tables::iterator p = this->stub_tables_.begin();
p != this->stub_tables_.end();
++p)
{
const Powerpc_relobj<size, big_endian>* object
= static_cast<const Powerpc_relobj<size, big_endian>*>((*p)->relobj());
Address off = object->get_output_section_offset((*p)->shndx());
gold_assert(off != invalid_address);
Output_section* os = (*p)->output_section();
(*p)->set_address_and_size(os, off);
}
if (pass != 1)
{
// Clear plt call stubs, long branch stubs and branch lookup table.
prev_brlt_size = this->branch_lookup_table_.size();
this->branch_lookup_table_.clear();
for (typename Stub_tables::iterator p = this->stub_tables_.begin();
p != this->stub_tables_.end();
++p)
{
(*p)->clear_stubs(false);
}
}
// Build all the stubs.
this->relax_failed_ = false;
Stub_table<size, big_endian>* ifunc_stub_table
= this->stub_tables_.size() == 0 ? NULL : this->stub_tables_[0];
Stub_table<size, big_endian>* one_stub_table
= this->stub_tables_.size() != 1 ? NULL : ifunc_stub_table;
for (typename Branches::const_iterator b = this->branch_info_.begin();
b != this->branch_info_.end();
b++)
{
if (!b->make_stub(one_stub_table, ifunc_stub_table, symtab)
&& !this->relax_failed_)
{
this->relax_failed_ = true;
this->relax_fail_count_++;
if (this->relax_fail_count_ < 3)
return true;
}
}
// Did anything change size?
unsigned int num_huge_branches = this->branch_lookup_table_.size();
bool again = num_huge_branches != prev_brlt_size;
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);
typedef Unordered_set<Output_section*> Output_sections;
Output_sections os_need_update;
for (typename Stub_tables::iterator p = this->stub_tables_.begin();
p != this->stub_tables_.end();
++p)
{
if ((*p)->size_update())
{
again = true;
(*p)->add_eh_frame(layout);
os_need_update.insert((*p)->output_section());
}
}
// Set output section offsets for all input sections in an output
// section that just changed size. Anything past the stubs will
// need updating.
for (typename Output_sections::iterator p = os_need_update.begin();
p != os_need_update.end();
p++)
{
Output_section* os = *p;
Address off = 0;
typedef Output_section::Input_section_list Input_section_list;
for (Input_section_list::const_iterator i = os->input_sections().begin();
i != os->input_sections().end();
++i)
{
off = align_address(off, i->addralign());
if (i->is_input_section() || i->is_relaxed_input_section())
i->relobj()->set_section_offset(i->shndx(), off);
if (i->is_relaxed_input_section())
{
Stub_table<size, big_endian>* stub_table
= static_cast<Stub_table<size, big_endian>*>(
i->relaxed_input_section());
off += stub_table->set_address_and_size(os, off);
}
else
off += i->data_size();
}
// If .branch_lt is part of this output section, then we have
// just done the offset adjustment.
os->clear_section_offsets_need_adjustment();
}
if (size == 64
&& !again
&& num_huge_branches != 0
&& parameters->options().output_is_position_independent())
{
// Fill in the BRLT relocs.
this->brlt_section_->reset_brlt_sizes();
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->brlt_section_->finalize_brlt_sizes();
}
return again;
}
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::do_plt_fde_location(const Output_data* plt,
unsigned char* oview,
uint64_t* paddress,
off_t* plen) const
{
uint64_t address = plt->address();
off_t len = plt->data_size();
if (plt == this->glink_)
{
// 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
// attach stub .eh_frame info. We also need to have
// registered a CIE that matches the stub CIE. Both of
// these requirements are satisfied by creating an FDE and
// CIE for .glink, even though static linking will leave
// .glink zero length.
// ??? Hopefully generating an FDE with a zero address range
// won't confuse anything that consumes .eh_frame info.
}
else if (size == 64)
{
// There is one word before __glink_PLTresolve
address += 8;
len -= 8;
}
else if (parameters->options().output_is_position_independent())
{
// 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;
if (oview[9] == elfcpp::DW_CFA_nop)
len -= resolve_size;
else
{
address += len - resolve_size;
len = resolve_size;
}
}
}
else
{
// Must be a stub table.
const Stub_table<size, big_endian>* stub_table
= static_cast<const Stub_table<size, big_endian>*>(plt);
uint64_t stub_address = stub_table->stub_address();
len -= stub_address - address;
address = stub_address;
}
*paddress = address;
*plen = len;
}
// A class to handle the PLT data.
template<int size, bool big_endian>
class Output_data_plt_powerpc : public Output_section_data_build
{
public:
typedef Output_data_reloc<elfcpp::SHT_RELA, true,
size, big_endian> Reloc_section;
Output_data_plt_powerpc(Target_powerpc<size, big_endian>* targ,
Reloc_section* plt_rel,
const char* name)
: Output_section_data_build(size == 32 ? 4 : 8),
rel_(plt_rel),
targ_(targ),
name_(name)
{ }
// Add an entry to the PLT.
void
add_entry(Symbol*);
void
add_ifunc_entry(Symbol*);
void
add_local_ifunc_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
// Return the .rela.plt section data.
Reloc_section*
rel_plt() const
{
return this->rel_;
}
// Return the number of PLT entries.
unsigned int
entry_count() const
{
if (this->current_data_size() == 0)
return 0;
return ((this->current_data_size() - this->first_plt_entry_offset())
/ this->plt_entry_size());
}
protected:
void
do_adjust_output_section(Output_section* os)
{
os->set_entsize(0);
}
// Write to a map file.
void
do_print_to_mapfile(Mapfile* mapfile) const
{ mapfile->print_output_data(this, this->name_); }
private:
// Return the offset of the first non-reserved PLT entry.
unsigned int
first_plt_entry_offset() const
{
// IPLT has no reserved entry.
if (this->name_[3] == 'I')
return 0;
return this->targ_->first_plt_entry_offset();
}
// Return the size of each PLT entry.
unsigned int
plt_entry_size() const
{
return this->targ_->plt_entry_size();
}
// Write out the PLT data.
void
do_write(Output_file*);
// The reloc section.
Reloc_section* rel_;
// Allows access to .glink for do_write.
Target_powerpc<size, big_endian>* targ_;
// What to report in map file.
const char *name_;
};
// Add an entry to the PLT.
template<int size, bool big_endian>
void
Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym)
{
if (!gsym->has_plt_offset())
{
section_size_type off = this->current_data_size();
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);
off += this->plt_entry_size();
this->set_current_data_size(off);
}
}
// Add an entry for a global ifunc symbol that resolves locally, to the IPLT.
template<int size, bool big_endian>
void
Output_data_plt_powerpc<size, big_endian>::add_ifunc_entry(Symbol* gsym)
{
if (!gsym->has_plt_offset())
{
section_size_type off = this->current_data_size();
gsym->set_plt_offset(off);
unsigned int dynrel = elfcpp::R_POWERPC_IRELATIVE;
if (size == 64 && this->targ_->abiversion() < 2)
dynrel = elfcpp::R_PPC64_JMP_IREL;
this->rel_->add_symbolless_global_addend(gsym, 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>::add_local_ifunc_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);
unsigned int dynrel = elfcpp::R_POWERPC_IRELATIVE;
if (size == 64 && this->targ_->abiversion() < 2)
dynrel = elfcpp::R_PPC64_JMP_IREL;
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);
}
}
static const uint32_t add_0_11_11 = 0x7c0b5a14;
static const uint32_t add_2_2_11 = 0x7c425a14;
static const uint32_t add_3_3_2 = 0x7c631214;
static const uint32_t add_3_3_13 = 0x7c636a14;
static const uint32_t add_11_0_11 = 0x7d605a14;
static const uint32_t add_11_2_11 = 0x7d625a14;
static const uint32_t add_11_11_2 = 0x7d6b1214;
static const uint32_t addi_0_12 = 0x380c0000;
static const uint32_t addi_2_2 = 0x38420000;
static const uint32_t addi_3_3 = 0x38630000;
static const uint32_t addi_11_11 = 0x396b0000;
static const uint32_t addi_12_12 = 0x398c0000;
static const uint32_t addis_0_2 = 0x3c020000;
static const uint32_t addis_0_13 = 0x3c0d0000;
static const uint32_t addis_11_2 = 0x3d620000;
static const uint32_t addis_11_11 = 0x3d6b0000;
static const uint32_t addis_11_30 = 0x3d7e0000;
static const uint32_t addis_12_2 = 0x3d820000;
static const uint32_t addis_12_12 = 0x3d8c0000;
static const uint32_t b = 0x48000000;
static const uint32_t bcl_20_31 = 0x429f0005;
static const uint32_t bctr = 0x4e800420;
static const uint32_t blr = 0x4e800020;
static const uint32_t bnectr_p4 = 0x4ce20420;
static const uint32_t cmpldi_2_0 = 0x28220000;
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_12 = 0xe80c0000;
static const uint32_t ld_2_1 = 0xe8410000;
static const uint32_t ld_2_2 = 0xe8420000;
static const uint32_t ld_2_11 = 0xe84b0000;
static const uint32_t ld_11_2 = 0xe9620000;
static const uint32_t ld_11_11 = 0xe96b0000;
static const uint32_t ld_12_2 = 0xe9820000;
static const uint32_t ld_12_11 = 0xe98b0000;
static const uint32_t ld_12_12 = 0xe98c0000;
static const uint32_t lfd_0_1 = 0xc8010000;
static const uint32_t li_0_0 = 0x38000000;
static const uint32_t li_12_0 = 0x39800000;
static const uint32_t lis_0_0 = 0x3c000000;
static const uint32_t lis_11 = 0x3d600000;
static const uint32_t lis_12 = 0x3d800000;
static const uint32_t lvx_0_12_0 = 0x7c0c00ce;
static const uint32_t lwz_0_12 = 0x800c0000;
static const uint32_t lwz_11_11 = 0x816b0000;
static const uint32_t lwz_11_30 = 0x817e0000;
static const uint32_t lwz_12_12 = 0x818c0000;
static const uint32_t lwzu_0_12 = 0x840c0000;
static const uint32_t mflr_0 = 0x7c0802a6;
static const uint32_t mflr_11 = 0x7d6802a6;
static const uint32_t mflr_12 = 0x7d8802a6;
static const uint32_t mtctr_0 = 0x7c0903a6;
static const uint32_t mtctr_11 = 0x7d6903a6;
static const uint32_t mtctr_12 = 0x7d8903a6;
static const uint32_t mtlr_0 = 0x7c0803a6;
static const uint32_t mtlr_12 = 0x7d8803a6;
static const uint32_t nop = 0x60000000;
static const uint32_t ori_0_0_0 = 0x60000000;
static const uint32_t srdi_0_0_2 = 0x7800f082;
static const uint32_t std_0_1 = 0xf8010000;
static const uint32_t std_0_12 = 0xf80c0000;
static const uint32_t std_2_1 = 0xf8410000;
static const uint32_t stfd_0_1 = 0xd8010000;
static const uint32_t stvx_0_12_0 = 0x7c0c01ce;
static const uint32_t sub_11_11_12 = 0x7d6c5850;
static const uint32_t sub_12_12_11 = 0x7d8b6050;
static const uint32_t xor_2_12_12 = 0x7d826278;
static const uint32_t xor_11_12_12 = 0x7d8b6278;
// Write out the PLT.
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')
{
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;
// The address of the .glink branch table
const Output_data_glink<size, big_endian>* glink
= this->targ_->glink_section();
elfcpp::Elf_types<32>::Elf_Addr branch_tab = glink->address();
while (pov < endpov)
{
elfcpp::Swap<32, big_endian>::writeval(pov, branch_tab);
pov += 4;
branch_tab += 4;
}
of->write_output_view(offset, oview_size, oview);
}
}
// Create the PLT section.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::make_plt_section(Symbol_table* symtab,
Layout* layout)
{
if (this->plt_ == NULL)
{
if (this->got_ == NULL)
this->got_section(symtab, layout);
if (this->glink_ == NULL)
make_glink_section(layout);
// 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 its range.
this->rela_dyn_section(layout);
Reloc_section* plt_rel = new Reloc_section(false);
layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
elfcpp::SHF_ALLOC, plt_rel,
ORDER_DYNAMIC_PLT_RELOCS, false);
this->plt_
= new Output_data_plt_powerpc<size, big_endian>(this, plt_rel,
"** PLT");
layout->add_output_section_data(".plt",
(size == 32
? elfcpp::SHT_PROGBITS
: elfcpp::SHT_NOBITS),
elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
this->plt_,
(size == 32
? ORDER_SMALL_DATA
: ORDER_SMALL_BSS),
false);
}
}
// Create the IPLT section.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::make_iplt_section(Symbol_table* symtab,
Layout* layout)
{
if (this->iplt_ == NULL)
{
this->make_plt_section(symtab, layout);
Reloc_section* iplt_rel = new Reloc_section(false);
this->rela_dyn_->output_section()->add_output_section_data(iplt_rel);
this->iplt_
= new Output_data_plt_powerpc<size, big_endian>(this, iplt_rel,
"** IPLT");
this->plt_->output_section()->add_output_section_data(this->iplt_);
}
}
// A section for huge long branch addresses, similar to plt section.
template<int size, bool big_endian>
class Output_data_brlt_powerpc : public Output_section_data_build
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
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_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)
{
unsigned int reloc_size
= Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_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)
{
os->set_entsize(0);
}
// Write to a map file.
void
do_print_to_mapfile(Mapfile* mapfile) const
{ mapfile->print_output_data(this, "** BRLT"); }
private:
// Write out the BRLT data.
void
do_write(Output_file*);
// The reloc section.
Reloc_section* rel_;
Target_powerpc<size, big_endian>* targ_;
};
// Make the branch lookup table section.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::make_brlt_section(Layout* layout)
{
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 (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->rela_dyn_->output_section()->add_output_section_data(brlt_rel);
}
this->brlt_section_
= new Output_data_brlt_powerpc<size, big_endian>(this, brlt_rel);
if (this->plt_ && is_pic)
this->plt_->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::SHF_ALLOC | elfcpp::SHF_WRITE,
this->brlt_section_,
(is_pic ? ORDER_SMALL_BSS
: ORDER_SMALL_DATA),
false);
}
}
// Write out .branch_lt when non-PIC.
template<int size, bool big_endian>
void
Output_data_brlt_powerpc<size, big_endian>::do_write(Output_file* of)
{
if (size == 64 && !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);
this->targ_->write_branch_lookup_table(oview);
of->write_output_view(offset, oview_size, oview);
}
}
static inline uint32_t
l(uint32_t a)
{
return a & 0xffff;
}
static inline uint32_t
hi(uint32_t a)
{
return l(a >> 16);
}
static inline uint32_t
ha(uint32_t a)
{
return hi(a + 0x8000);
}
template<int size>
struct Eh_cie
{
static const unsigned char eh_frame_cie[12];
};
template<int size>
const unsigned char Eh_cie<size>::eh_frame_cie[] =
{
1, // CIE version.
'z', 'R', 0, // Augmentation string.
4, // Code alignment.
0x80 - size / 8 , // Data alignment.
65, // RA reg.
1, // Augmentation size.
(elfcpp::DW_EH_PE_pcrel
| elfcpp::DW_EH_PE_sdata4), // FDE encoding.
elfcpp::DW_CFA_def_cfa, 1, 0 // def_cfa: r1 offset 0.
};
// Describe __glink_PLTresolve use of LR, 64-bit version ABIv1.
static const unsigned char glink_eh_frame_fde_64v1[] =
{
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_register, 65, 12,
elfcpp::DW_CFA_advance_loc + 4,
elfcpp::DW_CFA_restore_extended, 65
};
// Describe __glink_PLTresolve use of LR, 64-bit version ABIv2.
static const unsigned char glink_eh_frame_fde_64v2[] =
{
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_register, 65, 0,
elfcpp::DW_CFA_advance_loc + 4,
elfcpp::DW_CFA_restore_extended, 65
};
// Describe __glink_PLTresolve use of LR, 32-bit version.
static const unsigned char glink_eh_frame_fde_32[] =
{
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 + 2,
elfcpp::DW_CFA_register, 65, 0,
elfcpp::DW_CFA_advance_loc + 4,
elfcpp::DW_CFA_restore_extended, 65
};
static const unsigned char default_fde[] =
{
0, 0, 0, 0, // Replaced with offset to stubs.
0, 0, 0, 0, // Replaced with size of stubs.
0, // Augmentation size.
elfcpp::DW_CFA_nop, // Pad.
elfcpp::DW_CFA_nop,
elfcpp::DW_CFA_nop
};
template<bool big_endian>
static inline void
write_insn(unsigned char* p, uint32_t v)
{
elfcpp::Swap<32, big_endian>::writeval(p, v);
}
// 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
// input section allowing it to be resized to accommodate the stubs
template<int size, bool big_endian>
class Stub_table : public Output_relaxed_input_section
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
static const Address invalid_address = static_cast<Address>(0) - 1;
Stub_table(Target_powerpc<size, big_endian>* targ,
Output_section* output_section,
const Output_section::Input_section* owner)
: Output_relaxed_input_section(owner->relobj(), owner->shndx(),
owner->relobj()
->section_addralign(owner->shndx())),
targ_(targ), plt_call_stubs_(), long_branch_stubs_(),
orig_data_size_(owner->current_data_size()),
plt_size_(0), last_plt_size_(0),
branch_size_(0), last_branch_size_(0), eh_frame_added_(false)
{
this->set_output_section(output_section);
std::vector<Output_relaxed_input_section*> new_relaxed;
new_relaxed.push_back(this);
output_section->convert_input_sections_to_relaxed_sections(new_relaxed);
}
// Add a plt call stub.
bool
add_plt_call_entry(Address,
const Sized_relobj_file<size, big_endian>*,
const Symbol*,
unsigned int,
Address);
bool
add_plt_call_entry(Address,
const Sized_relobj_file<size, big_endian>*,
unsigned int,
unsigned int,
Address);
// Find a given plt call stub.
Address
find_plt_call_entry(const Symbol*) const;
Address
find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
unsigned int) const;
Address
find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
const Symbol*,
unsigned int,
Address) const;
Address
find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
unsigned int,
unsigned int,
Address) const;
// Add a long branch stub.
bool
add_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
unsigned int, Address, Address);
Address
find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
Address) const;
bool
can_reach_stub(Address from, unsigned int off, unsigned int r_type)
{
Address max_branch_offset = max_branch_delta(r_type);
if (max_branch_offset == 0)
return true;
gold_assert(from != invalid_address);
Address loc = off + this->stub_address();
return loc - from + max_branch_offset < 2 * max_branch_offset;
}
void
clear_stubs(bool all)
{
this->plt_call_stubs_.clear();
this->plt_size_ = 0;
this->long_branch_stubs_.clear();
this->branch_size_ = 0;
if (all)
{
this->last_plt_size_ = 0;
this->last_branch_size_ = 0;
}
}
Address
set_address_and_size(const Output_section* os, Address off)
{
Address start_off = off;
off += this->orig_data_size_;
Address my_size = this->plt_size_ + this->branch_size_;
if (my_size != 0)
off = align_address(off, this->stub_align());
// Include original section size and alignment padding in size
my_size += off - start_off;
this->reset_address_and_file_offset();
this->set_current_data_size(my_size);
this->set_address_and_file_offset(os->address() + start_off,
os->offset() + start_off);
return my_size;
}
Address
stub_address() const
{
return align_address(this->address() + this->orig_data_size_,
this->stub_align());
}
Address
stub_offset() const
{
return align_address(this->offset() + this->orig_data_size_,
this->stub_align());
}
section_size_type
plt_size() const
{ return this->plt_size_; }
bool
size_update()
{
Output_section* os = this->output_section();
if (os->addralign() < this->stub_align())
{
os->set_addralign(this->stub_align());
// FIXME: get rid of the insane checkpointing.
// We can't increase alignment of the input section to which
// stubs are attached; The input section may be .init which
// is pasted together with other .init sections to form a
// function. Aligning might insert zero padding resulting in
// sigill. However we do need to increase alignment of the
// output section so that the align_address() on offset in
// set_address_and_size() adds the same padding as the
// align_address() on address in stub_address().
// What's more, we need this alignment for the layout done in
// relaxation_loop_body() so that the output section starts at
// a suitably aligned address.
os->checkpoint_set_addralign(this->stub_align());
}
if (this->last_plt_size_ != this->plt_size_
|| this->last_branch_size_ != this->branch_size_)
{
this->last_plt_size_ = this->plt_size_;
this->last_branch_size_ = this->branch_size_;
return true;
}
return false;
}
// Add .eh_frame info for this stub section. Unlike other linker
// generated .eh_frame this is added late in the link, because we
// only want the .eh_frame info if this particular stub section is
// non-empty.
void
add_eh_frame(Layout* layout)
{
if (!this->eh_frame_added_)
{
if (!parameters->options().ld_generated_unwind_info())
return;
// Since we add stub .eh_frame info late, it must be placed
// after all other linker generated .eh_frame info so that
// merge mapping need not be updated for input sections.
// There is no provision to use a different CIE to that used
// by .glink.
if (!this->targ_->has_glink())
return;
layout->add_eh_frame_for_plt(this,
Eh_cie<size>::eh_frame_cie,
sizeof (Eh_cie<size>::eh_frame_cie),
default_fde,
sizeof (default_fde));
this->eh_frame_added_ = true;
}
}
Target_powerpc<size, big_endian>*
targ() const
{ return targ_; }
private:
class Plt_stub_ent;
class Plt_stub_ent_hash;
typedef Unordered_map<Plt_stub_ent, unsigned int,
Plt_stub_ent_hash> Plt_stub_entries;
// Alignment of stub section.
unsigned int
stub_align() const
{
if (size == 32)
return 16;
unsigned int min_align = 32;
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
{
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();
}
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);
}
}
// Size of a given plt call stub.
unsigned int
plt_call_size(typename Plt_stub_entries::const_iterator p) const
{
if (size == 32)
return 16;
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();
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_);
got_addr += ppcobj->toc_base_offset();
Address off = plt_addr - got_addr;
unsigned int bytes = 4 * 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)));
}
unsigned int align = 1 << parameters->options().plt_align();
if (align > 1)
bytes = (bytes + align - 1) & -align;
return bytes;
}
// Return long branch stub size.
unsigned int
branch_stub_size(Address to)
{
Address loc
= this->stub_address() + this->last_plt_size_ + this->branch_size_;
if (to - loc + (1 << 25) < 2 << 25)
return 4;
if (size == 64 || !parameters->options().output_is_position_independent())
return 16;
return 32;
}
// Write out stubs.
void
do_write(Output_file*);
// Plt call stub keys.
class Plt_stub_ent
{
public:
Plt_stub_ent(const Symbol* sym)
: sym_(sym), object_(0), addend_(0), locsym_(0)
{ }
Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index)
: sym_(NULL), object_(object), addend_(0), locsym_(locsym_index)
{ }
Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
const Symbol* sym,
unsigned int r_type,
Address addend)
: sym_(sym), object_(0), addend_(0), locsym_(0)
{
if (size != 32)
this->addend_ = addend;
else if (parameters->options().output_is_position_independent()
&& r_type == elfcpp::R_PPC_PLTREL24)
{
this->addend_ = addend;
if (this->addend_ >= 32768)
this->object_ = object;
}
}
Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index,
unsigned int r_type,
Address addend)
: sym_(NULL), object_(object), addend_(0), locsym_(locsym_index)
{
if (size != 32)
this->addend_ = addend;
else if (parameters->options().output_is_position_independent()
&& r_type == elfcpp::R_PPC_PLTREL24)
this->addend_ = addend;
}
bool operator==(const Plt_stub_ent& that) const
{
return (this->sym_ == that.sym_
&& this->object_ == that.object_
&& this->addend_ == that.addend_
&& this->locsym_ == that.locsym_);
}
const Symbol* sym_;
const Sized_relobj_file<size, big_endian>* object_;
typename elfcpp::Elf_types<size>::Elf_Addr addend_;
unsigned int locsym_;
};
class Plt_stub_ent_hash
{
public:
size_t operator()(const Plt_stub_ent& ent) const
{
return (reinterpret_cast<uintptr_t>(ent.sym_)
^ reinterpret_cast<uintptr_t>(ent.object_)
^ ent.addend_
^ ent.locsym_);
}
};
// Long branch stub keys.
class Branch_stub_ent
{
public:
Branch_stub_ent(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();
}
bool operator==(const Branch_stub_ent& that) const
{
return (this->dest_ == that.dest_
&& (size == 32
|| this->toc_base_off_ == that.toc_base_off_));
}
Address dest_;
unsigned int toc_base_off_;
};
class Branch_stub_ent_hash
{
public:
size_t operator()(const Branch_stub_ent& ent) const
{ return ent.dest_ ^ ent.toc_base_off_; }
};
// In a sane world this would be a global.
Target_powerpc<size, big_endian>* targ_;
// Map sym/object/addend to stub offset.
Plt_stub_entries plt_call_stubs_;
// Map destination address to stub offset.
typedef Unordered_map<Branch_stub_ent, unsigned int,
Branch_stub_ent_hash> Branch_stub_entries;
Branch_stub_entries long_branch_stubs_;
// size of input section
section_size_type orig_data_size_;
// size of stubs
section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_;
// Whether .eh_frame info has been created for this stub section.
bool eh_frame_added_;
};
// Add a plt call stub, if we do not already have one for this
// sym/object/addend combo.
template<int size, bool big_endian>
bool
Stub_table<size, big_endian>::add_plt_call_entry(
Address from,
const Sized_relobj_file<size, big_endian>* object,
const Symbol* gsym,
unsigned int r_type,
Address addend)
{
Plt_stub_ent ent(object, gsym, r_type, addend);
unsigned int off = this->plt_size_;
std::pair<typename Plt_stub_entries::iterator, bool> p
= this->plt_call_stubs_.insert(std::make_pair(ent, off));
if (p.second)
this->plt_size_ = off + this->plt_call_size(p.first);
return this->can_reach_stub(from, off, r_type);
}
template<int size, bool big_endian>
bool
Stub_table<size, big_endian>::add_plt_call_entry(
Address from,
const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index,
unsigned int r_type,
Address addend)
{
Plt_stub_ent ent(object, locsym_index, r_type, addend);
unsigned int off = this->plt_size_;
std::pair<typename Plt_stub_entries::iterator, bool> p
= this->plt_call_stubs_.insert(std::make_pair(ent, off));
if (p.second)
this->plt_size_ = off + this->plt_call_size(p.first);
return this->can_reach_stub(from, off, r_type);
}
// Find a plt call stub.
template<int size, bool big_endian>
typename Stub_table<size, big_endian>::Address
Stub_table<size, big_endian>::find_plt_call_entry(
const Sized_relobj_file<size, big_endian>* object,
const Symbol* gsym,
unsigned int r_type,
Address addend) const
{
Plt_stub_ent ent(object, gsym, r_type, addend);
typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
}
template<int size, bool big_endian>
typename Stub_table<size, big_endian>::Address
Stub_table<size, big_endian>::find_plt_call_entry(const Symbol* gsym) const
{
Plt_stub_ent ent(gsym);
typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
}
template<int size, bool big_endian>
typename Stub_table<size, big_endian>::Address
Stub_table<size, big_endian>::find_plt_call_entry(
const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index,
unsigned int r_type,
Address addend) const
{
Plt_stub_ent ent(object, locsym_index, r_type, addend);
typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
}
template<int size, bool big_endian>
typename Stub_table<size, big_endian>::Address
Stub_table<size, big_endian>::find_plt_call_entry(
const Sized_relobj_file<size, big_endian>* object,
unsigned int locsym_index) const
{
Plt_stub_ent ent(object, locsym_index);
typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
}
// Add a long branch stub if we don't already have one to given
// destination.
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)
{
Branch_stub_ent ent(object, to);
Address off = this->branch_size_;
if (this->long_branch_stubs_.insert(std::make_pair(ent, off)).second)
{
unsigned int stub_size = this->branch_stub_size(to);
this->branch_size_ = off + stub_size;
if (size == 64 && stub_size != 4)
this->targ_->add_branch_lookup_table(to);
}
return this->can_reach_stub(from, off, r_type);
}
// Find long branch stub.
template<int size, bool big_endian>
typename Stub_table<size, big_endian>::Address
Stub_table<size, big_endian>::find_long_branch_entry(
const Powerpc_relobj<size, big_endian>* object,
Address to) const
{
Branch_stub_ent ent(object, to);
typename Branch_stub_entries::const_iterator p
= this->long_branch_stubs_.find(ent);
return p == this->long_branch_stubs_.end() ? invalid_address : p->second;
}
// A class to handle .glink.
template<int size, bool big_endian>
class Output_data_glink : public Output_section_data
{
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_(),
end_branch_table_(), ge_size_(0)
{ }
void
add_eh_frame(Layout* layout);
void
add_global_entry(const Symbol*);
Address
find_global_entry(const Symbol*) const;
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;
}
protected:
// Write to a map file.
void
do_print_to_mapfile(Mapfile* mapfile) const
{ mapfile->print_output_data(this, _("** glink")); }
private:
void
set_final_data_size();
// Write out .glink
void
do_write(Output_file*);
// Allows access to .got and .plt for do_write.
Target_powerpc<size, big_endian>* targ_;
// Map sym to stub offset.
typedef Unordered_map<const Symbol*, unsigned int> Global_entry_stub_entries;
Global_entry_stub_entries global_entry_stubs_;
unsigned int end_branch_table_, ge_size_;
};
template<int size, bool big_endian>
void
Output_data_glink<size, big_endian>::add_eh_frame(Layout* layout)
{
if (!parameters->options().ld_generated_unwind_info())
return;
if (size == 64)
{
if (this->targ_->abiversion() < 2)
layout->add_eh_frame_for_plt(this,
Eh_cie<64>::eh_frame_cie,
sizeof (Eh_cie<64>::eh_frame_cie),
glink_eh_frame_fde_64v1,
sizeof (glink_eh_frame_fde_64v1));
else
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,
sizeof (glink_eh_frame_fde_64v2));
}
else
{
// 32-bit .glink can use the default since the CIE return
// address reg, LR, is valid.
layout->add_eh_frame_for_plt(this,
Eh_cie<32>::eh_frame_cie,
sizeof (Eh_cie<32>::eh_frame_cie),
default_fde,
sizeof (default_fde));
// Except where LR is used in a PIC __glink_PLTresolve.
if (parameters->options().output_is_position_independent())
layout->add_eh_frame_for_plt(this,
Eh_cie<32>::eh_frame_cie,
sizeof (Eh_cie<32>::eh_frame_cie),
glink_eh_frame_fde_32,
sizeof (glink_eh_frame_fde_32));
}
}
template<int size, bool big_endian>
void
Output_data_glink<size, big_endian>::add_global_entry(const Symbol* gsym)
{
std::pair<typename Global_entry_stub_entries::iterator, bool> p
= this->global_entry_stubs_.insert(std::make_pair(gsym, this->ge_size_));
if (p.second)
this->ge_size_ += 16;
}
template<int size, bool big_endian>
typename Output_data_glink<size, big_endian>::Address
Output_data_glink<size, big_endian>::find_global_entry(const Symbol* gsym) const
{
typename Global_entry_stub_entries::const_iterator p
= this->global_entry_stubs_.find(gsym);
return p == this->global_entry_stubs_.end() ? invalid_address : p->second;
}
template<int size, bool big_endian>
void
Output_data_glink<size, big_endian>::set_final_data_size()
{
unsigned int count = this->targ_->plt_entry_count();
section_size_type total = 0;
if (count != 0)
{
if (size == 32)
{
// space for branch table
total += 4 * (count - 1);
total += -total & 15;
total += this->pltresolve_size;
}
else
{
total += this->pltresolve_size;
// space for branch table
total += 4 * count;
if (this->targ_->abiversion() < 2)
{
total += 4 * count;
if (count > 0x8000)
total += 4 * (count - 0x8000);
}
}
}
this->end_branch_table_ = total;
total = (total + 15) & -16;
total += this->ge_size_;
this->set_data_size(total);
}
// Write out plt and long branch stub code.
template<int size, bool big_endian>
void
Stub_table<size, big_endian>::do_write(Output_file* of)
{
if (this->plt_call_stubs_.empty()
&& this->long_branch_stubs_.empty())
return;
const section_size_type start_off = this->offset();
const section_size_type off = this->stub_offset();
const section_size_type oview_size =
convert_to_section_size_type(this->data_size() - (off - start_off));
unsigned char* const oview = of->get_output_view(off, oview_size);
unsigned char* p;
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())
{
// 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 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 off = plt_addr - got_addr;
if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
gold_error(_("%s: linkage table error against `%s'"),
cs->first.object_->name().c_str(),
cs->first.sym_->demangled_name().c_str());
bool plt_load_toc = this->targ_->abiversion() < 2;
bool static_chain
= plt_load_toc && parameters->options().plt_static_chain();
bool thread_safe
= plt_load_toc && this->targ_->plt_thread_safe();
bool use_fake_dep = false;
Address cmp_branch_off = 0;
if (thread_safe)
{
unsigned int pltindex
= ((pltoff - this->targ_->first_plt_entry_offset())
/ this->targ_->plt_entry_size());
Address glinkoff
= (this->targ_->glink_section()->pltresolve_size
+ pltindex * 8);
if (pltindex > 32768)
glinkoff += (pltindex - 32768) * 4;
Address to
= this->targ_->glink_section()->address() + glinkoff;
Address from
= (this->stub_address() + cs->second + 24
+ 4 * (ha(off) != 0)
+ 4 * (ha(off + 8 + 8 * static_chain) != ha(off))
+ 4 * static_chain);
cmp_branch_off = to - from;
use_fake_dep = cmp_branch_off + (1 << 25) >= (1 << 26);
}
p = oview + cs->second;
if (ha(off) != 0)
{
write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
p += 4;
if (plt_load_toc)
{
write_insn<big_endian>(p, addis_11_2 + ha(off));
p += 4;
write_insn<big_endian>(p, ld_12_11 + l(off));
p += 4;
}
else
{
write_insn<big_endian>(p, addis_12_2 + ha(off));
p += 4;
write_insn<big_endian>(p, ld_12_12 + l(off));
p += 4;
}
if (plt_load_toc
&& ha(off + 8 + 8 * static_chain) != ha(off))
{
write_insn<big_endian>(p, addi_11_11 + l(off));
p += 4;
off = 0;
}
write_insn<big_endian>(p, mtctr_12);
p += 4;
if (plt_load_toc)
{
if (use_fake_dep)
{
write_insn<big_endian>(p, xor_2_12_12);
p += 4;
write_insn<big_endian>(p, add_11_11_2);
p += 4;
}
write_insn<big_endian>(p, ld_2_11 + l(off + 8));
p += 4;
if (static_chain)
{
write_insn<big_endian>(p, ld_11_11 + l(off + 16));
p += 4;
}
}
}
else
{
write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
p += 4;
write_insn<big_endian>(p, ld_12_2 + l(off));
p += 4;
if (plt_load_toc
&& ha(off + 8 + 8 * static_chain) != ha(off))
{
write_insn<big_endian>(p, addi_2_2 + l(off));
p += 4;
off = 0;
}
write_insn<big_endian>(p, mtctr_12);
p += 4;
if (plt_load_toc)
{
if (use_fake_dep)
{
write_insn<big_endian>(p, xor_11_12_12);
p += 4;
write_insn<big_endian>(p, add_2_2_11);
p += 4;
}
if (static_chain)
{
write_insn<big_endian>(p, ld_11_2 + l(off + 16));
p += 4;
}
write_insn<big_endian>(p, ld_2_2 + l(off + 8));
p += 4;
}
}
if (thread_safe && !use_fake_dep)
{
write_insn<big_endian>(p, cmpldi_2_0);
p += 4;
write_insn<big_endian>(p, bnectr_p4);
p += 4;
write_insn<big_endian>(p, b | (cmp_branch_off & 0x3fffffc));
}
else
write_insn<big_endian>(p, bctr);
}
}
// Write out long branch stubs.
typename Branch_stub_entries::const_iterator bs;
for (bs = this->long_branch_stubs_.begin();
bs != this->long_branch_stubs_.end();
++bs)
{
p = oview + this->plt_size_ + bs->second;
Address loc = this->stub_address() + this->plt_size_ + bs->second;
Address delta = bs->first.dest_ - loc;
if (delta + (1 << 25) < 2 << 25)
write_insn<big_endian>(p, b | (delta & 0x3fffffc));
else
{
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 = got_os_addr + bs->first.toc_base_off_;
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;
}
write_insn<big_endian>(p, mtctr_12), p += 4;
write_insn<big_endian>(p, bctr);
}
}
}
else
{
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;
// 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 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;
p = oview + cs->second;
if (parameters->options().output_is_position_independent())
{
Address got_addr;
const Powerpc_relobj<size, big_endian>* ppcobj
= (static_cast<const Powerpc_relobj<size, big_endian>*>
(cs->first.object_));
if (ppcobj != NULL && cs->first.addend_ >= 32768)
{
unsigned int got2 = ppcobj->got2_shndx();
got_addr = ppcobj->get_output_section_offset(got2);
gold_assert(got_addr != invalid_address);
got_addr += (ppcobj->output_section(got2)->address()
+ cs->first.addend_);
}
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();
}
got_addr = g_o_t;
}
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);
}
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);
}
}
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 out long branch stubs.
typename Branch_stub_entries::const_iterator bs;
for (bs = this->long_branch_stubs_.begin();
bs != this->long_branch_stubs_.end();
++bs)
{
p = oview + this->plt_size_ + bs->second;
Address loc = this->stub_address() + this->plt_size_ + bs->second;
Address delta = bs->first.dest_ - loc;
if (delta + (1 << 25) < 2 << 25)
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);
}
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 out .glink.
template<int size, bool big_endian>
void
Output_data_glink<size, big_endian>::do_write(Output_file* of)
{
const section_size_type off = this->offset();
const section_size_type oview_size =
convert_to_section_size_type(this->data_size());
unsigned char* const oview = of->get_output_view(off, oview_size);
unsigned char* p;
// The base address of the .plt section.
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
Address plt_base = this->targ_->plt_section()->address();
if (size == 64)
{
if (this->end_branch_table_ != 0)
{
// Write pltresolve stub.
p = oview;
Address after_bcl = this->address() + 16;
Address pltoff = plt_base - after_bcl;
elfcpp::Swap<64, big_endian>::writeval(p, pltoff), p += 8;
if (this->targ_->abiversion() < 2)
{
write_insn<big_endian>(p, mflr_12), 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, ld_2_11 + l(-16)), p += 4;
write_insn<big_endian>(p, mtlr_12), p += 4;
write_insn<big_endian>(p, add_11_2_11), p += 4;
write_insn<big_endian>(p, ld_12_11 + 0), p += 4;
write_insn<big_endian>(p, ld_2_11 + 8), p += 4;
write_insn<big_endian>(p, mtctr_12), p += 4;
write_insn<big_endian>(p, ld_11_11 + 16), p += 4;
}
else
{
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, ld_2_11 + l(-16)), p += 4;
write_insn<big_endian>(p, mtlr_0), 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, 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;
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;
// Write lazy link call stubs.
uint32_t indx = 0;
while (p < oview + this->end_branch_table_)
{
if (this->targ_->abiversion() < 2)
{
if (indx < 0x8000)
{
write_insn<big_endian>(p, li_0_0 + indx), p += 4;
}
else
{
write_insn<big_endian>(p, lis_0_0 + hi(indx)), p += 4;
write_insn<big_endian>(p, ori_0_0_0 + l(indx)), p += 4;
}
}
uint32_t branch_off = 8 - (p - oview);
write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)), p += 4;
indx++;
}
}
Address plt_base = this->targ_->plt_section()->address();
Address iplt_base = invalid_address;
unsigned int global_entry_off = (this->end_branch_table_ + 15) & -16;
Address global_entry_base = this->address() + global_entry_off;
typename Global_entry_stub_entries::const_iterator ge;
for (ge = this->global_entry_stubs_.begin();
ge != this->global_entry_stubs_.end();
++ge)
{
p = oview + global_entry_off + ge->second;
Address plt_addr = ge->first->plt_offset();
if (ge->first->type() == elfcpp::STT_GNU_IFUNC
&& ge->first->can_use_relative_reloc(false))
{
if (iplt_base == invalid_address)
iplt_base = this->targ_->iplt_section()->address();
plt_addr += iplt_base;
}
else
plt_addr += plt_base;
Address my_addr = global_entry_base + ge->second;
Address off = plt_addr - my_addr;
if (off + 0x80008000 > 0xffffffff || (off & 3) != 0)
gold_error(_("%s: linkage table error against `%s'"),
ge->first->object()->name().c_str(),
ge->first->demangled_name().c_str());
write_insn<big_endian>(p, addis_12_12 + ha(off)), p += 4;
write_insn<big_endian>(p, ld_12_12 + l(off)), p += 4;
write_insn<big_endian>(p, mtctr_12), p += 4;
write_insn<big_endian>(p, bctr);
}
}
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();
// Write out pltresolve branch table.
p = oview;
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;
while (p < end_p)
write_insn<big_endian>(p, nop), p += 4;
// Write out pltresolve call stub.
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);
Address got_bcl = g_o_t + 4 - (after_bcl_off + this->address());
write_insn<big_endian>(p + 28, addis_12_12 + ha(got_bcl));
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));
}
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 + 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);
}
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));
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));
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);
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));
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);
}
p += 64;
}
of->write_output_view(off, oview_size, oview);
}
// A class to handle linker generated save/restore functions.
template<int size, bool big_endian>
class Output_data_save_res : public Output_section_data_build
{
public:
Output_data_save_res(Symbol_table* symtab);
protected:
// Write to a map file.
void
do_print_to_mapfile(Mapfile* mapfile) const
{ mapfile->print_output_data(this, _("** save/restore")); }
void
do_write(Output_file*);
private:
// The maximum size of save/restore contents.
static const unsigned int savres_max = 218*4;
void
savres_define(Symbol_table* symtab,
const char *name,
unsigned int lo, unsigned int hi,
unsigned char* write_ent(unsigned char*, int),
unsigned char* write_tail(unsigned char*, int));
unsigned char *contents_;
};
template<bool big_endian>
static unsigned char*
savegpr0(unsigned char* p, int r)
{
uint32_t insn = std_0_1 + (r << 21) + (1 << 16) - (32 - r) * 8;
write_insn<big_endian>(p, insn);
return p + 4;
}
template<bool big_endian>
static unsigned char*
savegpr0_tail(unsigned char* p, int r)
{
p = savegpr0<big_endian>(p, r);
uint32_t insn = std_0_1 + 16;
write_insn<big_endian>(p, insn);
p = p + 4;
write_insn<big_endian>(p, blr);
return p + 4;
}
template<bool big_endian>
static unsigned char*
restgpr0(unsigned char* p, int r)
{
uint32_t insn = ld_0_1 + (r << 21) + (1 << 16) - (32 - r) * 8;
write_insn<big_endian>(p, insn);
return p + 4;
}
template<bool big_endian>
static unsigned char*
restgpr0_tail(unsigned char* p, int r)
{
uint32_t insn = ld_0_1 + 16;
write_insn<big_endian>(p, insn);
p = p + 4;
p = restgpr0<big_endian>(p, r);
write_insn<big_endian>(p, mtlr_0);
p = p + 4;
if (r == 29)
{
p = restgpr0<big_endian>(p, 30);
p = restgpr0<big_endian>(p, 31);
}
write_insn<big_endian>(p, blr);
return p + 4;
}
template<bool big_endian>
static unsigned char*
savegpr1(unsigned char* p, int r)
{
uint32_t insn = std_0_12 + (r << 21) + (1 << 16) - (32 - r) * 8;
write_insn<big_endian>(p, insn);
return p + 4;
}
template<bool big_endian>
static unsigned char*
savegpr1_tail(unsigned char* p, int r)
{
p = savegpr1<big_endian>(p, r);
write_insn<big_endian>(p, blr);
return p + 4;
}
template<bool big_endian>
static unsigned char*
restgpr1(unsigned char* p, int r)
{
uint32_t insn = ld_0_12 + (r << 21) + (1 << 16) - (32 - r) * 8;
write_insn<big_endian>(p, insn);
return p + 4;
}
template<bool big_endian>
static unsigned char*
restgpr1_tail(unsigned char* p, int r)
{
p = restgpr1<big_endian>(p, r);
write_insn<big_endian>(p, blr);
return p + 4;
}
template<bool big_endian>
static unsigned char*
savefpr(unsigned char* p, int r)
{
uint32_t insn = stfd_0_1 + (r << 21) + (1 << 16) - (32 - r) * 8;
write_insn<big_endian>(p, insn);
return p + 4;
}
template<bool big_endian>
static unsigned char*
savefpr0_tail(unsigned char* p, int r)
{
p = savefpr<big_endian>(p, r);
write_insn<big_endian>(p, std_0_1 + 16);
p = p + 4;
write_insn<big_endian>(p, blr);
return p + 4;
}
template<bool big_endian>
static unsigned char*
restfpr(unsigned char* p, int r)
{
uint32_t insn = lfd_0_1 + (r << 21) + (1 << 16) - (32 - r) * 8;
write_insn<big_endian>(p, insn);
return p + 4;
}
template<bool big_endian>
static unsigned char*
restfpr0_tail(unsigned char* p, int r)
{
write_insn<big_endian>(p, ld_0_1 + 16);
p = p + 4;
p = restfpr<big_endian>(p, r);
write_insn<big_endian>(p, mtlr_0);
p = p + 4;
if (r == 29)
{
p = restfpr<big_endian>(p, 30);
p = restfpr<big_endian>(p, 31);
}
write_insn<big_endian>(p, blr);
return p + 4;
}
template<bool big_endian>
static unsigned char*
savefpr1_tail(unsigned char* p, int r)
{
p = savefpr<big_endian>(p, r);
write_insn<big_endian>(p, blr);
return p + 4;
}
template<bool big_endian>
static unsigned char*
restfpr1_tail(unsigned char* p, int r)
{
p = restfpr<big_endian>(p, r);
write_insn<big_endian>(p, blr);
return p + 4;
}
template<bool big_endian>
static unsigned char*
savevr(unsigned char* p, int r)
{
uint32_t insn = li_12_0 + (1 << 16) - (32 - r) * 16;
write_insn<big_endian>(p, insn);
p = p + 4;
insn = stvx_0_12_0 + (r << 21);
write_insn<big_endian>(p, insn);
return p + 4;
}
template<bool big_endian>
static unsigned char*
savevr_tail(unsigned char* p, int r)
{
p = savevr<big_endian>(p, r);
write_insn<big_endian>(p, blr);
return p + 4;
}
template<bool big_endian>
static unsigned char*
restvr(unsigned char* p, int r)
{
uint32_t insn = li_12_0 + (1 << 16) - (32 - r) * 16;
write_insn<big_endian>(p, insn);
p = p + 4;
insn = lvx_0_12_0 + (r << 21);
write_insn<big_endian>(p, insn);
return p + 4;
}
template<bool big_endian>
static unsigned char*
restvr_tail(unsigned char* p, int r)
{
p = restvr<big_endian>(p, r);
write_insn<big_endian>(p, blr);
return p + 4;
}
template<int size, bool big_endian>
Output_data_save_res<size, big_endian>::Output_data_save_res(
Symbol_table* symtab)
: Output_section_data_build(4),
contents_(NULL)
{
this->savres_define(symtab,
"_savegpr0_", 14, 31,
savegpr0<big_endian>, savegpr0_tail<big_endian>);
this->savres_define(symtab,
"_restgpr0_", 14, 29,
restgpr0<big_endian>, restgpr0_tail<big_endian>);
this->savres_define(symtab,
"_restgpr0_", 30, 31,
restgpr0<big_endian>, restgpr0_tail<big_endian>);
this->savres_define(symtab,
"_savegpr1_", 14, 31,
savegpr1<big_endian>, savegpr1_tail<big_endian>);
this->savres_define(symtab,
"_restgpr1_", 14, 31,
restgpr1<big_endian>, restgpr1_tail<big_endian>);
this->savres_define(symtab,
"_savefpr_", 14, 31,
savefpr<big_endian>, savefpr0_tail<big_endian>);
this->savres_define(symtab,
"_restfpr_", 14, 29,
restfpr<big_endian>, restfpr0_tail<big_endian>);
this->savres_define(symtab,
"_restfpr_", 30, 31,
restfpr<big_endian>, restfpr0_tail<big_endian>);
this->savres_define(symtab,
"._savef", 14, 31,
savefpr<big_endian>, savefpr1_tail<big_endian>);
this->savres_define(symtab,
"._restf", 14, 31,
restfpr<big_endian>, restfpr1_tail<big_endian>);
this->savres_define(symtab,
"_savevr_", 20, 31,
savevr<big_endian>, savevr_tail<big_endian>);
this->savres_define(symtab,
"_restvr_", 20, 31,
restvr<big_endian>, restvr_tail<big_endian>);
}
template<int size, bool big_endian>
void
Output_data_save_res<size, big_endian>::savres_define(
Symbol_table* symtab,
const char *name,
unsigned int lo, unsigned int hi,
unsigned char* write_ent(unsigned char*, int),
unsigned char* write_tail(unsigned char*, int))
{
size_t len = strlen(name);
bool writing = false;
char sym[16];
memcpy(sym, name, len);
sym[len + 2] = 0;
for (unsigned int i = lo; i <= hi; i++)
{
sym[len + 0] = i / 10 + '0';
sym[len + 1] = i % 10 + '0';
Symbol* gsym = symtab->lookup(sym);
bool refd = gsym != NULL && gsym->is_undefined();
writing = writing || refd;
if (writing)
{
if (this->contents_ == NULL)
this->contents_ = new unsigned char[this->savres_max];
section_size_type value = this->current_data_size();
unsigned char* p = this->contents_ + value;
if (i != hi)
p = write_ent(p, i);
else
p = write_tail(p, i);
section_size_type cur_size = p - this->contents_;
this->set_current_data_size(cur_size);
if (refd)
symtab->define_in_output_data(sym, NULL, Symbol_table::PREDEFINED,
this, value, cur_size - value,
elfcpp::STT_FUNC, elfcpp::STB_GLOBAL,
elfcpp::STV_HIDDEN, 0, false, false);
}
}
}
// Write out save/restore.
template<int size, bool big_endian>
void
Output_data_save_res<size, big_endian>::do_write(Output_file* of)
{
const section_size_type off = this->offset();
const section_size_type oview_size =
convert_to_section_size_type(this->data_size());
unsigned char* const oview = of->get_output_view(off, oview_size);
memcpy(oview, this->contents_, oview_size);
of->write_output_view(off, oview_size, oview);
}
// Create the glink section.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::make_glink_section(Layout* layout)
{
if (this->glink_ == NULL)
{
this->glink_ = new Output_data_glink<size, big_endian>(this);
this->glink_->add_eh_frame(layout);
layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
this->glink_, ORDER_TEXT, false);
}
}
// Create a PLT entry for a global symbol.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::make_plt_entry(Symbol_table* symtab,
Layout* layout,
Symbol* gsym)
{
if (gsym->type() == elfcpp::STT_GNU_IFUNC
&& gsym->can_use_relative_reloc(false))
{
if (this->iplt_ == NULL)
this->make_iplt_section(symtab, layout);
this->iplt_->add_ifunc_entry(gsym);
}
else
{
if (this->plt_ == NULL)
this->make_plt_section(symtab, layout);
this->plt_->add_entry(gsym);
}
}
// Make a PLT entry for a local STT_GNU_IFUNC symbol.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::make_local_ifunc_plt_entry(
Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* relobj,
unsigned int r_sym)
{
if (this->iplt_ == NULL)
this->make_iplt_section(symtab, layout);
this->iplt_->add_local_ifunc_entry(relobj, r_sym);
}
// Return the number of entries in the PLT.
template<int size, bool big_endian>
unsigned int
Target_powerpc<size, big_endian>::plt_entry_count() const
{
if (this->plt_ == NULL)
return 0;
return this->plt_->entry_count();
}
// Create a GOT entry for local dynamic __tls_get_addr calls.
template<int size, bool big_endian>
unsigned int
Target_powerpc<size, big_endian>::tlsld_got_offset(
Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object)
{
if (this->tlsld_got_offset_ == -1U)
{
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);
unsigned int got_offset = got->add_constant_pair(0, 0);
rela_dyn->add_local(object, 0, elfcpp::R_POWERPC_DTPMOD, got,
got_offset, 0);
this->tlsld_got_offset_ = got_offset;
}
return this->tlsld_got_offset_;
}
// Get the Reference_flags for a particular relocation.
template<int size, bool big_endian>
int
Target_powerpc<size, big_endian>::Scan::get_reference_flags(
unsigned int r_type,
const Target_powerpc* target)
{
int ref = 0;
switch (r_type)
{
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:
ref = Symbol::ABSOLUTE_REF;
break;
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
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:
ref = Symbol::RELATIVE_REF;
break;
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:
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_TOC16:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_PPC64_TOC16_HI:
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
// Absolute in GOT.
ref = Symbol::ABSOLUTE_REF;
break;
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_TLS:
ref = Symbol::TLS_REF;
break;
case elfcpp::R_POWERPC_COPY:
case elfcpp::R_POWERPC_GLOB_DAT:
case elfcpp::R_POWERPC_JMP_SLOT:
case elfcpp::R_POWERPC_RELATIVE:
case elfcpp::R_POWERPC_DTPMOD:
default:
// Not expected. We will give an error later.
break;
}
if (size == 64 && target->abiversion() < 2)
ref |= Symbol::FUNC_DESC_ABI;
return ref;
}
// Report an unsupported relocation against a local symbol.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::Scan::unsupported_reloc_local(
Sized_relobj_file<size, big_endian>* object,
unsigned int r_type)
{
gold_error(_("%s: unsupported reloc %u against local symbol"),
object->name().c_str(), r_type);
}
// We are about to emit a dynamic relocation of type R_TYPE. If the
// dynamic linker does not support it, issue an error.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::Scan::check_non_pic(Relobj* object,
unsigned int r_type)
{
gold_assert(r_type != elfcpp::R_POWERPC_NONE);
// These are the relocation types supported by glibc for both 32-bit
// and 64-bit powerpc.
switch (r_type)
{
case elfcpp::R_POWERPC_NONE:
case elfcpp::R_POWERPC_RELATIVE:
case elfcpp::R_POWERPC_GLOB_DAT:
case elfcpp::R_POWERPC_DTPMOD:
case elfcpp::R_POWERPC_DTPREL:
case elfcpp::R_POWERPC_TPREL:
case elfcpp::R_POWERPC_JMP_SLOT:
case elfcpp::R_POWERPC_COPY:
case elfcpp::R_POWERPC_IRELATIVE:
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_UADDR32:
case elfcpp::R_POWERPC_ADDR24:
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_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
case elfcpp::R_POWERPC_REL32:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_POWERPC_TPREL16:
case elfcpp::R_POWERPC_TPREL16_LO:
case elfcpp::R_POWERPC_TPREL16_HI:
case elfcpp::R_POWERPC_TPREL16_HA:
return;
default:
break;
}
if (size == 64)
{
switch (r_type)
{
// These are the relocation types supported only on 64-bit.
case elfcpp::R_PPC64_ADDR64:
case elfcpp::R_PPC64_UADDR64:
case elfcpp::R_PPC64_JMP_IREL:
case elfcpp::R_PPC64_ADDR16_DS:
case elfcpp::R_PPC64_ADDR16_LO_DS:
case elfcpp::R_PPC64_ADDR16_HIGH:
case elfcpp::R_PPC64_ADDR16_HIGHA:
case elfcpp::R_PPC64_ADDR16_HIGHER:
case elfcpp::R_PPC64_ADDR16_HIGHEST:
case elfcpp::R_PPC64_ADDR16_HIGHERA:
case elfcpp::R_PPC64_ADDR16_HIGHESTA:
case elfcpp::R_PPC64_REL64:
case elfcpp::R_POWERPC_ADDR30:
case elfcpp::R_PPC64_TPREL16_DS:
case elfcpp::R_PPC64_TPREL16_LO_DS:
case elfcpp::R_PPC64_TPREL16_HIGH:
case elfcpp::R_PPC64_TPREL16_HIGHA:
case elfcpp::R_PPC64_TPREL16_HIGHER:
case elfcpp::R_PPC64_TPREL16_HIGHEST:
case elfcpp::R_PPC64_TPREL16_HIGHERA:
case elfcpp::R_PPC64_TPREL16_HIGHESTA:
return;
default:
break;
}
}
else
{
switch (r_type)
{
// These are the relocation types supported only on 32-bit.
// ??? glibc ld.so doesn't need to support these.
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_POWERPC_DTPREL16_HA:
return;
default:
break;
}
}
// This prevents us from issuing more than one error per reloc
// section. But we can still wind up issuing more than one
// error per object file.
if (this->issued_non_pic_error_)
return;
gold_assert(parameters->options().output_is_position_independent());
object->error(_("requires unsupported dynamic reloc; "
"recompile with -fPIC"));
this->issued_non_pic_error_ = true;
return;
}
// Return whether we need to make a PLT entry for a relocation of the
// given type against a STT_GNU_IFUNC symbol.
template<int size, bool big_endian>
bool
Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
Target_powerpc<size, big_endian>* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int r_type,
bool report_err)
{
// In non-pic code any reference will resolve to the plt call stub
// for the ifunc symbol.
if ((size == 32 || target->abiversion() >= 2)
&& !parameters->options().output_is_position_independent())
return true;
switch (r_type)
{
// 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 false;
break;
case elfcpp::R_PPC64_ADDR64:
case elfcpp::R_PPC64_UADDR64:
if (size == 64)
return false;
break;
// 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 false;
// 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:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
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:
return true;
default:
break;
}
// Anything else is a problem.
// If we are building a static executable, the libc startup function
// responsible for applying indirect function relocations is going
// to complain about the reloc type.
// If we are building a dynamic executable, we will have a text
// relocation. The dynamic loader will set the text segment
// writable and non-executable to apply text relocations. So we'll
// segfault when trying to run the indirection function to resolve
// the reloc.
if (report_err)
gold_error(_("%s: unsupported reloc %u for IFUNC symbol"),
object->name().c_str(), r_type);
return false;
}
// Scan a relocation for a local symbol.
template<int size, bool big_endian>
inline void
Target_powerpc<size, big_endian>::Scan::local(
Symbol_table* symtab,
Layout* layout,
Target_powerpc<size, big_endian>* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
const elfcpp::Sym<size, big_endian>& lsym,
bool is_discarded)
{
this->maybe_skip_tls_get_addr_call(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);
if (tls_type != tls::TLSOPT_NONE)
this->skip_next_tls_get_addr_call();
}
else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
|| (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
{
this->expect_tls_get_addr_call();
const 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
&& data_shndx == ppc_object->opd_shndx()
&& r_type == elfcpp::R_PPC64_ADDR64)
ppc_object->set_opd_discard(reloc.get_r_offset());
return;
}
// 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(target, 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(),
r_type, r_sym, reloc.get_r_addend());
target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
}
switch (r_type)
{
case elfcpp::R_POWERPC_NONE:
case elfcpp::R_POWERPC_GNU_VTINHERIT:
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_PPC64_TOCSAVE:
case elfcpp::R_POWERPC_TLS:
break;
case elfcpp::R_PPC64_TOC:
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
if (parameters->options().output_is_position_independent())
{
Address off = reloc.get_r_offset();
if (size == 64
&& target->abiversion() < 2
&& data_shndx == ppc_object->opd_shndx()
&& ppc_object->get_opd_discard(off - 8))
break;
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
Powerpc_relobj<size, big_endian>* symobj = ppc_object;
rela_dyn->add_output_section_relative(got->output_section(),
elfcpp::R_POWERPC_RELATIVE,
output_section,
object, data_shndx, off,
symobj->toc_base_offset());
}
}
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_ADDR24:
case elfcpp::R_POWERPC_ADDR16:
case elfcpp::R_POWERPC_ADDR16_LO:
case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_POWERPC_ADDR16_HA:
case elfcpp::R_POWERPC_UADDR16:
case elfcpp::R_PPC64_ADDR16_HIGH:
case elfcpp::R_PPC64_ADDR16_HIGHA:
case elfcpp::R_PPC64_ADDR16_HIGHER:
case elfcpp::R_PPC64_ADDR16_HIGHERA:
case elfcpp::R_PPC64_ADDR16_HIGHEST:
case elfcpp::R_PPC64_ADDR16_HIGHESTA:
case elfcpp::R_PPC64_ADDR16_DS:
case elfcpp::R_PPC64_ADDR16_LO_DS:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
// If building a shared library (or a position-independent
// executable), we need to create a dynamic relocation for
// this location.
if (parameters->options().output_is_position_independent()
|| (size == 64 && is_ifunc && target->abiversion() < 2))
{
Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
is_ifunc);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
if ((size == 32 && r_type == elfcpp::R_POWERPC_ADDR32)
|| (size == 64 && r_type == elfcpp::R_PPC64_ADDR64))
{
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(),
reloc.get_r_addend(), false);
}
else if (lsym.get_st_type() != elfcpp::STT_SECTION)
{
check_non_pic(object, r_type);
rela_dyn->add_local(object, r_sym, r_type, output_section,
data_shndx, reloc.get_r_offset(),
reloc.get_r_addend());
}
else
{
gold_assert(lsym.get_st_value() == 0);
unsigned int shndx = lsym.get_st_shndx();
bool is_ordinary;
shndx = object->adjust_sym_shndx(r_sym, shndx,
&is_ordinary);
if (!is_ordinary)
object->error(_("section symbol %u has bad shndx %u"),
r_sym, shndx);
else
rela_dyn->add_local_section(object, shndx, r_type,
output_section, data_shndx,
reloc.get_r_offset());
}
}
break;
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
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:
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:
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_SECTOFF_HI:
case elfcpp::R_POWERPC_SECTOFF_HA:
case elfcpp::R_PPC64_SECTOFF_DS:
case elfcpp::R_PPC64_SECTOFF_LO_DS:
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_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:
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_POWERPC_DTPREL16_HA:
case elfcpp::R_PPC64_DTPREL16_DS:
case elfcpp::R_PPC64_DTPREL16_LO_DS:
case elfcpp::R_PPC64_DTPREL16_HIGH:
case elfcpp::R_PPC64_DTPREL16_HIGHA:
case elfcpp::R_PPC64_DTPREL16_HIGHER:
case elfcpp::R_PPC64_DTPREL16_HIGHERA:
case elfcpp::R_PPC64_DTPREL16_HIGHEST:
case elfcpp::R_PPC64_DTPREL16_HIGHESTA:
case elfcpp::R_PPC64_TLSGD:
case elfcpp::R_PPC64_TLSLD:
case elfcpp::R_PPC64_ADDR64_LOCAL:
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:
{
// The symbol requires a GOT entry.
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
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);
else
got->add_local(object, r_sym, GOT_TYPE_STANDARD);
}
else if (!object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD))
{
// 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);
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);
}
}
break;
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_PPC64_TOC16_HI:
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
// We need a GOT section.
target->got_section(symtab, layout);
break;
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
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);
if (tls_type == tls::TLSOPT_NONE)
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
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);
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
// no GOT relocs needed for Local Exec.
}
else
gold_unreachable();
}
break;
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
{
const 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)
{
// no GOT relocs needed for Local Exec.
if (parameters->options().emit_relocs())
{
Output_section* os = layout->tls_segment()->first_section();
gold_assert(os != NULL);
os->set_needs_symtab_index();
}
}
else
gold_unreachable();
}
break;
case elfcpp::R_POWERPC_GOT_DTPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
got->add_local_tls(object, r_sym, GOT_TYPE_DTPREL);
}
break;
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_TPREL16_LO:
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);
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))
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
unsigned int off = got->add_constant(0);
object->set_local_got_offset(r_sym, GOT_TYPE_TPREL, off);
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);
}
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
// no GOT relocs needed for Local Exec.
}
else
gold_unreachable();
}
break;
default:
unsupported_reloc_local(object, r_type);
break;
}
switch (r_type)
{
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16:
case elfcpp::R_POWERPC_GOT16:
case elfcpp::R_PPC64_GOT16_DS:
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_PPC64_TOC16_DS:
ppc_object->set_has_small_toc_reloc();
default:
break;
}
}
// Report an unsupported relocation against a global symbol.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::Scan::unsupported_reloc_global(
Sized_relobj_file<size, big_endian>* object,
unsigned int r_type,
Symbol* gsym)
{
gold_error(_("%s: unsupported reloc %u against global symbol %s"),
object->name().c_str(), r_type, gsym->demangled_name().c_str());
}
// Scan a relocation for a global symbol.
template<int size, bool big_endian>
inline void
Target_powerpc<size, big_endian>::Scan::global(
Symbol_table* symtab,
Layout* layout,
Target_powerpc<size, big_endian>* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
Symbol* gsym)
{
if (this->maybe_skip_tls_get_addr_call(r_type, gsym) == Track_tls::SKIP)
return;
if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
|| (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);
if (tls_type != tls::TLSOPT_NONE)
this->skip_next_tls_get_addr_call();
}
else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
|| (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
{
this->expect_tls_get_addr_call();
const 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;
if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, 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()),
reloc.get_r_addend());
target->make_plt_entry(symtab, layout, gsym);
pushed_ifunc = true;
}
switch (r_type)
{
case elfcpp::R_POWERPC_NONE:
case elfcpp::R_POWERPC_GNU_VTINHERIT:
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_POWERPC_TLS:
break;
case elfcpp::R_PPC64_TOC:
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
if (parameters->options().output_is_position_independent())
{
Address off = reloc.get_r_offset();
if (size == 64
&& data_shndx == ppc_object->opd_shndx()
&& ppc_object->get_opd_discard(off - 8))
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());
rela_dyn->add_output_section_relative(got->output_section(),
elfcpp::R_POWERPC_RELATIVE,
output_section,
object, data_shndx, off,
symobj->toc_base_offset());
}
}
break;
case elfcpp::R_PPC64_ADDR64:
if (size == 64
&& target->abiversion() < 2
&& data_shndx == ppc_object->opd_shndx()
&& (gsym->is_defined_in_discarded_section()
|| gsym->object() != object))
{
ppc_object->set_opd_discard(reloc.get_r_offset());
break;
}
// Fall thru
case elfcpp::R_PPC64_UADDR64:
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_UADDR32:
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR16:
case elfcpp::R_POWERPC_ADDR16_LO:
case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_POWERPC_ADDR16_HA:
case elfcpp::R_POWERPC_UADDR16:
case elfcpp::R_PPC64_ADDR16_HIGH:
case elfcpp::R_PPC64_ADDR16_HIGHA:
case elfcpp::R_PPC64_ADDR16_HIGHER:
case elfcpp::R_PPC64_ADDR16_HIGHERA:
case elfcpp::R_PPC64_ADDR16_HIGHEST:
case elfcpp::R_PPC64_ADDR16_HIGHESTA:
case elfcpp::R_PPC64_ADDR16_DS:
case elfcpp::R_PPC64_ADDR16_LO_DS:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
{
// Make a PLT entry if necessary.
if (gsym->needs_plt_entry())
{
// 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
// the PLT call stub.
bool need_ifunc_plt = false;
if ((size == 32 || target->abiversion() >= 2)
&& gsym->is_from_dynobj()
&& !parameters->options().output_is_position_independent())
{
gsym->set_needs_dynsym_value();
need_ifunc_plt = true;
}
if (!is_ifunc || (!pushed_ifunc && need_ifunc_plt))
{
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);
}
}
// Make a dynamic relocation if necessary.
if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type, target))
|| (size == 64 && is_ifunc && target->abiversion() < 2))
{
if (!parameters->options().output_is_position_independent()
&& gsym->may_need_copy_reloc())
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
}
else if ((((size == 32
&& r_type == elfcpp::R_POWERPC_ADDR32)
|| (size == 64
&& r_type == elfcpp::R_PPC64_ADDR64
&& target->abiversion() >= 2))
&& gsym->can_use_relative_reloc(false)
&& !(gsym->visibility() == elfcpp::STV_PROTECTED
&& parameters->options().shared()))
|| (size == 64
&& r_type == elfcpp::R_PPC64_ADDR64
&& target->abiversion() < 2
&& (gsym->can_use_relative_reloc(false)
|| data_shndx == ppc_object->opd_shndx())))
{
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(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(),
reloc.get_r_addend());
}
}
}
break;
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_POWERPC_REL24:
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:
case elfcpp::R_POWERPC_REL32:
// Make a dynamic relocation if necessary.
if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type, target)))
{
if (!parameters->options().output_is_position_independent()
&& gsym->may_need_copy_reloc())
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym,
reloc);
}
else
{
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(),
reloc.get_r_addend());
}
}
break;
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
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:
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_SECTOFF_HI:
case elfcpp::R_POWERPC_SECTOFF_HA:
case elfcpp::R_PPC64_SECTOFF_DS:
case elfcpp::R_PPC64_SECTOFF_LO_DS:
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_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:
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_POWERPC_DTPREL16_HA:
case elfcpp::R_PPC64_DTPREL16_DS:
case elfcpp::R_PPC64_DTPREL16_LO_DS:
case elfcpp::R_PPC64_DTPREL16_HIGH:
case elfcpp::R_PPC64_DTPREL16_HIGHA:
case elfcpp::R_PPC64_DTPREL16_HIGHER:
case elfcpp::R_PPC64_DTPREL16_HIGHERA:
case elfcpp::R_PPC64_DTPREL16_HIGHEST:
case elfcpp::R_PPC64_DTPREL16_HIGHESTA:
case elfcpp::R_PPC64_TLSGD:
case elfcpp::R_PPC64_TLSLD:
case elfcpp::R_PPC64_ADDR64_LOCAL:
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:
{
// The symbol requires a GOT entry.
Output_data_got_powerpc<size, big_endian>* got;
got = target->got_section(symtab, layout);
if (gsym->final_value_is_known())
{
if (is_ifunc
&& (size == 32 || target->abiversion() >= 2))
got->add_global_plt(gsym, GOT_TYPE_STANDARD);
else
got->add_global(gsym, GOT_TYPE_STANDARD);
}
else if (!gsym->has_got_offset(GOT_TYPE_STANDARD))
{
// 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);
Reloc_section* rela_dyn
= target->rela_dyn_section(symtab, layout, is_ifunc);
if (gsym->can_use_relative_reloc(false)
&& !((size == 32
|| target->abiversion() >= 2)
&& gsym->visibility() == elfcpp::STV_PROTECTED
&& parameters->options().shared()))
{
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
{
unsigned int dynrel = elfcpp::R_POWERPC_GLOB_DAT;
rela_dyn->add_global(gsym, dynrel, got, off, 0);
}
}
}
break;
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_PPC64_TOC16_HI:
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
// We need a GOT section.
target->got_section(symtab, layout);
break;
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
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);
if (tls_type == tls::TLSOPT_NONE)
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, 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);
}
else if (tls_type == tls::TLSOPT_TO_IE)
{
if (!gsym->has_got_offset(GOT_TYPE_TPREL))
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
if (gsym->is_undefined()
|| gsym->is_from_dynobj())
{
got->add_global_with_rel(gsym, GOT_TYPE_TPREL, rela_dyn,
elfcpp::R_POWERPC_TPREL);
}
else
{
unsigned int off = got->add_constant(0);
gsym->set_got_offset(GOT_TYPE_TPREL, off);
unsigned int dynrel = elfcpp::R_POWERPC_TPREL;
rela_dyn->add_symbolless_global_addend(gsym, dynrel,
got, off, 0);
}
}
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
// no GOT relocs needed for Local Exec.
}
else
gold_unreachable();
}
break;
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
{
const 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)
{
// no GOT relocs needed for Local Exec.
if (parameters->options().emit_relocs())
{
Output_section* os = layout->tls_segment()->first_section();
gold_assert(os != NULL);
os->set_needs_symtab_index();
}
}
else
gold_unreachable();
}
break;
case elfcpp::R_POWERPC_GOT_DTPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
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,
target->rela_dyn_section(layout),
elfcpp::R_POWERPC_DTPREL);
else
got->add_global_tls(gsym, GOT_TYPE_DTPREL);
}
break;
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_TPREL16_LO:
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);
if (tls_type == tls::TLSOPT_NONE)
{
if (!gsym->has_got_offset(GOT_TYPE_TPREL))
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
if (gsym->is_undefined()
|| gsym->is_from_dynobj())
{
got->add_global_with_rel(gsym, GOT_TYPE_TPREL, rela_dyn,
elfcpp::R_POWERPC_TPREL);
}
else
{
unsigned int off = got->add_constant(0);
gsym->set_got_offset(GOT_TYPE_TPREL, off);
unsigned int dynrel = elfcpp::R_POWERPC_TPREL;
rela_dyn->add_symbolless_global_addend(gsym, dynrel,
got, off, 0);
}
}
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
// no GOT relocs needed for Local Exec.
}
else
gold_unreachable();
}
break;
default:
unsupported_reloc_global(object, r_type, gsym);
break;
}
switch (r_type)
{
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16:
case elfcpp::R_POWERPC_GOT16:
case elfcpp::R_PPC64_GOT16_DS:
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_PPC64_TOC16_DS:
ppc_object->set_has_small_toc_reloc();
default:
break;
}
}
// Process relocations for gc.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::gc_process_relocs(
Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols)
{
typedef Target_powerpc<size, big_endian> Powerpc;
typedef typename Target_powerpc<size, big_endian>::Scan Scan;
Powerpc_relobj<size, big_endian>* ppc_object
= static_cast<Powerpc_relobj<size, big_endian>*>(object);
if (size == 64)
ppc_object->set_opd_valid();
if (size == 64 && data_shndx == ppc_object->opd_shndx())
{
typename Powerpc_relobj<size, big_endian>::Access_from::iterator p;
for (p = ppc_object->access_from_map()->begin();
p != ppc_object->access_from_map()->end();
++p)
{
Address dst_off = p->first;
unsigned int dst_indx = ppc_object->get_opd_ent(dst_off);
typename Powerpc_relobj<size, big_endian>::Section_refs::iterator s;
for (s = p->second.begin(); s != p->second.end(); ++s)
{
Object* src_obj = s->first;
unsigned int src_indx = s->second;
symtab->gc()->add_reference(src_obj, src_indx,
ppc_object, dst_indx);
}
p->second.clear();
}
ppc_object->access_from_map()->clear();
ppc_object->process_gc_mark(symtab);
// Don't look at .opd relocs as .opd will reference everything.
return;
}
gold::gc_process_relocs<size, big_endian, Powerpc, elfcpp::SHT_RELA, Scan,
typename Target_powerpc::Relocatable_size_for_reloc>(
symtab,
layout,
this,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_symbols);
}
// Handle target specific gc actions when adding a gc reference from
// SRC_OBJ, SRC_SHNDX to a location specified by DST_OBJ, DST_SHNDX
// and DST_OFF. For powerpc64, this adds a referenc to the code
// section of a function descriptor.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::do_gc_add_reference(
Symbol_table* symtab,
Object* src_obj,
unsigned int src_shndx,
Object* dst_obj,
unsigned int dst_shndx,
Address dst_off) const
{
if (size != 64 || dst_obj->is_dynamic())
return;
Powerpc_relobj<size, big_endian>* ppc_object
= static_cast<Powerpc_relobj<size, big_endian>*>(dst_obj);
if (dst_shndx != 0 && dst_shndx == ppc_object->opd_shndx())
{
if (ppc_object->opd_valid())
{
dst_shndx = ppc_object->get_opd_ent(dst_off);
symtab->gc()->add_reference(src_obj, src_shndx, dst_obj, dst_shndx);
}
else
{
// If we haven't run scan_opd_relocs, we must delay
// processing this function descriptor reference.
ppc_object->add_reference(src_obj, src_shndx, dst_off);
}
}
}
// Add any special sections for this symbol to the gc work list.
// For powerpc64, this adds the code section of a function
// descriptor.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::do_gc_mark_symbol(
Symbol_table* symtab,
Symbol* sym) const
{
if (size == 64)
{
Powerpc_relobj<size, big_endian>* ppc_object
= static_cast<Powerpc_relobj<size, big_endian>*>(sym->object());
bool is_ordinary;
unsigned int shndx = sym->shndx(&is_ordinary);
if (is_ordinary && shndx != 0 && shndx == ppc_object->opd_shndx())
{
Sized_symbol<size>* gsym = symtab->get_sized_symbol<size>(sym);
Address dst_off = gsym->value();
if (ppc_object->opd_valid())
{
unsigned int dst_indx = ppc_object->get_opd_ent(dst_off);
symtab->gc()->worklist().push(Section_id(ppc_object, dst_indx));
}
else
ppc_object->add_gc_mark(dst_off);
}
}
}
// 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 && loc->shndx != 0)
{
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>
void
Target_powerpc<size, big_endian>::scan_relocs(
Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols)
{
typedef Target_powerpc<size, big_endian> Powerpc;
typedef typename Target_powerpc<size, big_endian>::Scan Scan;
if (sh_type == elfcpp::SHT_REL)
{
gold_error(_("%s: unsupported REL reloc section"),
object->name().c_str());
return;
}
gold::scan_relocs<size, big_endian, Powerpc, elfcpp::SHT_RELA, Scan>(
symtab,
layout,
this,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_symbols);
}
// Functor class for processing the global symbol table.
// Removes symbols defined on discarded opd entries.
template<bool big_endian>
class Global_symbol_visitor_opd
{
public:
Global_symbol_visitor_opd()
{ }
void
operator()(Sized_symbol<64>* sym)
{
if (sym->has_symtab_index()
|| sym->source() != Symbol::FROM_OBJECT
|| !sym->in_real_elf())
return;
if (sym->object()->is_dynamic())
return;
Powerpc_relobj<64, big_endian>* symobj
= static_cast<Powerpc_relobj<64, big_endian>*>(sym->object());
if (symobj->opd_shndx() == 0)
return;
bool is_ordinary;
unsigned int shndx = sym->shndx(&is_ordinary);
if (shndx == symobj->opd_shndx()
&& symobj->get_opd_discard(sym->value()))
{
sym->set_undefined();
sym->set_visibility(elfcpp::STV_DEFAULT);
sym->set_is_defined_in_discarded_section();
sym->set_symtab_index(-1U);
}
}
};
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::define_save_restore_funcs(
Layout* layout,
Symbol_table* symtab)
{
if (size == 64)
{
Output_data_save_res<64, big_endian>* savres
= new Output_data_save_res<64, big_endian>(symtab);
layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
savres, ORDER_TEXT, false);
}
}
// Sort linker created .got section first (for the header), then input
// sections belonging to files using small model code.
template<bool big_endian>
class Sort_toc_sections
{
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;
}
};
// Finalize the sections.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::do_finalize_sections(
Layout* layout,
const Input_objects*,
Symbol_table* symtab)
{
if (parameters->doing_static_link())
{
// At least some versions of glibc elf-init.o have a strong
// reference to __rela_iplt marker syms. A weak ref would be
// better..
if (this->iplt_ != NULL)
{
Reloc_section* rel = this->iplt_->rel_plt();
symtab->define_in_output_data("__rela_iplt_start", NULL,
Symbol_table::PREDEFINED, rel, 0, 0,
elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
elfcpp::STV_HIDDEN, 0, false, true);
symtab->define_in_output_data("__rela_iplt_end", NULL,
Symbol_table::PREDEFINED, rel, 0, 0,
elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
elfcpp::STV_HIDDEN, 0, true, true);
}
else
{
symtab->define_as_constant("__rela_iplt_start", NULL,
Symbol_table::PREDEFINED, 0, 0,
elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
elfcpp::STV_HIDDEN, 0, true, false);
symtab->define_as_constant("__rela_iplt_end", NULL,
Symbol_table::PREDEFINED, 0, 0,
elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
elfcpp::STV_HIDDEN, 0, true, false);
}
}
if (size == 64)
{
typedef Global_symbol_visitor_opd<big_endian> Symbol_visitor;
symtab->for_all_symbols<64, Symbol_visitor>(Symbol_visitor());
if (!parameters->options().relocatable())
{
this->define_save_restore_funcs(layout, symtab);
// 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->make_brlt_section(layout);
if (parameters->options().toc_sort())
{
Output_section* os = this->got_->output_section();
if (os != NULL && os->input_sections().size() > 1)
std::stable_sort(os->input_sections().begin(),
os->input_sections().end(),
Sort_toc_sections<big_endian>());
}
}
}
// Fill in some more dynamic tags.
Output_data_dynamic* odyn = layout->dynamic_data();
if (odyn != NULL)
{
const Reloc_section* rel_plt = (this->plt_ == NULL
? NULL
: this->plt_->rel_plt());
layout->add_target_dynamic_tags(false, this->plt_, rel_plt,
this->rela_dyn_, true, size == 32);
if (size == 32)
{
if (this->got_ != NULL)
{
this->got_->finalize_data_size();
odyn->add_section_plus_offset(elfcpp::DT_PPC_GOT,
this->got_, this->got_->g_o_t());
}
}
else
{
if (this->glink_ != NULL)
{
this->glink_->finalize_data_size();
odyn->add_section_plus_offset(elfcpp::DT_PPC64_GLINK,
this->glink_,
(this->glink_->pltresolve_size
- 32));
}
}
}
// Emit any relocs we saved in an attempt to avoid generating COPY
// relocs.
if (this->copy_relocs_.any_saved_relocs())
this->copy_relocs_.emit(this->rela_dyn_section(layout));
}
// Return TRUE iff INSN is one we expect on a _LO variety toc/got
// reloc.
static bool
ok_lo_toc_insn(uint32_t insn)
{
return ((insn & (0x3f << 26)) == 14u << 26 /* addi */
|| (insn & (0x3f << 26)) == 32u << 26 /* lwz */
|| (insn & (0x3f << 26)) == 34u << 26 /* lbz */
|| (insn & (0x3f << 26)) == 36u << 26 /* stw */
|| (insn & (0x3f << 26)) == 38u << 26 /* stb */
|| (insn & (0x3f << 26)) == 40u << 26 /* lhz */
|| (insn & (0x3f << 26)) == 42u << 26 /* lha */
|| (insn & (0x3f << 26)) == 44u << 26 /* sth */
|| (insn & (0x3f << 26)) == 46u << 26 /* lmw */
|| (insn & (0x3f << 26)) == 47u << 26 /* stmw */
|| (insn & (0x3f << 26)) == 48u << 26 /* lfs */
|| (insn & (0x3f << 26)) == 50u << 26 /* lfd */
|| (insn & (0x3f << 26)) == 52u << 26 /* stfs */
|| (insn & (0x3f << 26)) == 54u << 26 /* stfd */
|| ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
&& (insn & 3) != 1)
|| ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
&& ((insn & 3) == 0 || (insn & 3) == 3))
|| (insn & (0x3f << 26)) == 12u << 26 /* addic */);
}
// Return the value to use for a branch relocation.
template<int size, bool big_endian>
bool
Target_powerpc<size, big_endian>::symval_for_branch(
const Symbol_table* symtab,
const Sized_symbol<size>* gsym,
Powerpc_relobj<size, big_endian>* object,
Address *value,
unsigned int *dest_shndx)
{
if (size == 32 || this->abiversion() >= 2)
gold_unreachable();
*dest_shndx = 0;
// If the symbol is defined in an opd section, ie. is a function
// descriptor, use the function descriptor code entry address
Powerpc_relobj<size, big_endian>* symobj = object;
if (gsym != NULL
&& gsym->source() != Symbol::FROM_OBJECT)
return true;
if (gsym != NULL)
symobj = static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object());
unsigned int shndx = symobj->opd_shndx();
if (shndx == 0)
return true;
Address opd_addr = symobj->get_output_section_offset(shndx);
if (opd_addr == invalid_address)
return true;
opd_addr += symobj->output_section_address(shndx);
if (*value >= opd_addr && *value < opd_addr + symobj->section_size(shndx))
{
Address sec_off;
*dest_shndx = symobj->get_opd_ent(*value - opd_addr, &sec_off);
if (symtab->is_section_folded(symobj, *dest_shndx))
{
Section_id folded
= symtab->icf()->get_folded_section(symobj, *dest_shndx);
symobj = static_cast<Powerpc_relobj<size, big_endian>*>(folded.first);
*dest_shndx = folded.second;
}
Address sec_addr = symobj->get_output_section_offset(*dest_shndx);
if (sec_addr == invalid_address)
return false;
sec_addr += symobj->output_section(*dest_shndx)->address();
*value = sec_addr + sec_off;
}
return true;
}
// Perform a relocation.
template<int size, bool big_endian>
inline bool
Target_powerpc<size, big_endian>::Relocate::relocate(
const Relocate_info<size, big_endian>* relinfo,
Target_powerpc* target,
Output_section* os,
size_t relnum,
const elfcpp::Rela<size, big_endian>& rela,
unsigned int r_type,
const Sized_symbol<size>* gsym,
const Symbol_value<size>* psymval,
unsigned char* view,
Address address,
section_size_type view_size)
{
if (view == NULL)
return true;
switch (this->maybe_skip_tls_get_addr_call(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"));
break;
case Track_tls::EXPECTED:
// We have already complained.
break;
case Track_tls::SKIP:
return true;
case Track_tls::NORMAL:
break;
}
typedef Powerpc_relocate_functions<size, big_endian> Reloc;
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
Powerpc_relobj<size, big_endian>* const object
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
Address value = 0;
bool has_stub_value = false;
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
if ((gsym != NULL
? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
: object->local_has_plt_offset(r_sym))
&& (!psymval->is_ifunc_symbol()
|| Scan::reloc_needs_plt_for_ifunc(target, object, r_type, false)))
{
if (size == 64
&& gsym != NULL
&& target->abiversion() >= 2
&& !parameters->options().output_is_position_independent()
&& !is_branch_reloc(r_type))
{
unsigned int off = target->glink_section()->find_global_entry(gsym);
gold_assert(off != (unsigned int)-1);
value = target->glink_section()->global_entry_address() + off;
}
else
{
Stub_table<size, big_endian>* stub_table
= object->stub_table(relinfo->data_shndx);
if (stub_table == NULL)
{
// This is a ref from a data section to an ifunc symbol.
if (target->stub_tables().size() != 0)
stub_table = target->stub_tables()[0];
}
gold_assert(stub_table != NULL);
Address off;
if (gsym != NULL)
off = stub_table->find_plt_call_entry(object, gsym, r_type,
rela.get_r_addend());
else
off = stub_table->find_plt_call_entry(object, r_sym, r_type,
rela.get_r_addend());
gold_assert(off != invalid_address);
value = stub_table->stub_address() + off;
}
has_stub_value = true;
}
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)
{
gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD));
value = gsym->got_offset(GOT_TYPE_STANDARD);
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD));
value = object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
}
value -= target->got_section()->got_base_offset(object);
}
else if (r_type == elfcpp::R_PPC64_TOC)
{
value = (target->got_section()->output_section()->address()
+ object->toc_base_offset());
}
else if (gsym != NULL
&& (r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC_PLTREL24)
&& has_stub_value)
{
if (size == 64)
{
typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
bool can_plt_call = false;
if (rela.get_r_offset() + 8 <= view_size)
{
Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv);
Valtype insn2 = elfcpp::Swap<32, big_endian>::readval(wv + 1);
if ((insn & 1) != 0
&& (insn2 == nop
|| insn2 == cror_15_15_15 || insn2 == cror_31_31_31))
{
elfcpp::Swap<32, big_endian>::
writeval(wv + 1, ld_2_1 + target->stk_toc());
can_plt_call = true;
}
}
if (!can_plt_call)
{
// If we don't have a branch and link followed by a nop,
// we can't go via the plt because there is no place to
// put a toc restoring instruction.
// Unless we know we won't be returning.
if (strcmp(gsym->name(), "__libc_start_main") == 0)
can_plt_call = true;
}
if (!can_plt_call)
{
// g++ as of 20130507 emits self-calls without a
// following nop. This is arguably wrong since we have
// conflicting information. On the one hand a global
// symbol and on the other a local call sequence, but
// don't error for this special case.
// It isn't possible to cheaply verify we have exactly
// such a call. Allow all calls to the same section.
bool ok = false;
Address code = value;
if (gsym->source() == Symbol::FROM_OBJECT
&& gsym->object() == object)
{
unsigned int dest_shndx = 0;
if (target->abiversion() < 2)
{
Address addend = rela.get_r_addend();
code = psymval->value(object, addend);
target->symval_for_branch(relinfo->symtab, gsym, object,
&code, &dest_shndx);
}
bool is_ordinary;
if (dest_shndx == 0)
dest_shndx = gsym->shndx(&is_ordinary);
ok = dest_shndx == relinfo->data_shndx;
}
if (!ok)
{
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("call lacks nop, can't restore toc; "
"recompile with -fPIC"));
value = code;
}
}
}
}
else if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_HI
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_HA)
{
// 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;
if (tls_type == tls::TLSOPT_NONE)
got_type = GOT_TYPE_TLSGD;
else if (tls_type == tls::TLSOPT_TO_IE)
got_type = GOT_TYPE_TPREL;
if (got_type != GOT_TYPE_STANDARD)
{
if (gsym != NULL)
{
gold_assert(gsym->has_got_offset(got_type));
value = gsym->got_offset(got_type);
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
gold_assert(object->local_has_got_offset(r_sym, got_type));
value = object->local_got_offset(r_sym, got_type);
}
value -= target->got_section()->got_base_offset(object);
}
if (tls_type == tls::TLSOPT_TO_IE)
{
if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= (1 << 26) - (1 << 16); // extract rt,ra from addi
if (size == 32)
insn |= 32 << 26; // lwz
else
insn |= 58 << 26; // ld
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
}
r_type += (elfcpp::R_POWERPC_GOT_TPREL16
- elfcpp::R_POWERPC_GOT_TLSGD16);
}
else if (tls_type == tls::TLSOPT_TO_LE)
{
if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= (1 << 26) - (1 << 21); // extract rt
if (size == 32)
insn |= addis_0_2;
else
insn |= addis_0_13;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_HA;
value = psymval->value(object, rela.get_r_addend());
}
else
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = nop;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
}
}
}
else if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_HI
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_HA)
{
// First instruction of a local dynamic sequence, arg setup insn.
const tls::Tls_optimization tls_type = target->optimize_tls_ld();
if (tls_type == tls::TLSOPT_NONE)
{
value = target->tlsld_got_offset();
value -= target->got_section()->got_base_offset(object);
}
else
{
gold_assert(tls_type == tls::TLSOPT_TO_LE);
if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= (1 << 26) - (1 << 21); // extract rt
if (size == 32)
insn |= addis_0_2;
else
insn |= addis_0_13;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_HA;
value = dtp_offset;
}
else
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = nop;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
}
}
}
else if (r_type == elfcpp::R_POWERPC_GOT_DTPREL16
|| r_type == elfcpp::R_POWERPC_GOT_DTPREL16_LO
|| r_type == elfcpp::R_POWERPC_GOT_DTPREL16_HI
|| r_type == elfcpp::R_POWERPC_GOT_DTPREL16_HA)
{
// Accesses relative to a local dynamic sequence address,
// no optimisation here.
if (gsym != NULL)
{
gold_assert(gsym->has_got_offset(GOT_TYPE_DTPREL));
value = gsym->got_offset(GOT_TYPE_DTPREL);
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_DTPREL));
value = object->local_got_offset(r_sym, GOT_TYPE_DTPREL);
}
value -= target->got_section()->got_base_offset(object);
}
else if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_HI
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_HA)
{
// 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);
if (tls_type == tls::TLSOPT_NONE)
{
if (gsym != NULL)
{
gold_assert(gsym->has_got_offset(GOT_TYPE_TPREL));
value = gsym->got_offset(GOT_TYPE_TPREL);
}
else
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_TPREL));
value = object->local_got_offset(r_sym, GOT_TYPE_TPREL);
}
value -= target->got_section()->got_base_offset(object);
}
else
{
gold_assert(tls_type == tls::TLSOPT_TO_LE);
if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO)
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= (1 << 26) - (1 << 21); // extract rt from ld
if (size == 32)
insn |= addis_0_2;
else
insn |= addis_0_13;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_HA;
value = psymval->value(object, rela.get_r_addend());
}
else
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = nop;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
}
}
}
else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
|| (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
{
// 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);
if (tls_type != tls::TLSOPT_NONE)
{
if (tls_type == tls::TLSOPT_TO_IE)
{
Insn* iview = reinterpret_cast<Insn*>(view);
Insn insn = add_3_3_13;
if (size == 32)
insn = add_3_3_2;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
}
else
{
Insn* iview = reinterpret_cast<Insn*>(view);
Insn insn = addi_3_3;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_LO;
view += 2 * big_endian;
value = psymval->value(object, rela.get_r_addend());
}
this->skip_next_tls_get_addr_call();
}
}
else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
|| (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
{
// 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();
if (tls_type == tls::TLSOPT_TO_LE)
{
Insn* iview = reinterpret_cast<Insn*>(view);
Insn insn = addi_3_3;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
this->skip_next_tls_get_addr_call();
r_type = elfcpp::R_POWERPC_TPREL16_LO;
view += 2 * big_endian;
value = dtp_offset;
}
}
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);
if (tls_type == tls::TLSOPT_TO_LE)
{
Insn* iview = reinterpret_cast<Insn*>(view);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
unsigned int reg = size == 32 ? 2 : 13;
insn = at_tls_transform(insn, reg);
gold_assert(insn != 0);
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_LO;
view += 2 * big_endian;
value = psymval->value(object, rela.get_r_addend());
}
}
else if (!has_stub_value)
{
Address addend = 0;
if (!(size == 32 && r_type == elfcpp::R_PPC_PLTREL24))
addend = rela.get_r_addend();
value = psymval->value(object, addend);
if (size == 64 && is_branch_reloc(r_type))
{
if (target->abiversion() >= 2)
{
if (gsym != NULL)
value += object->ppc64_local_entry_offset(gsym);
else
value += object->ppc64_local_entry_offset(r_sym);
}
else
{
unsigned int dest_shndx;
target->symval_for_branch(relinfo->symtab, gsym, object,
&value, &dest_shndx);
}
}
Address max_branch_offset = max_branch_delta(r_type);
if (max_branch_offset != 0
&& value - address + max_branch_offset >= 2 * max_branch_offset)
{
Stub_table<size, big_endian>* stub_table
= object->stub_table(relinfo->data_shndx);
if (stub_table != NULL)
{
Address off = stub_table->find_long_branch_entry(object, value);
if (off != invalid_address)
{
value = (stub_table->stub_address() + stub_table->plt_size()
+ off);
has_stub_value = true;
}
}
}
}
switch (r_type)
{
case elfcpp::R_PPC64_REL64:
case elfcpp::R_POWERPC_REL32:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
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:
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
value -= address;
break;
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_PPC64_TOC16_HI:
case elfcpp::R_PPC64_TOC16_HA:
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());
break;
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_SECTOFF_HI:
case elfcpp::R_POWERPC_SECTOFF_HA:
case elfcpp::R_PPC64_SECTOFF_DS:
case elfcpp::R_PPC64_SECTOFF_LO_DS:
if (os != NULL)
value -= os->address();
break;
case elfcpp::R_PPC64_TPREL16_DS:
case elfcpp::R_PPC64_TPREL16_LO_DS:
case elfcpp::R_PPC64_TPREL16_HIGH:
case elfcpp::R_PPC64_TPREL16_HIGHA:
if (size != 64)
// R_PPC_TLSGD, R_PPC_TLSLD, R_PPC_EMB_RELST_LO, R_PPC_EMB_RELST_HI
break;
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_POWERPC_TPREL:
case elfcpp::R_PPC64_TPREL16_HIGHER:
case elfcpp::R_PPC64_TPREL16_HIGHERA:
case elfcpp::R_PPC64_TPREL16_HIGHEST:
case elfcpp::R_PPC64_TPREL16_HIGHESTA:
// tls symbol values are relative to tls_segment()->vaddr()
value -= tp_offset;
break;
case elfcpp::R_PPC64_DTPREL16_DS:
case elfcpp::R_PPC64_DTPREL16_LO_DS:
case elfcpp::R_PPC64_DTPREL16_HIGHER:
case elfcpp::R_PPC64_DTPREL16_HIGHERA:
case elfcpp::R_PPC64_DTPREL16_HIGHEST:
case elfcpp::R_PPC64_DTPREL16_HIGHESTA:
if (size != 64)
// R_PPC_EMB_NADDR32, R_PPC_EMB_NADDR16, R_PPC_EMB_NADDR16_LO
// R_PPC_EMB_NADDR16_HI, R_PPC_EMB_NADDR16_HA, R_PPC_EMB_SDAI16
break;
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_POWERPC_DTPREL16_HA:
case elfcpp::R_POWERPC_DTPREL:
case elfcpp::R_PPC64_DTPREL16_HIGH:
case elfcpp::R_PPC64_DTPREL16_HIGHA:
// tls symbol values are relative to tls_segment()->vaddr()
value -= dtp_offset;
break;
case elfcpp::R_PPC64_ADDR64_LOCAL:
if (gsym != NULL)
value += object->ppc64_local_entry_offset(gsym);
else
value += object->ppc64_local_entry_offset(r_sym);
break;
default:
break;
}
Insn branch_bit = 0;
switch (r_type)
{
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
branch_bit = 1 << 21;
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
{
Insn* iview = reinterpret_cast<Insn*>(view);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= ~(1 << 21);
insn |= branch_bit;
if (this->is_isa_v2)
{
// Set 'a' bit. This is 0b00010 in BO field for branch
// on CR(BI) insns (BO == 001at or 011at), and 0b01000
// for branch on CTR insns (BO == 1a00t or 1a01t).
if ((insn & (0x14 << 21)) == (0x04 << 21))
insn |= 0x02 << 21;
else if ((insn & (0x14 << 21)) == (0x10 << 21))
insn |= 0x08 << 21;
else
break;
}
else
{
// Invert 'y' bit if not the default.
if (static_cast<Signed_address>(value) < 0)
insn ^= 1 << 21;
}
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
}
break;
default:
break;
}
if (size == 64)
{
// Multi-instruction sequences that access the TOC can be
// optimized, eg. addis ra,r2,0; addi rb,ra,x;
// to nop; addi rb,r2,x;
switch (r_type)
{
default:
break;
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
case elfcpp::R_POWERPC_GOT_TPREL16_HA:
case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
case elfcpp::R_POWERPC_GOT16_HA:
case elfcpp::R_PPC64_TOC16_HA:
if (parameters->options().toc_optimize())
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
if ((insn & ((0x3f << 26) | 0x1f << 16))
!= ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("toc optimization is not supported "
"for %#08x instruction"), insn);
else if (value + 0x8000 < 0x10000)
{
elfcpp::Swap<32, big_endian>::writeval(iview, nop);
return true;
}
}
break;
case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
case elfcpp::R_POWERPC_GOT_TPREL16_LO:
case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
case elfcpp::R_POWERPC_GOT16_LO:
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())
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
if (!ok_lo_toc_insn(insn))
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("toc optimization is not supported "
"for %#08x instruction"), insn);
else if (value + 0x8000 < 0x10000)
{
if ((insn & (0x3f << 26)) == 12u << 26 /* addic */)
{
// Transform addic to addi when we change reg.
insn &= ~((0x3f << 26) | (0x1f << 16));
insn |= (14u << 26) | (2 << 16);
}
else
{
insn &= ~(0x1f << 16);
insn |= 2 << 16;
}
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
}
}
break;
}
}
typename Reloc::Overflow_check overflow = Reloc::CHECK_NONE;
elfcpp::Shdr<size, big_endian> shdr(relinfo->data_shdr);
switch (r_type)
{
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_UADDR32:
if (size == 64)
overflow = Reloc::CHECK_BITFIELD;
break;
case elfcpp::R_POWERPC_REL32:
if (size == 64)
overflow = Reloc::CHECK_SIGNED;
break;
case elfcpp::R_POWERPC_UADDR16:
overflow = Reloc::CHECK_BITFIELD;
break;
case elfcpp::R_POWERPC_ADDR16:
// We really should have three separate relocations,
// one for 16-bit data, one for insns with 16-bit signed fields,
// and one for insns with 16-bit unsigned fields.
overflow = Reloc::CHECK_BITFIELD;
if ((shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0)
overflow = Reloc::CHECK_LOW_INSN;
break;
case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_POWERPC_ADDR16_HA:
case elfcpp::R_POWERPC_GOT16_HI:
case elfcpp::R_POWERPC_GOT16_HA:
case elfcpp::R_POWERPC_PLT16_HI:
case elfcpp::R_POWERPC_PLT16_HA:
case elfcpp::R_POWERPC_SECTOFF_HI:
case elfcpp::R_POWERPC_SECTOFF_HA:
case elfcpp::R_PPC64_TOC16_HI:
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_PPC64_PLTGOT16_HI:
case elfcpp::R_PPC64_PLTGOT16_HA:
case elfcpp::R_POWERPC_TPREL16_HI:
case elfcpp::R_POWERPC_TPREL16_HA:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_POWERPC_DTPREL16_HA:
case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
case elfcpp::R_POWERPC_GOT_TPREL16_HI:
case elfcpp::R_POWERPC_GOT_TPREL16_HA:
case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
if (size != 32)
overflow = Reloc::CHECK_HIGH_INSN;
break;
case elfcpp::R_POWERPC_REL16:
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_POWERPC_GOT16:
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_TPREL16:
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16:
overflow = Reloc::CHECK_LOW_INSN;
break;
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
case elfcpp::R_PPC64_ADDR16_DS:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_PPC64_TPREL16_DS:
case elfcpp::R_PPC64_DTPREL16_DS:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_GOT16_DS:
case elfcpp::R_PPC64_SECTOFF_DS:
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
overflow = Reloc::CHECK_SIGNED;
break;
}
if (overflow == Reloc::CHECK_LOW_INSN
|| overflow == Reloc::CHECK_HIGH_INSN)
{
Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
if ((insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
overflow = Reloc::CHECK_BITFIELD;
else if (overflow == Reloc::CHECK_LOW_INSN
? ((insn & (0x3f << 26)) == 28u << 26 /* andi */
|| (insn & (0x3f << 26)) == 24u << 26 /* ori */
|| (insn & (0x3f << 26)) == 26u << 26 /* xori */)
: ((insn & (0x3f << 26)) == 29u << 26 /* andis */
|| (insn & (0x3f << 26)) == 25u << 26 /* oris */
|| (insn & (0x3f << 26)) == 27u << 26 /* xoris */))
overflow = Reloc::CHECK_UNSIGNED;
else
overflow = Reloc::CHECK_SIGNED;
}
typename Powerpc_relocate_functions<size, big_endian>::Status status
= Powerpc_relocate_functions<size, big_endian>::STATUS_OK;
switch (r_type)
{
case elfcpp::R_POWERPC_NONE:
case elfcpp::R_POWERPC_TLS:
case elfcpp::R_POWERPC_GNU_VTINHERIT:
case elfcpp::R_POWERPC_GNU_VTENTRY:
break;
case elfcpp::R_PPC64_ADDR64:
case elfcpp::R_PPC64_REL64:
case elfcpp::R_PPC64_TOC:
case elfcpp::R_PPC64_ADDR64_LOCAL:
Reloc::addr64(view, value);
break;
case elfcpp::R_POWERPC_TPREL:
case elfcpp::R_POWERPC_DTPREL:
if (size == 64)
Reloc::addr64(view, value);
else
status = Reloc::addr32(view, value, overflow);
break;
case elfcpp::R_PPC64_UADDR64:
Reloc::addr64_u(view, value);
break;
case elfcpp::R_POWERPC_ADDR32:
status = Reloc::addr32(view, value, overflow);
break;
case elfcpp::R_POWERPC_REL32:
case elfcpp::R_POWERPC_UADDR32:
status = Reloc::addr32_u(view, value, overflow);
break;
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
status = Reloc::addr24(view, value, overflow);
break;
case elfcpp::R_POWERPC_GOT_DTPREL16:
case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GOT_TPREL16_LO:
if (size == 64)
{
// On ppc64 these are all ds form
status = Reloc::addr16_ds(view, value, overflow);
break;
}
case elfcpp::R_POWERPC_ADDR16:
case elfcpp::R_POWERPC_REL16:
case elfcpp::R_PPC64_TOC16:
case elfcpp::R_POWERPC_GOT16:
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_TPREL16:
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_GOT_TLSGD16:
case elfcpp::R_POWERPC_GOT_TLSLD16:
case elfcpp::R_POWERPC_ADDR16_LO:
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_POWERPC_GOT16_LO:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_TPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
status = Reloc::addr16(view, value, overflow);
break;
case elfcpp::R_POWERPC_UADDR16:
status = Reloc::addr16_u(view, value, overflow);
break;
case elfcpp::R_PPC64_ADDR16_HIGH:
case elfcpp::R_PPC64_TPREL16_HIGH:
case elfcpp::R_PPC64_DTPREL16_HIGH:
if (size == 32)
// R_PPC_EMB_MRKREF, R_PPC_EMB_RELST_LO, R_PPC_EMB_RELST_HA
goto unsupp;
case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_PPC64_TOC16_HI:
case elfcpp::R_POWERPC_GOT16_HI:
case elfcpp::R_POWERPC_SECTOFF_HI:
case elfcpp::R_POWERPC_TPREL16_HI:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
case elfcpp::R_POWERPC_GOT_TPREL16_HI:
case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
Reloc::addr16_hi(view, value);
break;
case elfcpp::R_PPC64_ADDR16_HIGHA:
case elfcpp::R_PPC64_TPREL16_HIGHA:
case elfcpp::R_PPC64_DTPREL16_HIGHA:
if (size == 32)
// R_PPC_EMB_RELSEC16, R_PPC_EMB_RELST_HI, R_PPC_EMB_BIT_FLD
goto unsupp;
case elfcpp::R_POWERPC_ADDR16_HA:
case elfcpp::R_POWERPC_REL16_HA:
case elfcpp::R_PPC64_TOC16_HA:
case elfcpp::R_POWERPC_GOT16_HA:
case elfcpp::R_POWERPC_SECTOFF_HA:
case elfcpp::R_POWERPC_TPREL16_HA:
case elfcpp::R_POWERPC_DTPREL16_HA:
case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
case elfcpp::R_POWERPC_GOT_TPREL16_HA:
case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
Reloc::addr16_ha(view, value);
break;
case elfcpp::R_PPC64_DTPREL16_HIGHER:
if (size == 32)
// R_PPC_EMB_NADDR16_LO
goto unsupp;
case elfcpp::R_PPC64_ADDR16_HIGHER:
case elfcpp::R_PPC64_TPREL16_HIGHER:
Reloc::addr16_hi2(view, value);
break;
case elfcpp::R_PPC64_DTPREL16_HIGHERA:
if (size == 32)
// R_PPC_EMB_NADDR16_HI
goto unsupp;
case elfcpp::R_PPC64_ADDR16_HIGHERA:
case elfcpp::R_PPC64_TPREL16_HIGHERA:
Reloc::addr16_ha2(view, value);
break;
case elfcpp::R_PPC64_DTPREL16_HIGHEST:
if (size == 32)
// R_PPC_EMB_NADDR16_HA
goto unsupp;
case elfcpp::R_PPC64_ADDR16_HIGHEST:
case elfcpp::R_PPC64_TPREL16_HIGHEST:
Reloc::addr16_hi3(view, value);
break;
case elfcpp::R_PPC64_DTPREL16_HIGHESTA:
if (size == 32)
// R_PPC_EMB_SDAI16
goto unsupp;
case elfcpp::R_PPC64_ADDR16_HIGHESTA:
case elfcpp::R_PPC64_TPREL16_HIGHESTA:
Reloc::addr16_ha3(view, value);
break;
case elfcpp::R_PPC64_DTPREL16_DS:
case elfcpp::R_PPC64_DTPREL16_LO_DS:
if (size == 32)
// R_PPC_EMB_NADDR32, R_PPC_EMB_NADDR16
goto unsupp;
case elfcpp::R_PPC64_TPREL16_DS:
case elfcpp::R_PPC64_TPREL16_LO_DS:
if (size == 32)
// R_PPC_TLSGD, R_PPC_TLSLD
break;
case elfcpp::R_PPC64_ADDR16_DS:
case elfcpp::R_PPC64_ADDR16_LO_DS:
case elfcpp::R_PPC64_TOC16_DS:
case elfcpp::R_PPC64_TOC16_LO_DS:
case elfcpp::R_PPC64_GOT16_DS:
case elfcpp::R_PPC64_GOT16_LO_DS:
case elfcpp::R_PPC64_SECTOFF_DS:
case elfcpp::R_PPC64_SECTOFF_LO_DS:
status = Reloc::addr16_ds(view, value, overflow);
break;
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
status = Reloc::addr14(view, value, overflow);
break;
case elfcpp::R_POWERPC_COPY:
case elfcpp::R_POWERPC_GLOB_DAT:
case elfcpp::R_POWERPC_JMP_SLOT:
case elfcpp::R_POWERPC_RELATIVE:
case elfcpp::R_POWERPC_DTPMOD:
case elfcpp::R_PPC64_JMP_IREL:
case elfcpp::R_POWERPC_IRELATIVE:
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unexpected reloc %u in object file"),
r_type);
break;
case elfcpp::R_PPC_EMB_SDA21:
if (size == 32)
goto unsupp;
else
{
// R_PPC64_TOCSAVE. For the time being this can be ignored.
}
break;
case elfcpp::R_PPC_EMB_SDA2I16:
case elfcpp::R_PPC_EMB_SDA2REL:
if (size == 32)
goto unsupp;
// R_PPC64_TLSGD, R_PPC64_TLSLD
break;
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_PLTREL64:
case elfcpp::R_PPC64_PLTGOT16:
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:
case elfcpp::R_PPC_TOC16:
default:
unsupp:
gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
_("unsupported reloc %u"),
r_type);
break;
}
if (status != Powerpc_relocate_functions<size, big_endian>::STATUS_OK
&& (has_stub_value
|| !(gsym != NULL
&& gsym->is_undefined()
&& is_branch_reloc(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"));
}
return true;
}
// Relocate section data.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::relocate_section(
const Relocate_info<size, big_endian>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
unsigned char* view,
Address address,
section_size_type view_size,
const Reloc_symbol_changes* reloc_symbol_changes)
{
typedef Target_powerpc<size, big_endian> Powerpc;
typedef typename Target_powerpc<size, big_endian>::Relocate Powerpc_relocate;
typedef typename Target_powerpc<size, big_endian>::Relocate_comdat_behavior
Powerpc_comdat_behavior;
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::relocate_section<size, big_endian, Powerpc, elfcpp::SHT_RELA,
Powerpc_relocate, Powerpc_comdat_behavior>(
relinfo,
this,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
view,
address,
view_size,
reloc_symbol_changes);
}
class Powerpc_scan_relocatable_reloc
{
public:
// Return the strategy to use for a local symbol which is not a
// section symbol, given the relocation type.
inline Relocatable_relocs::Reloc_strategy
local_non_section_strategy(unsigned int r_type, Relobj*, unsigned int r_sym)
{
if (r_type == 0 && r_sym == 0)
return Relocatable_relocs::RELOC_DISCARD;
return Relocatable_relocs::RELOC_COPY;
}
// Return the strategy to use for a local symbol which is a section
// symbol, given the relocation type.
inline Relocatable_relocs::Reloc_strategy
local_section_strategy(unsigned int, Relobj*)
{
return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA;
}
// Return the strategy to use for a global symbol, given the
// relocation type, the object, and the symbol index.
inline Relocatable_relocs::Reloc_strategy
global_strategy(unsigned int r_type, Relobj*, unsigned int)
{
if (r_type == elfcpp::R_PPC_PLTREL24)
return Relocatable_relocs::RELOC_SPECIAL;
return Relocatable_relocs::RELOC_COPY;
}
};
// Scan the relocs during a relocatable link.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::scan_relocatable_relocs(
Symbol_table* symtab,
Layout* layout,
Sized_relobj_file<size, big_endian>* object,
unsigned int data_shndx,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
bool needs_special_offset_handling,
size_t local_symbol_count,
const unsigned char* plocal_symbols,
Relocatable_relocs* rr)
{
gold_assert(sh_type == elfcpp::SHT_RELA);
gold::scan_relocatable_relocs<size, big_endian, elfcpp::SHT_RELA,
Powerpc_scan_relocatable_reloc>(
symtab,
layout,
object,
data_shndx,
prelocs,
reloc_count,
output_section,
needs_special_offset_handling,
local_symbol_count,
plocal_symbols,
rr);
}
// Emit relocations for a section.
// This is a modified version of the function by the same name in
// target-reloc.h. Using relocate_special_relocatable for
// R_PPC_PLTREL24 would require duplication of the entire body of the
// loop, so we may as well duplicate the whole thing.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::relocate_relocs(
const Relocate_info<size, big_endian>* relinfo,
unsigned int sh_type,
const unsigned char* prelocs,
size_t reloc_count,
Output_section* output_section,
typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section,
const Relocatable_relocs* rr,
unsigned char*,
Address view_address,
section_size_type,
unsigned char* reloc_view,
section_size_type reloc_view_size)
{
gold_assert(sh_type == elfcpp::SHT_RELA);
typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
Reltype;
typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc_write
Reltype_write;
const int reloc_size
= Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
Powerpc_relobj<size, big_endian>* const object
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
const unsigned int local_count = object->local_symbol_count();
unsigned int got2_shndx = object->got2_shndx();
Address got2_addend = 0;
if (got2_shndx != 0)
{
got2_addend = object->get_output_section_offset(got2_shndx);
gold_assert(got2_addend != invalid_address);
}
unsigned char* pwrite = reloc_view;
bool zap_next = false;
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
{
Relocatable_relocs::Reloc_strategy strategy = rr->strategy(i);
if (strategy == Relocatable_relocs::RELOC_DISCARD)
continue;
Reltype reloc(prelocs);
Reltype_write reloc_write(pwrite);
Address offset = reloc.get_r_offset();
typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
const unsigned int orig_r_sym = r_sym;
typename elfcpp::Elf_types<size>::Elf_Swxword addend
= reloc.get_r_addend();
const Symbol* gsym = NULL;
if (zap_next)
{
// We could arrange to discard these and other relocs for
// tls optimised sequences in the strategy methods, but for
// now do as BFD ld does.
r_type = elfcpp::R_POWERPC_NONE;
zap_next = false;
}
// Get the new symbol index.
if (r_sym < local_count)
{
switch (strategy)
{
case Relocatable_relocs::RELOC_COPY:
case Relocatable_relocs::RELOC_SPECIAL:
if (r_sym != 0)
{
r_sym = object->symtab_index(r_sym);
gold_assert(r_sym != -1U);
}
break;
case Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA:
{
// We are adjusting a section symbol. We need to find
// the symbol table index of the section symbol for
// the output section corresponding to input section
// in which this symbol is defined.
gold_assert(r_sym < local_count);
bool is_ordinary;
unsigned int shndx =
object->local_symbol_input_shndx(r_sym, &is_ordinary);
gold_assert(is_ordinary);
Output_section* os = object->output_section(shndx);
gold_assert(os != NULL);
gold_assert(os->needs_symtab_index());
r_sym = os->symtab_index();
}
break;
default:
gold_unreachable();
}
}
else
{
gsym = object->global_symbol(r_sym);
gold_assert(gsym != NULL);
if (gsym->is_forwarder())
gsym = relinfo->symtab->resolve_forwards(gsym);
gold_assert(gsym->has_symtab_index());
r_sym = gsym->symtab_index();
}
// Get the new offset--the location in the output section where
// this relocation should be applied.
if (static_cast<Address>(offset_in_output_section) != invalid_address)
offset += offset_in_output_section;
else
{
section_offset_type sot_offset =
convert_types<section_offset_type, Address>(offset);
section_offset_type new_sot_offset =
output_section->output_offset(object, relinfo->data_shndx,
sot_offset);
gold_assert(new_sot_offset != -1);
offset = new_sot_offset;
}
// 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())
{
offset += view_address;
if (static_cast<Address>(offset_in_output_section) != invalid_address)
offset -= offset_in_output_section;
}
// Handle the reloc addend based on the strategy.
if (strategy == Relocatable_relocs::RELOC_COPY)
;
else if (strategy == Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA)
{
const Symbol_value<size>* psymval = object->local_symbol(orig_r_sym);
addend = psymval->value(object, addend);
}
else if (strategy == Relocatable_relocs::RELOC_SPECIAL)
{
if (addend >= 32768)
addend += got2_addend;
}
else
gold_unreachable();
if (!parameters->options().relocatable())
{
if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_HI
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_HA)
{
// 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))
{
case tls::TLSOPT_TO_IE:
r_type += (elfcpp::R_POWERPC_GOT_TPREL16
- elfcpp::R_POWERPC_GOT_TLSGD16);
break;
case tls::TLSOPT_TO_LE:
if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
r_type = elfcpp::R_POWERPC_TPREL16_HA;
else
{
r_type = elfcpp::R_POWERPC_NONE;
offset -= 2 * big_endian;
}
break;
default:
break;
}
}
else if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_HI
|| r_type == elfcpp::R_POWERPC_GOT_TLSLD16_HA)
{
// First instruction of a local dynamic sequence,
// arg setup insn.
if (this->optimize_tls_ld() == 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_TPREL16_HA;
const Output_section* os = relinfo->layout->tls_segment()
->first_section();
gold_assert(os != NULL);
gold_assert(os->needs_symtab_index());
r_sym = os->symtab_index();
addend = dtp_offset;
}
else
{
r_type = elfcpp::R_POWERPC_NONE;
offset -= 2 * big_endian;
}
}
}
else if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_HI
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_HA)
{
// First instruction of initial exec sequence.
const 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
|| r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO)
r_type = elfcpp::R_POWERPC_TPREL16_HA;
else
{
r_type = elfcpp::R_POWERPC_NONE;
offset -= 2 * big_endian;
}
}
}
else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
|| (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
{
// 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))
{
case tls::TLSOPT_TO_IE:
r_type = elfcpp::R_POWERPC_NONE;
zap_next = true;
break;
case tls::TLSOPT_TO_LE:
r_type = elfcpp::R_POWERPC_TPREL16_LO;
offset += 2 * big_endian;
zap_next = true;
break;
default:
break;
}
}
else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
|| (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
{
// Second instruction of a local dynamic sequence,
// the __tls_get_addr call
if (this->optimize_tls_ld() == tls::TLSOPT_TO_LE)
{
const Output_section* os = relinfo->layout->tls_segment()
->first_section();
gold_assert(os != NULL);
gold_assert(os->needs_symtab_index());
r_sym = os->symtab_index();
addend = dtp_offset;
r_type = elfcpp::R_POWERPC_TPREL16_LO;
offset += 2 * big_endian;
zap_next = 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();
if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
{
r_type = elfcpp::R_POWERPC_TPREL16_LO;
offset += 2 * big_endian;
}
}
}
reloc_write.put_r_offset(offset);
reloc_write.put_r_info(elfcpp::elf_r_info<size>(r_sym, r_type));
reloc_write.put_r_addend(addend);
pwrite += reloc_size;
}
gold_assert(static_cast<section_size_type>(pwrite - reloc_view)
== reloc_view_size);
}
// Return the value to use for a dynamic symbol which requires special
// treatment. This is how we support equality comparisons of function
// pointers across shared library boundaries, as described in the
// processor specific ABI supplement.
template<int size, bool big_endian>
uint64_t
Target_powerpc<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
{
if (size == 32)
{
gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
for (typename Stub_tables::const_iterator p = this->stub_tables_.begin();
p != this->stub_tables_.end();
++p)
{
Address off = (*p)->find_plt_call_entry(gsym);
if (off != invalid_address)
return (*p)->stub_address() + off;
}
}
else if (this->abiversion() >= 2)
{
unsigned int off = this->glink_section()->find_global_entry(gsym);
if (off != (unsigned int)-1)
return this->glink_section()->global_entry_address() + off;
}
gold_unreachable();
}
// Return the PLT address to use for a local symbol.
template<int size, bool big_endian>
uint64_t
Target_powerpc<size, big_endian>::do_plt_address_for_local(
const Relobj* object,
unsigned int symndx) const
{
if (size == 32)
{
const Sized_relobj<size, big_endian>* relobj
= static_cast<const Sized_relobj<size, big_endian>*>(object);
for (typename Stub_tables::const_iterator p = this->stub_tables_.begin();
p != this->stub_tables_.end();
++p)
{
Address off = (*p)->find_plt_call_entry(relobj->sized_relobj(),
symndx);
if (off != invalid_address)
return (*p)->stub_address() + off;
}
}
gold_unreachable();
}
// Return the PLT address to use for a global symbol.
template<int size, bool big_endian>
uint64_t
Target_powerpc<size, big_endian>::do_plt_address_for_global(
const Symbol* gsym) const
{
if (size == 32)
{
for (typename Stub_tables::const_iterator p = this->stub_tables_.begin();
p != this->stub_tables_.end();
++p)
{
Address off = (*p)->find_plt_call_entry(gsym);
if (off != invalid_address)
return (*p)->stub_address() + off;
}
}
else if (this->abiversion() >= 2)
{
unsigned int off = this->glink_section()->find_global_entry(gsym);
if (off != (unsigned int)-1)
return this->glink_section()->global_entry_address() + off;
}
gold_unreachable();
}
// Return the offset to use for the GOT_INDX'th got entry which is
// for a local tls symbol specified by OBJECT, SYMNDX.
template<int size, bool big_endian>
int64_t
Target_powerpc<size, big_endian>::do_tls_offset_for_local(
const Relobj* object,
unsigned int symndx,
unsigned int got_indx) 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;
got_type = Got_type(got_type + 1))
if (ppc_object->local_has_got_offset(symndx, got_type))
{
unsigned int off = ppc_object->local_got_offset(symndx, got_type);
if (got_type == GOT_TYPE_TLSGD)
off += size / 8;
if (off == got_indx * (size / 8))
{
if (got_type == GOT_TYPE_TPREL)
return -tp_offset;
else
return -dtp_offset;
}
}
}
gold_unreachable();
}
// Return the offset to use for the GOT_INDX'th got entry which is
// for global tls symbol GSYM.
template<int size, bool big_endian>
int64_t
Target_powerpc<size, big_endian>::do_tls_offset_for_global(
Symbol* gsym,
unsigned int got_indx) const
{
if (gsym->type() == elfcpp::STT_TLS)
{
for (Got_type got_type = GOT_TYPE_TLSGD;
got_type <= GOT_TYPE_TPREL;
got_type = Got_type(got_type + 1))
if (gsym->has_got_offset(got_type))
{
unsigned int off = gsym->got_offset(got_type);
if (got_type == GOT_TYPE_TLSGD)
off += size / 8;
if (off == got_indx * (size / 8))
{
if (got_type == GOT_TYPE_TPREL)
return -tp_offset;
else
return -dtp_offset;
}
}
}
gold_unreachable();
}
// The selector for powerpc object files.
template<int size, bool big_endian>
class Target_selector_powerpc : public Target_selector
{
public:
Target_selector_powerpc()
: Target_selector(size == 64 ? elfcpp::EM_PPC64 : elfcpp::EM_PPC,
size, big_endian,
(size == 64
? (big_endian ? "elf64-powerpc" : "elf64-powerpcle")
: (big_endian ? "elf32-powerpc" : "elf32-powerpcle")),
(size == 64
? (big_endian ? "elf64ppc" : "elf64lppc")
: (big_endian ? "elf32ppc" : "elf32lppc")))
{ }
virtual Target*
do_instantiate_target()
{ return new Target_powerpc<size, big_endian>(); }
};
Target_selector_powerpc<32, true> target_selector_ppc32;
Target_selector_powerpc<32, false> target_selector_ppc32le;
Target_selector_powerpc<64, true> target_selector_ppc64;
Target_selector_powerpc<64, false> target_selector_ppc64le;
// 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>
const typename Stub_table<size, big_endian>::Address
Stub_table<size, big_endian>::invalid_address;
template<int size, bool big_endian>
const typename Target_powerpc<size, big_endian>::Address
Target_powerpc<size, big_endian>::invalid_address;
} // End anonymous namespace.