template<int size, bool big_endian>
class Stub_table;
+template<int size, bool big_endian>
+class Target_powerpc;
+
+struct Stub_table_owner
+{
+ Output_section* output_section;
+ const Output_section::Input_section* owner;
+};
+
inline bool
is_branch_reloc(unsigned int r_type);
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);
{ 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);
+ 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];
+ gold_assert(indx < target->stub_tables().size());
+ return target->stub_tables()[indx];
+ }
return NULL;
}
+ void
+ clear_stub_table()
+ {
+ this->stub_table_index_.clear();
+ }
+
int
abiversion() const
{ return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
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)
{
}
ppc_object->set_has_14bit_branch(data_shndx);
}
- Stub_table<size, big_endian>*
- new_stub_table();
-
void
do_define_standard_symbols(Symbol_table*, 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_;
};
template<>
// value of the parameter --stub-group-size. If --stub-group-size
// is passed a negative value, we restrict stubs to be always before
// the stubbed branches.
- Stub_control(int32_t size)
+ Stub_control(int32_t size, bool no_size_errors)
: state_(NO_GROUP), stub_group_size_(abs(size)),
stub14_group_size_(abs(size) >> 10),
- stubs_always_before_branch_(size < 0), suppress_size_errors_(false),
+ stubs_always_before_branch_(size < 0),
+ suppress_size_errors_(no_size_errors),
group_end_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
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);
// 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());
i != (*o)->input_sections().rend();
++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());
+ table_owner->output_section = stub_control.output_section();
+ table_owner->owner = stub_control.owner();
stub_control.set_output_and_owner(*o, &*i);
- stub_table = NULL;
+ 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())
+
+ if (tables.size() >= 2 && tables[tables.size() - 2]->owner == i)
{
// Corner case. A new stub group was made for the first
// section (last one looked at here) for some reason, but
// the first section is already being used as the owner for
// a stub table for following sections. Force it into that
// stub group.
- gold_assert(this->stub_tables_.size() >= 2);
- this->stub_tables_.pop_back();
- delete stub_table;
+ tables.pop_back();
+ delete table_owner;
Powerpc_relobj<size, big_endian>* ppcobj = static_cast
<Powerpc_relobj<size, big_endian>*>(i->relobj());
- ppcobj->set_stub_table(i->shndx(), this->stub_tables_.back());
+ ppcobj->set_stub_table(i->shndx(), tables.size() - 1);
+ }
+ else
+ {
+ table_owner->output_section = stub_control.output_section();
+ table_owner->owner = i;
}
+ }
+ for (typename std::vector<Stub_table_owner*>::iterator t = tables.begin();
+ t != tables.end();
+ ++t)
+ {
+ Stub_table<size, big_endian>* stub_table;
+
+ if ((*t)->owner->is_input_section())
+ stub_table = new Stub_table<size, big_endian>(this,
+ (*t)->output_section,
+ (*t)->owner);
+ else if ((*t)->owner->is_relaxed_input_section())
+ stub_table = static_cast<Stub_table<size, big_endian>*>(
+ (*t)->owner->relaxed_input_section());
else
- 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,
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_);
+ return 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_);
+ return 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;
+ unsigned long 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);
}
&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_);
" 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);
+ return stub_table->add_long_branch_entry(this->object_,
+ this->r_type_, from, to);
}
}
+ return true;
}
// Relaxation hook. This is where we do stub generation.
}
}
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 %d"),
+ 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?
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),
+ orig_data_size_(owner->current_data_size()),
+ plt_size_(0), last_plt_size_(0),
branch_size_(0), last_branch_size_(0), eh_frame_added_(false)
- { }
+ {
+ this->set_output_section(output_section);
- // 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);
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)
+ {
+ unsigned long 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;
+ if (all)
+ {
+ this->last_plt_size_ = 0;
+ this->last_branch_size_ = 0;
+ }
}
Address
bool eh_frame_added_;
};
-// 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,
+ unsigned int r_type,
+ Address from,
Address to)
{
Branch_stub_ent ent(object, to);
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.
value = target->symval_for_branch(relinfo->symtab, value,
gsym, object, &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;
+ unsigned long max_branch_offset = max_branch_delta(r_type);
if (max_branch_offset != 0
&& value - address + max_branch_offset >= 2 * max_branch_offset)
{