Retry powerpc gold stub grouping when groups prove too large
An unusually large number of stubs can result in the default section group size being too large; sections plus stubs exceed the range of a branch. Restarting the relaxation pass with a smaller group size can sometimes help. * powerpc.cc (struct Stub_table_owner): New. (Powerpc_relobj): Rename stub_table_ to stub_table_index_, an unsigned int vector. Update all references. (powerpc_relobj::set_stub_table): Take an unsigned int param rather than a Stub_table. Update callers. (Powerpc_relobj::clear_stub_table): New function. (Target_powerpc): Add relax_failed_, relax_fail_count_ and stub_group_size_ vars. (Target_powerpc::new_stub_table): Delete. (max_branch_delta): New function, extracted from.. (Target_powerpc::Relocate::relocate): ..here.. (Target_powerpc::Branch_info::make_stub): ..and here. Return status on whether stub created successfully. (Stub_control::Stub_control): Add "no_size_errors" param. Move default sizing to.. (Target_powerpc::do_relax): ..here. Init stub_group_size_ and reduce on relax failure. (Target_powerpc::group_sections): Add "no_size_errors" param. Use stub_group_size_. Set up group info in a temp vector, before building Stub_table vector. Account for input sections possibly already converted to relaxed sections. (Stub_table::init): Delete. Merge into.. (Stub_table::Stub_table): ..here. (Stub_table::can_reach_stub): New function. (Stub_table::add_plt_call_entry): Add "from" parameter and return true iff stub could be reached. (Stub_table::add_long_branch_entry): Similarly. Add "r_type" param too. (Stub_table::clear_stubs): Add "all" param.
This commit is contained in:
parent
aa4893954a
commit
a3e60ddb0b
2 changed files with 243 additions and 132 deletions
|
@ -1,3 +1,35 @@
|
|||
2014-11-26 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* powerpc.cc (struct Stub_table_owner): New.
|
||||
(Powerpc_relobj): Rename stub_table_ to stub_table_index_, an
|
||||
unsigned int vector. Update all references.
|
||||
(powerpc_relobj::set_stub_table): Take an unsigned int param
|
||||
rather than a Stub_table. Update callers.
|
||||
(Powerpc_relobj::clear_stub_table): New function.
|
||||
(Target_powerpc): Add relax_failed_, relax_fail_count_ and
|
||||
stub_group_size_ vars.
|
||||
(Target_powerpc::new_stub_table): Delete.
|
||||
(max_branch_delta): New function, extracted from..
|
||||
(Target_powerpc::Relocate::relocate): ..here..
|
||||
(Target_powerpc::Branch_info::make_stub): ..and here. Return
|
||||
status on whether stub created successfully.
|
||||
(Stub_control::Stub_control): Add "no_size_errors" param. Move
|
||||
default sizing to..
|
||||
(Target_powerpc::do_relax): ..here. Init stub_group_size_ and
|
||||
reduce on relax failure.
|
||||
(Target_powerpc::group_sections): Add "no_size_errors" param.
|
||||
Use stub_group_size_. Set up group info in a temp vector,
|
||||
before building Stub_table vector. Account for input sections
|
||||
possibly already converted to relaxed sections.
|
||||
(Stub_table::init): Delete. Merge into..
|
||||
(Stub_table::Stub_table): ..here.
|
||||
(Stub_table::can_reach_stub): New function.
|
||||
(Stub_table::add_plt_call_entry): Add "from" parameter and
|
||||
return true iff stub could be reached.
|
||||
(Stub_table::add_long_branch_entry): Similarly. Add "r_type"
|
||||
param too.
|
||||
(Stub_table::clear_stubs): Add "all" param.
|
||||
|
||||
2014-11-26 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* powerpc.cc (Stub_control::set_output_and_owner): New function.
|
||||
|
|
343
gold/powerpc.cc
343
gold/powerpc.cc
|
@ -62,6 +62,15 @@ class Output_data_glink;
|
|||
template<int size, bool big_endian>
|
||||
class Stub_table;
|
||||
|
||||
template<int size, bool big_endian>
|
||||
class Target_powerpc;
|
||||
|
||||
struct Stub_table_owner
|
||||
{
|
||||
Output_section* output_section;
|
||||
const Output_section::Input_section* owner;
|
||||
};
|
||||
|
||||
inline bool
|
||||
is_branch_reloc(unsigned int r_type);
|
||||
|
||||
|
@ -77,7 +86,7 @@ public:
|
|||
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);
|
||||
|
@ -260,21 +269,34 @@ public:
|
|||
{ 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; }
|
||||
|
@ -343,7 +365,7 @@ private:
|
|||
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_;
|
||||
|
@ -487,7 +509,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
|
|||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -562,9 +585,6 @@ class Target_powerpc : public Sized_target<size, big_endian>
|
|||
ppc_object->set_has_14bit_branch(data_shndx);
|
||||
}
|
||||
|
||||
Stub_table<size, big_endian>*
|
||||
new_stub_table();
|
||||
|
||||
void
|
||||
do_define_standard_symbols(Symbol_table*, Layout*);
|
||||
|
||||
|
@ -1179,7 +1199,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
|
|||
|
||||
// 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
|
||||
|
@ -1206,7 +1226,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
|
|||
{ }
|
||||
|
||||
// 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;
|
||||
|
@ -1284,6 +1304,10 @@ class Target_powerpc : public Sized_target<size, big_endian>
|
|||
Branches branch_info_;
|
||||
|
||||
bool plt_thread_safe_;
|
||||
|
||||
bool relax_failed_;
|
||||
int relax_fail_count_;
|
||||
int32_t stub_group_size_;
|
||||
};
|
||||
|
||||
template<>
|
||||
|
@ -2361,27 +2385,13 @@ class Stub_control
|
|||
// 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
|
||||
|
@ -2495,12 +2505,14 @@ Stub_control::can_add_to_stub_group(Output_section* o,
|
|||
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());
|
||||
|
@ -2514,50 +2526,89 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout,
|
|||
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 (stub_table == NULL)
|
||||
stub_table = this->new_stub_table();
|
||||
ppcobj->set_stub_table(i->shndx(), stub_table);
|
||||
if (table_owner == NULL)
|
||||
{
|
||||
table_owner = new Stub_table_owner;
|
||||
tables.push_back(table_owner);
|
||||
}
|
||||
ppcobj->set_stub_table(i->shndx(), tables.size() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (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
|
||||
stub_table->init(i, stub_control.output_section());
|
||||
{
|
||||
table_owner->output_section = stub_control.output_section();
|
||||
table_owner->owner = i;
|
||||
}
|
||||
}
|
||||
for (typename std::vector<Stub_table_owner*>::iterator t = tables.begin();
|
||||
t != tables.end();
|
||||
++t)
|
||||
{
|
||||
Stub_table<size, big_endian>* stub_table;
|
||||
|
||||
if ((*t)->owner->is_input_section())
|
||||
stub_table = new Stub_table<size, big_endian>(this,
|
||||
(*t)->output_section,
|
||||
(*t)->owner);
|
||||
else if ((*t)->owner->is_relaxed_input_section())
|
||||
stub_table = static_cast<Stub_table<size, big_endian>*>(
|
||||
(*t)->owner->relaxed_input_section());
|
||||
else
|
||||
gold_unreachable();
|
||||
this->stub_tables_.push_back(stub_table);
|
||||
delete *t;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
max_branch_delta (unsigned int r_type)
|
||||
{
|
||||
if (r_type == elfcpp::R_POWERPC_REL14
|
||||
|| r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
|
||||
|| r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
|
||||
return 1L << 15;
|
||||
if (r_type == elfcpp::R_POWERPC_REL24
|
||||
|| r_type == elfcpp::R_PPC_PLTREL24
|
||||
|| r_type == elfcpp::R_PPC_LOCAL24PC)
|
||||
return 1L << 25;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If this branch needs a plt call stub, or a long branch stub, make one.
|
||||
|
||||
template<int size, bool big_endian>
|
||||
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,
|
||||
|
@ -2590,27 +2641,25 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
|
|||
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()
|
||||
|
@ -2625,16 +2674,16 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
|
|||
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;
|
||||
|
@ -2642,7 +2691,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
|
|||
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);
|
||||
}
|
||||
|
@ -2657,7 +2706,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
|
|||
&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_);
|
||||
|
@ -2680,11 +2729,13 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
|
|||
" 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.
|
||||
|
@ -2752,7 +2803,34 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
|
|||
}
|
||||
}
|
||||
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.
|
||||
|
@ -2777,11 +2855,12 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
|
|||
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
|
||||
|
@ -2790,7 +2869,14 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
|
|||
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?
|
||||
|
@ -3475,26 +3561,35 @@ class Stub_table : public Output_relaxed_input_section
|
|||
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);
|
||||
|
@ -3520,20 +3615,37 @@ class Stub_table : public Output_relaxed_input_section
|
|||
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
|
||||
|
@ -3837,44 +3949,13 @@ class Stub_table : public Output_relaxed_input_section
|
|||
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,
|
||||
|
@ -3886,11 +3967,13 @@ Stub_table<size, big_endian>::add_plt_call_entry(
|
|||
= 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,
|
||||
|
@ -3902,6 +3985,7 @@ Stub_table<size, big_endian>::add_plt_call_entry(
|
|||
= 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.
|
||||
|
@ -3956,9 +4040,11 @@ Stub_table<size, big_endian>::find_plt_call_entry(
|
|||
// 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);
|
||||
|
@ -3970,6 +4056,7 @@ Stub_table<size, big_endian>::add_long_branch_entry(
|
|||
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.
|
||||
|
@ -7107,15 +7194,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
|
|||
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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue