// powerpc.cc -- powerpc target support for gold.
-// Copyright (C) 2008-2014 Free Software Foundation, Inc.
+// Copyright (C) 2008-2016 Free Software Foundation, Inc.
template<int size, bool big_endian>
class Stub_table;
+template<int size, bool big_endian>
+class Output_data_save_res;
+
+template<int size, bool big_endian>
+class Target_powerpc;
+
+struct Stub_table_owner
+{
+ Stub_table_owner()
+ : output_section(NULL), owner(NULL)
+ { }
+
+ Output_section* output_section;
+ const Output_section::Input_section* owner;
+};
+
inline bool
is_branch_reloc(unsigned int r_type);
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_(),
+ opd_ent_(), access_from_map_(), has14_(), stub_table_index_(),
e_flags_(ehdr.get_e_flags()), st_other_()
{
this->set_abiversion(0);
// Add a reference from SRC_OBJ, SRC_INDX to this object's .opd
// section at DST_OFF.
void
- add_reference(Object* src_obj,
+ add_reference(Relobj* src_obj,
unsigned int src_indx,
typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
{
if (this->opd_ent_[i].gc_mark)
{
unsigned int shndx = this->opd_ent_[i].shndx;
- symtab->gc()->worklist().push(Section_id(this, shndx));
+ symtab->gc()->worklist().push_back(Section_id(this, shndx));
}
}
{ return shndx < this->has14_.size() && this->has14_[shndx]; }
void
- set_stub_table(unsigned int shndx, Stub_table<size, big_endian>* stub_table)
+ set_stub_table(unsigned int shndx, unsigned int stub_index)
{
- if (shndx >= this->stub_table_.size())
- this->stub_table_.resize(shndx + 1);
- this->stub_table_[shndx] = stub_table;
+ if (shndx >= this->stub_table_index_.size())
+ this->stub_table_index_.resize(shndx + 1, -1);
+ this->stub_table_index_[shndx] = stub_index;
}
Stub_table<size, big_endian>*
stub_table(unsigned int shndx)
{
- if (shndx < this->stub_table_.size())
- return this->stub_table_[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];
+ if (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; }
std::vector<bool> has14_;
// The stub table to use for a given input section.
- std::vector<Stub_table<size, big_endian>*> stub_table_;
+ std::vector<unsigned int> stub_table_index_;
// Header e_flags
elfcpp::Elf_Word e_flags_;
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)
+ plt_thread_safe_(false), relax_failed_(false), relax_fail_count_(0),
+ stub_group_size_(0), savres_section_(0)
{
}
ppc_object->set_has_14bit_branch(data_shndx);
}
- Stub_table<size, big_endian>*
- new_stub_table();
-
void
do_define_standard_symbols(Symbol_table*, Layout*);
do_can_check_for_function_pointers() const
{ return true; }
+ // Adjust -fsplit-stack code which calls non-split-stack code.
+ void
+ do_calls_non_split(Relobj* object, unsigned int shndx,
+ section_offset_type fnoffset, section_size_type fnsize,
+ const unsigned char* prelocs, size_t reloc_count,
+ unsigned char* view, section_size_type view_size,
+ std::string* from, std::string* to) const;
+
// Relocate a section.
void
relocate_section(const Relocate_info<size, big_endian>*,
const unsigned char* plocal_symbols,
Relocatable_relocs*);
+ // Scan the relocs for --emit-relocs.
+ void
+ emit_relocs_scan(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_syms,
+ Relocatable_relocs* rr);
+
// Emit relocations for a section.
void
relocate_relocs(const Relocate_info<size, big_endian>*,
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,
return 24;
}
+ Output_data_save_res<size, big_endian>*
+ savres_section() const
+ {
+ return this->savres_section_;
+ }
+
// Add any special sections for this symbol to the gc work list.
// For powerpc64, this adds the code section of a function
// descriptor.
// section of a function descriptor.
void
do_gc_add_reference(Symbol_table* symtab,
- Object* src_obj,
+ Relobj* src_obj,
unsigned int src_shndx,
- Object* dst_obj,
+ Relobj* dst_obj,
unsigned int dst_shndx,
Address dst_off) const;
inline bool
local_reloc_may_be_function_pointer(Symbol_table* , Layout* ,
Target_powerpc* ,
- Sized_relobj_file<size, big_endian>* ,
+ Sized_relobj_file<size, big_endian>* relobj,
unsigned int ,
Output_section* ,
const elfcpp::Rela<size, big_endian>& ,
// may be folded and we'll still keep function addresses distinct.
// That means no reloc is of concern here.
if (size == 64)
- return false;
- // For 32-bit, conservatively assume anything but calls to
+ {
+ 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>* ,
+ Sized_relobj_file<size, big_endian>* relobj,
unsigned int ,
Output_section* ,
const elfcpp::Rela<size, big_endian>& ,
{
// As above.
if (size == 64)
- return false;
+ {
+ 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);
}
bool issued_non_pic_error_;
};
- Address
- symval_for_branch(const Symbol_table* symtab, Address value,
+ bool
+ symval_for_branch(const Symbol_table* symtab,
const Sized_symbol<size>* gsym,
Powerpc_relobj<size, big_endian>* object,
- unsigned int *dest_shndx);
+ Address *value, unsigned int *dest_shndx);
// The class which implements relocation.
class Relocate : protected 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,
+ relocate(const Relocate_info<size, big_endian>*, unsigned int,
+ Target_powerpc*, Output_section*, size_t, const unsigned char*,
+ const Sized_symbol<size>*, const Symbol_value<size>*,
+ unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr,
section_size_type);
};
}
};
- // 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.
unsigned int shndx, Output_section* output_section,
Symbol* sym, const elfcpp::Rela<size, big_endian>& reloc)
{
+ unsigned int r_type = elfcpp::elf_r_type<size>(reloc.get_r_info());
this->copy_relocs_.copy_reloc(symtab, layout,
symtab->get_sized_symbol<size>(sym),
object, shndx, output_section,
- reloc, this->rela_dyn_section(layout));
+ r_type, reloc.get_r_offset(),
+ reloc.get_r_addend(),
+ this->rela_dyn_section(layout));
}
// Look over all the input sections, deciding where to place stubs.
void
- group_sections(Layout*, const Task*);
+ group_sections(Layout*, const Task*, bool);
// Sort output sections by address.
struct Sort_sections
{ }
// If this branch needs a plt call stub, or a long branch stub, make one.
- void
+ bool
make_stub(Stub_table<size, big_endian>*,
Stub_table<size, big_endian>*,
Symbol_table*) const;
Branches branch_info_;
bool plt_thread_safe_;
+
+ bool relax_failed_;
+ int relax_fail_count_;
+ int32_t stub_group_size_;
+
+ Output_data_save_res<size, big_endian> *savres_section_;
};
template<>
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
template<>
0, // large_common_section_flags
NULL, // attributes_section
NULL, // attributes_vendor
- "_start" // entry_symbol_name
+ "_start", // entry_symbol_name
+ 32, // hash_entry_size
};
inline bool
private:
typedef Powerpc_relocate_functions<size, big_endian> This;
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ typedef typename elfcpp::Elf_types<size>::Elf_Swxword SignedAddress;
template<int valsize>
static inline bool
}
// Do a simple RELA relocation
- template<int valsize>
+ template<int fieldsize, int valsize>
static inline Status
rela(unsigned char* view, Address value, Overflow_check overflow)
{
- typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
+ typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
- elfcpp::Swap<valsize, big_endian>::writeval(wv, value);
+ elfcpp::Swap<fieldsize, big_endian>::writeval(wv, value);
return overflowed<valsize>(value, overflow);
}
- template<int valsize>
+ template<int fieldsize, int valsize>
static inline Status
rela(unsigned char* view,
unsigned int right_shift,
- typename elfcpp::Valtype_base<valsize>::Valtype dst_mask,
+ typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask,
Address value,
Overflow_check overflow)
{
- typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
+ typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
Valtype* wv = reinterpret_cast<Valtype*>(view);
- Valtype val = elfcpp::Swap<valsize, big_endian>::readval(wv);
+ Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(wv);
Valtype reloc = value >> right_shift;
val &= ~dst_mask;
reloc &= dst_mask;
- elfcpp::Swap<valsize, big_endian>::writeval(wv, val | reloc);
+ elfcpp::Swap<fieldsize, big_endian>::writeval(wv, val | reloc);
return overflowed<valsize>(value >> right_shift, overflow);
}
// Do a simple RELA relocation, unaligned.
- template<int valsize>
+ template<int fieldsize, int valsize>
static inline Status
rela_ua(unsigned char* view, Address value, Overflow_check overflow)
{
- elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view, value);
+ elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, value);
return overflowed<valsize>(value, overflow);
}
- template<int valsize>
+ template<int fieldsize, int valsize>
static inline Status
rela_ua(unsigned char* view,
unsigned int right_shift,
- typename elfcpp::Valtype_base<valsize>::Valtype dst_mask,
+ typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask,
Address value,
Overflow_check overflow)
{
- typedef typename elfcpp::Swap_unaligned<valsize, big_endian>::Valtype
+ typedef typename elfcpp::Swap_unaligned<fieldsize, big_endian>::Valtype
Valtype;
- Valtype val = elfcpp::Swap<valsize, big_endian>::readval(view);
+ Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(view);
Valtype reloc = value >> right_shift;
val &= ~dst_mask;
reloc &= dst_mask;
- elfcpp::Swap_unaligned<valsize, big_endian>::writeval(view, val | reloc);
+ elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, val | reloc);
return overflowed<valsize>(value >> right_shift, overflow);
}
// R_PPC64_ADDR64: (Symbol + Addend)
static inline void
addr64(unsigned char* view, Address value)
- { This::template rela<64>(view, value, CHECK_NONE); }
+ { 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>(view, value, CHECK_NONE); }
+ { 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>(view, value, 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>(view, value, 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>(view, 0, 0x03fffffc, value, 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>(view, value, 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>(view, value, 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>(view, 0, 0xfffc, value, overflow);
- if (overflow != CHECK_NONE && (value & 3) != 0)
+ Status stat = This::template rela<16,16>(view, 0, 0xfffc, value, overflow);
+ if ((value & 3) != 0)
+ stat = STATUS_OVERFLOW;
+ return stat;
+ }
+
+ // R_POWERPC_ADDR16_DQ: (Symbol + Addend) & 0xfff0
+ static inline Status
+ addr16_dq(unsigned char* view, Address value, Overflow_check overflow)
+ {
+ Status stat = This::template rela<16,16>(view, 0, 0xfff0, value, overflow);
+ if ((value & 15) != 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>(view, 16, 0xffff, value, CHECK_NONE); }
+ { This::template rela<16,16>(view, 16, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HA: ((Symbol + Addend + 0x8000) >> 16) & 0xffff
static inline void
// R_POWERPC_ADDR16_HIGHER: ((Symbol + Addend) >> 32) & 0xffff
static inline void
addr16_hi2(unsigned char* view, Address value)
- { This::template rela<16>(view, 32, 0xffff, value, CHECK_NONE); }
+ { This::template rela<16,16>(view, 32, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHERA: ((Symbol + Addend + 0x8000) >> 32) & 0xffff
static inline void
// R_POWERPC_ADDR16_HIGHEST: ((Symbol + Addend) >> 48) & 0xffff
static inline void
addr16_hi3(unsigned char* view, Address value)
- { This::template rela<16>(view, 48, 0xffff, value, CHECK_NONE); }
+ { This::template rela<16,16>(view, 48, 0xffff, value, CHECK_NONE); }
// R_POWERPC_ADDR16_HIGHESTA: ((Symbol + Addend + 0x8000) >> 48) & 0xffff
static inline void
static inline Status
addr14(unsigned char* view, Address value, Overflow_check overflow)
{
- Status stat = This::template rela<32>(view, 0, 0xfffc, value, 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;
}
+
+ // R_POWERPC_REL16DX_HA
+ static inline Status
+ addr16dx_ha(unsigned char *view, Address value, Overflow_check overflow)
+ {
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+ Valtype* wv = reinterpret_cast<Valtype*>(view);
+ Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+ value += 0x8000;
+ value = static_cast<SignedAddress>(value) >> 16;
+ val |= (value & 0xffc1) | ((value & 0x3e) << 15);
+ elfcpp::Swap<32, big_endian>::writeval(wv, val);
+ return overflowed<16>(value, overflow);
+ }
};
// Set ABI version for input and output.
void
Powerpc_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
{
- Sized_relobj_file<size, big_endian>::do_read_symbols(sd);
+ this->base_read_symbols(sd);
if (size == 64)
{
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
}
}
-// Call Sized_dynobj::do_read_symbols to read the symbols then
+// 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)
{
- Sized_dynobj<size, big_endian>::do_read_symbols(sd);
+ this->base_read_symbols(sd);
if (size == 64)
{
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
symtab_(symtab), layout_(layout),
header_ent_cnt_(size == 32 ? 3 : 1),
header_index_(size == 32 ? 0x2000 : 0)
- { }
+ {
+ if (size == 64)
+ this->set_addralign(256);
+ }
// Override all the Output_data_got methods we use so as to first call
// reserve_ent().
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
+ // is passed a negative value, we restrict stubs to be always after
// the stubbed branches.
- Stub_control(int32_t size)
- : state_(NO_GROUP), stub_group_size_(abs(size)),
- stub14_group_size_(abs(size)),
- stubs_always_before_branch_(size < 0), suppress_size_errors_(false),
- group_end_addr_(0), owner_(NULL), output_section_(NULL)
+ Stub_control(int32_t size, bool no_size_errors, bool multi_os)
+ : stub_group_size_(abs(size)), stubs_always_after_branch_(size < 0),
+ suppress_size_errors_(no_size_errors), multi_os_(multi_os),
+ state_(NO_GROUP), group_size_(0), group_start_addr_(0),
+ owner_(NULL), output_section_(NULL)
{
- if (stub_group_size_ == 1)
- {
- // Default values.
- if (stubs_always_before_branch_)
- {
- stub_group_size_ = 0x1e00000;
- stub14_group_size_ = 0x7800;
- }
- else
- {
- stub_group_size_ = 0x1c00000;
- stub14_group_size_ = 0x7000;
- }
- suppress_size_errors_ = true;
- }
}
// Return true iff input section can be handled by current stub
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
{
+ // Initial state.
NO_GROUP,
+ // Adding group sections before the stubs.
FINDING_STUB_SECTION,
+ // Adding group sections after the stubs.
HAS_STUB_SECTION
} State;
- State state_;
uint32_t stub_group_size_;
- uint32_t stub14_group_size_;
- bool stubs_always_before_branch_;
+ bool stubs_always_after_branch_;
bool suppress_size_errors_;
- uint64_t group_end_addr_;
+ // True if a stub group can serve multiple output sections.
+ bool multi_os_;
+ State state_;
+ // Current max size of group. Starts at stub_group_size_ but is
+ // reduced to stub_group_size_/1024 on seeing a section with
+ // external conditional branches.
+ uint32_t group_size_;
+ uint64_t group_start_addr_;
+ // owner_ and output_section_ specify the section to which stubs are
+ // attached. The stubs are placed at the end of this section.
const Output_section::Input_section* owner_;
Output_section* output_section_;
};
// Return true iff input section can be handled by current stub
-// group.
+// group. Sections are presented to this function in order,
+// so the first section is the head of the 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();
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;
+ uint32_t group_size = this->stub_group_size_;
+ if (has14)
+ this->group_size_ = group_size = group_size >> 10;
- if (toobig && !this->suppress_size_errors_)
+ if (this_size > group_size && !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;
- }
+ gold_debug(DEBUG_TARGET, "maybe add%s %s:%s size=%#llx total=%#llx",
+ has14 ? " 14bit" : "",
+ i->relobj()->name().c_str(),
+ i->relobj()->section_name(i->shndx()).c_str(),
+ (long long) this_size,
+ (this->state_ == NO_GROUP
+ ? this_size
+ : (long long) end_addr - this->group_start_addr_));
if (this->state_ == NO_GROUP)
{
+ // Only here on very first use of Stub_control
+ this->owner_ = i;
+ this->output_section_ = o;
this->state_ = FINDING_STUB_SECTION;
- this->group_end_addr_ = end_addr;
+ this->group_size_ = group_size;
+ this->group_start_addr_ = start_addr;
+ return true;
}
- else if (this->group_end_addr_ - start_addr < group_size)
+ else if (!this->multi_os_ && this->output_section_ != o)
;
- // Adding this section would make the group larger than GROUP_SIZE.
- else if (this->state_ == FINDING_STUB_SECTION
- && !this->stubs_always_before_branch_
- && !toobig)
+ else if (this->state_ == HAS_STUB_SECTION)
{
- // 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;
+ // Can we add this section, which is after the stubs, to the
+ // group?
+ if (end_addr - this->group_start_addr_ <= this->group_size_)
+ return true;
}
- else
+ else if (this->state_ == FINDING_STUB_SECTION)
{
- this->state_ = NO_GROUP;
- return false;
+ if ((whole_sec && this->output_section_ == o)
+ || end_addr - this->group_start_addr_ <= this->group_size_)
+ {
+ // Stubs are added at the end of "owner_".
+ this->owner_ = i;
+ this->output_section_ = o;
+ return true;
+ }
+ // The group before the stubs has reached maximum size.
+ // Now see about adding sections after the stubs to the
+ // group. If the current section has a 14-bit branch and
+ // the group before the stubs exceeds group_size_ (because
+ // they didn't have 14-bit branches), don't add sections
+ // after the stubs: The size of stubs for such a large
+ // group may exceed the reach of a 14-bit branch.
+ if (!this->stubs_always_after_branch_
+ && this_size <= this->group_size_
+ && start_addr - this->group_start_addr_ <= this->group_size_)
+ {
+ gold_debug(DEBUG_TARGET, "adding after stubs");
+ this->state_ = HAS_STUB_SECTION;
+ this->group_start_addr_ = start_addr;
+ return true;
+ }
}
- return true;
+ else
+ gold_unreachable();
+
+ gold_debug(DEBUG_TARGET,
+ !this->multi_os_ && this->output_section_ != o
+ ? "nope, new output section\n"
+ : "nope, didn't fit\n");
+
+ // The section fails to fit in the current group. Set up a few
+ // things for the next group. owner_ and output_section_ will be
+ // set later after we've retrieved those values for the current
+ // group.
+ this->state_ = FINDING_STUB_SECTION;
+ this->group_size_ = group_size;
+ this->group_start_addr_ = start_addr;
+ return false;
}
// 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*)
+ const Task*,
+ bool no_size_errors)
{
- Stub_control stub_control(parameters->options().stub_group_size());
+ Stub_control stub_control(this->stub_group_size_, no_size_errors,
+ parameters->options().stub_group_multi());
// Group input sections and insert stub table
- Stub_table<size, big_endian>* stub_table = NULL;
+ Stub_table_owner* table_owner = NULL;
+ std::vector<Stub_table_owner*> tables;
Layout::Section_list section_list;
layout->get_executable_sections(§ion_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();
+ for (Layout::Section_list::iterator o = section_list.begin();
+ o != section_list.end();
++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();
+ for (Input_section_list::const_iterator i
+ = (*o)->input_sections().begin();
+ i != (*o)->input_sections().end();
++i)
{
- if (i->is_input_section())
+ 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))
{
- stub_table->init(stub_control.owner(),
- stub_control.output_section());
- stub_table = NULL;
+ 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);
}
- if (stub_table == NULL)
- stub_table = this->new_stub_table();
- ppcobj->set_stub_table(i->shndx(), stub_table);
+ ppcobj->set_stub_table(i->shndx(), tables.size() - 1);
}
}
}
- if (stub_table != NULL)
+ if (table_owner != NULL)
{
- const Output_section::Input_section* i = stub_control.owner();
- if (!i->is_input_section())
- {
- // 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.
- gold_assert(this->stub_tables_.size() >= 2);
- this->stub_tables_.pop_back();
- delete stub_table;
- Powerpc_relobj<size, big_endian>* ppcobj = static_cast
- <Powerpc_relobj<size, big_endian>*>(i->relobj());
- ppcobj->set_stub_table(i->shndx(), this->stub_tables_.back());
- }
+ table_owner->output_section = stub_control.output_section();
+ table_owner->owner = stub_control.owner();;
+ }
+ 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
- stub_table->init(i, stub_control.output_section());
+ 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>
-void
+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,
Target_powerpc<size, big_endian>* target =
static_cast<Target_powerpc<size, big_endian>*>(
parameters->sized_target<size, big_endian>());
+ bool ok = true;
+
if (gsym != NULL
? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
: this->object_->local_has_plt_offset(this->r_sym_))
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)
- stub_table->add_plt_call_entry(this->object_, gsym,
- this->r_type_, this->addend_);
+ ok = stub_table->add_plt_call_entry(from,
+ this->object_, gsym,
+ this->r_type_, this->addend_);
else
- stub_table->add_plt_call_entry(this->object_, this->r_sym_,
- this->r_type_, this->addend_);
+ ok = stub_table->add_plt_call_entry(from,
+ this->object_, this->r_sym_,
+ this->r_type_, this->addend_);
}
}
else
{
- unsigned long max_branch_offset;
- if (this->r_type_ == elfcpp::R_POWERPC_REL14
- || this->r_type_ == elfcpp::R_POWERPC_REL14_BRTAKEN
- || this->r_type_ == elfcpp::R_POWERPC_REL14_BRNTAKEN)
- max_branch_offset = 1 << 15;
- else if (this->r_type_ == elfcpp::R_POWERPC_REL24
- || this->r_type_ == elfcpp::R_PPC_PLTREL24
- || this->r_type_ == elfcpp::R_PPC_LOCAL24PC)
- max_branch_offset = 1 << 25;
- else
- return;
+ 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()
Object* symobj = gsym->object();
if (symobj->is_dynamic()
|| symobj->pluginobj() != NULL)
- return;
+ return true;
bool is_ordinary;
unsigned int shndx = gsym->shndx(&is_ordinary);
if (shndx == elfcpp::SHN_UNDEF)
- return;
+ return true;
}
break;
case Symbol::IS_UNDEFINED:
- return;
+ 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;
+ return true;
if (size == 64)
to += this->object_->ppc64_local_entry_offset(gsym);
}
const Symbol_value<size>* psymval
= this->object_->local_symbol(this->r_sym_);
Symbol_value<size> symval;
+ if (psymval->is_section_symbol())
+ symval.set_is_section_symbol();
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;
+ return true;
to = symval.value(this->object_, 0);
if (size == 64)
to += this->object_->ppc64_local_entry_offset(this->r_sym_);
}
- to += this->addend_;
+ 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;
- to = target->symval_for_branch(symtab, to, gsym,
- this->object_, &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)
" no long branch stub for you"),
this->object_->name().c_str(),
this->object_->section_name(this->shndx_).c_str());
- return;
+ return true;
}
- stub_table->add_long_branch_entry(this->object_, to);
+ bool save_res = (size == 64
+ && gsym != NULL
+ && gsym->source() == Symbol::IN_OUTPUT_DATA
+ && gsym->output_data() == target->savres_section());
+ ok = stub_table->add_long_branch_entry(this->object_,
+ this->r_type_,
+ from, to, save_res);
}
}
+ if (!ok)
+ gold_debug(DEBUG_TARGET,
+ "branch at %s:%s+%#lx\n"
+ "can't reach stub attached to %s:%s",
+ this->object_->name().c_str(),
+ this->object_->section_name(this->shndx_).c_str(),
+ (unsigned long) this->offset_,
+ stub_table->relobj()->name().c_str(),
+ stub_table->relobj()->section_name(stub_table->shndx()).c_str());
+
+ return ok;
}
// Relaxation hook. This is where we do stub generation.
/* 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())
}
}
this->plt_thread_safe_ = thread_safe;
- this->group_sections(layout, task);
+ }
+
+ 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 %#x"),
+ program_name, this->stub_group_size_);
+ this->group_sections(layout, task, true);
}
// We need address of stub tables valid for make_stub.
p != this->stub_tables_.end();
++p)
{
- (*p)->clear_stubs();
+ (*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
b != this->branch_info_.end();
b++)
{
- b->make_stub(one_stub_table, ifunc_stub_table, symtab);
+ 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?
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);
+ Address stub_table_size = stub_table->set_address_and_size(os, off);
+ off += stub_table_size;
+ // After a few iterations, set current stub table size
+ // as min size threshold, so later stub tables can only
+ // grow in size.
+ if (pass >= 4)
+ stub_table->set_min_size_threshold(stub_table_size);
}
else
off += i->data_size();
static const uint32_t add_0_11_11 = 0x7c0b5a14;
static const uint32_t add_2_2_11 = 0x7c425a14;
+static const uint32_t add_2_2_12 = 0x7c426214;
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 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_1 = 0x39810000;
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_3_2 = 0x3c620000;
-static const uint32_t addis_3_13 = 0x3c6d0000;
+static const uint32_t addis_2_12 = 0x3c4c0000;
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_1 = 0x3d810000;
+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 cmpld_7_12_0 = 0x7fac0040;
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_2_1 = 0xe8410000;
static const uint32_t ld_2_2 = 0xe8420000;
static const uint32_t ld_2_11 = 0xe84b0000;
+static const uint32_t ld_2_12 = 0xe84c0000;
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 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_0 = 0x3c000000;
+static const uint32_t lis_2 = 0x3c400000;
static const uint32_t lis_11 = 0x3d600000;
static const uint32_t lis_12 = 0x3d800000;
static const uint32_t lvx_0_12_0 = 0x7c0c00ce;
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_relaxed_input_section(NULL, 0, 0),
+ 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_(0), plt_size_(0), last_plt_size_(0),
- branch_size_(0), last_branch_size_(0), eh_frame_added_(false)
- { }
+ orig_data_size_(owner->current_data_size()),
+ plt_size_(0), last_plt_size_(0),
+ branch_size_(0), last_branch_size_(0), min_size_threshold_(0),
+ eh_frame_added_(false), need_save_res_(false)
+ {
+ this->set_output_section(output_section);
- // Delayed Output_relaxed_input_section init.
- void
- init(const Output_section::Input_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.
- void
- add_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+ bool
+ add_plt_call_entry(Address,
+ const Sized_relobj_file<size, big_endian>*,
const Symbol*,
unsigned int,
Address);
- void
- add_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+ bool
+ add_plt_call_entry(Address,
+ const Sized_relobj_file<size, big_endian>*,
unsigned int,
unsigned int,
Address);
Address) const;
// Add a long branch stub.
- void
- add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address);
+ bool
+ add_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
+ unsigned int, Address, Address, bool);
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()
+ clear_stubs(bool all)
{
this->plt_call_stubs_.clear();
this->plt_size_ = 0;
this->long_branch_stubs_.clear();
this->branch_size_ = 0;
+ this->need_save_res_ = false;
+ if (all)
+ {
+ this->last_plt_size_ = 0;
+ this->last_branch_size_ = 0;
+ }
}
Address
Address start_off = off;
off += this->orig_data_size_;
Address my_size = this->plt_size_ + this->branch_size_;
+ if (this->need_save_res_)
+ my_size += this->targ_->savres_section()->data_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;
+ // Ensure new size is always larger than min size
+ // threshold. Alignment requirement is included in "my_size", so
+ // increase "my_size" does not invalidate alignment.
+ if (my_size < this->min_size_threshold_)
+ my_size = this->min_size_threshold_;
this->reset_address_and_file_offset();
this->set_current_data_size(my_size);
this->set_address_and_file_offset(os->address() + start_off,
plt_size() const
{ return this->plt_size_; }
+ void set_min_size_threshold(Address min_size)
+ { this->min_size_threshold_ = min_size; }
+
bool
size_update()
{
class Branch_stub_ent
{
public:
- Branch_stub_ent(const Powerpc_relobj<size, big_endian>* obj, Address to)
- : dest_(to), toc_base_off_(0)
+ Branch_stub_ent(const Powerpc_relobj<size, big_endian>* obj,
+ Address to, bool save_res)
+ : dest_(to), toc_base_off_(0), save_res_(save_res)
{
if (size == 64)
toc_base_off_ = obj->toc_base_offset();
Address dest_;
unsigned int toc_base_off_;
+ bool save_res_;
};
class Branch_stub_ent_hash
section_size_type orig_data_size_;
// size of stubs
section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_;
+ // Some rare cases cause (PR/20529) fluctuation in stub table
+ // size, which leads to an endless relax loop. This is to be fixed
+ // by, after the first few iterations, allowing only increase of
+ // stub table size. This variable sets the minimal possible size of
+ // a stub table, it is zero for the first few iterations, then
+ // increases monotonically.
+ Address min_size_threshold_;
// Whether .eh_frame info has been created for this stub section.
bool eh_frame_added_;
+ // Set if this stub group needs a copy of out-of-line register
+ // save/restore functions.
+ bool need_save_res_;
};
-// Make a new stub table, and record.
-
-template<int size, bool big_endian>
-Stub_table<size, big_endian>*
-Target_powerpc<size, big_endian>::new_stub_table()
-{
- Stub_table<size, big_endian>* stub_table
- = new Stub_table<size, big_endian>(this);
- this->stub_tables_.push_back(stub_table);
- return stub_table;
-}
-
-// Delayed stub table initialisation, because we create the stub table
-// before we know to which section it will be attached.
-
-template<int size, bool big_endian>
-void
-Stub_table<size, big_endian>::init(
- const Output_section::Input_section* owner,
- Output_section* output_section)
-{
- this->set_relobj(owner->relobj());
- this->set_shndx(owner->shndx());
- this->set_addralign(this->relobj()->section_addralign(this->shndx()));
- this->set_output_section(output_section);
- this->orig_data_size_ = owner->current_data_size();
-
- 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, if we do not already have one for this
// sym/object/addend combo.
template<int size, bool big_endian>
-void
+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,
= 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>
-void
+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,
= 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.
// destination.
template<int size, bool big_endian>
-void
+bool
Stub_table<size, big_endian>::add_long_branch_entry(
const Powerpc_relobj<size, big_endian>* object,
- Address to)
+ unsigned int r_type,
+ Address from,
+ Address to,
+ bool save_res)
{
- Branch_stub_ent ent(object, to);
+ Branch_stub_ent ent(object, to, save_res);
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);
+ if (save_res)
+ this->need_save_res_ = true;
+ else
+ {
+ 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.
+// Find long branch stub offset.
template<int size, bool big_endian>
typename Stub_table<size, big_endian>::Address
const Powerpc_relobj<size, big_endian>* object,
Address to) const
{
- Branch_stub_ent ent(object, to);
+ Branch_stub_ent ent(object, to, false);
typename Branch_stub_entries::const_iterator p
= this->long_branch_stubs_.find(ent);
- return p == this->long_branch_stubs_.end() ? invalid_address : p->second;
+ if (p == this->long_branch_stubs_.end())
+ return invalid_address;
+ if (p->first.save_res_)
+ return to - this->targ_->savres_section()->address() + this->branch_size_;
+ return p->second;
}
// A class to handle .glink.
{
write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
p += 4;
- write_insn<big_endian>(p, addis_11_2 + ha(off));
- p += 4;
- write_insn<big_endian>(p, ld_12_11 + l(off));
- 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))
{
bs != this->long_branch_stubs_.end();
++bs)
{
+ if (bs->first.save_res_)
+ continue;
p = oview + this->plt_size_ + bs->second;
Address loc = this->stub_address() + this->plt_size_ + bs->second;
Address delta = bs->first.dest_ - loc;
}
else
{
- write_insn<big_endian>(p, addis_11_2 + ha(brltoff)), p += 4;
- write_insn<big_endian>(p, ld_12_11 + l(brltoff)), p += 4;
+ 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);
bs != this->long_branch_stubs_.end();
++bs)
{
+ if (bs->first.save_res_)
+ continue;
p = oview + this->plt_size_ + bs->second;
Address loc = this->stub_address() + this->plt_size_ + bs->second;
Address delta = bs->first.dest_ - loc;
}
}
}
+ if (this->need_save_res_)
+ {
+ p = oview + this->plt_size_ + this->branch_size_;
+ memcpy (p, this->targ_->savres_section()->contents(),
+ this->targ_->savres_section()->data_size());
+ }
}
// Write out .glink.
}
else
{
- write_insn<big_endian>(p, lis_0_0 + hi(indx)), p += 4;
+ write_insn<big_endian>(p, lis_0 + hi(indx)), p += 4;
write_insn<big_endian>(p, ori_0_0_0 + l(indx)), p += 4;
}
}
public:
Output_data_save_res(Symbol_table* symtab);
+ const unsigned char*
+ contents() const
+ {
+ return contents_;
+ }
+
protected:
// Write to a map file.
void
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;
+ ref = Symbol::RELATIVE_REF;
break;
case elfcpp::R_POWERPC_GOT_TPREL16:
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_PPC64_TOCSAVE:
case elfcpp::R_POWERPC_TLS:
+ case elfcpp::R_PPC64_ENTRY:
break;
case elfcpp::R_PPC64_TOC:
{
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 r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
: elfcpp::R_POWERPC_RELATIVE);
rela_dyn->add_local_relative(object, r_sym, dynrel,
reloc.get_r_offset(),
reloc.get_r_addend(), false);
}
- else
+ else if (lsym.get_st_type() != elfcpp::STT_SECTION)
{
check_non_pic(object, r_type);
- unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
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_REL16_LO:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
+ case elfcpp::R_POWERPC_REL16DX_HA:
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_SECTOFF_HI:
if (!parameters->options().output_is_position_independent())
{
- if ((size == 32 && is_ifunc)
- || (size == 64 && target->abiversion() >= 2))
+ 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);
case elfcpp::R_POWERPC_GNU_VTENTRY:
case elfcpp::R_PPC_LOCAL24PC:
case elfcpp::R_POWERPC_TLS:
+ case elfcpp::R_PPC64_ENTRY:
break;
case elfcpp::R_PPC64_TOC:
ppc_object->set_opd_discard(reloc.get_r_offset());
break;
}
- // Fall thru
+ // Fall through.
case elfcpp::R_PPC64_UADDR64:
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_UADDR32:
|| gsym->is_preemptible())))
target->make_plt_entry(symtab, layout, gsym);
}
- // Fall thru
+ // Fall through.
case elfcpp::R_PPC64_REL64:
case elfcpp::R_POWERPC_REL32:
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
+ case elfcpp::R_POWERPC_REL16DX_HA:
case elfcpp::R_POWERPC_SECTOFF:
case elfcpp::R_POWERPC_SECTOFF_LO:
case elfcpp::R_POWERPC_SECTOFF_HI:
got = target->got_section(symtab, layout);
if (gsym->final_value_is_known())
{
- if ((size == 32 && is_ifunc)
- || (size == 64 && target->abiversion() >= 2))
+ if (is_ifunc
+ && (size == 32 || target->abiversion() >= 2))
got->add_global_plt(gsym, GOT_TYPE_STANDARD);
else
got->add_global(gsym, GOT_TYPE_STANDARD);
const unsigned char* plocal_symbols)
{
typedef Target_powerpc<size, big_endian> Powerpc;
- typedef typename Target_powerpc<size, big_endian>::Scan Scan;
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+
Powerpc_relobj<size, big_endian>* ppc_object
= static_cast<Powerpc_relobj<size, big_endian>*>(object);
if (size == 64)
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;
+ Relobj* src_obj = s->first;
unsigned int src_indx = s->second;
symtab->gc()->add_reference(src_obj, src_indx,
ppc_object, dst_indx);
return;
}
- gold::gc_process_relocs<size, big_endian, Powerpc, elfcpp::SHT_RELA, Scan,
- typename Target_powerpc::Relocatable_size_for_reloc>(
+ gold::gc_process_relocs<size, big_endian, Powerpc, Scan, Classify_reloc>(
symtab,
layout,
this,
void
Target_powerpc<size, big_endian>::do_gc_add_reference(
Symbol_table* symtab,
- Object* src_obj,
+ Relobj* src_obj,
unsigned int src_shndx,
- Object* dst_obj,
+ Relobj* dst_obj,
unsigned int dst_shndx,
Address dst_off) const
{
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));
+ symtab->gc()->worklist().push_back(Section_id(ppc_object,
+ dst_indx));
}
else
ppc_object->add_gc_mark(dst_off);
}
}
+// FNOFFSET in section SHNDX in OBJECT is the start of a function
+// compiled with -fsplit-stack. The function calls non-split-stack
+// code. Change the function to ensure it has enough stack space to
+// call some random function.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_calls_non_split(
+ Relobj* object,
+ unsigned int shndx,
+ section_offset_type fnoffset,
+ section_size_type fnsize,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ unsigned char* view,
+ section_size_type view_size,
+ std::string* from,
+ std::string* to) const
+{
+ // 32-bit not supported.
+ if (size == 32)
+ {
+ // warn
+ Target::do_calls_non_split(object, shndx, fnoffset, fnsize,
+ prelocs, reloc_count, view, view_size,
+ from, to);
+ return;
+ }
+
+ // The function always starts with
+ // ld %r0,-0x7000-64(%r13) # tcbhead_t.__private_ss
+ // addis %r12,%r1,-allocate@ha
+ // addi %r12,%r12,-allocate@l
+ // cmpld %r12,%r0
+ // but note that the addis or addi may be replaced with a nop
+
+ unsigned char *entry = view + fnoffset;
+ uint32_t insn = elfcpp::Swap<32, big_endian>::readval(entry);
+
+ if ((insn & 0xffff0000) == addis_2_12)
+ {
+ /* Skip ELFv2 global entry code. */
+ entry += 8;
+ insn = elfcpp::Swap<32, big_endian>::readval(entry);
+ }
+
+ unsigned char *pinsn = entry;
+ bool ok = false;
+ const uint32_t ld_private_ss = 0xe80d8fc0;
+ if (insn == ld_private_ss)
+ {
+ int32_t allocate = 0;
+ while (1)
+ {
+ pinsn += 4;
+ insn = elfcpp::Swap<32, big_endian>::readval(pinsn);
+ if ((insn & 0xffff0000) == addis_12_1)
+ allocate += (insn & 0xffff) << 16;
+ else if ((insn & 0xffff0000) == addi_12_1
+ || (insn & 0xffff0000) == addi_12_12)
+ allocate += ((insn & 0xffff) ^ 0x8000) - 0x8000;
+ else if (insn != nop)
+ break;
+ }
+ if (insn == cmpld_7_12_0 && pinsn == entry + 12)
+ {
+ int extra = parameters->options().split_stack_adjust_size();
+ allocate -= extra;
+ if (allocate >= 0 || extra < 0)
+ {
+ object->error(_("split-stack stack size overflow at "
+ "section %u offset %0zx"),
+ shndx, static_cast<size_t>(fnoffset));
+ return;
+ }
+ pinsn = entry + 4;
+ insn = addis_12_1 | (((allocate + 0x8000) >> 16) & 0xffff);
+ if (insn != addis_12_1)
+ {
+ elfcpp::Swap<32, big_endian>::writeval(pinsn, insn);
+ pinsn += 4;
+ insn = addi_12_12 | (allocate & 0xffff);
+ if (insn != addi_12_12)
+ {
+ elfcpp::Swap<32, big_endian>::writeval(pinsn, insn);
+ pinsn += 4;
+ }
+ }
+ else
+ {
+ insn = addi_12_1 | (allocate & 0xffff);
+ elfcpp::Swap<32, big_endian>::writeval(pinsn, insn);
+ pinsn += 4;
+ }
+ if (pinsn != entry + 12)
+ elfcpp::Swap<32, big_endian>::writeval(pinsn, nop);
+
+ ok = true;
+ }
+ }
+
+ if (!ok)
+ {
+ if (!object->has_no_split_stack())
+ object->error(_("failed to match split-stack sequence at "
+ "section %u offset %0zx"),
+ shndx, static_cast<size_t>(fnoffset));
+ }
+}
+
// Scan relocations for a section.
template<int size, bool big_endian>
const unsigned char* plocal_symbols)
{
typedef Target_powerpc<size, big_endian> Powerpc;
- typedef typename Target_powerpc<size, big_endian>::Scan Scan;
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
if (sh_type == elfcpp::SHT_REL)
{
return;
}
- gold::scan_relocs<size, big_endian, Powerpc, elfcpp::SHT_RELA, Scan>(
+ gold::scan_relocs<size, big_endian, Powerpc, Scan, Classify_reloc>(
symtab,
layout,
this,
unsigned int shndx = sym->shndx(&is_ordinary);
if (shndx == symobj->opd_shndx()
&& symobj->get_opd_discard(sym->value()))
- sym->set_symtab_index(-1U);
+ {
+ sym->set_undefined();
+ sym->set_visibility(elfcpp::STV_DEFAULT);
+ sym->set_is_defined_in_discarded_section();
+ sym->set_symtab_index(-1U);
+ }
}
};
{
if (size == 64)
{
- Output_data_save_res<64, big_endian>* savres
- = new Output_data_save_res<64, big_endian>(symtab);
+ Output_data_save_res<size, big_endian>* savres
+ = new Output_data_save_res<size, big_endian>(symtab);
+ this->savres_section_ = savres;
layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
savres, ORDER_TEXT, false);
// Return the value to use for a branch relocation.
template<int size, bool big_endian>
-typename Target_powerpc<size, big_endian>::Address
+bool
Target_powerpc<size, big_endian>::symval_for_branch(
const Symbol_table* symtab,
- Address value,
const Sized_symbol<size>* gsym,
Powerpc_relobj<size, big_endian>* object,
+ Address *value,
unsigned int *dest_shndx)
{
if (size == 32 || this->abiversion() >= 2)
Powerpc_relobj<size, big_endian>* symobj = object;
if (gsym != NULL
&& gsym->source() != Symbol::FROM_OBJECT)
- return value;
+ 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 value;
+ return true;
Address opd_addr = symobj->get_output_section_offset(shndx);
if (opd_addr == invalid_address)
- return value;
+ return true;
opd_addr += symobj->output_section_address(shndx);
- if (value >= opd_addr && value < opd_addr + symobj->section_size(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);
+ *dest_shndx = symobj->get_opd_ent(*value - opd_addr, &sec_off);
if (symtab->is_section_folded(symobj, *dest_shndx))
{
Section_id folded
*dest_shndx = folded.second;
}
Address sec_addr = symobj->get_output_section_offset(*dest_shndx);
- gold_assert(sec_addr != invalid_address);
+ if (sec_addr == invalid_address)
+ return false;
+
sec_addr += symobj->output_section(*dest_shndx)->address();
- value = sec_addr + sec_off;
+ *value = sec_addr + sec_off;
}
- return value;
+ return true;
}
// Perform a relocation.
inline bool
Target_powerpc<size, big_endian>::Relocate::relocate(
const Relocate_info<size, big_endian>* relinfo,
+ unsigned int,
Target_powerpc* target,
Output_section* os,
size_t relnum,
- const elfcpp::Rela<size, big_endian>& rela,
- unsigned int r_type,
+ const unsigned char* preloc,
const Sized_symbol<size>* gsym,
const Symbol_value<size>* psymval,
unsigned char* view,
if (view == NULL)
return true;
+ const elfcpp::Rela<size, big_endian> rela(preloc);
+ unsigned int r_type = elfcpp::elf_r_type<size>(rela.get_r_info());
switch (this->maybe_skip_tls_get_addr_call(r_type, gsym))
{
case Track_tls::NOT_EXPECTED:
typedef Powerpc_relocate_functions<size, big_endian> Reloc;
typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
+ typedef typename Reloc_types<elfcpp::SHT_RELA,
+ size, big_endian>::Reloc Reltype;
+ // Offset from start of insn to d-field reloc.
+ const int d_offset = big_endian ? 2 : 0;
+
Powerpc_relobj<size, big_endian>* const object
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
Address value = 0;
- bool has_plt_value = false;
+ 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))
&& !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;
+ Address off = target->glink_section()->find_global_entry(gsym);
+ if (off != invalid_address)
+ {
+ value = target->glink_section()->global_entry_address() + off;
+ has_stub_value = true;
+ }
}
else
{
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;
+ if (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());
+ if (off != invalid_address)
+ {
+ value = stub_table->stub_address() + off;
+ has_stub_value = true;
+ }
+ }
}
- has_plt_value = true;
+ // We don't care too much about bogus debug references to
+ // non-local functions, but otherwise there had better be a plt
+ // call stub or global entry stub as appropriate.
+ gold_assert(has_stub_value || !(os->flags() & elfcpp::SHF_ALLOC));
}
if (r_type == elfcpp::R_POWERPC_GOT16
else if (gsym != NULL
&& (r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC_PLTREL24)
- && has_plt_value)
+ && has_stub_value)
{
if (size == 64)
{
if (target->abiversion() < 2)
{
Address addend = rela.get_r_addend();
- Address opdent = psymval->value(object, addend);
- code = target->symval_for_branch(relinfo->symtab,
- opdent, gsym, object,
- &dest_shndx);
+ code = psymval->value(object, addend);
+ target->symval_for_branch(relinfo->symtab, gsym, object,
+ &code, &dest_shndx);
}
bool is_ordinary;
if (dest_shndx == 0)
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* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= (1 << 26) - (1 << 16); // extract rt,ra from addi
if (size == 32)
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 = addis_3_13;
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn &= (1 << 26) - (1 << 21); // extract rt
if (size == 32)
- insn = addis_3_2;
+ 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* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = nop;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
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 = addis_3_13;
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+ insn &= (1 << 26) - (1 << 21); // extract rt
if (size == 32)
- insn = addis_3_2;
+ 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* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = nop;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
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* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
insn &= (1 << 26) - (1 << 21); // extract rt from ld
if (size == 32)
}
else
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = nop;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_NONE;
Insn insn = addi_3_3;
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += 2 * big_endian;
+ view += d_offset;
value = psymval->value(object, rela.get_r_addend());
}
this->skip_next_tls_get_addr_call();
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;
+ view += d_offset;
value = dtp_offset;
}
}
gold_assert(insn != 0);
elfcpp::Swap<32, big_endian>::writeval(iview, insn);
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- view += 2 * big_endian;
+ view += d_offset;
value = psymval->value(object, rela.get_r_addend());
}
}
- else if (!has_plt_value)
+ else if (!has_stub_value)
{
Address addend = 0;
- unsigned int dest_shndx;
- if (r_type != elfcpp::R_PPC_PLTREL24)
+ 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))
value += object->ppc64_local_entry_offset(r_sym);
}
else
- value = target->symval_for_branch(relinfo->symtab, value,
- gsym, object, &dest_shndx);
+ {
+ unsigned int dest_shndx;
+ target->symval_for_branch(relinfo->symtab, gsym, object,
+ &value, &dest_shndx);
+ }
}
- unsigned int max_branch_offset = 0;
- if (r_type == elfcpp::R_POWERPC_REL24
- || r_type == elfcpp::R_PPC_PLTREL24
- || r_type == elfcpp::R_PPC_LOCAL24PC)
- max_branch_offset = 1 << 25;
- else if (r_type == elfcpp::R_POWERPC_REL14
- || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
- || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
- max_branch_offset = 1 << 15;
+ Address max_branch_offset = max_branch_delta(r_type);
if (max_branch_offset != 0
&& value - address + max_branch_offset >= 2 * max_branch_offset)
{
{
Address off = stub_table->find_long_branch_entry(object, value);
if (off != invalid_address)
- value = (stub_table->stub_address() + stub_table->plt_size()
- + off);
+ {
+ value = (stub_table->stub_address() + stub_table->plt_size()
+ + off);
+ has_stub_value = true;
+ }
}
}
}
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_POWERPC_REL16_HA:
+ case elfcpp::R_POWERPC_REL16DX_HA:
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
if (size != 64)
// R_PPC_TLSGD, R_PPC_TLSLD, R_PPC_EMB_RELST_LO, R_PPC_EMB_RELST_HI
break;
+ // Fall through.
case elfcpp::R_POWERPC_TPREL16:
case elfcpp::R_POWERPC_TPREL16_LO:
case elfcpp::R_POWERPC_TPREL16_HI:
// 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;
+ // Fall through.
case elfcpp::R_POWERPC_DTPREL16:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_DTPREL16_HI:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
branch_bit = 1 << 21;
+ // Fall through.
case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
{
case elfcpp::R_PPC64_TOC16_HA:
if (parameters->options().toc_optimize())
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
if ((insn & ((0x3f << 26) | 0x1f << 16))
!= ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
case elfcpp::R_PPC64_TOC16_LO_DS:
if (parameters->options().toc_optimize())
{
- Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
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(),
}
}
break;
+
+ case elfcpp::R_PPC64_ENTRY:
+ value = (target->got_section()->output_section()->address()
+ + object->toc_base_offset());
+ if (value + 0x80008000 <= 0xffffffff
+ && !parameters->options().output_is_position_independent())
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+ if ((insn1 & ~0xfffc) == ld_2_12
+ && insn2 == add_2_2_12)
+ {
+ insn1 = lis_2 + ha(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+ insn2 = addi_2_2 + l(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+ return true;
+ }
+ }
+ else
+ {
+ value -= address;
+ if (value + 0x80008000 <= 0xffffffff)
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+ if ((insn1 & ~0xfffc) == ld_2_12
+ && insn2 == add_2_2_12)
+ {
+ insn1 = addis_2_12 + ha(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+ insn2 = addi_2_2 + l(value);
+ elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+ return true;
+ }
+ }
+ }
+ break;
+
+ case elfcpp::R_POWERPC_REL16_LO:
+ // If we are generating a non-PIC executable, edit
+ // 0: addis 2,12,.TOC.-0b@ha
+ // addi 2,2,.TOC.-0b@l
+ // used by ELFv2 global entry points to set up r2, to
+ // lis 2,.TOC.@ha
+ // addi 2,2,.TOC.@l
+ // if .TOC. is in range. */
+ if (value + address - 4 + 0x80008000 <= 0xffffffff
+ && relnum != 0
+ && preloc != NULL
+ && target->abiversion() >= 2
+ && !parameters->options().output_is_position_independent()
+ && rela.get_r_addend() == d_offset + 4
+ && gsym != NULL
+ && strcmp(gsym->name(), ".TOC.") == 0)
+ {
+ const int reloc_size
+ = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ Reltype prev_rela(preloc - reloc_size);
+ if ((prev_rela.get_r_info()
+ == elfcpp::elf_r_info<size>(r_sym,
+ elfcpp::R_POWERPC_REL16_HA))
+ && prev_rela.get_r_offset() + 4 == rela.get_r_offset()
+ && prev_rela.get_r_addend() + 4 == rela.get_r_addend())
+ {
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview - 1);
+ Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview);
+
+ if ((insn1 & 0xffff0000) == addis_2_12
+ && (insn2 & 0xffff0000) == addi_2_2)
+ {
+ insn1 = lis_2 + ha(value + address - 4);
+ elfcpp::Swap<32, big_endian>::writeval(iview - 1, insn1);
+ insn2 = addi_2_2 + l(value + address - 4);
+ elfcpp::Swap<32, big_endian>::writeval(iview, insn2);
+ if (relinfo->rr)
+ {
+ relinfo->rr->set_strategy(relnum - 1,
+ Relocatable_relocs::RELOC_SPECIAL);
+ relinfo->rr->set_strategy(relnum,
+ Relocatable_relocs::RELOC_SPECIAL);
+ }
+ return true;
+ }
+ }
+ }
+ break;
}
}
break;
case elfcpp::R_POWERPC_REL32:
+ case elfcpp::R_POWERPC_REL16DX_HA:
if (size == 64)
overflow = Reloc::CHECK_SIGNED;
break;
break;
}
+ Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+ Insn insn = 0;
+
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);
+ insn = elfcpp::Swap<32, big_endian>::readval(iview);
- overflow = Reloc::CHECK_SIGNED;
- 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)) == 10u << 26 /* cmpli */)
- : ((insn & (0x3f << 26)) == 29u << 26 /* andis */
- || (insn & (0x3f << 26)) == 25u << 26 /* oris */
- || (insn & (0x3f << 26)) == 27u << 26 /* xoris */))
+ 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;
}
+ bool maybe_dq_reloc = false;
typename Powerpc_relocate_functions<size, big_endian>::Status status
= Powerpc_relocate_functions<size, big_endian>::STATUS_OK;
switch (r_type)
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)
{
- status = Reloc::addr16_ds(view, value, overflow);
+ // On ppc64 these are all ds form
+ maybe_dq_reloc = true;
break;
}
+ // Fall through.
case elfcpp::R_POWERPC_ADDR16:
case elfcpp::R_POWERPC_REL16:
case elfcpp::R_PPC64_TOC16:
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_ADDR16_LO:
case elfcpp::R_POWERPC_REL16_LO:
case elfcpp::R_PPC64_TOC16_LO:
case elfcpp::R_POWERPC_DTPREL16_LO:
case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
- case elfcpp::R_POWERPC_GOT_TPREL16_LO:
- status = Reloc::addr16(view, value, overflow);
+ if (size == 64)
+ status = Reloc::addr16(view, value, overflow);
+ else
+ maybe_dq_reloc = true;
break;
case elfcpp::R_POWERPC_UADDR16:
if (size == 32)
// R_PPC_EMB_MRKREF, R_PPC_EMB_RELST_LO, R_PPC_EMB_RELST_HA
goto unsupp;
+ // Fall through.
case elfcpp::R_POWERPC_ADDR16_HI:
case elfcpp::R_POWERPC_REL16_HI:
case elfcpp::R_PPC64_TOC16_HI:
if (size == 32)
// R_PPC_EMB_RELSEC16, R_PPC_EMB_RELST_HI, R_PPC_EMB_BIT_FLD
goto unsupp;
+ // Fall through.
case elfcpp::R_POWERPC_ADDR16_HA:
case elfcpp::R_POWERPC_REL16_HA:
case elfcpp::R_PPC64_TOC16_HA:
Reloc::addr16_ha(view, value);
break;
+ case elfcpp::R_POWERPC_REL16DX_HA:
+ status = Reloc::addr16dx_ha(view, value, overflow);
+ break;
+
case elfcpp::R_PPC64_DTPREL16_HIGHER:
if (size == 32)
// R_PPC_EMB_NADDR16_LO
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_HIGHER:
case elfcpp::R_PPC64_TPREL16_HIGHER:
Reloc::addr16_hi2(view, value);
if (size == 32)
// R_PPC_EMB_NADDR16_HI
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_HIGHERA:
case elfcpp::R_PPC64_TPREL16_HIGHERA:
Reloc::addr16_ha2(view, value);
if (size == 32)
// R_PPC_EMB_NADDR16_HA
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_HIGHEST:
case elfcpp::R_PPC64_TPREL16_HIGHEST:
Reloc::addr16_hi3(view, value);
if (size == 32)
// R_PPC_EMB_SDAI16
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_HIGHESTA:
case elfcpp::R_PPC64_TPREL16_HIGHESTA:
Reloc::addr16_ha3(view, value);
if (size == 32)
// R_PPC_EMB_NADDR32, R_PPC_EMB_NADDR16
goto unsupp;
+ // Fall through.
case elfcpp::R_PPC64_TPREL16_DS:
case elfcpp::R_PPC64_TPREL16_LO_DS:
if (size == 32)
// R_PPC_TLSGD, R_PPC_TLSLD
break;
+ // Fall through.
case elfcpp::R_PPC64_ADDR16_DS:
case elfcpp::R_PPC64_ADDR16_LO_DS:
case elfcpp::R_PPC64_TOC16_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);
+ maybe_dq_reloc = true;
break;
case elfcpp::R_POWERPC_ADDR14:
r_type);
break;
}
- if (status != Powerpc_relocate_functions<size, big_endian>::STATUS_OK)
- gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
- _("relocation overflow"));
+
+ if (maybe_dq_reloc)
+ {
+ if (insn == 0)
+ insn = elfcpp::Swap<32, big_endian>::readval(iview);
+
+ if ((insn & (0x3f << 26)) == 56u << 26 /* lq */
+ || ((insn & (0x3f << 26)) == (61u << 26) /* lxv, stxv */
+ && (insn & 3) == 1))
+ status = Reloc::addr16_dq(view, value, overflow);
+ else if (size == 64
+ || (insn & (0x3f << 26)) == 58u << 26 /* ld,ldu,lwa */
+ || (insn & (0x3f << 26)) == 62u << 26 /* std,stdu,stq */
+ || (insn & (0x3f << 26)) == 57u << 26 /* lfdp */
+ || (insn & (0x3f << 26)) == 61u << 26 /* stfdp */)
+ status = Reloc::addr16_ds(view, value, overflow);
+ else
+ status = Reloc::addr16(view, value, overflow);
+ }
+
+ 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;
}
typedef typename Target_powerpc<size, big_endian>::Relocate Powerpc_relocate;
typedef typename Target_powerpc<size, big_endian>::Relocate_comdat_behavior
Powerpc_comdat_behavior;
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::relocate_section<size, big_endian, Powerpc, elfcpp::SHT_RELA,
- Powerpc_relocate, Powerpc_comdat_behavior>(
+ gold::relocate_section<size, big_endian, Powerpc, Powerpc_relocate,
+ Powerpc_comdat_behavior, Classify_reloc>(
relinfo,
this,
prelocs,
reloc_symbol_changes);
}
+template<int size, bool big_endian>
class Powerpc_scan_relocatable_reloc
{
public:
+ typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
+ Reltype;
+ static const int reloc_size =
+ Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ static const int sh_type = elfcpp::SHT_RELA;
+
+ // Return the symbol referred to by the relocation.
+ static inline unsigned int
+ get_r_sym(const Reltype* reloc)
+ { return elfcpp::elf_r_sym<size>(reloc->get_r_info()); }
+
+ // Return the type of the relocation.
+ static inline unsigned int
+ get_r_type(const Reltype* reloc)
+ { return elfcpp::elf_r_type<size>(reloc->get_r_info()); }
+
// Return the strategy to use for a local symbol which is not a
// section symbol, given the relocation type.
inline Relocatable_relocs::Reloc_strategy
const unsigned char* plocal_symbols,
Relocatable_relocs* rr)
{
+ typedef Powerpc_scan_relocatable_reloc<size, big_endian> Scan_strategy;
+
gold_assert(sh_type == elfcpp::SHT_RELA);
- gold::scan_relocatable_relocs<size, big_endian, elfcpp::SHT_RELA,
- Powerpc_scan_relocatable_reloc>(
+ gold::scan_relocatable_relocs<size, big_endian, Scan_strategy>(
symtab,
layout,
object,
rr);
}
+// Scan the relocs for --emit-relocs.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::emit_relocs_scan(
+ 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_syms,
+ Relocatable_relocs* rr)
+{
+ typedef gold::Default_classify_reloc<elfcpp::SHT_RELA, size, big_endian>
+ Classify_reloc;
+ typedef gold::Default_emit_relocs_strategy<Classify_reloc>
+ Emit_relocs_strategy;
+
+ gold_assert(sh_type == elfcpp::SHT_RELA);
+
+ gold::scan_relocatable_relocs<size, big_endian, Emit_relocs_strategy>(
+ symtab,
+ layout,
+ object,
+ data_shndx,
+ prelocs,
+ reloc_count,
+ output_section,
+ needs_special_offset_handling,
+ local_symbol_count,
+ plocal_syms,
+ 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
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,
Reltype_write;
const int reloc_size
= Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ // Offset from start of insn to d-field reloc.
+ const int d_offset = big_endian ? 2 : 0;
Powerpc_relobj<size, big_endian>* const object
= static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
bool zap_next = false;
for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
{
- Relocatable_relocs::Reloc_strategy strategy = rr->strategy(i);
+ Relocatable_relocs::Reloc_strategy strategy = relinfo->rr->strategy(i);
if (strategy == Relocatable_relocs::RELOC_DISCARD)
continue;
}
// Get the new symbol index.
+ Output_section* os = NULL;
if (r_sym < local_count)
{
switch (strategy)
unsigned int shndx =
object->local_symbol_input_shndx(r_sym, &is_ordinary);
gold_assert(is_ordinary);
- Output_section* os = object->output_section(shndx);
+ os = object->output_section(shndx);
gold_assert(os != NULL);
gold_assert(os->needs_symtab_index());
r_sym = os->symtab_index();
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);
+ gold_assert(os != NULL);
+ addend = psymval->value(object, addend) - os->address();
}
else if (strategy == Relocatable_relocs::RELOC_SPECIAL)
{
- if (addend >= 32768)
- addend += got2_addend;
+ if (size == 32)
+ {
+ if (addend >= 32768)
+ addend += got2_addend;
+ }
+ else if (r_type == elfcpp::R_POWERPC_REL16_HA)
+ {
+ r_type = elfcpp::R_POWERPC_ADDR16_HA;
+ addend -= d_offset;
+ }
+ else if (r_type == elfcpp::R_POWERPC_REL16_LO)
+ {
+ r_type = elfcpp::R_POWERPC_ADDR16_LO;
+ addend -= d_offset + 4;
+ }
}
else
gold_unreachable();
else
{
r_type = elfcpp::R_POWERPC_NONE;
- offset -= 2 * big_endian;
+ offset -= d_offset;
}
break;
default:
else
{
r_type = elfcpp::R_POWERPC_NONE;
- offset -= 2 * big_endian;
+ offset -= d_offset;
}
}
}
else
{
r_type = elfcpp::R_POWERPC_NONE;
- offset -= 2 * big_endian;
+ offset -= d_offset;
}
}
}
break;
case tls::TLSOPT_TO_LE:
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- offset += 2 * big_endian;
+ offset += d_offset;
zap_next = true;
break;
default:
r_sym = os->symtab_index();
addend = dtp_offset;
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- offset += 2 * big_endian;
+ offset += d_offset;
zap_next = true;
}
}
if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
{
r_type = elfcpp::R_POWERPC_TPREL16_LO;
- offset += 2 * big_endian;
+ offset += d_offset;
}
}
}
}
else if (this->abiversion() >= 2)
{
- unsigned int off = this->glink_section()->find_global_entry(gsym);
- if (off != (unsigned int)-1)
+ Address off = this->glink_section()->find_global_entry(gsym);
+ if (off != invalid_address)
return this->glink_section()->global_entry_address() + off;
}
gold_unreachable();
}
else if (this->abiversion() >= 2)
{
- unsigned int off = this->glink_section()->find_global_entry(gsym);
- if (off != (unsigned int)-1)
+ Address off = this->glink_section()->find_global_entry(gsym);
+ if (off != invalid_address)
return this->glink_section()->global_entry_address() + off;
}
gold_unreachable();