+// Generate a suitable FDE to describe code in this stub group.
+// The __tls_get_addr_opt call stub needs to describe where it saves
+// LR, to support exceptions that might be thrown from __tls_get_addr.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::init_plt_fde()
+{
+ unsigned char* p = this->plt_fde_;
+ // offset pcrel sdata4, size udata4, and augmentation size byte.
+ memset (p, 0, 9);
+ p += 9;
+ if (this->tls_get_addr_opt_bctrl_ != -1u)
+ {
+ unsigned int to_bctrl = this->tls_get_addr_opt_bctrl_ / 4;
+ if (to_bctrl < 64)
+ *p++ = elfcpp::DW_CFA_advance_loc + to_bctrl;
+ else if (to_bctrl < 256)
+ {
+ *p++ = elfcpp::DW_CFA_advance_loc1;
+ *p++ = to_bctrl;
+ }
+ else if (to_bctrl < 65536)
+ {
+ *p++ = elfcpp::DW_CFA_advance_loc2;
+ elfcpp::Swap<16, big_endian>::writeval(p, to_bctrl);
+ p += 2;
+ }
+ else
+ {
+ *p++ = elfcpp::DW_CFA_advance_loc4;
+ elfcpp::Swap<32, big_endian>::writeval(p, to_bctrl);
+ p += 4;
+ }
+ *p++ = elfcpp::DW_CFA_offset_extended_sf;
+ *p++ = 65;
+ *p++ = -(this->targ_->stk_linker() / 8) & 0x7f;
+ *p++ = elfcpp::DW_CFA_advance_loc + 4;
+ *p++ = elfcpp::DW_CFA_restore_extended;
+ *p++ = 65;
+ }
+ this->plt_fde_len_ = p - this->plt_fde_;
+}
+
+// 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.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::add_eh_frame(Layout* layout)
+{
+ 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;
+
+ if (this->plt_size_ + this->branch_size_ + this->need_save_res_ == 0)
+ return;
+
+ this->init_plt_fde();
+ layout->add_eh_frame_for_plt(this,
+ Eh_cie<size>::eh_frame_cie,
+ sizeof (Eh_cie<size>::eh_frame_cie),
+ this->plt_fde_, this->plt_fde_len_);
+}
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::remove_eh_frame(Layout* layout)
+{
+ if (this->plt_fde_len_ != 0)
+ {
+ layout->remove_eh_frame_for_plt(this,
+ Eh_cie<size>::eh_frame_cie,
+ sizeof (Eh_cie<size>::eh_frame_cie),
+ this->plt_fde_, this->plt_fde_len_);
+ this->plt_fde_len_ = 0;
+ }
+}
+