+// Similarly, but for ifunc symbols get the one for ifunc.
+
+template<int size, bool big_endian>
+typename Target_powerpc<size, big_endian>::Reloc_section*
+Target_powerpc<size, big_endian>::rela_dyn_section(Symbol_table* symtab,
+ Layout* layout,
+ bool for_ifunc)
+{
+ if (!for_ifunc)
+ return this->rela_dyn_section(layout);
+
+ if (this->iplt_ == NULL)
+ this->make_iplt_section(symtab, layout);
+ return this->iplt_->rel_plt();
+}
+
+class Stub_control
+{
+ public:
+ // Determine the stub group size. The group size is the absolute
+ // value of the parameter --stub-group-size. If --stub-group-size
+ // is passed a negative value, we restrict stubs to be always before
+ // the stubbed branches.
+ Stub_control(int32_t size)
+ : state_(NO_GROUP), stub_group_size_(abs(size)),
+ stub14_group_size_(abs(size) >> 10),
+ stubs_always_before_branch_(size < 0), suppress_size_errors_(false),
+ 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
+ // group.
+ bool
+ can_add_to_stub_group(Output_section* o,
+ const Output_section::Input_section* i,
+ bool has14);
+
+ const Output_section::Input_section*
+ owner()
+ { return owner_; }
+
+ Output_section*
+ output_section()
+ { return output_section_; }
+
+ void
+ set_output_and_owner(Output_section* o,
+ const Output_section::Input_section* i)
+ {
+ this->output_section_ = o;
+ this->owner_ = i;
+ }
+
+ private:
+ typedef enum
+ {
+ NO_GROUP,
+ FINDING_STUB_SECTION,
+ HAS_STUB_SECTION
+ } State;
+
+ State state_;
+ uint32_t stub_group_size_;
+ uint32_t stub14_group_size_;
+ bool stubs_always_before_branch_;
+ bool suppress_size_errors_;
+ uint64_t group_end_addr_;
+ const Output_section::Input_section* owner_;
+ Output_section* output_section_;
+};
+
+// Return true iff input section can be handled by current stub
+// group.
+
+bool
+Stub_control::can_add_to_stub_group(Output_section* o,
+ const Output_section::Input_section* i,
+ bool has14)
+{
+ uint32_t group_size
+ = has14 ? this->stub14_group_size_ : this->stub_group_size_;
+ bool whole_sec = o->order() == ORDER_INIT || o->order() == ORDER_FINI;
+ uint64_t this_size;
+ uint64_t start_addr = o->address();
+
+ if (whole_sec)
+ // .init and .fini sections are pasted together to form a single
+ // function. We can't be adding stubs in the middle of the function.
+ this_size = o->data_size();
+ else
+ {
+ start_addr += i->relobj()->output_section_offset(i->shndx());
+ this_size = i->data_size();
+ }
+ uint64_t end_addr = start_addr + this_size;
+ bool toobig = this_size > group_size;
+
+ if (toobig && !this->suppress_size_errors_)
+ gold_warning(_("%s:%s exceeds group size"),
+ i->relobj()->name().c_str(),
+ i->relobj()->section_name(i->shndx()).c_str());
+
+ if (this->state_ != HAS_STUB_SECTION
+ && (!whole_sec || this->output_section_ != o)
+ && (this->state_ == NO_GROUP
+ || this->group_end_addr_ - end_addr < group_size))
+ {
+ this->owner_ = i;
+ this->output_section_ = o;
+ }
+
+ if (this->state_ == NO_GROUP)
+ {
+ this->state_ = FINDING_STUB_SECTION;
+ this->group_end_addr_ = end_addr;
+ }
+ else if (this->group_end_addr_ - start_addr < group_size)
+ ;
+ // Adding this section would make the group larger than GROUP_SIZE.
+ else if (this->state_ == FINDING_STUB_SECTION
+ && !this->stubs_always_before_branch_
+ && !toobig)
+ {
+ // But wait, there's more! Input sections up to GROUP_SIZE
+ // bytes before the stub table can be handled by it too.
+ this->state_ = HAS_STUB_SECTION;
+ this->group_end_addr_ = end_addr;
+ }
+ else
+ {
+ this->state_ = NO_GROUP;
+ return false;
+ }
+ return true;
+}
+
+// Look over all the input sections, deciding where to place stubs.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::group_sections(Layout* layout,
+ const Task*)
+{
+ Stub_control stub_control(parameters->options().stub_group_size());
+
+ // Group input sections and insert stub table
+ Stub_table<size, big_endian>* stub_table = NULL;
+ 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();
+ ++o)
+ {
+ typedef Output_section::Input_section_list Input_section_list;
+ for (Input_section_list::const_reverse_iterator i
+ = (*o)->input_sections().rbegin();
+ i != (*o)->input_sections().rend();
+ ++i)
+ {
+ if (i->is_input_section())
+ {
+ 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_control.set_output_and_owner(*o, &*i);
+ stub_table = NULL;
+ }
+ if (stub_table == NULL)
+ stub_table = this->new_stub_table();
+ ppcobj->set_stub_table(i->shndx(), stub_table);
+ }
+ }
+ }
+ if (stub_table != 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());
+ }
+ else
+ stub_table->init(i, stub_control.output_section());
+ }
+}
+
+// If this branch needs a plt call stub, or a long branch stub, make one.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::Branch_info::make_stub(
+ Stub_table<size, big_endian>* stub_table,
+ Stub_table<size, big_endian>* ifunc_stub_table,
+ Symbol_table* symtab) const
+{
+ Symbol* sym = this->object_->global_symbol(this->r_sym_);
+ if (sym != NULL && sym->is_forwarder())
+ sym = symtab->resolve_forwards(sym);
+ const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym);
+ Target_powerpc<size, big_endian>* target =
+ static_cast<Target_powerpc<size, big_endian>*>(
+ parameters->sized_target<size, big_endian>());
+ if (gsym != NULL
+ ? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
+ : this->object_->local_has_plt_offset(this->r_sym_))
+ {
+ if (size == 64
+ && gsym != NULL
+ && target->abiversion() >= 2
+ && !parameters->options().output_is_position_independent()
+ && !is_branch_reloc(this->r_type_))
+ target->glink_section()->add_global_entry(gsym);
+ else
+ {
+ if (stub_table == NULL)
+ stub_table = this->object_->stub_table(this->shndx_);
+ if (stub_table == NULL)
+ {
+ // This is a ref from a data section to an ifunc symbol.
+ stub_table = ifunc_stub_table;
+ }
+ gold_assert(stub_table != NULL);
+ if (gsym != NULL)
+ stub_table->add_plt_call_entry(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_);
+ }
+ }
+ else
+ {
+ Address 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 from = this->object_->get_output_section_offset(this->shndx_);
+ gold_assert(from != invalid_address);
+ from += (this->object_->output_section(this->shndx_)->address()
+ + this->offset_);
+ Address to;
+ if (gsym != NULL)
+ {
+ switch (gsym->source())
+ {
+ case Symbol::FROM_OBJECT:
+ {
+ Object* symobj = gsym->object();
+ if (symobj->is_dynamic()
+ || symobj->pluginobj() != NULL)
+ return;
+ bool is_ordinary;
+ unsigned int shndx = gsym->shndx(&is_ordinary);
+ if (shndx == elfcpp::SHN_UNDEF)
+ return;
+ }
+ break;
+
+ case Symbol::IS_UNDEFINED:
+ return;
+
+ default:
+ break;
+ }
+ Symbol_table::Compute_final_value_status status;
+ to = symtab->compute_final_value<size>(gsym, &status);
+ if (status != Symbol_table::CFVS_OK)
+ return;
+ if (size == 64)
+ to += this->object_->ppc64_local_entry_offset(gsym);
+ }
+ else
+ {
+ const Symbol_value<size>* psymval
+ = this->object_->local_symbol(this->r_sym_);
+ Symbol_value<size> symval;
+ typedef Sized_relobj_file<size, big_endian> ObjType;
+ typename ObjType::Compute_final_local_value_status status
+ = this->object_->compute_final_local_value(this->r_sym_, psymval,
+ &symval, symtab);
+ if (status != ObjType::CFLV_OK
+ || !symval.has_output_value())
+ return;
+ to = symval.value(this->object_, 0);
+ if (size == 64)
+ to += this->object_->ppc64_local_entry_offset(this->r_sym_);
+ }
+ if (!(size == 32 && this->r_type_ == elfcpp::R_PPC_PLTREL24))
+ to += this->addend_;
+ if (stub_table == NULL)
+ stub_table = this->object_->stub_table(this->shndx_);
+ if (size == 64 && target->abiversion() < 2)
+ {
+ unsigned int dest_shndx;
+ if (!target->symval_for_branch(symtab, gsym, this->object_,
+ &to, &dest_shndx))
+ return;
+ }
+ Address delta = to - from;
+ if (delta + max_branch_offset >= 2 * max_branch_offset)
+ {
+ if (stub_table == NULL)
+ {
+ gold_warning(_("%s:%s: branch in non-executable section,"
+ " no long branch stub for you"),
+ this->object_->name().c_str(),
+ this->object_->section_name(this->shndx_).c_str());
+ return;
+ }
+ stub_table->add_long_branch_entry(this->object_, to);
+ }
+ }
+}
+
+// Relaxation hook. This is where we do stub generation.
+
+template<int size, bool big_endian>
+bool
+Target_powerpc<size, big_endian>::do_relax(int pass,
+ const Input_objects*,
+ Symbol_table* symtab,
+ Layout* layout,
+ const Task* task)
+{
+ unsigned int prev_brlt_size = 0;
+ if (pass == 1)
+ {
+ bool thread_safe
+ = this->abiversion() < 2 && parameters->options().plt_thread_safe();
+ if (size == 64
+ && this->abiversion() < 2
+ && !thread_safe
+ && !parameters->options().user_set_plt_thread_safe())
+ {
+ static const char* const thread_starter[] =
+ {
+ "pthread_create",
+ /* libstdc++ */
+ "_ZNSt6thread15_M_start_threadESt10shared_ptrINS_10_Impl_baseEE",
+ /* librt */
+ "aio_init", "aio_read", "aio_write", "aio_fsync", "lio_listio",
+ "mq_notify", "create_timer",
+ /* libanl */
+ "getaddrinfo_a",
+ /* libgomp */
+ "GOMP_parallel",
+ "GOMP_parallel_start",
+ "GOMP_parallel_loop_static",
+ "GOMP_parallel_loop_static_start",
+ "GOMP_parallel_loop_dynamic",
+ "GOMP_parallel_loop_dynamic_start",
+ "GOMP_parallel_loop_guided",
+ "GOMP_parallel_loop_guided_start",
+ "GOMP_parallel_loop_runtime",
+ "GOMP_parallel_loop_runtime_start",
+ "GOMP_parallel_sections",
+ "GOMP_parallel_sections_start",
+ /* libgo */
+ "__go_go",
+ };
+
+ if (parameters->options().shared())
+ thread_safe = true;
+ else
+ {
+ for (unsigned int i = 0;
+ i < sizeof(thread_starter) / sizeof(thread_starter[0]);
+ i++)
+ {
+ Symbol* sym = symtab->lookup(thread_starter[i], NULL);
+ thread_safe = (sym != NULL
+ && sym->in_reg()
+ && sym->in_real_elf());
+ if (thread_safe)
+ break;
+ }
+ }
+ }
+ this->plt_thread_safe_ = thread_safe;
+ this->group_sections(layout, task);
+ }
+
+ // We need address of stub tables valid for make_stub.
+ for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+ p != this->stub_tables_.end();
+ ++p)
+ {
+ const Powerpc_relobj<size, big_endian>* object
+ = static_cast<const Powerpc_relobj<size, big_endian>*>((*p)->relobj());
+ Address off = object->get_output_section_offset((*p)->shndx());
+ gold_assert(off != invalid_address);
+ Output_section* os = (*p)->output_section();
+ (*p)->set_address_and_size(os, off);
+ }
+
+ if (pass != 1)
+ {
+ // Clear plt call stubs, long branch stubs and branch lookup table.
+ prev_brlt_size = this->branch_lookup_table_.size();
+ this->branch_lookup_table_.clear();
+ for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+ p != this->stub_tables_.end();
+ ++p)
+ {
+ (*p)->clear_stubs();
+ }
+ }
+
+ // Build all the stubs.
+ Stub_table<size, big_endian>* ifunc_stub_table
+ = this->stub_tables_.size() == 0 ? NULL : this->stub_tables_[0];
+ Stub_table<size, big_endian>* one_stub_table
+ = this->stub_tables_.size() != 1 ? NULL : ifunc_stub_table;
+ for (typename Branches::const_iterator b = this->branch_info_.begin();
+ b != this->branch_info_.end();
+ b++)
+ {
+ b->make_stub(one_stub_table, ifunc_stub_table, symtab);
+ }
+
+ // Did anything change size?
+ unsigned int num_huge_branches = this->branch_lookup_table_.size();
+ bool again = num_huge_branches != prev_brlt_size;
+ if (size == 64 && num_huge_branches != 0)
+ this->make_brlt_section(layout);
+ if (size == 64 && again)
+ this->brlt_section_->set_current_size(num_huge_branches);
+
+ typedef Unordered_set<Output_section*> Output_sections;
+ Output_sections os_need_update;
+ for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+ p != this->stub_tables_.end();
+ ++p)
+ {
+ if ((*p)->size_update())
+ {
+ again = true;
+ (*p)->add_eh_frame(layout);
+ os_need_update.insert((*p)->output_section());
+ }
+ }
+
+ // Set output section offsets for all input sections in an output
+ // section that just changed size. Anything past the stubs will
+ // need updating.
+ for (typename Output_sections::iterator p = os_need_update.begin();
+ p != os_need_update.end();
+ p++)
+ {
+ Output_section* os = *p;
+ Address off = 0;
+ typedef Output_section::Input_section_list Input_section_list;
+ for (Input_section_list::const_iterator i = os->input_sections().begin();
+ i != os->input_sections().end();
+ ++i)
+ {
+ off = align_address(off, i->addralign());
+ if (i->is_input_section() || i->is_relaxed_input_section())
+ i->relobj()->set_section_offset(i->shndx(), off);
+ if (i->is_relaxed_input_section())
+ {
+ Stub_table<size, big_endian>* stub_table
+ = static_cast<Stub_table<size, big_endian>*>(
+ i->relaxed_input_section());
+ off += stub_table->set_address_and_size(os, off);
+ }
+ else
+ off += i->data_size();
+ }
+ // If .branch_lt is part of this output section, then we have
+ // just done the offset adjustment.
+ os->clear_section_offsets_need_adjustment();
+ }
+
+ if (size == 64
+ && !again
+ && num_huge_branches != 0
+ && parameters->options().output_is_position_independent())
+ {
+ // Fill in the BRLT relocs.
+ this->brlt_section_->reset_brlt_sizes();
+ for (typename Branch_lookup_table::const_iterator p
+ = this->branch_lookup_table_.begin();
+ p != this->branch_lookup_table_.end();
+ ++p)
+ {
+ this->brlt_section_->add_reloc(p->first, p->second);
+ }
+ this->brlt_section_->finalize_brlt_sizes();
+ }
+ return again;
+}
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_plt_fde_location(const Output_data* plt,
+ unsigned char* oview,
+ uint64_t* paddress,
+ off_t* plen) const
+{
+ uint64_t address = plt->address();
+ off_t len = plt->data_size();
+
+ if (plt == this->glink_)
+ {
+ // See Output_data_glink::do_write() for glink contents.
+ if (len == 0)
+ {
+ gold_assert(parameters->doing_static_link());
+ // Static linking may need stubs, to support ifunc and long
+ // branches. We need to create an output section for
+ // .eh_frame early in the link process, to have a place to
+ // attach stub .eh_frame info. We also need to have
+ // registered a CIE that matches the stub CIE. Both of
+ // these requirements are satisfied by creating an FDE and
+ // CIE for .glink, even though static linking will leave
+ // .glink zero length.
+ // ??? Hopefully generating an FDE with a zero address range
+ // won't confuse anything that consumes .eh_frame info.
+ }
+ else if (size == 64)
+ {
+ // There is one word before __glink_PLTresolve
+ address += 8;
+ len -= 8;
+ }
+ else if (parameters->options().output_is_position_independent())
+ {
+ // There are two FDEs for a position independent glink.
+ // The first covers the branch table, the second
+ // __glink_PLTresolve at the end of glink.
+ off_t resolve_size = this->glink_->pltresolve_size;
+ if (oview[9] == elfcpp::DW_CFA_nop)
+ len -= resolve_size;
+ else
+ {
+ address += len - resolve_size;
+ len = resolve_size;
+ }
+ }
+ }
+ else
+ {
+ // Must be a stub table.
+ const Stub_table<size, big_endian>* stub_table
+ = static_cast<const Stub_table<size, big_endian>*>(plt);
+ uint64_t stub_address = stub_table->stub_address();
+ len -= stub_address - address;
+ address = stub_address;
+ }
+
+ *paddress = address;
+ *plen = len;
+}
+