+// Define symbols on stubs, identifying the stub.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
+{
+ if (!this->plt_call_stubs_.empty())
+ {
+ // The key for the plt call stub hash table includes addresses,
+ // therefore traversal order depends on those addresses, which
+ // can change between runs if gold is a PIE. Unfortunately the
+ // output .symtab ordering depends on the order in which symbols
+ // are added to the linker symtab. We want reproducible output
+ // so must sort the call stub symbols.
+ typedef typename Plt_stub_entries::const_iterator plt_iter;
+ std::vector<plt_iter> sorted;
+ sorted.resize(this->plt_call_stubs_.size());
+
+ for (plt_iter cs = this->plt_call_stubs_.begin();
+ cs != this->plt_call_stubs_.end();
+ ++cs)
+ sorted[cs->second.indx_] = cs;
+
+ for (unsigned int i = 0; i < this->plt_call_stubs_.size(); ++i)
+ {
+ plt_iter cs = sorted[i];
+ char add[10];
+ add[0] = 0;
+ if (cs->first.addend_ != 0)
+ sprintf(add, "+%x", static_cast<uint32_t>(cs->first.addend_));
+ char obj[10];
+ obj[0] = 0;
+ if (cs->first.object_)
+ {
+ const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+ <const Powerpc_relobj<size, big_endian>*>(cs->first.object_);
+ sprintf(obj, "%x:", ppcobj->uniq());
+ }
+ char localname[9];
+ const char *symname;
+ if (cs->first.sym_ == NULL)
+ {
+ sprintf(localname, "%x", cs->first.locsym_);
+ symname = localname;
+ }
+ else if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ symname = this->targ_->tls_get_addr_opt()->name();
+ else
+ symname = cs->first.sym_->name();
+ char* name = new char[8 + 10 + strlen(obj) + strlen(symname) + strlen(add) + 1];
+ sprintf(name, "%08x.plt_call.%s%s%s", this->uniq_, obj, symname, add);
+ Address value
+ = this->stub_address() - this->address() + cs->second.off_;
+ unsigned int stub_size = this->plt_call_align(this->plt_call_size(cs));
+ this->targ_->define_local(symtab, name, this, value, stub_size);
+ }
+ }
+
+ typedef typename Branch_stub_entries::const_iterator branch_iter;
+ for (branch_iter bs = this->long_branch_stubs_.begin();
+ bs != this->long_branch_stubs_.end();
+ ++bs)
+ {
+ if (bs->second.save_res_)
+ continue;
+
+ char* name = new char[8 + 13 + 16 + 1];
+ sprintf(name, "%08x.long_branch.%llx", this->uniq_,
+ static_cast<unsigned long long>(bs->first.dest_));
+ Address value = (this->stub_address() - this->address()
+ + this->plt_size_ + bs->second.off_);
+ bool need_lt = false;
+ unsigned int stub_size = this->branch_stub_size(bs, &need_lt);
+ this->targ_->define_local(symtab, name, this, value, stub_size);
+ }
+}
+
+// Emit the start of a __tls_get_addr_opt plt call stub.
+
+template<int size, bool big_endian>
+bool
+Stub_table<size, big_endian>::build_tls_opt_head(
+ unsigned char** pp,
+ typename Plt_stub_entries::const_iterator cs)
+{
+ if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ {
+ unsigned char* p = *pp;
+ if (size == 64)
+ {
+ write_insn<big_endian>(p, ld_11_3 + 0);
+ p += 4;
+ write_insn<big_endian>(p, ld_12_3 + 8);
+ p += 4;
+ write_insn<big_endian>(p, mr_0_3);
+ p += 4;
+ write_insn<big_endian>(p, cmpdi_11_0);
+ p += 4;
+ write_insn<big_endian>(p, add_3_12_13);
+ p += 4;
+ write_insn<big_endian>(p, beqlr);
+ p += 4;
+ write_insn<big_endian>(p, mr_3_0);
+ p += 4;
+ if (cs->second.r2save_ && !cs->second.localentry0_)
+ {
+ write_insn<big_endian>(p, mflr_11);
+ p += 4;
+ write_insn<big_endian>(p, (std_11_1 + this->targ_->stk_linker()));
+ p += 4;
+ }
+ }
+ else
+ {
+ write_insn<big_endian>(p, lwz_11_3 + 0);
+ p += 4;
+ write_insn<big_endian>(p, lwz_12_3 + 4);
+ p += 4;
+ write_insn<big_endian>(p, mr_0_3);
+ p += 4;
+ write_insn<big_endian>(p, cmpwi_11_0);
+ p += 4;
+ write_insn<big_endian>(p, add_3_12_2);
+ p += 4;
+ write_insn<big_endian>(p, beqlr);
+ p += 4;
+ write_insn<big_endian>(p, mr_3_0);
+ p += 4;
+ write_insn<big_endian>(p, nop);
+ p += 4;
+ }
+ *pp = p;
+ return true;
+ }
+ return false;
+}
+
+// Emit the tail of a __tls_get_addr_opt plt call stub.
+
+template<int size, bool big_endian>
+bool
+Stub_table<size, big_endian>::build_tls_opt_tail(
+ unsigned char* p,
+ typename Plt_stub_entries::const_iterator cs)
+{
+ if (size == 64
+ && cs->second.r2save_
+ && !cs->second.localentry0_
+ && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+ {
+ write_insn<big_endian>(p, bctrl);
+ p += 4;
+ write_insn<big_endian>(p, ld_2_1 + this->targ_->stk_toc());
+ p += 4;
+ write_insn<big_endian>(p, ld_11_1 + this->targ_->stk_linker());
+ p += 4;
+ write_insn<big_endian>(p, mtlr_11);
+ p += 4;
+ write_insn<big_endian>(p, blr);
+ return true;
+ }
+ return false;
+}
+
+// Emit pc-relative plt call stub code.
+
+template<bool big_endian>
+static unsigned char*
+build_powerxx_offset(unsigned char* p, uint64_t off, uint64_t odd, bool load)
+{
+ uint64_t insn;
+ if (off - odd + (1ULL << 33) < 1ULL << 34)
+ {
+ off -= odd;
+ if (odd)
+ {
+ write_insn<big_endian>(p, nop);
+ p += 4;
+ }
+ if (load)
+ insn = pld_12_pc;
+ else
+ insn = paddi_12_pc;
+ insn |= d34(off);
+ write_insn<big_endian>(p, insn >> 32);
+ p += 4;
+ write_insn<big_endian>(p, insn & 0xffffffff);
+ }
+ else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ {
+ off -= 8 - odd;
+ write_insn<big_endian>(p, li_11_0 | (ha34(off) & 0xffff));
+ p += 4;
+ if (!odd)
+ {
+ write_insn<big_endian>(p, sldi_11_11_34);
+ p += 4;
+ }
+ insn = paddi_12_pc | d34(off);
+ write_insn<big_endian>(p, insn >> 32);
+ p += 4;
+ write_insn<big_endian>(p, insn & 0xffffffff);
+ p += 4;
+ if (odd)
+ {
+ write_insn<big_endian>(p, sldi_11_11_34);
+ p += 4;
+ }
+ if (load)
+ write_insn<big_endian>(p, ldx_12_11_12);
+ else
+ write_insn<big_endian>(p, add_12_11_12);
+ }
+ else
+ {
+ off -= odd + 8;
+ write_insn<big_endian>(p, lis_11 | ((ha34(off) >> 16) & 0x3fff));
+ p += 4;
+ write_insn<big_endian>(p, ori_11_11_0 | (ha34(off) & 0xffff));
+ p += 4;
+ if (odd)
+ {
+ write_insn<big_endian>(p, sldi_11_11_34);
+ p += 4;
+ }
+ insn = paddi_12_pc | d34(off);
+ write_insn<big_endian>(p, insn >> 32);
+ p += 4;
+ write_insn<big_endian>(p, insn & 0xffffffff);
+ p += 4;
+ if (!odd)
+ {
+ write_insn<big_endian>(p, sldi_11_11_34);
+ p += 4;
+ }
+ if (load)
+ write_insn<big_endian>(p, ldx_12_11_12);
+ else
+ write_insn<big_endian>(p, add_12_11_12);
+ }
+ p += 4;
+ return p;
+}
+
+// Gets the address of a label (1:) in r11 and builds an offset in r12,
+// then adds it to r11 (LOAD false) or loads r12 from r11+r12 (LOAD true).
+// mflr %r12
+// bcl 20,31,1f
+// 1: mflr %r11
+// mtlr %r12
+// lis %r12,xxx-1b@highest
+// ori %r12,%r12,xxx-1b@higher
+// sldi %r12,%r12,32
+// oris %r12,%r12,xxx-1b@high
+// ori %r12,%r12,xxx-1b@l
+// add/ldx %r12,%r11,%r12
+
+template<bool big_endian>
+static unsigned char*
+build_notoc_offset(unsigned char* p, uint64_t off, bool load)
+{
+ write_insn<big_endian>(p, mflr_12);
+ p += 4;
+ write_insn<big_endian>(p, bcl_20_31);
+ p += 4;
+ write_insn<big_endian>(p, mflr_11);
+ p += 4;
+ write_insn<big_endian>(p, mtlr_12);
+ p += 4;
+ if (off + 0x8000 < 0x10000)
+ {
+ if (load)
+ write_insn<big_endian>(p, ld_12_11 + l(off));
+ else
+ write_insn<big_endian>(p, addi_12_11 + l(off));
+ }
+ else if (off + 0x80008000ULL < 0x100000000ULL)
+ {
+ write_insn<big_endian>(p, addis_12_11 + ha(off));
+ p += 4;
+ if (load)
+ write_insn<big_endian>(p, ld_12_12 + l(off));
+ else
+ write_insn<big_endian>(p, addi_12_12 + l(off));
+ }
+ else
+ {
+ if (off + 0x800000000000ULL < 0x1000000000000ULL)
+ {
+ write_insn<big_endian>(p, li_12_0 + ((off >> 32) & 0xffff));
+ p += 4;
+ }
+ else
+ {
+ write_insn<big_endian>(p, lis_12 + ((off >> 48) & 0xffff));
+ p += 4;
+ if (((off >> 32) & 0xffff) != 0)
+ {
+ write_insn<big_endian>(p, ori_12_12_0 + ((off >> 32) & 0xffff));
+ p += 4;
+ }
+ }
+ if (((off >> 32) & 0xffffffffULL) != 0)
+ {
+ write_insn<big_endian>(p, sldi_12_12_32);
+ p += 4;
+ }
+ if (hi(off) != 0)
+ {
+ write_insn<big_endian>(p, oris_12_12_0 + hi(off));
+ p += 4;
+ }
+ if (l(off) != 0)
+ {
+ write_insn<big_endian>(p, ori_12_12_0 + l(off));
+ p += 4;
+ }
+ if (load)
+ write_insn<big_endian>(p, ldx_12_11_12);
+ else
+ write_insn<big_endian>(p, add_12_11_12);
+ }
+ p += 4;
+ return p;
+}
+
+// Size of a given plt call stub.
+
+template<int size, bool big_endian>
+unsigned int
+Stub_table<size, big_endian>::plt_call_size(
+ typename Plt_stub_entries::const_iterator p) const
+{
+ if (size == 32)
+ {
+ const Symbol* gsym = p->first.sym_;
+ return (4 * 4
+ + (this->targ_->is_tls_get_addr_opt(gsym) ? 8 * 4 : 0));
+ }
+
+ const Output_data_plt_powerpc<size, big_endian>* plt;
+ uint64_t plt_addr = this->plt_off(p, &plt);
+ plt_addr += plt->address();
+ unsigned int bytes = 0;
+ const Symbol* gsym = p->first.sym_;
+ if (this->targ_->is_tls_get_addr_opt(gsym))
+ {
+ if (p->second.r2save_ && !p->second.localentry0_)
+ bytes = 13 * 4;
+ else
+ bytes = 7 * 4;
+ }
+
+ if (p->second.r2save_)
+ bytes += 4;
+
+ if (this->targ_->powerxx_stubs())
+ {
+ uint64_t from = this->stub_address() + p->second.off_ + bytes;
+ if (bytes > 8 * 4)
+ from -= 4 * 4;
+ uint64_t odd = from & 4;
+ uint64_t off = plt_addr - from;
+ if (off - odd + (1ULL << 33) < 1ULL << 34)
+ bytes += odd + 4 * 4;
+ else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ bytes += 7 * 4;
+ else
+ bytes += 8 * 4;
+ return bytes;
+ }
+
+ if (p->second.notoc_)
+ {
+ uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
+ if (bytes > 32)
+ from -= 4 * 4;
+ uint64_t off = plt_addr - from;
+ if (off + 0x8000 < 0x10000)
+ bytes += 7 * 4;
+ else if (off + 0x80008000ULL < 0x100000000ULL)
+ bytes += 8 * 4;
+ else
+ {
+ bytes += 8 * 4;
+ if (off + 0x800000000000ULL >= 0x1000000000000ULL
+ && ((off >> 32) & 0xffff) != 0)
+ bytes += 4;
+ if (((off >> 32) & 0xffffffffULL) != 0)
+ bytes += 4;
+ if (hi(off) != 0)
+ bytes += 4;
+ if (l(off) != 0)
+ bytes += 4;
+ }
+ return bytes;
+ }
+
+ uint64_t 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();
+ uint64_t off = plt_addr - got_addr;
+ bytes += 3 * 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)));
+ }
+ return bytes;
+}
+
+// Return long branch stub size.
+
+template<int size, bool big_endian>
+unsigned int
+Stub_table<size, big_endian>::branch_stub_size(
+ typename Branch_stub_entries::const_iterator p,
+ bool* need_lt)
+{
+ Address loc = this->stub_address() + this->last_plt_size_ + p->second.off_;
+ if (size == 32)
+ {
+ if (p->first.dest_ - loc + (1 << 25) < 2 << 25)
+ return 4;
+ if (parameters->options().output_is_position_independent())
+ return 32;
+ return 16;
+ }
+
+ uint64_t off = p->first.dest_ - loc;
+ if (p->second.notoc_)
+ {
+ if (this->targ_->powerxx_stubs())
+ {
+ Address odd = loc & 4;
+ if (off + (1 << 25) < 2 << 25)
+ return odd + 12;
+ if (off - odd + (1ULL << 33) < 1ULL << 34)
+ return odd + 16;
+ if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ return 28;
+ return 32;
+ }
+ off -= 8;
+ if (off + 0x8000 < 0x10000)
+ return 24;
+ if (off + 0x80008000ULL < 0x100000000ULL)
+ {
+ if (off + 24 + (1 << 25) < 2 << 25)
+ return 28;
+ return 32;
+ }
+ unsigned int bytes = 32;
+ if (off + 0x800000000000ULL >= 0x1000000000000ULL
+ && ((off >> 32) & 0xffff) != 0)
+ bytes += 4;
+ if (((off >> 32) & 0xffffffffULL) != 0)
+ bytes += 4;
+ if (hi(off) != 0)
+ bytes += 4;
+ if (l(off) != 0)
+ bytes += 4;
+ return bytes;
+ }
+
+ if (off + (1 << 25) < 2 << 25)
+ return 4;
+ if (!this->targ_->powerxx_stubs())
+ *need_lt = true;
+ return 16;
+}
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::plt_error(const Plt_stub_key& p)
+{
+ if (p.sym_)
+ gold_error(_("linkage table error against `%s'"),
+ p.sym_->demangled_name().c_str());
+ else
+ gold_error(_("linkage table error against `%s:[local %u]'"),
+ p.object_->name().c_str(),
+ p.locsym_);
+}
+