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;
// 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)
{
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)
+ stub_group_size_(0), savres_section_(0)
{
}
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,
+ 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>*,
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;
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
this->object_->section_name(this->shndx_).c_str());
return true;
}
+ bool save_res = (size == 64
+ && gsym != NULL
+ && gsym->source() == Symbol::IN_OUTPUT_DATA
+ && gsym->output_data() == target->savres_section());
return stub_table->add_long_branch_entry(this->object_,
- this->r_type_, from, to);
+ this->r_type_,
+ from, to, save_res);
}
}
return true;
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_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 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 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_11 = 0x3d600000;
static const uint32_t lis_12 = 0x3d800000;
static const uint32_t lvx_0_12_0 = 0x7c0c00ce;
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)
+ branch_size_(0), last_branch_size_(0), eh_frame_added_(false),
+ need_save_res_(false)
{
this->set_output_section(output_section);
// Add a long branch stub.
bool
add_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
- unsigned int, Address, Address);
+ unsigned int, Address, Address, bool);
Address
find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
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;
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
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 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_;
+ // Set if this stub group needs a copy of out-of-line register
+ // save/restore functions.
+ bool need_save_res_;
};
// Add a plt call stub, if we do not already have one for this
const Powerpc_relobj<size, big_endian>* object,
unsigned int r_type,
Address from,
- Address to)
+ 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.
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;
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
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);
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
{
}
}
+// 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,
+ 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,
+ 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>
{
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);
}
// 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)
{