+ of->write_output_view(offset, oview_size, oview);
+ }
+}
+
+// Create the PLT section.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::make_plt_section(Symbol_table* symtab,
+ Layout* layout)
+{
+ if (this->plt_ == NULL)
+ {
+ if (this->got_ == NULL)
+ this->got_section(symtab, layout);
+
+ if (this->glink_ == NULL)
+ make_glink_section(layout);
+
+ // Ensure that .rela.dyn always appears before .rela.plt This is
+ // necessary due to how, on PowerPC and some other targets, .rela.dyn
+ // needs to include .rela.plt in its range.
+ this->rela_dyn_section(layout);
+
+ Reloc_section* plt_rel = new Reloc_section(false);
+ layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
+ elfcpp::SHF_ALLOC, plt_rel,
+ ORDER_DYNAMIC_PLT_RELOCS, false);
+ this->plt_
+ = new Output_data_plt_powerpc<size, big_endian>(this, plt_rel,
+ "** PLT");
+ layout->add_output_section_data(".plt",
+ (size == 32
+ ? elfcpp::SHT_PROGBITS
+ : elfcpp::SHT_NOBITS),
+ elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
+ this->plt_,
+ (size == 32
+ ? ORDER_SMALL_DATA
+ : ORDER_SMALL_BSS),
+ false);
+ }
+}
+
+// Create the IPLT section.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::make_iplt_section(Symbol_table* symtab,
+ Layout* layout)
+{
+ if (this->iplt_ == NULL)
+ {
+ this->make_plt_section(symtab, layout);
+
+ Reloc_section* iplt_rel = new Reloc_section(false);
+ this->rela_dyn_->output_section()->add_output_section_data(iplt_rel);
+ this->iplt_
+ = new Output_data_plt_powerpc<size, big_endian>(this, iplt_rel,
+ "** IPLT");
+ this->plt_->output_section()->add_output_section_data(this->iplt_);
+ }
+}
+
+// A section for huge long branch addresses, similar to plt section.
+
+template<int size, bool big_endian>
+class Output_data_brlt_powerpc : public Output_section_data_build
+{
+ public:
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ typedef Output_data_reloc<elfcpp::SHT_RELA, true,
+ size, big_endian> Reloc_section;
+
+ Output_data_brlt_powerpc(Target_powerpc<size, big_endian>* targ,
+ Reloc_section* brlt_rel)
+ : Output_section_data_build(size == 32 ? 4 : 8),
+ rel_(brlt_rel),
+ targ_(targ)
+ { }
+
+ void
+ reset_brlt_sizes()
+ {
+ this->reset_data_size();
+ this->rel_->reset_data_size();
+ }
+
+ void
+ finalize_brlt_sizes()
+ {
+ this->finalize_data_size();
+ this->rel_->finalize_data_size();
+ }
+
+ // Add a reloc for an entry in the BRLT.
+ void
+ add_reloc(Address to, unsigned int off)
+ { this->rel_->add_relative(elfcpp::R_POWERPC_RELATIVE, this, off, to); }
+
+ // Update section and reloc section size.
+ void
+ set_current_size(unsigned int num_branches)
+ {
+ this->reset_address_and_file_offset();
+ this->set_current_data_size(num_branches * 16);
+ this->finalize_data_size();
+ Output_section* os = this->output_section();
+ os->set_section_offsets_need_adjustment();
+ if (this->rel_ != NULL)
+ {
+ unsigned int reloc_size
+ = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ this->rel_->reset_address_and_file_offset();
+ this->rel_->set_current_data_size(num_branches * reloc_size);
+ this->rel_->finalize_data_size();
+ Output_section* os = this->rel_->output_section();
+ os->set_section_offsets_need_adjustment();
+ }
+ }
+
+ protected:
+ void
+ do_adjust_output_section(Output_section* os)
+ {
+ os->set_entsize(0);
+ }
+
+ // Write to a map file.
+ void
+ do_print_to_mapfile(Mapfile* mapfile) const
+ { mapfile->print_output_data(this, "** BRLT"); }
+
+ private:
+ // Write out the BRLT data.
+ void
+ do_write(Output_file*);
+
+ // The reloc section.
+ Reloc_section* rel_;
+ Target_powerpc<size, big_endian>* targ_;
+};
+
+// Make the branch lookup table section.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::make_brlt_section(Layout* layout)
+{
+ if (size == 64 && this->brlt_section_ == NULL)
+ {
+ Reloc_section* brlt_rel = NULL;
+ bool is_pic = parameters->options().output_is_position_independent();
+ if (is_pic)
+ {
+ // When PIC we can't fill in .branch_lt (like .plt it can be
+ // a bss style section) but must initialise at runtime via
+ // dynamic relocats.
+ this->rela_dyn_section(layout);
+ brlt_rel = new Reloc_section(false);
+ this->rela_dyn_->output_section()->add_output_section_data(brlt_rel);
+ }
+ this->brlt_section_
+ = new Output_data_brlt_powerpc<size, big_endian>(this, brlt_rel);
+ if (this->plt_ && is_pic)
+ this->plt_->output_section()
+ ->add_output_section_data(this->brlt_section_);
+ else
+ layout->add_output_section_data(".branch_lt",
+ (is_pic ? elfcpp::SHT_NOBITS
+ : elfcpp::SHT_PROGBITS),
+ elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
+ this->brlt_section_,
+ (is_pic ? ORDER_SMALL_BSS
+ : ORDER_SMALL_DATA),
+ false);
+ }
+}
+
+// Write out .branch_lt when non-PIC.
+
+template<int size, bool big_endian>
+void
+Output_data_brlt_powerpc<size, big_endian>::do_write(Output_file* of)
+{
+ if (size == 64 && !parameters->options().output_is_position_independent())
+ {
+ const section_size_type offset = this->offset();
+ const section_size_type oview_size
+ = convert_to_section_size_type(this->data_size());
+ unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+ this->targ_->write_branch_lookup_table(oview);
+ of->write_output_view(offset, oview_size, oview);
+ }
+}
+
+static inline uint32_t
+l(uint32_t a)
+{
+ return a & 0xffff;
+}
+
+static inline uint32_t
+hi(uint32_t a)
+{
+ return l(a >> 16);
+}
+
+static inline uint32_t
+ha(uint32_t a)
+{
+ return hi(a + 0x8000);
+}
+
+template<int size>
+struct Eh_cie
+{
+ static const unsigned char eh_frame_cie[12];
+};
+
+template<int size>
+const unsigned char Eh_cie<size>::eh_frame_cie[] =
+{
+ 1, // CIE version.
+ 'z', 'R', 0, // Augmentation string.
+ 4, // Code alignment.
+ 0x80 - size / 8 , // Data alignment.
+ 65, // RA reg.
+ 1, // Augmentation size.
+ (elfcpp::DW_EH_PE_pcrel
+ | elfcpp::DW_EH_PE_sdata4), // FDE encoding.
+ elfcpp::DW_CFA_def_cfa, 1, 0 // def_cfa: r1 offset 0.
+};
+
+// Describe __glink_PLTresolve use of LR, 64-bit version ABIv1.
+static const unsigned char glink_eh_frame_fde_64v1[] =
+{
+ 0, 0, 0, 0, // Replaced with offset to .glink.
+ 0, 0, 0, 0, // Replaced with size of .glink.
+ 0, // Augmentation size.
+ elfcpp::DW_CFA_advance_loc + 1,
+ elfcpp::DW_CFA_register, 65, 12,
+ elfcpp::DW_CFA_advance_loc + 4,
+ elfcpp::DW_CFA_restore_extended, 65
+};
+
+// Describe __glink_PLTresolve use of LR, 64-bit version ABIv2.
+static const unsigned char glink_eh_frame_fde_64v2[] =
+{
+ 0, 0, 0, 0, // Replaced with offset to .glink.
+ 0, 0, 0, 0, // Replaced with size of .glink.
+ 0, // Augmentation size.
+ elfcpp::DW_CFA_advance_loc + 1,
+ elfcpp::DW_CFA_register, 65, 0,
+ elfcpp::DW_CFA_advance_loc + 4,
+ elfcpp::DW_CFA_restore_extended, 65
+};
+
+// Describe __glink_PLTresolve use of LR, 32-bit version.
+static const unsigned char glink_eh_frame_fde_32[] =
+{
+ 0, 0, 0, 0, // Replaced with offset to .glink.
+ 0, 0, 0, 0, // Replaced with size of .glink.
+ 0, // Augmentation size.
+ elfcpp::DW_CFA_advance_loc + 2,
+ elfcpp::DW_CFA_register, 65, 0,
+ elfcpp::DW_CFA_advance_loc + 4,
+ elfcpp::DW_CFA_restore_extended, 65
+};
+
+static const unsigned char default_fde[] =
+{
+ 0, 0, 0, 0, // Replaced with offset to stubs.
+ 0, 0, 0, 0, // Replaced with size of stubs.
+ 0, // Augmentation size.
+ elfcpp::DW_CFA_nop, // Pad.
+ elfcpp::DW_CFA_nop,
+ elfcpp::DW_CFA_nop
+};
+
+template<bool big_endian>
+static inline void
+write_insn(unsigned char* p, uint32_t v)
+{
+ elfcpp::Swap<32, big_endian>::writeval(p, v);
+}
+
+// Stub_table holds information about plt and long branch stubs.
+// Stubs are built in an area following some input section determined
+// by group_sections(). This input section is converted to a relaxed
+// input section allowing it to be resized to accommodate the stubs
+
+template<int size, bool big_endian>
+class Stub_table : public Output_relaxed_input_section
+{
+ public:
+ 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_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_(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);
+
+ 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.
+ bool
+ add_plt_call_entry(Address,
+ const Sized_relobj_file<size, big_endian>*,
+ const Symbol*,
+ unsigned int,
+ Address);
+
+ bool
+ add_plt_call_entry(Address,
+ const Sized_relobj_file<size, big_endian>*,
+ unsigned int,
+ unsigned int,
+ Address);
+
+ // Find a given plt call stub.
+ Address
+ find_plt_call_entry(const Symbol*) const;
+
+ Address
+ find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+ unsigned int) const;
+
+ Address
+ find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+ const Symbol*,
+ unsigned int,
+ Address) const;
+
+ Address
+ find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+ unsigned int,
+ unsigned int,
+ Address) const;
+
+ // Add a long branch stub.
+ 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)
+ {
+ 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(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
+ set_address_and_size(const Output_section* os, Address off)
+ {
+ Address start_off = off;
+ off += this->orig_data_size_;
+ Address my_size = this->plt_size_ + this->branch_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;
+ this->reset_address_and_file_offset();
+ this->set_current_data_size(my_size);
+ this->set_address_and_file_offset(os->address() + start_off,
+ os->offset() + start_off);
+ return my_size;
+ }
+
+ Address
+ stub_address() const
+ {
+ return align_address(this->address() + this->orig_data_size_,
+ this->stub_align());
+ }
+
+ Address
+ stub_offset() const
+ {
+ return align_address(this->offset() + this->orig_data_size_,
+ this->stub_align());
+ }
+
+ section_size_type
+ plt_size() const
+ { return this->plt_size_; }
+
+ bool
+ size_update()
+ {
+ Output_section* os = this->output_section();
+ if (os->addralign() < this->stub_align())
+ {
+ os->set_addralign(this->stub_align());
+ // FIXME: get rid of the insane checkpointing.
+ // We can't increase alignment of the input section to which
+ // stubs are attached; The input section may be .init which
+ // is pasted together with other .init sections to form a
+ // function. Aligning might insert zero padding resulting in
+ // sigill. However we do need to increase alignment of the
+ // output section so that the align_address() on offset in
+ // set_address_and_size() adds the same padding as the
+ // align_address() on address in stub_address().
+ // What's more, we need this alignment for the layout done in
+ // relaxation_loop_body() so that the output section starts at
+ // a suitably aligned address.
+ os->checkpoint_set_addralign(this->stub_align());
+ }
+ if (this->last_plt_size_ != this->plt_size_
+ || this->last_branch_size_ != this->branch_size_)
+ {
+ this->last_plt_size_ = this->plt_size_;
+ this->last_branch_size_ = this->branch_size_;
+ return true;
+ }
+ return false;
+ }
+
+ // Add .eh_frame info for this stub section. Unlike other linker
+ // generated .eh_frame this is added late in the link, because we
+ // only want the .eh_frame info if this particular stub section is
+ // non-empty.
+ void
+ add_eh_frame(Layout* layout)
+ {
+ if (!this->eh_frame_added_)
+ {
+ if (!parameters->options().ld_generated_unwind_info())
+ return;
+
+ // Since we add stub .eh_frame info late, it must be placed
+ // after all other linker generated .eh_frame info so that
+ // merge mapping need not be updated for input sections.
+ // There is no provision to use a different CIE to that used
+ // by .glink.
+ if (!this->targ_->has_glink())
+ return;
+
+ layout->add_eh_frame_for_plt(this,
+ Eh_cie<size>::eh_frame_cie,
+ sizeof (Eh_cie<size>::eh_frame_cie),
+ default_fde,
+ sizeof (default_fde));
+ this->eh_frame_added_ = true;
+ }
+ }
+
+ Target_powerpc<size, big_endian>*
+ targ() const
+ { return targ_; }
+
+ private:
+ class Plt_stub_ent;
+ class Plt_stub_ent_hash;
+ typedef Unordered_map<Plt_stub_ent, unsigned int,
+ Plt_stub_ent_hash> Plt_stub_entries;
+
+ // Alignment of stub section.
+ unsigned int
+ stub_align() const
+ {
+ if (size == 32)
+ return 16;
+ unsigned int min_align = 32;
+ unsigned int user_align = 1 << parameters->options().plt_align();
+ return std::max(user_align, min_align);
+ }
+
+ // Return the plt offset for the given call stub.
+ Address
+ plt_off(typename Plt_stub_entries::const_iterator p, bool* is_iplt) const
+ {
+ const Symbol* gsym = p->first.sym_;
+ if (gsym != NULL)
+ {
+ *is_iplt = (gsym->type() == elfcpp::STT_GNU_IFUNC
+ && gsym->can_use_relative_reloc(false));
+ return gsym->plt_offset();
+ }
+ else
+ {
+ *is_iplt = true;
+ const Sized_relobj_file<size, big_endian>* relobj = p->first.object_;
+ unsigned int local_sym_index = p->first.locsym_;
+ return relobj->local_plt_offset(local_sym_index);
+ }
+ }
+
+ // Size of a given plt call stub.
+ unsigned int
+ plt_call_size(typename Plt_stub_entries::const_iterator p) const
+ {
+ if (size == 32)
+ return 16;
+
+ bool is_iplt;
+ Address plt_addr = this->plt_off(p, &is_iplt);
+ if (is_iplt)
+ plt_addr += this->targ_->iplt_section()->address();
+ else
+ plt_addr += this->targ_->plt_section()->address();
+ Address got_addr = this->targ_->got_section()->output_section()->address();
+ const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+ <const Powerpc_relobj<size, big_endian>*>(p->first.object_);
+ got_addr += ppcobj->toc_base_offset();
+ Address off = plt_addr - got_addr;
+ unsigned int bytes = 4 * 4 + 4 * (ha(off) != 0);
+ if (this->targ_->abiversion() < 2)
+ {
+ bool static_chain = parameters->options().plt_static_chain();
+ bool thread_safe = this->targ_->plt_thread_safe();
+ bytes += (4
+ + 4 * static_chain
+ + 8 * thread_safe
+ + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
+ }
+ unsigned int align = 1 << parameters->options().plt_align();
+ if (align > 1)
+ bytes = (bytes + align - 1) & -align;
+ return bytes;
+ }
+
+ // Return long branch stub size.
+ unsigned int
+ branch_stub_size(Address to)
+ {
+ Address loc
+ = this->stub_address() + this->last_plt_size_ + this->branch_size_;
+ if (to - loc + (1 << 25) < 2 << 25)
+ return 4;
+ if (size == 64 || !parameters->options().output_is_position_independent())
+ return 16;
+ return 32;
+ }
+
+ // Write out stubs.
+ void
+ do_write(Output_file*);
+
+ // Plt call stub keys.
+ class Plt_stub_ent
+ {
+ public:
+ Plt_stub_ent(const Symbol* sym)
+ : sym_(sym), object_(0), addend_(0), locsym_(0)
+ { }
+
+ Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
+ unsigned int locsym_index)
+ : sym_(NULL), object_(object), addend_(0), locsym_(locsym_index)
+ { }
+
+ Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
+ const Symbol* sym,
+ unsigned int r_type,
+ Address addend)
+ : sym_(sym), object_(0), addend_(0), locsym_(0)
+ {
+ if (size != 32)
+ this->addend_ = addend;
+ else if (parameters->options().output_is_position_independent()
+ && r_type == elfcpp::R_PPC_PLTREL24)
+ {
+ this->addend_ = addend;
+ if (this->addend_ >= 32768)
+ this->object_ = object;
+ }
+ }
+
+ Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
+ unsigned int locsym_index,
+ unsigned int r_type,
+ Address addend)
+ : sym_(NULL), object_(object), addend_(0), locsym_(locsym_index)
+ {
+ if (size != 32)
+ this->addend_ = addend;
+ else if (parameters->options().output_is_position_independent()
+ && r_type == elfcpp::R_PPC_PLTREL24)
+ this->addend_ = addend;
+ }
+
+ bool operator==(const Plt_stub_ent& that) const
+ {
+ return (this->sym_ == that.sym_
+ && this->object_ == that.object_
+ && this->addend_ == that.addend_
+ && this->locsym_ == that.locsym_);
+ }
+
+ const Symbol* sym_;
+ const Sized_relobj_file<size, big_endian>* object_;
+ typename elfcpp::Elf_types<size>::Elf_Addr addend_;
+ unsigned int locsym_;
+ };
+
+ class Plt_stub_ent_hash
+ {
+ public:
+ size_t operator()(const Plt_stub_ent& ent) const
+ {
+ return (reinterpret_cast<uintptr_t>(ent.sym_)
+ ^ reinterpret_cast<uintptr_t>(ent.object_)
+ ^ ent.addend_
+ ^ ent.locsym_);
+ }
+ };
+
+ // Long branch stub keys.
+ class Branch_stub_ent
+ {
+ public:
+ Branch_stub_ent(const Powerpc_relobj<size, big_endian>* obj, Address to)
+ : dest_(to), toc_base_off_(0)
+ {
+ if (size == 64)
+ toc_base_off_ = obj->toc_base_offset();
+ }
+
+ bool operator==(const Branch_stub_ent& that) const
+ {
+ return (this->dest_ == that.dest_
+ && (size == 32
+ || this->toc_base_off_ == that.toc_base_off_));
+ }
+
+ Address dest_;
+ unsigned int toc_base_off_;
+ };
+
+ class Branch_stub_ent_hash
+ {
+ public:
+ size_t operator()(const Branch_stub_ent& ent) const
+ { return ent.dest_ ^ ent.toc_base_off_; }
+ };
+
+ // In a sane world this would be a global.
+ Target_powerpc<size, big_endian>* targ_;
+ // Map sym/object/addend to stub offset.
+ Plt_stub_entries plt_call_stubs_;
+ // Map destination address to stub offset.
+ typedef Unordered_map<Branch_stub_ent, unsigned int,
+ Branch_stub_ent_hash> Branch_stub_entries;
+ Branch_stub_entries long_branch_stubs_;
+ // size of input section
+ section_size_type orig_data_size_;
+ // size of stubs
+ 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_;
+};
+
+// Add a plt call stub, if we do not already have one for this
+// sym/object/addend combo.
+
+template<int size, bool big_endian>
+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,
+ Address addend)
+{
+ Plt_stub_ent ent(object, gsym, r_type, addend);
+ unsigned int off = this->plt_size_;
+ std::pair<typename Plt_stub_entries::iterator, bool> p
+ = 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>
+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,
+ Address addend)
+{
+ Plt_stub_ent ent(object, locsym_index, r_type, addend);
+ unsigned int off = this->plt_size_;
+ std::pair<typename Plt_stub_entries::iterator, bool> p
+ = 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.
+
+template<int size, bool big_endian>
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_plt_call_entry(
+ const Sized_relobj_file<size, big_endian>* object,
+ const Symbol* gsym,
+ unsigned int r_type,
+ Address addend) const
+{
+ Plt_stub_ent ent(object, gsym, r_type, addend);
+ typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
+ return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
+}
+
+template<int size, bool big_endian>
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_plt_call_entry(const Symbol* gsym) const
+{
+ Plt_stub_ent ent(gsym);
+ typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
+ return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
+}
+
+template<int size, bool big_endian>
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_plt_call_entry(
+ const Sized_relobj_file<size, big_endian>* object,
+ unsigned int locsym_index,
+ unsigned int r_type,
+ Address addend) const
+{
+ Plt_stub_ent ent(object, locsym_index, r_type, addend);
+ typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
+ return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
+}
+
+template<int size, bool big_endian>
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_plt_call_entry(
+ const Sized_relobj_file<size, big_endian>* object,
+ unsigned int locsym_index) const
+{
+ Plt_stub_ent ent(object, locsym_index);
+ typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
+ return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
+}
+
+// Add a long branch stub if we don't already have one to given
+// destination.
+
+template<int size, bool big_endian>
+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);
+ 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);
+ }
+ return this->can_reach_stub(from, off, r_type);
+}
+
+// Find long branch stub.
+
+template<int size, bool big_endian>
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_long_branch_entry(
+ const Powerpc_relobj<size, big_endian>* object,
+ Address to) const
+{
+ Branch_stub_ent ent(object, to);
+ typename Branch_stub_entries::const_iterator p
+ = this->long_branch_stubs_.find(ent);
+ return p == this->long_branch_stubs_.end() ? invalid_address : p->second;
+}
+
+// A class to handle .glink.
+
+template<int size, bool big_endian>
+class Output_data_glink : public Output_section_data
+{
+ public:
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+ static const Address invalid_address = static_cast<Address>(0) - 1;
+ static const int pltresolve_size = 16*4;
+
+ Output_data_glink(Target_powerpc<size, big_endian>* targ)
+ : Output_section_data(16), targ_(targ), global_entry_stubs_(),
+ end_branch_table_(), ge_size_(0)
+ { }
+
+ void
+ add_eh_frame(Layout* layout);
+
+ void
+ add_global_entry(const Symbol*);
+
+ Address
+ find_global_entry(const Symbol*) const;
+
+ Address
+ global_entry_address() const
+ {
+ gold_assert(this->is_data_size_valid());
+ unsigned int global_entry_off = (this->end_branch_table_ + 15) & -16;
+ return this->address() + global_entry_off;
+ }
+
+ protected:
+ // Write to a map file.
+ void
+ do_print_to_mapfile(Mapfile* mapfile) const
+ { mapfile->print_output_data(this, _("** glink")); }
+
+ private:
+ void
+ set_final_data_size();
+
+ // Write out .glink
+ void
+ do_write(Output_file*);
+
+ // Allows access to .got and .plt for do_write.
+ Target_powerpc<size, big_endian>* targ_;
+
+ // Map sym to stub offset.
+ typedef Unordered_map<const Symbol*, unsigned int> Global_entry_stub_entries;
+ Global_entry_stub_entries global_entry_stubs_;
+
+ unsigned int end_branch_table_, ge_size_;
+};
+
+template<int size, bool big_endian>
+void
+Output_data_glink<size, big_endian>::add_eh_frame(Layout* layout)
+{
+ if (!parameters->options().ld_generated_unwind_info())
+ return;
+
+ if (size == 64)
+ {
+ if (this->targ_->abiversion() < 2)
+ layout->add_eh_frame_for_plt(this,
+ Eh_cie<64>::eh_frame_cie,
+ sizeof (Eh_cie<64>::eh_frame_cie),
+ glink_eh_frame_fde_64v1,
+ sizeof (glink_eh_frame_fde_64v1));
+ else
+ layout->add_eh_frame_for_plt(this,
+ Eh_cie<64>::eh_frame_cie,
+ sizeof (Eh_cie<64>::eh_frame_cie),
+ glink_eh_frame_fde_64v2,
+ sizeof (glink_eh_frame_fde_64v2));
+ }
+ else
+ {
+ // 32-bit .glink can use the default since the CIE return
+ // address reg, LR, is valid.
+ layout->add_eh_frame_for_plt(this,
+ Eh_cie<32>::eh_frame_cie,
+ sizeof (Eh_cie<32>::eh_frame_cie),
+ default_fde,
+ sizeof (default_fde));
+ // Except where LR is used in a PIC __glink_PLTresolve.
+ if (parameters->options().output_is_position_independent())
+ layout->add_eh_frame_for_plt(this,
+ Eh_cie<32>::eh_frame_cie,
+ sizeof (Eh_cie<32>::eh_frame_cie),
+ glink_eh_frame_fde_32,
+ sizeof (glink_eh_frame_fde_32));
+ }